Browser Helper Object,浏览器辅助对象BHO介绍

作者:傻猫 发布时间:October 15, 2007 分类:Delphi No Comments

BHO(Browser Helper Object,浏览器辅助对象,简称BHO)

    BHO是微软推出的作为浏览器对第三方程序员开放交互接口的业界标准,通过简单的代码就可以进入浏览器领域的“交互接口”(INTERACTIVED Interface)。通过这个接口,程序员可以编写代码获取浏览器的行为,比如“后退”、“前进”、“当前页面”等,利用BHO的交互特性,程序员还可以用代码控制浏览器行业,比如修改替换浏览器工具栏,添加自己的程序按钮等(见图1)。这些在系统看来都是没有问题的。BHO原来的目的是为了更好的帮助程序员打造个性化浏览器,以及为程序提供更简洁的交互功能,现在很多IE个性化工具就是利用BHO的来实现。

    “浏览器劫持”是一种不同于普通病毒木马感染途径的网络攻击手段,而是使用各种技术(如DLL插件等)插件对用户的浏览器进行篡改。安装后,它们会成为浏览器的一部分,可以直接控制浏览器进行指定的操作,根据需要,可以让你打开指定的网站,甚至是收集你系统中的各种私密信息。最可怕的是只有当浏览器已经被劫持了,你才会发现,反应过来,原来电脑已经出现了问题。比如IE主页被改,开机就会弹出广告等等。目前,浏览器劫持已经成为Internet用户最大的威胁之一。其实“浏览器劫持”就是通过BHO的技术手段进入你的系统的,而这种技术是合法的。

    从某种观点看,Internet Explorer同普通的Win32程序没有什么两样。借助于BHO,你可以写一个进程内COM对象,这个对象在每次启动时都要加载。这样的对象会在与浏览器相同的上下文中运行,并能对可用的窗口和模块执行任何行动。例如,一个BHO能够探测到典型的事件,如GoBack、GoForward、DocumentComplete等;另外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插件开发:flash上弹出浮动工具条

作者:傻猫 发布时间:October 15, 2007 分类:Delphi No Comments

网上有个很流行的Flash Save源码,相信很多人见过,可能因为代码很早就发布了,的确很简陋,有些情况下不能正常工作,事件的关联,在DocumnetComplete,或者在NavigateComplete2中实例化并与事件关联。  

