views:

1189

answers:

3


I'm trying to use P/Invoke to fetch a string (among other things) from an unmanaged DLL, but the string comes out garbled, no matter what I try.

I'm not a native Windows coder, so I'm unsure about the character encoding bits. The DLL is set to use "Multi-Byte Character Set", which I can't change (because that would break other projects). I'm trying to add a wrapper function to extract some data from some existing classes. The string in question currently exists as a CString, and I'm trying to copy it to an LPTSTR, hoping to get it into a managed StringBuilder.

This is what I have done that I believe is the closest to being correct (I have removed the irrelevant bits, obviously):

// unmanaged function
DLLEXPORT void Test(LPTSTR result)
{
  // eval->result is a CString
  _tcscpy(result, (LPCTSTR)eval->result);
}


// in managed code
[DllImport("Test.dll", CharSet = CharSet.Auto)]
static extern void Test([Out] StringBuilder result);


// using it in managed code
StringBuilder result = new StringBuilder();
Test(result);
// contents in result garbled at this point


// just for comparison, this unmanaged consumer of the same function works
LPTSTR result = new TCHAR[100];
Test(result);

Really appreciate any tips! Thanks!!!

+2  A: 

One problem is using CharSet.Auto.

On an NT-based system this will assume that the result parameter in the native DLL will be using Unicode. Change that to CharSet.Ansi and see if you get better results.

You also need to size the buffer of the StringBuilder that you're passing in:

StringBuilder result = new StringBuilder(100); // problem if more than 100 characters are returned

Also - the native C code is using 'TCHAR' types and macros - this means that it could be built for Unicode. If this might happen it complicates the CharSet situation in the DllImportAtribute somewhat - especially if you don't use the TestA()/TestW() naming convention for the native export.

Michael Burr
A: 

You didn't describe what your garbled string looks like. I suspect you are mixing up some MBCS strings and UCS-2 strings (using 2-byte wchar_ts). If every other byte is 0, then you are looking a UCS-2 string (and possibly misusing it as an MBCS string). If every other byte is not 0, then you are probably looking at an MBCS string (and possibly misusing it as a Unicode string).

In general, I would recommend not using TCHARs (or LPTSRs). They use macro magic to switch between char (1 byte) and wchar_t (2 bytes), depending on whether _UNICODE is #defined. I prefer to explicit use chat and wchar_t to make the codes intent very clear. However, you will need to call the -A or -W forms of any Win32 APIs that use TCHAR parameters: e.g. MessageBoxA() or MessageBoxW() instead of MessageBox() (which is a macro that checks whether _UNICODE is #defined.

Then you should change CharSet = CharSet.Auto to something CharSet = CharSet.Ansi (if both caller and callee are using MBCS) or CharSet = CharSet.Unicode (if both caller and callee are using UCS-2 Unicode). But it sounds like your DLL is using MBCS, not Unicode.

pinvoke.net is a great wiki reference with many examples of P/Invoke function signatures for Win32 APIs:

cpeterso
A: 

Dont use out paramaeter as you are not allocating in c function

[DllImport("Test.dll", CharSet = CharSet.Auto)]
static extern void Test(StringBuilder result);

StringBuilder result = new StringBuilder(100);
Test(result);

This should work for you

Uday