現在、当サイト「mozilla.org 日本語版」の和訳文書は更新されておらず、mozilla.org の原文 よりも内容が古くなっている可能性があります。ご不便をお掛けしますが、最新の情報は原文をご確認ください。



XPCOM Code FAQ

Suresh Duddi <dp@netscape.com>
Last Modified: May 25 2000


私は、人々の質問にランダムに答えたものをドキュメント化しました。 そのため、よりFAQらしく見えると思います。

目次

  1. XPCOMが保持しているグローバルオブジェクトにはどんなものがありますか。
  2. XPCOMが保持している静的なクラスにはどんなものがありますか。
  3. 最初に呼ぶべき静的クラスにおいてなにか制限がありますか。
  4. サービスマネージャ、コンポーネントマネージャ、レジストリの作られる順序はどうなっていますか。
  5. 保持されるグローバルなレジストリーはありますか。
  6. コンポーネントマネージャ対サービスマネージャ
  7. ProgID対CLSID
  8. コンポーネントのコードにブレイクポイントをセットする方法
  9. XPCOMログの生成

XPCOMが保持しているグローバルなオブジェクトにはどんなものがありますか。

    mGlobalServiceManager
    mGlobalComponentManager

XPCOMが保持している静的なクラスにはどんなものがありますか。

nsComponentManager
nsServiceManager

最初に呼ぶべき静的なクラスで何か制限がありますか。

制限はありません。静的なクラスのnsComponentManagerとnsServiceManagerから任意の関数を呼ぶことができます。XPCOMは、どちらにおいても自分自身の初期化を行うための適切な処理を行います。

Autoregistration()は、Init_XPCOM()が呼ばれた後でのみ呼ばれます。 なぜなら、DLLのSelfRegister()関数によってレジストリが要求されますが、コンポーネントマネージャにRegistryFactory()を登録するのは、Init_XPCOM()においてだけ行われるからです。

サービスマネージャ、コンポーネントマネージャとレジストリが作られる順序はどうなっていますか。

