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.
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 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.
my goal
i want to translate a left click to a right click
my approach
i register a low-level hook via SetWindowsHookEx (user32.dll)
filter left-mouse-clicks
check if i want to translate THAT specific click
in case that i really want to
do not pass on the message
create a new mouseclick via mouse_event (user32.dll too)
the problem
when i do the described stuff like that:
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
if(nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam && doRight) {
doRight = false;
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
mouse_event(/*right down + right up*/8 | 16, hookStruct.pt.x, hookStruct.pt.y, 0, 0);
return new IntPtr(1);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
the call to mouse_event fails with a PInvokeStackImbalance-Exception, which, i guess, i should care about.
DllImports
since normally a PInvokeStackImbalance comes due to incorrect import-signatures here are mine:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc 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);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
emergence
my normal approach to isolate the problem fails in that case - since the mouse_event-call for itself works, and the trashing of left-clicks work too. i hate it when the structure is more than the sum of the parts...
mouse_event is defined as:
VOID WINAPI mouse_event(
__in DWORD dwFlags,
__in DWORD dx,
__in DWORD dy,
__in DWORD dwData,
__in ULONG_PTR dwExtraInfo
);
WINAPI means StdCall, DWORD is uint, ULONG_PTR is UIntPtr. So correctly it should be:
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, UIntPtr dwExtraInfo);
The problem is that long is defined in C# as 64-bit, while (u)int is 32-bit. (U)IntPtr is 32-bit or 64-bit, depending on the bitness of the operating system.
EDIT: In other words, you are passing too much data to the function. Since the callee cleans the stack, PInvoke notices that not everything was removed from the stack. It does it for you (but you have passed the wrong data, so the function has likely done something different from what you wanted) and warns you.
There are a lot of bad p/invoke declarations on the Internet. They typically started life in VB6. In that language, Integer is 16 bits and Long is 32 bits. Goes back to VB1 which ran on 16-bit operating systems. They don't work properly in VB.NET or C#.
The stack imbalance MDA was specifically designed to catch such bad declarations, but it is an option (Debug + Exceptions). The actual call tends to work if there is only one argument or when passing a lot of zeros. The odds that code keeps running properly after the call is, unfortunately, not bad either. The stack pointer value corrects itself when the method returns. You can get some really screwy problems though.
The mouse_event signature should be like this
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, IntPtr dwExtraInfo);
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.