My scenario is that a program calls my DLL and I use Process.Start(notepad) to start a Notepad window on Windows 10 x64. I don't save the process ID of the started process. Then sometime later, a program calls my DLL again, and I find the Notepad window handle (by matching title strings).
The problem is that when I use the handle to send the window a WM_CLOSE or DESTROY message, the job isn't completed. The window does disappear from my screen. It does disappear from the taskbar. But when I look at windows with Alt-TAB, there it is. It's not really gone; it's just hidden from the taskbar. I'm using this Win32 API call to try to close the window.
[DllImport ("user32.dll", SetLastError = true)]
public static extern bool CloseWindow (IntPtr hWnd);
(1) What am I doing wrong?
(2) Are my expectations out of line? Is it even possible for me (as a DLL running under one process) to order Notepad in another process to close?
I suppose, having the Notepad handle in hand, that I could bring it to the foreground and send Alt+F4 to its keyboard buffer somehow to fake it into thinking that I was typing characters to it. But, that seems like the long way around.
(3) How can I programmatically tell apps to close their windows without bringing them to the foreground and sending them keystrokes, or without sending them mouse clicks on the X close button?
I've looked at other posts on the forum, but they mostly talk about terminating the process with the process APIs - a brute force kill method that isn't really what I want to do. (Besides, I want to close the single window that I'm interested in, not a whole process that might be running a dozen different windows like MS Word...)
I'm using [CloseWindow] to try to close the window.
CloseWindow doesn't close a window -- it minimizes it.
Instead, send the window a WM_CLOSE message:
SendMessage(h, WM_CLOSE, 0, 0);
Try doing this:
SendMessage (hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); // or PostMessage perhaps better
This is what Windows sends when you click on the Close box and is much more likely to work in a wider range of apps since the app will then believe that it has been closed by the user and should act accordingly.
Related
If I register a hook via SetWindowsHookEx(WH_SHELL, ShellProc, ...), what is the meaning of event HSHELL_WINDOWREPLACED? (My Google-fu fails me. I have searched high and low!)
Win32 Docs:
SetWindowsHookEx(): https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexw
ShellProc (callback): https://learn.microsoft.com/en-us/windows/win32/winmsg/shellproc
The offical docs read: A top-level window is being replaced. Weirdly, they also say: Windows 2000: Not supported. Does that mean only supported before or after Win2K?
I created a test driver to watch a Microsoft Windows session, but I was never able to trigger this mysterious event.
I also found a similar event here:
RegisterShellHookWindow: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registershellhookwindow
... that says:
HSHELL_WINDOWREPLACING: A handle to the window replacing the top-level window.
HSHELL_WINDOWREPLACED: A handle to the window being replaced.
Related:
How can I be notified when a new window is created on Win32?
Why HSHELL_WINDOWDESTROYED, HSHELL_WINDOWCREATED?
In this instance, the term "replace" refers to the occasions when a window stops responding to messages ("hangs") and, after a certain period, Windows hides it and replaces it on-screen with a faded-out copy (called a "ghost window").
Windows does this so that, even when the app is not processing messages, the user can interact with the ghost window to move it around and try to close it.
The wParam value is the handle of the hung window (the one being replaced) and the lParam value is the handle of the ghost window (its replacement).
If the window starts responding again, the notification is sent again, with the window handles swapped around.
I am trying to write a program what will manage few console windows, my program will be able to CreateProcess() for new console windows, get a window main handle and the use that handle to resize, close, hide, change title etc. But I cannot find a reliable way to get a main window handle. The purpose is to have a tab bar and switch between created console windows with the click on the tab.
I have tried few ways:
1) use windows "cmd.exe" ability to set window title, and then FindWindow("tmp_title"...)
This has a problem, I do not need cmd.exe running, and also I need a processID for the target program not the cmd.exe. Maybe I should use this way but check for children subprocesses?
2) EnumWindows() then CreateProcess() then wait 40 ms, then EnumWindows() again and find the new window.
This is unreliable! I got two new windows sometimes for weird reasons.
3) use GetWindowThreadProcessId() + EnumWindows(). This worked the best on XP, but on win7 the found window seems to be the wrong one, it's GetWindowText() returns "DefaultIME" and hide/show of this window does nothing. So it is obviously a wrong one.
So any idea how to do it reliably and if possible cross-platform (Cross-windows, XP,Vista,7)
By now everyone writing for Windows probably knows that applications cannot (officially) steal focus from foreground processes, and why. But I have just managed to steal focus, inadvertently, and don't understand how this is even possible.
I have a Delphi app that user brings up with a hotkey (or by a mouse click, or by Alt+Tab), selects a piece of text and hits Enter. My app then minimizes (hides to the tray, even), and pastes the text user just selected into the active window. Nothing new here, plenty of similar projects out there - clipboard extenders, glossaries, macro programs, etc.
What is puzzling to me is that after doing all the above and then sleeping for 1500 ms, I restore my main form and it gets the focus back! It becomes the foreground window, even though it wasn't 1500 ms ago (tested; Windows 7 32-bit.).
In fact, I don't want this at all, so before restoring my main form I record which window has foreground and I give it back to that window after it's been given to me. I'm just curious why my app gets to be in the foreground when by rules it should not. Maybe I don't understand the rules as fully as I thought I did?
If you look at the documentation for SetForegroundWindow you see a list of conditions for the call to succeed, one of them is "The process received the last input event." So if the user does not do anything after pressing enter in your app you still have the right to steal focus. I don't know if Delphi calls SetForegroundWindow for you when the window is restored but it might be something to look into.
I don't know how you restore your window but using SW_SHOWNOACTIVATE with ShowWindow might help...
There are cases when one needs to start a process based on some events not involving activity of the mouse of keyboard (from hardware or time-based). It works with ShellExecute and ShellExecuteEx generally, but if there's a full-screen app (the one, created borderless with exactly the dimensions of the screen), this launched application don't become active or even visible. Is there a technique to bring such application to top over this full-screen app? I'm aware about ShellExecuteEx and hProcess manipulation, but it seems it involves very recent api (GetProcessId) and seems that this function has some limitations related to user rights.
Thanks
You can use
SystemParametersInfo (SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (PVOID)0,
SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);
to do so before the process start, but you should do this temporary like described in How to ensure process window launched by Process.Start(ProcessStartInfo) has focus of all Forms?.
Please do this only you really need to start a process on top.
I have made an application already that sends commands to an activated window. I want to be able to use the computer while my process is running because as soon as I switch focus to another window the key strokes being sent via send keys will go to the window I just switched to.
Currently I use FindWindow, IsIconic, and ShowWindow from the Windows API. I have to check to see if the window is there with FindWindow and set my object to the specific window that is returned with that call, I then check if it's minimized with IsIconic and call ShowWindow if it is, and then finally I have to call Interaction.AppActivate to set focus to that window. All of this is done before I even send key strokes. Seems like there should be a way to just send key strokes without having to show the window and activate it. The big thing is while my application is running the key strokes I can't do anything on my computer.
Alright, this is kind of disappointing I'm sure, but you fundamentally cannot do this with 100% reliability.
Windows assumes that the active window is the one getting keyboard input. The proper way to fake keyboard input is with SendInput, and you'll notice that it sends messages to the active window only.
That being said, you can SendMessage WM_KEYUP, WM_CHAR, and WM_KEYDOWN messages and (depending on the WndProc receiving them) maybe get away with it. But remember, its going to break under some circumstances, period.
Sounds like you are using keybd_event() or SendInput(), which both send keystrokes to the currently active window. To direct keystrokes to a specific window, regardless of whether that widnow is focused or not, you need to find its HWND handle first, and then post appropriately-formatted WM_KEYUP/DOWN and WM_CHAR messages directly to it.
once you have the windows HWND, you can directly SendMessage() the WM_KEYDOWN and WM_KEYUP messages to its message queue. The window does not have to be active.
However, understand that this depends on how the target application processes keyboard input. There are several different ways to handle it.
WM_KEYUP/WM_KEYDOWN is most common and some applications only process one or the other (usually WM_KEYDOWN).
WM_CHAR is also fairly common
Some programs use GetAsyncKeyState, GetKeyState, or GetKeyboardState. This is extremely unusual, but effectively prevents keypress injection with SendMessage(). If this is the case fall back to keybd_event() which is directly handled by the keyboard driver. Of course the window will have to be active