Component Object Model - Microsoftが提唱する(していた)、ソフトウェア資産再利用のための技術。美しく厳格なオブジェクト指向の世界を追及していたようであるが、そのためにややこしくなって凡人はついていけなくなり、フェードアウトしつつある・・・という認識でいいのだろうか? 時代遅れなのかもしれないが、オートメーションはなんだかんだで便利(see: [[Windowsでスクリプティング]])だし、DirectXも大部分はCOMで実装されているみたいだし、後継の.NETもCOMベースの技術だそうなので、ちょっとやってみることにした。ヘルプや解説Webページを見るもわけがわからず、ムカつく日々を過ごしていたが、腰を据えて勉強してみると意味や意義がおぼろげに捉えられてきた。開発などはVC6で行う。 *全体的なこと [#x5109182] **link [#sb0e8ad9] -[[やじやじ's Page:http://www.asahi-net.or.jp/~kv8s-yjm/]]より[[温COM知新:http://www.asahi-net.or.jp/~kv8s-yjm/another/yjamain.htm]] -[[COM総合研究所:http://www5.plala.or.jp/atata/]] -[[COM プログラミングの基本 (上):http://www.microsoft.com/japan/msdn/thisWeek/combasics/combasics1.asp]] [[CodeGuru:http://www.codeguru.com/]]に色々記事がある。 -[[Step by Step COM Tutorial:http://www.codeguru.com/cpp/com-tech/activex/tutorials/article.php/c5567/]] などなど。 **メモ [#ddcc61ff] -[[SDKでActiveX MFCを利用する:http://www.heppoko.gr.jp/~shimatta/blog/programs/archives/000016.html]] -ProgIDが知りたい時には、レジストリでHKEY_CLASSES_ROOT以下を見るとよい。 **virtual [#s9ee7b72] C++における class と virtual が理解のために重要な概念・・・なのだろう。 -[[何でもかんでも virtual にしてはいけない:http://ray.sakura.ne.jp/tips/virtual.html]] -[[sizeofの不思議:http://www.s34.co.jp/cpptechdoc/article/sizeof/]] -[[クラスの継承と仮想関数:http://www.ysaitoh.k.hosei.ac.jp/labor/2002/otogawa/cpp/VirtualFunction.html]] *レジストリ [#c3cc0d02] クラスやインターフェース等の登録はレジストリに記録される(なんでCOM専用のデータベースが作られなかったのだろう?)。コンポーネントは登録や削除用の関数を備えなくてはならないのだそうだ。ローカルサーバ(実行ファイル)はコマンドラインオプションによって、プロセス内サーバ(DLL)の場合はregsvr32.exeによって、これらがコールされる。 **プロセス内サーバ [#y767e95e] 実際にAppWizardからATLでプロセス内サーバを作ってやってみた。ビルドすると HKCR\CLSID\(CLSID) \Interface\(interfaceのGUID) \(ProgID バージョンあるのとないの) HKLM\SOFTWARE\Classes\CLSID\(CLSID) \Interface\(interfaceのGUID) \(ProgID バージョンあるのとないの) に登録されていた。%%regsvr32/u して登録解除しても、CLSIDを含むエントリは残っていた。%%←改めて試したら、きちんとhousekeepingできているようだった。何かしら使用中のプロセスがあったのかなあ? **ローカルサーバ [#l9097baa] 今度は実行可能サーバを作ってみた。で、ダイアログだけ作ってビルドしてみた。すると HKCR\AppID\(AppID) \(実行ファイル名) \TypeLib\(TypeLibID) HKLM\SOFTWARE\Classes\AppID\(AppID) \(実行ファイル名) \TypeLib\(TypeLibID) なるエントリができていた。これらは-RegServerオプションによって登録されたのだが、-UnregServerオプションで実行してやるとこれらのエントリは全て消えた。 *ATL [#m97810ec] COM開発に役立つフレームワーク **FinalConstructとFinalRelease [#r6221f31] FinalConstructでS_OK以外を返すと、呼出し側でオブジェクト作成の際にエラーが出る。 **外部シンボル "_main" は未解決です [#q8f1bce4] リリースビルド時にこんなエラーが出るのはなぜか?perfectな記述がすぐに見つかった。 -[[外部シンボル "_main" は未解決です:http://frog.raindrop.jp/knowledge/archives/000129.html]] -[[COM プログラミング2:http://www.hi-ho.ne.jp/babaq/comprog2.html]] **cannot open input file oaidl.idl [#a85dd070] ビルド時、いかにも必須っぽいのにファイルが無いと怒られてしまった。MFCでも似たようなことがあったのだが、これはオプションメニューからインクルード、ライブラリのディレクトリをちゃんと指定(ATL、MFCのも)してやると消えた。 *IDL [#w88c3e3a] インターフェースを宣言するファイル。 **引数の省略 [#k6d7570b] [[COMプログラミング:http://www.ops.dti.ne.jp/~allergy/com/com.html]]というページの記述通りなのだが一応メモ。 引数の宣言にoptionalを入れると、その引数は省略可能となった。私はDWORD値にoptionalを加えてみたが、省略するとゼロが入るようであり、上記ページと同様であった。 **引数の変更 [#z14f097f] シンプルオブジェクトをウィザードから追加し、またそのメソッドもウィザードで追加した状況を考える。その後に引数の数や種類を変えたいと思った時にはどうすればよいか? IDLの記述を変更し、当該オブジェクトのヘッダとソースの記述を変えればそれで良いようである。削除も同様にすればOK? **引数の種類 [#j6bb2b0d] ***[out, retval] [#m5b4cda6] [out, retval]とすると、これがメソッドやプロパティの戻り値となる。これを引数リストのどこに持ってゆけばよいんだろう? http://www.microsoft.com/japan/msdn/thisweek/combasics/combasics3.asp に、最後に記述している例があった。実際に最後でやってみたらうまくいった。 文字列の戻り値は [out, retval] BSTR* ret_str などとする。 ***[out] [#ge62476f] 値をたくさん戻したい場合、戻り値ではなくポインタや参照渡しの引数を使うのが、まあ一般的なプログラミングテクニックである。 こんな時には引数の属性に[out]を用いるわけだが、VBScriptからの使用を考えた場合、VBScriptにはVARIANTしか存在しないため、引数はVARIANTでなくてはいけないそうだ (JScriptはそもそも参照渡しできないみたい?よくわからん)。 また、その場合には空のVARIANTを渡さねばメモリリークするそうだ。[in,out]だと受け取った側(COMコンポーネント)が元のVARIANTの領域を解放するが、[out]だけだったら入ってきたVARIANTに対しては何もしないからというのが理由みたい。 -[[Fabulous Adventures In Coding : In, Out, In-Out, Make Up Your Mind Already:http://blogs.msdn.com/ericlippert/archive/2003/09/29/53117.aspx]] -[[ATLオブジェクトをVBScriptから使用したい:http://forums.belution.com/ja/vc/000/133/14s.shtml]] *変数の型 [#r53bee5b] BSTR、VARIANT、SAFEARRAYあたりが聞き慣れないがCOMによく登場する型 **link [#b60192e7] -[[ActiveXオートメーションの概要:http://homepage1.nifty.com/macbs/auto1.htm]] -[[範囲指定したCellへ配列データを出力:http://www.attain-sys.com/vc/excel/xls_setarray_exp.html]]、及び[[範囲指定したCellのデータを取得:http://www.attain-sys.com/vc/excel/xls_getarray_exp.html]] **VARIANT_BOOL [#be6601b2] 互換性の観点から、booleanの型はVARIANT_BOOL型を使うべきなのだそうだ。定数 VARIANT_TRUE と VARIANT_FALSE があらかじめ定義されているが、VC6+ATLで開発中にVARIANT_TRUEを使ったら、警告レベル4でwarningをくらった。 warning C4310: キャストによって定数値が切り捨てられました。 これはMicrosoft側に非があるようだ。回避するためには #undef VARIANT_TRUE #undef VARIANT_FALSE const short VARIANT_TRUE = -1; const short VARIANT_FALSE = 0; などとすれば良いようだ。 -[[ATL Archives -- March 1999, week 1 (#404):http://discuss.microsoft.com/SCRIPTS/WA-MSD.EXE?A2=ind9903a&L=atl&P=45954]] **SAFEARRAY [#ob5d64c3] ATLにSAFEARRAYのラッパーは無い(MFCならCOleSafeArrayがある)。なんで、APIを直接使う。 _variant_t var = integer_values_are_here; SAFEARRAY* psa = var.parray; long lb, ub; SafeArrayGetLBound(psa, 1, &lb); SafeArrayGetUBound(psa, 1, &ub); long* val; SafeArrayAccessData(psa, (void**)&val); for(long i = lb; i <= ub; i++){ // val[i] holds the value } SafeArrayUnaccessData(psa); などとするらしい。 *エラー処理 [#g7495ed3] **HRESULT [#o345e0e5] -[[HRESULT (資料ページ) - @秘密工場:http://hyons.hp.infoseek.co.jp/ref/hresult.shtml]] **IErrorInfo [#t28fa621] HRESULT以上の情報が必要な場合に用いられるインターフェース。同時にISupportErrorInfoも持ってなくてはいけないなど実装するのは面倒だがATLを使うと簡潔に記述できる。 そのためには、ATLオブジェクトの挿入の際に「ISupportErrorInfo サポート」にチェックするだけでOK。これが本当に正しいかは知らないが、既存のオブジェクトでも -実装クラスに public ISupportErrorInfo からの継承を加える(IDispatchImplの直前の行に記述した) -BEGIN_COM_MAPに ISupportErrorInfo を加える(IDispatchの直後の行に記述した) -STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid); を定義し、下記のように実装 STDMETHODIMP CMyclass::InterfaceSupportsErrorInfo(REFIID riid) { static const IID* arr[] = { &IID_IMyclass }; for (int i=0; i< sizeof(arr) / sizeof(arr[0]); i++) { if (InlineIsEqualGUID(*arr[i],riid)) return S_OK; } return S_FALSE; } を手作業で行ったらば、まあ問題なく動いているようである。ここで、Myclassというのが既存のATLオブジェクトである。他のインターフェースのメソッドでもエラー情報扱う際には、arr[]には適宜インターフェースを追加する必要がある。 エラーはCComCoClass::Errorをコールするようだ。色々種類があるが、 return Error(TEXT("error!!!")); などとすれば、WSHクライアントからエラーメッセージが見られることを確認した。