You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

关键修改说明

  1. 添加Windows API声明:导入FindWindow、SetParent、SetWindowLongPtr三个API,用于获取桌面句柄、设置父窗口和调整窗口样式。
  2. 设置桌面父窗口:在Form1_Load中,先尝试获取Progman(默认桌面窗口类名)的句柄,若失败则尝试WorkerW(多显示器环境下的桌面容器),将当前窗体绑定为桌面的子窗口。
  3. 调整窗口样式:添加WS_CHILD样式,确保窗体以子窗口身份正常显示在桌面层级中。

注意事项

  • 该窗口会被系统识别为桌面的一部分,不会被Win+D/Win+M隐藏,也不会被任务栏覆盖(需确保窗口位置不在任务栏区域内)。
  • 若需要窗口始终显示在桌面图标上方,可额外设置Me.TopMost = True,但可能影响其他应用的交互体验。
  • 多显示器场景下,若窗口未正确显示,可扩展FindWindow的逻辑,遍历所有桌面容器句柄进行匹配。

内容的提问来源于stack exchange,提问作者Patrik Rydzík

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 18:54:53