Screenshot and handles with user32.dll - GetWindowRect - c#

I'm still quite new to how handles works and need some help to understand this a bit more.
I have this method in my class, "cbxWebsite" is a combobox.
public IntPtr GetCurrentHandle()
{
foreach (Process process in Process.GetProcesses())
{
if (process.ProcessName.Contains("chrome")
&& process.MainWindowTitle.Contains(cbxWebsite.SelectedValue.ToString()))
{
return process.MainWindowHandle;
}
}
return IntPtr.Zero;
}
And I'm trying to implement this: Capture screen of Window by handle
My question is about these lines:
IntPtr hwnd = GetCurrentHandle();
User32.GetWindowRect(new HandleRef(null, hwnd), ref rc);
When I call GetWindowRect, the HandleRef() complains about "Cannot convert from HandleRef to IntPtr".
MS docs says this about process.MainWindowHandle: The system-generated window handle of the main window of the associated process.
So I guess I don't understand from here. I thought I was returning the handle of the current window I want to work with from GetCurrentHandle().
But is it returning the handle of the process of that window and not the actual window-handle itself?
Can someone please clarify.

Related

Find child window by class

I can't get the "Edit" field handle when selecting a photo in a third-party application.
Spy++ shows everything correctly, but FindWindow fails. I can get the handle of the window itself, the parent. I assume I need to look for child windows. I can get some handles with GetWindow but but it's not clear what they are. The window title is empty. FindWindowEx doesn't work at all, returns 0. I indicate it like this:
IntPtr hwndchild = (hwnd, IntPtr.Zero, null, "Edit")
Based on the screenshot you provided, and using just the FindWindow/Ex() functions, you can get the HWND of the Edit control like this:
IntPtr hwndDlg = FindWindow(null, "Choose an image");
IntPtr hwndCBEx = FindWindowEx(hwndDlg, IntPtr.Zero, "ComboBoxEx32", null);
IntPtr hwndCB = FindWindowEx(hwndCBEx, IntPtr.Zero, "ComboBox", null);
IntPtr hwndEdit = FindWindowEx(hwndCB, IntPtr.Zero, "Edit", null);
However, once you have the HWND to the ComboBoxEx control, the correct way to get the HWND of its Edit control is to use the CBEM_GETEDITCONTROL message:
const int CBEM_GETEDITCONTROL = 1031;
IntPtr hwndDlg = FindWindow(null, "Choose an image");
IntPtr hwndCBEx = FindWindowEx(hwndDlg, IntPtr.Zero, "ComboBoxEx32", null);
IntPtr hwndEdit = SendMessage(hwndCBEx, CBEM_GETEDITCONTROL, 0, 0);
Note, for a standard ComboBox control (which you can get from a ComboBoxEx control using the CBEM_GETCOMBOCONTROL message), you can use the CB_GETCOMBOBOXINFO message or the GetComboBoxInfo() function. The HWND of the Edit control is returned in the COMBOBOXINFO.hwndItem field.
If you are looking for a child window of a parent you should use EnumChildWindows. The following is C++ code but could be pinvoked easily: you can marshal delegates as function pointers for the callback.
std::vector<HWND> FindChildrenByClass(HWND parent, const std::string& target_class)
{
struct EnumWndParam {
std::vector<HWND> output;
std::string target;
} enum_param;
enum_param.target = target_class;
EnumChildWindows(
parent,
[](HWND wnd, LPARAM lparam) -> BOOL {
auto param = reinterpret_cast<EnumWndParam*>(lparam);
char class_name[512];
GetClassName(wnd, class_name, 512);
if (param->target == class_name)
param->output.push_back(wnd);
return TRUE;
},
reinterpret_cast<LPARAM>(&enum_param)
);
return enum_param.output;
}
int main()
{
auto windows = FindChildrenByClass( reinterpret_cast<HWND>(0x0061024A), "Edit");
for (auto wnd : windows) {
std::cout << std::hex << wnd << std::endl;
}
}
Note in the above that I do not recursively call FindChildrenByClass in the callback lambda. This is not a mistake. EnumChildWindows already performs this recursion. It runs over a parent window's children and grand children, etc., out of the box without you having to specify this behavior or implement it.
Like someone already assumed. Try the EnumChildWindow method.
Here is an complete sample already on stackoverflow

