任务栏和开始菜单的扩展自动隐藏





5.00/5 (4投票s)
在鼠标未移动一段时间后自动隐藏任务栏和开始菜单
引言
让我很烦恼的是(“你有多讨厌罗马人?”“非常讨厌。”“好吧,你加入了。”),如果我不小心打开了任务栏和开始菜单,它们不会自动隐藏,因此当鼠标移动到它们上面时也不会隐藏。
这个小工具会在一段时间内没有鼠标移动到任务栏和开始菜单上时,自动隐藏任务栏(使用 Windows 的自动隐藏模式,位于屏幕左侧)和开始菜单。
代码
要隐藏任务栏和开始菜单,需要它们的窗口句柄。
对于任务栏,获取窗口句柄非常容易,窗口的类名是 "Shell_TrayWnd
"
taskbarWindowHandle = NativeMethods.FindWindow("Shell_TrayWnd", null);
用
// Stuff from unmanaged libraries (P/Invoke)
// shall always be located in a class named 'NativeMethods'.
// From the great pinvoke.net site
internal class NativeMethods {
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
FindWindow()
检索一个句柄,该句柄指向其类名和窗口名称与指定的字符串匹配的顶级窗口。
对于开始菜单,我发现它不太容易。
所以,我有一个想法,定期检查前台窗口的类名
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);
// foreground window class equal startmenu window class?
if (windowClass.ToString().Equals(startmenuWindowClass)) {
// do something with the startmenu window
}
}
用
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()
检索实际上位于 Z 顺序最顶层的窗口的句柄。
GetClassName()
检索此窗口的类名,然后我对其进行检查。
就我个人而言,我使用 'Classic Shell' 来使任务栏 + 开始菜单看起来更像我喜欢的样子(Win 2000 风格)。
所以我必须处理两个不同的窗口类
"Windows.UI.Core.CoreWindow
" 用于 Windows 10 开始菜单。
"ClassicShell.CMenuContainer
" 用于 Classic Shell 开始菜单。
通过在启动时枚举所有窗口,我通过查找类名为 "ClassicShell.COwnerWindow
" 的窗口来检查 Classic Shell 是否正在运行(通过:Settings.Default.StartmenuWindowClassClassicShell
)
// set default value
startmenuWindowClass = "Windows.UI.Core.CoreWindow"
// check if Classic Shell is running
NativeMethods.EnumWindows(EnumWindowsForClassicShell, IntPtr.Zero);
private bool EnumWindowsForClassicShell(IntPtr hWnd, IntPtr lParam) {
NativeMethods.GetClassName(hWnd, windowClass, windowClass.Capacity);
// Classic Shell main window found?
if (windowClass.ToString().Equals(Settings.Default.StartmenuWindowClassClassicShell)) {
// set window class for Classic Shell startmenu
startmenuWindowClass = Settings.Default.StartmenuWindowClassClassicShell;
// end enumerating
return false;
}
// continue enumerating
return true;
}
如果任务栏和开始菜单可见,但它们的窗口矩形上没有鼠标移动,我想隐藏它们。
因此,我必须检查鼠标移动并保存时间,如果存在于相关窗口的矩形中
CheckMouseMoves = new System.Threading.Timer(CheckMouseMovesProc, null, 0, 250);
private void CheckMouseMovesProc(object notUsed) {
Point newCursorPosition = Cursor.Position;
// mouse moved?
if (lastCursorPosition != newCursorPosition) {
lastCursorPosition = newCursorPosition;
// within the taskbar/startmenu area? => save time of mousemove
if (Cursor.Position.X < taskbarWidth) { lastTaskbarMouseMove = DateTime.Now.Ticks; }
// the startmenu may not be located at point(0,0) - do not mind
if ((Cursor.Position.X < startmenuRect.Right)
&& (Cursor.Position.Y < startmenuRect.Bottom)) {
lastStartmenuMouseMove = DateTime.Now.Ticks;
}
}
}
System.<wbr />Windows.<wbr />Forms.Cursor
类的 static
属性 Cursor.Position
获取一个 Point
结构,该结构表示光标在屏幕坐标中的位置((0,0) 是屏幕的左上边缘)。
要获取任务栏窗口矩形(即使它不可见,窗口也存在)
// get taskbar window handle + window rect (for window width)
taskbarWindowHandle = NativeMethods.FindWindow("Shell_TrayWnd", null);
NativeMethods.GetWindowRect(taskbarWindowHandle, out NativeMethods.RECT taskbarRect);
taskbarWidth = taskbarRect.Right - taskbarRect.Left;
用
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;
}
}
我只保留任务栏的宽度,因为我假设任务栏高度是全屏高度。
(Classic Shell)开始菜单窗口仅在可见时才存在,因此我仅在发现它是前台窗口时才获取其窗口矩形
private void CheckVisibilityProc(object notUsed) {
// ...
// foreground window class equal startmenu window class?
if (windowClass.ToString().Equals(startmenuWindowClass)) {
// startmenu was not visible?
if (!isStartmenuVisible) {
isStartmenuVisible = true;
// set time of last mousemove to begin of visibility
lastStartmenuMouseMove = DateTime.Now.Ticks;
}
// get the window rect to record mousemoves in the startmenu area
NativeMethods.GetWindowRect(foregroundWindowHandle, out startmenuRect);
// ...
}
}
现在,显示/隐藏任务栏
private void CheckVisibilityProc(object notUsed) {
// taskbar visible?
if (NativeMethods.IsWindowVisible(taskbarWindowHandle)) {
// mouse not on the left edge of the screen?
if (Cursor.Position.X > 0) {
// time since last mouse move > TaskbarHideDelay (in mSec)? => hide taskbar
if (DateTime.Now.Ticks - lastTaskbarMouseMove > 3000 * 10000) {
SetTaskbarVisibility(false);
}
}
}
else {
// mouse on the left edge of the screen? => show taskbar
if (Cursor.Position.X <= 0) {
SetTaskbarVisibility(true);
}
}
// ...
}
private void SetTaskbarVisibility(bool show) {
// show/hide taskbar window (setting position + size is not necessary => ', 0, 0, 0, 0,')
NativeMethods.SetWindowPos(taskbarWindowHandle, IntPtr.Zero, 0, 0, 0, 0,
show ? NativeMethods.SWP_SHOWWINDOW : NativeMethods.SWP_HIDEWINDOW);
}
SetWindowPos()
的最后一个参数是
show ? NativeMethods.SWP_SHOWWINDOW : NativeMethods.SWP_HIDEWINDOW
这使用条件运算符 ?:
,也称为三元条件运算符。它计算一个布尔表达式,如果为 true
,则返回 :
前面的表达式的结果,如果为 false
,则返回 :
后面的表达式的结果。
隐藏开始菜单
private void CheckVisibilityProc(object notUsed) {
// ...
IntPtr foregroundWindowHandle = NativeMethods.GetForegroundWindow();
NativeMethods.GetClassName(foregroundWindowHandle, windowClass, windowClass.Capacity);
// foreground window class equal startmenu window class?
if (windowClass.ToString().Equals(startmenuWindowClass)) {
// startmenu was not visible?
if (!isStartmenuVisible) {
isStartmenuVisible = true;
// set time of last mousemove to begin of visibility
lastStartmenuMouseMove = DateTime.Now.Ticks;
}
// get the window rect to record mousemoves in the startmenu area
NativeMethods.GetWindowRect(foregroundWindowHandle, out startmenuRect);
// time since last mousemove > StartmenuHideDelay (in mSec)? => hide startmenu
if (DateTime.Now.Ticks - lastStartmenuMouseMove > 5000 * 10000) {
// by simulating 'ESC'-keystroke
NativeMethods.keybd_event(NativeMethods.VK_ESCAPE, 0, 0, (IntPtr) 0);
NativeMethods.keybd_event
(NativeMethods.VK_ESCAPE, 0, NativeMethods.KEYEVENTF_KEYUP, (IntPtr) 0);
// and hide the taskbar which may be shown
SetTaskbarVisibility(false);
}
}
else { isStartmenuVisible = false; }
}
用
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;
}
通过模拟按键“Esc”来隐藏 startmenu
(不会对窗口造成任何混乱)。
因此,必须调用 keydb_event()
函数两次
NativeMethods.keybd_event(NativeMethods.VK_ESCAPE, 0, 0, (IntPtr) 0);
NativeMethods.keybd_event(NativeMethods.VK_ESCAPE, 0, NativeMethods.KEYEVENTF_KEYUP, (IntPtr) 0);
第一次调用生成 Key<wbr />Down
事件(第三个参数中的 0
)。
第二次调用生成 KeyUp
事件(第三个参数中的 NativeMethods.KEYEVENTF_KEYUP
)。
在项目中,我使用了一些应用程序设置
其中一件事是,string
常量永远不应出现在代码中。
它使一些更改非常容易,例如对于 StartmenuHideDelay
。
或者对于 StartmenuWindowClassDefault
,它对于 WIN 10 是 "Windows.UI.Core.CoreWindow
",但对于其他 WIN 版本可能不同,我没有检查。
好吧,这应该已经解释了大部分代码。
这只是一小段代码,但最终它并没有做那么多。
只是想让我的 Visual Studio 使用英语(而不是德语)。微软让我下载了 2.75 GB!
玩得开心!
历史
- 2020 年 1 月 25 日:初始版本