2013-08-09 4 views
3

, 나는 COM 스레드 (TRemoteDataModule) 내부 스레드를 만들었습니다. 이것은 내 구성 요소 공장입니다COM 스레드 내부에서 생성 된 스레드에서 CoInitialize를 호출 할 필요가없는 이유는 무엇입니까? 멀티 스레딩을 배우기 위해

: 스레드 내부

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

는, 나는

.Exec 내가 읽고 ... TADOQuery.Create, .Open을 사용으로 CoInitialize를 호출하는 데 필요하지 않았다 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.

그러나이 경우 CoInitialize가 없어도 아무런 문제가 발생하지 않았습니다.
는이 스레드 모델과 관련이 있습니까? 이 주제에 대한 설명은 어디에서 찾을 수 있습니까?

업데이트 : 내가 INSIDE 말할 때

, 그것은 COM 방법 컨텍스트 내에서 의미

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; 

업데이트 2 :

데이터베이스에 연결하는 데 사용되는 TADOConnection은 현재 COM 스레드 컨텍스트 (TThread.Create constructor)에서 만들어집니다. 비록 TADOConnection.OpenTADOQuery.Create/.Open 모두 TThread.Execute 내부에서 수행되고 있습니다.

UPDATE 3 - 시뮬

인터페이스 :

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; 

구현 :

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

오류는 여기에서 발생한다 : (? 때문에 TComponentFactory의 아마)

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; 

을 어떻게 든하여이 CoCreateInstanceTWorkerTServerConn2보다 같은 컨텍스트에와 오류를 발생하지 않는 것이 확인?

+0

스레드 프로 시저를 실행하기 전에? – Noseratio

+0

모르겠다. Thread Model과 관련된 뭔가가 있다고 생각한다. – EProgrammerNotFound

+0

.Net과 달리 @Noseratio는 델파이가 나에게'CoInitialize'를 호출하지 않는다! – EProgrammerNotFound

답변

4

다음 중 하나 또는 모두 적용될 수 :

  1. COM으로 초기화되지 않은 스레드에서 COM API 호출을하거나 그렇지 않으면 COM 마샬링이 필요할 때까지 모든 기존 인터페이스 포인터가 계속 작동합니다. 그러면 초기화되지 않은 thr ead.즉, "나에게 아무런 문제가 없었습니다"라는 말은 실제로 너무 일찍 말할 수 있습니다.

  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."은 - 그래서

+1

'tmpartment'로'TComponentFactory'를 생성하면 CoInitialize는'COINIT_APARTMENTTHREADED'와 함께 호출됩니다. 'COINIT_MULTITHREADED'에'tmFree' 또는'tmBoth'를 지정해야합니다. –

+0

링크가 다운 되었습니까? – EProgrammerNotFound

+0

몇 분 전에 작업했는데, 간헐적이라고 생각합니다. –

1

당신은 TComponentFactory 당신을 위해 CoInitializeCoUnInitialize 호출을 사용 아파트 스레드 만들 때 - 그것이 바로 VCL 소스 (System.Win.VCLCom.pas)에서의 :

procedure TApartmentThread.Execute; 
var 
    msg: TMsg; 
    Unk: IUnknown; 
begin 
    try 
    CoInitialize(nil); // *** HERE 
    try 
     FCreateResult := FFactory.CreateInstanceLic(FUnkOuter, nil, FIID, '', Unk); 
     FUnkOuter := nil; 
     FFactory := nil; 
     if FCreateResult = S_OK then 
     CoMarshalInterThreadInterfaceInStream(FIID, Unk, IStream(FStream)); 
     ReleaseSemaphore(FSemaphore, 1, nil); 
     if FCreateResult = S_OK then 
     while GetMessage(msg, 0, 0, 0) do 
     begin 
      DispatchMessage(msg); 
      Unk._AddRef; 
      if Unk._Release = 1 then break; 
     end; 
    finally 
     Unk := nil; 
     CoUninitialize; // ** AND HERE 
    end; 
    except 
    { No exceptions should go unhandled } 
    end; 
end; 
+0

작업자 스레드는 TApartmentThread 대신에'TThread'를 상속받습니다. 또한 Delphi 6 – EProgrammerNotFound

+0

