电竞比分网-中国电竞赛事及体育赛事平台

分享

COM深入編程學(xué)習(xí)筆記2

 aaie_ 2012-10-16

COM深入編程學(xué)習(xí)筆記2 

0.接口授權(quán)

    假設(shè)讀者有個(gè)經(jīng)過慎重考慮后產(chǎn)生的類叫做TObject1,它實(shí)現(xiàn)Iinterface1。讀者想要?jiǎng)?chuàng)建一個(gè)名為TCombinedObject的
    
的類,它實(shí)現(xiàn)Iinterface1船1和Iinterface2。好像是需要重新實(shí)現(xiàn)Iinterface1的方法,可能要從TObject1復(fù)制源代碼到

TCombinedObject,對(duì)嗎?

    不必這樣。Delphi可讓讀者把一個(gè)接口的實(shí)現(xiàn)授權(quán)給另一個(gè)類。授權(quán)意味著:一個(gè)類包含針對(duì)另—個(gè)類的指針。
    
內(nèi)部類實(shí)現(xiàn)一個(gè)或多個(gè)接口的功能性。外部類簡(jiǎn)單地將這些方法傳遞給內(nèi)部類,而不是重新實(shí)現(xiàn)接口。
    
    下列代碼實(shí)現(xiàn)TObject1類中的Iinterface1接口。TCombinedObject包含Tobject1引用,并且授權(quán)Iinterface1到

Fobjl的實(shí)現(xiàn)。

  Iinterface1 = interface
    ['{2DE825C1-EADF-11D2-B39F-0040F67455FE}']
    function Dolt1:integer;
  end;

  Iinterface2 = interface
    ['{2DE825C1-EADF-11D2-B39F-0040F67455FE}']
    function Dolt2:integer;
  end;
 
  TObject1 = class(TinterfaceObject,Iinterface1)
    protected
       function Dolt1:integer;
  end;
 
  TCombinedObject = class(TinterfaceObject,Iinterface1,Iinterface1)
    private
      FObj1:Iinterface1;
    public
      function Dolt2:integer;
      property MyIntface:Iinterface1 read FObj1 implements Iinterface1;
  end;
 
  //在使用中可以如下:
 
  procedure TForm1.btn1OnClick(Sender:TObject);
  var
    I1:Iinterface1;
    I2:Iinterface2;
  begin
    I2 := TCombinedObject.Create();
    I2.Dolt2();
    
    I1 := I2 as Iinterface1;
    I1.Dolt1();
  end;

    注意:I2 as Iinterface1語句將Iinterface2接口從TCombinedObject對(duì)象中自動(dòng)分離出來即使Iinterface1實(shí)際上

是由TOjbect1實(shí)現(xiàn)的。這就是授權(quán)的美鈔之處:用戶的代碼不必在意接口實(shí)際上是如何實(shí)現(xiàn)的。

在windows注冊(cè)表(Registry),有一個(gè)鍵HKEY_CLASSES_ROOT\CLSID
打開該節(jié)點(diǎn),發(fā)現(xiàn)一行行的GUID。

    每個(gè)CLSID或GUID都代表一個(gè)COM接口的實(shí)現(xiàn)。例如,列在CLSID第一個(gè)的是
    {00000010-0000-0010-8000-00AA006D2AE4}
    該CLSID把接口提供給miscrosoft數(shù)據(jù)訪問對(duì)象(DAO),即DAO引擎。
    其GUID下面的InprocServer32鍵包含windows使用的信息用來在電腦中定位DA0.DLL。
   
1.COM對(duì)象和類廠(class factories)   
 
    一個(gè)COM對(duì)象位于DLL或exe。位于DLL中的COM對(duì)象被引用為進(jìn)程內(nèi)服務(wù)器。位于ExE中的COM對(duì)象被引用為進(jìn)程外服務(wù)器。

