导出编组结构的正确方法

时间:2014-07-01 06:47:05

标签: c# marshalling

我正在编写一个托管DLL,它使用第三方本机DLL,其头部包含:

typedef struct {
   Bool isMounted;
   char *symbolicLink;
} VolumeInfo;

ErrorCode GetVolumeInfo(VolumeHandle handle, VolumeInfo **info);
void FreeVolumeInfo(VolumeInfo *info);

我把它整理成:

[StructLayout(LayoutKind.Sequential]
internal struct NativeVolumeInfo
{
    public bool IsMounted;
    public IntPtr SymbolicLink;
}

static extern ErrorCode GetVolumeInfo(IntPtr volumeHandle, out IntPtr volumeInfoPointer);
static extern void FreeVlumeInfo(IntPtr volumeInfoPointer);

理由是:
- VolumeInfo.SymbolicLink必须是IntPtr而不是string,因为它的大小是动态的,我不知道字符集是什么。此外,如果isMountedfalse,本机代码应该不设置,但情况并非总是如此,我有时会从非托管内存中得到胡言乱语。
- 我需要从本机获取指向volumeInfoHandle的指针,然后将其传回以供释放。因此,我不能简单地将托管结构作为out传递给GetVolumeInfo,并依靠编组器为我填充它。

我的目标是将此卷信息公开给我的DLL的最终用户。所以我有这个API类,它使用幕后的本机方法,并返回一个更加用户友好的结构:

public struct VolumeInfo
{
  public bool IsMounted {get;set;}
  public string SymbolicLink {get;set;}
}

public class PublicApi
{
  public VolumeInfo GetVolumeInfo(Volume volume)
  {
    IntPtr volumeInfoPointer;
    var errorCode = NativeMethods.GetVolumeInfo(volume.Handle, out volumeInfoPointer);
    if (errorCode == 0)
    {
      var nativeVolumeInfo = (NativeVolumeInfo)Marshal.PtrToStructure(volumeInfoPointer, typeof(NativeVolumeInfo));
      try
      {
        var volumeInfo = new VolumeInfo { IsMounted = nativeVolumeInfo.IsMounted };
        if (volumeInfo.IsMounted)
        {
          volumeInfo.SymbolicLink = Marshal.PtrToStringAnsi(nativeVolumeInfo.SymbolicLink);
        }
        return volumeInfo;
      }
      finally
      {
        NativeMethods.FreeVolumeInfo(volumeInfoPointer);
      }           
    }
    throw new InvalidOperationException("Didn't work");
  }
}

此时我担心,对于我需要手动编组的每个本机结构,我必须编写2个托管结构:一个用于编组,另一个用于导出到最终用户。

有没有办法让这更容易?

0 个答案:

没有答案