here is my problem. I'm trying to create a windowless program that still uses trayIcon and Hooks, so I need to use messages (or not?) but when I use them, I don't know how to free my memory when I kill my process. Even classes's destructors aren't called. Here is a test main:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR pCmdLine, int nCmdShow)
{
MSG msg;
OutputDebugStringW(L"Start.\n");
while (GetMessageW(&msg, NULL, NULL, NULL) > 0)
{
OutputDebugStringW(L"one.\n");
TranslateMessage(&msg);
OutputDebugStringW(L"two.\n");
DispatchMessage(&msg);
OutputDebugStringW(L"three.\n");
}
OutputDebugStringW(L"end.\n");
return 0;
}
As I saw in the doc, the GetMessage() call should return 0 or less to exit, but when I run it, and then shut it down, I get only the "Start." log, and I don't understand why. And if there is no way to get through this loop, then how can i call my destructors? I do have a window for my trayIcon that doesn't receive any message, maybe i should pass it as parameter to GetMessage()?
PS: my project uses trayIcons and Hooks, and when I run it with the same debug prints, it display once the 4 first strings, but nothing at shutdown, not even the "end." string.
EDIT: my (terrible) trayIcon creation:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
OutputDebugStringW(L"Message!\n");
switch (uMsg)
{
case WM_DESTROY:
OutputDebugStringW(L"Close message received\n");
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
case WM_RBUTTONUP:
{
OutputDebugStringW(L"Trying to open Context menu\n");
POINT const pt = { LOWORD(wParam), HIWORD(wParam) };
break;
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
bool CreateNotifyIcon(HINSTANCE& hInstance)
{
NOTIFYICONDATA notif = {};
static const wchar_t class_name[] = L"ExtendClass";
WNDCLASSEX wx = {};
wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = WindowProc;
wx.hInstance = hInstance;
wx.lpszClassName = class_name;
RegisterClassEx(&wx);
HWND win = CreateWindowEx(0, class_name, L"Windows Extend", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (win == NULL)
OutputDebugStringW(L"failed to create window\n");
ZeroMemory(¬if, sizeof(NOTIFYICONDATA));
notif.cbSize = sizeof(NOTIFYICONDATA);
notif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
// Need to find a non-busy uID
notif.uID = 44421;
notif.hWnd = win;
StringCchCopy(notif.szTip, ARRAYSIZE(notif.szTip), L"Access Windows Extend options.");
StringCchCopy(notif.szInfo, ARRAYSIZE(notif.szInfo), L"Access Windows Extend options.");
StringCchCopy(notif.szInfoTitle, ARRAYSIZE(notif.szInfoTitle), L"Windows Extend");
notif.hIcon = (HICON)LoadImage(NULL, L"./Luma.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
if (!Shell_NotifyIcon(NIM_ADD, ¬if))
{
OutputDebugStringW(L"failed to create notifIcon\n");
return false;
}
return true;
}
GetMessage() does not return until it receives a window/thread message, or has a message to synthesis. In the code you have shown, you are not creating any windows, not posting any thread messages to yourself, and not creating any hooks that require a message loop, so there is nothing for GetMessage() to do. It is blocked indefinitely. That is why you are only seeing your start message and nothing else happens until you forcibly kill the program.
Since you intend to display a system tray icon, you need a window for it, even if just a hidden window, in order to receive notifications about user interaction with the icon. Your GetMessage() loop will handle messages for all windows created in the same thread as the loop, since you are setting the hWnd parameter to NULL instead of a specific window. Once you create your tray icon window, the loop will receive messages for it just fine. You can then provide your tray icon with a popup menu that contains an item that will exit your message loop when clicked, thus allowing you to exit from WinMain() gracefully, invoke destructors, etc.
GetMessage returns -1 if an error occurs. So you should do while != 0 generally. But if you are not seeing "end", then your application is probably crashing.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
Related
I'm realize the console win32 app does not quit cleanly so I'm trying to switch to message only windows instead. I'm starting the app from another process and trying to kill it cleanly.
This is the win32 app, it spawns a calc.exe on startup and on clean shutdown, it should kill the calc.exe
LRESULT CALLBACK WindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_USER: PostQuitMessage (0); break;
default: return DefWindowProc (hWnd, message, wParam, lParam);break;
}
return 0;
}
int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int show)
{
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof (WNDCLASSEX);
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"WindowClass1";
RegisterClassEx (&wc);
HWND hWnd = CreateWindowEx (NULL,
L"WindowClass1", // name of the window class
L"Our First Windowed Program", // title of the window
0,
//WS_OVERLAPPEDWINDOW, // window style
300,300,500,400,
HWND_MESSAGE,
//NULL, // parent window, NULL
NULL, hInstance, NULL);
//ShowWindow (hWnd, SW_HIDE);
PROCESS_INFORMATION pi;
CreateWindowProcess (L"calc.exe", pi); // helper class to createprocess
MSG msg = { 0 };
while (true)
{
if (PeekMessage (&msg, 0, 0, 0, PM_REMOVE))
{
if (WM_QUIT == msg.message)
break;
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
// terminate the spawn process, this is not called cleanly
TerminateProcess (pi.hProcess, 0);
return (int)msg.wParam;
}
I made a c# program to start/kill the app cleanly (the calc.exe gets destroyed) by sending a WM_QUIT/WM_CLOSE/WM_USER message . The Win32 App does not receive messages unless the window is visible (WS_OVERLAPPED and ShowWindow true). PostMessage WM_QUIT is received but the calc.exe does not get destroyed, meaning it is not a clean exit.
How should I kill it cleanly from C# app?
class Program
{
[DllImport ("user32.dll")]
public static extern bool PostMessage (IntPtr hwnd, uint msg, int wparam, int lparam);
[DllImport ("User32.dll")]
public static extern int SendMessage (IntPtr hWnd, uint uMsg, int wParam, int lParam);
static void Main (string [] args)
{
try
{
Process myProcess;
myProcess = Process.Start ("My.exe");
// Display physical memory usage 5 times at intervals of 2 seconds.
for (int i = 0; i < 3; i++)
{
if (myProcess.HasExited) break;
else
{
// Discard cached information about the process.
myProcess.Refresh ();
Thread.Sleep (4000);
Console.WriteLine ("Sending Message");
const int WM_USER = 0x0400;
const int WM_CLOSE = 0xF060; // Command code for close window
const int WM_QUIT = 0x0012;
// Received only when windows is visible
//int result = SendMessage (myProcess.MainWindowHandle, WM_USER, 0, 0);
// not clean exit
PostMessage (myProcess.MainWindowHandle, WM_QUIT, 0, 0);
// doesn't receive
SendMessage (myProcess.MainWindowHandle, WM_QUIT, 0, 0);
}
}
}
}
}
Processes on Windows do not really have a "MainWindow". Process.MainWindowHandle is C# making a guess, and it guesses by looking to see if the process in question has a window with focus - which will only find visible windows. Use FindWindowEx to find the window handle you want to close.
Next, when a window closes, it does not automatically try to exit the current threads message loop. You need to handle WM_DESTROY to call PostQuitMessage.
In your message loop use GetMessage rather than PeekMessage if you are not doing any other work as PeekMessage returns immediately if there are no messages meaning the application thread will never have an opportunity to sleep.
With these changes in place you should be fine to simply post a WM_CLOSE to the valid window handle as it will be destroyed, post itself a WM-QUIT message to exit the message loop, and terminate the calc process properly.
The code below, sends a WM_CHANGEUISTATE to the window procedure itself, with the arguments:
LOWORD(wParam) = UIS_CLEAR
HIWORD(wParam) = UISF_HIDEACCEL
lParam = 0x00000000
when the window client area is left clicked with the mouse.
According to this blog by Raymond Chen this should make the mnemonics in the System menu to be shown, when the menu is accessed with the mouse. The paragraph below was extracted from this article:
Clearing a flag shows the corresponding indicator. For example, if you
have a UIS_CLEAR for UISF_HIDEĀFOCUS, that means that you want to show
focus indicators.
In my case, I have a UIS_CLEAR for UISF_HIDEACCEL, meaning that I want to show the menu accelerators.
If you run the code below and click with the mouse left button on the app client area, you should make the accelerators visible in the System menu, even when this menu is accessed with the mouse. But that doesn't happen, i.e., if you activate the system menu with a left click on the window's icon, or with a right click on the window's title bar, the mnemonics in the System menu will not be shown. What am I missing?
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, int nCmdShow)
{
WNDCLASSEX wndclassx;
wchar_t szAppName[] = L"WM_CHANGEUISTATE";
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = CS_HREDRAW | CS_VREDRAW;
wndclassx.lpfnWndProc = WndProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = LoadCursor(0, IDC_ARROW);
wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclassx.lpszClassName = szAppName;
wndclassx.lpszMenuName = nullptr;
wndclassx.hIconSm = 0;
if (!RegisterClassEx(&wndclassx)) return 0;
HWND hWnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, 0);
ShowWindow(hWnd, SW_MAXIMIZE);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
{
BOOL b;
// Note that in my system (Windows 10) I have:
//
// Control Panel > Ease of Access > Ease of Access Center > Make the keyboard easier
//
// and the option "Underline keyboard shortcuts and access keys" unmarked (the default). Therefore, the value
// returned in b below will be 0x00000000 (FALSE).
SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &b, 0);
// If b = FALSE, the SendMessage() below should underline the accelerators in the System menu, when this menu is
// accessed with the mouse. But that doesn't work. Why?
if( !b ) SendMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL), NULL);
}
break;
// Setting a break in the return below, one can see that WM_CHANGEUISTATE message is being sent to the window and
// passed to DefWindowProc().
case WM_CHANGEUISTATE:
return DefWindowProc(hwnd, message, wParam, lParam);
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
This seems like a bug/oversight in Windows. DefWindowProc does not send WM_QUERYUISTATE before displaying the system menu. The menu implementation checks the last input event directly and if it was from a keyboard it displays the underlines.
I tried sending and posting WM_CHANGEUISTATE and WM_UPDATEUISTATE in response to WM_ENTERMENULOOP, WM_INITMENUPOPUP, WM_NCRBUTTONDOWN and WM_SYSCOMMAND without any luck.
The only workaround I was able to come up with is to hack the HTSYSMENU/icon menu by changing SC_MOUSEMENU to SC_KEYMENU:
case WM_SYSCOMMAND:
if ((wParam & 0xFFF0) == SC_MOUSEMENU)
{
return SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, ' ');
}
return DefWindowProc(hwnd, message, wParam, lParam);
SC_KEYMENU has special handling in DefWindowProc and forces underlines if applicable.
This does not handle right-click on the icon, caption nor task bar...
I have been experimenting with the WINAPI trying to learn it but the window I have created closes instantly. As you see when the W key is pressed or the left button is pressed it will close the program but when running it with no buttons being pressed it still closes.
#include <windows.h>
#include <windowsx.h>
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;
// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"WindowClass1";
// register the window class
RegisterClassEx(&wc);
// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
L"WindowClass1", // name of the window class
L"Game", // title of the window
WS_OVERLAPPEDWINDOW, // window style
1, // x-position of the window
1, // y-position of the window
1800, // width of the window
1000, // height of the window
NULL, // we have no parent window, NULL
NULL, // we aren't using menus, NULL
hInstance, // application handle
NULL); // used with multiple windows, NULL
// display the window on the screen
ShowWindow(hWnd, nCmdShow);
// enter the main loop:
// this struct holds Windows event messages
MSG msg;
// wait for the next message in the queue, store the result in 'msg'
while (GetMessage(&msg, NULL, 0, 0))
{
// translate keystroke messages into the right format
TranslateMessage(&msg);
// send the message to the WindowProc function
DispatchMessage(&msg);
}
// return this part of the WM_QUIT message to Windows
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch (message)
{
// this message is read when the window is closed
case WM_MOUSEMOVE:
{
// Retrieve mouse screen position
int x = (short)LOWORD(lParam);
int y = (short)HIWORD(lParam);
// Check to see if the left button is held down:
bool leftButtonDown = wParam & MK_LBUTTON;
// Check if right button down:
bool rightButtonDown = wParam & MK_RBUTTON;
if (leftButtonDown == true)
{
//left click
//example lets close the program when press w
PostQuitMessage(0);
return 0;
}
}
case WM_KEYDOWN:
{
switch (wParam)
{
case 'W':
//w pressed
//example lets close the program when press w
PostQuitMessage(0);
return 0;
}
}
case WM_DESTROY:
{
// close the application entirely
PostQuitMessage(0);
return 0;
}
default:
break;
}
// Handle any messages the switch statement didn't
return DefWindowProc(hWnd, message, wParam, lParam);
}
You're missing some break statements in your switch, so for example, if you get the WM_MOUSEMOVE message and the leftButtonDown != true, execution will fall through to WM_KEYDOWN, etc.
Eventually you get to case WM_DESTROY:, which will Post you a lovely QuitMessage.
As an aside, this would be very easy to spot by stepping through, statement-by-statement, in a debugger.
There is no break in your switch statement.
You end up exetuting
PostQuitMessage(0);
You could do something like this:
case WM_FOO:
{
if ( bar ) {
return 0;
}
break;
}
Don't detect clicks via the WM_MOUSEMOVE message, use the WM_MOUSEDOWN instead.
The problem is that your code is probably launched by you clicking on something, so when your window gets its first WM_MOUSEMOVE message, the button is still actually pressed. Code runs much faster than fingers..
Recently I came about a strange difference between the two Win32 API calls "PostMessage" and "SendNotifyMessage" (at least noticed on Win7 64bit SP1):
An owned top-level window of another process seems not to receive messages broadcasted (HWND_BROADCAST) with "PostMessage" while it receives messages broadcasted with "SendNotifyMessage" in its WndProc.
The sent message have been registered with the help of a call to "RegisterWindowMessage".
Even using Spy++, I cannot see the message arriving when using "PostMessage". In addition, I want to mention, that if I send the message directly to the specific HWND with "PostMessage", it arrives as expected. So it looks like the windows-internal implementation of "PostMessage" just skips my window when iterating for execution of the broadcast.
Reading the respective MSDN documentation, I cannot see any statement about this difference and I am wondering if this is a bug in PostMessage or in SendNotifyMessage and if I can rely on SendNotifyMessage to continue to show this behavior in future versions of Windows.
So does someone have a plausible explanation why the both functions treat the broadcasts differently in this situation?
In addition, I would want to ask if there is any way to still use PostMessage to broadcast to an owned top-level window, because I would prefer to post the message because I would prefer to not skip the message queue (which is what SendNotifyMessage does).
In case you are curious why I want to reach a top-level owned window: in WPF, windows are hidden from the taskbar (Window.ShowInTaskbar property) by making them owned top-level windows with a hidden owner window.
Thanks a lot in advance for any ideas or comments on this topic.
Attachment: here a sample showing the behavior ... simply build it, and start it two times ... the second process should make a message show up in the first one.
Here is also a link to the complete solution including a build EXE: Link to the complete VS solution
#include <windows.h>
#include <stdio.h>
#include <string>
#include <vector>
HWND hwndMain = NULL;
HWND ownerHwnd = NULL;
std::vector<std::string> theOutput;
UINT MyRegisteredMessage1 = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc = NULL;
if (message == MyRegisteredMessage1 && wParam != (WPARAM) hwndMain)
{
if (lParam == (LPARAM) 1)
theOutput.push_back("Got a 'MyRegisteredMessage1' via PostMessage");
if (lParam == (LPARAM) 2)
theOutput.push_back("Got a 'MyRegisteredMessage1' via SendNotifyMessage");
InvalidateRect(hwndMain, NULL, TRUE);
}
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
for(size_t i = 0, pos = 0; i < theOutput.size(); ++i, pos += 20)
TextOutA(hdc, 0, pos, theOutput[i].c_str(), theOutput[i].size());
EndPaint (hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProcHidden(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
WNDCLASSA wc;
UNREFERENCED_PARAMETER(lpszCmdLine);
if (!hPrevInstance)
{
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProcHidden;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);;
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MyOwnerWindowClass";
if (!RegisterClassA(&wc))
return FALSE;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.lpszClassName = "MyOwnedWindowClass";
if (!RegisterClassA(&wc))
return FALSE;
}
ownerHwnd = CreateWindowA("MyOwnerWindowClass", "OwnerWindow",
WS_OVERLAPPEDWINDOW, 0, 0, 800, 400, (HWND) NULL,
(HMENU) NULL, hInstance, (LPVOID) NULL);
hwndMain = CreateWindowA("MyOwnedWindowClass", "OwnedWindow",
WS_OVERLAPPEDWINDOW, 0, 0, 800, 400, ownerHwnd,
(HMENU) NULL, hInstance, (LPVOID) NULL);
// only show the "real" window
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
MyRegisteredMessage1 = RegisterWindowMessageA("MyRegisteredMessage1");
char infoText[256];
_snprintf_s(infoText, 256,
"HWND = %X, registered message code for 'MyRegisteredMessage1' = %d",
hwndMain, MyRegisteredMessage1);
theOutput.push_back(infoText);
InvalidateRect(hwndMain, NULL, TRUE);
PostMessage(HWND_BROADCAST, MyRegisteredMessage1, (WPARAM) hwndMain, (LPARAM) 1);
Sleep(1000);
SendNotifyMessageA(HWND_BROADCAST, MyRegisteredMessage1, (WPARAM) hwndMain, (LPARAM) 2);
while( (bRet = ::GetMessage( &msg, NULL, 0, 0 )) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
You may need to register your message using RegisterWindowMessage() -- see the Remarks section of this MSDN article
Just adding this here for info..
I was able to get around this issue in c# by registering an IMessageFilter object on the Application level. PreFilterMessage on this object will receive the message and I can handle it from there.
public class FooMessageFilter : IMessageFilter
{
uint UM_FOO = 0;
public event EventHandler OnFoo;
public FooMessageFilter()
{
UM_FOO = Win32.RegisterWindowMessage("UM_FOO");
}
public bool PreFilterMessage(ref Message m)
{
if(m.Msg == UM_FOO)
{
if(OnFoo != null)
OnFoo(this, new EventArgs());
return true;
}
return false;
}
}
I then added this message filter to the Application context in my owned top-level form's constructor.
public partial class Form1 : Form
{
private FooMessageFilter fooFilter = new FooMessageFilter();
public Form1()
{
InitializeComponent();
// Register message filter
Application.AddMessageFilter(fooFilter);
// Subscribe to event
fooFilter.OnFoo += HandleFoo;
}
private void HandleFoo(object o, EventArgs e)
{
MessageBox.Show("Foo!");
}
}
From there it was just a matter of hooking up events in my top-level window to the message filter. This was necessary because of the need to adhere to current architecture, and the message originating from a third party process.
The documentation page(s) for PostMessage() mention that integrity level restrictions apply:
Starting with Windows Vista, message posting is subject to UIPI. The thread of a process can post messages only to message queues of threads in processes of lesser or equal integrity level.
There is no mention of such restrictions on SendNotifyMessage(). Since you don't check the return value of either, you could be running into that, and you wouldn't know it.
I've been playing around with the Windows api for uni, but I cant get the window messages to call WM_PAINT after the inital creation. It calls it when the window is created but not after.
All Other messages get called! Just cant get the WM_PAINT to be called.
This is part of a game lib we have to make using the Windows api, (the goal is to learn abit of the windows api, rather than the intricacies of making game libs).
From what I gather the UpdateWindow(handle) command calls inititates the WM_PAINT message, but no matter where I put this command (currently in the game loop) I cant get it to call the message.
There's a lot of code, so I've trimmed it a little.
I know I have multiple WM_PAINT cases, they are a result of trying to make sure its getting through.
Window Creation
HWND hwnd;
hwnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,
L"FirstWindowClass",
L"Mootlib",
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
m_screenWidth,
m_screenHeight,
NULL,
NULL,
WindowsUtilities::windowInstance(),
NULL);
Register the Window
WNDCLASSEX wcex;
wcex.cbSize = sizeof (wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = windowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = WindowsUtilities::windowInstance();
wcex.hIcon = 0;
wcex.hCursor = LoadCursor (NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"FirstWindowClass";
wcex.hIconSm = 0;
RegisterClassEx (&wcex);
Winproc
LRESULT CALLBACK windowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LONG_PTR lptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch(message)
{
case(WM_PAINT):
int f = 3;
}
if (lptr)
{
Window* obj = reinterpret_cast<Window*>(lptr);
return obj->handleWindowsMessages(message, wParam, lParam);
}
else
return DefWindowProc(hwnd, message, wParam, lParam);
}
Internal command
LRESULT Window::handleWindowsMessages(UINT message, WPARAM wParam, LPARAM lParam)
{
std::wstring szHello = L"Hello, world!";
PAINTSTRUCT ps;
HDC hdc;
switch(message)
{
case(WM_PAINT):
hdc = BeginPaint(m_handle, &ps);
TextOut(hdc, 0, 0, L"Hello, Windows!", 15);
EndPaint(m_handle, &ps);
return 0;
case(WM_CLOSE):
quitGame();
return 0;
// Move Me!!!
case(WM_KEYDOWN):
case(WM_LBUTTONDOWN):
case(WM_RBUTTONDOWN):
inputManager().addButtonEvent(reinterpret_cast<int&>(wParam), BUTTON_DOWN);
return 0;
// Move Me!!!
case(WM_KEYUP):
case(WM_LBUTTONUP):
case(WM_RBUTTONUP):
inputManager().addButtonEvent(reinterpret_cast<int&>(wParam), BUTTON_UP);
return 0;
}
return DefWindowProc(handle(), message, wParam, lParam);
}
Game Loop
while(IsWindowVisible(handle()))
{
UpdateWindow(handle());
WindowsUtilities::processMessages();
draw();
update();
inputManager().update();
inputManager().clearButtonUpEvents(); // this should be in the input update()
}
Thanks.
First, I really don't like your game loop as its not at all obvious where the windows message pump is.
Somewhere along the line you need to call PeekMessage() or GetMessage() and TranslateMessage() & DispatchMessage() on any messages retrieved. Lots of people unfamiliar with windows will filter the messages, either in a range, or for a window: don't - always pump all messages for all windows on the current thread.
Then you need to call InvalidateRect to mark the areas of the window you want repainted. Games will want to pass FALSE as getting the background painted is just a waste of time when youre painting the entire scene in WM_PAINT.
RedrawWindow is useful for a game if you just want to mark an entire part of the window dirty and repaint it in one step. The advantage of InvalidateRect is that it just adds the rectangle to a dirty region, so if your game consists of just a few small 'active' sprites, as each sprite animates you can mark just the corresponding rect dirty, and windows will collect the dirty rects and paint them in a batch when next you call UpdateWindow, or let the message loop generate a WM_PAINT message.
Here's a snippet from the canonical Windows code, auto-generated when you create a new Win32 project in Visual Studio:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
Note what's missing in yours: you forgot to call ShowWindow().
If you want WM_PAINT to be called, you can use RedrawWindow or InvalidateRect.
MSDN states:
The UpdateWindow function updates the
client area of the specified window by
sending a WM_PAINT message to the
window if the window's update region
is not empty.
Your issue is that you haven't set an update region.
You could just do:
SendMessage( handle(), WM_PAINT, 0, 0 );