分享

Delphi之COM深入编程笔记

 quasiceo 2012-12-08

Delphi之COM深入编程笔记

3279人阅读 评论(2) 收藏 举报

 

COM学习笔记

 

Delphi COM深入编程书签

内容,关键字

页数

说明

接口的授权转换程序

38

Taggregate类是专门为了解决这个问题

42页一个自己写的解决这种问题的类

使用类型库的理由

109

Delphi通过TTypedComObject及其派生类提供了对类型库的支持

支持类型库的COM对象将由TTypedComObject类派生,而不是TComObject

 

类型库可以生成IDL代码

派遣接口(Dispinterfaces

138  142 169

IDispatch

双重接口

143

双重接口备简单定义为自动化服务器,它支持客户使用接口(先期连接)与它连接以及使用variants(后期连接)与它连接,使用Delphi创建的任何自动化服务器将自动支持一个双重接口

注意及格函数

167

SetOleFont 将一个font转换成IDisp接口,便于边际间传递

COM事件和回调

168

 

 

 

 

事件接收器 EventSink

174

实现了自动化事件Event的一个例子

十分方便的创建自动化服务器实例的方法

183

将一个自动化服务器像一个组建一样使用,丢在窗口上

自动化服务器为多个客户断服务时需要做的修改

185

 

自动化服务器为多个客户断服务时需要做的修改

 

回调方法

189

201

不使用派遣接口,invoke方法,而是使用了回调接口的方法实现了回调,但是最好还是使用派遣接口,

当客户端实现那个事件的接口的时候,要选择派生自TautoIntfObject—201

TLB

 

TLB是一种OLE(ActiveX)定义文件,它包括常数、接口(Interface)、类等的定义。你可以在VB的集成环境的Project|Reference中将TLB文件加入项目,然后在Object Browser中看到该文件中包括哪些常数、接口、类,而每个类又包括什么方法和属性。微软提供的各种SDK中通常包括一个或数个TLB文件以方便编程。你也可以制作TLB文件,首先编写一个ODL文件(VC++的帮助中有语法说明),然后使用MKTYPLIB(VB光盘上有)编译生成TLB文件。类似的文件,还有Office所提供的OLB文件。

Activex控件和Activex Form

217

 

ie嵌入activex来测试自己做的Activex Form

279

 

安装DCOM服务器

288

 

创建DCOM客户端

295

使用CoSinpleServer.CreateRemote

使用派遣接口实现事件的DCOM服务器

309

在此例中 客户端将服务器的类作为一个Compoment装入,然后直接双击其事件,并编写代码,实现客户端事件

并直接TPartServerConnect与服务器相连? 183

 

 

 

 

 

所有的GUID存在注册表 HKEY_CLASSES_ROOT / CLSID下面,而GUIDProgID键指定了可读的名,该名可以用于自动化目的的接口


delphi中,所有的接口都是由Iunkonwn直接或间接继承

接口中所有的方法都是public

接口不能声明变量

接口声明的所有函数和过程,都是virtual

 

CoCreateGUID产生一个新的GUID

 

 

 

 

QueryInterface的作用:如果当前的这个类支持(继承自)IID代表的接口,那么新生成一个obj变量。

 

 

如果想销毁一个接口,只需将他置为nil

 

TinterfacedObject类自动实现了Iunknown接口所有的函数,自动实现了引用计数,

as转换符将自动调用AddRefRelease所以传递派生自TinterfacedObject的类的时候要小心自动计数导致销毁:

假设intf为派生自TinterfacedObject的类的实例

procedure DoSth(intf: IformattedNumber) //将自动引发计数, 导致intf出函数后,自动销毁

procedur DoSth(var intf: IformattedNumber)//ok

procedur DoSth(const intf: IformattedNumber)//ok

 

 

获取接口指针的三个方法

1 直接分配

var

  MyInt: TFormattedInteger;

  MyNumberIformattedNumber

begin

  MyInt := TFormattedInteger.Create(12);

MyNumber= MyInt

end;

 

或者if MyInt.GetInterface(IformattedNumber, MyNumber) then

begin

end

 

2 as转换符

       MyInt := TFormattedInteger.Create(12);

       MyNumber= MyInt  as  IformattedNumber;//如果不成功将触发exception

 

3自己重写引用计数

 

 

每一个com对象都有一个相关的类厂,该类厂负责创建在服务器中实现的com对象

 

 

进程内服务器输出的四个标准函数

DllRegisterServer windows用他来注册com对象

DllUnRegisterServer:

DllGetClassObject负责提供给com一个类工厂

DllCanUnloadNowcom负责调用此函数来看是否可以从内存中卸载com服务器


进程内服务器的线程模式

1 单一的:单线程com对象实际上没有线程支持,所有对com服务器的访问都是windows顺序执行的

2 公寓线程:公寓线程的com对象只可以处理来自创建他们的线程的请求,一个服务器可以创建多个com对象,每一个com对象从不同的线程中创建

3自由的:去掉公寓线程的限制,多个线程可以在任何给定的com对象上同时运行

4 both:同时公寓线程和自由线程

 

注册com服务器

RegSvr32:其实是访问DllRegisterServer函数

卸载com服务器

RegSvr32 u

 

客户端创建com对象实例

调用CreateComObject函数,并使用as转换成需要的接口

CreateComObject内部所做的工作:

1内部调用CoCreateInstance

2 CoCreateInstance内部负责创建com对象的类厂的实例,然后使用类厂创建对象,创建完com对象之后,类厂就被销毁

 

创建进程内com服务器步骤

1 File – New – ActiveX Library

2 新生成一个接口

3 File New COM Object

选择派生自新作的接口

4 编译出dll 并且注册

 

 

扩张已经发布接口的方法

新见一个接口,使得派生类同时派生自新旧两个接口

服务端:

INewFace InterfaceIOldFace

//新增一些方法

TAbstract ClassTComObjectIOldFaceINewFace 实现公共算法

 

TNewClass ClassTAbstract 实现一些核心算法

客户端

CreateComObject(××) as IOldFace

改为CreateComObjectTNewClass as INewFace

则原来的调动可以不变,还可以使用新的方法

 

 

注册进程外服务器

  只需运行该服务器,把/regserver放在命令行中。Delphi将注册服务器和com对象,要撤销注册服务器,使用命令行/unregserver

 

进程外服务器的实例化

1 单实例:每个应用程序只允许一个com对象的实例,每个需要com对象实例的应用程序将产生com服务器的单独拷贝。

第一个要求访问服务器的客户程序将启动一个服务器的实例,当第二个客户需要访问服务器时,将导致启动另一个独立的服务器

2 多实例:是指com Server可以创建一个cm对象的多个拷贝,当客户程序请求com对象的一个实例时,由当前运行的服务器创建com对象的一个实例

3 内部实例:用于不被客户应用程序使用的com对象,能创建这种com对象的应用程序只有包含此com对象的com服务器

 

 

调度(Marshaling Data):windows通过一个叫做调度的进程在应用程序和进程外com服务器之间移动数据。Windows不能调度数组或记录 如果需要调度数组或记录,可以有两个方法

方法1 为数据实现IMarshal接口

方法2 通过variant数组

 

 

 

 

创建DCOM服务器

1 File – New – Application

2 File – New – Automation Object

 

创建DCOM客户端

       首先 在客户计算机上注册DCOM服务器的类型库,最简单的方法如下:

       TregSvr DComServer.tlb

1 File – New – Application

2 Project – Import Type Library

       如果新建的DCOMServer不在服务器列表当中,选择ADD按钮,并且导入DCOMServer.tlb类型库,Delphi将自动生成DCOMServer_TLB.pas

3 创建实例并且使用

var

             FServer: ISimpleDCOMServer;

begin

             FServer := CoSimpleDCOMServer.CreateRemote(ecServer.Text);

             if FServer.AreYouThere then

           ShowMessage('I am here.')

      else

           ShowMessage('Where''d I go?');

end;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

Variant数组和结构之间的转换

       function RecordToVariant(Part TPartRecord):OleVariant

       var  P:Pointer;

       begin

              Result:=VarArrayCreate( [0 , sizieof(TPartRecord)] , varByte);

              P:=VarArrayLock( Result);     //varaint]数组转换为delphi数组

              Move( Part , P^ , sizeof(Part) );

              VarArrayUnlock( Result );

       end;

 

       function VariantToRecord( V:OleVariant) : TPartRecord;

       var P:Pointer;

begin

              P:=VarArrayLock(V);

              Move(P^ , Result,sizeof(TPartRecord));

              VarArrayUnlock(V);

End

 

VarArrayLowBound 

VarArrayHighBound

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

sql语句输出记录的行号

1 oracle直接就有rowid

2 SQL Server

       select identity(int,1,1) ID,a into #temp from b

       select * from #temp

drop table #temp

 

列表的重新绘制

TListView.OnCustomDrawItem

       可以重新绘制每一个列表选项的样式 比如如下代码 ,焦点上的节点变成蓝色且加下划线(OwnerDraw 已经设置为false

procedure TfList.LV1CustomDrawItem(Sender: TCustomListView;

  Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);

var cData: TMAM;

begin

  //是否获取的显示

  If Item = nil then Exit;

  cData :=TMAM(Item.Data);

  If cData = nil then Exit;

 

  with (Sender as TListView).Canvas do

  begin

    //Hot属性 

    If cdsHot in State then

      Font.Style :=Font.Style + [fsUnderLine]

    else

      Font.Style :=Font.Style - [fsUnderLine];

    //字体颜色

//    If cData.DBLocked then  //已经加锁

//      Font.Color := OCXFONTCOLOR_LOCKED

    If cdsHot in State then

      Font.Color := clBlue

    else  //没有加锁

      Font.Color := OCXFONTCOLOR_UNLOCKED;

  end;

end;      

 

 

 

 

 

 

 

基本概念

 

COMCOM定义了一组api和一个二进制标准,让来自于不同语言,不同平台的彼此独立的对象相互进行通信

 

调度(Marshaling):实现跨进程边界甚至跨机器边界的函数调用

 

OLE:在应用程序之间共享的一大块数据称为一个OLE对象,能够包含OLE对象的应用程序称为OLE容器,而允许自己的数据被包含到其他应用程序中的应用程序则称为OLE服务器

 

复合文档:一个包含一个或者多个OLE对象的文档称为复合文档。

 

线程模式:每个COM对象都是在一个特定的线程模式下工作的,线程模式决定了一个对象在多线程环境下被操纵的方式,当注册一个COM服务器时,,应该为服务器所包含的每一个COM对象制定他们支持的线程模式。

Single:整个服务器工作在单线程下

Apartment:也称为单线程单元(STA)。每个com对象的多个实例运行在各自独立的线程当中。

Free:也称为多线程单元(MTA)。COM对象必须保护自己的实例数据,避免多线程访问引起冲突。

Both:同时支持ApartmentFree两种线程模式

 

GUID一共128

IID:接口的ID

CLSID:类的ID

 

引用计数

       delphi中,接口的引用计数是自动的,当接口被赋位nil时,自动调用Release

 

AS转换

       并不是一种真正意义上的强制转换,而是对QueryInterface的内部调用,如果转换不成功,as将触发异常,而且接口只和直接支持这个接口的类赋值兼容

       比如

       Ifoo—Ibar—TbarClass

       Var IF:Ifoo;

              TB:TbarClass;

Begin

       TB:=TbarClass.Create;

       IF:=TB //将出现编译错误

End;

 

 

 

OleCheck函数:将Hresult错误转换为异常

 

 

 

In-process COM服务器:dll函数,dll函数与调用它的应用程序在同一个进程内

 

CoFreeUnUsedLies:卸载不再需要使用的COM服务器dll从内存中卸载

 

CreateComObejct内部调用API CoCreateInstance

API CoCreateInstance 首先搜索注册表,载入dll到内存,调用dllDllGetClassObject获得类工厂的接口,调用类工厂的CreateInstance创建COM对象的实例

 

Out-of-process COM服务器:是可执行的,用来被其他的应用程序用以创建COM对象

客户端也是调用CreateComObejct,但是此时CreateComObejct调用         CoGetClassObject在注册表中找到信息后,调用CreateProcess调用相关的应用程序,然后服务器调用CoRegisterClassObject注册类工厂,返回类工厂指针,指向com内部的对象表,从而建立一个com对象实例

注意:服务器是在进程外,存根和生成的com object实例都在服务器中,只有代理在进程内部//????????

 


DCOM服务器:

提供了一种访问网络上其他机器的COM对象的手段,CreateRemoteObject调用CoCreateInstancEx(),从而创建远程对象

其实就是增强版本的进程外服务器

 

自动化

自动化是 应用程序(exe)或者链接库(dll)显露可编程对象给其他应用程序的手段。是语言无关的。

自动化服务器

       显露可编程对象的应用程序或者dll成为自动化服务器

自动化控制器

       访问和控制自动化服务器的应用程序

 

自动化对象在本质上是一种实现Idispatch接口的COM对象

 

类型库:保存在TLB文件中,或者作为资源链接在服务器应用程序或dll

 

后期捆绑:通过Idispatchinvoke方法调用的

前期捆绑:直接调用

 

支持双重接口的自动化对象:可以invoke来调用方法,也可以通过Idispatch的派生接口调用方法

      

 

创建自动化服务器的步骤

1 创建一个应用程序exe或者dll

2 创建一个自动化对象到项目中 Automaton Object

3 通过类型库给自动化对象添加属性和方法

4 实现自动化对象的方法

 

 

SafeCall

       类型库编辑器中输入方法的默认方式

       1 SafeCall可以捕捉所有的异常情况,所有声明为safecall的函数都被包含在try except

2 方法将返回一个HResult

 

 

创建自动化控制器的步骤

1 注册自动化服务器

2 导入TLB  选择import type library 选择自动化服务器的exe或者dll文件,自动生成××××_TLB.pas

3 连接自动化服务器

 

 

连接自动化服务器的三种方式

       1接口

2调度接口

3 Variant

 

1 通过接口访问

      var I:IUnitAuto

       begin

              I:=CoUnitAuto.Create

       end;

 

2 通过variant

       var V:Variant

       begin

              V:=CreateOleObject(‘UnitServ.UnitAuto’)

end

3 通过派遣接口

       var  D1:IUnitAutoDisp

       begin

              D1:=CoUnitAuto.Create as IUnitAutoDisp

       end

 

 

 

 

通过Variant调用自动化服务器的方法func的实现步骤

A Delphi将这个方法的名字func传递到IDispatch.GetIDsOfNames

B GetIDsOfNames返回代表此方法名的整型ID

C Delphi通过第二步返回的ID来调用Invoke方法

 

自动化为每一个接口实现了一个派遣接口(Dispinterfaces),使用派遣接口的时候,可以把前两个步骤去掉,自动化服务器不用调用GetIDsOfNames,而且服务器也不必响应所述方法的dispid。运行时,客户程序预先决定的dispid简单调用invoke

派遣接口只是为了方便客户而设定的,读者实际上并没有在服务器上实现派遣接口,是服务器实现了接口

 

 

FIntf: = CoAutoTest . Create ( );

FdispIntf := CreateComObject ( Class_AtuoTest) as IautoTestDisp

Fvar := CreateOleObject (‘Srv.AutoTest’ )

(释放Variant变量的方式为 Fvar : = UnAssigned )

 

用接口来控制自动化服务器比使用调度接口和variant来控制要好,建议尽量使用接口方式

variant调用在运行期间,先调用GetIDSOfName将方法名字转换为调度号,然后才能执行用InVoke来执行这个方法

调度接口性能介于两者之间。因为它的方法调度号在编译时已经知道

 

 

 

注册自动化服务器的方法

方法一 Delphi –Run—Register Activex – Server

方法二 Regsvr32 . exe

方法三 使用delphi的工具 TregSvr . exe

 

COM事件

IconnectinPoint代表了事件接口

 

需要向客户端提供事件接口的服务器必须实现IconnectionPointContainer接口,在COM,连接点 (connection point) 描述了一个实体, 该实体提供了对事件接口的访问。如果客户端要判断一个服务器是否支持事件,就必须用QueryInterFace函数查询IconnectionPointContainer接口, 如果这个接口存在,那么服务器支持事件 IconnectionPointContainerEnumConnectionPoints遍历所有服务器支持的事件接口,

IconnectionPointContainerFindConnctionPoint得到一个指定得事件接口

 

 

            IconnectionPointContainer

 

 


             IconnectinPoint                                   

 

 


客户程序1  客户程序2  客户程序3

 

delphi中的InterfaceConnect源码

procedure InterfaceConnect(const Source: IUnknown; const IID: TIID;

  const Sink: IUnknown; var Connection: Longint);

var

  CPC: IConnectionPointContainer;

  CP: IConnectionPoint;

begin

  Connection := 0;

  if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then

    if Succeeded(CPC.FindConnectionPoint(IID, CP)) then

      CP.Advise(Sink, Connection);

end;

 

 

 

 

 

 

接收器(sink :包含在客户程序中的输出接口的实现称为接收器

源(source):客户端触发事件的服务器对象称为源

 

 

 

 

 

 

在客户端,如果对象实例已经在运行,那么如果要使各客户端都连到这个实例上,还要做一个小

的调整,这要通过调用COM API函数G e t A c t i v e O b j e c t来完成,如下所示

 

 

 

 

 

 

 

VariantOleVariant的区别是前者支持所有的类型,而后者只支持在自动化中兼容的类型

 

 

 

 

 

 

 

 

 

 

 

 

创建ActiveX控件的步骤

1 新建一个ActiveX Control

 

因为ActiveX控件可以运行在web浏览器上,所以调用web浏览器上的函数和接口将非常有意义,web浏览器上的函数和接口大部分在UrlMon单元中

HlinkNavigateString

HlinkGoBack(punk:IUnknown)

HlinkGoFrrward(punk:IUnknown)

 

 

当需要使用ActiveX 控件的Iunkonwn接口时,对于ActiveX控件可以传递Control As Iunknown给这个参数;对于ActiveXForm来说,可以传递IunknownVclComObject)给这个参数

 

 

 

 

cd ../

dos命令,跳到当前目录的上一层

 

 

SendMessage()函数支持WM_SETREDRAW消息。使用这个消息,你的代码可以决定是否一个窗口应该被重新绘制。传递True (等价于API1)允许窗口重新绘制,或者False (0)来阻止绘制

sendmessage(handle,wm_setredraw,0,0);
禁止刷新
sendmessage(handle,wm_setredraw,1,0);
允许刷新

begin

SendMessage(LV1.Handle,WM_SETREDRAW,0,0);

  try

    LV1.Items.Clear;

  finally

    SendMessage(LV1.Handle,WM_SETREDRAW,1,0);

  end;

end

 

 

使用命令行参数执行一个exe程序

function CmdOpen(Const AppExe,File1:String):Boolean;

var iRtn:integer;

    sCmd:string;

begin

  If File1<>'' then

    sCmd:=AppExe+' "'+File1+'"'

  else

    sCmd:=AppExe;

  iRtn:=WinExec(PChar(sCmd),SW_SHOWDEFAULT);

  Result:=(iRtn>31);

end;

 

 

 

在线程函数中,如下代码,比较好

repeat

       Application.ProcessMessage//迫使程序处理别的消息 使得界面有响应

until
使用windows默认的程序打开某一个文件

function  ShellOpen(hWnd :Integer; Const Value:String):Boolean;

var iRtn:integer;

begin

  Result:=False;

  if FileExists(Value) then

  begin

    iRtn:=ShellExecute(hWnd,'open',PChar(Value),nil,nil,SW_SHOWDEFAULT);

    Result:=(iRtn>32);

    if iRtn=SE_ERR_NOASSOC then

      MessageBox(hWnd,'没有对应的Windows应用程序打开!','信息',MB_ICONINFORMATION+MB_OK);

  end;

end;

 

 

类型库:提供了完全说明com服务器的无确定的编程语言的方法,通常可以作为一种资源集成到com服务器中,也可作为。TLB文件被分发

支持类型库的com对象将由TTypedComObject类派生

 

 

 

创建Activex Form步骤

1 创建Activex Library

2 创建 Activex Form

 

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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多