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