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.
Related
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.
I am working on C#.Net CF for WIN-CE platform. In my code I am using
int size = Marshal.SizeOf(typeof(struct_Obj));
IntPtr newptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(struct_Obj, newptr, false);
I am trying to send this struct info:
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
public struct __NIC_STAT
{
ulong Size; // Of this structure.
public Char[] ptcDeviceName; // The device name to be queried..
ulong DeviceState; // DEVICE_STATE_XXX above
ulong DeviceState; // DEVICE_STATE_XXX above
ulong MediaType; // NdisMediumXXX
ulong MediaState; // MEDIA_STATE_XXX above
ulong PhysicalMediaType;
ulong LinkSpeed; // In 100bits/s. 10Mb/s = 100000
UInt64 PacketsSent;
UInt64 PacketsReceived;
ulong InitTime; // In milliseconds
ulong ConnectTime; // In seconds
UInt64 BytesSent; // 0 - Unknown (or not supported)
UInt64 BytesReceived; // 0 - Unknown (or not supported)
UInt64 DirectedBytesReceived;
UInt64 DirectedPacketsReceived;
ulong PacketsReceiveErrors;
ulong PacketsSendErrors;
ulong ResetCount;
ulong MediaSenseConnectCount;
ulong MediaSenseDisconnectCount;
} ;
when I run the code in WIN-CE machine, I am getting "not supported exception".Those two methods are throwing exceptions.
Can anybody tell me how to find the structure size and how to convert structure to Ptr with out any issues for WIN-CE.
Thanks!!
Your struct is declared incorrectly. The C++ ULONG is a 32 bit unsigned type. But in C#, ulong is 64 bits. That's clearly a huge problem.
On top of that, I must admit to being slightly sceptical about using char[] in the way you do. I would do it as a string with UnmanagedType.LPWStr.
So I would have your struct like this:
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
public struct __NIC_STAT
{
uint Size;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
uint DeviceState;
uint DeviceState;
uint MediaType;
uint MediaState;
uint PhysicalMediaType;
uint LinkSpeed;
ulong PacketsSent;
ulong PacketsReceived;
uint InitTime;
uint ConnectTime;
ulong BytesSent;
ulong BytesReceived;
ulong DirectedBytesReceived;
ulong DirectedPacketsReceived;
uint PacketsReceiveErrors;
uint PacketsSendErrors;
uint ResetCount;
uint MediaSenseConnectCount;
uint MediaSenseDisconnectCount;
};
I'm not sure why Marshal.SizeOf is failing for you. You may need to declare ptcDeviceName as IntPtr and use Marshal.StringToHGlobalUni to set the value. That at least makes the struct blittable and if Marshal.SizeOf still fails then you can fall back on sizeof.
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.
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.
I am doing some C# interop work. I have the following struct:
#pragma pack(push,1)
typedef struct
{
unsigned __int64 Handle;
LinkType_t Type;
LinkState_t State;
unsigned __int64 Settings;
signed __int8 Name[MAX_LINK_NAME];
unsigned __int8 DeviceInfo[MAX_LINK_DEVINFO];
unsigned __int8 Reserved[40];
} LinkInfo_t;
This is my attempt to convert it into a C# struct:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct LinkInfo_t
{
[MarshalAs(UnmanagedType.U8)]
public UInt64 Handle;
[MarshalAs(UnmanagedType.I4)]
public LinkType_t Type;
[MarshalAs(UnmanagedType.I4)]
public LinkState_t State;
[MarshalAs(UnmanagedType.U8)]
public UInt64 Settings;
[MarshalAs(UnmanagedType.LPStr, SizeConst = MAX_LINK_NAME)]
public string Name;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_LINK_DEVINFO, ArraySubType = UnmanagedType.U1)]
public byte[] DeviceInfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 40, ArraySubType = UnmanagedType.U1)]
public byte[] Reserved;
}
However, whenever I initialize the struct the Name, DeviceInfo, and Reserved fields are all set to null. How do I fix this?
For the arrays, try to use the fixed keyword:
public fixed byte DeviceInfo[MAX_LINK_DEVINFO];
public fixed byte Reserved[40];
whenever I initialize the struct the
Name, DeviceInfo, and Reserved fields
are all set to null
This is correct, and your definition looks OK to me (BTW, you don't need [MarshalAs] on the primitive fields, the default behaviour is to do what you specified there). Because your array fields are null, the marshaler won't do anything about them when marshaling your struct to unmanaged memory, but it's going to create the strings and arrays when unmarshaling.
What Anton Tykhyy says is correct. I just want to clarify with some examples. Using 'fixed' works, but that forces you to use 'unsafe' as well. I like to avoid using unsafe wherever possible. Using Marshal is a way to get around that.
First, let's say that I have a library that was created in C with the following definitions.
typedef struct {
int messageType;
BYTE payload[60];
} my_message;
/**
* \param[out] msg Where the message will be written to
*/
void receiveMessage(my_message *msg);
/*
* \param[in] msg The message that will be sent
*/
void sendMessage(my_message *msg);
In C#, the following structure would be equivalent to the one in C.
[StructLayout(LayoutKind.Sequential, Size = 64), Serializable]
struct my_message
{
int messageType;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 60)]
byte[] payload;
public initializeArray()
{
//explicitly initialize the array
payload = new byte[60];
}
}
Since the msg in receiveMessage() is documented as [out], you don't need to do anything special to the array in the structure before passing it to the function. i.e.:
my_message msg = new my_message();
receiveMessage(ref msg);
byte payload10 = msg.payload[10];
Since the msg in sendMessage() is documented as [in], you will need to fill the array before calling the function. Before filling the array, the array needs to be explicitly instantiated before using it. i.e.:
my_message msg = new my_message();
msg.initializeArray();
msg.payload[10] = 255;
sendMessage(ref msg);
Calling initializeArray() should instantiate the array in the previously allocated space created within the struct for this array.