VC++でのXPCOM作成(その2)

はじめに

 Firefox(Mozilla)などの機能をC++によって拡張することができるXPCOMの作り方を紹介する。 ここでは具体的なDLLの作成方法についてを記す。前段階で準備を行っていない場合は「その1」 を参考にすること。



プロジェクトの作成

 Win32プロジェクトとして空のDLLプロジェクトを作成する。

 Visual C++を起動し、「ファイル」メニューの「新規作成」から「プロジェクト」を選択する。 「テンプレート」は「Win32プロジェクト」を使う。ここでは「プロジェクト名」は「xxx」とし た。
 プロジェクトの作成で「OK」ボタンを押すと「Win32アプリケーションウイザード」の画面が開く。 ここで「アプリケーションの設定」を開き、「アプリケーションの種類」で「DLL」を選択する。 さらに「空のプロジェクト」にチェックを入れる。



XPCOM用IDLの作成

 VC++の「プロジェクト」メニューの「新しい項目の追加」を選択する。そして「テンプレート」から 「MIDLファイル(.idl)」を選択してファイル名を「Ixxx.idl」としてファイルを作成する。

 新しく作成したIDLファイルには初めから2行あるが、それを削除してファイル内容を以下のようにす る。

#include "nsISupports.idl"

[scriptable, uuid(2E02A2B5-AD6F-4688-A75B-F642838BCCA2)]
interface Ixxx : nsISupports
{
	long	Add(in long nData1, in long nData2);
	AString	AddString(in AString strData,in long nData);
};

※太字で強調している部分はVC++に含まれるGuidGen.exeを使って新しいGUIDを生成して利用 する。同じ値を使いまわしてはいけない!



IDLファイルビルド設定

 Visual C++標準のIDLビルドをキャンセルして、XPCOM用のIDLビルドコマンドを登録する。

 VC++の「ソリューションエクスプローラー」に新しく追加された「Ixxx.idl」を右クリックし、 現れたメニューから「プロパティ」を選択する。そして開いたダイアログの「構成」から「すべて の構成」を選択し、左側のツリーの「全般」にある「ビルドから除外」を「はい」に変更する。そし て「OK」ボタンを押して設定を保存する。


 VC++の「プロジェクト」メニューから「プロパティ」を選択する。そして「構成」から「すべての構成」 を選択する。左側のツリーから「ビルドイベント」の「ビルド前のイベント」を選択する。ここで右側の 「コマンドライン」に「makexpidl.bat $(ProjectDir)Ixxx.idl」を入力して「OK」ボタンを押して保存す る。

※「クラスビュー」を開いた状態でないと「プロジェクト」メニューに「プロパティ」が見つからない。



main.cppファイルの作成

 VC++の「プロジェクト」メニューの「新しい項目の追加」を選択する。そして「テンプレート」から 「C++ファイル(.cpp)」を選択してファイル名を「main.cpp」としてファイルを作成する。

 ここでは暫定的にファイル内容を以下のようにする。

#include "mozilla-config.h"
#include "windows.h"
#include "wchar.h"
#include "nsEmbedString.h"
#include "Ixxx.h"

#pragma	comment(lib,"odbc32.lib")
#pragma	comment(lib,"odbccp32.lib")
#pragma	comment(lib,"nspr4.lib")
#pragma	comment(lib,"plds4.lib")
#pragma	comment(lib,"plc4.lib")
#pragma	comment(lib,"xpcomglue.lib")


#define MY_COMPONENT_CONTRACTID	"@mydomain.com/xxxxxxx/Ixxx;1"
#define MY_COMPONENT_CLASSNAME	"Ixxx XPCOM"
#define MY_COMPONENT_CID		{ 0x5506ca73, 0x8f02, 0x40b9, { 0x87, 0xeb, 0xfa, 0x53, 0xc9, 0x58, 0x70, 0xfc } }

※太字で強調している部分はVC++に含まれるGuidGen.exeを使って新しいGUIDを生成して利用 する。同じ値を使いまわしてはいけない!



main.cppファイル内容の作成

 cppファイル内容の作成には「Ixxx.h」が必要だが、まだこのファイルはどこにもない。「Ixxx.h」を 作成するために一度ビルドをする。「ビルド」メニューから「ソリューションのビルド」しビルドする。 これで自動的に「Ixxx.h」が作成される。

 「main.cpp」に書かれている「#include "Ixxx.h"」の上で右クリックをして現れたメニューから 「ドキュメント "Ixxx.h"を開く」を選択し、ヘッダーファイルを開く。

 自動的に作成された「Ixxx.h」の中には以下のように「#if 0」と「#endif」で囲まれた部分がある。 この部分を選択してmain.cppに追加する。

(...省略...)

#if 0		(この行を含まず下の部分を追加)
/* Use the code below as a template for the implementation class for this interface. */

/* Header file */
class _MYCLASS_ : public Ixxx
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_IXXX

  _MYCLASS_();

private:
  ~_MYCLASS_();

protected:
  /* additional members */
};

/* Implementation file */
NS_IMPL_ISUPPORTS1(_MYCLASS_, Ixxx)

_MYCLASS_::_MYCLASS_()
{
  /* member initializers and constructor code */
}

_MYCLASS_::~_MYCLASS_()
{
  /* destructor code */
}

/* long Add (in long nData1, in long nData2); */
NS_IMETHODIMP _MYCLASS_::Add(PRInt32 nData1, PRInt32 nData2, PRInt32 *_retval)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

