I'm trying to build a global hotkey application with C# in Visual Studio 2012 to run on Windows 7. I have everything working except the SendKeys are never showing in the application.
Here is the code I am using to send the keystrokes:
Updated to debug with GetFocusedWindow example.
StringBuilder className = new StringBuilder(256);
IntPtr hWnd = GetForegroundWindow();
GetClassName(hWnd, className, className.Capacity);
Debug.WriteLine("Foreground window: {0}={1}", hWnd.ToInt32().ToString("X"), className);
hWnd = GetFocusedWindow();
GetClassName(hWnd, className, className.Capacity);
Debug.WriteLine("Focused window: {0}={1}", hWnd.ToInt32().ToString("X"), className);
SendKeys.Send("Hello World");
When I debug the program, focus Notepad, and hit the hotkey I get the following debug message and the keystrokes are never inserted into Notepad:
Foreground Window: 4F02B6=Notepad
Focused Window: 1B6026A=WindowsForms10.Window.8.app.0.bf7d44_r11_ad1
How can I send keystrokes to the current foreground window?
Foreground windows doesn't necessary mean focused window. A child window of the top-level foreground window may have the focus, while you're sending keys to its parent.
Retrieving the focused child window from another process is a bit tricky. Try the following implementation of GetFocusedWindow, use it instead of GetForegroundWindow (untested):
static IntPtr GetFocusedWindow()
{
uint currentThread = GetCurrentThreadId();
IntPtr activeWindow = GetForegroundWindow();
uint activeProcess;
uint activeThread = GetWindowThreadProcessId(activeWindow, out activeProcess);
if (currentThread != activeThread)
AttachThreadInput(currentThread, activeThread, true);
try
{
return GetFocus();
}
finally
{
if (currentThread != activeThread)
AttachThreadInput(currentThread, activeThread, false);
}
}
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern IntPtr GetFocus();
[DllImport("user32.dll", SetLastError = true)]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
Updated to address the comment:
When I use this function it sets the focus to my application window.
It's hard to tell what's wrong on your side, the following works for me when the focus is inside Notepad:
private async void Form1_Load(object sender, EventArgs e)
{
var className = new StringBuilder(200);
while (true)
{
await Task.Delay(500);
IntPtr focused = GetFocusedWindow();
GetClassName(focused, className, className.Capacity);
var classNameStr = className.ToString();
this.Text = classNameStr;
if (classNameStr == "Edit")
SendKeys.Send("Hello!");
}
}
Related
I found command System.Windows.Forms.SendKeys.Send() for sending keypress some key. This function work if open external app like a notepad and set focus and I will be see that my Key printed in this text field. How do same but with key down event, System.Windows.Forms.SendKeys.SendDown("A");, for example?
I tried call in Timer this command System.Windows.Forms.SendKeys.Send() but have runtime error associated with very fast taped.
You can't use the SendKeys class for that, unfortunately. You will need to go to a lower level API.
Poking a window with a keydown message
In Windows, keyboard events are sent to windows and controls via the Windows message pump. A piece of code using PostMessage should do the trick:
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
const uint WM_KEYDOWN = 0x0100;
void SendKeyDownToProcess(string processName, System.Windows.Forms.Keys key)
{
Process p = Process.GetProcessesByName(processName).FirstOrDefault();
if (p != null)
{
PostMessage(p.MainWindowHandle, WM_KEYDOWN, (int)key, 0);
}
}
Note that the application receiving these events may not do anything with it until a corresponding WM_KEYUP is received. You can get other message constants from here.
Poking a control other than the main window
The above code will send a keydown to the "MainWindowHandle." If you need to send it to something else (e.g. the active control) you will need to call PostMessage with a handle other than p.MainWindowHandle. The question is... how do you get that handle?
This is actually very involved... you will need to temporarily attach your thread to the window's message input and poke it to figure out what the handle is. This can only work if the current thread exists in a Windows Forms application and has an active message loop.
An explanation can be found here, as well as this example:
using System.Runtime.InteropServices;
public partial class FormMain : Form
{
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("user32.dll")]
static extern IntPtr AttachThreadInput(IntPtr idAttach,
IntPtr idAttachTo, bool fAttach);
[DllImport("user32.dll")]
static extern IntPtr GetFocus();
public FormMain()
{
InitializeComponent();
}
private void timerUpdate_Tick(object sender, EventArgs e)
{
labelHandle.Text = "hWnd: " +
FocusedControlInActiveWindow().ToString();
}
private IntPtr FocusedControlInActiveWindow()
{
IntPtr activeWindowHandle = GetForegroundWindow();
IntPtr activeWindowThread =
GetWindowThreadProcessId(activeWindowHandle, IntPtr.Zero);
IntPtr thisWindowThread = GetWindowThreadProcessId(this.Handle, IntPtr.Zero);
AttachThreadInput(activeWindowThread, thisWindowThread, true);
IntPtr focusedControlHandle = GetFocus();
AttachThreadInput(activeWindowThread, thisWindowThread, false);
return focusedControlHandle;
}
}
The good news-- if SendKeys worked for you, then you might not need to do all this-- SendKeys also sends messages to the main window handle.
In Windows 10 Tablet mode, the TabTip keyboard is shown whenever any textbox (regardless of type, be it from a Java application, a Metro app, or a WPF app) is focused.
It seems impossible to implement this as a service using only simple Window hooks since Metro apps do not return a Window element, which led to the hypothesis that there must be a system call happening whenever any textfield is focused, when in Tablet mode.
I've been researching for days, but to no avail. The most useful thing I have seen would be this thread where the responder reverse-engineered the behavior when tapping the Keyboard key in the taskbar in Tablet mode.
I also tried this block of code, but this seems to only catch a small portion of the text fields:
public void DetectKeyboard()
{
AttemptKeyboardDetection:
try
{
AttachThreadInput(GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero), GetCurrentThreadId(), true);
IntPtr focus = GetFocus();
AttachThreadInput(GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero), GetCurrentThreadId(), false);
if (GetClassName(focus, this.StringBuilder, this.StringBuilder.Capacity))
{
ActiveProcessFocus = this.StringBuilder.ToString();
Debug.WriteLine("Active Process: " + ActiveProcessFocus);
if ((PreviousProcessFocus != ActiveProcessFocus) && !ActiveProcessFocus.Contains("KeyboardOpener"))
{
Debug.WriteLine("Checking if editing...");
if ((((ActiveProcessFocus == "Edit") || (ActiveProcessFocus == "SearchPane")) || (ActiveProcessFocus.Contains("RichEdit") || ActiveProcessFocus.Contains("SearchEdit"))) || ((ActiveProcessFocus.Contains("TextfieldEdit") || ActiveProcessFocus.Contains("Afx:00400000")) || (((ActiveProcessFocus == "_WwG") || (ActiveProcessFocus == "Scintilla")) || (ActiveProcessFocus == "SPEAD0C4"))))
{
Debug.WriteLine("Editing! Opening keyboard...");
try
{
ProcessStartInfo startInfo = new ProcessStartInfo(#"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(startInfo);
}
catch
{
}
}
else
{
try
{
SendMessage(FindWindow("IPTip_Main_Window", null), 0x112, (IntPtr) 0xf060, IntPtr.Zero);
foreach (Process process in Process.GetProcessesByName("osk"))
{
process.Kill();
}
}
catch
{
}
}
PreviousProcessFocus = ActiveProcessFocus;
}
}
Thread.Sleep(250);
goto AttemptKeyboardDetection;
}
catch
{
goto AttemptKeyboardDetection;
}
}
[DllImport("user32.dll")]
private static extern IntPtr AttachThreadInput(IntPtr AttachId, IntPtr AttachToId, bool AttachStatus);
[DllImport("user32.dll")]
private static extern bool GetClassName(IntPtr hWnd, StringBuilder ClassName, int ClassMax);
[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern IntPtr GetFocus();
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ThreadId);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
since not all text field elements contain "Edit", etc. in their element names.
Since I'm sure I've exhausted what I can find with my limited knowledge in Windows programming and through research, does anyone know of a system call that can be listened to that is similar to the one called when in Tablet mode? Or is this really impossible without diving in to assembly or even making viruses?
I have a WPF app that starts another application, I'd like for my application to change the Icon of this second app. I am able to use GetWindowText and SetWindowText to change the title. Is it possible to do this for the Icon as well?
update
I have no control of the second app.
To change the window title of another application:
Definitions of Win32 API functions and constants:
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetWindowText(IntPtr hwnd, String lpString);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hwnd, int message, int wParam, IntPtr lParam);
private const int WM_SETICON = 0x80;
private const int ICON_SMALL = 0;
private const int ICON_BIG = 1;
Usage:
Process process = Process.Start("notepad");
// If you have just started a process and want to use its main window handle,
// consider using the WaitForInputIdle method to allow the process to finish starting,
// ensuring that the main window handle has been created.
// Otherwise, an exception will be thrown.
process.WaitForInputIdle();
SetWindowText(process.MainWindowHandle, "Hello!");
Icon icon = new Icon(#"C:\Icon\File\Path.ico");
SendMessage(process.MainWindowHandle, WM_SETICON, ICON_BIG, icon.Handle);
In Windows Forms you would use
Icon ico = Icon.ExtractAssociatedIcon(#"C:\WINDOWS\system32\notepad.exe");
this.Icon = ico;
So im guessing for WPF it would be similar.
I am new to WINAPI and have figured out how to send a message to another program. The program I am using however I would like to be able to have it click on a specific button. From what I have learned by viewing Spy++ windows handles change for the programs every time they are reloaded and so do the handles for their controls. The control ID stays the same. After two days of trying to figure it out I am here.
under SendMesssageA if I specify the current handle as viewable by Spy++ and use that and run the code it works fine and clicks the button on my external application. I am attempting to use GetDlgItem as I have read that I can get the handle for the control (child window) using it. I am doing something wrong however since no matter what I do it returns 0 or 'null'.
How can I get GetDlgItem to return the child control handle so that I may use it to sendmessage to click that control in the external application?
Thanks for your help an input ahead of time.
[DllImport("User32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
Process[] myProcess = Process.GetProcessesByName("program name here");
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SendMessageA(IntPtr hwnd, int wMsg, int wParam, uint lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetDlgItem(int hwnd, int childID);
public const int WM_LBUTTONDOWN = 0x0201;
public const int WM_LBUTTONUP = 0x0202;
public void SendClick()
{
IntPtr hwnd = myProcess[0].MainWindowHandle;
SetForegroundWindow(hwnd);
int intCID = 1389;
IntPtr ptrTest = GetDlgItem(hwnd, intCID);
SendKeys.SendWait(" ");
Thread.Sleep(1000);
SendKeys.SendWait("various text to be sent here");
Thread.Sleep(1000);
SendKeys.SendWait("{ENTER}");
Thread.Sleep(1000);
SendMessageA(ptrTest, WM_LBUTTONDOWN, WM_LBUTTONDOWN, 0);
}
I think you have to use the Win32 API to find the "receiving" application window, and then find a child window of that handle.
This is something I found googling Win32 API FindWindow
http://www.c-sharpcorner.com/UploadFile/shrijeetnair/win32api12062005005528AM/win32api.aspx
I wanna do a macro program for a game. But there is a problem with sending keys to only game application (game window). I am using keybd_event API for sending keys to game window. But I only want to send keys to the game window, not to explorer or any opened window while my macro program is running. When I changed windows its still sending keys. I tried to use Interaction.App with Visual Basic.dll reference. But Interaction.App only Focus the game window.
I couldn't find anything about my problem. Can anyone help me? Thanx
i fixed my problem.
in this field ;
PostMessage(hWnd, WM_KEYDOWN, key, {have to give lParam of the key});
otherwise it does not work.And we can control of ChildWindow Class with Spy++ tool of Microsoft.
Thanks everyone for helping.
Are you retrieving the handle of the window all the time, or are you remembering it?
If you use the FindWindow() API, you can simply store the Handle and use the SendMessage API to send key/mouse events manually.
FindWindow API:
http://www.pinvoke.net/default.aspx/user32.FindWindowEx
SendMessage API:
http://www.pinvoke.net/default.aspx/user32/SendMessage.html
VB
Private Const WM_KEYDOWN As Integer = &H100
Private Const WM_KEYUP As Integer = &H101
C#
private static int WM_KEYDOWN = 0x100
private static int WM_KEYUP = 0x101
class SendKeySample
{
private static Int32 WM_KEYDOWN = 0x100;
private static Int32 WM_KEYUP = 0x101;
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, int Msg, System.Windows.Forms.Keys wParam, int lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
public static IntPtr FindWindow(string windowName)
{
foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
{
if (p.MainWindowHandle != IntPtr.Zero && p.MainWindowTitle.ToLower() == windowName.ToLower())
return p.MainWindowHandle;
}
return IntPtr.Zero;
}
public static IntPtr FindWindow(IntPtr parent, string childClassName)
{
return FindWindowEx(parent, IntPtr.Zero, childClassName, string.Empty);
}
public static void SendKey(IntPtr hWnd, System.Windows.Forms.Keys key)
{
PostMessage(hWnd, WM_KEYDOWN, key, 0);
}
}
Calling Code
var hWnd = SendKeySample.FindWindow("Untitled - Notepad");
var editBox = SendKeySample.FindWindow(hWnd, "edit");
SendKeySample.SendKey(editBox, Keys.A);
If you want to communicate with a game, you typically will have to deal with DirectInput, not the normal keyboard API's.