最近の記事
- 9/1 - 将棋始めました
- 5/16 - サーバー引っ越し
- 4/24 - 優先順位
- 3/17 - vbNullString と 空文字列 ""
- 3/15 - InputBox 関数の戻り値
- 3/13 - 紙と Excel と VBA
- 3/9 - 未だに Visual Basic 6
- 11/14 - ぼて閉鎖
- 11/9 - 関数オブジェクトの呼び出し
- 9/7 - メソッドとしての関数オブジェクト
Entering Passive Mode
Manager ができたので、これを使ってダイアログボックスに一覧表示するのだが、
プラグインには ISaver2 を実装していないものも存在する。
ISaver2 を実装している場合は Name プロパティを使えば良いが、
ISaver しか実装していない場合は、クラスの簡易名を使うことにしよう。
簡易名とは、名前空間を含まないクラスの名前部分のみを示す。
例えば、HelloSaver は ISaver2 を実装していないため、
クラス名である「HelloSaver」を名前として表示することになる。
設定ダイアログは Application::ConfigureProc で処理されているので、
WM_INITDIALOG メッセージを拾ってコンボボックスに設定する。
現在の Application はアンマネージクラスなのだが、
Manager クラスはマネージクラスだ。
これを ConfigureProc メソッド内で使用すると、
ConfigureProc メソッド全体がマネージコードになってしまう。
そこで、コンボボックスにロードする部分を、
LoadPlugins という名前のヘルパメソッドに分離する。
========== Application.hpp ==========
……
class Application {
……
private:
void LoadPlugins(HWND combobox);
……
};
……
========== Application.hpp ==========
ソースファイルでは、プラグマ managed と unmanaged を使うことで、
部分的にマネージコードを使うことが可能だ。
========== Application.cpp ==========
#include "config.hpp" #include "Application.hpp" #include "Dispatcher.hpp" #include "Manager.hpp" #include "resid.hpp" #using#using #using "Loafer.ScreenSaver.dll" #pragma unmanaged namespace Loafer { namespace ScreenSaver { namespace Host { …… BOOL Application::ConfigureProc( HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: try { // プラグイン一覧をロード LoadPlugins(GetDlgItem(hdlg, IDC_PLUGINS)); } catch (...) { // 例外は握りつぶす // 上と同じだが、ダイアログの戻り値はプロセスの終了コードになるので // 異常を示す 0 以外を返しておく EndDialog(hdlg, 1); } break; case WM_COMMAND: …… } // ここから C++/CLI #pragma managed using namespace System; using namespace Loafer::ScreenSaver; using namespace System::Runtime::InteropServices; void Application::LoadPlugins(HWND combobox) { Manager ^manager = gcnew Manager(); for each (Type ^type in manager) { String ^name; // ISaver2 を実装してるなら名前を // 実装してなければ簡易クラス名を使う if (ISaver2::typeid->IsAssignableFrom(type)) { Object ^instance = Activator::CreateInstance(type); ISaver2 ^saver = safe_cast (instance); name = saver->Name; } else { name = type->Name; } // System::String から char * に IntPtr ptr = Marshal::StringToCoTaskMemAnsi(name); try { SendMessage(combobox, CB_ADDSTRING, 0, (LPARAM)ptr.ToPointer()); } finally { Marshal::FreeCoTaskMem(ptr); } } } }}} // Loafer::ScreenSaver::Host
========== end of Application.cpp ==========
LoadPlugins() で、コンボボックスへの追加を行っている。
まず、Manager を作成し、for each 構文で ISaver 実装型を列挙。
Manager クラスは、型をインデックス順に列挙するので、
for each を使っても特に問題はない。
Type の IsAssignableFrom() を使って ISaver2 を実装しているか調べ、
実装されていれば、Activator::CreateInstance() でインスタンスを作成し、
ISaver にキャストした上で Name プロパティを取得する。
実装されていなければ、Type の Name プロパティで簡易名を取得する。
コンボボックスに項目を追加するためには、
CB_ADDSTRING メッセージを送って、一覧の末尾に文字列を追加すれば良いが、
このメッセージは、C 言語の文字列(char * または wchar_t *)を受け取る。
System::String から char * に変換するためには、
Marshal::StringToCoTaskMemAnsi() を使えば良い。
このメソッドは、COM の標準メモリアロケータを使ってメモリを確保し、
System::String を ACP 文字列に変換してコピーしたものを返す。
(システムの ANSI コードページ。日本語環境では Shift_JIS)
文字列を使い終わったら、忘れずに Marshal::FreeCoTaskMem() で解放すること。
.Net のメソッドなので、戻り値は IntPtr だが、
ToPointer() から char * にキャストすることができる。
SendMessage を使う場合、キャスト式が複雑になるので、
少々手抜きで旧スタイルのキャストで済ますことにした。
これで、一覧が得られるようになった。
コメント (0 件)