Get program handle and window handle for media session - c#

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.

Related

How to determine whether the WebView2 DevTools are currently open?

The DevTools that come with WebView2 can either be opened by the user e.g. by pressing the F12 key, and they also can be opened programatically from code.
What I do not know is how to determine whether the DevTools are currently being opened.
I want to store the state (opened/closed) upon the exit of my WPF application so that I can show them automatically upon the next start of my WPF application.
My question
How to get the current showing/not showing state of the WebView2 Developer Tools?
Update 1
I found a dirty hack because of the fact that the window title of the DevTools looks like this:
DevTools - localhost:38472/my/url
Whereas localhost:38472/my/url is the currently loaded URL of the WebView2.
So I'm doing the following.
With the help of this SO answer, I was able to create this method:
public static IEnumerable<string> GetAllDesktopMainWindowTitles()
{
// https://stackoverflow.com/a/7268375/107625
var processlist = Process.GetProcesses();
foreach (var process in processlist)
{
if (!string.IsNullOrEmpty(process.MainWindowTitle))
{
yield return process.MainWindowTitle;
}
}
}
I then can use it like this:
public static bool AreDevToolsOpen(this WebView2? webView)
{
var url = webView?.Source?.ToString();
if (string.IsNullOrEmpty(url)) return false;
url = url.Replace(#"https://", string.Empty).Replace(#"http://", string.Empty);
var titles = GetAllDesktopMainWindowTitles().ToList();
var devToolsOpen = titles.Any(t => t.Contains(#"DevTools") && t.Contains(url));
return devToolsOpen;
}
While this is a total hack, it seems to work good enough for now.
Still, I'm looking for a better way to do it.

Check if process is running for current user in C#

I have a program in which I check if the program is already launched or not.
I use:
if (System.Diagnostics.Process.GetProcessesByName(System.IO.Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetEntryAssembly().Location)).Count() > 1) return;
The check works but the problem is that this doesn't work on a terminal server.
The reason is, is because I check if the process exist in the existing processes.
Example:
If user A is connected to a terminal server and runs program X, user B won't be able to launch the program (because the program X usage of user A will show up in the list)
My question is, how in C#, can I check if the program is already running under the context of the user?
I have discovered the following WMI-code that works in PowerShell, but the problem is that this doesn't work in C#
$owners = #{ }
gwmi win32_process |% {$owners[$_.handle] = $_.getowner().user}
$ps = get - process | select processname,Id,#{ l = "Owner"; e ={$owners[$_.id.tostring()]} }
foreach ($p in $ps) {
if ($p.Owner - eq $env: USERNAME) {
$p
}
}
Would there be a method by editing my existing method to allow this?
I tried to do:
Process[] runningProcesses = Process.GetProcesses();
var currentSessionID = Process.GetCurrentProcess().SessionId;
Process[] sameAsThisSession =
runningProcesses.Where(p => p.SessionId == currentSessionID).ToArray();
if (sameAsThisSession.Contains(System.Diagnostics.Process.GetCurrentProcess()))
{
MessageBox.Show("Program already running!");
}
But this doesn't work. (It does show the processes of the user only though).

Obtaining tab title/text using process ID

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;
}
}

2 processes with the same name. How do I only end one of them?

I have a small "problem" windows has a system file that cant be killed csrss.exe but if there is another file named csrss.exe in a different folder and it is running, how do I end its process? All i would know about the file is it's location and name/size.. basic file info.
lol i answered my question with a question.. anyway i just wanted to say I dont want to kill the process of the system file csrss, i want to kill the process of a file who happens to have the same filename & process name. is it even possible?
If you would like to end a process based on its FileInfo such as Size/Attributes/etc.... Then, this is possible! Here's a simple example
Example
int FileSize = 0; //This belongs to the process you would like to terminate
//Create the process csrss for every process with the name csrss
foreach (Process csrss in Process.GetProcessesByName("csrss"))
{
FileInfo csrssFile = new FileInfo(csrss.StartInfo.FileName); //Get the properties of its file name
if (csrssFile.Length == FileSize)
{
csrss.Kill(); //Terminate the process based on its file length (size)
}
}
Another Example
int FileSize = 0;
Process[] RunningProcesses = Process.GetProcesses();
foreach (Process csrss in RunningProcesses)
{
if (csrss.ProcessName == "csrss")
{
FileInfo csrssFile = new FileInfo(csrss.StartInfo.FileName);
if (csrssFile.Length == FileSize)
{
csrss.Kill();
}
}
}
Thanks,
I hope you find this helpful :)
I'm not saying should kill the csrss.exe process, but you could check the main ProcessModule.FileName property that contains the full path to the executable if you want to find specific processes with the same name:
Process process = // Get your process here
process.Modules[0].FileName == "C:\\MyPath\csrss.exe";

How to get the URL of the Internet explorer tabs with PID of each tab?

I have my application which triggers Web Browser with specific URL .
After my program ends i want to close the web pages/tabs which i have opened..
By calling an EXE file with parameters a. Process Name b. String present in the URL
Detailed problem
How to kill firefox child process/tab from Java/C++
I used C# approach ...
I am able to find the process ID of all the tabs..
foreach (Process theprocess in processlist) {
if (theprocess.ProcessName == "iexplore") {
Console.WriteLine("Process: {0}\tID: {1}\tWindow name: {2}",
theprocess.ProcessName, theprocess.Id, theprocess.MainWindowTitle
);
}
}
Currently i can get only Window Title of the process....and in IE8 only one window title of main process is visible..
Provided i have the pids of each tabs,How to find the URL of the tab ...and kill only that tab ??
I got this help from
Access is denied - when trying to get the url (text) from address bar's handle
using SHDocVw;
.
.
foreach (InternetExplorer ieInst in new ShellWindowsClass())
Console.WriteLine(ieInst.LocationURL);
In IE7 and later versions, below code will kill only the tab which has matching string in its URL.
foreach (SHDocVw.InternetExplorer ieInst in new SHDocVw.ShellWindows())
{
String url = ieInst.LocationURL;
if (url.Contains("google"))
{
ieInst.Quit();
}
}
To focus a specific tab the code is :
foreach (SHDocVw.InternetExplorer ieInst in new SHDocVw.ShellWindows())
{
String url = ieInst.LocationURL;
if (url.Contains("google"))
{
int val = ieInst.HWND;
IntPtr hwnd = new IntPtr(val);
ShowWindow(hwnd, SW_MAXIMISE);
SetForegroundWindow(hwnd);
}
}
There is a way get the URL of each IExplorer instance !!
Add a reference "Microsoft Internet Controls" to the project.
The piece of code is
**foreach (SHDocVw.InternetExplorer ieInst in new SHDocVw.ShellWindowsClass())
{
System.Console.WriteLine(ieInst.LocationURL);
}**
Generate the exe and Interop.SHDocVw.dll
It will work ...:)

Categories

Resources