Well, I've tried my best to look at code examples and posts all over the web on how to do this, but I haven't been able to make any headway in a few months using windows API to interact with another program that's already running. I'm not the greatest programmer and some of this stuff is beyond me.
The most I've been able to do is find the Calculator process and its handle, and use that with SetWindowText to change the title. What I'd really like to learn how to do is make my program use the windows user32 (I think this must be the correct library) to enter some numbers by actually pressing the number key buttons on the software calculator to do a simple calculation.
I don't really have a use for this program, it's just a goal I'm trying to reach to learn how to use the windows API past my very beginner level SPECIFICALLY in C#. If no one has the code for this, or even if you do, I'd most appreciate some suggestions for books or resources on the web I should be reading to learn how to do this.
Since you say you're using C# you should be using the System.Windows.Automation namespace, whose entire purpose in life is to allow you to control other programs via automation.
You didn't give details as to what you wanted, but here's a program that pushes "7" in the calculator.
using System.Windows.Automation;
class Program
{
public static void Main()
{
var calcWindow = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.NameProperty, "Calculator"));
if (calcWindow == null) return;
var sevenButton = calcWindow.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "7"));
var invokePattern = sevenButton.GetCurrentPattern(InvokePattern.Pattern)
as InvokePattern;
invokePattern.Invoke();
}
}
You are entering the fun world of platform invoking (P/Invoke). Your best friend in this world is www.pinvoke.net, which contains c# (and vb.net) signatures for a great number of winapi functions.
For example, SendMessage:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
and this list of message codes.
You'll notice that all p/invoke methods use the DllImport attribute, found in the System.Runtime.InteropServices namespace.
They also have an extern modifier in the method signature. This means that the code is found outside of the current assembly.
You can then use SendMessage to send messages to the handle of the calculator.
As Jonathan mentioned in his comment, you should use Spy++ to detect which messages are being sent that result in the actions you want to replicate.
For good measure, here is an article with more introductory information on P/Invoke.
Related
I'm trying to find the sound control panel window via FindWindow. Since the application is used in multiple languages lpWindowName changes for example "Sound" becomes "Ääni" on a Finnish system. However the lpClassName stays the same.
// Get a handle to an application window.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);
// The window class and window name
// were obtained using the Spy++ tool.
IntPtr soundHandle = FindWindow("#32770", "Sound");
// Verify that Sound is a running process.
if (soundHandle == IntPtr.Zero)
{
System.Windows.MessageBox.Show("Sound is not running.");
return;
}
I have searched and cannot find a way to grab a window based upon it's lpClassName alone. I believe this is because it can change for most windows, this doesn't seem to be the case for Sound control panel.
Is there a way around the lpWindowName changing on different language systems?
Thank you for reading.
The trouble is, multiple windows may share the same class name. FindWindow() filters windows based on class name.
You might consider using: EnumWindows() and enumerate all instances of your window class yourself. Careful, there may be more than one.
I am searching for alternatives to the old User32.dll version of switching to a different application with FindWindow() and SetForegroundWindow().
I did find an alternative to the first with the usage of Process.GetProcessesByName() but I do not see the corresponding method to switch (set active/foreground) to that application.
Is there a way of doing that without using the old way with the User32.dll?
Thank you for your help.
EDIT
I accepted the answer of #Sorceri although it is not the answer I was looking for.
Answer: No.
But, to help the next wonderer looking to find a window and activate it from C# here's what you have to do:
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
void ActivateApp(string processName)
{
Process[] p = Process.GetProcessesByName(processName);
// Activate the first application we find with this name
if (p.Count() > 0)
SetForegroundWindow(p[0].MainWindowHandle);
}
To bring notepad to the front, for example, you would call:
ActivateApp("notepad");
As a side note - for those of you who are trying to bring a window within your application to the foreground just call the Activate() method.
You could use SetActiveWindow as an alternative to SetForeGroundWindow. I'd say you should go through all the Windows Manipulation Api Functions and see if there's something you're missing out.
Also, note that you can obtain the handle of the System.Diagnostics.Process object via the Process.Handle property.
An alternative to SetForeGroundWindow is VisualBasic's AppActivate
Call it like this
Microsoft.VisualBasic.Interaction.AppActivate("WindowTitle")
Just because it is in the VisualBasic namespace doesn't mean you can't use it in C#.
Full Documentation here
You can use System.Diagnostics.Process Object for a FindWindow equivalent. There currently is no equivalent for SetForegroundWindow. You will want use Pinvoke with SetForgroundWindow.
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
I've recently swapped from MDX 2.0 to SlimDX, using Direct3D 11, but I'm struggling to implement keyboard and mouse controls.
In MDX you can use
keyb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
keyb.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
keyb.Acquire();
to set up a keyboard interface, however SlimDX has a different method. In SlimDX the Device is an abstract class, instead there is a Keyboard class that must be initialized by passing in a DirectInput object, but I can't for the life of me work out how to create a DirectInput object or what it's for.
As far as I can find documentation is pretty slim for SlimDX, if anybody knows of any good resources for learning its particular quirks that would be fantastic, thanks.
I used it in this way. The mouse handling is the same.
using SlimDX.DirectInput;
private DirectInput directInput;
private Keyboard keyboard;
[...]
//init
directInput = new DirectInput();
keyboard = new Keyboard(directInput);
keyboard.SetCooperativeLevel(form, CooperativeLevel.Nonexclusive | CooperativeLevel.Background);
keyboard.Acquire();
[...]
//read
KeyboardState keys = keyboard.GetCurrentState();
But you should use SlimDX.RawInput because Microsoft recommends it:
While DirectInput forms a part of the
DirectX library, it has not been
significantly revised since DirectX 8
(2001-2002). Microsoft recommends that
new applications make use of the
Windows message loop for keyboard and
mouse input instead of DirectInput (as
indicated in the Meltdown 2005
slideshow[1]), and to use XInput
instead of DirectInput for Xbox 360
controllers.
(http://en.wikipedia.org/wiki/DirectInput)
A rawinput mouse sample (keyboard is nearly the same):
SlimDX.RawInput.Device.RegisterDevice(UsagePage.Generic, UsageId.Mouse, SlimDX.RawInput.DeviceFlags.None);
SlimDX.RawInput.Device.MouseInput += new System.EventHandler<MouseInputEventArgs>(Device_MouseInput);
Now you can react on the events.
Use the SlimDX.RawInput
To actually get the cursor from the hWnd (the control's/form's handle), you need to extern functions from the "user32.dll"
BOOL GetCursorPos(LPOINT lpPoint)
using the System.Runtime.Interlop and System.Drawing.Point (unless you decide to create a POINT struct instead).
[DllImport("user32.dll",CallingConvention=CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
internal unsafe static extern bool GetCursorPos(Point* lpPoint);
This will get you the actual position of the cursor on desktop screen
Next you will take lpPoint address and pass it into the ScreenToClient(HWND hWnd, LPPOINT lpPoint) which also return a BOOL.
[DllImport("user32.dll",CallingConvention=CallingConvention.StdCall,SetLastError=true)]
internal static extern int ScreenToClient(IntPtr hWnd, Point* p);
Let's just get the Point from it like this then:
public unsafe Point GetClientCurorPos(IntPtr hWnd, Point*p)
{
Point p = new Point();
if (GetCursorPos(&p))
{
ScreenToClient(hWnd, &p);
}
return p;
}
You can use the SlimDX.RawInput.Device.MouseInput handler in you want or you can just do some coding in override for WndProc which is preferred in you are using to handling message which all of us WINAPI programmers are just used to and the tedious writing with it. However, the lower you go the more control you got.
Like I said you get all the info back but the mouse position from the handler's MouseInputEventArgs. I find it better to check the processed messages via WndProc callback.
I'm trying to get my C# form to be parented correctly in a third party app, I have the handle to the control that I would like my form parented to but just can't seem to get it to work.
alt text http://img693.imageshack.us/img693/8871/examplec.jpg
I would like to create my form so that it is part of the MDIClient, handle 005E0ED6. Just like Window 01D7157D.
Is this possible? If so can it be done in C#?
How have you tried doing it? Did you try SetParent? See the following StackOverflow question to see if it helps. Embedding HWND into external process using SetParent
This code seems to work:
[DllImport("user32.dll")]
private static extern
IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("user32.dll")]
private static extern
IntPtr AttachThreadInput(IntPtr idAttach, IntPtr idAttachTo, int fAttach);
WinAPI.SetParent(this.Handle, otherappshandle);
IntPtr otherprocessID = GetWindowThreadProcessId(otherappshandle, new IntPtr(0));
IntPtr threadID = new IntPtr(AppDomain.GetCurrentThreadId());
AttachThreadInput(threadID , otherprocessID , 1);
Good luck. I've gone down that road, and found that there's enough little irritating gotchas that I eventually gave up on it.
SetParent() and the like will get you part of the way there, but there's a bunch of little gotchas to watch as far as the overall system (message pump blocking etc.) that just make it a time sink.
With WinForms, especially, I'd highly recommend just running your UI in the main process (if you can), and if you want to isolate your processing in another process do that instead.
I would make a tools like Google toolbar translate function, but it is for desktop.
What i want to do is
highlight the text in any application (word,pdf,live messenger etc) , and translate by google translate api ,return as a tool tips.
I have search msdn about monitoring text, i only found using copy&paste and monitoring clipboard to tick the event.
so, any idea about that?
thanks you.
A starting point would be to get a reference to the current foreground window. The code below will get the currently selected window and the title of that window:
[ DllImport("user32.dll") ]
static extern int GetForegroundWindow();
[ DllImport("user32.dll") ]
static extern int GetWindowText(int hWnd, StringBuilder text, int count);
private void GetActiveWindow()
{
const int nChars = 256;
int handle = 0;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if ( GetWindowText(handle, Buff, nChars) > 0 )
{
this.captionWindowLabel.Text = Buff.ToString();
this.IDWindowLabel.Text = handle.ToString();
}
}
You could run this code within a timer: i.e give the user 10 seconds to select a window.
I am not sure how you would retrieve selected text within a window, but I will look into it for you.
I think you'll need to start by getting the handle of any window that is activated when your program is active. My guess is you need to look into InteropServices here to do this.
Using Windows API.
It sounds like you need to have your code intercept any window handle of any process, this is where it gets a bit complex as you have to ensure you do have access permissions to access another process.
Speaking of which, I do not think it is a good idea as you could end up crashing another process by poking around under the hood in regards to the winapi calls to trap the text selection event, not too mention the fact that you would have to determine if the process has any text selected. The best direction I can give is this...an article was written on how to spy on a process on CodeProject here, this can be a step in the right direction, bear in mind that the code used was for the .NET 1.0 framework.
Hope this helps and good luck in your coding,
Best regards,
Tom.