Win32 API memo

Win32 API について、SDK HELP/MSDN に載っていない/分かりにくい事項の覚え書きです。

update:2008/09/21
Homeへ戻る

個別API等の動作関連

GetTempPathName(lpszPath, lpszPrefix, uUnique, lpszTempFile)

uUnique == 0 のとき、生成したファイル名で filesize = 0 のファイルが作成される。
→一時ディレクトリを作る時はこのファイルを一旦削除するか、uUnique != 0 で利用する必要あり。
(2000/9/2)

GetTempPath(cchBuffer, lpszTempPath)

cchBuffer は 約 0x7000 未満にしないといけない。
0x7fff 等では例外が発生し、0x8000 では FALSE を返す。
(2000/11/4)

GetFileInformationByHandle()

NEC PC-98x1用 Windows9x 上からネットワーク上の Windows9x(機種問わず)のエントリを見た場合、dwFileAttributesが正常に取得できない。また、nFileSizeHigh/nFileSizeLowが正常で無い場合もある。FindFirstFile()/FileNextFile(),GetFileAttributes()なら問題無いようなので、こちらから情報を得るといいようだ。
(2001/9/8)

FindFirstFile()
FindNextFile()
int 21H AX=714eH/714fH

RegisterDragDrop (OLE2 D&D)

内部で作成される別スレッドで監視を行っている
(2001/2/4)

GetDiskFreeSpace

(2001/2/20)

GetDiskFreeSpaceEx

(2001/2/20)

CompareString

Windows2000等では、NORM_IGNORECASEとNORM_IGNOREKANATYPEが有効になったまま変更できないようだ。
(2001/2/20)

MoveFile

異なるボリュームに対してファイルの移動ができないことになっているが、実際は可能になっている。(95B/NT4/2000で確認)
※エントリの付け替えによる高速移動と区別する方法が見当たらず、MoveFileで待たされることがある。
(2001/4/4)

NotifyChangeEventLog

このAPIで得られるシグナル状態は、PulseEventによって変化する。つまり、待機API(WaitForSingleObject等)内で変化しないと取得できない。例えば、
while ( WaitForSingleObject(hEvent,0) != WAIT_OBJECT_0 );
というコードを書いてもイベントログの書き込みがされたことをすべて検出できない。
→GetNumberOfEventLogRecordsを併用したほうがよさそうだ
(2001/12/6)

PulseEvent

待機API(WaitForSingleObject等)によって待機している場合でないとPulseEventの効果が期待できない。例えば、
while ( WaitForSingleObject(hEvent,0) != WAIT_OBJECT_0 );
というコードを書くと検出漏れが発生する

(2001/12/6)

SetWindowText
GetWindowText
WM_SETTEXT
WM_GETTEXT

?etWindowTextとWM_?ETTEXTは基本的に同じ動作だが、条件によっては異なる動作となる。 (2002/2/2)修正

FindFirstChangeNotification

エラーの返値は、INVALID_HANDLE_VALUEだが、NULLが帰ってくることがある。(Win2000 SP3 で MAX_PATH を越えるパスを指定したときに確認)

(2002/11/8)

SelectObject

フォントの切り替えはかなりコストが掛かるようだ。
ppv.exe は、tab を矢印で表示させるときに、一時的に SelectObject を使って Symbolフォント切り替えている。この状態で tab code が大量に入っているファイルを表示させたとき、画面描画中であることががはっきりわかるほど遅くなる。

(2003/4/19)

ImpersonateNamedPipeClient

ImpersonateNamedPipeClientは、「パイプから直前に読み取ったメッセージ」の送信元クライアントに偽装することができる。つまり、ReadFile等で予めメッセージを読んだ後に、ImpersonateNamedPipeClientを使う必要がある。
ConnectNamedPipeを実行した直後等は、まだメッセージを読んだことにならないので、ImpersonateNamedPipeClientを実行してもエラーになる。
(2003/9/20)

ToAscii(uVirtKey,uScanCode,lpbKeyState,lpwTransKey,fuState)

変換された結果が入る lpwTransKey は WORD だが、実際は下位バイトのみ書き換えられ、上位バイトは ToAscii の実行前と同じ値である。
(Windows2000 SP4で確認、2004/11/27)

ReadConsoleInput

SetConsoleModeでENABLE_MOUSE_INPUTを指定し、コンソールのプロパティで簡易編集を解除した状態では、ReadConsoleInputでマウスカーソルの位置を取得できる。
この位置は、Windows2000/XPならスクリーンバッファ内の座標に収まるが、WindowsNTではバッファ外の座標も入る(X,Y=-1,-1等)
(2005/11/26)

SHGetFileInfo

(2007/08/11)

