Sending keys to background window (not notepad) - c#

So I've been stuck for about 10 hours now. This code works perfectly for notepad. It sends the keystrokes to notepad in the background without having to click on it or anything. Problem is, no other window or process works and I've no idea why. I tried changing the names and using the window name instead of the process name stuff like that and nothing works. Only Notepad.
No, I can't use SendKeys because I need this to be in the background.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
private static extern bool PostMessage(
IntPtr hWnd, // handle to destination window
UInt32 Msg, // message
Int32 wParam, // first message parameter
Int32 lParam // second message parameter
);
public void Start()
{
const int WM_KEYDOWN = 0x100;
// IntPtr Notepad = FindWindow(null, "");
//Console.WriteLine(Notepad);
Process notepadProccess = Process.GetProcessesByName("notepad")[0];
Console.WriteLine(notepadProccess.MainWindowHandle);
Console.WriteLine(notepadProccess.MainWindowTitle);
IntPtr editx = FindWindowEx(notepadProccess.MainWindowHandle, IntPtr.Zero, "Edit", null);
PostMessage(editx, WM_KEYDOWN, (int)Keys.X, 0);
}

Related

C# Win32 Interop Crashes when Enumerating Window Handles

I have a C# wrapper for some Win32 operations involving window handles, but I am experiencing an unexpected crash, with no details, when I call a Win32 function.
Interestingly, this whole code sample works fine when the class is constructed (at application init time), but later fails when the same buildCache() method is called.
Here is the relevant code:
public delegate bool CallBack(int hWnd, int lParam);
public class Win32Interop {
private Dictionary<int, string> windowCache = new Dictionary<int, string>();
public Win32Interop() {
buildCache();
}
public void buildCache() {
windowCache.Clear();
CallBack hWndCacher = new CallBack(saveHWndHandler);
EnumWindows(hWndCacher, 0);
}
public void doThings(string title, uint message, bool rebuildCache = false) {
//Use the window title to get its handle
int hWnd = titleToHWnd(title, rebuildCache);
SendMessage(hWnd, message, 0, 0);
}
private bool saveHWndHandler(int hWnd, int lParam) {
if(IsWindow(hWnd) != 0) { / ***** CRASHES HERE ***** /
int length = GetWindowTextLength(hWnd);
StringBuilder title = new StringBuilder(length + 1);
GetWindowText(hWnd, title, title.Capacity);
string formatted = title.ToString().Trim();
windowCache.Add(hWnd, formatted);
}
return true;
}
private int titleToHWnd(string title, bool rebuildCache = false) {
if(rebuildCache)
buildCache();
if(windowCache.ContainsValue(title)) {
return windowCache.FirstOrDefault(x => x.Value.Contains(title)).Key;
} else {
throw new KeyNotFoundException(string.Format("\"{0}\" is not a window title which is available in the cache.", title));
}
}
#region Win32 API Functions
[DllImport("user32.dll")]
private static extern int EnumWindows(CallBack lpEnumFunc, int lParam);
[DllImport("user32.dll")]
private static extern int GetWindowText(int hWnd, StringBuilder lpString, int maxCount);
[DllImport("user32.dll")]
private static extern int GetWindowTextLength(int hWnd);
[DllImport("user32.dll")]
private static extern int IsWindow(int hWnd);
[DllImport("user32.lib")]
private static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
#endregion
}
Inside of the saveHWndHandler() method, I've marked the line were the debugger shows execution stops. Interestingly, there are usually ~300 window handles returned by EnumWindows(), and it always crashes on iteration number 45 or 46. The window handle it crashes on is a reasonable value, such as 12345.
According to MSDN, IsWindow() should return 0, if a window was not associated with the handle, not crash the thread.
Does anyone know why this is happening? There isn't a thrown exception, or any details in the Windows event log.
Thank you.
For those who don't want to figure out the buildCache() process works:
(1.) When buildCache() is called, the dictionary of <HWnd, Title> values is cleared.
(2.) The Win32 function EnumWindows() is called, which calls the saveHWndHandler() method for each window handle.
(3.) saveHWndHandler() will check if the current window handle still exists, the call another Win32 to function to get the window title from the handle.
(4.) The title and window handle are added to the dictionary.
I can't reproduce your issue, but a likely problem is that all of your P/Invoke signatures are wrong. HWND, LPARAM, and WPARAM data types need to at least be mapped to IntPtr in P/Invoke code.
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString,
int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam,
IntPtr lParam);
You'll need to change your corresponding instance method signatures and usages to match these correct signatures.

How to Click "Yes" button in order application with code

I have a code
private const int WM_CLOSE = 16;
private const int BN_CLICKED = 245;
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
[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);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
public void Click(string _btnTitle)
{
int hwnd = 0;
IntPtr hwndChild = IntPtr.Zero;
//Get a handle for the Calculator Application main window
// foreach (Process p in Process.GetProcesses())
//{
hwnd = FindWindow(null, FrmTitle);
if (hwnd != 0)
{
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", _btnTitle);
SendMessage((int)hwndChild, BN_CLICKED, 0, IntPtr.Zero);
}
}
I can't Click button "Yes" on MessageBox of application :(
Anyone got a tip? Tks
You aren't sending the correct message.
Try using BM_CLICK (0x00F5) in your call to SendMessage(). That should work provided that hwndChild is the window handle of the button, rather than the container dialog box.
BN_CLICKED doesn't work because that is a notification code, not a message.

