Windows API (および関連 API) をC言語で使用するための Tips 集です.随時追加します. 主な更新は Blog でお知らせします.
Windows のエラーメッセージは FormatMessage() で取得できるが, 引数が多いので自由度が高い反面,使うのが面倒. そこでエラー番号だけを指定すればエラーメッセージ文字列を取得できる, 次のような関数を用意しておくと簡単に使えて便利.
#include <windows.h>
#include <tchar.h>
TCHAR *WinErrorMessage(DWORD errorCode)
{
TCHAR *message;
DWORD n = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, errorCode, LANG_USER_DEFAULT,
(TCHAR*)&message, 0, NULL);
return (n > 0) ? message : NULL;
}
この関数は,必要な文字列バッファを自分で確保するので,
不要になったら LocalFree() で解放する必要がある.
(注意:このため,メモリ不足に陥った場合,
そのメモリ不足を知らせるメッセージを取得できない可能性がある.)
この関数は Windows API を直接呼び出す場合だけでなく, C標準ライブラリ関数を呼び出した後にも (仕様として保証されているわけではないが) 事実上使用できる. (ライブラリ関数の中で Windows API を呼んでいるため.)
C標準ライブラリでエラーメッセージを取得するには strerror(errno) を使うが, これは英語だけなので,日本語表示が必要な場合には WinErrorMessage() や FormatMessage() の方が便利.また FormatMessage() の dwLanguageId 引数に言語識別子を指定すれば, 他言語のメッセージを取得できる (こっちのページにサンプルプログラムあり).
Windows の基本的なエラーメッセージは kernel32.dll のリソース (メッセージテーブル) で定義されており,Windows XP 日本語版では日本語と英語のメッセージテーブルが定義されている (SP2,SP3 で確認). その内容は,Resource Hacker などのリソース編集ツールで見ることができる.
TCHAR *fileName, *message, buf[1024];
FILE *in;
if((in = _tfopen(fileName, _T("r"))) == NULL) {
message = WinErrorMessage(GetLastError());
_stprintf(buf, _T("\"%s\" をオープンできません。\n%s"), fileName, message);
LocalFree(message);
MessageBox(NULL, buf, AppName, MB_ICONERROR);
return -1;
}
FormatMessage() が返すメッセージの一部には, 末尾に余分な空白や改行がついているものがあり, これらが邪魔になる場合がある. そこで私は,これらを削除するようにした WinErrorMessage() を使用している (こっちのページのサンプルプログラムの StringRightTrim() 参照).
ごみ箱は Windows95 以降のシェルアプリケーションの機能です. したがってごみ箱用の API は Windows API ではなく,シェル API であり, 次の3つがあります.
| API 名 | 機能 |
|---|---|
| SHFileOperation() | ファイルまたはディレクトリをごみ箱に移動する. (ごみ箱以外の機能もあり.) |
| SHEmptyRecycleBin() | ごみ箱を空にする. |
| SHQueryRecycleBin() | ごみ箱内にあるオブジェクトの個数と合計サイズを取得する. |
ここでは,SHFileOperation() を用いてファイルやディレクトリをごみ箱に送る方法を説明します. といっても,私は SHFileOperation() の動作についてまだ十分理解しているわけではないので, 色々試行錯誤しながら理解したことや注意点を書きます. 間違いや補足があればご指摘ください.
SHFileOperation() は,次の構造体を使って必要なパラメータを受け取ります.
typedef struct {
HWND hwnd;
UINT wFunc;
LPCTSTR pFrom;
LPCTSTR pTo;
FILEOP_FLAGS fFlags;
BOOL fAnyOperationsAborted;
LPVOID hNameMappings;
LPCTSTR lpszProgressTitle; // only used if FOF_SIMPLEPROGRESS
} SHFILEOPSTRUCT;
この構造体のメンバのうち,注意が必要なのは (というより,私がつまずいたのは (^^;)) 次の2つです.
<パス名1>\0<パス名2>\0 … \0<パス名N>\0\0
私がつまづいたのはこの点.マニュアルには書かれていないし,
SHFileOperation() を説明している他のサイトにも書かれていません.
(そもそも,Windows でパス名の区切りに '/'
を使おうとする人自体,非常に少ないんだろうけど.(^^;)
私は (少なくとも日本語環境では) 見やすいから Unix 流の '/'
の方が好き.)
多くの (すべての?) Windows API は,パス名の区切りとして '\\' の代わりに '/' を使っても,あるいは両者を混在させてもかまいません. しかし SHFileOperation() は Windows API ではなくシェル API であるせいか,そうはなっていないようです. この点にハマってしまいました.
パス名の区切りとして '/' を用いたファイル名を指定したところ, ごみ箱には移動されるのですが, ごみ箱の中身を表示した際の「名前」が空文字列に, 「元の場所」が "D:" になってしまいました. また,「ファイル削除の確認」ダイアログに表示されるファイル名は, 単一の '"' だけになっていました. どうやらパス名の最初の '/' 以後の部分が無視されてしまったようです. しかし,ごみ箱のディレクトリ内にあるごみ箱管理ファイル "INFO2" をダンプしてみたところ, そのようなファイルでも元のパス名がちゃんと記録されており, ごみ箱から「元に戻す」こともできました. どうやら表示だけの問題のようです.
2007/08/18(土) 追記パス名を \\?\ で始めると,Windows API でも '/' をディレクトリ区切り文字として使用できなくなるらしい. (参考:下記 外部へのリンク → Windowsパス名の落とし穴 → サービス機能の不活性化)
NULL で大丈夫です.GUI アプリケーションだけでなく,hwnd に NULL
を渡さざるを得ないコンソール・アプリケーションでも動きます.
ただしコンソール・アプリケーションの場合はなぜか ERROR_INVALID_HANDLE
がセットされますが,ごみ箱への移動はちゃんとできているようなので,
このエラーコードは無視してよさそうです.
(ついでに言うと,fFlags に FOF_NOCONFIRMATION や FOF_NOERRORUI
を指定しなければ,コンソール・アプリケーションであっても
削除確認ダイアログやエラーダイアログが表示されます.)
いやむしろ,NULL 以外の値として何を渡せばよいのか, マニュアルを見てもさっぱりわかりません. マニュアルの hwnd の説明には次のように書かれています.
「ファイル操作の状態に関する情報を表示するダイアログボックスのウインドウハンドル」(拙訳)
しかし,どんなコントロールをどのように配置すればいいのか? 全く謎です. テキトーにダミーのダイアログを作ってそのハンドルを渡してみたり, アプリケーションで別の目的に使用しているダイアログのハンドルを渡してみたりしましたが, ERROR_INVALID_HANDLE になり,ファイルはごみ箱に送られませんでした. 他のサイトのサンプルコードでは,当たり前のようにハンドルを渡しており, 疑問を持っている人はいないようです. ひょっとして,私はなにか大きな勘違いをしているのでしょうか?
現時点では「どんなダイアログのハンドル渡したらええかサッパわからんし, NULL で動くんやったら NULL でええやん. ハンドル渡したらどんなエエことがあるっちゅーねん?」 というノリでやってます.
あと,ごみ箱に移動できないファイルがあると, なぜかそのようなファイルごとに数秒程度時間がかかります. SHFILEOPSTRUCT::fFlags に FOF_NOCONFIRMATION と FOF_NOERRORUI を指定していない場合,削除確認ダイアログはすぐに表示されるものの, それに「はい」と答えると約5秒後にエラーダイアログが出ました. なぜこんなに時間がかかるんだろう?
ファイルまたはディレクトリをごみ箱に送る関数の例を次に示します.
#include <ShellAPI.h>
#include <tchar.h>
/* その他のヘッダは省略 */
/* fullPathName はフツーの '\0' 終端文字列.
* ただしフルパス名で,ディレクトリの区切りに '/' は不可.
*/
int MoveFileToRecycleBin(const TCHAR *fullPathName, HWND hwnd, FILEOP_FLAGS flags)
{
SHFILEOPSTRUCT fileOp;
const TCHAR *src = fullPathName;
TCHAR *dest;
/* ファイルのフルパス名を2つの _T('\0') で終端する必要があるので,
* fullPathName[] を fileOp.pFrom[] にコピーし,2個目の _T('\0') を追加する.
*/
fileOp.pFrom = dest = alloca(sizeof(*dest) * (_tcslen(fullPathName) + 2));
while((*dest++ = *src++) != _T('\0')) {}
*dest = _T('\0'); /* もう1個必要 */
fileOp.hwnd = hwnd;
fileOp.wFunc = FO_DELETE;
fileOp.pTo = NULL;
fileOp.fFlags = FOF_ALLOWUNDO | flags;
fileOp.fAnyOperationsAborted = FALSE;
fileOp.hNameMappings = NULL;
fileOp.lpszProgressTitle = NULL;
return SHFileOperation(&fileOp);
}
まだ調査中です.
ごみ箱フォルダのパス名は,各ドライブの <drive>:\RECYCLER\<OwnerSID> (隠しフォルダ).
<drive> はドライブ文字 (A〜Z), <OwnerSID> はユーザの SecurityID で,例えば次のような文字列.
"S-1-5-21-1234567890-123456789-123456789-1234"
したがって複数ユーザが使用している PC では, ドライブごと,ユーザごとにごみ箱フォルダが作られる (はず).
●参考ごみ箱フォルダの中には,削除されたファイルが名前を変えて入れられる. そのファイル名は次のとおり.
D<drive><number>.<元の拡張子>
ここで <drive> はドライブ文字 (小文字), <number> はファイル番号 (削除された順に1から).
例えば,E: ドライブで5番目に削除された *.txt ファイルは, De5.txt という名前になる.
ファイルを「元に戻す」と,そのファイル番号は欠番になる.
ごみ箱フォルダ内の隠しファイル INFO2 には,
ごみ箱内の削除ファイルの情報が含まれている.
INFO2 の先頭20バイトはヘッダ部.その後に,
ごみ箱内のファイルの情報が必要個数繰り返される.
(ファイルはごみ箱フォルダと同じドライブのものだけで,
「元に戻した」ものを含む.)
// INFO2 ファイル全体の構造
typedef struct {
Info2Header_t header; // ヘッダ (20バイト)
Info2FileData_t fileInfo[N]; // ファイル情報 (各800バイト)
} INFO2_t;
調べた範囲では,どのドライブのごみ箱も,ヘッダは全く同じ内容だった
(下記16進ダンプ).このため色々条件を変えて内容の変化を調べ,
各部の意味を推定するという手が使えない.orz
// ヘッダ部の16進ダンプ 00000000 05 00 00 00 00 00 00 00-00 00 00 00 20 03 00 00 00000010 00 00 00 00
よくあるパターンだが,ファイル先頭の数バイトはおそらくバージョン番号だろう. 途中の 20 03 00 00 は,sizeof(Info2FileData_t) と思われる.
// ヘッダ部の構造 (20バイト)
typedef struct {
BYTE unknown1[12]; // 不明 (最初の数バイトは INFO2 ファイルのバージョンか?)
DWORD fileInfoSize; // sizeof(Info2FileData_t) か?
BYTE unknown2[4]; // 0固定?
} Info2Header_t;
// ごみ箱内の各ファイルの情報
typedef struct {
// 削除前のフルパス名 (マルチバイト文字列).
// ・余白は '\0' で埋める.
// ・ファイルを元に戻した場合,最初の1バイトが '\0' になる.
char ansiPathName[MAX_PATH];
DWORD fileNo; // ファイル番号? (1から始まる連番)
DWORD driveNo; // ドライブ番号? (A:0,B:1,…,Z:25)
BYTE unknown[12]; // 未確認.削除時刻と属性フラグか?
// 削除前のフルパス名 (UTF-16LE 文字列).
// ・余白は L'\0' で埋める.
// ・ファイルを元に戻しても変更されない.
wchar_t unicodePathName[MAX_PATH];
} Info2FileData_t;
GetModuleFileName() の第1引数 (hModule) に NULL を渡すと, 現在のプロセスに対応する実行ファイルのフルパス名を取得できる.
#include <windows.h> #include <tchar.h> TCHAR ExeFileName[MAX_PATH]; GetModuleFileName(NULL, ExeFileName, MAX_PATH);
コントロールのクラス名は,CommCtrl.h の中で次のように定義されている.
(どうしてこんなに名前の付け方がバラバラなんだ.)
| コントロール名 | クラス名 | クラス名定義マクロ | 備考 |
|---|---|---|---|
| Animate | SysAnimate32 | ANIMATE_CLASS[AW] | |
| Button | Button | WC_BUTTON[AW] | |
| ComboBox | ComboBox | WC_COMBOBOX[AW] | |
| ComboBoxEx | ComboBoxEx32 | WC_COMBOBOXEX[AW] | |
| DateTimePicker | SysDateTimePick32 | DATETIMEPICK_CLASS[AW] | |
| Edit | Edit | WC_EDIT[AW] | |
| Header | SysHeader32 | WC_HEADER[AW] | |
| HotKey | msctls_hotkey32 | HOTKEY_CLASS[AW] | |
| IPAddress | SysIPAddress32 | WC_IPADDRESS[AW] | |
| ListBox | ListBox | WC_LISTBOX[AW] | |
| ListView | SysListView32 | WC_LISTVIEW[AW] | |
| MonthCalendar | SysMonthCal32 | MONTHCAL_CLASS[AW] | |
| NativeFont | NativeFontCtl | WC_NATIVEFONTCTL[AW] | |
| PageScroller | SysPager | WC_PAGESCROLLER[AW] | |
| RichEdit 4.1 | RICHEDIT50W | MSFTEDIT_CLASS | Msftedit.dll |
| RichEdit 2.0/3.0 | RichEdit20W | RICHEDIT_CLASS[AW] | Riched20.dll |
| Progress | msctls_progress32 | PROGRESS_CLASS[AW] | |
| Rebar | ReBarWindow32 | REBARCLASSNAME[AW] | |
| ScrollBar | ScrollBar | WC_SCROLLBAR[AW] | |
| Static | Static | WC_STATIC[AW] | |
| StatusBar | msctls_statusbar32 | STATUSCLASSNAME[AW] | |
| SysLink | SysLink | WC_LINK (Unicode のみ) | XP 以降 |
| Tab | SysTabControl32 | WC_TABCONTROL[AW] | |
| ToolBar | ToolbarWindow32 | TOOLBARCLASSNAME[AW] | |
| ToolTips | tooltips_class32 | TOOLTIPS_CLASS[AW] | |
| TrackBar | msctls_trackbar32 | TRACKBAR_CLASS[AW] | |
| TreeView | SysTreeView32 | WC_TREEVIEW[AW] | |
| UpDown | msctls_updown32 | UPDOWN_CLASS[AW] | |
| Dialog | MAKEINTATOM(0x8002) | WC_DIALOG | WinUser.h |
| TaskDialog | Vista 以降 | ||
■マニュアル
|
2006/09/21(木) 作成 2009/01/20(火) SysLink を追加 2010/02/13(土) マニュアルへのリンクを追加 |
ダイアログでは,座標の単位として画素数ではなく 「ダイアログテンプレート単位 (dialog template units,ダイアログ単位 (DLU) ともいう)」を用いる. リソースファイル (*.rc) の中のダイアログ定義データに含まれる座標はこれである. ダイアログテンプレート単位は「ダイアログベース単位 (dialog base units)」 を用いて次のように定義される.
TemplateUnitX = BaseUnitX / 4 TemplateUnitY = BaseUnitY / 8
ダイアログベース単位は, そのダイアログで使用するフォントのサイズにより次のように定義される.
BaseUnitX = <ダイアログのフォントの平均幅> BaseUnitY = <ダイアログのフォントの高さ>
したがってダイアログ (テンプレート) 単位は次のようになる.
TemplateUnitX = <ダイアログのフォントの平均幅> / 4 TemplateUnitY = <ダイアログのフォントの高さ> / 8
ダイアログベース単位を取得する API 関数として, GetDialogBaseUnits() がある.この関数は引数なしである. つまり特定のダイアログのベース単位を返すのではなく,(Windows 95 時代の?) システムフォント (8ポイントの "System") を用いたダイアログのベース単位を返す. そのためそれ以外のフォントを使用するダイアログでは使えない. 困ったことに,Visual Studio.NET 2003 のダイアログエディタでは, ダイアログのデフォルトフォントは "MS Shell Dlg" であって "System" ではない. だから,使えない. (ダイアログエディタでは "MS Shell Dlg" の方を「システムフォント」と呼んでいる.)
ダイアログテンプレート単位の座標を画素座標に変換するには, API 関数 MapDialogRect() を用いる.この関数は,RECT のX座標 (left および right) およびY座標 (top および bottom) をテンプレート単位から画素単位に変換する. テンプレート単位の座標を (Tx, Ty),画素座標を (Px, Py) とすると変換式は次のとおり.
Px = MulDiv(Tx, BaseUnitX, 4) Py = MulDiv(Ty, BaseUnitY, 8)
この式において,Tx=4,Ty=8 とすれば, Px=BaseUnitX,Py=BaseUnitY となる. つまり使えない GetDialogBaseUnits() の代わりに,MapDialogRect() を用いて実際のダイアログベース単位を求めることができる. 次にその例を示す.
/*─────────────────────────────────────
機能 :ダイアログ hDialog の実際のダイアログベース単位を取得する.
GetDialogBaseUnits() はシステムフォント (8ポイントの "System") を使
用するダイアログでしか使えないが,この関数は hDialog が使用している
フォントに基づいて実際のダイアログベース単位を返す.
入力 :hDialog:ダイアログのウインドウハンドル.
出力 :*baseUnit:hDialog の実際のダイアログベース単位.
戻り値:取得成功のときそのときに限り真.
2006/12/24(日) 作成
─────────────────────────────────────*/
BOOL GetActualDialogBaseUnits(HWND hDialog, SIZE *baseUnit)
{
RECT rect;
BOOL result;
rect.left = 4;
rect.top = 8;
if(result = MapDialogRect(hDialog, &rect)) {
baseUnit->cx = rect.left;
baseUnit->cy = rect.top;
}
return result;
}
■実行結果
GetDialogBaseUnits() で求めたベース単位:(8, 18) ・ダイアログのフォントが "System" (サイズ:8ポイント) の場合 GetActualDialogBaseUnits() で求めた実際のベース単位:(8, 18) ・ダイアログのフォントが "MS Shell Dlg" (サイズ:8ポイント) の場合 GetActualDialogBaseUnits() で求めた実際のベース単位:(6, 13)
| 2006/12/24(日) 2007/01/08(月) 誤記訂正 |
この項目は,そもそも昨年12月に複数のモードレスダイアログを使おうとして TAB が効かなかったので追加したんだけど, リンクだけ紹介して中身を書くのをサボっていた. しかしついさっき「モードレスダイアログ 複数」で検索してきた人がいるのでこの機会に書くことにした. (2007/03/20(火) 13:40)
モードレスダイアログで TAB が使えるようにする方法は下のリンク先にあるとおりだが, 複数のモードレスダイアログの場合については書かれていなかった. そこで自分で試した結果, メッセージループを次のようにするとうまくいった.
HWND Dialog[NDIALOGS]; // モードレスダイアログのハンドルの配列
HACCEL hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDR_xxxx);
MSG msg;
UINT i;
while(GetMessage(&msg, NULL, 0, 0)) {
for(i = 0; ; i++) {
if(i >= NDIALOGS) {
// Dialog[*] 宛ではないメッセージの場合
if(!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
break;
}
// Dialog[i] 宛のメッセージか?
if(IsDialogMessage(Dialog[i], &msg)) break;
}
}
なお,モードレスダイアログの親 (または祖先) がモーダルダイアログだとダメなので, 親 (祖先) もモードレスにする必要がある. これはたぶん,モーダルダイアログが自分自身のメッセージループ (モードレスダイアログ非対応) を持っている (たぶん間違っていない推測) からだろう.(違ってたら教えてください.)
また,上記のコードはすべてのウインドウがモードレスダイアログ (最大4個) であるアプリケーションでうまく動いたが, ダイアログでないウインドウが混在するアプリケーションでは修正が必要かもしれない.
2009/12/29(火) 追記:Microsoft の公式情報.
|
2006/12/11(月) リンクのみ掲載 2007/03/20(火) 本文作成 |
ウインドウクラスを登録する際,スタイルに CS_HREDRAW | CS_VREDRAW を指定しておくと,ウインドウがリサイズされるたびに再描画が行われるが, それが頻繁すぎてちらつきが気になる場合がある. これは,次のようにすると低減することができる場合が多い (ちらつきを減らすのであって,完全になくせるわけではない).
CS_HREDRAW | CS_VREDRAW を指定するのをやめ, ウインドウプロシージャの WM_SIZE の処理で InvalidateRect(hwnd, NULL, FALSE) を行うようにする.
この方法が効果がある理由は次のとおり.(前半は推測)
CS_HREDRAW | CS_VREDRAW を指定した場合,ウインドウをリサイズするたびに WM_PAINT メッセージが (たぶん) send され,非常に頻繁に再描画が行われる. (いまいち確信なし)
これに対し,WM_SIZE の処理で InvalidateRect を行う方法では次のようになる.
このため再描画が行われる回数は激減する. リサイズが行われている間は WM_SIZE などが繰り返しディスパッチされ続け, WM_PAINT はほとんどディスパッチされないのに対し,リサイズが (一瞬でも) 停止して 他のメッセージがなくなると WM_PAINT が (1回だけ) ディスパッチされて再描画が行われる.
Windows GDI では多角形や閉じたパスの塗りつぶしモードとして,ALTERNATE および WINDING の2種類がある.これらは次の API (や MFC のメソッド) で使用できる.
ALTERNATE と WINDING とは何かというと, どちらも閉曲線の内部と外部を区別 (定義) するための規則である. 閉曲線を塗りつぶすには,当然ながら内部と外部を区別する必要がある. 閉曲線が単純 (数学用語で「曲線がそれ自身と交差・接触しない」) ならば,その内部と外部の区別は明らかだが, 単純閉曲線でない場合はあいまいになる場合がある.
┏━━━━━━━━━━━━━━━━━┓ ┃ Y ┃ ┃ ┏━━━┓ ┏━━┓ ┃ L ┃ ┃ P ┃ ┃ ┃ ┃ ─────╂─╂─・ ┃ ┃ ┃ ┃C ┃ ┃ ┃ ┃ ┃ ┃ W┗━╋━━━┛X ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┗━━━━━━━┛ ┃ ┃ Z ┃ ┃ ┗━━━━┛
上図で点Pが閉曲線Cの内部にあるかどうかを判定したい場合, Pから無限遠に半直線Lを引き,それがCと何回交差するかを数える. 奇数回ならばPはCの内部,偶数回ならば外部にあると定義する. 上図の場合は2回なので,PはCの外部ということになる.
閉曲線C上の点QがCを1周するとき, 点Pのまわりを何回回るか (巻数) により内外を定義する方法.
(1) Pが単純閉曲線Cの内部にある場合 ┏━━━━━┓C ┃ P ┃ ┃ ・ ┃ ┃ ・Q ┗━━━━━┛ QがCを1周すると,Pのまわりを1回回る.つまり巻数は1. (2) Pが閉曲線Cの「明らかに」外部にある場合 ┏━━━━━━━┓C ┃ ┏━━━┓ ┃ ┃ ┃ ┃ ・Q ┗━┛ ┃ ┃ ・P ┏━━╋━┛ ┃ ┃ ┗━━┛ QがCを何周しようが,Pからみれば8時の方向と10時の方向の間をウロウロ するだけで,Pの回りを1周もすることはない.つまり巻数は0.
そこでPの回りを1周以上するとき内部と定義すれば, 最初の ALTERNATE の図のPは,
| Windows | NTDDI_VERSION |
|---|---|
| Windows 7 | ≧NTDDI_WIN7 |
| Server 2008 SP4 | ≧NTDDI_WS08SP4 |
| Server 2008 SP3 | ≧NTDDI_WS08SP3 |
| Server 2008 SP2 | ≧NTDDI_WS08SP2 |
| Server 2008 | ≧NTDDI_WS08 |
| Vista SP4 | ≧NTDDI_VISTASP4 |
| Vista SP3 | ≧NTDDI_VISTASP3 |
| Vista SP2 | ≧NTDDI_VISTASP2 |
| Vista SP1 | ≧NTDDI_VISTASP1 |
| Vista | ≧NTDDI_VISTA |
| Server 2003 SP4 | ≧NTDDI_WS03SP4 |
| Server 2003 SP3 | ≧NTDDI_WS03SP3 |
| Server 2003 SP2 | ≧NTDDI_WS03SP2 |
| Server 2003 SP1 | ≧NTDDI_WS03SP1 |
| Server 2003 | ≧NTDDI_WS03 |
| XP SP4 | ≧NTDDI_WINXPSP4 |
| XP SP3 | ≧NTDDI_WINXPSP3 |
| XP SP2 | ≧NTDDI_WINXPSP2 |
| XP SP1 | ≧NTDDI_WINXPSP1 |
| XP | ≧NTDDI_WINXP |
| 2000 SP4 | ≧NTDDI_WIN2KSP4 |
| 2000 SP3 | ≧NTDDI_WIN2KSP3 |
| 2000 SP2 | ≧NTDDI_WIN2KSP2 |
| 2000 SP1 | ≧NTDDI_WIN2KSP1 |
| 2000 | ≧NTDDI_WIN2K |
| Windows | WINVER | _WIN32_WINNT | _WIN32_WINDOWS |
|---|---|---|---|
| Windows 7 | ≧0x0601 | ≧0x0601 | |
| Vista / Server 2008 |
≧0x0600 | ≧0x0600 | |
| XP SP2 / Server 2003 SP1 |
≧0x0502 | ≧0x0502 | |
| XP / Server 2003 |
≧0x0501 | ≧0x0501 | |
| 2000 | ≧0x0500 | ≧0x0500 | |
| NT4.0 | ≧0x0400 | ≧0x0400 | |
| Me | ≧0x0490 | ≧0x0490 | |
| 98 | ≧0x0410 | ≧0x0410 | |
| 95 | ≧0x0400 | ≧0x0400 |
| IE | _WIN32_IE |
|---|---|
| IE 8.0 | ≧0x0800 |
| IE 7.0 | ≧0x0700 |
| IE 6.0 SP2 | ≧0x0603 |
| IE 6.0 SP1 | ≧0x0601 |
| IE 6.0 | ≧0x0600 |
| IE 5.5 | ≧0x0550 |
| IE 5.01 | ≧0x0501 |
| IE 5.0 / 5.0a / 5.0b | ≧0x0500 |
| IE 4.01 | ≧0x0401 |
| IE 4.0 | ≧0x0400 |
| IE 3.02 | ≧0x0302 |
| IE 3.0 / 3.01 | ≧0x0300 |
| IE 2.0 | ≧0x0200 |
参考
| Visual C/C++ Version |
Compiler Version |
_MSC_VER | 出典 |
|---|---|---|---|
| 2003 | 13.10 | 1310 | Predefined Macros (Visual Studio 2005) |
| 2005 | 14.00 | 1400 | |
| 2008 | 15.00 | 1500 | Predefined Macros (Visual Studio 2008) |
| 2010 | 16.00 | 1600 | Predefined Macros (Visual Studio 2010) |
アプリケーションのメインウインドウとして使用するためのトップレベル・ ウインドウ.タイトルバー,境界,クライアントエリアを持つ. ウインドウメニュー,最大化および最小化ボタン, スクロールバーを持つこともできる.
ウインドウスタイル:
// WinUser.h
#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | \
WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
Overlapped window の一種で, アプリケーションのメインウインドウの外に表示されるダイアログボックス, メッセージボックス,ツールチップ, その他の一時的なウインドウとして用いられる. タイトルバーがオプションである点を除いて WS_OVERLAPPED と同じ.
CreateWindowEx() で WS_POPUP を指定する場合, hwndParent には親ではなくオーナー (または NULL) を指定する. ポップアップウインドウの親はデスクトップウインドウになる.(実験で確認)
ウインドウスタイル:
// WinUser.h #define WS_POPUPWINDOW (WS_POPUP | WS_BORDER | WS_SYSMENU)
通常,子ウインドウはクライアントエリアのみを持つが, 明示的に指定すれば次の要素を持つこともできる.
レイヤード・ウインドウ. 次のような視覚効果を持つトップレベル・ウインドウ (子ウインドウは不可) を,ちらつかずに高速表示する.
参考
メッセージを受信するためだけのウインドウ.不可視であり,Z-order はなく,列挙できない.また,ブロードキャストメッセージは受信しない.
参考
下記のマニュアルの項目から推測した DestroyWindow(hwnd) の処理手順.
参考
| 番号範囲 | 説明 |
|---|---|
| 0x0000 〜 WM_USER−1 (0x03FF) | システム定義済または予約. |
| WM_USER (0x0400) 〜 WM_APP−1 (0x7FFF) | WM_USER+n:
ウインドウクラス内のプライベート・メッセージ. (コモンコントロール固有メッセージなど) |
| WM_APP (0x8000) 〜 0xBFFF | WM_APP+n:アプリケーション定義のプライベート・メッセージ. |
| 0xC000 〜 0xFFFF | RegisterWindowMessage() が動的に割り当てる. アプリケーション間の通信用. |
| 0x10000 〜 | システム予約. |
■Memo
ComboBox のメッセージの一部に,
マニュアルの内容から予想されるのと異なる動作があったので実験で確認してみた.
(Windows XP SP3 + comctl32.dll 5.82.2900.5512 (2004/08/05))
case CBN_DROPDOWN: comboBox->isDroppedDown = TRUE ; break; case CBN_CLOSEUP : comboBox->isDroppedDown = FALSE; break;一方,これらのタイミングで CB_GETDROPPEDSTATE を使用する方法は期待通りに動作しない. (CBN_DROPDOWN は表示される直前に発生するため.)
// ×うまくいかない. // (仮にうまくいくとしても,上記の方法に比べて効率が悪い.) case CBN_DROPDOWN: case CBN_CLOSEUP: comboBox->isDroppedDown = SendMessage(comboBox->hwnd, CB_GETDROPPEDSTATE, 0, 0); break;
TRACKMOUSEEVENT track; track.cbSize = sizeof(track); track.hwndTrack = hwnd; track.dwHoverTime = HOVER_DEFAULT; // WM_MOUSEHOVER,WM_MOUSELEAVE をリクエストする. track.dwFlags = TME_HOVER | TME_LEAVE; if(!TrackMouseEvent(&track)) goto Error; // WM_NCMOUSEHOVER,WM_NCMOUSELEAVE をリクエストする. track.dwFlags = TME_HOVER | TME_LEAVE | TME_NONCLIENT; if(!TrackMouseEvent(&track)) goto Error;
| Mapping Mode | 1論理単位 | X軸 方向 |
Y軸 方向 |
デバイス 依存性 |
等方性 (*1) |
備考 |
|---|---|---|---|---|---|---|
| MM_TEXT | 1画素 | → | ↓ | 有 | デバイス 依存 |
デフォルトのマッピングモード. |
| MM_LOMETRIC | 0.1mm | → | ↑ | 無 | ○ | |
| MM_HIMETRIC | 0.01mm | → | ↑ | 無 | ○ | |
| MM_LOENGLISH | 0.01inch (約 0.254mm) |
→ | ↑ | 無 | ○ | |
| MM_HIENGLISH | 0.001inch (約 0.0254mm) |
→ | ↑ | 無 | ○ | |
| MM_TWIPS | 1/1440 inch (約 0.0176mm) |
→ | ↑ | 無 | ○ | |
| MM_ISOTROPIC | ユーザ定義 (等方座標系) | ○ | SetWindowExtEx(),SetViewportExtEx()
で座標軸の向きとスケールを設定する. 等方座標系になるように自動調整される. |
|||
| MM_ANISOTROPIC | ユーザ定義 (非等方座標系) | 座標系 依存 |
SetWindowExtEx(),SetViewportExtEx() で座標軸の向きとスケールを設定する. | |||
(*1) 等方性 (isotropy):X軸方向とY軸方向の1論理単位の (デバイス上での物理的な) 長さが等しいこと. 論理座標空間で正方形や円を描くと,デバイス上でも正方形や円になる. 非等方 (異方) 座標系ではデバイス上で長方形や楕円になる.
Modifier Keys (SHIFT,CTRL,ALT など) を指定するための定数. いずれも WinUser.h で定義されている.
| MOD_ALT | 0x0001 |
| MOD_CONTROL | 0x0002 |
| MOD_SHFIT | 0x0004 |
| MOD_WIN | 0x0008 |
| MK_SHFIT | 0x0004 |
| MK_CONTROL | 0x0008 |
| FSHFIT | 0x04 |
| FCONTROL | 0x08 |
| FALT | 0x10 |
■ATOM とは何か,なぜそういう名前なのか?
(たぶん間違っていない個人的推測 ― 以前回答したページに今更リンク.)
■システム定義ウインドウクラスの ATOM の値
(出典:DLGITEMTEMPLATEEX Structure,WinUser.h)
| Class | ATOM |
|---|---|
| Button | 0x0080 |
| Edit | 0x0081 |
| Static | 0x0082 |
| ListBox | 0x0083 |
| ScrollBar | 0x0084 |
| ComboBox | 0x0085 |
| Dialog | 0x8002 |
■comctl32.dll の各種コントロールの ATOM の値
comctl32.dll (version:5.82.2900.2) の各種コントロールについて,
テストプログラムを作って実際の値を調べてみると次のようになった.
上記の表と異なるのは,システム定義クラスを comctl32.dll が再定義しているためか?
| Class | ATOM |
|---|---|
| Button | 0xC017 |
| Edit | 0xC018 |
| Static | 0xC019 |
| ListBox | 未確認 |
| ScrollBar | 未確認 |
| ComboBox | 0xC01C |
| ComboBoxEx | 0xC04A |
| StatusBar | 0xC03E |
| その他 | 未確認 |
INITCOMMONCONTROLSEX::dwICC に ICC_STANDARD_CLASSES または ICC_LINK_CLASS
が含まれているとなぜかエラーになる.
(Windows XP SP3 + comctl32.dll 5.82.2900.5512 (2004/08/05))
Windows 98 ではエラーにならないらしい.
(参考:http://ir9.jp/prog/ayu/datlog/tech_win32api/1092524669/1092524669_02.html
>>440〜442)
GetLastError() で ERROR_ALREADY_EXISTS が返るが, 上記のフラグの代わりに未定義のビット (0x00010000 以上) をセットしても同じ結果になるので, このエラーコードには意味がなさそうである.

Windowsプログラマのバイブル本!
初版で勉強しました。
基本から確実に理解したいなら
詳しいが、少しバランスが!
硬派なWindowsプログラマーを志すならば
Windowsプログラマのバイブル本!
初版で勉強しました。
基本から確実に理解したいなら
詳しいが、少しバランスが!
硬派なWindowsプログラマーを志すならば
Advanced Windows第5版翻訳本
極めて良書ですが、難しい。
文章が難解
本物のWindows技術者になるには避けられない本
Windows技術者必読
内容的にはとても詳しくていいのだが
お勧めです
これは歴史的な事情で・・・が書かれた本
この本はいいですよ
入門者にはお勧めできない
MFCを使ったWindowsプログラミング入門に最適です。
普通にわかりません。
猫ではわかりません
実践重視でテンポよい解説
著者はMSDNライブラリを読んでいない
Visual C++ 2008対応リニューアル版
こんな辞書を待っていました
内容はいいんだけど
C言語経験者向き
Windowsプログラミング
|
Windowsプログラミングの極意 歴史から学ぶ実践的Windowsプログラミング! posted with amazlet at 12.02.05 Raymond Chen アスキー 売り上げランキング: 190469 |
日本語のタイトルは誤解を招きやすいが,Windows のプログラミング方法ではなく, 「Windows の○○はなぜそうなっているのか?」を解説した本.
■参考
Windows のパス名 (ファイル名,ディレクトリ名,デバイス名) を扱う上で非常に参考になる (MSDN を調べても,パス名の細かい規則に関するまとまった情報がなかなか得られない).
「末尾文字の落とし穴」は私も昨年偶然発見した.ノード名の先頭, 途中,末尾,そして単一文字のノード名で使える文字を調べるプログラムを書いてテストしてみたところ, ノード名の末尾に半角のピリオドまたはスペースが連続している (正規表現で書けば "[ .]+$") と, それらはすべて無視 (削除) されることがわかった.
なお,「Windows のパス名は大文字小文字を区別しない」 ということは誰でも知っていると思うが,これは ASCII 英字だけの話ではなく,Unicode レベルで大文字小文字が同一視される点に注意 (えっ,それも知ってる?). したがって日本語環境で使える文字としては,全角英字もそうだし, ギリシャ文字やロシア文字もそうである. 例えばギリシャ文字を含むファイル名 "ΑΒΓ.TXT" は, それらを小文字にした "αβγ.txt" と同じと見なされる (実験で確認済).
パス名の先頭に \\.\ を付けて,259文字 (MAX_PATH=260 から終端 NUL を除いた有効文字数) 以上のパス名を持つファイルを作ってみたらどうなるか, という実験.私も \\.\ で長いパス名が使えることは知っていたので, 以前から実験してみたいとは思っていた.
しかしほとんどのアプリケーションは固定長のバッファ (ひどいのになると MAX_PATH より小さい値を勝手に定義している (-_-#)) でパス名を扱い,\\.\ なんて使っていないので, そういうパス名は扱えないだろう.
長過ぎるパス名が存在する可能性がある以上, 受け入れられるようにはしておきたいが, それを扱えないアプリケーションがほとんどなので, 自分からそういうパス名のファイルを作るのは避けた方がよい.
このページの主な更新は Blog でお知らせします.
|
Copyright © 2006-2012 noocyte, All rights reserved. E-mail: relipmoced (a) yahoo.co.jp (" (a) " を半角のアットマークに書き替えてください.) リンクはご自由に. 「noocyte のプログラミング研究室」トップページに戻る. |
|