Accessing array in struct of C DLL from C# - 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.

Related

Determine C# P/Invoke Structure Alignment at Runtime

I'm trying to write good P/Invoke signatures for some Windows setupapi calls, and I've encountered the following problem with the packing of setupapi's structures:
// Excerpt from setupapi.h
#if defined(_WIN64)
#include <pshpack8.h> // Assume 8-byte (64-bit) packing throughout
#else
#include <pshpack1.h> // Assume byte packing throughout (32-bit processor)
#endif
Now, what that means is that I can't just set the StructLayoutAttribute.Pack property to a constant value.
I tried doing the following:
[StructLayout(LayoutKind.Sequential, Pack = Environment.Is64BitProcess ? 8 : 1)]
public struct SP_DEVINFO_DATA
{
public uint cbSize;
public Guid ClassGuid;
public uint DevInst;
public IntPtr Reserved;
}
As expected, this fails with the following compilation error:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
I'd really like to avoid #if and setting up different compilation platforms, as opposed to Any CPU. Can I determine the packing of a C# structure at run-time?
No, packing is a compile-time concept, because it determines (among other things) the overall size of the type. That can't change at run-time.
In this case, if you're not willing to have separate builds for x86 and x64, you'll have to do the marshalling yourself. Thanks for #Hans Passant for a clean way to achieve this:
Define two structures, say SP_DEVINFO_DATA32 and SP_DEVINFO_DATA64,
Define a method overload for each structure
At run-time, select which structure to create and call the appropriate overload.
It would look something like this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SP_DEVINFO_DATA32 { /* stuff */ }
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct SP_DEVINFO_DATA64 { /* stuff */ }
[DllImport("setupapi.dll")]
public static extern void Function(ref SP_DEVINFO_DATA32 data);
[DllImport("setupapi.dll")]
public static extern void Function(ref SP_DEVINFO_DATA64 data);
public void DoStuff()
{
if (Environment.Is64BitProcess)
{
var data = new SP_DEVINFO_DATA64
{
cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA64))
};
Function(ref data);
}
else
{
var data = new SP_DEVINFO_DATA32
{
cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA32))
};
Function(ref data);
}
}
I have done in C++ and C#, both at x86 and x64, and I'll say that the "pack" doesn't have any effect on that structure, so you only need
[StructLayout(LayoutKind.Sequential)]
At x86 both C++ and C# structures have a sizeof/Marshal.SizeOf of 28 bytes, at x64 of 32 bytes. If I create a
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
public SP_DEVINFO_DATA a;
public SP_DEVINFO_DATA b;
}
and in C
struct MyStruct
{
SP_DEVINFO_DATA a;
SP_DEVINFO_DATA b;
};
the sizes are 56 at x86 and 64 at x64 (so exactly the double, zero padding). The difference in size between x86 and x64 is 4 bytes, so exactly the difference in size of the IntPtr.

PInvoke does not change the object

I have the following PInvoke:(C to C#)
[DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void InitBoard([MarshalAs(UnmanagedType.LPArray, SizeConst = 64)]sPiece[] board);
On C:
__declspec(dllexport) void InitBoard(sPiece board[8][8]);
In InitBoard function, the values ​​of the matrix changing, but after a call to PInvoke I do not see the change.
sPiece[] board = new sPiece[64];
InitBoard(board);
//Here the values ​​of the board is still initialized (as before the function call) at default values
I tried to change the variable to ref (although it already reference) but it stuck the program when the function was called, so I do not think it's the solution.
It took me a while to get here (I'm new to the subject) I'd love to help!
EDIT:
sPiece On C:
typedef struct Piece
{
ePieceType PieceType; //enum
ePlayer Player; //enum
int IsFirstMove;
} sPiece;
sPiece On C#:
[StructLayout(LayoutKind.Sequential)]
public struct sPiece
{
public ePieceType PieceType;
public ePlayer Player;
public int IsFirstMove;
}
Possibly you are failing to allocate memory before calling the function.
sPiece[] board = new sPiece[64];
InitBoard(board);
Declare the function like this:
[DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void InitBoard([Out] sPiece[] board);
The default marshalling is [In]. Although since your struct is blittable, the array you pass is pinned and the call behaves as though it was [In,Out]. So I think you could omit [Out] if you wished, but it is clearer as written above.
You can add the UnmanagedType.LPArray option if you wish but it's not needed.

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