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.
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.
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).
I have an Electron app that spawns a C# app. The C# app wants to fetch the Electron BrowserWindow's MainWindowHandle, but it always returns IntPtr.Zero, and I don't know why.
The docs say:
You must use the Refresh method to refresh the Process object to get the current main window handle if it has changed.
If the associated process does not have a main window, the MainWindowHandle value is zero. The value is also zero for processes that have been hidden, that is, processes that are not visible in the taskbar.
My C# app runs Refresh just in case, and my Electron window is definitely visible, and I see the icon in the taskbar:
My Electron code launches my C# app and sends it the renderer process' pid (you can download the electron-quick-start app and make the following changes to reproduce):
const mainWindow = new BrowserWindow({width: 800, height: 600, show: false});
mainWindow.once("ready-to-show", () => {
mainWindow.show();
});
mainWindow.once("show", () => {
// by now, our window should have launched, and we should have a pid for it
const windowPid = mainWindow.webContents.getOSProcessId();
const proc = cp.spawn("my/exeFile.exe");
// send the pid to the C# process
const buff = Buffer.allocUnsafe(4);
buff.writeIntLE(windowPid, 0, 4);
proc.stdin.write(buff);
});
And the C# process starts and joins a thread with an infinite loop that reads that pid and tries to get its main window handle:
byte[] buffer = new byte[4];
inStream.Read(buffer, 0, 4);
int pid = BitConverter.ToInt32(buffer, 0); // I've verified that the pid I'm sending is the pid I'm getting
Process proc = Process.GetProcessById(pid);
proc.Refresh(); // just in case
IntPtr windowHandler = proc.MainWindowHandle; // 0x00000000
IntPtr handle = proc.Handle; // 0x000004b8
Am I sending the right electron pid over? I don't see which other pid I can use. The main process pid doesn't seem right, so all I'm left with is the renderer pid, which is what I'm using.
Should I expect MainWindowHandle to be set when the window is an Electron/Chromium window, or does this only work for C# windows?
There's a BrowserWindow API for this:
win.getNativeWindowHandle()
which return the HWND you can use in any native windows code
In your case I guess you can use it like this:
byte[] bytes = new byte[8];
for (int i = 0; i < data.Length; i++) {
object item = data[i];
bytes[i] = (byte)(int)item;
}
return BitConverter.ToUInt64(bytes, 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;
}
}