Как подключить приложение? - PullRequest
1 голос
/ 02 февраля 2012

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

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) я никогда не захожу в свой Hook,что я делаю не так?

Спасибо.


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

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

Ответы [ 5 ]

7 голосов
/ 02 февраля 2012

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

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

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

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

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

2 голосов
/ 02 февраля 2012

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

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

hMod [in]

Тип: HINSTANCE

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


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

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

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

1 голос
/ 02 февраля 2012

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

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

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

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

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

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

( Источник )

0 голосов
/ 07 октября 2013

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

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

и просто ввести приложение.start () у вас в главном и все будет хорошо:)

0 голосов
/ 02 февраля 2012

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

dummy.MainWindowHandle

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

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

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

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

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

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

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

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

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

...