C++ union in C# — weird behaviour - c#

I am trying to create some vhd/vhdx files using the VHD API in C#.
There's a C++ union that looks like this:
typedef struct _CREATE_VIRTUAL_DISK_PARAMETERS
{
CREATE_VIRTUAL_DISK_VERSION Version;
union
{
struct
{
GUID UniqueId;
ULONGLONG MaximumSize;
ULONG BlockSizeInBytes;
ULONG SectorSizeInBytes;
PCWSTR ParentPath;
PCWSTR SourcePath;
} Version1;
struct
{
GUID UniqueId;
ULONGLONG MaximumSize;
ULONG BlockSizeInBytes;
ULONG SectorSizeInBytes;
ULONG PhysicalSectorSizeInBytes;
PCWSTR ParentPath;
PCWSTR SourcePath;
OPEN_VIRTUAL_DISK_FLAG OpenFlags;
VIRTUAL_STORAGE_TYPE ParentVirtualStorageType;
VIRTUAL_STORAGE_TYPE SourceVirtualStorageType;
GUID ResiliencyGuid;
} Version2;
struct
{
GUID UniqueId;
ULONGLONG MaximumSize;
ULONG BlockSizeInBytes;
ULONG SectorSizeInBytes;
ULONG PhysicalSectorSizeInBytes;
PCWSTR ParentPath;
PCWSTR SourcePath;
OPEN_VIRTUAL_DISK_FLAG OpenFlags;
VIRTUAL_STORAGE_TYPE ParentVirtualStorageType;
VIRTUAL_STORAGE_TYPE SourceVirtualStorageType;
GUID ResiliencyGuid;
PCWSTR SourceLimitPath;
VIRTUAL_STORAGE_TYPE BackingStorageType;
} Version3;
};
} CREATE_VIRTUAL_DISK_PARAMETERS, *PCREATE_VIRTUAL_DISK_PARAMETERS;
I'm trying to convert that to C#, but not having much luck. I'm not interested in Version3 at all, so am leaving that out.
I've tried a number of things and the best I could get to was having Version2 working (by doing something really bizarre), but I've never managed to get Version1 and Version2 working at the same time.
The solution that has wielded the best results so far has been this, but there has to be something wrong there because Version1 simply doesn't work, and SectorSizeInBytes in Version1 is a ulong rather than uint (if I change it to uint like it should be, I break Version2 and Version1 still doesn't work!)
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParameters
{
[FieldOffset(0)] public CreateVirtualDiskParametersVersion1 Version1;
[FieldOffset(0)] public CreateVirtualDiskParametersVersion2 Version2;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParametersVersion1
{
public CreateVirtualDiskVersion Version;
public Guid UniqueId;
public ulong MaximumSize;
public uint BlockSizeInBytes;
public ulong SectorSizeInBytes;
public string ParentPath;
public string SourcePath;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParametersVersion2
{
public CreateVirtualDiskVersion Version;
public Guid UniqueId;
public ulong MaximumSize;
public uint BlockSizeInBytes;
public uint SectorSizeInBytes;
public uint PhysicalSectorSizeInBytes;
public string ParentPath;
public string SourcePath;
public OpenVirtualDiskFlags OpenFlags;
public VirtualStorageType ParentVirtualStorageType;
public VirtualStorageType SourceVirtualStorageType;
public Guid ResiliencyGuid;
}
I know theoretically the Version field should be set outside the Version structs and I have tried that as well, but it just breaks things even more funnily enough...
So, can someone advise how to properly translate the above to C#, leaving out the Version3 struct as that's not needed?

Using Pack = 1 to StructLayout attributes eliminates any padding between struct members.
In TCP connections structs are usually passed around without padding so that all programs using the struct can agree on its layout in memory.
However as #David Heffernan pointed out, that may not be the case when passing structs to Windows DLL's. I didn't test the actual call to CreateVirtualDisk because it seemed a bit risky, given that I haven't used this call before and didn't want to clobber my disk if I made a mistake. It looks as if the default packing of 8 bytes (Pack = 0 for default or Pack = 8) may be the correct setting, based on the following quote.
See 64-bit Windows API struct alignment caused Access Denied error on named pipe
The Windows SDK expects packing to be 8 bytes. From Using the Windows Headers
Projects should be compiled to use the default structure packing, which is currently 8 bytes because the largest integral type is 8 bytes. Doing so ensures that all structure types within the header files are compiled into the application with the same alignment the Windows API expects. It also ensures that structures with 8-byte values are properly aligned and will not cause alignment faults on processors that enforce data alignment.
Version is moved to the top of CreateVirtualDiskParameters.
The two unions then follow. Both have the same offset sizeof(CREATE_VIRTUAL_DISK_VERSION).
Also SectorSizeInBytes is uint rather than ulong.
You can let the marshaller do the work of filling string members using the attribute, eg
[MarshalAs(UnmanagedType.LPWStr)] public string ParentPath;
Or, you can represent it as it appears in memory, which is a pointer to a Unicode string:
public IntPtr ParentPath;
and then extract the string yourself with
Marshal.PtrToStringAuto(vdp.Version1.ParentPath)
If you're passing the C# struct to an external DLL, populate it with an unmanaged string
vdp.Version1.ParentPath = (IntPtr)Marshal.StringToHGlobalAuto("I am a managed string");
then free the unmanaged string when you're finished with it
Marshal.FreeHGlobal(vdp.Version1.ParentPath);
Try this.
public enum CREATE_VIRTUAL_DISK_VERSION
{
CREATE_VIRTUAL_DISK_VERSION_UNSPECIFIED = 0,
CREATE_VIRTUAL_DISK_VERSION_1 = 1,
CREATE_VIRTUAL_DISK_VERSION_2 = 2
};
public enum OPEN_VIRTUAL_DISK_FLAG
{
OPEN_VIRTUAL_DISK_FLAG_NONE = 0x00000000,
OPEN_VIRTUAL_DISK_FLAG_NO_PARENTS = 0x00000001,
OPEN_VIRTUAL_DISK_FLAG_BLANK_FILE = 0x00000002,
OPEN_VIRTUAL_DISK_FLAG_BOOT_DRIVE = 0x00000004,
OPEN_VIRTUAL_DISK_FLAG_CACHED_IO = 0x00000008,
OPEN_VIRTUAL_DISK_FLAG_CUSTOM_DIFF_CHAIN = 0x00000010
};
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)]
public struct VIRTUAL_STORAGE_TYPE
{
uint DeviceId;
Guid VendorId;
};
[StructLayout(LayoutKind.Explicit, Pack = 8, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParameters
{
[FieldOffset(0)]
public CREATE_VIRTUAL_DISK_VERSION Version;
[FieldOffset(8))]
public CreateVirtualDiskParametersVersion1 Version1;
[FieldOffset(8))]
public CreateVirtualDiskParametersVersion2 Version2;
}
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParametersVersion1
{
public Guid UniqueId;
public ulong MaximumSize;
public uint BlockSizeInBytes;
public uint SectorSizeInBytes;
//public IntPtr ParentPath; // PCWSTR in C++ which is a pointer to a Unicode string
//public IntPtr SourcePath; //string
[MarshalAs(UnmanagedType.LPWStr)] public string ParentPath;
[MarshalAs(UnmanagedType.LPWStr)] public string SourcePath;
}
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)]
public struct CreateVirtualDiskParametersVersion2
{
public Guid UniqueId;
public ulong MaximumSize;
public uint BlockSizeInBytes;
public uint SectorSizeInBytes;
public uint PhysicalSectorSizeInBytes;
//public IntPtr ParentPath; //string
//public IntPtr SourcePath; //string
[MarshalAs(UnmanagedType.LPWStr)] public string ParentPath;
[MarshalAs(UnmanagedType.LPWStr)] public string SourcePath;
public OPEN_VIRTUAL_DISK_FLAG OpenFlags;
public VIRTUAL_STORAGE_TYPE ParentVirtualStorageType;
public VIRTUAL_STORAGE_TYPE SourceVirtualStorageType;
public Guid ResiliencyGuid;
}

