Click here to Skip to main content
15,881,559 members
Articles / Programming Languages / C#

Modifying an array of structures (default marshaling)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
14 Aug 2013CPOL 9.4K   2  
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.

C++
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.

C#
extern "C" __declspec(dllexport) void ModifyArrayOfEmployeeStruct(int nSize, EMPLOYEE* pArray);

Implementing functions

C#
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 "<modified>" 
        nLen2 = sizeof(char) * ( nLen2 + 11 );    //    To accomodate "<modified>" 
        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 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++; 
   } 
}</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.

C#
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.

image

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
India India
More than 10 years of experience in designing and development of GUIs and Middleware for industrial control systems.

Comments and Discussions

 
-- There are no messages in this forum --