2

前段时间我问了这个问题,在这里解决了:

但是现在,由于未知原因,那里提供的 C# 或 Vb.Net 代码不起作用,我不明白为什么不能。

我对那里提供的原始代码进行了一些修改,但我测试了原始代码并没有工作。

发生的事情是我无法取消隐藏的过程,我不确定我在哪里失败了。乍一看,我认为我得到的句柄FindWindowEx并不真正对应我想要的句柄。

这些是我的 P/Invoking 函数签名和显示窗口枚举:

<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto, 
           BestFitMapping:=False, ThrowOnUnmappablechar:=True)>
Friend Shared Function FindWindow(
                 ByVal lpClassName As String,
                 ByVal lpWindowName As String
) As IntPtr
End Function

<DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto, 
           BestFitMapping:=False, ThrowOnUnmappablechar:=True)>
Friend Shared Function FindWindowEx(
                 ByVal hwndParent As IntPtr,
                 ByVal hwndChildAfter As IntPtr,
                 ByVal strClassName As String,
                 ByVal strWindowName As String
) As IntPtr
End Function

<DllImport("user32.dll")>
Friend Shared Function GetWindowThreadProcessId(
                 ByVal hWnd As IntPtr,
                 ByRef processId As Integer
) As Integer
End Function

<DllImport("User32", SetLastError:=False)>
Friend Shared Function ShowWindow(
                 ByVal hwnd As IntPtr,
                 ByVal nCmdShow As ProcessUtil.WindowState
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

Public Enum WindowState As Integer
    Hide = 0
    Normal = 1
    ShowMinimized = 2
    Maximize = 3
    ShowMaximized = Maximize
    ShowNoActivate = 4
    Show = 5
    Minimize = 6
    ShowMinNoActive = 7
    ShowNA = 8
    Restore = 9
    ShowDefault = 10
    ForceMinimize = 11
End Enum

功能:

Public Function SetWindowState(ByVal p As Process,
                               ByVal windowState As ProcessUtil.WindowState) As Boolean

    Dim pHandle As IntPtr = IntPtr.Zero
    Dim pid As Integer

    ' If window is visible then...
    If (p.MainWindowHandle <> IntPtr.Zero) Then
        Return ProcessUtil.NativeMethods.ShowWindow(p.MainWindowHandle, windowState)

    Else ' window is hidden.

        ' Check all open windows (not only the process we are looking), 
        ' begining from the child of the desktop.
        While (pid <> p.Id)

            ' Get child handle of window who's handle is "pHandle".
            pHandle = NativeMethods.FindWindowEx(IntPtr.Zero, pHandle, Nothing, Nothing)

            ' Get PID from "pHandle".
            NativeMethods.GetWindowThreadProcessId(pHandle, pid)

        End While

        Return NativeMethods.ShowWindow(pHandle, windowState)

    End If

End Function

以及我尝试测试该功能的方式,首先我隐藏记事本进程的窗口,然后尝试取消隐藏它。

Dim p As Process = Process.GetProcessesByName("notepad").First
ProcessUtil.SetWindowState(p, ProcessUtil.WindowState.Hide)

' I find again the process to renew the "p.MainWindowHandle" as IntPtr.Zero.
p = Process.GetProcessesByName("notepad").First
ProcessUtil.SetWindowState(p, ProcessUtil.WindowState.Restore)
4

2 回答 2

2

记事本的问题在于它有 3 个窗口(spy++类名):

1.“记事本”
2.“MSCTFIME UI”
3.“输入法”

你得到了第二个(无论如何我得到了)的句柄,MSCTFIME UI,这就是你不能显示它的原因。您需要指定类名记事本以获得正确的句柄:

pHandle = FindWindowEx(IntPtr.Zero, pHandle, "Notepad", Nothing)
于 2015-11-07T07:42:27.310 回答
2

现在是您开始使用 .NET Framework 源代码的时候了,这样您就可以发现为什么这样的代码不能自己运行。访问参考源网站以开始使用。

在搜索框中键入“Process.MainWindowHandle”,您将到达此页面。很容易看出是 ProcessManager.GetMainWindowHandle 完成了工作。单击“GetMainWindowHandle”。它很小,点击“FindMainWindow”。请注意它是如何枚举窗口的,向右滚动一下以查看完成工作的“EnumWindowsCallback”。点击它,很容易看到它是“IsMainWindow”,它决定了一个窗口是否是主窗口。点击它:

    bool IsMainWindow(IntPtr handle) {

        if (NativeMethods.GetWindow(new HandleRef(this, handle), NativeMethods.GW_OWNER) != (IntPtr)0
            || !NativeMethods.IsWindowVisible(new HandleRef(this, handle)))
            return false;

        return true;
    }

换句话说,如果它是一个拥有的窗口,它就不是一个主窗口句柄。 或者如果它不可见

后一个条款当然是你的克星。你隐藏了窗口,现在框架代码不再认为它可以是主窗口。所以 Process.MainWindowHandle 返回 IntPtr.Zero 并且您的代码无法再工作。

一种明显的解决方法是简单地重写 .NET Framework 代码并跳过IsWindowVisible()测试。然而,这是一项重要的测试,它避免了寻找“消息窗口”,几乎所有进程都会创建这种窗口。它们用于内部管道。另一个答案提到了它们。你可以用 Spy++ 看到它们,记事本有两个。不是按照使您找到它们的顺序创建的,您将首先找到主窗口。这不能保证在任何过程中都会发生。

正确的解决方法是你不能忘记你做了一些对那个窗口非常不友好的事情。记事本本身从不隐藏其主窗口。你基本上把它变成了一个僵尸,用户不能再做任何事情来恢复窗口了。剩下的唯一选择是使用任务管理器来终止进程。因此,让它再次可见完全是你的工作。您不能忽略的工作,例如,您必须在程序退出之前恢复它们。

或者只是不这样做,像这样弄乱用户的窗口只是纯粹的敌对。

于 2015-11-07T09:12:16.817 回答