Jump to content
The simFlight Network Forums

Recommended Posts

Posted

MSFSVariableServices

Background

A new class called MSFSVariableServices has been added to the FSUIPCClient dll from Version 3.2.1-Beta. 

This module allows direct access to LVars and HVars for MSFS. Other Flight Sims (e.g. P3D) are NOT supported. 

It uses John Dowson's WASM module for MSFS which must be installed in your MSFS and will also need to be installed in your users' MSFS. This module is installed by the FSUIPC7 installer.

This facility is completely independent of FSUIPC. When using this feature in the FSUIPCClient dll you will not need a connection to FSUIPC. It works separately.

Note that the FSUIPCConnection.ReadLVar() and WriteLVar() methods still use the legacy LVAR access provided by Offset 0x0D70. You can still use these, however this method of LVAR access has historically been slow (read one variable per Process()) and will continue to be. The new MSFSVariableServices offers significant performance advantages.


Requirements

As the developer you will also need John's FSUIPC_WAPID.dll. This can be found in latest the WASM package which can be downloaded from the "Additional Free Software" section of the FSUIPC website: (Look for "FSUIPC WASM Module x.x.x")

http://www.fsuipc.com/

Inside the Zip file, the DLL can be found in \FSUIPC_WAPI\dll

This dll must be distributed with your application and should be located in the same folder as your .exe and FSUIPCClient.dll.

In Visual Studio you can add this DLL to your project as a normal project item and set the property "Copy to Output Directory" to "Copy if Newer".

You CANNOT add this dll as a reference to your project as it's an unmanged C-style DLL.

 

64Bit Only

The WAPI DLL is 64 bit so you must run your .NET application as 64-bit. This means the 'Platform Target' must be x64, not x86. 

You can also target 'Any CPU' but you must untick the box that says "Prefer 32-bit".

These options can be found in the project properties page on the Build tab.  (Right-Click the project in the solution explorer and select Properties).

 

Example Code:

