I am trying to capture the keypress event. Below is the code. If I press say for example 'F8' from anywhere, the method 'HookCallback' is being called. But If I do a key press from excel VBA window, it is not being called. I need to capture a keypress event in excel VBA. I don't know what I am missing here.
Edit:
The same code works in Excel 2010. It doesn't work with Excel 2016 and Excel 2022. So it should be a office 2016 and greater version problem.
#region Keyboard Event Handler
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private KeyBoardHookProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private IntPtr SetHook(KeyBoardHookProc proc)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
0, 0);
}
private delegate IntPtr KeyBoardHookProc(
int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
....
}
Related
I am using the following code in C# to simulate the keyboard pressing:
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
[DllImport("user32")]
private static extern bool SetForegroundWindow(IntPtr hwnd);
void sim_key(string text, string proc)
{
var process = Process.GetProcessesByName(proc).FirstOrDefault();
if (process != null && process.MainWindowHandle != IntPtr.Zero)
{
SetForegroundWindow(process.MainWindowHandle);
SendKeys.Send(kat_id);
}
}
I tested it on Windows Xp, 7, 8.1, 10, and Server 2012. On windows 7 and Xp, the external app window is handled properly, however the keys are not being sent. On systems above Win 7 everything is correct. How should I fix it?
Edit:
I checked it on .NET 4.0 Client Profile and .NET 4.6.1 on 32 and 64 bit machines, but results are the same as described above.
I think you can go with this rough code
const int WM_CHAR = 0x0102;
const int WM_KEYDOWN = 0x0100;
if (process != null && process.MainWindowHandle != IntPtr.Zero)
{
PostMessage(process.MainWindowHandle, WM_CHAR, (int) <a character you want to send> , 1);
}
Use
PostMessage(process.MainWindowHandle, WM_KEYDOWN, 13, 1);
to send the "Enter".
Note that you can use SendMessage instead of PostMessage. The difference is the last one does not wait the processing of the key. They have the same signature:
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
You don't need calling SetForegroundWindow(process.MainWindowHandle); if you just want to send keys.
Thank you for your answer. I tried using PostMessage to send Ctrl+F10 to the other window. Here is my code:
public const int WM_KEYDOWN = 0x100;
public const int WM_KEYUP = 0x101;
public const int VK_CONTROL = 0x11;
public const int VK_F10 = 0x79;
PostMessage(process.MainWindowHandle, WM_KEYDOWN, VK_CONTROL, null);
PostMessage(process.MainWindowHandle, WM_KEYDOWN, VK_F10, null);
PostMessage(process.MainWindowHandle, WM_KEYUP, VK_F10, null);
PostMessage(process.MainWindowHandle, WM_KEYUP, VK_CONTROL, null);
However, it doesn't pass the keys to the app.
I am currently developing a program that will send a "key press" (the letter A or 0x41 in virtual key codes) to another program (notepad) every X seconds.
The problem is that for it to work I need the other program (notepad) to be in the FOREGROUND, example :
Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process proc in processes) {
SetForegroundWindow(proc.MainWindowHandle);
// Do Whatever
}
Thread.Sleep(1000);
Is there a way to do that WITHOUT notepad having to be in the foreground ?
Like something that could run in the background ?
You could do it via winApi. Try to use SendMessage
According to this link you can do following:
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public static void sendKeystroke(ushort k)
{
const uint WM_KEYDOWN = 0x100;
const uint WM_SYSCOMMAND = 0x018;
const uint SC_CLOSE = 0x053;
IntPtr WindowToFind = FindWindow(null, "Untitled1 - Notepad++");
IntPtr result3 = SendMessage(WindowToFind, WM_KEYDOWN, ((IntPtr)k), (IntPtr)0);
//IntPtr result3 = SendMessage(WindowToFind, WM_KEYUP, ((IntPtr)c), (IntPtr)0);
}
I am working on a project in which I have to first split and then re size the the windows on desktop. I am able to split the desktop screen by using pinvoke service TileWindows. But i am stuck in the other part, to resize the windows on desktop. Because first I have to catch the event that some window is re sized on the desktop. To catch this event I have used this code answered in SO.
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventProc lpfnWinEventProc, int idProcess, int idThread, uint dwflags);
[DllImport("user32.dll")]
internal static extern int UnhookWinEvent(IntPtr hWinEventHook);
internal delegate void WinEventProc(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime);
const uint WINEVENT_OUTOFCONTEXT = 0;
const uint EVENT_SYSTEM_FOREGROUND = 3;
private IntPtr winHook;
private WinEventProc listener;
public void StartListeningForWindowChanges()
{
listener = new WinEventProc(EventCallback);
//setting the window hook
winHook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, listener, 0, 0, WINEVENT_OUTOFCONTEXT);
}
public void StopListeningForWindowChanges()
{
UnhookWinEvent(winHook);
}
private static void EventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime)
{
MessageBox.Show("I am here");
}
the problem I am getting is the event is fired even when i have not re sized any window on screen. And as you can see I have put a messageBox in the eventCallback function and it pops up but interestingly it shows nothing on its ok button.
As #HansPassant already said you should use the
EVENT_SYSTEM_MOVESIZEEND = 0x000B constant that indicates that: The movement or resizing of a window has finished.
Event Constants
you are also receiving events from ANY window. If you look at the signature of SetWinEventHook function:
HWINEVENTHOOK WINAPI SetWinEventHook(
_In_ UINT eventMin,
_In_ UINT eventMax,
_In_ HMODULE hmodWinEventProc,
_In_ WINEVENTPROC lpfnWinEventProc,
_In_ DWORD idProcess,
_In_ DWORD idThread,
_In_ UINT dwflags
);
you'll find that:
idProcess - Specifies the ID of the process from which the hook function receives events. Specify zero (0) to receive events from all processes on the current desktop.
however in your code you have spacified a 0 there:
winHook = SetWinEventHook(
EVENT_SYSTEM_FOREGROUND,
EVENT_SYSTEM_FOREGROUND,
IntPtr.Zero,
listener,
0, // <--- idProcess
0,
WINEVENT_OUTOFCONTEXT);
You need to get your process' ID to listen only for events about your window. For that you can use the Process.Id Property. I think this should work (it works for me):
const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;
Process currentProcess = Process.GetCurrentProcess();
winHook = SetWinEventHook(
EVENT_SYSTEM_MOVESIZEEND,
EVENT_SYSTEM_MOVESIZEEND,
IntPtr.Zero,
listener,
currentProcess.Id,
0,
WINEVENT_OUTOFCONTEXT);
I am trying to click on 'OK' button on a message box of C# windows form using winapi. Below is the code that I am working on.
private const int WM_CLOSE = 16;
private const int BN_CLICKED = 245;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
//this works
hwnd = FindWindow(null, "Message");
if(hwnd!=0)
SendMessage(hwnd, WM_CLOSE, 0, IntPtr.Zero);
//this doesn't work.
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "ok");
SendMessage((int)hwndChild, BN_CLICKED, 0, IntPtr.Zero);
Though i get a value in hwndChild, it is not recognising BN_CLICKED.
I am not sure what am I missing. any help?
I am trying to close the message box button of another application and this is what I am doing. But, I m still missing something.
IntPtr hwndChild = IntPtr.Zero;
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero,' '"Button", "OK");
SendMessage((int)hwndChild, WM_COMMAND, (BN_CLICKED '<<16) | IDOK, hwndChild);
BN_CLICKED is not a message. You need to send a WM_COMMAND message containing the BN_CLICKED notification and the button ID in the wParam and the button handle in lParam.
The parent window of the button receives this notification code
through the WM_COMMAND message.
private const uint WM_COMMAND = 0x0111;
private const int BN_CLICKED = 245;
private const int IDOK = 1;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
SendMessage(hwndChild, WM_COMMAND, (BN_CLICKED << 16) | IDOK, hwndChild);
Finallu, this works for me.
First click probably activates the window and second click clicks the button.
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);
I want to handle mouse click in a native MFC application from a C# application.
To do so I'm trying to subclass the native application. I don't get any errors, but the wndproc are newer invoked.
private const int GwlWndProc = -4;
private delegate int Win32WndProc(IntPtr hWnd, int msg, int wParam, int lParam);
[DllImport("user32")]
private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, Win32WndProc newProc);
Win32WndProc _newWndProc = MyWndProc;
SetLastError(0);
IntPtr oldWndProc = SetWindowLong(hWnd, GwlWndProc, _newWndProc);
if (oldWndProc == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode != 0)
throw new Win32Exception(errorCode);
}
private int MyWndProc(IntPtr hWnd, int msg, int wParam, int lParam)
{
Debug.WriteLine("MyWndProc " + (WindowsMessage)msg);
if (msg == (int) WindowsMessage.LeftButtonDown)
{
MessageBox.Show("Clicked");
return 0;
}
else return CallWindowProc(_subclasses[hWnd], hWnd, msg, wParam, lParam);
}
Edit:
To get the hWnd I use GetForegroundWindow()
What I try to do is is to prevent the application to get the mouse click
I think you need to use hooking because SetWindowLong does not work across different processes: have a look here http://www.codeproject.com/Articles/5264/Cross-Process-Subclassing