How can I determine when my application's console window gets or loses focus?

Is there a simple way to do this?
Or at the very least check if the console is currently in focus?
Imagine something like a game (thats not the case here but the analogy holds) - it would be useful if it could pause automatically. I need something similar.
If the window you were interested in were not a console window, this would have been very simple to do by just tapping into the appropriate focus event. But console windows don't have focus events, so the easy way out is not available here.
What you can do is set up an event handler to receive WinEvents generated by the UI Automation services. An event is generated whenever the window focus changes; you can get the HWND of the newly focused window and compare it to that of your console window. If they match, you just got focus; if they don't, you don't have focus (either just lost it or never had it to begin with).
The most convenient way to tap into UI Automation is through the System.Windows.Automation namespace. You can set up the event handler with AddAutomationFocusChangedEventHandler, which will give you an instance of AutomationFocusChangedEventArgs from which you can determine which window has received focus.
Here's some sample code:
AutomationFocusChangedEventHandler focusHandler = OnFocusChange;
Automation.AddAutomationFocusChangedEventHandler(focusHandler);
MessageBox.Show("Listening to focus changes");
Automation.RemoveAutomationFocusChangedEventHandler(focusHandler);
where OnFocusChange is:
void OnFocusChange(object source, AutomationFocusChangedEventArgs e)
{
var focusedHandle = new IntPtr(AutomationElement.FocusedElement.Current.NativeWindowHandle);
var myConsoleHandle = Process.GetCurrentProcess().MainWindowHandle;
if (focusedHandle == myConsoleHandle)
{
// ...
}
}
Note that I am assuming the console is your process's main window for simplicity; if that's not the case, you need to get a HWND to the console window some other way.
Also note that in order to receive automation events, your process must be running a message loop (in this case also known as a "dispatcher loop"), which in turn requires a thread being dedicated to running it. In the example above this happens automatically when MessageBox.Show is called, but in the general case you will have to take proper care of it.
I can't add a comment so I'm just going to have to post an answer. You can test the theory posted by DJ KRAZE like this:
/// <summary>Returns true if the current application has focus, false otherwise</summary>
public static bool ApplicationIsActivated()
{
var activatedHandle = GetForegroundWindow();
if (activatedHandle == IntPtr.Zero) {
return false; // No window is currently activated
}else{
Console.WriteLine("Application is focused!");
}
var procId = Process.GetCurrentProcess().Id;
int activeProcId;
GetWindowThreadProcessId(activatedHandle, out activeProcId);
return activeProcId == procId;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
So if it doesn't return false, then it will print "Application is focused!" in the application. You can always add something where "return false" is to do something when it's not focused as well.

Reading c++ window in C# using handler

I am implementing a plugin for a product called AmiBroker in C#.
AmiBroker is a trading software it has exposed a few functions which can be used by 3rd party vendors to pass the stock data to solution. So, we can create a plugin in C# which can be recognized by AmiBroker.
In my scenario I am getting a handler of Main Window of AmiBroker [Note : AmiBroker is fully written in C++] In C# we can retrieve the handler of Main Window, so using this handle can I read the data of the window e.g. Child windows, Panels showing stock lists or things which are visible to the User, and if so, how would I go about doing this?
You can, but it's messy. I literally just worked on something very similar. Pinvoke.net is great for this stuff, but I'll show you some examples of how I'd find controls. If AmiBroker has any documentation for control names or AccessibleNames or anything that allows you to find the exact controls you're looking for, that'd be killer. Because if they're ambiguously named, you're gonna have a helluva time finding the ones you're specifically looking for. But basically, what you'll want to do is EnumChildWindows on the handle you have, iterate through them and look for a unique property to allow you to find the control you want. Then you'll need to execute a specific SendMessage to get the text off of a control (GetWindowText or whatever it's called only works for labels). Code as follows, adapted or swiped from Pinvoke.net at some point (great starting point):
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
public static extern uint GetClassName(IntPtr handle, StringBuilder name, int maxLength);
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
private 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);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
//THIS IS THE ONE YOU'LL CALL!
public static IntPtr GetWindowByClass(IntPtr mainWindow, string name)
{
List<IntPtr> windows = GetChildWindows(mainWindow);
foreach (IntPtr window in windows)
{
StringBuilder response = new StringBuilder();
response.Capacity = 500;
if (GetClassName(window, response, response.Capacity) > 0)
if (response.ToString() == name)
return window;
}
return IntPtr.Zero;
}
So basically it iterates through a whole set of child windows for the handle you have on the app, sees if the class name matches a control you're looking for, then returns it. There are thousands of ways to improve it (search for all the ones you want in a single shot, FindWindow MAY work by class name, etc.) but I wanted to show you more how it's done, not declare this is how it should be done. Finally, the call to get the text from the window/control is as follows (also adapted from pinvoke.net: look under User32.dll for all this stuff):
public static string GetText(IntPtr control)
{
StringBuilder builder = new StringBuilder(40);
IntPtr result = IntPtr.Zero;
uint response = SendMessageTimeoutText(control, 0xd, 40, builder, APITypes.SendMessageTimeoutFlags.SMTO_NORMAL, 2000, out result);
return builder.ToString();
}
[DllImport("user32.dll", EntryPoint = "SendMessageTimeout", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint SendMessageTimeoutText(
IntPtr hWnd,
int Msg, // Use WM_GETTEXT
int countOfChars,
StringBuilder text,
APITypes.SendMessageTimeoutFlags flags,
uint uTImeoutj,
out IntPtr result);
[Flags]
public enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0,
SMTO_BLOCK = 0x1,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x8
}
EDIT: An addendum: the application I worked on to access another form like this actually didn't have unique control names, so I ended up using Spy++ to determine its place in the window hierarchy and pulling the children and selecting each child in turn. God help if you have to go that route, especially because it may not be consistent at all, especially if what you need is on a form that isn't created, or it's hidden behind another one that jumped it in the Z-Order (breaking your hierarchical list you're searching from). That said, you should know that EnumChildWindows will always enum ALL CHILD WINDOWS for a given window, no matter where they are in the hierarchy. If you really have to drill down and search for each control by its parent and its parent's parent, you'll need to use FindWindowEx, and declare the last child you looked at (or IntPtr.Zero if you want the first child):
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
What you are asking for seems counter intuitive if the software you are writing the plug-in for provides an API. You should really be using that.
While it is possible to use the Win32 API to enumerate child windows of the main window given the handle and then use more Win32 API functions to determine the state of the UI (i.e. "read the data") it's going to be very tedious and error-prone.
Here's a link to MSDN for EnumChildWindows which will allow you to enumerate child windows for the main window given the handle.
If you want to go down that rabbit hole you might also find SendMessage and GetWindowText useful. And most definitely you should check out pinvoke.net if you are going to be using these Win32 APIs from C#.

Subclassing a window from a thread in c#

I'm creating a thread that looks for a window. When it finds the window, it overrides its windowproc, and handles WM_COMMAND and WM_CLOSE.
Here's the code that looks for the window and subclasses it:
public void DetectFileDialogProc()
{
Window fileDialog = null;
// try to find the dialog twice, with a delay of 500 ms each time
for (int attempts = 0; fileDialog == null && attempts < 2; attempts++)
{
// FindDialogs enumerates all windows of class #32770 via an EnumWindowProc
foreach (Window wnd in FindDialogs(500))
{
IntPtr parent = NativeMethods.User32.GetParent(wnd.Handle);
if (parent != IntPtr.Zero)
{
// we're looking for a dialog whose parent is a dialog as well
Window parentWindow = new Window(parent);
if (parentWindow.ClassName == NativeMethods.SystemWindowClasses.Dialog)
{
fileDialog = wnd;
break;
}
}
}
}
// if we found the dialog
if (fileDialog != null)
{
OldWinProc = NativeMethods.User32.GetWindowLong(fileDialog.Handle, NativeMethods.GWL_WNDPROC);
NativeMethods.User32.SetWindowLong(fileDialog.Handle, NativeMethods.GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(new WindowProc(WndProc)).ToInt32());
}
}
And the windowproc:
public IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
lock (this)
{
if (!handled)
{
if (msg == NativeMethods.WM_COMMAND || msg == NativeMethods.WM_CLOSE)
{
// adding to a list. i never access the window via the hwnd from this list, i just treat it as a number
_addDescriptor(hWnd);
handled = true;
}
}
}
return NativeMethods.User32.CallWindowProc(OldWinProc, hWnd, msg, wParam, lParam);
}
This all works well under normal conditions. But I am seeing two instances of bad behavior in order of badness:
If I do not close the dialog within a minute or so, the app crashes. Is this because the thread is getting garbage collected? This would kind of make sense, as far as GC can tell the thread is done? If this is the case, (and I don't know that it is), how can I make the thread stay around as long as the dialog is around?
If I immediately close the dialog with the 'X' button (WM_CLOSE) the app crashes. I believe its crashing in the windowproc, but I can't get a breakpoint in there. I'm getting an AccessViolationException, The exception says "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." Its a race condition, but of what I don't know. FYI, I had been reseting the old windowproc once I processed the commands, but that was crashing even more often!
Any ideas on how I can solve these issues?
Two points of observation that I can make....
In your DetectFileDialogProc, you are comparing wnd to null, that is an IntPtr type yes? if so, that check for the comparison should be if (wnd > IntPtr.Zero){ .... }
In your WndProc, you are using the this variable for the lock which is a bad thing to do...you should do something like this private readonly object objLock = new object(); and within your WndProc use this lock (objLock){....}
and see if that resolves the issue....
Finally came up with a solution, attacking the problem from a different angle. I was able to set a system-wide hook in managed code using SetWinEventHook, and the option WINEVENT_OUTOFCONTEXT, which amazingly has the property: The callback function is not mapped into the address space of the process that generates the event.
I trap the event EVENT_SYSTEM_DIALOGSTART to receive notifications whenever a dialog is created, and EVENT_SYSTEM_DIALOGEND when its destroyed.

Open another application's System Menu

I have a WPF TaskBar like application that i am developing for fun and i want to be able to open the System Menu for the application (that is in my custom taskbar) that i right click on, preserving any custom menu that the app might create (i.e. Google Chrome). I have the following code that works for a borderless window app i made previously, but it doesn't seem to work and i am wondering why? And what do i need to do to get it to work?
public static void ShowContextMenu(IntPtr hWnd)
{
SetForegroundWindow(hWnd);
IntPtr wMenu = GetSystemMenu(hWnd, false);
// Display the menu
uint command = TrackPopupMenuEx(wMenu,
TPM.LEFTBUTTON | TPM.RETURNCMD, 0, 0, hWnd, IntPtr.Zero);
if (command == 0)
return;
PostMessage(hWnd, WM.SYSCOMMAND, new IntPtr(command), IntPtr.Zero);
}
Tip: It appears that TrackPopupMenuEx(...) returns immediately with the value of 0, instead of waiting for a response...
It appears there is an issue with giving TrackPopupMenuEx the owner window handle of the Menu... instead i used the handle of my wpf window and then when posting the message, i used the Menu's owner... seems a bit strange to me but it works!
public static void ShowContextMenu(IntPtr hAppWnd, Window taskBar, System.Windows.Point pt)
{
WindowInteropHelper helper = new WindowInteropHelper(taskBar);
IntPtr callingTaskBarWindow = helper.Handle;
IntPtr wMenu = GetSystemMenu(hAppWnd, false);
// Display the menu
uint command = TrackPopupMenuEx(wMenu,
TPM.LEFTBUTTON | TPM.RETURNCMD, (int) pt.X, (int) pt.Y, callingTaskBarWindow, IntPtr.Zero);
if (command == 0)
return;
PostMessage(hAppWnd, WM.SYSCOMMAND, new IntPtr(command), IntPtr.Zero);
}

Categories

Resources