65.9K
CodeProject is changing. Read more.
Home

Fetching buffer of numeric types from a native DLL

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2 votes)

Aug 15, 2013

CPOL
viewsIcon

5949

Fetching buffer of numeric types from a native DLL.

Define some functions in native DLL (let's say it “Win32Native.dll”) as shown below. These three 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)

We 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