winspector ダウンロード 入手
winspectorがないぞ
ないので調べた→あった
winspectorとは
開発者向けの無料でダウンロードできる(はず)のソフト。
Spy++という、有償版のVisualStudioに同梱されている(らしい)ツールと同じようなことができるとか、あるいは、それ以上だとかいう噂。
あらゆるウィンドウの情報を調べられるほか、任意のウィンドウに送られてくるウィンドウメッセージを細かく調べることができる。
(多分他にも機能はあるけど、あまり使ってないから知らない)
窓の社でも紹介されています。
本家が落ちてる?
本家と思しきページに繋がらなくなっているようだ。
いつからか、また、いつまで続くのかはわからないけど、ここ数日は繋がらない。
まさか、日本からのアクセスだから弾かれてる・・・とかじゃないよね。
コンボボックス、ドロップダウンリスト
前書き(その1)
WTLでコンボボックス(ComboBox)を使ってみました。
そこでわかったことや、参考にさせて
もらったサイトをまとめます。
コンボボックスとは
Windowsユーザーじゃなくても、こういうのを使ったことはあると思います。(以下の写真)
複数の選択肢から1つだけを選択させるやつですね。
コンボボックスの種類
コンボボックスにはいくつかの種類があります。
- 写真の一番上の"山手線"の部分を編集できるタイプ/編集できないタイプ
- スクロールバーを表示するタイプ/スクロール機能がないタイプ
- 自動でスクロールしてくれるタイプ/してくれないタイプ
- 拡張コンボボックス(フォント,アイコンなどの拡張。*1)
ドロップダウンリストとは
どうやら、コンボボックスのことをドロップダウンリストと呼ぶことがあるらしいです。
そのように呼んだ場合は、編集不可能なタイプのことを指すらしいです。
ただしソースはウィキペディア。略してTSW。
通知メッセージ
コンボボックスに入力があった場合、親ウィンドウにWM_COMMANDが送られます。
WPARAM,LPARAMの情報をもとに、親ウィンドウが処理します。
どのような入力が発生したかは、WPARAMの上位ワードを見ます。
通知メッセージの一覧は、こちらのページにありました。
標準 Windows API - コンボボックス
CBN_SELENDOK、CBN_SELENDCANCELはわかりにくいかもしれません。
うまく説明できないので、実際に試してみてください。
コンボボックスを使ってみる(WinAPIでゴリゴリ)
標準 Windows API - コンボボックスや、VC++の使い方 - コンボボックスコントロールが参考になりそうです。
どちらのサイトも結構お世話になってます。
コンボボックスを使ってみる(WTL)
こちらも、人様のページのリンクで済ませさせていただきます。
ATL-WTL コンボボックス
こちらはダイアログ内にコンボボックスを表示していますが、ダイアログリソースを使わないで作るときは
// あるウィンドウのOnCreate(WM_CREATEメッセージハンドラ)の最後にでも書いておく // ▼ボタンを押したときの、最大の大きさ // この大きさにおさまらない場合は、スクロールバーがつく RECT rcCmbBox = { 100, 100, 300, 300, }; DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWNLIST | WS_VSCROLL | CBS_NOINTEGRALHEIGHT; m_combo.Create( m_hWnd, &rcCmbBox, NULL, dwStyle); m_combo.AddString( _T("山手線")); // // 中略 // // 0番を選択する。これがないと…コメントアウトして試してみるといい。 m_combo.SetCurSel( 0);
とします。
コンボボックスの大きさ(フォント)を変更する
コンボボックスに限らず、コントロールのフォントを変更するには、WM_SETFONTメッセージを使います。
(WTLならCComboBox::SetFont()みたいなのがある。多分MFCでも。)
もっと凝った演出(*2)をしたいときは、オーナードローやカスタムドローを行わなきゃいけないらしいです。
WinAPI式
// hCtrlは、コンボボックスやその他コントロールのウィンドウハンドルとします。 // フォントの設定。面倒くさかったら、これをコピペしてもいいよ。 // 後ろから2番目の引数は、フォントの幅(第2引数との関連は不明) // DEFAULT_PITCH(既定)/FIXED_PITCH(固定幅)/VARIABLE_PITCH(可変幅) の中から1つ。 // // 注意: hFontは、コントロールが破棄されるまでDeleteObjectしないでください。 HFONT hFont = CreateFont( 12, // 高さ(というか大きさ) 0, // 平均文字幅 0, // 角度 0, // 角度 FW_REGULAR, // 太さ 0, // 斜体(1で有効) 0, // 下線(1で有効) 0, // 打ち消し線(1で有効) SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, VARIABLE_PITCH | FF_DONTCARE, // 幅 NULL // フォント名 ); if(hFont == NULL) { // CreateFont()失敗 } // コントロールのフォントを設定する SendMessage( hCtrl, WM_SETFONT, (WPARAM)hFont, 1);
WTL式
WinAPI式とほとんど同じです。
SendMessageしないで、wndCmbBox.SetFont( hFont);ってすればOK。
WTL::CFontというHFONTをクラス化したものがありますが、むやみに使うと、
CFontのデストラクタが、いつの間にかにDeleteObjectしてしまうので注意。
コンボボックスを破棄してから、DeleteObjectしてください。
参考
CreateFontの詳細はMSDNのこちらのページを。
コモンコントロールとWM_NOTIFYとWM_COMMAND
通知メッセージのくせにWM_COMMANDで届く
WM_COMMANDと言えばメニューバーが操作されたときに届くメッセージ。
だけどそれだけじゃない。
なんと、コンボボックスが操作されたとき、通知メッセージがWM_COMMANDで届く。
(コンボボックスはコモンコントロールの一種。他のコモンコントロールの通知メッセージは、普通はWM_NOTIFYで届く。)
MSDNで適当に調べてみたので、そのまとめ。
コモンコントロールの一覧と、通知メッセージ
普通はWM_NOTIFYで通知メッセージが届きます。
また、WM_COMMAND/WM_NOTIFY両方とも使うやつもあるみたいです。
C=WM_COMMAND, N=WM_NOTIFY
コモンコントロール | 通知メッセージ |
ComboBox | C |
AnimationCtrl | C |
RichEditCtrl | C/N |
ListView | N |
TreeView | N |
HeaderCtrl | N |
ToolBar | N |
TabCtrl | N |
ToolTip | N |
TrackBar | N |
UpDownCtrl | N |
ReBarCtrl | N |
DragListBox | 不明(*1) |
Progress | ないかもしれない(*2) |
StatusBar | ないかもしれない |
HotKeyCtrl | 不明 |
僕自身、GUIの経験があまりにも浅いので、使ったことのないものがほとんどです。
ひょっとしたら大きな勘違いをしてるかもしれないですが、指摘していただけるとうれしいです。
あとがき
MSDNって文書の形式に一貫性がないですね。
同じコモンコントロールにしても、タイトルが"List-View"とか"Combo Box"とか、
"TreeView"なんてなってて検索しにくい。
中身(構成)も結構ばらばら。ページが分けてあったり、分けてなかったり。
サンプルはあったりなかったり。(あるだけでも、ありがたいんですけどね。)
追記
10/04/02
WM_COMMANDとWM_NOTIFYの違いには歴史的な理由があるかもしれません。
歴史的な理由っていうとすごい感じですが、つまり負の遺産ですね。
こちらのページの最初の方を参照。
VC++テクニック:豆知識-WM_NOTIFYとOn_Notify
WM_COMMANDには、(負の遺産により)あまり多くの情報を渡すことができない。
だからWM_NOTIFYが作られた。(WM_NOTIFYのlParamは、情報が詰まった構造体へのポインタ)
ってな所ですかね。
インスタンスハンドルとモジュールハンドルの違い
前書き
HINSTANCEとHMODULEの違いについて、また、使い分けについて、調べたことをまとめようと思います。
違いは何なのか
歴史的な違い
HINSTANCE と HMODULE の違い こちらをご覧ください。(タイトルがまんまかぶってますね)
2行でまとめると、歴史的には違うものだったが、
今となっては同じで、モジュールの先頭アドレスらしいです。
定義の違い
windef.hに
typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */
って書いてありました。
つまり、現在では、事実上型としての違いは何もありません。
インスタンスハンドルは、GetWindowLongで調べていいのか?
実はこっちが本題です。
別にGetWindowLongでいいのですが、それだとうまくいかないことがあるかもしれません。
前回の記事「ウィンドウハンドルから実行ファイル名を取得する」の、モジュールハンドルを取得する部分に関して。
// モジュールハンドルを得る // EnumProcessModule式 HMODULE hModule = NULL; DWORD gomi = 0; if(!EnumProcessModules( hProcess, &hModule, sizeof(HMODULE), &gomi)) { dwErr = GetLastError(); MessageBox( NULL, _T("EnumProcessModules"), NULL, NULL); }
と書きましたが、これは
// GetWindowLong式 HMODULE hModule = (HMODULE)GetWindowLong( hWnd, GWL_HINSTANCE); // (GetWindowLongPtrでも同じ)
と書き換えても、一応動きます。しかしこれだと一部のウィンドウではうまくいきません。
前回の記事のコードをGetWindowLong式に書き換えて実行した場合、
実行ファイル名が"ieframe.dll"となってしまいました。(本当は"iexplorer.exe")
ieframe.dllがIEのウィンドウをCreateWindowするのでしょう。
だから、GetWindowLongは、ieframe.dllのモジュールハンドル(DLLが読み込まれたアドレス)を返したのです。
参考インスタンスハンドルとモジュールハンドルって同じ用に扱ってもいい?
GetWindowLongとEnumProcessModules
ウィンドウハンドルからインスタンスハンドルを取得する方法としてはGetWindowLong/GetWindowLongPtrがあるわけですが、これで得たインスタンスハンドルではどうしてもうまくいかない場合、上で書いたような現象が起きているかもしれません。
そのような場合は以下のものを使ってみると、動くかもしれません。
// // GetWindowLongやGetWindowLongPtrがうまくいかないときに使ってみると // いいことがあるかもしれない。 // // ウィンドウハンドルから実行ファイルのインスタンスハンドルを取得する // 戻り値: 成功 インスタンスハンドル / 失敗 NULL // HINSTANCE GetInstanceHandle( HWND hWnd) // 対象のHWND { // プロセスID DWORD processID = NULL; GetWindowThreadProcessId( hWnd, &processID); // プロセスハンドル HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); if(!hProcess) return NULL; // モジュールハンドル HMODULE hModule = NULL; // HINSTANCE ≒ HMODULE DWORD dummy = 0; BOOL bResult = EnumProcessModules( hProcess, &hModule, sizeof(HMODULE), &dummy); CloseHandle( hProcess); if(!bResult) return NULL; return hModule; }
ウィンドウハンドルから実行ファイル名を取得する
前書き
昨日書いたのですが、間違って消してしまったのでもう一度投稿します。
ソースは残っているのですが、そのほかに何を書いたのかは忘れました。
グーグルキャッシュにも残ってなかった。IEのキャッシュも更新しちゃった。
なので書き直します。
ソース
// // ウィンドウハンドルから、実行ファイル名を調べる。 // destのサイズが足りなくてもエラーにはならない。 // inline bool GetExeFileName( // 戻り値: 成功 true HWND hWnd, // 対象のHWND TCHAR *dest, // ファイル名の出力先(サイズはMAX_PATH+1を推奨) size_t size_including_null) // destのサイズ(NULLを含む) { // プロセスID DWORD processID = NULL; GetWindowThreadProcessId( hWnd, &processID); // プロセスハンドル HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); if(!hProcess) return false; // モジュールハンドル HMODULE hModule = NULL; DWORD dummy = 0; if(!EnumProcessModules( hProcess, &hModule, sizeof(HMODULE), &dummy)) return false; // ファイル名(フルパス) memset( dest, 0, size_including_null); if(!GetModuleFileNameEx( hProcess, hModule, dest, size_including_null)) return false; CloseHandle( hProcess); // hModuleは自分で閉じちゃいけない return true; }
注意
- EnumProcessModulesは内部でスナップショットがどうのこうのなので、hModuleはCloseHandleしてはいけません。
- 自分以外のプロセスに対してGetModuleFileNameは使えません。GetModuleFileNameExを使いましょう。
プロセスIDからウィンドウハンドルを取得する
前書き
「プロセスIDならわかるけど、ウィンドウハンドルはわからないなー」なんていう状況、よくありますよね?
僕は先ほど初めてそのような状況に出くわしましたけど。
流れ
トップレベルウィンドウを列挙
各ウィンドウのプロセスIDを調べる
目当てのプロセスIDが見つかったら、そのウィンドウのウィンドウハンドルを返す
ソース
// // プロセスIDからウィンドウハンドルを取得する。 // トップレベルウィンドウを列挙して、プロセスIDが一致するやつのHWNDを返す。 // 同じプロセスIDでトップレベルウィンドウが複数個あった場合は // どうなっても知らない。 // HWND GetWindowHandle( // 戻り値: 成功 望みのHWND / 失敗 NULL const DWORD TargetID) // プロセスID { HWND hWnd = GetTopWindow(NULL); do { if(GetWindowLong( hWnd, GWL_HWNDPARENT) != 0 || !IsWindowVisible( hWnd)) continue; DWORD ProcessID; GetWindowThreadProcessId( hWnd, &ProcessID); if(TargetID == ProcessID) return hWnd; } while((hWnd = GetNextWindow( hWnd, GW_HWNDNEXT)) != NULL); return NULL; }
注意
ただし、トップレベルウィンドウ(親ウィンドウがない普通の窓)が、複数あるようなアプリケーションでは、Zオーダーが上の窓(より前面にある窓)が返されます。
なのでIEのような、1つのプロセスで複数のウィンドウが開くアプリケーションに対して実行する場合は要注意。
あとがき
10/04/01追記
教えて!gooの質問にも載ってました。
C/C++でフォルダ(ディレクトリ)のサイズを取得する
VC++でフォルダ(ディレクトリ)のサイズを取得する例って、検索してもひっかからないんですよね。需要ないのかな。
エラーチェックは中途半端なので、安心して使えませんけど。
"C:\System Volume Information"など、特別なアクセス権が必要なフォルダが含まれていると失敗します。
#include <windows.h> #include <tchar.h> #include <stdio.h> typedef unsigned __int64 __uint64; // DWORD2つからQWORDを作る inline __uint64 MakeQWord( DWORD hi, DWORD low) { return ((__uint64)hi << 32) | low; } // WIN32_FIND_DATAの情報から、ファイルなのかディレクトリなのかを識別する // GetDirSize関数で使うだけ bool IsDirectory( WIN32_FIND_DATA *FindData) { return FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; } // フォルダのサイズを再帰的に調べる。 // アクセス権の関係などで、1つでも失敗したら中止して戻ってくる。 // 戻り値 成功 0 / 失敗 0以外 int GetDirSize( LPCTSTR path, __uint64 *lpSize) { int res = 1; *lpSize = 0; TCHAR DirSpec[MAX_PATH+1]; // _stprintf_p( DirSpec, MAX_PATH+1, _T("%s\\*"), path); _stprintf( DirSpec, _T("%s\\*"), path); // BCC用 WIN32_FIND_DATA FindData; HANDLE hFind = FindFirstFile( DirSpec, &FindData); if(hFind == INVALID_HANDLE_VALUE) goto Error; do { __uint64 size = 0; if(IsDirectory( &FindData)) { // ディレクトリ。「.」「..」は無視して、再帰。 if(!_tcscmp( _T("."), FindData.cFileName) || !_tcscmp( _T(".."), FindData.cFileName)) continue; TCHAR buf[MAX_PATH+1]; // _stprintf_p( buf, MAX_PATH+1, _T("%s%s\\"), path, FindData.cFileName); _stprintf( buf, _T("%s%s\\"), path, FindData.cFileName); // BCC用 if(GetDirSize( buf, &size)) goto Error; *lpSize += size; } else { // ファイル *lpSize += MakeQWord( FindData.nFileSizeHigh, FindData.nFileSizeLow); } // デバッグ用 _tprintf( _T("%s, %llu\n"), FindData.cFileName, MakeQWord( FindData.nFileSizeHigh, FindData.nFileSizeLow)); } while(FindNextFile(hFind, &FindData)); if(GetLastError() != ERROR_NO_MORE_FILES) goto Error; // 成功 res = 0; Error: FindClose(hFind); return res; } // example int main() { __uint64 size; if(GetDirSize(_T(".\\"), &size)) printf( "はい失敗\n"); printf( "%llu\n", size); return 0; }
参考
後から調べたら、同じようなコードを見つけました。見つけてしまいました。
その1
フォルダ内のファイルを再帰的に「列挙する」だけで、「サイズを調べる」わけじゃないですけど。
http://shoppers-jp.com/tech/sdk017.html
http://shoppers-jp.com/tech/sdk006.html
その2
http://www.geocities.jp/dolanpura/free/index.htm
こちらの方は、WIN32_FIND_DATAをちゃんとクラス化してるみたいでした。
もう英語なんかで検索したらいくらでもヒットしそう。