#c #windows #message
#c #Windows #Сообщение
Вопрос:
Я создаю простое окно, но когда я вижу создаваемое окно и закрываю его, сообщение WM_QUIT никогда не получено. Вот некоторый код:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow)
{
cWindowApplication app(hInstance);
const long width = 1024L;
const long height = 768L;
if (app.CreateWindowApplication(width, height) == false)
{
MessageBox(NULL, "Unable to create OpenGL Window", "An error occurred", MB_ICONERROR | MB_OK);
app.DestroyWindowApplication();
return 1;
}
return app.MainLoop();
}
Вот функция CreateWindowApplication(int, int):
bool cWindowApplication::CreateWindowApplication(long width, long height, bool full_screen /*= false*/)
{
DWORD dwExStyle; // Window Extended Style
DWORD dwStyle; // Window Style
mWindowRect.left = 0L; // Set Left Value To 0
mWindowRect.right = width; // Set Right Value To Requested Width
mWindowRect.top = 0L; // Set Top Value To 0
mWindowRect.bottom = height; // Set Bottom Value To Requested Height
mFullScreen = full_screen;
// fill out the window class structure
const char* class_name = "MyClass";
mWindowClass.cbSize = sizeof(WNDCLASSEX);
mWindowClass.style = CS_HREDRAW | CS_VREDRAW;
mWindowClass.lpfnWndProc = cWindowApplication::StaticWindowsProcessCallback;
mWindowClass.cbClsExtra = 0;
mWindowClass.cbWndExtra = 0;
mWindowClass.hInstance = mhInstance;
mWindowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // default icon
mWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // default arrow
mWindowClass.hbrBackground = NULL; // don't need background
mWindowClass.lpszMenuName = NULL; // no menu
mWindowClass.lpszClassName = class_name;
mWindowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // windows logo small icon
// register the windows class
if (!RegisterClassEx(amp;mWindowClass))
{
return false;
}
if (mFullScreen == true) //If we are Full Screen, we need to change the display mode
{
DEVMODE dmScreenSettings; // device mode
memset(amp;dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = width; // screen width
dmScreenSettings.dmPelsHeight = height; // screen height
dmScreenSettings.dmBitsPerPel = BITS_PER_PIXEL; // bits per pixel
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if (ChangeDisplaySettings(amp;dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
// setting display mode failed, switch to windowed
MessageBox(NULL, "Display mode failed", NULL, MB_OK);
mFullScreen = false;
}
}
if (mFullScreen == true) // Are We Still In Full Screen Mode?
{
dwExStyle = WS_EX_APPWINDOW; // Window Extended Style
dwStyle = WS_POPUP; // Windows Style
//ShowCursor(false); // Hide Mouse Pointer
}
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style
dwStyle = WS_OVERLAPPEDWINDOW; // Windows Style
}
AdjustWindowRectEx(amp;mWindowRect, dwStyle, false, dwExStyle); // Adjust Window To True Requested Size
// class registered, and create our window
mHWND = CreateWindowEx(NULL, // extended style
class_name, // class name
"My Windows", // application name
dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, // x,y coordinate
mWindowRect.right - mWindowRect.left,
mWindowRect.bottom - mWindowRect.top, // width, height
NULL, // handle to parent
NULL, // handle to menu
mhInstance, // application instance
this); // this pointer to call member functions
// check if window creation failed (hwnd would equal NULL)
if (mHWND == false)
{
return false;
}
mHDC = GetDC(mHWND);
ShowWindow(mHWND, SW_SHOW); // display the window
UpdateWindow(mHWND); // update the window
return true;
}
По сути, после вызова этой функции функция CreateWindowEx() вызовет StaticWindowProcessCallback(), которая выглядит следующим образом:
LRESULT cWindowApplication::StaticWindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
cWindowApplication* win_app = NULL;
if (msg == WM_CREATE)
{
//Creation event
//Get the pointer we pass during CreateWindowApplication() call
win_app = (cWindowApplication*)((LPCREATESTRUCT)lParam)->lpCreateParams;
//Associate window pointer with the hwnd for the other events to access
SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)win_app);
}
else
{
//Non-creation event
win_app = (cWindowApplication*)GetWindowLongPtr(wnd, GWLP_USERDATA);
if (win_app != NULL)
{
return DefWindowProc(wnd, msg, wParam, lParam);
}
}
//call member
return win_app->WindowsProcessCallback(wnd, msg, wParam, lParam);
}
Наконец, последняя строка этой функции вызывает функцию-член WindowProcessCallback(), которая выглядит следующим образом:
LRESULT cWindowApplication::WindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
mHDC = GetDC(wnd);
SetupPixelFormat();
//Set the version that we want, in this case 3.0
int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0 }; //zero indicates the end of the array
//Create temporary context so we can get a pointer to the function
HGLRC tmp_context = wglCreateContext(mHDC);
//Make it current
wglMakeCurrent(mHDC, tmp_context);
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
if (wglCreateContextAttribsARB == NULL)
{
//No OpenGL 3.0, back to 2.0
mHGLRC = tmp_context;
}
else
{
//Create OpenGL 3.0
mHGLRC = wglCreateContextAttribsARB(mHDC, 0, attribs);
//Delete the temp context
wglDeleteContext(tmp_context);
}
//Make OpenGL 3.0
wglMakeCurrent(mHDC, mHGLRC);
mIsRunning = true;
}
break;
case WM_QUIT:
case WM_DESTROY:
case WM_CLOSE:
wglMakeCurrent(mHDC, NULL);
wglDeleteContext(mHGLRC);
mIsRunning = false;
PostQuitMessage(0); //Send a WM_QUIT message
return 0;
default:
break;
}
return DefWindowProc(wnd, msg, wParam, lParam);
}
Как вы можете видеть, там есть некоторый код обработки сообщений… но, кроме WM_CREATE, никаких других случаев не происходит. После отправки сообщения WM_CREATE вызывается функция MainLoop(), которая выглядит следующим образом:
int cWindowApplication::MainLoop()
{
while (mIsRunning == true)
{
ProcessWindowsMessages();
}
DestroyWindowApplication();
return 0;
}
По сути, функция ProcessWindowsMessages() не получает никаких сообщений после закрытия окна… Я должен нажать stop VS от запуска, чтобы остановить процесс. Функция ProcessWindowsMessages() выглядит следующим образом:
void cWindowApplication::ProcessWindowsMessages()
{
MSG msg;
if (PeekMessage(amp;msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(amp;msg);
DispatchMessage(amp;msg);
}
}
Комментарии:
1. Вероятно, потому, что вы должны были найти это с помощью отладчика, если бы вы знали, как он должен был себя вести. Когда вы вводите сторонний код, убедитесь, что вы его понимаете.
2. Если это так, то этот сайт не нужен.
3. Вы можете прочитать некоторые часто задаваемые вопросы. Этот сайт не предназначен для справки по отладке. Проблема с вашим вопросом заключается в том, что даже если у кого-то в будущем возникнет точно такая же проблема, он не сможет найти ваш вопрос и мое решение. (Не ваша вина, я тоже не могу придумать хороший заголовок или поисковые запросы)
4. Хорошо, я буду стараться изо всех сил. Это просто сложно, когда вы пытались отлаживать несколько дней подряд и все еще не знаете, что не так.
5. В любом случае, рад, что он теперь работает.
Ответ №1:
Эта логика StaticWindowsProcessCallback
выглядит в обратном направлении:
if (win_app != NULL)
{
return DefWindowProc(wnd, msg, wParam, lParam);
}
Если у вас нет указателя на объект window wrapper, вам нужно вызвать DefWindowProc
. Так что это должно произойти if (win_app == NULL)
. Это делается для обработки нескольких сообщений, которые были отправлены ранее WM_CREATE
. В результате этого ваш код имеет неопределенное поведение для сообщений, обработанных ранее WM_CREATE
, и отбрасывает (применяя обработку по умолчанию) все сообщения после WM_CREATE
.
Однако было бы еще лучше использовать WM_NCCREATE
для настройки ссылки. Кроме того, win_app
это не очень хорошее название для этого, может win_obj
быть, или что-то в этом роде.
Вы также не должны обрабатывать WM_QUIT
в своей оконной процедуре, поскольку она не отправляется в окно. И поведение по умолчанию WM_CLOSE
должно быть в порядке, оно вызовет DestroyWindow
, который сработает WM_DESTROY
.
Но первый, неспособность пересылать какие-либо сообщения после WM_CREATE
вашей оконной процедуры, вероятно, объясняет ваше отсутствие WM_QUIT
в основном цикле сообщений.
Комментарии:
1. Спасибо, это все объясняет. Я просматривал код из другого источника, и у них был if (!win_app) … Я перевел это неправильно.