Monday, September 12, 2011

Passing structures between C# and C/C++

Data structures can be passed between managed C# code and unmanaged C/C++ dynamic link libraries but it can be a little complicated. The trick is to define structures that match between the two different languages; and perhaps write wrapper functions that hide away the complexity of the memory marshaling code. I will show a couple of simple examples of passing a system time data structure from C/C++ to C# and vice versa here.

The C/C++ definition of the system time structure is as follows:

typedef struct _SYSTEMTIME {
    WORD wYear;
    WORD wMonth;
    WORD wDayOfWeek;
    WORD wDay;
    WORD wHour;
    WORD wMinute;
    WORD wSecond;
    WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;

The matching C# equivalent of the system time structure can be defined as shown below.

private struct SYSTEMTIME
{
    public UInt16 wYear;
    public UInt16 wMonth;
    public UInt16 wDayOfWeek;
    public UInt16 wDay;
    public UInt16 wHour;
    public UInt16 wMinute;
    public UInt16 wSecond;
    public UInt16 wMilliseconds;
}

Example 1: Passing a structure from C/C++ to C#
If the C/C++ function has the following signature:

HRESULT GetDateTime( HANDLE hClient, SYSTEMTIME* pSysTime );

Then the following wrapper functions can be used to call the C/C++ function.

[DllImport("MyDynamicLinkLib.dll")]
private static extern int GetDateTime(IntPtr hClient, IntPtr pSysTime);
public static int GetDateTime(IntPtr hClient, out DateTime sysTime)
{
    int hr = 0;
    IntPtr pSysTime = IntPtr.Zero;
    SYSTEMTIME theTime = new SYSTEMTIME();
    sysTime = new DateTime();
    pSysTime = Marshal.AllocHGlobal( Marshal.SizeOf(theTime));
    hr = GetDateTime(hClient, pSysTime);
    theTime = (SYSTEMTIME) Marshal.PtrToStructure(pSysTime, typeof(SYSTEMTIME));
    Marshal.FreeHGlobal(pSysTime);
    sysTime = new DateTime(theTime.wYear, theTime.wMonth, theTime.wDay, theTime.wHour, theTime.wMinute, theTime.wSecond, theTime.wMilliseconds);
    return hr;
}

To use the wrapper functions, simply do something like the following:

IntPtr hClient = IntPtr.Zero;
DateTime dt = new DateTime();
int ret = GetDateTime( hClient, out dt);

Example 2: Passing a structure from C# to C/C++
If the C/C++ function has the following signature,

HRESULT SetDateTime( HANDLE hClient, SYSTEMTIME* pSysTime );

Then the following wrapper functions can be used to call the C/C++ function.

[DllImport("MyDynamicLinkLib.dll")]
private static extern int SetDateTime(IntPtr hClient, IntPtr pSysTime);
public static int SetDateTime(IntPtr hClient, DateTime sysTime)
{
    int hr = 0;
    IntPtr pSysTime = IntPtr.Zero;
    SYSTEMTIME theTime = new SYSTEMTIME();
    theTime.wYear = (ushort) sysTime.Year;
    theTime.wMonth = (ushort) sysTime.Month;
    theTime.wDay = (ushort) sysTime.Day;
    theTime.wDayOfWeek = (ushort) sysTime.DayOfWeek;
    theTime.wHour = (ushort) sysTime.Hour;
    theTime.wMinute = (ushort) sysTime.Minute;
    theTime.wSecond = (ushort) sysTime.Second;
    theTime.wMilliseconds = (ushort) sysTime.Millisecond;
    pSysTime = Marshal.AllocHGlobal(Marshal.SizeOf(theTime));
    hr = SetDateTime(hClient, pSysTime);
    Marshal.FreeHGlobal(pSysTime);
    return hr;
}

Do the following to call the wrapper functions.
DateTime dt = new DateTime();
int hr = SetDateTime(hClient, dt);
 

3 comments:

Sarkaar said...

Could you please provide a sample code for this one..

sandhya said...

Could you please explain how exactly we can share structure (user defined struct) from c# to c++ using C++/CLI.

Tolaemon said...

Wouldn't it be something like this ?:
"...
pSysTime = Marshal.AllocHGlobal(Marshal.SizeOf(theTime));
Marshal.StructureToPtr(theTime, pSysTime, false);
hr = SetDateTime(hClient, pSysTime);
..."