1. 程式人生 > >IE瀏覽器 自定義地址協議的實現

IE瀏覽器 自定義地址協議的實現


關鍵字:IE外掛,shell介面程式設計,自定義IE協議,VC2003 ATL 實現COM

瀏覽QQ空間的時候發現,只要在IE地址中輸入象一下這種形式的地址,
tencent://Message/?Uin=251464630&websiteName=qzone.qq.com&Menu=yes
就會彈出給 251464630 傳送資訊的對話方塊,也就是說QQ對IE位址列的東西做了監控。而且可以發現輸入地址確定之後他就啟動了timwp.exe這

個程式。 在PPlive 也有實現類似的功能,只要你電腦上安裝了PPlive 這個程式,在IE位址列中輸入
synacast://09jN1+TK3K3nodzJoaLOmqeS1KGhoKOZoqGcltid1qeZy9ec1dbRy9ue1aKe5pzI2dSpna+VpJbayuPKrbOvvcySpRMUHl01NaScmcEIGRMUNh4vQz

NmNR8IGaqemauXq7OvvcySpZiekrCWoKOfj+LU162emaiToaGgl6eToaalo66VoKCmoaaVoJbX2LPa1ODgo6WU057TmtqT3tXgo66VoKCn3trV5KqbmNuT16HQl+T

K5KqkmaaVq+XQ2eqfn5/Nl92W1J7azuqfqKCcmbHZ0+Dgo6WU1J7TmtqT3tXgo66Vq+TP2eqfn5/Ol92W1J7azuqfqKCn3dnV5KqbmNyT16HQl+TK5KqkmZzZ2NXZ

zrPN5ePg3N7G4tWSwtvR3N/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編

   下面這個是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 類——》新增->實現介面,利用嚮導找到 shell介面中的IURLSearchHook 後新增實現。
    

    嚮導裡面列了很多,不過我是沒找到啦,所以之後手工新增介面實現了,以下全部程式碼,紅色的是自己寫的。

// 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.netmailto:[email protected]的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的功能無限延伸。

相關推薦

IE瀏覽器 定義地址協議實現

關鍵字:IE外掛,shell介面程式設計,自定義IE協議,VC2003 ATL 實現COM 瀏覽QQ空間的時候發現,只要在IE地址中輸入象一下這種形式的地址,tencent://Message/?Uin=251464630&w

用socket定義簡單協議實現檔案上傳與接受

一個上傳的資料包,主要包含檔案頭和檔案內容倆部分,主要按下面的格式,傳送: "File-Name:xxxxxx.zip;File-Type:exe;File-Length:1029292\r\n" ------檔案內容--------- 1、服務端的檔案接受服務 MySoc

Android ListView三級聯動,實現定義地址選擇器

說到地址選擇器,好多小夥伴第一印象就是——wheelView~這玩意確實挺好用的^(* ̄(oo) ̄)^! 然而悲劇的故事發生了,傲嬌的老闆不喜歡wheelView那種選中條不動的效果 ~(⊙o⊙)! 好吧,其實是老闆不知道從哪個忘記名字的App看到這種效果,

安卓定義控制元件-實現IOS版UC瀏覽器三點載入動畫效果

1.實現分析 廢話不多說,看下IOS版UC瀏覽器的載入效果 簡單畫個圖看下整個過程 1.B圓的圓心移動的座標為:A圓和B圓的圓心的距離L的中點為圓心O1的下半圓的運動軌跡經過的座標,就有一個由B位置到A位置圓周運動的軌跡。 2.C圓的圓心

webapi 定義緩存實現