在本章后面將討論進(jìn)程內(nèi)和進(jìn)程外服務(wù)器。

    COM服務(wù)器可以包含一個(gè)或COM對(duì)象。COM對(duì)象在下面小節(jié)中討論。
 
  1)COM對(duì)象
  //Delphi封裝的可以實(shí)現(xiàn)COM對(duì)象的類,不能從TInterfacedObject派生,而是直接從IUnknown接口派生
  TComObject = class(TObject, IUnknown, ISupportErrorInfo) //所有COM對(duì)象的基類
  private
    FController: Pointer;
    FFactory: TComObjectFactory;
    FNonCountedObject: Boolean;
    FRefCount: Integer;
    FServerExceptionHandler: IServerExceptionHandler;
    function GetController: IUnknown;
  protected
    { IUnknown }
    function IUnknown.QueryInterface = ObjQueryInterface; //映射IUnknown.QueryInterface方法
    function IUnknown._AddRef = ObjAddRef;                //映射IUnknown._AddRef方法
    function IUnknown._Release = ObjRelease;              //映射IUnknown._Release方法
    { IUnknown methods for other interfaces }
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    { ISupportErrorInfo }
    function InterfaceSupportsErrorInfo(const iid: TIID): HResult; stdcall;
  public
    constructor Create;
    constructor CreateAggregated(const Controller: IUnknown);
    constructor CreateFromFactory(Factory: TComObjectFactory;
      const Controller: IUnknown);
    destructor Destroy; override;
    procedure Initialize; virtual;
    function ObjAddRef: Integer; virtual; stdcall;
    function ObjQueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
    function ObjRelease: Integer; virtual; stdcall;
{$IFDEF MSWINDOWS}
    function SafeCallException(ExceptObject: TObject;
      ExceptAddr: Pointer): HResult; override;
{$ENDIF}
    property Controller: IUnknown read GetController;
    property Factory: TComObjectFactory read FFactory;
    property RefCount: Integer read FRefCount;
    property ServerExceptionHandler: IServerExceptionHandler
      read FServerExceptionHandler write FServerExceptionHandler;
  end;
  {$EXTERNALSYM TComObject}   
 
  2) HResult 和 OleCheck
     在COM編程中,大多數(shù)函數(shù)(除了_AddRef和_Release)都返回HResult類型的一個(gè)值。HResult是一個(gè)特殊的返回值,
  
   意味著函數(shù)調(diào)用成功還是失敗,如果失敗的話它也包含一個(gè)錯(cuò)誤代碼。
 
    OleCheck(MyComObject.SomeFunction);
    OleCheck(MyComObject.SomeOtherFunction);
    當(dāng)調(diào)用返回HResult的COM函數(shù)時(shí)就應(yīng)使用OleCheck()來檢查返回值是否成功。
    {
        procedure OleCheck(Result: HResult);
        begin
           if not Succeeded(Result) then OleError(Result); //如果成功則返回S_OK =0,否則拋出異常
        end;
       
        procedure OleError(ErrorCode: HResult);
        begin
              raise EOleSysError.Create('', ErrorCode, 0);
        end;
       
        function Succeeded(Res: HResult): Boolean;
        begin
              Result := Res and $80000000 = 0;
        end;       
    }
   
   3)類工廠
   
    COM對(duì)象不是由應(yīng)用程序直接例示的。相反,COM使用類工廠來創(chuàng)建對(duì)象。類工廠是一個(gè)對(duì)象,該對(duì)象的明確目的就是
   
 創(chuàng)建其它對(duì)象。每一個(gè)COM對(duì)象都有一個(gè)相關(guān)的類工廠。該類工廠負(fù)責(zé)創(chuàng)建在服務(wù)器中實(shí)現(xiàn)的COM對(duì)象。
   
    類廠把COM從實(shí)際構(gòu)造一個(gè)對(duì)象的過程中分離出來。如果不是有了類廠.COM就必須直接調(diào)用對(duì)象的構(gòu)造函數(shù)以便創(chuàng)建對(duì)象。
   
COM對(duì)于如何實(shí)現(xiàn)COM對(duì)象沒有任何限制,并其構(gòu)造是實(shí)現(xiàn)過程的完整部分,因此COM沒有對(duì)象構(gòu)造過程的直接信息是很重要的。

    盡管DLL可以提供COM可能調(diào)用的來創(chuàng)建一個(gè)對(duì)象實(shí)例的標(biāo)準(zhǔn)函數(shù),但EXE并不可以。例如,DLL可能輸出一個(gè)名為
   
ConstractMyComObject的函數(shù).然后當(dāng)需要?jiǎng)?chuàng)建MyComObject實(shí)例時(shí)告訴COM調(diào)用此函數(shù)。

    當(dāng)創(chuàng)建EXE時(shí)必須注冊(cè)它們的類廠,并其COM調(diào)用類廠接口以便創(chuàng)建COM對(duì)象。為保持一致,DLL按照和EXE相同的方式創(chuàng)建

