I'm trying to install a LOCAL keyboard hook into a process using a C++ DLL from managed C# code, as follows:
public class KeyboardHook
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("DLL.dll", CallingConvention = CallingConvention.Cdecl)]
protected static extern IntPtr Install(int idHook, IntPtr windowHandle, HookCallback callback);
private IntPtr instance;
private HookCallback handler;
public KeyboardHook()
{
instance = IntPtr.Zero;
handler = Callback;
}
public void Install(Process process)
{
instance = Install(WH_KEYBOARD, process.MainWindowHandle, handler);
}
public void Uninstall()
{
UnhookWindowsHookEx(instance);
}
private IntPtr Callback(int nCode, IntPtr wParam, IntPtr lParam)
{
// TODO Use hook data here
return CallNextHookEx(instance, nCode, wParam, lParam);
}
}
The C++ DLL code should be just enough to dispatch the hook data to the C#'s Callback function, like this:
// dll.h
#pragma data_seg(".foo")
HOOKPROC _hookCallback = NULL;
#pragma comment(linker, "/SECTION:.foo,RWS")
#pragma data_seg()
static HINSTANCE _moduleHandle = NULL;
extern "C" __declspec(dllexport)
HHOOK Install(int idHook, HWND window, HOOKPROC hookCallback);
extern "C" __declspec(dllexport)
LRESULT CALLBACK HookProc(int code, WPARAM wparam, LPARAM lparam);
// dll.cpp
HHOOK Install(int idHook, HWND window, HOOKPROC hookCallback)
{
auto processId = 0ul;
auto threadId = GetWindowThreadProcessId(window, &processId);
_hookCallback = hookCallback;
_hookCallback(-1, NULL, NULL); // Test callback (works)
return SetWindowsHookExA(idHook, HookProc, _moduleHandle, threadId);
}
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
// The following line terminates the target process
return _hookCallback(code, wParam, lParam);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
_moduleHandle = hModule;
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
The local hook is successfuly installed as the DLL KeyboardProc function is triggered, however, calling the C# delegate from the C++ DLL terminates the application. Why?
NOTES:
Both the DLL and the application are 32 bits
_hookCallback_ is not null when HookProc is triggered (although I'm not sure whether it points to a valid memory address)
KeyboardProc::handler shouldn't be garbage collected, as the KeyboardProc instance lives for as long as the C# application does
Using _hookCallback function pointer within the DLL's Install function works flawlessly, but terminates the process when used inside the HookProc function.
There's no exception or whatsoever, the process just terminates abruptly
WHAT ALSO HAVE BEEN TRIED:
Making HookCallback a UnmanagedFunctionPointer, as well as using Marshal.GetFunctionPointerForDelegate and telling the garbage collector not to collect the handler property by using GCHandle.Alloc() and GC.KeepAlive():
public class KeyboardHook
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("DLL32.dll", CallingConvention = CallingConvention.Cdecl)]
protected static extern IntPtr Install(int idHook, IntPtr windowHandle, IntPtr delegatePointer);
// ...
protected readonly GCHandle garbageCollectorHandle;
public KeyboardHook()
{
instance = IntPtr.Zero;
handler = new HookCallback(Callback);
garbageCollectorHandle = GCHandle.Alloc(handler); // Or GC.KeepAlive(handler)
}
~KeyboardHook()
{
garbageCollectorHandle.Free();
}
public void Install(Process process)
{
IntPtr delegatePointer = Marshal.GetFunctionPointerForDelegate(handler);
instance = Install(WH_KEYBOARD, process.MainWindowHandle, delegatePointer);
}
// ...
}
Using handler directly into SetWindowsHookExA (C++):
HHOOK Install(int idHook, HWND window, HOOKPROC hookCallback)
{
auto processId = 0ul;
auto threadId = GetWindowThreadProcessId(window, &processId);
_hookCallback = hookCallback;
return SetWindowsHookExA(idHook, hookCallback, _moduleHandle, threadId);
}
The C# code is calling the DLL's Install() function with the 2nd and 3rd parameters flipped.
Change this line:
instance = Install(WH_KEYBOARD, handler, process.MainWindowHandle);
To this:
instance = Install(WH_KEYBOARD, process.MainWindowHandle, handler);
UPDATE: Also, you did not show the C# code's declaration of the DLL'S Install() function. But in the C++ code, the Install() function is not specifying a calling convention, so it will (likely, depending on compiler config) default to __cdecl (HookProc() is using the __stdcall calling convention). So make sure the C# code is specifying the correct calling convention when importing the Install() function. C# delegates use __stdcall by default.
The process terminates due to accessing an invalid memory address.
Every Windows process have different virtual memory regions. In other words, the 0x1234 memory address within the process A does not point to the same value/function as 0x1234 memory address within the process B, since 0x1234 is a virtual memory address bound to it's corresponding process.
In order to achieve that communication between a C++ DLL and a C# application (any different process overall), an inter-process communication (IPC) is required.
For those interested on this specific case, I ended up creating an invisible dummy window to serve as a central point for receiving messages through SendMessage calls from the DLL.
Related
I'm trying to install either a WH_KEYBOARD_LL or WH_MOUSE_LL hook into a certain Process/Window.
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookHandler fn, IntPtr module, uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
public bool Install(Process process)
{
const int WH_KEYBOARD_LL = 0x0D;
if (instance == IntPtr.Zero)
{
var threadId = GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero);
instance = SetWindowsHookEx(WH_KEYBOARD_LL, handler, IntPtr.Zero, threadId);
}
return instance != IntPtr.Zero;
}
Where handler is a reference to my IntPtr Callback(int nCode, IntPtr wParam, IntPtr lParam) callback.
I can successfully hook globaly by replacing the third SetWindowsHookEx argument to a result of the LoadLibrary("User32") call and making threadId 0, like so:
var module = LoadLibrary("User32");
SetWindowsHookEx(WH_KEYBOARD_LL, handler, module, 0u);
How do I get it to work?
You can't.
As documented, a WH_KEYBOARD_LL can be installed as a global hook only. Both low-level keyboard and mouse hooks are executed long before the eventual input receiver has been determined.
The diagram posted under When something gets added to a queue, it takes time for it to come out the front of the queue illustrates, when low-level hooks run.
I'm creating an application that needs to check the response of some process.
System.Diagnostics.Responding doesn't works properly. So I'm trying to find a solution using windows API.
I already tried SendMessageTimeOut like this:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd,
uint msg,
UIntPtr wParam,
IntPtr lParam,
uint flags,
uint timeout,
out IntPtr pdwResult);
public static bool IsResponding(Process process)
{
IntPtr hProcess = IntPtr.Zero;
try
{
hProcess = OpenProcess(
ProcessAccessFlags.QueryInformation | ProcessAccessFlags.DuplicateHandle | ProcessAccessFlags.Synchronize,
false,
(uint)process.Id
);
if (hProcess == IntPtr.Zero)
return false;
IntPtr hResult = SendMessageTimeout(hProcess, 13, UIntPtr.Zero, IntPtr.Zero, SMTO_ABORTIFHUNG, 2000, out IntPtr pdwResult);
return hResult != IntPtr.Zero;
}
catch
{
return false;
}
finally
{
if (hProcess != IntPtr.Zero)
CloseHandle(hProcess);
}
}
Other attempt was with IsHungAppWindow.
[DllImport("user32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsHungAppWindow(IntPtr hwnd);
I really do not know why it always returns me that the application is not responding.
My PInvokes works fine, I think, don't throw anytime. And OpenProcess are working too.
I've create a WPF app that can respond and not respond every 15 sec. Task Manager can get No responding properly but my app no.
What I did wrong? All I read about advice me to use that two functions.
The problem is that SendMessageTimeout and IsHungAppWindow require you to pass window handles, but you are passing process handles. Obtain a handle to the application's main window, and pass that instead.
So there is a beautiful technique for acquiring a COM pointer cross-process (same machine) from an Excel.exe session if you know its Hwnd using the Accessibility API. The specific Windows API function is AccessibleObjectFromWindow; if called with parameter of OBJID_NATIVEOM then Excel.exe marshals back a COM pointer to an Excel.Window object. Very cool.
So I was wondering if developers can implement the same technique for their own applications. The answer is yes, they respond to a certain message, WM_GETOBJECT, in their message pump code. Whilst this is doable for a C++ application, I am puzzled as how to do this for a C# application.
I'm presuming the answer is to do something to get access to the message pump handling code and alter it. It maybe the case that some magic attribute could be used. I'm open to either technique so long as it works.
Here is the code that acquires COM pointer from Excel
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("oleacc.dll", SetLastError = true)]
internal static extern int AccessibleObjectFromWindow(IntPtr hwnd, uint id, ref Guid iid,
[In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);
bool IXlMoniker.GetExcelByHwnd(int lhwndApp2, ref object appRetVal)
{
bool bRetVal = false;
IntPtr lhwndApp = (IntPtr)lhwndApp2;
IntPtr lHwndDesk = FindWindowEx(lhwndApp, IntPtr.Zero, "XLDESK", "");
if (lHwndDesk != IntPtr.Zero)
{
IntPtr lHwndExcel7 = FindWindowEx(lHwndDesk, IntPtr.Zero, "EXCEL7", null);
if (lHwndExcel7 != IntPtr.Zero)
{
Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
const uint OBJID_NATIVEOM = 0xFFFFFFF0;
object app = null;
if (AccessibleObjectFromWindow(lHwndExcel7, OBJID_NATIVEOM, ref IID_IDispatch, ref app) == 0)
{
dynamic appWindow = app;
appRetVal = appWindow.Application;
return true;
}
}
}
return bRetVal;
}
This looks promising Return an IOleCommandTarget from processing WM_GETOBJECT in a NativeWindow
I am using the below code to exit a process programatically.
since i am new to this concept. I want to know how to use this below code.
Logic : I will have the process name to be terminated, i shud assign that to
this function.
Say if want to terminate a notepad, how to pass parameter [Process Name]
to this function ?
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
static uint WM_CLOSE = 0xF060;
public void CloseWindow(IntPtr hWindow)
{
SendMessage(hWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
Use the Process.CloseMainWindow instead of manually sending the message. This will send the message to the main window of the process:
using System.Diagnostics;
// ...
foreach (var process in Process.GetProcessesByName("notepad.exe"))
process.CloseMainWindow();
Alternatively, you can use MainWindowHandle to get the handle of the main window of a Process and send a message to it:
foreach (var process in Process.GetProcessesByName("notepad.exe"))
CloseWindow(process.MainWindowHandle); // CloseWindow is defined by OP.
If you want to kill the process immediately instead of closing the main window, this is not a good approach. You should use the Process.Kill method instead.
Although I agree with Mehrdad's answer but if you really want to re-invent the wheel, then this is how to do it (This is without any error checking etc. Please add that yourself).
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
static uint WM_CLOSE = 0x10;
static bool CloseWindow(IntPtr hWnd)
{
SendMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
return true;
}
static void Main()
{
IntPtr hWnd = FindWindowByCaption(IntPtr.Zero, "Untitled - Notepad");
bool ret = CloseWindow(hWnd);
}
BTW, Here is a good place to view Managed declarations of native API's
I'm writing a program that sits in the systray. It will have a keyboard hook into all typed input, in any open application. I want it to intercept any typed input, run code on that input, and then send the new 'correct' character to be typed. This new character will be what shows up in the app that has focus.
I found this code via another question on StackOverflow, and it looks good.
http://blogs.msdn.com/toub/archive/2006/05/03/589423.aspx
I added it as a class in my solution, created a new instance of it in the Form1 constructor, and it compiles. Any typed input into any app shows up in the Visual Studio output pane, one character at a time.
The problem is that this code is way over my head. That's the drawback of using this great concise code for my keyboard hooks: I haven't had the trial and error to teach me how to use it.
I envisioned this program working something like this:
key is pressed, triggers an event
get key information from the event
do computation on the key information, pick the character to be typed
send that key to the relevant program
my character is typed rather than the original keypress character
How does this code fit in this chain of events? I need to read the input characters before they are inputed. Then, where would the code to analyse the input and decide the correct input live? Finally, what's the correct 'sendInput()' type command to invoke to send a character to whatever app has focus?
Here's the full code for reference:
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine((Keys)vkCode);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
Thanks for any advice you have! Or, is there a better way to go about this?
Thanks!
UPDATE
My HookCallback method now looks like this:
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
//Console.WriteLine((Keys)vkCode);
KBDLLHOOKSTRUCT replacementKey = new KBDLLHOOKSTRUCT();
Marshal.PtrToStructure(lParam, replacementKey);
replacementKey.vkCode = 90; // char 'Z'
Marshal.StructureToPtr(replacementKey, lParam, true);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
Shoot... adding Marshal.StructureToPtr is close, but results in a A first chance exception of type 'System.ArgumentException' occurred in foobar.exe error.
VERSION WITH UNSAFE CODE, NO MARSHALLING, etc. (still doesn't output 'Z'!)
unsafe private static IntPtr HookCallback(int nCode, IntPtr wParam, void* lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
KBDLLHOOKSTRUCT* replacementKey = (KBDLLHOOKSTRUCT*)lParam;
replacementKey->vkCode = 90;
}
return CallNextHookEx(_hookID, nCode, wParam, (IntPtr) lParam);
}
At the moment, this code just writes to the console. This happens in line 4 of the HookCallback method. So you need to replace that line with your own code.
However, the info you're given isn't in a very C# friendly format. You need to look at the SDK docs for LowLevelKeyboardProc to figure out how to unpack it. It turns out all the good stuff is in lParam, which is a pointer to a KBDLLHOOKSTRUCT structure.
So your code needs to marshal that to a C# equivalent. You need to declare a KBDLLHOOKSTRUCT structure, or see if there's one on pinvoke.net, and use Marshal.PtrToStructure to unpack it. You can then party on this structure as much as Windows will allow -- I think modifying the event data is a better bet than trying to kill the keyboard event and then simulate a new one (for a start, I think the simulated "send keys" would run straight into the hook itself!) -- however I haven't tried this to see what it possible. The SDK struct does include a virtual key code and scan code, so you may be able to replace them to achieve the effect you want, but this will probably take some trial and error, and will be very specific to your app.
How does this code fit in this chain of events? I need to read the input characters before they are inputed. Then, where would the code to analyse the input and decide the correct input live?
The HookCallback method is the method which you're installing to intercept keystrokes.
Finally, what's the correct 'sendInput()' type command to invoke to send a character to whatever app has focus?
To modify the character which is passed to other processes, modify the parameters (nCode, wParam, and/or lParam) before you pass them to the CallNextHookEx method.