Inside COM讀書筆記-----ExE中的服務器

1.      不同的進程

每一個EXE文件都將在不同的進程中運行,而每一個進程都有其自己的進程空間。一個進程空間中的邏輯地址0X0000ABBA所對應的物理地址將不同於另外一個進程中同一邏輯地址所對應的物理地址。所以若一個進程將地址0X0000ABBA傳給另一個進程,後者訪問到的邏輯單元將不是前一個進程所希望的。

同每一個EXE都有自己的進程不同,DLL將映射到鏈接他們的EXE文件的進程空間中,因此DLL也被稱作進程中服務器,而EXE則被稱作是進程外服務器。在某些情況下,EXE也被稱作本地服務器。

對於跨進程邊界的藉口,需要考慮的:

  • 一個進程需要能夠調用另外一個進程中的函數
  • 一個進程需要能夠將數據傳遞給另外一個進程
  • 客戶無需關心他所訪問的服務器是進程內服務器還是進程外服務器

 

本地過程調用


對於進程間的通信,有幾種不同的方法,如動態數據交換(DDE)、命名管道以及共享內存等。COM所用的方法是本地過程調用(LPC)。LPC是同一機器上不同進程間通信的一種方法。LPC是如何工作呢?他們是由操作系統實現的,由於操作系統知道同每一個進程邏輯地址空間向對應的物理地址,因此操作系統可以調用任何進程的任何函數。

 

調整


調用EXE中的函數只是第一步,另外我們還需要一種方法將函數調用的參數從一個進程空間傳到另一個進程的地址空間中,這種方法稱爲“調整”。LPC技術可以將數據從一個進程複製到另一個進程中去,需要其他一些信息以將參數打包並傳遞給其他進程。比如對指針的處理將將不同於對整數的處理,將指針引用的結構賦值到另一個進程中去。

 

代理/存根DLL


當調用Win32函數時,系統實現上講調用一個DLL的函數,而此函數將通過LPC調用windows中的實際代碼,這種結構可以將用戶進程同Windows代碼隔離開。

COM使用的結構與此類似,客戶將同一個模仿組件的DLL進行通信,這個DLL可以爲客戶完成參數的調整及LPC調用。一個代理就是同另外一個組件行爲相同的組件,代理必須是DLL形式,因爲他們需要訪問客戶進程的地址空間以便對傳給接口函數的數據進行調整,組件還需要一個被稱爲存根的DLL,以從客戶傳來的數據進行反調整。

 

2.      IDL/MIDL簡介


藉助IDL(接口定義語言)可以編寫接口的一個描述,然後用MIDL編譯器生成代理和存根DLL。

 

IDL接口描述舉例

//interface ISum

[

    object,

    uuid(B3B82DE4-4EA0-475F-A386-22C5DF7DC213),

    helpstring("IPrimeinterface"),

    oleautomation

]

interface ISum : IUnknown

{

    HRESULTSum(int x, inty, [out, retval]int* retval);

}

 

IDL的語法同C++並無太大的差異,其中object表示所定義的接口爲一個com接口,關鍵字object是Microsoft對於IDL的一個擴展,第二個關鍵字uuid爲相應接口的IID。第三個關鍵字用於將一個幫助串放到一個類型庫中,

  •  Pointer_default關鍵字

使用IDL的目的是爲了提供足夠的信息,以便函數參數可以調整。Pointer_default關鍵字具有三個不同的選項

                        i.             Ref---將指針當成是引用對待,此時表示此指針將總是指向一個合法的地址,並可被反引用。不能爲空,在調整前後他們將指向同一內存地址。

                      ii.             Unique---此類指針可以爲空,並且在函數內可以修改他們的值。但不能爲之指定別名。

                     iii.             Ptr---此選項指定相應的指針就是一個c指針,此類指針可以是一個別名、可以爲NULL並且其值可以被修改。

  •  IDL中輸入與輸出參數

MIDL可以使用in、out參數屬性對代理和存根代碼進一步優化。對於被標記爲in的參數,MIDL將指導僅僅需要將此參數指從客戶傳遞給組件,存根代碼不需要送回任何值。Out關鍵字告訴MIDL相應的參數僅被用來從組件向客戶傳回有關的數據。代理不需要對輸出參數指進行調整也不需要將此值傳送給組件。

  • IDL中的字符串

Com中對於字符串的標準約定是使用Unicode字符及wchar_t。

  • IDL中的import關鍵字

Import用於將其他IDL文件中的定義包含到當前文件中,同C++中的預處理指令#include命令式類似的,

 

MIDL編輯器


           可以用如下命令編譯idl文件

           Midl foo.idl

           Idl文件有一個library語句,將生成一個類型庫。

 

代理/存根的登記

3.      本地服務器的實現


EXE無法輸出函數,所有在進程中服務器依賴的一些輸出函數:

DllGetClassObjectPRIVATE

    DllRegisterServerPRIVATE

    DllUnregisterServerPRIVATE

    DllCanUnloadNow  PRIVATE

需要找到替代物,替換DllCanUnloadNow是很簡單的,exe不是被動的,可以對自己控制生命期。DllRegisterServer和DllUnregisterServer可以直接通過exe文件提供相應的命令行參數,來完成自登記。

 

類廠的啓動


COM通過維護一個關於被登記的類廠的內部表格,當客戶用合適的參數調用CoGetClassObject時,COM將首先檢查此關於類廠的私有表格,已得到與客戶請求的CLSID相應的類廠,若存在將進行查找並啓動EXE,EXE可以調用COM函數CoRegisterClassObject來完成類廠的登記。只需要建立相應的類廠並將其接口指針傳給CoRegisterClassObject即可。

  • CoRegisterClassObject

第一個參數是被登記類的CLSID,第二個和第三個參數是一起使用的,第四個參數表示EXE的單個實例能否支持一個組件的多個實例,他能提供單個組件REGCLS_SINGLEUSER和CLSCTX_LOCAL_SERVER,可以支持多個組件的實例,REGCLS_MULTI_SEPARATE。

 

CoRegisterClassObject(__in REFCLSID rclsid,

    __inLPUNKNOWN pUnk,

    __inDWORD dwClsContext,

    __inDWORD flags,

    __outLPDWORD lpdwRegister

);

  • 類廠的釋放

當服務器被關閉時,他必須衝內部表格中刪除相應的類廠,使用COM庫德CoRevokeClassObject。

 

對LockServer的修改


           進程中服務器將輸出函數DllCanUnloadNow,用此函數以決定能否從內存中講服務器卸載。對於本地服務器,需要對LockServer進行修改,其原因在於:DLL不能控制他們的生命期,他們的裝載和卸載都是由另外的EXE文件完成。但EXE本身可以控制,當退出的時候發送退出消息即可。C和C++程序的標準入口點爲main。程序的執行將從main函數開始,當main退出時,程序也就終止了。Windows程序通過入口函數WinMain,通過引入windows消息循環來不致EXE退出,

 

 

發佈了154 篇原創文章 · 獲贊 870 · 訪問量 85萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章