Let’s assume you have a user defined structure EMPLOYEE
in a Win32 DLL (let's
call it Win32Native.dll) as shown below.
typedef struct _EMPLOYEE
{
int Age ;
int Sex;
double Salary ;
char* FirstName ;
char* LastName ;
} EMPLOYEE;
Define some functions in the native DLL (let call it Win32Native.dll) as shown below.
extern "C" __declspec(dllexport) void ModifyArrayOfEmployeeStruct(int nSize, EMPLOYEE* pArray);
Implementing functions
extern "C" __declspec(dllexport) void ModifyArrayOfEmployeeStruct( int nSize, EMPLOYEE* pArray)
{
int result = 0;
EMPLOYEE* pCur = pArray;
STRSAFE_LPSTR temp1, temp2 ; for ( int i = 0; i < nSize; i++ )
{
size_t nLen1 = 0;
size_t nLen2 = 0;
StringCchLengthA( pCur->FirstName, STRSAFE_MAX_CCH, &nLen1 );
StringCchLengthA( pCur->LastName, STRSAFE_MAX_CCH, &nLen2 );
nLen1 = sizeof(char) * ( nLen1 + 11 ); nLen2 = sizeof(char) * ( nLen2 + 11 ); temp1 = (STRSAFE_LPSTR)CoTaskMemAlloc( nLen1 );
temp2 = (STRSAFE_LPSTR)CoTaskMemAlloc( nLen2 );
StringCchCopyA( temp1, nLen1, (STRSAFE_LPCSTR)"<modified>" );
StringCbCatA( temp1, nLen1, (STRSAFE_LPCSTR)pCur->FirstName);
StringCchCopyA( temp2, nLen2, (STRSAFE_LPCSTR)"<modified>" );
StringCbCatA( temp2, nLen2, (STRSAFE_LPCSTR)pCur->LastName);
CoTaskMemFree( pCur->FirstName );
CoTaskMemFree( pCur->LastName );
pCur->FirstName = (char *)temp1;
pCur->LastName = (char *)temp2;
pCur->Age += 1 ;
pCur->Salary += 1000.0 ;
pCur++;
}
}</modified></modified></modified></modified>
Points of interest
- CoTaskMemAlloc is used to allocated the memory required.
CoTaskMemFree
is used to free any previously allocated buffer, if null is passed then, CoTaskMemFree
is not called.
StringCchCopyA
copies an ANSI string to a string buffer.
StringCbCatA
Appends an ANSI string to a string buffer.
If you want to use a heap that is shared between native and managed, it is more common to use the COM heap. On the native side use
CoTaskMemAlloc()
and
CoTaskMemFree()
.
Writing the client code (the managed part)
We can simply create a console based application which can use this DLL. Let’s name it
MarshallingTest.
See the code snippet below.
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace MarshallingTest
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Employee
{
public int Age;
public int Sex;
public double Salary;
public String FirstName;
public String LastName;
}
class Program
{
[DllImport("Win32Native.dll")]
public static extern int ModifyArrayOfEmployeeStruct(
int nSize,
[In, Out] Employee[] empArr);
static void Main(string[] args)
{
int nCnt = 5; Employee[] emp = new Employee[nCnt];
emp[0].FirstName = "Ramesh"; emp[0].LastName = "Sharma";
emp[0].Age = 42; emp[0].Salary = 40000; emp[0].Sex = 0;
emp[1].FirstName = "Shalini"; emp[1].LastName = "Verma";
emp[1].Age = 30; emp[1].Salary = 25000; emp[1].Sex = 1;
emp[2].FirstName = "Ramesh"; emp[2].LastName = "Sharma";
emp[2].Age = 51; emp[2].Salary = 35000; emp[2].Sex = 0;
emp[3].FirstName = "Aarushi"; emp[3].LastName = "Shukla";
emp[3].Age = 25; emp[3].Salary = 20000; emp[3].Sex = 0;
emp[4].FirstName = "Malini"; emp[4].LastName = "Kapoor";
emp[4].Age = 33; emp[4].Salary = 30000; emp[4].Sex = 1;
Console.WriteLine("\nEmployee Array Before Call");
for (int nI = 0; nI < nCnt; nI++)
{
StringBuilder sb = new StringBuilder( "First Name=[" );
sb.Append(emp[nI].FirstName);
sb.Append("] Last Name=[");
sb.Append(emp[nI].LastName );
sb.Append("] Age=[");
sb.Append(emp[nI].Age .ToString ());
sb.Append("] Salary=[");
sb.Append(emp[nI].Salary.ToString ("F2"));
sb.Append("] Sex=[");
sb.Append(((emp[nI].Sex == 0) ? "Male" : "Female"));
sb.Append("]");
Console.WriteLine(sb.ToString ());
}
ModifyArrayOfEmployeeStruct(emp.Length, emp);
Console.WriteLine("\nEmployee Array After Call");
for (int nI = 0; nI < nCnt; nI++)
{
StringBuilder sb = new StringBuilder("First Name=[");
sb.Append(emp[nI].FirstName);
sb.Append("] Last Name=[");
sb.Append(emp[nI].LastName);
sb.Append("] Age=[");
sb.Append(emp[nI].Age.ToString());
sb.Append("] Salary=[");
sb.Append(emp[nI].Salary.ToString("F2"));
sb.Append("] Sex=[");
sb.Append(((emp[nI].Sex == 0) ? "Male" : "Female"));
sb.Append("]");
Console.WriteLine(sb.ToString());
}
}
}
}
Points of Interest
namespace System.Runtime.InteropServices;
defines the declarations necessary for Interop operations, like
DllImport.
DllImport
defines the DLL entry point.
Compile and execute you will get following output.