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 string values (default Marshaling)

    Define some functions in native dll (let say it “Win32Native.dll”)  as shown below. this function receives array of char pointers (string).

    extern "C" __declspec(dllexport) void ModifyStringArrayValues( int nCount, char* ppStrArray[] )

    Implementing Functions

    extern "C" __declspec(dllexport) void ModifyStringArrayValues( int nCount, char* ppStrArray[] )
    {
        size_t cchDest = 40;
        const size_t alloc_size = sizeof(char) * 40;
        for ( int nI = 0; nI < nCount; nI++ )
        {
            char *pszFormat = "<<from DLL>> [Modified String %2d]";
            STRSAFE_LPSTR temp = (STRSAFE_LPSTR)CoTaskMemAlloc( alloc_size );
            StringCchPrintfA(temp, cchDest, pszFormat, nI);
            CoTaskMemFree( ppStrArray[nI] );
            ppStrArray[nI] = (char *) temp;
       }
    }

    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.
    • StringCchPrintfA printf used to print a formatted output 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.

    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 double ModifyStringArrayValues(int nSize, [In, Out] String[] strArr);
        
            static void Main(string[] args)
            {
                int nSize = 5;
                string[] arrStr = new string[nSize];
                for (int nI = 0; nI < nSize; nI++) arrStr[nI] = "String " + (nI + 1).ToString();

               
                Console.WriteLine("String Values in Array, before calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1}", i, arrStr[i]));
                }

                ModifyStringArrayValues(nSize, arrStr);

                Console.WriteLine("String Values in Array, after calling function");
                for (int i = 0; i < nSize; i++)
                {
                    Console.WriteLine(string.Format("Array[{0:D2}] : {1}", i, arrStr[i]));
                }
            }
        }
    }

    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, 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 an array of strings from a Win32 DLL

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

    extern "C" __declspec(dllexport) void FetchStringArray( int nCount, char* ppStrArray[])
    {
        int result = 0;
        STRSAFE_LPSTR temp;
        size_t cchDest = 40;

        const size_t alloc_size = sizeof(char) * 40;

        for ( int nI = 0; nI < nCount; nI++ )
        {
           char *pszFormat = "from DLL >> [returned String %d]";
           STRSAFE_LPSTR temp = (STRSAFE_LPSTR)CoTaskMemAlloc( alloc_size );
           StringCchPrintfA(temp, cchDest, pszFormat, nI);

           CoTaskMemFree( ppStrArray[nI] );
           ppStrArray[nI] = (char *) temp;
        }
    }

    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 simply create a console based application

    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    namespace MarshallingTest
    {
       class Program
       {
           [DllImport("Win32Native.dll")]
           public static extern void FetchStringArray(int nCount, [In, Out] String[] arrStr);

           static void Main(string[] args)
           {
                int nSize = 10;
                String[] arrStr = new String[nSize];
                FetchStringArray(nSize, arrStr);
                Console.WriteLine("Returned String Array");
                for (int nI = 0; nI < nSize; nI++)
                {
                     Console.WriteLine(arrStr[nI]);
                }
           }

       }

    }

    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 4, 2013 Posted by | .NET, CodeProject, Interoperability, 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 Returning a string from Wn32 DLL

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

    extern "C" __declspec(dllexport)  char * GetStringFromDLL()
    {
        const size_t alloc_size = 128;
        STRSAFE_LPSTR result=(STRSAFE_LPSTR)
    CoTaskMemAlloc(alloc_size);
        STRSAFE_LPCSTR teststr = "This is return string From Native DLL";
        StringCchCopyA ( result, alloc_size, teststr );
        return result;
    }

    Point of Interest

    • STRSAFE_LPSTR is a typedef of char *
    • StringCchCopy is a replacement for strcpy (with safety).  The size, in characters, of the destination buffer is provided to the function to ensure that StringCchCopyb does not write past the end of this buffer. has two variants.

    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 String GetStringFromDLL();

            static void Main(string[] args)
            {
                Console.WriteLine("Line displayed below is returned from a Native DLL");
                string strData = GetStringFromDLL();
                Console.WriteLine ( "Returned value [" + strData +"]" );
            }
        }
    }

    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

     

    July 30, 2013 Posted by | CodeProject, Interoperability, 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

    Packing a array inside a VARIANT (VC++ 6.0) (Series 3 of N)

    This is the third post about variant, in this post I I am going to explain, how an array of structures can be packed inside a VARIANT variable. suppose we have a structure as definition shown below.

    typedef struct MyStructType
    {
        int nID ;
        long lVal ;
        double dblVal ;
        TCHAR szBuffer[255];
    } MyStructType ;

    Packing  a array of structure inside a Variant

    int  PackVariantWithStructArray(short nCnt, VARIANT * pVar )
    {
        // TODO: Add your dispatch handler code here
        USES_CONVERSION ;

        //Initialize the VARIANT (Type is SAFEARRAY of BYTE)
        VariantInit(pVar);
        pVar->vt = VT_ARRAY | VT_UI1 ;

        int nBufferSize = nCnt * sizeof ( MyStructType );

        // Define a safe array of nCnt Item and Starting index as 0
        SAFEARRAYBOUND safeBounds = { nBufferSize, 0};
       
        //Create the Safe Array passing it the bounds
        SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, &safeBounds);

        //Get a pointer to the array data, This actually increments the array’s lock count)
        MyStructType * structArray = NULL;
        SafeArrayAccessData(pSafeArray, (void**)&structArray);
        for ( int i = 0 ; i < nCnt ; i++ )
        {
            CString strTmp ;
            strTmp.Format ( _T("This is Item %2d"), i );
            structArray[i].dblVal = (i +1) * 101.0 ;
            structArray[i].lVal = (i +1) * 11 ;
            structArray[i].nID = (i +1) ;
            _tcscpy ( structArray[i].szBuffer, strTmp) ;
        }

        // We are done wth populating the array Decrement the lock

         SafeArrayUnaccessData(pSafeArray);

        //Assign our VARIANT out param with the array   
        pVar->parray = pSafeArray ;
        return nCnt ;
    }

    as it can be seen from the above code, here I have just created a byte buffer sufficient to accommodate the all structures,

    it involves following steps.

    1. Allocating the desired temporary buffer space to hold the array of structures (that should be equivalent to (number of structure x size of structure), and filling that array with values one need to return.
    2. creating a safe array of the desired (VT_U1) type.
    3. copying the temporary buffer to the safe array of the variant.
    4. free the memory allocated for temporary buffer. (avoid memory leaks).

    Fetching a array of structure from a variant.

    MyStructType * UnPackVariantWithStructArray(short nCount, VARIANT var )
    {
        MyStructType *pBuffer = (MyStructType *) calloc ( nCount, sizeof (MyStructType)) ; 

        USES_CONVERSION ;
        SAFEARRAY* pSafeArray  = var.parray ;
        //Get a pointer to the array data, This actually increments the array’s lock count)
        MyStructType *structArray = NULL ;
        SafeArrayAccessData ( pSafeArray, (void**)&structArray );
        for ( int i = 0 ; i < nCount ; i++ )
        {
            pBuffer[ i].dblVal = structArray [i].dblVal  ;
            pBuffer[ i].lVal = structArray [i].lVal ;
            pBuffer[ i].nID = structArray [i].nID   ;
            pBuffer[ i].dblVal = structArray [i].dblVal  ;
            _tcscpy ( pBuffer[ i].szBuffer, structArray [i].szBuffer );
        }
        //    We are done wth populating the array Decrement the array’s lock count
        SafeArrayUnaccessData(pSafeArray);
        return pBuffer ;
    }

    this is a crude method, although there is a better method using type libraries, that I will explain in some other article.

    July 19, 2013 Posted by | CodeProject, Interoperability, Win32 | , , | 2 Comments