启动进程并将句柄存储到窗口

时间:2014-04-15 00:44:26

标签: c# winapi process window-handles

有人会介意帮我解决一个问题,我已经被困了一段时间吗?我正在使用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。

2 个答案:

答案 0 :(得分:2)

当您使用Process.Start打开文档时,它由shell处理。 shell查找文件关联注册表并执行打开文档所需的任何步骤。

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

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

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

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

答案 1 :(得分:0)

对于那些仍在寻找答案的人来说,这就是我为实现这一目标所做的工作。

首先,在开始任何新进程之前,使用EnumWindows获取每个打开窗口的句柄。然后开始您的过程,然后再次检查所有窗口,确保它们可见并具有窗口文本。如果您只有一个新流程,则可能是您的新窗口。如果没有,我在失败前尝试了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);
}