3

I'm working on a basic desktop app in C++ / Win32.

My goal right now is to create a basic "sticky note" app that would be pinned / glued to the desktop, i.e always in front of the desktop but always behind of any other application. Really a personal project there, just to fight my bad memory and have my tasks/notes always visible on the desktop so I couldn't miss them when starting the computer & so on.

The behaviour I'm aiming for would be similar to Stardock Fences ("kind of" because I'm not going to store any desktop icon in there, but you hopefully get the idea)

I started with the sample code from the Get Started with Win32 and C++ docs to have the most basic Win32 minimal window setup.

What I got so far :

  1. I managed to keep my window on bottom of every other app and in front of the desktop by calling SetWindowPos in the window procedure (WindowProc), when handling the event WM_SETFOCUS (I first tried with the event WM_WINDOWPOSCHANGING as suggested in this answer but this resulted in an annoying flickering when dragging the window).
case WM_SETFOCUS:
        SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
        return 0;
  1. The problem : my window stays in front of the desktop, except when I click on the "Show Desktop" button in the taskbar (or hit the Windows + D shortcut). As I often use this shortcut myself, I'd like my window to stay over the desktop no matter what.
  2. A not-satisfying-enough-but-still-something I managed to do is to bring back my window in front of the desktop when clicking on any other window after hitting Windows + D (this mostly makes sense with multiple monitors, as opening a random app on the first one for example, will toggle back my own app in front of the desktop on another screen). I could do this using this time the event WM_SIZE and calling ShowWindow then SetWindowPos, still in the WindowProc
case WM_SIZE:
        ShowWindow(hwnd, SW_SHOWNORMAL);
        SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
        return 0;

Not ideal though, as I'd really want my app to always remain in front of the desktop and "survive" to the Show Desktop action.

What I tried :

I checked out those answers but couldn't figure out how to achieve what I want.

  1. How to make 'always-on-bottom'-window
  2. Window “on desktop” : Note on this one, I tried the trick with SetParent like this in the wWinMain
HWND desktop = FindWindow(L"ProgMan", L"Program Manager");
if (desktop == NULL)
{
    return 0;
}

ShowWindow(hwnd, nCmdShow);
SetParent(hwnd, desktop);

However, my app isn't visible at all anymore with this, even though the FindWindow didn't return NULL but an actual handle.

  1. Make aplication always on Bottom (pinned to desktop, behind all other apps) in C++/WinAPI [duplicate]
  2. Disable Minimize, Maximize, Close buttons in Win32 I tried those to "intercept" the Show Desktop event but it seems this event doesn't get fired with the Show Desktop action.

Did I miss something ?

cigien
  • 57,834
  • 11
  • 73
  • 112
