Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Returning string from unmanaged dll

Returning string from unmanaged dll

Scheduled Pinned Locked Moved C#
csharpc++jsonquestion
6 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Mikke_x
    wrote on last edited by
    #1

    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?

    H 1 Reply Last reply
    0
    • M Mikke_x

      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?

      H Offline
      H Offline
      Heath Stewart
      wrote on last edited by
      #2

      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 the CharSet 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* or char[] 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 the MarshalAsAttribute 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

      M 1 Reply Last reply
      0
      • H Heath Stewart

        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 the CharSet 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* or char[] 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 the MarshalAsAttribute 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

        M Offline
        M Offline
        Mikke_x
        wrote on last edited by
        #3

        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...

        H 1 Reply Last reply
        0
        • M Mikke_x

          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...

          H Offline
          H Offline
          Heath Stewart
          wrote on last edited by
          #4

          And what does your managed declaration look like?

          Microsoft MVP, Visual C# My Articles

          M 1 Reply Last reply
          0
          • H Heath Stewart

            And what does your managed declaration look like?

            Microsoft MVP, Visual C# My Articles

            M Offline
            M Offline
            Mikke_x
            wrote on last edited by
            #5

            [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 ) );

            H 1 Reply Last reply
            0
            • M Mikke_x

              [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 ) );

              H Offline
              H Offline
              Heath Stewart
              wrote on last edited by
              #6

              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 your Console? Also, since your using just printf and not tprintf, this tells me you're not using ASCII on Windows and Unicode on Windows NT (like Auto dictates), so use CharSet.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 as int, and it's typically better to use pre-proc defs). The lpszText param is both [in,out], and the cbText ([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 using printf[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)

              1 Reply Last reply
              0
              Reply
              • Reply as topic
              Log in to reply
              • Oldest to Newest
              • Newest to Oldest
              • Most Votes


              • Login

              • Don't have an account? Register

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • World
              • Users
              • Groups