C#调用C++写的Native DLL

C#部份

namespace testInteroperability
{
    class MsgBoxTest
    {
        [DllImport("user32.dll")]
        static extern int MessageBox(IntPtr hWnd,string text, string caption,int type);

        public static void testMB()
        {
            MessageBox(IntPtr.Zero,
                "Please do not press this again.", "Attention", 0);
        }
    }
}


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

namespace testInteroperability
{
    class testMyDLL
    {
        /*
         * 内容:C#调用C++写的Native DLL的示例
         * 作者:kagula
         * 日期:20120920
         * 测试环境:VS2008SP1 .NET FRAMEWORK 3.5
         * 
         * 注意:
         * [1]你需要使用exeScope工具查看C++的函数导出后在DLL中的入口名称
         * 下面语句EntryPoint后跟的是C++函数导出后,在DLL中的入口名称
         * 因为c++的重载机制,同个函数名可以有不同的参数和返回值等等,为了区别开来,所以加了@@之类
         * 
         * [2]如果你经常修改C++DLL中的函数参数列表,VC会自动生成新的引用名称很不方便
         * 这时,你可以在C++项目中,[Add]->[New Item]->[Visual C++]->[Code]->[Module-Definition File(.def)]
         * 添加一个模块定义文件(.def),定义函数外部引用的名称。
         * 
         * [3]你必须修改你的DLL项目的DLL输出路径到C#程序的输出路径中
         * 例"<Solution所在路径>\<Solution名称>\<项目名称>\bin\Debug"
         * 
         * [4]C++的DLL项目同C#测试DLL的项目要放在同一个Solution中
         * 勾选C#项目属性[Debug]->[Enable Debuggers]->[Enable unmanaged code debugging]选项
         * 现在调试你的C#项目自动会进入到你在C++项目中下的断点。
         * 
         * 
         * 参考资料
         * [1]《Type Library Importer in Managed Code》
         * http://clrinterop.codeplex.com/releases/view/17579
         * [2]《How to copy a String into a struct using C#》
         * http://www.codeproject.com/Articles/7357/How-to-copy-a-String-into-a-struct-using-C
         * [3]《C#中具体如何调用Win32函数》
         * http://www.pinvoke.net
         * [4]《C++字符集问题终极分析(可解决乱码问题)》
         * http://hi.baidu.com/xddipuauedhkpqe/item/b59ee29082aaff35336eebcc
         */

        //测试基本调用,引用名称,为VC自动生成
        [DllImport(@"MyDLL.dll", EntryPoint = "?fnMyDLL@@YAHXZ")]  
        static extern int fnMyDLL();

        
        public static void test_fnMyDLL()
        {
            try
            {
                int nR = fnMyDLL();
                Console.WriteLine("test fnMyDLL()=" + nR);
            }
            catch (Exception e)
            {
                //找不到动态链接库会抛出异常
                Console.WriteLine(e.Message);
            }
        }

        //测试Primitive类型参数传入,引用名称在VC++项目的DEF文件中定义
        [DllImport(@"MyDLL.dll")]
        static extern void fnHaveMorePara(byte c,byte uc,
            short s,ushort us,
            int n,uint un,
            int l,uint ul,
            Int64 ll,Int64 ull,
            float f,double d,
            [MarshalAs(UnmanagedType.LPTStr)] string ss, [MarshalAs(UnmanagedType.LPWStr)] string ws);

        public static void test_fnHaveMorePara()
        {
            try
            {
                fnHaveMorePara(1,2,3,4,5,6,7,8,
                    9L,10L,11.0f,12.0,
                    "ANSI String", "test_fnHaveMorePara[来自C#的字符串]");
            }
            catch (Exception e)
            {
                //找不到动态链接库会抛出异常
                Console.WriteLine(e.Message);
            }
        }