Related

AccessViolationException when calling C dll from C#

I'm trying to create a wrapper for a C dll to use it in C#. I don't have the source code of the dll.
I'm having problem using a method that gives me "System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt'" when executing. I tried to search online for the same problem but I didn't find what the problem with my code is.
The method that gives the problem is the following:
C:
L3B6_API L3B6_ERR_t STDCALL L3B6_ActivateBoot(int nodeid, unsigned cpunum, unsigned ms_timeout, const char * devicenames, A * dev_param);
the wrapper I created in C# is this:
[DllImport("L3B6.dll", EntryPoint = "L3B6_ActivateBoot")]
return: MarshalAs(UnmanagedType.I4)]
static extern L3B6ErrorCode ActivateBoot(
int nodeid,
uint cpunum,
uint msTimeout,
[MarshalAs(UnmanagedType.LPStr)]
string deviceNames,
ref A devParams
);
Where L3B6ErrorCode is an enum I created but it doesn't give any problem (I used it for other methods that work).
I think the problem is in the structure A. The original structure is like this:
struct A
{
unsigned char blv;
unsigned char cpunum;
unsigned char nodeid;
unsigned char hwverA;
unsigned char hwverB;
unsigned char hwverC;
unsigned char hwverD;
unsigned char cputype;
unsigned char hwcode;
B memini_devrec;
};
struct B
{
unsigned hwCode;
unsigned cpuCode;
unsigned dualCPU;
unsigned cpuNumber;
unsigned internalFlashStart;
unsigned internalFlashEnd;
unsigned externalFlashAccess;
unsigned externalFlashStart;
unsigned externalFlashEnd;
unsigned isResetVectorSpecified;
unsigned resetVectors;
char bootName[32];
unsigned isS19toPhyConversionAllowed;
unsigned noFlashPaging;
char deviceName[32];
char cpuName[32];
};
The corresponding structures I created are the following:
[StructLayout(LayoutKind.Sequential)]
public struct A
{
public byte bootloaderversion;
public byte cpuNum;
public byte nodeId;
public byte hwVerA;
public byte hwVerB;
public byte hwVerC;
public byte hwVerD;
public byte cpuType;
public byte hwCode;
public B meminiDeviceRecord;
};
[StructLayout(LayoutKind.Sequential)]
public struct B
{
public uint hwCode;
public uint cpuCode;
[MarshalAs(UnmanagedType.Bool)]
public bool dualCPU;
public uint cpuNumber;
public uint internalFlashStart;
public uint internalFlashEnd;
public uint externalFlashAccess;
public uint externalFlashStart;
public uint externalFlashEnd;
[MarshalAs(UnmanagedType.Bool)]
public bool isResetVectorSpecified;
public uint resetVectors;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string bootName;
[MarshalAs(UnmanagedType.Bool)]
public bool isS19toPhyConversionAllowed;
[MarshalAs(UnmanagedType.Bool)]
public bool noFlashPaging;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string deviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string cpuName;
};
I've used the structure B in another method that needed to fill it and didn't have any problem, so I suppose the problem is in the A structure, but I can't understand what I'm doing wrong. I think the problem is that structure because also another method that takes that structure as parameter is giving me the same exception. I checked the size of the structs in C and C# and it is the same.

