Component Object Model - Microsoftが提唱する(していた)、ソフトウェア資産再利用のための技術。美しく厳格なオブジェクト指向の世界を追及していたようであるが、そのためにややこしくなって凡人はついていけなくなり、フェードアウトしつつある・・・という認識でいいのだろうか? 時代遅れなのかもしれないが、オートメーションはなんだかんだで便利だし、DirectXも大部分はCOMで実装されているみたいだし、後継の.NETもCOMベースの技術だそうなので、ちょっとやってみることにした。ヘルプや解説Webページを見るもわけがわからず、ムカつく日々を過ごしていたが、腰を据えて勉強してみると意味や意義がおぼろげに捉えられてきた。開発などはVC6で行う。 *レジストリ [#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開発に役立つフレームワーク **外部シンボル "_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]とすると、これがメソッドやプロパティの戻り値となる。これを引数リストのどこに持ってゆけばよいんだろう? http://www.microsoft.com/japan/msdn/thisweek/combasics/combasics3.asp に、最後に記述している例があった。実際に最後でやってみたらうまくいった。 文字列の戻り値は [out, retval] BSTR* ret_str などとする。 *スクリプティング [#te8c5cc1] IDispatchを備えるオブジェクトはスクリプトから簡単に操作することができる。特に、Windows2000以降はWSH(Windows Scripting Host)が標準で備わっているため、特別な準備をしなくともVBScript、JScriptが使える。 -[[WSH For Office2000:http://homepage3.nifty.com/aya_js/office2k/outlook1.htm]] **VBScript [#w2840068] ***利点 [#df43cb0f] -InputBox関数が標準で使え、パラメータのインタラクティブな入力が容易 -人口と作品が多い? ***link [#l0eb2992] -[[VBScript チュートリアル:http://tryasp.winscom.co.jp/document/vbscript/vbstutor.htm]] ***memo [#t25ce704] Wscript.sleep(1000) ' 1000[ms] 待つ WScript.Quit ' やめる msgbox("hello" & "world") ' メッセージボックスを表示 inputbox("type the param") ' パラメータ入力可能なダイアログ ' ループ Dim i For i = 1 To 10 ' any Next **JScript [#a2f4b483] ***利点 [#w5557d2e] -文法がC++に近いので、経験がある者にとっては記述しやすい ***link [#w20ccc6e] -[[fooling around with JScript:http://www.imasy.org/~hir/hir/tech/js.html]] -[[JScript - Dynamic Scripting:http://www.interq.or.jp/student/exeal/dss/ref/jscript/top.html]] -[[WSH入門:0章 JScript概要:http://www.h6.dion.ne.jp/~binary/wsh/wsh0.htm]] *変数の型 [#r53bee5b] BSTR、VARIANT、SAFEARRAYあたりが聞き慣れないがCOMによく登場する型 **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クライアントからエラーメッセージが見られることを確認した。