天天看點

浏覽器擴充系列————異步可插入協定(pluggable protocol)的實作

      IE中有很多我們比較熟悉的協定,如http,https,mailto,ftp等。當然你也可以實作自己定義的協定,稍微談一下這裡所說的協定,從我的了解來說這裡的協定隻有當你的網頁引用某個資源時才會調用,而不是随便在某個屬性的值前面加上某個協定的名稱就可以了。常見的協定調用如img的src屬性中,很多元素style中的background-image屬性中,還有a标簽的href屬性中。

       言歸正傳,前面說到的實作自定義協定就用到了一種IE下異步可插入協定的技術。

       從分類上來說,這種異步可插入協定的技術還分為兩種:

永久的異步可插入協定,就像http,https,mailto這種不論在ie中或是其它用到浏覽器控件中使用。

臨時的異步可插入協定,隻能用在某個程序内,用完可以擦除。

     因為網上介紹永久的異步可插入協定的資源還很多,如codeproject上的:

<a href="http://www.cppblog.com/bigsml/archive/2008/03/23/45145.html" target="_blank">http://www.cppblog.com/bigsml/archive/2008/03/23/45145.html</a>
<a href="http://www.codeproject.com/KB/aspnet/AspxProtocol.aspx" target="_blank">http://www.codeproject.com/KB/aspnet/AspxProtocol.aspx</a>

    這篇就主要談談如何實作臨時的異步可插入協定的方法。

下面談下具體的實作。

在本實作中主要用到了下面這幾個接口:

IInternetProtocol

IInternetProtocolRoot

IInternetSession

IInternetProtocolInfo

IInternetProtocol接口

它有四個方法:

LockRequest 

Locks the requested resource so that the IInternetProtocolRoot::Terminate method can be called, and the remaining data can be read.

Read 

Reads data that the pluggable protocol handler gets.  

Seek 

Moves the current seek offset. 

UnlockRequest 

Frees any resources associated with a lock.

主要用于下載下傳資源,将處理後的資源傳遞給IE進行顯示。

IInternetProtocolRoot接口

Abort 

Cancels an operation that is in progress.  

Continue 

Enables the pluggable protocol handler to continue processing data on the apartment thread.  

Resume 

Not currently implemented.  

Start 

Starts the operation.

Suspend 

Not implemented. 

Terminate 

Releases the resources used by the pluggable protocol handler.  

主要用于解析資源,準備待下載下傳的資源。

IInternetSession接口

它包括9個方法,根據需要我們隻用到了下面兩個方法:

RegisterNameSpace

Registers a temporary pluggable namespace handler on the current process.

UnregisterNameSpace 

Unregisters a temporary pluggable namespace handler. 

實作臨時可插入協定的注冊和取消。

IInternetProtocolInfo接口

它包括4個方法。

CombineUrl 

Combines a base URL and relative URL into a full URL.  

CompareUrl 

Compares two URLs and determines if they are equal.

ParseUrl 

Parses a URL.  

QueryInfo 

Gets information related to the specified URL.  

主要提供了對于Url的處理。

此外,在構造IInternetSession的時候還用到了一個外部方法:

[DllImport("urlmon.dll")]

private

static

extern

void CoInternetGetSession(int sessionMode,

out

IInternetSession session, int reserved);

預備的知識介紹完,下面就是具體實作了。

一般方法是在一個類中實作IInternetProtocol,IInternetProtocolRoot,IInternetProtocolInfo三個接口,然後通過IInternetSession接口的RegisterNameSpace方法來注冊這個自定義協定,用完這後再調用UnregisterNameSpace方法來登出這個自定義協定。

關于IE和IInternetProtocol,IInternetProtocolRoot,IInternetProtocolInfo三個接口的調用流程可以參考msdn上的介紹,中文版的翻譯可以參考:

<a href="http://www.cnblogs.com/volnet/archive/2008/03/28/About_Asynchronous_Pluggable_Protocols.html" target="_blank">http://www.cnblogs.com/volnet/archive/2008/03/28/About_Asynchronous_Pluggable_Protocols.html</a>

首先通過CoInternetGetSession方法得到一個IInternetSession對象,然後注冊自定義的協定:

IInternetSession session;

CoInternetGetSession(0, out session, 0);

Guid guid = new

Guid("79EAC9E4-BAF9-11CE-8C82-00AA004BA90B");

session.RegisterNameSpace(new

ClassFactory(), ref guid, ProcotolName, 0, null, 0);

在注冊的時候要傳入一個實作了IClassFactory接口的對象,下面是對次接口的實作:

// Interface IClassFactory is here to provide a C# definition of the

// COM IClassFactory interface.

[

ComImport, // This interface originated from COM.

ComVisible(true), // It is not hard to imagine that this interface must not be exposed to COM.

InterfaceType(ComInterfaceType.InterfaceIsIUnknown), // Indicate that this interface is not IDispatch-based.

Guid("00000001-0000-0000-C000-000000000046") // This GUID is the actual GUID of IClassFactory.

]

public

interface

IClassFactory

{

void CreateInstance(IntPtr pUnkOuter, ref

Guid riid, out

IntPtr ppvObject);

}

[ComVisible(true)]

class

ClassFactory : IClassFactory

#region IClassFactory Implementations

IntPtr ppvObject)

ppvObject = Marshal.GetComInterfaceForObject(new

MyImageProtocol(), typeof(IInternetProtocolInfo));

#endregion