并注冊(cè)類廠。  類廠支持IClassFactory接口,它的定義如下:

  IClassFactory = interface(IUnknown)
    ['{00000001-0000-0000-C000-000000000046}']
    function CreateInstance(const unkOuter: IUnknown; const iid: TIID;
      out obj): HResult; stdcall;
    function LockServer(fLock: BOOL): HResult; stdcall;
  end;

    正如讀者所見,IIClassFactory只定義兩個(gè)函數(shù):CreateInstance和LockServer。
   
    CreateInstance是負(fù)責(zé)創(chuàng)建類廠涉及的COM對(duì)象的實(shí)例的函數(shù)。一般來說讀者自己不調(diào)用此函數(shù)。將看到的是,
   
COM為讀者調(diào)用此函數(shù)。

    當(dāng)沒有在運(yùn)行的客戶使用服務(wù)器時(shí),刪服務(wù)器就會(huì)從內(nèi)存中卸載??梢哉{(diào)用LockServer來迫使服務(wù)器保存在內(nèi)存中。
   
調(diào)用LockServer(TRUE)來增加內(nèi)部鎖的計(jì)數(shù)。調(diào)用LockServer(False)來降低鎖的計(jì)數(shù)。當(dāng)鎖的計(jì)數(shù)為零時(shí),如果沒有

客戶調(diào)用服務(wù)器的話,就有可能從內(nèi)存中卸裁服務(wù)器。

    注意:必須平衡LockServer(TRUE)和LockServer(FALSE)的調(diào)用才能使系統(tǒng)正常進(jìn)行。向LockServer(TRUE)發(fā)出
   
調(diào)用而沒有調(diào)用相應(yīng)的LockServer(FALSE)就會(huì)使COM服務(wù)器永遠(yuǎn)駐留在內(nèi)存中。

 

2.進(jìn)程內(nèi)的服務(wù)器(In-Process COM Server)   
 
   我們要看的第一個(gè)COM服務(wù)器是個(gè)進(jìn)程內(nèi)服務(wù)器。
  
   進(jìn)程內(nèi)服務(wù)器是由于它們?cè)贒LL內(nèi)實(shí)現(xiàn)而獲得這個(gè)名稱的。因此,服務(wù)器占據(jù)了和使用它的應(yīng)用程序一樣的地址空間(進(jìn)程)。
  
