Modifying an array of structures (default marshaling)





5.00/5 (2 votes)
Modifying an array of structures.
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 ); // To accomodate ""
nLen2 = sizeof(char) * ( nLen2 + 11 ); // To accomodate ""
temp1 = (STRSAFE_LPSTR)CoTaskMemAlloc( nLen1 );
temp2 = (STRSAFE_LPSTR)CoTaskMemAlloc( nLen2 );
StringCchCopyA( temp1, nLen1, (STRSAFE_LPCSTR)"" );
StringCbCatA( temp1, nLen1, (STRSAFE_LPCSTR)pCur->FirstName);
StringCchCopyA( temp2, nLen2, (STRSAFE_LPCSTR)"" );
StringCbCatA( temp2, nLen2, (STRSAFE_LPCSTR)pCur->LastName);
// CoTaskMemFree must be used instead of delete to free memory.
CoTaskMemFree( pCur->FirstName );
CoTaskMemFree( pCur->LastName );
pCur->FirstName = (char *)temp1;
pCur->LastName = (char *)temp2;
pCur->Age += 1 ;
pCur->Salary += 1000.0 ;
pCur++;
}
}
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; // Number of Items in Structure Array
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 ());
}
// Call the Function.
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.