noocyte のプログラミング研究室 〜プログラムは楽しげに走らねばならない♪〜

Windows Programming Tips & Memos

公開:2006/09/21(木)
最終更新:2012/05/06(日)

Windows API (および関連 API) をC言語で使用するための Tips 集です.随時追加します. 主な更新は Blog でお知らせします.

目次

  1. Windows のエラーメッセージ文字列を取得する.
  2. ファイルをごみ箱に移動する.
  3. ごみ箱の構造 (Windows XP Pro SP2 の場合)
  4. 実行ファイルのフルパス名を取得する.
  5. コントロールのウインドウクラス名一覧
  6. ダイアログの座標系 (ダイアログ (テンプレート) 単位とダイアログベース単位)
  7. (1つ以上の) モードレスダイアログで TAB が効かない!
  8. ウインドウリサイズ時のちらつきを抑える.
  9. 閉曲線・多角形の塗りつぶしモード (ALTERNATE と WINDING)
    〜 点が閉曲線の内部にあるかどうかを判定する方法 〜
  10. 実行環境バージョン指定マクロ (Windows SDK v7.0)
  11. ウインドウの特徴
  12. DestroyWindow() の処理手順
  13. ウインドウメッセージ
  14. その他
  15. 参考図書
  16. 外部へのリンク
  17. 更新履歴

Windows のエラーメッセージ文字列を取得する.

2006/10/29(日) 作成
2009/03/25(水) 改定

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つです.

pFrom
  • pFrom の最後は2個の '\0' で終端する.
    pFrom は複数のパス名を指定できる仕様になっており, 次の形式の文字列を受け取ります.
    <パス名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パス名の落とし穴 → サービス機能の不活性化)

hwnd

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);
}
(2006/10/14(土))

ごみ箱の構造 (Windows XP Pro SP2 の場合)

まだ調査中です.

ごみ箱フォルダのパス名

ごみ箱フォルダのパス名は,各ドライブの <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 には, ごみ箱内の削除ファイルの情報が含まれている. 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;

■参考リンク
(2007/06/29(金))

実行ファイルのフルパス名を取得する.

GetModuleFileName() の第1引数 (hModule) に NULL を渡すと, 現在のプロセスに対応する実行ファイルのフルパス名を取得できる.

#include <windows.h>
#include <tchar.h>

TCHAR ExeFileName[MAX_PATH];

GetModuleFileName(NULL, ExeFileName, MAX_PATH);
(2006/09/21(木))

コントロールのウインドウクラス名一覧

コントロールのクラス名は,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

GetDialogBaseUnits() は使えない.

ダイアログベース単位を取得する API 関数として, GetDialogBaseUnits() がある.この関数は引数なしである. つまり特定のダイアログのベース単位を返すのではなく,(Windows 95 時代の?) システムフォント (8ポイントの "System") を用いたダイアログのベース単位を返す. そのためそれ以外のフォントを使用するダイアログでは使えない. 困ったことに,Visual Studio.NET 2003 のダイアログエディタでは, ダイアログのデフォルトフォントは "MS Shell Dlg" であって "System" ではない. だから,使えない. (ダイアログエディタでは "MS Shell Dlg" の方を「システムフォント」と呼んでいる.)

GetDialogBaseUnits() の代わりに MapDialogRect() を使う.

ダイアログテンプレート単位の座標を画素座標に変換するには, 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(月) 誤記訂正

(1つ以上の) モードレスダイアログで TAB が効かない!

この項目は,そもそも昨年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回だけ) ディスパッチされて再描画が行われる.

(2007/05/06(日))

閉曲線・多角形の塗りつぶしモード (ALTERNATE と WINDING)
〜 点が閉曲線の内部にあるかどうかを判定する方法 〜

Windows GDI では多角形や閉じたパスの塗りつぶしモードとして,ALTERNATE および WINDING の2種類がある.これらは次の API (や MFC のメソッド) で使用できる.

ALTERNATE と WINDING とは何かというと, どちらも閉曲線の内部と外部を区別 (定義) するための規則である. 閉曲線を塗りつぶすには,当然ながら内部と外部を区別する必要がある. 閉曲線が単純 (数学用語で「曲線がそれ自身と交差・接触しない」) ならば,その内部と外部の区別は明らかだが, 単純閉曲線でない場合はあいまいになる場合がある

ALTERNATE (交互ルール)

     ┏━━━━━━━━━━━━━━━━━┓
     ┃ Y               ┃
     ┃ ┏━━━┓   ┏━━┓    ┃
 L   ┃ ┃ P ┃   ┃  ┃    ┃
