アウトプロセス サーバー実装ヘルパー

アウトプロセス サーバーによって呼び出すことができる 4 つのヘルパー関数を使用して、サーバー コードを記述するジョブを簡略化できます。 通常、COM クライアントと COM インプロセス サーバーでは呼び出されません。 これらの関数は、サーバーに複数のアパートメントまたは複数のクラス オブジェクトがある場合に、サーバーのアクティブ化における競合状態を防ぐために設計されています。 ただし、シングルスレッドおよび単一クラス オブジェクト サーバーにも簡単に使用できます。 機能は次のとおりです。

適切にシャットダウンするには、COM サーバーはインスタンス化されたオブジェクト インスタンスの数と、その IClassFactory::LockServer メソッドが呼び出された回数を追跡する必要があります。 両方のカウントが 0 に達した場合にのみ、サーバーをシャットダウンできます。 シングルスレッドの COM サーバーでは、シャットダウンの決定は、メッセージ キューによってシリアル化された受信アクティベーション要求と調整されて行われていました。 サーバーは、最終オブジェクト インスタンスのリリースを受信し、シャットダウンを決定すると、それ以上のアクティベーション要求がディスパッチされる前に、そのクラス オブジェクトを取り消します。 この時点の後にアクティブ化要求が受信された場合、COM はクラス オブジェクトが取り消されたことを認識し、サービス コントロール マネージャー (SCM) にエラーを返します。これにより、ローカル サーバー プロセスの新しいインスタンスが実行されます。

ただし、異なるクラス オブジェクトが異なるアパートメントに登録されているアパートメント モデル サーバー、およびすべてのフリースレッド サーバーでは、このシャットダウンの決定は、サーバーの 1 つのスレッドが停止しないように、複数のスレッドにわたるアクティベーション要求と調整する必要があります。サーバーの別のスレッドがクラス オブジェクトまたはオブジェクト インスタンスの配布で忙しい間にシャットダウンすることを決定します。 これを解決するための古典的だが面倒なアプローチの 1 つは、クラス オブジェクトを取り消した後、サーバーにそのインスタンス数を再チェックさせて、すべてのインスタンスが解放されるまで存続させることです。

サーバー ライターがこれらの種類の競合状態を処理しやすくするために、COM には 2 つの参照カウント関数が用意されています。

  • CoAddRefServerProcess は、グローバルなプロセスごとの参照カウントをインクリメントします。
  • CoReleaseServerProcess は、グローバルなプロセスごとの参照カウントをデクリメントします。

プロセスごとのグローバル参照数が 0 に達すると、COM は自動的に CoSuspendClassObjects を呼び出します。これにより、新しいアクティブ化要求が受信されなくなります。 その後、サーバーは、別のアクティブ化要求が入ってくることを心配することなく、自由にさまざまなスレッドからさまざまなクラス オブジェクトの登録を解除できます。 今後、すべての新しいアクティブ化要求は、ローカル サーバー プロセスの新しいインスタンスを起動する SCM によって処理されます。

ローカル サーバー アプリケーションでこれらの関数を使用する最も簡単な方法は、各インスタンス オブジェクトのコンストラクターで CoAddRefServerProcess を呼び出し、fLock パラメーターが TRUE の場合は各 IClassFactory::LockServer メソッドで呼び出す方法です。 サーバー アプリケーションは、各インスタンス オブジェクトのデストラクターで CoReleaseServerProcess を呼び出し、fLock パラメーターが FALSE の場合は各 IClassFactory::LockServer メソッドでも呼び出す必要があります。

最後に、サーバー アプリケーションは CoReleaseServerProcess からのリターン コードに注意を払う必要があります。また、0 が返された場合、サーバー アプリケーションはクリーンアップを開始する必要があります。これは、複数のスレッドを持つサーバーの場合、通常、さまざまなスレッドにメッセージ ループを終了し、CoAddRefServerProcessCoReleaseServerProcess を呼び出すように通知する必要があることを意味します。 サーバー プロセスの有効期間管理機能を使用する場合は、オブジェクト インスタンスと LockServer メソッドの両方で使用する必要があります。それ以外の場合は、サーバー アプリケーションが途中でシャットダウンされる可能性があります。

