您的位置:首页 > 编程语言 > C语言/C++

C#调用C++写的Native DLL

2012-09-20 10:56 381 查看
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++ c# dll string 测试 api