Open file explorer at coordinates - windows

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)

Related

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 to auto-hide the taskbar from the command line

Does anyone know how I can automatically hide the task bar in windows 7 via command line or some other method?
To autohide the taskbar from a cmd prompt or in a .cmd or. bat file:
Windows 7 (StuckRects2)
powershell -command "&{$p='HKCU:SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2';$v=(Get-ItemProperty -Path $p).Settings;$v[8]=3;&Set-ItemProperty -Path $p -Name Settings -Value $v;&Stop-Process -f -ProcessName explorer}"
Windows 10 (StuckRects3)
powershell -command "&{$p='HKCU:SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StuckRects3';$v=(Get-ItemProperty -Path $p).Settings;$v[8]=3;&Set-ItemProperty -Path $p -Name Settings -Value $v;&Stop-Process -f -ProcessName explorer}"
Explanation
The registry key which stores this value also stores a number of other settings. Since we only want to change position 9 ($v[8] in the cmd) of that registry setting, we need to preserve the other settings.
Normally from cmd, it's enough to use a reg add command to modify the registry, but we use powershell because it makes it easy to preserve the other settings stored under the same registry key.
Explorer also needs to be restarted to pick up the change. We use Stop-Process because Windows automatically restarts Explorer when it is stopped.
Note: change $v[8]=3 to $v[8]=2 in the commands above to undo this change (if you want the taskbar to be always visible).
Here's a little C program that will toggle the hidden/shown state of the taskbar window. Note that when it's hidden it's actually gone from the screen completely (it's not in auto-hide mode).
#include <windows.h>
int main() {
HWND hwnd = FindWindow("Shell_traywnd", "");
if (IsWindowVisible(hwnd))
SetWindowPos(hwnd,0,0,0,0,0,SWP_HIDEWINDOW);
else
SetWindowPos(hwnd,0,0,0,0,0,SWP_SHOWWINDOW);
return 0;
}
Using SHAppBarMessage. This one toggles the autohide state.
#include <windows.h>
#include <shellapi.h>
// This isn't defined for me for some reason.
#ifndef ABM_SETSTATE
#define ABM_SETSTATE 0x0000000A
#endif
int main() {
APPBARDATA abd = {sizeof abd};
UINT uState = (UINT) SHAppBarMessage(ABM_GETSTATE, &abd);
LPARAM param = uState & ABS_ALWAYSONTOP;
if (uState & ABS_AUTOHIDE)
abd.lParam = param;
else
abd.lParam = ABS_AUTOHIDE | param;
SHAppBarMessage(ABM_SETSTATE, &abd);
return 0;
}

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 do I change the current Windows theme programmatically?