/* AString AddString (in AString strData, in long nData); */
NS_IMETHODIMP _MYCLASS_::AddString(const nsAString & strData, PRInt32 nData, nsAString & _retval)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

/* End of implementation class template. */
#endif		(この行を含まず上の部分までを追加)

(...省略...)


 ただし追加したときに以下の太字の部分を変更しておく

/* long Add (in long nData1, in long nData2); */
NS_IMETHODIMP _MYCLASS_::Add(PRInt32 nData1, PRInt32 nData2, PRInt32 *_retval)
{
	*_retval = nData1 + nData2;
	return	NS_OK;
}

/* AString AddString (in AString strData, in long nData); */
NS_IMETHODIMP _MYCLASS_::AddString(const nsAString & strData, PRInt32 nData, nsAString & _retval)
{
	PRUnichar	uchText[128];

	swprintf(uchText,L"%d",nData);
	_retval = uchText;
	_retval += strData;

	return	NS_OK;
}


さらに以下の内容もmain.cppに追加する。

#include "nsIGenericFactory.h"

NS_GENERIC_FACTORY_CONSTRUCTOR(_MYCLASS_)

static nsModuleComponentInfo components[] =
{
	{
		MY_COMPONENT_CLASSNAME, 
		MY_COMPONENT_CID,
		MY_COMPONENT_CONTRACTID,
		_MYCLASS_Constructor,
	}
};

NS_IMPL_NSGETMODULE("_MYCLASS_Module", components) 

※ここまでの処理で完成したmain.cppの内容はこのページ下部に表示してあります。



プロジェクトの設定

 VC++の「プロジェクト」メニューから「プロパティ」を選択する。そして「構成」から「Debug」 を選択する。左側のツリーから「C/C++」の「プリプロセッサ」を選択する。ここで右側の 「プロセッサの定義」に「;XPCOM_GLUE」を追加する(既存の文字列は消さないこと!)。
 同じように「構成」が「Release」のときの「プロセッサの定義」にも追加しておく。

 次に「構成」を「すべての構成」にして「リンカ」の「入力」にある「特定のライブラリの無視」 に「libcmtd.lib」を追加する。

※「クラスビュー」を開いた状態でないと「プロジェクト」メニューに「プロパティ」が見つからない。
※「libcmtd.lib」を除外するという方法はかなり強引で、本来なら避けるべきものだが...今回は簡 便化のためにこうした。除外しない方法は次回以降に紹介する。



XPCOMの生成

 これで「ビルド」メニューから「ソリューションのビルド」を選択してびるどをする。すると以下のファイル を得ることができる。

・xxx.dll
・Ixxx.idl
・Ixxx.xpt

 これら3つのファイルがFirefoxなどで利用するためのXPCOMコンポーネントとなる。




main.cppの内容

 上の説明が分かりずらいのでここまででできたmain.cppの全内容を以下に示す。

#include "mozilla-config.h"
#include "windows.h"
#include "wchar.h"
#include "nsEmbedString.h"
#include "Ixxx.h"

#pragma	comment(lib,"odbc32.lib")
#pragma	comment(lib,"odbccp32.lib")
#pragma	comment(lib,"nspr4.lib")
#pragma	comment(lib,"plds4.lib")
#pragma	comment(lib,"plc4.lib")
#pragma	comment(lib,"xpcomglue.lib")


#define MY_COMPONENT_CONTRACTID	"@mydomain.com/xxxxxxx/Ixxx;1"
#define MY_COMPONENT_CLASSNAME	"Ixxx XPCOM"
#define MY_COMPONENT_CID		{ 0x5506ca73, 0x8f02, 0x40b9, { 0x87, 0xeb, 0xfa, 0x53, 0xc9, 0x58, 0x70, 0xfc } }


/* Header file */
class _MYCLASS_ : public Ixxx
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_IXXX

  _MYCLASS_();

private:
  ~_MYCLASS_();

protected:
  /* additional members */
};

/* Implementation file */
NS_IMPL_ISUPPORTS1(_MYCLASS_, Ixxx)

_MYCLASS_::_MYCLASS_()
{
  /* member initializers and constructor code */
}

_MYCLASS_::~_MYCLASS_()
{
  /* destructor code */
}

/* long Add (in long nData1, in long nData2); */
NS_IMETHODIMP _MYCLASS_::Add(PRInt32 nData1, PRInt32 nData2, PRInt32 *_retval)
{
	*_retval = nData1 + nData2;
	return NS_OK;
}

/* AString AddString (in AString strData, in long nData); */
NS_IMETHODIMP _MYCLASS_::AddString(const nsAString & strData, PRInt32 nData, nsAString & _retval)
{
	PRUnichar	uchText[128];

	swprintf(uchText,L"%d",nData);
	_retval = uchText;
	_retval += strData;

	return NS_OK;
}




#include "nsIGenericFactory.h"

NS_GENERIC_FACTORY_CONSTRUCTOR(_MYCLASS_)

static nsModuleComponentInfo components[] =
{
	{
		MY_COMPONENT_CLASSNAME, 
		MY_COMPONENT_CID,
		MY_COMPONENT_CONTRACTID,
		_MYCLASS_Constructor,
	}
};

NS_IMPL_NSGETMODULE("_MYCLASS_Module", components) 

カテゴリー「VC++ TIPS」 のエントリー