─────╂─╂─・ ┃   ┃  ┃    ┃C
     ┃ ┃   ┃   ┃  ┃    ┃
    W┗━╋━━━┛X  ┃  ┃    ┃
       ┃       ┃  ┃    ┃
       ┗━━━━━━━┛  ┃    ┃
       Z          ┃    ┃
                  ┗━━━━┛

上図で点Pが閉曲線Cの内部にあるかどうかを判定したい場合, Pから無限遠に半直線Lを引き,それがCと何回交差するかを数える. 奇数回ならばPはCの内部,偶数回ならば外部にあると定義する. 上図の場合は2回なので,PはCの外部ということになる.

WINDING (巻数ルール)

閉曲線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は,

参考リンク

(2007/06/16(土))

実行環境バージョン指定マクロ (Windows SDK v7.0)


最低限必要な Windows バージョン
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 バージョン
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 バージョン
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

参考

_MSC_VER

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)


ウインドウの特徴 (Window Features)

ウインドウ種別 (Window Types)

Overlapped Window (WS_OVERLAPPED,WS_OVERLAPPEDWINDOW)

アプリケーションのメインウインドウとして使用するためのトップレベル・ ウインドウ.タイトルバー,境界,クライアントエリアを持つ. ウインドウメニュー,最大化および最小化ボタン, スクロールバーを持つこともできる.

ウインドウスタイル:

  • WS_OVERLAPPED:タイトルバーと境界を持つ.
  • WS_OVERLAPPEDWINDOW:次の要素を持つ.
    • タイトルバー (WS_CAPTION)
    • サイズ変更可能境界 (WS_THICKFRAME (=WS_SIZEBOX))
    • ウインドウメニュー (WS_SYSMENU)
    • 最小化ボタン (WS_MINIMIZEBOX)
    • 最大化ボタン (WS_MAXIMIZEBOX)
    // WinUser.h
    #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | \
                                 WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
    
Pop-up Window (WS_POPUP)

Overlapped window の一種で, アプリケーションのメインウインドウの外に表示されるダイアログボックス, メッセージボックス,ツールチップ, その他の一時的なウインドウとして用いられる. タイトルバーがオプションである点を除いて WS_OVERLAPPED と同じ.

CreateWindowEx() で WS_POPUP を指定する場合, hwndParent には親ではなくオーナー (または NULL) を指定する. ポップアップウインドウの親はデスクトップウインドウになる.(実験で確認)

ウインドウスタイル:

  • WS_POPUP:ポップアップ・ウインドウ.
  • WS_POPUPWINDOW:WS_POPUP に加え,境界 (WS_BORDER) とウインドウメニュー (WS_SYSMENU) を持つ.
    // WinUser.h
    #define WS_POPUPWINDOW      (WS_POPUP | WS_BORDER | WS_SYSMENU)
    
Child Window (WS_CHILD)

通常,子ウインドウはクライアントエリアのみを持つが, 明示的に指定すれば次の要素を持つこともできる.

  • タイトルバー (WS_CAPTION)
  • ウインドウメニュー (WS_SYSMENU)
  • 最小化ボタン (WS_MINIMIZEBOX)
  • 最大化ボタン (WS_MAXIMIZEBOX)
  • 境界 (WS_BORDER)
    指定しない場合は境界のないウインドウを作成する. これは親ウインドウのクライアント領域を, 見えない境界で分割するのに使用できる.
  • スクロールバー (WS_HSCROLL,WS_VSCROLL)
  • メニュー (不可)
    CreateWindow() の hMenu 引数の説明からわかるように, メニューハンドルと子ウインドウ ID の一方しか指定できないので, 子ウインドウはメニューを持つことができないのだろう.
Layered Window (WS_EX_LAYERED)

レイヤード・ウインドウ. 次のような視覚効果を持つトップレベル・ウインドウ (子ウインドウは不可) を,ちらつかずに高速表示する.

  • 複雑な形状 (アニメーションも可)
  • アルファブレンド

参考

Message-Only Window

メッセージを受信するためだけのウインドウ.不可視であり,Z-order はなく,列挙できない.また,ブロードキャストメッセージは受信しない.

参考


DestroyWindow() の処理手順

下記のマニュアルの項目から推測した DestroyWindow(hwnd) の処理手順.

  1. 拡張ウインドウスタイル WS_EX_NOPARENTNOTIFY が指定されていなければ, すべての祖先ウインドウに WM_PARENTNOTIFY を send する.
  2. ウインドウを非表示 (!IsWindowVisible()) にする.
  3. WM_DESTROY を send する.
    ⇒ アプリケーションは hwnd に関連付けられているメモリを解放すべき.
    また,このウインドウが (SetClipboardViewer() で設定された) clipboard viewer chain の一部の場合は,ChangeClipboardChain() で削除しなければならない.
  4. 子孫ウインドウを破棄する.
  5. WM_NCDESTROY を send し,hwnd が使用していたメモリを解放する.
    したがってこの時点で hwnd が無効になる.つまり,hwnd が最後に受け取るメッセージは WM_NCDESTROY.

