Вызов GetGUIThreadInfo через P / Invoke - PullRequest
11 голосов
/ 06 апреля 2009

Я хочу отправить ввод с клавиатуры в окно другого процесса, не выводя это окно на передний план. Я могу использовать PostMessage для подделки WM_KEYDOWN и WM_KEYUP; все, что мне нужно знать, это то, какой дескриптор окна должен получать ввод с клавиатуры - то есть что-то вроде GetFocus , но для другого неактивного приложения.

API GetGUIThreadInfo выглядит многообещающе - он возвращает hwndFocus для другого приложения. Но мне не повезло заставить его работать из C # на моей 64-битной ОС. Я скопировал (а затем и далее подправил) объявления из pinvoke.net , но все, что я когда-либо получаю, - это общий код ошибки (более подробно ниже).

Я устанавливаю cbSize перед вызовом GetGUIThreadInfo, поэтому я избежал наиболее очевидной потенциальной проблемы.

Я использую 64-битную Vista, поэтому я не знаю, в чем проблема: я неправильно использую API или что он работает по-другому в 64-битной системе - мне еще не удалось найти код пример, который конкретно говорит, что он успешно работает в Win64.

Вот пример кода. Я использую GetWindowThreadProcessId как рекомендовано , поэтому я не думаю, что проблема связана с смешением идентификаторов потоков с дескрипторами потоков:

[StructLayout(LayoutKind.Sequential)]
internal struct Rect
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

[StructLayout(LayoutKind.Sequential)]
internal class GuiThreadInfo
{
    public int cbSize;
    public uint flags;
    public IntPtr hwndActive;
    public IntPtr hwndFocus;
    public IntPtr hwndCapture;
    public IntPtr hwndMenuOwner;
    public IntPtr hwndMoveSize;
    public IntPtr hwndCaret;
    public Rect rcCaret;
}

[DllImport("user32.dll")]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetGUIThreadInfo(uint idThread, ref GuiThreadInfo lpgui);

IntPtr GetFocusedHandleFromProcessWithWindow(IntPtr window)
{
    var threadId = GetWindowThreadProcessId(window, IntPtr.Zero);
    var info = new GuiThreadInfo();
    info.cbSize = Marshal.SizeOf(info);
    if (!GetGUIThreadInfo(threadId, ref info))
        throw new Win32Exception();
    return info.hwndFocus;
}

window - допустимый дескриптор окна; GetWindowThreadProcessId возвращает ненулевой дескриптор потока. Но вызов GetGUIThreadInfo всегда возвращает false, и сообщение об исключении всегда "Параметр неверен".

На всякий случай проблема была в том, что GetGUIThreadInfo почему-то не имеет 64-битной версии, я попытался изменить все 8-байтовые IntPtr с в объявлении GuiThreadInfo на 4-байтовые int с , но я все еще получил ту же ошибку.

У кого-нибудь есть рабочий образец C # GetGUIThreadInfo на Win64? Или есть другой способ узнать, какой дескриптор сфокусированного дочернего окна будет в другом приложении, не делая его активным?

1 Ответ

5 голосов
/ 06 апреля 2009

Я не смотрел на это слишком внимательно, но кое-что выпрыгивает. В вашем вызове GetGUIThreadInfo вы передаете структуру GUIThreadInfo по ссылке, но вы определили ее как класс, поэтому отправляете ссылку по ссылке, другими словами, указатель на указатель. Либо измените GUIThreadInfo на struct, либо удалите ссылку на параметр и добавьте атрибуты [In, Out].

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...