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

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

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

前些日子用 Delphi 写了一个 Windows 外壳扩展程序,大家知道 Windows 外壳扩展实际上就是 COM 的一种应用 -- Shell COM,虽然整个程序写得还算比较顺利,但写完后还是感觉对 Delphi 中 COM 的实现有点雾里看花的感觉,因此我认为有必要花一点时间对 COM 在 Delphi 中的实现做一些研究。另外我也买了李维的新书 --《深入核心 -- VCL架构剖析》,里面有两章涉及了与 COM 相关内容,看完后我知道了COM 在 Delphi 中的实现是基于接口(Interface),而 Delphi 中的接口概念又起源于对 COM 的支持,总之他们之间互相影响,发展成接口在 Delphi 中已经是 First-Class 的地位,并且完全摆脱 COM 而独立存在。
    本系列文章侧重于描述 COM 在 Delphi 中的实现手法,主要配合 VCL 源码片断进行分析,不会涉及过多的基本概念,因此要求读者有一定的 COM 和 接口概念,可以参考我在文章末尾列出的文献。本篇主要讲 COM 对象在 Delphi 中的创建过程。

正文
   为了让读者能跟着我的分析轻松读完本篇文章,我引用文献[2]中的范例做解释,但为了更清楚地阐述问题,我改写了部分代码。所有分析请在 Delphi 7 上测试。Demo 源码这里
下载
   在
Delphi 中首先通过选择菜单 File-->New-->Other...新建一个 ActiveX Library 并保存名称为 SimpleComServer,再新建一个 COM Object,在COM Object Wizard 中将对象命名为 SimpleCOMObject,Options 中的两个复选框都可以不必选中其他的保持默认, 现在 COM服务器端的框架已经建立起来了。剩下的就是需要我们把声明的接口 ISimpleCOMObject 的代码实现,其他的读者自己看源码吧,很简单。
 

服务器端代码

library SimpleComServer;

uses
  ComServ,
  SimpleCOMObject in ''''SimpleCOMObject.pas''''
,
  SimpleComInterface in ''''SimpleComInterface.pas''''
,

exports
  DllGetClassObject,
  DllCanUnloadNow,
  DllRegisterServer,
  DllUnregisterServer;

{$R *.RES}

begin
end.


unit SimpleComInterface;

interface
uses
Windows;
const
  Class_SimpleComObject: TGUID = ''''{3714CF21-D272-11D3-947F-0050DA73BE5D}''''
;

type
  ISimpleComObject =
interface
    [''''{2E2A6DD0-D282-11D3-947F-0050DA73BE5D}''''
]
    function Multiply(X, Y: Integer): Integer; stdcall
;
    function GetClassName: Widestring; stdcall
;
  end
;

implementation

end


unit SimpleCOMObject;

interface

// SimpleCOMObject 的实现部分
uses
  Windows, ActiveX, Classes, ComObj, SimpleComInterface;

type
  TSimpleComObject = class
(TComObject, ISimpleComObject)
 
protected
    function Multiply(X, Y: Integer): Integer; stdcall;
    function GetClassName: Widestring; stdcall;

  end;

const
  Class_SimpleComObject: TGUID = ''''{3714CF21-D272-11D3-947F-0050DA73BE5D}''''
;

implementation

uses
ComServ;

{ TSimpleComObject }

function
TSimpleComObject.GetClassName: Widestring;
begin
  Result := TSimpleComObject.ClassName;
end
;

function
TSimpleComObject.Multiply(X, Y: Integer): Integer;
begin
  Result := X * Y;
end
;

initialization
  TComObjectFactory.Create(ComServer, TSimpleComObject, Class_SimpleComObject,
    ''''SimpleComObject'''', ''''A simple implementation of a COM Object''''
,
      ciMultiInstance, tmApartment);
end.


   完成服务器端的代码后,我们需要写一个客户端小程序来执行服务器端内的接口代码,我仅列出由我改写的关键代码部分,其他的见源码

客户端关键代码

procedure TForm1.Button1Click(Sender: TObject);
var
  aFactory: IClassFactory;
begin
  OleCheck(CoGetClassObject(Class_SimpleComObject, CLSCTX_INPROC_SERVER
or
    CLSCTX_LOCAL_SERVER, nil
, IClassFactory, aFactory));
  aFactory.CreateInstance(nil
, ISimpleComObject, ComInterface);
  ShowMessage(''''The result is: ''''
+
    IntToStr(ComInterface.Multiply(StrToInt(Edit1.Text), StrToInt(Edit2.Text))));
  ComInterface := nil
;
end
;

procedure
TForm1.Button2Click(Sender: TObject);
begin
  ComInterface := CreateComObject(Class_SimpleComObject) as