task sys memcached cheat span tps turn 客戶端 穿透 定義一個Filter public class MyOutputCacheAttribute : ActionFilterAttribute {

異常-定義異常的實現和測試

trace message alt exc 異常 runt stack com code 1 考試成績必須在0-100之間 2 很明顯java沒有對應的異常,需要我們自己來做一個異常 3 自定義異常 4 繼承自Exception 5 繼承自RuntimeExce

android 定義dialog的實現方法

listener params .get animator miss nim style wrap 參數 最近一直在做 java 相關的東西, 雖然一直在看 Android 但感覺有點留於理論,總這樣畢竟不行,寫的多不一定懂得多,但要想懂得多就一定要寫的多,於是今天動手寫了

在 Windows Server Container 中運行 Azure Storage Emulator(一):能否監聽定義地址

list use contos 域名 嘗試 accounts res conf 是我   我要做什麽? 改 ASE 的監聽地址。對於有強迫癥的我來說,ASE 默認監聽的是 127.0.0.1:10000-10002,這讓我無法接受,所以我要將它改成域名 + 80 端口的方

網站流量日誌數據定義采集實現

之前 數據 拼接 win 其它 .com rac 特性 網頁 為什麽要進行網站流量數據統計分析? 隨著大數據時代的到來,各行各業產生的數據呈爆發式增長,大數據的技術從之前的“虛無”變成可能,數據產生的各種潛在價值慢慢的被人們挖掘出來利用在各行各業上。比如網站流量數據統計分析

定義控件實現-今日頭條圖集效果

layout code 地址 ack cti https app 第一個 ret 前提 產品有個新需求,類似今日頭條的圖集效果 大致看了下UI,大致就是ViewPager,橫向滑動切換圖片,縱向滑動移動圖片,縱向超過一定距離,圖片飛出,圖集淡出動畫退出,支持圖片的雙擊放

窗體背景的繪制(Windows窗體每次都會重繪其窗體背景,所以我們可以通過攔截窗體重繪背景的消息(WM_ERASEBKGND),並定義方法來實現重繪窗體背景)

height com call 消息響應 int idt http msg mes 核心思想:由於Windows窗體每次都會重繪其窗體背景,所以我們可以通過攔截窗體重繪背景的消息(WM_ERASEBKGND),並自定義方法來實現重繪窗體背景。通過TImage組件也可以實現,

Yaf定義autoload以實現Model文件和Controller文件命名區分

處理 image func new rep down 眼睛 自定義 錯誤 先上圖: 由於Yaf作者在設計Yaf框架目錄時沒有直接區分開models文件和controllers文件,所以在IDE看著會很難受,眼睛離開了編輯器就不大好區分這兩個文件夾的文件。所以自己寫了一個

mac下定義協議配置

xxx element https src internet -c pos tro six 之前查了很多資料,最近也在挖掘研究這方面的漏洞. windows的很簡單,在註冊表配置就好了,但是mac os 是unix的,沒有註冊表這麽一說。 但是發現騰訊等配置了自定義等協

Django定義UserModel並實現認證和登錄

eric one email post blog rac ner self object 自定義UserModel 環境:django 1.9.11+python 2.7 from django.contrib.auth.models import AbstractUse

MapReduce實戰:定義輸入格式實現成績管理

stat app 註意 false exce 考試成績 fileinput collect 劃分 1. 項目需求   我們取有一份學生五門課程的期末考試成績數據,現在我們希望統計每個學生的總成績和平均成績。 樣本數據如下所示,每行數據的數據格式為:學號、

純C++版500VIP源碼下載的Faster R-CNN(通過caffe定義RPN層實現

方便 預測 大致 ole test cto oop 可執行文件 names 這裏500VIP源碼下載 dsluntan.com 介紹的是通過添加自定義層(RPN層)代替python層,實現c++版的Faster R-CNN,因為去掉python了,所以部署時不會因為牽扯到p

傲遊瀏覽器---定義 UserAgent 字符串

2.3 0.10 tab andro body aid 聯想 uc瀏覽器 box 遨遊瀏覽器:http://www.maxthon.cn/ 自定義 UserAgent : http://www.fynas.com/ua 手機UserAgent大全

Docker使用定義網絡實現容器互聯

toc -- 進入 定義網絡 ping href busybox 定義 dock 目錄 容器互聯 步驟 新建網絡 連接容器 測試連接 添加已經運行的容器到自定義網絡 容器互聯 隨著 Docker 網絡的完善,強烈建議大家將容器加入自定義的 Docker 網絡來實現互聯

博客園定義地址欄logo

child icon圖標 href append bject setting reat lan blog 自定義博客園地址欄logo 一、首先自己需要下載一個logo圖片,png、jpg格式的都可以 。挑選自己喜歡的圖片就可以。 二、然後制作成icon圖標 在線制作i

Arrays.sort()定義排序的實現

port margin tor urn 排列 util int ava 升序 1. Arrays.sort(T[] a)是對數組元素按字典序進行升序排列 import java.util.*; public class Main { public static