Как зацепить приложение?

Я пытаюсь подключить создание окон в моем приложении С#.

static IntPtr hhook = IntPtr.Zero;
static NativeMethods.HookProc hhookProc;

static void Main(string[] args)
{
    // Dummy.exe is a form with a button that opens a MessageBox when clicking on it.
    Process dummy = Process.Start(@"Dummy.exe");

    try
    {
        hhookProc = new NativeMethods.HookProc(Hook);
        IntPtr hwndMod = NativeMethods.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
        hhook = NativeMethods.SetWindowsHookEx(HookType.WH_CBT, hhookProc, hwndMod, (uint)AppDomain.GetCurrentThreadId());

        Console.WriteLine("hhook valid? {0}", hhook != IntPtr.Zero);

        while (!dummy.HasExited)
            dummy.WaitForExit(500);                
    }
    finally
    {
        if(hhook != IntPtr.Zero)
            NativeMethods.UnhookWindowsHookEx(hhook);
    }
}

static int Hook(int nCode, IntPtr wParam, IntPtr lParam)
{
    Console.WriteLine("Hook()");
    return NativeMethods.CallNextHookEx(hhook, nCode, wParam, lParam);
}

Проблема в том, что при нажатии на мою кнопку (в Dummy.exe) я никогда не вхожу в свой хук, что я делаю неправильно?

Спасибо.


РЕДАКТИРОВАТЬ

Программа.cs

using System;
using System.Diagnostics;

namespace Hooker
{
    class Program
    {
        static IntPtr hhook = IntPtr.Zero;
        static NativeMethods.HookProc hhookProc;

        static void Main(string[] args)
        {
            Process dummy = Process.Start(@"Dummy.exe");

            try
            {
                hhookProc = new NativeMethods.HookProc(Hook);
                hhook = NativeMethods.SetWindowsHookEx(HookType.WH_CBT, hhookProc, IntPtr.Zero, 0);

                Console.WriteLine("hhook valid? {0}", hhook != IntPtr.Zero);

                while (!dummy.HasExited)
                    dummy.WaitForExit(500);                
            }
            finally
            {
                if(hhook != IntPtr.Zero)
                    NativeMethods.UnhookWindowsHookEx(hhook);
            }
        }

        static int Hook(int nCode, IntPtr wParam, IntPtr lParam)
        {
            Console.WriteLine("Hook()");
            return NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
        }
    }
}

NativeMethods.cs

namespace Hooker
{
    using System;
    using System.Runtime.InteropServices;

    internal static class NativeMethods
    {
        public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, int dwThreadId);
        [DllImport("user32.dll")]
        public static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int pid);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);
    }
}

Для манекена создайте новую форму с помощью:

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show("CONTENT", "TITLE");
    }

person Arnaud F.    schedule 01.02.2012    source источник
comment
Зачем использовать хук для клавиатуры? Я хочу определить, когда исполняемый файл (без графического интерфейса) вызывает другой исполняемый файл (с графическим интерфейсом). Какая связь с клавиатурой?   -  person Arnaud F.    schedule 02.02.2012
comment
Я неправильно запомнил, что имел в виду WH_CBT.   -  person CodesInChaos    schedule 02.02.2012
comment
Почему вы ставите перед дескриптором модуля префикс hwnd? Это не оконная ручка.   -  person CodesInChaos    schedule 02.02.2012
comment
Напишите это в неуправляемом коде.   -  person CodesInChaos    schedule 02.02.2012
comment
Этот поток хорошо описывает эту тему: stackoverflow.com/questions/11607133/ глобальный обработчик событий мыши   -  person Christian Larsson    schedule 12.07.2020


Ответы (5)


Одна проблема с вашим кодом заключается в том, что hhookProc может быть собран мусор, в то время как ваш собственный код все еще нуждается в этом. Используйте GC.KeepAlive или введите статическую переменную.

Параметр hMod, вероятно, должен быть нулевым, поскольку вы указали поток в своем собственном процессе:

