I am creating a program that will send media key inputs (such as MediaPlayPause, MediaNextTrack, etc) to an application that I have the IntPtr of. Sort of like a virtual remote control.
So after researching I found this, which almost tells me exactly how to solve my problem.
However, there are three problems with the approach mentioned in the link.
I cannot set the application as foreground window as I need my application to be focused.
They use the SendKeys function which requires the target window to be focused, goes against problem 1.
From what I know, SendKeys cannot send keyboard buttons such as the keyboard Play/Pause button.
In the end, I am rather confused on what I have to use (SendInput?, SendMessage?).
Any help would be appreciated.
EDIT
Using the answer I received, I hacked together the sample code below.
Theoretically, it is supposed to find notepad and insert the letter "L" into it.
However nothing shows up on notepad, nor does the application crash. Is there an obvious error I am missing?
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(int ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, IntPtr lParam);
const int WM_KEYDOWN = 0x100;
//const int WM_KEYUP = 0x101;
const int L_KEY = 0x4C;
private void button1_Click(object sender, EventArgs e)
{
IntPtr ip = FindWindowByCaption(0, "Untitled - Notepad");
SendMessage(ip, WM_KEYDOWN, L_KEY, IntPtr.Zero);
//SendMessage(ip, WM_KEYUP, L_KEY, IntPtr.Zero);
}
Most of these keys are translated to WM_APPCOMMAND* messages... so you can try SendMessage, the other option being SendInput (if the application is DirectInput-based)...
Check out the links in Windows API for common media player functions? - perhaps there is some information you can use...
As for the focus problem - there is no 100% reliable solution (see How do I send key strokes to a window without having to activate it using Windows API?)... best you can achieve with 100% reliability is to focus the application, send the keys, refocus your application... except you would write some sort of device driver (kernel mode)...
To send multimedia keys, including Play/Pause, NextTrack, PrevTrack, etc, you can use keybd_event:
public class Program
{
public const int KEYEVENTF_EXTENTEDKEY = 1;
public const int KEYEVENTF_KEYUP = 0;
public const int VK_MEDIA_NEXT_TRACK = 0xB0;
public const int VK_MEDIA_PLAY_PAUSE = 0xB3;
public const int VK_MEDIA_PREV_TRACK = 0xB1;
[DllImport("user32.dll")]
public static extern void keybd_event(byte virtualKey, byte scanCode, uint flags, IntPtr extraInfo);
public static void Main(string[] args)
{
keybd_event(VK_MEDIA_PLAY_PAUSE, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); // Play/Pause
//keybd_event(VK_MEDIA_PREV_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); // PrevTrack
//keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); // NextTrack
}
Here is a list to the supported key codes that this windows api can handle:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
The SendKeys class is very nice, but it's also limited. The approach above sends the key command directly to Windows OS.
Related
I want to simulate F5 key press in my C# program. When IE is open, I want to be able refresh my website automatically.
How can I do that?
Here's an example...
static class Program
{
[DllImport("user32.dll")]
public static extern int SetForegroundWindow(IntPtr hWnd);
[STAThread]
static void Main()
{
while(true)
{
Process [] processes = Process.GetProcessesByName("iexplore");
foreach(Process proc in processes)
{
SetForegroundWindow(proc.MainWindowHandle);
SendKeys.SendWait("{F5}");
}
Thread.Sleep(5000);
}
}
}
a better one... less anoying...
static class Program
{
const UInt32 WM_KEYDOWN = 0x0100;
const int VK_F5 = 0x74;
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
[STAThread]
static void Main()
{
while(true)
{
Process [] processes = Process.GetProcessesByName("iexplore");
foreach(Process proc in processes)
PostMessage(proc.MainWindowHandle, WM_KEYDOWN, VK_F5, 0);
Thread.Sleep(5000);
}
}
}
You can use the Win32 API FindWindow or FindWindowEx to find the window handle of the open browser and then just call SendMessage with WM_KEYDOWN. Typically it's easiest just to pass the window caption to FindWindowEx and have it find the associated window handle for you.
If you are starting the browser process yourself via a Process process object then you can use process.MainWindowHandle instead of calling FindWindowEx.
Spy++ is a very useful tool when you want to start working with other windows. It basically allows you to learn another program's hierarchy of UI elements. You can also monitor all of the messages that go into the window you're monitoring. I have more info in this thread.
The F5 keystroke has this virtual key code:
const int VK_F5 = 0x74;
The p/invoke signature for FindWindowEx in C# is:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
You can p/invoke (bring in) the Win32 API SendMessage like this:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
So to recap, you call FindWindowEx directly from your C# code after having the above code somewhere inside your class. FindWindowEx will return a window handle. Then once you have the window handle, you can send any keystroke(s) to the window, or call many other Win32 API calls on the window handle. Or even find a child window by using another call to FindWindowEx. For example you could select the edit control of the browser even and then change it's text.
If all else goes wrong and you think you're sending the right key to the window, you can use spy++ to see what messages are sent to the window when you manually set focus to the browser and manually press F5.
The easiest way to send (simulate) KeyStrokes to any window is to use the SendKeys.Send method of .NET Framework.
Checkout this very intuitive MSDN article http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx
Particularly for your case, if your browser window is in focus, sending F5 would just involve the following line of code:
SendKeys.Send("{F5}");
Simple one, add before Main
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
Code inside Main/Method:
string className = "IEFrame";
string windowName = "New Tab - Windows Internet Explorer";
IntPtr IE = FindWindow(className, windowName);
if (IE == IntPtr.Zero)
{
return;
}
SetForegroundWindow(IE);
InputSimulator.SimulateKeyPress(VirtualKeyCode.F5);
Note:
Add InputSimulator as reference. To download Click here
To find Class & Window name, use WinSpy++. To download Click here
Another alternative to simulating a F5 key press would be to simply host the WebBrowser control in the Window Forms application. You use the WebBrowser.Navigate method to load your web page and then use a standard Timer and on each tick of the timer you just re-Navigate to the url which will reload the page.
Easy, short and no need window focus:
Also here a usefull list of Virtual Key Codes
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private void button1_Click(object sender, EventArgs e)
{
const int WM_SYSKEYDOWN = 0x0104;
const int VK_F5 = 0x74;
IntPtr WindowToFind = FindWindow(null, "Google - Mozilla Firefox");
PostMessage(WindowToFind, WM_SYSKEYDOWN, VK_F5, 0);
}
Use mouse_event or keybd_event. They say not to use them anymore but you don't have to find the window at all.
using System;
using System.Runtime.InteropServices;
public class SimulatePCControl
{
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void keybd_event(uint bVk, uint bScan, uint dwFlags, uint dwExtraInfo);
private const int VK_LEFT = 0x25;
public static void LeftArrow()
{
keybd_event(VK_LEFT, 0, 0, 0);
}
}
Virtual Key Codes are here for this one: http://www.kbdedit.com/manual/low_level_vk_list.html
Also for mouse:
using System.Runtime.InteropServices;
using UnityEngine;
public class SimulateMouseClick
{
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
//Mouse actions
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
public static void Click()
{
//Call the imported function with the cursor's current position
uint X = (uint)0;
uint Y = (uint)0;
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
Debug.LogError("SIMULATED A MOUSE CLICK JUST NOW...");
}
//...other code needed for the application
}
Instead of forcing an F5 keypress when you're just trying to get the page to postback, you can call a postback based on a JS event (even mousemove or timer_tick if you want it to fire all the time). Use the code at http://weblogs.asp.net/mnolton/archive/2003/06/04/8260.aspx as a reference.
I am trying to do one of the following
1. open desired program and press a key programmatically
2. find open window of program and press a key programmatically
(either is fine)
I have tried numerous implementations of SendKeys.SendWait(), PostMessage(), and SendMessage() unsuccessfully. Below are my code snippets
//included all these for attempts
[DllImport("User32.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll")]
static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
[DllImport("user32.dll")]
static extern byte VkKeyScan(char ch);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
Get handle of window, variables used by sendmessage/postmessage/sendkeys
IntPtr ptrOBS = proc.Handle;//this works properly, proc is instantiated properly
//IntPtr ptrOBS = FindWindow(null, "Open Broadcaster Software v0.472b");
SetForegroundWindow(ptrOBS);
const UInt32 WM_CHAR = 0x0102;
const uint WM_KEYDOWN = 0x100;
const int VK_R = 0x52; // taken from http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
const int VK_S = 0x53;
SendMessage attempt:
SendMessage(ptrOBS, WM_KEYDOWN, (IntPtr)VK_R, (IntPtr)1);//tried both WM_CHAR and WM_KEYDOWN
PostMessage attempt:
string message = "rs";
bool sent = PostMessage(ptrOBS, WM_KEYDOWN, VkKeyScan(message[0]), 0);
SendKeys attempt:
SendKeys.SendWait("{r}");
Tried SetFocus on the parent window (application) and child window (button triggered by keypress im trying to send):
static void SetFocus(IntPtr hwndTarget, string childClassName)
{
// hwndTarget is the other app's main window
// ...
IntPtr targetThreadID = GetWindowThreadProcessId(hwndTarget, IntPtr.Zero); //target thread id
IntPtr myThreadID = GetCurrentThread(); // calling thread id, our thread id
try
{
bool lRet = AttachThreadInput(myThreadID, targetThreadID, -1); // attach current thread id to target window
// if it's not already in the foreground...
lRet = BringWindowToTop(hwndTarget);
SetForegroundWindow(hwndTarget);
// if you know the child win class name do something like this (enumerate windows using Win API again)...
IntPtr hwndChild = (IntPtr)1183492;//(IntPtr)EnumAllWindows(hwndTarget, childClassName).FirstOrDefault();
if (hwndChild == IntPtr.Zero)
{
// or use keyboard etc. to focus, i.e. send keys/input...
// SendInput (...);
return;
}
// you can use also the edit control's hwnd or some child window (of target) here
SetFocus(hwndChild); // hwndTarget);
SendKeys.SendWait("{r}");
}
finally
{
SendKeys.SendWait("{r}");
bool lRet = AttachThreadInput(myThreadID, targetThreadID, 0); //detach from foreground window
SendKeys.SendWait("{r}");
}
}
For NSGaga:
string windowName = "Open Broadcaster Software v0.472b";
IntPtr outerPtr = FindWindow(null, windowName);
IntPtr ptrOBS = (IntPtr)527814;//button that im trying to trigger keypress on
SetForegroundWindow(outerPtr);
SetForegroundWindow(ptrOBS);
SetFocus(outerPtr, "OBSWindowClass");//SetFocus(ptrOBS, "Button");
const UInt32 WM_CHAR = 0x0102;
const int VK_R = 0x52; // taken from http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
const int VK_S = 0x53;
//SetForegroundWindow(ptrOBS);
System.Threading.Thread.Sleep(3000);
SendKeys.SendWait("{r}");
SendMessage(outerPtr, WM_KEYDOWN, (IntPtr)VK_R, (IntPtr)1);
PostMessage(outerPtr, WM_KEYDOWN, VkKeyScan('r'), 0);
You cannot reliably use SendMessage and PostMessage for synthesizing keyboard input. They are just not designed for this. These messages (WM_CHAR, WM_KEYDOWN, etc.) are notifications raised by lower-level subsystems when keyboard input has been received, processed, and forwarded on to the appropriate recipient. Sending or posting these messages yourself is like prank-calling someone.
SendKeys (like all other input synthesizer methods, including the SendInput function which was explicitly designed for synthesizing keyboard input and in at least some implementation is what SendKeys actually uses under the hood) works only when the window you wish to receive the keyboard input has the focus. In Windows, only focused (active) windows receive input events.
So SendKeys is probably the way to go if you're ever going to get this to work (either that or P/Invoking SendInput and all of its associated structures), but you do need to respect the caveat that the recipient window must have the focus. Otherwise, it's not going to get anything.
It looks like from your sample code that you're trying to use the SetForegroundWindow function to meet this precondition. Unfortunately, you're passing it an invalid value, and not doing any error checking that might alert you to this mistake. Specifically, this code is wrong:
IntPtr ptrOBS = proc.Handle;//this works properly, proc is instantiated properly
SetForegroundWindow(ptrOBS); // WRONG, ptrOBS is not a window handle
Even if I trust you on ptrOBS being initialized correctly, that makes it a valid handle to a process, which is a very different thing than a valid handle to a window. Aside from the obvious nominal differences, processes can have multiple windows and only a single window can have the focus (i.e., be "in the foreground").
You will need to obtain the handle to a particular window before calling SetForegroundWindow, and given that we know a process can have multiple windows, that can be tricky. You need some reliable way of determining which window you want. Lots of people accomplish this by hard-coding the name of the window as a string, which works great until the target app is recompiled and this implementation detail changes. The only bulletproof way that I can think of is to have the user click the target window and your code to retrieve the handle of the window that is currently under the mouse pointer.
And of course all of this assumes that you've observed the restrictions on the use of SetForegroundWindow, enumerated in the "Remarks" section of the linked SDK documentation.
There is lot of trial and error with that, to get it working
Here is a bit of code I posted before, you might wanna give a try (and there is some more info attached)...
Pinvoke SetFocus to a particular control
Try setting focus first (using the mechanism mentioned) - and then using SendKeys or SendInput.
Here is some detailed code for SendInput...
How to send a string to other application including Microsoft Word
i am trying to grab the selected text from the open form on a users machine. Currently i have tried using GetFocus which is defined as
'[DllImport("user32.dll")]
static extern int GetFocus();'
In the api it says - Retrieves the handle to the window that has the keyboard focus, if the window is attached to the calling thread's message queue. Which explains why my app can grab the selected text from a window thats part of my app, but not one thats external, like a pdf for example.
What alternative win32 method is available for me to use that would fit this purpose?
Thanks.
edit: this is the attempt at the moment
[DllImport("user32.dll")]
static extern int GetFocus();
[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(int hWnd, int ProcessId);
[DllImport("user32.dll")]
static extern int GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern int SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam);
// second overload of SendMessage
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, out int wParam, out int lParam);
const int WM_SETTEXT = 12;
const int WM_GETTEXT = 13;
private static string PerformCopy()
{
try
{
//Wait 5 seconds to give us a chance to give focus to some edit window,
//notepad for example
System.Threading.Thread.Sleep(1000);
StringBuilder builder = new StringBuilder(500);
int foregroundWindowHandle = GetForegroundWindow();
uint remoteThreadId = GetWindowThreadProcessId(foregroundWindowHandle, 0);
uint currentThreadId = GetCurrentThreadId();
//AttachTrheadInput is needed so we can get the handle of a focused window in another app
AttachThreadInput(remoteThreadId, currentThreadId, true);
//Get the handle of a focused window
int focused = GetFocus();
//Now detach since we got the focused handle
AttachThreadInput(remoteThreadId, currentThreadId, false);
//Get the text from the active window into the stringbuilder
SendMessage(focused, WM_GETTEXT, builder.Capacity, builder);
return builder.ToString();
}
catch (System.Exception oException)
{
throw oException;
}
}
Check GetForegroundWindow.
I don't think you have much chance of succeeding with your current approach. I'm pretty sure there's no single general purpose API for getting hold of the current selection. I believe this because each application can implement text selection in its own way.
As an alternative solution you should consider using a clipboard listener. Listen for changes to the clipboard contents and whenever text is added you can suck it out of the clipboard and put it in your app's window.
I think this is a job for UI Automation (the API screen readers use). Here's a post that get's the selected text in C#.
I'm creating an application that uses a main project that is connected to several different DLLs. From one DLL window I need to be able to open a window in another but the DLL's can't reference each other.
It was suggested to me to use the sendmessage function in the first DLL and have a listener in the main program that directs that message to the appropriate DLL to open it's window.
However I'm not familiar at all with the sendmessage function and am having a lot of diffculty piecing things together from information I'm finding online.
If someone could please show me the correct way (if there is any) to use the sendmessage function and maybe how a listener captures that message that would be amazing. Here is some of the code I've got so far I'm not sure if I'm heading in the right direction.
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName, String lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
public void button1_Click(object sender, EventArgs e)
{
int WindowToFind = FindWindow(null, "Form1");
}
public static extern int FindWindow(string lpClassName, String lpWindowName);
In order to find the window, you need the class name of the window. Here are some examples:
C#:
const string lpClassName = "Winamp v1.x";
IntPtr hwnd = FindWindow(lpClassName, null);
Example from a program that I made, written in VB:
hParent = FindWindow("TfrmMain", vbNullString)
In order to get the class name of a window, you'll need something called Win Spy
Once you have the handle of the window, you can send messages to it using the SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam) function.
hWnd, here, is the result of the FindWindow function. In the above examples, this will be hwnd and hParent. It tells the SendMessage function which window to send the message to.
The second parameter, wMsg, is a constant that signifies the TYPE of message that you are sending. The message might be a keystroke (e.g. send "the enter key" or "the space bar" to a window), but it might also be a command to close the window (WM_CLOSE), a command to alter the window (hide it, show it, minimize it, alter its title, etc.), a request for information within the window (getting the title, getting text within a text box, etc.), and so on. Some common examples include the following:
Public Const WM_CHAR = &H102
Public Const WM_SETTEXT = &HC
Public Const WM_KEYDOWN = &H100
Public Const WM_KEYUP = &H101
Public Const WM_LBUTTONDOWN = &H201
Public Const WM_LBUTTONUP = &H202
Public Const WM_CLOSE = &H10
Public Const WM_COMMAND = &H111
Public Const WM_CLEAR = &H303
Public Const WM_DESTROY = &H2
Public Const WM_GETTEXT = &HD
Public Const WM_GETTEXTLENGTH = &HE
Public Const WM_LBUTTONDBLCLK = &H203
These can be found with an API viewer (or a simple text editor, such as notepad) by opening (Microsoft Visual Studio Directory)/Common/Tools/WINAPI/winapi32.txt.
The next two parameters are certain details, if they are necessary. In terms of pressing certain keys, they will specify exactly which specific key is to be pressed.
C# example, setting the text of windowHandle with WM_SETTEXT:
x = SendMessage(windowHandle, WM_SETTEXT, new IntPtr(0), m_strURL);
More examples from a program that I made, written in VB, setting a program's icon (ICON_BIG is a constant which can be found in winapi32.txt):
Call SendMessage(hParent, WM_SETICON, ICON_BIG, ByVal hIcon)
Another example from VB, pressing the space key (VK_SPACE is a constant which can be found in winapi32.txt):
Call SendMessage(button%, WM_KEYDOWN, VK_SPACE, 0)
Call SendMessage(button%, WM_KEYUP, VK_SPACE, 0)
VB sending a button click (a left button down, and then up):
Call SendMessage(button%, WM_LBUTTONDOWN, 0, 0&)
Call SendMessage(button%, WM_LBUTTONUP, 0, 0&)
No idea how to set up the listener within a .DLL, but these examples should help in understanding how to send the message.
You are almost there. (note change in the return value of FindWindow declaration). I'd recommend using RegisterWindowMessage in this case so you don't have to worry about the ins and outs of WM_USER.
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, String lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
public void button1_Click(object sender, EventArgs e)
{
// this would likely go in a constructor because you only need to call it
// once per process to get the id - multiple calls in the same instance
// of a windows session return the same value for a given string
uint id = RegisterWindowMessage("MyUniqueMessageIdentifier");
IntPtr WindowToFind = FindWindow(null, "Form1");
Debug.Assert(WindowToFind != IntPtr.Zero);
SendMessage(WindowToFind, id, IntPtr.Zero, IntPtr.Zero);
}
And then in your Form1 class:
class Form1 : Form
{
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
private uint _messageId = RegisterWindowMessage("MyUniqueMessageIdentifier");
protected override void WndProc(ref Message m)
{
if (m.Msg == _messageId)
{
// do stuff
}
base.WndProc(ref m);
}
}
Bear in mind I haven't compiled any of the above so some tweaking may be necessary.
Also bear in mind that other answers warning you away from SendMessage are spot on. It's not the preferred way of inter module communication nowadays and genrally speaking overriding the WndProc and using SendMessage/PostMessage implies a good understanding of how the Win32 message infrastructure works.
But if you want/need to go this route I think the above will get you going in the right direction.
You don't need to send messages.
Add an event to the one form and an event handler to the other. Then you can use a third project which references the other two to attach the event handler to the event. The two DLLs don't need to reference each other for this to work.
It doesn't sound like a good idea to use send message. I think you should try to work around the problem that the DLLs can't reference each other...
Some other options:
Common Assembly
Create another assembly that has some common interfaces that can be implemented by the assemblies.
Reflection
This has all sorts of warnings and drawbacks, but you could use reflection to instantiate / communicate with the forms. This is both slow and runtime dynamic (no static checking of this code at compile time).
Building on Mark Byers's answer.
The 3rd project could be a WCF project, hosted as a Windows Service. If all programs listened to that service, one application could call the service. The service passes the message on to all listening clients and they can perform an action if suitable.
Good WCF videos here - http://msdn.microsoft.com/en-us/netframework/dd728059
I'm interested in working on a plugin for Keepass, the open-source password manager. Right now, Keepass currently detects what password to copy/paste for you based off of the window title. This prevents Keepass from detecting the current password you need for apps that don't actively update their window title based on the current site (Chrome for instance).
How can I walk through another processes window elements (buttons, labels, textbox) similar to how Spy++ works? When you run Spy++ you can hover over other programs windows and get all kinds of information about various properties concerning various controls (labels, textboxes, etc). Ideally, I'd like my Keepass plugin to enhance the current window detection by walking through the active window's elements in an effort to find a matching account to copy/paste the password.
How can I walk other processes window elements and be able to retrieve label and textbox values using C#?
I've being answering similar questions like this here: How can I detect if a thread has windows handles?. Like it states, the main idea is to enumerate through process windows and their child windows using EnumWindows and EnumChildWindows API calls to get window handles and then call GetWindowText or SendDlgItemMessage with WM_GETTEXT to get window text. I've modified code to make an example which should be doing what you need (sorry it's a bit long :). It iterates through processes and their windows and dumps window text into console.
static void Main(string[] args)
{
foreach (Process procesInfo in Process.GetProcesses())
{
Console.WriteLine("process {0} {1:x}", procesInfo.ProcessName, procesInfo.Id);
foreach (ProcessThread threadInfo in procesInfo.Threads)
{
// uncomment to dump thread handles
//Console.WriteLine("\tthread {0:x}", threadInfo.Id);
IntPtr[] windows = GetWindowHandlesForThread(threadInfo.Id);
if (windows != null && windows.Length > 0)
foreach (IntPtr hWnd in windows)
Console.WriteLine("\twindow {0:x} text:{1} caption:{2}",
hWnd.ToInt32(), GetText(hWnd), GetEditText(hWnd));
}
}
Console.ReadLine();
}
private static IntPtr[] GetWindowHandlesForThread(int threadHandle)
{
_results.Clear();
EnumWindows(WindowEnum, threadHandle);
return _results.ToArray();
}
// enum windows
private delegate int EnumWindowsProc(IntPtr hwnd, int lParam);
[DllImport("user32.Dll")]
private static extern int EnumWindows(EnumWindowsProc x, int y);
[DllImport("user32")]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowsProc callback, int lParam);
[DllImport("user32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
private static List<IntPtr> _results = new List<IntPtr>();
private static int WindowEnum(IntPtr hWnd, int lParam)
{
int processID = 0;
int threadID = GetWindowThreadProcessId(hWnd, out processID);
if (threadID == lParam)
{
_results.Add(hWnd);
EnumChildWindows(hWnd, WindowEnum, threadID);
}
return 1;
}
// get window text
[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);
private static string GetText(IntPtr hWnd)
{
int length = GetWindowTextLength(hWnd);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText(hWnd, sb, sb.Capacity);
return sb.ToString();
}
// get richedit text
public const int GWL_ID = -12;
public const int WM_GETTEXT = 0x000D;
[DllImport("User32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int index);
[DllImport("User32.dll")]
public static extern IntPtr SendDlgItemMessage(IntPtr hWnd, int IDDlgItem, int uMsg, int nMaxCount, StringBuilder lpString);
[DllImport("User32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd);
private static StringBuilder GetEditText(IntPtr hWnd)
{
Int32 dwID = GetWindowLong(hWnd, GWL_ID);
IntPtr hWndParent = GetParent(hWnd);
StringBuilder title = new StringBuilder(128);
SendDlgItemMessage(hWndParent, dwID, WM_GETTEXT, 128, title);
return title;
}
hope this helps, regards
Have a look at this article here which contains information about the Managed Spy and why the author wrote the tool.
You can use EnumWindows to find every top-level Chrome window and then call EnumChildWindows recursively (see Jeroen Wiert Pluimers' comment) to get every child of the main window. Alternatively, once you have the main Chrome window, you can use GetWindow to manually navigate the tree since you probably know what you're looking for (3rd child's children collection or something similar).
Once you find your window, you can use SendMessage with a WM_GETTEXT parameter to read the window's label.
You can use HWndSpy. Source code is here.
For the functionality of pointing to a window. You need to SetCapture() so that you get mouse messages that are outside of your window. Then use WindowFromPoint() to convert a mouse position to a Window. You will need to convert the moust position from client coordinates to window coordinates first.
If you try an call SetCapture() anywhere but on a mouse click message, you will probably be ignored. This is the reason that Spy++ makes you click on an Icon and drag and drop it on the window you want to point to.