Hi,
here comes a C# example for a client.
- Create an empty C# application for FS as described in the SDK documentation.
- I always need to set the properties of the project to Build X64 in order to compile correctly
- Use Winforms and create a textbox named Statustext. Set the textbox to multiline.
I basically used the server by VFRMau for this example. I did some minor changes to the wasm Server Module:
- I renamed the client data area from “EFIS_CDA” to “ClientDataArea_1”
- I added a second value to the client data area:
// Definition of the client data area format.
// double data = 1.;
double data[2];
FLOAT64 lvarValue;
lvarValue = get_named_variable_value(lvarID);
data[0] = lvarValue;
data[1] = 99.999;
Here comes the C# Code.
Generally speaking it’s almost identical with the c++ implementation.
BUT there is one magic trick which is often overseen:
We need to declare a struct for the data, that we want to receive and we MUST register this struct via RegisterStruct(…);
If this step is missing, we will never get data that make any sense.
// This struct will receive the data that is received from the sim
// It's necessary to provide the struct. If this step is skipped, the
// returned vaules won't make sense.
// The struct can also be used to name the informations.
// IHMO it's always double. At least when using c# (?? Not sure ??)
struct ClientDataStruct
{
public double HEADING;
public double Dummyval;
};
(...)
MySimconnectObject.RegisterStruct(CLIENT_DATA_DEFINITION_IDs.CLIENT_DATA_1);
This is the complete code of my simple example:
using System;
using System.Windows.Forms;
// For FS
using Microsoft.FlightSimulator.SimConnect;
using System.Runtime.InteropServices;
namespace WASMClientCsharp
{
public partial class Form1 : Form
{
SimConnect MySimconnectObject = null;
const int WM_USER_SIMCONNECT = 0x0402;
// ID of the client Data definition
enum CLIENT_DATA_DEFINITION_IDs
{
CLIENT_DATA_1
}
// REQUEST_IDs
// This ID will be returned by the sim in the event.
enum REQUEST_IDs
{
REQUEST_1
}
// EVENT_IDs
// Every event needs it's own ID
// To fire an event, the ID is used.
enum EVENT_IDs
{
WASM_EVENT_1
}
// Unique ID for the client data area
// Remark: A client data area can contain an array of data.
// We can pack multiple vaules into the data array
// and communicate those vaules together. So this
// ID doesn't represent an single variable!
enum CLIENT_DATA_AREA_IDs
{
CLIENT_DATA_AREA_1
}
// EVENTs can be assigned to a group. That is a convenient way to
// set the priority for the events.
enum EVENT_NOTIFICATION_GROUP_IDs
{
EVENT_NOTIFICATION_GROUP_1
}
// This struct will receive the data that is received from the sim
// It's necessary to provide the struct. If this step is skipped, the
// returned vaules won't make sense.
// The struct can also be used to name the informations.
// IHMO it's always double. At least when using c# (?? Not sure ??)
struct ClientDataStruct
{
public double HEADING;
public double Dummyval;
};
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
WriteStatus("Connecting to sim ...");
// Create a SimConnect Object and connect to the sim
MySimconnectObject = new SimConnect("My_ConnectionName", this.Handle, WM_USER_SIMCONNECT, null, 0);
WriteStatus("Object successfully created.");
// The Simconnect oject offers a number of events. The most important is probably "OnRecvOpen". We
// get the event, when the connection to the sim is successfully established.
MySimconnectObject.OnRecvOpen += MeinSimconnectObject_OnRecvOpen;
}
catch
{
WriteStatus("Object creation failed.");
}
}
// The following methode is needed in order to catch the events from the Windows OS
// If this method is missing, we do not get any event from the sim
// Remark: Works only for forms applications. Not for WPF. WPF need a slightly different solution
protected override void DefWndProc(ref Message m)
{
if (m.Msg == WM_USER_SIMCONNECT)
{
if (MySimconnectObject != null)
{
MySimconnectObject.ReceiveMessage();
}
}
else
{
base.DefWndProc(ref m);
}
}
// Just a little helper, to write the status text into a textbox.
private void WriteStatus(string message)
{
Statustext.Text += message + "\r\n";
}
// This event will be called, when the Simconnect Object has established a connection to the sim.
private void MeinSimconnectObject_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
{
// The sim sends some data in the data object. For example the version of the Sim.
WriteStatus("OnRecvOpen ist erreicht. Version: V" + data.dwApplicationVersionMajor +"."+ data.dwApplicationVersionMinor);
// Map a clientdata name to a client data area ID.
// The name MUST be the same as it's used in the WASM Module.
MySimconnectObject.MapClientDataNameToID("ClientDataArea_1", CLIENT_DATA_AREA_IDs.CLIENT_DATA_AREA_1);
// Then we need to add some memory space for the client data area.
// Note: Convert.ToUInt32(Marshal.SizeOf(typeof(ClientDataStruct))) determines the size of the struct.
// In this example with two double values the size will be 16 (bytes);
MySimconnectObject.AddToClientDataDefinition(CLIENT_DATA_DEFINITION_IDs.CLIENT_DATA_1, SimConnect.SIMCONNECT_CLIENTDATAOFFSET_AUTO, Convert.ToUInt32(Marshal.SizeOf(typeof(ClientDataStruct))), 0.0f, SimConnect.SIMCONNECT_UNUSED);
// THIS IS THE SECRET SAUCE!!!
// The following step is mandatory for C# implementations. We need to register the struct in order to get data that make sense.
// If this step is missing. There will be events with data. But none of them will be equal to the data that have been sent.
MySimconnectObject.RegisterStruct(CLIENT_DATA_DEFINITION_IDs.CLIENT_DATA_1);
// Then we create the client data area
MySimconnectObject.CreateClientData(CLIENT_DATA_AREA_IDs.CLIENT_DATA_AREA_1, Convert.ToUInt32(Marshal.SizeOf(typeof(ClientDataStruct))), SIMCONNECT_CREATE_CLIENT_DATA_FLAG.DEFAULT);
// Then we need an event, that will be called by the sim with the data.
MySimconnectObject.OnRecvClientData += MySimconnectObject_OnRecvClientData;
// Then we initiate the Request of the client Data area.
MySimconnectObject.RequestClientData(CLIENT_DATA_AREA_IDs.CLIENT_DATA_AREA_1
, REQUEST_IDs.REQUEST_1
, CLIENT_DATA_DEFINITION_IDs.CLIENT_DATA_1
, SIMCONNECT_CLIENT_DATA_PERIOD.SECOND // In this case: We want to receive the data every second.
, SIMCONNECT_CLIENT_DATA_REQUEST_FLAG.DEFAULT // No matter if the data has changed.
, 0
, 0
, 0);
// In this example, we need to send an event to the wasm module.
// This event is used to start the transfer of the LVAR values to the client data area .
// *** I'm not happy with that solution. I will change that: The WASM module should read the LVARs periodically and write into the
// Client Data area. But for now, I leave it as it is.
MySimconnectObject.MapClientEventToSimEvent(EVENT_IDs.WASM_EVENT_1, "LVAR_ACCESS.EFIS");
MySimconnectObject.AddClientEventToNotificationGroup(EVENT_NOTIFICATION_GROUP_IDs.EVENT_NOTIFICATION_GROUP_1, EVENT_IDs.WASM_EVENT_1, true);
MySimconnectObject.SetNotificationGroupPriority(EVENT_NOTIFICATION_GROUP_IDs.EVENT_NOTIFICATION_GROUP_1, SimConnect.SIMCONNECT_GROUP_PRIORITY_HIGHEST);
}
// This is the event, that will occur every second and provide the data.
private void MySimconnectObject_OnRecvClientData(SimConnect sender, SIMCONNECT_RECV_CLIENT_DATA data)
{
// Note: data provides more information: data.requestID for example provides the Request ID of the RequestClientData call.
// Now we create a data struct in order to read the data
ClientDataStruct ReadClientDataStruct = (ClientDataStruct)data.dwData[0];
// From this point on, we can use the names that we declared in the struct in order to access the values
// Note that there can be multiple values. If I understand the documentation correctly, we can use 512 double
// values in one client data area. If we need more data we need to create multiple client data areas.
WriteStatus("Data received: Heading: " + ReadClientDataStruct.HEADING);
WriteStatus("Data received: â– â– â– â– â– : " + ReadClientDataStruct.Dummyval);
// For this example we need to fire an event to the wasm module to initiate the transfer of the LVARS to the
// client data area.
MySimconnectObject.TransmitClientEvent(SimConnect.SIMCONNECT_OBJECT_ID_USER, EVENT_IDs.WASM_EVENT_1, 2, EVENT_NOTIFICATION_GROUP_IDs.EVENT_NOTIFICATION_GROUP_1, SIMCONNECT_EVENT_FLAG.GROUPID_IS_PRIORITY);
}
}
}
I hope that somebody can use this information.