Get Explorer's children in batch - windows

I created folder shortcuts for my taskbar and I would like them to stop launching a new explorer every time
So I decided to create a batch script, howover I can not get the kids from explorer.exe
#echo off
pushd
tasklist /nh /fi "imagename eq explorer.exe C:\Users\danil\Desktop\ISO" | find /i "explorer.exe C:\Users\danil\Desktop\ISO" > nul ||(start explorer.exe C:\Users\danil\Desktop\ISO)

The issue with your attempt is that tasklist will list only one instance of explorer.exe but not the titles of each window openned.
With some edits over this I've created listWindows.bat - it will list all visible windows names and their coresponding executable. So you can try this:
call listWindows.bat|findstr /i /b /e "explorer::Downloads" >nul 2>nul || (
start "" explorer.exe "C:\Users\%username%\Downloads"
)
To check the windows you need to start you can just try this:
call listWindows.bat|findstr /i /b "explorer::"

You cannot check the opening folders by checking the command line options, because the arguments stay the same across the whole lifetime of the process even after you changed to some other folders in that window. You need to use scriptable shell objects to get the current address.
Here's a PowerShell script to open a folder if it's not already opened in explorer
$folder = 'C:\Users\danil\Desktop\ISO'
$folderOpened = $false
foreach ($w in (New-Object -ComObject Shell.Application).Windows()) {
if ($w.LocationURL -ieq ([uri]$folder).AbsoluteUri) {
$folderOpened = $true; break
}
}
if (-not $folderOpened) { Invoke-Item $folder } # or start $folder
Below is an equivalent hybrid batch-jscript snippet
#if (#CodeSection == #Batch) #then
#echo off
cscript //e:jscript //nologo "%~f0" %*
exit /b
#end
// JScript Section
var objShell = new ActiveXObject("shell.application");
var objShellWindows;
objShellWindows = objShell.Windows();
if (objShellWindows != null)
{
// the folder you want to open
var folder = "file:///C:/Users/danil/Desktop/ISO";
var folderOpened = 0;
for (var objEnum = new Enumerator(objShellWindows);
!objEnum.atEnd(); objEnum.moveNext())
{
if (folder == objEnum.item().LocationUrl)
{
folderOpened = 1;
break;
}
}
if (!folderOpened) // open the folder if it's not already opened
objShell.Explore(folder); // or objshell.Open(folder)
}
Each explorer window is represented by an InternetExplorer object that can be retrieved from the Shell.Windows() collection. You need to use a file URI scheme instead of a normal Windows path, but it works. Of course you can even further change it to switch to the folder window if it's being opened. You can also use VBS or any other languages that support scriptable shell objects
Update:
You can avoid the file URI scheme by changing objEnum.item().LocationUrl to objEnum.item().Document.Folder.Self.Path
In the PowerShell version above it means changing
if ($w.LocationURL -ieq ([uri]$folder).AbsoluteUri) {
to
if ($w.Document.Folder.Self.Path -ieq $folder) {

Related

Open file explorer at coordinates

When I boot up my computer I open several file explorers and sort them around the screen to help speed up my workflow. It's not time consuming, only tedious, and I'd like a small program to do it for me. I know how to open an explorer, but I don't see any positional arguments.
Is there a way to spawn a file explorer at a set of screen coordinates, or move it programatically after it opens? Preferably with python 3+, but batch will work as well.
That was simultaneously easier and harder than I thought it was going to be. Everything is commented, let me know if you have any more questions. This is a PowerShell/batch hybrid script (so save it as a .bat file) because PowerShell is disabled on systems by default or something.
<# :
:: Based on https://gist.github.com/coldnebo/1148334
:: Converted to a batch/powershell hybrid via http://www.dostips.com/forum/viewtopic.php?p=37780#p37780
:: Array comparison from http://stackoverflow.com/a/6368667/4158862
#echo off
setlocal
set "POWERSHELL_BAT_ARGS=%*"
if defined POWERSHELL_BAT_ARGS set "POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%"
endlocal & powershell -NoLogo -NoProfile -Command "$_ = $input; Invoke-Expression $( '$input = $_; $_ = \"\"; $args = #( &{ $args } %POWERSHELL_BAT_ARGS% );' + [String]::Join( [char]10, $( Get-Content \"%~f0\" ) ) )"
goto :EOF
#>
# Create an instance of the Win32 API object to handle and manipulate windows
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class Win32 {
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
}
"#
# Get a list of existing Explorer Windows
$previous_array = #()
$shell_object = New-Object -COM 'Shell.Application'
foreach($old_window in $shell_object.Windows())
{
$previous_array += $old_window.HWND
}
# Open four more Explorer Windows in the current directory
explorer
explorer
explorer
explorer
# Pause for 1 second so that the windows have time to finish opening
sleep 1
# Get the list of new Explorer Windows
$new_array = #()
foreach($new_window in $shell_object.Windows())
{
$new_array += $new_window.HWND
}
# Compare the two arrays and only process the new windows
$only_new = Compare-Object -ReferenceObject $previous_array -DifferenceObject $new_array -PassThru
# MoveWindow takes HWND value, X-position on screen, Y-position on screen, window width, and window height
# I've just hard-coded the values, adjust them to suit your needs
[Win32]::MoveWindow($only_new[0],0,0,960,540,$true)
[Win32]::MoveWindow($only_new[1],960,0,960,540,$true)
[Win32]::MoveWindow($only_new[2],0,540,960,540,$true)
[Win32]::MoveWindow($only_new[3],960,540,960,540,$true)

How to open a html file whenever it is modified?

I am faced with the issue of IIS express stopping abruptly without any stack trace while debugging in local.
I have found a work around for this by writing the stack trace to a html file.
string file = #"C:\Users\INLASKD\Desktop\ExceptionHandlerError.html";
using (FileStream fs = new FileStream(file, FileMode.Create))
{
using (StreamWriter w = new StreamWriter(fs, Encoding.UTF8))
{
w.WriteLine(sb.ToString());
}
}
Now, I want to open this ExceptionHandlerError.html automatically everytime its modified when IIS Express stops.
I want bat file or a script to automate this. How can I go about this?
Note: I am connected to a network that doesn't allow administrator access and can't access event viewer.
After some research, I was able to open the browser right after writing it to the file with
System.Diagnostics.Process.Start(file);
Final code is as below:
string file = #"C:\Users\iraacn-9ajm\Desktop\ExceptionHandlerError.html";
using (FileStream fs = new FileStream(file, FileMode.Create))
{
using (StreamWriter w = new StreamWriter(fs, Encoding.UTF8))
{
w.WriteLine(sb.ToString());
}
}
System.Diagnostics.Process.Start(file);
You should be able to use this:
#echo off
set "file=C:\Users\INLASKD\Desktop\ExceptionHandlerError.html"
:loop
attrib "%file%" | findstr /B /L A 1>nul
if %errorlevel% equ 0 (
::open the file here
start "" "%file%"
attrib -A "%file%"
)
timeout /t 2 /nobreak >nul
goto loop

How to close window with .bat script?

I have ran into a slight problem while configuring some keybinds with my mouse.
The thing I want to achieve is to open the program from the backround(open or closed window) and close it again, but not kill the task(eqivalent to the x-button, so it keeps running in the backround)
I have set up my mouse to start up a application called everything (which is able of very fast file system searches)
I have figured out that with this code:
TASKKILL /F /IM Everything.exe
which I can run with a macro key I can kill the application. But I dont want to kill the application itself just the window, becase it takes a while to index all the important files once I have to restart it.
So heres the question is there any way to bring up a window/task to the front of the screen or trigger the x button event, without having to kill the entire process ?
Also the Mouse(g600) supports lua scripting, but that is proabbly the more harder way to go about it.
Minimizing a window can be usually done by activating the window (bringing it to the foreground) then using SendKeys() to send Alt+Space, then whatever letter your locale has underlined for "Minimize" (N in English Windows). Here's one of possibly dozens of examples of this using VBScript.
That sequence is overused, inelegant, and boring in my opinion -- and it doesn't work for some windows (the cmd console, for example). It is possible to minimize a window without bringing it to the foreground, and without simulating key presses. One simply needs to import a function from user32.dll. And as long as we're going through that trouble, we might as well import a second function to detect the current window state so we can toggle minimized / restored.
We can import the functions using PowerShell like this. Save this Batch / PowerShell hybrid script with a .bat extension and give it a run.
<# : minimize.bat
:: toggles minimized state of a window by its filename
:: minimize.bat /? for usage
:: https://stackoverflow.com/a/34834953/1683264
#echo off & setlocal
if "%~1"=="" goto usage
set "prog=%~n1"
tasklist | findstr /i "\<%prog%\>" >NUL || goto usage
set /P "=Toggling the minimized state of %prog%... " <NUL
powershell -noprofile -noninteractive "iex ((gc \"%~f0\") -join \"`n\")"
goto :EOF
:usage
echo syntax: %~nx0 progname[.exe]
echo;
echo If the program is visible, minimize it. If minimized, restore it.
goto :EOF
:: end Batch / begin PowerShell hybrid chimera #>
Add-Type user32_dll #'
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
'# -namespace System
$hwnd = #(Get-Process $env:prog)[0].MainWindowHandle
$state = [user32_dll]::GetWindowLong($hwnd, -16)
# mask of 0x20000000 = minimized; 2 = minimize; 4 = restore
if ($state -band 0x20000000) { $action = 4 } else { $action = 2 }
if ([user32_dll]::ShowWindowAsync($hwnd, $action)) {
write-host "Success" -f green
} else {
write-host "Fail" -f red
}
Now, if your program minimizes to the systray, the previous script will be fine for minimizing, but won't be able to find the window handle when minimized. It will fail when trying to restore. Finding the HWND in this situation is tricky. If one knows the window title, one can find the HWND using the user32.dll FindWindow() or FindWindowEx() functions.
I haven't had much luck using WMI or built-in PowerShell commands for finding the title of a window minimized to the tray. Neither get-process nor gwmi win32_process nor [diagnostics.process]::getProcessByName() yielded success. I did, however, discover that the final line of tasklist /v /fo list will show the window title. That's enough to invoke FindWindow() and get the HWND.
This might be more complicated than it needs to be, but then again it might not. I just know I found 1,000 ways to fail, but one way to succeed. Thanks to JPBlanc for helping me figure out how to pass null arguments into FindWindow() and FindWindowEx().
<# : minimize.bat
:: toggles minimized state of a window by its filename
:: minimize.bat /? for usage
#echo off & setlocal
if "%~1"=="" goto usage
set "prog=%~n1"
tasklist | findstr /i "\<%prog%\>" >NUL || goto usage
set /P "=Toggling the minimized state of %prog%... " <NUL
powershell -noprofile -noninteractive "iex ((gc \"%~f0\") -join \"`n\")"
goto :EOF
:usage
echo syntax: %~nx0 progname[.exe]
echo;
echo If the program is visible, minimize it. If minimized, restore it.
goto :EOF
End batch / begin PowerShell hybrid chimera #>
Add-Type user32_dll #'
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(IntPtr lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter,
IntPtr lclassName, string windowTitle);
'# -namespace System
$hwnd = (ps $env:prog)[0].MainWindowHandle
if ($hwnd -eq 0) {
tasklist /v /fi "imagename eq $env:prog*" /fo list | %{
$title = $_ -replace '^[^:]+:\s+'
}
$zero = [IntPtr]::Zero
$hwnd = [user32_dll]::FindWindow($zero, $title)
if ($hwnd -eq 0) {
$hwnd = [user32_dll]::FindWindowEx($zero, $zero, $zero, $title)
}
}
$state = [user32_dll]::GetWindowLong($hwnd, -16)
# mask of 0x20000000 = minimized; 2 = minimize; 4 = restore
if ($state -band 0x20000000) { $action = 4 } else { $action = 2 }
if ([user32_dll]::ShowWindowAsync($hwnd, $action)) {
write-host "Success" -f green
} else {
write-host "Fail" -f red
}
can't comment so I'll try to answer.
the 'x' button will likely kill your application unless the application is configured otherwise I guess.
I think I understand you'd rather minimize it into icon tray or something. This is not possible as far as I know if your application isn't doing it naturally.
You could try to hit alt + space + n which is a shortcut to minimize the application.

How can I get the current active window at the time a batch script is run?

I have a batch script I want to run with hotkeys, and this script is supposed to make some actions in the active window (for example, creating a particular set of folders, or lowercase all names of the files inside the folder). So the script needs to refer to the active window when it's called.
I have tried to leave the "Start in" field of the alias empty, but echoing %cd% always print "C:\Windows\System32" instead of the current active window.
You can lookup which process got the window in foreground using pinvoke of user32.dll.
I've used this trick for system.window.forms.sendkeys method in a script:
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class Tricks {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}
"#
$a = [tricks]::GetForegroundWindow()
get-process | ? { $_.mainwindowhandle -eq $a } # in my case:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
161 7 13984 15820 91 9,75 7720 Console
For anyone looking for a non-Powershell solution, here's a batch script that uses cscript to invoke a block of JScript. The JScript creates a new child process, gets its PID, then walks up the ParentProcessID line of ancestors until it gets to explorer.exe, then returns the PID of the direct child. It ought to return the correct PID for the console window in which the script runs, even if there are multiple instances of cmd.exe or cscript.exe running.
What can I say? I was feeling creative today.
#if (#a==#b) #end /* JScript multiline comment
:: begin batch portion
#echo off
setlocal
for /f "delims=" %%I in ('cscript /nologo /e:Jscript "%~f0"') do (
echo PID of this console window is %%I
)
goto :EOF
:: end batch portion / begin JScript */
var oShell = WSH.CreateObject('wscript.shell'),
johnConnor = oShell.Exec('%comspec% /k #echo;');
// returns PID of the direct child of explorer.exe
function getTopPID(PID, child) {
var proc = GetObject("winmgmts:Win32_Process=" + PID);
// uncomment the following line to watch the script walk up the ancestor tree
// WSH.Echo(proc.name + ' has a PID of ' + PID);
return (proc.name == 'explorer.exe') ? child : getTopPID(proc.ParentProcessID, PID);
}
var PID = getTopPID(johnConnor.ProcessID);
johnConnor.Terminate();
// send the console window to the back for a second, then refocus, just to show off
oShell.SendKeys('%{ESC}');
WSH.Sleep(1000);
oShell.AppActivate(PID);
// output PID of console window
WSH.Echo(PID);

How can I move files to the Recycle Bin in a Windows batch script or Perl?

I've got a Windows XP batch script which cleans some directories, but I would like to move the deleted files to trash instead of using plain del. How is this done?
It looks like the only languages I can use for this is plain batch or Perl.
use Win32::FileOp qw(Recycle);
Recycle(#ARGV);
Write a VBS script (Original Link) then call it with MyDelScript.vbs
function main()
{
if (WScript.Arguments.length != 1)
{
WScript.Echo("<Insert informative error message here>");
return;
}
var Path = WScript.Arguments(0);
var Shell = WScript.CreateObject("Shell.Application");
var Item = Shell.Namespace(0).ParseName(Path);
Item.InvokeVerb("delete");
}
The Win32::FileOp module has a Recycle function. From the docs:
Recycle #filenames
Send the files into the recycle bin. You will not get any confirmation dialogs.
Returns true if successful.
It can be done like this with plain batch and embedded VBScript. Put the following code into a file called recycle.cmd:
<!-- : Begin batch script
#echo off
if "%1"=="" (
echo Usage: %~nx0 FILE_TO_RECYCLE[...]
echo This script puts files into the recycle bin
exit /b 1
)
cscript //nologo "%~f0?.wsf" %*
exit /b %errorlevel%
----- Begin embedded wsf script --->
<job><script language="VBScript">
Set app = WScript.CreateObject("Shell.Application")
Set fso = CreateObject("Scripting.FileSystemObject")
For Each arg In WScript.Arguments
If fso.FileExists(arg) Then
Set file = fso.GetFile(arg)
Set folderItem = app.Namespace(0).ParseName(file.Path)
folderItem.InvokeVerb("delete")
Else
WScript.Echo "File not found: " & arg
End If
Next
</script></job>
Example:
echo This file is dirt.> dirt.txt
echo This file is trash.> trash.txt
recycle dirt.txt trash.txt
As you can see the script allows recycling multiple files with one command.
It does not suppport the wildcards * and ? though.
The idea of embedding VBScript inside a batch file is taken from dbenham's answer to Is it possible to embed and execute VBScript within a batch file without using a temporary file? (scroll down to UPDATE 2014-04-27).
You could use the "recycle" utility which is part of CmdUtils from MaDdoG Software. From the page listing -
Recycle, a safe replacement for the DEL command, that sends files to the recycle bin instead of deleting them. Recycle is also more flexible than DEL; you can specify multiple files at once (or use wildcards), and you can recycle whole directories at once (be careful!)
I would suggest you try its various switches before you incorporate it into your script - there is quite a bit of deviation from the default behaviour of the "del" command.
UPDATE: Contrary to my original claim that the following code does not work, it indeed seems to work. I just forgot that the file I wanted to delete was not in $ENV{TEMP} but a subdirectory of $ENV{TEMP}. The problem is, the file does not go to the Recycle Bin.
The right solution is to use Win32::FileOp but I am going to leave this script here as an example of how to use Win32::API and Win32::API::Struct. I would appreciate it if anyone can point out what I am doing wrong. For your reference:
SHFileOperation: http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx
LPSHFILEOPSTRUCT: http://msdn.microsoft.com/en-us/library/bb759795(VS.85).aspx
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec::Functions qw( catfile );
use Win32::API;
Win32::API::Struct->typedef(
SHFILEOPSTRUCT => qw(
HWND hwnd;
UINT wFunc;
LPCTSTR pFrom;
LPCTSTR pTo;
FILEOP_FLAGS fFlags;
BOOL fAnyOperationsAborted;
LPVOID hNameMappings;
LPCTSTR lpszProgressTitle;
)
);
Win32::API->Import(
shell32 => q{ int SHFileOperation( LPSHFILEOPSTRUCT lpFileOp ) }
);
my $op = Win32::API::Struct->new( 'SHFILEOPSTRUCT' );
$op->{wFunc} = 0x0003; # FO_DELETE from ShellAPI.h
$op->{fFlags} = 0x0040; # FOF_ALLOWUNDO from ShellAPI.h
my $to_delete = catfile( $ENV{TEMP}, "test.file" );
$op->{pFrom} = $to_delete . "\0\0";
my $result = SHFileOperation( $op );
if ( $result ) {
warn sprintf "The operation failed: %4.4X\n", $result;
}
else {
if ( $op->{fAnyOperationsAborted} ) {
warn "Operation was aborted\n";
}
else {
warn "The operation succeeded\n";
}
}
__END__

Resources