Jump to content
The simFlight Network Forums

Added Support for LVars/HVars Access in MSFS via John Dowson's WASM Module.


Recommended Posts

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.

 

Example Code:

An example project can be downloaded from the FSUIPCClient DLL website:

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

C# only at the moment. I will convert to VB.NET if anyone requests it in this thread.


How to Use:

Setup:

First create an instance of the MSFSVariableServices class:

MSFSVariableServices VS = new MSFSVariableServices();

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.LVARUpdateFrequency

This is the number of times per second (Hz) that the WASM Module will get the latest LVar values. This can be used to limit the impact of the WASM module if fast updates are not required. The default is 6Hz. You can slow this down or speed it up depending on how frequently your application will be requesting updates. 

 

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 in three ways:

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"];

3. Get a specific LVar by ID

FsLVar myLVar = VS.LVars[26];

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 ID or Name use:

if (VS.LVars.Exists(26))
{
    // Exists
}

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. The Value property will be updated after the Sim has received the new value and it's sent back to your application. The OnPropertyChanged 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", 100);

Executing Calculator Code:

VS.ExecuteCalculatorCode(myCode);

where myCode is a string containing the code to execute. 

Paul

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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?  

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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()); 

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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? 

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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)
{}

 

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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? 

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • 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.