I am trying to send a custom message (WM_APP + 1) when WM_SIZE is send to the window procedure. I want to be able to catch it from an other function using PeekMessage and do somethings. But when I test it the messages seem to not be send to the queue. Adding some printf statements shows me that it goes to the window procedure. The weird thing is that when I step through the code in the debugger it works fine but when I'm running normally, it goes back to not working.
Working example program with the problem, resize the window to test:
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "user32.lib")
#define OBG_EVENT_QUIT 0
#define OBG_EVENT_RESIZE 1
#define OBG_EVENT_NO -1
#define OBG_EVENT_UNKNOWN -2
//user defined event
#define OBG_WM_RESIZE (WM_APP + 1)
typedef union
{
int type;
struct
{
int type;
int width;
int height;
} resizeEvent;
} obg_event;
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
int OBGGetEvent(obg_event *event)
{
int moreMessages = 0;
MSG message;
if(PeekMessage(&message, 0, 0, 0, PM_REMOVE))
{
moreMessages = 1;
switch(message.message)
{
case WM_QUIT:
{
event->type = OBG_EVENT_QUIT;
} break;
case OBG_WM_RESIZE:
{
event->type = OBG_EVENT_RESIZE;
event->resizeEvent.type = OBG_EVENT_RESIZE;
event->resizeEvent.width = LOWORD(message.lParam);
event->resizeEvent.height = HIWORD(message.lParam);
} break;
default:
{
event->type = OBG_EVENT_UNKNOWN;
TranslateMessage(&message);
DispatchMessage(&message);
} break;
}
}
else
{
event->type = OBG_EVENT_NO;
}
return moreMessages;
}
int main()
{
HINSTANCE instance = GetModuleHandleA(0);
WNDCLASSEX windowClass = {0};
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = obgpf_DefaultWindowCallback;
windowClass.hInstance = instance;
windowClass.lpszClassName = "testClass";
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
windowClass.hCursor = LoadCursorA(0, IDC_ARROW);
HWND window;
if(RegisterClassEx(&windowClass))
{
window = CreateWindowEx(0,
windowClass.lpszClassName,
"test window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
300,
0,
0,
instance,
0);
if(window)
{
int appIsRunning = 1;
obg_event event = {0};
event.type = -1;
while(appIsRunning)
{
while(OBGGetEvent(&event))
{
if(event.type == OBG_EVENT_QUIT)
{
printf("event quit\n");
appIsRunning = 0;
break;
}
else if(event.type == OBG_EVENT_RESIZE)
{
printf("window resized: width %d height %d\n", event.resizeEvent.width, event.resizeEvent.height);
}
}
Sleep(33);
}
}
else
{
printf("window error\n");
}
}
else
{
printf("windowClass error\n");
}
return 0;
}
I tried doing this with SendMessage instead of PeekMessage but the same thing happened. Not sure what I'm missing or misunderstanding but any help is appreciated!
EDIT: added a complete working program that reproduces the problem
Thank you for the example code.
The behaviour you're seeing is because when the window is being resized using the mouse, the OS enters a modal message loop to process mouse input. During this loop, your message loop doesn't run - which means your PeekMessage() and the special message handling doesn't run either. Instead, messages are simply dispatched as normal to your window procedure.
There are two solutions that come to mind immediately but how you deal with this really depends on the design of your program and why you want to process size events in this way.
The first idea I had is to keep track of whether you're in a modal sizing loop or not, and defer posting the notification message until the loop is finished. An example of how to do that using your provided window procedure is below.
The second solution is to simply call your resize event handler directly whenever you get WM_SIZE (or, if you must go through the event system, put the handler for it in the window procedure rather than using PostMessage).
Example code for the first suggestion:
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
static bool fResized = false;
static bool fInSizeLoop = false;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
if (fInSizeLoop) // in modal size loop, defer notification
fResized = true;
else
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
case WM_ENTERSIZEMOVE:
fInSizeLoop = true; // begin modal size loop
break;
case WM_EXITSIZEMOVE:
fInSizeLoop = false; // left modal size loop
// post resize notification now
if (fResized) {
RECT rc;
GetClientRect(window, &rc);
PostMessageA(window, OBG_WM_RESIZE, 0, MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top));
fResized = false;
}
break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
(sorry, this is C++ code and I just noticed you had tagged as C - but the principle is the same).
Related
I'm not sure what I'm doing wrong, but here's how it looks (there's no close button, no caption bar):
It looks as if it's not updating/ticking/repainted.
Here's my Resource.rc file:
#include "resource.h"
#include "windows.h"
#define IDC_STATIC -1
ID_ICON_MAIN ICON "Smile.ico"
ID_DIALOG_ABOUT DIALOG 0, 0, 240, 70
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_BORDER
CAPTION "About"
FONT 8, "Terminal"
{
DEFPUSHBUTTON "&OK", IDOK, 174, 18, 50, 14
PUSHBUTTON "&Cancel", IDCANCEL, 174, 36, 50, 14
GROUPBOX "About this program...", IDC_STATIC, 7, 7, 225, 52
CTEXT "An example program\r\nBy vexe", IDC_STATIC, 16, 18, 144, 33
}
Changing the styles, caption, font size, etc doesn't change anything. Note that I'm using Windows Classic Theme, changing to the standard Windows 7 doesn't help either so it's not a theme issue.
Here's my window code:
#include <Windows.h>
#include "resource.h"
INT_PTR WINAPI DialogWndProc(HWND Window, UINT Message,
WPARAM WParam, LPARAM LParam)
{
INT_PTR Result = TRUE;
switch (Message)
{
case WM_INITDIALOG:
{
// UpdateWindow(Window); // Doesn't really help. Taking out the case didn't do anything either
}
break;
case WM_COMMAND:
{
int Command = LOWORD(WParam);
switch(Command)
{
case IDOK:
{
EndDialog(Window, IDOK);
}
break;
case IDCANCEL:
{
EndDialog(Window, IDCANCEL);
}
break;
default:
{
Result = FALSE;
}
break;
}
}
break;
}
return(Result);
}
LRESULT WINAPI MainWndProc(HWND Window, UINT Message,
WPARAM WParam, LPARAM LParam)
{
LRESULT Result = 0;
switch (Message)
{
// (Causes GetMessage to return false)
case WM_CLOSE:
{
DestroyWindow(Window);
}
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
case WM_COMMAND:
{
int Command = LOWORD(WParam);
switch (Command)
{
case ID_MENU_FILE_EXIT:
{
PostMessage(Window, WM_QUIT, 0, 0);
}
break;
case ID_MENU_HELP_ABOUT:
{
int Choice = DialogBox(0, MAKEINTRESOURCE(ID_DIALOG_ABOUT), Window, DialogWndProc);
switch (Choice)
{
case IDOK: MessageBox(Window, "Okay!", "OK", MB_OK | MB_ICONINFORMATION); break;
case IDCANCEL: MessageBox(Window, "Canceled!", "Cancel", MB_OK | MB_ICONINFORMATION); break;
}
}
break;
}
}
break;
case WM_CREATE:
{
HMENU Menu;
HMENU SubMenu;
// Create the main menu
Menu = CreateMenu();
// Create submenus
{
SubMenu = CreatePopupMenu();
AppendMenu(SubMenu, MF_STRING, ID_MENU_FILE_EXIT, "E&xit");
AppendMenu(Menu, MF_STRING | MF_POPUP, (UINT_PTR)SubMenu, "&File");
SubMenu = CreatePopupMenu();
AppendMenu(SubMenu, MF_STRING, ID_MENU_HELP_ABOUT, "&About");
AppendMenu(Menu, MF_STRING | MF_POPUP, (UINT_PTR)SubMenu, "&Help");
}
// Set the menu on the window
SetMenu(Window, Menu);
}
break;
default:
{
Result = DefWindowProc(Window, Message, WParam, LParam);
}
}
return(Result);
}
int CALLBACK WinMain(HINSTANCE Instance, HINSTANCE Previous, LPSTR CommandLine, int CmdShow)
{
// Create window class
WNDCLASSEX WindowClass = {0};
WindowClass.cbSize = sizeof(WNDCLASSEX);
WindowClass.style = CS_HREDRAW | CS_VREDRAW;
WindowClass.lpfnWndProc = (WNDPROC)MainWndProc;
WindowClass.hInstance = Instance;
WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
WindowClass.lpszMenuName = MAKEINTRESOURCE(ID_MENU_MAIN);
WindowClass.lpszClassName = "SaedoGames_0";
WindowClass.hIcon = LoadIcon(Instance, MAKEINTRESOURCE(ID_ICON_MAIN));
WindowClass.hIconSm = (HICON)LoadImage(0, "Smile.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
RegisterClassEx(&WindowClass);
// Create window
HWND Window = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "SaedoGames_0", "Test Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
640, 480,
0, 0,
Instance, 0
);
// Show window
ShowWindow(Window, CmdShow);
UpdateWindow(Window);
// Message loop
MSG Msg;
while (GetMessage(&Msg, Window, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return(0);
}
Pretty sure I'm missing a simple function call but just not sure what it is. What am I missing?
Your dialog procedure unconditionally returns TRUE to every message, which means "I handled the message, you don't have to do anything else". Your dialog procedure does so by setting Result to TRUE at the top of the function and then never changing it (unless you get a WM_COMMAND from an unknown control). This is wrong; you need to return FALSE if you don't handle a message yourself.
WM_INITDIALOG is one of a handful of messages that return their values directly to the dialog system instead of through DWLP_MSGRESULT. In that case and that case only, you do need to return TRUE (unless you manually adjusted tab stops).
After enabling a disabled child window, I try to turn mouse tracking on in WM_ENABLE only if mouse cursor is hovering over the window using TrackMouseEvent() with dwFlags of TRACKMOUSEEVENT structure set to TME_LEAVE. TrackMouseEvent() returns TRUE, but then right after calling it I get a WM_MOUSELEAVE message. This happens only under 2 conditions. With first condition, move cursor outside of child window, press Enter key to disable the window, then move cursor over the child window and press the Space key. With second condition, move the cursor over window, press Enter key to disable it, then before pressing the Space key move the cursor 1 pixel or more and then press the Space key. If you retest the second condition, but instead of moving cursor before you press the Space key, if you press the Space key right after you press the Enter key, mouse tracking is turned on properly. I've tried really hard to fix this but I've not been lucky so far. Can somebody please fix this code and explain why mouse tracking is being canceled when I'm trying to turn it on?
#include <windows.h>
const WCHAR g_szChildClassName[] = L"Childclass////";
HINSTANCE g_hInst;
LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static BOOL bMouseTracking = FALSE;
switch(msg)
{
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
if(hdc)
{
HBRUSH hbr = CreateSolidBrush(bMouseTracking?RGB(255, 0, 0):RGB(0, 0, 255));
if(hbr)
{
FillRect(hdc, &ps.rcPaint, hbr);
DeleteObject(hbr);
}
EndPaint(hwnd, &ps);
}
}
break;
case WM_MOUSEMOVE:
if(!bMouseTracking)
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
bMouseTracking = TrackMouseEvent(&tme);
InvalidateRect(hwnd, 0, TRUE);
}
break;
case WM_MOUSELEAVE:
bMouseTracking = FALSE;
InvalidateRect(hwnd, 0, TRUE);
break;
case WM_ENABLE:
if(wParam)
{
RECT rc;
if(GetWindowRect(hwnd, &rc))
{
POINT pt;
if(GetCursorPos(&pt))
if(PtInRect(&rc, pt))
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
//TrackMouseEvent() posts WM_MOUSELEAVE if conditions 1 and 2 are met, even though I'm trying to turn
//mouse tracking on and the cursor is over the child window. It doesn't make sense
//The problems is this piece of code right here /* bMouseTracking = TrackMouseEvent(&tme); */
//It should turn tracking on but it doesn't it cancels it even though WS_DISABLED has already been removed
//at this point
bMouseTracking = TrackMouseEvent(&tme);
InvalidateRect(hwnd, 0, TRUE);
}
}
} else {
if(bMouseTracking) {
////////If you comment everything from here ...
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE | TME_CANCEL;
tme.hwndTrack = hwnd;
//if(TrackMouseEvent(&tme)) PostMessage(hwnd, WM_MOUSELEAVE, 0, 0); //Commented this line out to do things a bit differently with the same results
if(TrackMouseEvent(&tme)) { //If this succeeds it means mouse tracking was canceled
bMouseTracking = FALSE;
InvalidateRect(hwnd, 0, TRUE);
}
////////all the way down to here the result is the same
//If you comment everything in this block out then you have another problem which can be tested with this condition:
//With window enabled move mouse over window, then press the ENTER key. The color should change
//from red to blue but it doesn't. It will change to blue though if you move the mouse 1 or more pixels after you've pressed the ENTER key
}
}
break;
case WM_DESTROY:
bMouseTracking = FALSE;
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hChild;
switch(msg)
{
case WM_CREATE:
hChild = CreateWindowEx(0, g_szChildClassName, 0, WS_VISIBLE | WS_CHILD, 4, 4, 240, 80, hwnd, 0, g_hInst, 0);
break;
case WM_KEYDOWN:
if(wParam == VK_SPACE) EnableWindow(hChild, TRUE);
else if(wParam == VK_RETURN) EnableWindow(hChild, FALSE);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
const TCHAR szClassName[] = L"abccccccc";
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szClassName;
if(!RegisterClassEx(&wc)) return 0; //Register main window class
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hInstance = hInstance;
wc.lpfnWndProc = ChildProc;
wc.lpszClassName = g_szChildClassName;
if(!RegisterClassEx(&wc)) return 0; //Register child window class
g_hInst = hInstance;
hwnd = CreateWindowEx(0, szClassName, L"Test", WS_OVERLAPPEDWINDOW, 40, 40, 420, 200, 0, 0, hInstance, 0);
if(!hwnd) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
EDIT: You can't see the cursor in the pictures cause I used screen capture and it doesn't capture the cursor. In the first picture the cursor is outside of the child window and in the second picture the cursor is inside of the child window
ENTER key pressed when cursor is outside of child window
SPACE key pressed after the ENTER key was previously pressed and cursor is hovering over child window
I don't know if this is a shortcoming of the documentation or a bug in TrackMouseEvent, but it looks like TrackMouseEvent doesn't expect to be called within your WM_ENABLE handler.
To avoid that, try posting a message from WM_ENABLE and calling TrackMouseEvent from that:
case WM_ENABLE:
PostMessage(hwnd, WM_USER, wParam, lParam);
break;
case WM_USER:
if(wParam)
{
RECT rc;
if(GetWindowRect(hwnd, &rc))
{
POINT pt;
if(GetCursorPos(&pt))
if(PtInRect(&rc, pt))
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
//TrackMouseEvent() posts WM_MOUSELEAVE if conditions 1 and 2 are met, even though I'm trying to turn
//mouse tracking on and the cursor is over the child window. It doesn't make sense
//The problems is this piece of code right here /* bMouseTracking = TrackMouseEvent(&tme); */
//It should turn tracking on but it doesn't it cancels it even though WS_DISABLED has already been removed
//at this point
bMouseTracking = TrackMouseEvent(&tme);
InvalidateRect(hwnd, 0, TRUE);
}
}
} else {
if(bMouseTracking) {
////////If you comment everything from here ...
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE | TME_CANCEL;
tme.hwndTrack = hwnd;
//if(TrackMouseEvent(&tme)) PostMessage(hwnd, WM_MOUSELEAVE, 0, 0); //Commented this line out to do things a bit differently with the same results
if(TrackMouseEvent(&tme)) { //If this succeeds it means mouse tracking was canceled
bMouseTracking = FALSE;
InvalidateRect(hwnd, 0, TRUE);
}
////////all the way down to here the result is the same
//If you comment everything in this block out then you have another problem which can be tested with this condition:
//With window enabled move mouse over window, then press the ENTER key. The color should change
//from red to blue but it doesn't. It will change to blue though if you move the mouse 1 or more pixels after you've pressed the ENTER key
}
}
break;
I have created a simple Win32 Application and try to fill the Client Area with a Color. When the line "Clear RenderTarget" is included I see that the memory increases a few KB every time I resize the window.
My WindowProc:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
{
if (pRenderTarget != NULL)
{
RECT rc;
GetClientRect(globalWindowHandle, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);
pRenderTarget->Resize(size);
InvalidateRect(globalWindowHandle, NULL, FALSE);
}
return 0;
}
break;
case WM_CREATE:
{
HRESULT dx = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory);
if (FAILED(dx))
{
MessageBox(globalWindowHandle, "Error creating D2D1Factory", "Error", MB_ICONERROR);
return -1;
}
return 0;
}
break;
case WM_KEYDOWN:
{
int ret = HandleKeyboardInput(uMsg, wParam, lParam);
if (ret == 0)
{
return 0;
}
}
break;
case WM_PAINT:
{
HRESULT hr = CreateGraphicsResources();
if (SUCCEEDED(hr))
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
pRenderTarget->BeginDraw();
// Clear RenderTarget
pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::SkyBlue));
hr = pRenderTarget->EndDraw();
if (FAILED(hr) || hr == D2DERR_RECREATE_TARGET)
{
pRenderTarget->Release();
pSolidBrush->Release();
pRenderTarget = NULL;
pSolidBrush = NULL;
}
EndPaint(hwnd, &ps);
return 0;
}
}
break;
case WM_CLOSE:
{
int box = MessageBox(hwnd, "Would you like to close the editor ?", "Question", MB_OKCANCEL);
if (box == IDOK)
{
DestroyWindow(hwnd);
}
return 0;
}
break;
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
break;
default:
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
break;
}
return 0;
}
CreateGraphicsResources
HRESULT CreateGraphicsResources()
{
HRESULT hr = S_OK;
if (pRenderTarget == NULL)
{
RECT rc;
GetClientRect(globalWindowHandle, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);
hr = pFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(globalWindowHandle, size),
&pRenderTarget);
if (SUCCEEDED(hr))
{
const D2D1_COLOR_F color = D2D1::ColorF(1.0f, 1.0f, 1.0f, 0);
hr = pRenderTarget->CreateSolidColorBrush(color, &pSolidBrush);
if (SUCCEEDED(hr))
{
}
}
}
return hr;
}
Globals:
BOOL ctrlPressed = FALSE;
HWND globalWindowHandle;
ID2D1Factory *pFactory;
ID2D1SolidColorBrush *pSolidBrush;
ID2D1HwndRenderTarget *pRenderTarget;
Do I miss something to free up memory or what could be the reason? If I resize the window e.g. 5 sec the memory goes from 4KB up to 22KB.
My OS is Windows 10 x64
The issue seems to be solved. In fact the memory did not go up much more so it seems the OS assigns the other memory.
I've got this code to open an InputBox defined on a DLL that get HMODULE that I save on hInstance variable when main program calls. How can I center it over the main program window?
It occurs that doesn't work and shows the DialogBox on top left on Screen or on top left of program window randomly.
#include <windows.h>
#include "resource.h"
char IB_res[10];
double defaultValue = 0;
BOOL CALLBACK InputBox_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
if (defaultValue != -1)
SetDlgItemText(hwnd, IDC_EDIT, (LPCSTR)(my_printf("%f", defaultValue).c_str()));
else
SetDlgItemText(hwnd, IDC_EDIT, (LPCSTR)"");
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
if (!GetDlgItemText(hwnd, IDC_EDIT, IB_res, 10))
*IB_res = 0;
case IDCANCEL:
EndDialog(hwnd, wParam);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
DWORD processId;
HWND hwndParent;
BOOL CALLBACK enumWindowsProc(HWND hwnd, LPARAM lParam)
{
DWORD procid;
GetWindowThreadProcessId(hwnd, &procid);
if (procid == processId)
hwndParent = hwnd;
return TRUE;
}
HINSTANCE hInstance;
const char* InputBox(double def_value)
{
defaultBetValue = def_value;
processId = GetCurrentProcessId();
EnumWindows(enumWindowsProc, 0);
INT_PTR ret = DialogBox(hInstance, MAKEINTRESOURCE(IDD_IB), hwndParent, InputBox_WndProc);
DWORD error = GetLastError();
if (ret != IDOK)
*IB_res = 0;
return IB_res;
}
From:
http://msdn.microsoft.com/en-gb/library/windows/desktop/ms644996(v=vs.85).aspx
case WM_INITDIALOG:
// Get the owner window and dialog box rectangles.
if ((hwndOwner = GetParent(hwndDlg)) == NULL)
{
hwndOwner = GetDesktopWindow();
}
GetWindowRect(hwndOwner, &rcOwner);
GetWindowRect(hwndDlg, &rcDlg);
CopyRect(&rc, &rcOwner);
// Offset the owner and dialog box rectangles so that right and bottom
// values represent the width and height, and then offset the owner again
// to discard space taken up by the dialog box.
OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
OffsetRect(&rc, -rc.left, -rc.top);
OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
// The new position is the sum of half the remaining space and the owner's
// original position.
SetWindowPos(hwndDlg,
HWND_TOP,
rcOwner.left + (rc.right / 2),
rcOwner.top + (rc.bottom / 2),
0, 0, // Ignores size arguments.
SWP_NOSIZE);
if (GetDlgCtrlID((HWND) wParam) != ID_ITEMNAME)
{
SetFocus(GetDlgItem(hwndDlg, ID_ITEMNAME));
return FALSE;
}
return TRUE;
I am totally new in programming.
For my first programm I tried to make a small game in c with windows api.
the following code is a nearly working snake, (some bugs not fixed yet)
but i cant find a working solution to fix that flickering.
(I dont know double Buffering so I just took from here: [http://www.codeguru.com/forum/archive/index.php/t-272723.html][1]
// snake1.02.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "snake1.02.h"
#include "stdio.h"
#define ID_MYTIMER 1234567
#define ID_BERRY 1
#define SEGMENTSIZE 10
const WCHAR szClassName[] = L"Snake1.02";
HWND hWnd;
UINT myTimer;
struct point {
int x, y;
};
point snake[500];
int dx = SEGMENTSIZE; //Change direction l=l+1
int dy = 0;
int snakelength=3;
int highscore=0;
WCHAR nachricht[100];
int map[80][60]; //Wenn Bildschirmgröße angepasst wird, auch hier anpassen!!!
// Global Variables:
HINSTANCE hInst; // current instance
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
// Initialize global strings
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
myTimer = SetTimer ( hWnd, ID_MYTIMER, 40, NULL );
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
KillTimer ( hWnd, myTimer );
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage are only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+2);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szClassName;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szClassName, szClassName, WS_OVERLAPPED,
CW_USEDEFAULT, 0, 800, 600, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
for(int i=snakelength; i>=0; i--){
snake[i].x =100-(i*SEGMENTSIZE);
snake[i].y=20;}
for(int x=0; x<78;x++){
map[x][0]=1;
map[x][56]=1;
}
for(int y=0; y<57;y++){
map[0][y]=1;
map[78][y]=1;
}
map[50][70]=2;
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
HDC memDC;
HBITMAP hMemBmp;
HBITMAP hOldBmp;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
memDC = CreateCompatibleDC(hdc); //flimmerfrei
hMemBmp = CreateCompatibleBitmap(hdc, 800, 600);
hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);
// TODO: Add any drawing code here...
for (int i = 0; i<=snakelength; i++) {
Rectangle(memDC, snake[i].x,snake[i].y,snake[i].x+10,snake[i].y+10);
}
for (int y=0; y<60;y++){
for (int x=0; x<80;x++){
if(map[x][y]==1||map[x][y]==2){
Rectangle(memDC,x*SEGMENTSIZE,y*SEGMENTSIZE,x*SEGMENTSIZE+SEGMENTSIZE,y*SEGMENTSIZE+SEGMENTSIZE);
}}
}
BitBlt(hdc, 0, 0, 800, 600, memDC, 0, 0, SRCCOPY);
SelectObject(memDC, hOldBmp);
DeleteObject(hMemBmp);
DeleteDC(memDC);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_TIMER:
switch (wParam)
{
case ID_MYTIMER: {
if (map[snake[0].x/10][snake[0].y/10]==1){ //crashcheck
KillTimer ( hWnd, myTimer );
wsprintf (nachricht,L"Game Over. Punktestand: %d",highscore);
MessageBox(hWnd, (LPCWSTR)nachricht,L"Ende",0);
}
if (map[snake[0].x/10][snake[0].y/10]==2) //grow
{
for (int i = snakelength; i>0; i--)
{
snake[i].x = snake[i-1].x;
snake[i].y = snake[i-1].y;
}
snake[0].x = snake[1].x + dx;
snake[0].y = snake[1].y + dy;
map[snake[0].x/10][snake[0].y/10]=0;
snakelength++;
highscore+=10;
}
for (int i = snakelength; i>0; i--) {
snake[i].x = snake[i-1].x;
snake[i].y = snake[i-1].y;
}
snake[0].x = snake[1].x + dx;
snake[0].y = snake[1].y + dy;
InvalidateRect(hWnd, 0,1);
return 0;
}
}
break;
case WM_KEYDOWN: {
switch (wParam) {
case VK_DOWN:{
if(dy!=-SEGMENTSIZE){
dy=SEGMENTSIZE;
dx=0;
}
}
break;
case VK_RIGHT:{
if (dx!=-SEGMENTSIZE){
dy=0;
dx=SEGMENTSIZE;
}
}
break;
case VK_UP:{
if (dy!=SEGMENTSIZE){
dy=-SEGMENTSIZE;
dx=0;
}
}
break;
case VK_LEFT:{
if(dx!=SEGMENTSIZE){
dy=0;
dx=-SEGMENTSIZE;
}
}
break;
case VK_ESCAPE:{
ExitProcess(0);
}
break;
}
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Add one case to your main switch statement in WndProc:
case WM_ERASEBKGND:
return TRUE;