`
congfeng02
  • 浏览: 200078 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

在DLL里加载IE控件

阅读更多

在DLL里加载IE控件

原文地址:http://qzone.qq.com/blog/61960104-1241083040

以前写MFC的DLL的时候,总会在自动生成的代码框架里看到提示,需要在每一个输出的函数开始添加上AFX_MANAGE_STATE(AfxGetStaticModuleState())。一直不明白这样做的含义,也一直没有这样做,而且代码也工作得好好的,所以感觉这好像一句废话。

最近的项目中,需要在DLL里使用MFC生成界面,这才发现一旦资源放在不同的动态库里,而且还和多线程搅和在一起的时候,事情就变得异常的复杂,以前对MFC的一知半解已经不足与应付了。程序莫名的崩溃,莫名的ASSERT,资源怎样也装载不起来,为什么呢?每次,总是尝试着,在每一个线程的开始,把AFX_MANAGE_STATE(AfxGetStaticModuleState())添加上去,或者在某些地方用AfxSetResourceHandler()一把,然后问题就解决了,但是不是很明白到底是怎么回事,总感觉这种解决办法让人很不安心,仿佛在下一秒问题又会突然冒出来。

前天,这个问题终于发挥到了极致,任我花费了好几个小时,怎样的尝试都不能成功,在项目的关键时候发生这种事情,让我暗暗发誓以后再也不用MFC了。正像很多的电影情节一样,事情最后还是得到了解决,这次我决定不能再这么算了,一定要把这个事情理解得明明白白。

在这里,我遇到的问题就是,如何让DLL里的界面代码使用该DLL的资源(Resource),如何在工作线程里加载有IE控件的对话框?

我问同事,他们是如何实现DLL资源切换的?AFX_MANAGE_STATE(AfxGetStaticModuleState())这就是他们的答案,一如微软的推荐,原来就是这么简单啊!让我们来看看,这句代码到底做了什么?

#define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE2 _ctlState(p);

AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pNewState)

{

m_pThreadState = _afxThreadState;

m_pPrevModuleState = m_pThreadState->m_pModuleState;

m_pThreadState->m_pModuleState = pNewState;

}

_AFXWIN_INLINE AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2()

{ m_pThreadState->m_pModuleState = m_pPrevModuleState; }

原来,就是定义一个局部的对象,利用其构造和析构函数在函数的入口和函数的出口进行State状态的切换,我猜AfxGetStaticModuleState()一定是获取当前代码所在DLL的State。

果然,请看

static _AFX_DLL_MODULE_STATE afxModuleState;

AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState()

{

AFX_MODULE_STATE* pModuleState = &afxModuleState;

return pModuleState;

}

class _AFX_DLL_MODULE_STATE : public AFX_MODULE_STATE

// AFX_MODULE_STATE (global data for a module)

class AFX_MODULE_STATE : public CNoTrackObject

{

...

CWinApp* m_pCurrentWinApp;

HINSTANCE m_hCurrentInstanceHandle;

HINSTANCE m_hCurrentResourceHandle;

LPCTSTR m_lpszCurrentAppName;

BYTE m_bDLL; // TRUE if module is a DLL, FALSE if it is an EXE

...

COccManager* m_pOccManager;

...

这里不得不说,MFC把很多的数据都堆放在这里,搞得很复杂,结构性非常的差。

}

afxModuleState是dll的静态成员,自然可以被同样的dll里的代码所访问,但是何时初始化的?

extern "C"

BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)

{

...

AfxWinInit(hInstance, NULL, _T(""), 0);

...

}

BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

ASSERT(hPrevInstance == NULL);

// handle critical errors and avoid Windows message boxes

SetErrorMode(SetErrorMode(0) |

SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);

// set resource handles

AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

pModuleState->m_hCurrentInstanceHandle = hInstance;

pModuleState->m_hCurrentResourceHandle = hInstance;

...

}

原来在DLL的入口函数,用该DLL的hInstance初始化了该结构。

到这时候,我们还是不明白,为什么要进行资源切换?前面开始的_afxThreadState到底是什么?好像跟Thread有关系,到底是什么呢?

THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)

#define THREAD_LOCAL(class_name, ident_name) \

AFX_DATADEF CThreadLocal<class_name> ident_name;

template<class TYPE>

class CThreadLocal : public CThreadLocalObject

再往下跟踪,发现其实代码越发生涩难懂,但是基本的功能就是访问当前此行代码的线程的私有数据。所谓线程的私有数据,就是说,不同的线程执行同样的一段代码,得到的数据可能是不同的。这才想起来,MFC的很多句柄啦,都是保存在全局的Map里的,而且放在线程的私有数据区里,所以跨线程传递MFC对象是很不安全的。但是,MFC为什么要这么做呢?这个问题,到目前为止,我还是搞不明白。

还是回到开始的代码,资源切换到底是如何进行的?

int CDialog::DoModal()

{

...

HINSTANCE hInst = AfxGetResourceHandle();

if (m_lpszTemplateName != NULL)

{

hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);

HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);

hDialogTemplate = LoadResource(hInst, hResource);

...

}

_AFXWIN_INLINE HINSTANCE AFXAPI AfxGetResourceHandle()

{ ASSERT(afxCurrentResourceHandle != NULL);

return afxCurrentResourceHandle; }

#define afxCurrentResourceHandle AfxGetModuleState()->m_hCurrentResourceHandle

AFX_MODULE_STATE* AFXAPI AfxGetModuleState()

{

_AFX_THREAD_STATE* pState = _afxThreadState;

AFX_MODULE_STATE* pResult;

if (pState->m_pModuleState != NULL)

{

// thread state's module state serves as override

pResult = pState->m_pModuleState;

}

else

{

// otherwise, use global app state

pResult = _afxBaseModuleState.GetData();

}

ASSERT(pResult != NULL);

return pResult;

}

原来MFC的对话框装载资源是通过获取当前线程对应的ModuleState保存的ResourceHandler来装载资源的。所以,DLL里的代码,需要在函数的入口,首先把当前执行线程的ModuleState换成该Dll的State,这样才能装载该dll的资源!这时候,我突然明白过来,为什么需要要依赖线程的私有数据来保存ModuleState,其实确切的说是传递!--这其实是因为CDialog是存放在另一个DLL里的,比如MFC40.dll,如果以共享模式连接MFC库的话。而用户自己编写的CDialog的子类并不放在CDialog同样的Dll里,他们如何来传递这个资源句柄呢?两种解决办法:1,利用参数传递。2,存放在一个公共的地方。前者需要增加参数,显得很麻烦,Win32的API好像就是这样实现的吧?后者,需要确定这个公共地方在何处?这让人想起来,建立一个公共的动态库?由主程序的提供?再多说一句,J2EE里有一个容器的概念(COM+好像也有,不知道.NET是如何的),组件都是生存在容器里,这时候我们就可以设想把该数据存放在容器里。不管怎样,MFC的实现就是放在线程的私有数据区,不需要公共的动态库,也不需要麻烦主程序,它自己就搞定了!它自以为很好的解决方式,很完美,却引发了我们的一系列的问题,特别是不明白就里的人。

关于资源装载,问题似乎已经解决了,但是还有一点点小麻烦就是,我实现的dll不是以普通的输出函数进行输出的,而是输出类,我可不想在每一个类的成员函数里添加AFX_MANAGE_STATE(AfxGetStaticModuleState())。怎么办呢?既然已经知道了资源切换的原理,我们添加两个输出函数,分别对应AFX_MAINTAIN_STATE2的构造和析构函数,在类的使用前后调用,就可以了。或者,分别放在类的构造和析构函数里。又或者,就声明为成员变量。无论怎样,需要保证的一点就是资源的切换要正确嵌套,不可交叉--这种情况在不同的DLL之间交叉调用的时候会发生。

好了,现在DLL里的资源可以正确调用了,但是在当Dialog上包含有IE控件的时候,我们还是失败了,为什么呢?我知道对于ActiveX控件,Dialog需要做一些特殊的处理,AfxEnableControlContainer(),我也知道,要使用COM,需要CoInitialize(),但是我一直没有想过需要两个一起用才能把IE弄出来,但是最后就是这样的。奇怪的是,如果不是在工作线程里,根本不需要CoInitialize(),就能装载IE控件的,这个暂时就先不管了。

PROCESS_LOCAL(COccManager, _afxOccManager)

void AFX_CDECL AfxEnableControlContainer(COccManager* pOccManager)

{

if (pOccManager == NULL)

afxOccManager = _afxOccManager.GetData();

else

afxOccManager = pOccManager;

}

#define afxOccManager AfxGetModuleState()->m_pOccManager

这样看来,这个_afxOccManager应该是属于整个进程的,整个进程只有一个,就在那个定义它的dll里。但是,你需要把该对象(或者创建一个自定义的)传给ModuleState(请注意前面的AFX_MODULE_STATE里就包含了该属性),也就是要AfxEnableControlContainer()一下,这样特定的ModuleState就有了OccManager的信息!但是,请注意,一定要在目标dll里,正确切换了资源之后,才能进行,如下:

AFX_MANAGE_STATE(AfxGetStaticModuleState());

CoInitialize(NULL);

AfxEnableControlContainer();

至此,这个困扰我很久的问题,终于脉络清晰起来了。

分享到:
评论

相关推荐

    Delphi封装DLL到IE OCX控件

    将DLL做成RES编译进工程,运行时释放资料保存到系统TEMP目录下调用,WIN32模式下调用一切正常,用Delphi自带的WebBrowser调用也正常,但在IE下调用DLL提示无法加载,望高手解决,谢谢。

    解决vb中ie控件失效的办法

    这些变化可能与VB6中使用的IE控件(通常是MSComCtrl.ocx或SHDocVw.dll)的旧版接口不兼容,导致控件在新版本的IE中运行异常。 解决VB中IE控件失效的方法主要有以下几步: 1. **注册表调整**:问题的核心在于注册表...

    iertutil.dll IE7.0

    5. **ActiveX 控件管理**:管理与浏览器相关的 ActiveX 控件,确保它们的正确加载和安全执行。 **遇到 iertutil.dll 错误的解决办法** 如果用户在使用 Internet Explorer 7.0 时遇到 `iertutil.dll` 文件相关的...

    IE打印控件

    IE打印控件是用于在Internet Explorer浏览器中实现高效、自定义打印功能的一种组件。它通常由一系列DLL动态链接库和安装程序组成,旨在增强用户在网页浏览时的打印体验,提供比浏览器内置打印功能更丰富的选项和设置...

    QTP/LoadRunner录制IE的控件BHOManager.dll

    不能录制IE时,请检查IE控件BHOManager.dll是否已有,如有则启用,如果没有可下载BHOManager.dll,放到C:\Windows\System32目录,并使用管理员用户注册。

    IE 加载jre时需要的dll文件

    5. **shdocvw.dll** 和 **actxprxy.dll**:这两个DLL文件并不是直接与Java相关的,但它们对于确保ActiveX控件在IE中的正常工作至关重要。由于Java Applet通常会使用ActiveX技术,因此这些文件也是解决问题的关键之一...

    vc做的ocx控件,ie下断点调试问题

    标题“vc做的ocx控件,ie下断点调试问题”所描述的情况是一个典型的问题,即OCX控件在VC(Visual C++)的测试容器中可以正常进行断点调试,但当它被嵌入到IE浏览器中时,无法进入断点。这通常涉及到调试器的设置、IE...

    VBS加载webview2控件代替ie的webbrowser(Edge-Chromium谷歌内核)

    VBS加载微软网页控件webview2(Edge-Chromium谷歌内核) 代替了ie的webbrowser控件,效果类似: set IE = CreateObject("InternetExplorer.Application") IE.Visible = true IE.Navigate "https://www.baidu.com/"

    FreeTextBox.dll 控件

    FreeTextBox.dll 是一款广泛应用于网页开发中的第三方文本编辑控件,尤其在ASP.NET环境中非常常见。这个控件为开发者提供了一个功能丰富的富文本编辑界面,用户可以像在Word文档中那样进行文字编辑、格式设置、插入...

    明宇报表控件IE8版本

    **明宇报表控件在IE8环境下的应用与配置** 明宇报表控件是一款广泛应用于企业级报表开发的工具,它提供了丰富的报表设计功能和强大的数据处理能力,使得开发者能够轻松创建复杂的数据展示和分析报表。针对IE8浏览器...

    vb iemenu控件

    这种控件在数据库管理系统和企业人事系统中尤其有用,因为它提供了一种直观且用户友好的方式来组织和访问功能。 IEMenu控件允许开发者自定义菜单项,设置菜单层次结构,并且可以响应用户的点击事件,从而执行相应的...

    Delphi程序中应用IE浏览器控件

    #### 三、添加IE浏览器控件到Delphi项目 1. **注册控件**:在Delphi中,通过“Component Palette”中的“Registered Controls”面板找到“Microsoft Internet Controls”。注册完成后,您可以在组件面板中看到...

    [注册表]注册DLL控件 让IE浏览器复活

    DLL文件是Windows操作系统中用于存储可重用代码和数据的库,例如,actxprxy.dll和shdocvw.dll等文件在IE浏览器的正常运行中起着重要作用。当这些文件出现问题时,可以通过在命令行中使用`regsvr32`命令来重新注册...

    Delphi开发IEActiveX插件封装外部DLL完整示例

    将外部DLL文件编译入ActiveX资源(res),运行时再将DLL释放到指定位置,供ActiveX调用,特别注意:DLL或EXE不能加壳,编译的不要做任何改动,否则IE环境下无法加载。 内附完整JAVASCRIPT与ActiveX互通消息的HTML...

    bat脚本设置IE浏览器使用OA控件ocx及修改注册表

    通过bat脚本修改注册表,可以一键完成:添加浏览器受信任站点,设置浏览器兼容性视图,修改浏览器中安全站点自定义级别中关于ActiveX的选项为启用,关闭IE浏览器数据执行保护及其他安全设置,自动注册ocx及dll控件

    window平台下内嵌HTML控件到窗口中

    这种技术主要依赖于Microsoft的ActiveX技术,它允许开发人员在本地应用程序中嵌入Web浏览器控件(也称为IE控件或WebBrowser控件)。 在C语言环境下,尤其是使用Visual C++ 6.0(简称VC6)这样的经典开发工具时,...

    IE插件开发,c#,IE

    - 在`.rgs`文件中添加必要的注册表项,以使IE识别并加载插件。 - 注册表项包括插件的基本信息、加载位置等关键配置。 #### 五、常见问题与解决方案 在实际开发过程中可能会遇到一些常见的问题,以下是一些可能...

    DLL文件基本原理及修改方法和通过改DLL来美化系统图标

    在系统中,有许多 DLL 文件,每个 DLL 文件都有其特定的功能,如 Browselc.dll 是 IE 所需要调用的库文件,Shdoclc.dll 是系统窗口及设置等,如删除文件、重命名,Shell32.dll 是系统窗口及设置等,如删除文件、...

    网页中显示带控件的ActiveX控件

    五、ActiveX控件在现代Web开发中的位置 随着Web技术的发展,HTML5、JavaScript及其框架如Angular、React等提供了更安全、跨平台的解决方案,逐渐取代了ActiveX控件的角色。现代浏览器倾向于使用沙盒环境来限制脚本...

    网页中使用CAD控件

    - **控件在ASP语言中的使用例程代码**:提供具体的代码示例,帮助开发者了解如何在ASP环境中集成和使用控件。 - **去掉IE的安全警告**:介绍如何通过设置来避免浏览器显示安全警告。 - **在网页中保存编辑的文件至...

Global site tag (gtag.js) - Google Analytics