locked
Converting from non-unicode to unicode string RRS feed

  • Question

  • Hi All,

    I'm calling an unmanaged DLL - giving it a pointer to a string I'd like to fill. The DLL is not returning a Unicode string so I "Believe" that I need to use the [MarshalAs(UnmanagedType.LPStr)]  syntax in my decleration.

    In the code below cb does seem to be populated with something, however when I try printing it to the screen it using Console.WriteLine(cb); - I get something like this on screen.

    YY♥"G▼


    I'm guessing this is because it's expecting to write a unicode string to the console. What the best way to convert cb .. or to at least verify my theory?

    Thanks
    Warrick

    ===========

    [DllImport("LT_LiveStorage.dll")]
            static extern bool FunctionName(IntPtr handle, string aPath, [MarshalAs(UnmanagedType.LPStr)] StringBuilder buf);


    StringBuilder cb = new StringBuilder(256);

    // Call DLL here

    Console.WriteLine(cb);
    Tuesday, February 10, 2009 2:39 PM

Answers

  • The out attribute did not solve it, but I was able to figure it out. It turns out I need to declare as ref and pass it in as a ref

    [MarshalAs(UnmanagedType.LPStr)] ref StringBuilder cb

    Thanks very much for your help though Nish - it's much appreciated!
    • Marked as answer by Wozza99 Tuesday, February 10, 2009 5:41 PM
    Tuesday, February 10, 2009 5:41 PM
  • To understand the difference, consider the kernel32:GetCurrentDirectory function. You'd do it like :

    public static extern uint GetCurrentDirectory(uint nBufferLength, [Out]StringBuilder lpBuffer);

    (if you try and use ref, it'll just crash). This is because here lpBuffer is passed by value.

    But in your Delphi function's specific case, it seems the function allocates memory and returns a pointer to this memory in lpBuffer. So in that case, unless you pass the pointer (reference in C#) by reference (as opposed to by value), you will not get the correct pointer (that points to the newly allocated memory).

    Hope that made sense.

    http://blog.voidnish.com
    • Marked as answer by Wozza99 Tuesday, February 10, 2009 6:43 PM
    Tuesday, February 10, 2009 6:40 PM
    Moderator

All replies

  • Can you show how your native C method is prototyped?
    http://blog.voidnish.com
    Tuesday, February 10, 2009 2:45 PM
    Moderator
  • It's really a Borland Delphi method, but sure.


      TFsGetMimeTypeFromPath = function(
        aHandle: Cardinal;
        aCallbackBuffer: Pointer;
        aPath: PChar;
        var aResult: PChar): HRESULT; stdcall;

    =========

            [DllImport("XXX.dll")]
            static extern int GetMimeTypeFromPath(IntPtr handle, MyCallback callback, string aPath, [MarshalAs(UnmanagedType.LPStr)] StringBuilder buf);


            static void Main(string[] args)
            {

                MyCallback call = new MyCallback( test );

                IntPtr handle = Initialize("", "xx", call);
                String aResult = "";
                StringBuilder cb = new StringBuilder(256);
     
                int result = GetMimeTypeFromPath(handle, call, "\\\\10.10.5.5\\1_Bedroom3.jpeg~image~jpeg", cb);

                Console.WriteLine(cb);
                string xx = Console.ReadLine();
            }

    Thanks
    Warrick


    Tuesday, February 10, 2009 2:51 PM
  • I did a search on "Delphi pchar unicode" and it seems pchar can be a wide string too depending on compiler options. So perhaps you should remove the MarshalAs attribute on the buf argument. What does that give you?
    http://blog.voidnish.com
    Tuesday, February 10, 2009 2:56 PM
    Moderator
  • I get something similar.

    YY♥(J▼

    I work with the guys who wrote the DLL and they've told me that they did not compile with wide strings.
    Tuesday, February 10, 2009 3:10 PM
  • Well then you are really doing everything the right way. Perhaps the function is failing for some reason (perhaps because the other parameters are incorrect). If so the char* returned may not be initialized correctly and is thus showing whatever garbage is there in memory at that point.
    http://blog.voidnish.com
    Tuesday, February 10, 2009 3:31 PM
    Moderator
  • So Console.Writeline is smart enough to figure out that cb is not a unicode string and convert it? 
    Tuesday, February 10, 2009 3:37 PM
  • No, the conversion (if any) is done by the marshaller (which you can control by the MarshalAs attribute). What I suspect is happening here is that your native function is not succeeding. Can you step into the native function some way? [perhaps via the Delphi debugger]. Or can you figure out from the return code whether the function succeeded or not? That's what I'd work on next.
    http://blog.voidnish.com
    Tuesday, February 10, 2009 3:47 PM
    Moderator
  • I can't debug the DLL myself, but I've asked the developer who wrote the DLL to add logging to it. He's logging both the input and the output values and I can confirm that the function does work as expected.

    We also have two other application in C++ and Borland Delphi that both load and use this DLL without a problem, so I'm fairly certain that it works as expected - I must be missing something here.
    Tuesday, February 10, 2009 5:27 PM
  • Well the one thing left to try is the [out] attribute.

    [DllImport("XXX.dll")]
    static extern int GetMimeTypeFromPath(IntPtr handle, MyCallback callback, string aPath, [MarshalAs(UnmanagedType.LPStr), Out] StringBuilder buf);

    http://blog.voidnish.com
    Tuesday, February 10, 2009 5:36 PM
    Moderator
  • The out attribute did not solve it, but I was able to figure it out. It turns out I need to declare as ref and pass it in as a ref

    [MarshalAs(UnmanagedType.LPStr)] ref StringBuilder cb

    Thanks very much for your help though Nish - it's much appreciated!
    • Marked as answer by Wozza99 Tuesday, February 10, 2009 5:41 PM
    Tuesday, February 10, 2009 5:41 PM
  • I really should not have marked this as the answer until someone who know what the difference is confirms that I'm doing the correct thing. This is a classic example of banging your head against the table until you find a solution, but then not learning from your mistakes. I don't really UNDERSTAND why this works not and not before. Can someone explain this please?
    Tuesday, February 10, 2009 5:46 PM
  • To understand the difference, consider the kernel32:GetCurrentDirectory function. You'd do it like :

    public static extern uint GetCurrentDirectory(uint nBufferLength, [Out]StringBuilder lpBuffer);

    (if you try and use ref, it'll just crash). This is because here lpBuffer is passed by value.

    But in your Delphi function's specific case, it seems the function allocates memory and returns a pointer to this memory in lpBuffer. So in that case, unless you pass the pointer (reference in C#) by reference (as opposed to by value), you will not get the correct pointer (that points to the newly allocated memory).

    Hope that made sense.

    http://blog.voidnish.com
    • Marked as answer by Wozza99 Tuesday, February 10, 2009 6:43 PM
    Tuesday, February 10, 2009 6:40 PM
    Moderator
  • That does make sense thanks! - I guess I need to read up a little more on how the [out] param works as I thought this was doing that for me.

    - Thanks again for the help.
    Warrick
    Tuesday, February 10, 2009 6:43 PM