1

有人介意帮助我解决我一直坚持一段时间的问题吗?我正在使用 C# 并尝试启动几个进程,然后将这些窗口移动到单独的监视器。

到目前为止,这是主要思想:

Process p1 = Process.Start(@"1.pptx");
Process p2 = Process.Start(@"2.pptx");

SetWindowPos(p1.MainWindowHandle, -1,
                        0,
                        0,
                       100,
                        100,
                        SWP_SHOWWINDOW);
SetWindowPos(p2.MainWindowHandle, -1,
                        200,
                        200,
                       100,
                        100,
                        SWP_SHOWWINDOW);

但是在尝试了一堆不同的东西之后,我无法让它发挥作用。谁能给我一些指示?

作为一个让我感到困惑的旁注,如果我打印那些以处理 ID(p1,p2),然后运行此代码:

Process[] processlist = Process.GetProcesses();

foreach (Process process in processlist)
{
       if (!String.IsNullOrEmpty(process.MainWindowTitle))
       {
            Console.WriteLine("Process: {0} ID: {1} Window title: {2}", process.ProcessName, process.Id, process.MainWindowTitle);
       }
}

这些进程 ID 不存在。我知道一定有一些简单的东西我错过了......?

更新:上述问题的原因是由于某种原因 MainWindowTitle 没有值,所以它没有打印 pid。

4

2 回答 2

2

当您使用Process.Start打开文档时,这是由 shell 处理的。shell 查看文件关联注册表并采取打开文档所需的任何步骤。

这可能涉及创建一个新流程,但同样可能不会。Office 应用程序通常会重用已打开的进程来打开新文档。这就是这里发生的事情。

当这种情况发生时,当没有新进程启动时,shell 为新进程句柄返回 0。这反映在 .netProcess对象上。它解释了为什么你没有主窗口句柄。

所以从根本上说,你的基本方法是有缺陷的。使用Process.Start不会产生这些文档的窗口句柄。您必须找到另一种方法来定位这些窗口。例如EnumWindows或 CBT 钩子。或者也许 COM 自动化是正确的解决方案。

顺便说一句,您似乎在调用时没有检查错误SetWindowPos。这会帮助你更快地解决这个问题。调用 Win32 函数时始终检查返回值。

于 2014-04-15T05:54:06.960 回答
1

对于那些仍在寻找答案的人,这就是我为使其正常工作所做的。

首先,在开始任何新进程之前,使用 EnumWindows 获取每个打开的窗口的句柄。然后开始您的过程,然后再次检查所有窗口,确保它们可见并具有窗口文本。如果您只有 1 个新进程,那很可能就是您的新窗口。如果没有,我在失败前已经尝试了 3 次。到目前为止,代码运行良好。

这是帮助函数/Win32 API 调用的代码。

        public delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList lParam);

        public static ArrayList GetWindows()
        {
            ArrayList windowHandles = new ArrayList();
            EnumedWindow callBackPtr = GetWindowHandle;
            EnumWindows(callBackPtr, windowHandles);

            return windowHandles;
        }

        private static bool GetWindowHandle(IntPtr windowHandle, ArrayList windowHandles)
        {
            windowHandles.Add(windowHandle);
            return true;
        }

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool IsWindowVisible(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

    const int SWP_SHOWWINDOW = 0x0040;
    [DllImport("user32.dll", EntryPoint = "SetWindowPos", SetLastError = true)]
    public static extern Boolean SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x,     int Y, int cx, int cy, int wFlags);

然后我的主要逻辑是这样的(根据需要进行调整):

List<IntPtr> alreadyOpenWindows = new List<IntPtr>();
foreach (IntPtr ip in GetWindows())
{
     alreadyOpenWindows.Add(ip);
}

Process.Start("your command here");
System.Threading.Thread.Sleep(1000);

foreach (IntPtr ip in GetWindows())
{
    // To consider it a new window, it must be visible, it must not have been open previously, and it must have window text length > 0
    if (IsWindowVisible(ip) && alreadyOpenWindows.Contains(ip) == false)
    {
        StringBuilder windowText = new StringBuilder();
        windowText.Length = 256;
        GetWindowText(ip, windowText, windowText.Length);

        if (windowText.Length > 0)
        {
            numNewWindows++;
            handle = ip;
            // break if your confident there will only be one new window opened
         }
    }
}


// Check numNewWindows if you'd like

if (handle != IntPtr.Zero)
{
    SetWindowPos(handle, -1,
         this.GetScreen().WorkingArea.X,
         this.GetScreen().WorkingArea.Y,
         this.GetScreen().WorkingArea.Width,
         this.GetScreen().WorkingArea.Height,
         SWP_SHOWWINDOW);
}
于 2014-04-29T18:24:05.847 回答