参考

(2009/03/14(土))

ウインドウメッセージ

ウインドウメッセージ番号 (WM_*) の範囲

番号範囲 説明
0x0000WM_USER−1 (0x03FF) システム定義済または予約.
WM_USER (0x0400) 〜 WM_APP−1 (0x7FFF) WM_USER+n: ウインドウクラス内のプライベート・メッセージ.
(コモンコントロール固有メッセージなど)
WM_APP (0x8000) 〜 0xBFFF WM_APP+n:アプリケーション定義のプライベート・メッセージ.
0xC0000xFFFF RegisterWindowMessage() が動的に割り当てる.
アプリケーション間の通信用.
0x10000 システム予約.

■Memo

(2010/10/09(土))

WM_INITDIALOG

(2009/03/20(金))

ComboBox のメッセージ

ComboBox のメッセージの一部に, マニュアルの内容から予想されるのと異なる動作があったので実験で確認してみた.
(Windows XP SP3 + comctl32.dll 5.82.2900.5512 (2004/08/05))

(2009/03/21(土))

ダブルクリック・メッセージ (WM_xBUTTONDBLCLK) を使用するには

(2008/04/23(水))

TrackMouseEvent(),WM_(NC)MOUSEHOVER,WM_(NC)MOUSELEAVE

(2009/07/20(月))

その他

DC のマッピングモード

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論理単位の (デバイス上での物理的な) 長さが等しいこと. 論理座標空間で正方形や円を描くと,デバイス上でも正方形や円になる. 非等方 (異方) 座標系ではデバイス上で長方形や楕円になる.


(2012/05/06(日))

Modifier Keys

Modifier Keys (SHIFT,CTRL,ALT など) を指定するための定数. いずれも WinUser.h で定義されている.


ATOM

■ATOM とは何か,なぜそういう名前なのか?
(たぶん間違っていない個人的推測 ― 以前回答したページに今更リンク.)


■システム定義ウインドウクラスの ATOM の値
(出典:DLGITEMTEMPLATEEX StructureWinUser.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() のエラー

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 以上) をセットしても同じ結果になるので, このエラーコードには意味がなさそうである.

(2009/03/21(土))

参考図書

プログラミングWindows第5版〈上〉Win32 APIを扱う開発者のための決定版! (Microsoft Programming Series)
チャールズ ペゾルド
アスキー
売り上げランキング: 97738
おすすめ度の平均: 4.0
4 Windowsプログラマのバイブル本!
5 初版で勉強しました。
4 基本から確実に理解したいなら
4 詳しいが、少しバランスが!
5 硬派なWindowsプログラマーを志すならば

プログラミングWindows第5版〈下〉Win32 APIを扱う開発者のための決定版! (Microsoft Programming Series)
チャールズ ペゾルド
アスキー
売り上げランキング: 208637
おすすめ度の平均: 4.0
4 Windowsプログラマのバイブル本!
5 初版で勉強しました。
4 基本から確実に理解したいなら
4 詳しいが、少しバランスが!
5 硬派なWindowsプログラマーを志すならば

著者サイト


Advanced Windows 第5版 上 (マイクロソフト公式解説書)
Jeffrey Richter Christophe Nasarre
日経BPソフトプレス
売り上げランキング: 127753
おすすめ度の平均: 5.0
5 Advanced Windows第5版翻訳本

Advanced Windows 第5版 下 (マイクロソフト公式解説書)
Jeffrey Richter Christophe Nasarre
日経BPソフトプレス
売り上げランキング: 131018



インサイド Microsoft Windows 第4版〈上〉 (マイクロソフト公式解説書)
ディビット ソロモン マーク ルシノビッチ
日経BPソフトプレス
売り上げランキング: 113533
おすすめ度の平均: 5.0
5 極めて良書ですが、難しい。
4 文章が難解
5 本物のWindows技術者になるには避けられない本
5 Windows技術者必読

インサイドMicrosoft Windows第4版〈下〉 (マイクロソフト公式解説書)
ディビット ソロモン マーク ルシノビッチ
日経BPソフトプレス
売り上げランキング: 128015
おすすめ度の平均: 4.0
4 内容的にはとても詳しくていいのだが



