我正在编写一个托管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
,因为它的大小是动态的,我不知道字符集是什么。此外,如果isMounted
是false
,本机代码应该不设置,但情况并非总是如此,我有时会从非托管内存中得到胡言乱语。
- 我需要从本机获取指向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个托管结构:一个用于编组,另一个用于导出到最终用户。
有没有办法让这更容易?