プロが教えるわが家の防犯対策術!

Windows API の勉強を始めた者です。

CreateWindow関数を使ったときに発行されるWM_CREATEについて質問させていただきます。
以下のようなプログラムを作りました。このプログラムはウインドウを作るプログラムなのですが、はじめにウインドウを作るかどうかをWindowProcの中で聞いています。
このプログラムでウインドウを作らないという選択肢をとった時が分かりません。
WindowProcでは return -1 をするのでウインドウは作られず、従ってhWndにはNULLが入ります。そして次の文で、
 if (hWnd == NULL) {
  MessageBox(NULL,TEXT("CreateWindow failed"),NULL,MB_OK);
  return 0;
 }
メッセージボックスが表示されてからプログラムが終了すると思ったのですが、実際にはメッセージボックスは表示されませんでした。
なぜでしょうか。return -1 をした時点で、ウインドウが作られないのでWM_DESTROYがウインドウプロシージャにSendされるのかと考えたのですが、そうなのでしょうか。
よろしくお願いします。

/* 作ったプログラム */
#include <windows.h>

LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 int YESNO;

 switch(uMsg) {
 case WM_DESTROY:
  PostQuitMessage(0);
 return 0;
 case WM_CREATE:
 YESNO = MessageBox(NULL, TEXT("Create a new window?"),
       ((LPCREATESTRUCT)lParam)->lpszName, MB_YESNO);
 if (YESNO == IDYES)
  return 0;
 if (YESNO == IDNO)
  return -1;
 }
 return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance,
          HINSTANCE hPrevInstance,
          PSTR lpCmdLine,
           int nCmdShow)
{
 WNDCLASS wc;
 ATOM atom;
 MSG msg;
 HWND hWnd;

 wc.style = CS_HREDRAW| CS_VREDRAW;
 wc.lpfnWndProc = WindowProc;
 wc.cbClsExtra = 0;
 wc.cbWndExtra = 0;
 wc.hInstance = hInstance;
 wc.hIcon = NULL;
 wc.hCursor = NULL;
 wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND + 1;
 wc.lpszMenuName = NULL;
 wc.lpszClassName = TEXT("sample_window");

 if ((atom = RegisterClass(&wc)) == 0) {
  MessageBox(NULL,TEXT("RegisterClass failed"),NULL,MB_OK);
  return 0;
 }

 hWnd = CreateWindow((LPCTSTR)atom, TEXT("Sampe window"),
             WS_OVERLAPPEDWINDOW | WS_VISIBLE,
             100, 100, 400, 300,
             NULL, NULL, hInstance, NULL);
             
 if (hWnd == NULL) {
  MessageBox(NULL,TEXT("CreateWindow failed"),NULL,MB_OK);
  return 0;
 }

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

 return msg.wParam;

}

A 回答 (4件)

 こんばんは。

御礼頂きました。

>>WM_CREATEメッセージが送られた後にWM_DESTROYが送られるのは、なぜなのでしょうか?return -1 をすることでウインドウが破棄されるのでDestroyWindow()関数が使われるためなのでしょうか。
 
 に関してですが、流石に当方もWindows OSの実装者では無いので其処までは分かりません・・・。
 ただ、想像する分には、そのサイトの一番下にも書いて有る様に、「GetMessage()に到達するまでの間に必要なコールバックはKiUserCallbackDispatcher()を介して行われる」と言うのですから、その中でWM_CREATEを呼び出して戻り値を確認し、エラー値が返された場合は即座にWM_DESTROYを呼び出しているのではないでしょうか。
 此れならば、GetMessage()に到達していなくても、WM_DESTROY位は呼び出せる筈です。
 単純な関数ポインタの呼び出し見たいな感覚で行われる物なのかもしれません。

>>もう一点に関して

 取り敢えずPostQuitMessage()をコメントアウトしてWM_CREATEに失敗させると(メッセージループは無くても同じ)、MessageBox()が表示されました(Windows2000 sp4 VC++6.0で試しています)。
 確かにPostQuitMessage()があると、MessageBox()が表示されなかったです(ただしサウンドだけは鳴った)。

 今まで色々とWindows関連のサイトなどを見てきましたが、流石にココまでメスを入れていたものは見た事がありません。
 海外から輸入されて来ているWindows関連の詳しい書籍であれば、こう言った詳細が書かれているかもしれません。
    • good
    • 0
