• 使用DLL中的资源


    我很早的时候写过一篇MFC中CDialog与其对话框资源的绑定,但这几天在MFC DLL上做了一些工作后发现当时的理解实在肤浅。说不定过了几年再回头看看目前这篇文章,又觉得本文也是鬼话连篇了吧,哈哈。

    使用DLL中的资源面临的一个主要问题是,DLL和EXE中都有资源集,但是程序在运行态到底会去哪个资源集中找常常令我们疑惑。
    考虑如下的经典情况:

    在新建MFC DLL工程时选中Regular DLL using shared MFC DLL选项,新建一个与MFC自身DLL共享的DLL。在新DLL中新建一个ID名为IDD_DLLDIALOG的对话框资源。在这个DLL中导出一个ShowDialog()函数,内容如下:

    1 extern "C" void Show Dialog()
    2 {
    3     CDialog dlg(IDD_DLLDIALOG);
    4     dlg.DoModal();
    5 }

    新建一个MFC的EXE工程,导入刚刚新建的DLL工程的lib和dll,将OK按钮的响应函数设置为:  ShowDialog();  编译链接通过后,我们运行起程序来会发现点击OK按钮后什么反应都没有!怎么会这样?我们不是明明在DLL里设定好了IDD_DLLDIALOG吗?请大家注意“在新建MFC DLL工程时选中Regular DLL using shared MFC DLL”这句话,如果我们当时选的是“Regular DLL with MFC statically linked”又会是什么情况呢?又能正常显示了!

    使用静态连接的方式访问MFC库DLL,会把MFC库DLL与工程本身一起打包,而共享的方式则会在运行时去系统路径中动态加载MFC库DLL。我们可以这么理解以上的现象:如果新建的DLL使用共享的方式访问库DLL,当EXE程序运行时,实际上有两套上下文系统在运作,而静态连接则只有一套上下文系统了,因此在静态连接时会需要进行模块状态的切换。

    当我们新建的是共享方式访问 库DLL的DLL时,要解决上述问题比较通用的做法是在ShowDialog函数里面加上一句AFX_MANAGE_STATE(AfxGetStaticModuleState());

    AfxGetStaticModuleState()的原型为:

    AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( );

    即当前模块的ModuleState指针。

    AFX_MANAGE_STATE宏则会以传入的指针为参数去在栈上新建一个类,此类会做哪些事相信聪明的你也能猜到了.没错,它会在构造函数中存下当前的ThreadState所指向的ModuleState指针,再将传入的指针指定为当前ThreadState所指向的指针。等到这个类的实例被析构是,又将其改回去。

    令外一种方法是使用AfxGetResourceHander()配合AfxSetResourceHander(HINSTANCE)了(这也正是我在MFC中CDialog与其对话框资源的绑定中介绍的方法)。

    如果我们不想在EXE中通过间接的调用DLL导出的ShowDialog(),而是直接想在EXE的OnBnClickedOk()中生成一个使用DLL中对话框资源的对话框呢?这时AFX_MANAGE_STATE(AfxGetStaticModuleState())就派不上用场了(因这它是用在DLL工程中的)。最好的办法是用AfxSetResourceHander(HINSTANCE)配合GetModuleHandle(LPCTSTR)。

    GetModuleHandle(LPCTSTR str)将会根据传入的字符串得到相应的模块句柄。注意,EXE和DLL都可以被看作是模块。当传入为空(NULL)时,将得到当前进程(EXE)的HINSTANCE句柄。使用GetModuleHandle要保证相应的dll已经被加载,也许你会说这个条件不是废话么,但是这确实是很容易犯错的地方。

    如果是用静态加载DLL的方式(即直接把生成的那个.lib文件导入),而之前又从未调用过这个dll中的任何函数或变量的话,GetModuleHandle只会得到一个空值。因为系统会直到有某个语句调用过dll里面的东西时才会将dll真正加载。

    例如:

    1 void CUseDllDlg::OnBnClickedOk()
    2 {
    3     HINSTANCE temp = AfxGetResourceHandle();
    4     AfxSetResourceHandle(GetModuleHandle(_T("RegDll")));
    5     CDialog dlg(4000);
    6     dlg.DoModal();
    7     AfxSetResourceHandle(temp);
    8 }

    上面的这份代码运行时会报错,因为AfxSetResourceHandle在检查入参时发现了个空值。而下面的代码则不会有问题:

    1 void CUseDllDlg::OnBnClickedOk()
    2 {
    3     ASSERT(TestOnly() == 1);
    4     HINSTANCE temp = AfxGetResourceHandle();
    5     AfxSetResourceHandle(GetModuleHandle(_T("RegDll")));
    6     CDialog dlg(4000);
    7     dlg.DoModal();
    8     AfxSetResourceHandle(temp);
    9 }

    其中TestOnly也是从RegDll中导出的函数,它什么都不做,只是返回一个1。我们使用它只是为了把RegDll加载进来。当然了,如果我们直接使用动态加载的方法,像下面这样直接LoadLibrary那更是没有问题了。

    1 void CUseDllDlg::OnBnClickedOk()
    2 {
    3     HMODULE hm= LoadLibrary(_T("..//Debug//RegDll.dll"));
    4     HINSTANCE temp = AfxGetResourceHandle();
    5     AfxSetResourceHandle(GetModuleHandle(_T("RegDll")));
    6     CDialog dlg(4000);
    7     dlg.DoModal();
    8     AfxSetResourceHandle(temp);
    9 }

    不过眼尖的读者可能会立即发现其实在上面这种情况下使用GetModuleHandle不是最好的选择。因为直接来个AfxSetResourceHandle(hm)就可解决问题了。

    from:http://www.thankcreate.com/c/275

  • 相关阅读:
    HDU4385Moving Bricks【状压DP】
    用位运算实现加减法
    hdu 1874(最短路 Dilkstra +优先队列优化+spfa)
    codeforces 782B The Meeting Place Cannot Be Changed+hdu 4355+hdu 2438 (三分)
    hdu 1542(线段树+扫描线 求矩形相交面积)
    hdu 2602(经典01背包)
    hdu 1698(线段树区间更新)
    hdu 1754(单点更新 ,区间最大值)
    NYOJ 寻找最大数
    hdu 2222(AC自动机模版题)
  • 原文地址:https://www.cnblogs.com/lidabo/p/2802028.html
Copyright © 2020-2023  润新知