“Hot Corners” for Windows (PowerShell)

GitHub files

PoShHotCorners

"Hot Corners" for Windows

install

  • none really, simply download the ico and ps1 files to a folder and launch the ps1
  • please note MakeShortcut.cmd batch file provided for convenience ...
    • throw resulting .lnk file in your "run on startup" folder if you like: "%appdata%\Microsoft\Windows\Start Menu\Programs\Startup"
    • as you can see, the batch file simply runs shortcut tool xxmklink.exe with appropriate arguments

notes

  • currently coded to power down monitors triggered by mouse in lower right corner ... but it's just powershell folks, so think grand!
  • the lion's share of the code is actually just for the task tray icon...
  • look for "beef" as the key line where mouse location triggers action
  • DOES work with multiple monitors.
    • also includes tray menu options for blanking multiple displays independently
  • the timer loop inherently keeps watching the mouse so if your screens stubbornly randomly wake up like mine, this will bonk them right back to nappy time for the win, yes!

supporting multiple extended displays

  • if your scenario isn't working, drop me an issue on github or if you're inclined, check your [System.Windows.Forms.Screen]::AllScreens
  • here's mine:

    BitsPerPixel : 32
    Bounds       : {X=2560,Y=0,Width=1920,Height=1200}
    DeviceName   : \\.\DISPLAY2
    Primary      : False
    WorkingArea  : {X=2560,Y=0,Width=1920,Height=1160}
    
    BitsPerPixel : 32
    Bounds       : {X=0,Y=0,Width=2560,Height=1600}
    DeviceName   : \\.\DISPLAY3
    Primary      : True
    WorkingArea  : {X=0,Y=0,Width=2560,Height=1560}
  • i have 2 screens side-by-side, so note the Bounds of the first where X has a value...
  • so that's where the $mouse.X-$bounds.X in the "beef" check works for me...
  • hopefully that approach will carry through other monitor arragements with a little testing

tips

  • if you find that your windows get all jumbled after sleeping the monitors, this post actually seemed to help... but all i did was simply delete the whole registry folder HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Configuration (i probably had at least 50 entries in there) and let Windows recreate.

PowerShell WinForms interactive Prototyping/Debugging

Credits:

Motivation

  • PowerShell’s interactive nature lends it to be a handy way to experiment with .Net objects… including UI elements like WinForms/WPF…
  • Yet firing up UI’s classically take over the active thread to service the user interaction (e.g. mouse events etc)…
  • Without the extra gravy below, our otherwise handy interactive powershell locks up tight until we close down the Windows Forms application thus releasing the main thread back to the command line
  • The following gravy throws a Windows Form onto a background thread such that we can continue to manipulate the UI objects WHILE THEY’RE RUNNING, yay!

The Gravy

create RunSpaceWinForm.ps1 as such

function RunSpaceWinForm {
    param($frm)
    if (!$frm) {return}

    #RunSpace is a PowerShell thread
    [System.Management.Automation.Runspaces.Runspace]$rs = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
    $rs.ApartmentState = "STA"
    $rs.ThreadOptions = "ReuseThread"
    $rs.Open()

    # make the WinForm object instantiated in current scope also available inside the runspace
    $rs.SessionStateProxy.SetVariable("frm", $frm)

    $ps = [System.Management.Automation.PowerShell]::Create()
    $ps.Runspace = $rs
    $rs.SessionStateProxy.SetVariable("ps", $ps)

    [System.IAsyncResult]$invokeHandle = $null
    $rs.SessionStateProxy.SetVariable("invokeHandle", $invokeHandle)

    # nugget: here's basically where the magic sauce kicks in
    # create the script that will run on the background thread, this lets WinForm have it's WndProc message pump while freeing our current PowerShell thread to further manipulate WinForm objects
    $ps.AddScript({
      #this call will take over the thread until the application is shut down by closing the main form
      [System.Windows.Forms.Application]::Run($frm)

      # clean up the powershell thread objects
      $ps.Runspace.Close()

      #honestly not sure if these commands work and are beneficial
      $ps.EndInvoke($invokeHandle)
      $ps.Runspace.Dispose() #this will block the runspace state on "closing" until you close the interactive powershell window
      $ps.Dispose()
    }) | Out-Null
    $invokeHandle = $ps.BeginInvoke()
    return "Use Debug-Runspace -id $($rs.Id) to activate breakpoints"
}

