I'm trying to get the properties of a button but when I drag the spy++'s find window tool it doesn't reach the button (of caption "iniciar gravação" in that case) but rather the button grid parent, like this:
Why is that and how can I get the button properties? I tried with auto it info tool but I get the same behavior.
I tried get that button properties from C# code, like this:
public class WindowHandleInfo
{
private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam);
private IntPtr _MainHandle;
public WindowHandleInfo(IntPtr handle)
{
this._MainHandle = handle;
}
public List<IntPtr> GetAllChildHandles()
{
List<IntPtr> childHandles = new List<IntPtr>();
GCHandle gcChildhandlesList = GCHandle.Alloc(childHandles);
IntPtr pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(this._MainHandle, childProc, pointerChildHandlesList);
}
finally
{
gcChildhandlesList.Free();
}
return childHandles;
}
private bool EnumWindow(IntPtr hWnd, IntPtr lParam)
{
GCHandle gcChildhandlesList = GCHandle.FromIntPtr(lParam);
if (gcChildhandlesList == null || gcChildhandlesList.Target == null)
{
return false;
}
List<IntPtr> childHandles = gcChildhandlesList.Target as List<IntPtr>;
childHandles.Add(hWnd);
return true;
}
}
void printWindows(IntPtr parent, int tabLevel)
{
var w = new WindowHandleInfo(parent);
foreach (IntPtr h in w.GetAllChildHandles())
{
string caption = GetTextBoxText(h) ?? "not found";
Debug.Write(new string('\t', tabLevel));
Debug.WriteLine(caption);
printWindows(h, tabLevel + 1);
}
}
int GetTextBoxTextLength(IntPtr hTextBox)
{
// helper for GetTextBoxText
const int WM_GETTEXTLENGTH = 0x000E;
int result = SendMessage(hTextBox, WM_GETTEXTLENGTH, 0, IntPtr.Zero);
return result;
}
string GetTextBoxText(IntPtr hTextBox)
{
const int WM_GETTEXT = 0x000D;
int len = GetTextBoxTextLength(hTextBox);
if (len <= 0) return null; // no text
StringBuilder sb = new StringBuilder(len + 1);
SendMessage(hTextBox, WM_GETTEXT, len + 1, sb);
return sb.ToString();
}
using like this:
IntPtr parent = FindWindow(IntPtr.Zero, "my app title");
printWindows(parent, 0);
But I can't see anyhing related to Iniciar gravação button.
Qt UI controls (widgets, in Qt lexicon. Aka buttons, line edits, comboboxes, etc.) are not backed by native controls. They just steal the native UI look and feel from them and mimic it.
For this reason, you won't find those controls using some of those "spy" tools -- you may still find them through accessibility, though (*).
Or, you can inspect your application using other Qt-specific tools, such as GammaRay for debugging or Squish for UI testing.
(*) controls shipped with Qt are accessibile, but if someone manually reimplements something that looks like a button and doesn't provide it with accessibility then you can't do much about that.
Related
I am working on a console executable that may run in multi monitor environment.
It may be started by double clicking on the Exe file name inside Windows Explorer.
I want to move the console to the same monitor as the Windows Explorer window that started it. Is there any way to do it?
I was able to get parent process and get MainWindowHandle.
But it will not give me the correct window. It will give me the first Explorer window. See code below. SetupConsoleWindow is the main function.
internal static class NativeMethods
{
internal const int SWP_NOSIZE = 0x0001;
[DllImport("kernel32.dll")]
internal static extern bool AllocConsole();
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
internal static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetConsoleWindow();
}
public static Process GetParentProcess()
{
var currentProcessName = Process.GetProcessById(Process.GetCurrentProcess().Id).ProcessName;
var processesByName = Process.GetProcessesByName(currentProcessName);
for (var index = 0; index < processesByName.Length; index++)
{
string processIndexdName = index == 0 ? currentProcessName : currentProcessName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == Process.GetCurrentProcess().Id)
break;
}
var parentPerformanceCounter = new PerformanceCounter("Process", "Creating Process ID", currentProcessName);
Process parentProcess = Process.GetProcessById((int)parentPerformanceCounter.NextValue());
return parentProcess;
}
public static void SetupConsoleWindow()
{
NativeMethods.AllocConsole();
var parentProcess = GetParentProcess();
if (parentProcess != null)
{
IntPtr consoleWindowHandle = NativeMethods.GetConsoleWindow();
Screen parentScreen = Screen.FromHandle(parentProcess.MainWindowHandle);
Rectangle monitor = parentScreen.WorkingArea;
NativeMethods.SetWindowPos(consoleWindowHandle, 0, monitor.Left, monitor.Top, 0, 0, NativeMethods.SWP_NOSIZE);
}
}
So what you want to do is launch your program on the same monitor as the Windows Explorer window that started it. Doing this the way you currently intend to may be technically possible, but it would be cumbersome and flaky. It would involve enumerating all the open windows, figuring out which ones are Explorer windows, then for each of those which folder is being viewed and then which file is currently selected. You would then compare the name of that file with your program's name and, if multiple such windows are found, choose the most top-level one. You would have to hope that the file did not somehow get deselected between the double click and your main().
This process is well documented here. For reference, here is what the code to get the currently selected item looks like in C++:
#include <shlobj.h>
#include <exdisp.h>
TCHAR g_szPath[MAX_PATH];
TCHAR g_szItem[MAX_PATH];
void CALLBACK RecalcText(HWND hwnd, UINT, UINT_PTR, DWORD)
{
HWND hwndFind = GetForegroundWindow();
g_szPath[0] = TEXT('\0');
g_szItem[0] = TEXT('\0');
IShellWindows *psw;
if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL,
IID_IShellWindows, (void**)&psw))) {
VARIANT v;
V_VT(&v) = VT_I4;
IDispatch *pdisp;
BOOL fFound = FALSE;
for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK;
V_I4(&v)++) {
IWebBrowserApp *pwba;
if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) {
HWND hwndWBA;
if (SUCCEEDED(pwba->get_HWND((LONG_PTR*)&hwndWBA)) &&
hwndWBA == hwndFind) {
fFound = TRUE;
IServiceProvider *psp;
if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) {
IShellBrowser *psb;
if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser,
IID_IShellBrowser, (void**)&psb))) {
IShellView *psv;
if (SUCCEEDED(psb->QueryActiveShellView(&psv))) {
IFolderView *pfv;
if (SUCCEEDED(psv->QueryInterface(IID_IFolderView,
(void**)&pfv))) {
IPersistFolder2 *ppf2;
if (SUCCEEDED(pfv->GetFolder(IID_IPersistFolder2,
(void**)&ppf2))) {
LPITEMIDLIST pidlFolder;
if (SUCCEEDED(ppf2->GetCurFolder(&pidlFolder))) {
if (!SHGetPathFromIDList(pidlFolder, g_szPath)) {
lstrcpyn(g_szPath, TEXT("<not a directory>"), MAX_PATH);
}
int iFocus;
if (SUCCEEDED(pfv->GetFocusedItem(&iFocus))) {
LPITEMIDLIST pidlItem;
if (SUCCEEDED(pfv->Item(iFocus, &pidlItem))) {
IShellFolder *psf;
if (SUCCEEDED(ppf2->QueryInterface(IID_IShellFolder,
(void**)&psf))) {
STRRET str;
if (SUCCEEDED(psf->GetDisplayNameOf(pidlItem,
SHGDN_INFOLDER,
&str))) {
StrRetToBuf(&str, pidlItem, g_szItem, MAX_PATH);
}
psf->Release();
}
CoTaskMemFree(pidlItem);
}
}
CoTaskMemFree(pidlFolder);
}
ppf2->Release();
}
pfv->Release();
}
psv->Release();
}
psb->Release();
}
psp->Release();
}
}
pwba->Release();
}
pdisp->Release();
}
psw->Release();
}
InvalidateRect(hwnd, NULL, TRUE);
}
However, you may be making this more complicated than it needs to be. Consider this instead:
Get the current mouse coordinates with Cursor.Position (or GetCursorPos)
Get the monitor on which the mouse cursor is present
Move your window to that monitor
I wrote this code to test the Inject mouse method but it is not working for me. The test is supposed to click in the google text box search area, but the box never gets highlighted. Any idea why?
Google's page does load. The code runs (confirmed through break points), but nothing happens.
public partial class Form1 : Form
{
private IWebView webView;
public Form1()
{
InitializeComponent();
initiate();
}
private void button1_Click(object sender, EventArgs e)
{
click(650, 405);
}
private async void initiate()
{
WebSession session = WebCore.CreateWebSession(
#"C:\SessionDataPath", WebPreferences.Default);
webView = WebCore.CreateWebView(
this.ClientSize.Width,
this.ClientSize.Height, session, WebViewType.Window
);
webView.ParentWindow = this.Handle;
webView.Source = new Uri("http://www.google.com");
await Task.Delay(30000);
click(650, 405);
}
public void click(int x, int y)
{
webView.InjectMouseMove(x, y);
webView.InjectMouseDown(MouseButton.Left);
webView.InjectMouseUp(MouseButton.Left);
}
}
I tried to get this code to work with chromium handle by looking at the proper chromium class but it didn't work
private async Task<bool> clickCoorindate(Point point)
{
webView.FocusView();
int x = point.X; // X coordinate of the click
int y = point.Y; // Y coordinate of the click
IntPtr handle = webView.ProcessHandle;
StringBuilder className = new StringBuilder(100);
while (className.ToString() != "Chrome_RenderWidgetHostHWND") // The class control for the browser
{
handle = GetWindow(handle, 5); // Get a handle to the child window
GetClassName(handle, className, className.Capacity);
if (className.ToString() == "Chrome_RenderWidgetHostHWND")
handle = Handle;
}
IntPtr lParam = (IntPtr)((y << 16) | x); // The coordinates
IntPtr wParam = IntPtr.Zero; // Additional parameters for the click (e.g. Ctrl)
const uint downCode = 0x201; // Left click down code
const uint upCode = 0x202; // Left click up code
const uint moveCode = 0x200;
SendMessage(handle, downCode, wParam, lParam); // Mouse button down
SendMessage(handle, upCode, wParam, lParam); // Mouse button up
Thread.Sleep(20);
SendMessage(handle, downCode, wParam, lParam); // Mouse button down
SendMessage(handle, upCode, wParam, lParam); // Mouse button up
return true;
}
As mentioned in the documentation (see: WebViewType), a windowed view captures all input itself and you cannot inject input programmatically using Awesomium API (you could do this as you tried, by sending native Windows messages to the appropriate HWND but it's not suggested and straightforward procedure).
To be able to inject input programmatically using the InjectXXX methods, make sure your view is of type offscreen.
in my XNA game i use a lot Awesomium and this is my InputSystem i've implemented in my awesomium component, it works very well.
note that this is just a part of my class, so some methods aren't here but they are not needed to understand the process
basically in my thread. basically i hook to the messages in my form and relay them to the WebView. Hope this helps
public partial class BasicAwesomiumComponent : DrawableGameComponent {
private delegate Int32 ProcessMessagesDelegate(Int32 code, Int32 wParam, ref Message lParam);
private static class User32 {
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetWindowsHookEx(Int32 windowsHookId, ProcessMessagesDelegate function, IntPtr mod, Int32 threadId);
[DllImport("user32.dll", SetLastError = true)]
internal static extern Int32 UnhookWindowsHookEx(IntPtr hook);
[DllImport("user32.dll", SetLastError = true)]
internal static extern Int32 CallNextHookEx(IntPtr hook, Int32 code, Int32 wParam, ref Message lParam);
[DllImport("user32.dll", SetLastError = true)]
internal static extern Boolean TranslateMessage(ref Message message);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr FindWindow(String className, String windowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int RegisterWindowMessage(String msg);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(HandleRef hWnd, Int32 msg, Int32 wParam, Int32 lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern bool SystemParametersInfo(Int32 nAction, Int32 nParam, ref Int32 value, Int32 ignore);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int GetSystemMetrics(Int32 nIndex);
}
private static class Kernel32 {
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern Int32 GetCurrentThreadId();
}
private static class SystemMetrics {
internal static Int32 MouseWheelScrollDelta {
get {
return 120;
}
}
internal static Int32 MouseWheelScrollLines {
get {
var scrollLines = 0;
if (User32.GetSystemMetrics(75) == 0) {
var hwnd = User32.FindWindow("MouseZ", "Magellan MSWHEEL");
if (hwnd != IntPtr.Zero) {
var windowMessage = User32.RegisterWindowMessage("MSH_SCROLL_LINES_MSG");
scrollLines = (Int32)User32.SendMessage(new HandleRef(null, hwnd), windowMessage, 0, 0);
if (scrollLines != 0) {
return scrollLines;
}
}
return 3;
}
User32.SystemParametersInfo(104, 0, ref scrollLines, 0);
return scrollLines;
}
}
}
private enum WindowsMessage {
KeyDown = 0x0100,
KeyUp = 0x0101,
Char = 0x0102,
MouseMove = 0x0200,
LeftButtonDown = 0x0201,
LeftButtonUp = 0x0202,
LeftButtonDoubleClick = 0x0203,
RightButtonDown = 0x0204,
RightButtonUp = 0x0205,
RightButtonDoubleClick = 0x0206,
MiddleButtonDown = 0x0207,
MiddleButtonUp = 0x0208,
MiddleButtonDoubleClick = 0x0209,
MouseWheel = 0x020A,
}
private struct Message {
internal IntPtr HWnd;
internal Int32 Msg;
internal IntPtr WParam;
internal IntPtr LParam;
internal IntPtr Result;
}
private IntPtr hookHandle;
private ProcessMessagesDelegate processMessages;
private Int32 ProcessMessages(Int32 code, Int32 wParam, ref Message lParam) {
if (this.Enabled && code == 0 && wParam == 1) {
bool processed = false;
switch ((WindowsMessage)lParam.Msg) {
case WindowsMessage.KeyDown:
case WindowsMessage.KeyUp:
case WindowsMessage.Char:
WebKeyboardEvent keyboardEvent = new WebKeyboardEvent((uint)lParam.Msg, lParam.WParam, lParam.LParam, 0);
awesomiumContext.Post(state => {
if (!WebView.IsLive) return;
WebView.InjectKeyboardEvent(keyboardEvent);
}, null);
processed = true;
break;
case WindowsMessage.MouseWheel:
var delta = (((Int32)lParam.WParam) >> 16);
awesomiumContext.Post(state => {
if (!WebView.IsLive) return;
WebView.InjectMouseWheel(delta / SystemMetrics.MouseWheelScrollDelta * 16 * SystemMetrics.MouseWheelScrollLines, 0);
}, null);
processed = true;
break;
}
if (!processed) {
WindowsMessage message = (WindowsMessage)lParam.Msg;
awesomiumContext.Post(state => {
if (!WebView.IsLive) return;
switch (message) {
case WindowsMessage.MouseMove:
var mouse = Mouse.GetState();
WebView.InjectMouseMove(mouse.X - area.X, mouse.Y - area.Y);
break;
case WindowsMessage.LeftButtonDown:
WebView.InjectMouseDown(MouseButton.Left);
break;
case WindowsMessage.LeftButtonUp:
WebView.InjectMouseUp(MouseButton.Left);
break;
case WindowsMessage.LeftButtonDoubleClick:
WebView.InjectMouseDown(MouseButton.Left);
break;
case WindowsMessage.RightButtonDown:
WebView.InjectMouseDown(MouseButton.Right);
break;
case WindowsMessage.RightButtonUp:
WebView.InjectMouseUp(MouseButton.Right);
break;
case WindowsMessage.RightButtonDoubleClick:
WebView.InjectMouseDown(MouseButton.Right);
break;
case WindowsMessage.MiddleButtonDown:
WebView.InjectMouseDown(MouseButton.Middle);
break;
case WindowsMessage.MiddleButtonUp:
WebView.InjectMouseUp(MouseButton.Middle);
break;
case WindowsMessage.MiddleButtonDoubleClick:
WebView.InjectMouseDown(MouseButton.Middle);
break;
}
}, null);
}
User32.TranslateMessage(ref lParam);
}
return User32.CallNextHookEx(IntPtr.Zero, code, wParam, ref lParam);
}
}
update:
note that in my component, to hook the message pump, i use
int currentThread = Kernel32.GetCurrentThreadId();
// Create the message hook.
hookHandle = User32.SetWindowsHookEx(3, ProcessMessages, IntPtr.Zero, currentThread);
my surface in an Offscreen webview so the more complex, this should work for you too
i posted a separated answer to give another direction:
take a look at this gist: https://gist.github.com/robertkhrona/918109
it seems to suggest to do
webView.InjectMouseMove(x,y);
webView.InjectMouseDown(MouseButton.Left);
webView.InjectMouseMove(x,y);
webView.InjectMouseUp(MouseButton.Left);
so moving (to the same position) between the two mousedown/up event
btw i think this shouldn't be needed tho
which version of awesomium are you running?
update:
Remember to set the focus on your WebView before injecting inputs
webView.Focus();
I set the viewtype to offscreen and the injectclick worked fine, when set to window it doesn't work. I don't know why, but I can work with that.
I'm trying to click a Button in another Application (started from my Programm with Process.Start)
The problem: I need to wait until the Loading screen is disappeared and the GUI pop's up...
My idea was to read all (Hwnd)Controls until a specific Control (Button: "Kill Client") from the GUI was found (=GUI Opened).
But this only works if I wait manually for the GUI and press a "Search Control" button.
If I press the "Search Button" if the Loading Screen is aktive I get a Hwnd = 0 (List<'IntPtr> Count is also 0...) and if i press it again if the GUI is opened it is 0 again(List<'IntPtr> Count too...) !!!
Here my Code:
public class WndSearcher
{
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
return true;
}
}
My Button:
List<IntPtr> AllControlHandles = WndSearcher.GetChildWindows(selectedCharacter.Botprocess.MainWindowHandle);
IntPtr ControlHandle = AllControlHandles.Find(x => PInvoke.GetWindowTextRaw(x) == "Kill Client" ? true : false);
MessageBox.Show(ControlHandle.ToString());
Part of PInvoke (Class):
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [Out] StringBuilder lParam);
public static string GetWindowTextRaw(IntPtr hwnd)
{
// Allocate correct string length first
int length = (int)SendMessage(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, null);
StringBuilder sb = new StringBuilder(length + 1);
SendMessage(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
Found no solution till now.
So I decided to use AutoHotKey in combination with C#.
In C# I start my AutoHotKey Script and wait until the Script is finished. (Then the external Programm is started completely)
Starting Arguments: 1.Processid 2.NewExternalProgramName
Here my AutoHotKey Script:
counter := 0
Loop, %0% ; For each parameter:
{
param := %A_Index%
if (counter = 0) ; do sth with parameter 1
winwait, ahk_pid %param% ; Not logged in ;wait until the text "Not logged in" can be read (Program started completely)
if (counter = 1) ; do sth with parameter 2
WinSetTitle, %param%
counter += 1
}
I have a strange problem.
I am using SendMessage to send a string to all running instances of the same Windows Forms application.
I can successfully send the string representation of the numeric value of an IntPtr pointer, like so:
unsafe private void SendString(IntPtr handle, IntPtr myHandle)
{
string s = handle.ToString(); // This will work and the value will be received.
// Try with "123553" which wont work.
// How can that be?
IntPtr lpData = Marshal.StringToHGlobalUni(s);
COPYDATASTRUCT data = new COPYDATASTRUCT();
data.dwData = 0;
data.cbData = s.Length * 2;
data.lpData = lpData;
IntPtr lpStruct = Marshal.AllocHGlobal(
Marshal.SizeOf(data));
Marshal.StructureToPtr(data, lpStruct, false);
int hTarget;
var succes = Int32.TryParse(s, out hTarget);
if (succes)
SendMessage(hTarget, WM_COPYDATA, handle, lpStruct);
}
The receiving application(s) correctly outputs a value like '123553'.
However, if I manually assign a value to s nothing is received:
string s = "123553";
Does anyone have an idea why calling ToString on an IntPtr and hardcoding the value doesn't produce the same behavior?
The code for running the application yourself is here:
public const int WM_COPYDATA = 0x004a;
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
[MarshalAs(UnmanagedType.I4)]
public int dwData;
[MarshalAs(UnmanagedType.I4)]
public int cbData;
[MarshalAs(UnmanagedType.SysInt)]
public IntPtr lpData;
}
[DllImport("User32.dll")]
private static extern bool SendMessage(int hWnd,
int wMsg, IntPtr wParam, IntPtr lParam);
public Form1()
{
InitializeComponent();
}
unsafe protected override void WndProc(ref Message message)
{
if (message.Msg == WM_COPYDATA)
{
COPYDATASTRUCT data = (COPYDATASTRUCT)
message.GetLParam(typeof(COPYDATASTRUCT));
string str = new string((char*)(data.lpData),
0, data.cbData / 2);
Debug.WriteLine(str);
}
base.WndProc(ref message);
}
unsafe private void SendString(IntPtr handle, IntPtr myHandle)
{
string s = handle.ToString();
IntPtr lpData = Marshal.StringToHGlobalUni(s);
COPYDATASTRUCT data = new COPYDATASTRUCT();
data.dwData = 0;
data.cbData = s.Length * 2;
data.lpData = lpData;
IntPtr lpStruct = Marshal.AllocHGlobal(
Marshal.SizeOf(data));
Marshal.StructureToPtr(data, lpStruct, false);
int hTarget;
var succes = Int32.TryParse(s, out hTarget);
if (succes)
SendMessage(hTarget, WM_COPYDATA, handle, lpStruct);
}
private void button1_Click(object sender, EventArgs e)
{
Process currentProcess = Process.GetCurrentProcess();
var handles = (from process in Process.GetProcesses()
where
process.Id != currentProcess.Id &&
process.ProcessName.Equals(
currentProcess.ProcessName,
StringComparison.Ordinal)
select process.MainWindowHandle).ToList<IntPtr>();
foreach (var handle in handles)
{
SendString(handle, this.Handle);
Debug.WriteLine(string.Format("Sending handle {0} from handle {1}", handle, this.Handle));
}
}
Sources:
Detecting if another instance of the application is already running
Using WM_COPYDATA for interprocess communication (VFP9)
I tried it with findwindow and process but it didn't work, how can I find a specific button ?
For example I have the button class AfxWnd90u and the instance 21. I want to check if this button is visible. I tried it with this code, but I couldn't find the button. I think I made a mistake with the instance.
Between I didn't use findwindow here because I experimented a little bit.
//////IMPORTANT/////////////
System.Diagnostics.Process[] move = System.Diagnostics.Process.GetProcessesByName("PartyGaming");
ArrayList save = new ArrayList();
RECT rct = new RECT();
listBox1.Items.Add(move.Length);
List<System.Diagnostics.Process> process = new List<System.Diagnostics.Process>();
// use only the process with the button AfxWnd90u21
for (int i = 0; i < move.Length;++i )
{
IntPtr hCheck = FindWindowEx(move[i].MainWindowHandle, IntPtr.Zero, "AfxWnd90u21", null);
//if button is visible
if (hCheck != IntPtr.Zero)
process.Add(move[i]);
//////IMPORTANT/////////////
}
I believe a combination of FindWindow and SendMessage Windows API functions will give you want you want. The tricky part will be discovering the window class names, but something like WinSpy++ could help you there.
Here's a sample of how to use the API. Open Notepad.exe a few times, type in some text and then run this sample.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<WinText> windows = new List<WinText>();
//find the "first" window
IntPtr hWnd = FindWindow("notepad", null);
while (hWnd != IntPtr.Zero)
{
//find the control window that has the text
IntPtr hEdit = FindWindowEx(hWnd, IntPtr.Zero, "edit", null);
//initialize the buffer. using a StringBuilder here
System.Text.StringBuilder sb = new System.Text.StringBuilder(255); // or length from call with GETTEXTLENGTH
//get the text from the child control
int RetVal = SendMessage(hEdit, WM_GETTEXT, sb.Capacity, sb);
windows.Add(new WinText() { hWnd = hWnd, Text = sb.ToString() });
//find the next window
hWnd = FindWindowEx(IntPtr.Zero, hWnd, "notepad", null);
}
//do something clever
windows.OrderBy(x => x.Text).ToList().ForEach(y => Console.Write("{0} = {1}\n", y.hWnd, y.Text));
Console.Write("\n\nFound {0} window(s).", windows.Count);
Console.ReadKey();
}
private struct WinText
{
public IntPtr hWnd;
public string Text;
}
const int WM_GETTEXT = 0x0D;
const int WM_GETTEXTLENGTH = 0x0E;
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, int msg, int Param, System.Text.StringBuilder text);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
}
}
Autoit provides a great way to interact with Windows.
Install the nuget package called AutoItX.Dotnet
using AutoIt;
class Program
{
static void Main(string[] args)
{
var buttonVisible = AutoItX.ControlCommand("Untitled - Notepad", "", "[CLASSNN:Edit1]", "IsVisible", "");
//in your case it would be:
//var buttonVisible = AutoItX.ControlCommand("Put the application title here", "", "[CLASSNN:AfxWnd90u21]", "IsVisible", "");
if (buttonVisible == "1")
{
Console.WriteLine("Visible");
} else
{
Console.WriteLine("Not visible");
}
}
}