SHFileOperation

SHFileOperationの実行中に実行元のウィンドウを閉じてPostQuitMessageを実行しても、途中で止まって、場合によってはプロセスが残ったままになる。
このため、SHFileOperationの実行後、実行元のウィンドウを閉じたかどうかを確認し(例えば、IsWindow)、既に閉じていた場合はPostQuitMessageを再度実行する必要がある。
※SHFileOperationの実行中は、実行元の入力を抑制しない為、実行元のメッセージループが回る疑似バックグラウンド状態になる。
(2007/12/01)

UnhookWindowsHookEx

UnhookWindowsHookExを実行しただけでは、全てのプロセスにフックしたDLLは全て解放されない。これは、DLLが解放されるタイミングが該当プロセスでGetMessage等を呼び出したときだからである。このため、DLLをすぐに解放したいときは、UnhookWindowsHookExの後にPostMessage(HWND_BROADCAST,WM_NULL,0,0)を実行するとよい。
※SetWindowsHookExによりDLLがフックするタイミングもGetMessage等を呼び出したときと思われる。
(2008/08/24)

WM_xBUTTONUP
WM_xBUTTONDOWN
WM_xBUTTONDBLCLK

最小化時等でクライアント外の座標が渡されることがある。
(2000/9/2)

WM_MOUSEWHEEL

SendMessageによって呼ばれているようだ。
(2000/9/2)

WM_INITMENU

任意のメニュー/ポップアップメニューがアクティブになるときに発生する。また、一旦メニューループを抜けない限り再発生しない。具体例は次の通り。 (2001/6/19)

WM_INITMENUPOPUP

任意のポップアップメニューがアクティブになるときに発生する。同じメニューループであってもアクティブになる度に発生する。 (2001/6/19)

EM_GETSEL
EM_SETSEL
EM_LIMITTEXT

WindowsXP でビジュアルスタイル(Luna)の ANSI 版エディットボックスを開いているにも関らず、EM_GETSEL/EM_SETSEL/EM_LIMITTEXTで UNICODE 版の動作をする。
例えば、次のコードでは XP 以外では FALSE を返すが、XP では TRUE となる。
[WinXP] Common Control 6.0 の EM_LIMITTEXT による入力制限によれば仕様ということである。
(ビジュアルスタイルではANSI版/完全なANSI変換は提供せず、UNICODE版のみ提供のようだ)
また、エディットボックスが UNICODE版 の動作をするかは IsWindowUnicode を使えば判定できる。
BOOL CheckXPEditBox(void){
    DWORD lP,wP;
    HWND hWnd;
    BOOL result;

    hWnd = CreateWindowA("EDIT","漢字",0,0,0,0,0,NULL,NULL,hInstance,0);
    ShowWindow(hWnd,SW_HIDE);

    SendMessageA(hWnd,EM_SETSEL,(WPARAM)0,(LPARAM)-1);
    SendMessageA(hWnd,EM_GETSEL,(WPARAM)&wP,(LPARAM)&lP);
    if ( GetWindowTextLengthA(hWnd) > (lP - wP) ){
        result = TRUE;
    }else{
        result = FALSE;
    }
    PostMessage(hWnd,WM_CLOSE,0,0);
    return result;
}
※キーボードフックを行うプログラムがあるときなど、
 このコードで検出できない状況もある。
(2003/12/6)

EM_SETSEL

無条件に文字列の末尾を指定する方法として -1 を指定するのが一般的だが、この値だと失敗することがあるようだ。正の最大値 0x7fffffff を使えば回避できるようだ。
manifest 適用 XPで、ANSI版エディットボックスを使用したときにこの現象を確認した
(2004/09/20)

特定機能関連

プロパティシート関連

RAS関連

RAS 関連の API が入った DLL を動的読込みする場合は RASAPI32.DLL を読込むことになると思うが、RASAPI32.DLLがあってもそれに関連する DLL がない場合があり、GetProcAddressが成功しても API の実行時に DLL の読込み失敗ダイアログが表示されることがある。
(2000/9/2)

Popup Menu

