C# - user32.dll - GetWindowRect问题

4
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int Left;        // x position of upper-left corner
    public int Top;         // y position of upper-left corner
    public int Right;       // x position of lower-right corner
    public int Bottom;      // y position of lower-right corner
}

foreach (Process pr in Process.GetProcesses())
{
    RECT rc;
    GetWindowRect(???, out rc);

我应该在 "???" 处放什么?它告诉我必须放一个 HandleRef 对象,但我不知道如何从 Process 方法中获取 HandleRef 对象。

1
你跳过了一个步骤;你需要先从窗口获取本地句柄。例如,如果该窗口是你的应用程序中的C#主窗体(假设是WinForms应用程序),你将使用新的HandleRef(this, this.Handle);。你需要首先获取所需窗口的句柄(而Process.Handle可能不是它)。 - Ed S.
4个回答

10
如果您需要窗口坐标,而该窗口已经在您的进程中,那么有其他方法可以获取窗口句柄,而不需要枚举进程。
对于WinForms窗口,请使用Handle属性。 System.Windows.Forms.Control ... Handle Property @ MSDN 对于WPF应用程序,请使用WindowInteropHelper。 System.Windows.Interop ... WindowInteropHelper Class @ MSDN 如果您正在尝试枚举无法直接从.NET访问的窗口;例如来自创建超出代码范围的顶级窗口的第三方控件,则可能希望通过win32 EnumWindows函数进行枚举。 EnumWindows (Win32) @ MSDN EnumWindows的P/Invoke签名可在此处找到: User32.dll EnumWindows @ pinvoke.net 添加:
看起来您想枚举所有窗口和相关进程。使用EnumWindows,然后调用GetWindowThreadProcessId为每个窗口获取关联的进程和非托管线程ID。

在MSDN上获取Win32的GetWindowThreadProcessId函数

这里提供了P/Invoke签名:

在pinvoke.net上获取User32.dll的GetWindowThreadProcessId函数

最后,您可以通过静态方法GetProcessById获取一个进程对象。

在MSDN上获取Process.GetProcessById函数

附加内容 (#2):

这是一个简短的控制台程序,可以枚举窗口、进程和线程ID。与您的代码片段有一些不同之处。

  1. 我使用IntPtr而不是HandleRef。正如其他人指出的那样,这可能会让您感到困惑。
  2. 我没有指定return属性。如果需要,您应该能够将其添加回去。
  3. 我以管理员身份运行;如果您以用户级别权限运行,则某些事情可能会运行得不同。

在gist.github上获取C#源代码示例


谢谢!但我有点困惑。是的,看起来它不允许我从.NET访问另一个进程的窗口属性。 - xoromer
我已经成功地获取了一个样例;但是我在我的计算机上以管理员身份运行。我将发布源代码的链接,以便您可以在您的环境中的测试项目中尝试它。 - meklarian

4

这样可以实现,但首先需要使用类似 FindWindowEx 等WinAPI函数找到窗口的IntPtr句柄:

[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hwnd, ref Rect rectangle);

Rect r = new Rect();
GetWindowRect(hwnd, ref r);

2
使用新的HandleRef(pr, pr.MainWindowHandle)可能有效。假设你的程序实际上有一个主窗口。获取此信息的方法肯定更简单。
您的foreach循环需要修改,因为它无法在Process.GetCurrentProcess()上编译。尝试迭代所有进程将导致代码崩溃,您将获得不太关心共享信息的特权系统进程。无法猜测您为什么要这样做。使用EnumWindows来枚举桌面上的所有顶级窗口。

HandleRef(pr, pr.MainWindowHandle) 不起作用。感谢 Process.GetCurrentProcess()。我已经修复了它。 - xoromer
2
把 GetWindowRect() 的参数改成 IntPtr,HandleRef 毫无必要。 - Hans Passant

0

我需要知道所有进程的句柄,而不仅仅是我的应用程序。 - xoromer
1
@Sergey:如果你阅读了那份文档,你会意识到它并不适用于这里。那是一个进程句柄,而不是窗口句柄。 - Ed S.
你提供的链接对OP(显然已经找到了它)没有帮助。它只展示了如何获取应用程序中窗体的句柄。如果这是使用情况,你根本不需要使用GetWindowRect;.NET表单已经可以为你提供这些信息。-1 - Ed S.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接