Telroshan
  • 73
  • 9
  • 1
    Are you handling the `WM_WINDOWPOSCHANGING` message? – Nik Bougalis Nov 26 '20 at 19:47
  • 1
    `WM_WINDOWPOSCHANGING` is probably what you want to use. Set the `SWP_NOZORDER` flag to stop your z-order being changed. Show Desktop seems to be implemented by moving windows off-screen to -32000,-32000 so you should be able to check for that and set `SWP_NOMOVE` to prevent it. – Jonathan Potter Nov 26 '20 at 20:03
  • Thanks for your answers, I tried handling the `WM_WINDOWPOSCHANGING` with the following code : ```cpp case WM_WINDOWPOSCHANGING: SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER); return 0; ``` (Seems I can't get formatting right in this comment, apologies ) However the behaviour is still the same ; when hitting `Windows + D` or clicking the desktop button, the window gets hidden :/ Did I miss something ? – Telroshan Nov 26 '20 at 23:23
  • 2
    No, you don't call `SetWindowPos` in response to that message. You modify the parameters of the `WINDOWPOS` structure that it passes you. See the [documentation](https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanging). – Jonathan Potter Nov 26 '20 at 23:36
  • @ZhuSong-MSFT Unfortunately desktop gadgets were dropped in Windows 8. – Jonathan Potter Nov 27 '20 at 05:30
  • As far as I know, the window cannot remain visible when you select Show Desktop. You can refer to [How do I make a window remain visible even when the user selects Show Desktop?](https://devblogs.microsoft.com/oldnewthing/20110617-00/?p=10403). – Zeus Nov 27 '20 at 06:27
  • @ZhuSong-MSFT Yeah a gadget would be exactly what I want, but as Jonathan said, [they got completely removed](https://learn.microsoft.com/en-us/windows/win32/w8cookbook/desktop-gadgets-removed) – Telroshan Nov 27 '20 at 11:58
  • @JonathanPotter thanks, I tried both setting `hwndInsertAfter` to `HWND_BOTTOM` and setting the `NO_ZORDER` flag (as it's said for the latter in the doc `Retains the current Z order (ignores the hwndInsertAfter member).`) So for the first method : ```cpp case WM_WINDOWPOSCHANGING: WINDOWPOS* pos = (WINDOWPOS*)lParam; pos->hwndInsertAfter = HWND_BOTTOM; pos->flags = SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE; return 0; ``` The second method being the same without the `hwndInsertAfter` assignment and adding the flag `SWP_NOZORDER` Both give the same behaviour as previously though :/ – Telroshan Nov 27 '20 at 12:01
  • If by "same behaviour as previously" you mean disappears when you press Windows+D, that isn't a z-order issue. As I said, your window is being moved off-screen. You would need to check for a move to those off-screen coordinates and set the `SWP_NOMOVE` flag to prevent it. – Jonathan Potter Nov 27 '20 at 20:12
  • @JonathanPotter I also set the flag `SWP_NOMOVE` to prevent any movement, I put it in other events too to prevent any movement at all but it still disappears and remains invisible after hitting `Windows + D`. I wrote a `timer` function that logs the window's position (via the `GetWindowRect` function) along with a timestamp to the debug output to see what happens when I hit `Windows + D` ; turns out the coords don't move an inch, the function `IsWindowVisible` also returns true no matter if the window is on screen or hidden after a `Windows + D`, I don't see any difference in any of the values – Telroshan Nov 27 '20 at 22:09
  • 1
    Ok so @JonathanPotter you were right, don't know what exactly I messed up but I went back to the original code from the doc, recreated a timer to debug log the window's position and it's indeed moved to -32 000, -32 000 when hitting Windows + D. So in the end I just handled the events `WM_SIZE` & `WM_SETFOCUS` as well as `WM_WINDOWPOSCHANGING` and setting the flags `SWP_NOMOVE` and `SWP_NOSIZE` if the window pos x is -32000. Thanks again ! Going to write the final answer – Telroshan Nov 28 '20 at 17:19

2 Answers2

4

As @JonathanPotter pointed out, when hitting Windows + D or the Show Desktop button, the event WM_WINDOWPOSCHANGING gets fired, and the window gets moved to -32 000, -32 000 (its size also gets changed)

NOTE : a window without the style WS_MINIMIZEBOX seems not receiving WINDOWPOSCHANGING event when hitting Windows + D. Thus, no -32 000 coordinates detection in that case... Also noticed the same issue when using the ex style WS_EX_TOOLWINDOW (as this one gets rid of the minimize box, even if you set the style flag WS_MINIMIZEBOX). Didn't find a solution for that case, so I'm sticking to an overlapped window.

To prevent this movement, just set the flags SWP_NOMOVE and SWP_NOSIZE on the WINDOWPOS structure passed in the lParam

So as a final result, to achieve the wanted behaviour (i.e always behind every other window but always in front of the desktop), the only needed code to add to the doc's sample is the following, placed in the window procedure WindowProc's switch statement :

EDIT : the best place to force the Z order with HWND_BOTTOM thus ensuring the window is always on bottom is also in the WM_WINDOWPOSCHANGING event. Indeed, calling SetWindowPos to force it in the WM_SIZE event when dragging the window over, as I was doing previously, causes some flickering on the window when resizing it, whereas no flickering occurs when setting directly the hwndInsertAfter property of the WINDOWPOS structure in WM_WINDOWPOSCHANGING.

case WM_WINDOWPOSCHANGING:
{
    WINDOWPOS* pos = (WINDOWPOS*)lParam;
    // Show desktop (Windows + D) results in the window moved to -32000, -32000 and size changed
    if (pos->x == -32000) {
        // Set the flags to prevent this and "survive" to the desktop toggle
        pos->flags |= SWP_NOMOVE | SWP_NOSIZE;
    }
    // Also force the z order to ensure the window is always on bottom
    pos->hwndInsertAfter = HWND_BOTTOM;
    return 0;
}
Telroshan
  • 73
  • 9
0

I had the same problem and found a solution via using "autohotkey". autohotkey is a powerful free scripting language for automation in windows that you can download from here. Below scripts are an instance of making sticky notes always on top when you execute it inside a .ahk file (you can copy these codes to a simple notepad and then change .txt to .ahk if you already had installed autohotkey on your computer). by right clicking on .ahk file, you can also see option of Compiling .ahk into .exe which enables you to share it with your other friends too:

#SingleInstance Force
GroupAdd, ontop, Sticky Notes ; you can replace sticky note by any application, 
                              ; if you know the name of that application window
                              ; (and its ahk_class for more specific cases). 
                              ; for example you can replace "Sticky Notes"
                              ; with "Calculator" to make calculator stay always
                              ; on top.

Loop {
 WinWait, ahk_group ontop
 WinSet, AlwaysOnTop, On
 SoundBeep, 1500
 WinWaitClose
 SoundBeep, 1000
}

(main codes are credit of mikeyww)

hamflow
  • 27
  • 6