Introduction
It annoyed me a lot ('How much do you hate the Romans?' 'A lotttt.' 'OK, you're in.') that taskbar and startmenu do not autohide if I accidentally opened them, so didn't move with the mouse over them.
This little tool autohides taskbar (with Windows' autohide mode set, situated on the left side of the screen) and startmenu when there are no mouse moves over them for a defined time.
The Code
To hide taskbar and startmenu, the handles of their windows are needed.
For the taskbar, it's very easy to get the window handle, the window's class name is "Shell_TrayWnd
":
taskbarWindowHandle = NativeMethods.FindWindow("Shell_TrayWnd", null);
with:
internal class NativeMethods {
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
FindWindow()
retrieves a handle to the top-level window whose class name and window name match the specified strings.
For the startmenu, I found it not so easy.
So, I had the idea to check the class name of the window in the foreground periodically:
CheckVisibility = new System.Threading.Timer(CheckVisibilityProc, null, 0, 500);
private void CheckVisibilityProc(object notUsed) {
IntPtr foregroundWindowHandle = NativeMethods.GetForegroundWindow();
StringBuilder windowClass = new StringBuilder(32);
NativeMethods.GetClassName(foregroundWindowHandle, windowClass, windowClass.Capacity);
if (windowClass.ToString().Equals(startmenuWindowClass)) {
}
}
with:
internal class NativeMethods {
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}
GetForegroundWindow()
retrieves the handle of the window that is actually the highest in the Z-order.
GetClassName()
retrieves the class name of this window which I then check.
Personally, I use 'Classic Shell' to make Taskbar + Startmenu look more as I like (Win 2000 style).
So I have to handle two different window classes:
"Windows.UI.Core.CoreWindow
" for the Windows 10 startmenu.
"ClassicShell.CMenuContainer
" for the Classic Shell startmenu.
By enumerating all windows at start, I check if Classic Shell is running by looking for a window with the class name "ClassicShell.COwnerWindow
" (via: Settings.Default.StartmenuWindowClassClassicShell
):
startmenuWindowClass = "Windows.UI.Core.CoreWindow"
NativeMethods.EnumWindows(EnumWindowsForClassicShell, IntPtr.Zero);
private bool EnumWindowsForClassicShell(IntPtr hWnd, IntPtr lParam) {
NativeMethods.GetClassName(hWnd, windowClass, windowClass.Capacity);
if (windowClass.ToString().Equals(Settings.Default.StartmenuWindowClassClassicShell)) {
startmenuWindowClass = Settings.Default.StartmenuWindowClassClassicShell;
return false;
}
return true;
}
I want to hide taskbar and startmenu, if they are visible but there are no mouse moves over their window rectangle.
So, I have to check the mouse moves and save the time if there was one in the concerned window's rectangle:
CheckMouseMoves = new System.Threading.Timer(CheckMouseMovesProc, null, 0, 250);
private void CheckMouseMovesProc(object notUsed) {
Point newCursorPosition = Cursor.Position;
if (lastCursorPosition != newCursorPosition) {
lastCursorPosition = newCursorPosition;
if (Cursor.Position.X < taskbarWidth) { lastTaskbarMouseMove = DateTime.Now.Ticks; }
if ((Cursor.Position.X < startmenuRect.Right)
&& (Cursor.Position.Y < startmenuRect.Bottom)) {
lastStartmenuMouseMove = DateTime.Now.Ticks;
}
}
}
The static
property Cursor.Position
of the System.<wbr />Windows.<wbr />Forms.Cursor
class gets a Point
structure that represents the cursor's position in screen coordinates ((0,0) is the left upper edge of the screen).
To get the taskbar window rectangle (the window exists even if it is not visible):
taskbarWindowHandle = NativeMethods.FindWindow("Shell_TrayWnd", null);
NativeMethods.GetWindowRect(taskbarWindowHandle, out NativeMethods.RECT taskbarRect);
taskbarWidth = taskbarRect.Right - taskbarRect.Left;
with:
internal class NativeMethods {
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}
I only keep the width of the taskbar, because I assume that the taskbar height is of the full screen height.
The (Classic Shell) startmenu window only exists if it is visible, so I get its window rectangle only when I found it to be the foreground window:
private void CheckVisibilityProc(object notUsed) {
if (windowClass.ToString().Equals(startmenuWindowClass)) {
if (!isStartmenuVisible) {
isStartmenuVisible = true;
lastStartmenuMouseMove = DateTime.Now.Ticks;
}
NativeMethods.GetWindowRect(foregroundWindowHandle, out startmenuRect);
}
}
Now, to show / hide the taskbar:
private void CheckVisibilityProc(object notUsed) {
if (NativeMethods.IsWindowVisible(taskbarWindowHandle)) {
if (Cursor.Position.X > 0) {
if (DateTime.Now.Ticks - lastTaskbarMouseMove > 3000 * 10000) {
SetTaskbarVisibility(false);
}
}
}
else {
if (Cursor.Position.X <= 0) {
SetTaskbarVisibility(true);
}
}
}
private void SetTaskbarVisibility(bool show) {
NativeMethods.SetWindowPos(taskbarWindowHandle, IntPtr.Zero, 0, 0, 0, 0,
show ? NativeMethods.SWP_SHOWWINDOW : NativeMethods.SWP_HIDEWINDOW);
}
The last parameter of SetWindowPos()
is:
show ? NativeMethods.SWP_SHOWWINDOW : NativeMethods.SWP_HIDEWINDOW
This uses the conditional operator ?:
, also known as the ternary conditional operator. It evaluates a Boolean expression and if true
returns the result of the expression preceding the :
, if false
returns the result of the expression following the :
.
To hide the startmenu:
private void CheckVisibilityProc(object notUsed) {
IntPtr foregroundWindowHandle = NativeMethods.GetForegroundWindow();
NativeMethods.GetClassName(foregroundWindowHandle, windowClass, windowClass.Capacity);
if (windowClass.ToString().Equals(startmenuWindowClass)) {
if (!isStartmenuVisible) {
isStartmenuVisible = true;
lastStartmenuMouseMove = DateTime.Now.Ticks;
}
NativeMethods.GetWindowRect(foregroundWindowHandle, out startmenuRect);
if (DateTime.Now.Ticks - lastStartmenuMouseMove > 5000 * 10000) {
NativeMethods.keybd_event(NativeMethods.VK_ESCAPE, 0, 0, (IntPtr) 0);
NativeMethods.keybd_event
(NativeMethods.VK_ESCAPE, 0, NativeMethods.KEYEVENTF_KEYUP, (IntPtr) 0);
SetTaskbarVisibility(false);
}
}
else { isStartmenuVisible = false; }
}
with:
internal class NativeMethods {
[DllImport("user32.dll")]
public static extern void keybd_event
(byte bVk, byte bScan, uint dwFlags, IntPtr dwExtraInfo);
public const int KEYEVENTF_KEYUP = 0x0002;
public const int VK_ESCAPE = 27;
}
The startmenu
is hidden by simulating a keystroke of the 'Escape'-Key (makes no mess to the window).
Therefore the keydb_event()
-Function has to be called two times:
NativeMethods.keybd_event(NativeMethods.VK_ESCAPE, 0, 0, (IntPtr) 0);
NativeMethods.keybd_event(NativeMethods.VK_ESCAPE, 0, NativeMethods.KEYEVENTF_KEYUP, (IntPtr) 0);
The first call generates the Key<wbr />Down
-event (0
in the third parameter).
The second call generates the KeyUp
-event (NativeMethods.KEYEVENTF_KEYUP
in the third parameter).
In the project, I use some application settings:
One thing is, that string
constants should never appear in the code.
And it makes some changes very easy, for example for the StartmenuHideDelay
.
Or for the StartmenuWindowClassDefault
which is "Windows.UI.Core.CoreWindow
" for WIN 10 but may be different for other WIN Versions, I didn't check.
Well, this should have explained most of the code.
It's quite a short piece of code, but finally it does not so much.
Just wanted English language for my Visual Studio (instead of German). MS made me download 2,75 GB!
Have fun!
History
- 25th January, 2020: Initial version