Close open Explorer windows without terminating explorer.exe - c#

I've tried searching but nothing really matches my demand.
I don't want explorer.exe to be terminated or restarted.
I just want any open explorer windows to close.

[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr lpdwProcessId);
[DllImport("user32.dll")]
private static extern uint RealGetWindowClass(IntPtr hwnd, StringBuilder pszType, uint cchType);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
static uint WM_CLOSE = 0x10;
private delegate bool EnumWindowsDelegate(IntPtr hwnd, IntPtr lParam);
private static bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam)
{
IntPtr pid = new IntPtr();
GetWindowThreadProcessId(hwnd, out pid);
var wndProcess = System.Diagnostics.Process.GetProcessById(pid.ToInt32());
var wndClass = new StringBuilder(255);
RealGetWindowClass(hwnd, wndClass, 255);
if (wndProcess.ProcessName == "explorer" && wndClass.ToString() == "CabinetWClass")
{
//hello file explorer window...
SendMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); // ... bye file explorer window
}
return (true);
}
static void Main()
{
EnumWindowsDelegate childProc = new EnumWindowsDelegate(EnumWindowsCallback);
EnumWindows(childProc, IntPtr.Zero);
Console.ReadKey();
}
edit:
so i guess the only interesting thing is the callback which will be called by windows for each enumerated window (handle of said window in hwnd)
GetWindowThreadProcessId provides us with the processid for a given window handle
GetProcessById then provides us with a process object to read things like the process name from
RealGetWindowClass provides us with the registered class name for a given window handle
finally we can look to see if the process for the current window is the explorer and if the window class is "CabinetWClass", which is the window class for the normal file explorer window
last but not least, if our check is ok, send a WM_CLOSE message to kindly ask the window to close itself...

The following alternative uses the COM API of the Shell object to retrieve and identify File Explorer windows. It requires the addition of the COM references to:
Microsoft Shell Controls And Automation
Microsoft Internet Controls
The object returned by Shell.Windows method is an IEnumerable. Each object in the collection is a SHDocVw.InternetExplorer instance. If the Document object is a Shell32.ShellFolderView, then the explorer is a File Explorer.
private static void CloseExplorerWindows()
{
Shell32.Shell shell = new Shell32.Shell();
// ref: Shell.Windows method
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb774107(v=vs.85).aspx
System.Collections.IEnumerable windows = shell.Windows() as System.Collections.IEnumerable;
if (windows != null)
{
// ref: ShellWindows object
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb773974(v=vs.85).aspx
foreach (SHDocVw.InternetExplorer window in windows)
{
object doc = window.Document;
if (doc != null && doc is Shell32.ShellFolderView)
{
window.Quit(); // closes the window
}
}
}
}

public static void CloseExplorerWindows() => EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
static uint WM_CLOSE = 0x10;
private static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
{
int size = GetWindowTextLength(hWnd);
if (size++ > 0 && IsWindowVisible(hWnd))
{
var sb = new StringBuilder(size);
GetWindowText(hWnd, sb, size);
var threadID = GetWindowThreadProcessId(hWnd, out var processID);
var s = System.Diagnostics.Process.GetProcessById((int)processID).ProcessName;
if (s == "explorer" && sb.ToString() != "Program Manager")
SendMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
return true;
}

By default, explorer runs as single process, and any windows that open are just a thread of the process.
Normally, to close a program, you'd send a close message to the process. In this case, closing explorer.exe will close all explorer windows.
To close individual windows, you'd open each window via it's own process.
This can be done via registry setting or enabling under View->Options->View->Advanced Settings: "Launch ... separate process"
a) Find PID (process ID) of window you wanna close.
via taskmanager:
1. In list of processes, click the arrow to the left of "Windows Explorer"
2. Check the window name matches the window you wanna close
3. Right click on "Windows Explorer", click "Go to Details"
4. Record the pid
via CMD:
tasklist /V /FI "IMAGENAME eq explorer.exe"
If each explorer window is open in it's own process, the above command would display the window title in the last column.
Otherwise "N/A" would be displayed.
The pid of all explorer windows would be the same. Explorer.exe processes have their own pid, and title "N/A"
If 'separate process' has been enabled eg. via Folder View option, then each window can be closed via the process id & filter option of taskkill.
To close, the desired window has to be activated first, otherwise closing with pid will close the last active window, or closing with window title filter will give error:
INFO: No tasks running with the specified criteria.
b) taskkill /pid <pid>
will close the last active window.
Repeating this command will the next window.
or taskkill /im explorer.exe /fi "windowtitle eq <window name>"
or taskkill /fi "IMAGENAME eq explorer.exe" /fi "windowtitle eq <window name>"
< window name > is not case sensitive
If full path in title bar has been enabled in Folder view, then include full path or wildcards.
To close all explorer windows:
taskkill /im explorer.exe
Notes:
To activate explorer window, issue same command to open the window, if window reusing is enabled.
The pid of explorer window(ing) process is in the last row of the response table, in column "PID"; can be accessed via FOR loop.
A vbs workaround to close window from #HelpingHand: https://superuser.com/questions/1263315/how-to-close-a-particular-opened-folder-using-cmd-or-batch-file
A vbs workaround to activate window: http://superuser.com/questions/327676/application-to-automatically-switch-between-two-applications-in-windows
Tested on Win 10