How can I get struct char[] field value use DllImport function?

In DLL struct is:
typedef struct tagEKIDinfo{
short usbNo;
short printerID;
CHAR serialNo[6];
WORD mediaType;
} EKIDinfo, *PEKIDinfo;
In C# :
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EKIDinfo
{
public int usbNo;
public int printerID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string serialNo;
public ushort mediaType;
}
And the function is:
DLL:
DWORD WINAPI EKSearchPrinters( PEKIDinfo pIDInfo, DWORD infoSize, LPDWORD pSizeNeeded, LPDWORD pinfoNum )
C#:
[DllImport("EKUSB.dll", EntryPoint = "EKSearchPrinters", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int EKSearchPrinters(IntPtr hPrinter, Int32 infoSize, ref Int32 pSizeNeeded, ref Int32 pNumber);
The serialNo true value is "218699", but I got "99?" with C#.
Why about this? Can anyone help me? Thans a lot!
Kind of a guess, but if you're off by four bytes, maybe the first two fields are too big? Try this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EKIDinfo
{
public System.Int16 usbNo; //<--- changed
public System.Int16 printerID; //<--- changed
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string serialNo;
public ushort mediaType;
}
I think you've got wrong types. DLL interface says
struct tagEKIDinfo {
short usbNo; <- short (2 bytes), not int (4bytes)
short printerID; <- short (2 bytes), not int (4bytes)
CHAR serialNo[6];
WORD mediaType;
}
Note the short. This is likely to be int16 on C# side, not int as you have now. Since there are 2 such fields, all before char-array, 2x(4-2)=4 and that would account exactly to the 218699->99 = 4 chars missing
The struct is declared incorrectly. The first two members should be short.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EKIDinfo
{
public short usbNo;
public short printerID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string serialNo;
public ushort mediaType;
}
Rather than manually marshal the struct I'd pass it by reference.
[DllImport("EKUSB.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint EKSearchPrinters(
ref EKIDinfo pIDInfo,
uint infoSize,
ref uint pSizeNeeded,
ref uint pNumber
);
I'm not sure why you named the first parameter to suggest a handle, but it certainly doesn't look like one to me.
It's entirely possible that some of the ref parameters should be out depending on the data flow intent which has not been shown.
The other potential issue is how you call the function. You didn't show the call or any details of how it must be done.

C# <-> C++ : Marshaling an [In,Out] struct containing Char[]

I have a C++ .dll (unable to make changes to the code) from which I am trying to call a function that takes a reference parameter of a Struct type (also defined in the .dll).
The function requires the Struct to have the 'paramName' and 'groupName' fields populated, and based on those fields, it will return the Struct with the remaining fields populated.
I am not getting any marshaling errors, however, the library call returns an error code and a debug log shows that it is receiving an empty string for the two string fields (see below for how fields are set). My assumption is that the size of this struct is not aligning between the managed and unmanaged representation, and thus the struct is not blittable.
Here is the C++ method signature:
int GetConfigs(int contextHandle, Configs* configs);
And the C++ Configs struct:
struct Configs {
int myInt;
float myFloat;
bool flag;
char name[64];
char group[64];
}
The C# function wrapper:
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int GetConfigs(int contextHandle, [MarshalAs(UnmanagedType.Struct), In, Out] ref Configs configs);
The C# struct definition:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Configs
{
public int myInt;
public float myFloat;
[MarshalAs(UnmanagedType.U1)]
public bool flag;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string group;
}
As per the Microsoft documentation I have declared the C++ char[]'s to be represented in C# by strings and marshaled as ByValTStr with a set size.
Also from Microsoft documentation:
bool is not blittable, so I mark it with an explicit MarshalAs.
float is also not blittable, but declaring these fields as a float or a double made no difference in the original issue.
Lastly, the C# calling code:
var configs = new Library.Configs
{
name = "testName",
group = "testGroup"
};
var returnCode = Library.GetConfigs(GetContextId(), ref configs);
The return code comes back as a failure code and the library debug output file shows that the struct argument had:
name = []
(name is clearly set to what I expect it to be at the time of the call when I debug through the C# code)
Instead of declaring the string fields as
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name;
I tried:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] name;
but that also made no difference.
The float/double was what was throwing off the struct byte size- the library was expecting a 4 byte floating point number but the data marshaler by default keeps those as 8 bytes. Fixing that along with the boolean as mentioned in the comments above solved the problem:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Configs
{
public int myInt;
[MarshalAs(UnmanagedType.R4)]
public float myFloat;
[MarshalAs(UnmanagedType.U1)]
public bool flag;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string group;
}

How to convert a C++ Struct with Union into C#?

Guys I am having difficulties on retrieving struct member values after calling a function in the DLL. I tried to convert the C++ codes into C# but I’m not sure if it is correct or not. Please help me understand my mistakes here (if there is) and how to correct.
My problem here is I can’t correctly retrieved the values of the INNER STRUCTS (Union) after I called the ReceiveMessage function from the DLL. Like for example m_objMsg.MsgData.StartReq.MsgID is always 0.
But when I try to use the C++ .exe program, the MsgID has a correct value. (not 0)
C++ Code:
extern int ReceiveMessage(SESSION, int, Msg*);
typedef struct
{
char SubsId[15];
int Level;
char Options[12];
} ConxReq;
typedef struct
{
char MsgId[25];
} StartReq;
typedef struct
{
long Length;
short Type;
union
{
ConxReq oConxReq;
StartReq oStartReq;
} Data;
} Msg;
/////////////////////////////////////////////////////
Msg oMsg;
int rc=ReceiveMessage(Session, 0, &oMsg);
switch(rc)
{
case 0:
switch(oMsg.Type)
{
case 0: // ConxReq
…
break;
case 1: // StartReq
…
break;
…
}
And here is my attempt to convert this into c#:
[DllImport("MyDLL.dll",
CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
protected static extern Int32 ReceiveMessage(IntPtr session,
Int32 nTimeOut,
[MarshalAs(UnmanagedType.Struct)] ref Msg ptrMsg);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public StartReq oStartReq;
}
Msg m_objMsg = new Msg();
m_objMsg.MsgData = new Data();
m_objMsg.MsgData.oConxReq = new ConxReq();
m_objMsg.MsgData.oStartReq = new StartReq();
int rc = ReceiveMessage(m_Session, nTimeOut, ref m_objMsg);
then the SWITCH Condition
And If I add this struct inside the UNION for c++ and c#...
I've got an error stating the "... incorrectly align" or "...overlapped..."
c++
ConxNack oConxNack;
typedef struct
{
int Reason;
} ConxNack;
[StructLayout(LayoutKind.Sequential)]
public struct ConxNack
{
public int nReason;
}
[FieldOffset(0)]
public ConxNack oConxNack;
Thank you so much in advance for your time and help...
Akash is right, have a look here: http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/60150e7b-665a-49a2-8e2e-2097986142f3
Another option is create two structs and use an appropriate cast once you know which type it is.
hth
Mario
In C++, we know that all members of UNION shared the same memory chunk and can only have one member of an object at a time.
In order to implement this in C#, we need to use the LayoutKind to Explicit and set all the starting point of each member to 0.
In my previous example, An error message is displayed stating that the offset of an object type is incorrectly aligned or overlapped by a non-object type.
Answer is we cannot set all the members to FieldOffSet to 0 since it is not allowed to combine the reference type with the value type.
- Thanks to the explanation of Hans Passant
What I did is to create a copy of the UNION Member Structs and change the type of all the String Member Variables to bytes.
I used bytes since this is a value type so I can put this struct into FieldOffSet(0).
Take note, i adjust the FieldOffSet of the next member variable so i can still get the same size of my string variable.
And also for the struct size since i have byte member at the last.
Thanks to Akash Kava and Mario The Spoon for giving me an idea and providing me a useful link.
After calling the function in the DLL and passed this Struct Obj (ref m_objMsg) as a paramter, I need to extract the values.
One way is to have a pointer that points to the address of the struct in the unmanaged memory and convert this pointer a new
Struct with the corresponding member variables (my original structs).
NEW STRUCTS (BYTES)
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 31)]
public struct ConxReq
{
[FieldOffSet(0)]
public byteSubsId;
[FieldOffSet(15)]
public Int32 Level;
[FieldOffSet(19)]
public byte Options;
}
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct ConxNack
{
[FieldOffSet(0)]
public int nReason;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 25)]
public struct StartReq
{
[FieldOffSet(0)]
public byte MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public ConxNack oConxNack;
[FieldOffset(0)]
public StartReq oStartReq;
}
////////////////////////////////////////////////////////////////
MY ORIGINAL STRUCTS
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyConxNack
{
public int nReason;
}
///////////////////////////////////////////////////////////////
Since I have a Msg.Type, i know what kind of struct (type) I could cast the object.
Like for example
ReceiveMessage(m_Session, nTimeOut, ref oMsg);
switch (oMsg.Type)
{
case 0: // ConxReq
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(oMsg.MsgData.ConxReq); // use the new struct (bytes)
Marshal.StructureToPtr(oMsg.MsgData.ConxReq, ptr, false);
MyConxReq oMyConxReq = new MyConxReq;
oMyConxReq = (MyConxReq) Marshal.PtrToStructure(ptr, typeof(MyConxReq)); // convert it to the original struct
Marshal.FreeHGlobal(ptr);
Then you can use now the oMyConxReq object to acccess the member variables directly.
Please let me know if you have other or better way to do this...
Kindly advise if what I did is correct or if I missed something.
Thank you so much!!! :)
You have to use StructLayout(LayoutKind.Explicit) and FieldOffsets to make union.

Converting this C signature to C# for P/Invoke

I have the following C function:
int w_ei_connect_init(ei_cnode* ec, const char* this_node_name,
const char *cookie, short creation);
ei_cnode looks like this:
typedef struct ei_cnode_s {
char thishostname[EI_MAXHOSTNAMELEN+1];
char thisnodename[MAXNODELEN+1];
char thisalivename[EI_MAXALIVELEN+1];
char ei_connect_cookie[EI_MAX_COOKIE_SIZE+1];
short creation;
erlang_pid self;
} ei_cnode;
Which I have converted to C#:
[StructLayout(LayoutKind.Sequential)]
public struct cnode {
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_HOSTNAME_LEN + 1)]
public string thishostname;
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_NODE_LEN + 1)]
public string thisnodename;
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_ALIVE_LEN + 1)]
public string thisalivename;
[MarshalAsAttribute(UnmanagedType.ByValTStr,
SizeConst = Ei.MAX_COOKIE_SIZE + 1)]
public string ei_connect_cookie;
public short creation;
public erlang_pid self;
}
I'm not good with pointers or C in general, so I'm not sure how I'm supposed to supply a cnode to ei_connect_init.
What would the equivalent C# signature be for the C function above?
Whenever you want to pass a C# struct to a parameter value containing the equivalent native struct but with a pointer, the typical method is to label the parameter as "ref". This causes the PInvoke layer to essentially pass the address in.
[DllImport("somedll")]
public static extern w_ei_connect_init(
ref cnode v,
[In] string this_node_name,
[In] string cookie,
int16 creation);
Something like this should work:
int w_ei_connect_init(ref cnode ec,
[MarshalAs(UnmanagedType.LPStr)] string this_node_name,
[MarshalAs(UnmanagedType.LPStr)] string cookie, short creation);
You should also consider marking your struct with
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
attribute, so those TStr will be ansi-strings, not unicode.

Categories

Resources