Win32API

真偽不明の昔のメモ。


(新)2023年現在

(旧) traditional
ストアアプリ以前のものをtraditionalと呼んでいると思われる。

WinMain

新型の方を参照(2023年現在)
①WNDCLASS作る。その際コールバックを設定(WindowProc)
飛んできたメッセージを実際に処理するのはコールバック。
②RegisterClassする。
③CreateWindowする。
④メッセージループでTranslateMessageしてDispatchMessageする。
このループはシステムからメッセージを受け取ってコールバックに渡す。
⑤WindowProcでWM_PAINTを処理し、BeginPaintするのが基本。

WNDCLASSにセットしたコールバックを呼んでるのはDispatchMessage。

Notice that the program does not explicitly call the WindowProc function, even though we said this is where most of the application logic is defined. Windows communicates with your program by passing it a series of messages. The code inside the while loop drives this process. Each time the program calls the DispatchMessage function, it indirectly causes Windows to invoke the WindowProc function, once for each message.
このプログラムでは、WindowProc関数を明示的に呼び出していないことに注意してください。Windows は一連のメッセージを渡すことで、プログラムと通信を行います。whileループの中のコードがこのプロセスを駆動します。プログラムが DispatchMessage 関数を呼び出すたびに、間接的に Windows に WindowProc 関数を呼び出させています。

MSDNの旧型はShowWindow直後にUpdateWindowを実行しているが、
新型にはこれがない。不要なのかやってないだけなのかは不明。
WindowProcでWM_PAINTメッセージを処理しているなら、多分UpdateWindowすべき。

#ifndef UNICODE
#define UNICODE
#endif 

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI wWinMain(
  HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    // Register the window class.
    const wchar_t CLASS_NAME[]  = L"Sample Window Class";
    
    WNDCLASS wc = { };

    wc.lpfnWndProc   = WindowProc;
    wc.hInstance     = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    // Create the window.

    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        L"Learn to Program Windows",    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style

        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
        );

    if (hwnd == NULL)
    {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);

    // Run the message loop.

    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}


要点だけかいつまむとこう。

WNDCLASS wc = {};
wc.lpfnWndProc   = WindowProc;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx();
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}


WinMain関数の細かなこと。

int WINAPI wWinMain(
  HINSTANCE hInstance, 
  HINSTANCE hPrevInstance, 
  PWSTR pCmdLine, 
  int nCmdShow);

WINAPI

この型は WinDef.h で次のように宣言されています。

#define WINAPI __stdcall

defineはマクロ
WINAPIは__stdcallに変換される。
__stdcallは呼び出し規約

__stdcall


HINSTANCE

typedefはマクロの逆。HANDLEはHINSTANCEとして定義される。
HINSTANCEはHANDLEに変換され、HANDLEはvoid*に変換される。

typedef HANDLE HINSTANCE;

HはHandleのH。接頭語。プレフィックス。

HANDLE

typedef void *HANDLE;

認識としては一意の識別子くらいと思ってよいと思われる。


PWSTR

16 ビット Unicode 文字の null で終わる文字列へのポインター。
この型は、次のように WinNT.h で宣言されています。

typedef WCHAR *PWSTR;

WCHAR

16 ビット Unicode 文字。
この型は WinNT.h で次のように宣言されています。

typedef wchar_t WCHAR;

Pはポインタ
Lはロング
Wはワイド
_tは接尾語。サフィックス。typedefのtと思われる。
C言語の頃はtypedefされていたがC++では予約語になった。

char
8bit 1byte
これはCとC++の規格である。

wikipediaより
一方、wchar_t 型は C および C++ 双方の標準規格において、符号付きの場合は少なくとも-127から+127まで、符号無しの場合は少なくとも0から255までの範囲を表現できる数値型[3]、ということのみ定められている(少なくともchar型と同じ大きさが必要)。

WNDCLASS

ウィンドウズプログラムはウィンドウがないとプログラムが始まらないのでウィンドウについてあれこれ決める。

多分これ

typedef WINDCLASSA WNDCLASS;

WNDCLASSA
この構造体はWNDCLASSEXに置き換えられる。
EX系はメンバが2つ、cbSizeとhIconSmが増える。

cbSizeはどのみちsizeofされる。

wc.cbSize = sizeof(WNDCLASSEX);

おそらくかつてWNDCLASSがあって、
WNDCLASSEXが増えて、
その後WNDCLASSが改良されて(うまいことWNDCLASSEXに変換されるようになって)、WNDCLASSでも普通に動くようになっているのではないかと
推測されるが不明。
少なくとも適当に調べてEXにすべきみたいな論は見受けられない。

