winspector ダウンロード 入手

winspectorがないぞ

ないので調べた→あった

winspectorとは

開発者向けの無料でダウンロードできる(はず)のソフト。
Spy++という、有償版のVisualStudioに同梱されている(らしい)ツールと同じようなことができるとか、あるいは、それ以上だとかいう噂。
あらゆるウィンドウの情報を調べられるほか、任意のウィンドウに送られてくるウィンドウメッセージを細かく調べることができる。
(多分他にも機能はあるけど、あまり使ってないから知らない)
窓の社でも紹介されています。

本家が落ちてる?

本家と思しきページに繋がらなくなっているようだ。
いつからか、また、いつまで続くのかはわからないけど、ここ数日は繋がらない。
まさか、日本からのアクセスだから弾かれてる・・・とかじゃないよね。

他のサイトからでも入手できるらしい

ちゃんと検索したら出てきた。
このページ(英語)
こちらからダウンロードできます。
直リン1(US)
直リン2(RU)

とりあえず動作してますが、スパイウェアがおまけでついてきても知りません。

追記 10/04/12

直リン切れてますね。でも、上に書いてあるページをたどっていけばダウンロードできます。

あとがき

「メモ」なんて、記事の真偽に保険をかけたようなタグ名は嫌いなのですが、他に何もいいのが思い浮かばないので、仕方なくこのタグ名を使いました。(機嫌悪)

spy++を探してて疲れた・・・。
spy++も使ってみたいので、「ここから無償で入手できたよ」っていう情報があったら、コメントください。お願いします。

コンボボックス、ドロップダウンリスト

前書き(その0)

C++というタグとWindowsというタグをまとめて、VC++というタグに置き換えることにしました。

前書き(その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のこちらのページを。

あとがき

WTLによる例を主体にするつもりだったのに、先達がやってました。
この記事の存在意義が
それと、最近やっとダイアログリソースや、コントロールIDの話がわかりました。
実際に使ってみないと理解できませんね。

*1:ComboBoxExという名前で、ComboBoxとは別物

*2:項目ごとに色やフォントや背景色を変えたいなど

コモンコントロールと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は、情報が詰まった構造体へのポインタ)
ってな所ですかね。

*1:わかんないけど、流れからしてWM_NOTIFY

*2:プログレスバーは表示するだけで入力がなく、情報が一方通行だから、通知メッセージそのものがないかもしれない。

インスタンスハンドルとモジュールハンドルの違い

前書き

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をちゃんとクラス化してるみたいでした。


もう英語なんかで検索したらいくらでもヒットしそう。