この回答へのお礼

回答をありがとうございます。

回答者様のおっしゃる通り、PostQuitMessage()をコメントアウトしてWM_CREATEを失敗させるとMessageBoxが表示されました。ということは確かにWM_DESTROYのケースが行われている訳ですが、PostQuitMessage()が存在する場合でもMessageBoxのサウンドだけなるということは、やはり
if (hWnd == NULL)
のところのreturn 0; でプログラムが終了しているようです。

なぜ表示されないかについては興味深いのでもう少し他の書籍で勉強してみたいと思います。

何度もの詳しい回答、参考ページの紹介など、いろいろ本当にありがとうございました。

お礼日時:2009/03/28 22:53

MessageBoxは呼ばれてますよ


デバッガでステップ実行でもすれば(しなくてもですけど)ifに入ってメッセージボックスが出る音が出てます
戻り値もGetLastErrorもエラー無しな値しか返しませんし表示されない理由もわかりませんけど
    • good
    • 0
この回答へのお礼

回答をありがとうございます。

回答者様のおっしゃるとおり、メッセージボックスの音が出ていました。
これはつまり、メッセージボックスは表示はされていないが、
if (hWnd == NULL)
のところのreturn 0; でプログラムが終了しているのですね。

なぜ表示されないかについてはいろいろ調べてみたいと思います。
回答をありがとうございました。

お礼日時:2009/03/28 22:56

 こんにちは。



 WM_DESTROYの中にMessageBox()を置きます。
 また、WM_CREATEはCreateWindow()APIの内部でメッセージループに先立って呼び出される物です。

 http://keicode.com/windows/win01.php

 ですのでWM_CREATEで失敗させる分には

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

 が無くてもWM_DESTROYが呼び出されます。
 WM_DESTROYの下にあるPostQuitMessage()が呼び出されて終了したため、MessageBox()が出なかったのだと思います。

 また、WM_CREATEが呼び出される順番は、

 (1)WM_GETMINMAXINFO
 (2)WM_NCCREATE
 (3)WM_NCCALCSIZE
 (4)WM_CREATE

 と言う事で、四番目に位置します。
    • good
    • 0
この回答へのお礼

回答をありがとうございます。
参考ページをありがとうございます。いろいろ細かいところまであり参考になります。
なお、気になった点があるのですが、どうなのでしょうか。

WM_CREATEメッセージが送られた後にWM_DESTROYが送られるのは、なぜなのでしょうか?return -1 をすることでウインドウが破棄されるのでDestroyWindow()関数が使われるためなのでしょうか。

もう一点は、WM_DESTROYメッセージが送られた後、PostQuitMessage()関数が呼びだされます。
これはWM_QUITをメッセージキューにポストします。WM_QUITはGetMessage()関数にメッセージループの終了を通知するもので対象がウインドウプロシージャではないと思うのですが、もしそうするとメッセージキューからWM_QUITメッセージを取り出すにはGetMessage()関数が必要で、このプログラムが終了するにはWM_QUITを取り出さなければなりません(もしくはreturn 0 に到達する)。しかし、それ以前にメッセージボックスを出す命令がありますから、やはりメッセージボックスが出ると思うのですが、どうでしょうか?
よろしくお願いします。

お礼日時:2009/03/28 19:05

WindowProc はメッセージループに入ってから呼び出される物として記憶しています。

つまりWM_CREATEが生じたときには既に

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

の中にいるのではないかと。

WM_CREATEで判断している以上、メッセージボックスを出したいのであればWM_DESTROYの分岐で書くのが自然な気がします。
    • good
    • 0
この回答へのお礼

素早い回答をありがとうございます。

私の方では、CreateWindow()関数はメッセージループに入る前の段階でウインドウプロシージャにメッセージを送ると考えています。
このプログラムではメッセージループの前の段階でコメントボックスを出すように書かれているため、どうやらこのプログラムはメッセージループに入ることなく終了しているようなのです。どうもその辺りの動きが分かりません。

回答をありがとうございました。

お礼日時:2009/03/28 19:18

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!