.NET WinForms应用实现不受Win+D/Win+M隐藏的桌面窗口
.NET WinForms 实现Win+D/Win+M不隐藏的桌面常驻窗口
我在编写.NET Windows Forms应用时遇到需求:需要实现一个按下Win+D或Win+M时不会被隐藏、始终显示在桌面的窗口。尝试过AI生成方案、捕获Win+D事件、参考Rainmeter实现方式,但效果均不理想。以下是现有前端功能代码,寻求可行解决方案:
Imports NetWorkBackEnd Imports System.IO Imports System.Threading.Tasks Imports System.Runtime.InteropServices Public Class Form1 ' datagridviewname DgvNet #Region "Classes and objects" Dim taskNet As New NetTask() Dim file As New ValueHolder() Dim proxy As New ProxySetter() #End Region #Region "Form Controls" Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Me.ShowInTaskbar = False Me.TopMost = False Me.Opacity = 0.8 Me.FormBorderStyle = FormBorderStyle.None Centering() startupInit() StatusChecker() LoadDgv() End Sub Private Async Sub DgvNet_CellDoubleClick(sender As Object, e As DataGridViewCellEventArgs) Handles DgvNet.CellDoubleClick If e.RowIndex >= 0 Then Dim row = DgvNet.Rows(e.RowIndex) Dim adapterName = row.Cells("name").Value.ToString taskNet.ToggleAdapterStatus(adapterName) Await Threading.Tasks.Task.Delay(10000) LoadDgv() End If End Sub Private Sub Btn_close_Click(sender As Object, e As EventArgs) Handles Btn_close.Click Me.Close() End Sub Private Sub Tb_script_KeyDown(sender As Object, e As KeyEventArgs) Handles Tb_script.KeyDown If e.KeyValue = Keys.Enter Then If String.IsNullOrWhiteSpace(Tb_script.Text) Then MsgBox("Zadaj adresu scriptu") Else Dim value As String = Tb_script.Text.Trim() file.WriteScript(value) End If End If End Sub Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged ProxyStatus() End Sub #End Region #Region "aditional methods" Private Sub Centering() Dim screenC = Screen.PrimaryScreen.WorkingArea Me.StartPosition = FormStartPosition.Manual Me.Location = New Point((screenC.Width - Me.Width) \ 2, 0) End Sub Private Sub startupInit() If taskNet.IsScriptAllowed Then Dim values = taskNet.FirstBoot() file.WriteScript(values) End If End Sub Private Sub StatusChecker() If taskNet.IsScriptAllowed() Then CheckBox1.Checked = True Tb_script.Font = New Font(Tb_script.Font, FontStyle.Regular) Tb_script.ReadOnly = False Else CheckBox1.Checked = False Tb_script.ReadOnly = True Tb_script.Font = New Font(Tb_script.Font, FontStyle.Strikeout) End If End Sub Private Sub ProxyStatus() If CheckBox1.Checked Then proxy.EnableProxyScript() Tb_script.Font = New Font(Tb_script.Font, FontStyle.Regular) Else Tb_script.Font = New Font(Tb_script.Font, FontStyle.Strikeout) proxy.DisableProxyScript() End If End Sub Private Sub LoadDgv() Dim cards = taskNet.GetNetWorkCards() DgvNet.DataSource = cards Tb_script.Text = file.ReadScript() End Sub #End Region End Class
解决方案
要实现这种桌面常驻窗口,核心思路是将窗口设置为桌面的子窗口,让系统将其识别为桌面的一部分,而非普通顶级窗口。这样Win+D/Win+M只会隐藏普通应用窗口,不会影响该窗口。具体需要借助Windows API完成以下操作:
- 使用
FindWindow获取桌面窗口的句柄 - 使用
SetParent将当前窗体的父句柄设置为桌面窗口 - 调整窗体样式,确保其正确显示在桌面层级
修改后的完整代码
Imports NetWorkBackEnd Imports System.IO Imports System.Threading.Tasks Imports System.Runtime.InteropServices Public Class Form1 ' datagridviewname DgvNet ' 添加Windows API声明 <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)> Private Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr End Function <DllImport("user32.dll")> Private Shared Function SetWindowLongPtr(ByVal hWnd As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As IntPtr) As IntPtr End Function Private Const GWL_STYLE As Integer = -16 Private Const WS_CHILD As Integer = &H40000000 #Region "Classes and objects" Dim taskNet As New NetTask() Dim file As New ValueHolder() Dim proxy As New ProxySetter() #End Region #Region "Form Controls" Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Me.ShowInTaskbar = False Me.TopMost = False Me.Opacity = 0.8 Me.FormBorderStyle = FormBorderStyle.None ' 设置窗口为桌面子窗口 Dim desktopHandle = FindWindow("Progman", Nothing) ' 如果Progman找不到,尝试WorkerW(多显示器场景下可能存在) If desktopHandle = IntPtr.Zero Then desktopHandle = FindWindow("WorkerW", Nothing) End If If desktopHandle <> IntPtr.Zero Then SetParent(Me.Handle, desktopHandle) ' 调整窗口样式,确保作为子窗口正常显示 SetWindowLongPtr(Me.Handle, GWL_STYLE, New IntPtr(WS_CHILD)) End If Centering() startupInit() StatusChecker() LoadDgv() End Sub Private Async Sub DgvNet_CellDoubleClick(sender As Object, e As DataGridViewCellEventArgs) Handles DgvNet.CellDoubleClick If e.RowIndex >= 0 Then Dim row = DgvNet.Rows(e.RowIndex) Dim adapterName = row.Cells("name").Value.ToString taskNet.ToggleAdapterStatus(adapterName) Await Threading.Tasks.Task.Delay(10000) LoadDgv() End If End Sub Private Sub Btn_close_Click(sender As Object, e As EventArgs) Handles Btn_close.Click Me.Close() End Sub Private Sub Tb_script_KeyDown(sender As Object, e As KeyEventArgs) Handles Tb_script.KeyDown If e.KeyValue = Keys.Enter Then If String.IsNullOrWhiteSpace(Tb_script.Text) Then MsgBox("Zadaj adresu scriptu") Else Dim value As String = Tb_script.Text.Trim() file.WriteScript(value) End If End If End Sub Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged ProxyStatus() End Sub #End Region #Region "aditional methods" Private Sub Centering() Dim screenC = Screen.PrimaryScreen.WorkingArea Me.StartPosition = FormStartPosition.Manual Me.Location = New Point((screenC.Width - Me.Width) \ 2, 0) End Sub Private Sub startupInit() If taskNet.IsScriptAllowed Then Dim values = taskNet.FirstBoot() file.WriteScript(values) End If End Sub Private Sub StatusChecker() If taskNet.IsScriptAllowed() Then CheckBox1.Checked = True Tb_script.Font = New Font(Tb_script.Font, FontStyle.Regular) Tb_script.ReadOnly = False Else CheckBox1.Checked = False Tb_script.ReadOnly = True Tb_script.Font = New Font(Tb_script.Font, FontStyle.Strikeout) End If End Sub Private Sub ProxyStatus() If CheckBox1.Checked Then proxy.EnableProxyScript() Tb_script.Font = New Font(Tb_script.Font, FontStyle.Regular) Else Tb_script.Font = New Font(Tb_script.Font, FontStyle.Strikeout) proxy.DisableProxyScript() End If End Sub Private Sub LoadDgv() Dim cards = taskNet.GetNetWorkCards() DgvNet.DataSource = cards Tb_script.Text = file.ReadScript() End Sub #End Region End Class
关键修改说明
- 添加Windows API声明:导入
FindWindow、SetParent、SetWindowLongPtr三个API,用于获取桌面句柄、设置父窗口和调整窗口样式。 - 设置桌面父窗口:在
Form1_Load中,先尝试获取Progman(默认桌面窗口类名)的句柄,若失败则尝试WorkerW(多显示器环境下的桌面容器),将当前窗体绑定为桌面的子窗口。 - 调整窗口样式:添加
WS_CHILD样式,确保窗体以子窗口身份正常显示在桌面层级中。
注意事项
- 该窗口会被系统识别为桌面的一部分,不会被Win+D/Win+M隐藏,也不会被任务栏覆盖(需确保窗口位置不在任务栏区域内)。
- 若需要窗口始终显示在桌面图标上方,可额外设置
Me.TopMost = True,但可能影响其他应用的交互体验。 - 多显示器场景下,若窗口未正确显示,可扩展
FindWindow的逻辑,遍历所有桌面容器句柄进行匹配。
内容的提问来源于stack exchange,提问作者Patrik Rydzík
相关产品推荐
相关产品推荐

