瀏覽QQ空間的時候發現,只要在IE地址中輸入象一下這種形式的地址,
tencent://Message/?Uin=251464630&websiteName=qzone.qq.com&Menu=yes
就會彈出給 251464630 傳送資訊的對話方塊,也就是說QQ對IE位址列的東西做了監控。而且可以發現輸入地址確定之後他就啟動了timwp.exe這個程式。
還有新增QQ書籤時的:tencent://AddPortal/?PanelID=10020 在PPlive 也有實現類似的功能,只要你電腦上安裝了PPlive 這個程式,在IE位址列中輸入
synacast://09jN1+TK3K3nodzJoaLOmqeS1KGhoKOZoqGcltid1qeZy9ec1dbRy9ue1aKe5pzI2dSpna
+VpJbayuPKrbOvvcySpRMUHl01NaScmcEIGRMUNh4vQzNmNR8IGaqemauXq7OvvcySpZiekrCWoKOfj
+LU162emaiToaGgl6eToaalo66VoKCmoaaVoJbX2LPa1ODgo6WU057TmtqT3tXgo66VoKCn3trV5KqbmNuT16HQl
+TK5KqkmaaVq+XQ2eqfn5/Nl92W1J7azuqfqKCcmbHZ0+Dgo6WU1J7TmtqT3tXgo66Vq+TP2eqfn5/Ol92W1J7azuqfqKCn3dnV5KqbmNyT16HQl
+TK5KqkmZzZ2NXZzrPN5ePg3N7G4tWSwtvR3N/judfM1bnQpqeXpZavyurG3N/Tstqip6k=然
後確定,就會彈出播放CCTV5的視窗。很有意思的一種功能,竟然這麼多程式都用到了,我也就找了一下,找到了一下實現方法。
方法一: 也就是QQ和PPlive所採用的方法,在登錄檔裡面新增兩種型別的註冊。
QQ的:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\TENCENT]
@="TencentProtocol"
"URL Protocol"="C:\\Program Files\\Tencent\\QQ\\Timwp.exe"
[HKEY_CLASSES_ROOT\TENCENT\DefaultIcon]
@="C:\\Program Files\\Tencent\\QQ\\Timwp.exe,1"
[HKEY_CLASSES_ROOT\TENCENT\shell]
[HKEY_CLASSES_ROOT\TENCENT\shell\open]
[HKEY_CLASSES_ROOT\TENCENT\shell\open\command]
@="\"C:\\Program Files\\Tencent\\QQ\\Timwp.exe\" \"%1\""
PPlive的:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Synacast]
@="URL:synacast Protocol"
"Version"="1.5.38"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\Synacast\DefaultIcon]
@="C:\\Program Files\\PPLive\\PPLive.exe"
[HKEY_CLASSES_ROOT\Synacast\Shell]
[HKEY_CLASSES_ROOT\Synacast\Shell\Open]
[HKEY_CLASSES_ROOT\Synacast\Shell\Open\Command]
@="C:\\Program Files\\PPLive\\PPLive.exe \"%1\""
通過多方查詢終於發現是登錄檔這兩項在起作用,原來只要在登錄檔裡象新增副檔名一樣,新增兩個Synacast和TENCENT副檔名來,IE就會
自動查詢到這裡來呼叫相應的程式。IE果然和windows系統核心整合起來了!原來登錄檔副檔名項還有這種作用,自己見識太少了,這種方法實
現IE地址的自定義估計是最簡單的了。
方法二:一開始不知道方法一的時候,在網上找了很多可以實現這種功能的程式碼,採用BHO(Browser Helper Object,瀏覽器輔助物件)或者
IURLSearchHook介面 來做到。也就是通常所說的IE外掛了,我這裡統稱為方法二。如果不知道什麼叫做BHO和IURLSearchHook的就去搜尋一下
吧,最近流氓外掛很火,所以這個技術也有多人提到,藉助IURLSearchHook還可以實現中文實名上網等功能,不過那些臭名昭著的流氓軟體可
都不是這樣子坐的他更多的精力是放到防止別人解除安裝那邊去了。
因為我沒有編寫過ATL或者COM方面的程式,所以也就藉著這個機會寫了個IURLSearchHook的實現,以後碰到IE外掛程式設計,ATL程式設計,COM編
程,Shell介面程式設計的時候也好能夠玩一玩,好像shell介面程式設計還是有很多有意思東西的。
這個是MSDN上IURLSearchHook介面的說明:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/ifaces/iurlsearchhook/iurlsearchhook.asp
下面這個是MSDN上shell介面程式設計的說明:http://msdn2.microsoft.com/en-us/library/ms631201.aspx
這個是ATL程式設計的 http://msdn2.microsoft.com/zh-cn/library/65t81w8a(vs.80).aspx
有一片不錯的來之VCbase的文章說得是“ATL 實現定製的 IE 瀏覽器欄、工具欄和桌面工具欄”
http://www.vckbase.com/document/viewdoc/?id=1457
簡單的說,IURLSearchHook被瀏覽器用來轉換一個未知的URL協議地址,當瀏覽器企圖去開啟一個未知協議的URL地址時,瀏覽器首先嚐試從這
個地址得到當前的協議,如果不成功,瀏覽器將尋找系統裡所有註冊為“URL Search
Hook”(資源搜尋鉤子,USH)的物件並把這個IE不能理解的地址傳送過去,如果某個USH物件“認識”這個地址,它就返回一個特定的標識告訴IE它
知道怎麼開啟這個地址,然後IE就根據約定的方法呼叫它,最終開啟這個地址。其實USH物件並不陌生,我們一些偷懶的使用者就經常為了省事而不輸入
“http://”,但是IE最終還是能認出並開啟某個地址,就是USH的功勞。當然通過BHO的 GetSite方法也可以做到同樣的事情,不過
IURLSearchHook簡單一些,只有一個Translate方法,我技術不行所以專挑簡單的^_^
以下是實現程式碼:
使用VC2003新建一個名字為UrlSearchHook的ATL工程
進入工程之後在類檢視中右擊工程名字-》新增類—》 新增一個 叫WidebrightBlog的 “ATL簡單物件”
嚮導裡面列了很多,不過我是沒找到啦,所以之後手工新增介面實現了,以下全部程式碼,紅色的是自己寫的。
// WidebrightBlog.h : CWidebrightBlog 的宣告
#pragma once
#include "resource.h" // 主符號
#include <comdef.h>
#include <shlobj.h>
// IWidebrightBlog
[
object,
uuid("1F0B2F61-221A-456C-A8E1-E0E01796E482"),
dual, helpstring("IWidebrightBlog 介面"),
pointer_default(unique)
]
__interface IWidebrightBlog : IDispatch
{
};
// CWidebrightBlog
[
coclass,
threading("apartment"),
vi_progid("UrlSearchHook.WidebrightBlog"),
progid("UrlSearchHook.WidebrightBlog.1"),
version(1.0),
uuid("44AA49F1-7E20-472E-A5A4-08D3233D9132"),
helpstring("WidebrightBlog Class")
]
class ATL_NO_VTABLE CWidebrightBlog :
public IWidebrightBlog,
public IURLSearchHook
{
public:
CWidebrightBlog()
{
// MessageBox(NULL,"在CWidebrightBlog()","widebright ",MB_OK);
}
DECLARE_PROTECT_FINAL_CONSTRUCT()
//元件介面對映部分,該部分對映主要是告訴QueryInterface能返回哪些介面給外部
BEGIN_COM_MAP(CWidebrightBlog)
COM_INTERFACE_ENTRY(IWidebrightBlog)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IURLSearchHook)
END_COM_MAP()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
public:
STDMETHODIMP Translate( LPWSTR lpwszSearchURL, DWORD cchBufferSize)
{
// MessageBox(NULL,"在Translate函式中了","widebright ",MB_OK);
if (wcsncmp(L"widebright",lpwszSearchURL,10 )==0)
{
// MessageBox(NULL,"找到了","widebright ",MB_OK);
wcscpy(lpwszSearchURL,L"http://hi.baidu.com/widebright");
return S_OK ;
}
return E_FAIL; //沒有修改lpwszSearchURL
return S_FALSE; //修改了lpwszSearchURL的,但還需要繼續處理
}
int MyFunction(void) //這個是自己利用嚮導生成Method的函式,想試一下COM介面,沒什麼用的,根本程式無關。
{
return 0;
}
};
不過編譯一下,有錯,說是IURLSearchHook介面GUID沒定義,明顯是有這個介面的,在MSDN裡面沒有什麼說明,最後在CSDN上找到一張帖子
,說是VC2003裡面 和VC6的ATL不同,在VC6裡面上面程式碼是可以通過的,但在VC2003以後版本就不行了。這是引用帖子中原話“There are two
<comdef.h> header files in VC.NET, one in Vc7/include and the other in Vc7/PlatformSDK/include. The former splits off the
smart pointer typedefs into comdefsp.h, and it doesn't include IContextMenu. The latter does. You can try to #include the
PlatformSDK header directly, change your INCLUDE path order, or supply the missing typedef yourself, e.g.
struct __declspec(uuid("000214e4-0000-0000-c000-000000000046"))
IContextMenu;
_COM_SMARTPTR_TYPEDEF(IContextMenu, __uuidof(IContextMenu));”
我檢視了VC目錄下的兩個檔案也確實如此,所以手工新增了一下Include路徑確保#include <comdef.h> 包含的是Platform中的那個comdef.h就行了。在 UrlSearchHook工程-“工程屬性”-》“C/C++” -》 “附加包含目錄” 屬性,增加一個 "C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include" 。
然後再編譯就通過了,生成了dll檔案,整個程式碼很簡單,就是實現IURLSearchHook介面的 Translate函式,BEGIN_COM_MAP和 COM_INTERFACE_ENTRY幾個ATL巨集宣告IURLSearchHook介面外部可見,就一些ok了。
不過要讓IE知道有這個URL Search Hook擴充套件,還要修改登錄檔才行。我手工在HKEY_CURRENT_USER\Software\ Microsoft\ Internet Explorer\ UrlSearchHooks 新增了一項 REG_SZ型別名字為{44AA49F1-7E20-472E-A5A4-08D3233D9132} 的項,其中名字和你生成的dll註冊型別對應 ,在上面程式碼裡也可以看到。注意的是MSDN上說的是HKEY_LOCAL_MACHINE\..\.. 登錄檔位置,但其他文件說得是HKEY_CURRENT_USER位置,而且我在HKEY_LOCAL_MACHINE下也沒看到 UrlSearchHooks項,不知道新增在HKEY_LOCAL_MACHINE會不會有效果,不過新增在HKEY_CURRENT_USER\ Software\ Microsoft\ Internet Explorer\ UrlSearchHooks會成功就是了。
好了,啟動IE7,輸入widebright開頭的地址,都跳到http://hi.baidu.com/widebright 來了, 在卡卡上網助手裡可見 CWidebrightBlog Object 位址列搜尋 項 。
方法三: 也是使用IE擴充套件介面實現,我之所以把它獨立處理列為一種方法,是我覺得是一種很使用的價值的功能。我發現電腦裡面有的電子書在我升級到IE7後看不了,可能也是這方面有問題。在登錄檔中都可見到這些Ebook電子書的註冊值,可能Ebook也就是這麼幹的吧。
更多相關內容搜尋:協議外掛(Asynchronous Pluggable Protocols),MIME Filter
我是在下面這篇文章裡面發現這種實現方法的,下面轉載:
轉貼 來網際網路 ,不過文章作者倒是很眼熟,好像是看過他幾篇文章了,支援!
IE非同步可插入協議擴充套件
作者 陳省
介紹
對於每天都要使用的IE瀏覽器的人來說,輸入www.google.com 等網址進行網上衝浪就象呼吸一樣自然。大多數情況時,我們可能根本想不起來要在網址前面加上http:// 來宣告要訪問的是一個基於http協議的Web網站。所謂網路協議,其實無非就是一組描述如何獲取不同資源並進行通訊的行為規則。IE瀏覽器除了內建了對 http協議外,還持ftp和gopher等協議。
從IE4開始,IE允許通過插入式非同步協議擴充套件來擴充套件它處理協議的功能,人們可以通過自定義的擴充套件來讓IE支援更多的協議,比如一些不是普遍支援的流媒體協議等。此外,我們還可以通過插入式協議擴充套件讓IE可以以HTML檔案的形式顯示一個資料庫中的表。
非同步可插入協議的原理
可插入式協議是基於非同步的URL
Moniker技術的。Moniker最早是從OLE2中引入的概念,當時的Moniker就是一個COM繫結和定位物件,人們可以使用Moniker來
定位並載入被儲存到檔案中的COM元件,實現COM的可持續性,一開始Moniker是基於同步方式實現的。隨著網路技術的發展,定位並從網路上獲取資訊
的需求逐漸超過了對本地資料的存取需求,因為網路的通訊通常都是不穩定的,因此需要以非同步的方式來實現。為此微軟設計了URL
moniker物件來提供網路資訊下載過程的一個統一介面,基於URL來訪問網路資源的Moniker演變成了以非同步方式實現的Moniker。
IE的URL moniker是在urlmon.dll動態連線庫中實現的。當urlmon.dll處理http, ftp,
Gopher等內建協議的訪問時,它把訪問請求轉發給內部的一個COM元件來處理,該COM元件使用WinInet函式來完成實際的處理工作。對於非內建
的協議,urlmon.dll則把請求轉發給特定的可插入協議擴充套件進行處理,比如說mailto:協議。
一個典型的非同步可插入協議(APP)的主要工作的就是接收一個非IE內建的UrlURL協議字串,對字串進行解析,分析字串的元素,並根據協議訪問相應的系統或者網路資源,並將網路資源的內容輸出到瀏覽器。
一個自定義的電子書可插入協議的實現
我平時業餘時間喜歡上網上找一些娛樂小說和技術書籍來看,其中有一些小說採用的是付費方式才能看既然是付費的小說,自然會提供一些加密的方式,避免盜版書在網上的傳播。
接下來,我想寫一個程式對一些Html檔案進行加密,只有使用者在瀏覽器中鍵入EBook://c:\abc.htm,然後輸入口令後,才能看到解密後的Html頁面。接下來,就看如何使用APP來實現這樣一個可插入協議。
建立COM元件
首先,新建一個ActiveX Library專案,儲存為IEProtocol.dpr,然後新建一個名為TIEEncryptAPP的COM元件,儲存為 CIEProtocol.pas檔案。一個APP元件至少要實現IInternetProtocol介面(該介面定義在urlmon.pas單元中),又 由於IInternetProtocol介面派生自IInternetProtocolRoot,所以我們還需要實現 IInternetProtocolRoot介面。下面是實現了IInternetProtocol介面的TIEEncryptAPP類的定義:
type
TIEEncryptAPP = class(TComObject, IInternetProtocol)
protected
//IInternetProtocolRoot介面定義
function Start(szUrl: LPCWSTR; OIProtSink: IInternetProtocolSink;
OIBindInfo: IInternetBindInfo; grfPI, dwReserved: DWORD): HResult;
stdcall;
function Continue(const ProtocolData: TProtocolData): HResult; stdcall;
function Abort(hrReason: HResult; dwOptions: DWORD): HResult; stdcall;
function Terminate(dwOptions: DWORD): HResult; stdcall;
function Suspend: HResult; stdcall;
function Resume: HResult; stdcall;
//IInternetProtocol介面定義
function Read(pv: Pointer; cb: ULONG; out cbRead: ULONG): HResult; stdcall;
function Seek(dlibMove: LARGE_INTEGER; dwOrigin: DWORD; out libNewPosition:
ULARGE_INTEGER): HResult; stdcall;
function LockRequest(dwOptions: DWORD): HResult; stdcall;
function UnlockRequest: HResult; stdcall;
end;
其中IInternetProtocolRoot介面的方法意義如下:
Abort
停止一個正在進行的資源下載過程
Continue
允許協議擴充套件繼續進行進行資源資料下載過程。
Resume
未來擴充需要,暫時未實現。
Start
啟動同該協議相關的資源下載過程。
Suspend
未來擴充需要,暫時未實現
Terminate
結束下載過程,釋放擴充套件分配的資源。
而IInternetProtocol協議的方法定義如下:
LockRequest
鎖定資源下載請求,這時IInternetProtocolRoot介面的Terminate方法將允許被呼叫,與此同時未下載完的資料仍然可以被讀取。
Read
瀏覽器呼叫這個方法從協議擴充套件獲得相應的資料。
Seek
移動讀取資料的位置。
UnlockRequest
釋放請求鎖定
對於電子圖書這樣一個簡單的協議擴充套件來說,我們只需要實現Start方法來啟動下載過程,並通過Read方法向瀏覽器返回解密後的電子圖書的資料就可以了。其它的方法只要簡單的返回請求結果,而無須做任何的操作:
function TIEEncryptAPP.Abort(hrReason: HResult; dwOptions: DWORD): HResult;
begin
Result := Inet_E_Invalid_Request;
end;
function TIEEncryptAPP.Continue(
const ProtocolData: TProtocolData): HResult;
begin
Result := Inet_E_Invalid_Request;
end;
function TIEEncryptAPP.LockRequest(dwOptions: DWORD): HResult;
begin
Result := S_OK;
end;
function TIEEncryptAPP.Resume: HResult;
begin
Result := Inet_E_Invalid_Request;
end;
function TIEEncryptAPP.Seek(dlibMove: LARGE_INTEGER; dwOrigin: DWORD;
out libNewPosition: ULARGE_INTEGER): HResult;
begin
Result := E_Fail;
end;
function TIEEncryptAPP.Suspend: HResult;
begin
Result := Inet_E_Invalid_Request;
end;
function TIEEncryptAPP.Terminate(dwOptions: DWORD): HResult;
begin
Result := S_OK;
end;
function TIEEncryptAPP.UnlockRequest: HResult;
begin
Result := S_OK;
end;
啟動協議處理
首先來看如何啟動協議處理,當我們在瀏覽器中輸入EBook://c:\ebook.htm字串想要瀏覽加密的頁面檔案時,IE會找到EBook的擴充套件協議,然後呼叫協議的Start方法來啟動協議處理過程:
threadvar
ResultHTML: array[0..64 * 1024 - 1] of Char; { 64 kB }
CurrPos: Integer;
BytesLeft: Integer;
ProtSink: IInternetProtocolSink;
function TIEEncryptAPP.Start(szUrl: LPCWSTR;
OIProtSink: IInternetProtocolSink; OIBindInfo: IInternetBindInfo; grfPI,
dwReserved: DWORD): HResult;
Const
ErrorHTML = '<HTML><BODY BGCOLOR="#FFFFFF">'#13+
'<H2>瀏覽電子書%s時發生錯誤</H2>'#13+
'<P><I>%s</I></P>'#13+
'</BODY></HTML>';
var
S: string;
begin
S := WideCharToString(szURL);
{ EBook:// }
Delete(S, 1, 8);
//去掉後面/符號
SetLength(S, Length(S) - 1);
S := HTTPDecode(S);
if FileExists(S) then
begin
//顯示密碼提示框
if InputBox('密碼','請輸入密碼', '')<>'hubdog' then
S:=Format(ErrorHTML, [S, '無效的密碼'])
else
S := Decrypt(S);
end
else
S := Format(ErrorHTML, [S, '沒有找到檔案']);
CurrPos := 0;
BytesLeft := Length(S);
FillChar(ResultHTML, SizeOf(ResultHTML), 0);
StrPCopy(ResultHTML, S);
ProtSink := OIProtSink;
//資料通知
OIProtSink.ReportData(bscf_LastDataNotification, 0, BytesLeft);
//資料可完全獲得的通知
OIProtSink.ReportData(bscf_DataFullyAvailable, 0, BytesLeft);
Result := S_OK;
end;
Start方法中有一個szUrl的引數,對應著我們在瀏覽器中輸入的url字串(注意:IE會在輸入的字串末尾自動加上一個斜槓),為了獲得 要處理的被加了密的html檔案,使用Delete函式先從字串中刪除EBook://8個字元,然後在用SetLength去掉IE新增的斜槓,同時 要注意IE傳過來的字串引數是進行Http編碼的,所以還要呼叫HttpApp單元中的HttpDecode來進行解碼還原為c:\ebook.htm 的檔名字串。
如果輸入的檔案存在的話,則提示使用者輸入密碼,如果密碼匹配的話,則呼叫Decrypt函式對檔案進行解密並,返回解密後的文字串。如果檔案不存 在,或者密碼不匹配,則生成ErrorHtml返回一個錯誤描述的HTML頁面。關於加密和解密過程,比較簡單,我會在後面介紹。
獲得解密後的文字後,將文字內容複製到ResultHTML字串緩衝區中(這裡的緩衝區處於簡單的考慮,寫死成64K)。另外要注意的是這裡用的 引數都使用ThreadVar來宣告,這是因為協議處理過程是一個多執行緒非同步的過程,同一時刻,可能有多個EBook的協議請求在處理中,所以變數都要聲 明為執行緒安全的,以避免資源衝突。接下來儲存IE通過Start方法傳過來的OIProtSink協議處理事件介面(稍後還會用到),然後呼叫介面的 ReportData方法通知IE要獲取的資料量為BytesLeft,並通過設定ReportData的grfBSCF引數為 LastDataNotification 和DataFullyAvailable通知IE,資料已經完全準備好了,這樣稍後IE就會呼叫擴充套件的Read方法來獲得解密後的頁面資料。
返回解密資料
function TIEEncryptAPP.Read(pv: Pointer; cb: ULONG;
out cbRead: ULONG): HResult;
var
I: Integer;
begin
if (BytesLeft > 0) then
begin
I := CB;
if (I > BytesLeft) then
I := BytesLeft;
Move(ResultHTML[CurrPos], PV^, I);
CBRead := I;
Dec(BytesLeft, I);
Inc(CurrPos, I);
Result := S_OK;
{通知IE讀取更多的資料 }
end
else
begin
//資料全部下載完成
Result := S_False;
ProtSink.ReportResult(S_OK, 0, nil);
end;
end;
在Read 方法中,IE會傳過來一個內部緩衝區的指標pv,同時cb參數列示緩衝區的大小,電子書的資料有可能會很大,而IE的緩衝區不會無限大,因此IE會分多次 來讀取電子書的資料,我們每次應該儘可能讀取cb大小的資料,將其移動到IE的緩衝區內,讀取完成後減少BytesLeft的值,同時增加CurrPos 的值來記錄當前以傳送給IE的資料位置,並返回cbRead告訴IE傳送的資料到底有多少。如果一次沒有返回全部的資料,則返回S_OK通知IE還有沒傳 送完的資料,這樣IE就會繼續呼叫Read方法來完成資料下載,最後當所有的資料都處理完畢後,則返回S_False通知IE已經沒有要傳的資料了,同 時,呼叫事件介面ProtSink的ReportData方法通知IE,協議處理完畢。
加密解密
還是為了簡單起見,html頁面的加密非常簡單,我使用XOR加密,這樣的好處是,處理簡單。因為XOR加密和解密是一個可逆過程,加密和解密使用同一個函式就可以完成了。下面是加密和解密字串類:
type
//加密字串類
TEncryptStrings = class(TStringList)
public
procedure SaveToStream(Stream: TStream); override;
end;
//解密字串類
TDecryptStrings = class(TStringList)
public
procedure LoadFromStream(Stream: TStream); override;
end;
implementation
//用xor演算法進行加密
procedure EncodeStream(Input, Output: TStream);
var
InBuf: array[0..1023] of byte;
BufPtr: PChar;
I, BytesRead: Integer;
begin
Assert(Assigned(Input), '無效的流指標');
//必須重新設定流指標位置
Input.Position := 0;
Output.Position := 0;
repeat
BytesRead := Input.Read(InBuf, SizeOf(InBuf));
I := 0;
while I < BytesRead do
begin
InBuf[I] := InBuf[I] xor 8;
Inc(I);
end;
OutPut.Write(InBuf, BytesRead);
until BytesRead = 0;
Input.Position := 0;
Output.Position := 0;
end;
{ TDecryptStrings }
procedure TDecryptStrings.LoadFromStream(Stream: TStream);
var
OutStream:TMemoryStream;
begin
//解密
OutStream:=TMemoryStream.Create;
try
EncodeStream(Stream, OutStream);
inherited LoadFromStream(OutStream);
finally
OutStream.Free;
end;
end;
{ TEncryptStrings }
procedure TEncryptStrings.SaveToStream(Stream: TStream);
var
OutStream: TMemoryStream;
begin
inherited;
//加密
OutStream := TMemoryStream.Create;
try
EncodeStream(Stream, OutStream);
Stream.CopyFrom(OutStream, 0);
finally
OutStream.Free;
end;
end;
為了減少編碼工作量,我直接從TStringList類派生了兩個字串列表處理類,並過載了LoadFromStream和 SaveToStream方法來對流進行加解密處理。加解密處理都是呼叫的EncodeStream方法來對字串流進行加密,加密使用每個字元同8進行 xor運算。
下面我寫了一個程式,可以對html檔案進行處理點選Button1,則將檔案進行加密處理,點選Button2可以對察看解密後檔案的原有內容:
procedure TForm1.Button1Click(Sender: TObject);
var
Strings:TEncryptStrings;
begin
if not OpenDialog1.Execute then Exit;
Strings:=TEncryptStrings.Create;
try
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
Strings.Text:=Memo1.Text;
Strings.SaveToFile(OpenDialog1.FileName);
Memo2.Lines.LoadFromFile(OpenDialog1.FileName);
finally
Strings.Free;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
Strings:TDecryptStrings;
begin
if not OpenDialog1.Execute then Exit;
Strings:=TDecryptStrings.Create;
try
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
Strings.LoadFromFile(OpenDialog1.FileName);
Memo2.Lines.Text:=Strings.Text;
finally
Strings.Free;
end;
end;
介面如下:
註冊擴充套件
完成了擴充套件協議後,只剩下註冊擴充套件了,要想註冊擴充套件,需要在登錄檔的HKEY_CLASSES_ROOT\PROTOCOLS\Handler\下新增EBook關鍵字,然後在該關鍵字下新增名為CLSID的欄位,設定其值為擴充套件的Guid,下面是用於註冊的類工廠:
type
TIEEncryptAPPFactory = class(TComObjectFactory)
public
procedure UpdateRegistry(Register: Boolean); override;
end;
{ TIEEncryptAPPFactory }
procedure TIEEncryptAPPFactory.UpdateRegistry(Register: Boolean);
begin
inherited;
if Register then
CreateRegKeyValue(HKEY_CLASSES_ROOT, 'PROTOCOLS\Handler\EBook', 'CLSID',
GuidToString(ClassID))
else
DeleteRegKeyValue(HKEY_CLASSES_ROOT, 'PROTOCOLS\Handler\EBook', 'CLSID');
end;
initialization
TIEEncryptAPPFactory.Create(ComServer, TIEEncryptAPP, Class_IEEncryptAPP,
'IEEncryptAPP', '', ciMultiInstance, tmApartment);
end.
最後,將本書光碟中的ebook.htm檔案放到c:根目錄下,註冊擴充套件後,啟動IE,輸入ebook://c:\ebook.htm,然後在彈出的密碼框中輸入hubdog,IE就會顯示解密後的電子小說,介面示意如下:
臨時註冊擴充套件
上面的註冊方法可以稱為持久註冊的方法,一旦註冊就總是生效,。IE還提供臨時註冊的方法,只要編寫一個BHO擴充套件,在BHO載入時,呼叫TemporyRegister方法進行註冊,在IE退出時呼叫:
var
Factory:IClassFactory;
procedure TemporaryRegister;
begin
CoGetClassObject(Class_IEEncryptAPP, CLSCTX_SERVER, nil, IClassFactory, Factory);
CoInternetGetSession(0, InternetSession, 0);
InternetSession.RegisterNameSpace(Factory, Class_IEEncryptAPP, 'EBook', 0, nil, 0);
end;
procedure UnRegister;
begin
InternetSession.UnregisterNameSpace(Factory, 'EBook');
end;
這樣的好處是,在程式執行時,可以隨時解除對擴充套件協議的支援,而前面的永久註冊法必須在解除註冊後,重新啟動IE才行。缺點是必須通過一個BHO來實現臨時註冊。
其它的APP
除了上面的協議擴充套件外,IE還支援NameSpace Handler以及Mime-Handler兩種APP擴充套件。其中NameSpace擴充套件是對特定名字空間進行處理的協議擴充套件,比如如果我們註冊一個對名字空間<hubdog>,則當IE處理http://hubdog.csdn.net、mailto:hubdog@263.net的URL 時,一旦遇到hubdog名字空間,就會呼叫我們的NameSpace Handler進行處理,而不管URL是基於http協議的還是ftp等其它協議的都進行處理。從實現的角度來看,NameSpace的實現方法和前面的 協議擴充套件幾乎一樣,除了註冊時要填寫的登錄檔項內容不同而已。
而Mime協議擴充套件處理的主要是對一些特殊的媒體資源如圖片,聲音檔案進行處理,比如下表是IE預設支援的一些媒體形式。
text/richtext
text/html
audio/x-aiff
audio/basic
audio/wav
image/gif
image/jpeg
…
如果那天哪天你發明一種新的音樂形式,比如副檔名為.sy,就可以註冊一個Mime擴充套件對 .sy檔案處理,讓IE播放相應的聲音。
Mime擴充套件除了需要支援IInternetProtocol介面外,還必須實現IInternetProtocolSink介面,介面定義如下:
IInternetProtocolSink = interface
['{79eac9e5-baf9-11ce-8c82-00aa004ba90b}']
function Switch(const ProtocolData: TProtocolData): HResult; stdcall;
function ReportProgress(ulStatusCode: ULONG; szStatusText: LPCWSTR): HResult; stdcall;
function ReportData(grfBSCF: DWORD; ulProgress, ulProgressMax: ULONG): HResult; stdcall;
function ReportResult(hrResult: HResult; dwError: DWORD; szResult: LPCWSTR): HResult; stdcall;
end;
資料通訊方式上來看,Mime擴充套件同一般的協議擴充套件差別比較大,通訊的流程是這樣的:
1. 首先,IE會在遇到相應資源下載請求時,呼叫擴充套件的Start方法來啟動下載過程。
2. 然後IE會呼叫擴充套件的ReportProgress方法,告知擴充套件被下載的資料儲存的快取檔名稱。
3. 當IE下載完原始資料後,會呼叫擴充套件的ReportData方法通知擴充套件準備對原始資料進行加工處理。
4. 這時,擴充套件需要呼叫IE提供的IInternetProtocol介面的Read方法來獲得原始資料。
5. 對原始資料處理後,擴充套件要呼叫IE的IInternetProtocolSink介面的ReportData方法通知IE資料處理完畢。
6. 最後,IE呼叫擴充套件的Read方法獲得處理後的資料。
可以看出來同一般協議擴充套件的純主動向IE返回資料的方式不同,Mime的資料通訊方式即有被動的接收IE獲取的原始資料,也有將處理後的資料返回IE的主動通訊方式。
由於本質上來看,Mime同一般的APP的實現相差不多,所以這裡我將不再浪費篇幅來給出Mime擴充套件的實現例項了。
總結
IE早已經不再是一個單純意義的Web瀏覽程式了,通過對IE支援的協議擴充,我們可以將IE變成一個網路開發平臺,可以將IE的功能無限延伸。