Question

I have a Windows app that uses the AppBar API to install as an application bar at the top of the screen (similar to the Windows task bar itself). This works great and the desktop size is adjusted accordingly, so my application is always visible.

However, if the user choose 'Show Desktop' (Windows+D), my application is hidden. Does anyone know of a way to trap 'Show Desktop' so I can ensure my application stays visible (I assume that Windows enumerates all top-level windows and hides them with ShowWindow(SW_HIDE).

Was it helpful?

Solution

Use the following code and pass the window handle to the function while form load. Hopefully this resolves your problem.

public void SetFormOnDesktop(IntPtr hwnd)
{
    IntPtr hwndf = hwnd;
    IntPtr hwndParent = FindWindow("ProgMan", null);
    SetParent(hwndf, hwndParent);
}

OTHER TIPS

I was under the impression that setting the window as a topmost window (via SetWindowPos and the HWND_TOPMOST flag) prevented the desktop from covering it. Windows+D goes through minimizing all windows, and then covering up those that can't be minimized by raising the desktop in the z-order (well, it did at one point anyway). I belive you can make a window unminimizable by not passing WS_MINIMIZEBOX to CreateWindowEx, or using WS_EX_TOOLWINDOW though I'm not 100% on that part.

A much heavier handed approach would be to hook the global keyboard using SetWindowsHookEx and a KeyboardProc. This will have a deleterious effect on the user experience.


I went and coded up a really simple example of what I'm talking about. The following code makes a window that is not minimized OR covered by a user hitting Windows+D. Note that on Windows 7, gadgets on the desktop can still be brought above it; which I can't really explain.

#include <windows.h>
#include <tchar.h>

#define WIN_TITLE _T("Resists Win+D Window")
#define WIN_CLASS _T("Resists Win+D Class")

LRESULT CALLBACK CustomWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    //Special behavior goes here

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

HWND CreateMainWindow(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = CustomWndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = WIN_CLASS;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

    if (!RegisterClassEx(&wcex))
    {
        exit(1);
    }

    HWND hWnd = CreateWindowEx(
        WS_EX_TOOLWINDOW,
        WIN_CLASS,
        WIN_TITLE,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        500,
        100,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hWnd)
    {
        exit(1);
    }

    return hWnd;
}

/*
  Main entry point
*/
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    HWND hwnd = CreateMainWindow(hInstance);

    ShowWindow(hwnd, nCmdShow);
    SetWindowPos(hwnd, HWND_TOPMOST, -1, -1, -1, -1, SWP_NOMOVE | SWP_NOSIZE);
    UpdateWindow(hwnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

In your ABN_FULLSCREENAPP notification, you need to determine whether the window occupying the working area is the Desktop and if so, ignore the ABN_FULLSCREENAPP message.

P.S. As an alternative implementation, consider the commercial ShellAppBar component.

In addition to the answer of JKS, here is working code for VB.NET, assuming you already converted your form to an appbar. You need to p/invoke the functions FindWindow and SetFormOnDesktop.

'In your form
Public Sub New()
    'Stuff
    SetFormOnDesktop(Me.Handle)
    'More stuff
End Sub

'In your form or somewhere else.
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindow( _
 ByVal lpClassName As String, _
 ByVal lpWindowName As String) As IntPtr
End Function

<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Public Shared Function SetParent(_
 ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
End Function

Public Sub SetFormOnDesktop(hwnd As IntPtr)
    Dim hwndf As IntPtr = hwnd
    Dim hwndParent As IntPtr = FindWindow("ProgMan", Nothing)
    SetParent(hwndf, hwndParent)
End Sub
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top