今回は、 COM 操作のところでも記述されているフォルダ選択ダイアログボックスについてです。このフォルダ選択ダイアログボックスは、コールバック関数を準備することで、初期状態で選択されているフォルダを指定できるようになるのであります。アプリケーション側で BrowseCallbackProc コールバック関数を準備し、 SHBrowseForFolder 関数呼び出し時に、 BROWSEINFO 構造体にこのコールバック関数のアドレスを指定します。
int CALLBACK BrowseCallbackProc( HWND hwnd, // ダイアログボックスのハンドル UINT uMsg, // イベントを識別する値 LPARAM lParam, // uMsg に依存する値 LPARAM lpData // アプリケーション定義値 );
uMsg パラメータに BFFM_INITIALIZED が渡されたときには、フォルダ選択ダイアログボックスが初期化されたことを示すので、このときにダイアログボックスにメッセージを送信して、選択されているフォルダを変更するようにします。
lpData パラメータには、 BROWSEINFO 構造体の lParam メンバに指定された値が渡されるので、必要な情報を格納した構造体を作成して、そのアドレスをこのパラメータに渡してしまいましょう。
ダイアログボックスで選択されているフォルダを変更するには、 SendMessage 関数を使用してダイアログボックスに BFFM_SETSELECTION メッセージを送信します。このとき、 SendMessage 関数の wParam パラメータには TRUE を、 lParam パラメータにはパス名を表す文字列のアドレスを指定します。
さて、 HSP 側からどの情報を渡せばいいのかを考えて見ましょう。必要となる情報は、言うまでもなく、初期フォルダのパス名です。これは、 HSP 側の変数にパス名を格納して、そのアドレスを指定することにします。
実は、もう1つ必要となる情報があります。それは、 SendMessage 関数(より正確には SendMessageA 関数)のアドレスです。コールバック関数側では、 API 関数アドレスの情報を取得することができないため、必要となる関数アドレスの情報は HSP 側から送ってやる必要があるのです。
ということで、以下のような構造体を考えて、そのアドレスを BROWSEINFO 構造体の lParam メンバに指定することにしましょう。
typedef struct { LPSTR pszPath; // パス名 FARPROC pfnSendMessage; // SendMessageA 関数アドレス } HSP_BROWSE_DATA;
FARPROC 型とは、 API 関数アドレスを表すデータ型として定義されているものです。 FARPROC 型では、引数の型については何も定義されていませんが、戻り値の型は int 型と定義されています。ですから、戻り値を使用する場合に、本来の関数の型が int 型以外の場合にはキャストする必要があります。今回は戻り値を使用しないので、問題ありませんが。
実際のソースコードは以下のようになります。
#include <windows.h> #include <shlobj.h> // HSPからの情報を渡すための構造体 typedef struct { LPCSTR pszPath; // パス名 FARPROC pfnSendMessage; // SendMessageA 関数アドレス } HSP_BROWSE_DATA; int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) { // lpData は HSP_BROWSE_DATA 構造体アドレス HSP_BROWSE_DATA *pData = (HSP_BROWSE_DATA *)lpData; if (uMsg == BFFM_INITIALIZED) { pData->pfnSendMessage(hwnd, BFFM_SETSELECTION, TRUE, pData->pszPath); } return 0; }
コンパイル・マシン語抽出を行ないます。前回使用したバッチファイルをそのまま使用することにしましょう。
C:\WINDOWS>d: D:\>cd hspmcn D:\hspmcn>mcnbcc brfolder D:\hspmcn>bcc32 -c -O1 brfolder.c Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland brfolder.c: 警告 W8064 brfolder.c 16: プロトタイプ宣言のない関数の呼び出し(関数 BrowseCallbackProc ) 警告 W8057 brfolder.c 19: パラメータ 'lParam' は一度も使用されない(関数 BrowseCallbackProc ) D:\hspmcn>tdump -oxCOMENT brfolder.obj brfolder.txt Turbo Dump Version 5.0.16.12 Copyright (c) 1988, 2000 Inprise Corporation D:\hspmcn>strat brfolder.txt D:\hspmcn>
今回も2つほど警告が出ています。「プロトタイプ宣言のない関数の呼び出し」の警告は、関数の型として FARPROC 型を使用しているために発生します。この型では、引数の型の定義がされませんので。もう1つの警告は前回と同じですね。
これによって作成されたファイル brfolder.txt の内容は以下のようになっています。
(…………………… 省 略 ……………………) 000F55 PUBDEF 'BrowseCallbackProc' Segment: _TEXT:0000 000F9B LEDATA Segment: _TEXT Offset: 0000 Length: 0023 0000: 55 8B EC 83 7D 0C 01 8B 45 14 75 0F FF 30 6A 01 U駆マ..畿.u..0j. 0010: 68 66 04 00 00 FF 75 08 FF 50 04 33 C0 5D C2 10 hf....u..P.3タ]ツ. 0020: 00 . 000FC5 MODE32
上のマシン語コードの部分を取り出して前回のバイナリ変換プログラムにかけると、以下のスクリプトが出力されます。
xdim fncode, 9 fncode.0 = $83ec8b55, $8b010c7d, $0f751445, $016a30ff, $00046668 fncode.5 = $0875ff00, $330450ff, $10c25dc0, $00000000
以上から、全体のスクリプトは次のようになります。今回はダイアログボックス表示部分をモジュール命令にしてみました。実行させるには lollipop モジュールも必要になります。
#include "llmod.as" #include "rrmod/com/lollipop.as" ; #include "xdim.as" ; Lollipop モジュールでも xdim が定義されている #module #deffunc BrowseFolder str mref p1, 32 ; 初期フォルダ mref stt, 64 ; stat(選択されれば 1) mref rfst, 65 ; refstr mref bmscr, 67 ; ウィンドウの BMSCR 構造体 sdim inifldr, 260 : inifldr = p1 sdim retfldr, 260 ; IMalloc インターフェース取得 getptr pm, pMalloc dllproc "SHGetMalloc", pm, 1, D_SHELL@ if (stat < 0)|(pMalloc == 0) : stt = 0 : return ; BROWSEINFO 構造体 dim binfo, 8 binfo.0 = bmscr.13 ; 親ウィンドウハンドル binfo.1 = 0 ; デスクトップフォルダをルートに sdim buf, 260 getptr binfo.2, buf sztitle = "フォルダを選択してください。" getptr binfo.3, sztitle ; 表示文字列 binfo.4 = 0x0003 ; BIF_RETURNONLYFSDIRS | BIF_DONTGOBELOWDOMAIN if inifldr != "" { ; コールバック関数のマシン語コード xdim fncode, 9 fncode.0 = $83ec8b55, $8b010c7d, $0f751445, $016a30ff, $00046668 fncode.5 = $0875ff00, $330450ff, $10c25dc0, $00000000 ; HSP_BROWSE_DATA 構造体 getptr hbdata.0, inifldr ; パス名のアドレス dll_getfunc hbdata.1, "SendMessageA", D_USER@ getptr binfo.5, fncode ; コールバック関数のアドレス getptr binfo.6, hbdata ; HSP_BROWSE_DATA 構造体のアドレス } ; フォルダ選択ダイアログボックス表示 ; SHBrowseForFolderA 関数呼び出し getptr pm, binfo dllproc "SHBrowseForFolderA", pm, 1, D_SHELL@ pidl = stat ; 選択アイテムの PIDL fret = 0 if pidl { ; PIDL からパス名取得 pm.0 = pidl ; PIDL getptr pm.1, retfldr ; パス名を格納する変数アドレス dllproc "SHGetPathFromIDListA", pm, 2, D_SHELL@ if stat : fret = 1 ; IMalloc->Free(pidl); icall pMalloc, 5, pidl, 1 } ; インターフェース解放 release pMalloc stt = fret if fret : rfst = retfldr return #global ; Windows ディレクトリを初期フォルダに BrowseFolder windir if stat { dialog "選択されたフォルダは "+refstr } else { dialog "キャンセルされました" } end