Returning string from unmanaged dll
-
Hi! I'm writing a console application in C# and a dll in unmanaged C++. The dll is loaded via p/invoke and I access some functions in it that are dllexport'ed. What I would like to do is return a string or rather char[] from the dll to the C# console. I have tried several ways but at best I get about half the string, the rest of it is replaced by "?". :confused: I have tried to return a pointer to the string. This is when I get the "half string". ( the secont half of the string actually.. ?? The first ten letters are questionmarks.. ) What I would like to do is to pass a char* as an argument and that it should be pointed to a string in the unmanaged code. Is it possible? What should I pass as argument and what should I recieve at the dll?
-
Hi! I'm writing a console application in C# and a dll in unmanaged C++. The dll is loaded via p/invoke and I access some functions in it that are dllexport'ed. What I would like to do is return a string or rather char[] from the dll to the C# console. I have tried several ways but at best I get about half the string, the rest of it is replaced by "?". :confused: I have tried to return a pointer to the string. This is when I get the "half string". ( the secont half of the string actually.. ?? The first ten letters are questionmarks.. ) What I would like to do is to pass a char* as an argument and that it should be pointed to a string in the unmanaged code. Is it possible? What should I pass as argument and what should I recieve at the dll?
If your unmanaged declaration looked like this:
LPCTSTR SomeFunc();
Then your managed declaration should look like this
[DllImport("whatever.dll", CharSet=CharSet.Auto)]
private static extern string SomeFunc();Notice the
CharSet=CharSet.Auto
? That marshals the string correctly. Most likely what you're seeing is the result of marshaling an ASCII string as Unicode, or a Unicode string as ASCII. Strings in .NET are all treated as Unicode, so you have to tell the CLR how to marshal strings from unmanaged code. See theCharSet
enumeration in the .NET Framework SDK for more information. The reason some characters are written as ? is because the character representation isn't printable, meaning you got some junk data in there. This could be an indication that something is wrong with your unmanaged function. You should read Consuming Unmanaged DLL Functions[^] and Interop Marshaling[^] in the .NET Framework SDK. What you're trying to do is easy, but you need to understand the basics first. BTW,char*
orchar[]
is a string (in this case, an LPSTR, as defined in the Windows headers, but it could be define as anything). When P/Invoking unmanaged APIs, it's better to use a string and marshaling it (also see theMarshalAsAttribute
for more assistance and information) rather than a char array. It works the same, but passing a string is much easier than passing a char array.Microsoft MVP, Visual C# My Articles
-
If your unmanaged declaration looked like this:
LPCTSTR SomeFunc();
Then your managed declaration should look like this
[DllImport("whatever.dll", CharSet=CharSet.Auto)]
private static extern string SomeFunc();Notice the
CharSet=CharSet.Auto
? That marshals the string correctly. Most likely what you're seeing is the result of marshaling an ASCII string as Unicode, or a Unicode string as ASCII. Strings in .NET are all treated as Unicode, so you have to tell the CLR how to marshal strings from unmanaged code. See theCharSet
enumeration in the .NET Framework SDK for more information. The reason some characters are written as ? is because the character representation isn't printable, meaning you got some junk data in there. This could be an indication that something is wrong with your unmanaged function. You should read Consuming Unmanaged DLL Functions[^] and Interop Marshaling[^] in the .NET Framework SDK. What you're trying to do is easy, but you need to understand the basics first. BTW,char*
orchar[]
is a string (in this case, an LPSTR, as defined in the Windows headers, but it could be define as anything). When P/Invoking unmanaged APIs, it's better to use a string and marshaling it (also see theMarshalAsAttribute
for more assistance and information) rather than a char array. It works the same, but passing a string is much easier than passing a char array.Microsoft MVP, Visual C# My Articles
Hi! Thanks! But I still get the wrong output. :confused: My exported dll has a function that first writes the string to export to file and then returns it, Ie: __declspec(dllexport) LPCTSTR GetString( int i ) { FILE* file; file = fopen("test.txt", "a" ); fprintf(file, "%s%c",m_strings[i], '\n'); fclose(file); return m_devices[i]; } This gives the correct output in the file, but the string I receive in the managed console is containing ten questionmarks first and then some correct charakters: --- test.txt: PCI-1750 I/O=2040H Advantech DEMO I/O=1H --- Console output: ????????? /O=2040H ??????????HEMO I/O=1H This is really confusing...
-
Hi! Thanks! But I still get the wrong output. :confused: My exported dll has a function that first writes the string to export to file and then returns it, Ie: __declspec(dllexport) LPCTSTR GetString( int i ) { FILE* file; file = fopen("test.txt", "a" ); fprintf(file, "%s%c",m_strings[i], '\n'); fclose(file); return m_devices[i]; } This gives the correct output in the file, but the string I receive in the managed console is containing ten questionmarks first and then some correct charakters: --- test.txt: PCI-1750 I/O=2040H Advantech DEMO I/O=1H --- Console output: ????????? /O=2040H ??????????HEMO I/O=1H This is really confusing...
And what does your managed declaration look like?
Microsoft MVP, Visual C# My Articles
-
And what does your managed declaration look like?
Microsoft MVP, Visual C# My Articles
-
[System.Runtime.InteropServices.DllImport("pciDriver.dll", CharSet=CharSet.Auto) ] private static extern string GetString( int i ); and I call it like: GetNumberOfStrings( ref i ); System.Console.Out.WriteLine( GetString( j ) );
That's correct, so the problem is in your unmanaged code. How is
m_Devices
declared? What codepage is Windows set up for? Are you using a different codepage for yourConsole
? Also, since your using justprintf
and nottprintf
, this tells me you're not using ASCII on Windows and Unicode on Windows NT (like Auto dictates), so useCharSet.Ansi
instead (if this is the case). Like I said before, you should read the documentation, not just expect the exact answer (especially when you don't provide enough details to say for sure). One thing I would warn you about as well is that it's actually a bad idea to return strings from functions. I don't remember all the details (it's been a while since I was hot n' heavy into C/C++), but you should instead declare your function like so:__declspec(dllexport) void GetString(INT i, LPSTR lpszText, DWORD cbText);
Also notice my use of
INT
(declared asint
, and it's typically better to use pre-proc defs). ThelpszText
param is both [in,out], and thecbText
([in]) is the max string length. Without this protection, you open your code up - and potentially your entire system - to buffer overruns (the leading cause of security threats). This signature is common for functions and structs throughout the Windows APIs. Your managed declaration would then look like this (taking into account that is seems you're using ASCII only strings since you're usingprintf[DllImport("pciDriver.dll", CharSet=CharSet.Ansi)] private static extern void GetString( int i, [In, Out] string lpszText, [MarshalAs(UnmanagedType.U4)] int cbSize);Technically, however, the first param - `i` - should be an `IntPtr`. An `int` in C# refers to `Int32`, which is always 32 bits. An `int` in C/C++ is the width of the processor, so 32 bits on a 32-bit processor and 64 bits on a 64-bit processor (depends on the compiler, really). An `IntPtr` is like that. If you wanted to make your code more portable to other architectures, then change the param type to `IntPtr` and when you pass it, pass it as `new IntPtr(0)` (or whatever index you need). Microsoft MVP, Visual C#[](</x-turndown)