2012-02-15 12 views
3

私は、ADOデータベースコンポーネントを使用してSQL Serverに接続するDelphi XE2でマルチスレッドWindowsサービスアプリケーションを構築しています。私は内部スレッドの前にCoInitialize(nil);を何度も使用しましたが、この場合、私は確信している機能を持っています。このシナリオではいつCoInitialize()を呼び出す必要がありますか?

この関数はTryConnectと呼ばれ、指定された接続文字列でデータベースに接続しようとします。接続成功時にtrueまたはfalseを返します。問題は、この機能は、内側と外側の両方のメインサービススレッド外で使用されることがあり、それは

私の質問は、私はこの内のCoInitializeを呼び出す必要はありませんですCoInitializeを必要とし、独自の一時的なTADOConnectionコンポーネントを、...作成されます機能も?私がして、サービスの実行プロシージャはCoInitializeも使用しているので、サービス内からこの関数を呼び出すと干渉しますか? TryConnect関数は、メインサービススレッドから作成されたオブジェクトの内部にあります(ただし、最終的には独自のスレッドに移動されます)。同じスレッド(CoUninitialize)から2回呼び出すCoInitialize()が干渉し、このシナリオを適切に処理する方法を知る必要があります。

ここでは以下のコードだ...

//This is the service app's execute procedure 
procedure TJDRMSvr.ServiceExecute(Sender: TService); 
begin 
    try 
    CoInitialize(nil); 
    Startup; 
    try 
     while not Terminated do begin 
     DoSomeWork; 
     ServiceThread.ProcessRequests(False); 
     end; 
    finally 
     Cleanup; 
     CoUninitialize; 
    end; 
    except 
    on e: exception do begin 
     PostLog('EXCEPTION in Execute: '+e.Message); 
    end; 
    end; 
end; 

//TryConnect might be called from same service thread and another thread 
function TDBPool.TryConnect(const AConnStr: String): Bool; 
var 
    DB: TADOConnection; //Do I need CoInitialize in this function? 
begin 
    Result:= False; 
    DB:= TADOConnection.Create(nil); 
    try 
    DB.LoginPrompt:= False; 
    DB.ConnectionString:= AConnStr; 
    try 
     DB.Connected:= True; 
     Result:= True; 
    except 
     on e: exception do begin 
     end; 
    end; 
    DB.Connected:= False; 
    finally 
    DB.Free; 
    end; 
end; 

だから、本当にやっているものを明確にするために、私はこの機会かもしれません:

CoInitialize(nil); 
try 
    CoInitialize(nil); 
    try 
    //Do some ADO work 
    finally 
    CoUninitialize; 
    end; 
finally 
    CoUninitialize; 
end; 
+3

[STAs](http://msdn.microsoft.com/en-us/library/windows/desktop/ms680112(v = vs.85).aspx)の記事を読んだことがありますか? 'CoInitialize()'を使うと、STAがあることを意味します。 COMはすべてのスレッドに対して初期化されなければならず、 'CoInitialize()'/'CoUnitialize()'呼び出しのバランスをとる必要があります。私はこれがDelphiでどのように動作するのかわかりませんが、おそらくスレッド間のポインタを整列化する必要があります。 –

答えて

10

CoInitializeを、すべて単一のスレッドで呼び出されなければならどのスレッドに関係なく、または親スレッドまたは子スレッドを持つかどうかにかかわらず、COMを使用します。スレッドがCOMを使用する場合は、CoInitializeを呼び出す必要があります。

ここでの正解は「それは依存する」です。サービススレッドがCoInitializeを呼び出したことを知っているので、TryConnectがサービススレッドから呼び出された場合、再度呼び出す必要はありません。それを呼び出すことができる他のスレッドがCoInitializeを呼び出している場合、関数は呼び出しスレッドのもとで実行されるため、呼び出す必要はありません。

MSDNドキュメントは、特にこの問題(強調追加)対処:通常

を、COMライブラリは一度だけのスレッドで初期化されます。 同じスレッドでCoInitializeまたはCoInitializeExを後で呼び出すと、並行性モデルを変更しようとしない限りS_FALSEが返されます。 COMライブラリを正常に閉じるには、CoInitializeまたはCoInitializeEx(S_FALSEを返すコールを含む)への呼び出しが成功するたびに、対応するCoUninitializeの呼び出しによってバランスが取られている必要があります。ただし、CoInitializeを0(またはCOINIT_APARTMENTTHREADEDのCoInitializeEx)と呼び出すアプリケーションの最初のスレッドは、CoUninitializeを呼び出す最後のスレッドである必要があります。そうしないと、STAのCoInitializeへのその後の呼び出しは失敗し、アプリケーションは機能しません。

答えは:わからない場合はCoInitializeに電話してください。 try..finallyブロックで実行し、finallyCoUnitializeを呼び出すか、コンストラクタで初期化し、デストラクタで初期化を解除します。

+0

これはyesに変換されます。 'CoInitialize'は' try'ブロックでラップされ、それぞれの後に最後に 'CoUninitialize'する必要がある限り、複数回呼び出すことができます。 –

+0

はい、私の答えの最後の段落に記載されています。 –

4

サービススレッドは、実際には何もしていないはずです。 Service Managerの呼び出しにのみ応答する必要があります。サービスのOnExecuteまたはOnStart/OnStopは、サービスの機能を表す「MainWorkThread」のインスタンス化と実行を制御する必要があります。例については、https://stackoverflow.com/a/5748495/11225を参照してください。

メインワークスレッドは実際の作業を行い、および/または他のスレッドに委譲することができます。 COMを使用できる各スレッドは、CoInitialize/CoUninitialize呼び出しを持つ必要があります。最も簡単な方法は、スレッドの(オーバーライドされた)Executeメソッドの最も外側のtryブロックにコードを記述することです。

TDBPoolまたはCOMを使用している他のどのクラスも、CoInitializeおよびCoUninitialize呼び出しには関係しません。これらのメソッドは、COMを使用する可能性のあるすべてのスレッドで呼び出される必要があり、クラスは実行されるスレッドを知らず、またそれを知らないはずです。