I've starting watching the handmade hero videos and I'm trying to make a win32 window but the CreateWindowEx() function keeps failing.
I checked the error code and I get 1407.
Code is below.
Thanks in advance.
#include <Windows.h>
LRESULT CALLBACK WindowProcedure(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT result;
switch (uMsg)
{
case WM_ACTIVATEAPP:
{
OutputDebugStringA("The window is now active");
break;
}
case WM_SIZE:
{
OutputDebugStringA("The window is now being resized");
break;
}
case WM_CREATE:
{
OutputDebugStringA("The window has been created");
break;
}
default:
{
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
}
}
return result;
};
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
WNDCLASS GameWindow;
GameWindow.style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
GameWindow.lpfnWndProc = WindowProcedure;
GameWindow.hInstance = hInstance;
// HICON hIcon;
GameWindow.lpszClassName = "HandmadeHeroWindowClass";
RegisterClass(&GameWindow);
if (HWND GameWindowHandle = CreateWindowEx(
0,
GameWindow.lpszClassName,
"Handmade Hero",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
hInstance,
0
))
{
for (;;)
{
MSG message;
BOOL messageResult = GetMessage(&message, GameWindowHandle, 0, 0);
if (messageResult != 0)
{
DispatchMessage(&message);
}
else if (messageResult == 0)
{
break;
}
else
{
// ERROR
}
}
}
else
{
OutputDebugStringA("Couldn't create window");
}
DWORD error = GetLastError();
return 0;
};
Your window procedure returns an uninitialized variable in every path except for default:, this is undefined behavior and failure of window creation is entirely possible.
For WM_CREATE, the documentation says:
If an application processes this message, it should return zero to continue creation of the window.
As Michael noted in the comments, RegisterClass is failing. Same category of mistake, you're passing a WNDCLASS structure leaving most members uninitialized.
Thanks to Remy Lebeau for the answer, the problem was that my WNDCLASS had uninitialized values for all fields except those I changed, this caused the RegisterClass() to fail and consequently the CreateWindowEx() to fail.
I changed WNDCLASS declaration to this:
WNDCLASS GameWindow = {0};
Thanks to everyone who helped.
Related
I have a problem handling a Windows window, even though I did it this way once before and it worked fine. After reading through the most common suggestions for this problem, it still resides. Could someone tell me why Input-handling is broken?
Intended Behaviour:
create a window titled 'FirstTry'
Make its background black, using PatBlt
Show a message box when the main loop is entered the first time, and after pressing 'w'.
Close the window when pressing either Alt+F4, Escape, or the Close-button, displaying a closing message.
Observed behaviour:
as intended
as intended
MessageBox shows up the first time, but is not retriggerable with 'w'
Window it not closable, except with TaskManager (one time it showed the 'closing Application'-MessageBox as intended, but only one time)
window draggable until the first 'entered loop'-MessageBox is closed, after that its fixed
little blue 'busy'-circle of windows10 is shown full-time, after the first MessageBox
Conclusion: The Message-handling is broken.
And i cannot figure out why...
System:
Windows 10, Version 1803 (Build 17134.81), 64-bit
Compiler from VS 2017 Community Edition:
vcvarsall.bat amd64
cl -MTd -nologo -FC -Zi -W4 -WX -wd4100 -wd4312 FirstTry.cpp /link User32.lib Gdi32.lib
#include "windows.h"
static bool bAppIsRunning = false;
static bool bMessageAlreadyShown = false;
LRESULT CALLBACK win_MainWNDCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam){
LRESULT result = 0;
switch(msg){
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:{
WPARAM vKeyCode = wParam;
bool bWasDown = ((lParam & (1 << 30)) != 0);
bool bIsDown = ((lParam & (1 << 31)) == 0);
if (bWasDown != bIsDown)
{
switch (vKeyCode)
{
case VK_ESCAPE:{
bAppIsRunning = false;
}break;
default:{
result = DefWindowProc(wnd,msg,wParam,lParam);
}break;
}
}
}break;
default:{
result = DefWindowProc(wnd,msg,wParam,lParam);
}break;
}
return result;
}
int CALLBACK WinMain(HINSTANCE HInstance, HINSTANCE HPrevInstance, LPSTR LpCmdLine, int NCmdShow){
WNDCLASSA wndCLass = {};
wndCLass.style = CS_HREDRAW | CS_VREDRAW;
wndCLass.lpfnWndProc = win_MainWNDCallback;
wndCLass.hInstance = HInstance;
wndCLass.lpszClassName = (LPCSTR)"WindowClass";
if(RegisterClassA(&wndCLass)){
HWND wnd = CreateWindowExA(
0, wndCLass.lpszClassName, (LPCSTR)"FirstTry",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
1240, 720,
0, 0, HInstance, 0);
if(wnd){
bAppIsRunning = true;
HDC DeviceContext = GetDC(wnd);
PatBlt(DeviceContext, 0, 0, 1240, 720, BLACKNESS);
ReleaseDC(wnd, DeviceContext);
while(bAppIsRunning){
if(!bMessageAlreadyShown){
MessageBoxA(NULL, (LPCSTR)"Successfully entered loop.", (LPCSTR)"Success!", MB_ICONINFORMATION | MB_OK);
bMessageAlreadyShown = true;
}
MSG msg;
while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)){
switch(msg.message){
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:{
WPARAM vKeyCode = msg.wParam;
bool bWasDown = ((msg.lParam & (1<<30)) != 0);
bool bIsDown = ((msg.lParam & (1<<31)) != 0);
if(bIsDown != bWasDown){
switch(vKeyCode){
case 'W':{
bMessageAlreadyShown = false;
}break;
default:{
TranslateMessage(&msg);
DispatchMessageA(&msg);
}break;
}
}
}
}
}
}
MessageBoxA(NULL, (LPCSTR)"Closing Application.", (LPCSTR)"Bye bye!", MB_ICONINFORMATION | MB_OK);
}
}
return ERROR_SUCCESS;
}
The main problem with your code is that you are calling TranslateMessage() and DispatchMessage() only when you receive certain key press messages. You need to call them in your main message loop for ALL messages instead. And you should be processing ALL of the messages in your WndProc callback.
You are also using TCHAR-based APIs, but are misusing LPCTSTR typecasts. You need to use the TEXT() macro instead when casting string/char literals to TCHAR.
Try something more like this instead:
#include <windows.h>
static bool bMessageAlreadyShown = false;
LRESULT CALLBACK win_MainWNDCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP: {
WPARAM vKeyCode = wParam;
bool bWasDown = ((lParam & (1 << 30)) != 0);
bool bIsDown = ((lParam & (1 << 31)) == 0);
if (bWasDown != bIsDown) {
switch (vKeyCode) {
case 'W':
case VK_ESCAPE:
DestroyWindow(wnd);
return 0;
}
}
break;
}
case WM_ERASEBKGND:
PatBlt((HDC)wParam, 0, 0, 1240, 720, BLACKNESS);
return 0;
}
return DefWindowProc(wnd, msg, wParam, lParam);;
}
int CALLBACK WinMain(HINSTANCE HInstance, HINSTANCE HPrevInstance, LPSTR LpCmdLine, int NCmdShow) {
WNDCLASS wndCLass = {};
wndCLass.style = CS_HREDRAW | CS_VREDRAW;
wndCLass.lpfnWndProc = win_MainWNDCallback;
wndCLass.hInstance = HInstance;
wndCLass.lpszClassName = TEXT("WindowClass");
if (RegisterClass(&wndCLass)) {
HWND wnd = CreateWindowEx( 0, wndCLass.lpszClassName, TEXT("FirstTry"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 1240, 720, 0, 0, HInstance, 0);
if (wnd) {
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
if (!bMessageAlreadyShown) {
bMessageAlreadyShown = true;
MessageBox(NULL, TEXT("Successfully entered loop."), TEXT("Success!"), MB_ICONINFORMATION | MB_OK);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
MessageBox(NULL, TEXT("Closing Application."), TEXT("Bye bye!"), MB_ICONINFORMATION | MB_OK);
return ERROR_SUCCESS;
}
Note that I removed your bAppIsRunning variable, as it has beecome redundant once the message loop processes the WM_QUIT message instead.
I also removed handling of ALT-F4, as the OS handles that for you automatically. It closes the window, triggering a WM_CLOSE message. By default, DefWindowProc() handles WM_CLOSE by destroying the window, which triggers a WM_DESTROY message.
I also added handling for WM_ERASEBKGND to draw a background on the window. Drawing from outside of the message loop is wrong. As soon as the window needs to be refreshed onscreen, any drawing you do is lost, so you have to redraw everything in response to WM_ERASEBKGND and WM_PAINT.
I've started Direct2D from a very simple example.
Acquire the factory and ID2D1HwndRenderTarget, then handle WM_PAINT message to draw just a background with a solid color using "Clear" function.
It's work fine, until I start to move the window. When the window is moving it turns gray like nothing is drawing. I've tried to draw an ellipse, and the result is the same.
How could one present the window content with the window moving?
P.S. In case the code is needed
#include <Windows.h>
#include <d2d1_1.h>
#pragma comment(lib,"d2d1")
ID2D1Factory * d2factory_ptr = NULL;
ID2D1HwndRenderTarget * renderTarget_ptr = NULL;
LRESULT CALLBACK mainWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(
HINSTANCE hInstance
, HINSTANCE prevInstance
, LPWSTR cmd
, int nCmdShow
) {
WNDCLASSEX wndClassStruct;
ZeroMemory(&wndClassStruct, sizeof(WNDCLASSEX));
wndClassStruct.cbSize = sizeof(WNDCLASSEX);
wndClassStruct.hbrBackground = (HBRUSH)COLOR_WINDOW;
wndClassStruct.style = CS_HREDRAW | CS_VREDRAW;
wndClassStruct.hInstance = hInstance;
wndClassStruct.lpfnWndProc = mainWinProc;
wndClassStruct.lpszClassName = TEXT("MainWnd");
RegisterClassEx(&wndClassStruct);
RECT windowRect = { 0,0,640,480};
AdjustWindowRectEx(&windowRect, WS_OVERLAPPEDWINDOW, 0, WS_EX_APPWINDOW);
HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW, TEXT("MainWnd"), TEXT("Direct 2D Test Window"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, CW_USEDEFAULT, 0, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, NULL, NULL, hInstance, 0);
{
D2D1_FACTORY_OPTIONS fo;
ZeroMemory(&fo, sizeof(D2D1_FACTORY_OPTIONS));
IID const factoryIID = IID_ID2D1Factory1;
HRESULT res = S_OK;
if (S_OK != (res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factoryIID, &fo, &d2factory_ptr))) {
return 0;
}
RECT clientRect;
GetClientRect(hWnd, &clientRect);
D2D1_RENDER_TARGET_PROPERTIES renderTargetProps;
ZeroMemory(&renderTargetProps, sizeof(D2D1_RENDER_TARGET_PROPERTIES));
renderTargetProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
renderTargetProps.pixelFormat = (D2D1_PIXEL_FORMAT) { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED };
renderTargetProps.dpiX = 0;
renderTargetProps.dpiY = 0;
renderTargetProps.usage = D2D1_RENDER_TARGET_USAGE_FORCE_BITMAP_REMOTING;
renderTargetProps.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
D2D1_HWND_RENDER_TARGET_PROPERTIES hwndRenderProps;
ZeroMemory(&hwndRenderProps, sizeof(D2D1_HWND_RENDER_TARGET_PROPERTIES));
hwndRenderProps.hwnd = hWnd;
hwndRenderProps.pixelSize = (D2D1_SIZE_U) { clientRect.right - clientRect.left, clientRect.bottom - clientRect.top };
hwndRenderProps.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
if (S_OK != (res = d2factory_ptr->lpVtbl->CreateHwndRenderTarget(d2factory_ptr, &renderTargetProps, &hwndRenderProps, &renderTarget_ptr))) {
return 0;
}
}
ShowWindow(hWnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hWnd);
if(NULL != renderTarget_ptr)
renderTarget_ptr->lpVtbl->Base.Base.Base.Release((IUnknown*)renderTarget_ptr);
if (NULL != d2factory_ptr)
d2factory_ptr->lpVtbl->Base.Release((IUnknown*)d2factory_ptr);
return 0;
}
LRESULT onPaintMainWindow() {
ID2D1RenderTargetVtbl renderTargetFuncs = renderTarget_ptr->lpVtbl->Base;
ID2D1RenderTarget * This = (ID2D1RenderTarget*)renderTarget_ptr;
D2D1_TAG tag1, tag2;
D2D1_COLOR_F backgroundClr = (D2D1_COLOR_F) { 0.0, 0.5, 1.0, 1.0 };
renderTargetFuncs.BeginDraw(This);
renderTargetFuncs.Clear(This, &backgroundClr);
renderTargetFuncs.EndDraw(This, &tag1, &tag2);
return 0;
}
LRESULT CALLBACK mainWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (WM_PAINT == uMsg)
return onPaintMainWindow();
if (WM_DESTROY == uMsg) {
PostQuitMessage(0); return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Configure your WNDCLASSEX to not have a background brush.
Replace this line:
wndClassStruct.hbrBackground = (HBRUSH)COLOR_WINDOW;
With this:
wndClassStruct.hbrBackground = GetStockObject(NULL_BRUSH);
Alternatively, you can modify mainWndProc to swallow the WM_ERASEBKGND messages. It achieves the same effect by not allowing the window to erase itself before issuing a WM_PAINT.
LRESULT CALLBACK mainWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (WM_PAINT == uMsg)
return onPaintMainWindow();
if (uMsg == WM_ERASEBKGND)
{
// ignore requests to erase the background since the wm_paint
// handler is going to redraw the entire window.
return 0;
}
if (WM_DESTROY == uMsg) {
PostQuitMessage(0); return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Here is my code. I'm trying to create an edit control. It's not showing, however. Can some take a look at my code and point out the errors please. I can't figure out where the error is. I feel It might have something to do with the parent child relationship.
#include <cstdlib>
#include <windows.h>
#define MAINWINDOW_CLASS_ID 140;
const char* MAINWINDOW_CLASS_NAME = "Main Window";
const char* TEXTAREA_CLASS_NAME = "EDIT";
using namespace std;
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
/*
* Initialize the window class and register it.
*/
BOOL initInstance(HINSTANCE hInstance);
/*
* Create and show the window
*/
HWND initWindow(HINSTANCE hInstance);
HWND createTextArea(HWND hParent);
/*
*
*/
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprevInstance, LPSTR lpCmdline, INT cmdlShow)
{
if (!initInstance(hInstance))
return 0;
HWND hwndMain = initWindow(hInstance);
ShowWindow(hwndMain, cmdlShow);
MSG msg = {} ;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
BOOL initInstance(HINSTANCE hInstance)
{
WNDCLASS wc = {};
wc.lpfnWndProc = WinProc;
wc.hInstance = hInstance,
wc.lpszClassName = MAINWINDOW_CLASS_NAME;
return RegisterClass(&wc);
}
HWND initWindow(HINSTANCE hInstance)
{
HWND hwndMain = CreateWindow(
MAINWINDOW_CLASS_NAME, // The class name
"Text Editor", // The window name
WS_OVERLAPPEDWINDOW,// The window style
CW_USEDEFAULT, // The x pos
CW_USEDEFAULT, // The y pos
CW_USEDEFAULT, // The width
CW_USEDEFAULT, // The height
(HWND) NULL, // Handle to parent
(HMENU) NULL, // Handle to the menu
hInstance, // Handle to the instance
NULL // Window creation data
);
if (!hwndMain) {
return NULL;
}
else {
return hwndMain;
}
}
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam) {
HWND htextArea;
char sztestText[] = "I should be able to see this text.";
switch (uMsg) {
case WM_CREATE:
htextArea = createTextArea(hwnd);
SendMessage(htextArea, WM_SETTEXT, 0, (LPARAM) sztestText);
break;
case WM_PAINT:
break;
case WM_CLOSE:
break;
case WM_SIZE:
RECT rectMainWindow;
GetWindowRect(hwnd, &rectMainWindow);
INT x = rectMainWindow.right - rectMainWindow.left + 50;
INT y = rectMainWindow.bottom - rectMainWindow.top + 50;
MoveWindow(htextArea, 0, 0, x, y, TRUE);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
/*******************************************************************/
HWND createTextArea(HWND hParent) {
return CreateWindow(
TEXTAREA_CLASS_NAME, // Class control name
NULL, // Title
WS_CHILD | WS_VISIBLE | ES_MULTILINE, // Styles
0, 0, 0, 0, // Sizing and position
hParent,
(HMENU) MAKEINTRESOURCE(100),
(HINSTANCE) GetWindowLong(hParent, GWL_HINSTANCE),
NULL
);
}
You've defined htextArea as a local variable in WinProc and so every time your window procedure is called its value will be uninitialized. You should make it static or move it to global data outside the function.
(The actual problem is that when you get a WM_SIZE message, you've lost the handle to the edit control, and so its size remains as 0,0).
I'm trying to know when a console window has moved so I created a new message-only window to get the messages of the console but I don't know if it is working because the message is apparently never being received.
#define WINVER 0x0501
#include <windows.h>
#include <iostream>
WNDPROC glpfnConsoleWindow; // NEW
using namespace std;
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
cout<<"Window moved"<<endl;
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
// NEW
return CallWindowProc(glpfnConsoleWindow, hwnd, uMsg, wParam, lParam);
//return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
MSG Msg;
const char lpcszClassName[] = "messageClass";
WNDCLASSEX WindowClassEx;
// == NEW
HWND consHwnd;
consHwnd = GetConsoleWindow();
glpfnConsoleWindow = (WNDPROC)SetWindowLong(consHwnd, GWL_WNDPROC, (LONG)MainWndProc);
// ==
ZeroMemory(&WindowClassEx, sizeof(WNDCLASSEX));
WindowClassEx.cbSize = sizeof(WNDCLASSEX);
WindowClassEx.lpfnWndProc = MainWndProc;
WindowClassEx.hInstance = hInstance;
WindowClassEx.lpszClassName = lpcszClassName;
if (RegisterClassEx(&WindowClassEx) != 0)
{
// Create a message-only window
hwnd = CreateWindowEx(0, lpcszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
if (hwnd != NULL)
cout<<"Window created"<<endl;
else
UnregisterClass(lpcszClassName, hInstance);
}
ShowWindow(hwnd,nCmdShow);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return (int)Msg.wParam;
}
Perhaps you should process WM_MOVE, which, according to the documentation, is sent after the window has been moved. WM_SIZE is sent when the size changes.
I think your problem is that the WM_MOVE and WM_SIZE messages are going to the console window rather than to your hidden window.
I suspect you'll have to call GetConsoleWindow to get the console window handle, and then call SetWindowLong to attach your window proc to the console window. Be sure to pass messages on to the original window proc.
MSDN doc's for MoveWindow() says:
"If the bRepaint parameter is TRUE, the system sends the WM_PAINT message to the window procedure immediately after moving the window (that is, the MoveWindow function calls the UpdateWindow function)."
But when I call GetUpdateRect() after MoveWindow(), while processing the WM_LBUTTONDOWN message in the parent, I get a beep, which shows that the child is invalid. What is the explanation ???
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
HINSTANCE ghInstance;
LRESULT CALLBACK WindowProc (HWND hwnd, UINT message, UINT wParam, LONG lParam);
LRESULT CALLBACK ChildProc (HWND hwnd, UINT message, UINT wParam, LONG lParam);
int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASSEX wndclassx;
ghInstance = hInstance;
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = 0;
wndclassx.lpfnWndProc = WindowProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclassx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclassx.lpszMenuName = NULL;
wndclassx.lpszClassName = _T("ParentWindow");
wndclassx.hIconSm = NULL;
if( !RegisterClassEx(&wndclassx) ) return 0;
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = 0;
wndclassx.lpfnWndProc = ChildProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = 0;
wndclassx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclassx.lpszMenuName = NULL;
wndclassx.lpszClassName = _T("ChildWindow");
wndclassx.hIconSm = NULL;
if( !RegisterClassEx(&wndclassx) ) return 0;
if( !(hWnd = CreateWindow(_T("ParentWindow"), _T("Parent Window"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance,
NULL)) ) return 0;
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WindowProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
HWND hWnd;
switch ( message )
{
case WM_CREATE:
CreateWindow(_T("ChildWindow"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 100, 100, hwnd, (HMENU)0,
ghInstance, NULL);
break;
case WM_LBUTTONDOWN:
hWnd = GetWindow(hwnd, GW_CHILD);
MoveWindow(hWnd, 10, 10, 200, 200, true);
if( GetUpdateRect(hWnd, NULL, FALSE) ) MessageBeep(-1);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
return DefWindowProc(hwnd, message, wParam, lParam);
}
The MSDN doc's is wrong. MoveWindow() with TRUE doesn't call UpdateWindow() as the documentation says. It just invalidates the window client area. If I call UpdateWindow() just after MoveWindow() the program runs as expected.
I tried it myself and WM_PAINT is triggered before the if(GetUpdateRect()) is. Also, GetUpdateRect returns FALSE for me.
I'm running Visual Studio 2008 on XP.
I guess it could depend on what compiler you are using, what operating system that is used and whatnot. According to the code you passed everything is done in the same thread, but if it is a multithreaded program I think that this could inflict some issues as well.