RegisterPointerInputTarget not consuming all input - windows

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.

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.

Incorrect WM_DPICHANGED triggered by SetWindowPlacement

I'm saving/restoring my window position using GetWindowPlacement/SetWindowPlacement on Windows 10. My application is DPI aware. The issue occurs when SetWindowPlacement is both sizing and moving the window from monitor #1 with one DPI to monitor #2 with a different DPI. The coordinates have been saved as the correct size for monitor #2 in the WINDOWPLACEMENT structure.
The window is first resized during SetWindowPlacement while it's still on monitor #1. Then window is moved to monitor #2, which causes a WM_DPICHANGED message to fire, saying the window size should be changed. The suggested size is incorrect since it's changing the size of the window which was already the correct size for monitor #2.
What is the correct way to solve this? Should I be setting a flag before SetWindowPlacement to ignore WM_DPICHANGED messages until that call is done? Are there cases where that will cause me to miss a message I shouldn't be ignoring?
Thanks
Edit: Attached repro for #SongZhu-MSFT.
In this test case I'm using a Surface Studio 2 as my primary monitor, running at 4500x3000, using 175% scaling. On the right of that monitor, aligned to the bottom is a 1920x1080 monitor set to 100% scaling. This code attempts to open the monitor on the right monitor with a set size, however a DPICHANGE message comes through during the SetWindowPlacement() call which causes the size to be adjusted incorrectly, unless I manually avoid it. Sample code is edited from:
https://learn.microsoft.com/en-us/windows/win32/learnwin32/windows-hello-world-sample
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, 1280, 720,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
WINDOWPLACEMENT wp = {};
wp.length = sizeof(wp);
wp.showCmd = 1;
wp.ptMaxPosition.x = -1;
wp.ptMaxPosition.y = -1;
wp.ptMinPosition.x = -1;
wp.ptMinPosition.y = -1;
wp.rcNormalPosition.left = 4510;
wp.rcNormalPosition.top = 2320;
wp.rcNormalPosition.right = wp.rcNormalPosition.left + 1850;
wp.rcNormalPosition.bottom = 2909;
::SetWindowPlacement((HWND)hwnd, &wp);
ShowWindow(hwnd, nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_DPICHANGED:
{
int dpi = HIWORD(wParam);
{
RECT* const prcNewWindow = (RECT*)lParam;
SetWindowPos(hwnd,
NULL,
prcNewWindow->left,
prcNewWindow->top,
prcNewWindow->right - prcNewWindow->left,
prcNewWindow->bottom - prcNewWindow->top,
SWP_NOZORDER | SWP_NOACTIVATE);
}
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
And the .manifest I'm using.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,permonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
</windowsSettings>
</application>
</assembly>
I encountered the same issue in a slightly different context: saving and restoring the positions of other applications' windows (i.e. when un-docking a laptop all windows move to the primary display, but when re-docking we'd like them to go back where they were).
Since I don't control the third-party application windows, neither of #Sunius' suggestions work: I cannot make them ignore WM_DPICHANGED, nor can I know whether they will indeed react to WM_DPICHANGED -- if they aren't actually DPI-aware applications then pre-adjusting the window size is unnecessary and counterproductive.
My solution so far is a little clumsy, but simple and effective: when moving windows between different-DPI monitors, simply call SetWindowPlacement() twice. The first call will put it on the right monitor, but possibly with the wrong size, but the second call will immediately fix the size since it's already in the correct position.
The only catch here is that GetDpiForWindow() appeared to be unreliable for some applications, especially if the window remained minimized (I think Windows internally doesn't update the window's DPI setting for its new monitor if it's minimized). So instead I had to use MonitorFromWindow() and then GetDpiForMonitor() to detect when a window is going to change DPI, in order to trigger the second SetWindowPlacement().
There are two ways to solve this:
Before calling SetWindowPlacement, set a flag like s_IsInsideWindowMove to true, and if it's set when WM_DPICHANGED fires, do not follow the suggestion to resize the window. Once SetWindowPlacement returns, set the flag back to false;
Calculate the size passed to SetWindowPlacement as if you were placing it on the monitor with the same DPI as the monitor the window is currently on. For instance, if you're moving the window from DPI 144 to DPI 192 monitor and you want the end result size to be 800x600, ask SetWindowPlacement for a size of 600x450.
We use the first option as it's just easier to reason about and implement.

Windows API: Simple window does not redraw correctly

I'm working on a Lua binding for Windows API. So far I've been able to create a basic window with a list box control:
require('Alien')
package.path = 'libs\\?.lua;libs\\?\\init.lua;' .. package.path
local function printf(...) io.write(string.format(...)) end
Windows = require('Windows')
local W = Windows
print("loaded OK")
print("Command line=", W.GetCommandLine())
local windowClassName = "testWindow"
local windowClass, window
local hInstance = assert(W.GetModuleHandle(nil))
assert(W.WM_CREATE)
assert(W.WM_CLOSE)
assert(W.WM_DESTROY)
assert(W.DefWindowProc)
assert(W.DestroyWindow)
assert(W.PostQuitMessage)
local msgID = {}
for k, v in pairs(Windows) do
if k:match('^WM_') then msgID[v] = k end
end
local function wndProc(hWnd, msg, wParam, lParam)
local id = msgID[msg] or ('%08X'):format(msg)
printf('wndProc(%08X, %20s, %08X, %08X\n',
hWnd,id, wParam, lParam)
if msg == W.WM_CREATE then
printf("window %08X create\n", hWnd)
elseif msg == W.WM_CLOSE then W.DestroyWindow(hWnd)
elseif msg == W.WM_DESTROY then W.PostQuitMessage(0)
else return W.DefWindowProc(hWnd, msg, wParam, lParam)
end
return 0
end
local wndProcCB = alien.callback(wndProc,
assert(W.LRESULT), assert(W.HWND),
assert(W.UINT), assert(W.WPARAM), assert(W.LPARAM))
windowClass = assert(W.WNDCLASSEX:new {
cbSize = assert(W.WNDCLASSEX.size),
style = bit.bor(W.CS_HREDRAW, W.CS_VREDRAW),
lpfnWndProc = assert(wndProcCB),
cbClsExtra = 0,
cbWndExtra = 0,
hInstance = assert(hInstance),
hIcon = nil,
hCursor = nil,
hbrBackground = assert(W.COLOR_APPWORKSPACE)+1,
lpszMenuName = nil,
lpszClassName = assert(windowClassName),
hIconSm = nil,
})
assert(W.RegisterClassEx(windowClass))
local dwExStyle = bit.bor(
W.WS_EX_TOOLWINDOW,
W.WS_EX_OVERLAPPEDWINDOW)
local dwStyle = bit.bor(
W.WS_OVERLAPPEDWINDOW,
W.WS_CLIPSIBLINGS,
W.WS_CLIPCHILDREN)
window = assert(W.CreateWindowEx(
assert(dwExStyle), --dwExStyle
assert(windowClassName), --lpClassName
"Test Window", --lpWindowName
assert(dwStyle), --dwStyle
assert(W.CW_USEDEFAULT), --x
assert(W.CW_USEDEFAULT), --y
420, 314, --width, height
nil, nil, assert(hInstance), nil)) --hWndParent, hMenu, hInstance, lpParam
local SWP_SHOW_ONLY = bit.bor(
W.SWP_ASYNCWINDOWPOS,
W.SWP_SHOWWINDOW,
W.SWP_NOACTIVATE,
W.SWP_NOMOVE,
W.SWP_NOSIZE,
W.SWP_NOZORDER)
W.SetWindowPos(assert(window), nil, 0, 0, 0, 0, assert(SWP_SHOW_ONLY))
local msg = assert(W.MSG:new())
while W.GetMessage(msg, 0, 0, 0) do
W.TranslateMessage(msg)
W.DispatchMessage(msg)
end
It creates and displays a window, but the window doesn't redraw correctly. From the console output I can see lots of WM_PAINT, WM_ERASEBKGND, WM_NCPAINT etc, which I pass on to DefWindowProc, but it seems to be not handling them.
Example screenshots:
When the window first appears, it either takes the image of whatever was behind it (as in this case) or appears solid gray. As it's dragged around, it keeps that image and anything that drags over it leaves behind a ghost image. (Can be seen on the titlebar, where I have a small white button following the active window around.) If I drag it off screen a bit, it starts to get even more glitchy but never successfully repaints itself.
I've tried various methods of handling WM_PAINT, WM_ERASEBKGND, etc but none have had any effect. Examples don't show these messages being handled either. Am I doing something wrong, or is there something else I need to be doing?
Well, I found the issue (I hope) with the help of Rohitab API Monitor. It was indeed a problem with the Lua binding: it had DefWindowProc declared as taking four int parameters. That's mostly fine, until we get a WM_ERASEBKGND message with a wParam (a handle to a device context) which is > 0x7FFFFFFF, and the call to DefWindowProc fails at converting that to an int and passes the wrong value. So the window never renders certain items to the correct device context. My debug logging (which wraps Windows API functions to print their parameters to the console) didn't catch this either, since it was logging the value before it got mangled.
Changing the declaration to use uint seems to have resolved it. (And maybe I can use API Monitor's definition files for my bindings, since the ones I have seem to be somewhat inaccurate.)

No matter what, I can't get this progress bar to update from a thread

I have a Windows app written in C (using gcc/MinGW) that works pretty well except for a few UI problems. One, I simply cannot get the progress bar to update from a thread. In fact, I probably can't get ANY UI stuff to update.
Basically, I have a spawned thread that does some processing, and from that thread I attempt to update the progress bar in the main thread. I tried this by using PostMessage() to the main hwnd, but no luck even though I can do other things like open message boxes. However, it's unclear whether the message box is getting called within the thread or on the main thread.
Here's some code:
// in header/globally accessible
HWND wnd; // main application window
HWND progress_bar; //progress bar
typedef struct { //to pass to thread
DWORD mainThreadId;
HWND mainHwnd;
char *filename;
} THREADSTUFF;
//callback function
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg){
case WM_CREATE:{
// create progress bar
progress_bar = CreateWindowEx(
0,
PROGRESS_CLASS,
(LPCTSTR) NULL,
WS_CHILD | WS_VISIBLE,
79,164,455,15,
hwnd,
(HMENU)20,
NULL,
NULL);
SendMessage(progress_bar, PBM_SETSTEP, 1, 0 );
SendMessage(progress_bar, PBM_SETPOS, 0, 0 );
//test to make sure it actually works
SendMessage(progress_bar, PBM_STEPIT, 0, 0 ); //works fine
SendMessage(progress_bar, PBM_STEPIT, 0, 0 ); //works fine
SendMessage(progress_bar, PBM_STEPIT, 0, 0 ); //works fine
SendMessage(progress_bar, PBM_STEPIT, 0, 0 ); //works fine
break;
}
case WM_COMMAND: {
if(LOWORD(wParam)==2){ //do some processing in a thread
//struct of stuff I need to pass to thread
THREADSTUFF *threadStuff;
threadStuff = (THREADSTUFF*)malloc(sizeof(*threadStuff));
threadStuff->mainThreadId = GetCurrentThreadId();
threadStuff->mainHwnd = hwnd;
threadStuff->filename = (void*)&filename;
hThread1 = CreateThread(NULL,0,convertFile (LPVOID)threadStuff,0,NULL);
}else if(LOWORD(wParam)==5){ //update progress bar
MessageBox(hwnd,"I got a message!", "Message", MB_OK | MB_ICONINFORMATION);
PostMessage(progress_bar,PBM_STEPIT,0,0);
}
break;
}
}
}
This all seems to work okay. The problem is in the thread:
DWORD WINAPI convertFile(LPVOID params){
//get passed params, this works perfectly fine
THREADSTUFF *tData = (THREADSTUFF*)params;
MessageBox(tData->mainHwnd,tData->filename,"File name",MB_OK | MB_ICONINFORMATION); //yep
PostMessage(tData->mainHwnd,WM_COMMAND,5,0); //only shows message
PostThreadMessage(tData->mainThreadId,WM_COMMAND,5,0); //does nothing
}
When I say, "only shows message," that means the MessageBox() function in the callback works, but not the PostMessage() to update the position of the progress bar.
If I use PostThreadMessage() to send a message to the main thread's message loop, I can intercept it and launch MessageBoxes so it's definitely working. However, even if I try to update the progress bar this way. it still won't update.
What am I missing?
From the MSDN documentation for PBM_STEPIT:
wParam
Must be zero.
lParam
Must be zero.
CLR_DEFAULT is defined as 0xFF000000L. What happens if you change your code to:
PostMessage(progress_bar, PBM_STEPIT, 0, 0);
I suspect the problem lies in your message loop. Anyway then, three things:
I don't think theres any reason to post a PBM_STEPIT message, just send it.
Check your message loop and make sure you arn't doing something silly like GetMessage(&msg,hwnd,... - always pass NULL for the hwnd parameter for GetMessage otherwise posted messages destined for other windows will never get dispatched.
WindowProc's are always called by windows in the correct thread. So you can SendMessage, or PostMessage, directly from the worker thread to the progress control to update it.
bonus 4th point:
MessageBox creates a window, and runs its message loop in the calling thread. In your case all the windows are created and are running in the main thread. But there is no real minus to displaying message boxes in your worker thread (other than the fact that the worker threads processing will be stopped until the Message Box is closed).

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.

Resources