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