Windows© Internals, Fifth Edition (PRO-Developer)
Mark E. Russinovich David A. Solomon Alex Ionescu
Microsoft Press
売り上げランキング: 10314
おすすめ度の平均: 5.0
5 お勧めです



Windowsプログラミングの極意 歴史から学ぶ実践的Windowsプログラミング!
Raymond Chen
アスキー
売り上げランキング: 136355
おすすめ度の平均: 5.0
5 これは歴史的な事情で・・・が書かれた本



C++ユーザーのためのWindowsプログラミングの基礎知識―Visual C++とMFCを利用したプログラミング入門 (Ascii books)
柏原 正三
アスキー
売り上げランキング: 359295
おすすめ度の平均: 3.5
5 この本はいいですよ
1 入門者にはお勧めできない
5 MFCを使ったWindowsプログラミング入門に最適です。



猫でもわかるWindowsプログラミング 第3版 (猫でもわかるプログラミングシリーズ)
粂井 康孝
ソフトバンククリエイティブ
売り上げランキング: 122154
おすすめ度の平均: 3.0
3 普通にわかりません。
2 猫ではわかりません
5 実践重視でテンポよい解説
2 著者はMSDNライブラリを読んでいない
4 Visual C++ 2008対応リニューアル版



Windowsプログラミング逆引きクロス大辞典
佐納 康治 曽我部 雄樹
秀和システム
売り上げランキング: 318690
おすすめ度の平均: 5.0
5 こんな辞書を待っていました



Windowsゲームプログラミング 第2版 Game Developer
赤坂 玲音
ソフトバンククリエイティブ
売り上げランキング: 24726
おすすめ度の平均: 3.5
2 内容はいいんだけど
4 C言語経験者向き
5 Windowsプログラミング



【送料無料】Windowsプログラミングの極意

楽天で買う
価格:3,990円(税込)

日本語のタイトルは誤解を招きやすいが,Windows のプログラミング方法ではなく, 「Windows の○○はなぜそうなっているのか?」を解説した本.

■参考


外部へのリンク


更新履歴

このページの主な更新は Blog でお知らせします.

  1. 2006/09/21(木) 公開
  2. 2006/10/14(土) ファイルをごみ箱に移動する.を追加.
  3. 2006/10/29(日) Windows のエラーメッセージ文字列を取得する.を追加.
  4. 2006/12/11(月) (1つ以上の) モードレスダイアログで TAB が効かない! 準備開始.
  5. 2006/12/24(日) ダイアログの座標系 (ダイアログテンプレート単位とダイアログベース単位) を追加.
  6. 2007/01/08(月) ダイアログの座標系 (ダイアログテンプレート単位とダイアログベース単位) の誤記訂正.
  7. 2007/03/20(火) モードレスダイアログで TAB が効かない! ようやく内容作成.
  8. 2007/05/06(日) ウインドウリサイズ時のちらつきを抑える.を追加.
  9. 2007/06/16(土) 閉曲線・多角形の塗りつぶしモード (ALTERNATE と WINDING) 〜 点が閉曲線の内部にあるかどうかを判定する方法 〜 を追加.
  10. 2007/06/29(金) ごみ箱の構造 (Windows XP Pro SP2 の場合) を追加 (まだ調査中).
  11. 2007/08/18(土) 外部へのリンクの「Windowsパス名の落とし穴」にコメントを追記. またそれに関連して,「ファイルをごみ箱に移動する.」の pFrom の説明にも追記.
  12. 2007/11/04(日) 外部へのリンクに「あなたの知らない259文字越えの世界 (Tatsuの開発室)」を追加.
  13. 2009/01/20(火) コントロールのウインドウクラス名一覧に SysLink を追加.
  14. 2009/03/28(土) 下記を追加.
  15. 2009/07/20(月) TrackMouseEvent(),WM_(NC)MOUSEHOVER,WM_(NC)MOUSELEAVE を追加.
  16. 2009/08/11(火) 実行環境バージョン指定マクロ に Windows 7 と IE8 を追加.
  17. 2010/06/22(火) CB_GETCURSEL の説明を少し改定.
  18. 2010/10/09(土)「ウインドウメッセージ番号 (WM_*) の範囲」を追加.
  19. 2010/10/28(木) WS_POPUP にちょっと追記.
  20. 2011/09/11(日)「ATOM」を追加.
  21. 2012/01/15(日)「_MSC_VER」を追加.
  22. 2012/05/06(日)「DC のマッピングモード」を追加.


Copyright © 2006-2012 noocyte, All rights reserved.
E-mail: relipmoced (a) yahoo.co.jp
  (" (a) " を半角のアットマークに書き替えてください.)
リンクはご自由に.
「noocyte のプログラミング研究室」トップページに戻る.


track feed noocyte