Oliviaophia / SmartTaskbar

A lightweight utility which can automatically switch the display state of the Windows Taskbar.
MIT License
1.31k stars 74 forks source link

A new way to implement Auto Mode #27

Closed Oliviaophia closed 5 years ago

Oliviaophia commented 6 years ago

I have been looking for new ways to implement Auto Mode in the past few months.

The basic idea is to set the taskbar to Auto-Hide and then activate it. In this way, many of the SmartTaskbar bugs will not longer exist (#8 , #13 , #19 , #24 , #25 ).

Currently I have found three successful but flawed ways:

  1. Use the WM_ACTIVATE message to activate the taskbar:

    PostMessage(FindWindow(TEXT("Shell_TrayWnd"), nullptr), WM_ACTIVATE, WA_ACTIVE, NULL);

    Using this method, we can bring out the taskbar in a very short time. It will quickly return to the hidden state and then display again when it receives the next WM_ACTIVATE message. This way is a bit stupid, but it looks very funny ~

  2. Use the WM_USER + 459 message to activate the taskbar:

    PostMessage(FindWindow(TEXT("Shell_TrayWnd"), nullptr), WM_USER + 459, TRUE, 0x10001);

    Deactivate:

    PostMessage(FindWindow(TEXT("Shell_TrayWnd"), nullptr), WM_USER + 459, FALSE, 0);

    This method can successfully activate the taskbar for a long time, and the effect is perfect. But its last parameter doesn't always have to be 0x10001, and its changing pattern looks like random. This makes it difficult for me to locate really valid values. Moreover, this method can only activate one taskbar at a time, not the desired multi-monitor solution.

  3. Use the WM_USER + 443 message to activate the taskbar by activate start button?

    PostMessage(FindWindow(TEXT("Shell_TrayWnd"), nullptr), WM_USER + 443, TRUE, 0);

    Deactivate:

    PostMessage(FindWindow(TEXT("Shell_TrayWnd"), nullptr), WM_USER + 443, FALSE, 0);

    This method requires a click on the start button to make it selected, and then the taskbar with the button can be activated. Similarly, only one taskbar can be activated at a time.

    The above methods can be executed correctly on Windows10 1809. But these methods are not perfect, if you happen to know how to activate the taskbar when it is auto-hide, please help me!

Oliviaophia commented 6 years ago

It seems that methods 2 and 3 do not support windows below win10.

Oliviaophia commented 6 years ago

It seems that window10 changed some methods of the taskbar to message control. So I found the reason why the taskbar refused to hide sometimes. It can be hidden correctly by sending a WM_USER + 459 message.

Oliviaophia commented 5 years ago

Buggy experimental version (C++):

#include <iostream>
#include <Windows.h>
#include <dwmapi.h>

APPBARDATA msg_data = { sizeof(APPBARDATA) };

WINDOWPLACEMENT placement = { sizeof(WINDOWPLACEMENT) };

POINT cursor;

HWND window;

DWORD pid;

BOOL cloaked_val = TRUE;

inline bool is_cursor_over_taskbar()
{
    GetCursorPos(&cursor);
    switch (msg_data.uEdge)
    {
    case ABE_BOTTOM:
        if (cursor.y >= msg_data.rc.top)
            return true;
        return false;
    case ABE_LEFT:
        if (cursor.x <= msg_data.rc.right)
            return true;
        return false;
    case ABE_TOP:
        if (cursor.y <= msg_data.rc.bottom)
            return true;
        return false;
    default:
        if (cursor.x >= msg_data.rc.left)
            return true;
        return false;
    }
}

int main()
{
    auto try_show_bar = true;
    msg_data.lParam = ABS_AUTOHIDE;
    SHAppBarMessage(ABM_SETSTATE, &msg_data);
    while (true)
    {
        while (is_cursor_over_taskbar())
            Sleep(250);
        window = GetForegroundWindow();
        if (window == nullptr)
        {
            Sleep(375);
            continue;
        }

        RECT window_rect;
        GetWindowRect(window, &window_rect);
        SHAppBarMessage(ABM_GETTASKBARPOS, &msg_data);
        if (msg_data.rc.top > window_rect.bottom || window_rect.left > msg_data.rc.right || msg_data.rc.left > window_rect.right || window_rect.top > msg_data.rc.bottom)
        {
            if (try_show_bar)
            {
                try_show_bar = false;
                PostMessage(FindWindow(TEXT("Shell_TrayWnd"), nullptr), WM_USER + 459, TRUE, 0x10001);
                Sleep(375);
                continue;
            }
            Sleep(375);
            continue;
        }
        PostMessage(FindWindow(TEXT("Shell_TrayWnd"), nullptr), WM_USER + 459, FALSE, 0);
        try_show_bar = true;
    }
}

Buggy experimental version (C#): With some improvements.

switch (MsgData.uEdge)
{
    case 3:
        if (Control.MousePosition.Y >= MsgData.rc.top)
        {
            return;
        }

        break;
    case 0:
        if (Control.MousePosition.X <= MsgData.rc.right)
        {
            return;
        }

        break;
    case 1:
        if (Control.MousePosition.Y <= MsgData.rc.bottom)
        {
            return;
        }

        break;
    default:
        if (Control.MousePosition.X >= MsgData.rc.left)
        {
            return;
        }

        break;
}

Forewindow = GetForegroundWindow();

if (IsWindowVisible(Forewindow) == false)
{
    return;
}

DwmGetWindowAttribute(Forewindow, 14, out Cloakedval, sizeof(int));
if (Cloakedval)
{
    return;
}

StringBuilder sb = new StringBuilder(255);
GetClassNameW(Forewindow, sb, 255);
string name = sb.ToString();
if (name == "WorkerW" || name == "Progman")
{
    PostMessageW(FindWindow("Shell_TrayWnd", null), 0x05CB, (IntPtr)1, (IntPtr)0x10001);
    return;
}

GetWindowRect(Forewindow, out TagRect lpRect);
SHAppBarMessage(5, ref MsgData);

if (MsgData.rc.top > lpRect.bottom || lpRect.left > MsgData.rc.right || MsgData.rc.left > lpRect.right || lpRect.top > MsgData.rc.bottom)
{
    PostMessageW(FindWindow("Shell_TrayWnd", null), 0x05CB, (IntPtr)1, (IntPtr)0x10001);
    return;
}
PostMessageW(FindWindow("Shell_TrayWnd", null), 0x05CB, (IntPtr)0, (IntPtr)0);
Oliviaophia commented 5 years ago

done~