I want to allow my users to toggle the current user theme between Aero and Windows Classic(1). Is there a way that I can do this programatically?
I don't want to pop up the "Display properties", and I'm dubious about just changing the registry. (This requires a log out and a log back in for the changes to take effect).
Application skinning (using the Codejock libraries) doesn't work either.
Is there a way of doing this?
The application is hosted/run on a Windows Server 2008 over RDP.
(1) The application in question is a hosted "Remote App", and I want users to be able to change the look of the displayed application to match their desktop.
You can set it using the following command:
rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,#Themes /Action:OpenTheme /file:"C:\Windows\Resources\Themes\aero.theme"
Caveat is that this will show the theme selector dialog. You could kill that dialog straight after.
There are certainly good reasons for wanting to change the current theme programmatically. E.g. an automated test tool may need to switch between various themes to make sure the application works correctly with all of them.
As a user, you can change the theme by double-clicking a .theme file in Windwos Explorer and then closing the Control Panel applet that pops up. You can easily do the same from code. The steps below work just fine for me. I've only tested on Windows 7.
Use SHGetKnownFolderPath() to get the "Local AppData" folder for the user. Theme files are stored in the Microsoft\Windows\Themes subfolder. Theme files stored there are applied directly, while theme files stored elsewhere are duplicated when you execute them. So it's best to use files from that folder only.
Use ShellExecute() to execute the .theme file you located in step 1.
Wait for the theme to be applied. I simply let my app sleep for 2 seconds.
Call FindWindow('CabinetWClass', 'Personalization') to get the handle of the Control Panel window that popped up when the theme was applied. The "Personalization" caption will likely be different on non-US-English versions of Windows.
Call PostMessage(HWND, WM_CLOSE, 0, 0) to close the Control Panel window.
This isn't a very elegant solution, but it does the job.
I know this is an old ticket, but somebody asked me how to do this today. So starting from Mike's post above I cleaned things up, added comments, and will post full C# console app code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
namespace Windows7Basic
{
class Theming
{
/// Handles to Win 32 API
[DllImport("user32.dll", EntryPoint = "FindWindow")]
private static extern IntPtr FindWindow(string sClassName, string sAppName);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
/// Windows Constants
private const uint WM_CLOSE = 0x10;
private String StartProcessAndWait(string filename, string arguments, int seconds, ref Boolean bExited)
{
String msg = String.Empty;
Process p = new Process();
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
p.StartInfo.FileName = filename;
p.StartInfo.Arguments = arguments;
p.Start();
bExited = false;
int counter = 0;
/// give it "seconds" seconds to run
while (!bExited && counter < seconds)
{
bExited = p.HasExited;
counter++;
System.Threading.Thread.Sleep(1000);
}//while
if (counter == seconds)
{
msg = "Program did not close in expected time.";
}//if
return msg;
}
public Boolean SwitchTheme(string themePath)
{
try
{
//String themePath = System.Environment.GetFolderPath(Environment.SpecialFolder.Windows) + #"\Resources\Ease of Access Themes\basic.theme";
/// Set the theme
Boolean bExited = false;
/// essentially runs the command line: rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,#Themes /Action:OpenTheme /file:"%WINDIR%\Resources\Ease of Access Themes\classic.theme"
String ThemeOutput = this.StartProcessAndWait("rundll32.exe", System.Environment.GetFolderPath(Environment.SpecialFolder.System) + #"\shell32.dll,Control_RunDLL " + System.Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\desk.cpl desk,#Themes /Action:OpenTheme /file:\"" + themePath + "\"", 30, ref bExited);
Console.WriteLine(ThemeOutput);
/// Wait for the theme to be set
System.Threading.Thread.Sleep(1000);
/// Close the Theme UI Window
IntPtr hWndTheming = FindWindow("CabinetWClass", null);
SendMessage(hWndTheming, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}//try
catch (Exception ex)
{
Console.WriteLine("An exception occured while setting the theme: " + ex.Message);
return false;
}//catch
return true;
}
public Boolean SwitchToClassicTheme()
{
return SwitchTheme(System.Environment.GetFolderPath(Environment.SpecialFolder.Windows) + #"\Resources\Ease of Access Themes\basic.theme");
}
public Boolean SwitchToAeroTheme()
{
return SwitchTheme(System.Environment.GetFolderPath(Environment.SpecialFolder.Windows) + #"\Resources\Themes\aero.theme");
}
public string GetTheme()
{
string RegistryKey = #"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes";
string theme;
theme = (string)Registry.GetValue(RegistryKey, "CurrentTheme", string.Empty);
theme = theme.Split('\\').Last().Split('.').First().ToString();
return theme;
}
// end of object Theming
}
//---------------------------------------------------------------------------------------------------------------
class Program
{
[DllImport("dwmapi.dll")]
public static extern IntPtr DwmIsCompositionEnabled(out bool pfEnabled);
/// ;RunProgram("%USERPROFILE%\AppData\Local\Microsoft\Windows\Themes\themeName.theme") ;For User Themes
/// RunProgram("%WINDIR%\Resources\Ease of Access Themes\classic.theme") ;For Basic Themes
/// ;RunProgram("%WINDIR%\Resources\Themes\aero.theme") ;For Aero Themes
static void Main(string[] args)
{
bool aeroEnabled = false;
Theming thm = new Theming();
Console.WriteLine("The current theme is " + thm.GetTheme());
/// The only real difference between Aero and Basic theme is Composition=0 in the [VisualStyles] in Basic (line omitted in Aero)
/// So test if Composition is enabled
DwmIsCompositionEnabled(out aeroEnabled);
if (args.Length == 0 || (args.Length > 0 && args[0].ToLower(CultureInfo.InvariantCulture).Equals("basic")))
{
if (aeroEnabled)
{
Console.WriteLine("Setting to basic...");
thm.SwitchToClassicTheme();
}//if
}//if
else if (args.Length > 0 || args[0].ToLower(CultureInfo.InvariantCulture).Equals("aero"))
{
if (!aeroEnabled)
{
Console.WriteLine("Setting to aero...");
thm.SwitchToAeroTheme();
}//if
}//else if
}
// end of object Program
}
}
I'm not sure if this is a new thing, but you can just double click the .theme file and Windows 10 will apply the theme. Hence, you can do this with PowerShell easily:
$Windows10Theme = "C:\Windows\Resources\Themes\aero.theme"
Invoke-Expression $Windows10Theme
The command for newer Windows versions (Windows 8 and 8.1, haven't tried it on W10 yet) is:
rundll32.exe themecpl.dll,OpenThemeAction %1
or with full paths:
C:\WINDOWS\system32\rundll32.exe C:\WINDOWS\system32\themecpl.dll,OpenThemeAction %LocalAppData%\Microsoft\Windows\Themes\yourtheme.theme
Basically it's the Personalisation CPL "open" command for .theme & .themepack extensions taken from registry...
You'll still end up with the Personalisation window beeing open after using this command so to close it down programatically you'll have to use one of the suggested methods mentioned above... (I personally prefer the Powershell script)
I have been experimenting about changing the windows theme via command line and I learned that by executing the theme file it is being applied by the Windows 10 as well. So in your batch file, you could use one of the following lines:
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Dark_Mode.theme
or
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Light_Mode.theme
Please note the path to the theme files might be needed to adjust depending on your system user configuration. I strongly advise saving your themes with names excluding spaces as it makes much easier moving forward. Executing such line leaving you with the Settings window opened. To deal with I considered using VBS script instead. Thanks to Patrick Haugh user1390106 there is a much easier way to close the Settings window.
taskkill /F /IM systemsettings.exe
So the updated version of batch file could look like this:
#echo off
if %1 == dark (
REM ================== Go Dark ==================
color 09
echo.
echo Applying DARK MODE
echo Windows Theme ...
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Dark_Mode.theme
timeout /T 1 /nobreak > nul
taskkill /F /IM systemsettings.exe > nul
echo DONE
) else (
REM ============== Return to Light ==============
color 30
echo.
echo Applying LIGHT MODE
echo Windows Theme ...
C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Themes\Light_Mode.theme
timeout /T 1 /nobreak > nul
taskkill /F /IM systemsettings.exe > nul
echo DONE
)
REM ================== Goodbye ==================
echo.
echo Goodbye
cls
exit
Please note the path to the theme files might be needed to adjust depending on your system user configuration. Save above script with the name theme.bat somewhere in your drive.
This batch file taking one parameter which needs to be either dark or any other string. Then you could prepare two shortcuts to this batch file each with one of the following in the box called “Target” on the “Shortcut” tab in its properties:
C:\full-path-to-your-batch-file\theme.bat dark
or
C:\full-path-to-your-batch-file\theme.bat light
Please replace “full-path-to-your-batch-file” with actual path to that file.
Here are links to the videos showing how this works:
a) Going Dark – https://youtu.be/cBcDNhAmfyM
b) Returning to the Light – https://youtu.be/2kYJaJHubi4
Please note that my script in those videos also activating/deactivating the Stylish plug-in for chrome. I have omitted to explain how I accomplished that part as it is not a subject of this article.
I believe the best you can do is open your target .msstyles file (in c:\windows\resources\themes), which will pop up the display properties box. At this point you could use window subclassing to programmatically click the right buttons.
In addition of the post of "Jan Goyvaerts":
I use SendMessage instead of PostMessage. The difference is that SendMessage waits for the command to be taken in by the window. Meaning that in the SendMessages returns, you know that the theme dialog is closed.
So if you start it with the monstrous (but genious) rundll32.exe method suggested by "Campbell". You should wait a sec before sending WM_CLOSE. Otherwise the theme will not be set and the application closes right away.
The code snippet below extracts a file from resource (a themepack). Then executes the desk.cpl with rundll32.exe, waits 3 sceonds, then sends WM_CLOSE (0x0010), waits for the command to be process (the time it takes for the theme to be set).
private Boolean SwitchToClassicTheme()
{
//First unpack the theme
try
{
//Extract the theme from the resource
String ThemePath = System.Environment.GetFolderPath(Environment.SpecialFolder.Windows) + #"\Resources\Themes\ClassicTheme.themepack";
//WriteFileToCurrentDirectory("ClassicTheme.theme", TabletConfigurator.Resources.ClassicTheme);
if(File.Exists(ThemePath))
{
File.Delete(ThemePath);
}
if(File.Exists(ThemePath))
{
throw new Exception("The file '" + ThemePath + "' exists and can not be deleted. You can try to delete it manually.");
}
using (BinaryWriter sw = new BinaryWriter(new FileStream(ThemePath, FileMode.OpenOrCreate)))
{
sw.Write(TabletConfigurator.Resources.ClassicTheme);
sw.Flush();
sw.Close();
}
if(!File.Exists(ThemePath))
{
throw new Exception("The resource theme file could not be extracted");
}
//Set the theme file as like a user would have clicked it
Boolean bTimedOut = false;
String ThemeOutput = StartProcessAndWait("rundll32.exe", System.Environment.GetFolderPath(Environment.SpecialFolder.System) + #"\shell32.dll,Control_RunDLL " + System.Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\desk.cpl desk,#Themes /Action:OpenTheme /file:\"" + ThemePath + "\"", ref bTimedOut);
System.Threading.Thread.Sleep(3000);
//Wait for the theme to be set
IntPtr hWndTheming = FindWindow("CabinetWClass", null);
SendMessage(hWndTheming, (uint)WM_CLOSE, 0, 0);
//using (Bitmap bm = CaptureScreenShot())
//{
// Boolean PixelIsGray = true;
// while (PixelIsGray)
// {
// System.Drawing.Color pixel = bm.GetPixel(0, 0)
// }
//}
}
catch(Exception ex)
{
ShowError("An exception occured while setting the theme: " + ex.Message);
return false;
}
return true;
}
I just realized you can double click the theme and it autoswitches it - much simpler, so just executing the theme works, ex batch file:
:: Reactivate my theme after an remote desktop session
:: We must select another theme first before we can select ours again and hence re-activate Aero, please wait..."
#echo Off
"C:\Windows\Resources\Themes\aero.theme"
::echo "Simulating a pause while"
ping 127.0.0.1 -n 10 > null && "D:\Users\danielsokolowski\Windows 7 Aero Themes\`danielsokolowski` Theme (without Glass).theme"
::or ping 127.0.0.1 -n 3 > null && "%userprofile%\AppData\Local\Microsoft\Windows\Themes\`danielsokolowski` Theme (without Glass).theme"
For Windows 10 I wrote this simple solution (it can also be used in DSC) in PowerShell
# Apply your theme
& "C:\Windows\Resources\Themes\Brand.theme"
# We need to wait for the theme to be applied
Start-Sleep -s 5
# Close the settings window that is opened by the action above
$window = Get-Process | Where-Object {$_.Name -eq "SystemSettings"}
Stop-Process -Id $window.Id
Okay so here is my take on this - a VB script. It's a bit nasty but the best I could come up with (sadly).
For a user that logs in, we simply run ChangeTheme.vbs as the user logs in (e.g. autorun). The script starts desk.cpl and passes the required parameters to it as well as the name of the selected theme.
One can run the script with or without parameters:
> ChangeTheme.vbs
> ChangeTheme.vbs AnyThemeName
The script:
' ////////////////////////////////////////////////////////////////////
'
' Changes the theme.
'
' Name:
' ChangeTheme.vbs
' Parameter 1:
' Theme name e.g. aero or anything
' located in in C:\Windows\Resources\Themes.
' If not present, a default theme will be used.
'
' Example:
' Inside a command line run
' > ChangeTheme.vbs TheThemeName
'
' ////////////////////////////////////////////////////////////////////
If(Wscript.Arguments.Count <= 0) Then
' If no parameter was given we set the following theme as default
selectedTheme = "aero"
Else
' Get theme via the first argument
selectedTheme = Wscript.Arguments(0)
End If
' Create WScript shell object
Set WshShell = WScript.CreateObject("WScript.Shell")
' Run the command to open the "theme application" (or whatever)
Set process = WshShell.Exec("rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,#Themes /Action:OpenTheme /file:""C:\Windows\Resources\Themes\" & selectedTheme & ".theme""")
' Wait for the application to start
Wscript.Sleep 250
Success = False
maxTries = 20
tryCount = 0
Do Until Success = True
Wscript.Sleep 1000
' Set focus to our application
' If this fails, or the application loses focus, it won't work!
Success = WshShell.AppActivate(process.ProcessId)
tryCount = tryCount + 1
If (tryCount >= maxTries) Then
' If it does not work after maxTries we give up ..
MsgBox("Cannot change theme - max tries exceeded ..")
Exit Do
End If
Loop
' The crucial part: Send keys ALT + B for applying the theme
WshShell.Sendkeys "%(B)"
' Send key "escape" to close the window
WshShell.Sendkeys "{ESCAPE}"
Hope that helps.
It works on Windows 10.
this is my script. It changes the theme and closes the window. I save it to a batch file and run this patch file from TaskScheduler:
C:\WINDOWS\system32\rundll32.exe C:\WINDOWS\system32\themecpl.dll,OpenThemeAction C:\Users\xxx\Misc_computer_stuff\themes\my_fav_gr.theme
TIMEOUT 1 & REM Waits 1 seconds before executing the next command
TASKKILL /F /IM systemsettings.exe & close window
exit
You can simply open any of the .theme files present in C:\Windows\Resources\Themes\ to change the theme.
The only catch is that the settings app is also opened after this. But we can kill it using Stop-Process in PowerShell
Invoke-Expression "C:\Windows\Resources\Themes\<theme_name>.theme"
Start-Sleep -Seconds 2
Stop-Process -Name SystemSettings
For Example:
Invoke-Expression "C:\Windows\Resources\Themes\dark.theme"

How can I mute/unmute my sound from PowerShell

Trying to write a PowerShell cmdlet that will mute the sound at start, unless already muted, and un-mute it at the end (only if it wasn't muted to begin with).
Couldn't find any PoweShell or WMI object I could use. I was toying with using Win32 functions like auxGetVolume or auxSetVolume, but couldn't quite get it to work (how to read the values from an IntPtr?).
I'm using V2 CTP2. Any ideas folks?
Thanks!
Starting with Vista you have to use the Core Audio API to control the system volume. It's a COM API that doesn't support automation and thus requires a lot of boilerplate to use from .NET and PowerShell.
Anyways the code bellow let you access the [Audio]::Volume and [Audio]::Mute properties from PowerShell. This also work on a remote computer which could be useful. Just copy-paste the code in your PowerShell window.
Add-Type -TypeDefinition #'
using System.Runtime.InteropServices;
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioEndpointVolume {
// f(), g(), ... are unused COM method slots. Define these if you care
int f(); int g(); int h(); int i();
int SetMasterVolumeLevelScalar(float fLevel, System.Guid pguidEventContext);
int j();
int GetMasterVolumeLevelScalar(out float pfLevel);
int k(); int l(); int m(); int n();
int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, System.Guid pguidEventContext);
int GetMute(out bool pbMute);
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice {
int Activate(ref System.Guid id, int clsCtx, int activationParams, out IAudioEndpointVolume aev);
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator {
int f(); // Unused
int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumeratorComObject { }
public class Audio {
static IAudioEndpointVolume Vol() {
var enumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
IMMDevice dev = null;
Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(/*eRender*/ 0, /*eMultimedia*/ 1, out dev));
IAudioEndpointVolume epv = null;
var epvid = typeof(IAudioEndpointVolume).GUID;
Marshal.ThrowExceptionForHR(dev.Activate(ref epvid, /*CLSCTX_ALL*/ 23, 0, out epv));
return epv;
}
public static float Volume {
get {float v = -1; Marshal.ThrowExceptionForHR(Vol().GetMasterVolumeLevelScalar(out v)); return v;}
set {Marshal.ThrowExceptionForHR(Vol().SetMasterVolumeLevelScalar(value, System.Guid.Empty));}
}
public static bool Mute {
get { bool mute; Marshal.ThrowExceptionForHR(Vol().GetMute(out mute)); return mute; }
set { Marshal.ThrowExceptionForHR(Vol().SetMute(value, System.Guid.Empty)); }
}
}
'#
Usage sample:
PS C:\> [Audio]::Volume # Check current volume (now about 10%)
0,09999999
PS C:\> [Audio]::Mute # See if speaker is muted
False
PS C:\> [Audio]::Mute = $true # Mute speaker
PS C:\> [Audio]::Volume = 0.75 # Set volume to 75%
PS C:\> [Audio]::Volume # Check that the changes are applied
0,75
PS C:\> [Audio]::Mute
True
PS C:\>
There are more comprehensive .NET wrappers out there for the Core Audio API if you need one but I'm not aware of a set of PowerShell friendly cmdlets.
P.S. Diogo answer seems clever but it doesn't work for me.
Use the following commands on a ps1 powershell script:
$obj = new-object -com wscript.shell
$obj.SendKeys([char]173)
Alexandre's answer fit my situation, but his example does not work due to compilation errors regarding the namespace of 'var'. It seems that newer/different versions of .net may cause the example not to work. If you found that you received compilation errors, this is an alternative version to try for those cases:
Add-Type -Language CsharpVersion3 -TypeDefinition #'
using System.Runtime.InteropServices;
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioEndpointVolume {
// f(), g(), ... are unused COM method slots. Define these if you care
int f(); int g(); int h(); int i();
int SetMasterVolumeLevelScalar(float fLevel, System.Guid pguidEventContext);
int j();
int GetMasterVolumeLevelScalar(out float pfLevel);
int k(); int l(); int m(); int n();
int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, System.Guid pguidEventContext);
int GetMute(out bool pbMute);
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice {
int Activate(ref System.Guid id, int clsCtx, int activationParams, out IAudioEndpointVolume aev);
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator {
int f(); // Unused
int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumeratorComObject { }
public class Audio {
static IAudioEndpointVolume Vol() {
var enumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
IMMDevice dev = null;
Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(/*eRender*/ 0, /*eMultimedia*/ 1, out dev));
IAudioEndpointVolume epv = null;
var epvid = typeof(IAudioEndpointVolume).GUID;
Marshal.ThrowExceptionForHR(dev.Activate(ref epvid, /*CLSCTX_ALL*/ 23, 0, out epv));
return epv;
}
public static float Volume {
get {float v = -1; Marshal.ThrowExceptionForHR(Vol().GetMasterVolumeLevelScalar(out v)); return v;}
set {Marshal.ThrowExceptionForHR(Vol().SetMasterVolumeLevelScalar(value, System.Guid.Empty));}
}
public static bool Mute {
get { bool mute; Marshal.ThrowExceptionForHR(Vol().GetMute(out mute)); return mute; }
set { Marshal.ThrowExceptionForHR(Vol().SetMute(value, System.Guid.Empty)); }
}
}
'#
Usage is the same:
PS C:\> [Audio]::Volume # Check current volume (now about 10%)
0,09999999
PS C:\> [Audio]::Mute # See if speaker is muted
False
PS C:\> [Audio]::Mute = $true # Mute speaker
PS C:\> [Audio]::Volume = 0.75 # Set volume to 75%
PS C:\> [Audio]::Volume # Check that the changes are applied
0,75
PS C:\> [Audio]::Mute
True
PS C:\>
You could skin the cat another way by simply managing the Windows Audio Service. Stop it to mute, start it to unmute.
There does not seem to be a quick and easy way to adjust the volume.. If you have c++ experience, you could do something with this blog post, where Larry Osterman describes how to call the IAudioEndpointVolume interface from the platform api(for Vista, XP might be more difficult from what I've found in a few searches).
V2 does allow you to compile inline code (via Add-Type), so that might be an option.
I know it isn't PowerShell, but combining the answers from Michael and Diogo gives a one-line VBScript solution:
CreateObject("WScript.Shell").SendKeys(chr(173))
Slap this in mute.vbs, and you can just double-click to toggle mute
still works in Windows 10 (10586.104)
no need to Set-ExecutionPolicy as you might with PowerShell
Solution in vbscript:
Set WshShell = CreateObject("WScript.Shell")
For i = 0 To 50
WshShell.SendKeys(chr(174))
WScript.Sleep 100
Next
The keys reduce the volume by 2 each time.
I didn't find how to do this in PowerShell, but there is a command-line utility called NirCmd that will do the trick by running this command:
C:\utils\nircmd.exe mutesysvolume 0 # 1 to to unmute, 2 to toggle
NirCmd is available for free here:
http://www.nirsoft.net/utils/nircmd.html
Check out my answer to Change audio level from powershell?
Set-DefaultAudioDeviceMute
lemme try again, after getting some feedback on my answer
This is based on #frgnca's AudioDeviceCmdlets, found here: https://github.com/frgnca/AudioDeviceCmdlets
Here is code that will mute every recording device.
Import-Module .\AudioDeviceCmdlets
$audio_device_list = Get-AudioDevice -list
$recording_devices = $audio_device_list | ? {$_.Type -eq "Recording"}
$recording_devices
$recording_device_index = $recording_devices.Index | Out-String -stream
foreach ($i in $recording_device_index) {
$inti = [int]$i
Set-AudioDevice $inti | out-null -erroraction SilentlyContinue
Set-AudioDevice -RecordingMute 1 -erroraction SilentlyContinue
}
You import the AudioDeviceCmdlets dll, then save a list of all audio devices, filtered down into recording devices. You grab the index of all the recording devices and then iterate through each one, first setting the device to be your primary audio device, then setting that device to mute (this 2 step process is a limitation imposed by the dll).
To unmute everything, you change -RecordingMute 1 to RecordingMute 0
Similarly, to mute playback devices you can use this code:
Import-Module .\AudioDeviceCmdlets
$audio_device_list = Get-AudioDevice -list
$playback_devices = $audio_device_list | ? {$_.Type -eq "Playback"}
$playback_devices
$playback_device_index = $playback_devices.Index | Out-String -stream
foreach ($i in $playback_device_index) {
$inti = [int]$i
Set-AudioDevice $inti | out-null -erroraction SilentlyContinue
Set-AudioDevice -PlaybackMute 1 -erroraction SilentlyContinue
}
To unmute everything, you change -PlaybackMute 1 to PlaybackMute 0
This code comes from part of a bigger project I have which hooks up to a physical button/LED mute status display via Arduino to enable single button press to Mute/Unmute all system microphones (to help with Zoom, Meet, Teams, Discord, etc.) and to help people avoid embarrassing hot-mic incidents.
https://github.com/dakota-mewt/mewt
P.S. I really like some of the one-line solutions like Change audio level from powershell?
However, note that a single-line muting typically applies only to the single device that's currently set as the windows default. So if you have software that's capable of addressing different audio devices (like Zoom and Meet), and they happen to be using a non-default device, it may not work as desired.
In the second script above, to work in PowerShell 7 or PowerShell Core in the 1st line change:
-Language CsharpVersion3
to...
-Language Csharp
Working on W10
Here's a simple timed mute script using autohotkey (from autohotkey dotcom).
mbutton:: ; Trigger this script with a click on the mouse wheel
SoundSet, +1,, Mute ; Mute the PC sound
sleep, 27000 ; Wait 27 seconds for the annoying advertisement to run
SoundSet, +1,, Mute ; Unmute the PC sound

Resources