Marshalling a Pointer to Array of Structs - c#

I am trying to invoke a driver dll for a force sensor thats been written for c/cpp. The working Cpp code looks like this:
I the header file that was delivered with the dll, the struct is defined like this
typedef struct
{
DWORD usb_hid_idx;
int open;
char vid_pid[256];
char dev_info[256];
char sn_info[256];
int hw_info;
unsigned char hw_var;
int fw_vers;
} t_DeviceInfo;
And the function I need to call is defined like this:
extern "C" DLL_API int Search(t_DeviceInfo *p_dev_info);
In the main code I just create an array of the former defined t_DeviceInfostruct and a pointer to the first element and call the function Searchwith this pointer:
t_DeviceInfo deviceInfo[16];
t_DeviceInfo* deviceInfoPtr = &deviceInfo[0];
int ret = search(deviceInfoPtr);
At least there, everything works fine. With C# it currently looks like this:
unsafe public struct t_DeviceInfo
{
long usb_hid_idx;
int open;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
char[] vid_pid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
char[] dev_info;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
char[] sn_info;
int hw_info;
char hw_var;
int fw_vers;
}
unsafe public class ASTAS
{
[DllImport("ASTAS_DLL.dll")]
public extern static int Search(t_DeviceInfo* devInfPtr);
}
And in the main:
t_DeviceInfo[] devInfo = new t_DeviceInfo[16];
But thats about it. How do I marshal the array of structs to a fixed memory location and pass the corresponding pointer to Search()?

There's no need for unsafe here. You may as well remove that. And your struct declaration is incorrect. Some of the types are wrong. An C++ DWORD is a unsigned 32 bit value. A C# long is a signed 64 bit value. A C++ unsigned char is an unsigned 8 bit value. A C# char is a 16 bit character. Typically unsigned char maps to byte.
The struct should be:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct t_DeviceInfo
{
uint usb_hid_idx;
int open;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
string vid_pid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
string dev_info;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
string sn_info;
int hw_info;
byte hw_var;
int fw_vers;
}
The function declaration should then be:
[DllImport("ASTAS_DLL.dll")]
public extern static int Search([In] t_DeviceInfo[] devInfPtr);
This assumes that the calling convention is stdcall. It looks to me as though it is actually cdecl. In which case you need:
[DllImport("ASTAS_DLL.dll", CallingConvention=CallingConvention.Cdecl)]
public extern static int Search([In] t_DeviceInfo[] devInf);
I've used [In] marshalling. You may need to modify that depending on the semantics, which I cannot discern from the information provided.

Use the fixed statement in an unsafe context.

Related

unable to invoke function of dll written in c++ from c#