hMod [в]

Тип: ИНСТАНС

Дескриптор библиотеки DLL, содержащей процедуру ловушки, на которую указывает параметр lpfn. Параметр hMod должен быть установлен в NULL, если параметр dwThreadId указывает поток, созданный текущим процессом, и если процедура подключения находится в коде, связанном с текущим процессом.


Но я думаю, что это работает только для окон в указанном вами потоке.

Теоретически вы можете указать потоки в других приложениях или даже глобальный хук. Затем указанный обратный вызов вызывается в соответствующем потоке, даже если этот поток находится в другом процессе, и в этом случае ваша dll внедряется в этот процесс (по этой причине вам нужно указать дескриптор модуля в первую очередь).

Но я считаю, что это невозможно с кодом .net, потому что механизм внедрения в другие процессы и вызова там метода ловушки не работает с кодом, скомпилированным JIT.

person CodesInChaos    schedule 01.02.2012
comment
@Arnaud Ваш новый код все еще не работает. Вам либо нужно указать 0 hModule и поток внутри вашего собственного процесса. В этом случае вы можете наблюдать окна только в своем собственном процессе. Или вы указываете 0 или внешний поток и ненулевой hModule. Но, как я уже писал, второй подход, скорее всего, невозможен в .net. - person CodesInChaos; 02.02.2012
comment
На самом деле, я не знаю, какое значение я должен передать SetWindowsHookEx, чтобы перехватить приложение, которое я запускаю из текущего приложения... - person Arnaud F.; 02.02.2012
comment
@Arnaud Вам нужно будет передать текущую dll hModule как hMod, а поток, связанный с созданным окном, как threadId. Затем, когда происходит событие, ваша dll внедряется в целевой процесс и вызывается ваш метод ловушки. Но метод ловушки должен быть с постоянным смещением от вашей dll, т.е. он не может быть скомпилирован JIT. - person CodesInChaos; 02.02.2012
comment
Спасибо, на самом деле я прочитал это: klocwork.com/products/documentation/current/How_kwinject_works, в этой статье написано On Windows, kwinject starts the user process in debug mode and listens to CREATE_PROCESS debug events., как это делается, это можно сделать с помощью SetWindowsHook, да? - person Arnaud F.; 02.02.2012
comment
Навряд ли. Согласно тексту, он действует как отладчик для процесса, который он хочет контролировать. - person CodesInChaos; 02.02.2012
comment
Хорошо, неважно, тогда я удалю обсуждение, потому что это было целью моего вопроса. Спасибо за терпение и пояснения. - person Arnaud F.; 02.02.2012
comment
Настоящий ответ скрыт внизу. Возможно, вы захотите переместить его наверх. Вы не можете установить хук WH_CBT из приложения C#. Процедура подключения должна находиться в неуправляемой библиотеке DLL. - person Cody Gray; 02.02.2012

Как и большинство перехватчиков SetWindowsHookEx, перехватчики WH_CBT требуют, чтобы обратный вызов перехватчика находился в отдельной Win32 DLL, которая загружается в целевой процесс. По сути, это требует, чтобы хук был написан на C/C++, C# здесь не сработает.

