You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将非托管EC_VARIANT结构体转换为C#结构体?

非托管C++结构体EC_VARIANT转C#结构体问题

原C++结构体定义

typedef struct _EC_VARIANT {
  union {
    BOOL                            BooleanVal;
    UINT32                          UInt32Val;
    ULONGLONG                       DateTimeVal;
    LPCWSTR                         StringVal;
    PBYTE                           BinaryVal;
    BOOL                            *BooleanArr;
    INT32                           *Int32Arr;
    LPWSTR                          *StringArr;
    EC_OBJECT_ARRAY_PROPERTY_HANDLE PropertyHandleVal;
  };
  DWORD Count;
  DWORD Type;
} EC_VARIANT, *PEC_VARIANT;

我尝试的C#实现(未成功)

调用代码:

uint get = Internals.EcOpenSubscription("adfs", (uint)WecAcessTypes.EC_READ_ACCESS, (uint)WecFlags.EC_OPEN_EXISTING);
Console.WriteLine($"addr :{get}");
byte[] some = new byte[16];
uint a = 0;
bool success = Internals.EcGetSubscriptionProperty(get, (uint)EC_SUBSCRIPTION_PROPERTY_ID.EcSubscriptionEventSources, 0, 128, some, out a);
if (!success)
{
    Console.WriteLine("err EcSubscriptionEventSources");
    Console.ReadKey();
    return;
}
foreach (byte b in some)
{
    Console.Write(b);
}
GCHandle gCHandle = GCHandle.Alloc(some, GCHandleType.Pinned);
EC_VARIANT eC = (EC_VARIANT)Marshal.PtrToStructure(gCHandle.AddrOfPinnedObject(), typeof(EC_VARIANT));
Console.WriteLine($"\n{eC.Type} {eC.EC_OBJECT_ARRAY_PROPERTY_HANDLE}");
Console.WriteLine($"length {a}");
Console.ReadKey();

定义的C#结构体:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct EC_VARIANT
{
    public uint BooleanVal;
    public uint UInt32Val;
    public ulong DateTimeVal;
    public IntPtr StringVal;
    public IntPtr BinaryVal;
    public IntPtr BoolArr;
    public IntPtr Int32Arr;
    public IntPtr StringArr;
    public int EC_OBJECT_ARRAY_PROPERTY_HANDLE;
    public int Type;
    public int Count;
}

我曾尝试使用LayoutKind.Explicit调整字段布局,但仍无法解决问题,请求技术帮助。


正确的C#结构体转换方案

C++中的union需要用C#的LayoutKind.Explicit配合FieldOffset模拟,所有union成员共享同一块内存起始地址,同时要严格匹配字段类型和内存顺序:

// 先定义EC_OBJECT_ARRAY_PROPERTY_HANDLE的类型,适配Windows API的实际定义
public struct EC_OBJECT_ARRAY_PROPERTY_HANDLE
{
    public uint Value; // 若为64位平台也可改用IntPtr
}

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct EC_VARIANT
{
    // Union成员共享FieldOffset(0)
    [FieldOffset(0)]
    public int BooleanVal; // Windows BOOL是typedef int BOOL,对应C# int
    [FieldOffset(0)]
    public uint UInt32Val;
    [FieldOffset(0)]
    public ulong DateTimeVal;
    [FieldOffset(0)]
    public IntPtr StringVal;
    [FieldOffset(0)]
    public IntPtr BinaryVal;
    [FieldOffset(0)]
    public IntPtr BooleanArr;
    [FieldOffset(0)]
    public IntPtr Int32Arr;
    [FieldOffset(0)]
    public IntPtr StringArr;
    [FieldOffset(0)]
    public EC_OBJECT_ARRAY_PROPERTY_HANDLE PropertyHandleVal;

    // Union之后的字段,偏移为union的最大成员尺寸(8字节,对应ULONGLONG)
    [FieldOffset(8)]
    public uint Count; // DWORD对应C# uint
    [FieldOffset(12)]
    public uint Type; // DWORD对应C# uint
}

额外优化建议

  1. 类型匹配细节:Windows API中的DWORD对应C#uint,BOOL对应int,指针类型用IntPtr自动适配32/64位平台。
  2. 调用方式简化:无需用byte[]中转,直接分配结构体内存更可靠(需确保EcGetSubscriptionProperty的P/Invoke定义支持直接输出结构体):
uint get = Internals.EcOpenSubscription("adfs", (uint)WecAcessTypes.EC_READ_ACCESS, (uint)WecFlags.EC_OPEN_EXISTING);
EC_VARIANT variant = new EC_VARIANT();
uint size = (uint)Marshal.SizeOf(typeof(EC_VARIANT));
bool success = Internals.EcGetSubscriptionProperty(get, (uint)EC_SUBSCRIPTION_PROPERTY_ID.EcSubscriptionEventSources, 0, size, out variant, out size);
if (!success)
{
    Console.WriteLine("err EcSubscriptionEventSources");
    Console.ReadKey();
    return;
}
Console.WriteLine($"Type: {variant.Type}, PropertyHandle: {variant.PropertyHandleVal.Value}");
Console.WriteLine($"Length: {size}");
Console.ReadKey();

内容的提问来源于stack exchange,提问作者nikolya92

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 02:25:14