I need to invoke a function that have dll written in c++ from C# console app. I made marshaling and wrote the same datatypes in C#. But anyway I get errors. Where is the problem?
Here is code of the dll's function
extern "C" {
SAMPLENATIVEDLL_API int GetCardInfoEx(INT64 Card, DWORD Restaurant, DWORD UnitNo, TCardInfoEx* Info, char* InpBuf, DWORD InpLen, WORD InpKind, char* OutBuf, DWORD OutLen, WORD OutKind) {
int res = getCardInfoEx(Card, Info, UnitNo);
return res;
}
}
Here is TCardInfoEx structure code
#pragma pack(1)
typedef struct TCardInfoEx {
WORD Size;
BYTE Deleted;
BYTE Seize;
BYTE StopDate;
BYTE Holy;
BYTE Manager;
BYTE Blocked;
CHAR WhyLock[256];
CHAR Holder[40];
INT64 UserID;
DWORD CardAccountNumber;
DWORD TypeOfDefaulter;
WORD Bonus;
WORD Discount;
INT64 Summa;
INT64 AvailableSumma;
INT64 SummaOnCardAccount2;
INT64 SummaOnCardAccount3;
INT64 SummaOnCardAccount4;
INT64 SummaOnCardAccount5;
INT64 SummaOnCardAccount6;
INT64 SummaOnCardAccount7;
INT64 SummaOnCardAccount8;
CHAR OtherCardInformation[256];
CHAR OtherCardInformation1[256];
CHAR OtherCardInformation2[256];
};
Here is I made struct in c#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TCardInfoEx
{
ushort Size;
byte Deleted;
byte Seize;
byte StopDate;
byte Holy;
byte Manager;
byte Blocked;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
String WhyLock;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
String Holder;
Int64 UserID;
uint CardAccountNumber;
uint TypeOfDefaulter;
ushort Bonus;
ushort Discount;
Int64 Summa;
Int64 AvailableSumma;
Int64 SummaOnCardAccount2;
Int64 SummaOnCardAccount3;
Int64 SummaOnCardAccount4;
Int64 SummaOnCardAccount5;
Int64 SummaOnCardAccount6;
Int64 SummaOnCardAccount7;
Int64 SummaOnCardAccount8;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
String OtherCardInformation;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
String OtherCardInformation1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
String OtherCardInformation2;
};
Here is code where I invoke the method (function)
class Program
{
[DllImport("SampleNativeDLL.dll", CharSet = CharSet.Unicode)]
public static extern int GetCardInfoEx(Int64 Card, uint Restaurant, uint UnitNo, TCardInfoEx Info, String InpBuf, uint InpLen, ushort InpKind, String OutBuf, uint OutLen, ushort OutKind);
static void Main(string[] args)
{
TCardInfoEx tc = new TCardInfoEx();
GetCardInfoEx(1248000259045, 1, 1, tc, "", 0, 1, "", 1, 1);
}
}
And I get the following error:
Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in '\bin\Debug\FCFake.vshost.exe'.
Additional information: A call to PInvoke function 'Program::GetCardInfoEx' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
How to solve the issue?
UPD: I edited TCardInfoEx struct according to Dirk's answer. Unfortunatelly, it didn't help me
At a quick glance I'd say the problem is the fixed size char arrays in the C++ struct that you try to map to C# strings.
When you use such a fixed sized array it becomes embedded in the struct itself, as opposed of using a pointer, which would only add the pointer to the struct and not the data it points to.
You can modify the C# struct so .NET knows how map it to the native world using the MarshalAs-attribute:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
string OtherCardInformation;
This applies for all mappings for .NET strings to C++ char arrays.
You might also have to set the character encoding for the struct using
[StructLayout(CharSet = CharSet.Ansi)]
public struct TCardInfoEx
for example, if your C++ program uses ANSI characters.
You have specified a pack value (#pragma pack(1)). That also has to be translated into the .NET world using
[StructLayout(CharSet = CharSet.Ansi, Pack = 1)]
public struct TCardInfoEx
And finally your signatures don't really match:
int GetCardInfoEx(
INT64 Card, // long
DWORD Restaurant, // uint
DWORD UnitNo, // uint
TCardInfoEx* Info, // must be ref TCarfInfoEx
char* InpBuf, // could be string, could also be byte[]
DWORD InpLen, // uint
WORD InpKind, // ushort
char* OutBuf, // could be StringBuilder, or IntPtr
DWORD OutLen, // uint
WORD OutKind) // ushort
In .NET structs are passed by value, so you have to pass it by ref in order for the signatures to match.
Mapping a C++ char * to a .NET type is always context-dependant. Sometimes such a pointer is used as an input string, sometimes for arbitrary input data, and of course it could also just be a single character output parameter.
And (hopefully finally) your calling conventions don't match. The default calling convention with DllImport is StdCall while your function uses __cdecl. So you have to add that to your DllImport attribute too
[DllImport(..., CallingConvetion = CallingConvention.Cdecl, ...)]
Thanks to #DavidHeffernan this post expanded quite a bit from its original version, in which I only covered the fixed array size problem.

PInvoke - Attempted to read or write protected memory. This is often an indication that other memory is corrupt

I've seen the attempted to read or write protected memory error before.
Typically the error shows up when I don't set up the c# struct correctly. I do have other calls working properly but this one is not co-operating.
I'm almost certain that it could be both my function call and the struct that is causing the problem.
C Syntax
int CardTransaction(pTRequest req, char *ProductCodes)
Request structure (I condensed it b/c there were repetitive data types)
typedef struct _cardRequest
{
unsigned short RedemptionNum
long TotalAmount;
unsigned char filler1[257];
char CardNumber[80];
unsigned char cardType;
} TRequest, *pTRequest;
C# function call
[DllImport("card.dll"), CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int CardTransaction(ref CardRequest cardRequest, [MarshalAs(UnManagedType.LPStr)] StringBuilder productCodes);
ProductCodes is null so I just instantiated a stringbuilder object with nothing in it and passed it through. This is one place I'm thinking could be a problem.
C# structure
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct CardRequest
{
public uint16 RedemptionNum
public int TotalAmount;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public string filler1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string CardNumber;
public byte cardType;
}
The obvious problem is that the C code uses an aligned struct, but for some reason you have elected to pack the C# struct. Remove the Pack = 1 from the C# code to make the two structures match.
Beyond that the filler array looks more like a byte array than a string. I'd declare it like this:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
public byte[] filler1;
If you want to pass null to the productCodes parameter, then I expect you can do just that. I cannot recall every doing that myself, but generally when you pass null to a p/invoke, then the marshaller will pass NULL to the native code.

iterate over unknown c struct passed from c#

I am developing a c .dll that should be accessed from a C# program. Ideally, the .dll should receive any struct defined in C# and do something with it. So, initially, the struct type and size is unknown for the C dll. I was able to pass the struct through the C extern function, and it is supossed to be received alright, but, is there a way to find out the size and characteristics of this receive structure? is there a way to iterate over its members?
This is the C function defined for the dll
extern int __cdecl testCSharp(mystruct * Test){
//sizeof(Test) is 4, so it is ok
for(int i=0;i < sizeof(Test) ; i++){
char * value = (char*) Test; //This access the first element.
cout << value << endl; //Prints "some random string", so, it is received ok
}
return 1;
}
This is the C# code
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
unsafe public struct myStruct{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value1;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value2;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value3;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value4;
};
[DllImport("SMKcomUploadDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int testCSharp(ref myStruct message);
static void Main()
{
int result;
myStruct message = new myStruct();
message.value1 = "Some randome string";
message.value2 = "0";
message.value3 = "olkujhikvb";
message.value4 = "isabedfbfmlk";
result = testCSharp(ref message);
}
All the types are String in C#, and it is suppossed to remain like that, so it is the only thing I know about the struct that is going to be passed.
Any idea?
Thanks in advance
As you're marshalling them as ByValTStr with a length of 100, I'm not sure you'll be able to work any any more than what you already have (i.e. the first element).
From the MSDN (here)
.NET Framework ByValTStr types behave like C-style, fixed-size strings
inside a structure (for example, char s[5])
If you used LPStr or LPWStr null-termination instead you would be able to work out their lengths.

How can I import a DLL function which returns this structure?

I have a DLL and its header file, which were written in Visual C++. I need to use the following function in a C# project:
LIBSPEC int
CprFindDevices(
PCprDeviceInfo *ppDevInfo,
int *pNumDevices,
DWORD timeout
);
I am able to import it with a DllImport, but I cannot figure out how to implement the following structure in C#:
typedef struct _CprDeviceInfo
{
unsigned char id[ID_LEN];
unsigned char macAddr[MAC_LEN];
in_addr inAddr;
char ipAddr[IP_LEN];
char devName[INFO_NAME_LEN];
char port1Name[INFO_NAME_LEN];
char port2Name[INFO_NAME_LEN];
int tcpPort1;
int tcpPort2;
char interfaceIpAddr[IP_LEN];
} CprDeviceInfo, *PCprDeviceInfo;
The all-upper-case variables are all known constants which I can use in my C# project.
I know I must use [StructLayout(LayoutKind.Sequential)], but am not sure what the equivalent types are for each of the structure's members, and what the function signature would be when I import it. It's been a while since my C++ days.
This should do the job for you:
struct CprDeviceInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = ID_LEN)]
string id;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAC_LEN)]
string macAddr;
uint inAddr;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = IP_LEN)]
string ipAddr;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = INFO_NAME_LEN)]
string devName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = INFO_NAME_LEN)]
string port1Name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = INFO_NAME_LEN)]
string port2Name;
int tcpPort1;
int tcpPort2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = IP_LEN)]
string interfaceIpAddr;
}
Make sure you are using Charset.ASCII in your DLLImport which is the default.

