文章目录
MFC作为微软的长期主力开发套件之一,之前很多设备开发的C/S端界面都是通过MFC框架来做的,而在我自己的CEF项目中,会集成很多之前的DLL来完成设备驱动和开发。所以,在CEF项目开发中,肯定会碰到一些和原有的MFC代码集成的工作。
最近收集了几个在集成工作中的坑,自己做个记录,如果能帮到各位,请各位点赞收藏。感谢
DLL 导出方式,函数指针的坑
在其中的一个DLL集成中,这个DLL提供的头文件中有如下的一个函数:
virtual int GetInfo(CString& nVersion) = 0;
这里的形参CString就是MFC框架中的一个字符串库,因为我嫌麻烦,而且判断我在我自己的项目中不需要用到这个函数,也不想在CEF框架中再将MFC的一些库打包进来,这样会降低编译速度,也会增加项目打包后的大小。
所以我第一次的解决方法就是将这个函数从头文件中注释掉。
灾难就是因为这一条注释引起的。
问题出现
注释掉这个函数后,集成顺利进行,而且能顺利控制设备完成一些操作。但发现的问题是,这个DLL提供的所有设备驱动函数中,有部分可以执行,有部分不能执行,而且不能执行的错误很夸张,直接是内存越界,整个程序coredown:
这让我百思不得其解,还找提供DLL的同事增加日志等,结果发现出错的操作函数直接无法进入。
然后又对了编译参数等等,所有的编译参数都对了一遍,结果还是不行。因为这个问题项目停了两三天。
问题解决
直到有一次查看了一下这个DLL的导出方式:
extern "C"
{
DLL_API APIHANDLE* CreateHandleObj();
DLL_API void DeleteHandleObj(APIHANDLE*);
}
也就是说,这个DLL是导出的这个操作类对象,而不是方法。
- 如果DLL导出的是方法的话,那么在DLL会把这些方法加载到内存后,将这些方法的内存地址都导出,在程序中调用这些方法的话,就可以直接通过函数指针的方式访问这些地址,完成函数调用。
- 如果DLL导出的是类对象的话,那么DLL只会导出这个对象的函数首地址,然后通过函数指针的方式,一个一个的去做函数移动,然后移动到响应内存位置,再完成函数调用。
那么我的问题就出在这里,比如DLL中有20个函数,我将上面那个函数注释掉之后,上层的应用程序就只会认为DLL中有19个函数。
而注释掉的函数如果是在第10个位置的话,那么前9个函数的调用都是没问题的,都可以通过函数指针的方式移动获取到相应的内存地址。
但是这个函数和这个函数后续的函数地址就会发生错位,就会发生上面提到的内存越界访问的问题!!
解决的方案就是恢复这个函数。
CEF集成MFC
如果不注释那个函数,就要解决CString的问题,也就是要集成MFC。
afxwin.h
一般所有的MFC程序都会集成这个头文件: afxwin.h,这个头文件包括了大部分的MFC基础类,当然也包括了CString这个类。
预编译
visual studio提供了预编译的能力,像mfc这种固定的框架头文件,没必要放在业务头文件中,在visual studio创建的项目中,一般都会有一个pch.h的文件,这个头文件中的内容就是visual studio会提前编译好的。
所以,集成MFC的第一步就是将afxwin.h放到pch.h中:
// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
#ifndef PCH_H
#define PCH_H
// 添加要在此处预编译的标头
#include "framework.h"
#include <afxwin.h> // MFC 核心组件和标准组件
#endif //PCH_H
在visual studio中可以配置这个预编译能力:
编译参数修改
集成MFC还需要修改一个编译参数:
这里需要选择MFC库,使用DLL的话,就表示在你的程序中,MFC的功能是以DLL方式存在的,如果是静态库的话,就会把MFC的lib库打入到你的应用程序里。
我选择的是DLL库。
DLL重命名
最后一个坑,因为我集成MFC的DLL也是一个DLL程序,在没有集成MFC之前,我的DLL命名就是visual studio默认的命名:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
但是集成了MFC之后,也就是上面提到的集成了MFC的DLL之后,再编译就会提示DllMain重定义,也就是说和MFC的DLL中的DllMain冲突了,所以这里需要改个名字即可:
BOOL APIENTRY DllMainMyOwn( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
顺利解决问题!!