nikolya92
nikolya92

Reputation: 3

marshall unmanaged structure to C#

how can I translate this unmanaged structure into 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;

docs: https://learn.microsoft.com/ru-ru/windows/win32/api/evcoll/ns-evcoll-ec_variant i try this code:

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();

my struct:

[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;
}

I tried to use LayoutKind.Explicit and arrange the fields differently but to no avail

Upvotes: -1

Views: 91

Answers (2)

Simon Mourier
Simon Mourier

Reputation: 139187

The equivalent C# structure would be this:

    public partial struct EC_VARIANT
    {
        [StructLayout(LayoutKind.Explicit)]
        public struct EC_VARIANT_UNION
        {
            [FieldOffset(0)]
            public bool BooleanVal;

            [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 IntPtr PropertyHandleVal;
        }

        public EC_VARIANT_UNION Anonymous;
        public uint Count;
        public uint Type;
    }

Upvotes: 1

Serg
Serg

Reputation: 4696

You can use the CsWin32 source generator to get all the definitions for related functions/structs/enums/etc.

How to use:

  • install the CsWin32 nuget package: dotnet add package Microsoft.Windows.CsWin32 or Install-Package Microsoft.Windows.CsWin32

  • add the NativeMethods.txt file in the root of the project (in the same folder as the .csproj).

  • Place the following content in the NativeMethods.txt:

    EC_VARIANT
    EcGetSubscriptionProperty
    EcOpenSubscription
    EC_*
    EC_VARIANT_TYPE
    

    This will tell the generator to generate definitions for the all listed entities.

  • use in your code as following (I rewrite your sample and slightly simplified it).

      using Windows.Win32.System.EventCollector;
      using static Windows.Win32.PInvoke;
    
      var get = EcOpenSubscription("adfs", EC_READ_ACCESS, EC_OPEN_EXISTING);
      Console.WriteLine($"addr :{get}");
      var some = new EC_VARIANT();
      var success = EcGetSubscriptionProperty(get,
          EC_SUBSCRIPTION_PROPERTY_ID.EcSubscriptionEventSources, 0, 128,
          ref some, out var used);
    
      if (!success)
      {
          Console.WriteLine("Failed to get subscription property");
          return;
      }
    
      Console.WriteLine($"Type: {some.Type}");
      var union = some.Anonymous;
      var strVal = (EC_VARIANT_TYPE)some.Type switch
      {
          EC_VARIANT_TYPE.EcVarTypeNull => "null",
          EC_VARIANT_TYPE.EcVarTypeBoolean => union.BooleanVal.ToString(),
          EC_VARIANT_TYPE.EcVarTypeUInt32 => union.UInt32Val.ToString(),
          EC_VARIANT_TYPE.EcVarTypeDateTime => union.DateTimeVal.ToString(),
          EC_VARIANT_TYPE.EcVarTypeString => union.StringVal.ToString(),
          EC_VARIANT_TYPE.EcVarObjectArrayPropertyHandle => union.PropertyHandleVal.ToString(),
          _ => throw new ArgumentOutOfRangeException()
      };
      Console.WriteLine($"Value: {strVal}");
    

Note: I'm not sure that the parameters for the EcGetSubscriptionProperty are correct. The most suspicious is the 128 as the buffer size. You may try to use the Marshal.SizeOf(some) or something similar instead.

Upvotes: 0

Related Questions