I need a Windows native window handle for a renderer, but I'm struggling to poll events correctly.
First, I create a window, which works fine on its own:
WNDPROC Window::MakeWindow( LPCWSTR _title, unsigned int _width, unsigned int _height ) {
HINSTANCE hInstance = GetModuleHandle( NULL );
HWND hwnd;
//Step 1: Registering the Window Class
m_WindowClass.cbSize = sizeof(WNDCLASSEX);
m_WindowClass.style = 0;
m_WindowClass.lpfnWndProc = WindowProc;
m_WindowClass.cbClsExtra = 0;
m_WindowClass.cbWndExtra = 0;
m_WindowClass.hInstance = hInstance;
m_WindowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
m_WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
m_WindowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
m_WindowClass.lpszMenuName = NULL;
m_WindowClass.lpszClassName = (LPCWSTR)g_szClassName;
m_WindowClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&m_WindowClass))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
0, // Optional window styles.
(LPCWSTR)g_szClassName, // Window class
_title, // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT,
_width, _height,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if(hwnd == NULL)
{
MessageBox(NULL, L"Window Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, 1);
UpdateWindow(hwnd);
PollEvents();
return NULL;
}
After creating the window, I want to check for user inputs. In the code snippets I copied, they did it like this:
void PollEvents() {
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
But, since this blocks my code, I tried using a separate thread to do this. So, at the end of my window creation, I create a thread like so:
m_PollThread = new std::thread(PollEvents);
To test if it's working, I wrote this main() function:
int main() {
// poll thread is created here
Window* window = new Window( "Test Window", 1024, 720 );
while (true) {
Sleep(10);
};
// poll thread is closed/awaited here
delete window;
}
But, the window ends up frozen, so just the while loop is executed while the other thread seems to do nothing.
Only the thread that creates a window can receive messages for that window. You cannot create a window in one thread and then receive messages for that window in another thread.
GetMessage() blocks until it receives a message, so your code freezes because you are calling GetMessage() in a thread that has no window to receive messages for, and the thread that created the window is not processing any messages for that window.
So, if you want to poll events periodically, in the creating thread, without blocking your code, use PeekMessage() instead of GetMessage(), eg:
void PollEvents() {
MSG Msg;
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
Otherwise, you will have to move both the window creation and the event loop into your worker thread.
Related
Experimenting with attempts to create a Win32 window with minimal code I discovered a strange (perhaps undocumented) behavior. If the programmer omits defining a cursor in the WNDCLASSEX struct or defines the cursor as:
WNDCLASSEX wc
...
wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
The window will be displayed but never activate unless the user hovers the mouse over a border or the title bar. I found out that Windows was constantly sending WM_SETCURSOR messages as well as the normal messages expected during application startup and window creation when over the client area. I never noticed this in the past until I created a window that was initially borderless. That window displayed a continuous "wait" cursor (spinning circle). This behaviour can be reproduced with the following code. Since I could not find any explanation on MSDN or other sites, I just wanted to put this out there for others to find. To see this, run this code and make sure the cursor is over the client area when the window is created.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
int main(int argc, char** argv)
{
HINSTANCE hinstance(GetModuleHandleW(0));
WNDCLASSEXW wc({ 0 });
wc.cbSize = sizeof(WNDCLASSEXW);
//wc.style = CS_HREDRAW | CS_VREDRAW;// | CS_OWNDC; // unneeded
wc.lpszClassName = L"classname";
wc.lpfnWndProc = WndProc;
wc.hInstance = hinstance;
//wc.hbrBackground = HBRUSH(COLOR_WINDOW + 1); // unneeded
//wc.hIcon = LoadIconW(0, IDI_APPLICATION); // unneeded
//wc.hIconSm = wc.hIcon; // unneeded
//wc.hCursor = LoadCursorW(hinstance, IDC_ARROW); // will not activate properly
//wc.hCursor = LoadCursorW(0, IDC_ARROW); // needed for proper activation
if (!RegisterClassExW(&wc)) {
DWORD err = GetLastError();
return -1;
}
DWORD style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
HWND handle = CreateWindowExW(0, L"classname", L"Test Window"
, style, CW_USEDEFAULT, CW_USEDEFAULT
, CW_USEDEFAULT, CW_USEDEFAULT, 0
, (HMENU)0, hinstance, 0);
if (!handle)
return -1;
MSG msg;
while (1) {
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
return ((int)msg.wParam);
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
return 0;
}
I'm following a tutorial for directx12 and can't even get over the creation of window D: There is an implemented error window which tells me that there was an error when creating the window i actually want to create.
It's supposed to mean that it was "unsuccessful in getting a window handle"...
I don't have anything as much as a clue of what that would mean but i would GREATLY appreciate some help about that.
#include "stdafx.h"
//Handle to the window
HWND hwnd = NULL;
//Name of the window
LPCTSTR WindowName = L"GameEngineApp";
//Title of the window
LPCTSTR WindowTitle = L"My Game Engine";
//Width and height of the window
int Width = 800;
int Height = 600;
//Toggle for fool screen
bool FullScreen = false;
//Toggle for window creation
bool InitializeWindow(HINSTANCE hInstance,
int ShowWnd, int width, int height, bool fullscreen);
//Main application loop
void mainLoop()
{
//The message var hold any windows message received through the
//PeekMessage function
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
//If there is a message, it is translated
//then dispatched so Windows
//knows the program hasn't stopped working
if (msg.message = WM_QUIT) {
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
//If there is no message the game
//code will run and the loop will continue
}
}
}
}
//Callback function for windows messages
LRESULT CALLBACK WndProc(HWND hWnd,
UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) {
if (MessageBox(0, L"Are you sure you want to exit?",
L"Really?", MB_YESNO | MB_ICONQUESTION) == IDYES)
DestroyWindow(hwnd);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,
msg, wParam, lParam);
}
//Main Windows function
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
//Toggle window creation
if (!InitializeWindow(hInstance, nShowCmd, Width, Height, FullScreen))
{
MessageBox(0, L"Window Initialization - Failed",
L"Error", MB_OK);
return 0;
}
mainLoop(); //Call main loop to start
return 0;
}
bool InitializeWindow(HINSTANCE hInstance,
int ShowWnd, int width, int height, bool fullscreen)
{
//Check if fullscreen is needed and set to fullscreen if so
if (fullscreen)
{
HMONITOR hmon = MonitorFromWindow(hwnd,
MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = { sizeof(mi) };
GetMonitorInfo(hmon, &mi);
width = mi.rcMonitor.right - mi.rcMonitor.left;
height = mi.rcMonitor.bottom - mi.rcMonitor.top;
}
//Window class structure
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2);
wc.lpszMenuName = NULL;
wc.lpszClassName = WindowName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
//Registering the window class
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Error registering class",
L"Error", MB_OK | MB_ICONERROR);
return false;
}
//Create the window with the now registered window class
hwnd = CreateWindowEx(NULL,
WindowName,
WindowTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
width, height,
NULL,
NULL,
hInstance,
NULL);
//Return error msg if unsuccessful in getting a window handle
if (!hwnd)
{
MessageBox(NULL, L"Error creating window",
L"Error", MB_OK | MB_ICONERROR);
return false;
}
//Removing the style from the window when fullscreen
if (fullscreen)
{
SetWindowLong(hwnd, GWL_STYLE, 0);
}
//Showing and updating the window
ShowWindow(hwnd, ShowWnd);
UpdateWindow(hwnd);
return true;
}
I've tried to change CreateWIndowEx to CreateWindowA and that's really the only thing that seemed to possibly have an effect but i did not see any results.
I've tried printing the error but there is no error.
Creating a window in Windows with the C++ code so i can make a game engine.
Though shalt not abuse global variables.
You're calling DefWndProc in your WndProc with hwnd as the handle argument. But that isn't set until the return of CreateWindowEx, and there are plenty very important messages that are rifled through WndProc during window stand-up. Those messages need the window handle, and you're not passing it along. Instead you're just passing NULL (the startup value of hwnd).
Change all hwnd to hWnd (the parameter name) in your WndProc
The remainder of the problems mentioned in comments I leave for you to address.
I am writing the beginnings of a game as a Win32 application. Everything worked fine when I registered the class within Main.cpp, but now I'm trying to move it to a Game class to make the code easier for me to use.
Since I have moved the code into the Game class, window class registration fails. Here is my code, please note that some functions are empty simply because I haven't got that far yet.
GetLastError() returns 87.
Main.cpp
#include "Main.h"
// entry point for the program, see Game for explanation of parameters
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
game = std::unique_ptr<Game>(new Game(hInstance, hPrevInstance, lpCmdLine, nCmdShow));
game->Init();
// main game loop
while(game->IsRunning())
{
game->HandleMessages();
game->Update(0.0f);
game->Render();
}
return EXIT_SUCCESS;
}
Game.cpp
#include "Game.h"
#include <Windows.h>
LPCWSTR g_szClassName = L"Life Simulator Window Class";
Game::Game(HINSTANCE _hInstance, // handle to an instance of the application
HINSTANCE _hPrevInstance, // handle to previous instance of the application
LPSTR _lpCmdLine, // command line parameters
int _nCmdShow) // controls how the window is show)
{
hInstance = _hInstance;
hPrevInstance = _hPrevInstance;
lpCmdLine = _lpCmdLine;
nCmdShow = _nCmdShow;
return;
}
Game::~Game(void)
{
}
bool
Game::Init()
{
// set paramaters for window class
wc.cbClsExtra = 0; // number of extra bytes to allocate after window class, not needed but showing for verbosity
wc.cbSize = sizeof(WNDCLASSEX); // stores the size of the WNDCLASSEX structure, helping future proof your application in case new fields are added
wc.cbWndExtra = 0; // similar to cbClsExtra, but refers to the window itself rather than the window class
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW); // handle to a background brush, in this case it's simply a colour cast into a brush handle
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // handle to cursor, first paramater is a handle to the instance of the application containing the cursor (not needed in this case), second is the resource identifier
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // similar to hCursor, but for the application icon instead
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // as above, but for the smaller version of the icon
wc.hInstance = hInstance; // handle to the instance of the application that contains the window procedure
wc.lpfnWndProc = Game::WndProc; // a pointer to the window procedure
wc.lpszClassName = g_szClassName; // the window class name (see global variables)
wc.lpszMenuName = NULL; // specifies the resource name of the menu, which isn't used in this case
wc.style = CS_HREDRAW | CS_VREDRAW; // style for the window class, which in this case means to redraw if it's affected (i.e. resized or moved) vertically or horizontally
// register the window class
if(!RegisterClassEx(&wc))
{
// this code is executed if the window class fails to register successfully
MessageBox(NULL, // an owner for the message box can be specified here
L"Window Class Registation Failed.", // message to be displayed
L"Fatal Error", // title of the message box
MB_ICONEXCLAMATION | MB_OK); // type of message box, in this case it has an exclamation icon and an OK button
return EXIT_FAILURE; // return EXIT_FAILURE to indicate that the program closed due to a runtime error
}
// create the window
hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, // extended window style
g_szClassName, // class of window to be created (this is the window class created earlier)
L"Life Simulator", // title of the window
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // x position of the window, here default values are used
CW_USEDEFAULT, // as above, but for the y position
wndWidth, // width of the window
wndHeight, // height of the window
NULL, // parent window, if it has one
NULL, // handle to the menu for the window
hInstance, // handle to the instance of the application
NULL); // lpParam can be passed on here
if(hWnd == NULL)
{
// this code is executed if the creating the window failed
MessageBox(NULL, // an owner for the message box can be specified here
L"Window Creation Failed.", // message to be displayed
L"Fatal Error", // title of the message box
MB_ICONEXCLAMATION | MB_OK); // type of message box, in this case it has an exclamation icon and an OK button
return EXIT_FAILURE; // return EXIT_FAILURE to indicate that the program closed due to a runtime error
}
ShowWindow(hWnd, // handle to the window to be shown
nCmdShow); // passed on from WinMain, controls how the window should be shown (i.e. minimised or maximised)
UpdateWindow(hWnd); // forces the window the be updated by forcing a WM_PAINT message past the application queue
}
// window procedure for the game
LRESULT CALLBACK
Game::WndProc(HWND hWnd, // handle to the window
UINT msg, // message to be processed
WPARAM wParam, // additional message information
LPARAM lParam) // even more additional message information
{
switch(msg)
{
case WM_CLOSE: // red X has been clicked
DestroyWindow(hWnd); // sends WM_DESTROY to the window
break;
case WM_DESTROY: // some part of the program has requested the window to be destroyed
PostQuitMessage(0); // sends quit message to window
break;
default: // unhandled messages
return DefWindowProc(hWnd, msg, wParam, lParam);// windows will handle any messages that haven't been handled explicitly
}
return 0;
}
void
Game::HandleMessages()
{
while(PeekMessage(&msg, // container for the message
NULL, // when multiple windows are used, you can specify which one here
0, // used to filter messages, not needed here
0, // as above
PM_REMOVE)) // remove messages after they've been processed
{
TranslateMessage(&msg); // turns virtual key messages into character messages
DispatchMessage(&msg); // sends the message on to its window procedure (i.e. WndProc)
}
return;
}
void
Game::Update(float elapsedTime)
{
return;
}
void
Game::Render()
{
return;
}
Previously working Main.cpp
#include <Windows.h>
// global variables
LPCWSTR g_szClassName = L"Life Simulator Window Class"; // the L casts the string to a wide string and it is called g_szClassName by convention, making the code easier to read for others
static const int wndHeight = 800;
static const int wndWidth = 600;
// window procedure for the program
LRESULT CALLBACK WndProc(HWND hWnd, // handle to the window
UINT msg, // message to be processed
WPARAM wParam, // additional message information
LPARAM lParam) // even more additional message information
{
switch(msg)
{
case WM_CLOSE: // red X has been clicked
DestroyWindow(hWnd); // sends WM_DESTROY to the window
break;
case WM_DESTROY: // some part of the program has requested the window to be destroyed
PostQuitMessage(0); // sends quit message to window
break;
default: // unhandled messages
return DefWindowProc(hWnd, msg, wParam, lParam);// windows will handle any messages that haven't been handled explicitly
}
}
// entry point for the program
int WINAPI WinMain(HINSTANCE hInstance, // handle to an instance of the application
HINSTANCE hPrevInstance, // handle to previous instance of the application
LPSTR lpCmdLine, // command line parameters
int nCmdShow) // controls how the window is show
{
// initialise variables
HWND hWnd; // handle to window
WNDCLASSEX wc; // window class container
MSG msg; // window message container
// set paramaters for window class
wc.cbClsExtra = 0; // number of extra bytes to allocate after window class, not needed but showing for verbosity
wc.cbSize = sizeof(WNDCLASSEX); // stores the size of the WNDCLASSEX structure, helping future proof your application in case new fields are added
wc.cbWndExtra = 0; // similar to cbClsExtra, but refers to the window itself rather than the window class
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW); // handle to a background brush, in this case it's simply a colour cast into a brush handle
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // handle to cursor, first paramater is a handle to the instance of the application containing the cursor (not needed in this case), second is the resource identifier
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // similar to hCursor, but for the application icon instead
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // as above, but for the smaller version of the icon
wc.hInstance = hInstance; // handle to the instance of the application that contains the window procedure
wc.lpfnWndProc = WndProc; // a pointer to the window procedure
wc.lpszClassName = g_szClassName; // the window class name (see global variables)
wc.lpszMenuName = NULL; // specifies the resource name of the menu, which isn't used in this case
wc.style = CS_HREDRAW | CS_VREDRAW; // style for the window class, which in this case means to redraw if it's affected (i.e. resized or moved) vertically or horizontally
// register the window class
if(!RegisterClassEx(&wc))
{
// this code is executed if the window class fails to register successfully
MessageBox(NULL, // an owner for the message box can be specified here
L"Window Class Registation Failed.", // message to be displayed
L"Fatal Error", // title of the message box
MB_ICONEXCLAMATION | MB_OK); // type of message box, in this case it has an exclamation icon and an OK button
return EXIT_FAILURE; // return EXIT_FAILURE to indicate that the program closed due to a runtime error
}
// create the window
hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, // extended window style
g_szClassName, // class of window to be created (this is the window class created earlier)
L"Life Simulator", // title of the window
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // x position of the window, here default values are used
CW_USEDEFAULT, // as above, but for the y position
wndWidth, // width of the window
wndHeight, // height of the window
NULL, // parent window, if it has one
NULL, // handle to the menu for the window
hInstance, // handle to the instance of the application
NULL); // lpParam can be passed on here
if(hWnd == NULL)
{
// this code is executed if the creating the window failed
MessageBox(NULL, // an owner for the message box can be specified here
L"Window Creation Failed.", // message to be displayed
L"Fatal Error", // title of the message box
MB_ICONEXCLAMATION | MB_OK); // type of message box, in this case it has an exclamation icon and an OK button
return EXIT_FAILURE; // return EXIT_FAILURE to indicate that the program closed due to a runtime error
}
ShowWindow(hWnd, // handle to the window to be shown
nCmdShow); // passed on from WinMain, controls how the window should be shown (i.e. minimised or maximised)
UpdateWindow(hWnd); // forces the window the be updated by forcing a WM_PAINT message past the application queue
// message loop
while(true){ // program closes instantly otherwise
while(PeekMessage(&msg, // container for the message
NULL, // when multiple windows are used, you can specify which one here
0, // used to filter messages, not needed here
0, // as above
PM_REMOVE)) // remove messages after they've been processed
{
TranslateMessage(&msg); // turns virtual key messages into character messages
DispatchMessage(&msg); // sends the message on the its window procedure (i.e. WndProc)
}
}
return msg.wParam; // contains the exit code from the last message, most likely WM_QUIT
}
Game.h
#pragma once
#include <Windows.h>
class Game
{
public:
Game(HINSTANCE hInstance, // handle to an instance of the application
HINSTANCE hPrevInstance, // handle to previous instance of the application
LPSTR lpCmdLine, // command line parameters
int nCmdShow); // controls how the window is show
~Game(void);
bool Init();
bool IsRunning(){return isRunning;}
// window procedure for the game
static LRESULT CALLBACK WndProc(HWND hWnd, // handle to the window
UINT msg, // message to be processed
WPARAM wParam, // additional message information
LPARAM lParam); // even more additional message information
void HandleMessages(); // messages are translated and dispatched here
void Update(float elapsedTime); // game logic
void Render(); // display results
public: // changed to public until I can get it all working
bool isRunning;
HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
LPCWSTR g_szClassName; // the L casts the string to a wide string and it is called g_szClassName by convention, making the code easier to read for others
static const int wndHeight = 600; // window height
static const int wndWidth = 800; // window width
HWND hWnd; // handle to window
WNDCLASSEX wc; // window class container
MSG msg; // window message container
};
The problem is that you have g_szClassName declared as both a member variable and a global variable. The member variable g_szClassName is not initialized anywhere and can point to anything.
There is also no reason to have wc and msg declared as member variables as they do not need to persist throughout the lifetime of the object. Make them local variables instead.
So i have this code that creates two windows:
WNDCLASS wc;
wc.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = StaticWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = (HINSTANCE)GetModuleHandle(nullptr);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = nullptr;
wc.lpszClassName = _T("Move Engine");
RegisterClass(&wc);
m_hWnd = CreateWindow("Move Engine", "Move Engine", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, usWidth, usHeight, nullptr, nullptr, wc.hInstance, this);
// Create the settings window
wc.lpszClassName = _T("Settings");
RegisterClass(&wc);
s_hWnd = CreateWindow("Settings", "Settings", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 20, nullptr, nullptr, wc.hInstance, this);
ShowWindow(m_hWnd, SW_SHOW);
ShowWindow(s_hWnd, SW_SHOW);
The problem is, application terminates each time i close one of the windows. What i need is two separate windows; main application window and the one that gives access to various settings of the application - therefore closing the settings window should not affect the main window. Is there any way to achieve this? Thank you.
P.S. My WinMain function is modified like this:
int WINAPI WinMain(HINSTANCE a_hInstance, HINSTANCE a_hPrevInstance, LPTSTR a_lpCmdLine, int a_iCmdShow)
{
int iReturnCode;
// Initialise the engine.
if (!MyEngine.InitInstance(a_hInstance, a_lpCmdLine, a_iCmdShow)) return 0;
// Begin the gameplay process and return when the application due to exit
iReturnCode = MyEngine.StartEngine();
// Return the correct exit code.
return iReturnCode;
}
and StaticWndProc looks like this:
LRESULT CALLBACK CMoveEngine::StaticWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
// If this is a create message, trap the 'this' pointer passed in and store it within the window.
if (Message == WM_CREATE) SetWindowLong(hWnd, GWL_USERDATA, (LONG)((CREATESTRUCT FAR *)lParam)->lpCreateParams);
// Obtain the correct destination for this message
CMoveEngine *Destination = (CMoveEngine*)GetWindowLong(hWnd, GWL_USERDATA);
// If the hWnd has a related class, pass it through
if (Destination) return Destination->DisplayWndProc(hWnd, Message, wParam, lParam);
// No destination found, defer to system...
return DefWindowProc(hWnd, Message, wParam, lParam);
}
Message loop:
int CMoveEngine::StartEngine()
{
MSG msg;
// Start main loop
while (true)
{
// Did we recieve a message, or are we idling?
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage (&msg);
}
else
{
}
}
return 0;
}
You register both window classes with the same lpfnWndProc. Thus, all the messages (including mouse clicks and keyboard presses) will be directed to the same function, StaticWndProc.
Thus, when this function receives the WM_CLOSE message, it will respond by destroying the window, which stops the message pump and terminates the program. Since both windows have the same message handler function, they will both respond in the same way.
The solution is to define two different WndProc methods, one for each of your windows, or special-case the handling of the WM_CLOSE message, only calling DefWindowProc for the window that you want to allow to close the entire application.
Make your StaticWndProc handle either WM_CLOSE or WM_DESTROY: In that case it should decrement an openedWindows-counter. If that counter reaches zero call PostQuitMessage.
I have a problem. I want in my program(follow code) have 2 windows: console and empty form to output graphics. And from my func main send messages to form to draw shapes. Input data to console. But func SendMessage() doesn't work. What wrong?
int main()
{
char szClassName[] = "CG_WAPI_Template";
HWND hWnd = GetConsoleWindow();
HINSTANCE hInstance = NULL;
MSG lpMsg;
if(!AllocConsole())
MessageBox(NULL, "Failed to create the console!", "Ошибка", MB_ICONEXCLAMATION|MB_OK);
void *h_inc = GetStdHandle(STD_INPUT_HANDLE);
void *h_out = GetStdHandle(STD_OUTPUT_HANDLE);
WNDCLASS wc;
/*wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
...
*/
if(!RegisterClass(&wc))
{MessageBox(NULL, "Не могу зарегистрировать класс окна!", "Ошибка", MB_OK);
return 0;
}
hWnd = CreateWindow(...);
ShowWindow(hWnd, SW_MAXIMIZE);
UpdateWindow(hWnd);
char buf[2];
unsigned long lengh;
ReadConsole(h_inc,buf,1,&lengh,NULL);
SendMessage(hWnd, WM_USER+2, 0, 0);
if(GetMessage(&lpMsg, NULL, 0, 0))
{
TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
}
ReadConsole(h_inc,buf,1,&lengh,NULL);
if (!FreeConsole())
MessageBox(NULL, "Could not free the console!", "Ошибка", MB_OK);
return 0;
}
Thank you.
SendMessage function does not return until the message is processed by the window. You need to have an event loop in order to handle messages. Look fo r a tutorial here.
In your event loop you will have to handle messages for two windows: for the console window and for the GUI winodow. For the console messages you will need to handle the key press events, and send your custom message (WM_USER + X) to the GUI window.