An example project (C# and VB.NET) can be downloaded from the FSUIPCClient DLL website:

http://fsuipc.paulhenty.com/#downloads


How to Use:

The MSFSVaraibleServices is a static class in the FSUIPC namespace. You need to add a using statement for the namespace:

using FSUIPC;

If you don't want to use the full class name you can optionally add an alias:

using VS = FSUIPC.MSFSVariableServices;

The rest of the code examples here use this alias.

Events:

This module is purely event-driven. The following events should be handled:

OnValuesChanged will notify you when any of the LVar values in the sim change.

        VS.OnValuesChanged += VS_OnValuesChanged;

        private void VS_OnValuesChanged(object sender, EventArgs e)
        {
            // At least one LVar value has changed.
            // Do work with new values. For examples see later in this post
        }

OnVariableListChanged will notify you when the WASM module has discovered new LVars and HVars. 

        VS.OnVariableListChanged += VS_VariableListChanged ;

        private void VS_VariableListChanged (object sender, EventArgs e)
        {
          // Variables have been discovered or changed
        }

OnLogEntryReceived. This event will notify you of log entries being produced by the WASM module. This will give you status updates, errors etc. What you do with the messages is up to you. In the example below I'm writing the log entries to a listbox, but you could show them in the Debug output window or write to your own log.

VS.OnLogEntryReceived += VS_OnLogEntryReceived;

private void VS_OnLogEntryReceived(object sender, LogEventArgs e)
{
    this.lstLog.Items.Add(e.LogEntry);
}

 

Initialisation:

After setting up your events, you need to call Init(). This will attempt a connection to the flight sim via SimConnect. The result of this will be returned as a log entry. You need to pass in the handle of the main window in your application:

e.g: Assuming the code is in the code behind the main form/window:

WinForms:

VS.Init(this.Handle);

WPF:

VS.Init(new WindowInteropHelper(this).Handle);

 

Configuration:

There are a number of properties to control how the module behaves. The default values for these properties are good for most cases.

VS.LogLevel 

Defines what kinds of events (if any) are logged via the logging system. Values can be:

  •         DISABLE_LOG
  •         LOG_LEVEL_INFO
  •         LOG_LEVEL_BUFFER
  •         LOG_LEVEL_DEBUG
  •         LOG_LEVEL_TRACE
  •         ENABLE_LOG

 

VS.SimConfigConnection

By default the MSFSVariableServices will connect to the local instance of SimConnect. If you have other instances configured on your machine (e.g. connections to remote PCs running MSFS) you can change this property to use them.

 

Starting the module:

After initializing and setting your configuration, you must then call Start() to tell the WASM module to start receiving data.

VS.Start();

NOTE: After calling Start() it will take the WASM module a few seconds to collect the data from the flight sim and do its housekeeping. This means that LVars and HVars may not be instantly available. It's best to wait until the OnVariableListChanged event has fired before reading LVars.

If you ever want to pause the data transfer you can call 

VS.Stop();

No more data updates will be received by the WASM module until you call Start() again.

 

Accessing LVars:

The module automatically discovers the loaded LVars. These are located in the LVars property and are stored as a collection of FsLVar objects.

You can access the individual FsLVar objects like this:

1. Iterate through all of them:

foreach (FsLVar lvar in VS.LVars)
{
   // Do something with each LVar... e.g.
   CheckLVar(lvar);
}

2. Get a specific LVar by name:

FsLVar myLVar = VS.LVars["B747_8_MFD_Range"];

 

Getting names of all LVars

You can find a List<string> of all known LVar names at:

VS.LVars.Names

Checking if LVars exist

To find if an LVar exists by Name use:

if (VS.LVars.Exists("B747_8_MFD_Range"))
{
    // Exists
}

Counting number of LVars

You can get the number of LVars with:

VA.LVars.Count


Accessing HVars

HVars are stored as a collection in the property

VS.HVars

This collection works in exactly the same way as LVars above.


Using the LVar values:

Each FsLVar object has a Value property. This is a double type as all LVars are stored as doubles within the Flight Sim.

e.g. To get the value for B747_8_MFD_Range use:

double range = VS.LVars["B747_8_MFD_Range"].Value;

Advanced use:

You can also keep a reference to the LVar 

FsLVar mfdRange = VS.LVars["B747_8_MFD_Range"];

double range = mfdRange.Value;


Updating the LVar values:

To write a new value for an LVar call the SetValue() method. This will sent the new value to be written to the Flight Sim. 

e.g. 

VS.LVars["B747_8_MFD_Range"].SetValue(2);

NOTE: This will NOT update the local Value property of the FsLVar. It only sends a request to the Sim to change the value. The local Value property will be updated when the Sim sends back the new value. The OnValueChanged event will also fire at this time.


Setting HVars:

To set an HVar, call the Set() method of the FsHVar object:

VS.HVars["AS1000_PFD_NAV_Switch"].Set();

This will cause the new HVar to be set in the Flight Sim immediately. 


Checking for changes in LVars:

There are a number of ways to know if LVar values have changed:

1. Check the ValueChanged property of the FsLVar object:

if (VS.LVars["B747_8_MFD_Range"].ValueChanged)
{
    // do something with new range value
}


2. Get a collection of all the changed LVars from the LVarsChanged property:

foreach (FsLVar lvar in VS.LVarsChanged)
{
        // Do something with each changed LVar... e.g.
        NotifyChangesTo(lvar);
}

Note that the LVarChanged collection works the same way as the normal LVars collection.

3. You can handle the OnValueChanged event of an FsLVar:

e.g. to be notified of changed to B747_8_MFD_Range:

 VS.LVars["B747_8_MFD_Range"].OnValueChanged += MFDRange_OnValueChanged;

 private void MFDRange_OnValueChanged(object sender, LVarEvent e)
 {
     double newSetting = e.LVar.Value;
     MessageBox.Show("MFD Range Changed to " + newSetting.ToString());
 }

Logging LVars and HVars:

List of LVars and HVars can sent to the log by calling:

VS.LogLVars();
VS.LogHVars();

Creating a new LVar:

Call CreateNewLVar passing in the name of the new LVar and its initial value.

VS.CreateLVar("My_New_LVar", 1.25);

Executing Calculator Code:

VS.ExecuteCalculatorCode(myCode);

where myCode is a string containing the code to execute. 

Paul

Posted
On 5/3/2021 at 5:59 PM, kingm56 said:

Hello, My Friend!  Is the beta 3.2.1-Beta available via NuGet package?  

Yes, but for Beta versions you must check the box "Include Pre-release" in the Nuget manager..

Paul

Posted

Paul,

I continue get a "An attempt was made to load a program with an incorrect format."  The FSUIPC_WAPID.dll is located inside the same folder as the .exe and FSUPIC.dll.  Any ideas?  

Posted

Make sure your exe is compiling and running as a 64 bit process. The FSUIPC_WASPID.dll is 64bit so cannot be loaded from a 32-bit process.

In your exe project properties, go to the Build tab. Make sure your platform target is either x64 or "any cpu". If it's "any" make sure the box for 'prefer 32bit" is NOT checked.

Paul

Posted

Thank you, sir!

I'm able to instantiate the object now; however, I'm still getting a object reference not set to an instance error...even when initializing it first.  Below is just a simple method to obtain A320 battery state:

            MSFSVariableServices VS = new MSFSVariableServices();
            VS.OnLogEntryReceived += VS_OnLogEntryReceived;
            VS.Init(this.Handle);
            VS.Start();
            Thread.Sleep(2000);//gives a few seconds to collect the data from wasm module
            double batt = VS.LVars["A320_Neo_BAT_State"].Value;
            MessageBox.Show(batt.ToString()); 

Posted

You'll need to add a call to VS.RefreshData() after the sleep, and before you try to read the LVAR.

If it still doesn't work try the following:

1. Waiting longer than 2 seconds.

2. Check the list of LVar names at VS.LVars.Names to see all the LVars that have been found. (If you have logging set up you could also use VS.LogLVars())

Paul

Posted

Hey Paul!

 

            MSFSVariableServices VS = new MSFSVariableServices();
            VS.OnLogEntryReceived += VS_OnLogEntryReceived;
            VS.Init(this.Handle);
            VS.LogLVars();
            VS.Start();
            Thread.Sleep(5000);//gives a few seconds to collect the data
            VS.RefreshData();

            try
            {
                MessageBox.Show(log);
            }
            catch(Exception ex) { MessageBox.Show(ex.ToString()); }

Apparently I'm only logging 000 LVARS.  Any other ideas, my friend? 

Posted

Sorry to be nausaunse, Paul; however, I'm still unable to read any LVARS.  I continue to get this:

    public partial class Dashboard : Form
    {
        MSFSVariableServices VS = new MSFSVariableServices();

      .....

        public Dashboard()
        {
            try
            {
                InitializeComponent();
                Connect.Click += Button_Click;

                VS.OnLogEntryReceived += VS_OnLogEntryReceived;
            }
            catch (Exception) { }
        }

        private void debugToolStripMenuItem_Click(object sender, EventArgs e)
        {
            VS.Init(this.Handle);            
            VS.Start();
            Thread.Sleep(5000);//gives a few seconds to collect the data
            VS.RefreshData();
            VS.LogLVars();
            Thread.Sleep(2000);//gives a few seconds to collect the data
            try
            {
                MessageBox.Show(log);
            }
            catch(Exception ex) { MessageBox.Show(ex.ToString()); }
        }

 

 

image.png.b37e7bb327fa666923f2692b1bf2aadf.png

Posted

I have written a small class that handles the connection starting, connection error handling etc.

Reason is my app is constantly running and making requests and should not be impacted if the sim is not running or starting or shut down in between.

With the FSUIPCConnection I could just check if the connection is open, if not try to open it and if there were an error (e.g. FSUIPC was not running) I would get some kind of exception and the app would wait a bit and try again.
With the new MSFSVariableServices there seems to be no immediate failure (e.g. when start fails), instead the information is only then received in the log entry message events.

Hence why I wrote this handler class.
Usage is quite simple, when you run code through the "Try"-methods it will make sure the connection is available, start the connection if it's not and give a result if your action was successful. In case of an error it will try to stop the connection so on the next request it will try to start it again.
Examples:

// Initialize the wrapper. You still need your handle (e.g. this.Handle from Windows.Forms
// or from a custom Messaging component in a console app) and optional a handler for messages
VariableHandler variables = new VariableHandler(handle, msg => Console.WriteLine(msg));

// As a connection start is implicitly ensured before each action this is enough to perform
// an initial start (e.g. if you want to allow for time to gather the data before your first
// real request):
variables.Try(vs => {});

// Reading all LVars:
variables.Try(vs =>
{
	vs.RefreshData();
	Console.WriteLine(vs.LVars.Count + " LVars:");
	foreach (FsLVar lvar in vs.LVars)
	{
		Console.WriteLine(lvar.Name + " = " + lvar.Value);
	}
});

// Do a refresh:
variables.Try(vs => vs.RefreshData());

// Reading a single LVar:
if (variables.Try(vs => vs.LVars["LVAR_NAME"], out FsLVar lvar))
{
	if (lvar != null)
	{
		Console.WriteLine(lvar.Name + " = " + lvar.Value);
	}
}

// Force a connection stop (something that should not be necessary usually):
variables.Try(vs => vs.Stop());

This is not a higher level abstraction layer, as you still work with the normal MSFSVariableServices object through the "Try"-methods, but you don't have to care about managing the connection or handling an Exception that occurred because you sent a request after the sim has shut down etc.

It's not much but I think it could help someone having similar problems.

VariableHandler.cs

Posted

Your code looks okay now.

Can you please try running John's WASMClient.exe program to check if that is returning any LVars. You can find it in the FSUIPC-WASMv0.4.10.Zip file.

Paul

Posted
1 hour ago, kingm56 said:

Sorry to be nausaunse, Paul; however, I'm still unable to read any LVARS.  I continue to get this:

Have you tried not using LogLVars and instead simply accessing the LVars directly? Very simple WindowsForms example:

MSFSVariableServices VS;
private void Form1_Load(object sender, EventArgs e)
{
	VS = new MSFSVariableServices();
	VS.OnLogEntryReceived += VS_OnLogEntryReceived;
	VS.Init(this.Handle);
	VS.Start();
}

private void Button1_Click(object sender, EventArgs e)
{
	VS.RefreshData();
	string text = VS.LVars.Count + " LVARS\r\n";
	foreach (FsLVar lvar in VS.LVars)
	{
		text += lvar.Name + " = " + lvar.Value + "\r\n";
	}
	MessageBox.Show(text);
}

private void VS_OnLogEntryReceived(object sender, LogEventArgs e)
{}

 

Posted
4 hours ago, Paul Henty said:

Your code looks okay now.

Can you please try running John's WASMClient.exe program to check if that is returning any LVars. You can find it in the FSUIPC-WASMv0.4.10.Zip file.

Paul

Hey Paul! I tried running WASMClient.exe; the WASM Client connected, but does not collect any LVARS, which appears to be my culprit.  Here's the log entried:

 

Sun May 09 2021 17:17:35.385    [INFO]: Connected to MSFS
Sun May 09 2021 17:25:24.355    [INFO]: SimConnect_Close done

Posted
3 hours ago, jaxx said:

Have you tried not using LogLVars and instead simply accessing the LVars directly? Very simple WindowsForms example:


MSFSVariableServices VS;
private void Form1_Load(object sender, EventArgs e)
{
	VS = new MSFSVariableServices();
	VS.OnLogEntryReceived += VS_OnLogEntryReceived;
	VS.Init(this.Handle);
	VS.Start();
}

private void Button1_Click(object sender, EventArgs e)
{
	VS.RefreshData();
	string text = VS.LVars.Count + " LVARS\r\n";
	foreach (FsLVar lvar in VS.LVars)
	{
		text += lvar.Name + " = " + lvar.Value + "\r\n";
	}
	MessageBox.Show(text);
}

private void VS_OnLogEntryReceived(object sender, LogEventArgs e)
{}

 

Hey Jaxx!

It appears WASMClient isn't collect any LVARs.  Any thoughts, my friend? 

Posted
Quote

the WASM Client connected, but does not collect any LVARS,

There seems to be a problem somewhere other than your code/my dll then. The best thing to do would be to ask John about this in the MSFS support forum under a new topic.

If you tell him the WASMClient.exe is connecting but not finding any LVARs he may be able to suggest things to check,

Paul

Posted

Hi everyone, I developed MSFS software with C #, I tried following the instructions in this post and I was able to set HVars, but after I fixed it, now it doesn't work and it crashes as pictured.

I tried WASMClient and it still works fine. Now i use

-FSUIPC 7.10

-fsuipcClient 3.2.2.364

-FSUIPC-WASMv0.4.10 And use FSUIPC_WAPID.DLL from it.

 

Can anyone advise me to fix this issue?

Best regards,

patcharapol Phophun

Error1.jpg

Posted
Quote

now it doesn't work and it crashes as pictured.

It crashes because that HVar doesn't exist. You can see the list of LVARs and HVARs found by using LogHVars() and LogLVars(). What do they show?

Remember that you need to wait a few seconds after Start() before the WASM module is ready.

Paul

Posted
8 hours ago, Paul Henty said:

It crashes because that HVar doesn't exist. You can see the list of LVARs and HVARs found by using LogHVars() and LogLVars(). What do they show?

Remember that you need to wait a few seconds after Start() before the WASM module is ready.

Paul

Thank you very much for your advice.

Best regards,

patcharapol Phophun

Posted

Hi everyone,

John made some very useful changes to the WAPI DLL. 

This has enabled me to improve the way the MSFSVariableServices works. See version 3.2.3-Beta of my DLL. Note that you will need version 0.5.0 or later of the FSUIPC_WAPID.dll. (See the first post for the location of the download).

The first post in this thread has been updated to include the new changes and also a quick-start example.

The main change is that RefreshData() has now been removed. You don't need to call this anymore because it's now purely event-driven.

The MSFSVariableServices module will now automatically maintain the current LVar values in the background (according to the LVARUpdateFrequency property).

Two new events are now available:

OnVariablesReadyChanged will tell you when the variable have been discovered by the WASM module and it's ready to use. (Check the OnVariablesReady property).

OnValuesChanged will tell you when at least one of the LVar values has been changed. 

I can't test here so please report success/failure here.

Thanks,

Paul

Posted

I've tried your new code (as indicated at the top of this post) and neither OnVariablesReadyChanged or OnValuesChanged gets called.

(WASMClient indicates that variables are avaliable).

Steve.

Posted

OnVariablesReadyChanged and OnValuesChanged now get called (after the VS.Start) but then system fails with this error:-  

[INFO]: **** Starting FSUIPC7 WASM Interface (WAPI) version 0.5.1
[INFO]: Connected to MSFS
An unhandled exception of type 'System.ExecutionEngineException' occurred in Unknown Module.

I have no code in the OnVariablesReadyChanged and OnValuesChanged methods.

Steve.

Posted

More testing ...

My previous post was Net5.

Not a very helpful error message.

If I use Net Framework 4.8 I get this error:-

  [INFO]: **** Starting FSUIPC7 WASM Interface (WAPI) version 0.5.1
  [INFO]: Connected to MSFS
Managed Debugging Assistant 'CallbackOnCollectedDelegate' 
A callback was made on a garbage collected delegate of type 'fsuipcClient!FSUIPC.WAPI+NotifyLVarChangedCallback::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.

The program '[20936] WpfApp3.exe' has exited with code -1 (0xffffffff).

Hope it helps

Steve.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use. Guidelines Privacy Policy We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.