所有的進(jìn)程內(nèi)COM服務(wù)器輸出四個(gè)標(biāo)準(zhǔn)函數(shù): 
  DllGetClassObject,
  DllCanUnloadNow,
  DllRegisterServer,
  DllUnregisterServer;
  Borland已在Delphi中提供了這些函數(shù)的缺省實(shí)現(xiàn)。因此,讀者不必自己編碼這些函數(shù),但是應(yīng)該理解它們的用途。
 
  2.1 線程支持(Treading Support)
 
    線程支持只適合于進(jìn)程內(nèi)服務(wù)器,并且不適用于進(jìn)程外服務(wù)器。進(jìn)程內(nèi)服務(wù)器可以附著在幾個(gè)線程模型中的一個(gè)。
   
    進(jìn)程內(nèi)服務(wù)器的線程模型被存在windows注冊(cè)表中。接口服務(wù)器可支持的線程模型是:
   
    1)單一的。單線程COM對(duì)象實(shí)際上根本沒有線程支持。所有對(duì)COM服務(wù)器的訪問都是由windows來順序執(zhí)行的,
               因此不必?fù)?dān)心多個(gè)線程會(huì)同時(shí)訪問服務(wù)器。所有COM服務(wù)器的訪問都存在于線程,在此創(chuàng)建了COM服務(wù)器DLL。
              
    2)公寓線程(Apartment)。公寓-線程(或有時(shí)叫做單線程公寓)的COM對(duì)象可以只處理來自創(chuàng)建它們的線程的請(qǐng)求。
                            一個(gè)服務(wù)器可以輸出一些COM對(duì)象,并且每個(gè)COM對(duì)象可以從一個(gè)不同的線程中創(chuàng)建。
                            所以,通過使用互斥體、事件、臨界部分或其它同步方法訪問在服務(wù)器中定義的任何
                            全局?jǐn)?shù)據(jù)必須是同步的。
    3)自由的。自由線程服務(wù)器移走公寓-線程服務(wù)器施加的限制。在自由線程模型中,多個(gè)線程在任何給定COM對(duì)象上可以
               同時(shí)運(yùn)行。因此,不僅必須同步訪問全局?jǐn)?shù)據(jù),而且對(duì)于多個(gè)線程訪問的全局?jǐn)?shù)據(jù)訪問必須也是同步的。
              
    4)同時(shí)公寓線程和自由線程。支持此選項(xiàng)的COM服務(wù)器附著在公寓線程模型和自由線程模型二者之上。這是最難支持的
                               線性模型,因?yàn)橹С止⒕€程和自由線程二者的COM服務(wù)器必須使他們自己對(duì)于公寓線程
                               對(duì)象實(shí)例和整個(gè)線程的排列參數(shù)數(shù)據(jù)的訪問是同步的
                              
   2.2 注冊(cè)服務(wù)器
     
      所有的COM服務(wù)器需要用windows注冊(cè)表來正常工作。注冊(cè)過程包括創(chuàng)建進(jìn)入windows注冊(cè)表所需的條目以便windows知道
     
      服務(wù)器(進(jìn)程內(nèi)的或進(jìn)程外的)的位置和類。
     
      regSvr32  <ServerName>注冊(cè)
      regSvr32 -u <ServerName>注銷

    2.3 定制構(gòu)造函數(shù)
     
      不要試圖重載一個(gè)COM對(duì)象的構(gòu)造函數(shù)。TComObject中定義的構(gòu)造函數(shù)都調(diào)用了虛方法函數(shù)Initialize。
     
   如果需要為自己的COM對(duì)象提供初始化代碼,只需重載Initialize方法。

    2.4 創(chuàng)建一個(gè)進(jìn)程內(nèi)COM對(duì)象的實(shí)例
   
     當(dāng)用戶需要在自己的客戶程序代碼中創(chuàng)建一個(gè)進(jìn)程內(nèi)COM對(duì)象時(shí),一般會(huì)使用CreateComObject函數(shù):
     function CreateComObject(const ClassID: TGUID): IUnknown;
     //封裝了CoCreateInstance
     function CreateComObject(const ClassID: TGUID): IUnknown;
     begin
         OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
        CLSCTX_LOCAL_SERVER, IUnknown, Result));
     end;
   
     //如下也是可以的使用CreateRemoteComObject,多加一個(gè)遠(yuǎn)程機(jī)器的名稱,這樣服務(wù)器端和客戶端可以在兩個(gè)機(jī)器上
     //  ifn := CreateRemoteComObject('DC2D51F3248443E',CLASS_FORMAT) as IFormattedNumber;

    一個(gè)需要注意的是:coCreateInstance內(nèi)部創(chuàng)建負(fù)責(zé)創(chuàng)建COM對(duì)象類廠的實(shí)例,然后使用類廠再創(chuàng)建對(duì)象。創(chuàng)建完COM對(duì)
像后,類廠就被銷毀。
    顯然,如果要?jiǎng)?chuàng)建相同COM對(duì)象的眾多實(shí)例,這不是非常有效的。在這種惜況下,就要自己創(chuàng)建一個(gè)類廠的實(shí)例并在刪除
它之前使用它的coCreateInstance方法來創(chuàng)建COM對(duì)象.
 
  //下面代碼是創(chuàng)建一個(gè)COM對(duì)象的類廠 ,他在加載DLL的時(shí)候就已經(jīng)運(yùn)行了,在卸載DLL時(shí)會(huì)自動(dòng)銷毀
  TComObjectFactory.Create(ComServer, TNextFit, Class_NextFit,
    'NextFit', 'Next-fit algorithm', ciMultiInstance, tmApartment);
   
     正因?yàn)橛辛薈OM對(duì)象的類廠,在客戶端程序中使用CreateComObject通常返回一個(gè)IUnknown指針。要獲取需要的接口指針,

應(yīng)使用as操作符,如下所示:
     FOneD := CreateComObject(Class_NextFit) as IOneDBin; //獲取COM對(duì)象,并授權(quán)給接口引用

3.進(jìn)程外COM對(duì)象(Out-of-Process COM Server )
 
 
    進(jìn)程外服務(wù)器是由于它們?cè)贓XE內(nèi)實(shí)現(xiàn)的。進(jìn)程外COM服務(wù)器不輸出進(jìn)程內(nèi)COM服務(wù)器所需的四個(gè)函數(shù),所以,他們?cè)?br>
注冊(cè)表中使用不同的注冊(cè)方法。要注冊(cè)一個(gè)進(jìn)程外的COM服務(wù)器,只需運(yùn)行該服務(wù)器,把/RegServer放在命令行中。