ISimpleComObject;
  ShowMessage(ComInterface.GetClassName);
  ComInterface := nil
;
end;


    现在开始进入主题,跟随我一起走进 Delphi 的 COM Framework 世界吧。我主要从客户端程序创建 COM 对象来剖析 VCL 源码。
   客户端代码中我用两种获得创建 SimpleCOMObject 对象并获得 ISimpleCOMObject 接口,一旦获得接口,你就可以自由地使用接口指定的方法了。
    让我们先看看 Button1Click 里如何创建 COM 对象的。代码调用了 CoGetClassObject 获得创建 SimpleCOMObject 对象的类工厂 -- IClassFactory 接口,紧接着又通过调用该接口的 CreateInstance 方法创建了真正的 SimpleCOMObject 对象实例,返回 ISimpleComObject 接口指针。 那么上面整个过程在 VCL 中是如何实现的呢?让我们先从 CoGetClassObject 这个API 说起。
    CoGetClassObject 是 Windows 的一个标准 COM API,该函数存在于 OLE32.DLL中,它是 Windows COM DLL 之一。函数先根据系统注册表中的信息,找到类标识符 CLSID 对应的组件程序(即服务器端程序,我们这里讨论的是一个 DLL 文件)的全路径,然后调用 LoadLibrary(实际上是 CoLoadLibrary)函数初始化服务器(Dll 被加载到客户程序进程中)并调用组件程序的 DllGetClassObject 输出函数。DllGetClassObject 函数负责创建相应的类厂对象,并返回类厂对象的 IClassFactory 接口。至此 CoGetClassObject 函数的任务完成,然后客户程序继续调用类厂对象的 CreateInstance 成员函数,由它负责 COM 对象的创建工作。
    注意:Windows COM 规范中指定你必须在服务器中完成并输出 DllGetClassObject,如果这个没有被发现,Windows 将不能传递对象到客户端,DllGetClassObject 将是进入我们的 dll(COM 服务器)的入口点。
    从上面的一番简要陈述不难看出获得 IClassFactory 接口是通过调用服务器端的 DllGetClassObject 函数获得的,传奇实际也就是从这个输出函数开始的。让我们看看它是如何实现的(如果源码中我附加了注释,请一定仔细看看,下面不再提示):

// 摘自 ComServ 单元
function DllGetClassObject(const CLSID, IID: TGUID; var Obj): HResult;
var
  Factory: TComObjectFactory;
begin
  Factory := ComClassManager.GetFactoryFromClassID(CLSID);
  if Factory <> nil
then
    if Factory.GetInterface(IID, Obj)
then
      Result := S_OK
   
else
      Result := E_NOINTERFACE
 
else
 
begin
    Pointer(Obj) := nil
;
    Result := CLASS_E_CLASSNOTAVAILABLE;
  end
;
end;


   ComClassManager 是什么?它是我们需要介绍的 Delphi COM Framework 中的第一个类。    

// 摘自 ComObj 单元
var
  ComClassManagerVar: TObject;

function
ComClassManager: TComClassManager;
begin
  if ComClassManagerVar = nil
then
    ComClassManagerVar := TComClassManager.Create;
  Result := TComClassManager(ComClassManagerVar);
end;


    每个服务器端内存在一个 TComClassManager 实例,即ComClassManagerVar 全局对象变量,它负责管理 COM 服务器中的所有类工厂(class factory)对象(本例中只有一个类工厂)。而类工厂又是什么时候创建的?其实我前面已经列出了,COM Object Wizard 生成的 SimpleCOMObject 的骨架代码的 Initialization 部分已经自动为我们创建一个 TComObjectFactory 对象:

initialization
  TComObjectFactory.Create(ComServer, TSimpleComObject, Class_SimpleComObject,
    ''''SimpleComObject'''', ''''A simple implementation of a COM Object''''
, ciMultiInstance,
    tmApartment);


    Delphi 关键字 Initialization 提示我们 dll 在被载入客户端程序进程空间时,负责创建 SimpleCOMObject 对象的类工厂 TComObjectFactory 就已经被创建了。我们知道,一个服务器端里可以包含多个 COM 对象,并且每一个独立的 COM 对象都必须相应有创建该类的类工厂,假如你设计的服务器端里有十个 COM 对象,那么肯定会有十个负责创建不同类的类工厂,这十个类工厂在程序初始化时都会被一一创建出来。这个概念一定在你的头脑中建立起来,否则后面就不好理解了。再提示一下,VCL 中定义了数种 ClassFactory 类,分别负责某一种类型的 COM 对象创建,TComObjectFactory 是其中最简单的一种[1]。那么 ComClassManager 和 TComObjectFactory 又是如何联系到一起呢?看看 TComObjectFactory 的 Constructor:

[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……
    咸宁网络警察报警平台