How to change the button text of another programs window

I have been tasked with changing the text of a button in a window. I don't have and cannot access the source code as it's owned by a company we have a paid subscription with.
How can I change the button text with no source code? I'm trying with pInvoke and having problems. The window title changes depending on who you are working with:
"Order Entry Sheet - LASTNAME, FIRSTNAME"
So the window title may not be useable for me inside of the win32 call
FindWindow(string lpClassName, string lpWindowName);
I know both params are optional. I'm using Spy++ and I'm not sure what to use for lpClassName. The class name I see listed is #32770 (Dialog). I tried it and got a return of 0.
IntPtr windowHandle = FindWindow("#32770 (Dialog)", null);
How can I change the button text from another process?
UPDATE
According to MSDN I should be able to achieve this via SetWindowText.
Changes the text of the specified window's title bar (if it has one).
If the specified window is a control, the text of the control is
changed. However, SetWindowText cannot change the text of a control in
another application.
I can't use SetWindowText to do what I want. Is it possible to use something else?
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32", SetLastError = true)]
public static extern int EnumWindows(CallBack x, int y);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, CallBack lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, string lParam);
public const uint WM_SETTEXT = 0x000C;
public delegate bool CallBack(int hwnd, int lParam);
public static void Main()
{
CallBack windowsCallback = new CallBack(IterateWindows);
EnumWindows(windowsCallback, 0);
}
public static bool IterateChildren(int hwnd, int lParam)
{
string newButtonText = "Some text";
bool continueIteratingChildren = true;
//Console.WriteLine("Child handle: " + hwnd);
int length = GetWindowTextLength((IntPtr)hwnd);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText((IntPtr)hwnd, sb, sb.Capacity);
//Console.WriteLine(sb);
if (sb.ToString().StartsWith("My Button Text "))
{
HandleRef hrefHWndTarget = new HandleRef(null, (IntPtr)hwnd);
SendMessage(hrefHWndTarget, WM_SETTEXT, IntPtr.Zero, newButtonText);
continueIteratingChildren = false;
}
return continueIteratingChildren;
}
public static bool IterateWindows(int hwnd, int lParam)
{
bool continueIteratingWindows = true;
int windowTextLength = GetWindowTextLength((IntPtr)hwnd);
StringBuilder sb = new StringBuilder(windowTextLength + 1);
GetWindowText((IntPtr)hwnd, sb, sb.Capacity);
if (sb.ToString().StartsWith("My Window Caption"))
{
//Console.Write("Window handle is ");
//Console.WriteLine(hwnd);
//Console.WriteLine(sb);
//Console.WriteLine(Marshal.GetLastWin32Error());
var childrenCallback = new CallBack(IterateChildren);
EnumChildWindows((IntPtr)hwnd, childrenCallback, IntPtr.Zero);
continueIteratingWindows = false;
}
return continueIteratingWindows;
}

IPC in C#, sending text from one exe to another exe

I would like to send a message from a WPF application's textbox to an open notepad. After I click the button next to the the textbox, I would like the content is written into the notepad, I mean.
How can I send messages between 2 different applications ?
[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);
private static void DoSendMessage(string message)
{
Process notepad = Process.Start(new ProcessStartInfo("notepad.exe"));
notepad.WaitForInputIdle();
if (notepad != null)
{
IntPtr child = FindWindowEx(notepad.MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessage(child, 0x000C, 0, message);
}
}
For sending data between two applications you control, you could use NamedPipeClientStream and NamedPipeServerStream

How to send text to Notepad in C#/Win32?

I'm trying to use SendMessage to Notepad, so that I can insert written text without making Notepad the active window.
I have done something like this in the past using SendText, but that required giving Notepad focus.
Now, first I'm retrieving the Windows handle:
Process[] processes = Process.GetProcessesByName("notepad");
Console.WriteLine(processes[0].MainWindowHandle.ToString());
I've confirmed it's the right handle for Notepad, the same shown within Windows Task Manager.
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);
From here, I haven't been able to get SendMessage to work in all my experimentation. Am I going in the wrong direction?
[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);
private void button1_Click(object sender, EventArgs e)
{
Process [] notepads=Process.GetProcessesByName("notepad");
if(notepads.Length==0)return;
if (notepads[0] != null)
{
IntPtr child= FindWindowEx(notepads[0].MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessage(child, 0x000C, 0, textBox1.Text);
}
}
WM_SETTEXT=0x000c
You first have to find the child window where the text is entered. You can do this by finding the child window with the window class "Edit".
Once you have that window handle, use WM_GETTEXT to get the text which is already entered, then modify that text (e.g., add your own), then use WM_SETTEXT to send the modified text back.
using System.Diagnostics;
using System.Runtime.InteropServices;
static class Notepad
{
#region Imports
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
//this is a constant indicating the window that we want to send a text message
const int WM_SETTEXT = 0X000C;
#endregion
public static void SendText(string text)
{
Process notepad = Process.Start(#"notepad.exe");
System.Threading.Thread.Sleep(50);
IntPtr notepadTextbox = FindWindowEx(notepad.MainWindowHandle, IntPtr.Zero, "Edit", null);
SendMessage(notepadTextbox, WM_SETTEXT, 0, text);
}
}

Categories