C#最小化的窗口未通过调用System.Diagnostics.Process.GetProcesses()

时间:2018-10-29 19:02:01

标签: c# windows user32

我试图找到一个最小化的窗口并显示它。

该程序可以从Samsung下载,并且标题为“ SideSync”。要完全复制我的问题,您将需要安装它,并且还需要使用三星手机来插入计算机。

以下是其已完全配置并正在运行的屏幕截图:

enter image description here

观察到有两个窗口A和B。我使用了一个名为Microsoft Inspect的工具来确定这两个程序窗口是普通窗口。他们没有孩子的父母关系。但是,当我启动SideSync时,仅出现窗口A。然后,我必须单击“电话屏幕”,然后出现窗口B(除了窗口A)。这可能是解决此问题的线索吗?我们将会看到。

这两个窗口都是Microsoft Inspect中出现的窗口:

enter image description here

两个窗口都有窗口标题。使用下面的代码,我可以检索窗口的Process(这是我的目标)。

服务器代码:

public static Process GetProcessByWindowTitle(string windowTitleContains)
{
    foreach (var windowProcess in GetWindowProcesses())
        if (windowProcess.MainWindowTitle.Contains(windowTitleContains))
            return windowProcess;

    return null;
}

但是,正在发生一些奇怪的行为。 GetProcessByWindowTitle()将返回一个,但不返回两个进程。我假设这是因为有两个窗口,所以必须有两个进程。

返回的Process取决于我用鼠标单击的最后一个窗口。

例如,如果我最后单击窗口A;然后GetProcessByWindowTitle("SideSync")将返回Process,但是随后GetProcessByWindowTitle("SAMSUNG")将返回void

...反之亦然,如果我上次单击窗口B,则GetProcessByWindowTitle("SideSync")将返回void,但随后GetProcessByWindowTitle("SAMSUNG")将返回Process

客户代码:

[Ignore("Requires starting SideSync and clicking one of the windows. Only the last clicked will return a Process.")]
[Test]
public void NonMinimizedWindowProcessIsDetected()
{

    Process p1 = Windows.GetProcessByWindowTitle("SAMSUNG");

    if(p1==null) { Console.WriteLine("SAMSUNG process is null.");}
    else { Console.WriteLine("SAMSUNG process detected.");}

    Process p2 = Windows.GetProcessByWindowTitle("SideSync");

    if (p2 == null) { Console.WriteLine("SideSync process is null."); }
    else { Console.WriteLine("SideSync process detected."); }
}

我的目标是显示窗口B。 我的问题是,只有当我最后单击它时,这才可能实现,这会创建有害的依赖关系。 我希望能够独立于任何单击顺序显示窗口B。

1 个答案:

答案 0 :(得分:11)

我花了一些时间尝试重现您的问题。 根据我的分析(花了一些时间,因为我最初没有使SideSync正确运行;-),所以只有一个进程运行,该进程以 SideSync.exe 的名称运行,该进程承载多个窗口。

我想您的方法中的“缺陷”是您试图通过MainWindowTitle来获取该过程。但是,如果您使用以下代码段,您将看到MainWindowTitle会根据该过程中当前活动的窗口而变化。

while (true)
{
    var processes = Process.GetProcesses();

    foreach (var process in processes)
    {
        if (process.ProcessName != "SideSync")
            continue;

        Console.WriteLine($"{process.ProcessName}, {process.MainWindowTitle}, {process.MainWindowHandle.ToString()}");
    }

    Thread.Sleep(1000);
}

在我的情况下,MainWindowTitle在不同的标题之间进行了更改,例如:

SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, ToolTip, 3148196
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, ToolTip, 3148196
SideSync, ToolTip, 3148196
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728

您可以看到输出中还包含 Notifier ToolTip 之类的标题,当出现 Notification Window 或将鼠标移到上方时出现 SideSync 应用程序中的图标。从输出中可以进一步看到,活动窗口的MainWindowHandle当然也发生了变化。

因此,我认为您只需要使用Process.GetProcessesByName("SideSync")来获取 SideSync 进程,就不用其他了。

我希望这会有所帮助;-)

更新

根据OP的评论,他需要一种方法来打开上次打开的<​​em> SideSync 进程的一个特定窗口。为了实现这一目标,第一步是找到属于 SideSync 进程的窗口的相应窗口句柄。

我将以下代码基于cREcker的答案代码,后者将答案基于资源Getting a list of all the open windows

以下类的方法 GetOpenWindowsByProcessId 允许获取属于指定进程ID的所有窗口的句柄。

public static class OpenWindowGetter
{
    public static IDictionary<string, IntPtr> GetOpenWindowsByProcessId(int processId)
    {
        IntPtr shellWindow = GetShellWindow();
        Dictionary<string, IntPtr> windows = new Dictionary<string, IntPtr>();

        EnumWindows(delegate (IntPtr hWnd, int lParam)
        {
            uint ownerProcessId;
            GetWindowThreadProcessId(hWnd, out ownerProcessId);

            if (ownerProcessId != processId)
                return true;

            if (hWnd == shellWindow)
                return true;

            if (!IsWindowVisible(hWnd))
                return true;

            int length = GetWindowTextLength(hWnd);

            if (length == 0)
                return true;

            StringBuilder builder = new StringBuilder(length);
            GetWindowText(hWnd, builder, length + 1);
            windows[builder.ToString()] = hWnd;

            return true;

        }, 0);

        return windows;
    }

    private delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);

    [DllImport("USER32.DLL")]
    private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);

    [DllImport("USER32.DLL")]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

    [DllImport("USER32.DLL")]
    private static extern int GetWindowTextLength(IntPtr hWnd);

    [DllImport("USER32.DLL")]
    private static extern bool IsWindowVisible(IntPtr hWnd);

    [DllImport("USER32.DLL")]
    private static extern IntPtr GetShellWindow();

    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
}

此外,我们将需要一种“显示”窗口的方法。

private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private const int SW_SHOWMAXIMIZED = 3;

[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

有了这些知识,我们现在可以编写类似于以下代码的代码了(可以肯定地以数千种不同的方式对其进行了优化):

private static void ShowPhoneScreenWindow()
{
    const string PROCESSNAME = "sidesync";
    const string WINDOWTITLE = "samsung";

    var process = Process.GetProcessesByName(PROCESSNAME).FirstOrDefault();

    if (process == null)
        throw new InvalidOperationException($"No process with name {PROCESSNAME} running.");

    var windowHandles = OpenWindowGetter.GetOpenWindowsByProcessId(process.Id);
    IntPtr windowHandle = IntPtr.Zero;

    foreach (var key in windowHandles.Keys)
        if (key.ToLower().StartsWith(WINDOWTITLE))
        {
            windowHandle = windowHandles[key];
            break;
        }

    if (windowHandle == IntPtr.Zero)
        throw new InvalidOperationException($"No window with title {WINDOWTITLE} hosted.");

    ShowWindowAsync(windowHandle, SW_SHOWNORMAL);
}