一般我们会有这样一种情况,函数由c++来写,在c#里调用这一函数。这一要求是因为,同样地代码,c++的运行速度比c#运行速度快。
比如说,我们使用OpenCV获取摄像机图像,在xna中显示出来。
在c++中函数导出的代码这样写:
extern "C" __declspec(dllexport) bool __stdcall GetImg(byte imgData[])
{
if (cvGrabFrame(capture1))
{
img1=cvRetrieveFrame(capture1);
int height = img1->height;
int width = img1->width;
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
imgData[(i + j * width)*4 + 0] = (byte)(img1->imageData + img1->widthStep * j)[3 * i + 0];
imgData[(i + j * width)*4 + 1] = (byte)(img1->imageData + img1->widthStep * j)[3 * i + 1];
imgData[(i + j * width)*4 + 2] = (byte)(img1->imageData + img1->widthStep * j)[3 * i + 2];
imgData[(i + j * width)*4 + 3] = 255;
}
}
return true;
}
else
{
return false;
}
}
我们一一来解释:
一: extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。
1. extern在c++语言里是全局变量的意思,如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。
2. 被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;
一般来说在DLL的设计中中,如果使用C++开发,通常在导出函数的定义中使用extern "C",为什么呢?因为当用户使用"运行时动态链接"的时候将使用GetProcAddress函数得到导出函数的地址,该函数是通过导出函数的函数名定位导出函数的,而C++编译器会对开发者定义的函数名进行修饰,导致导出表中的函数名通常不是开发者使用的函数名,比如函数ExportedFn可能被修饰成??ExportedFn@QAEX。所以使用extern "C"通知编译器按照C的格式进行编译,而不是使用C++的方法进行编译。
二: __declspec(dllexport)
声明一个导出函数,是说这个函数要从本DLL导出。我要给别人用。一般用于dll中
省掉在DEF文件中手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类
__stdcall是函数调用约定的一种,函数调用约定主要约束了两件事:
1.参数传递顺序 2.调用堆栈由谁(调用函数或被调用函数)清理__stdcall表示
1.参数从右向左压入堆栈 2.函数被调用者修改堆栈 3.函数名(在 编译器这个层次)自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸 如果不用 __stdcall修饰函数,在vs2010的c#里调用此DLL会出现函数签名不匹配的错误。
在c#里调用DLL这样写:
[DllImportAttribute("OpenCV2010.dll", CharSet = CharSet.Auto, EntryPoint = "BegainCamera")]
public static extern bool BegainCamera(ref Int32 outWidth, ref Int32 outHeight, Int32 preWidth, Int32 preHeight, Int32 devcieNumber);
[DllImportAttribute("OpenCV2010.dll")]
public static extern bool GetImg(byte[] imgData);
[DllImportAttribute("OpenCV2010.dll")]
public static extern void EndCamera();
声明了之后,c++写得函数就能在c#中调用了。。