使用PInvoke从C#获取工具提示文本

时间:2011-09-22 08:50:38

标签: c# pinvoke tooltip sendmessage

我在C#中使用PInvoke,试图在具有已知处理程序的窗口中查看可见的工具提示,但是我尝试以这种方式检查的窗口的应用程序因内存访问冲突错误而崩溃,或者只是不显示lpszText TOOLINFO成员中的工具提示文本。

我正在使用回调调用EnumWindows,然后在该函数的工具提示窗口中发送消息:

public delegate bool CallBackPtr(IntPtr hwnd, IntPtr lParam);

static void Main(string[] args)
{
  callBackPtr = new CallBackPtr(Report);

  IntPtr hWnd = WindowFromPoint(<mouse coordinates point>);

  if (hWnd != IntPtr.Zero)
  {
        Console.Out.WriteLine("Window with handle " + hWnd +
                              " and class name " +
                              getWindowClassName(hWnd));

        EnumWindows(callBackPtr, hWnd);

        Console.Out.WriteLine();
  }


  public static bool Report(IntPtr hWnd, IntPtr lParam)
  {
        String windowClassName = getWindowClassName(hWnd);

        if (windowClassName.Contains("tool") &&
             GetParent(hWnd) == lParam)
        {
            string szToolText = new string(' ', 250);

            TOOLINFO ti = new TOOLINFO();
            ti.cbSize = Marshal.SizeOf(typeof(TOOLINFO));
            ti.hwnd = GetParent(hWnd);
            ti.uId = hWnd;
            ti.lpszText = szToolText;

            SendMessage(hWnd, TTM_GETTEXT, (IntPtr)250, ref ti);

            Console.WriteLine("Child window handle is " + hWnd + " and class name " + getWindowClassName(hWnd) + " and value " + ti.lpszText);
        }

        return true;
    }

以下是我定义TOOLINFO结构的方法:

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    private int _Left;
    private int _Top;
    private int _Right;
    private int _Bottom;
}


struct TOOLINFO
{
    public int cbSize;
    public int uFlags;
    public IntPtr hwnd;
    public IntPtr uId;
    public RECT rect;
    public IntPtr hinst;

    [MarshalAs(UnmanagedType.LPTStr)]
    public string lpszText;

    public IntPtr lParam;
}

TTM_GETTEXT值

private static UInt32 WM_USER = 0x0400;
private static UInt32 TTM_GETTEXT = (WM_USER + 56);

SendMessage重载

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, ref TOOLINFO lParam);

那么,我的代码中是否存在任何明显错误,我应该更改哪些内容以解决此问题?

编辑:Here是整个代码,因此您可以进行测试。

2 个答案:

答案 0 :(得分:4)

您正在跨进程发送私人消息,这需要手动编组。 Here's another stackoverflow question on the same topic。更好的方法是完全改变方向,并使用Active Accessibility和/或UI Automation,这些都是为这类事情而设计的。

答案 1 :(得分:1)

我最终使用UI自动化,正如雷蒙德建议的那样。 AutomationElementName属性值包含工具提示的文本,证明正是代码所需的内容。我在所有桌面的子窗口中循环,所有工具提示都驻留在那里,我只显示那些属于鼠标下拥有窗口的进程的窗口:

    public static bool Report(IntPtr hWnd, IntPtr lParam)
    {
        if (getWindowClassName(hWnd).Contains("tool"))
        {
            AutomationElement element = AutomationElement.FromHandle(hWnd);
            string value = element.Current.Name;

            if (value.Length > 0)
            {
                uint currentWindowProcessId = 0;
                GetWindowThreadProcessId(currentWindowHWnd, out currentWindowProcessId);

                if (element.Current.ProcessId == currentWindowProcessId)
                    Console.WriteLine(value);
            }
        }

        return true;
    }


    static void Main(string[] args)
    {
        callBackPtr = new CallBackPtr(Report);

        do
        {
            System.Drawing.Point mouse = System.Windows.Forms.Cursor.Position; // use Windows forms mouse code instead of WPF

            currentWindowHWnd = WindowFromPoint(mouse);
            if (currentWindowHWnd != IntPtr.Zero)
                EnumChildWindows((IntPtr)0, callBackPtr, (IntPtr)0);

            Thread.Sleep(1000);
        }
        while (true);
    }