分享

COM深入编程学习笔记1

 aaie_ 2012-10-16

COM深入编程学习笔记1 

    接口有如下特征:
    .接口被声明为Interface类型,不是class类型。惯例是:接口名从字母I开始,正如类类型名从字母T开始。
   
    .所有的接口都从IUnknown直接或间接继承。就像TObject是Delphj中所有类的基类一样,IUnknown是coM中所有接口的基础。

    .接口不能自我创建和实现,必须有其派生的类来创建实例和实现功能。下列是错误的:
   
    var
      FormattedNumber:IFormattedNumber;
     
      FormattedNumber := IFormattedNumber.create(); //这将导致编译错误
      FormattedNumber := TFormattedNumber.create(); //TFormattedNumber类是IFormattedNumber接口的派生实现,这是正确的。
     
1.不能创建接口实例。下面的代码是非法的;

    1)不能在接口中指定范围指示。接口定义的所有方法都是公有型(public),不能在接口声明中包括范围指示(包括公有型)。
   
    2)接口不能声明变量。接口只能决定提供什么样功能。对于如何完成该功能没有限制。如果允许在接口声明中放一个成员
   
    变量,就会在某种程度上命令用何种方法来完成想要的功能。
   
    3)在接口中声明的所有函数和过程,概念上讲都是虚(virtual)抽象函数和过程。声明时不必带virtual关键字;
   
      事实上,这样做是非法的。

2.接口是标准协议,一旦定义经不能随便修改,如果要增强接口功能,可以派生新的接口:
  例如:要为IFormattedNumber接口增加一个SetCaption函数
 
   IFormattedNumber2 = inerface(IFormattedNumber);
      procedure SetCatpion(ACapiton:String);
   end;