Popup Menu は表示中の PopupWindow の Window Class("#32768") ではなく、別のところで制御しているらしく、サブクラス化が困難な模様。また、メッセージループも内部で行っているため、大本のメッセージループではメッセージをつかまえることが困難。
 PopupMenuをマウスのホイールで選択できるかどうかを試していたのだか、このために挫折(^^;。
(2001/2/4)

Listbox

LBS_HASSTRINGS でないオーナードローのリスト ボックスは、LB_ADDSTRING等で任意の値を設定することができる。
 しかし、Windows XP 上では 0 等の値を使用すると正しくアイテムが格納されなくなる。
※SP1 で修正されている。
(2002/9/27)

FILETIME

西暦1601年 01/01 0:00:00(UTC)からの100ns単位の経過時間を表す。現在使われているグレゴリオ暦が1582年から使われているため、それに近くてきりのいい年から始めているようだ。
FILETIMEがオーバーフローを起こすのは西暦約60,000年である。
FILETIMEが 0x7fff ffff ffff ffff(西暦約30,800年)を越えるとSystemTimeToFileTime()でエラーになる。
日常使う時間の単位との換算は次の通り。
1秒10,000,0000x0098 9680
1分600,000,0000x23c3 4600
1時間36,000,000,0000x0008 61c4 6800
1日864,000,000,0000x00C9 2A69 C000
1980/1/1 00:00:00
(FAT,MS-DOSで扱える最初の日)
UTC : 0x01A8 E79F E1D5 8000
JST : 0x01A8 E754 71ED D800
2000/1/1 00:00:00UTC : 0x01BF 53EB 256D 4000
JST : 0x01BF 539F B585 9800

※うるう秒があるため現実の日時と上記値とのずれが生じる(1972年〜2006年で33秒)。
(2006/12/16)

画面描画

WindowsXPで「ドラッグ中にウィンドウの内容を表示する」を有効にした状態でウィンドウをドラッグすると、その後ろのウィンドウに未描画部分が残ることがある。この現象は親ウィンドウのみ発生して、子ウィンドウでは発生しない。
(2004/12/18)

ウィンドウハンドルの有効チェック(IsWindow)

長時間スワップ時などの高負荷状態では、対象ウィンドウが存在するにもかかわらずウィンドウハンドルが無効の扱いとなり、IsWindow,SendMessage,SendMessageTimeout,GetWindowThreadProcessId等でエラーになる。
※詳細を調べていないが、LPCでやりとりしている雰囲気があるので、自プロセスのウィンドウハンドルなら有効のままだが、別プロセスのウィンドウハンドルが無効になると思われる。

このような高負荷状態でもウィンドウハンドルが有効かどうか判定する方法は、一般的なAPIにはない模様。(試していないが、ToolHelp32やPSAPI辺りを使って該当ウィンドウハンドルが一覧中にあるかどうかを判断すればできるかもしれない。ただし、コストは大きい。)

もし、該当ウィンドウを所有するプロセスが存在しているかどうかを知りたいために、ウィンドウハンドルの有効性を判断するなら、予めプロセスIDを取得して、これの有効性をOpenProcess等を用いて判断したほうが良さそうである。
(2006/04/29)

プラットホーム(Win32/Win32c/Win32s)による動作違い関連

※Win32 は WindowsNT/2000/XP/2003、Win32c は Windows95/98/98SE/Me、Win32s は Windows3.1にWin32sを組み込んだ状態です。

GetWindowRect()
GetWindowPlacement()


(2000/9/2)

TimerProc()(SetTimer/WM_TIMER)


(2000/9/2)

TrackPopupMenu()

TPM_RETURNCMD指定は、Win32sでは無効。
(2000/9/2)
戻り値は、Win32では32bit、Win32cでは16bitに丸められる。
(2004/11/20)

OpenFileMapping()

Win32cでは、名前付ファイルマッピングで既存のマップがないと作成してしまうようだ。
(2000/9/2)

SetClipboardData

Win32ではOSが元のクリップ内容を元に別の形式を自動作成する(ex)CF_TEXTからCF_UNICODETEXT)が、Win32cでは行われない
(2003/3/24)

EnableWindow(HWND hWnd,BOOL bEnable)

Windows95では bEnable は TRUE(1) FALSE(0) のみ解釈し、0,1 以外の値では機能しない。
Windows2000では 0 か 0 以外で解釈する。
(2002/11/24)

MapDialogRect(HWND hwndDlg,LPRECT lprc)

Win32cでは、lprcの中身によってはDivideBy0例外が発生する。
(2006/04/29)

CallNextHookEx(HHOOK hhk,int nCode,WPARAM wParam,LPARAM lParam)

パラメータにあるhhkは、Win32cではSetWindowsHookExの戻り値、Win32では最新の英語版MSDNでは、「Ignored.」になっている。
※一時期の英語版MSDNでは、Win32のみの記載になっていた。
(2007/04/21)

コンソール

Win32c上のコンソールは次の問題がある。
(2000/9/2)

(2001/2/4)

時間管理精度

