Praveen Katiyar' Blog

Learning along the way . . . .

Modifying array of structures (default marshaling)

Let’s assume you have a user defined structure “EMPLOYEE” in a win32 DLL (let say it “Win32Native.dll”) as shown below.

typedef struct _EMPLOYEE
{
    int Age ;
    int Sex;
    double Salary ;
    char* FirstName ;
    char* LastName ;
} EMPLOYEE;

Define some functions in native dll (let say 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 "<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++;
   }
}

Point 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)

    one can simple create a console base 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());
                }

            }
        }
    }

    Point 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

  • August 6, 2013 Posted by | .NET, CodeProject, Interoperability, Marshaling, Win32 | , , , , | Leave a comment

    Modifying array of numeric types (default Marshaling)

    Define some functions in native dll (let say it “Win32Native.dll”)  as shown below. These 2 functions receives integer buffer, and double buffer respectively.

    extern "C" __declspec(dllexport) int ModifyIntegerArrayValues ( int nSize, int* pArray)

    extern "C" __declspec(dllexport) double ModifyDoubleArrayValues ( int nSize, double* pArray)

    Implementing Functions

    extern "C" __declspec(dllexport) int ModifyIntegerArrayValues ( int nSize, int* pArray)
    {
        int nResult = 0 ;
        for ( int i = 0; i < nSize; i++ )
        {
            nResult += pArray[ i ];
            pArray[i] += 100;
        }
        return nResult;
    }

    extern "C" __declspec(dllexport) double ModifyDoubleArrayValues ( int nSize, double* pArray)
    {
        double dblResult = 0 ;
        for ( int i = 0; i < nSize; i++ )
        {
            dblResult += pArray[ i ];
            pArray[i] += 200 ;
        }
        return dblResult ;
    }

    Point 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.

    If you want to use a heap that is shared between native and managed, it is more common to use the COM heap.

    Writing the client code (the managed part)

    one can simple create a console base 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
    {
        class Program
        {
             [DllImport("Win32Native.dll")]
            public static extern void ModifyIntegerArrayValues(int nCount, [In, Out] int[] arrStr);

             [DllImport("Win32Native.dll")]
             public static extern int ModifyDoubleArrayValues(int nSize, [In, Out] double[] nArr);
        
            static void Main(string[] args)
            {
                int nSize = 5 ;
                int[] arrInt = new int[nSize];
                for (int nI = 0; nI < nSize; nI++) arrInt[nI] = (nI + 1) * 10;

                Console.WriteLine("\nValues of Integer array, before calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1:D3}", i, arrInt[i]));
                }

                ModifyIntegerArrayValues(nSize, arrInt);

                Console.WriteLine("\nValues of Integer array, after calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1:D3}", i, arrInt[i]));
                }

                double [] arrDbl= new double [nSize];
                for (int nI = 0; nI < nSize; nI++) arrDbl[nI] = (nI + 1) * 5.0 ;

                Console.WriteLine("\nValues of Double array, before calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1:F2}", i, arrDbl[i]));
                }

                ModifyDoubleArrayValues(nSize, arrDbl);

                Console.WriteLine("\nValues of double array, after calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1:F2}", i, arrDbl[i]));
                }
            }
        }
    }

    Point of Interest
    • namespace System.Runtime.InteropServices;  defines the declarations necessary for Interop operations, like DllImport,
    • DllImport defines the DLL entry point.
    • Marshal.Copy function used to copy buffer from managed buffer to unmanaged buffer and vice versa.
    • Marshal.FreeCoTaskMem frees the memory allocated by native DLL.

    compile and execute you will get following output.

    image

    August 5, 2013 Posted by | .NET, CodeProject, Interoperability, Marshaling, Win32 | , , , | Leave a comment

    Fetching buffer of numeric types from a Win32 DLL

    Define some functions in native dll (let say it “Win32Native.dll”)  as shown below. These 3 functions receives integer buffer, and double buffer respectively.

    extern "C" __declspec(dllexport) int FetchIntegerArray ( int nNewSize, int** ppnArray );

    extern "C" __declspec(dllexport) double  FetchDoubleArray ( int nNewSize, double ** ppnArray );

    Implementing Functions

    extern "C" __declspec(dllexport) int FetchIntegerArray ( int nNewSize, int** ppnArray )
    {
          int result = 0;
          //    CoTaskMemAlloc must be used because code on the managed side will call
          //    Marshal.FreeCoTaskMem to free this memory.
          int* newArray = (int*)CoTaskMemAlloc( sizeof(int) * nNewSize);
          for ( int j = 0; j < nNewSize ; j++ )
          {
               newArray[j] = ( j + 1 ) * 10 ;
               result += newArray[j];
           }

          if ( *ppnArray != NULL ) CoTaskMemFree( *ppnArray );
          *ppnArray = newArray;
          return result;
    }

    extern "C" __declspec(dllexport) double  FetchDoubleArray ( int nNewSize, double ** ppnArray )
    {
         double result = 0;
         //    CoTaskMemAlloc must be used because code on the managed side will call
         //    Marshal.FreeCoTaskMem to free this memory.
         double* newArray = (double*)CoTaskMemAlloc( sizeof(double) * nNewSize );
         for ( int j = 0; j < nNewSize ; j++ )
         {
             newArray[j] = 10 + ( j+1 ) * 30 ;
             result += newArray[j];
         }

         if ( *ppnArray != NULL ) CoTaskMemFree( *ppnArray );
         *ppnArray = newArray;
         return result;
    }

    Point 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.

    If you want to use a heap that is shared between native and managed, it is more common to use the COM heap.

    Writing the client code (the managed part)

    one can simple create a console base 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
    {
       class Program
       {
           [DllImport("Win32Native.dll")]
           public static extern int FetchIntegerArray(int nSize, ref IntPtr arrInt);

           [DllImport("Win32Native.dll")]
           public static extern double FetchDoubleArray(int nSize, ref IntPtr arrInt);

           static void Main(string[] args)
           {
               int nSize = 10;
               IntPtr ptrArr = IntPtr.Zero;

               int nSum = FetchIntegerArray(nSize, ref ptrArr);
               int [] arrInt = new int [nSize];
               Marshal.Copy(ptrArr, arrInt, 0, nSize);

               Console.WriteLine("\nReturned Integer Buffer\n");

               for (int i = 0; i < nSize; i++)
               {
                   Console.Write("{0:}  ", arrInt[i]);
               }

               Console.Write("\nSum of Integer Buffer : {0}\n", nSum );
               Marshal.FreeCoTaskMem(ptrArr);

               ptrArr = IntPtr.Zero;
              double dblSum = FetchDoubleArray(nSize, ref ptrArr);
              double[] arrDbl = new double[nSize];
              Marshal.Copy(ptrArr, arrDbl, 0, nSize);

              Console.WriteLine("\nReturned Double Buffer\n");
              for (int i = 0; i < nSize; i++)
              {
                  Console.Write("{0:F2}  ", arrDbl[i]);
              }

              Console.Write("\nSum of Double Double Buffer : {0}\n", dblSum);
              Marshal.FreeCoTaskMem(ptrArr);
          }
       }
    }

    Point of Interest

    • namespace System.Runtime.InteropServices;  defines the declarations necessary for Interop operations, like DllImport,
    • DllImport defines the DLL entry point.
    • Marshal.Copy function used to copy buffer from managed buffer to unmanaged buffer and vice versa.
    • Marshal.FreeCoTaskMem frees the memory allocated by native DLL.

    compile and execute you will get following output.

    image

    August 3, 2013 Posted by | .NET, CodeProject, Interoperability, Marshaling, Win32 | , , , | Leave a comment

    Fetching a byte buffer from a Win32 DLL

     

    Define  a function in native dll (let say it “Win32Native.dll”)  as shown below.

    extern "C" __declspec(dllexport) int FetchByteArray ( int nSize, byte** ppnArray )
    {
        int result = 0;
        //    CoTaskMemAlloc must be used instead of the new operator because code on the managed side will call
        //    Marshal.FreeCoTaskMem to free this memory.
        byte* newArray = (byte*)CoTaskMemAlloc( sizeof(byte) * nSize );
        for ( int j = 0; j < nNewSize ; j++ )
        {
            newArray[j] = ( j+1 ) % 255 ;
            result += newArray[j];
        }

        // release the previous buffer, if any allocated.
        if ( *ppnArray != NULL ) CoTaskMemFree( *ppnArray );
        *ppnArray = newArray;
        return result;
    }

    Point 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.

    If you want to use a heap that is shared between native and managed, it is more common to use the COM heap.

    Writing the client code (the managed part)

    one can simple create a console base application which can use this dll. let’s name it MarshallingTest.

    see the code snippet below.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;

    namespace MarshallingTest
    {
        class Program
        {
            [DllImport("Win32Native.dll")]
            public static extern int FetchByteArray(int nSize, ref IntPtr arrInt);
        
            static void Main(string[] args)
            {
                int nSize = 10;
                IntPtr ptrArr = IntPtr.Zero;

                int nSum = FetchByteArray(nSize, ref ptrArr);
                byte[] arrByte = new byte[nSize];
                Marshal.Copy(ptrArr, arrByte, 0, nSize);

                Console.WriteLine("\nReturned Buffer\n");

                for (int i = 0; i < nSize; i++)
                {
                    Console.Write ( "{0:D2}  ", arrByte[i] );
                }
               
                Console.Write("\nSum of Buffer : {0}\n", nSum );
                Marshal.FreeCoTaskMem(ptrArr);
            }
        }
    }

    Point of Interest
    • namespace System.Runtime.InteropServices;  defines the declarations necessary for Interop operations, like DllImport,
    • DllImport defines the DLL entry point.
    • Marshal.Copy function used to copy buffer from managed buffer to unmanaged buffer and vice versa.
    • Marshal.FreeCoTaskMem frees the memory allocated by native DLL.

    compile and execute you will get following output.

    image

    July 31, 2013 Posted by | .NET, CodeProject, Interoperability, Marshaling, Win32 | , , , | Leave a comment

    Interoperability with Winodws API (1 of N)

    In this article I will explain, how one can call windows API, from managed world. let’s define the signatures of windows API for managed world.

    Calling a Win32 MessageBox function from Managed Word

    //****************************

    // MyProgram.cs

    namespace MyManagedProgram   {  //1

    class Program   { // 2
    static void Main(string[] args)   { //3

    Win32DllWrapper.MsgBox(0, "This Text has been sent from  Managed World",          "Win32 Messsage Box", 0);

    } //3

    }  //2

    ///defining the helper class

    ///define a helper class which will contain a functions for

    public class Win32DllWrapper   {    //4
    //  Standard Functions from Windows API.
    [DllImport("User32.dll", EntryPoint = "MessageBox", CharSet = CharSet.Auto)]
    public static extern int MsgBox(int hWnd, String text, String caption, uint type);

    }  //4

    }  // 1

    compile and execute

    July 26, 2013 Posted by | .NET, CodeProject, Interoperability, Marshaling, Win32 | , , | Leave a comment