delphi代码
     
  1.   TObjectProcedure = procedure(var oDoc: IHTMLDocument2) of object;   
  2.   TEventObject = class(TInterfacedObject, IDispatch)   
  3.   private  
  4.     FOnEvent: TObjectProcedure;   
  5.     FoDoc: IHTMLDocument2;   
  6.   protected  
  7.     function GetTypeInfoCount(out Count: Integer): HResult; stdcall;   
  8.     function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;   
  9.     function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;   
  10.     function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;   
  11.   public  
  12.     constructor Create(const OnEvent: TObjectProcedure; var oDoc: IHTMLDocument2);   
  13.     property OnEvent: TObjectProcedure read FOnEvent write FOnEvent;   
  14.   end;   
  15.   
  16. htmlDoc.onmouseover := (TEventObject.Create(Document_OnMouseOver, htmlDoc) as IDispatch);   
  17.   
  18. htmlDoc.onmouseout := (TEventObject.Create(Document_OnMouseOut, htmlDoc) as IDispatch);   
  19.   
  20.   
  21. 2、事件发生后,取得坐标上,可以使用srcelement的属性,但对于iframe中的事件无效。为此,改用事件eventobject来取得坐标位置。取得为屏幕坐标,再转换为工作区坐标。ScreenToClient需要一个HWND,我们采用最上层IE为容器,在setsite时,将IE 的HWND记录下来。    
  22.   
  23. 事件发生的每次坐标都不一样,但是事件上IHTMLEventObj.offsetY属性,表示偏移量,与事件的screenX相减后,即可得到控件的左上角坐标。    
  24.   
  25. pt.X:=vEvt.screenX-vEvt.offsetX; //offsetX是相对于事件下面那个空间左上角的偏移量,所以减去他可以得到控件的原点(左上角)   
  26. pt.Y:=vEvt.screenY-vEvt.offsetY;    
  27.   
  28. 3、解决flash影片的相对路径问题    
  29.   
  30. src:= (vEle as IHTMLElement).getAttribute('movie',0);    
  31. src:='<A HREF='''+ src + '''></A>';通过建立超级连接,让系统自动为我们解决相对路么的问题。    
  32.   
  33. obj:=vdoc.createElement(src);    
  34.   
  35. src:= obj.getAttribute('href',0);    
  36.   
  37. 4.Flash有两个可能的tagName,object和embed   
  38.   
  39. 5.新建form后在启用alexa工具条的浏览器上使用有严重问题,关闭浏览器会弹出红色的错误提示,表明严重错误,解决方法是在隐藏窗口时重新设置SetParent(hwmd,0);   
  40.   

Browser Helper Objects: The Browser the Way You Want It

作者:傻猫 发布时间:October 15, 2007 分类:Delphi 2 Comments

Dino Esposito
Microsoft Corporation

January 1999

April 9, 2004 security update: Please also see Security Considerations: Programming and Reusing the Browser to learn more about addressing browser security issues.

Summary: Describes how to use BHOs to customize your browser. (16 printed pages) Covers:

Introduction
Program Customization
What Are Browser Helper Objects?
The Lifecycle of Helper Objects
The IObjectWithSite Interface
Writing a Browser Helper Object
Detecting Who's Calling
Getting in Touch with WebBrowser
Getting Events from the Browser
Accessing the Document Object
Managing the Code Window
Registration of Helper Objects
Summary

Introduction

There are sometimes circumstances in which you need a more or less specialized version of the browser. Sometimes you work around this by developing a completely custom module built on top of the WebBrowser control, complete with buttons, labels, and whatever else the user interface requires. In this case, you're free to add to that browser any new, nonstandard feature you want. But what you actually have is just a new, nonstandard browser. The WebBrowser control is just the parsing engine of the browser. This means there still remains a number of UI-related tasks for you to do: adding an address bar, toolbar, history, status bar, channels, and favorites, just to name a few. So, to create a custom browser you have to write two types of code: the code that transforms the WebBrowser control into a full-fledged browser like Microsoft® Internet Explorer, and the code that implements the new features you want it to support. Wouldn't it be nice if there was a straightforward way to customize Internet Explorer instead? Browser Helper Objects (BHO) do just that.

Program Customization

Historically speaking, the first way to customize the behavior of a program was through subclassing. By this means, you could change the way a given window in a program processed messages and actually obtain a different behavior. Although considered a brute-force approach, because the victim is largely unaware of what happens, it's been the only choice for a long time.

With the advent of the Microsoft Win32® API, interprocess subclassing was discouraged and made a bit harder to code. If you're brave-hearted, however, pointers have never scared you; above all, if you're used to living in symbiosis with system-wide hooks, you might even find it too simple. But this is not always the case. Despite the cleverness of the programming, the point is that each Win32 process runs in its own address space and breaking the process boundaries is somewhat incorrect. On the other hand, there might be circumstances that require you to do this with the best of intentions. More often, customization might be a specific feature the program itself allows by design.

In the latter case, the programs search for additional modules in well-known and prefixed disk zones, load, initialize, and then leave them free to do the job they have been designed to do. This is exactly what happens with the Internet Explorer browser and its helper objects.

What Are Browser Helper Objects?

From this point of view, Internet Explorer is just like any other Win32-based program with its own memory space to preserve. With Browser Helper Objects you can write components—specifically, in-process Component Object Model (COM) components—that Internet Explorer will load each time it starts up. Such objects run in the same memory context as the browser and can perform any action on the available windows and modules. For example, a BHO could detect the browser's typical events, such as GoBack, GoForward, and DocumentComplete; access the browser's menu and toolbar and make changes; create windows to display additional information on the currently viewed page; and install hooks to monitor messages and actions.

Before going any further with the nitty-gritty details of BHO, there are a couple of points I need to illuminate further. First, the BHO is tied to the browser's main window. In practice, this means a new instance of the object is created as soon as a new browser window is created. Any instance of the BHO lives and dies with the browser's instance. Second, BHOs only exist in Internet Explorer, version 4.0 and later.

If you're running the Microsoft Windows® 98, Windows 2000, Windows 95, or Windows NT® version 4.0 operating system with the Active Desktop™ Shell Update (shell version 4.71), BHOs are supported also by Windows Explorer. This has some implications that I'll talk more about later when making performance considerations and evaluating the impact of BHOs.

In its simplest form, a BHO is a COM in-process server registered under a certain registry's key. Upon startup, Internet Explorer looks up that key and loads all the objects whose CLSID is stored there. The browser initializes the object and asks it for a certain interface. If that interface is found, Internet Explorer uses the methods provided to pass its IUnknown pointer down to the helper object. This process is illustrated in Figure 1.

Figure 1. How Internet Explorer loads and initializes helper objects. The BHO site is the COM interface used to establish a communication.

The browser may find a list of CLSIDs in the registry and create an in-process instance of each. As a result, such objects are loaded in the browser's context and can operate as if they were native components. Due to the COM-based nature of Internet Explorer, however, being loaded inside the process space doesn't help that much. Put another way, it's true that the BHO can do a number of potentially useful things, like subclassing constituent windows or installing thread-local hooks, but it is definitely left out from the browser's core activity. To hook on the browser's events or to automate it, the helper object needs to establish a privileged and COM-based channel of communication. For this reason, the BHO should implement an interface called IObjectWithSite. By means of IObjectWithSite, in fact, Internet Explorer will pass a pointer to its IUnknown interface. The BHO can, in turn, store it and query for more specific interfaces, such as IWebBrowser2, IDispatch, and IConnectionPointContainer.

Another way to look at BHOs is in terms of Internet Explorer shell extensions. As you know, a Windows shell extension is a COM in-process server that Windows Explorer loads when it is about to perform a certain action on a document—for example, displaying its context menu. By writing a COM module that implements a few COM interfaces, you're given a chance to add new items to the context menu and then handle them properly. A shell extension must also be registered in such a way that Windows Explorer can find it. A Browser Helper Object follows the same pattern—the only changes are the interfaces to implement. Slightly different is the trigger that causes a BHO to be loaded. Despite the implementation differences, however, shell extensions and BHOs share a common nature, as the following table demonstrates.

Table 1. How Shell Extensions and Browser Helper Objects Implement Common Features

Feature Shell extension Browser Helper Object
Loaded by Windows Explorer. Internet Explorer (and Windows Explorer for shell version 4.71 and later).
Triggered by User's action on a document of a certain class (that is, right-click). Opening of the browser's window.
Unloaded when A few seconds later the reference count goes to 0. The browser window that caused it to load gets closed.
Implemented as COM in-process DLL. COM in-process DLL
Registration requirements Usual entries for a COM server plus other entries, depending on the type of shell extension and the document type that it will apply to. Usual entries for a COM server plus one entry to qualify it as a BHO.
Interfaces needed Depends on the type of the shell extension. IObjectWithSite.

If you're interested in shell extensions, see the MSDN Library Online or CD documentation for a primer. For deeper coverage, check out my recently published book, Professional Shell Programming for Windows (Wrox Press, 1-861001-84-3).

The Lifecycle of Helper Objects

As I mentioned earlier, BHOs aren't just supported by Internet Explorer. Provided you're running at least shell version 4.71, your BHOs will also be loaded by Windows Explorer—meaning that a unique browser can navigate both the Web and local disks with a similar user experience. The next table provides a product-oriented view of the various shell versions available today. The shell version number depends on the version information stored in shell32.dll.

Table 2. Browser Helper Objects Support for the Various Shell Versions

Shell version Installed products BHOs supported by
4.00 Windows 95 and Windows NT 4.0 with or without Internet Explorer 4.0 or earlier.
Note   The Shell Update isn't installed.
Internet Explorer 4.0
4.71 Windows 95 and Windows NT 4.0 with Internet Explorer 4.0 with the Active Desktop Shell Update release. Both Internet Explorer and Windows Explorer
4.72 Windows 98. Both Internet Explorer and Windows Explorer
5.00 Windows 2000 Both Internet Explorer and Windows Explorer

A Browser Helper Object is loaded when the main window of the browser is about to be displayed and is unloaded when that window is destroyed. If you open more copies of the browser window, more instances of the BHO will be created. The BHO is loaded despite the command line that launches the browser. For example, it gets loaded even if you simply want to see only a specific HTML page or a given folder. In general, the BHO is taken into account when either explorer.exe or iexplore.exe execute. If you set the "Open each folder in its own window" folder setting, the BHO will load each time you open a folder.

Figure 2. With this setting, each time you open a folder, a separate instance of explorer.exe executes and loads the registered BHOs.

Notice, however, that this applies only when you open folders starting from the My Computer icon on the desktop. In this case, the shell calls explorer.exe each time you move to another folder. The same won't occur if you start browsing from a two-paned view. In fact, when you change the folder the shell doesn't launch a new instance of the browser but simply creates another instance of the embedded view object. Curiously, if you change the folder by typing a new name in the Address bar, the browsing always takes place in the same window whether Window Explorer's view is single or two-paned.

Things are far simpler with Internet Explorer. You have multiple copies of it only if you explicitly run iexplore.exe multiple times. When you open new windows from Internet Explorer, each window is duplicated in a new thread without originating a new process, and therefore without reloading BHOs.

Above all, the most interesting feature of BHOs is that they are extremely dynamic. Each time Window Explorer's or Internet Explorer's window is opened, the loader reads the CLSID of the installed helper objects from the registry and deals with them. You can have different BHOs loaded by different copies of the browser if you edited the registry between instances of opening the browser. This means that now you have an excellent alternative to writing a new browser from scratch—you can embed WebBrowser in a Microsoft Visual Basic® or Microsoft Foundation Classes (MFC) frame window. At the same time, you're given a great opportunity to arrange very extensible browsing applications. You can rely on the full power of Internet Explorer and add as many add-ons as you want when it suits your needs.

The IObjectWithSite Interface

From this high-level overview of Browser Helper Objects one concept emerges clearly: A BHO is a dynamic-link library (DLL) capable of attaching itself to any new instance of Internet Explorer and, under certain circumstances, also Windows Explorer. Such a module can get in touch with the browser through the container's site.

In general, a site is an intermediate object placed in the middle of the container and each contained object. Through it, the container manages the content of the contained object and, in return, makes the object's internal functionality available. The site-based relationship between containers and objects involves the implementation of interfaces like IOleClientSite on the container side, and IOleObject on the object side. By calling methods on IOleObject, the container makes the object aware of its host environment.

When the container is Internet Explorer (or the Web-enabled version of Windows Explorer), performance issues reduce this communication pattern to the essential. The object is now required to implement a simpler and lighter interface called IObjectWithSite. It provides just two methods.

C++代码
  1. Table 3. The IObjectWithSite Interface Definition   
  2.   
  3. Method Description    
  4. HRESULT SetSite(    
  5. IUnknown* pUnkSite)   
  6.  Receives the IUnknown pointer of the browser. The typical implementation will simply store such a pointer for further use.    
  7. HRESULT GetSite(    
  8. REFIID riid,   
  9.   
  10. void** ppvSite)   
  11.  Retrieves and returns the specified interface from the last site set through SetSite(). The typical implementation will query the previously stored pUnkSite pointer for the specified interface.    

The only strict requirement for a BHO is implementing this interface. Notice that you should avoid returning E_NOTIMPL from any of the preceding functions. Either you don't implement the interface or you should be able to code its methods properly.

Writing a Browser Helper Object

A Browser Helper Object is a COM in-process server, so what's better than the Active Template Library (ATL) to build one? Another reason for choosing ATL is that it already provides a default and good enough implementation of the IObjectWithSite interface. Plus, among the predefined types of objects that the ATL COM Wizard natively supports, there's one, the Internet Explorer Object, that is just the type of object a BHO should be. An ATL Internet Explorer Object, in fact, is a simple object—that is, a COM server that supports IUnknown and self-registration—plus IObjectWithSite. If you add such an object to your ATL project, and call the corresponding class CViewSource, you get the following code from the wizard:

1
class ATL_NO_VTABLE CViewSource : public CComObjectRootEx&lt;CComSingleThreadModel&gt;, public CComCoClass&lt;CViewSource, &amp;CLSID_ViewSource&gt;, public IObjectWithSiteImpl&lt;CViewSource&gt;, public IDispatchImpl&lt;IViewSource, &amp;IID_IViewSource, &amp;LIBID_HTMLEDITLib&gt;

As you can see, the wizard already makes the class inherit from IObjectWithSiteImpl, which is an ATL template class that provides a basic implementation of IObjectWithSite. (See atlcom.h in the ATL\INCLUDE directory of Microsoft Visual Studio® 98.) Usually there's no need to override the GetSite() member function. Instead, the coded behavior of SetSite() often, if not always, needs customization. ATL, in fact, simply stores the IUnknown pointer to a member variable called m_spUnkSite.

Throughout the remainder of the article I'll discuss a quite complex and rich example of BHO. The object will attach itself to Internet Explorer only, and show a text box with the source code of the page being viewed. This code window will be automatically updated when you change the page and grayed out if the document that Internet Explorer is displaying is not an HTML page. Any change you apply to the raw HTML code is immediately reflected in the browser. This kind of magic is made possible by dynamic HTML (DHTML). Such a code window can be hidden and then recalled through a hot key. When visible, it shares the whole desktop work area with Internet Explorer, resizing properly as shown in Figure3.

Figure 3. The Browser Helper Object in action. It attaches to Internet Explorer and shows the source code of the page being viewed. It also allows you to enter (but not save) changes.

The key point with this example is accessing Internet Explorer's browsing machinery, which is nothing more than an instance of the WebBrowser control. This example can be broken out into five main steps:

  1. Detecting who's loading the object, be it Internet Explorer or Windows Explorer.
  2. Getting the IWebBrowser2 interface that renders the WebBrowser object.
  3. Catching the WebBrowser's specific events.
  4. Accessing the document being viewed, making sure it is an HTML document.
  5. Managing the dialog box window with the HTML source code.

The first step is accomplished in the DllMain() code. SetSite(), instead, is the right place to get the pointer to the WebBrowser object. Let's look at all these steps in a bit more detail. For information on what isn't covered here you can refer to the source code available on the MSDN Online Web site.

Detecting Who's Calling

As mentioned earlier, a BHO can be called either by Internet Explorer or Windows Explorer if you're running at least shell version 4.71. In this case, I'm designing a helper object specifically targeted to work with HTML pages, so it will have nothing to do with Windows Explorer. A DLL that doesn't want to be loaded by a certain caller can simply return False in its DllMain() function once it detects who's calling. The GetModuleFileName() API function returns the name of the caller module if you pass NULL as its first argument. Such a parameter is the handle of the module whose name you want to know. NULL means that you want the name of the calling process.

1
if (dwReason == DLL_PROCESS_ATTACH){TCHAR pszLoader[MAX_PATH];GetModuleFileName(NULL, pszLoader, MAX_PATH);_tcslwr(pszLoader);if (_tcsstr(pszLoader, _T(&quot;explorer.exe&quot;))) return FALSE;}

Once you know the name of the process, you can quit loading if it is Windows Explorer. Notice that a more selective choice might be dangerous. In fact, other processes could try to load the DLL for legitimate reasons and be rejected. The first victim of this situation is regsvr32.exe, the program used to automatically register the object. If you make a different test, say, only against the Internet Explorer executable:

1
if (!_tcsstr(pszLoader, _T(&quot;iexplore.exe&quot;)))

you won't be able to register the DLL any longer. In fact, when regsvr32.exe attempts to load the DLL to invoke the DllRegisterServer() function, the call will be rejected.

Get in Touch with WebBrowser

The SetSite() method is where the BHO is initialized and where you would perform all the tasks that happen only once. When you navigate to a URL with Internet Explorer, you should wait for a couple of events to make sure the required document has been completely downloaded and then initialized. Only at this point can you safely access its content through the exposed object model, if any. This means you need to acquire a couple of pointers. The first one is the pointer to IWebBrowser2, the interface that renders the WebBrowser object. The second pointer relates to events. This module must register as an event listener with the browser in order to receive the notification of downloads and document-specific events. By making use of ATL smart pointers:

1
CComQIPtr&lt;IWebBrowser2, &amp;IID_IWebBrowser2&gt; m_spWebBrowser2;CComQIPtr&lt;IConnectionPointContainer, &amp;IID_IConnectionPointContainer&gt; m_spCPC;

The source code looks like the following:

1
HRESULT CViewSource::SetSite(IUnknown *pUnkSite){ // Retrieve and store the IWebBrowser2 pointer m_spWebBrowser2 = pUnkSite; if (m_spWebBrowser2 == NULL) return E_INVALIDARG; // Retrieve and store the IConnectionPointerContainer pointer m_spCPC = m_spWebBrowser2; if (m_spCPC == NULL) return E_POINTER; // Retrieve and store the HWND of the browser. Plus install // a keyboard hook for further use RetrieveBrowserWindow(); // Connect to the container for receiving event notifications return Connect();}

To get a pointer to the IWebBrowser2interface, you simply query it. The same occurs for IConnectionPointContainer, the first step for event handling. The code for SetSite() also retrieves the HWND of the browser and installs a keyboard hook on the current thread. The HWND will be used later to move and resize the Internet Explorer window. The hook, instead, serves the purpose of providing a hot key to make the code window appear and disappear at the user's leisure.

Getting Events from the Browser

When you navigate to a new URL, the browser needs to primarily accomplish two things: download the referred document and prepare the host environment for it. In other words, it must initialize and make externally available an object model for it. Depending on the type of document, this means either loading a Microsoft ActiveX® server application registered to handle that document (for example, Microsoft Word for .doc files) or initializing some internal components that analyze the document content and fill the elements of the object model that renders it. This is what happens with HTML pages whose content is made available through the DHTML object model. When the document has been completely downloaded, a DownloadComplete event is fired. This does not necessarily mean that it's safe to manage the document's content through its object model. Instead, a DocumentComplete event indicates that everything has been done and the document is ready. (Notice that DocumentComplete arrives only the first time you access the URL. Subsequently, if you press F5 or click the Refresh button, you'll receive only a DownloadComplete event.)

To intercept the events fired by the browser, the BHO needs to connect to it via an IConnectionPoint interface and pass the IDispatch table of the functions that will handle the various events. The pointer to IConnectionPointContainer obtained previously is used to call the FindConnectionPoint method that returns a pointer to the connection point object for the required outgoing interface: in this case, DIID_DWebBrowserEvents2. The following code shows how the connection takes place:

1
HRESULT CViewSource::Connect(void){ HRESULT hr; CComPtr&lt;IConnectionPoint&gt; spCP; // Receives the connection point for WebBrowser events hr = m_spCPC-&gt;FindConnectionPoint(DIID_DWebBrowserEvents2, &amp;spCP); if (FAILED(hr)) return hr; // Pass our event handlers to the container. Each time an event occurs // the container will invoke the functions of the IDispatch interface // we implemented. hr = spCP-&gt;Advise( reinterpret_cast&lt;IDispatch*&gt;(this), &amp;m_dwCookie); return hr; }

By calling the IConnectionPoint's Advise() method, the BHO lets the browser know that it is interested in receiving notifications about events. Due to the COM event-handling mechanism, all this actually means that the BHO provides the browser with a pointer to its IDispatch interface. The browser will then call back the IDispatch's Invoke() method, passing the ID of the event as the first argument.

1
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; } :}

It's important to remember to disconnect from the browser when events are no longer needed. If you forget to do this, the BHO will remain locked even after you close the browser's window. (Among other things, this means you can't recompile or delete the object.) A good time to disconnect is when you receive the OnQuit event.

Accessing the Document Object

At this point the BHO has a reference to Internet Explorer's WebBrowser control and is connected to the browser for receiving all the events it generates. When the Web page is completely downloaded and properly initialized, it's finally possible to access it through the DHTML document object model. The Document property of WebBrowser returns a pointer to the IDispatch interface of the document object:

1
CComPtr&lt;IDispatch&gt; pDisp;HRESULT hr = m_spWebBrowser2-&gt;get_Document(&amp;pDisp);

What the get_Document() method provides is just a pointer to an interface. We need to make sure that behind that IDispatch pointer there's really an HTML document object. If I were using Visual Basic, the following would have been equivalent code:

1
Dim doc As ObjectSet doc = WebBrowser1.DocumentIf TypeName(doc)=&quot;HTMLDocument&quot; Then ' Get the document content and displayElse ' Disable the display dialogEnd If

What's needed is a way to learn about the nature of the IDispatch pointer returned by get_Document(). Internet Explorer is more than an HTML browser and is capable of hosting any ActiveX document—that is, any document for which an application exists that acts as an ActiveX document server. Given this, there's no guarantee the document viewed is really an HTML page.

One solution is to look at the location URL and check the URL's extension. But what about Active Server Pages (ASP) or a URL where the HTML page is implicit? And what if you're using custom protocols like about or res? (For more information about custom protocols, check out my Cutting Edge column in the January 1999 issue of MIND magazine.)

I decided to take another approach, much more akin to the Visual Basic code just shown. The idea is, if the IDispatch pointer actually refers to an HTML document, querying for the IHTMLDocument2 interface would be successful. IHTMLDocument2 is the interface that wraps up all the functionality that the DHTML object model exposes for an HTML page. The following code snippet shows how to proceed:

1
CComPtr&lt;IDispatch&gt; pDisp;HRESULT hr = m_spWebBrowser2-&gt;get_Document(&amp;pDisp);CComQIPtr&lt;IHTMLDocument2, &amp;IID_IHTMLDocument2&gt; spHTML;spHTML = pDisp;if (spHTML) { // get the content of the document and display it} else { // disable the Code Window controls}

The spHTML pointer is NULL if the query interface for IHTMLDocument2 failed. Otherwise, we're fine with the methods and properties of the DHTML object model.

Now the problem becomes how to get the source code of the displayed page. Fortunately, to work around this a rudimentary knowledge of DHTML will suffice. Just as an HTML page encloses all its content into a <BODY> tag, the DHTML object model requires you to get a pointer to the Body object as the first step:

1
CComPtr&lt;IHTMLElement&gt; m_pBody;hr = spHTML-&gt;get_body(&amp;m_pBody);

Curiously, the DHTML object model doesn't let you know about the raw content of the tags that precede <BODY>, such as <HEAD>. Their content is processed and then stored in a number of properties, but you still don't have one returning the raw text contained in the original HTML file. What the body can tell, however, will suffice here. To get the HTML code included in the <BODY>…</BODY> tags I need to read the content of the outerHTML property into a BSTR variable:

1
BSTR bstrHTMLText;hr = m_pBody-&gt;get_outerHTML(&amp;bstrHTMLText);

At this point, displaying the text into the code window is a matter of creating the window, converting the string from Unicode to ANSI, and setting the edit box, as shown in Figure 3. The following shows the full code for this:

1
HRESULT CViewSource::GetDocumentContent(){ USES_CONVERSION; // Get the WebBrowser's document object CComPtr&lt;IDispatch&gt; pDisp; HRESULT hr = m_spWebBrowser2-&gt;get_Document(&amp;pDisp); if (FAILED(hr)) return hr; // Verify that what we get is a pointer to a IHTMLDocument2 // interface. To be sure, let's query for // the IHTMLDocument2 interface (through smart pointers) CComQIPtr&lt;IHTMLDocument2, &amp;IID_IHTMLDocument2&gt; spHTML; spHTML = pDisp; // Extract the source code of the document if (spHTML) { // Get the BODY object hr = spHTML-&gt;get_body(&amp;m_pBody); if (FAILED(hr)) return hr; // Get the HTML text BSTR bstrHTMLText; hr = m_pBody-&gt;get_outerHTML(&amp;bstrHTMLText); if (FAILED(hr)) return hr; // Convert the text from Unicode to ANSI LPTSTR psz = new TCHAR[SysStringLen(bstrHTMLText)]; lstrcpy(psz, OLE2T(bstrHTMLText)); // Enable changes to the text HWND hwnd = m_dlgCode.GetDlgItem(IDC_TEXT); EnableWindow(hwnd, true); hwnd = m_dlgCode.GetDlgItem(IDC_APPLY); EnableWindow(hwnd, true); // Set the text in the Code Window m_dlgCode.SetDlgItemText(IDC_TEXT, psz); delete [] psz; } else // The document isn't a HTML page { m_dlgCode.SetDlgItemText(IDC_TEXT, &quot;&quot;); HWND hwnd = m_dlgCode.GetDlgItem(IDC_TEXT); EnableWindow(hwnd, false); hwnd = m_dlgCode.GetDlgItem(IDC_APPLY); EnableWindow(hwnd, false); } return S_OK; }

Because I run this code in response to the DocumentComplete notification, each new page is automatically and promptly processed. The DHTML object model lets you modify on the fly the structure of the page, but all the changes will be lost as soon as you refresh the view by hitting F5 or clicking the browser's Refresh button. By also handling the DownloadComplete event you can refresh the code window as well. (Pay attention to the fact that the DownloadComplete event comes before DocumentComplete.) So, you should ignore the DownloadComplete generated by the first download of the page and consider it only when it originates from a refresh. A simple Boolean member, for example m_bDocumentCompleted, is of great help in distinguishing between the situations.

Managing the Code Window

The code window used to show the HTML source code of the current page is another ATL basic element—a dialog box window that you find in the Miscellaneous page of the ATL Object Wizard. I resize this window in response to the WM_INITDIALOG message and make it occupy the lowest portion of the desktop work area—that is, the available screen minus the taskbar, wherever it is docked.

This window may or may not appear at the browser startup. By default it does, but this can be prevented by clearing the Show window at startup check box. You can also close the window if you like. By pressing F12, however, you can bring it back at any time. F12 is caught by the keyboard hook I installed in SetSite().

The startup setting is saved to the registry in full accordance with Microsoft guidelines. To read and write the registry I employed the new Shell Lightweight API (shlwapi.dll) instead of the Win32 functions, saving the hassle of opening and closing the involved keys:

1
DWORD dwType, dwVal;DWORD dwSize = sizeof(DWORD);SHGetValue(HKEY_CURRENT_USER, _T(&quot;Software\\MSDN\\BHO&quot;), _T(&quot;ShowWindowAtStartup&quot;), &amp;dwType, &amp;dwVal, &amp;dwSize);

This DLL has been introduced with Internet Explorer 4.0 and Active Desktop, and is a standard system component beginning with Windows 98. Such functions are more direct than the corresponding Win32 functions and are preferred for single reading and writing.

Registration of Helper Objects

A BHO is a COM server and should be registered both as a COM server and as a BHO. The ATL Wizard provides you with the necessary registrar script code (RGS) that accomplishes the first task. What follows is the RGS code that properly installs a helper object. (The CLSID comes from the example.)

1
HKLM { SOFTWARE { Microsoft { Windows { CurrentVersion { Explorer { 'Browser Helper Objects' { ForceRemove {1E1B2879-88FF-11D2-8D96-D7ACAC95951F} }}}}}}}

Note the ForceRemove clause that causes the key to be removed when you unregister the object.

Under the Browser Helper Objects key fall all the installed helper objects. Such a list is never cached by the browser, so installing and testing BHOs is really a quick matter.

Summary

In this article, I presented Browser Helper Objects—a relatively new and powerful way of injecting your code directly inside the browser's address space. What you have to do is write a COM server that supports the IObjectWithSite interface. At this point, your module is for all legal purposes a component of the browser machinery. The sample I've built throughout the article also touched on topics such as COM events, the dynamic HTML object model, and the WebBrowser programming interface, which may appear to be a little off the topic. Instead, I think this demonstrates the power of BHOs, and at the same time provides a real-world platform on which to build your own objects. If you need to know what the browser is displaying, you absolutely need to sink events and become familiar with WebBrowser. Now you know: forewarned is forearmed. To conclude, let me also remind you that BHOs are useful with Windows Explorer as well and, thanks to WebBrowser, they can be driven from your code.

基于delphi的bho开发笔记

作者:傻猫 发布时间:October 15, 2007 分类:Delphi No Comments

 终于解决了让我头疼了很久的在IE工具条上backspace和tab键无效的问题,具体的解决方法如下:(这是个demo的文件)   

主要要实现接口:IInputObject;     

    {Declare IInputObject methods here}  

    function UIActivateIO(fActivate: BOOL; var lpMsg: TMsg): HResult; stdcall; 

    function HasFocusIO: HResult; stdcall;   

    function TranslateAcceleratorIO(var lpMsg: TMsg): HResult; stdcall;   

  以及方法:

    procedure FocusChange(bHasFocus: Boolean);   

    procedure BandWndProc(var Message: TMessage);    

具体请看以下demo代码:

 

delphi代码
  1. 终于解决了让我头疼了很久的在IE工具条上backspace和tab键无效的问题,具体的解决方法如下:(这是个demo的文件)   
  2. 主要要实现接口:IInputObject;   
  3.   
  4.     {Declare IInputObject methods here}  
  5.     function UIActivateIO(fActivate: BOOL; var lpMsg: TMsg): HResult; stdcall;   
  6.     function HasFocusIO: HResult; stdcall;   
  7.     function TranslateAcceleratorIO(var lpMsg: TMsg): HResult; stdcall;   
  8.   
  9. 以及方法:   
  10.   
  11.     procedure FocusChange(bHasFocus: Boolean);   
  12.     procedure BandWndProc(var Message: TMessage);    
  13. 具体请看以下demo代码:  
  14.    
  15.   
  16.     
  17.   
  18. 窗体文件:   
  19.   
  20. unit fmIEBar;   
  21.   
  22. interface  
  23.   
  24. uses  
  25.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,   
  26.   Dialogs, StdCtrls, SHDocVw;   
  27.   
  28. type  
  29.   TfrmIEBar = class(TForm)   
  30.     TxtUrl: TEdit;   
  31.     procedure FormActivate(Sender: TObject);   
  32.     procedure FormShow(Sender: TObject);   
  33.   private  
  34.     { Private declarations }  
  35.   public  
  36.     { Public declarations }  
  37.     IEThis: IWebbrowser2;   
  38.   end;   
  39.   
  40. var  
  41.   frmIEBar: TfrmIEBar;   
  42.   
  43. implementation  
  44.   
  45. {$R *.dfm}  
  46.   
  47. { TfrmIEBar }  
  48.   
  49. procedure TfrmIEBar.FormActivate(Sender: TObject);   
  50. begin  
  51.   TxtUrl.SetFocus;   
  52. end;   
  53.   
  54. procedure TfrmIEBar.FormShow(Sender: TObject);   
  55. begin  
  56.   TxtUrl.SetFocus;   
  57. end;   
  58.   
  59. end.   
  60.   
  61.     
  62.   
  63. 具体实现文件:   
  64.   
  65.     
  66.   
  67. unit UTestTextBox;   
  68.   
  69. {$WARN SYMBOL_PLATFORM OFF}  
  70.   
  71. interface  
  72.   
  73. uses  
  74.   Windows, ActiveX, Classes, ComObj, MSHTML, SHDocVw, ShellAPI, TlHelp32, ShlObj, fmIEBar,   
  75.   Registry, Messages;   
  76.   
  77. type  
  78.   TTestTextBoxFactory = class(TComObjectFactory)   
  79.   public  
  80.     procedure UpdateRegistry(Register: Boolean); override;   
  81.   end;   
  82.   TTestTextBox = class(TComObject, IDeskBand, IObjectWithSite, IPersistStreamInit, IInputObject)   
  83.   private  
  84.     HasFocus: Boolean;   
  85.     frmIE: TfrmIEBar;   
  86.     m_pSite:IInputObjectSite;   
  87.     m_hwndParent:HWND;   
  88.     m_hWnd:HWND;   
  89.     m_dwViewMode:Integer;   
  90.     m_dwBandID:Integer;   
  91.     SavedWndProc: TWndMethod;   
  92.   protected  
  93.     procedure FocusChange(bHasFocus: Boolean);   
  94.     procedure BandWndProc(var Message: TMessage);   
  95.   public  
  96.     {Declare IDeskBand methods here}  
  97.     function GetBandInfo(dwBandID, dwViewMode: DWORD; var pdbi: TDeskBandInfo):   
  98.          HResult; stdcall;   
  99.     function ShowDW(fShow: BOOL): HResult; stdcall;   
  100.     function CloseDW(dwReserved: DWORD): HResult; stdcall;   
  101.     function ResizeBorderDW(var prcBorder: TRect; punkToolbarSite: IUnknown;   
  102.        fReserved: BOOL): HResult; stdcall;   
  103.     function GetWindow(out wnd: HWnd): HResult; stdcall;   
  104.     function ContextSensitiveHelp(fEnterMode: BOOL): HResult; stdcall;   
  105.   
  106.     {Declare IObjectWithSite methods here}  
  107.     function SetSite(const pUnkSite: IUnknown ):HResult; stdcall;   
  108.     function GetSite(const riid: TIID; out site: IUnknown):HResult;stdcall;   
  109.   
  110.     {Declare IPersistStream methods here}  
  111.     function GetClassID(out classID: TCLSID): HResult; stdcall;   
  112.     function IsDirty: HResult; stdcall;   
  113.     function InitNew: HResult; stdcall;   
  114.     function Load(const stm: IStream): HResult; stdcall;   
  115.     function Save(const stm: IStream; fClearDirty: BOOL): HResult; stdcall;   
  116.     function GetSizeMax(out cbSize: Largeint): HResult; stdcall;   
  117.     {Declare IInputObject methods here}  
  118.     function UIActivateIO(fActivate: BOOL; var lpMsg: TMsg): HResult; stdcall;   
  119.     function HasFocusIO: HResult; stdcall;   
  120.     function TranslateAcceleratorIO(var lpMsg: TMsg): HResult; stdcall;   
  121.   end;   
  122.   
  123. const  
  124.   Class_TestTextBox: TGUID = '{9FC0A716-35A4-4ACB-8565-EAA1C2D9E0A1}';   
  125.   //以下是系统接口的IID  
  126.   IID_IUnknown: TGUID = (   
  127.       D1:$00000000;D2:$0000;D3:$0000;D4:($C0,$00,$00,$00,$00,$00,$00,$46));   
  128.   IID_IOleObject: TGUID = (   
  129.       D1:$00000112;D2:$0000;D3:$0000;D4:($C0,$00,$00,$00,$00,$00,$00,$46));   
  130.   IID_IOleWindow: TGUID = (   
  131.       D1:$00000114;D2:$0000;D3:$0000;D4:($C0,$00,$00,$00,$00,$00,$00,$46));   
  132.   
  133.   IID_IInputObjectSite : TGUID = (   
  134.       D1:$f1db8392;D2:$7331;D3:$11d0;D4:($8C,$99,$00,$A0,$C9,$2D,$BF,$E8));   
  135.   sSID_SInternetExplorer : TGUID = '{0002DF05-0000-0000-C000-000000000046}';   
  136.   sIID_IWebBrowserApp : TGUID= '{0002DF05-0000-0000-C000-000000000046}';   
  137.   
  138.   //面板所允许的最小宽度和高度。  
  139.   MIN_SIZE_X = 54;   
  140.   MIN_SIZE_Y = 23;   
  141.   EB_CLASS_NAME = 'BackSpace有效性测试';   
  142. implementation  
  143.   
  144. uses ComServ;   
  145.   
  146. { TTestTextBoxFactory }  
  147.   
  148. procedure TTestTextBoxFactory.UpdateRegistry(Register: Boolean);   
  149. var  
  150.   ClassID: string;   
  151.   a:Integer;   
  152. begin  
  153.    inherited UpdateRegistry(Register);   
  154.    if Register then  
  155.    begin  
  156.      ClassID:=GUIDToString(Class_TestTextBox);   
  157.      with TRegistry.Create do  
  158.      begin  
  159.        try  
  160.          //添加附加的注册表项  
  161.          RootKey:=HKEY_LOCAL_MACHINE;   
  162.          OpenKey('\SOFTWARE\Microsoft\Internet Explorer\Toolbar',False);   
  163.          a:=0;   
  164.          WriteBinaryData(GUIDToString(Class_TestTextBox),a,0);   
  165.          OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved',True);   
  166.          WriteString (GUIDToString(Class_TestTextBox), EB_CLASS_NAME);   
  167.          RootKey:=HKEY_CLASSES_ROOT;   
  168.          OpenKey('\CLSID\'+GUIDToString(Class_TestTextBox),False); 
  169.          WriteString('',EB_CLASS_NAME); 
  170.        finally 
  171.          Free; 
  172.        end; 
  173.      end; 
  174.    end 
  175.    else 
  176.    begin 
  177.      with TRegistry.Create do 
  178.      begin 
  179.        try 
  180.          RootKey:=HKEY_LOCAL_MACHINE; 
  181.          OpenKey('\SOFTWARE\Microsoft\Internet Explorer\Toolbar',False); 
  182.          DeleteValue(GUIDToString(Class_TestTextBox)); 
  183.          OpenKey('\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved',False); 
  184.          DeleteValue(GUIDToString(Class_TestTextBox)); 
  185.        finally 
  186.          Free; 
  187.        end; 
  188.      end; 
  189.    end; 
  190. end; 
  191.  
  192. { TTestTextBox } 
  193.  
  194. procedure TTestTextBox.BandWndProc(var Message: TMessage); 
  195. begin 
  196.   if (Message.Msg = WM_PARENTNOTIFY)  then 
  197.   begin 
  198.     HasFocus := True; 
  199.     FocusChange(HasFocus); 
  200.   end; 
  201.   SavedWndProc(Message); 
  202. end; 
  203.  
  204. function TTestTextBox.CloseDW(dwReserved: DWORD): HResult; 
  205. begin 
  206.   if Assigned(frmIE) then 
  207.   begin 
  208.     frmIE.Free; 
  209.     frmIE := nil; 
  210.   end; 
  211.   Result:= S_OK; 
  212. end; 
  213.  
  214. function TTestTextBox.ContextSensitiveHelp(fEnterMode: BOOL): HResult; 
  215. begin 
  216.   Result:= E_NOTIMPL; 
  217. end; 
  218.  
  219. procedure TTestTextBox.FocusChange(bHasFocus: Boolean); 
  220. begin 
  221.   if m_pSite <> nil then 
  222.     m_pSite.OnFocusChangeIS(Self, bHasFocus); 
  223. end; 
  224.  
  225. function TTestTextBox.GetBandInfo(dwBandID, dwViewMode: DWORD; 
  226.   var pdbi: TDeskBandInfo): HResult; 
  227. begin 
  228.   Result:=E_INVALIDARG; 
  229.   if not Assigned(frmIE) then 
  230.     frmIE:= TfrmIEBar.CreateParented(m_hwndParent); 
  231.   if(@pdbi<>nil)then 
  232.   begin 
  233.     m_dwBandID := dwBandID; 
  234.     m_dwViewMode := dwViewMode; 
  235.     if(pdbi.dwMask and DBIM_MINSIZE)<>0 then 
  236.     begin 
  237.       pdbi.ptMinSize.x := MIN_SIZE_X; 
  238.       pdbi.ptMinSize.y := MIN_SIZE_Y; 
  239.     end; 
  240.     if(pdbi.dwMask and DBIM_MAXSIZE)<>0 then 
  241.     begin 
  242.       pdbi.ptMaxSize.x := -1; 
  243.       pdbi.ptMaxSize.y := -1; 
  244.     end; 
  245.     if(pdbi.dwMask and DBIM_INTEGRAL)<>0 then 
  246.     begin 
  247.       pdbi.ptIntegral.x := 1; 
  248.       pdbi.ptIntegral.y := 1; 
  249.     end; 
  250.     if(pdbi.dwMask and DBIM_ACTUAL)<>0 then 
  251.     begin 
  252.       pdbi.ptActual.x := 0; 
  253.       pdbi.ptActual.y := 0; 
  254.     end; 
  255.     if(pdbi.dwMask and DBIM_MODEFLAGS)<>0 then 
  256.       pdbi.dwModeFlags := DBIMF_VARIABLEHEIGHT; 
  257.     if(pdbi.dwMask and DBIM_BKCOLOR)<>0 then 
  258.       pdbi.dwMask := pdbi.dwMask and (not DBIM_BKCOLOR); 
  259.   end; 
  260. end; 
  261.  
  262. function TTestTextBox.GetClassID(out classID: TCLSID): HResult; 
  263. begin 
  264.   ClassID:= Class_TestTextBox; 
  265.   Result:=S_OK; 
  266. end; 
  267.  
  268. function TTestTextBox.GetSite(const riid: TIID; 
  269.   out site: IInterface): HResult; 
  270. begin 
  271.   if Assigned(m_pSite) then 
  272.     Result := m_pSite.QueryInterface(riid, site) 
  273.   else 
  274.     Result := E_FAIL; 
  275. end; 
  276.  
  277. function TTestTextBox.GetSizeMax(out cbSize: Largeint): HResult; 
  278. begin 
  279.   Result := E_NOTIMPL; 
  280. end; 
  281.  
  282. function TTestTextBox.GetWindow(out wnd: HWnd): HResult; 
  283. begin 
  284.   Wnd := frmIE.Handle; 
  285.   SavedWndProc := frmIE.WindowProc; 
  286.   frmIE.WindowProc := BandWndProc; 
  287.   Result := S_OK; 
  288. end; 
  289.  
  290. function TTestTextBox.HasFocusIO: HResult; 
  291. begin 
  292.   if Assigned(frmIE) and (frmIE.Active) then 
  293.   begin 
  294.     Result := S_OK; 
  295.   end 
  296.   else 
  297.   begin 
  298.     Result := E_FAIL; 
  299.   end; 
  300. end; 
  301.  
  302. function TTestTextBox.InitNew: HResult; 
  303. begin 
  304.   Result := E_NOTIMPL; 
  305. end; 
  306.  
  307. function TTestTextBox.IsDirty: HResult; 
  308. begin 
  309.   Result:=S_FALSE; 
  310. end; 
  311.  
  312. function TTestTextBox.Load(const stm: IStream): HResult; 
  313. begin 
  314.   Result:=S_OK; 
  315. end; 
  316.  
  317. function TTestTextBox.ResizeBorderDW(var prcBorder: TRect; 
  318.   punkToolbarSite: IInterface; fReserved: BOOL): HResult; 
  319. begin 
  320.   Result:=E_NOTIMPL; 
  321. end; 
  322.  
  323. function TTestTextBox.Save(const stm: IStream; fClearDirty: BOOL): HResult; 
  324. begin 
  325.   Result:=S_OK; 
  326. end; 
  327.  
  328. function TTestTextBox.SetSite(const pUnkSite: IInterface): HResult; 
  329. var 
  330.   pOleWindow:IOleWindow; 
  331.   pOLEcmd:IOleCommandTarget; 
  332.   pSP:IServiceProvider; 
  333.   rc:TRect; 
  334. begin 
  335.   if Assigned(pUnkSite) then 
  336.   begin 
  337.     m_hwndParent := 0; 
  338.     m_pSite:=pUnkSite as IInputObjectSite; 
  339.     pOleWindow := PunkSIte as IOleWindow; 
  340.     //获得父窗口IE面板窗口的句柄 
  341.     pOleWindow.GetWindow(m_hwndParent); 
  342.     if(m_hwndParent=0)then 
  343.     begin 
  344.        Result := E_FAIL; 
  345.        exit; 
  346.     end; 
  347.     //获得父窗口区域 
  348.     GetClientRect(m_hwndParent, rc); 
  349.     if not Assigned(frmIE) then 
  350.     begin 
  351.        //建立TIEForm窗口,父窗口为m_hwndParent 
  352.        frmIE:= TfrmIEBar.CreateParented(m_hwndParent); 
  353.        m_Hwnd:= frmIE.Handle; 
  354.        SetWindowLong(frmIE.Handle, GWL_STYLE, GetWindowLong(frmIE.Handle, 
  355.           GWL_STYLE) Or WS_CHILD); 
  356.        //根据父窗口区域设置窗口位置 
  357.        with frmIE do 
  358.        begin 
  359.           Left :=rc.Left; 
  360.           Top:=rc.top; 
  361.           Width:=rc.Right - rc.Left; 
  362.           Height:=rc.Bottom - rc.Top; 
  363.        end; 
  364.        frmIE.Visible := True; 
  365.        //获得与浏览器相关联的Webbrowser对象。 
  366.        pOLEcmd:=pUnkSite as IOleCommandTarget; 
  367.        pSP:=pOLEcmd as  IServiceProvider; 
  368.        if Assigned(pSP)then 
  369.        begin 
  370.          pSP.QueryService(IWebbrowserApp, IWebbrowser2, frmIE.IEThis); 
  371.        end; 
  372.     end; 
  373.   end; 
  374.   Result := S_OK; 
  375. end; 
  376.  
  377. function TTestTextBox.ShowDW(fShow: BOOL): HResult; 
  378. begin 
  379.   HasFocus := fShow; 
  380.   FocusChange(HasFocus); 
  381.   Result := S_OK; 
  382. end; 
  383.  
  384. function TTestTextBox.TranslateAcceleratorIO(var lpMsg: TMsg): HResult; 
  385. begin 
  386.   if (lpMsg.wParam <> VK_TAB) then 
  387.   begin 
  388.     TranslateMessage(lpMsg); 
  389.     DispatchMessage(lpMsg); 
  390.     Result := S_OK; 
  391.   end 
  392.   else 
  393.   begin 
  394.     Result := S_FALSE; 
  395.   end; 
  396. end; 
  397.  
  398. function TTestTextBox.UIActivateIO(fActivate: BOOL; 
  399.   var lpMsg: TMsg): HResult; 
  400. begin 
  401.   HasFocus := fActivate; 
  402.   if HasFocus then 
  403.     frmIE.SetFocus; 
  404.   Result := S_OK; 
  405. end; 
  406.  
  407. initialization 
  408.   TTestTextBoxFactory.Create(ComServer, TTestTextBox, Class_TestTextBox, 
  409.     'BackSpace有效性测试', '测试输入框中的BackSpace', ciMultiInstance, tmApartment);   
  410. end.  

文章来源:http://mailysf.blog.zj.com/d-143742.html

阻断弹出式广告的BHO

作者:傻猫 发布时间:October 15, 2007 分类:Delphi No Comments

随着网络免费的大潮的退去,网站变得越来越商业化。浏览一些常去的网站,每看一个页面都会弹出N多的广告窗口,而且都是花花绿绿的Flash和Gif小动画,浪费带宽(我在家还是拨号上网),同时干扰了正常的阅读,非常讨厌。那么如何才能将这些广告屏蔽掉呢?答案就是Browser Helper Object(简称BHO)。

BHO实际上也是一个简单的IE扩展COM组件,它和其它COM组件的区别就在于其它扩展需要一些用户的手工操作,如点击菜单,点击工具条按钮,在地址栏输入 网址等等触发动作才会被IE加载。而BHO则不同,每当IE启动时,都会自动去加载BHO而无须任何触发条件,另外BHO还可以监听IE的各类事件的通知消息,比如窗 口大小的变化,下载是否完成等事件。

由于BHO可以在一启动IE就被加载,并能监听各种事件,我们就可以使用BHO扩展实现限制用户浏览某些色情网站,或者搜集用户浏览喜好信息等功能。接下来, 我们就来实现一个能够阻断广告弹出的BHO扩展。

delphi代码
  1. 创建COM组件   
  2.   
  3.        新建一个ActiveX Library,保存为IEBHO.dpr,然后新建一个名为TIEAdvBHO的COM Object,然后保存生成的文件为CIEBHO.pas,作为BHO扩展,需要实现两 个接口IObjectWithSite和IDispatch,其中 IObjectWithSite接口同前面的工具条扩展一样可以用来获得浏览器的接口,而IDispatch接口,则被用来监听浏览器的事件。下面就是BHO扩展的类定义:   
  4.   
  5. type  
  6.   TTIEAdvBHO = class(TComObject, IObjectWithSite, IDispatch)   
  7.   private  
  8.     FIESite: IUnknown;   
  9.     FIE: IWebBrowser2;   
  10.     FCPC: IConnectionPointContainer;   
  11.     FCP: IConnectionPoint;   
  12.     FCookie: Integer;   
  13.   protected  
  14.     //IObjectWithSite接口方法定义  
  15.     function SetSite(const pUnkSite: IUnknown): HResult; stdcall;   
  16.     function GetSite(const riid: TIID; out site: IUnknown): HResult; stdcall;   
  17.     //IDispatch接口方法定义  
  18.     function GetTypeInfoCount(out Count: Integer): HResult; stdcall;   
  19.     function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;   
  20.       stdcall;   
  21.     function GetIDsOfNames(const IID: TGUID; Names: Pointer;   
  22.       NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;   
  23.     function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;   
  24.       Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;   
  25.       stdcall;   
  26.     //阻断广告弹出事件处理过程  
  27. procedure DoNewWindow2(var ppDisp: IDispatch; var Cancel: WordBool);   
  28.     procedure DoBeforeNavigate2(const pDisp: IDispatch; var URL: OleVariant; var Flags: OleVariant; var TargetFrameName: OleVariant; var PostData: OleVariant;var Headers: OleVariant; var Cancel: WordBool);   
  29. end;   
  30.     
  31.   
  32. IObjectWithSite的接口的实现   
  33.   
  34.     
  35.   
  36. 先看IObjectWithSite的接口的实现,当IE加载BHO扩展后,会调用BHO的扩展,把自身的IUnknown接口作为参数pUnkSite传给扩展,BHO扩展应该从pUnkSite参数中获得浏览器接口IWebBrowser2,同时为了监听浏览器的事件,还需要获得事件链接点接口,IE的支持的事件都定义在DWebBrowserEvents2的双接口中,使用链接点的Advise方法建立对IE事件的监听,注意Advise方法调用后 会返回一个Cookie,需要保存Cookie,后面在退出IE时,需要Cookie作为参数来断开对IE事件的监听。   
  37.   
  38. function TTIEAdvBHO.SetSite(const pUnkSite: IInterface): HResult;   
  39. begin  
  40.   Result := E_FAIL;   
  41.   //保存接口  
  42.   FIESite := pUnkSite;   
  43.   if not Supports(FIESite, IWebBrowser2, FIE) then  
  44. Exit;   
  45.   //获得事件连接点  
  46.   if not Supports(FIE, IConnectionPointContainer, FCPC) then  
  47.     Exit;   
  48.   FCPC.FindConnectionPoint(DWebBrowserEvents2, FCP);   
  49.   //监听事件  
  50.   FCP.Advise(Self, FCookie);   
  51.   Result := S_OK;   
  52. end;   
  53. 后面IE有时会调用IObjectWithSite接口的GetSite方法获得需要的接口,这时可以将保存的接口返回。   
  54.   
  55. function TTIEAdvBHO.GetSite(const riid: TIID;   
  56.   out site: IInterface): HResult;   
  57. begin  
  58.   if Supports(FIESite, riid,site) then  
  59.     Result := S_OK   
  60.   else  
  61.     Result:= E_NOINTERFACE;   
  62. end;   
  63.     
  64.   
  65. IDispatch接口的实现   
  66.   
  67.     
  68.   
  69. 前面我们在SetSite中建立了对IE事件的监听,建立事件监听后每当IE产生了新的事件,它就会调用扩展的IDispatch接口的Invoke方法通知扩展发生的事件类型以及事件参数,并请求扩展对事件进行处理。因此对于BHO扩展来说,IDispatch接口的Invoke方法是必须实现的,而其它的GetTypeInfoCount,GetTypeInfo和GetIDsOfNames方法都无须实现,只要返回结果为E_NOTIMPL,表示未实现该方法就可以了。   
  70.   
  71.     
  72.   
  73. function TTIEAdvBHO.GetIDsOfNames(const IID: TGUID; Names: Pointer;   
  74.   NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;   
  75. begin  
  76.   Result := E_NOTIMPL;   
  77. end;   
  78.     
  79. function TTIEAdvBHO.GetTypeInfo(Index, LocaleID: Integer;   
  80.   out TypeInfo): HResult;   
  81. begin  
  82.   Result := E_NOTIMPL;   
  83.   pointer(TypeInfo) := nil;   
  84. end;   
  85.     
  86. function TTIEAdvBHO.GetTypeInfoCount(out Count: Integer): HResult;   
  87. begin  
  88.   Result := E_NOTIMPL;   
  89.   Count := 0;   
  90. end;   
  91.     
  92.   
  93. 事件的监听   
  94.   
  95.     
  96.   
  97. IE支持的事件都定义在DWebEvents2接口中,如下:   
  98.   
  99.   DWebBrowserEvents2 = dispinterface   
  100.     ['{34A715A0-6587-11D0-924A-0020AFC7AC4D}']   
  101.     procedure StatusTextChange(const Text: WideString); dispid 102;   
  102.     procedure ProgressChange(Progress: Integer; ProgressMax: Integer); dispid 108;   
  103.     procedure CommandStateChange(Command: Integer; Enable: WordBool); dispid 105;   
  104.     procedure DownloadBegin; dispid 106;   
  105.     procedure DownloadComplete; dispid 104;   
  106.     procedure TitleChange(const Text: WideString); dispid 113;   
  107.     procedure PropertyChange(const szProperty: WideString); dispid 112;   
  108. procedure BeforeNavigate2(const pDisp: IDispatch; var URL: OleVariant; var Flags:   
  109.  OleVariant; var TargetFrameName: OleVariant; var PostData: OleVariant;                                  
  110. var Headers: OleVariant; var Cancel: WordBool); dispid 250;   
  111.     procedure NewWindow2(var ppDisp: IDispatch; var Cancel: WordBool); dispid 251;   
  112.     procedure NavigateComplete2(const pDisp: IDispatch; var URL: OleVariant); dispid 252;   
  113.     procedure DocumentComplete(const pDisp: IDispatch; var URL: OleVariant); dispid 259;   
  114.     procedure OnQuit; dispid 253;   
  115.     procedure OnVisible(Visible: WordBool); dispid 254;   
  116.     procedure OnToolBar(ToolBar: WordBool); dispid 255;   
  117.     procedure OnMenuBar(MenuBar: WordBool); dispid 256;   
  118.     procedure OnStatusBar(StatusBar: WordBool); dispid 257;   
  119.     procedure OnFullScreen(FullScreen: WordBool); dispid 258;   
  120.     procedure OnTheaterMode(TheaterMode: WordBool); dispid 260;   
  121.   end;   
  122.     
  123.   
  124. 可以看到每个事件中的后面都有一个dispid关键加上数字如 258 ,260等等。Dispid的数字就是事件类型的标识符号。IDispatch的Invoke方法定义如下:   
  125.     function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;   
  126. Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;   
  127.   
  128. 当IE调用Invoke方法时,会设定DispId参数为事件的标识符号,这样我们就可以知道IE发生了什么事件。对于要实现 的阻断广告窗口弹出来说,我们只需关心BeforeNavigate2和OnQuit事件就可以了,因为当广告窗口弹出前,会激发 IE的BeforeNavigate2事件,而弹出式窗口一般没有工具条,所以只要BeforeNavigate2事件中判断当前页面是否有 工具条就可以判断是否是弹出窗口,并予以禁止。而当IE退出时,会激发OnQuit事件,在OnQuit事件中应该断开事件 监听,同时清理分配的资源。下面就是截获BeforeNavigate2和OnQuit事件的Invoke方法的实现:   
  129.   
  130. procedure BuildPositionalDispIds(pDispIds: PDispIdList; const dps: TDispParams);   
  131. var  
  132.   i: integer;   
  133. begin  
  134.   Assert(pDispIds <> nil);   
  135.   for i := 0 to dps.cArgs - 1 do  
  136.     pDispIds^[i] := dps.cArgs - 1 - i;   
  137.   if (dps.cNamedArgs <= 0then  
  138.     Exit;   
  139.   for i := 0 to dps.cNamedArgs - 1 do  
  140.     pDispIds^[dps.rgdispidNamedArgs^[i]] := i;   
  141. end;   
  142.     
  143.   
  144. function TTIEAdvBHO.Invoke(DispID: Integer; const IID: TGUID;   
  145.   LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,   
  146.   ArgErr: Pointer): HResult;   
  147. var  
  148.   dps: TDispParams absolute Params;   
  149.   bHasParams: boolean;   
  150.   pDispIds: PDispIdList;   
  151.   iDispIdsSize: integer;   
  152. begin  
  153.   pDispIds := nil;   
  154.   iDispIdsSize := 0;   
  155.   bHasParams := (dps.cArgs > 0);   
  156.   if (bHasParams) then  
  157.   begin  
  158.     iDispIdsSize := dps.cArgs * SizeOf(TDispId);   
  159.     GetMem(pDispIds, iDispIdsSize);   
  160.   end;   
  161.   try  
  162.     if (bHasParams) then  
  163.       BuildPositionalDispIds(pDispIds, dps);   
  164.     Result := S_OK;   
  165.     case DispId of  
  166.       250://BeforeNaviage2事件id  
  167.         begin  
  168.           DoBeforeNavigate2(IDispatch(dps.rgvarg^[pDispIds^[0]].dispval),   
  169.               POleVariant(dps.rgvarg^[pDispIds^[1]].pvarval)^,   
  170.               POleVariant(dps.rgvarg^[pDispIds^[2]].pvarval)^,   
  171.               POleVariant(dps.rgvarg^[pDispIds^[3]].pvarval)^,   
  172.               POleVariant(dps.rgvarg^[pDispIds^[4]].pvarval)^,   
  173.               POleVariant(dps.rgvarg^[pDispIds^[5]].pvarval)^,   
  174.               dps.rgvarg^[pDispIds^[6]].pbool^);   
  175.         end;   
  176.       253://OnQuit事件ID  
  177.         begin  
  178.           FCP.Unadvise(FCookie);   
  179.         end;   
  180.     else  
  181.       Result := DISP_E_MEMBERNOTFOUND;   
  182.     end;   
  183.   finally  
  184.     if (bHasParams) then  
  185.       FreeMem(pDispIds, iDispIdsSize);   
  186.   end;   
  187. end;   
  188.     
  189.   
  190. 在Invoke方法中,Params参数包含了被激发的事件包含的参数的数目以及参数的值,而BuildPositionalDispIds 则从Params参数中提取参数值,并放到数组中,然后在BeforeNavigate2事件中,调用DoBeforeNavigate2过程对 事件进行处理,事件参数作为过程参数被传递过去,下面是具体禁止弹出网页的DoBeforeNavigate2的处理过程:   
  191.   
  192. procedure TTIEAdvBHO.DoBeforeNavigate2(const pDisp: IDispatch; var URL,   
  193.   Flags, TargetFrameName, PostData, Headers: OleVariant;   
  194.   var Cancel: WordBool);   
  195. begin  
  196.   if FIE.ToolBar=0 then FIE.Quit;   
  197. end;   
  198.     
  199.   
  200. 在过程中,首先,调用IWebBrowser2接口的Toolbar属性判断页面是否有工具条,如果没有,则调用IE的退出方法关闭弹出窗口。另外在Invoke中还在OnQuit事件激发时,调用事件连接点的UnAdvise方法,断开事件监听。   
  201.   
  202.  注册扩展   
  203.   
  204.  注册扩展非常简单,只要在注册表中关键字HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion   
  205. \explorer\Browser Helper Objects\下添加值为扩展的Guid的字符串形式的下级关键字就可以了。   
  206.   
  207.  type  
  208.   
  209.   TIEAdvBHOFactory = class(TComObjectFactory)   
  210.   public  
  211.     procedure UpdateRegistry(Register: Boolean); override;   
  212.   end;   
  213.     
  214. { TIEAdvBHOFactory }  
  215.     
  216. procedure TIEAdvBHOFactory.UpdateRegistry(Register: Boolean);   
  217. begin  
  218.   inherited;   
  219.   if Register then  
  220.     CreateRegKeyValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects\'                       + GuidToString(ClassID), '', '') 
  221.   else 
  222.     DeleteRegKeyValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects\'                       + GuidToString(ClassID), ''); 
  223. end; 
  224.   
  225. initialization 
  226.   TIEAdvBHOFactory.Create(ComServer, TTIEAdvBHO, Class_TIEAdvBHO, 
  227.     'TIEAdvBHO', '', ciMultiInstance, tmApartment);   
  228. end.   
  229.     
  230.   
  231. 注册扩展后,打开浏览器浏览新浪网站(http://www.sina.com.cn),你会发现平时讨厌的弹出广告窗口都消失了。  
  232.   
  233. 文章来源:http://delphi.sharpplus.com/Delphi/bho.htm  

电脑出问题了,奶奶的

作者:傻猫 发布时间:October 14, 2007 分类:我的生活 2 Comments

最近几天电脑老出问题,打开一个文件夹吧,只要里边有图片的,肯定死机,也不是怎么死机,反正就是怎么也打不开,点开后就无响应了,气死咯,昨天晚上还杀了一晚上的病毒,也没查出什么毒来,查查是不是流氓软件的原因,用360安全卫士扫了半天也没扫出个屁来,能想到的办法都想尽了,依旧如此,万般无奈之下,只有重装系统,我有朋友三天一大装系统,两天一小装,重来不用杀毒软件,一直裸奔,我可不行,电脑上要装的软件多,每次都重装那多麻烦啊,其实装系统花不了多少时间,唯独装软件麻烦,装个系统最多几分钟就搞定,GHOST恢复,两三分钟就恢复到新系统了,但是装个软件可不是几分钟就搞的事,因为我要用的软件有点多,要用的工具软件也多,真郁闷。。。

这电脑真他奶奶的贱,重装系统后速度确实快了,这个镜像盘是去年的时候建的,以前还装的瑞星2006正版呢,拨上网后就要求我升级到瑞星2007,升就升吧,反正是正版,不升白不升,大概花了半小时才升好,要下的文件太多了,将就用吧,我一直认为瑞星不错,前段时间用的NOD32,感觉还可以,他们都说NOD32占内存比较少,其实也不少啊,用的是免ID升级镜像,一天要升级好几次,虽然心里得意的笑,可是是免ID升级,用的是免费的升级服务器,还是有点虚,不是官方的升级服务器,万一下个什么什么文件,系统坏了,文件丢了划不着,还是就用偶的正版瑞星吧,至于以后还用不用NOD32,那要看心情了,就不定那天心血来潮就换来了

晚上本来想给韩国的一个朋友打个电脑,就是因为系统原因,打开SKYPE后老是无响应,启动不了,重新了几次skype软件,还是一样,挨球哦。于是下定决定重装系统,安好系统后,给韩国那个MM打了一个电话,虽然以前对她有点意思,但是现在已经成为历史了,因为偶已经有女朋友,她又太远,不现实啊,没缘分吧,其实当好朋友比当情人好,朋友之间最真诚,最纯洁的,朋友可以做一辈子的朋友,但是情人却不一样,朋友与情人的要求也不样,所以还是做朋友比较好,做好朋友好啊!

睡觉了,发发牢骚,今天下午跟LMM去河边溜达了一圈,从沙板桥那边乱窜,也不知道走到那里去了,只知道大方向,从那个小巷里穿来穿去,那个路真叫烂啊,全是水,最终还是走出了死胡同,看到铁轨就知道方向了,仔细一看,原来走了杨三住的地方那边去了,在路上还碰到他咯,哈哈,脚都走痛了,路边人太多,想方便一下又找不到厕所,于是搭上一个三轮车直接跑到SM广场去了,买了点吃的东西回来,用了近三百块,除了一大堆零食外,还买了两件衣服,一条牛仔裤,出门的时候还去参加SM广场的活动了,得了两张万达的电影院,爽,明天晚上可以去看电影了,哈哈

SM广场一周年庆活动,今天还拿了10月20号的生日派对入场券,下周六去Happy一下,说不定还中个小奖呢,哇哈哈哈。

成都成华SM欢乐周年庆,惊喜五重奏

作者:傻猫 发布时间:October 14, 2007 分类:魅力成都 No Comments

今天下午去SM广场买东西,出门时看到门口有几个漂亮的广告栏,于是凑上去瞧了一下,原来是SM商场一周岁生日,正在搞活动呢,在商场里有部分服务器戴上了寿星帽,原来是这个意思嗦。只要在SM广场购物,满100元就可以参加活动,还可以得到万达影院的电影票,偶已经占先了,领了两张电影票,准备明天晚上去看《天下第二》,还有SM生日派对入场券,下周六晚上去参加生日派对呢,住在东门朋友快去抢电影票吧。

他们的活动详细内容如下:

一重奏 —— SM广场图片展

dsc05486.jpg

活动时间:2007年10月1日—2007年10月21日
活动地点:一楼中间内
活动内容:SM发展历史展览,向您介绍SM的成长历史,看她如何从当初的小小鞋城,跻身成为现在的“亚太地区最佳零售商500强”。

 

二重奏 —— 寿星有贺礼
活动时间:2007年10月1日—2007年10月20日每天10:00-22:00
活动地点:一楼南门入口
活动内容:凡是10月20日出生的顾客,活动期间,当日在SM广场任意店铺累积消费满50元,都可凭本人身份证领取电影票2张,并可参加10月20日晚的生日派对,还有机会羸取总价值1020元的现金大奖,和价值1140元的海螺沟全家游。

三重奏 —— 购物抽大奖
活动时间:2007年10月1日—2007年10月20日每天10:00-22:00
活动地点:一楼南门入口

dsc05487.jpg

活动内容:活动期间,凭当日在SM广场任意店铺内单张收银条满100元的顾客,即可参加10月20日晚的生日派对,还有机会羸取价值1140元的海螺沟全家游。累计消费满勤300元的顾客,还可领取SM精美雨伞1把。

 

四重奏 —— 明星贺岁生日派对
活动时间:2007年10月20日18:30-20:30
活动地点:三楼多功能厅
活动内容:当晚会有神秘明星嘉宾到达生日派对现场,与所有到场顾客共庆SM一周年生日。分享美味的蛋糕,羸取惊喜大奖!

五重奏 —— 商品大优惠
活动时间:2007年10月8日-2007年10月30
活动地点:SM广场内
活动内容:活动期间,全场商家大让利,热卖全面出击中,感谢无情年来对SM广场的厚爱。

相濡以沫,不如相忘于江湖

作者:傻猫 发布时间:October 14, 2007 分类:我的生活 No Comments

语出自《庄子·大宗师》,原文“泉涸,鱼相与处于陆,相呴以湿,相濡以沫,不如相忘于江湖。与其誉尧而非桀也,不如两忘而化其道 。 ”

两条鱼被困在车辙里面,为了生存,两条小鱼彼此用嘴里的湿气来喂对方。 这样的情景也许令人感动,但是, 这样的生存环境并不是正常的,甚至是无奈的。对于鱼儿而言,最理想的情况是,海水终于漫上来,两条鱼也终于要回到属于它们自己的天地,最后,他们,相忘于江湖。在自己最适宜的地方,快乐的生活,忘记对方,也忘记那段相濡以沫的生活。

能够忘记的鱼,或许是最快乐的。而如果有其中一条鱼不能忘记呢?

对于人,对于感情或许也是如此吧。 相濡以沫,有时是为了生存的必要或是无奈。 “相濡以沫”,或许令人感动;而“相忘于江湖”则是一种境界,或许更需要坦荡、淡泊的心境吧。 能够忘记,能够放弃,也是一种幸福。

“相濡以沫”长久以来当做了爱情美好的代名词,人们总是只看见了美好的开头,广为赞扬逆境中的互助,却很少有人看完故事后面。两条鱼最终也各自东西,它们相别于江湖,一条鱼是否还怀念另一条,已经无从考证。它们是不是还记得有那么一个时候,困在车辙里面。 不知道故事后面的,或者说,不愿意读故事后面的,都只是对美好的向往罢了。