and here is simple usage… spin up a running WinForm and give control back to the powershell prompt

Add-Type -AssemblyName System.Windows.Forms
$frmMain = New-Object System.Windows.Forms.Form

. "{put_your_path_here}\RunSpaceWinForm.ps1"
RunSpaceWinForm $frmMain

Breakpoints

  • by default, breakpoints will now message as: “WARNING: Breakpoint Line breakpoint on ‘xyz’ will not be hit”
  • the above run will kick out something like the following…
Use "Debug-Runspace -id 4" to go into breakpoint mode
  • execute that debug-runspace command and your breakpoints will light up in the editor and break when the code fires
  • CTRL-BREAK your way out of that mode when you want to go back to the powershell command line and manipulate the UI objects some more

PowerShell Photo Slideshow

GitHub Source

Project Description

Photo slideshow implemented in PowerShell -> Windows Forms

Simply target a (nested) folder of images. Local or LAN UNC path supported.  
 

Features:

  • task tray icon to start slideshow on demand...
  • otherwise kicks off after user defined idle timeout (honors running video)
  • good randomization - one soon realizes pleasantly random photos are the key want of a photo slideshow ... fortunately PowerShell has a readily available random commandlet that seems to do quite well
    • persists "lastShown" for each subfolder and avoids re-showing within XX days (currently 1 month)
  • image fade-in and slide for ambience
  • several hotkeys functional:
    • open current image folder
    • copy current image to _My Photos_
    • rotate current image (and save) - *generally honors EXIF rotation metadata where present, this option allows for manual correction where EXIF is missing*
    • reverse to previously shown photo (left cursor)
    • pause/play (space)
    • hotkey legend pops up on any other keypress
  • screen click functions:
    • double click in center hides slideshow
    • single click in center pauses slideshow
    • click arrows on far left and right for prev/next image
  • skips .hidden folders
  • plays videos via VLC
  • open to modification - it's just PowerShell 🙂 no compiling tools required

