Can you have a win32 program that consists solely of a tray (notification) icon? - windows

I have a program that literally consists of a tray icon. No GUI is needed. However, when writing the win32 code, is it necessary to still initialize a hWnd object to be associated with the tray icon?
For instance, it is normal to have a the NOTIFYICONDATA hWnd field point to the window's handle. Like
nid.hWnd = hwnd;
Essentially, will my icon be able to still receive messages if i set
nid.hwnd = NULL;

How would you receive messages without a window?
Yes you need a window associated with the tray icon.

You could create a message-only window by specifying HWND_MESSAGE creating the window. However, message-only windows do not receive broadcast messages and you would miss out on the TaskbarCreated message. This message tells your application that explorer.exe has restarted and that your application needs to re-add its notification icons. Rather important. So create a window that never becomes visible: never call ShowWindow().

Related

SetWindowsHookEx(WH_SHELL, ...): What is the meaning of event HSHELL_WINDOWREPLACED?

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.

Why does WM_CLOSE/DESTROY only partially close my Notepad window?

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.

How to intercept a window message in a shell extension

I have a shell extension that needs to reload its configuration when a specific window message (custom message registered with RegisterWindowMessage) is broadcasted by another application.
I tried several approaches to intercept the message:
Installing a window subclass callback on a window of Windows Explorer, using SetWindowSubclass. This works on Window 7, but not on Windows 8, because apparently DllMain is not called on the main thread, and SetWindowSubclass doesn't work from another thread. This is mentioned in the documentation:
You cannot use the subclassing helper functions to subclass a window across threads
Installing a hook for CALLWNDPROC, using SetWindowsHookEx. Because I don't want to slow down the whole system, I install the hook for a specific thread only (the explorer's main thread). This works on Windows 8, but not on Windows 7... I suspect this is because I'm hooking on the wrong thread, but I'm not sure. And anyway, this approach seems overly intrusive.
Creating a message-only window to handle the message. This doesn't work at all, because message-only windows don't receive broadcasted messages.
Is there a reliable way to receive a window message in a shell extension?
A window message initially seemed to be the easiest way to notify the shell extension, but if you think another mechanism would be more appropriate, I'm open to suggestions.
Create a hidden window and listen for the message in its window procedure.
Register a window class that has default values for all the fields apart from the window procedure and class name. You don't need to specify anything else in the window class since the window will never be visible.
When you create the window, pass 0 for the window style. Specifically exclude WS_VISIBLE.
Pass 0 for the WndParent when you create the window. This will make it a top level window and so eligible to receive broadcast messages.

What is the HWND ref that comes with HCBT_ACTIVATE when you alt-tab to the Desktop (on Win7)?

I'd need to detect when the user alt-tabs to the 'Desktop' on Windows 7. The resulting HCBT_ACTIVATE message, intercepted by my global CBT hook, contains the HWND reference to a window with WorkerW set as it's class. Safe (enough) to assume there aren't many visible top level alt tab windows with WorkerW as their class? Perhaps there is a better way you'd suggest to detect that the alt-tab activated window indeed corresponds to the window switching operation that gets you to the desktop?
Comparing the handle to the value returned by GetDesktopWindow() could be useful.

How do I send key strokes to a window without having to activate it using Windows API?

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

Resources