Marshaling a C++ two-dimensional fixed length char array as a structure member

I am trying to call an unmanaged C++ function, that has a structure as an input parameter.
The structure is defined in the header file like this:
struct MyStruct
{
int siOrder;
char aaszNames[6][25];
int siId[6];
int siTones[6];
};
I tried to declare the managed struct as following:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct MyStruct {
public int siOrder;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst=150)]
public string aaszNames;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=6, ArraySubType=UnmanagedType.I4)]
public int[] siId;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=6, ArraySubType=UnmanagedType.I4)]
public int[] siTones;
}
But without any success. I am guessing that the marshaling fails, since the aaszNames is actually an array of six 25 long null-terminating strings.
I tried declaring aaszNames as
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=150)]
public char[] aaszNames;
filling the array with nulls where necessary. But, again, nothing.
Is there something I am missing? What am I dong wrong? What is the best way to marshal this 2-D char array?
Any hints, please.
Try using multiple C# structs:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct MyStruct_Name
{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 25)]
public string name;
}
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStruct
{
public int siOrder;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 6)]
public MyStruct_Name aaszNames;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.I4)]
public int[] siId;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.I4)]
public int[] siTones;
}
This is how I've been passing arrays of C-style strings around.
Don't forget to create the contents of aaszNames! The marshaller hates null references.
MyStruct foo = new MyStruct();
for (int i = 0; i < 6; i++)
{
foo.aaszNames[i] = new MyStruct_Name();
foo.aaszNames[i].name = "";
}
Good luck!
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=150)]
public char[] aaszNames;
That marshalling type looks well. Probably issue in function call, or bad memory allocation/
I would write a small c-program to check the byte size of the C-structure.
Then I would go with the other suggestion to extract the data field by field.
From a C standpoint the /0 is treated as normal character included in the 6 bytes whereas C# would use a length of 5 and have the /0 hidden.
char aaszNames[6][25];
char of C++ Type is 8 bits~
but char of C# Type is Unicode ,(16 bits) !
so char of C++ Type <-> byte of C# type

Categories

Resources