2012-02-01 2 views
1

Я пытаюсь подключить создание окон в моем приложении 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, что я делаю неправильно?

Спасибо.


EDIT

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"); 
    } 
+0

Зачем использовать клавиатуры крюк? Я хочу определить, когда exe (что без GUI) вызывает другой exe (с графическим интерфейсом). Какая связь с клавиатурой? –

+0

Я понял, что означал WH_CBT. – CodesInChaos

+0

Почему вы устанавливаете дескриптор модуля 'hwnd'? Это не окно. – CodesInChaos

ответ

2

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

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

hMod [в]

Тип: HINSTANCE

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


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

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

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

+0

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

+0

@Arnaud Ваш новый код все еще сломан. Вам либо нужно указать '0' hModule и поток внутри вашего собственного процесса. В этом случае вы можете наблюдать только окна в своем собственном процессе. Или вы указываете '0' или внешний поток и непустой' hModule'. Но, как я писал, второй подход, вероятно, невозможно в .net. – CodesInChaos

+0

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

0

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

dummy.MainWindowHandle

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

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

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

, вероятно, будет возвращать тот же результат, как

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

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

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

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

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

+0

Я модифицировал код, но ничего не менял, не смог зацепить ничего ... –

+0

@ArnaudF. Ну, мне понадобится больше кода в отношении материала NativeMethod или копии проекта для дальнейшего тестирования. Мне недостаточно работать. – Corylulu

+0

Добавил (а) код –

7

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

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

Если вы хотите следить за появлением новых окон из другого приложения, альтернативный подход, поддерживающий C#, заключается в том, чтобы использовать API SetWinEventHook вместо этого, указать 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.

1

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

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

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

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

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

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

(Source)

+0

Единственными возможными перехватами окон, реализованными на C#, являются крючки нижнего уровня _global и _local hook hooks_. – ordag

0

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

простое решение включить System.Windows.Forms

и просто введите application.start() в основных и все будет нормально :)

Смежные вопросы