        //测试Primitive类型参数传出,引用名称在VC++项目的DEF文件中定义
        /*
         * [1]测试传入字节数组
         * [2]测试取字符串结果
         * [3]测试取int结果(其它原始数据类型的传出同int)
         */
        [DllImport(@"MyDLL.dll")]
        static extern void fnHaveMorePara2(IntPtr pData, 
            [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pWStr, 
            ref int n);

        unsafe public static void  test_fnHaveMorePara2()
        {
            byte[] data = new byte[2];
            data[0] = 1;
            data[1] = 2;

            fixed (byte* p = data)
            {

                StringBuilder sb = new StringBuilder(255);
                int n = 0;
                fnHaveMorePara2((IntPtr)p, sb, ref n);
                Console.WriteLine("1+2=" + n +"  sb="+sb.ToString());
            }
        }

        //测试结构数据的传入传出
        //C#负责分配内存,C++(DLL)负责往内存里填数据
        /*
         * Struct中的字符串成员转递给C++DLL用String类型,
         * [1]从C++DLL返回得用StringBuilder类型,问题是Struct里不能有StringBuilder类型的字符串类型。
         * 所要要返回struct里的String类型的东东,struct里必须是String类型,[2]而且外部函数声明时
         * 必须用ref。如果用out语法,传到C++里的struct里的String对象成员为空指针。
         * out 后面跟的结构里的成员只能是原始数据类型。
        */
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct StudentInfo
        {
            public int           id;
            [MarshalAs(UnmanagedType.LPWStr,SizeConst=256)]
            public String        name;
            public int age;
        }
        [DllImport(@"MyDLL.dll")]
        static extern void fnHaveMorePara3(StudentInfo siIn,ref StudentInfo siOut,
            ref StudentInfo siOut2);
        public static void test_fnHaveMorePara3()
        {
            StudentInfo si = new StudentInfo();
            StudentInfo siOut2 = new StudentInfo();
            si.id = 12;
            si.name = "来自C#的字符串!";
            si.age = 13;

            //分配足够长度的内存,让C++程序能够往里填要返回的字符串。
            //若C++程序地址越界(超出C#所分配实际内存长度),C#程序会抛出OutOfMemory异常。
            siOut2.name = new String('\0',256);

            fnHaveMorePara3(si, ref si, ref siOut2);
            Console.WriteLine("id="+si.id);
            Console.WriteLine("name=" + si.name);
            Console.WriteLine("age=" + si.age);

            Console.WriteLine("siOut2 id=" + siOut2.id);
            Console.WriteLine("siOut2 name=" + siOut2.name);
            Console.WriteLine("siOut2 age=" + siOut2.age); 
        }
        //测试union结构参数的传入传出
        //暂时不会用到,故省略

        //测试2D数组的传递
        [DllImport(@"MyDLL.dll")]
        static extern void AddMatrix4x4(int [,] s1,int [,] s2,ref int[,] d);
        public static void test_fnHaveMorePara4()
        {
            int[,] s1 = new int[4,4];
            int[,] s2 = new int[4,4];
            int[,] d = new int[4, 4];
            for(int i=0;i<4;i++)
                for (int j = 0; j < 4; j++)
                {
                    s1[i,j]  = i;
                    s2[i, j] = j;
                    d[i, j] = i * j;
                }

            AddMatrix4x4(s1, s2, ref d);
            Console.WriteLine("AddMatrix4x4 D[3][3]= >" + d[3,3]);
        }

        //测试C++对C#的回调
        delegate bool FromDLLCallBack(int r);
        [DllImport(@"MyDLL.dll")]
        static extern void Add(FromDLLCallBack func, int s1,int s2);
        static bool _FromDLLCallBack(int r)
        {
            Console.WriteLine("_FromDLLCallBack =>" + r);
            return false;
        }
        public static void test_fnHaveMorePara5()
        {
            Add(_FromDLLCallBack, 1, 2);
        }
    }
}

C++部份,模块定义文件

LIBRARY	"MyDLL"
EXPORTS
fnHaveMorePara  @2
fnHaveMorePara2 @3
fnHaveMorePara3 @4
AddMatrix4x4    @5
Add             @6

C++部份,头文件

#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif


//测试简单函数的调用
MYDLL_API int  fnMyDLL(void);

//测试原始数据类型参数列表的传入
MYDLL_API void fnHaveMorePara(char c,unsigned char uc,
							  short s,unsigned short us,
							  int n,unsigned int un,							  
							  long l,unsigned long ul,
							  long long ll,unsigned long long ull,
							  float f,double d,
							  char* pStr,wchar_t* pWStr);

//测试原始数据类型参数的返回
/*
[in]  pData   字节型数组指针
      pWStr   要返回字符串指针
[out] pWStr,n
      n       pData数组第一个单元的值 + pData数组第二个单元的值
*/
MYDLL_API void fnHaveMorePara2(void* pData,wchar_t* pWStr,int* n);

//测试数据结构对象的传入传出
typedef struct _StudentInfo
{
    int      id;
    wchar_t* name;
    int      age;
} StudentInfo;

MYDLL_API void fnHaveMorePara3(StudentInfo siIn,StudentInfo* siOut,
							   StudentInfo* siOut2);

/*
功能:四阶矩阵加法
目的:二维数组传递
备注:int s1[4][4]可以声明为int *s1,
      这时对s1二维数组进行引用可以采用s1[i*4+j]形式,i、j的定义域为[0,3]
*/
MYDLL_API void AddMatrix4x4(int s1[4][4],int s2[4][4],int **_d);

//测试回调功能
typedef BOOL (CALLBACK *FromDLLCallBack) (int r);
MYDLL_API void Add(FromDLLCallBack func,int a,int b);

C++部份,源文件

// MyDLL.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "MyDLL.h"

#include "stdio.h"
#include <string.h>

#include <iostream>
/*
环境:VS2008SP1
项目设置:新建DLL项目时,勾选[Additional options]的[Export Symbols]选项
*/
MYDLL_API int fnMyDLL(void)
{
	return 42;
}

MYDLL_API void fnHaveMorePara(char c,unsigned char uc,
							  short s,unsigned short us,
							  int n,unsigned int un,							 
							  long l,unsigned long ul,
							  long long ll,unsigned long long ull,
							  float f,double d,
							  char* pStr,wchar_t* pWStr)
{
	printf("[char,unsigned char]=>%c,%c\n",c,uc);
	printf("[short,unsigned short]=>%d,%d\n",s,us);
	printf("[int,unsigned  int]=>%d,%d\n",n,un);
	printf("[long,unsigned long]=>%d,%d\n",l,ul);
	printf("[long long,unsigned long long]=>%I64d,%I64d\n",ll,ull);
	printf("[float,double]=>%d,%d\n",c,uc);

	printf("[char *]=>%s\n",pStr);
	//打印中文会乱码,还需要做字符集编码转换,但这不是本文的重点,故忽视。
	wprintf(L"[wchar_t *]=>%s\n",pWStr);
}

MYDLL_API void fnHaveMorePara2(void* pData,wchar_t* pWStr,int* n)
{
	if(pData!=NULL)
	{
		char *pC = (char *)pData;
		std::wstring ws=L"Unicode String[来自DLL的字符串]";
		*n = (int)(pC[0] + pC[1]);
		wcsncpy(pWStr,ws.c_str(),255);
	}
}

/*
功能:
目的:结构对象的传递
备注:
*/
MYDLL_API void fnHaveMorePara3(StudentInfo siIn,StudentInfo* siOut,StudentInfo* siOut2)
{
	printf("SI(1)->id=%d\n",siIn.id);
	wprintf(L"SI(1)->name=%s\n",siIn.name);
	printf("SI(1)->age=%d\n",siIn.age);

	siOut->id=21;
    //不能对siOut->name操作,因为没有分配足够长度的内存
	siOut->age=22;

	siOut2->id = 31;
	//C#对应的是String类型所以不能给wchar_t*类型对象赋值,
	//否则在C#里引用这个对象会抛出OutOfMemeoryException。
	if(siOut2->name!=NULL)
		wcsncpy(siOut2->name,L"来自C++DLL的名字",255);
	siOut2->age = 32;	
}


MYDLL_API void AddMatrix4x4(int s1[4][4],int s2[4][4],int **d)
{
	for(int i=0;i<4;i++)
	{
		for(int j=0;j<4;j++)
		{
			(*d)[i*4+j] = s1[i][j] + s2[i][j];
		}
	}
	printf("d[3][3]=%d\n",(*d)[15]);
}

MYDLL_API void Add(FromDLLCallBack func,int a,int b)
{
	BOOL bR = (*func)(a+b);
	if(bR==1)
		printf("C#返回True!\n");
	else if(bR==0)
		printf("C#返回False!\n");
}


你可能感兴趣的:(C++,api,String,C#,测试,dll)