2016年4月28日 星期四

Calling C/C++ Functions From C#




      Dot net framework is profound for coding, features the integrated API with service. However, some people would not be satisfied at its performance for large scale numerical computing.

     
Essentially, dot net framework is a runtime interpreting virtual machine, it cost CPU resource to explain instructions.

      To exploit the convenience of dot.Net combining with efficiency of native instruction, the burden for this goad is to increase the coding complexity. This post is to demonstrate how to reach the purpose.

零.

    Create 2 projects separately, One is for C#, named as CsharpCallC; Another is for C++.( I use VS2005 in here.), named as clibrary. Close the 2 project and  place the two project in the same folder.






一.

   Open CsharpCallC solution file, and add the clibrary project under this solution:


Set the clibrary project as dll project:




The post-build event should be added some  command :



copy /Y "$(SolutionDir)\$(ConfigurationName)\$(ProjectName).dll" "$(SolutionDir)\bin\$(ConfigurationName)\$(ProjectName).dll"

It is, after the build has been done , the C/C++ dll binary would be copyed to the folder which the C# binary would be put after the C# build has been done


二.

   For the C# project, setthe build target be x86 ( for VC++ of VS2005 support 32 bits only), and allow unsafe code:



And Set the C# project depends on clibary project.



三.

Change the default C# main class file Program.cs, as Csharp.cs, and the code on the file be :



using System;
using System.Collections.Generic;
using System.Text;

using System.Runtime.InteropServices;

namespace LoadCPPExample
{
    public class Demonstration
    {
        [DllImport("Clibrary")]
        public static extern void CallByRefUnmanagedFun(double inVar, ref double outVar);

        public static void CallByRefFun(double inVar, ref double refOutVar)
        {
            CallByRefUnmanagedFun(inVar, ref refOutVar);
        }//CallByRefFun


        [DllImport("Clibrary")]
        public static extern void SetStringValueUnmanagedFun(StringBuilder pStr);

        public static void SetStringValue(StringBuilder str)
        {
            SetStringValueUnmanagedFun(str);
        }//CallByRefFun


        [DllImport("Clibrary")]
        public static extern void ArrayOperationUnmanagedFun(int m, int n,
            double[,] matrix);

        public static void ArrayFun(int m, int n, double[,] matrix)
        {
            ArrayOperationUnmanagedFun(m, n, matrix);
        }//ArrayFun



        public struct arrayStruct
        {
            public int length;
            public IntPtr data;
        };

        [DllImport("Clibrary")]
        public static extern int InitArrayStructUnmanagedFun(
            ref arrayStruct refArrayStruct, int len);

        public static int InitArrayStruct(ref arrayStruct refArrayStruct, int len)
        {
            return InitArrayStructUnmanagedFun(ref refArrayStruct, len);
        }//InitArrayStruct

        [DllImport("Clibrary")]
        public static extern void
            FreeArrayStructUnmanagedFun(ref arrayStruct refArrayStruct);

        public static void FreeArrayStruct(ref arrayStruct refArrayStruct)
        {
            FreeArrayStructUnmanagedFun(ref refArrayStruct);
        }//FreeArrayStruct



        public delegate void CSharpCallBack(IntPtr args);

        public static void OnCSharpCalled(IntPtr args)
        {
            unsafe
            {
                Console.Write(
                    "C# callback :{0} has been called, args as int = {1}\n",
                    System.Reflection.MethodBase.GetCurrentMethod().Name, 
                    *((Int32*)args) );
            }//unsafe

            Console.Write("\n");
        }//OnCSharpCalled


        [DllImport("Clibrary")]
        public static extern int CFunWouldCallCSharpCallBacktUnmanagedFun(
            CSharpCallBack OnCSharpCalled);

        public static void
            CFunWouldCallCSharpCallBack(CSharpCallBack OnCSharpCalled)
        {
            CFunWouldCallCSharpCallBacktUnmanagedFun(OnCSharpCalled);
        }//CFunWouldCallCSharpCallBack


        public static void Main()
        {
            {
                Console.Write("\n");

                double inVar = 5.0;
                double outVar = 0.0;
                CallByRefFun(inVar, ref outVar);
                Console.Write("invar = {0}, outvar = {1}\n", inVar, outVar);
            }


            {
                Console.WriteLine("");

                StringBuilder str;
                str = new StringBuilder(256);
                SetStringValue(str);
                Console.Write("str = {0}\n", str);
            }


            {
                Console.Write("\n");

                int m = 2;
                int n = 3;
                double[,] matrix;
                matrix = new double[m, n];
                ArrayOperationUnmanagedFun(m, n, matrix);

                Console.Write("matrix:\n");

                for (int i = 0; i < m; i++)
                {
                    for (int j = 0; j < n; j++)
                    {
                        Console.Write("{0}\t", matrix[i, j]);
                    }/*for j*/
                    Console.Write("\n");
                }/*for i*/
            }


            {
                Console.Write("\n");

                int len = 5;
                arrayStruct charArray = new arrayStruct();

                InitArrayStruct(ref charArray, len);

                Console.WriteLine("InitArray:");

                unsafe
                {
                    byte* data;
                    data = (byte*)charArray.data;

                    for (int i = 0; i < len; i++)
                        Console.Write("{0}\t", data[i]);

                }

                //byte[] data = new byte[len];
                //Marshal.Copy(charArray.data, data, 0, len); 
                //for (int i = 0; i < len; i++)
                //    Console.Write("{0}\t", data[i]);

                Console.Write("\n");
                FreeArrayStruct(ref charArray);

                Console.WriteLine("");
            }

            {
                CFunWouldCallCSharpCallBack(OnCSharpCalled);
                Console.WriteLine("");
            }

            Console.ReadKey();
        }//Main
    }//class Demonstration

}//namespace LoadCPPExample