インターバル処理をするために、最短はどの程度の間隔で行えるかを調べてみた。
※計測、制御、ゲーム目的ではないので、時間自体の精度や、最長呼び出し間隔等は考慮していない。
Windows95Windows98/MEWindowsNT系
(Workstation)
WindowsNT系
(Server)
Windows3.1+Win32s
(NEC PC9801)
Windows98
(NEC PC9801)
Sleep13ms5ms10ms15ms1ms5ms
Sleep
(timeBeginPeriod=1ms)
1ms1ms1ms1ms1ms1ms
timeGetTime1ms1ms10ms15ms1ms1ms
timeGetTime
(timeBeginPeriod=1ms)
1ms1ms1ms1ms1ms1ms
GetTickCount13ms5ms10ms15ms25ms5ms
GetTickCount
(timeBeginPeriod=1ms)
1ms1ms10ms15ms25ms1ms
SetTimer55ms55ms10ms15ms25ms25ms
SetTimer
(timeBeginPeriod=1ms)
55ms55ms10ms15ms25ms25ms
QueryPerformanceFrequency1,193,180Hz1,193,180Hz3,579,545Hz3,579,545Hz1,193,180Hz2,457,600Hz
※55ms は IBM-PC のインターバルタイマ(i8254)由来の値。1,193,180Hzの2の16乗分周値。
※13ms は 55ms の四分の一。1,193,180Hzの2の14乗分周値。
※1,193,180Hzは、IBM-PCのシステムタイマ(i8254)由来の値。3,579,545Hzの3分周値。
※3,579,545Hzは、ACPIタイマー由来の値。この周波数はテレビ放送のNTSCの色副搬送波周波数でもある(発振器が安く、入手しやすい)。
尚、これらの値は、OSが同じでも使用しているハードウェアによって異なる値になることがある。
また、計測値から求めた値であるため、実際とは異なる場合がある。
更に、常に同じ間隔で使えるわけではなく、スレッドのタイムスライス(20ms)等の要因で遅れることもある。このため、正確な間隔を期待してはいけない。
(Windowsでms単位の正確な時間管理を期待するのは、もともとそのようなリアルタイム重視の設計をしていないので見当違いである)
(2006/04/29)
ms 単位で正確な時間を管理する必要がある場合、正確な間隔で処理できることを期待したアルゴリズムを使わず、QueryPerformanceCounter, timeGetTime, GetTickCount 等を用いてより正確な時間差を求め、その時間差に応じた処理を行う必要がある。
また、各カウンタのオーバフローも考慮する必要がある。
(2004/4/11)

パラメータチェック

Win32ではAPIのパラメータチェックが厳密である。
例えば、フラグ指定のパラメータは、対応しているフラグ以外を指定するとエラーになる。また、内部にサイズ指定がある構造体は、厳密にサイズが一致しないとエラーになる(サイズを多めにしてもエラーになる)。ディレクトリパスを格納するバッファのサイズも32K以上を指定するとエラーになる。※32Kは「¥¥?¥〜」を用いたときの最大値
逆に、Win32c/Win32s はこれらのチェックがされていない。

これに関連して、新しいWindowsで追加されたフラグを指定する場合は、古いWindowsでエラーとなるため、どちらでも機能するようにするためにはバージョンによる場合分けが必要になる。
(2005/6/23)

UNICODE

WORD境界

一部のAPIは引数にUNICODE文字列を与えるときに、アラインメントをWORD境界に合わせないと意図する動作をしない。
(2004/10/4)

DLL

ANSI前提のDLLをLoadLibraryWで読み込もうとすると、例外を発生する場合があるようだ。UNxxx.DLLやSusie Plug-inでLoadLibraryWではだめで、LoadLibraryAなら読める例がいくつかあった。
(2004/4/12)

Borland C++

BCC32 で UNICODE 文字列を使う場合に(個人的に?)問題となる挙動。

その他

TRUE/FALSE

BOOL 型のとき、FALSE で比較することは問題無いですが、TRUE で比較すると、TRUEのつもりで様々な真となる値を書いてしまったときに対処できません。
自分でも気をつけているはずだったのですが、ふと grep をかけると、かなりの場所で TRUE で比較してました(^^;。
自信があっても定期的に調べることが大切ですね。
(2000/12/21)

WINMM.DLL / sndPlaySound

sndPlaySound か WINMM.DLL 自体かは調べていないが、WINMM.DLL のロードは秒単位で待たされる場合がある
(2001/2/4)

バッファのチェック

WindowsXP SP2以降で、バッファとそのサイズをパラメータとして指定するAPIは、そのサイズの範囲内でバッファが有効であるかのチェックがされる。つまり、指定したバッファにサイズ分のページが割り当てられていない場合は、エラーになる。
(2008/9/21)
Homeへ戻る
Copyright(c)1997-2008 TORO/高橋 良和 E-mail: ghe00667 @​nifty.com
access counter用