与DLL相比,COM就是多了一些规范,按照规范写能够让系统进行调用
C++来写,uuid是定位COM
然后自动读取DLLCreateInstant,里面创建类工厂
然后类厂根据传入的CLSID , IID 然后创建对象.但在DELPHI
找不到DLLMain
那四个接口让DELPHI的ComServ自动处理
但是,不论我怎样简单的写,什么代码也没有(就用帮助的那个直接生成一个类和接口而已)
创建ActiveDll,然后建Com
注册
然后调用CreateComObject居然说没有类=.=(默认是继承自TComObject的)
到底怎么回事啊?
而且使用TypeLibrary建立接口,接口的类型不但定死,而且指针也没有似的,选择一个带*号的,然后选择out,居然报错,自己加入一些类型定义都给你去掉不让加
接口规定死是function类型.自己再写也照旧改掉写的.但看人家的就算TypeLibrary里面是Function,在外面可以写成Procedure的.我晕
也看见人家的TLB文件里面有自定义的函数指针的,我倒
现在的情况就是高不成低不就,想自己全部再写,DELPHI不让你写.用DELPHI带的,这又限制那又限制,
结果,还只是简单的写个DLL,露个函数出来,制造类厂,然后用这个类厂来创建其他类.唉.哪位高手能指点指点啊? C++转DELPHI果然就是痛苦,以为要做的不用做,想要做的不让做. 换过来DELPHI转C++,一直不做的却要做,什么都让做却不知道从哪开始做.