四.

  Add a file clibrary.cpp under the project clibrary, the code in it be :


#include <stdlib.h>
#include <string.h>


#define UNMANAGED_CALL      __stdcall
#define UNMANAGED_EXPORT     __declspec(dllexport)

#ifdef __cplusplus
extern "C"{
#endif

typedef  void( UNMANAGED_CALL *CSharpCallbackFun)(void* pArgs);

UNMANAGED_EXPORT void UNMANAGED_CALL 
 CallByRefUnmanagedFun(double, double*); 


UNMANAGED_EXPORT void UNMANAGED_CALL 
 SetStringValueUnmanagedFun(char *pStr);


UNMANAGED_EXPORT void UNMANAGED_CALL 
 ArrayOperationUnmanagedFun(int m, int n, double *pMatrix);


struct arrayStruct;

UNMANAGED_EXPORT int UNMANAGED_CALL 
 InitArrayStructUnmanagedFun( arrayStruct *pArrayStruct, int len);

UNMANAGED_EXPORT void UNMANAGED_CALL 
 FreeArrayStructUnmanagedFun(arrayStruct *pArrayStruct);



UNMANAGED_EXPORT void UNMANAGED_CALL 
 CFunWouldCallCSharpCallBacktUnmanagedFun( 
  void(UNMANAGED_CALL *pCallbackFun)(void* pArgs) );

#ifdef __cplusplus
}
#endif


UNMANAGED_EXPORT void UNMANAGED_CALL CallByRefUnmanagedFun(double in, double *out) 
{ 
  *out = in; 
}/*CallByRefUnmanagedFun*/


UNMANAGED_EXPORT void UNMANAGED_CALL 
 SetStringValueUnmanagedFun(char *pStr)
{
 strncpy(pStr, "it is a string", 256);
}/*SetStringValueUnmanagedFun*/


UNMANAGED_EXPORT void UNMANAGED_CALL 
  ArrayOperationUnmanagedFun(int m, int n, double *pMatrix)
{
 int i, j;
 double value;

 value = 0.0;

 for(i = 0;i < m; i++){
  for(j = 0; j< n; j++){
   *(pMatrix + n*i + j) = value;
   value += 1.0;
  }/*for j*/
 }/*for i*/

}/*ArrayFunUnmanagedFun*/

struct arrayStruct
{
 int length;
    unsigned char *pData;
};

UNMANAGED_EXPORT int UNMANAGED_CALL 
 InitArrayStructUnmanagedFun( arrayStruct *pArrayStruct, int len)
{
 int i;
 pArrayStruct->length = len;
 pArrayStruct->pData = (unsigned char*)malloc(len*sizeof(unsigned char));

 if(NULL == pArrayStruct->pData)
 {
  pArrayStruct->length = 0; 
  return -1;
 }

 for(int i = 0; i< len ; i++)
  pArrayStruct->pData[i] = (unsigned char)(i%256);

 return 0;
}/*InitArrayStructUnmanagedFun*/


UNMANAGED_EXPORT void UNMANAGED_CALL 
 FreeArrayStructUnmanagedFun(arrayStruct *pArrayStruct)
{
 if(NULL == pArrayStruct->pData)
  return;

 free(pArrayStruct->pData); pArrayStruct->pData = NULL;
 pArrayStruct->length = 0;
}/*FreeArrayStructUnmanagedFun*/




UNMANAGED_EXPORT void UNMANAGED_CALL 
 CFunWouldCallCSharpCallBacktUnmanagedFun( 
  void(UNMANAGED_CALL *pCallbackFun)(void* pArgs) )
{
 int number;

 number = 12345;

 pCallbackFun(&number);
}/*CFunWouldCallCSharpCallBacktUnmanagedFun*/


Though the file be C++ file (VS would use C++ compiler to build  this file automatically), but the code is written in strict C89 standard. You could change the extension name frome .cpp to c, there is no different at all.

you could found the console has been printed some thing :


invar = 5, outvar = 5

str = it is a string

matrix:
0       1       2
3       4       5

InitArray:
0       1       2       3       4

C# callback :OnCSharpCalled has been called, args as int = 12345


五.

Summary in here :


The knack for C# calling C/C++ be :

0. The target machine and output folder should be set as the same for C/C++ and C#.

1. As common dynamical linking library manufacturing, the functions which would be exported should be denoted as   __declspec(dllexport), and extern "C".

2. The C/C++ function should be declared as stdcall function to satisfy dot net protocol. thus, it is forbidden to call a c function be variadic( variable number of arguments).

3. In C#, the C/C++ functions should be declaired as public static extern functions, and there should be denotions  [DllImport("DLLFileName")] to indicate what dll file the function be.




 This article has referred to this one, an Numerical Algorithms Group's pdf file.