转至繁体中文版     | 网站首页 | 图文教程 | 资源下载 | 站长博客 | 图片素材 | 武汉seo | 武汉网站优化 | 
最新公告:     敏韬网|教学资源学习资料永久免费分享站!  [mintao  2008年9月2日]        
您现在的位置: 学习笔记 >> 图文教程 >> 软件开发 >> Delphi程序 >> 正文
Delphi中COM 实现研究手记(一)         ★★★★

Delphi中COM 实现研究手记(一)

作者:闵涛 文章来源:闵涛的学习笔记 点击数:1828 更新时间:2009/4/23 18:28:02

// 摘自 ComObj 单元
constructor TComObjectFactory.Create(ComServer: TComServerObject;
  ComClass: TComClass; const ClassID: TGUID; const
ClassName,
  Description: string
; Instancing: TClassInstancing;
  ThreadingModel: TThreadingModel);
begin
   // ...
  // 将自己插入到 ComClassManager 的 Factory List 中去
  ComClassManager.AddObjectFactory(Self); 

  // ...
end;


    再看看 ComClassManager 相关实现代码:

// 摘自 ComObj 单元
TComClassManager = class(TObject)
private
 
// TComClassManager 维护着一个 TComObjectFactory 链表
  FFactoryList: TComObjectFactory;
  FLock: TMultiReadExclusiveWriteSynchronizer;
 
procedure AddObjectFactory(Factory: TComObjectFactory);
   // ...
end;

procedure TComClassManager.AddObjectFactory(Factory: TComObjectFactory);
begin
  FLock.BeginWrite;
 
try
    Factory.FNext := FFactoryList;
    FFactoryList := Factory;
 
finally
    FLock.EndWrite;
  end
;
end;


    ComClassManagerVar 维护着服务器中的所有的类工厂的一个链表,每个单一类工厂的实例都是自动初始化,在我们的服务器 Initialization 节你可以看到,并自动将自己添加到 ComClassManager 的链表(FactoryList)中。现在想想,这样的设计是不是非常棒。
    请跟随我继续往下走。当客户端要求 DllGetClassObject 返回指定创建的类工厂,在函数内部调用了 TComClassManager 的 GetFactoryFromClassID 方法。该方法遍历 FactoryList 链表,根据 ClassID 找到对应的类工厂,并返回类工厂对象实例。

// 摘自 ComObj 单元
function TComClassManager.GetFactoryFromClassID(const ClassID: TGUID): TComObjectFactory;
begin
  FLock.BeginRead;
 
try
    Result := FFactoryList;
    while Result <> nil
do
   
begin
      if IsEqualGUID(Result.ClassID, ClassID)
then
        Exit;
      Result := Result.FNext;
    end
;
 
finally
    FLock.EndRead;
  end
;
end;


    对上面的代码分析我再多说一下,链表 FFactoryList 变量实际就是 TComObjectFactory 类型,TComObjectFactory 创建时就获得了丰富的关于它要创建的相关 COM 对象信息,例如在我们这个范例里,ClassFactory 知道了它要创建的 COM 对象类型是 TSimpleComObject, ClassID 是 Class_SimpleComObject..等等,这些都为类工厂在创建相关类以及一些辅助方法(函数)都提供了极为重要的信息。

// 摘自 ComObj 单元
constructor TComObjectFactory.Create(ComServer: TComServerObject;
  ComClass: TComClass; const ClassID: TGUID; const
ClassName,
  Description: string
; Instancing: TClassInstancing;
  ThreadingModel: TThreadingModel);
begin
   // ...
  FComServer := ComServer;
  FComClass := ComClass;
  FClassID := ClassID;
  FClassName := ClassName;
  FDescription := Description;
  FInstancing := Instancing;
  FErrorIID := IUnknown;
  FShowErrors := True;
  FThreadingModel := ThreadingModel;
  FRegister := -1
;
end;


    DllGetClassObject 获得正确的类工厂对象之后调用它的 GetInterface 方法,这个方法实际上是继承自 TObject.GetInterface,Delphi 为每一个带有 GUID 的接口设计了一个记录结构 -- TInterfaceEntry 记录,实现 IClassFactory 接口的 TComObjectFactory 对象 VMT 中的 vmtIntfTable 指向一个 TInterfaceTable 记录, 该记录包含有它实现的接口数量(IUnknown、IClassFactory)、相应接口的 TInterfaceEntry 记录等信息,通过查询 IClassFactory 接口相应 TInterfaceEntry 记录中的 IOffset 域获得该接口在  TComObjectFactory 对象实例中的正确位置,并返回指向该位置的 IClassFactory 接口指针[1][3]。

// 摘自 System 单元
function TObject.GetInterface(const IID: TGUID; out Obj): Boolean;
var
  InterfaceEntry: PInterfaceEntry;
begin
  Pointer(Obj) := nil
;
  InterfaceEntry := GetInterfaceEntry(IID);
  if InterfaceEntry <> nil
then
 
begin
    if InterfaceEntry^.IOffset <> 0
then
   
begin
      Pointer(Obj) := Pointer(Integer(Self) + InterfaceEntry^.IOffset);
      if Pointer(Obj) <> nil
then
        IInterface(Obj)._AddRef;
   
end
   
else
      IInterface(Obj) := InvokeImplGetter(Self, InterfaceEntry^.ImplGetter);
  end
;
  Result := Pointer(Obj) <> nil