解决方案 »

  1.   

    DllMain被封装起来了,就类似于MFC中的WinMain一样,DLL在装载的时候会从begin开始执行,到end结束。
    DLL主要是用其导出函数,所以在begin和end之间一般没有代码。
    Delphi中写COM组件一般从基类继承,类似于MFC中的CCmdTarget实现了IUnkown一样,当然Delphi中已经实现的东西很多,所以不用什么都自己写。
    新建项目的时候要想好自己要做什么样的COM组件
    如果是普通的COM组件就选择COM Object,它会从TTypedComObject继承下来,默认是支持类型信息的(IProvideClassInfo接口)。
    如果是自动化组件,就选Automation Object,他会从TAutoObject继承下来,它是在TTypedComObject的基础上实现了IDispatch接口。
    此两种对象既可以是进程内的也可以是进程外的,所以Appilaction和Library的项目都可以添加。如果你的COM组件是可视控件,可以选择ActiveX Control,它可以直接将Delphi中的控件包装为ActiveX控件,因此你可以把你要做的东西当成Delphi组件来设计,然后包装一下就可以了。
    如果你的COM组件是一个组合型的控件,建议使用Active Form,创建一个窗体,把你所需要的东西组合在一起,并发布接口来进程操作。
    此两种对象只能是进程内组件,所以只能是ActiveX Library项目才能添加。Delphi中写进程内组件的话选择ActiveX Library,默认包含了ComServ单元,其中COM导出的四个标准方法在ComServ中已经实现。你可以选择添加一个或多个COM组件,选择后可以发现每一个组件都对应了类厂的实现(TTypedComObjectFactory或TAutoObjectFactory),而类厂都交给了ComServer进行管理,类厂的CreateInstance使用RTTI实现的,因此你只要简单的将你的COM对象的类型信息告诉它就可以了,而ComServer则会在DllGetClassObject中将对应的类厂查找出来提供给外部,所有的这些细节全部给处理好了,你可以专注于设计你的对外接口以及COM对象的实现。再跟你介绍以下TLB的设计器
    其实各种类型都可以设计成指针类型(out),类型并不是只能选,可以手动输入的,在后面加上*号就可以是输出参数了。Delphi中,接口的方法有可能被标识为safecall,其含义是带HRESULT返回值的方法。Delphi中可以把IDL中描述的RetVal的参数映射为返回值(虽然实际返回值仍是HRESULT,RetVal只是个特殊的out类型参数而已),这个是编译器进行了相关的识别处理,并对HRESULT的返回值使用OleCheck进行处理,意味着在返回值不正确时会抛出异常(因此成为safecall)。由于编译器的处理导致TLB中的接口看起来跟C++的不一样(居然有procedure)。如果你研究透了Delphi对COM的支持的源代码的本质,你会发现他的思路很清晰,使用起来也很方便,优雅而大方(相对MFC和ATL来说)。TLB的设计器貌似挺好用的,我至今还没发现需要自己手动修改TLB的源代码才能实现某些功能,所以建议楼主不要轻易下结论,尽量按常规方法多尝试几次。
      

  2.   


    兄台,可以再赐教一下:
    我到底如何操作,才能定义自己的函数指针?定义procedure 型函数?
    还有就是,一个对象,继承自一TComObject和另一个线程接口 ,然后在initialize 里面已经加好 TComObjectFactory.Create (名字好像是这个).
    通常,实现把线程采用面向对象式管理, 
    那么会在全局区定义 个线程入口函数.
    然后,定义线程接口,每个线程类都继承自这个接口.
    然后创建线程的时候,把Self当作参数传入BeginThread, 在线程入口函数里面,再采用QueryInterface 或者 直接强制类型转换(必须是类).
    然后调用线程接口作为执行内容.(当然这个内容最好对自身的任何值都只是采用读取方式,要写就问题大了)
    那么,就可以实现线程的面向对象式管理了,不过要带自定义参数就麻烦点.怪事就是这样开始了. 刚开始,线程是好好的运作的(需要不停的新建,同时会有6个线程运行左右) ,然后运行了不久,就会弹出错误.
    说什么 访问失败, 或者什么ExcutXXXXXXXX 过多. 我倒.完全不知道什么错误.
      

  3.   

    还有一点
    我查了过20本书,居然没有一本书能够告诉我:到底我在ActiveX里面可不可以有多个组件? 多个组件怎样写?
    如果可以多个组件,那么我会直接发布一个,然后提供多个接口.
    组件里面由于有一个统一线程管理类.每个组件内部会有线程处理某些东西.
    所以每个组件的交流必须通过这个核心的.
    现在只是把组件交流的核心写成DLL(COM看样子是写不成=.=,本来想着只要注册了COM,然后这些组件不论放在哪里都可以正确读取DLL.)
    对DELPHI所缺乏的知识太多了
    而且现在的DLL,不知道为何.如果把代码写成非DLL形式,那么是不会出错的,放在DLL里面,就报错了.
    组件跟核心的关系是 Has A 关系的,并非is a . 我也只是单单把这个a ,从普通的PAS 移到了DLL里面查询出来而已(查询出来是没错的,初期运行也没错的)
    我真想哭-.-
      

  4.   

    1、定义方法指针
    type
      TMyCallback = function(Param: Pointer): Integer;//TMyCallback也就是普通的回调函数指针
      TMyEvent = procedure(Param: Pointer) of object;//of object表示该方法隐含Self指针,也就是thiscall
    你要在接口中使用可以直接在TLB中将类型写成void *,它会映射成Pointer类型,使用的时候进行强制转换就行了
    其实在COM中不是有连接点可以支持事件回调,如果你要用连接点的话需要用Automation Object类型,在TLB设计器中添加一个dispinterface,指定一个CoClass实现它,并将该接口设置为Source,然后重写CoClass的EventSinkChanged方法,如
    procedure TMyAutoObject.EventSinkChanged(const EventSink: IUnknown);
    begin
      FEvents := EventSink as IMyEvents;
      inherited EventSinkChanged(EventSink);
    end;
    IMyEvents是dispinterface(实际就是一个dispid的对应表,编译器在调用时会根据后面的dispid调用Invoke)
    你要触发事件时就直接调用FEvents.On...2、COM的线程模型
    COM的线程模型向来有两种
    一、Apartment
    运行于套间的COM组件,你写组件的时候就不再需要考虑方法重入的问题,因为COM库对于注册为Apartment的对象,如果跨线程调用会直接报错(非创建线程)。对该类组件,客户端如果一定要跨线程(进程)调用,需要将接口进行列集(CoMarshalInterface),然后在另一个线程(进程)中进行散集(CoUnmarshalInterface),这样处理之后COM库会对接口的调用进行同步处理。
    二、Free
    自由线程组件,写该类组件时所有同步都需要考虑,接口的各种方法都需要是线程安全的。客户端可以在任何线程使用组件。
    不过楼主所说的线程貌似是COM组件内部的东西,对于客户端来说并不可见,应该不会有什么影响。而COM跟普通的DLL并没什么不同,代码移植应该是可以的,大概是某些细节被忽略了吧。楼主很多东西说的太笼统,我没法理解出楼主想要实现的功能,例如那个线程接口是个什么东西。
      

  5.   

    线程是DLL自身内部的.
    向外伸出线程执行内容接口.使用方法就是外面向里面赋上执行函数入口,然后DLL里面进行创建线程并调用.
    而线程是在COM内部创建运行的.现在一个最大的问题就是:
    对于普通的PAS单元.
    我使用 WaitForSingleObjec(BeginThread(XXXX)) 这样,主线程是不会锁死的.
    但是如果我在DLL单元里面伸出一个接口 InsertThread
    procedure InsertThread 
    begin
      WaitForSingleObjec(BeginThread(XXXX))
    end然后在主线程里面写上
    procedure InsertThread external 'XXX.dll'然后对这个函数进行调用.百分百会死锁.经过测试,不论DLL内部的线程有多少.
    只要外部主线程对这个DLL有所操作,主线程都是优先处理的.
    任何一个地方使用WaitFor函数,即使主线程处理等待状态.DLL里面的线程也照旧不会执行下去的.
    这样就发生优先权死锁的问题.COM也不想考虑了.现在只弄个DLL,里面创建线程能够跟调用的线程处于同等级的priority 来执行我就满足了.
    因为:假如DLL内部线程进行获得一个临界,然后这个主线程向DLL调用申请,也要获得临界的.
    那么DLL里面的线程会停下来,主线程也会由于得不到临界而一直等待.如果放在普及PAS里面,主线程是等待,但起码,子线程会继续执行,主线程等子线程释放临界就会继续运行下去了.
    而子线程是放在DLL里面,那么主线程调用个申请,子线程都会立刻停止等到响应完主线程的任何再继续自身的线程,这样再怎么简单的一个线程也死锁啊
    为了实现DLL线程这个东西,我所有线程同步的方法都用过了,没有一个成功,包括设置优先权为 ABOVE_NORMAL (并且是设置成功的)可以有人告诉我是怎么一个回事么?
      

  6.   

    [quote 引用 CSDN 名家YOUCUNZAI的回复:你们打的字好多啊,看眼睛都花了...]
    [QUOTE CSDN]
      

  7.   

    [QUOTE 引用 CSDN 名家YOUCUNZAI的回复:你们打的字好多啊,看眼睛都花了...]
    ..
    [/QUOTE] 
     
      

  8.   

    你并没说你的子线程干了些什么事?你说子线程在Dll中会阻塞,你有没有调试过子线程在什么地方阻塞的?
      

  9.   

    阻塞在哪里是知道的,就是优先权的问题
    就像上述那样.
    一句普通的开启线程并等待返回,在PAS里面写,然后编译执行,是不会锁死的这个人人都知道.
    但是如果这个过程是调用DLL来做,那么就会锁死了.子线程里面就算简单到只作 1+2 .
    只要是采用DLL创建线程,并且是WaitForSingleObject(BeginThread(XXX),INFINITE);
    这样主线程就会锁死了.DLL 线程里面执行,有一个 
    CriticalSection.Acqurie
    //执行代码
    CriticalSection.Release要是在执行代码的过程中,主线程向DLL发出调用.
    调用过程中,也有一句
    CriticalSection.Acqurie
    //执行另一段代码
    CriticalSection.Release.这段插入,很明显普通来说 : DLL中的线程还没有执行代码完毕,是不会Release放临界的
    但是,只要主线程向DLL一发出调用,如果DLL中的线程还没有Release.
    那么主线程肯定会在Acqurie过程中等待.普通来说,主线程这个等待,子线程是会继续执行完并释放临界的.
    但DLL里创建的线程就有区别了,主线程只要一申请,那么两个就会立刻相互死锁.
    主线程在等待临界,DLL子线程也在等待主线程返回.难道真的是要采用注入方式申请线程才能解决问题?
      

  10.   

    贴代码吧
    难道你是
    CriticalSection.Acqurie 
    WaitForSingleObject(BeginThread(XXX),INFINITE); 
    CriticalSection.Release.XXX中
    CriticalSection.Acqurie 
    //执行代码 
    CriticalSection.ReleaseCriticalSection在exe和dll中是同一个么?
      

  11.   

    Delphi的DLLMain好像是这样子的:
    library TestDll;uses
      ……
    exports
      ……
    procedure DllEntry(dwReason:Integer);
    begin
      //add your code here
    end;begin
      EntryProc:=@DllEntry;
      DllEntry(DLL_PROCESS_ATTACHE);
    end;
      

  12.   

    由于工作关系, 好长一段时间没有上来
    谢谢 lake_cx 等各位的帮助,派分了