String Parameter not Being Marshalled Correctly to C++ DLL - c#

I have been provided with a DLL which is to be called by C#. The DLL contains two methods as follows
extern "C" {
__declspec(dllexport) BSTR GroupInit(LPCTSTR bstrIniFile, bool bDiagErr, bool bProcErr);
}
BSTR GroupInit(LPCTSTR bstrIniFile, bool bDiagErr, bool bProcErr) {
CString strResult = "";
char* sz;
::SetVars(bDiagErr, bProcErr);
if (sz = ::GroupInit((char*)bstrIniFile, 1))
strResult = sz;
return strResult.AllocSysString();
}
I am attempting to call these DLLs from C# by first defining the class:
[DllImport("GrouperServer.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GroupInit(
string strCmdFile,
bool bAllowBadDiagCodes,
bool bAllowBadProcCodes
);
and doing
this.strCommandFilePath = "C:\\MyDir\\MyCommandFile.txt";
string s = Grouper.GrouperServer.GroupInit(this.strCommandFilePath, true, true);
But the DLL is returning the error: 'Cannot find command file: "C"' (the first character of the path only, which I have checked in the C++ DLL). For some reason the string this.strCommandFilePath is not being passed into the C++ method correctly.
What is wrong with the above call?
Edit to address comments.
The method being called in the if (sz = ::GroupInit((char*)bstrIniFile, 1)) statement is defined in a .c file and has the signature
char *GroupInit(char *szCmd, int iType)
{
...
}

It is a mistake to use TCHAR and related types here. The use case of TCHAR is for code that needs to compile for both Windows 9x which has no Unicode support, and Windows NT which does. Those days are long gone and TCHAR is obscuring the problem. What's more, the underlying code uses char* so it makes little sense to pretend that your wrapper code can do anything else. So switch to char.
On top of that you are casting away const. I guess because the function you call accepts a modifiable buffer for a parameter that it does not modify. Best solution is to fix the original library code that erroneously accepts char* and make it accept const char*. If you cannot do that then you'll need to cast away the const. But do that the C++ way with const_cast<>.
So, I'd have the C++ code like this:
BSTR GroupInit(const char* szIniFile, bool bDiagErr, bool bProcErr) {
CString strResult = "";
char* sz;
::SetVars(bDiagErr, bProcErr);
if (sz = ::GroupInit(const_cast<char*>(szIniFile), 1))
strResult = sz;
return strResult.AllocSysString();
}
And the C# code should be:
[DllImport("GrouperServer.dll", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GroupInit(
string strCmdFile,
bool bAllowBadDiagCodes,
bool bAllowBadProcCodes
);
Now, one wonders what happens to sz. Who is expected to deallocate that? Does it even need to be deallocated? Only you can answer those questions.

Related

Tracing source of access violation in unmanaged C# code

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.

.NET Interop: How to get returned string (not null terminated) from unmanaged DLL in C#

I defined a function in C DLL library.
__declspec(dllexport) void* GetText();
It will return a string which is dynamically allocated from heap memory (And GlobalAlloc is used here for allocating memory). Note that the returned string is not null-terminated.
Then at C# side I tried two methods to declare the function
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern String GetText();
When calling above method, the application will crash without any exception thrown.
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr GetText();
ptr = GetText();
string text = Marshal.PtrToStringAuto(ptr, 1000);
And calling this method will return incorrect string. Checked the real bytes by using Marshal.Copy, I found the bytes value is not same as the value in DLL library. (I think it's caused by Virtual Memory, C# process cannot access memory space of the DLL directly)
(Don't mind the string length, I hard coded it to 1000 for ease)
This is the C++ code and the memory value of the string when debugging (It's a Console Application but not the original DLL, because Console Application is easy to debug. But the DLL code is same as this one except the logging part).
Following is the original DLL code
__declspec(dllexport) char* GetText(){
VTHDOC hDoc = NULL;
VTHTEXT hText = VTHDOC_INVALID;
DAERR da_err = NULL;
DAERR ta_err = NULL;
DAERR read_err = NULL;
char *buf = (char*)GlobalAlloc(GMEM_FIXED, 1000);
DWORD real_size;
DAInitEx(SCCOPT_INIT_NOTHREADS, OI_INIT_DEFAULT);
da_err = DAOpenDocument(&hDoc, 2, "D:\\1TB.doc", 0);
ta_err = TAOpenText(hDoc, &hText);
read_err = TAReadFirst(hText, (VTLPBYTE)buf, 1000, &real_size);
return buf;
}
But at C# side the bytes are not same as C++ side
You can see the first byte in C++ is 0, but it's 200 for C# (decimal)
Another thing to note: if I return a const string(e.g. "AASSDD") directly in DLL code, C# side will get the correct string
You can't do it that way. Marshaling of string works only for null-terminated strings (or for BSTR, if you specify some options). You can:
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr GetText();
But from there, it isn't clear how the C# program should know the length of the string.
The various Marshal methods of C# handle BSTR (that have internally their length) or NUL terminated strings.
As already stated, it works for null-terminated strings only, in the following way:
C# part, declaration:
[DllImport("myDll.dll", EntryPoint = "myString", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
extern private static string myString(out int size);
C# part, usage:
int size;
string s = myString(out size);
C++ part:
char* myString(int* size)
{
*size = 20;
char* strg = (char*)::GlobalAlloc(GMEM_FIXED, *size);
memset(strg, 0x3f, *size); //preset with a questionmark
for (int i=0; i < 9; i++)
strg[i] = 0x40 + i;
strg[*size -1] = 0; //limit the maximum string length
return strg;
}
And the obtained C# string:
"#ABCDEFGH??????????", value of size: 20
A treatment of the issue may be found here

Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt

I am trying to pass a byte array to a c++ dll:
c++:
extern "C" __declspec(dllexport) char* myfunction(byte bytes[])
{
char *byteschar = (char*)bytes;
//do somethings with it
return byteschar;
}
c#:
[DllImport("mydll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl
,CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string myfunction(byte[] bytes);
but I get a System.AccessViolationException when I call myfunction.
When I run the executable without the debugger it seems to be working fine
If you want a buffer be allocated in C# and filled in C++, the approach is a little bit different.
You should allocate a kind of "unmanaged" buffer, pass to the DLL and then convert the result and free the buffer. It's exactly the same way in C, but calling from a managed environment.
Your C++ code should be something like:
extern "C" __declspec(dllexport) void myfunction(char* buffer, int length)
{
//Fill buffer with something observing the maximum length of the buffer.
}
The signature of your DLL in C# should be:
[DllImport("mydll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl
,CharSet = CharSet.Ansi)]
public static extern string myfunction(IntPtr buffer, Int32 length);
To call it from C#, you should do:
IntPtr unmanagedBuffer = Marshal.AllocHGlobal(100);
// Your Unmanaged Call
myfunction(unmanagedBbuffer, 100);
string yourString = Marshal.PtrToStringUni(unmanagedBuffer);
Marshal.FreeHGlobal(unmanagedBuffer);
Don't forget to call FreeHGlobal if you don't want a memory leak in your app. It's interesting to protect this in "try/finally" clauses.
Other observation is the encoding of the string. Uni, means Unicode. If you use another string representation, check for an equivalent PtrToStringXXX function.
It suppose to be:
extern "C" __declspec(dllexport) char* myfunction(unsigned char * bytes)
{
//do somethings with it
return bytes;
}

C++ dll and C# call

I have a function made in C++ that calls a COM interface's function
Its signature:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
{
//initcom
//do something
// release pointers
}
In C#:
[DllImport("funcdll.dll")]
static extern bool func(String strIn, ref String strOut);
// use it
for(int i=0;i<10;i++)
{
if(func(strin, strout))
{
//do something with strout
}
}
I have tested my dll in a C++ console application, it works, but in C# it crashes with an unknown error.
You've got three problems that I can see.
The calling conventions don't match. Your C++ code is cdecl and your C# code is stdcall.
The C++ code uses wide strings, but the C# code marshals ANSI strings.
The second parameter doesn't match. Your C# code assumes that the C++ code returns a new pointer to a C string which the C# code then deallocates with the COM allocator. Your C++ code doesn't do this.
Now, dealing with these in more detail.
Calling conventions
This is pretty easy to fix. Simple change the C++ code to stdcall, or the C# code to cdecl. But don't do both. I'd change the C# code:
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl]
Unicode/ANSI strings
I presume you are wanting to use Unicode strings since you have explicitly selected them in the C++ code. But P/invoke defaults to marshalling ANSI strings. You can change this again in the DllImport like so:
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
Returning a string from C++ to C#
Your current C++ function declaration is so:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
The __out decorator has no real effect, other than documenting that you want to modify the buffer pointed to by strOut and have those modifications returned to the caller.
Your C# declaration is:
static extern bool func(String strIn, ref String strOut);
Now, ref String strOut simply does not match. A ref string parameter matches this in C++:
BOOL func(LPWSTR strIn, LPWSTR *strOut)
In other words the C# code is expecting you to return a new pointer. In fact it will then proceed to deallocate the buffer you returned in strOut by calling CoTaskMemFree. I'm confident that's not what you want.
Your original C++ code can only return a string to the C# code by modifying the buffer that was passed to it. That code would look like this:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
{
...
wcscpy(strOut, L"the returned string");
...
}
If this is what you want then you should allocate a sufficient buffer in C# in a StringBuilder object.
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
static extern bool func(string strIn, StringBuilder strOut);
...
StringBuilder strOutBuffer = new StringBuilder(128);
bool res = func("input string", strOutBuffer);
string strOut = StringBuilder.ToString();
If you simply cannot decide in the C# code how big a buffer you need then your best bet is to use a BSTR to marshal strOut. See this answer for details.
It's hard for me to tell without seeing the details of your C++ method... but, I've never had much luck using String with P/Invoke.
Try using IntPtr instead of String, and use Marshal.PtrToStringUni for the outgoing string, and marshal your managed string into unmanaged land with Marshal.StringToHGlobalUni and, after the function call, make sure to free the unmanaged string with Marshal.FreeHGlobal.
Also, coming from C99 land, my bools don't work with .NET bools... I don't know why. I have to use byte and check == 1. Don't know if you'll run into this with C++... but, if you want my advice on a place to start which seems least-breakable in my experience, here ya go:
[DllImport("funcdll.dll")]
static extern byte func(IntPtr strIn, out IntPtr strOut);
// use it
string myString = "Testing";
IntPtr stringOut;
IntPtr stringIn = Marshal.StringToHGlobalUni(myString);
if(func(stringIn, out stringOut) == 1)
{
//do something with strout
string stringOutValue = Marshal.PtrToStringUni(stringOut);
// Depending on how you dealt with stringOut in your
// unmanaged code, you may have to: Marshal.FreeCoTaskMem(stringOut);
// or Marshal.FreeHGlobal(stringOut) if you're returning
// an owning reference to a copied string.
}
Marshal.FreeHGlobal(stringIn);
I beleive you're having a calling convention mismatch. The default calling convention in C++ is cdecl while for .NET it's stdcall. try
[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool func(String strIn, ref String strOut);
Also you might have to specifically tell the marshallar that you want to marshal the strings as LPWSTR using [MarshalAs(UnmanagedType.LPWStr)] attributes.
[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool func([MarshalAs(UnmanagedType.LPWStr)]String strIn
,[MarshalAs(UnmanagedType.LPWStr)]ref String strOut);
have a look at http://msdn.microsoft.com/en-us/library/s9ts558h.aspx
In C++ make sure you have something like this (at least for the second argument)
extern "C" BOOL __stdcall func( BSTR * pBstr )
{
*pBstr = SysAllocString( L"Foobar" );
return 0;
}
In C# write something like this (for the second argument) :
static extern bool func( [MarshalAs(UnmanagedType.BStr)] ref String strOut);
Sorry I do not have a big answer but just remember something from my experience.. did you try to use StringBuilder eg changing your c# import function signature as
[System.Runtime.InteropServices.DllImport("funcdll.dll")]
static extern bool func(String strIn, System.Text.StringBuilder strOut);

C# & C++, runtime error when call C++ dll from C#

I have written a C++ wrapper DLL for C# to call. The DLL was tested and worked fine with my C++ test program.
now integrated with C#, I got runtime error and crashed. Cannot use debugger to see more details.
The C++ side has only one method:
#ifdef DLLWRAPPERWIN32_EXPORTS
#define DLLWRAPPERWIN32_API __declspec(dllexport)
#else
#define DLLWRAPPERWIN32_API __declspec(dllimport)
#endif
#include "NB_DPSM.h"
extern "C" {
DLLWRAPPERWIN32_API int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
string& message) ;
}
in the C# side, there is a definition,
[DllImport("..\\..\\thirdParty\\cogs\\DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
ref string message);
and a call:
string msg = "";
int returnVal = WriteGenbenchDataWrapper(rawDataFileName,
parameterFileName, outputBaseName, logFileName, ref msg);
I guess there must be something wrong with the last parameter of the function. string& in C++ should be ref string in C#?
EDIT:
Do we really need the extern "C"?
EDIT 2:
after I remove the extern "C from the dll, I got the EntryPointNotFoundException. When I look at the dll by using DLL Export Viewer, I found the function name is "int __cdecl WriteGenbenchDataWrapper(class std:: ..." Do I need to include the " __cdecl"?
There are a bunch of rules for marsheling with PInvoke.
For reference Marsheling between managaed & unmanaged
Focusing on the C# side first.
If you knew a reasonable size of the message up front you could use StringBuilder type and define that size, something like.
[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
StringBuilder message
int messageLength );
Impression from the name message (and other posts) indiciates you don't know the size up front, and you won't be passing a partial message to the function so maybe
[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(in string fileNameToAnalyze,
in string parameterFileName,
in string baseNameToSaveData,
in string logFileName,
out string message );
Now on the C/C++ side - to match the second definition
extern "C" // if this is a C++ file to turn off name mangling for this function only
int WriteGenbenchDataWrapper( char * fileNameToAnalyze,
char * parameterFileName,
char * baseNameToSaveData,
char * logFileName,
char ** message ) {
string internalMessage;
SomeFunc( internalMessage ); // these functions won't have extern "C" applied
* message = (char *)::CoTaskMemAlloc(internalMessage.length()+1);
strcpy(* message, internalMessage.c_str());
}
Consideration of unicode/ansi strings is also important, refer to [MarshalAsAttribute(UnmanagedType.LPWSTR)]
For release mode you will want to remove your development path settings "..\..\thirdParty\cogs"
In your C++ code:
I've always needed the extern "C". C++ mangles function names if you don't (the mangling is needed to support function overloading). The extern "C" tells it not to do this.
I also will declare the functions as __stdcall. I believe you can tell C# which type of calling convention to use, but I think __stdcall is the default.
As far as passing a string object, I'm not sure about that, I stick to only using primitives for parameter passing, so I would use const char * and adjust accordingly in my C++ code.
Also, I try to avoid passing by reference. Rather, if I need to return several values, I'll set up a series of getters to handle this (a const char * returns as an IntPtr).
In your C# code:
I use String for the const char *, int for int, and so on. I believe Microsoft has a chart somewhere to tell you what should sub in for what.
When dealing with a returned string, you need to convert it to ANSI. This can be done with a call to Marshal.PtrToStringAnsi().
For Example:
In my C++ code:
extern "C" __declspec(dllexport) const char* __stdcall GetCompany(const char *In) {
return MyGetCompany(In); // Calls the real implementation
}
In my C# code:
[DllImport("TheDLL.dll", EntryPoint = "GetCompany")]
private static extern IntPtr privGetCompany(String In);
// Call this one, not the one above:
public String GetProvince(String In)
{
return Marshal.PtrToStringAnsi(privGetCompany(In));
}
One final note, if you're running on a 64-bit machine, the 'Any CPU' configuration will make a 64-bit C# executable, which will need a 64-bit DLL. If you only have a 32-bit DLL, you'll need to add a configuration (x86).
The error message you got indicates that your C# program is probably finding the DLL correctly and the function as well, so name mangling is not likely the problem. It sounds like calling convention issue or a problem with the parameter passing.

Categories

Resources