3.声明一个接口:
  IFormattedNumber = interface
   [GUID]  //16字节,无重复标识符,在delphi中可Ctrl+Shift+G自动创建一个,在内部调用了coCreateGuid里产生GUID
   function formattedString:String; 
  end;
    { //动态获取GUID的方法
  procedure TForm1.btnGenerateClick(Sender: TObject);
   var
       Guid: TGUID;
  begin
       CoCreateGuid(Guid);
       Memo1.Lines.Add(GuidToString(Guid));
  end;  
 }
 
  1)TGUID数据结构定义:  
  type
  PGUID = ^TGUID;
  TGUID = packed record
    D1: Longword;
    D2: Word;
    D3: Word;
    D4: array[0..7] of Byte;
  end;
 
 
  2)下面声明完全一样:
    const IID_IMalloc: TGUID ='{DC1D7C5F-C0DC-4056-B1F2-1D3A1ADA6D51}';
    const IID_IMalloc: TGUID =(D1:$DC1D7C5F;D2:$C0DC;D3:$4056;D4:($B1,$F2,$1D,$3A,$1A,$DA,$6D,$51));
 
  3)接口的实现
    TFormattedNumber = class(TInterfacedObject,IFormattedNumber)
      private
        FormattedString:String;
      public
        function formattedString:String;    
    end;
   
  4)IUnkonwn接口
    IiInterface = interface
      ['00000000-0000-0000-C000-00000000000046']
      function QueryInterface(const IID:TGUID;out obj):HResult;stdcall;
      function _AddRef:integer;stdcall;
      function _Release:Integer;stdcall;
    end;
    IUnkonwn = IiInterface;
    由此可见IUnkonwn是所有COM接口的基础接口,QueryInterface,_AddRef,_Release必须由IUnkonwn的派生类来实现,
    在Delphi中由TInterfacedObject来实现了。
    (1)QueryInterface
          QueryInterface是请求指向一个接口指针的函数。如果接口是由问题中的对象实现的,QueryInterface返回在则
          参数中的接口,且返回数值为0。如果接口不是由对象实现的,QueryInterface返回MicroSoft定义的
          常量E_NOINTERFACE。
   
 (2)_AddRef
        接口是引用计数。因此在对象中当获得接口指针时,对象被引用的次数就增加了。当结束使用接口时,对象的
        引用次数就下降了。当引用次数到达零时,就自动销毁对象。_AddRef是负责增大引用计数的函数。
        如何物理上存储引用计数由读者决定,但是典型情况下将建立整型(Integer)变量去保存计数。

 (3)_Release
     _Release既是个负责降低引用计数的函数。当引用计数达到零时,就自动销毁对象。
 
  5)TInterfacedObject实现了Iunkonwn接口的基本类
     TInterfacedObject = class(TObject,IiInterface)
     ....
     end;
    
  6)多接口继承
    一个类可以实现多个接口,但是如果这些接口中有同名函数,必须给他们取别名。
    type
      Ifoot = interface
        ...
        function F1:Integer;
      end;
     
      IBall = interface
        ...
        function F1:integer;
      end;
     
      TFootBall = class(TInterfacedObject,IFoot,IBall)
        //为同名方法取别名
        function IFoot.F1 = FootF1;
        function IBall.F1 = BallF1;
        //接口方法
        function FootF1:integer;
        function BallF1:integer;
      end;
   
    7)接口的引用和销毁
      Delphi在引用对象时:
      var
         FormattedNumber:IFormattedNumber;
      begin
       FormattedNumber := TFormattedNumber.create();
      end;
     
      不必显示调用_Release()去释放资源。这是因为Delphi在后台已经帮你做了释放工作。如果是C或C++就必须显示调用。
      如果要强制销毁一个接口:
     
      FormattedNumber := nil; //强制销毁接口,仅仅把该接口赋值为nil即可
     
    8)获取接口的指针
      Delphi提供了获取接口指针的一些方法。可以获得一个接口的指针,该指针给出了实现接口的一个对象,或给出了另
    一个接口的指针。
     (1)直接分配
          从对象获取接口的最简单的方法是通过直接分配。类是与它们实现的接口类型兼容的,因此可以编写代码如下;
           
            //直接创建实现接口的类,直接赋值
        var
           FormattedNumber:IFormattedNumber;
           ForInteger: TFormattedInteger;
        begin
          ForInteger := TFormattedInteger.Create();
         FormattedNumber := ForInteger;
         //或者直接写为: FormattedNumber=TFormattedInteger.Create();
        end;
        
        //或直接从接口查询QueryInterface函数得到
        //function Tobject.GetInterface(const IID:TGUID;out:obj);Boolean;
        var
          MyObject :Tobject;
          MyNumber :IFormattedNumber;
        begin
          MyObject := TFormattedInteger.Create();
          if MyObject.GetInterface(IFormattedNumber,MyNumber) then  //安全方式查询,不会产生异常
          begin
            showmessage(MyNumber.formattedString());
          end;
        end;
        其实IUnkonwn接口中的QueryInterface正是调用了GetInterface方法来实现的接口查询:
        {
         function TinterfaceObject.QueryInterface(const IID:TGUID;out:Obj):HResult;
            const E_NOINTERFACE = $80004002;
         begin
             if GetInterface(IID,Obj) then result := 0 else Result:= E_NOINTERFACE;
         end;
        }
       
      (2)as 操作符
        as操作符即可在Tobject上使用,也可以在接口本身使用。
        var
          MyObject :Tobject;
          MyNumber :IFormattedNumber;
        begin
          MyObject := TFormattedInteger.Create();
          MyNumber := MyObject as IFormattedNumber; //如果MyObject与IFormattedNumber接口不匹配会有异常
          showmessage(MyNumber.formattedString());
        end;
        
       (3)绕过引用计数器
           Delphi为我们提供了一个绕过引用计数,不会自动销毁的类:
           TNonRefCountedObject = Class(TinterfaceObject,IUnkonwn)
              protected
         function _AddRef:integer;stdcall;
         function _Release:Integer;stdcall;
           end;
          
           //例如:下面var A: Array of IUnknown参数如果换为var A: Array of TInterfaceObject;那么下面
           //一行代码就使对象过早地销毁;
           //if (A[J] as ICompare).CompareWith(A[I] as ICompare, ASortBy) < 0 then begin
           //as操作符会使Delphi调用ICompare接口中的_AddRef,_Release,这样就会在排序过程中销毁排序的正确对象,
           //这是一个混合引用模型时会产生的一个麻烦的经典实例。所以使用最基类的IUnknown类型可以避免这个问题。
    type
      ICompare = interface
        ['{DDFE0840-E8FB-11D2-9085-0040F6741DE2}']
        function CompareWith(ACompare: ICompare; ASortBy: Integer): Integer;
      end;
    
        procedure SortArray(var A: Array of IUnknown; ASortBy: Integer);
    var
      I, J: Integer;
      Temp: IUnknown;
    begin
      for I := Low(A) to High(A) - 1 do begin
        for J := I + 1 to High(A) do begin
          if (A[J] as ICompare).CompareWith(A[I] as ICompare, ASortBy) < 0 then begin
            Temp := A[I];
            A[I] := A[J];
            A[J] := Temp;
          end;
        end;
      end;
    end;
    
    end.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多