I've created a wpf project which has a helper static class that contains all my c++ backend code. One such function is defined as:
public static unsafe class Backend {
[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void write(void* ptr, char* path);
}
public partial class MainWindow : Window
{
public MainWindow()
{
string path = "mypath";
InitializeComponent();
unsafe
{
char *p; //convert
void* myObj = Backend.init_obj(1920, 1080);
Backend.gen(myObj);
Backend.write(myObj, p);
}
}
}
The void* ptr is actually my object that is casted in order to marshall it onto the C# side. The problem I face is that whenever I try to invoke this with a string literal in wpf, I get that Visual C# cannot convert this because string literals are encoded in UTF16. Naturally I tried many things other than manually copying the relevant bytes to a char array. Any tips?
One of the things the CLR can do pretty well for interop with C/C++ code is marshalling data structures between managed and unmanaged code. Since strings are pretty important, a lot of work went into making strings marshal as well as possible.
As a side note, you're using void* for the context object that's created by init and passed to write. Since you're just handing it back, you can replace it with IntPtr and avoid unsafe blocks altogether. IntPtr is always the size of a pointer in the current architecture.
First, let's change the declaration of the imported functions. CharSet.Ansi tells it to marshal strings as ANSI. The ptr parameter becomes IntPtr
[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static IntPtr init(int width, int height);
[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void gen(IntPtr ptr);
[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void write(IntPtr ptr, string path);
And from there, you can figure out how to modify the function to deallocate ptr and any others you have to call.
Using those functions becomes a lot easier and a lot cleaner. You don't need the unsafe block and you can pass path directly to write.
public MainWindow()
{
string path = "mypath";
InitializeComponent();
IntPtr myObj = Backend.init_obj(1920, 1080);
Backend.gen(myObj);
Backend.write(myObj, path);
}
Original comment that got it working:
Instead of trying to create the char* parameter yourself, change the declaration so the second parameter is string and let the Runtime marshal it for you. Because it's an ANSI string, you're never going to get full unicode fidelity but that's a problem created by the C++ code.
Related
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.
Consider the following C function:
void get_lib_version(const char **ver_string);
How do I marshall this correctly with PInvoke? The documentation says it returns a pointer to a static string. I thought this would do it:
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int get_lib_version(StringBuilder version);
but all I get is gibberish.
The function returns a brand new C-string. The pinvoke marshaller always makes sure that the memory required to store a string that's returned by native code is released again. This will not come to a good end, surely the caller of this function is not supposed to release it. The const keyword is a strong hint that the native code will return a pointer to a string literal that's not allocated on the heap. Trying to release such a pointer will crash your program on later Windows versions, the kind that have a strict heap implementation (after XP).
You have to help to stop the marshaller from doing this. This requires you to declare the argument as a raw pointer, not a string:
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
public static extern int get_lib_version(out IntPtr version);
And you have to make the extra step to convert the pointer to a string:
public string GetLibraryVersion() {
IntPtr strptr;
get_lib_version(out strptr);
return Marshal.PtrToStringAnsi(strptr);
}
Write a little test program to verify this assumption. Call GetLibraryVersion() a billion times. If the memory usage doesn't explode then you're good.
According to this answer, when you marshal something as string, PInvoke makes all sorts of assumptions about how it's supposed to get freed. Notice that this is a const char *; it's a constant string somewhere. It never needs to be deallocated!
Apparently the way to deal with this is
Marshall as IntPtr.
Use Marshall.PtrToStringAnsi() to copy the result into a C# string.
I managed to get this to work correctly:
[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
private static extern int get_lib_version(ref IntPtr version);
public static string GetLibVersion()
{
var ptrVersion = IntPtr.Zero;
get_lib_version(ref ptrVersion);
var version = Marshal.PtrToStringAnsi(ptrVersion);
return version;
}
I am trying to store and retrieve some data into/from an unmanaged dll. I have tried to narrow down my problem by simplifying the struct as much as possible and here is what I am getting down to:
Structure definition
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public class MyStruct
{
private UInt32 size;
public UInt16 SomeData;
public MyStruct()
{
size = (UInt32)Marshal.SizeOf(this);
this.SomeData = 66; //just put any non 0 value for test
}
}
DLL imports:
[DllImport(MY_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
[return:MarshalAs(UnmanagedType.U1)]
public static extern bool SetData(ref MyStruct ms);
[DllImport(MY_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr GetData();
Function calls:
MyStruct ms_in = new MyStruct();
bool b = Wrapper.SetData(ref ms_in);
IntPtr ptr = Wrapper.GetData();
MyStruct ms_out = (MyStruct)Marshal.PtrToStructure(ptr, typeof(MyStruct));
Simple enough I guess. I know that charset and packing are ok as I simply pasted the struct layout attributes from another struct definition for the same dll as I did for most of the code actually.
When reading the content of ms_out it is just full of garbage (random large numbers).
I finally found the answer to my question by trial and error but I can't understand it much. Here is the working version:
[DllImport(MY_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool SetData( [In, MarshalAs(UnmanagedType.LPStruct)] MyStruct ms);
Replacing ref by [In, MarshalAs(UnmanagedType.LPStruct)] did the trick but why?
Thank you for your answers, happy coding.
Now why is the structure being full of garbage on return from the unmanaged dll?
This occurs when I set the data on the unmanaged dll and then get it back but not when I locally create an unmanaged pointer, set the data and read them back:
MyClass x = new MyClass(); //create class
IntPtr ptr2 = Marshal.AllocHGlobal(Marshal.SizeOf(x)); //allocate unmanaged memory
Marshal.StructureToPtr(x, ptr2, false); //marshall to unmanaged memory
MyClass xOut = (MyClass)Marshal.PtrToStructure(ptr2, typeof(MyClass)); //marshall from unmanaged memory
Marshal.FreeHGlobal(ptr2); //free unmanaged memory
If your data "survives" the above test then all the StructLayout, charset, marshalling, etc is OK. In my case it is.
If you simply create the data in c# and pass a pointer to it to the unmanaged code you are only certain that the pointer address will be valid, the data it points to will be invalid as soon as the c# variable is out of scope.
The following method will work with any kind of data I tested including strings:
[return:MarshalAs(UnmanagedType.U1)]
public static extern bool SetData( IntPtr data); //no marshalling in, no ref, no problem...
MyClass x = new MyClass(); //create class
//...store some data in x....
IntPtr ptrIn = Marshal.AllocHGlobal(Marshal.SizeOf(x)); //allocate unmanaged memory
Marshal.StructureToPtr(x, ptrIn, false); //marshall to unmanaged memory
bool b = Wrapper.SetData(ref ms_in); //store data in unmanaged dll
IntPtr ptrOut = Wrapper.GetData(); //get data back from unmanaged dll
MyClass xOut = (MyClass)Marshal.PtrToStructure(ptrOut, typeof(MyClass)); //marshall from unmanaged memory
Marshal.FreeHGlobal(ptrIn); //free unmanaged memory
The problem here is that the caller must release the memory allocated. In my scenario this is used to pass the data on to yet another unmanaged dll (don't ask...) but the calling program has no way to make sure that the final recipient has actually read the data.
Who is going to take care of cleaning up the memory in the end?
I will probably go with the callee cleaning up the data and the caller checking that the data is released/cleaning if not before creating a new block and same thing on exit unless I find a scenario a bit simpler.
Investigating this further I found that marshalling can be used with both c# structs or classes as destination.
Using a struct:
[StructLayout...
struct MyStruct
{
//some properties
//can't have parameterless constructor
public void MakeStruct()
{
size = ...;
//initialize properties as needed
}
}
...
public static extern bool SetData(ref MyStruct ms); //ref is ok for struct, equivalent to c &struct
Using a class:
[StructLayout...
class MyClass
{
//some properties
//parameterless constructor
public void MyClass()
{
size = ...;
//initialize properties as needed
}
}
...
public static extern bool SetData([In, MarshalAs(UnmanagedType.LPStruct)] MyClass ms); //no ref for class
Using a class has the advantage of automatically setting the size in the constructor instead of the caller having to set it explicitly with the struct version.
-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.
I want to send a string from C# to a function in a native C++ DLL.
Here is my code:
The C# side:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Unicode)]
private static extern void func1(byte[] path);
public void func2(string path)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] arr = encoding.GetBytes(path);
func1(this.something, arr);
}
The C++ side:
void func1(char *path)
{
//...
}
What I get in the C++ side is an empty string, every time, no matter what I send.
Help?
Thanks.
It looks like you have 2 issues. The first is your native C++ uses an ANSI string but you are specifying unicode. Secondly, it's easiest to just marshal a string as a string.
Try changing the DllImport to the following
[DllImport(
#"Native3DHandler.dll",
EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(void* something, [In] string path);
Works fine for me with no extra marshalling instructions in VS2008:
C# side:
[DllImport("Test.dll")]
public static extern void getString(StringBuilder theString, int bufferSize);
func()
{
StringBuilder tstStr = new StringBuilder(BufSize);
getString(tstStr, BufSize);
}
C++ side:
extern "C" __declspec(dllexport) void getString(char* str, int bufferSize)
{
strcpy_s(str, bufferSize, "FOOBAR");
}
Your declaration is wrong. The parameter should be of type string, and you should set the character set encoding to Ansi, like so:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(string path);
This assumes that you are not modifying the contents of the path variable in your C++ code. Then, you pass the string parameter directly (no need for the wrapper).
If you just want to send a string, just declare func1's parameter as a string. If you want to receive a string, declare it as a StringBuilder and allocate enough buffer space for what you want to receive.
Default Marshaling for Strings
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx