none
Interop/Marshalling (C <-> C# land)

    Question

  • I'm pretty familiar with interop, and usually figure stuff out after reading the documentation and experimenting. But now I've hit a snag caused by my own C library! :)

    I've defined a structure in the C code, which looks like this:

    #pragma pack(1)
    typedef struct MODULE_INFO
    {
    Int32 ID;
    Int32 RefCount;
    WCHAR_STR Name;
    HMODULE Address;
    } ModuleInfo;

    Just for clarity, if you don't recognize some of those types, they are defined in my standard company headers for consistency and readability. Int32 = long int (or long long), WCHAR_STR = const wchar*.

    The problem arises in marshaling THAT structure type back into the managed C# world. And can you guess where the problem is? "WCHAR_STR Name;"

    I first attempted redefining the managed version of the struct like this:

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct ModuleInfo
    {
    Int32 id;
    Int32 refCount;

    [MarshalAs(UnmanagedType.LPWStr)]
    string name;

    IntPtr address;
    };

    Note, I cut out all properties/constructors because only the fields matter. But doing it like this is a no-go. I just had the idea to try the MarshalAs attribute like this, and apparently, the CLR doesn't like that! It says, if any imported function returns a type ModuleInfo, that the method's signature is not compatible with PInvoke (I suppose it's not liking the idea of a non-blittable reference type in the struct?).

    To confirm it was the string field causing the problem, I changed the type to char* and all of the libraries and the test application ran like a well-oiled machine. BUT, this is the cause of a problem. Namely, I don't want an exposed pointer in there that may or may not point to something, and I don't want client code to be forced to deal with pointers. So if this is the only way to do things, I'm going to have to use a new version of this struct with a char* field in the wrapper class, then I'll have to write a brand new one with the managed string field and write conversion methods. Tedious and slow! And I've already spit out about 3000 lines of code tonight, lol.

    So I'm wondering if anyone has any advice for me here, or can tell me what I ought to do with this issue?
    Thursday, October 28, 2010 12:10 PM

Answers

  • Problem is the marshaler doesn't know how to handle the memory allocated for the string. When you step outside the simple cases, it isn't safe to use the default marshaler. There are too many ways for it to assume things and do something wrong.

    I would create a second structure UnmanagedModuleInfo containing an IntPtr and marshal myself the string. Alternatively, you can use a char* and create a string with new string(charPtr)

    The following example assumes the string is allocated with SysAllocString. If it is allocated with CoTaskMemAlloc or LocalAlloc, use FreeCoTaskMem or FreeHGlobal instead of FreeBSTR. If it allocated with something else, you'll need to have another p/invoke call to deallocate it. If you shouldn't deallocate it yourself, just remove the FreeBSTR call.

    public struct ModuleInfo
    {
     Int32 id;
     Int32 refCount;
    
     string name;
    
     IntPtr address;
    
     public static ModuleInfo GetModuleInfo()
     {
      return UnmanagedModuleInfo.GetModuleInfo();
     }
    
     #region Unmanaged structure
     [StructLayout(LayoutKind.Sequential, Pack = 1)]
     struct UnmanagedModuleInfo
     {
      public Int32 id;
      public Int32 refCount;
    
      public IntPtr name;
    
      public IntPtr address;
    
      public static implicit operator ModuleInfo(UnmanagedModuleInfo umi)
      {
       string name = Marshal.PtrToStringUni(umi.name);
       Marshal.FreeBSTR(umi.name);
       return new ModuleInfo
       {
        id = umi.id,
        address = umi.address,
        name = name,
        refCount = umi.refCount
       };
      }
    
      [DllImport("biblio")]
      public static extern UnmanagedModuleInfo GetModuleInfo();
     }
     #endregion
    }
    
    • Marked as answer by Bin-ze Zhao Monday, November 01, 2010 6:56 AM
    Thursday, October 28, 2010 3:10 PM
  • Success! This was a hideously complicated project, and this was just one little part and one little snag that stopped me cold. But I'm glad I've settled it. I never thought about using Marshal's CoTask-X methods to perform this conversion. But thanks for the idea where you presented a similar approach, Louis! You shoved me in the right direction for sure! :)

    My solution was to rename the structure "NativeModuleInfo", and just have it store a simple char* for the "Name" field. NativeModuleInfo was marked internal, and I created a CLASS called ModuleInfo which holds a string for the "Name" field. Then I wrote a constructor for ModuleInfo which takes a NativeModuleInfo structure as a parameter, and convert's it's char* into a managed string copy, like: name = new string(pChar);

    Then I made a simple implict conversion operator, and changed the logic in my C# wrapper of the native C code. I use Marshal.StringToCoTaskMemUni(string) to create an unmanaged wchar* buffer when I need to send a ModuleInfo instance to go to the "native world" (by converting it to NativeModuleInfo), and I use Marshal.ZeroFreeCoTaskMemUnicode((IntPtr)moduleName) to get rid of the extra memory block (it persists in the native C dll's memory once I send it over, so I no longer need the memory Marshal created).

    All in all, it works perfectly! This is not DONE completely for sure, but I think I can refactor/tidy up this code, add some robust exception handling logic, and be done with this big part of my overall project. Here's how I tested it, and it's lovely! But this is a useless/contrived example. :)

     

      class Program 
      {
        delegate int TestDelegate([MarshalAs(UnmanagedType.LPWStr)] string title);
    
        static void Main(string[] args)
        {
          var loader = DynamicLoader.Get();
          
          ModuleInfo info = loader.LoadModule("kernel32.dll");
    
          var SetConsoleTitleW = (TestDelegate)
            loader.GetDelegateForFunction(info, "SetConsoleTitleW", typeof(TestDelegate));
    
          SetConsoleTitleW("Testing 123");
        }
      }
    

    Useless example, but cool, eh? Thanks again Louis! :)

    • Marked as answer by Bin-ze Zhao Monday, November 01, 2010 6:56 AM
    Friday, October 29, 2010 9:21 AM

All replies

  • Problem is the marshaler doesn't know how to handle the memory allocated for the string. When you step outside the simple cases, it isn't safe to use the default marshaler. There are too many ways for it to assume things and do something wrong.

    I would create a second structure UnmanagedModuleInfo containing an IntPtr and marshal myself the string. Alternatively, you can use a char* and create a string with new string(charPtr)

    The following example assumes the string is allocated with SysAllocString. If it is allocated with CoTaskMemAlloc or LocalAlloc, use FreeCoTaskMem or FreeHGlobal instead of FreeBSTR. If it allocated with something else, you'll need to have another p/invoke call to deallocate it. If you shouldn't deallocate it yourself, just remove the FreeBSTR call.

    public struct ModuleInfo
    {
     Int32 id;
     Int32 refCount;
    
     string name;
    
     IntPtr address;
    
     public static ModuleInfo GetModuleInfo()
     {
      return UnmanagedModuleInfo.GetModuleInfo();
     }
    
     #region Unmanaged structure
     [StructLayout(LayoutKind.Sequential, Pack = 1)]
     struct UnmanagedModuleInfo
     {
      public Int32 id;
      public Int32 refCount;
    
      public IntPtr name;
    
      public IntPtr address;
    
      public static implicit operator ModuleInfo(UnmanagedModuleInfo umi)
      {
       string name = Marshal.PtrToStringUni(umi.name);
       Marshal.FreeBSTR(umi.name);
       return new ModuleInfo
       {
        id = umi.id,
        address = umi.address,
        name = name,
        refCount = umi.refCount
       };
      }
    
      [DllImport("biblio")]
      public static extern UnmanagedModuleInfo GetModuleInfo();
     }
     #endregion
    }
    
    • Marked as answer by Bin-ze Zhao Monday, November 01, 2010 6:56 AM
    Thursday, October 28, 2010 3:10 PM
  • Thanks. I had a similar idea, but haven't finished trying it out. One thing though...the native code is keeping the actual wchar* Name field, and I think I should copy it into a managed string but NOT release the memory. Is that right?
    Friday, October 29, 2010 8:02 AM
  • Success! This was a hideously complicated project, and this was just one little part and one little snag that stopped me cold. But I'm glad I've settled it. I never thought about using Marshal's CoTask-X methods to perform this conversion. But thanks for the idea where you presented a similar approach, Louis! You shoved me in the right direction for sure! :)

    My solution was to rename the structure "NativeModuleInfo", and just have it store a simple char* for the "Name" field. NativeModuleInfo was marked internal, and I created a CLASS called ModuleInfo which holds a string for the "Name" field. Then I wrote a constructor for ModuleInfo which takes a NativeModuleInfo structure as a parameter, and convert's it's char* into a managed string copy, like: name = new string(pChar);

    Then I made a simple implict conversion operator, and changed the logic in my C# wrapper of the native C code. I use Marshal.StringToCoTaskMemUni(string) to create an unmanaged wchar* buffer when I need to send a ModuleInfo instance to go to the "native world" (by converting it to NativeModuleInfo), and I use Marshal.ZeroFreeCoTaskMemUnicode((IntPtr)moduleName) to get rid of the extra memory block (it persists in the native C dll's memory once I send it over, so I no longer need the memory Marshal created).

    All in all, it works perfectly! This is not DONE completely for sure, but I think I can refactor/tidy up this code, add some robust exception handling logic, and be done with this big part of my overall project. Here's how I tested it, and it's lovely! But this is a useless/contrived example. :)

     

      class Program 
      {
        delegate int TestDelegate([MarshalAs(UnmanagedType.LPWStr)] string title);
    
        static void Main(string[] args)
        {
          var loader = DynamicLoader.Get();
          
          ModuleInfo info = loader.LoadModule("kernel32.dll");
    
          var SetConsoleTitleW = (TestDelegate)
            loader.GetDelegateForFunction(info, "SetConsoleTitleW", typeof(TestDelegate));
    
          SetConsoleTitleW("Testing 123");
        }
      }
    

    Useless example, but cool, eh? Thanks again Louis! :)

    • Marked as answer by Bin-ze Zhao Monday, November 01, 2010 6:56 AM
    Friday, October 29, 2010 9:21 AM
  • I have similar problem with you and glad that you found your solution. I tried to implement your code, but when I use marshal's StructureToPtr function to convert the structure to bytes, it didn't give me the correct bytes. Here is the sample code that I am using. did I do anything wrong?

    public static byte[] LogfontToByteArray(LogFont logFont)
    {
      IntPtr ptPoint = Marshal.AllocHGlobal(92);
      Marshal.StructureToPtr(logFont, ptPoint, true);
      byte[] bff = new byte[92];
      Marshal.Copy(ptPoint, bff, 0, 92);
      Marshal.FreeHGlobal(ptPoint);
      return bff;
    }

    • Edited by devil .net Friday, November 26, 2010 5:36 AM insert codes
    Friday, November 26, 2010 5:34 AM
  • Do you take into account that the bytes of the int values are reversed?

    Friday, November 26, 2010 8:38 AM