Delphi將注冊(cè)服務(wù)器和COM對(duì)象,然后就退出。要撤銷注冊(cè)服務(wù)器,使用命令行/unregserver
  
    如果正常運(yùn)行服務(wù)器,Delphi也將注冊(cè)它而沒有任何命令行選項(xiàng)。然而,服務(wù)器應(yīng)用程序?qū)?huì)繼續(xù)運(yùn)行。

   3.1 實(shí)例化(Instancing)
  
   進(jìn)程外COM服務(wù)器可以支持三個(gè)實(shí)例化方法中的一個(gè)。實(shí)例化指創(chuàng)建多少個(gè)客戶需要的實(shí)例。

   1)單實(shí)例(single Instance):指每個(gè)應(yīng)用程序只允許一個(gè)COM對(duì)象的實(shí)例。每個(gè)需要COM對(duì)象實(shí)例的應(yīng)用程序?qū)a(chǎn)
                             生COM服務(wù)器的單獨(dú)拷貝。
  
   2)多實(shí)例(Multiple Instance)是指CDMServer可以創(chuàng)建一個(gè)COM對(duì)象的多個(gè)拷貝。
       當(dāng)客戶程序請(qǐng)求COM對(duì)象的一個(gè)實(shí)例時(shí),并不是啟動(dòng)一個(gè)新的服務(wù)器(除非服務(wù)器還未運(yùn)行)。相反,由當(dāng)前運(yùn)行的服
       務(wù)器創(chuàng)建COM對(duì)象的一個(gè)實(shí)例。
   3)內(nèi)部實(shí)例(Internal Only):用于不被客戶應(yīng)用程序使用的COM對(duì)象。能創(chuàng)建這種COM對(duì)象的應(yīng)用程序只有包含此COM對(duì)象
                              的COM服務(wù)器。
                             
    一般來說,讀者希望創(chuàng)建支持多實(shí)例的COM服務(wù)器。例如,假設(shè)讀考已經(jīng)編寫了一個(gè)COM服務(wù)器,它控制對(duì)串口的訪問,
 
 需要同時(shí)運(yùn)行兩個(gè)客戶應(yīng)用程序來向串口發(fā)送數(shù)據(jù)(通過COM服務(wù)器)。一個(gè)支持多實(shí)例的COM服務(wù)器可以打開串門并同時(shí)為
 
 兩個(gè)客戶程序服務(wù)。
 
   3.2 創(chuàng)建一個(gè)進(jìn)程外COM對(duì)象的實(shí)例
    
     創(chuàng)建一個(gè)進(jìn)程外服務(wù)器的COM對(duì)象實(shí)例的方法與創(chuàng)建進(jìn)程內(nèi)服務(wù)器中COM對(duì)象實(shí)例的方法是相同的。仍然可以調(diào)用
 
 CreateCOMObject函數(shù),將需要?jiǎng)?chuàng)建的COM對(duì)象的GUDI作為一個(gè)參數(shù)傳遞給它。
 
  3.3 調(diào)度數(shù)據(jù)
    
     當(dāng)一個(gè)程序使用一個(gè)進(jìn)程外COM服務(wù)器時(shí),在內(nèi)存的一個(gè)地址上裝載該程序,并且在一個(gè)不向的地址裝載COM服務(wù)器。
    
 在特定的內(nèi)存地址裝載在調(diào)用應(yīng)用程序中聲明的變量,該地址表示虛內(nèi)存中的地址。例如,假設(shè)客戶應(yīng)用程序聲明一個(gè)整
 
 型變量Myht,它的內(nèi)存地址為$00442830。進(jìn)程外COM服務(wù)器不訪問內(nèi)存中的那個(gè)位置。
 
     因?yàn)橐粋€(gè)可執(zhí)行的程序不直接訪問另一個(gè)可執(zhí)行的程序的地址空間,Windows通過個(gè)叫做調(diào)度(marshaling)的進(jìn)程在
    
調(diào)用應(yīng)用程序和進(jìn)程外COM服務(wù)器之間移動(dòng)數(shù)據(jù)。
  
     Windows可以自動(dòng)調(diào)度下列數(shù)據(jù)類型:   
     Smallint,Integer、Single、Double、Double、Currency、TDatatime、wideString、IDispatch、
     SCODE、WordBool、OleVariant、IUnknown、Shortint和Byte是自動(dòng)化兼容的,意思是它們可以安全地用于自動(dòng)化服
     務(wù)器(COM Automation)。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多