@MatheusFreitas를 사용하고 있으며 스레드를 만드는 데 사용하는 정확한 코드를 표시하십시오. 당신은'TComponentFactory'를 사용하여 보여주었습니다 - 만약 당신이 스레드를 생성한다면, 이것은 (TComponentFactory.CreateInstance') –

+0

입니다. TComponentFactory는 델파이 IDE의 초기화에 의해 초기화 섹션에 위치합니다. TComponentFactory.Create (ComServer, TServerConn2 , Class_ServerConn2, ciMultiInstance, tmApartment);'인터페이스 이름을 선택할 때. – EProgrammerNotFound

4

데이터베이스에 연결하는 데 사용되는 TADOConnection는 현재 COM 스레드 컨텍스트 (TThread.Create 생성자)에서 생성되는 impicit MTA라고하는 것. TADOConnection.Open과 TADOQuery.Create/.Open은 모두 TThread.Execute 내에서 수행됩니다.

  1. TWorker.Create()TWorker.Execute()가 다른 스레드 컨텍스트에서 실행됩니다 : 2 개 이유로 작동하지 않습니다

. Create()TServerConn2.CheckException() (이미 미리 자체적으로 CoInitialize/Ex()을 호출했을 것입니다)을 호출하는 스레드의 컨텍스트에서 실행되지만 Execute()TThread 스레드의 컨텍스트에서 대신 실행됩니다. ADO는 아파트 스레드입니다. 즉, IGlobalInterfaceTable 인터페이스 또는 및 CoGetInterfaceAndReleaseStream() 함수를 통해 마샬링하지 않는 한 COM 인터페이스를 스레드/아파트 경계를 넘어서 사용할 수 없습니다. 당신이 ADO 인터페이스를 마샬링 한 경우에도

  • , TWorker.Execute() 자체에 CoInitialize/Ex()를 호출해야합니다. EVERY 개별 스레드는 COM 인터페이스를 액세스하기 전에 스레딩 모델을 설정하기 위해 COM을 초기화해야합니다. 스레딩 모델은 COM 등이

  • 그래서 문제에 대한 간단한 솔루션 생성 및 스레드에 걸쳐 ADO 구성 요소를 사용하지이며, 메시지 큐의 사용 여부, 인터페이스 (직접 또는 프록시를 통해)에 액세스하는 방법을 지시 전혀 경계. 대신 TADOConnectionExecute()로 이동 :

    은 아마도, 델파이는 새로운 스레드에서 당신을 위해`CoInitialize`를 호출
    constructor TWorker.Create(Owner: TServerConn2); 
    begin 
        inherited Create(False); 
        FreeOnTerminate := True; 
        FOwner := Owner; 
    end; 
    
    destructor TWorker.Destroy; 
    begin 
        FOwner.Ok := True; 
        inherited; 
    end; 
    
    procedure TWorker.Execute; 
    var 
        DB: TADOConnection; 
        Qry: TADOQuery; 
    begin 
        CoInitialize; 
        try 
        DB := TADOConnection.Create(nil); 
        try 
         DB.LoginPrompt := False; 
         DB.ConnectionString := FOwner.Str; 
         DB.Open(FOwner.User, FOwner.Pswd); 
    
         Qry := TADOQuery.Create(nil); 
         try 
         Qry.Connection := DB; 
         Qry.LockType := ltReadOnly; 
         Qry.SQL.Text := 'SELECT TOP 1 * FROM MyTable'; 
         Qry.Open; 
         finally 
         Qry.Free; 
         end; 
        finally 
         DB.Free; 
        end; 
        finally 
        CoUninitialize; 
        end; 
    end; 
    
    +0

    문제는 ** 작동 중 **입니다. 예외를 제기하지 않는 이유는 무엇입니까? – EProgrammerNotFound

    +0

    당신이 [여기] (http://blogs.msdn.com/b/oldnewthing/archive/2013/04/19/10412399.aspx)에서 볼 수 있듯이 @Roman answear와 관련이 있다고 생각합니다. – EProgrammerNotFound

    +1

    AFAIK, ADO 개체 * 마샬링하지 않고 스레드 경계를 넘어서 사용할 수 없습니다. 그러나, 나는'TADOQuery'가'CallInitialize()'에 대한 예외를 제기하지 않는다는 점에서 @RomanR에 동의해야 할 것입니다. 왜냐하면'TThread' 스레드는 ** 암시 적 MTA **를 상속 받기 때문입니다. 나는 오늘 전에 들어 본 적이 없다.) COM 프로그래밍의 경험 법칙은 임의의 스레드에서 COM 객체를 사용하기 전에 항상 ** 자신의 코드에서 명시 적으로'CoInitialize/Ex()'를 명시 적으로 호출하는 것입니다. 그렇게하면 놀랄만한 일은 없습니다. –