目标
使C#在.Net4.0平台、AnyCPU(x64)下能成功调用基于OpenCV、Eigen的C++代码编译成的dll
- VisionPro8.2只支持.Net4.0平台、AnyCPU
- IDE:VS2012
- 操作系统:Win7 64位
操作步骤
C++部分
新建文件
新建项目 —— Visual C++ —— .Net Framework 4 —— 空项目
项目名称与代码文件名*.h、*.cpp、*.def最好一致,此处均取”defcpp”
- 头文件 —— *.h
- 源文件 —— *.cpp、*.def
defcpp.h1
2
3
4
5
6
int add(int a, int b);
defcpp.cpp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using namespace std;
int add(int a, int b)
{
return a+b;
}
int main1()
{
cout<<add(1, 2);
return 0;
}
defcpp.def1
2
3LIBRARY defcpp
EXPORTS
add
导出dll
属性页 —— 配置属性 —— 常规 —— 目标文件扩展名(从.exe改为.dll)
属性页 —— 配置属性 —— 链接器 —— 输入 —— 模块定义文件(从空改为defcpp.def)
点击”重新生成解决方案”,生成dll
编译完的dll在C++项目根目录下/Debug/defcpp.dll
修改平台为64位
属性页 —— 配置管理器 —— 活动解决方案平台:x64
生成 —— 重新生成解决方案
编译完的dll在C++项目根目录下/x64/Debug/defcpp.dll
调试技巧
把*.def中的内容注释掉(每行开始加;),把defcpp.cpp中的main1()改为main(),即可成功运行
常见报错
c++编译dll时可能出现的报错:
- “error LNK1561: 必须定义入口点”
解决方法:在属性页中配置 模块定义文件*.def
使用dumpbin命令验证dll中的函数是否导出成功1
dumpbin /exports a.dll
C#部分
调用dll
新建项目 —— Visual C# —— .Net Framework 4 - Windows窗体应用程序
将dll放在C#项目根目录下/bin/Debug/defcpp.dll
代码格式正确,即可成功运行
Form1.cs1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices; // 调用dll所必须的
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[ ]
public extern static int add(int a, int b);
public Form1()
{
InitializeComponent();
int res = add(1, 2); // 调用dll中的函数add()
}
}
}
常见报错
c#调用dll时可能出现的报错:
“检测到LoaderLock 正尝试在OS加载程序锁内执行托管代码。…”
解决方法:将C++代码中的main()函数改个名字,重新编译。(C++中写主函数,会导致C#调用时发生死锁)“对PInvoke 函数…的调用导致堆栈不对称”
解决方法:修改C#代码中导入dll时的参数[DllImport(… CallingConvention …)],参考:函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配“试图加载格式不正确的程序。(异常来自HRESULT:0x8007000B)”
解决方法:C++编译成的dll的平台(32位/64位),与C#选择的平台不对应关于Any CPU
参考:关于C#编译方式的一些说明(x86\x64\anycpu)
测试结果
C++ | C# | 运行结果 |
---|---|---|
.Net 4.0、平台Win32 | .Net 4.0、目标平台x86 | 成功 |
同上 | .Net 4.0、目标平台Any CPU/x64 | 失败 |
.Net 4.0、平台x64 | .Net 4.0、目标平台Any CPU/x64 | 成功 |
同上 | .Net 4.0、目标平台x86 | 失败 |
小结:事实证明,c#选择.Net 4.0、.Net 4.5效果相同,关键在于32位/64位需要对应
进阶
非本机运行
目标:C#代码与dll拷贝到其他电脑时不必配置环境变量
操作:将涉及到的OpenCV的dll拷贝到与自己生成的dll同一目录下,即可成功运行
测试:改变OpenCV的dll所在目录的文件夹名称
更改OpenCV的dll名称
配置OpenCV(64位)、Eigen
OpenCV2.4.9的安装目录:1
D:\_xmvision\opencv\
Eigen3.3.5的安装目录(修改eigen的原始目录名为eigen3):1
D:\_xmvision\eigen3下
配置OpenCV
环境变量PATH(使编译可以找到32位/64位的dll、.dll .pdb .ilk)
1 | D:\_xmvision\opencv\build\x64\vc11\bin |
VC++目录->包含目录(C++程序可以include opencv、.h .hpp)
1 | D:\_xmvision\opencv\build\include |
VC++目录->库目录(静态链接库、.lib .exp)
1 | D:\_xmvision\opencv\build\x64\vc11\lib |
链接器->输入->附加依赖项(动态链接库的)
1 | opencv_calib3d249d.lib |
用python获取”附加依赖项”
在ipython中输入如下命令:1
2
3
4
5
6
7
8
9
10
11
12
13# 切换目录
cd D:\_xmvision\opencv\build\x64\vc11\lib
# 读取文件
import os
file_list = os.listdir()
# 过滤出"d.lib"结尾的文件名
file_lib_list = [x for x in file_list if x[-5:]=="d.lib"] # debug使用的lib
# 输出文件名
for ele in file_lib_list:
print(ele)
配置Eigen
VC++目录->包含目录
1 | D:\_xmvision\eigen3 |
参考
编译dll
配置环境
修改dll名称
常见报错:配置完成后,编译成功,运行程序时出现:”无法启动此程序 因为计算机中丢失opencv_core243d.dll”
解决方法:重启计算机即可