Using SPY++ I got window handle and using it I can get text control value from my code, but when I trying to find this window handle by application parent handle and control id through GetDlgItem(handle, ButtonId) function it throws error.
const int TextCtrlId = 0x0000E910;
IntPtr handle = IntPtr.Zero;
Process[] localAll = Process.GetProcesses();
foreach (Process p in localAll)
{
if (p.MainWindowHandle != IntPtr.Zero)
{
ProcessModule pm = GetModule(p);
if (pm != null && p.MainModule.FileName == fn)
handle = p.MainWindowHandle;
}
}
if (handle == IntPtr.Zero)
{
Console.WriteLine("Not found");
return;
}
GetText getText = new GetText();
Console.WriteLine("{0:X}", handle);
IntPtr hWndButton = GetDlgItem(handle, TextCtrlId);
string myText = getText.GetControlText(hWndButton);
Console.WriteLine("Error Code = {0}", GetLastError());
Console.WriteLine("Iput Window Text !!! = {0}", myText);
error code is
ERROR_CONTROL_ID_NOT_FOUND
1421 (0x58D)
Control ID not found.
but when I use direct handle (got it from SPY++) from function GetDlgItem(handle) I got same control id which in SPY++.
Control ID is different from Windows Handle. What you have is a Windows Handle (HWND).
Historically you could call GetWindowText() providing the handle to get the text of the control. But limitations have been imposed on this call (Since Windows XP if I remember well) for security reasons to counter password sniffing. Modern control inspection software depend on advanced techniques such as DLL injection to access controls in other processes with no restrictions (inject code in target process to read the control).
Related
I want to get information about all currently active media playbacks. I found a way to read out all media sessions here:
using Windows.Media.Control;
namespace ConsoleApp1 {
public class Program {
[STAThread]
static void Main(string[] args) {
var gsmtcsm = GlobalSystemMediaTransportControlsSessionManager.RequestAsync().GetAwaiter().GetResult().GetSessions();
foreach (var session in gsmtcsm) {
var mediaProperties = session.TryGetMediaPropertiesAsync().GetAwaiter().GetResult();
Console.WriteLine("{0} - {1}", mediaProperties.Artist, mediaProperties.Title);
}
Console.ReadKey();
}
}
}
Now I want to get the corresponding programs for these sessions. Also, I want to get the window of the program if it exists. My goal is to programmatically move the window if it exists to another screen. The program handle is just used as an identifier.
For example:
I open a random .mp4 file. By default, it is played by Windows Films & TV. Now I want to get the session, program, and window (Films & TV has a window) and move it to another screen (by code)
Another example:
I watch a video on Youtube. Now I want to get the window of the browser I opened Youtube in.
You can find the main window for the app that is running the media session with
var gsmtcsm = GlobalSystemMediaTransportControlsSessionManager.RequestAsync().GetAwaiter().GetResult().GetSessions();
foreach (var session in gsmtcsm)
{
string modelId = session.SourceAppUserModelId;
// Get all processes
Process[] processlist = Process.GetProcesses();
// Create and array to hold matched processes
List<Process> modelProcesslist = new List<Process>();
// Using a direct filter on the Process.GetProcesses() call will raise an exception, you will need to cycle through them
// to find a process that has the same filename as reported by the media session
// The filename may be different to the process name
foreach (Process p in processlist)
{
try
{
if (p.MainModule.FileName.Contains(modelId) && p.MainWindowHandle != IntPtr.Zero)
{
modelProcesslist.Add(p);
}
}
catch(System.Exception)
{
// Couldn't look at the MainModule of this process, move on
}
}
foreach (Process p in modelProcesslist)
{
IntPtr windowHandle = p.MainWindowHandle;
// The main window(s) for apps that have the same name as the source app for the media session
}
}
One big issue will be if the main window is owned by chrome.exe (or any tabbed browser), then the media player is in one of the sub tabs, finding content within a tab of the browser is a whole new level of headache.
Also if you have multiple browser windows open, they will all have the same executable name and get picked up by the above code.
If the app is a dedicated media player, you should be able to use the handle to move the window.
I am using Visual Studio 2017. I added two projects in a solution. One project is in C# with WPF. The other is in VC++ with ATL.
From C#, I call a function in the VC++ project, which sets low level mouse hook. A part of the code in the low level mouse proc is as follows:
MSLLHOOKSTRUCT stMouse = *(MSLLHOOKSTRUCT*)(lParam);
POINT pt = stMouse.pt;
IAccessible* pAcc;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromPoint(pt, &pAcc, &varChild);
VARIANT varRole;
hr = pAcc->get_accRole(varChild, &varRole);
When I test by clicking on a check box under View tab in MS Word 2013, then I get the role as client for WM_LBUTTONDOWN and WM_LBUTTONUP messages. But I should get the role as check box.
I checked with Inspect.exe that comes with Windows 10 SDK. Inspect.exe shows role correctly as check box. Inspect.exe has two options - one to see UI Automation properties and the other to see MSAA properties. I am seeing the MSAA properties.
How can I get the correct role? What method deos Inspect.exe uses?
Microsoft Active Accessibility / MSAA (based on the IAccessible interface) is a legacy API. You should now use Windows Automation. UIA is based on COM, and uses interfaces, the most important one being IUIAutomationElement. Inspect.exe uses UIA or MSAA.
Note .NET is compatible with UIA and WPF can expose its UI Elements to UIA pretty easily, through the AutomationPeer class and UIElement.OnCreateAutomationPeer Method method. WPF provides a default implementation that can be tweaked if needed.
Here is a similar example to yours in C++, using the UIA API, instead of MSAA (we could write the same using C#):
#include "stdafx.h" // needs <uiautomation.h>
class CCoInitialize { // https://blogs.msdn.microsoft.com/oldnewthing/20040520-00/?p=39243
public:
CCoInitialize() : m_hr(CoInitialize(NULL)) { }
~CCoInitialize() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
operator HRESULT() const { return m_hr; }
HRESULT m_hr;
};
// this is a poor-man COM object class
class CHandler : public IUIAutomationEventHandler
{
public:
HRESULT QueryInterface(REFIID riid, LPVOID * ppv)
{
if (!ppv) return E_INVALIDARG;
*ppv = NULL;
if (riid == IID_IUnknown || riid == IID_IUIAutomationEventHandler)
{
*ppv = this;
return NOERROR;
}
return E_NOINTERFACE;
}
ULONG AddRef() { return 1; }
ULONG Release() { return 1; }
// this will be called by UIA
HRESULT HandleAutomationEvent(IUIAutomationElement *sender, EVENTID eventId)
{
wprintf(L"Event id: %u\n", eventId);
return S_OK;
}
};
int main()
{
CCoInitialize init;
// this sample uses Visual Studio's ATL smart pointers, but it's not mandatory at all
CComPtr<IUIAutomation> uia;
if (SUCCEEDED(uia.CoCreateInstance(CLSID_CUIAutomation)))
{
// get mouse pos now
POINT pt;
GetCursorPos(&pt);
// find what type of "control" was under the mouse
CComPtr<IUIAutomationElement> element;
if (SUCCEEDED(uia->ElementFromPoint(pt, &element)))
{
CComBSTR type;
element->get_CurrentLocalizedControlType(&type);
wprintf(L"type at %u,%u: %s\n", pt.x, pt.y, type.m_str);
}
// get root
CComPtr<IUIAutomationElement> root;
if (SUCCEEDED(uia->GetRootElement(&root)))
{
// add a handler that will trigger every time you open any window on the desktop
// in the real world, you'll need to delete this pointer to avoid memory leaks of course
CHandler *pHandler = new CHandler();
if (SUCCEEDED(uia->AddAutomationEventHandler(UIA_Window_WindowOpenedEventId, root, TreeScope::TreeScope_Children, NULL, pHandler)))
{
// since this is a console app, we need to run the Windows message loop otherwise, you'll never get any UIA events
// for this sample, just press CTRL-C to stop the program. in the real world, you'll need to get out properly
// if you have a standard windows app, the loop will be already there
while (TRUE)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
}
return 0;
}
I don't want to use SetForegroundWindow(), sending keyboard keys or similar techniques, because that can cause issues (unexpected behaviour) in my software.
I have tried to find the title using Cheat Engine program (but haven't found anything useful as Google Chrome seems to work "upside-down").
So I went step ahead, using Process Hacker program I have realized that there is a parent (chrome.exe) process with a valid window handle to the current active tab and all other chrome processes are children of it a.k.a. background processes (with invalid window handle).
By browsing deeper into windows of chrome.exe (parent process), I have found the class name of the window handle being "Chrome_WidgetWin_1" and current active tab's title/text.
Here's a picture of Google Chrome's Task Manager.
I'm looking for a function in C# or C or C++ that will take an integer (process ID) and return a string (tab title/text).
static string GetChromeTabTitle(uint processId)
{
// Assuming I call this function with valid process identifier (PID).
// What do I do next, here??
}
The best way I have found is by using the System.Windows.Automation library. It allows interacting with an application (primarily for accessibility purposes), but you can use it for other purposes like getting Chrome tabs.
Note that this will only work when the Chrome windows is not minimized.
The process is not exactly simple, if you want you can look how I did it in my own project, though it's not something you can just copy it paste, you'll find what you need in the ChromeTabsFinder: https://github.com/christianrondeau/GoToWindow/blob/master/GoToWindow.Plugins.ExpandBrowsersTabs/Chrome/ChromeTabsFinder.cs
Here's the code (you'll need the automation librairies):
public IEnumerable<ITab> GetTabsOfWindow(IntPtr hWnd)
{
var cacheRequest = new CacheRequest();
cacheRequest.Add(AutomationElement.NameProperty);
cacheRequest.Add(AutomationElement.LocalizedControlTypeProperty);
cacheRequest.Add(SelectionItemPattern.Pattern);
cacheRequest.Add(SelectionItemPattern.SelectionContainerProperty);
cacheRequest.TreeScope = TreeScope.Element;
AutomationElement tabBarElement;
using (cacheRequest.Activate())
{
var chromeWindow = AutomationElement.FromHandle(hWnd);
var mainElement = chromeWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (mainElement == null)
yield break;
tabBarElement = mainElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "tab"));
}
if(tabBarElement == null)
yield break;
var tabElements = tabBarElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "tab item"));
for (var tabIndex = 0; tabIndex < tabElements.Count; tabIndex++)
{
yield return "Tab: " + tabElements[tabIndex].Current.Name + ", Index: " + tabIndex + 1;
}
}
I want to get the name of current application that it's running on windows.
For example, if i work with i.e, this function give "Internet explorer" or if i work with Google chrome, give Chrome...
System.AppDomain.CurrentDomain.FriendlyName
or maybe
System.Reflection.Assembly.GetExecutingAssembly()
or if you mean that you want the name of the active window then you should look at;
How do I get the title of the current active window using c#?
You can use Assembly.FullName
System.Reflection.Assembly.GetEntryAssembly().FullName;
Please also take a look at this question:
How do I get the name of the current executable in C#?
Try this, If you have used GetWindowThreadProcessId.
public String GetActiveFileNameTitle()
{
IntPtr hWnd = GetForegroundWindow();
uint procId = 0;
GetWindowThreadProcessId(hWnd, out procId);
var proc = Process.GetProcessById((int)procId);
if (proc != null)
{
return proc.MainModule.FileVersionInfo.ProductName;
}
}
I have the following code snippet
List<String> sensitiveApps = testConnection.SelectSensitive();
foreach (string sensitiveApp in sensitiveApps)
{
Console.Write(sensitiveApp);
// retrieve applications to minimize handle (connect to database and systematically minimize all applications?)
IntPtr hwnd = UnsafeNativeMethods.FindWindow(sensitiveApp, null);
StringBuilder stringBuilder = new StringBuilder(256);
UnsafeNativeMethods.GetWindowText(hwnd, stringBuilder, stringBuilder.Capacity);
Console.WriteLine(stringBuilder.ToString());
if (!hwnd.Equals(IntPtr.Zero))
{
// SW_SHOWMAXIMIZED to maximize the window
// SW_SHOWMINIMIZED to minimize the window
// SW_SHOWNORMAL to make the window be normal size
ShowWindowAsync(hwnd, SW_SHOWMINIMIZED);
}
}
Where sensitiveApps is a list containing the strings "Notepad", "Recuva" and "VLC media player 2.0.3".
However, the only application that can be minimized using this code is Notepad. Debugging the program finds that
Console.WriteLine(stringBuilder.ToString());
would not return any value for the last 2 programs, but would return a Untitled - Notepad.
Is there anything I'm doing wrong?
Try using Spy++ and check the FindWindow names are correct.
MS Word is OpusApp and VLC is QWidget.