Получить текст подсказки из C # с PInvoke - PullRequest
1 голос
/ 22 сентября 2011

Я использую PInvoke в C #, пытаюсь прочитать всплывающие подсказки, видимые в окне с известным обработчиком, но приложения, окна которых я пытаюсь проверить таким образом, вылетают с ошибками нарушения доступа к памяти или просто не раскрываютТекст всплывающей подсказки в элементе 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);

Итак,Есть ли какие-либо очевидные ошибки, которые я пропускаю в своем коде, что я должен изменить, чтобы разрешить эту ситуацию?

Редактировать: Здесь - это весь код, чтобы вы могли проверить.

Ответы [ 2 ]

4 голосов
/ 22 сентября 2011

Вы отправляете личное сообщение всем процессам, что требует ручного маршалинга. Вот еще один вопрос stackoverflow по той же теме . Лучше было бы полностью изменить направление и использовать Active Accessibility и / или UI Automation, которые предназначены для такого рода вещей.

1 голос
/ 23 сентября 2011

Я закончил с использованием автоматизации пользовательского интерфейса, как предложил Рэймонд. AutomationElement, значение свойства которого Name содержит текст в случае всплывающих подсказок, оказалось именно тем, что требуется для кода. Я перебираю все дочерние окна рабочего стола, где находятся все всплывающие подсказки, и отображаю только те, которые принадлежат процессу, которому принадлежит окно, под мышкой:

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