解读封送类、结构体和联合体实例
作者:C6666888
封送类、结构体和联合体实例
封送类
在.NET Framework中,类是引用类型,而结构体是值类型。对于类而言,它们只能通过COM互操作来封送,并且总是作为接口封送。
当托管类传递给COM时,互操作封送处理器会自动使用COM代理包装该类,并将由代理生成的类接口传递到COM方法调用。
例如:
// 假设有一个非托管的COM接口IDemoInterface
[ComImport]
[Guid("...")]
interface IDemoInterface {
void DoSomething();
}
// 定义一个实现该接口的托管类
public class ManagedClass : IDemoInterface {
public void DoSomething() {
Console.WriteLine("Doing something...");
}
}
// 在托管代码中创建并传递给非托管代码
var managedInstance = new ManagedClass();
// 这里假设有一个非托管函数接收IDemoInterface类型的参数
NativeMethods.PassToUnmanaged(managedInstance);这里PassToUnmanaged是一个平台调用(P/Invoke)定义的方法,它负责将ManagedClass对象转换成COM接口指针传递给非托管代码。
封送结构体
结构体作为值类型,在跨平台调用时也需要适当的封送处理。
为了确保结构体成员按照预期的方式被解释,通常会在结构体上应用StructLayoutAttribute属性来指定布局方式。
考虑如下C++定义的结构体:
typedef struct _MYPERSON {
char* first;
char* last;
} MYPERSON, *LP_MYPERSON;C#封送定义可能是这样的:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyPerson {
[MarshalAs(UnmanagedType.LPStr)]
public string first;
[MarshalAs(UnmanagedType.LPStr)]
public string last;
}这里使用了StructLayout特性指定了顺序布局,并通过MarshalAs特性指定了字符串字段如何被封送。
如果要传递此结构体到非托管代码,则可以像下面这样操作:
[DllImport("PinvokeLib.dll")]
static extern int TestStructInStruct([In] MyPerson2 person2);
// 构建MyPerson实例
var person = new MyPerson { first = "John", last = "Doe" };
// 创建包含person指针的MyPerson2实例
var person2 = new MyPerson2 { person = Marshal.AllocCoTaskMem(Marshal.SizeOf(person)), age = 30 };
// 复制person的内容到分配的非托管内存
Marshal.StructureToPtr(person, person2.person, false);
try {
// 调用非托管函数
var result = TestStructInStruct(person2);
} finally {
// 清理非托管资源
Marshal.FreeCoTaskMem(person2.person);
}这段代码展示了如何安全地管理非托管内存,并确保正确地封送结构体给非托管函数。
封送联合体
联合体(Union)允许在同一段内存空间内存储不同类型的数据成员。这意味着任何时候只有一个成员有效。
在C#中表示联合体通常涉及到使用StructLayout(LayoutKind.Explicit)特性以及FieldOffset特性来精确控制成员的位置。
比如,我们有以下C++定义的联合体:
union MYUNION {
int i;
double d;
};C#封送定义可以是:
[StructLayout(LayoutKind.Explicit)]
public struct MyUnion {
[FieldOffset(0)]
public int i;
[FieldOffset(0)]
public double d;
}这里使用了Explicit布局模式,所有字段都从偏移量0开始,意味着它们共享相同的内存位置。
当需要传递这个联合体到非托管代码时,可以直接使用上述定义,因为.NET运行时知道如何正确地封送联合体中的成员。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