(Низкоуровневые перехватчики мыши и клавиатуры являются двумя исключениями из этого правила. В C# также можно использовать другие перехватчики, но только если вы перехватываете один из ваших собственных потоков, поэтому dwThreadId — это идентификатор потока в текущем процессе. , а не 0. Однако я не подтверждал это. И вам нужно убедиться, что вы используете идентификатор потока Win32, поэтому использование GetCurrentThreadId, вероятно, является лучшим выбором здесь.)

Если вы хотите следить за новыми окнами, появляющимися из другого приложения, альтернативным удобным для C# подходом является использование SetWinEventHook API вместо этого укажите WINEVENT_OUTOFCONTEXT (это волшебный флаг, который доставляет события в ваш собственный процесс, устраняя необходимость в DLL и делая C# можно использовать здесь) и обработайте события EVENT_OBJECT_CREATE и EVENT_OBJECT_SHOW. Вы можете прослушивать события своего собственного процесса/потока или всех процессов/потоков на текущем рабочем столе.

Это даст вам все виды «создания» и отображения уведомлений, в том числе для дочерних HWND в диалоговом окне и даже элементов в списках и т.п.; поэтому вам нужно будет отфильтровать, чтобы извлечь только те, которые относятся к HWND верхнего уровня: например. проверить, что idObject==OBJID_WINDOW и idChild==0; этот hwnd виден (IsVisible()) и находится на верхнем уровне.

Обратите внимание, что для использования WinEvents требуется, чтобы поток, вызывающий SetWinEventHook, перекачивал сообщения, что в любом случае обычно имеет место, если это поток с пользовательским интерфейсом. Если нет, вам может потребоваться добавить цикл обработки сообщений (GetMessage/TranslateMessage) вручную. И вы также захотите использовать GC.KeepAlive() с обратным вызовом здесь, чтобы предотвратить его сбор до тех пор, пока вы не вызовете UnhookWinEvents.

person BrendanMcK    schedule 01.02.2012

Это не будет работать в C#

Область действия: поток

Если приложение устанавливает процедуру ловушки для потока другого приложения, эта процедура должна находиться в DLL.

(Документация по SetWindowsHookEx)

Область применения: глобальная

Чтобы установить глобальный хук, хук должен иметь собственный экспорт DLL, чтобы внедрить себя в другой процесс, для вызова которого требуется допустимая, непротиворечивая функция. Это поведение требует экспорта DLL. .NET Framework не поддерживает экспорт DLL.

(Источник)

person ordag    schedule 01.02.2012
comment
Единственные возможные обработчики окон, реализованные в C#, — это глобальные низкоуровневые обработчики и обработчики локальных потоков. - person ordag; 02.02.2012

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

манекен.MainWindowHandle

представляет ручку самого переднего окна, что обычно и нужно. Однако в этом случае вы открываете MessageBox.Show(), который, вероятно, имеет другой дескриптор, чем тот, к которому вы подключились.

я бы предположил, что

IntPtr hwndMod = NativeMethods.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);

вероятно, вернет тот же результат, что и

dummy.Refresh();
IntPtr hwndMod = dummy.MainWindowHandle;

Поэтому я думаю, что можно с уверенностью сказать, что они могут дать вам ручку, которую вы не ищете.

Возможно, попробуйте сделать тестовое приложение WinForm. Таким образом, вы можете взять правильную ручку. Просто убедитесь, что используете

dummy.WaitForInputIdle();
dummy.Refresh(); 

прежде чем браться за ручку, убедитесь, что вы берете правильную ручку во время запуска.

person corylulu    schedule 01.02.2012
comment
@Арно Ф. Что ж, мне понадобится больше кода, касающегося материала NativeMethod, или копия проекта для дальнейшего тестирования. Мне недостаточно для работы. - person corylulu; 02.02.2012
comment
В какой момент вы получаете ручку манекенов? Судя по всему (если я правильно понимаю), вы даете ему только текущую обработку приложений. - person corylulu; 02.02.2012
comment
hModule не является дескриптором окна. - person CodesInChaos; 02.02.2012
comment
Да, фиктивный дескриптор происходит от процесса, запущенного внутри текущего приложения. - person Arnaud F.; 02.02.2012
comment
Хорошо, но из этого кода фиктивное приложение кажется неуместным. Вы можете запустить его извне и получить те же результаты. Информация манекена никогда не используется вне его запуска. Этот хук, похоже, пытается использовать глобальный хук. Но послушайте ответ CodeInChaos, кажется, он знает больше об этой теме. - person corylulu; 02.02.2012

Я вижу, что это консольное приложение, поэтому консольное приложение не входит в цикл сообщений Windows.

простое решение - включить system.windows.forms

и просто введите application.start() в свой main, и все будет хорошо :)

person user2533527    schedule 06.10.2013