最近因为项目需要,需要将之前生成的exe在另一个大的项目中调用,因此需要将当前的项目转化为dll。
之前虽然经常使用dll和Lib,,但却没有亲身尝试生成,对于格式和流程不是很清楚,借机学习了一下。
参考和查阅了很多帖子,比如
http://blog.csdn.net/yanbdsky/article/details/4587813
http://topic.csdn.net/u/20100727/11/13a60518-7710-44a0-857e-cadd12da7524.html。
还有很多类似的,总之大致就两种思路,虽然我不知道两种在具体实现上有什么异同,现将他们过程介绍一下。
介绍之前,简要说明一下我的需求和背景,手上的这个项目是基于MFC的一个对话框工程,框架很简单,其中大量引用和调用VTK和OpenCV的函数,还有一些控件操作,屏幕交互,计算分析等。最终希望生成一个dll,提供一个函数接口,调用该函数弹出设计的对话框程序,之所以没有一开始就采用dll,主要是因为对方的需求突然有变。
方案一:新建一个DLL工程,比如MyDLL,将原工程中的*.h 和 *.cpp文件复制到新建的工程目录下,并添加到工程中。同样的方式导入res文件,不用再去建一遍对话框。我的做法是,新建的工程和原工程同名,然后仅在MyDLL.h中添加相应的头文件,最关键的一步是添加dll函数接口:
在MyDLL.h中添加
extern "C" __declspec(dllexport) void ShowDentalViewDlg(void);// "C"很必要,多个教程中强调,不加的话,由于C++编译器为函数添加类型名,可能导致在其他程序中调用无法找到对应的函数
然后在MyDLL.cpp中实现它:
extern "C" __declspec(dllexport) void ShowDentalViewDlg(void)
{
CDentalViewDlg dlg;
dlg.DoModal();
}
不用多解释,CDentalViewDlg 就是我想调出的对话框类。
编译后可以再debug文件夹下找到对应工程的dll和lib文件。
然后可能你会说之后就是调用了啊,我原来也是这么做的,但是这却让我绕了很大的圈,调用失败后只是一味地怀疑这种方法无效,去忽略了一个非常关键的问题,我生成的dll和lib参考完整吗?很多人压根就不会想到之前exe下没问题的程序,生成dll和lib怎么会有参考不完整问题呢?这个问题我遇到了,虽然我不知道为什么。下面说说如何检查生成的dll的参考完整性。
我用的是VC自带的工具depends.exe, 很简单,打开dll文件后,可以看到其参考的dll还有其接口函数。我发现之所以我生成的dll和lib在测试程序中调用失败,是因为生成的dll中参考dll中有黄颜色的不完整项,因为我大量调用VTK和Opencv,而它们又调用很多其他的库,最终缺少msvcr80.dll和msvcp80.dll,解决办法自然是添加这两个dll了,好不容易找到了,我不知道原先exe下是怎么找到它们的,因为在系统盘中好像我没有发现这两个文件的踪迹。看到黄色dll都没了,心里好不欣慰。
生成完整的dll和lib 后,最后一步就是测试了,这一步只要按部就班就好了。首先将lib和dll拷贝到测试工程中,添加它们的路径(这一步有多种方式:可以像添加VTk库一样,也可以用#pragma comment(lib,"***"))。不要忘了最重要的一步,拷贝并添加所有dll中用到的*.h文件到测试程序。最后为测试控件添加响应函数:
void CTestDLLDlg::OnBnClickedButton1()
{
ShowDentalViewDlg();
#pragma comment(lib,"..\\debug\\DentalView")//这里冗余了,其实就是给定lib所在路径,相对和绝对都可以
}
这样就可以编译、测试了。
第二种方案
直接在原工程中修改配置,在 项目/项目属性/配置属性/常规/配置类型中将.exe改为dll,添加接口函数(在项目的.h中声明在.cpp中定义):
extern "C" void show()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDentalViewDlg * dlg = new CDentalViewDlg();
dlg->DoModal();
};
然后编译,这次可以看到在debug文件夹中相比方案一少了.lib文件。
其调用方式也不太一样:
void CTestDLLDlg::OnBnClickedButton1()
{
typedef void (*Msgs)(void);
HINSTANCE h = ::LoadLibrary(L"DentalView.dll");
if (h == NULL) return;
Msgs dllfun = (Msgs)::GetProcAddress(h, "show");
dllfun();
::FreeLibrary(h);
}
这里要注意,如前面参考网页所述,需将工程的.cpp中的InitInstance()函数中调用dlg.DoModel()相关语句注释掉,以便其不在导入dll时就弹出对话框。
但这个方法我遇到一个问题,LoadLibrary(L"DentalView.dll")返回值总是为null,目前还在测试,有待进一步补充。
以上就是自己对MFC 的exe转dll的尝试和学习心得,有其他方法的欢迎补充。