CoGetClassObject 要求が行われると、COM はサーバーに接続し、クラス オブジェクトの IClassFactory インターフェイスをマーシャリングし、クライアント プロセスに戻り、IClassFactory インターフェイスのマーシャリングを解除して、これをクライアントに返します。 この時点で、クライアントは通常、サーバー プロセスのシャットダウンを防ぐために、TRUELockServer を呼び出します。 ただし、クラス オブジェクトがマーシャリングされてから、別のクライアントが同じサーバーに接続してインスタンスを取得し、そのインスタンスを解放できる LockServer をクライアントが呼び出すまでの間に時間枠があるため、サーバーはシャットダウンされ、最初のクライアントは切断された IClassFactory ポインターで高く乾燥したままになります。 この競合状態を回避するために、COM は IClassFactory インターフェイスをマーシャリングするときにクラス オブジェクトに対して TRUE を指定した LockServer への暗黙的な呼び出しを追加し、クライアントが IClassFactory インターフェイスを解放するときに FALSE を使用して LockServer を暗黙的に呼び出します。 そのため、サーバーへの LockServer 呼び出しをリモートで行う必要はありません。LockServer のプロキシは、呼び出しを実際にリモート処理せずに S_OK を返すだけです。

プロセス外のサーバー プロセスの初期化中に、もう 1 つのアクティブ化関連の競合状態があります。 複数のクラスを登録する COM サーバーは、通常、サポートする CLSID ごとに REGCLS_LOCAL_SERVER を使用して CoRegisterClassObject を呼び出します。 すべてのクラスに対してこれを行うと、サーバーはメッセージ ループに入ります。 シングルスレッド COM サーバーの場合、サーバーがメッセージ ループに入るまで、すべてのアクティブ化要求がブロックされます。 ただし、異なるアパートメントに異なるクラス オブジェクトを登録するアパートメント モデル サーバーおよびすべてのフリースレッド サーバーの場合、アクティベーション要求はこれより早く到着する可能性があります。 アパートメント モデル サーバーの場合、いずれかのスレッドがメッセージ ループに入るとすぐにアクティブ化要求が到着する可能性があります。 フリースレッドサーバーの場合、最初のクラスオブジェクトが登録されるとすぐにアクティブ化要求が到着する可能性があります。 アクティブ化はこれほど早い段階で行われる可能性があるため、サーバーの残りの部分が初期化を完了する前に最終リリースが行われる (したがって、サーバーがシャットダウンを開始する) 可能性もあります。

このような競合状態を排除し、サーバー ライターの作業を簡素化するために、COM に複数のクラス オブジェクトを登録したいサーバーは、サーバーがサポートする異なる CLSID ごとに REGCLS_LOCAL_SERVER | REGCLS_SUSPENDED を指定して CoRegisterClassObject を呼び出す必要があります。 すべてのクラスが登録され、サーバー プロセスが受信アクティブ化要求を受け入れる準備ができたら、サーバーは CoResumeClassObjects を 1 回呼び出す必要があります。 この関数は、登録されているすべてのクラスについて SCM に通知するように COM に指示し、サーバー プロセスへのアクティブ化要求の送信を開始します。 これらの関数を使用すると、次の利点があります。

  • 登録されている CLSID の数に関係なく、SCM への呼び出しは 1 回だけ行われるため、全体の登録時間 (したがってサーバー アプリケーションの起動時間) が短縮されます。
  • サーバーに複数のアパートメントがあり、異なる CLSID が異なるアパートメントに登録されている場合、またはサーバーがフリースレッド サーバーの場合、サーバーが CoResumeClassObjects を呼び出すまでアクティブ化要求は行われず、サーバーはすべての CLSID を登録し、アクティブ化要求と可能なシャットダウン要求に対処する前に適切に設定できます。

COM サーバーの責任