01-14-2020 08:28 AM
Hello everyone,
I need to implement a MQTT client and server in LV. I found a library in C# which looks promising and I am trying to create a DLL which I will call from LV which in turn calls the functions from the library. I do not want to use .NET Constructor in LV but CLFN so when building the bridge DLL it needs to be StdCall. This is the C# code:
01-14-2020 10:42 AM - edited 01-14-2020 10:44 AM
You definitely should NOT attempt to store the returned object in a static variable. Or is that necessary that you can create a globally exported function from inside an object? Can you not do this (or something similar):
[DllExport(CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
static MqttClient Connect(string IpAdress, int Port, string clientID)
{
MqttClient mqttClient = new MqttClient(IpAdress, Port, false, new System.Security.Cryptography.X509Certificates.X509Certificate(), new System.Security.Cryptography.X509Certificates.X509Certificate(), MqttSslProtocols.None);
mqttClient.Connect(clientID);
return mqttClient;
}
The return value is an object pointer so as far as LabVIEW is concerned you would want to configure this as pointer sized integer.
The nastier part is your string variable though. In .Net a string is a 16-bit Unicode string, but LabVIEW uses 8-bit ANSI strings.
You would either have to configure the parameter in LabVIEW as an Array of 16-bit Integers and call some function to convert the 8-bit LabVIEW string into a 16-bit Unicode string for instance by calling the function in the ni_lib_unicode VI package that you can download and install with the VIPM.
The other variant is to declare the function with a byte array input instead of string and convert that into a .Net string through the according string encoding facility.
[DllExport(CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
static MqttClient Connect(byte IpAdress[], int Port, byte ClientID[])
{
string strIpAddress = Encoding.ASCII.GetString(IpAddress);
string strClientID = Encoding.ASCII.GetString(ClientID);
MqttClient mqttClient = new MqttClient(strIpAdress, Port, false, new System.Security.Cryptography.X509Certificates.X509Certificate(), new System.Security.Cryptography.X509Certificates.X509Certificate(), MqttSslProtocols.None);
mqttClient.Connect(strClientID);
return mqttClient;
}
01-15-2020 02:40 AM
Thank you for your reply.
I made some adjustments from your post but now I face another problem. In C# only the first element from both arrays reach the function and I do not know why.
So for example: 10.10.10.10 -> [49,49,49,49,49,....] if I use a popup in C# I will see a 1 element array and only a single 49 reaches the function.
Have a nice day.
01-15-2020 04:03 AM - edited 01-15-2020 04:18 AM
I was actually afraid of that. On further consideration the .Net code has no way of knowing how long the byte array is that is passed in, so the interop translation probably just assumes one element when the pointer seems valid. Maybe it would work if you changed the syntax to something like this:
static MqttClient Connect(byte IpAdress[50], int Port, byte clientID[50])
and then made sure that you configure the Minimum length in the Call Library Node to these lengths. Or there might be a .Net Interop annotation of some kind that you can append to the parameter specification, to tell it that the array length is determined by a Zero byte in the stream?
https://docs.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-arrays says:
When marshaling arrays from unmanaged code to managed code, the marshaler checks the MarshalAsAttribute associated with the parameter to determine the array size. If the array size is not specified, only one element is marshaled.
This would indicate that you might have to do something like this instead:
static MqttClient Connect(
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] byte IpAdress[], int addressLen, int Port,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=4)] byte clientID[], int clientLen)
But!!!!
Your library you want to call is a .Net library! Why don't you use the LabVIEW .Net functionality to call it directly instead?? That removes the need to try to make the LabVIEW Call Library Node to match the .Net Interop specific nitty gritty details, that can be sometimes hard to determine.
If you can't call it directly because its constructors or methods use generics then you can still create much more easily a .Net assembly wrapper for it in C# that you then call with the LabVIEW .Net functions.
01-15-2020 05:10 AM
Hello,
I will use LVs .Net function. I avoided using them because i was used to CLFN and didn't want to change. I don't know how LV handles the constructor when building. Does it add it to a "data" folder automatically? Something I got the "the folder already exists and I need to select a new build folder" when using constructors.
I didn't want to deal with these building "problems" which are maybe just misunderstandings on my side.
Have a nice day and thank you for your information. It helped :).