I need to enumerate all open windows and get their title, but the problem is that some windows belong to the same process but to a different thread, which is blocked (waiting for a mutex). Therefore I cannot use GetWindowText for windows that belong in my own process as this will result to a SendMessage call which will block my code's execution (as it will be waiting a relpy for the blocked thread).
Btw here is an interesting article on how the GetWindowText works internally: http://blogs.msdn.com/b/oldnewthing/archive/2003/08/21/54675.aspx
As a solution decided to use SendMessageTimeout to the window in order to retrieve its title but I can't make it work. What I am doing is:
[DllImport("User32.dll")]
public static extern int SendMessageTimeout(
IntPtr hWnd,
int uMsg,
int wParam,
int lParam,
int fuFlags,
int uTimeout,
out StringBuilder lpdwResult);
...
StringBuilder sb = new StringBuilder(256);
int result = Win32API.SendMessageTimeout(
hWnd,
0x0D /*WM_GETTEXT*/,
256,
0,
10 /*SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG*/,
500,
out sb);
but I always get 0 meaning that the function failed, and sb is always null.
Any ideas?
Thanks a lot.
#Alex has given you the correct answer. But since I typed the code already, here is a P/Invoke declaration you can use.
[DllImport("User32.dll", SetLastError=true)]
public static extern int SendMessageTimeout(
IntPtr hWnd,
uint uMsg,
uint wParam,
StringBuilder lParam,
uint fuFlags,
uint uTimeout,
IntPtr lpdwResult);
Pass your StringBuilder in for the lParam, since WM_GETTEXT fills the buffer specified by the lParam and you can just pass IntPtr.Zero for teh lpdwResult.
You need to pass string bufer in lParam, and not in lpdwResult. lpdwResult may be NULL or out Int32, it contains result of the message handling. Note that SendMessageTimeout doesn't help to get title of non-responsive window, but prevents caller blocking.
Edit:
Interesting point here that PInvoke declaration of SendMessageTimeout depends on the message you want to send. In the case it is necessary to send different messages in the same program, I think it is possible to make several PInvoke declarations calling the same API.
Related
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.
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.
I know how to PInvoke a method that wants a CString by using LPCTSTR instead and setting up the DllImport to call with the LPstr conversion.
However, how would I do it with SendMessage where LPARAM is an IntPtr?
Would this work?
[DllImport("user32.dll", CharSet = CharSet.Ansi)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam,
[MarshalAs(UnmanagedType.LPStr)] String lParam);
You can declare it simply like this:
[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr SendMessage(
IntPtr hWnd, uint Msg, IntPtr wParam, string lParam);
The default marshalling is as an pointer to null-terminated character array. If you really want the ANSI version, then that's the default. And you should use SetLastError in case you want to capture the error code in case of failure.
I trust you know that it cannot work if the window is in a different process.
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.