【问题标题】:How do I get the coordinates of the client window file menu bar?如何获取客户端窗口文件菜单栏的坐标?
【发布时间】:2017-12-27 22:38:33
【问题描述】:

当我使用 ClientToScreen() 时,我得到了客户端窗口的坐标,但它不包括文件菜单栏。当一个窗口有一个文件菜单栏时,这对我来说只是一个问题,例如Windows 计算器。

public Point createPoint(IntPtr handle)
{
    Point myPoint = new Point(0, 0);
    Point myPointClient = new Point(0, 0);
    RECT myRectWindow;
    if (radioButtonEntireWindow.Checked)   // Green cross
    {
        // Result: (0, 0)
    }
    else if (radioButtonClientWindowFileMenu.Checked)   // Red cross
    {
        // This works for windows 7, but probably not for windows 10:
        myPoint = new Point(8, 30);
        // Result: (8, 30)
    }
    else if (radioButtonClientWindow.Checked)   // Purple cross
    {
        GetWindowRect(handle, out myRectWindow);
        ClientToScreen(handle, ref myPointClient);
        myPoint = new Point(myPointClient.X - myRectWindow.Location.X, myPointClient.Y - myRectWindow.Location.Y);
        // Result: (8, 50)
    }
    return myPoint;
}

如何在不明确使用 (8, 30) 的情况下获得红十字坐标?使用 GetWindowRect() 和 GetClientRect() 获取标题栏高度不起作用,因为它不包含文件菜单栏,就像 ClientToScreen() 一样。

【问题讨论】:

  • 使用本机窗口菜单的应用程序在菜单栏下方开始其客户区。菜单本身位于非客户区。例如,有些做,有些不做,比较 Winforms MainMenu 控件与 MenuStrip。 GetMenu() 将返回一个非空菜单句柄,GetMenuBarInfo() 返回它的大小。
  • @HansPassant 我没有使用winforms。我正在寻找这样做,例如Chrome 或 windows 计算器
  • 只是一个例子,这就是我推荐winapi函数的原因。
  • 你想完成什么?听起来像是一个问题,答案是 UI 自动化。
  • @IInspectable 我正在尝试使用例如文件菜单栏截取客户端窗口区域或客户端窗口区域的屏幕截图。 windows计算器

标签: c# winapi


【解决方案1】:

您可以使用 System.Windows.SystemParameters 的属性来获取各种元素的大小,例如它具有 MenuBarHeight 和 CaptionBarHeight 属性。

【讨论】:

  • 我能以某种方式获得 chrome 的标题栏高度吗?
  • @Xrio:不。Chrome 对其整个 UI 使用自定义渲染。
  • 由于 DPI 和多显示器设置,每个窗口的元素大小可能会有所不同,Windows 10 现在有 GetSystemMetricsForDPI 等。
【解决方案2】:

本机菜单栏位于 HWND 客户区之外。一些窗口还使用自定义菜单栏,在这种情况下所有的赌注都关闭,它可能在客户区内部或外部。

您可以通过调用GetMenu 来检测窗口是否具有原生菜单栏。

您不能真正使用系统指标(SM_CYMENU 等)来查找它的大小,因为:

  • Windows 可以有不同的 DPI 支持,有些可以缩放/拉伸,有些是原生大小。也可能取决于他们在哪个显示器上。
  • 如果窗口太窄而无法在一行中显示所有项目,则菜单栏可以有多行。

如果您只想知道屏幕上的某个点是否是菜单栏,您可以发送WM_NCHITTEST 消息并将返回值与HTMENU 进行比较。

要获得原生菜单栏的大小,您可以执行以下操作(以纯 C 语言发布,抱歉):

HWND hCalc = FindWindow(TEXT("CalcFrame"), NULL);
HMENU hMenu = hCalc ? GetMenu(hCalc) : NULL;
MENUBARINFO mbi;
printf("Menu of %p is %p\n", hCalc, hMenu);
mbi.cbSize = sizeof(MENUBARINFO);
if (GetMenuBarInfo(hCalc, OBJID_MENU, 0, &mbi))
{
    printf("GetMenuBarInfo: pos:%dx%d size:%dx%d\n", mbi.rcBar.left, mbi.rcBar.top, mbi.rcBar.right - mbi.rcBar.left, mbi.rcBar.bottom - mbi.rcBar.top);
}

不过,我建议您使用 MSAA 或 UI 自动化,因为它也可以在带有自定义菜单栏的应用程序中工作:

OleInitialize(0);
HWND hCalc = FindWindow(TEXT("CalcFrame"), NULL);
IAccessible*pAcc;
HRESULT hr;
hr = AccessibleObjectFromWindow(hCalc, OBJID_MENU, IID_IAccessible, (void**) &pAcc);
if (SUCCEEDED(hr))
{

    long x, y, w, h;
    VARIANT v;
    V_VT(&v) = VT_I4;
    V_I4(&v) = CHILDID_SELF;
    if (SUCCEEDED(pAcc->accLocation(&x, &y, &w, &h, v)))
    {
        printf("pos: %dx%d size: %dx%d\n", x, y, w, h);
    }
    pAcc->Release();
}

PInvoke 调用也是如此:

using System;
using System.Runtime.InteropServices;
using Accessibility;

namespace Test
{
class TestApp 
{


[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, IntPtr zero);

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass"), DllImport("oleacc.dll", ExactSpelling = true, PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface)]
static extern object AccessibleObjectFromWindow(IntPtr hwnd, uint dwObjectID, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);

static Guid IID_IAccessible = new Guid("{618736E0-3C3D-11CF-810C-00AA00389B71}");

enum OBJID : uint { WINDOW = 0x00000000, SYSMENU = 0xFFFFFFFF, TITLEBAR = 0xFFFFFFFE, MENU = 0xFFFFFFFD, CLIENT = 0xFFFFFFFC, }


static void Main() 
{
    IntPtr hCalc = FindWindow("CalcFrame", IntPtr.Zero);
    Console.WriteLine("Calc HWND is "+hCalc);
    if (hCalc == IntPtr.Zero) return ;
    try
    {
        IAccessible acc = (IAccessible) AccessibleObjectFromWindow(hCalc, (uint)OBJID.CLIENT, IID_IAccessible);
        int x, y, w, h;
        acc.accLocation(out x, out y, out w, out h, null);
        Console.WriteLine(string.Format("pos: {0}x{1} size:{2}x{3}", x, y, w, h));
    }
    catch (System.Runtime.InteropServices.COMException)
    {
    }
}
}
}

【讨论】:

  • 我无法确定 idObject / OBJID_MENU 应该是什么?这应该是 hMenu 吗?
  • 不,它是一个常数。其值为 0xFFFFFFFD。
  • 使用 UI 自动化有什么缺点吗?
  • 如果您想在 .NET 中使用 UI 自动化,可以使用 System.Windows.Automation.AutomationElement.FromHandle。然后,您可能必须遍历子树来尝试查找菜单。
  • 如果代码在 c# 中,我会接受这个作为答案
猜你喜欢
  • 2011-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-05
  • 2015-07-27
  • 2011-11-19
  • 1970-01-01
  • 2010-12-29
相关资源
最近更新 更多