VC自动加载动态库的一点想法
2015-12-08 19:00
267 查看
动态库的加载方式有静态加载和动态加载。动态加载有个好处就是只要接口没变,重新编译之后,调用者不用重新编译。
之前在某网站看到有个大神说c++作为一门面向对象语言,其使用便捷性远远不如C#以及java,还编写了一个自动调用动态库的过程。确实c++对象封装上面不如C#和Java,也不如他们使用便捷,但是c++的功能非常强大的,特别是其强大的指针功能(这是从C语言继承来的)。尽管指针在使用时容易造成内存问题,但我们不能否认指针功能的强大,尤其是万能指针(void*),基本上所有指针都可转化为(void*)。
c++在动态库加载处理上确实有点麻烦,下面是c++和c#调用动态库流程的比较:
从表中可以看出C++动态加载动态库确实有点繁琐,它不像c#那样将动态库加载、获取接口地址、释放动态库过程封装起来自动实现,无需开发者去实现。所以大神用下面的C宏实现自动化动态库加载,感受一下,应该容易看懂:
调用方式如下:
分析:
DLLIMPORTCLASSBEGIN(User32,"User32.dll")
FUNCTIONENTRY(int (WINAPI *MessageBoxA)(HWND,LPCTSTR,LPCTSTR,UINT),MessageBoxA)
DLLIMPORTCLASSEND()
这段宏具体展开就比较清晰了,相当于定义了类User32,继承自DllImportAttribute,该类有两个公共方法:Init()加载动态库,UnInit()释放动态库。
然后定义函数指针类型:
typedef int (WINAPI *MessageBoxA)(HWND,LPCTSTR,LPCTSTR,UINT);
MessageBoxA MessageBoxAPtr;
然后宏DLLIMPORTCLASSIMPLEMENT(User32)定义了类User32的对象 gUser32DLL,构造的时候自动将对象放入 gDllImportList链表中,
DllImportInit()函数将链表中的动态库全部加载。
然后DLLIMPORTCALL(CLASS,ENTRYPOINT)宏获取接口地址然后调用接口。
DllImportUnInit()接口释放链表中的所有对象的动态库释放。
这个方法确实不错,C++也只能这样实现自动加载动态库,将这段代码放到头文件中,这样以后使用动态库时不必再去实现加载动态库流程。
但是有个问题,如果获取接口地址失败或者接口不存在,这样GetProcAddress会返回iNULL,这时这段代码直接就崩溃了。另外这段代码可以稍微改进一下,利用c++的构造与析构来实现动态库的加载与释放,这也是C++的一个特点,可以将内存释放放到析构函数里面,在调用之前检查一下接口是否获取成功。下面是具体实现:
调用的时候如下:
这样DllImportInit()、DllImportUnInit()这两个方法完全被砍掉。
之前在某网站看到有个大神说c++作为一门面向对象语言,其使用便捷性远远不如C#以及java,还编写了一个自动调用动态库的过程。确实c++对象封装上面不如C#和Java,也不如他们使用便捷,但是c++的功能非常强大的,特别是其强大的指针功能(这是从C语言继承来的)。尽管指针在使用时容易造成内存问题,但我们不能否认指针功能的强大,尤其是万能指针(void*),基本上所有指针都可转化为(void*)。
c++在动态库加载处理上确实有点麻烦,下面是c++和c#调用动态库流程的比较:
c++ | c# |
(1)、定义动态库句柄:HINSTANCE hModule; 定义函数指针接收接口地址: typedef int (WINAPI* Sendmessage)( IntPtr hWnd, int Msg, int wParam, ref lParam lparam); Sendmessage sendPtr; (2)、加载动态库:hModule = LoadLibrary("User32.dll") (3)、获取接口地址: sendPtr= (Sendmessage)GetProcAddress(hModule,"SendMessage") (4)、调用接口 (5)、释放动态库:FreeLibray(hModule) | (1)、加载动态库: [DllImport("User32.dll", EntryPoint = "SendMessage")] (2)、接口声明 private static extern int SendMessage( IntPtr hWnd, int Msg, int wParam, ref lParam lparam); (3)、调用接口 |
#include <wtypes.h> #include <list> using namespace std; class DllImportAttribute { public: //加载动态链接库 virtual BOOL Init() = 0; //卸载动态链接库 virtual BOOL UnInit() = 0; }; BOOL DllImportInit();//加载所有库 BOOL DllImportUnInit();//卸载所有库 //声明全局变量动态库列表,此时并没有分配空间,除非声明时初始化 extern list<DllImportAttribute*> gDllImportList; #define DLLIMPORTCLASSBEGIN(CLASS,DLLPATH) class CLASS; \ extern CLASS g##CLASS##DLL; \ class CLASS : public DllImportAttribute \ { \ protected: \ HMODULE m_hModule; \ public: \ CLASS(): m_hModule(NULL) \ { \ gDllImportList.push_back(this); \ } \ virtual BOOL Init() \ { \ m_hModule = LoadLibrary(DLLPATH); \ return (m_hModule != NULL); \ } \ virtual BOOL UnInit() \ { \ return FreeLibrary(m_hModule); \ } \ #define FUNCTIONENTRY(ENTRYTYPE,ENTRYPOINT) public: \ typedef ENTRYTYPE; \ ENTRYPOINT ENTRYPOINT##Ptr; \ public: \ ENTRYPOINT ENTRYPOINT##Func(){ \ ENTRYPOINT##Ptr = \ (ENTRYPOINT)GetProcAddress(m_hModule,#ENTRYPOINT); \ return ENTRYPOINT##Ptr; \ } \ #define DLLIMPORTCLASSEND() }; #define DLLIMPORTCLASSIMPLEMENT(CLASS) CLASS g##CLASS##DLL; #define DLLIMPORTCALL(CLASS,ENTRYPOINT) g##CLASS##DLL.ENTRYPOINT##Func() list<DllImportAttribute*> gDllImportList; //加载动态库 BOOL DllImportInit() { BOOL bSuccess = TRUE; for (list<DllImportAttribute*>::iterator iter = gDllImportList.begin(); iter != gDllImportList.end(); iter++) { bSuccess &= (*iter)->Init(); } return bSuccess; } //卸载动态库 BOOL DllImportUnInit() { BOOL bSuccess = TRUE; for (list<DllImportAttribute*>::iterator iter = gDllImportList.begin(); iter != gDllImportList.end(); iter++) { bSuccess &= (*iter)->UnInit(); } return bSuccess; }
调用方式如下:
//类定义 DLLIMPORTCLASSBEGIN(User32,"User32.dll") FUNCTIONENTRY(int (WINAPI *MessageBoxA)(HWND,LPCTSTR,LPCTSTR,UINT),MessageBoxA) DLLIMPORTCLASSEND() int main(int argc, char* argv[]) { //对象声明 DLLIMPORTCLASSIMPLEMENT(User32) //调用 DllImportInit(); DLLIMPORTCALL(User32,MessageBoxA)(NULL,"Message","Hips",MB_OK); DllImportUnInit(); return 0; }
分析:
DLLIMPORTCLASSBEGIN(User32,"User32.dll")
FUNCTIONENTRY(int (WINAPI *MessageBoxA)(HWND,LPCTSTR,LPCTSTR,UINT),MessageBoxA)
DLLIMPORTCLASSEND()
这段宏具体展开就比较清晰了,相当于定义了类User32,继承自DllImportAttribute,该类有两个公共方法:Init()加载动态库,UnInit()释放动态库。
然后定义函数指针类型:
typedef int (WINAPI *MessageBoxA)(HWND,LPCTSTR,LPCTSTR,UINT);
MessageBoxA MessageBoxAPtr;
然后宏DLLIMPORTCLASSIMPLEMENT(User32)定义了类User32的对象 gUser32DLL,构造的时候自动将对象放入 gDllImportList链表中,
DllImportInit()函数将链表中的动态库全部加载。
然后DLLIMPORTCALL(CLASS,ENTRYPOINT)宏获取接口地址然后调用接口。
DllImportUnInit()接口释放链表中的所有对象的动态库释放。
这个方法确实不错,C++也只能这样实现自动加载动态库,将这段代码放到头文件中,这样以后使用动态库时不必再去实现加载动态库流程。
但是有个问题,如果获取接口地址失败或者接口不存在,这样GetProcAddress会返回iNULL,这时这段代码直接就崩溃了。另外这段代码可以稍微改进一下,利用c++的构造与析构来实现动态库的加载与释放,这也是C++的一个特点,可以将内存释放放到析构函数里面,在调用之前检查一下接口是否获取成功。下面是具体实现:
#include <wtypes.h> #include <list> using namespace std; #define DLLIMPORTCLASSBEGIN(CLASS,DLLPATH) class CLASS; \ extern CLASS g##CLASS##DLL; \ class CLASS \ { \ protected: \ HMODULE m_hModule; \ public: \ BOOL bSuccess; \ public: \ CLASS(): m_hModule(NULL) \ { \ m_hModule = LoadLibrary(DLLPATH); \ m_hModule == NULL ? bSuccess=FALSE:bSuccess=TRUE; \ } \ ~CLASS() \ { \ if(m_hModule) \ FreeLibrary(m_hModule); \ } \ #define FUNCTIONENTRY(ENTRYTYPE,ENTRYPOINT) public: \ typedef ENTRYTYPE; \ ENTRYPOINT ENTRYPOINT##Ptr; \ public: \ ENTRYPOINT ENTRYPOINT##Func(){ \ if(!bSuccess) return NULL; \ ENTRYPOINT##Ptr = \ (ENTRYPOINT)GetProcAddress(m_hModule,#ENTRYPOINT); \ return ENTRYPOINT##Ptr; \ } \ #define DLLIMPORTCLASSEND() }; #define DLLIMPORTCLASSIMPLEMENT(CLASS) CLASS g##CLASS##DLL; #define DLLENTRYEXIST(CLASS,ENTRYPOINT) (g##CLASS##DLL.ENTRYPOINT##Func() == NULL?FALSE:TRUE) #define DLLIMPORTCALL(CLASS,ENTRYPOINT) g##CLASS##DLL.ENTRYPOINT##Ptr
调用的时候如下:
//类定义 DLLIMPORTCLASSBEGIN(User32,"User32.dll") FUNCTIONENTRY(int (WINAPI *MessageBoxA)(HWND,LPCTSTR,LPCTSTR,UINT),MessageBoxA) DLLIMPORTCLASSEND() int main(int argc, char* argv[]) { //对象声明 DLLIMPORTCLASSIMPLEMENT(User32) //调用 if (DLLENTRYEXIST(User32,MessageBoxA)) { DLLIMPORTCALL(User32,MessageBoxA)(NULL,"Message","Hips",MB_OK); } return 0; }
这样DllImportInit()、DllImportUnInit()这两个方法完全被砍掉。
相关文章推荐
- 创业公社:亦庄分中心开业 借好创业东风
- 图像处理卷积算法实现
- 初到CSDN
- 图片缩放功能
- Basic脚本解释器移植到STM32
- 管理工具MongoVUE使用
- 图像处理卷积算法实现
- div盒布局
- css3画三角形的原理
- C#驱动及应用
- android greenDao SQLite数据库操作使用的工具
- boost.spirit之解析C++头文件
- 如何创建swift工程
- Matlab中图像函数大全
- 如何判断PHP数组是否为空
- java同步synchronized详解
- 虚拟机、Linux及Xshell问题汇总(最终方案)【慕课网笔记】
- linux下的权限详解
- 源码探索系列1---Handler与HandlerLeak的那些事
- OpenJudge_P7833 幂的末尾