アウトプロセス サーバー実装ヘルパー
アウトプロセス サーバーによって呼び出すことができる 4 つのヘルパー関数を使用して、サーバー コードを記述するジョブを簡略化できます。 通常、COM クライアントと COM インプロセス サーバーでは呼び出されません。 これらの関数は、サーバーに複数のアパートメントまたは複数のクラス オブジェクトがある場合に、サーバーのアクティブ化における競合状態を防ぐために設計されています。 ただし、シングル スレッドおよび単一クラス オブジェクト サーバーにも簡単に使用できます。 関数は次のとおりです。
- CoAddRefServerProcessの
- CoReleaseServerProcessの
- CoSuspendClassObjectsの
- CoResumeClassObjects
適切にシャットダウンするには、COM サーバーはインスタンス化されたオブジェクト インスタンスの数と、IClassFactory::LockServer メソッドが呼び出された回数を追跡する必要があります。 両方のカウントが 0 に達した場合にのみ、サーバーをシャットダウンできます。 シングル スレッド COM サーバーでは、シャットダウンの決定は、メッセージ キューによってシリアル化された受信アクティブ化要求と調整されました。 サーバーは、最終的なオブジェクト インスタンスでリリースを受け取り、シャットダウンを決定すると、それ以上のアクティブ化要求がディスパッチされる前に、そのクラス オブジェクトを取り消します。 この時点以降にアクティブ化要求が発生した場合、COM はクラス オブジェクトが取り消されたことを認識し、Service Control Manager (SCM) にエラーを返します。これにより、ローカル サーバー プロセスの新しいインスタンスが実行されます。
ただし、アパートメント モデル サーバーでは、異なるクラス オブジェクトが異なるアパートメントに登録され、すべてのフリー スレッド サーバーで、シャットダウンの決定は、サーバーの 1 つのスレッドがシャットダウンを決定せず、サーバーの別のスレッドがクラス オブジェクトまたはオブジェクト インスタンスを渡すビジー状態になるように、複数のスレッドにわたるアクティブ化要求と調整する必要があります。 これを解決するための古典的だが面倒なアプローチの1つは、サーバーがそのクラスオブジェクトを取り消した後、そのインスタンス数を再確認し、すべてのインスタンスが解放されるまで存続することです。
サーバー ライターがこれらの種類の競合状態を処理しやすくするために、COM には 2 つの参照カウント関数が用意されています。
- CoAddRefServerProcess、プロセスごとのグローバル参照カウントがインクリメントされます。
- CoReleaseServerProcess、プロセスごとのグローバル参照カウントをデクリメントします。
プロセスごとのグローバル参照数が 0 に達すると、COM は自動的 CoSuspendClassObjectsを呼び出します。これにより、新しいアクティブ化要求が受信されなくなります。 その後、サーバーは、別のアクティブ化要求が入ってくる可能性を心配することなく、さまざまなスレッドからさまざまなクラス オブジェクトを自由に登録解除できます。 そのため、すべての新しいアクティブ化要求は、ローカル サーバー プロセスの新しいインスタンスを起動する SCM によって処理されます。
ローカル サーバー アプリケーションでこれらの関数を使用する最も簡単な方法は、fLock パラメーターが TRUE 場合に、各インスタンス オブジェクトのコンストラクターと各 IClassFactory::LockServer メソッドで、CoAddRefServerProcess を呼び出す方法です。 また、fLock パラメーターが FALSE 場合、サーバー アプリケーションは、各インスタンス オブジェクトのデストラクターおよび各 IClassFactory::LockServer メソッドで、CoReleaseServerProcessを呼び出す必要があります。
最後に、サーバー アプリケーションは、CoReleaseServerProcessからのリターン コードに注意を払う必要があります。また、0 を返す場合、サーバー アプリケーションはクリーンアップを開始する必要があります。これは、複数のスレッドを持つサーバーでは、通常、さまざまなスレッドにメッセージ ループを終了し、CoAddRefServerProcessと CoReleaseServerProcess呼び出すように通知する必要があることを意味します。 サーバー プロセスの有効期間管理機能を使用する場合は、オブジェクト インスタンスと LockServer メソッドの両方で使用する必要があります。そうしないと、サーバー アプリケーションが途中でシャットダウンされる可能性があります。
CoGetClassObject 要求が行われると、COM はサーバーに接続し、クラス オブジェクトの IClassFactory インターフェイスをマーシャリングし、クライアント プロセスに戻り、IClassFactory インターフェイスのマーシャリングを解除して、これをクライアントに返します。 この時点で、クライアントは通常、サーバー プロセスのシャットダウン 防ぐために、TRUE を使用して LockServer を呼び出します。 ただし、クラス オブジェクトがマーシャリングされてから、別のクライアントが同じサーバーに接続し、インスタンスを取得し、そのインスタンスを解放できる LockServer クライアントが呼び出されてから、最初のクライアントがシャットダウンされ、IClassFactory ポインターが切断され、最初のクライアントが高く乾燥したままになるまでの期間があります。 この競合状態を回避するために、COM は、IClassFactory インターフェイスをマーシャリングするときに、クラス オブジェクトに TRUE を持つ LockServer を暗黙的に呼び出し、クライアントが IClassFactory インターフェイスを解放するときに、FALSE を使用して LockServer を する暗黙的な 呼び出しを追加します。 そのため、LockServer 呼び出しをサーバーにリモート する必要はありません。また、lockServer のプロキシは、呼び出しを実際にリモート処理することなく、S_OKを返すだけです。
プロセス外のサーバー プロセスの初期化中に、もう 1 つのアクティブ化関連の競合状態があります。 通常、複数のクラスを登録する COM サーバーは、サポートする CLSID ごとに REGCLS_LOCAL_SERVER を使用して CoRegisterClassObject呼び出します。 すべてのクラスに対してこれを行うと、サーバーはメッセージ ループに入ります。 シングル スレッド COM サーバーの場合、サーバーがメッセージ ループに入るまで、すべてのアクティブ化要求がブロックされます。 ただし、異なるアパートメント内の異なるクラス オブジェクトを登録するアパートメント モデル サーバーと、すべてのフリー スレッド サーバーの場合、アクティブ化要求はこれより早く到着する可能性があります。 アパートメント モデル サーバーの場合、1 つのスレッドがメッセージ ループに入るとすぐにアクティブ化要求が到着する可能性があります。 フリー スレッド サーバーの場合、最初のクラス オブジェクトが登録されるとすぐにアクティブ化要求が到着する可能性があります。 アクティブ化は早期に行われる可能性があるため、サーバーの残りの部分が初期化を完了する前に、最終リリースが発生する可能性もあります (そのため、サーバーのシャットダウンが開始されます)。
このような競合状態を排除し、サーバー ライターのジョブを簡略化するには、COM に複数のクラス オブジェクトを登録するすべてのサーバーで、REGCLS_LOCAL_SERVERを使用して CoRegisterClassObject呼び出す必要があります。サーバーがサポートする CLSID ごとにREGCLS_SUSPENDEDします。 すべてのクラスが登録され、サーバー プロセスが受信アクティブ化要求を受け入れる準備ができたら、サーバーは CoResumeClassObjects1 回呼び出す必要があります。 この関数は、登録されているすべてのクラスについて SCM に通知するように COM に指示し、アクティブ化要求をサーバー プロセスに送信し始めます。 これらの関数を使用すると、次の利点があります。
- 登録されている CLSID の数に関係なく、SCM への呼び出しは 1 つだけであるため、全体的な登録時間 (そのため、サーバー アプリケーションの起動時間) が短縮されます。
- サーバーに複数のアパートメントがあり、異なる CLSID が異なるアパートメントに登録されている場合、またはサーバーがフリースレッド サーバーの場合、サーバーが CoResumeClassObjects呼び出すまでアクティブ化要求は行われず、サーバーはすべての CLSID を登録し、適切に設定してから、アクティブ化要求とシャットダウン要求の可能性を処理する必要があります。
関連トピック