最近开发了一个DCOM服务器,内部有个队列任务。客户端可以连接调用以添加任务到队列。现在有个问题是当客户端调用完断开连接后,DCOM服务器的队列任务还没执行完毕DCOM就退出了。请问有什么办法可以让队列执行完才退出呢?

解决方案 »

  1.   

    调用addRef来增加一个COM的引用计数就可以了。
      

  2.   

    你都没试怎么知道不行?经常我们为了防止异步线程处理的业务不因为用户的断开使得进程退出而中断,就为COM增加一个引用计数,便得用户断开之后引用计数不为零而保留进程,直到业务完成,才退出。
      

  3.   

    我试过才回复的,我加了引用计数都为-1,并在DCOM服务器中加了finalization来标识程序关闭,并在任务管理器观察DCOM进程.客户端调用自动化对象的方法后,进程消失了,打开日志,并没发现有finalization的标识.证明DCOM是什么都不运行就消失了,真奇怪.
      

  4.   

    其实有两种办法,一个是正规的做法,通过AddTerminateProc来拦载,Terminate,然后判断自己的计数是否为0,如果不为0就返回false.另一种是不推荐的做法,其实引用计数在ComServer实例当中保存着,本来可以调用CountObject(True)增加和CountObject(False)来减少,不过这个却是一个protected函数。一种破坏性的做法,那就是:
    增加:
    InterlockedIncrement( PInteger(Integer(ComServer) + sizeof(Pointer))^);
    减少:
    InterlockedDecrement( PInteger(Integer(ComServer) + sizeof(Pointer))^);
      

  5.   

    还有第三种方法就是自己创建一个自己的COM实例(忘说了,我们使用的是这种方法^_^)。
      

  6.   

    _Addref是可行的,不能放在Create过程当中。可以放在被调用方法当中。正好可以替代第二种方法,但是此时有一个问题是导致DllCanUnloadNow调用时返回False,而必须自己去判断并释放。否则就会多出引用计数。这个问题很严重。上面给出的第二种方法也是不可行的。所以方法三,创建自己的COM实例应该是最有效的方法。
      

  7.   

    终于完成了一个演示,希望对楼主有用。方法比较简单。
    演示是通过增加一个方法Method1,被用户调用后,记下自己当前的ThreadID(GetCurrentThradId获得),然后调用_Addref增加引用计数。
    启动一个线程,在线程当中只做一个事,等待15秒,线程置为FreeOnTerminate,并且OnTerminated事件置过程放在COM对象当中,事件发生时(线程是主线程),调用_Release释放引用计数。此时COM的处理线程会在等待消息,所以不会去释放对象,但是此时可以PostThreadMessage(ThreadID,0,0)唤醒线程,线程就会检查计数,(而此时计数正好为0)并释放对象。
      

  8.   

    unit Unit4;{$WARN SYMBOL_PLATFORM OFF}interfaceuses
      Windows, Messages, SysUtils, Classes, ComServ, ComObj, VCLCom, DataBkr,
      DBClient, Project2_TLB, StdVcl;type
      Tcc = class(TRemoteDataModule, Icc)
      private
        { Private declarations }
        ThrdID: DWORD;
        procedure OnThradTerminated(Sender: TObject);
      protected
        class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID: string); override;
        procedure Method1; safecall;
      public
        { Public declarations }
      end;  //一个定时器线程
      TMyThread = Class(TThread)
        protected
          procedure Execute;override;
      End;
    implementation{$R *.DFM}procedure TMyThread.Execute;
    var
      I: Integer;
    begin
      I := 15000;//15秒
      while I>0 do begin
        Sleep(15);
        Dec(I,15);
      end;end;
    class procedure Tcc.UpdateRegistry(Register: Boolean; const ClassID, ProgID: string);
    begin
      if Register then
      begin
        inherited UpdateRegistry(Register, ClassID, ProgID);
        EnableSocketTransport(ClassID);
        EnableWebTransport(ClassID);
      end else
      begin
        DisableSocketTransport(ClassID);
        DisableWebTransport(ClassID);
        inherited UpdateRegistry(Register, ClassID, ProgID);
      end;
    end;procedure Tcc.Method1;
    var
      Thd: TMyThread;
    begin
      //添加引用计数
      _AddRef;
      //取得当前线程的ID,以方便释放时唤醒
      ThrdID := GetCurrentThreadId;
      //创建一个线程用于15秒后释放计数,以模拟线程异步操作
      Thd := TMyThread.Create(True);
      //置线程线程事件过程,以执行释放计数的过程代码
      Thd.OnTerminate := OnThradTerminated;
      //线程自动销毁
      Thd.FreeOnTerminate := True;
      //唤醒线程
      Thd.Resume;
    end;procedure Tcc.OnThradTerminated(Sender: TObject);
    begin
      //线程结束,表示异步任务完成,释放计数
      _Release;
      //唤醒COM线程
      PostThreadMessage(ThrdID,0,0,0);
    end;initialization
      TComponentFactory.Create(ComServer, Tcc,
        Class_cc, ciMultiInstance, tmApartment);
    end.
      

  9.   

    谢谢楼上的热心帮助,如果我是在调用方法里再创建执行事务线程(不是引用计数线程)的。如果用你的15秒释放引用计数的话,当调用完后也是再等待多15秒COM对象就释放了吧,但我的事务线程不一定执行完毕呢。