Install - basically just launch the ps1... here's some tips:

  1. only the ps1 and ico files are needed, download them to a folder
  2. ensure VLC.exe is in your path
  3. (see screenshot below) create a shortcut to the ps1 and tweak the target to include powershell before the ps1 filename...
  4. example full shorcut command line: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden \\beejquad\Dev\_PersonalProjects\PoShSlideshow\PoShSlideshow.ps1 -photoPath \\beejquad\photos -idleTimeout 2
  5. select Run: Minimized to make script launch more polished
  6. add -WindowStyle Hidden after powershell.exe on target command line for further polish
  7. then hit the Advanced button and select Run as administrator - this is only required for the powercfg /requests used to identify running video and avoid starting slideshow after user input idle timeout (wouldn't mind hearing a slicker approach???)
  8. script parameters:
    • add -photoPath {path\to\your\images} to the end of the shortcut path - UNC shared folder fair game, write permissions required to persist folder cache flat file
    • add -idleTimeout 2 to the end of the shortcut path - units are in minutes
  9. Copy this shortcut to shell:startup in Windows FileExplorer to automatically launch this script when you login to your desktop

Wishlist

  1. [done] show videos as well - thinking VLC convenient
  2. Right mouse to show commands menu same as keyboard
  3. Implement a Hide button akin to the forward back buttons
  4. Email current photo - on screen keyboard? fire gmail to get contacts
  5. blog request: Automatically update folder cache upon new items... to be clear, current approach automatically recognizes new files in existing folders since it only caches the list of folders from which it randomly grabs the next image. Thoughts - Seems pretty straightforward to throw in PowerShell FileWatcher configured to call the existing updateFolderCache function.

Transcode MOV to MP4

GitHub Source

Motivation:

Digital cameras and phones typically save video to MOV (Motion JPEG) files. I share my photos & videos via a self hosted open source photo gallery (zenPhoto). MOV files must be converted to a compatible format like MP4 to stream through the readily available web video players like Flowplayer.

Script features:

  • Handles multiple files at once… even from different folders, e.g. when part of a file explorer search result
  • Applies rotation where recognized in the EXIF metadata.
  • Touches new file datestamp to be same as original.

Leverages 3rd party tools:

  • FileMenu Tools (FMT) - handy for creating a FileExplorer right mouse context menu for executing transcode on selected files
  • See new free approach for the Context Menu piece
  • HandBrake - read something that suggested HandBrake is faster than ffmpeg and that appears true on my quick comparison
  • MediaInfo - pulls the EXIF metadata to determine if any rotation is necessary

Install:

  1. save transcode.ps1 to a known location
  2. install FileMenu Tools and disable all the commands you don’t want.
  3. Configure a “transcode” command as shown in screenshot below... edit for your path
    • updated FMT Arguments: -Command "{path}\transcode.ps1" -list %TEMPFILEPATHS% -rotate auto
  4. install HandBrake and put HandBrakeCli in your path
  5. minimally, put MediaInfo.exe and MediaInfo.dll in your path

Windows File Explorer UI example

enter image description here

PowerShell Dual Windows Explorers

 

Nutshell: Hosting two Windows File Explorers inside a WinForm… with the potential of sprinkling some utility hotkeys on top – e.g. “copy from left to right”.

 

Full source on GitHub


 

Highlights:

  • Always wanted to try this and just finally got around to it… and it actually works to a decent degree.
  • This is of course well covered ground with various other file managers… i just wanted to see if you could do it this poor man’s way with PowerShell driving… so one could readily make it one’s own with further customizations
  • I was a longtime fan of Directory Opus… I think it’s significant that this meager alternative is customized via standard PowerShell vs a 3rd party scripting environment that must be learned… i.e. if you happen to already know PowerShell, you can jump right in with all that file handling power available
  • The obnoxious part is hunting down the COM interfaces necessary to pull stuff out of FileExplorer… it dips into silliness like how IE is somehow part of the equation.
  • See comments for all the good posts i drew from to cobble it together… lots of handy Shell programming nuggets to be had
  • thanks to a handy github project, Font-Awesome is now in the WinForms domain – too cool
  • notes to self
    • interop.SHDocVw.dll is generated from doing a Visual Studio reference to C:\windows\system32\shdocvmw.dll
    • interop.Shell32.dll seemed like it was going to come in handy but didn’t wind up being necessary
    • these are the only real FileExplorer API calls necessary for the CopyFile piece
      • $objFolder = $objShell.NameSpace($explorerRight_SHDocVw.LocationUrl)
      • $objFolder.CopyHere($explorerLeft_SHDocVw.Document.SelectedItems())
    • there are a few wacky interfaces behind the shell objects but the neat thing is that runtime dynamic type binding makes using real types largely irrelevant… i feel that does lose some self documentation in the balance so i’ve tried to include the pertinent interfaces in the comments for future reference and expansion

Chrome Blacklist Blocker (PowerShell)

If you need this, you’ll know why 😉

Save ChromeBlacklistBlocker.ps1 somewhere local.

You can run it via right mouse > “Run with PowerShell”.
It will dump out some event text whenever it notices a registry change.
(this is currently commented out and latest code hides the powershell console window after launch)

Or more permanently, put a shortcut like this into your “shell:startup” folder:
powershell.exe -ExecutionPolicy Bypass {path}ChromeBlacklistBlocker.ps1

It will monitor the HKLMSoftwarePolicies registry branch and delete the value named “1” under GoogleChromeExtensionInstallBlacklist.
This value is specific to my scenario but is of course editable to your specific needs.

You can test it is working by creating the “1” value yourself and it should disappear.

Another good way to test is to fire gpupdate.exe force a group policy update – again, if you need this, that should make sense 🙂

More Google search keywords: block registry key

List all your Azure RDP’s

Get-AzureVM | #this first one gets the entire list of VMs in subscription
    Get-AzureVM | # this one gets the detailed object for each specific VM
    %{
        $port = ($_ | Get-AzureEndpoint | ? {$_.name -like "Remote*"})[0].Port;
        $null = $_.DNSName -match 'http://(.*?)/'
        write-host "$($_.Name) - $($matches[1]):$($port)"
    }

Outlook Bulk Remove Encryption

PowerShell Script to scan through specified Outlook folder and remove the encryption flag on each email.

Background: We tend to cycle smart cards over the years in my environment. The old certs from those cards must be maintained in order to be able to pull up old emails sent to you which were encrypted with your old public key(s). This also makes it interesting to hand off a PST full of emails piled up as a simple knowledge base to someone else, since you really wouldn’t want to give them your certs of course.  Removing the encryption flag via the Outlook COM API turns out to be pleasantly trivial… literally just one line of code and a resave. The rest of this script is a simple UI and the optional folder recursion logic. If you don’t have a particular cert loaded, the script will output the subject of the failed message and then it’s a matter of getting that missing cert loaded on your system and retrying.

[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null 
$notes = "Drill down into subfolders?`r`rThis works for Outlook 2010 at least.`rMake sure to have Outlook running.`rand manually open an encrypted email before running this script.`r(this will cache your pin# so it will apply to scripted access)" 
$recursive = [Windows.Forms.MessageBox]::Show($notes, "Process Subfolders?", "yesnocancel") 
if ($recursive -eq "Cancel") { exit } 

$outlook = new-object -com Outlook.Application 
$f = $outlook.Session.PickFolder() 
if ($f -eq $null) { exit } 

$PR_SECURITY_FLAGS = "http://schemas.microsoft.com/mapi/proptag/0x6E010003" 
$cr = [char]0x2028 
function RemoveCrypto($folder) 
{ 
  $txt.AppendText($folder.folderpath + $cr); 
  
  if ($recursive -eq "Yes") { $folder.folders | foreach-object { RemoveCrypto($_) } } 
  
  $folder.items | foreach-object { 
    $mailitem = $_ 
    try { 
      ############################################ 
      # here's the beef. too easy! 
      # thanks to this post: http://support.microsoft.com/kb/2636465 
      $mailitem.PropertyAccessor.SetProperty($PR_SECURITY_FLAGS, 0) 
      $mailitem.save() 
      ############################################ 
    } 
    catch { 
      $txt.AppendText($mailitem.subject + $cr); 
    } 
  } 
} 

# Create Window 
$form = new-object System.Windows.Forms.Form 
$form.Size = new-object System.Drawing.Size @(600,600) 
$form.topmost = $true 
$form.text = "Folder progress & messages that couldn't be decrypted" 

############################################################### 
# this is what kicks off the loop 
$form.add_shown({ RemoveCrypto($f) }) 
############################################################### 

$txt = new-object System.Windows.Forms.RichTextBox 
$txt.Dock = "Fill" 
$txt.AutoSize = "True" 
$txt.Font = new-object System.Drawing.Font("cambria", 13) 
$form.Controls.Add($txt) 

$close = New-Object System.Windows.Forms.Button 
$close.Dock = "Bottom" 
$close.AutoSize = "True" 
$close.Text = "Close" 
$close.add_click({ 
$form.Close() 
}) 
$form.Controls.Add($close) 

# Show window 
$form.showdialog() 
$form.dispose()

PowerShell f’in rocks!

  • Main Microsoft landing page for PowerShell… download and install it!!  (if you’re running Windows Server 2008, it’s already loaded)
  • Out of the box executing PowerShell scripts is completely disabled for security… there’s a couple immediate tasks to enable…
    • Go find the PowerShell icon and fire it up… Vista (booo hiss!) > Start > Programs > Windows PowerShell 1.0 … Win7 (yay!) > Start > Programs > Accessories > Windows PowerShell
    • Turn on ability to run PowerShell script files via
      “Set-ExecutionPolicy RemoteSigned” … you’ll need to do this right up front (only once) since this is locked down by default
    • Download and install the PowerShell Community Extensions (PSCX)… all kinds of baseline goodies
    • The PSCX install will establish a default “profile.ps1” script that establishes a bunch of handy default shell and script functionality
    • Profile.ps1 lives here: “%USERPROFILE%My DocumentsWindowsPowerShellProfile.ps1” (don’t worry, Vista maps “My Documents” to the appropriate new physical folder for backwards compatibility with XP)
  • One of the main advantages that PowerShell provides beyond previously existing scripting technologies (Windows or otherwise) is this: they’ve taken the power of command line piping and implemented it at an object oriented level versus simple flat text… this alone makes PowerShell game changingly more powerful than what Unix/OS X/Linux could lay claim to fame.
    • For an immeidate mental example to take in how profound this is… take a common task like piping LS to SORT so that you can sort by file size (ls does not support file size sorting via an argument)… eg:
    • ls -al | sort +4nr
    • the problem is (as this immediate Google hit proves) that this depends on hard coding which column of the flat text output you want operate on
    • PowerShell elegantly eliminates this issue by allowing us to pass true data structures along the pipeline such that we can cleanly sort on the “size” property!!  not some random column of flat text… eg:
    • PS C:> dir | sort-object Length –descending
    • Is that not freaking beautiful or what!?!?  They’ve really taken some effort to make the commands and syntax humanly readable… i think that is giving a big nod to the fact that the unfortunate evolution of Windows has created a class of so called Sys Admins that are dismally dependent on clicking pretty buttons rather than automating away their recurring issues
  • We get full access to everything in the .Net Framework from a friendly script syntax… there’s already a gaggle of PowerShell wrappers (aka “cmdlets”) that hide the uglier details of accessing Windows Registry, Web Services, XML, WMI, Active Directory, Exchange Server, SQL Server Admin, SharePoint… on and on
  • Cmdlets are just more PowerShell code bundled in a subroutine (plus .Net “snapin” DLLs where needed) so there has been tons of rapid adoption where folks are creating nice pretty PowerShell cmdlet wrappers around everything with more and more publicly available cmdlet libraries all the time.
  • Windows Forms via PowerShell is pretty cool… PowerShell’s script is a pretty easy flowing syntax that washes away the usual pain of stuff like declaring variables of the proper type and has loads of friendly libraries available to do all the heavy lifting… marry that with .Net Forms GUIs and you can get some nifty stuff rolling in a day…
  • See my first script below… it does a nice batch of meat and potatoes ditch work in about 75 short lines of fairly readable code
  • Invoke-Win32” – The code gods have already built us an awesome “function which uses code generation to dynamically create a .Net P/Invoke definition” so that we can fire Win32 API’s on a whim! Toss just the Invoke-Win32 function from that page into your Profile.ps1 right after everything else.
  • Hide-PowerShell() – (code posted below) very handy for hiding the PowerShell console when all you really want to see is the Window Forms stuff you threw together… just toss this short code into your Profile.ps1 as well
  • PowerShellPlus – very nice IDE with gonzo Intellisense & context Help for E-VER-Y-THING in da Shell… love it… running 2.1.0.45 Beta, hasn’t crashed once. [Update: 21 Sep 2010] Looks like they’re already up to v3.5
  • [Update: 21 Sep 2010] Windows 7 includes a very decent IDE for free: Start > Program > Accessories > Windows PowerShell > Windows PowerShell ISE

My First PowerShell Script (awww aint she a cutie 😉

  1. Hits SQL Server to pull a list of data via the ol’ ADO.Net APIs
  2. Pings all those machines to determine if they’re online (currently commented out but it runs quite fast),
  3. Throws the list into a simple Windows Forms ListBox for user selection
  4. and finally sets a registry entry (an ODBC System DSN Server entry) based on the chosen selection & launches a an app

This little ditty is a great example for scripting… it wrappers another legacy app with a front end that compensates for something lacking.

Hide-PowerShell 

$cn = new-object system.data.SqlClient.SqlConnection("Data Source=blah;User ID=blah;Password=blah"); 
$ds = new-object "System.Data.DataSet" "dsTaxOffices" 
$q = "SELECT Name + ' ('+Code+')' as Name, IPAddress FROM master.dbo.TaxOffices (nolock) where Active = 1" 
$da = new-object "System.Data.SqlClient.SqlDataAdapter" ($q, $cn) 
$da.Fill($ds) 

# ping each box to see who's online 
#foreach($row in $ds.Tables[0].Rows) { 
# $ipaddress = $row["IPAddress"] 
# $ping_result = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$ipaddress'" 
# if ($ping_result.StatusCode -ne "0") { 
# $row["Name"] = $row["Name"] + "`t* offline *" 
# } 
#} 

# Load Windows Forms Library 
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null 

#pointer for easy debug message box popups 
$mb=[Windows.Forms.MessageBox] 
#$mb::Show("you clicked me! ", "Message") 

# Create Window 
$form = new-object "System.Windows.Forms.Form" 
$form.Size = new-object System.Drawing.Size @(300,377) 
$form.topmost = $true 
$form.text = "Choose Office" 
$form.FormBorderStyle = "FixedToolWindow" 
#$form.add_load({ 
    #$mb::Show("onload", "onload") 
# $form.WindowState = "Normal" 
#}) 

# Create Flow Panel 
#$panel = new-object "System.Windows.Forms.flowlayoutpanel" 
#$panel.Dock = [System.Windows.Forms.DockStyle]::Fill 
#$form.Controls.Add($panel) 

# Create Controls 
$lst = new-object System.Windows.Forms.ListBox 
#$lst.FormattingEnabled = true; 
#$lst.TabIndex = 0; 
$lst.Location = new System.Drawing.Point(3, 3); 
$lst.Name = "listBox1"; 
$lst.Size = New-Object System.Drawing.Size @(285, 320); 

# databind list of offices to listbox 
$lst.ValueMember = "IPAddress" 
$lst.DisplayMember = "Name" 
$lst.DataSource = $ds.Tables[0] 

$btnSelect = New-Object System.Windows.Forms.Button 
$btnSelect.Location = New-Object System.Drawing.Point @(65, 325) 
$btnSelect.Name = "button1"; 
$btnSelect.Size = New-Object System.Drawing.Size @(159, 23); 
#$btnSelect.TabIndex = 1; 
$btnSelect.Text = "Select"; 
#$btnSelect.UseVisualStyleBackColor = true; 
$form.AcceptButton = $btnSelect 

$btnSelect.add_click({ 
    Set-ItemProperty hklm:softwareodbcodbc.iniitraac Server $lst.SelectedItem.IPAddress 
    Get-Process | Where-Object {$_.Name -eq "itraacw32"} | kill 
    sleep 1 #wait a sec for the old process to really drop off, otherwise new one collides and stops itself 
    start "C:Program FilesiTRAAC ConsoleiTRAACw32.exe" 
    $form.Close() 
}) 

# Add controls to Panel 
$form.Controls.Add($lst) 
$form.Controls.Add($btnSelect) 

# Show window 
$form.showdialog()

Everybody needs a picture right?

image

Hide-PowerShell() script source

function ShowWindowAsync([IntPtr] $hWnd, [Int32] $nCmdShow) 
{ 
  $parameterTypes = [IntPtr], [Int32]  
  $parameters = $hWnd, $nCmdShow 
  Invoke-Win32 "user32.dll" ([Boolean]) "ShowWindowAsync" $parameterTypes $parameters 

    # Values for $nCmdShow 
    # SW_HIDE = 0; 
    # SW_SHOWNORMAL = 1; 
    # SW_NORMAL = 1; 
    # SW_SHOWMINIMIZED = 2; 
    # SW_SHOWMAXIMIZED = 3; 
    # SW_MAXIMIZE = 3; 
    # SW_SHOWNOACTIVATE = 4; 
    # SW_SHOW = 5; 
    # SW_MINIMIZE = 6; 
    # SW_SHOWMINNOACTIVE = 7; 
    # SW_SHOWNA = 8; 
    # SW_RESTORE = 9; 
    # SW_SHOWDEFAULT = 10; 
    # SW_MAX = 10 
} 
function Show-PowerShell() { 
  $null = ShowWindowAsync (Get-Process -Id $pid).MainWindowHandle 1  
} 
function Hide-PowerShell() {  
    $null = ShowWindowAsync (Get-Process -Id $pid).MainWindowHandle 0  
} 
function Minimize-PowerShell() {  
    $null = ShowWindowAsync (Get-Process -Id $pid).MainWindowHandle 2  
}