c#开发IE控件
c#开发IE控件主要是对BHO对象是使用,但是我们知道BHO是一个COM对象,而在.NET下开发基于COM的应用,总觉得不是很简单,这里有受控代码与COM的调用,我查找了下,国内并没有此类信息,下面是译稿,翻译的不好,欢迎指出.
介绍:
我们在浏览Internet信息的时候,往往需要增强用户浏览信息的,IE浏览器其实是一个可扩展的模型,提供了大量的插件来完成这样的目标.其实很多时 候你已经在使用了,比如GOOGLE的工具条,Babelfish Translator,等你很多时候都在使用,这些插件都是扩展BHO或浏览器中Band的对象,用这些插件来集成一些复杂的功能,具体是实现一个COM 的接口.
许多BHO对象的实现都是利用C+编写一个ATL 组件,如果用c#来写,即用受控代码来实现COM 和 ATL ,好象是不可能实现的,但是很幸运,NET Framework提供了百分之百的与COM的互操作,在这篇文章里,我介绍下怎么利用.NET的互操作性来实现一个BHO对象.
认识下COM:
我多年从事c++,MFC,VB,ATL的开发,所以,在.NET之前我想成为一个优秀的COM开发者,正因为如此,COM的互操作性一直在我脑海里,虽然时间一长,我开发的都是完全用.NET开发的项目,COM变得很遥远,但有的时候,
比如我想改写一个ATL BHO 组件让c#来使用,这个想法又付出水面.
在进一步了解BHO细节之前,有几点我需要进一步阐述。首先,BHO对象依托于浏览器主窗口。实际上,这意味着一旦一个浏览器窗口产生,一个新的BHO对 象实例就要生成。任何 BHO对象与浏览器实例的生命周期是一致的。其次, BHO仅存在于Internet Explorer 4.0及以后版本中。
如果你在使用Microsoft Windows? 98, Windows 2000, Windows 95, or Windows NT版本4.0 操作系统的话,也就一块运行了活动桌面外壳4.71,BHO也被 Windows资源管理器所支持。 BHO是一个COM进程内服务,注册于注册表中某一键下。在启动时,Internet Explorer查询那个键并把该键下的所有对象预以加载。
BHO对象随着浏览器主窗口的显示而装入,随着浏览器主窗口的销毁而缷载。如果你打开多个浏览器窗口,多个BHO实例也一同产生。
无论浏览器以什么样的命令行启动,BHO对象都被加载。举例来说,即使你只是想要见到特定的 HTML 页或一个给定的文件夹,BHO对象也被加载。一般地,当 explorer.exe 或 iexplore.exe 运行的时候,BHO都要被考虑在内。如果你设置了"Open each folder in its own window"(对每一个文件夹以一个独立窗口打开)文件夹选项,那么你每次打开一个文件夹,BHO对象都要被加载
如前所述,一个BHO对象会被Internet Explorer或者Windows资源管理器(前提:外壳版本4.71或者更高)所加载。所以我专门设计了一个BHO来处理HTML网页,因此这个 BHO与资源管理器毫无关系。如果一个Dll不想被调用者一起加载,只需在DllMain()中实现了探明谁在调用该对象后返回FALSE即可。参看下面 代码:
if (dwReason == DLL_PROCESS_ATTACH){TCHAR pszLoader[MAX_PATH];//返回调用者模块的名称,第一个参数应为NULL,详见msdn。 GetModuleFileName(NULL, pszLoader, MAX_PATH);_tcslwr(pszLoader);if (_tcsstr(pszLoader, _T("explorer.exe")))return FALSE;}一旦知道了当前进程是Windows资源管理器,可立即退出。
注意,再多加一些条件语句是危险的!事实上,另外一些进程试图装入该DLL时将被放弃。如果你做另外一个试验,比方说针对Internet Explorer的执行文件iexplorer.exe,这时第一个受害者就是regsvr32.exe(该程序用于自动注册对象)。
if (!_tcsstr(pszLoader, _T("iexplore.exe"))) 你不能够再次注册该DLL库了。 事实上,当 regsvr32.exe 试图装入DLL以激活函数DllRegisterServer()时,该调用将被放弃。
八、与Web浏览器取得联系
SetSite()方法正是BHO对象被初始化的地方,此外,在这个方法中你可以执行所有的仅仅允许发生一次的任务。当你用Internet Explorer打开一个URL时,你应该等待一系列的事件以确保要求的文档已完全下载并被初始化。唯有在此时,你才可以通过对象模型暴露的接口(如果存 在的话)存取文档内容。这就是说你要取得一系列的指针。第一个就是指向IWebBrowser2(该接口用来生成WebBrowser对象)的指针。第二 个指针与事件有关。该模块必须作为一个浏览器的事件侦听器来实现,目的是为接收下载以及与文档相关的事件。下面用ATL灵敏指针加以封装:
CComQIPtr< IWebBrowser2, &IID_IWebBrowser2> m_spWebBrowser2;CComQIPtr<IConnectionPointContainer,&IID_IConnectionPointContainer> m_spCPC;源代码部分如下所示:
HRESULT CViewSource::SetSite(IUnknown *pUnkSite){// 检索并存储 IWebBrowser2 指针m_spWebBrowser2 = pUnkSite;if (m_spWebBrowser2 == NULL)return E_INVALIDARG;//检索并存储 IConnectionPointerContainer指针m_spCPC = m_spWebBrowser2;if (m_spCPC == NULL)return E_POINTER;//检索并存储浏览器的句柄HWND. 并且安装一个键盘钩子备后用RetrieveBrowserWindow();// 为接受事件通知连接到容器return Connect();} 为了取得IWebBrowser2接口指针,你可以进行查询。当然也可以在事件刚刚发生时查询 IConnectionPointContainer。这里,SetSite()检索了浏览器的句柄HWND,并且在当前线程中安装了一个键盘钩子。 HWND用于后面Internet Explorer窗口的移动或尺寸调整。这里的钩子用来实现热键功能,用户可以按动热键来显示/隐藏代码窗口。
九、从Internet Explorer浏览器取得事件
当你导向一个新的URL时,浏览器最需要完成的是两种事件:下载文档并为之准备HOST环境。也就是说,它必须初始化某对象并使该对象从外部可以利 用。针对不同的文档类型,或者装入一个已注册的Microsoft ActiveX? 服务器来处理该文档(如Word对于.doc文件的处理)或者初始化一些内部组件来分析文档内容并生成和显示该文档。对于HTML网页就是这样,其内容由 于DHTML对象作用而变得可用。当文档全部下载结束,DownloadComplete事件被激活。这并不是说,这样利用对象模型就可以安全地管理文档 的内容了。事实上,DocumentComplete 事件仅指明一切已经结束,文档已准备好了 (注意DocumentComplete事件仅在你第一次存取URL时到达,如果你执行了刷新动作,你仅仅收到一个DocumentComplete事 件)。
为了截获浏览器发出的事件, BHO需要通过IConnectionPoint 接口连接到浏览器上 并且实现传递接口IDispatch指针以处理各种事件。现在利用前面取得的IConnectionPointContainer指针来调用 FindConnectionPoint方法――它返回一个指针指向连接点对象(正是通过这个连接点对象来取得要求的外向接口,此时是 DIID_DWebBrowserEvent2)。 下列代码显示了连接点的发生情况:
HRESULT CViewSource::Connect(void){HRESULT hr;CComPtr<IConnectionPoint> spCP;//为Web浏览器事件而接收(receive)连接点hr = m_spCPC->FindConnectionPoint(DIID_DWebBrowserEvent2, &spCP);if (FAILED(hr))return hr;// 把事件处理器传递到容器。每次事件发生容器都将激活我们实现的IDispatch接口上的相应的函数。hr = spCP->Advise( reinterpret_cast<IDispatch*>(this), &m_dwCookie);return hr;} 通过调用接口IConnectionPoint的Advise() 方法, BHO告诉浏览器它对它产生的事件很感兴趣。 由于COM事件处理机制,所有这些意味着BHO把IDispatch接口指针提供给浏览器。浏览器将回调IDispatch接口的Invoke() 方法,以事件的ID值作为第一参数:
HRESULT CViewSource::Invoke(DISPID dispidMember, REFIID riid,LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr){if (dispidMember == DISPID_DOCUMENTCOMPLETE) {OnDocumentComplete();m_bDocumentCompleted = true;}:} 切记,当事件不再需要时,应该使之与浏览器分离。如果你忘记了做这件事情,BHO对象将被锁定,即使在你关闭浏览器窗口之后。很明 显,实现分离的最佳时机是收到事件OnQuit时。
介绍:
我们在浏览Internet信息的时候,往往需要增强用户浏览信息的,IE浏览器其实是一个可扩展的模型,提供了大量的插件来完成这样的目标.其实很多时 候你已经在使用了,比如GOOGLE的工具条,Babelfish Translator,等你很多时候都在使用,这些插件都是扩展BHO或浏览器中Band的对象,用这些插件来集成一些复杂的功能,具体是实现一个COM 的接口.
许多BHO对象的实现都是利用C+编写一个ATL 组件,如果用c#来写,即用受控代码来实现COM 和 ATL ,好象是不可能实现的,但是很幸运,NET Framework提供了百分之百的与COM的互操作,在这篇文章里,我介绍下怎么利用.NET的互操作性来实现一个BHO对象.
认识下COM:
我多年从事c++,MFC,VB,ATL的开发,所以,在.NET之前我想成为一个优秀的COM开发者,正因为如此,COM的互操作性一直在我脑海里,虽然时间一长,我开发的都是完全用.NET开发的项目,COM变得很遥远,但有的时候,
比如我想改写一个ATL BHO 组件让c#来使用,这个想法又付出水面.
在进一步了解BHO细节之前,有几点我需要进一步阐述。首先,BHO对象依托于浏览器主窗口。实际上,这意味着一旦一个浏览器窗口产生,一个新的BHO对 象实例就要生成。任何 BHO对象与浏览器实例的生命周期是一致的。其次, BHO仅存在于Internet Explorer 4.0及以后版本中。
如果你在使用Microsoft Windows? 98, Windows 2000, Windows 95, or Windows NT版本4.0 操作系统的话,也就一块运行了活动桌面外壳4.71,BHO也被 Windows资源管理器所支持。 BHO是一个COM进程内服务,注册于注册表中某一键下。在启动时,Internet Explorer查询那个键并把该键下的所有对象预以加载。
BHO对象随着浏览器主窗口的显示而装入,随着浏览器主窗口的销毁而缷载。如果你打开多个浏览器窗口,多个BHO实例也一同产生。
无论浏览器以什么样的命令行启动,BHO对象都被加载。举例来说,即使你只是想要见到特定的 HTML 页或一个给定的文件夹,BHO对象也被加载。一般地,当 explorer.exe 或 iexplore.exe 运行的时候,BHO都要被考虑在内。如果你设置了"Open each folder in its own window"(对每一个文件夹以一个独立窗口打开)文件夹选项,那么你每次打开一个文件夹,BHO对象都要被加载
如前所述,一个BHO对象会被Internet Explorer或者Windows资源管理器(前提:外壳版本4.71或者更高)所加载。所以我专门设计了一个BHO来处理HTML网页,因此这个 BHO与资源管理器毫无关系。如果一个Dll不想被调用者一起加载,只需在DllMain()中实现了探明谁在调用该对象后返回FALSE即可。参看下面 代码:
if (dwReason == DLL_PROCESS_ATTACH){TCHAR pszLoader[MAX_PATH];//返回调用者模块的名称,第一个参数应为NULL,详见msdn。 GetModuleFileName(NULL, pszLoader, MAX_PATH);_tcslwr(pszLoader);if (_tcsstr(pszLoader, _T("explorer.exe")))return FALSE;}一旦知道了当前进程是Windows资源管理器,可立即退出。
注意,再多加一些条件语句是危险的!事实上,另外一些进程试图装入该DLL时将被放弃。如果你做另外一个试验,比方说针对Internet Explorer的执行文件iexplorer.exe,这时第一个受害者就是regsvr32.exe(该程序用于自动注册对象)。
if (!_tcsstr(pszLoader, _T("iexplore.exe"))) 你不能够再次注册该DLL库了。 事实上,当 regsvr32.exe 试图装入DLL以激活函数DllRegisterServer()时,该调用将被放弃。
八、与Web浏览器取得联系
SetSite()方法正是BHO对象被初始化的地方,此外,在这个方法中你可以执行所有的仅仅允许发生一次的任务。当你用Internet Explorer打开一个URL时,你应该等待一系列的事件以确保要求的文档已完全下载并被初始化。唯有在此时,你才可以通过对象模型暴露的接口(如果存 在的话)存取文档内容。这就是说你要取得一系列的指针。第一个就是指向IWebBrowser2(该接口用来生成WebBrowser对象)的指针。第二 个指针与事件有关。该模块必须作为一个浏览器的事件侦听器来实现,目的是为接收下载以及与文档相关的事件。下面用ATL灵敏指针加以封装:
CComQIPtr< IWebBrowser2, &IID_IWebBrowser2> m_spWebBrowser2;CComQIPtr<IConnectionPointContainer,&IID_IConnectionPointContainer> m_spCPC;源代码部分如下所示:
HRESULT CViewSource::SetSite(IUnknown *pUnkSite){// 检索并存储 IWebBrowser2 指针m_spWebBrowser2 = pUnkSite;if (m_spWebBrowser2 == NULL)return E_INVALIDARG;//检索并存储 IConnectionPointerContainer指针m_spCPC = m_spWebBrowser2;if (m_spCPC == NULL)return E_POINTER;//检索并存储浏览器的句柄HWND. 并且安装一个键盘钩子备后用RetrieveBrowserWindow();// 为接受事件通知连接到容器return Connect();} 为了取得IWebBrowser2接口指针,你可以进行查询。当然也可以在事件刚刚发生时查询 IConnectionPointContainer。这里,SetSite()检索了浏览器的句柄HWND,并且在当前线程中安装了一个键盘钩子。 HWND用于后面Internet Explorer窗口的移动或尺寸调整。这里的钩子用来实现热键功能,用户可以按动热键来显示/隐藏代码窗口。
九、从Internet Explorer浏览器取得事件
当你导向一个新的URL时,浏览器最需要完成的是两种事件:下载文档并为之准备HOST环境。也就是说,它必须初始化某对象并使该对象从外部可以利 用。针对不同的文档类型,或者装入一个已注册的Microsoft ActiveX? 服务器来处理该文档(如Word对于.doc文件的处理)或者初始化一些内部组件来分析文档内容并生成和显示该文档。对于HTML网页就是这样,其内容由 于DHTML对象作用而变得可用。当文档全部下载结束,DownloadComplete事件被激活。这并不是说,这样利用对象模型就可以安全地管理文档 的内容了。事实上,DocumentComplete 事件仅指明一切已经结束,文档已准备好了 (注意DocumentComplete事件仅在你第一次存取URL时到达,如果你执行了刷新动作,你仅仅收到一个DocumentComplete事 件)。
为了截获浏览器发出的事件, BHO需要通过IConnectionPoint 接口连接到浏览器上 并且实现传递接口IDispatch指针以处理各种事件。现在利用前面取得的IConnectionPointContainer指针来调用 FindConnectionPoint方法――它返回一个指针指向连接点对象(正是通过这个连接点对象来取得要求的外向接口,此时是 DIID_DWebBrowserEvent2)。 下列代码显示了连接点的发生情况:
HRESULT CViewSource::Connect(void){HRESULT hr;CComPtr<IConnectionPoint> spCP;//为Web浏览器事件而接收(receive)连接点hr = m_spCPC->FindConnectionPoint(DIID_DWebBrowserEvent2, &spCP);if (FAILED(hr))return hr;// 把事件处理器传递到容器。每次事件发生容器都将激活我们实现的IDispatch接口上的相应的函数。hr = spCP->Advise( reinterpret_cast<IDispatch*>(this), &m_dwCookie);return hr;} 通过调用接口IConnectionPoint的Advise() 方法, BHO告诉浏览器它对它产生的事件很感兴趣。 由于COM事件处理机制,所有这些意味着BHO把IDispatch接口指针提供给浏览器。浏览器将回调IDispatch接口的Invoke() 方法,以事件的ID值作为第一参数:
HRESULT CViewSource::Invoke(DISPID dispidMember, REFIID riid,LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr){if (dispidMember == DISPID_DOCUMENTCOMPLETE) {OnDocumentComplete();m_bDocumentCompleted = true;}:} 切记,当事件不再需要时,应该使之与浏览器分离。如果你忘记了做这件事情,BHO对象将被锁定,即使在你关闭浏览器窗口之后。很明 显,实现分离的最佳时机是收到事件OnQuit时。