Hi all I am trying to compile a C++ project into a DLL and then use the Structs and methods from C#. I made an openCV program and I need to pass data back and forth. For now I am trying simple things to get familiar with the language since it is the first time I touch C++ and C# ...
So in C++ I have :
#pragma pack(push, 2)
typedef struct
{
//char *plate;
unsigned int width;
unsigned int height;
//bool hasPlateOn;
//unsigned char *imgData;
} Sframe;
extern "C" Sframe MYCVAPI findPossiblePlates(Sframe& in)
And I try to use it from C# like this :
[StructLayout(LayoutKind.Sequential)]
struct Sframe
{
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
// char[] plate;
[MarshalAs(UnmanagedType.U4)]
int width;
[MarshalAs(UnmanagedType.U4)]
int height;
// bool hasPlateOn;
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
// char[] imgData;
}
[DllImport(#"ALPR_pwv.dll",CallingConvention=CallingConvention.Cdecl,EntryPoint = "findPossiblePlates")]
static extern Sframe findPossiblePlates(Sframe infoOut);
But no matter what I try I get errors that the method's type signature is not PInvoke compatible.
is there anyone that could help me understand this issue?
Is it even possible to pass and get custom structs among C++ and C# ?
because the end stuct will be much more complex in the end since I have to pass images with data like coordinates etc...
As #SaniSinghHuttunen mentioned I was missing the ref keyword and had to use MarshalAs.U4 ... Thanks again :)
Related
The project I am working on has a case where I have to read and display hardware info, for which they have a function written in a C++ DLL, and I am writing a C# stub to PInvoke it. I am fairly new to PInvoke, and hold beginner status in C# and C++ in general, as I mostly work in the Java space.
The issue here is that I am getting an exception, I feel like there is some marshaling issue going on here, which is causing this exception, but please correct me if I am wrong. Can you please help me spot the issue?
Also, I did try combining multiple MarshalAs options and different data types. In addition to the complication as it is, the function takes in a reference to a struct as an argument, and the struct itself has a nested struct within. Also, it returns a long as a flag I believe, when called with no arguments returns 0 with no exception, which is interesting.
===========================================================================================================
Part of sample hardware operation C++ dll header.
===========================================================================================================
#define APOINTER *
typedef unsigned char BYTETYPE;
typedef BYTETYPE CHARTYPE;
typedef unsigned long int ULONGTYPE;
typedef HDINFO APOINTER PTR_HDINFO;
typedef struct VERSION {
BYTETYPE major;
BYTETYPE minor;
}VERSION;
typedef VERSION APOINTER PTR_VERSION;
typedef struct HDINFO {
VERSION hardwareVersion;
CHARTYPE hardwareManufactureID[32]; /* blank padded */
ULONGTYPE hardwareflags; /* must be zero */
CHARTYPE hardwareDesc[32]; /* blank padded */
VERSION apiVersion;
}HDINFO;
typedef HDINFO APOINTER PTR_HDINFO;
extern "C" __declspec(dllexport) ULONGTYPE GetHardwareInfo(PTR_HDINFO pSoInfo);
===========================================================================================================
Sample C# code for PInvoke. Throws Exception
===========================================================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace ConsoleHardwareApp
{
[StructLayout(LayoutKind.Sequential, Size =1)]
struct VERSION
{
byte majorVersion;
byte minorVerison;
}
[StructLayout(LayoutKind.Sequential, Size =1)]
struct HDINFO
{
VERSION hardwareVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
string hardwareManufactureID;
int hardwareflags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
string hardwareDesc;
VERSION apiVersion;
}
class Program
{
[DllImport("sampleHardwareOp.dll")]
public static extern int GetHardwareInfo(ref IntPtr hdInfoPtr);
static void Main(string[] args)
{
HDINFO hdInfo = new HDINFO();
IntPtr hdInfoPtr = new IntPtr();
hdInfoPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(HDINFO)));
Marshal.StructureToPtr<HDINFO>(hdInfo, hdInfoPtr, false);
int rv = 1;
rv = GetHardwareInfo(ref hdInfoPtr); //Exception thrown here
Console.WriteLine(rv);
Console.ReadKey();
}
}
}
The exception:
ConsoleHardwareApp.Program::GetHardwareInfo' 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.
Without concise details about which C++ compiler was used to make the DLL, what size its long data type is (may be 32bit or 64bit), what alignment settings are used, if any structure padding is present, etc, then a translation to C# is difficult. However, your C# code should probably look something more like the following instead:
namespace ConsoleHardwareApp
{
[StructLayout(LayoutKind.Sequential/*, Pack=4 or 8*/)]
struct VERSION
{
byte major;
byte minor;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi/*, Pack=4 or 8*/)]
struct HDINFO
{
VERSION hardwareVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
string hardwareManufactureID;
uint hardwareflags; // or ulong, depending on the C++ compiler used
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
string hardwareDesc;
VERSION apiVersion;
}
class Program
{
[DllImport("sampleHardwareOp.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern uint/*or: ulong*/ GetHardwareInfo(ref HDINFO pSoInfo);
static void Main(string[] args)
{
HDINFO hdInfo = new HDINFO();
uint rv = GetHardwareInfo(ref hdInfo);
Console.WriteLine(rv);
Console.ReadKey();
}
}
}
The CallingConvention in particular is very important. The default calling convention in C++ is usually __cdecl (but can be changed in compiler settings), however in C# the default CallingConvention in DllImport is CallingConvention.Winapi, which is __stdcall on Windows. A calling convention mismatch can easily corrupt the call stack, causing the error you are seeing.
I have written a function using C and built it into a DLL file.
Then from C#, I call my function from Dll like this sample.
https://blogs.msdn.microsoft.com/jonathanswift/2006/10/03/dynamically-calling-an-unmanaged-dll-from-net-c/
There is a function like that:
In C:
typedef struct {
DWORD Old;
}STRUCT_SON;
typedef struct {
DWORD NumberOfGirl;
STRUCT_SON SonList[8];
}STRUCT_PARENT;
int getStructExample(STRUCT_PARENT* x_lstExample, DWORD* x_dwSum)
{
....
}
In C#:
private delegate int getStructExampleDelegate(out ?????,out int iSum);
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll,
"getStructExample");
getStructExampleDelegate getStructExample =
(getStructExampleDelegate)Marshal.GetDelegateForFunctionPointer(
pAddressOfFunctionToCall,typeof(getStructExampleDelegate));
int iSum;
int theResult = getStructExample(out ??????, out iSum);
So my question is How to get the struct data type?
Maybe using Marshal.PtrToStructure. But What is "????" to get array STRUCT_SON.
C# Calling C++ DLL Function which returns a struct
Im using like this example to get x_dwSum, but dont know to to get that struct.
???? is not out. Its using IntPtr to get address of struct then using Marshal.PtrToStructure to Parse into redefined struct in c#.
redefine in C# must be like this:
public struct STRUCT_PARENT {
DWORD NumberOfGirl;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
STRUCT_SON[] SonList;
};
I am currently working on some C# code that talks to a C++ dll. This is not an area in which I - or anyone else at my company - has any experience. It's been an eye-opener to say the least.
After a lot of reading, trial and error, and frustration, I've managed to iron out most of the kinks and get something that's largely functional. However, from time to time, it still throws this at me ...
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
.. and then dies. This error only appears when I run the call on parallel threads - it's fine single threaded. This dll is supposed to be thread safe and we've good reason to believe it ought to be, if handled correctly.
The cause of this error is always a call to the same function:
[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi)]
public static extern int QABatchWV_Close(IntPtr vi1);
I have the header file for the library, which defines this function as:
__declspec(dllimport) int __stdcall QABatchWV_Close(int);
From what I understand there are additional tools at my disposal like SafeHandle and MarshalAs. But, frankly, I'm unsure as to how to best deploy them in this situation.
This error tends to take several hours of use time to show up, so tweaking and hoping isn't going to be a productive approach here. Can anyone point me as to what I might be doing wrong in calling down to the C++ function?
Well, first of all you don't need setting Charset here, because there are no strings.
Second of all - function in cpp should be declared as exported not imported, so it should look like:
__declspec(dllimport) int __stdcall QABatchWV_Close(int);
Next, you should set calling convention in your C# code to stdcall:
[DllImport(DLL, SetLastError = true, CallingConvention=CallingConvention.Stdcall)]
Next you should have int instead of IntPtr in C# code. And I'm nearly sure that name of this function (in C++ dll) is mangled and it's not QABatchWV_Close but rather something like QABatchWV_Close#32. You should check it using "dll export viewer".
Have a look at the following code which I use to call a c (not c++) dll. I know it is not really an answer to your question, but perhaps you can use some of this going foreward.
Note the "CallingConvention"-specifier in the dll declaration and also the "FreeGlobal" in the "finally" part of the try catch.
public class csInterface
{
[DllImport(#"myDLL.dll", EntryPoint = "dllFunc", CallingConvention = CallingConvention.StdCall)]
private static extern void dllFunc(IntPtr inp, IntPtr outp);
public static int myDll(ref MyInput myInput, ref MyOutput myOutput)
{
int sizeIn, sizeOut;
IntPtr ptr_i = IntPtr.Zero, ptr_u = IntPtr.Zero;
sizeIn = Marshal.SizeOf(typeof(myInput));
sizeOut = Marshal.SizeOf(typeof(myOutput));
/* Calling C */
try
{
ptr_i = Marshal.AllocHGlobal(sizeIn);
ptr_u = Marshal.AllocHGlobal(sizeOut);
Marshal.StructureToPtr(myInput, ptr_i, true);
Marshal.StructureToPtr(myOutput, ptr_u, true);
dllFunc(ptr_i, ptr_u);
myOutput = (MyOutput)(Marshal.PtrToStructure(ptr_u, typeof(MyOutput)));
}
catch (Exception)
{
//Return something meaningful (or not)
return -999;
}
finally
{
//Free memory
Marshal.FreeHGlobal(ptr_i);
Marshal.FreeHGlobal(ptr_u);
}
//Return something to indicate it all went well
return 0;
}
}
In C# I declare my types
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MySubType
{
public int a;
public double b;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyInput
{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)]
public string aString; //A string of length 3
public bool aBoolean;
public int anInt;
public char aChar;
public double aDouble;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 12)]
public MySubType[] aSubType; //Array of struct of length 12
}
And something similar for the output.
Now in C (its probably the same or similar in c++) i declare my dll
__declspec(dllexport) void _stdcall dllFunc(MyCInput *myCInput, MyCOutput *myCOutput)
{
//Code
}
And the corresponding C types which obviously have to mirror the C# types exactly
typedef struct
{
int a;
double b;
} MyCSubType;
typedef struct
{
char aString[4];
int aBoolean; //This needs to be cast over to your C boolean type
int anInt;
char aChar;
double aDouble;
MyCSubType myCSubType[12];
} MyCType;
Now the types I have used in this example do not exactly match what I have used in my code, and i have not tested this code. So there may be typos and such, but the "principle" is ok.
I'm trying to access the Nikon image SDK(for those interested see: 1) to implement access to *.nef file in a programm. I'm stuck at a return code from the dll which should be interpreted as "invalid parameter" and I'm running out of ideas.
Yes I know the chance, that somebody is exactly using this dll is sparse, but I'm rather looking for "writing"/"thinking" errors... I'm still learning (so excuse to any wrong used terms, etc...) and also for this reason this is a little "longer" post (some "aloud thinking" on my side ;-) )
1.) the dll has an entry function where you pass a identifier and a struct as parameter. The identifier stands for a specific command (like open,close,etc....). The struct is used for data exchange with the camera.
2.) I do have everything together and working (since, I'm getting a "return code") but I can't figure out the reason for the return code (maybe some datatype is incompatible?)
So first the "C++"-part:
c++ function definition:
extern "C" unsigned long __declspec(dllexport) WINAPI Nkfl_Entry(unsigned long ulCommand, void* pParam );
this is stdcall, so I do need to worry about any further options to dllimport, since usigned long(c++) corresponds to uint(c#) i get two uints one "out" and one "in"...
c++ struct defintion:
typedef struct tagNkflLibraryParam
{
unsigned long ulSize; // Size of structure
unsigned long ulVersion; // Version
unsigned long ulVMMemorySize; // Size of vertual memory
NkflPtr* pNkflPtr; // Pointer of StratoObject
unsigned char VMFileInfo[ MAX_PATH ]; // Swap file info
} NkflLibraryParam, *NkflLibraryPtr;
so I do need to pass 3 times uints, one pointer to an "StratoObject" ((1.) the doc says "typedef void* NkflPtr" so this is "just" a void* pointer 2.) the doc says if this is zero it will be filled up by the sdk) and finally one byte (since unsigned char(c++) corresponds to byte(c#)).
So first question: Is this correct?
Then going to the "coding-part":
c# struct defintion:
namespace NikonStruct
{
[StructLayout(LayoutKind.Sequential)]
public struct NkflLibraryParam
{
public uint ulSize; // size of the NkflLibraryParam structure
public uint ulVersion; // version number of the interface specification
public uint ulVMMMemorySize; // upper limit of the physical memory that can be used
public IntPtr pNkflPtr; // pointer to the StratoManager object
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] VMFileInfo; // swap file information
}
}
now this should correspond to my defintions above...
c# Program class:
class Program
{
public enum eNkflCommand : int
{
kNkfl_Cmd_OpenLibrary = 1,
kNkfl_Cmd_CloseLibrary = 2,
};
[DllImport("NkImgSDK.dll", EntryPoint = "Nkfl_Entry")]
public static extern uint kNkfl_Cmd_OpenLibrary(eNkflCommand ulCommand, ref NikonStruct.NkflLibraryParam data);
[DllImport("NkImgSDK.dll", EntryPoint = "Nkfl_Entry")]
public static extern uint kNkfl_Cmd_CloseLibrary(eNkflCommand ulCommand, IntPtr close);
static void Main(string[] args)
{
try
{
// specify return value of entry function
uint result1, result2;
/// call the kNkfl_Cmd_OpenLibrary Function
// generate data structure, which is used to communicate with kNkfl_Cmd_OpenLibrary function
NikonStruct.NkflLibraryParam _NkflLibraryParam = new NikonStruct.NkflLibraryParam();
// fill the fields of _NkflLibraryParam structure for kNkfl_Cmd_OpenLibrary function
_NkflLibraryParam.ulVersion = 16777216;
_NkflLibraryParam.ulSize = ((uint)Marshal.SizeOf(_NkflLibraryParam)); ;
// call the entry function with parameters for kNkfl_Cmd_OpenLibrary
result1 = kNkfl_Cmd_OpenLibrary(eNkflCommand.kNkfl_Cmd_OpenLibrary, ref _NkflLibraryParam);
Console.WriteLine(result1);
/// call the kNkfl_Cmd_CloseLibrary Function
result2 = kNkfl_Cmd_CloseLibrary(eNkflCommand.kNkfl_Cmd_CloseLibrary, IntPtr.Zero);
Console.WriteLine(result2);
}
catch
{
string errorMsg = new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()).Message;
throw new ArgumentException(errorMsg);
}
}
}
So nothing specific here:
eNkflCommand is from the doc
the structure is passed by reference so ref...
the "close" function expects "null pointer" (according to doc)
ulVersion is 0x01000000 (according to doc)
all other struct values are not set (and are zero by default if I understood the clr doc correctly)
Compiles and runs as already mentioned but result1 returns wrong "status-code" which translates to "invalid param" as already mentioned.
Any help appreciated....
FOUND IT:
never trust a documentation of a software developer: there was actually a missing parameter (not declared in the documentation BUT in an additional header definition file which was in another sub-directory of the sdk-package...)
so actually the struct defintion in the c# should be:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct struktur
{
public uint ulSize; // size of the NkflLibraryParam structure
public uint ulVersion; // version number of the interface specification
public uint ulVMMMemorySize; // upper limit of the physical memory that can be used
public IntPtr pNkflPtr; // pointer to the StratoManager object
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] VMFileInfo; // swap file information
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)]
public byte[] DefProfPath; // <- this one is not included in the doc of NIKON (I still don't now what this should hold but it works if it's empty...)
}
Thanks to jszigeti and DavidHeffernan for trying...
-In my c code I have a struct which contains many unknown sized arrays in an unmanaged dll (c code)
-I need the data of one instance of this struct marshaled over to c#, which I will later on send back to the unmanaged c code
-I do not need to manipulate this data once it gets to csharp, only hold onto it/store it for a while (so it can remain in a byte array).
-I do not want to use the keyword 'unsafe' as it is a big project and this is just one small piece and I don't want to be compiling like that.
I tried marshaling it as a lpArray and everything looks fine but when i look at the contents after coming back to the csharp, it is always empty. This type of marshaling style worked for me for dynamic arrays of various types but not the struct.
Searching the web is drawing blanks and much more complicated scenarios than my own, but if anybody has seen such a link please post it here I would be very greatful!
Thanks.
--update here is more or less the structure of my code:
c#:
[DllImport("mydll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern int W_Thread_Connect_NET(
[MarshalAs(UnmanagedType.LPStr, SizeConst = 100)] string IPAddress,
int DevicePort,
[MarshalAs(UnmanagedType.LPArray)] byte[] connectionHandle
);
//and call it like this, with an empty struc to be populated by c (can this be done? it is comming back without the data):
byte[] myStrucParam= new byte[100];
int result = W_Thread_Connect_NET(myStrucParam, myParam1, myParam2, ...);
c:
typedef struct myStructDef{
char* myArray1,
char* myArray2,
int myInt1,
...
} mystrucObj, *pMystrucObj;
//method that i am wanting to marshal the struct as a paramter here..
MYDLL_DLLIMPORT int APIENTRY W_Thread_Connect_NET(pMystrucObj strucReturn_handle, char * IPAddress, int DevicePort, ...)
{
//(omitted)
}
You say that the C# code does not need to manipulate the struct. That makes it a pretty simple problem to solve. You can treat the struct pointer as an opaque pointer, that is an IntPtr.
First of all you add a new function to your native code:
pMystrucObj CreateStruct(void)
{
pMystrucObj res = malloc(sizeof(*res));
return res;
}
Then in your C# code you call it like this:
[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern IntPtr CreateStruct();
Now declare W_Thread_Connect_NET like this:
[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern int W_Thread_Connect_NET(
IntPtr theStructPtr,
string IPAddress,
int DevicePort,
....
);
And call it all like this:
IntPtr theStructPtr = CreateStruct();
int res = W_Thread_Connect_NET(theStructPtr, IPAddress, DevicePort, ...);
And of course you'll want to add another function named DestroyStruct to deallocate the struct's memory once you are done with it.