|
I know this is possible, just wondering if it's possible in C#.NET, and how it's done.
I'd like my application to interact with other apps/windows. Examples:
1) Type some text into a notepad window. 2) Force Firefox to do Help->About. 3) Read text from a notepad window.
I realize #3 could potentially be a security risk and .NET may not allow it (imagine an app reading text from some open window and sending it off somewhere via the net).
And in case anyone is concerned, no, I'm not trying to write any kind of malware or virus. I'm interested in writing a program that will automate tedious processes in another application. I guess this is similar to Rexx.
So, how can I do something like this?
| | MDesigner Thursday, August 31, 2006 7:53 PM | Yes actually, it works very fine with the current window. However, with other processes(windows), you should first find the window's handle, show it (it might be minimized), set it as the foreground window, sleep the thread for a short period (0.25 second will be fine) and then send your keys! You do that using the FindWindow, ShowWindow and SetForgroundWindow API calls.
The following sample shows a small application, to build it, create a windows application form, add two text boxes, textBox1 for the window title, and textBox2 for the text( actually keys) to send and a button button1 that sends the keys to the window with the specified title. Title you provide must be an exact match to the title bar text of the window you want to send the keys to. Don't forget to attach the appropriate event handler (button1_Click to the Click event) for the button!
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Threading; namespace ProcessControl {
public partial class Form1 : Form {
[ DllImport("user32.dll", EntryPoint = "FindWindowA")] public static extern int FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll")] public static extern int ShowWindow(int hwnd,int nCmdShow); [DllImport("user32.dll")] public static extern int SetForegroundWindow(int hwnd);
public Form1() {
InitializeComponent(); textBox1.Text = "Untitled - Notepad"; textBox2.Text = "Some Sample Text ";
}
private void button1_Click(object sender, EventArgs e) {
int i = Form1.FindWindow(null, textBox1.Text); if (i == 0) {
MessageBox.Show("Could not find " + textBox1.Text);
}
else {
string s = new string(' ', 255); Form1.ShowWindow(i, 1); Form1.SetForegroundWindow(i); Thread.Sleep(250); SendKeys.SendWait(textBox2.Text);
}
}
}
}
I initialized the text boxes to the default title of the notepad application and some sample text. You can change that at runtime, but for the trial, just open a notepad window (unsaved one so that the title is exactly "Untitled - Notepad") and then run the application
Hope this helps! | | CSharpFreak Friday, September 01, 2006 9:11 AM | First view the post: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=602071&SiteID=1 Although it is small but the second reply by nobugz says it all!
Now for the first task 1)type som text ... etc., you might use SendKeys to send keyboard strokes to the notepad application without having to break the barrier!
The second and third items, I have no knowldge of an easy (or even moderate) way to do this, unless maybe using the threading Win32 API using paltform invokes, but i don't think that will be easy! | | CSharpFreak Thursday, August 31, 2006 8:17 PM | Just FYI - I have not been successful in getting .NET to send keys. I tried it with a MS Word dialog and it did not work at all. Have you ever gotten it to work? | | DeborahK Friday, September 01, 2006 12:15 AM | Alternative way to do the workis to get the handle of these application's windows and simulate the mouse and keyboard event. But i think .net framework is not suitable for such jobs.By the way, there are already some softwares such as AutoIt, whichcan make the automation become a easy job. | | Wang Chi Friday, September 01, 2006 8:53 AM | Yes actually, it works very fine with the current window. However, with other processes(windows), you should first find the window's handle, show it (it might be minimized), set it as the foreground window, sleep the thread for a short period (0.25 second will be fine) and then send your keys! You do that using the FindWindow, ShowWindow and SetForgroundWindow API calls.
The following sample shows a small application, to build it, create a windows application form, add two text boxes, textBox1 for the window title, and textBox2 for the text( actually keys) to send and a button button1 that sends the keys to the window with the specified title. Title you provide must be an exact match to the title bar text of the window you want to send the keys to. Don't forget to attach the appropriate event handler (button1_Click to the Click event) for the button!
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Threading; namespace ProcessControl {
public partial class Form1 : Form {
[ DllImport("user32.dll", EntryPoint = "FindWindowA")] public static extern int FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll")] public static extern int ShowWindow(int hwnd,int nCmdShow); [DllImport("user32.dll")] public static extern int SetForegroundWindow(int hwnd);
public Form1() {
InitializeComponent(); textBox1.Text = "Untitled - Notepad"; textBox2.Text = "Some Sample Text ";
}
private void button1_Click(object sender, EventArgs e) {
int i = Form1.FindWindow(null, textBox1.Text); if (i == 0) {
MessageBox.Show("Could not find " + textBox1.Text);
}
else {
string s = new string(' ', 255); Form1.ShowWindow(i, 1); Form1.SetForegroundWindow(i); Thread.Sleep(250); SendKeys.SendWait(textBox2.Text);
}
}
}
}
I initialized the text boxes to the default title of the notepad application and some sample text. You can change that at runtime, but for the trial, just open a notepad window (unsaved one so that the title is exactly "Untitled - Notepad") and then run the application
Hope this helps! | | CSharpFreak Friday, September 01, 2006 9:11 AM | Here are some useful starter API methods, structs and consts that can be a good start. These are just samples and you might search for more APIs according to what kind of automation you are looking for!
public const int CCHDEVICENAME = 32; public const int CCHFORMNAME = 32; [StructLayout(LayoutKind.Sequential)] public struct DEVMODE{ [MarshalAs(UnmanagedType.ByValTStr, SizeConst=CCHDEVICENAME)] internal string dmDeviceName; internal Int16 dmSpecVersion; internal Int16 dmDriverVersion; internal Int16 dmSize; internal Int16 dmDriverExtra; internal int dmFields; internal Int16 dmOrientation; internal Int16 dmPaperSize; internal Int16 dmPaperLength; internal Int16 dmPaperWidth; internal Int16 dmScale; internal Int16 dmCopies; internal Int16 dmDefaultSource; internal Int16 dmPrintQuality; internal Int16 dmColor; internal Int16 dmDuplex; internal Int16 dmYResolution; internal Int16 dmTTOption; internal Int16 dmCollate; internal string * CCHFORMNAME dmFormName; internal Int16 dmUnusedPadding; internal Int16 dmBitsPerPel; internal int dmPelsWidth; internal int dmPelsHeight; internal int dmDisplayFlags; internal int dmDisplayFrequency; }
[DllImport( "uxtheme.dll", EntryPoint="SetWindowTheme")] public static extern int ActivateWindowTheme ( int hWnd,ref int = 0 Optional pszSubAppName,ref int = 0 Optional pszSubIdList); [DllImport("user32.dll")] public static extern int AdjustWindowRect ( ref RECT lpRect,int dwStyle,int bMenu); [DllImport("user32.dll")] public static extern int BringWindowToTop ( int hwnd); [DllImport("user32.dll", EntryPoint="ChangeDisplaySettingsA")] public static extern int ChangeDisplaySettings ( ref DEVMODE lpDevMode,int dwFlags); [DllImport("user32.dll", EntryPoint="ChangeDisplaySettingsExA")] public static extern int ChangeDisplaySettingsEx ( string lpszDeviceName,ref DEVMODE lpDevMode,int hwnd,int dwflags,int lParam); [DllImport("user32.dll")] public static extern int CloseWindow ( int hwnd); [DllImport("user32.dll")] public static extern int DestroyWindow ( int hwnd); [DllImport("user32.dll")] public static extern int EnableWindow ( int hwnd,int fEnable); [DllImport("user32.dll")] public static extern int EnumChildWindows ( int hWndParent,int lpEnumFunc,int lParam); [DllImport("user32.dll")] public static extern int EnumThreadWindows ( int dwThreadId,int lpfn,int lParam); [DllImport("user32.dll")] public static extern int EnumWindows ( int lpEnumFunc,int lParam); [DllImport("user32.dll", EntryPoint="FindWindowA")] public static extern int FindWindow ( string lpClassName,string lpWindowName); [DllImport("user32.dll", EntryPoint="FindWindowExA")] public static extern int FindWindowEx ( int hWnd1,int hWnd2,string lpsz1,string lpsz2); [DllImport("user32.dll")] public static extern int FlashWindow ( int hwnd,int bInvert); [DllImport("user32.dll")] public static extern int FlashWindowEx ( ref PFLASHWINFO pfwi); [DllImport("user32.dll")] public static extern int SetFocus ( int hwnd); [DllImport("user32.dll")] public static extern int SetForegroundWindow ( int hwnd); [DllImport("user32.dll")] public static extern int ShowWindow ( int hwnd,int nCmdShow); [DllImport("user32.dll")] public static extern int ShowWindowAsync ( int hWnd,int nCmdShow); [DllImport("user32.dll")] public static extern int SwitchDesktop ( int hDesktop); [DllImport("kernel32.dll")] public static extern int SwitchToThread ();
I recommend you download the API Guide and API Viewer from the following location:
http://www.allapi.net/
You'll have a good place to start looking for Win32 APIs !
Hope this helped! | | CSharpFreak Friday, September 01, 2006 9:43 AM | Wow! Definitely did not try all of that.
One of the goals of the port (ie. rewrite) from VB6 to .NET for the project I was on was to get rid of all API calls. So that would not have been acceptable for that project.
This is good info to know, however, because I had come to the conclusion that it was not even possible.
Thanks! | | DeborahK Friday, September 01, 2006 9:29 PM | You are very right! The .NET framework is a great platform for developing Business applications but there are still many things that we can not do without having to go to the good-old-fashioned Win32 API calls! SendKeys is one of the most clear examples to that along with the other three APIs. I can not be sure 100% that the .NET framework does not provide alternative ways to them, but believe me, I've done exhaustive searching and re-searching and found nothing! However, The greatest thing about the .NET framework is its extensibility. You develop your class with all these static API calls and then reuse them whenever you need and this is done much more easily than before! You're a MVP so you mostly new that already, but i just felt an urgent need to express it! After all, a little opinion won't hurt anyone, will it?!  | | CSharpFreak Friday, September 01, 2006 9:50 PM | Implement the API calls found in this thread (FindWindow and SetForegroundWindow).
Here is how to send the keys
1) Type some text into a notepad window. SendKeys.Send("S")
2) Force Firefox to do Help->About. SendKeys.Send("%h") SendKeys.SendWait("a")
3) Read text from a notepad window. SendKeys.SendWait("^a") /* selects all notepad text */ SendKeys.SendWait("^c") /* copy the text to clipboard */ // Read the ClipBoard from you application
// OR programmatically bring your application back to foreground and put focus on destination textbox and use SendKeys.SendWait("^v")
| | JRQ Friday, September 01, 2006 10:56 PM |
|