为什么我不需要在COM线程中创建的线程中调用CoInitialize? [英] Why I don't need call CoInitialize in a thread created inside a COM Thread?

查看:311
本文介绍了为什么我不需要在COM线程中创建的线程中调用CoInitialize?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了学习多线程,我在COM线程( TRemoteDataModule )中创建了一个线程。



这是我的组件工厂:

  TComponentFactory.Create(ComServer,TServerConn2,Class_ServerConn2,ciMultiInstance,tmApartment); 

在Thread中,我不需要调用CoInitialize来使用 TADOQuery .Create .Open ... .Exec



我读到我需要在调用任何库函数(CoGetMalloc除外)之前初始化COM库,获取一个指向标准分配器的指针和内存分配函数。



但在这种情况下,没有CoInitialize没有给我带来任何麻烦。

这与线程模型相关吗?
在哪里可以找到此主题的解释?



UPDATE:



当我说INSIDE时,它意味着在COM方法上下文:

  interface 
type
TWorker = class(TThread);

TServerConn2 = class(TRemoteDataModule,IServerConn2)
public
procedure Method();安全报
end;


实现
procedure TServerConn2.Method();
var W:TWorker;
begin
W:= TWorkerTread.Create(Self);
end;

UPDATE 2:



用于连接数据库的 TADOConnection 当前正在COM线程上下文中创建( TThread.Create构造函数)。虽然 TADOConnection.Open TADOQuery.Create / .Open 都在 。执行



UPDATE 3 - Simulacrum



接口:

  type 
TServerConn2 = class;

TWorker = class(TThread)
private
FDB:TADOConnection;
FOwner:TServerConn2;
protected
procedure执行;覆盖;
public
构造函数Create(Owner:TServerConn2);
destructor Destroy;覆盖;
end;

TServerConn2 = class(TRemoteDataModule,IServerConn2)
ADOConnection1:TADOConnection;
procedure RemoteDataModuleCreate(Sender:TObject);
private
{private declarations}
protected
类过程UpdateRegistry(Register:Boolean; const ClassID,ProgID:string);覆盖;
procedure CheckException;安全报
public
用户,Pswd,Str:String;
Ok:Boolean;
end;实施:

 类过程TServerConn2.UpdateRegistry(Register:Boolean; const ClassID,ProgID:string); 
begin
if注册then
begin
继承UpdateRegistry(Register,ClassID,ProgID);
EnableSocketTransport(ClassID);
EnableWebTransport(ClassID);
end else
begin
DisableSocketTransport(ClassID);
DisableWebTransport(ClassID);
继承UpdateRegistry(Register,ClassID,ProgID);
end;
end;

{TWorker}

构造函数TWorker.Create(Owner:TServerConn2);
begin
inherited Create(False);
FreeOnTerminate:= True;
FDB:= TADOConnection.Create(nil);
FOwner:=所有者;
end;

destructor TWorker.Destroy;
begin
FDB.Free;
FOwner.Ok:= True;
inherited;
end;

过程TWorker.Execute;
var Qry:TADOQuery;
begin
FDB.LoginPrompt:= False;
FDB.ConnectionString:= FOwner.Str;
FDB.Open(FOwner.User,FOwner.Pswd);

Qry:= TADOQuery.Create(nil);
try
Qry.Connection:= FDB;
Qry.LockType:= ltReadOnly;
Qry.SQL.Text:='SELECT TOP 1 * FROM MyTable';
Qry.Open;
finally
Qry.Free;
end;
end;

过程TServerConn2.CheckException;
var W:TWorker;
begin
W:= TWorker.Create(Self);
while not Ok do Sleep(100);
end;

procedure TServerConn2.RemoteDataModuleCreate(Sender:TObject);
begin
User:='user';
Pswd:='pass';
Str:= ADOConnection1.ConnectionString;
end;

初始化
TComponentFactory.Create(ComServer,TServerConn2,
Class_ServerConn2,ciMultiInstance,tmApartment);
end。

UPDATE 4



错误应该发生在这里:

  function CreateADOObject(const ClassID:TGUID):IUnknown; 
var
状态:HResult;
FPUControlWord:Word;
begin
asm
FNSTCW FPUControlWord
end;
状态:= CoCreateInstance(ClassID,nil,CLSCTX_INPROC_SERVER或
CLSCTX_LOCAL_SERVER,IUnknown,Result);
asm
FNCLEX
FLDCW FPUControlWord
end;
if(Status = REGDB_E_CLASSNOTREG)then
raise Exception.CreateRes(@SADOCreateError)else
OleCheck(Status);
end;