Init_XPCOM()において以下の処理が行われます。
  • まず、グローバルなサービスマネージャを作成します。
  • そして、グローバルなコンポーネントマネージャを作成し、それをグローバルなサービスマネージャにサービスとして登録します。
  • そして、レジスターファクトリ(...レジストリファクトリ...(訳注:ここの括弧内の意味不明))が、コンポーネントマネージャにレジストリファクトリを登録します。これにより新しいレジストリオブジェクトを作成することができます。
  • 難しい問題は、いつInit_XPCOM()の引金を引くかということです。nsComponentManagerとnsServiceManagerという二つの静的なオブジェクトがあります。 それらの任意の関数を最初に呼び出すことができます。 現在のところ、nsServiceManager::GetService()が最初に呼ばれています。 静的なnsServiceManagerのすべてのメンバは、NS_GetGlobalServiceManager()を使って、グローバルなサービスマネージャを取得します。 静的なnsComponentManagerのすべてのメンバは、NS_GetGlobalComponentManager()を使って、グローバルなコンポーネントマネージャを取得します。 そのため、もしInit_XPCOM()をNS_GetGlobalComponentManager()とNS_GetGlobalServiceManager()の両方から呼び出せば安全でしょう。

    保持されたグローバルなレジストリは存在しますか

    いいえ。nsIRegistryは、レジストリへの軽いアクセスを目的として設計されました。 レジストリに対するアクセスを必要とするクライアントは、コンポーネントマネージャを使って、自分用のレジストリへのアクセスオブジェクトを作成する必要があります。 これは、open()の呼び出しがnsIRegistry()によりサポートされており、もしグローバルなレジストリを保持すると、どのレジストリファイルがオープンされているかを調整するのに頭を痛めることになるからです。

    レジストリのProgIDは、component://netscape/registryになる予定です。
     

    コンポーネントマネージャ 対 サービスマネージャ

    コンポーネントマネージャは、コンポーネントを作成する唯一の方法です。 コンポーネントマネージャは、コンポーネントのインスタンスを作成するのに、常にコンポーネントのファクトリを使います。 クライアント(CreateInstance()を呼び出してコンポーネントを作成し、それを使用するコード)は、インスタンスを作成するためにコンポーネントマネージャを呼び出します。 コンポーネント(NSRegisterSelf()を実装したコード)は、自分自身を登録し、クライアントがコンポーネントのインスタンスを作成したいときに呼ばれるようにするために、コンポーネントマネージャを呼び出します。

    サービスマネージャは、シングルトンのコンポーネント、つまりNetlibのようにアプリケーション全体で一つのインスタンスしか存在しないコンポーネントを取得するのに役立ちます。 サービスマネージャは、一つのコンポーネントごとに一つ(のインスタンス)しか存在できないようにします。 そのため、サービスを取得するということが、それを作るということを意味するわけではありません。(コンポーネントマネージャでインスタンスを作成するということとは反対になります。) サービスマネージャは、便宜的なものです。なぜなら、ファクトリがインスタンスを作成する際に、すでにそれを作成している場合に、常に同じインスタンスを返すようにすることで、技術的にコンポーネントをシングルトンにすることができるからです。 サービスマネージャを使う別の使い方は、(まだ未実装なのですが)サービスのシャットダウンを行うことです。

    クライアント

    • クライアントがサービスマネージャを使う場合とコンポーネントマネージャを使う場合

    • クライアントが、インスタンスを作成しようとしているコンポーネントがシングルトンであることを知っている場合、コンポーネントマネージャではなく、サービスマネージャを呼び出す必要があります。 この場合クライアントは、コンポーネントマネージャを呼び出すことを全く心配する必要はありません。 もし最初のインスタンスがまだ存在していなければ、サービスマネージャがインスタンスの作成の面倒を見ます。
       
    • クライアントがサービスマネージャではなくコンポーネントマネージャを使う場合

    • クライアントがコンポーネントのプライベートなインスタンスを欲しい場合、コンポーネントマネージャを呼ぶ必要があります。 クライアントの視点からは、CreateInstance()を呼び出すたびに、新しいXPCOMオブジェクトがいつも作成されます。その他のことはクライアントが心配する必要のない実装の詳細部分です。
       
    • クライアントは、インスタンスをシングルトンにする必要があるとどうやって知ることができるか

    • 今のところ、クライアントは単に知る必要があります。どのコンポーネントがサービスでどのコンポーネントがそうでないかを知る方法はありません。 実際、現在(1999年3月時点)のところ、XPCOMはどのコンポーネントもサービスとしてアクセスできます。 適切な方法が見付かるまで、あるいはサービスマネージャが取り除かれるまでは、あなたが判断してください。 インスタンスからサービスを見付けるコードもありません。

      このことに対する解決策が必要です。提案をwarren@netscape.com,dp@netscape.comにメールしてください。
       

    コンポーネント
    • コンポーネントをサービスとしてだけ使えるようにできまするか。

    • いいえ。サービスマネージャという概念はクライアントのためだけのものです。

      ある意味で、コンポーネントが他のコンポーネントを必要としている場合、実際にはクライアントとして振舞うことになります。そのため、必要とされるコンポーネントに対してCreateInstance()やGetService()を実行する場合は、上記のクライアントのルールに従うことに注意してください。

      対処方法: ただし、もしコンポーネントがそのインスタンスが一つだけ存在するようにしたい場合で、かつクライアントが十分理解していてサービスマネージャだけを使ってくれるかどうかが分からない場合、ファクトリ側でシングルトンにするように実装することができます。 つまりファクトリは、最初のインスタンスの作成において、そのインスタンスを保持しておきます。続くインスタンスの作成において、新しいインスタンスを作成する代わりに、保持しているインスタンスのaddrefを呼び出し、それを返します。

         
        例えば、preferenceの処理がこれを行っています。 nsPref.cpp nsPrefFactory::CreateInstance()nsPref.cpp nsPref::GetInstance()がコードサンプルです。 この実装において、クライアントがインスタンスの取得において、nsIServiceManager::GetService()かnsIComponentManager::CreateInstance()のどちらを呼び出しても同じオブジェクトが返され、シングルトンであることが保証されています。
    • コンポーネントがサービスになるには作成時に何かするのでしょうか。

    • いいえ。もう一度いうと、サービスマネージャという概念は、クライアントのためだけのものです。
       
    • コンポーネントは、サービスであることを宣伝して、クライアントがそれをサービスの一つとして使えるようにするのでしょうか。

    • いいえ。ヘッダファイルのインタフェースの中にコメントで書く以外の方法はありません。

    ProgID 対 CLSID

    ClassIDまたはCLSIDは、コンポーネントのユニークなIDです。 これは、とても大きな数の構造体で、Windows上ではuuidgenによって作られます。 ドキュメント上では、{108d75a0-bab5-11d2-96c4-0060b0fb9956}のように文字列で表現されます。

    ProgIDは、クライアントが要求しているコンポーネントの実装に対する文字列IDです。 その表現には、URIのシンタックスが使われます。例 component://netscape/network/protocol?name=http;description=Http%20Protocol%20Handler
    簡単にいうと、ProgIDはCLSIDをより読みやすい文字列形式にしたものです。 表面的には、この定義を受け入れることは可能です。ProgIDは、クライアント側のものです。コンポーネントは、コンポーネントマネージャに、あるProgIDの実装であることを登録します。 コンポーネントは、複数のProgIDの実装として登録することができます。(まだこの機能は実装されていませんが)

    クライアント

    • CreateInstance()の呼び出しにおいて、ProgIDとCLSIDのどちらを使うべきなのでしょうか

    • クライアントは、CreateInstance()の呼び出しにおいてProgIDを使うべきです。クライアントは、コンポーネントの特定の実装(のインスタンス)を作成する向こう見ずでなければ、CLSIDについて知ることさえ、すべきではありません。
    コンポーネント
    • コンポーネントはCID(CLSID)とProgIDの両方で登録すべきでしょうか

    • そのとおりです。

    コンポーネントのコードにブレークポイントを設定する方法

    コンポーネントは、要求時に動的にロードされるため、デバッグするのは大変です。 コンポーネントをデバッグするためのいくつかのヒントを挙げます。

    Windows: VC5.0 VC6.0

    Project->Settingの"Additional DLLドロップダウンコンボボックス"で、あなたのコンポーネントライブラリをインクルードしてください。(訳注:この訳で正しいのか? 日本語版VC5.0,VC6.0ではどうなるのか?) これでブレークポイントが有効になります。
    Unix: gdb
    • あなたがコンポーネントがロードされていると確信できるまで、プログラムを実行してください。そして、Control-Cをタイプしてください。 これであなたのコンポーネントのすべてのシンボルがGDBで有効になっているはずです。ブレークポイントを設定して、アプリケーションを再起動してください。 GDBは、ブレークポイントをセットできないと文句を言って、それを一時的に無効にしたと言ってくるかもしれませんが、*.soがロードされた時に、ブレークポイントは自動的に有効になります。 - <Eric Van Der Poel>
    • はじめに"dir components"とタイプしても(あなたがdist/binディレクトリにいることを仮定しています)、スタック中のシンボルが見えるようになると思います。 - <Alec Flett>
    • XPCOM_BREAK_ON_LOAD環境変数を使用する:

    • gdb> set env XPCOM_BREAK_ON_LOAD "necko:rdf"
      gdb> r

      これにより、XPCOMがneckoまたはrdfという文字列を名前の一部に含むDLLをロードした後に、デバッガの中でブレークします。 この時点で、デバッガにDLLシンボルをロードして、ブレークポイントをセットすることができます。

      gdb> sha libnecko.so
      gdb> b nsFunctionInNecko

    Mac: Codewarrior
    適当な.xSYMファイルをデバッガで開くだけです。デバッガは、アプリケーションを実行したときに、そのライブラリをターゲットとします。 - <Simon Fraser>
    XPCOMからログを生成する:
    XPCOMはログ出力機能を提供します。ログ出力を有効にするには:
    [unix]
    setenv NSPR_LOG_MODULES nsComponentManager:5
    setenv NSPR_LOG_FILE xpcom.log

    [win]
    set NSPR_LOG_MODULES=nsComponentManager:5
    set NSPR_LOG_FILE=xpcom.log

    上記の環境変数をセットした後、アプリケーションを実行してください。XPCOMからのデバッグログは、xpcom.logというファイルです。


    XPCOMログ解析

    xpcom/doc/xpcom-log-analyze.sh は、XPCOMのログを解析して、役に立つ統計情報をHTML形式で出力するスクリプトです。 使い方はこうです:
    xpcom-log-analyze.sh < xpcom.log > xpcom-log.html
     

    このドキュメントのオリジナルはmozilla.orgにおいて英語で公布されています。
    またドキュメントの管理の言語は現在も英語です。この日本語訳は、
    利用者の利便のためにmozilla.org 和訳プロジェクトによって提供されたものです。
    フィードバックは英語で、元の著者に送って下さい。
    翻訳された文書の一覧は、現在以下のURLで見ることが出来ます。
    http://www.mozilla-japan.org/jp/td/index.html