Is creating child windows expensive with winapi? [closed] - winapi

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
If I use a pre-defined class emptyPanel like this:
WNDCLASSW emptyPanel;
emptyPanel.style = CS_HREDRAW | CS_VREDRAW;
emptyPanel.lpszClassName = L"Empty Panel";
emptyPanel.hInstance = hInstance;
emptyPanel.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
emptyPanel.lpfnWndProc = WndProc;
emptyPanel.hCursor = LoadCursor(NULL, IDC_ARROW);
emptyPanel.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&emptyPanel);
how expensive is it to call create child windows with that class like this:
CreateWindowW(L"Empty Panel", L"", WS_CHILD | WS_VISIBLE, 0, 0, 100, 100, hwnd, NULL, NULL, NULL);
The calls to CreateWindowW are generated from within the WndProc function like this:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
...
case WM_USER_ADD_COMPONENT:
callFunctionThatCreatesWindow();
break;
...
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
Where callFunctionThatCreatesWindow calls a different function depending on circumstance (e.g. what type of component to add), to create the window. However, in the case of this question, there is only one type of window being created.
Then to trigger the WM_USER_ADD_COMPONENT event, I call SendMessage(hwnd, WM_USER_ADD_COMPONENT, 0, 0); for each child window I want to create.
Creating 100+ child windows like this froze the window. Is there a better way to create child windows that have their own drawing functionality/event handlers?

Creating 100+ child windows like this froze the window.
Doing anything in a loop in Windows will freeze the window. Windows is event driven and the message pump must be allowed to run, or the system will 'freeze'. You should change the code to be event driven, eg. post an event (a message) and then create the child window as response to this event. Then post a second event and so on and so forth. Some may recommend to pepper the code with message loops using PeekMessage (eg. in your 100+ loop) but I don't favor this approach.
I call SendMessage(hwnd, WM_USER_ADD_COMPONENT, 0, 0)
Try using PostMessage instead.

Related

Duration of the busy cursor when launching a Windows desktop application

When you launch a Windows desktop application, the system changes the arrow to one with an animated busy spinner (presumably from IDI_ARROW to IDI_APPSTARTING). Once the application presents a window, the cursor is restored to indicate that it's ready for input.
With my own Win32 applications, the spinner continues for several (5?) seconds, even though the main window is fully rendered and ready for interaction. It was my understanding that the spinner vanishes once the just-launched program started pumping messages. More specifically, I thought it used the same criteria as WaitForInputIdle.
Since Windows 10, however, some applications, including ones I've written, appear to be stuck with the busy cursor for the duration of the timeout—even beyond the moment that the application becomes responsive to mouse and keyboard input.
What should I do in my programs to let the system know that initialization is complete and the spinner is no longer needed?
UPDATE: The problem occurs only when launching the application from the keyboard (like a CMD prompt, the Run window, or using the keyboard to launch the process in the debugger in Visual Studio). If you double-click the program with the mouse, the spinner vanishes quickly, as expected.
UPDATE 2: Given that others cannot repro, my best guess is that there's a buggy driver involved. Thanks to everyone who helped brainstorm in the comments.
Self-contained repro below.
#include <Windows.h>
LRESULT OnPaint(HWND hwnd, UINT, WPARAM, LPARAM) {
PAINTSTRUCT ps;
::BeginPaint(hwnd, &ps);
RECT rc;
::GetClientRect(hwnd, &rc);
::DrawTextW(ps.hdc, L"Hello, World", -1, &rc,
DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_NOPREFIX);
::EndPaint(hwnd, &ps);
return 0;
}
LRESULT MyWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_DESTROY: ::PostQuitMessage(0); break;
case WM_PAINT: return OnPaint(hwnd, msg, wp, lp);
}
return ::DefWindowProcW(hwnd, msg, wp, lp);
}
int WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int nShowCmd) {
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MyWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinst;
wc.hIcon = NULL;
wc.hCursor = ::LoadCursorW(NULL, IDC_ARROW);
wc.hbrBackground = ::GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = nullptr;
wc.lpszClassName = L"My Very Own Window Class";
const ATOM atomClass = ::RegisterClassW(&wc);
if (atomClass == 0) return ::GetLastError();
// Ugly cast for WinAPI's legacy type punning.
const LPCWSTR wndclass =
reinterpret_cast<LPCWSTR>(static_cast<UINT_PTR>(atomClass));
const HWND hwnd =
::CreateWindowW(wndclass, L"Test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,
NULL, hinst, nullptr);
if (hwnd == NULL) return ::GetLastError();
::ShowWindow(hwnd, nShowCmd);
MSG msg = {0};
while (::GetMessageW(&msg, NULL, 0, 0) > 0) {
::TranslateMessage(&msg);
::DispatchMessageW(&msg);
}
return (msg.message == WM_QUIT) ? static_cast<int>(msg.wParam) : 1;
}
What should I do in my programs to let the system know that initialization is complete and the spinner is no longer needed?
There seems to be agreement that the spinner should vanish once the application has started pumping messages and has no pending input events queue.
The fact that's not working in all cases for me is likely a local issue, like a buggy driver spamming the input queue (which somehow never make it to window messages). There's not much point in hashing that out here on SO, so I'm putting up this community wiki answer and moving on.