typedef struct tagWNDCLASSA {
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCSTR    lpszMenuName;
  LPCSTR    lpszClassName;
} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;

他にWNDCLASSEX系として
WNDCLASSEXA
WNDCLASSEXW
などがある。

サフィックス
AはANSI
Wはワイド、すなわちユニコード対応
と、思われる。

ShowWindow

UpdateWindow

WM_PAINTを発行する。

WindowProc

基本はWM_PAINTを処理すること。
次にどのタイミングでWM_PAINTを発生させるかを考える事。

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            // All painting occurs here, between BeginPaint and EndPaint.

            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));

            EndPaint(hwnd, &ps);
        }
        return 0;

    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


マルチメディア

マルチメディア
色んなメディア。例えばテキスト、画像、音楽、動画。

MMTIME

色んなメディアが同期するためのもの。

UINT wType;
フォーマットを示す整数値。各マクロはmmsystem.hあたりに定義。

timeSetEvent

一定時間ごとにタイマーイベントを発行する。
廃止された関数。

MMRESULT timeSetEvent( 
   UINT           uDelay, 
   UINT           uResolution, 
   LPTIMECALLBACK lpTimeProc, 
   DWORD_PTR      dwUser, 
   UINT           fuEvent 
);

uDelay時間毎にイベントが発行される。単位はミリ秒。1000で1秒。
uResolution : 精度。単位はミリ秒。
1ならプラスマイナス1ミリ秒の誤差になるよう努力する。。
0の場合は、周期的なイベントを可能な限り正確に発生させる必要があることを示します。
lpTimeProc : コールバック
dwUser : コールバックに渡すデータ。

fuEvent : モード。
TIME_ONESHOT Event occurs once, after uDelay milliseconds.
TIME_PERIODIC Event occurs every uDelay milliseconds.

MCI

Media Control Interface?

オーディオおよびビジュアル周辺機器を制御するためのデバイスに依存しない機能をアプリケーションに提供します。



タイマーキュー


CreateTimerQueueTimer

新型。タイマーの有効期限が切れると、コールバック関数が呼び出されます。

int main()
{
    HANDLE hTimer = NULL;
    HANDLE hTimerQueue = NULL;
    int arg = 123;

    // Use an event object to track the TimerRoutine execution
    gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (NULL == gDoneEvent)
    {
        printf("CreateEvent failed (%d)\n", GetLastError());
        return 1;
    }

    // Create the timer queue.
    hTimerQueue = CreateTimerQueue();
    if (NULL == hTimerQueue)
    {
        printf("CreateTimerQueue failed (%d)\n", GetLastError());
        return 2;
    }

    // Set a timer to call the timer routine in 10 seconds.
    if (!CreateTimerQueueTimer( &hTimer, hTimerQueue, 
            (WAITORTIMERCALLBACK)TimerRoutine, &arg , 10000, 0, 0))
    {
        printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
        return 3;
    }

    // TODO: Do other useful work here 

    printf("Call timer routine in 10 seconds...\n");

    // Wait for the timer-queue thread to complete using an event 
    // object. The thread will signal the event at that time.

    if (WaitForSingleObject(gDoneEvent, INFINITE) != WAIT_OBJECT_0)
        printf("WaitForSingleObject failed (%d)\n", GetLastError());

    CloseHandle(gDoneEvent);

    // Delete all timers in the timer queue.
    if (!DeleteTimerQueue(hTimerQueue))
        printf("DeleteTimerQueue failed (%d)\n", GetLastError());

    return 0;
}


BOOL CreateTimerQueueTimer(
  [out]          PHANDLE             phNewTimer,
  [in, optional] HANDLE              TimerQueue,
  [in]           WAITORTIMERCALLBACK Callback,
  [in, optional] PVOID               Parameter,
  [in]           DWORD               DueTime,
  [in]           DWORD               Period,
  [in]           ULONG               Flags
);

TimerQueueTimerはTimerQueueに格納されるTimerと思われる。

phNewTimer : 作成されたTimerQueueTimer
TimerQueue : TimerQueueTimerを格納するTimerQueue
Callback : コールバック。
Parameter : コールバックに渡すデータ。
DueTime : 初回のイベント発行までの時間。
Period : 作成するタイマーの期間 (ミリ秒単位)。
このパラメーターが 0 の場合、タイマーは 1 回だけ通知されます。 このパラメーターが 0 より大きい場合、タイマーは定期的です。






この記事が気に入ったらサポートをしてみませんか?