Related

How to kill a Shell executed process in c#

I want to display a tiff file using shell execute. I am assuming the default app is the photoviewer. My Problem is that when i want to kill the process with photoviewer.Kill() i get an System.InvalidOperationException. When setting a breakpoint after photoViewer.Start() i realised that photoviewer does not conatain an Id.
I there a sufficent way to kill it? As it runs via dllhost.exe i do not want to retrun all processes named dllhost and kill them all since i do not know what else is run by dllhost.
Process photoViewer = new Process();
private void StartProcessUsingShellExecute(string filePath)
{
photoViewer.StartInfo = new ProcessStartInfo(filePath);
photoViewer.StartInfo.UseShellExecute = true;
photoViewer.Start();
}
I have another approach without shell execute but this approach seems to have dpi issues.
Approach without shell execute
Found a solution, may help anyone with a similar problem.
When i looked into the task manager i found that the windows 10 photoviewer runs detached from the application via dllhost. So since i have 4 dllhost processes up and running and just want to close the window. I do:
private void StartProcessAsShellExecute(string filePath)
{
photoViewer.StartInfo = new ProcessStartInfo(filePath);
photoViewer.StartInfo.UseShellExecute = true;
photoViewer.Start();
Process[] processes = Process.GetProcessesByName("dllhost");
foreach (Process p in processes)
{
IntPtr windowHandle = p.MainWindowHandle;
CloseWindow(windowHandle);
// do something with windowHandle
}
viewerOpen = true;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
//I'd double check this constant, just in case
static uint WM_CLOSE = 0x10;
public void CloseWindow(IntPtr hWindow)
{
SendMessage(hWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
that closes all dllhost windows (of which i have just 1, the photoviewer)

C# How do I get unique PID for each browser tab in external browser ie. Chrome/Firefox/IE

When opening Windows Task Manager to view the PID of all open processes, I can see that all the open tabs in my Chrome browser have their own unique PID, however I get the same PID no matter what tab is in focus when using:
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
IntPtr hwnd = GetForegroundWindow();
GetWindowThreadProcessId(hwnd, out pid);
Does anyone know how to uniquely identify browser tabs? And even further, does anyone know how to catch an event when the browser tabs change state or focus? Any browser - Chrome, Firefox, IE
Below is my full code catching the WinEventHook event when the foreground window changes:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
namespace focusWindow2
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(SystemEvents eventMin, SystemEvents eventMax, IntPtr hmodWinEventProc,
SystemEventHandler lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
delegate void SystemEventHandler(IntPtr hWinEventHook, SystemEvents #event, IntPtr hwnd, int idObject, int idChild,
uint dwEventThread, uint dwmsEventTime);
enum SystemEvents
{
ForegroundWindowChanged = 0x3 // EVENT_SYSTEM_FOREGROUND - The only flag we care about
}
IntPtr _WinEventHook;
SystemEventHandler _WinEventHookHandler;
public Form1()
{
InitializeComponent();
// Create the handler
_WinEventHookHandler = new SystemEventHandler(WinEventHook);
// Set the hook
_WinEventHook = SetWinEventHook(SystemEvents.ForegroundWindowChanged, SystemEvents.ForegroundWindowChanged,
IntPtr.Zero, _WinEventHookHandler, 0, 0, 0);
this.FormClosing += delegate
{
UnhookWinEvent(_WinEventHook);
};
}
private void WinEventHook(IntPtr hWinEventHook, SystemEvents #event, IntPtr hwnd, int idObject, int idChild,
uint dwEventThread, uint dwmsEventTime)
{
uint pid = 0;
GetWindowThreadProcessId(hwnd, out pid);
Process p = Process.GetProcessById((int)pid);
string procName = Path.GetFileName(p.ProcessName);
textBox1.Text += procName + " " + pid + "\r\n";
}
}
}
You aren't going to be able to do what you want to - at least not in a reliable way.
The browsers are implemented differently. FireFox and older IE versions use a one-process model, IE9 is multiprocess but groups several tabs per process for performance reasons, and Chrome does one process per tab. Other browsers/browser versions do whatever they want (but most likely single-process). You'd have to write specific code for the browsers you want to support, and you'd be dealing with pretty deep-down implementation details.
For Chrome, iirc the model is one "host window" that handles the rendering. The processes for handling each tab are "worker processes", with communication back and forth between the host and workers... If Chrome doesn't have an official API for getting PIDs for the worker processes, then don't bother :)

Suppress Process Popup in .NET

Our user panel runs all of our software as services. For whatever reason though, the Mumble voice software creates a popup when you run it with an administrator password set in the command line. Someone posted an alternative by using a bat file that runs two processes as a work around, but is there any way to just suppress the popup message using .NET? I have written a lot of launcher type apps to fix things like this, but I have no idea how I could suppress this message.
Here is what the .bat file looks like from the workaround.
set /p VAR= < superadmin.txt
start murmur2.exe -supw %var%
ping 0.0.0.0 -n 3 > NUL
tskill murmur2
murmur.exe
You can try to monitor open windows and close popup via winapi when it shows up
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
public const int WM_COMMAND = 0x0112;
public const int WM_CLOSE = 0xF060;
int handle = FindWindow(lpClassName, lpWindowName);
SendMessage(handle, WM_COMMAND, WM_CLOSE, 0);

How to get the name of an External window in C# Application?

i've developed a simple application (.dll) in LABVIEW and i implorted that dll to a C# windows application(Winforms) . Like
[DllImport(#".\sample.dll")]
public static extern void MyFunc(char[] a, StringBuilder b ,Int32 c);
so when i call the function MyFunc a window will be popped up( the Lab View window( Front panel of my labview application
i need to get the window name (ExpectedFuncName) in my C# application. i.e i need to get the name of the external window which is opend by my C# application. Can we use FileVersionInfo or assembly loader to get the name?
Is there any idea to do this?
Thanks in advance.
If you have the window handle, this is relatively easy:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
...
int len;
// Window caption
if ((len = GetWindowTextLength(WindowHandle)) > 0) {
sb = new StringBuilder(len + 1);
if (GetWindowText(WindowHandle, sb, sb.Capacity) == 0)
throw new Exception(String.Format("unable to obtain window caption, error code {0}", Marshal.GetLastWin32Error()));
Caption = sb.ToString();
}
Here, 'WindowHandle' is the handle of the created window.
In the case you do not have a window handle (I see you don't), you have to enumerate every desktop top-level window, filter them by the creating process (I see the window is created by you application by calling MyFunc, so you know the process ID [*]), and then use some heuristic to determine the required information.
Here is the C# import of the functions you shall use in the case you do not have the handle:
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
Basically EnumWindows calls EnumWindowsProc for each window found in the current desktop. So you can get the window caption.
List<string> WindowLabels = new List<string>();
string GetWindowCaption(IntPtr hWnd) { ... }
bool MyEnumWindowsProc(IntPtr hWnd, IntPtr lParam) {
int pid;
GetWindowThreadProcessId(hWnd, out pid);
if (pid == Process.GetCurrentProcess().Id) {
// Window created by this process -- Starts heuristic
string caption = GetWindowCaption(hWnd);
if (caption != "MyKnownMainWindowCaption") {
WindowLabels.Add(caption);
}
}
return (true);
}
void DetectWindowCaptions() {
EnumWindows(MyEnumWindowsProc, IntPtr.Zero);
foreach (string s in WindowLabels) {
Console.WriteLine(s);
}
}
[*] In the case the window is not created by your application (i.e but from another background process), you shall filter the values returned by GetWindowThreadProcessId using another process ID, but this requires another question...
If you activate LabVIEW scripting (LabVIEW 2010), or install it (LV 8.6, 2009) there is a front-panel property called 'FP.nativewindow'. This returns a handle to the front panel window.
Use the following snippet to get the property:

How to use WM_Close in C#?

Can anyone provide me an example of how to use WM_CLOSE to close a small application like Notepad?
Provided you already have a handle to send to.
...Some Class...
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
//I'd double check this constant, just in case
static uint WM_CLOSE = 0x10;
public void CloseWindow(IntPtr hWindow)
{
SendMessage(hWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
...Continue Class...
Getting a handle can be tricky. Control descendant classes (WinForms, basically) have Handle's, and you can enumerate all top-level windows with EnumWindows (which requires more advanced p/invoke, though only slightly).
Suppose you want to close notepad. the following code will do it:
private void CloseNotepad(){
string proc = "NOTEPAD";
Process[] processes = Process.GetProcesses();
var pc = from p in processes
where p.ProcessName.ToUpper().Contains(proc)
select p;
foreach (var item in pc)
{
item.CloseMainWindow();
}
}
Considerations:
If the notepad has some unsaved text it will popup "Do you want to save....?" dialog or if the process has no UI it throws following exception
'item.CloseMainWindow()' threw an exception of type
'System.InvalidOperationException' base {System.SystemException}:
{"No process is associated with this object."}
If you want to force close process immediately please replace
item.CloseMainWindow()
with
item.Kill();
If you want to go PInvoke way you can use handle from selected item.
item.Handle; //this will return IntPtr object containing handle of process.

Categories

Resources