不知何故(因为 TComponentFactory CoCreateInstance 标识 TWorker TServerConn2

解决方案

以下任一或两者可能适用:



  • 在未使用COM初始化的线程中,所有现有的接口指针都会一直工作,直到您进行COM API调用,否则需要COM编组,然后检测未初始化的线程失败。也就是说,您的没有给我带来任何麻烦实际上可能太早了。


  • 如果进程中的任何线程调用具有COINIT_MULTITHREADED标志的CoInitialize [Ex],那么不仅会初始化当前线程作为多线程公寓的成员,但它也说,任何从未调用CoInitialize [Ex]的线程也是多线程公寓的一部分。 - 所谓的隐含MTA的东西



  • In order to learn multithreading, I've created a thread inside a COM Thread (TRemoteDataModule).

    This is my Component Factory:

    TComponentFactory.Create(ComServer, TServerConn2, Class_ServerConn2, ciMultiInstance, tmApartment);
    

    Inside the Thread, I didn't needed to Call CoInitialize to use TADOQuery.Create, .Open... .Exec

    I read that I need to initialize the COM library on a thread before you call any of the library functions except CoGetMalloc, to get a pointer to the standard allocator, and the memory allocation functions.

    But in this case, the absence of CoInitialize didn't brought me any trouble.
    Is this related with Thread Model? Where can I Find the explanation for this subject?

    UPDATE:

    When I say INSIDE, it means inside the COM method context:

    interface
    type
      TWorker = class(TThread); 
    
      TServerConn2 = class(TRemoteDataModule, IServerConn2)
      public 
        procedure Method(); safecall;
      end;
    
    
    implementation 
      procedure TServerConn2.Method(); 
      var W: TWorker;
      begin
        W := TWorkerTread.Create(Self);
      end;
    

    UPDATE 2:

    The TADOConnection used to connect to database are currently being created in the COM Thread context (TThread.Create constructor). Although, TADOConnection.Open and TADOQuery.Create/.Open are both being performed inside TThread.Execute .

    UPDATE 3 - Simulacrum

    Interface:

    type
      TServerConn2 = class;
    
      TWorker = class(TThread)
      private
        FDB: TADOConnection;
        FOwner: TServerConn2;
      protected
        procedure Execute; override;
      public
        constructor Create(Owner: TServerConn2);
        destructor Destroy; override;
      end;
    
      TServerConn2 = class(TRemoteDataModule, IServerConn2)
        ADOConnection1: TADOConnection;
        procedure RemoteDataModuleCreate(Sender: TObject);
      private
        { Private declarations }
      protected
        class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID: string); override;
        procedure CheckException; safecall;
      public
        User, Pswd, Str: String;
        Ok: Boolean;
      end;
    

    Implementation:

    class procedure TServerConn2.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;
    
    { TWorker }
    
    constructor TWorker.Create(Owner: TServerConn2);
    begin
      inherited Create(False);
      FreeOnTerminate := True;
      FDB := TADOConnection.Create(nil);
      FOwner := Owner;
    end;
    
    destructor TWorker.Destroy;
    begin
      FDB.Free;
      FOwner.Ok := True;
      inherited;
    end;
    
    procedure TWorker.Execute;
    var Qry: TADOQuery;
    begin
      FDB.LoginPrompt := False;
      FDB.ConnectionString := FOwner.Str;
      FDB.Open(FOwner.User, FOwner.Pswd);
    
      Qry := TADOQuery.Create(nil);
      try
        Qry.Connection := FDB;
        Qry.LockType := ltReadOnly;
        Qry.SQL.Text := 'SELECT TOP 1 * FROM MyTable';
        Qry.Open;
      finally
        Qry.Free;
      end;
    end;
    
    procedure TServerConn2.CheckException;
    var W: TWorker;
    begin
      W := TWorker.Create(Self);
      while not Ok do Sleep(100);
    end;
    
    procedure TServerConn2.RemoteDataModuleCreate(Sender: TObject);
    begin
      User := 'user';
      Pswd := 'pass';
      Str := ADOConnection1.ConnectionString;
    end;
    
    initialization
      TComponentFactory.Create(ComServer, TServerConn2,
        Class_ServerConn2, ciMultiInstance, tmApartment);
    end.
    

    UPDATE 4

    The error should happen here:

    function CreateADOObject(const ClassID: TGUID): IUnknown;
    var
      Status: HResult;
      FPUControlWord: Word;
    begin
      asm
        FNSTCW  FPUControlWord
      end;
      Status := CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
        CLSCTX_LOCAL_SERVER, IUnknown, Result);
      asm
        FNCLEX
        FLDCW FPUControlWord
      end;
      if (Status = REGDB_E_CLASSNOTREG) then
        raise Exception.CreateRes(@SADOCreateError) else
        OleCheck(Status);
    end;
    

    By somehow (because of TComponentFactory maybe?) CoCreateInstance identifies that TWorker is in the same context than TServerConn2 and don't raise errors?

    解决方案

    Either or both of the following might apply:

    1. On a thread not initialized with COM all existing interface pointers keep working until you make a COM API call or otherwise require COM marshalling which then fails detecting an uninitialized thread. That is, your "didn't brought me any trouble" might actually be too early to say.

    2. If any thread in the process calls Co­Initialize­[Ex] with the COINIT_MULTI­THREADED flag, then that not only initializes the current thread as a member of the multi-threaded apartment, but it also says, "Any thread which has never called Co­Initialize­[Ex] is also part of the multi-threaded apartment." - so called impicit MTA thing

    这篇关于为什么我不需要在COM线程中创建的线程中调用CoInitialize?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