如何获取客户端窗口文件菜单栏的坐标?

时间:2017-12-27 22:38:34

标签: c# winapi

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

calculator image

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()一样。

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

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

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

您无法真正使用系统指标(HTMENU等)来查找它的大小,因为:

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

如果您只是想知道屏幕上的某个点是否是菜单栏,您可以发送WM_NCHITTEST消息并将返回值与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); } 进行比较。

要获得原生菜单栏的大小,您可以执行以下操作(在简单C中发帖,抱歉):

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();
}

但我建议您使用MSAA或UI自动化,因为它也适用于具有自定义菜单栏的应用程序:

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)
    {
    }
}
}
}

与PInvoke调用相同:

{{1}}