RegisterPointerInputTarget not consuming all input

I'm working in Windows 10 and I am trying to write a C++ program that intercepts all touch screen input, however some input is still getting through.
My code calls RegisterPointerInputTarget with PT_TOUCH to intercept touch input. This mostly seems to work, however the results are inconsistent. As a test I have added code that uses SendInput to move the mouse slowly to the right whenever touch input is detected. With my program running I can, for example, open up MS Paint and touch the screen. So long as I hold my finger still on the cursor moves slowly to the right as expected. The moment I move my finger however, the cursor snaps to the position under my finger, just as it would if my program were not running at all.
To give another example, if I try the same thing in visual studio, again the cursor moves slowly to the right until I move my finger at which point the cursor follows my fingers' movement but slowly, lagging be behind it with a significant delay.
The code to set up my window looks like this;
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
static const char* class_name = "DUMMY_CLASS";
WNDCLASSEX wx = {};
wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = WndProc; // function which will handle messages
wx.hInstance = hInst;
wx.lpszClassName = class_name;
HWND hWnd = 0;
if (RegisterClassEx(&wx)) {
hWnd = CreateWindowEx(0, class_name, "dummy_name", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
}
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
if (RegisterTouchWindow(hWnd, 0) &&
RegisterPointerInputTarget(hWnd, PT_TOUCH))
{
...
and my message handling code looks like this;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_TOUCH:
{
INPUT Inputs[1] = { 0 };
Inputs[0].type = INPUT_MOUSE;
Inputs[0].mi.dx = 1;
Inputs[0].mi.dy = 0;
Inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE;
SendInput(1, Inputs, sizeof(INPUT));
Ideally this test code would simply move the cursor for any touch input. Any help in fixing or just understanding this would be greatly appreciated!
I have made some progress with this but have hit other, related, problems I will ask about in a separate question. I will add a comment here when that question is live. The key to sorting out this initial issue however was to make sure I am returning 0, without calling DefWindowProc from all WM_POINTERENTER, WM_POINTERLEAVE, WM_POINTERUP, WM_POINTERDOWN, WM_POINTERUPDATE and WM_TOUCH messages, and to put my SendInput call into the code processing the WM_UPDATE message.

WINAPI control "EDIT"-Textbox receives no user input

I have a textbox that is not set to "read-only", so theoretically I should be able to write to it. It is activated with Edit_Enable(hwnd, true), Edit_SetReadOnly(hwnd, false) and SetFocus(hwnd). I can give the textbox focus by clicking on it and I can even see the caret blinking, but however, when I press any key the textbox receives no input. I can set its text with Edit_SetText(), but I cannot write anything manually to it.
I create this textbox with following code:
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL /*| ES_OEMCONVERT*/ | ES_LEFT /*| ES_WANTRETURN */| WS_TABSTOP;
HWND h = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", (LPCWSTR)NULL, dwStyle, posX, posY, width, height, hParent, NULL, (HINSTANCE)GetModuleHandle(NULL), NULL);
Messages are handled in this function. It is called every frame.
bool PumpMessages()
{
MSG msg;
ZeroMemory( &msg, sizeof(MSG) );
bool bQuit = false;
// Use PeekMessage() so we can use idle time to update the system.
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Translate and dispatch the message
TranslateMessage(&msg);
// message bug workaround
if (msg.message == WM_QUIT)
{
bQuit = true;
}
DispatchMessage(&msg);
}
return bQuit;
}
And the message procedure looks like following:
LRESULT CALLBACK Window::WndProcThunk(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_CHAR)
Log("WM_CHAR");
...
// Finally, if the message isn't consumed by the window or any registered listeners, let Windows do its thing.
return msgConsumed ? 0 : DefWindowProc(hWnd, msg, wParam, lParam);
}
The second strange thing happens here in the WndProcThunk message procedure. No WM_CHAR messages are logged. I think this is the reason that my TextBox doesn't do anything, because it needs WM_CHAR messages to react on user key presses. I have to find the reason why no WM_CHAR messages are sent, even though I use TranslateMessage() in my PumpMessages function. Any ideas?
PeekMessage returns FALSE if no messages are in the message queue, so your message loop will exit almost immediately. You need to either switch to using GetMessage or refactor the loop so that you are using your bQuit flag as a test rather than the return value of PeekMessage.
I solved the problem by disabling the DirectInput API. DirectInput 8 was actually eating all WM_KEYDOWM messages, which resulted in no WM_CHAR messages being sent. But the textbox needs WM_CHAR messages to react to user input. This is a strange behaviour of the API. If anyone has ideas to fix this problem without disabling DirectInput, feel free to post it. But I will use GetAsyncKeyState() for keyboard input from now on.

Win32/C can't display msg box right after CreateWindow

I'm creating my app window in code and I'm trying to show a message box as soon as the window exists. But I can't. I see only the newly created window, no msg box. If I quit the app by closing its window, the msg box suddenly appears, as if it were waiting in some queue, to be shown only when the app window is closed. Does the way I create the window somehow block modal msg boxes? Note: the MessageBox line is there just for testing. I'll take it out for normal use, as it would obviously interfere with the GetMessage loop.
//start relevant section of WinMain:
WNDCLASS wc={0};
wc.lpfnWndProc = WindowProc;
...
if (!RegisterClass(&wc) || !CreateWindow("mc", "mc", WS_POPUPWINDOW|WS_CAPTION|WS_VISIBLE, 100, 50, 100, 100, NULL, NULL, hInstance, NULL)) {
Error("Can't create window");
return 0;
}
ShowWindow(win, SW_SHOWNORMAL);
MessageBox(0, "Test", 0 ,0);
while (GetMessage(&msg,NULL,0,0)>0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//end relevant section of WinMain
long FAR PASCAL WindowProc(HWND h, UINT m, WPARAM wParam, LPARAM l)
{
switch (m) {
//process other messages
case WM_CREATE:
win=h;
//init stuff, paint something in the main window
break;
}
return DefWindowProc(h, m, wParam, l);
}
It sounds like you're not returning immediately from WM_CREATE like you're supposed to, but your window's entire lifetime is inside CreateWindow. So MessageBox doesn't actually get called until your window is dead, and trying to pass wnd as the parent of the message box is an invalid argument (the window no longer exists).
You shouldn't call DefWindowProc for WM_CREATE. You shouldn't have a message loop (i.e. DispatchMessage) inside WindowProc (exception: a message loop handling a modal dialog that is a child of the main window).
Re-entrancy of window procedures is something to avoid if possible.

Why can my property sheet, shown from a system tray icon, lock up the taskbar?

Note: code samples have been simplified, but the overall structure remains intact.
I am working on a Win32 application whose main interface is a system tray icon. I create a dummy window, using HWND_MESSAGE as its parent, to receive the icon's messages:
WNDCLASSEX wndClass;
wndClass.lpfnWndProc = &iconWindowProc;
// ...
iconWindowHandle = CreateWindow(wndClass.lpszClassName, _T(""), 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, GetModuleHandle(NULL), 0);
Then the icon is created, referring to this message-only window:
NOTIFYICONDATA iconData;
iconData.hWnd = iconWindowHandle;
iconData.uCallbackMessage = TRAYICON_MESSAGE;
// ...
Shell_NotifyIcon(NIM_ADD, &iconData)
When the tray icon is double-clicked, I create and show a property sheet (from comctl32.dll):
LRESULT CALLBACK iconWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case TRAYICON_MESSAGE:
switch (lParam) { // that contains the "real" message
case WM_LBUTTONDBLCLK:
showPropertySheet();
return 0;
// ...
}
break;
// ...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
The property sheet has no parent window. The PropertySheet function is called from the window procedure of the message-only window. The PSH_MODELESS flag is not set; thus, PropertySheet only returns after the property sheet window is closed again:
void showPropertySheet() {
PROPSHEETPAGE pages[NUM_PAGES];
pages[0].pfnDlgProc = &firstPageDialogProc;
// ...
PROPSHEETHEADER header;
header.hwndParent = NULL;
header.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
header.ppsp = pages;
// ...
PropertySheet(&header);
}
Now all this works just fine, until I set a breakpoint inside the dialog procedure of one of the property sheet's pages:
BOOL CALLBACK firstPageDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return FALSE; // breakpoint here
}
When the program stops on the breakpoint, the entire taskbar locks up!
The call stack is quite useless; it shows that the dialog procedure is called from somewhere inside comctl32.dll, via some calls inside user32.dll. No window procedure of my own is in between.
Making the property sheet modeless doesn't seem to help. Also, I'd rather not do this because it makes the code more complex.
As long as my dialog procedure returns quickly enough, this shouldn't be a problem. But it seems so weird that a longer operation inside the dialog procedure would not only lock up the dialog itself, but the entire shell. I can imagine that the message-only window procedure has the power to cause this behaviour, since it's more closely related to the tray icon... but this function is not shown on the call stack.
Am I doing something fundamentally wrong? Can anyone shed some light on this issue?
Actually, it's rather obvious, and the confusion must have been due to a lack of coffee.
The taskbar probably uses SendMessage to send the message to my application, which causes it to block until the message is handled. SendMessageTimeout is apparently not used.
I still think it's strange that no function of my own shows up on the call stack. Surely, such a message must flow through my message loop in order to be processed? Maybe the warning that "stack frames below this line may be incomplete or missing" was actually right, then.

Resources