;
end;


    至此,CoGetClassObject 内部调用服务器端的 DllGetClassObject 已经正确获得了负责创建 SimpleCOMObject 对象的 IClassFactory 接口。在获得这个接口后,就可以调用它的方法 CreateInstance 创建 SimpleCOMObject 对象并返回 ISimpleCOMObject 接口,现在你可以对 ISimpleCOMObject 接口任意进行操作了。
    让我们再看看 ButtonClick2 中是如何创建 SimpleCOMObject 对象的。
    ButtonClick2 是调用 CreateComObject 函数创建 SimpleCOMObject 对象的。 CreateComObject 函数只是对 COM API -- CoCreateInstance 的一个简单包装。为什么要包装它,你可以看一下 CoCreateInstance 的参数就知道为什么了,参数多且复杂,这是 Windows API 的通病,而 VCL 实现却很体贴我们,它传递 CLSID 作为唯一的参数,其实平时应用中我们创建的大部分 COM 对象都是 CLSID 已知,并且对象是驻留在本地或进程内服务器的指定对象。

// 摘自 ComObj 单元
function CreateComObject(const ClassID: TGUID): IUnknown;
begin
  OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER
or
    CLSCTX_LOCAL_SERVER, IUnknown, Result));
end;


    CoCreateInstance 也存在于 OLE32.DLL中,其内部也是先调用 CoGetClassObject 函数,返回负责创建 SimpleCOMObject 的IClassFactory 接口,然后也还是调用该接口的 CreateInstance 创建 SimpleCOMObject 并返回该对象的 IUnknown 接口,到这一步,与Button1Click 中创建 SimpleCOMObject 的实现方法区别在于 Button1Click 通过 ClassFactory 的 CreateInstance 直接返回 ISimpleCOMObject 接口而不是它的 IUnknown 接口,其他的并没有什么区别,相对 Button1Click 的方法更直观。在获得了 SimpleCOMObject 的 IUnknown 接口之后,我们并不能立即用此接口去调用 ISimpleCOMObject 的方法,为了和对象通信,必须先将它转换成 ISimpleComObject 接口。那么有读者会问为什么 CreateComObject 不设计成能直接返回需要的接口呢,我想还是为了简化这个函数的使用吧。获得 ISimpleComObject 接口可以通过调用 IUnknown 接口的 QueryInterface 方法查询 SimpleCOMObject 对象是否支持该接口, Delphi 为我们提供了更简单的方法 -- “AS”关键字。先让我们看看 As 在幕后到底为我们做了什么(Debug 状态下的反汇编源码):

Unit1.pas.49: ComInterface := CreateComObject(Class_SimpleComObject) as ISimpleComObject;
0045B2C6 8D55FC        lea edx,[ebp-$04]
0045B2C9 A16CD24500    mov eax,[$0045d26c]
0045B2CE E8C9F0FFFF    call CreateComObject
0045B2D3 8B55FC        mov edx,[ebp-$04]
0045B2D6 8D8314030000  lea eax,[ebx+$00000314]
0045B2DC B93CB34500    mov ecx,$0045b33c
0045B2E1 E87AA9FAFF    call @IntfCast


    可以看到, AS 被转换成调用 @IntfCast,即 system 单元的 _IntfCast 函数。呵呵,其实就是调用 IUnknown 接口的 QueryInterface 方法。

// 摘自 System 单元
procedure _IntfCast(var Dest: IInterface; const Source: IInterface; const IID:
  TGUID);
{$IFDEF PUREPASCAL}
// PIC:  EBX must be correct before calling QueryInterface
var
  Temp: IInterface;
begin
  if Source = nil
then
    Dest :=
nil
 
else
 
begin
    Temp := nil
;
    if Source.QueryInterface(IID, Temp) <> 0
then
      Error(reIntfCastError)
   
else

上一页  [1] [2] [3]  下一页


[聊天工具]Google Suggest十大妙用  [聊天工具]保驾护航Web迅雷 全新版本给你更多安全
[聊天工具]玩转火狐的Cookie 让火狐狸吃好小甜饼!  [聊天工具]P2P下载的好工具 POCO完全攻略
[聊天工具]横扫一切高价话费 Vbuzzer八分钱国际长途任你打  [聊天工具]众人拾柴火焰高 改进迅雷于不经意间 迅雷
[聊天工具]中英文互翻 Google Toolbar4中文版试用手记  [聊天工具]巧用µTorrent 体验国外下载站的乐趣
[聊天工具]可远程搜索桌面—Google Desktop 3全新体验  [聊天工具]腾讯浏览器 TT 之实用技巧荟萃 TT,技巧荟萃
教程录入:mintao    责任编辑:mintao 
  • 上一篇教程:

  • 下一篇教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)

    同类栏目
    · C语言系列  · VB.NET程序
    · JAVA开发  · Delphi程序
    · 脚本语言
    更多内容
    热门推荐 更多内容
  • 没有教程
  • 赞助链接
    更多内容
    闵涛博文 更多关于武汉SEO的内容
    500 - 内部服务器错误。

    500 - 内部服务器错误。

    您查找的资源存在问题,因而无法显示。

    | 设为首页 |加入收藏 | 联系站长 | 友情链接 | 版权申明 | 广告服务
    MinTao学以致用网

    Copyright @ 2007-2012 敏韬网(敏而好学,文韬武略--MinTao.Net)(学习笔记) Inc All Rights Reserved.
    闵涛 投放广告、内容合作请Q我! E_mail:admin@mintao.net(欢迎提供学习资源)

    站长:MinTao ICP备案号:鄂ICP备11006601号-18

    闵涛站盟:医药大全-武穴网A打造BCD……
    咸宁网络警察报警平台