PInvoke calling of function with wchar16_t parameters - c#

I have a .dll with C++ function which takes const wchar16_t* parameters.
I'm trying to import and use it in c# with string, char array, and char array with additional '\0' char but I got no result.
When I check it in original c++ program in debug mode it have additional '\0' char at the end. What exact type should I use?
P.s. I'm not 100% sure that problems arises because of these parameters.
I would very appreciate and give many points to rep if someone could kindly look into small projects I attach illustrating the problem. C++ program works fine (we getlogin response), but in c# project OnLoginResponseCallback is never fire.
What I do in C#
[DllImport("ActiveTickServerAPI.dll", CharSet = CharSet.Unicode, EntryPoint = "?ATCreateLoginRequest##YA_K_KPB_W1P6AX00PAU_ATLOGIN_RESPONSE###Z#Z", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
private static extern ulong ATCreateLoginRequest(ulong sessionId, string user, string pwd, ATLoginResponseCallback onLoginResponse);
public delegate void ATLoginResponseCallback(ulong hSession, ulong hRequest, ATLOGINRESPONSE response);
public delegate void ATRequestTimeoutCallback(ulong origRequest);
static void Main(string[] args)
{
lastRequest = ATCreateLoginRequest(hSession, userId, pw, OnLoginResponseCallback);
bool rc = ATSendRequest(hSession, lastRequest, 3000, OnRequestTimeoutCallback);
}
static void OnLoginResponseCallback(ulong hSession, ulong hRequest, ATLOGINRESPONSE response)
{
Console.WriteLine("!THIS SHOULD FIRE, but fire only timeout callback");
}
[StructLayout(LayoutKind.Sequential)]
public struct ATLOGINRESPONSE
{
public byte loginResponse;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
public byte[] permissions;
public ATTIME serverTime;
}
[StructLayout(LayoutKind.Sequential)]
public struct ATTIME
{
public ushort year;
public ushort month;
public ushort dayOfWeek;
public ushort day;
public ushort hour;
public ushort minute;
public ushort second;
public ushort milliseconds;
}
in c++ it's work (getting login response). Here is function description from api docs:
ACTIVETICKSERVERAPI_API uint64_t ATCreateLoginRequest ( uint64_t session,
const wchar16_t * userid,
const wchar16_t * password,
ATLoginResponseCallback pCallback
)
working struct from c++
typedef struct _ATLOGIN_RESPONSE
{
ATLoginResponseType loginResponse;
uint8_t permissions[255];
ATTIME serverTime;
} ATLOGIN_RESPONSE, *LPATLOGIN_RESPONSE;
typedef struct _ATTIME
{
uint16_t year;
uint16_t month;
uint16_t dayOfWeek;
uint16_t day;
uint16_t hour;
uint16_t minute;
uint16_t second;
uint16_t milliseconds;
} ATTIME, *LPATTIME;

I downloaded your test project and immediately fixed the delegate declarations because they were wrong:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ATSessionStatusChangeCallback(ulong hSession, byte statusTyp);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ATLoginResponseCallback(ulong hSession, ulong hRequest, ref ATLOGINRESPONSE response);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ATRequestTimeoutCallback(ulong origRequest);
First time I ran the program I got:
SessionStatus - Connected
request timeout
!THIS SHOULD FIRE
SessionStatus - Connected
!THIS SHOULD FIRE
Second and subsequent times I got:
SessionStatus - Connected
!THIS SHOULD FIRE
No timeout. Why it behaved the way it did the first time is very hard to guess. You probably need to wait with the ATSendRequest() until you get confirmation of a proper login, something like that.
But clearly fixing the delegates took care of the problem, you are now getting the proper hSession value in the callback. It is not yet perfect, you need to ensure that they cannot be garbage collected. Store them in a static variable:
static ATSessionStatusChangeCallback statusCallback;
....
if (res == true) {
statusCallback = new ATSessionStatusChangeCallback(OnSessionStatusChangeCallback);
res = ATInitSession(sessionNumber, "activetick1.activetick.com",
"activetick2.activetick.com", 443, statusCallback, true);
}
Do the same thing on the 2 other ones.

The declaration of ATLOGINRESPONSE is almost certainly wrong.
public class ATLOGINRESPONSE
{
public byte loginResponse;
public byte[] permissions;
public ATTIME serverTime;
}
By declaring it as a class, you ensure that it will be marshalled by reference. But you preclude it ever being marshalled by value. Which might be fine. Anyway, I think I'd prefer to declare as a struct.
Further, the first two parameters look wrong to me. The first is probably an enum and should be declared as such. The second is a byte array but you need to specify how to marshal it. Like this:
[StructLayout(LayoutKind.Sequential)]
public struct ATLOGINRESPONSE
{
public ATLoginResponseType loginResponse; // you need to define the ATLoginResponseType enum
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
public byte[] permissions;
public ATTIME serverTime;
}
We don't know whether or not ATTIME is declared correctly.
I demangled your function name here: https://demangler.com/
The C++ function has this signature
unsigned __int64 __cdecl ATCreateLoginRequest(
unsigned __int64,
wchar_t const *,
wchar_t const *,
void (__cdecl*)(unsigned __int64,unsigned __int64,struct _ATLOGIN_RESPONSE *)
)
Your delegate is therefore declared with the wrong calling convention. It should be:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ATLoginResponseCallback(
ulong hSession,
ulong hRequest,
ref ATLOGINRESPONSE response
);
Note that having changed ATLOGINRESPONSE to be a struct, we have to make the parameter a ref parameter.
The function ATLoginResponseCallback is declared correctly in your C# code.
Your other delegates will also need to be declared with [UnmanagedFunctionPointer(CallingConvention.Cdecl)].
Unless the callback is only called from ATCreateLoginRequest then the delegate that is passed is subject to being garbage collected. So if ATCreateLoginRequest takes a copy of that delegate, and calls later, then you will need to keep the delegate alive. Assign it to a variable of type ATLoginResponseCallback whose life extends beyond the final call to the callback.
It's entirely plausible that the problems lie elsewhere.

Related

Accessing array in struct of C DLL from C#

I am trying to access and make changes to elements in a struct that are in a DLL. I have followed this example on how to use struct from a DLL: http://nawatt.com/index.php/corporate/blog/78-using-c-dlls-in-c
I have been successful in making changes to non-array variables, but whenever I try to make changes to an array I get a Runtime Error.
This is an example of my C DLL code:
//lib_qrs.dll
#ifdef __cplusplus
extern "C" {
#endif
typedef struct TEST_STRUCT
{
unsigned short check[5];
} test_struct;
__declspec(dllexport) void __stdcall test(test_struct *test, unsigned short val){
// This is an example of what DOES NOT WORK
test->check[0]=val+1;
test->check[1]=val+2;
test->check[2]=val+3;
test->check[3]=val+4;
test->check[4]=val+5;
}
#ifdef __cplusplus
}
#endif
This is an example of my C# code:
[StructLayout(LayoutKind.Sequential)]
public struct TEST_STRUCT
{
public UInt16[] check;
}
public class Program
{
[DllImport("lib_qrs.dll", EntryPoint="test", CallingConvention = CallingConvention.StdCall)]
public static extern void test(ref TEST_STRUCT test, int val);
public TEST_STRUCT testStruct = new TEST_STRUCT();
static void Main(string[] args)
{
testStruct.check=new UInt16[5];
// WHERE ERROR OCCURS
test(ref testStruct, 5);
}
}
The error that I get is:
*"An unhandled exception of type 'System.AccessViolationException' occurred in Test.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I understand that I have to be very careful with memory allocation when I replicate my structure in C#, but I don't know what I am doing wrong or how I can fix this array issue.
Does anyone have an idea of how I could get around this?
The default marshaling for that array is unsigned short*, not unsigned short[]. You'll need to apply the [MarshalAs] attribute to tell the pinvoke marshaller about it. Fix:
[StructLayout(LayoutKind.Sequential)]
public struct TEST_STRUCT {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public UInt16[] check;
}
And since you are returning values in the array, you also have to tell the pinvoke marshaller that it needs to copy the array values back. That requires the [Out] attribute:
[DllImport("lib_qrs.dll")]
public static extern void test([Out] ref TEST_STRUCT test, int val);
Do note that neither is necessary at all if the you just declare the argument as ushort[], assuming that the structure doesn't contain any additional fields.

Struct from C++ to C#

I have a project which uses a library which was mostly build on and around C++.
The delivered DLL with this library, i have imported into my C# project.
After importing the following method in Unity:
[DllImport("pst")]
private static extern int pst_get_sensor(PSTSensor sensor);
I require this PSTSensor struct, so actually use the method.
In the C++ .h file, the struct is defines as :
struct PSTSensor
{
char name[80]; /**< Device name */
int id; /**< Device identifier (for other tracking interfaces */
float pose[16]; /**< Device pose estimate as row-major matrix */
double timestamp; /**< Time the data was recorded */
};
I have tried to replicate it in C#, and i ended up with this following:
struct PSTSensor{
PSTSensor(char[] name, int id, float[] pose, double timestamp){
this.name = name;
this.id = id;
this.pose = pose;
this.timestamp = timestamp;
}
public char[] name;
public int id;
public float[] pose;
public double timestamp;
}
In the example C++ code that came with this project was stated to call pst_get_sensor(&sensor) this '&' sign, i do not recognize? How would i call this method in C#, and make it work?
I think i ruined the struct, seeing how i never worked with them before. At least it doesn't throw errors on compilation anymore, but i think it is still wrong. Any ideas on that?
Many thanks in advance,
Smiley
I'm not sure if I'm answering your question entirely, but in c++ the & is used to pass an argument by reference which means that the argument you're passing in can be manipulated inside the function. To me it looks like the original function is used to fill out a sensor struct.
int C# you can pass by reference with the ref or out keyword:
private static extern int pst_get_sensor(PSTSensor ref sensor);
Why did you add a constructor in your C# implementation?
It seams that all you need to do is declare the parameter as ref
private static extern int pst_get_sensor(ref PSTSensor sensor);
If you want to have a look at an example, there is one here
You did not show the C++ declaration of your function, but give that you call it like this:
pst_get_sensor(&sensor);
it is presumably declared like so:
int pst_get_sensor(PSTSensor *sensor);
The function receives a pointer to the struct. That is clear since the call uses the & operator which takes the address of an object.
On the C# side you translate this parameter as a ref parameter. Like this:
[DllImport(...)]
static extern int pst_get_sensor(ref PSTSensor sensor);
Now, your other problem is that your struct is declared incorrectly. It contains inline arrays and you must communicate the lengths of those arrays to the marshaller. Like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct PSTSensor
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string name;
public int id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public float[] pose;
public double timestamp;
}

calling c++ function from c# with a reference type as parameter

int Set(CANMsg &CANObj)
I have to call the method above, from c#. Untill now i have defined a wrapper :
extern "C" __declspec(dllexport) int SetWrapper(CANMsg CANObj);
CANMsg CANObj --- is this parameter ok or should i use CANMsg *CANObj ?
and here i implement the wrapper:
extern "C" __declspec(dllexport) int SetWrapper(CANMsg CANObj)
{
return Set(CANObj);
}
I am creating this wrapper because this is an overloaded version of the function and i had to make a difference somehow.
Here is the CANMsg class:
class CANMsg
{
public:
CANMsg();
~CANMsg();
void AddRef() const;
void Release() const;
unsigned int MsgId;
unsigned int DLC;
unsigned int Handle;
unsigned int Interval;
unsigned int TimeStamp;
unsigned char Data0;
unsigned char Data1;
unsigned char Data2;
unsigned char Data3;
unsigned char Data4;
unsigned char Data5;
unsigned char Data6;
unsigned char Data7;
protected:
mutable int refCount;
};
Now, in C# i have the following :
[StructLayout(LayoutKind.Sequential)]
public class CANmsg
{
public int MsgId;
public int DLC;
public int Handle;
public int Interval;
public int TimeStamp;
public char Data0;
public char Data1;
public char Data2;
public char Data3;
public char Data4;
public char Data5;
public char Data6;
public char Data7;
}
and the import is like this :
[DllImport("engine.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I4)]
public static extern int SetWrapper(IntPtr canMSGObject);
I am a bit confused about that CANMsg object, am I declaring it ok as an IntPtr, is the marshal ok, or the types ? If i let it like so, with the IntPtr, what kind of instantiation should i perform there? If i send a CANMsg object, i get an error regarding some invalid arguments.
Let me know if you need some more details about this.
When I see your C++ class definition, I ask myself "what happens in the constructor and the destructor?" and "what do AddRef() and Release() do?" These are important questions because you can't simply project data from a C# object onto that IntPtr and hope for the best. Instead, you should think about making a helper dll that does this work for you. You might need methods something like this:
public ref class MyLibraryHelper {
public:
IntPtr MakeCANMsg() { return gcnew IntPtr(new CANMsg()); }
void DestroyCANMsg(IntPtr msgPtr) {
CANMsg *msg = reinterpret_cast<CANMsg *>(msgPtr.ToPointer());
if (msg) delete msg;
}
void ProjectTo(CSharpCANMsg ^csh, IntPtr msgPtr)
{
CANMsg *msg = reinterpret_cast<CANMsg *>(msgPtr.ToPointer());
if (!msg) return;
msg->MsgId = csh->get_MsgId();
// etc
}
void ProjectFrom(IntPtr msgPtr, CSharpCANMsg ^csh)
{
CANMsg *msg = reinterpret_cast<CANMsg *>(msgPtr.ToPointer());
if (!msg) return;
csh->set_MsgId(msg->MsgId);
// etc
}
}
My C++/CLI is rusty, so expect some issues. If this looks like hand-marshalling, well, it is because given the class that you've exposed, it seems like you need it.
Now honestly, you probably don't want this. Really, you want a C++/CLI class that constructs a CANMsg and keeps it as a private member and then maps .NET properties onto the lower level object. This type of class will have to be disposable and the !ClassName() destructor will be responsible for deleting the underlying object.
You can not pass a C# object to native C++ like this. Marshal.StructureToPtr is what you need, the details and examples are here

Calling C function with struct in C# - what am I doing wrong?

I thought this one was fairly straight forward but still trying to understand all of this and having some issues.
I don't know much about the C function b/c i've been given limited information.
Here is the function call in C:
int GetCard(CardInfo card);
Here is the request structure:
typedef struct _tCardInfo
{
char CardNumber[80];
char isExist;
} TCardInfo, *pTCardInfo;
I want to pass the card number to see if it exists.
So in C# I did the following:
public struct CardInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string cardNumber;
public byte isExist;
}
[DllImport("card.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int GetCardInfo(ref CardInfo cardInfo);
Then in the c# method:
CardInfo cardInfo = new CardInfo();
cardInfo.cardNumber = "1234567890";
int success = GetCardInfo (ref cardInfo);
The good thing about the DLL that I'm calling is it generates a log file.
When I execute the call, the log tells me that I'm hitting the DLL but it is not passing the card number which then sets a message saying the card number was not passed.
Any help is greatly appreciated.
Thanks!
The problem is that you're requesting TChar marshaling, but the DLL requires 8-byte characters. Change the C struct to wchar_t.
Also, use Visual Studio to set a breakpoint in your DLL, and actually inspect the data when it comes in! Visual Studio can debug across .NET/native boundaries, which is super cool!
Try to add attribute StructLayout for struct
[StructLayout(LayoutKind.Sequential)]
public struct CardInfo
{
...
Try to create the .Net struct like this:
[StructLayout(LayoutKind.Sequential)]
public struct CardInfo
{
[MarshalAs(UnmanagedType.AnsiBStr, SizeConst = 80)]
public string cardNumber;
[MarshalAs(UnmanagedType.I1)]
public sbyte isExist;
}
And for the function declaration: try not to use the CallingConvention and CharSet in the DLL import, and use the [In, Out] attributes before the parameter. Like this:
[DllImport("card.dll")]
public static extern int GetCardInfo([In, Out] CardInfo cardInfo);

Translating native API into C#, Marshalling structs arrays and delegates

I've worked on a C# version of a C++ API but I can't manage to get it right.
Since the whole API was too big the first time I posted, I've cut it down to some core functions for which I really need some help to make a managed version of.
So basically, this is a C++ dll containing exported functions that are used to communicate with a technical analysis software.
C++ function I'd like to convert
#define PLUGINAPI extern "C" __declspec(dllexport)
PLUGINAPI int GetFunctionTable( FunctionTag **ppFunctionTable )
{
*ppFunctionTable = gFunctionTable;
// must return the number of functions in the table
return gFunctionTableSize;
}
GetFunctionTable is called by the software which provides a pointer to an array of FunctionTag called gFunctionTable:
typedef struct FunctionTag
{
char *Name;
FunDesc Descript;
} FunctionTag;
FunctionTag gFunctionTable[] = {"ExampleA",{ VExampleA, 0, 0, 0, 0, NULL },
"ExampleB",{ VExampleB, 1, 0, 1, 0, NULL }
};
FunctionTag structure contains an embedded structure called Fundesc:
// FunDesc structure holds the pointer to actual
// user-defined function that can be called by AmiBroker.
typedef struct FunDesc
{
AmiVar (*Function)( int NumArgs, AmiVar *ArgsTable );
UBYTE ArrayQty; // number of Array arguments required
UBYTE StringQty; // number of String arguments required
SBYTE FloatQty; // number of float args
UBYTE DefaultQty; // number of default float args
float *DefaultValues; // the pointer to defaults table
} FunDesc;
Finally, Fundesc contains AmiVar type:
#pragma pack( push, 2 )
typedef struct AmiVar
{
int type;
union
{
float val;
float *array;
char *string;
void *disp;
};
} AmiVar;
#pragma pack(pop)
C# conversion so far
Now, this is what I've written so far in an attempt to get my C# dll to "mimic" the C++ original API. The GetFunctionTable() exported function:
namespace AmiBrokerFrontDll
{
internal static class AmiBrokerFrontDll
{
[DllExport("GetFunctionTable", CallingConvention = CallingConvention.Cdecl)]
public static Int32 GetFunctionTable(ref FunctionTag[] ppFunctionTable)
{
FillFunction();
ppFunctionTable=gFunctionTable;
return gFunctionTableSize;
}
Then comes the definition of FunctionTag structure and gFunctionTableSize:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct FunctionTag
{
[MarshalAs(UnmanagedType.LPStr)]
public string Name;
public FunDesc Description;
}
public static FunctionTag[] gFunctionTable=new FunctionTag[1];
public static FunctionTag gfunc;
static Int32 gFunctionTableSize = Marshal.SizeOf(gFunctionTable) / Marshal.SizeOf(gfunc);
public static void FillFunction()
{
gFunctionTable[0].Name = "VExempleA";
gFunctionTable[0].Description.Function += VExempleDeMacd;
//ArrayQty, StringQty, FloatQty, DefaultQty, DefaultTablePtr
gFunctionTable[0].Description.ArrayQty = 0;
gFunctionTable[0].Description.StringQty = 0;
gFunctionTable[0].Description.FloatQty = 2;
gFunctionTable[0].Description.DefaultQty = 0;
gFunctionTable[0].Description.DefaultValues = new IntPtr();
}
FunDesc declaration includes a delegate:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate AmiVar FunctionDelegate(int NumArgs, ref AmiVar ArgsTable);
public struct FunDesc
{
[MarshalAs(UnmanagedType.FunctionPtr)]
public FunctionDelegate Function;
public byte ArrayQty; // The number of Array arguments required
public byte StringQty; // The number of String arguments required
public byte FloatQty; // The number of float args
public byte DefaultQty; // The number of default float args
public IntPtr DefaultValues; // The pointer to defaults table
}
Finally, we have an AmiVar structure:
[StructLayoutAttribute(LayoutKind.Explicit, Size = 8)]
public struct AmiVar
{
[FieldOffset(0)]
public Int32 type;
[FieldOffset(4)]
public Single val;
[FieldOffset(4)]
public IntPtr array;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.LPStr)]
public string name;
[FieldOffset(4)]
public IntPtr disp;
}
Sorry this is too long. Unfortunately, I couldn't make a small consise question.
So this code compiled (maybe not anymore since this is an extract from the bigger picture) but when loading the resulting dll from the technical analysis software, I received an ACCESS VIOLATION ERROR. I believe this means the C# conversion doesn't map the C++ variables size correctly. With the arrays of structures and delegates this project has become too difficult for me to solve alone.
Any help will be much appreciated!
Thanks,
Guillaume
I can't help in your specific case, but I can tell you a couple things that will make your life easier:
Function pointers created from managed delegates should never, ever, ever, be stored in unmanaged code. I don't say this lightly. There are claims that if you create a function pointer from a delegate with GetFunctionPointerForDelegate, that the appropriate thunks will get created and won't ever get garbage collected. This is NOT true. I have watched function pointers that were good on one call go sour on the next. The safest bet is to guarantee that function pointers will never get stored beyond the use of an unmanaged call.
P/Invoke is OK for some tasks, but by far the easiest way to integrate a non-C#-friendly C++ library is to make a better/more appropriate wrapper for it in C++/CLI. See here for a description of one way to approach the problem.

Categories