The Hammer of Thor, the Lightening of Zeus, None compares to Windows Powershell! It can CLOSE MY OPEN FILES!

The Hammer of Thor, the Lightening of Zeus, None compares to Windows Powershell! It can CLOSE MY OPEN FILES!

  • Comments 2
  • Likes

Hi, I’m Sean.

I’m an ITPro and about three years ago (and you can’t tell my wife this) I began my affair with Windows Powershell.   As you can tell from my background (and forget all the silly letters) I really just consider myself a true blue, grease under my fingers, crimpers in my pocket, screwdriver in my mouth, TECH.

A few months back somebody asked on TechNet Talk Radio if Powershell could close files.   It might be able to do this but somebody else already wrote a great utility that already does this.  It’s called HANDLE.EXE from Sysinternals 

The STRENGTH of this application is that it works well and can’t accidentally close all the files on the server. The weakness is that I cannot easily (out of the box) automate it. Or can I?

Within Powershell one of the biggest pieces you need to remember is that everything (And I mean EVERYTHING) is returned as an “Object”.

Now the rest of you ItPros, quit hissing like Gollum.   An “Object” really isn’t scary or nasty.   You won’t turn into “Darth Vader” because you used one.

So here we’ve downloaded HANDLE.EXE into a little folder called C:\HANDLE PROGRAM and we’re going to query for all the open Word documents on the local computer.  As per the instructions in HANDLE.EXE I have run the console as an Administrator to gain the rights needed to globally close files

C:\Handle Program\HANDLE.EXE .DOCX

And we’ll get some output on the screen like this

Handle v3.45
Copyright (C) 1997-2011 Mark Russinovich
Sysinternals - www.sysinternals.com

WINWORD.EXE        pid: 13352  type: File           15C: C:\TEC2011\Why does Sean always leave his files open.docx
WINWORD.EXE        pid: 13352  type: File           1B0: C:\TEC2011\If Sean Keeps doing this we won.docx
WINWORD.EXE        pid: 7496   type: File            1A8: C:\TEC2011\demodocbadsean.docx

It appears “SOMEBODY” who shall remain nameless has been leaving their Word documents open (tap tap tap)

Normally if I would like to close an open file in HANDLE.EXE I would execute

C:\Handle Program\HANDLE.EXE -c 15c -p 13352 –y

And we would get an output like this, which if it’s successful even outputs a line with the status at the end.

Handle v3.45
Copyright (C) 1997-2011 Mark Russinovich
Sysinternals - www.sysinternals.com

  15C: File  (R--)   C:\TEC2011\Why does Sean always leave his files open.docx

Handle closed.

So I can do this in Windows Powershell as well with a minor difference.  I’m going to store all the output from HANDLE.EXE (Since the output to my screen is an Object) into a Variable.

$RESULTS=(& 'C:\HANDLE PROGRAM\HANDLE.EXE' .docx)

So the output that WOULD have gone to the console (which is an Object) is stored in $RESULTS.   If you type $RESULTS into the Powershell Console you’ll see the same output as before.

But since it’s in a variable we can now access and manipulate that output in a way we couldn’t before.  If we look at the screen we’ll see that the output is consistent.   All the lines that have the information we need contain “pid:” just before the Process and “type: File” just before the File handle.

So in Windows Powershell I can take the [String] object that is stored in $RESULTS (My Console output) and pull all of that out with a quick comparison, which I’ll store away in a variable I’ll call $HANDLEDATA

$HANDLEDATA=$RESULTS –match “pid:”

Which will output to the screen only that data.  At this point we can run each line through a SELECT-STRING to find where in the string the content sits.  I need to find the location of “pid:” and “type: file” in each line and pull the number following it out.  I’m going to work on the first entry in the [String] array as an example

We’ll grab the ProcessID number first.  SELECT-STRING will find it’s position which we’ll store away as $StartPid

$StartPid=($HandleData[0] | SELECT-STRING 'pid:').matches[0].Index

Then we’ll access the content which is consistently starting at positions away (The length of ‘pid: ‘) and is never more than 7 bytes long (I counted Smile with tongue out).  We tack on the “trim()” to make sure if there are any blank spaces before or AFTER the Number they are “Trimmed” off

$Processpid=$Handledata[0].substring($StartPid+5,7).trim()

We’ll repeat the process for the File Handle in a similar method.  Find the String with SELECT-STRING, Grab it’s index and then pull out what we need with a little substring()

$StartFileID=($HandleData[0] | SELECT-STRING 'type: File').matches[0].Index
$Fileid=$HandleData[0].substring($StartFileID+10,14).trim()

At this point we have a way of pulling out consistently the the necessary data to send it back to HANDLE.EXE and close it.  I can execute a line like this now after I pull the information on each line.

(& 'C:\HANDLE PROGRAM\HANDLE.EXE' –p $ProcessId –c $FileId -y)

Now this will work but looks messy on the screen.  We could just store the results away in a variable and grab that last line showing a success/failure

$CLOSEFILESTATUS=(& 'C:\HANDLE PROGRAM\HANDLE.EXE' –p $ProcessId –c $FileId -y)[-1]

Ok so there’s all the pieces.   So what does it look like as a script?

Just look below

-------------------------CLOSEWORDDOC.PS1-------------------------------------------------------------------------

#
# Script to close all open Word Documents on my File Server because Sean keeps forgetting
# Requires HANDLE.EXE from Sysinternals on the system.  Free download
#

$RESULTS=(& 'C:\HANDLE PROGRAM\HANDLE.EXE' .docx)

$HANDLEDATA=$RESULTS –match ‘pid: ‘

Foreach ($File in $Handledata)
     {
     # Get the Handle ID of the Process for this file

     $StartPid=($File | SELECT-STRING 'pid:').matches[0].Index
     $Processpid=$File.substring($StartPid+5,7).trim()

     # Get the File ID of this open File

     $StartFileID=($File | SELECT-STRING 'type: File').matches[0].Index
     $Fileid=$File.substring($StartFileID+10,14).trim()

     # Close It

     $CLOSEFILESTATUS=(& 'C:\HANDLE PROGRAM\HANDLE.EXE' –p $ProcessId –c $FileId -y)[-1]

     }

#

-------------------------CLOSEWORDDOC.PS1-------------------------------------------------------------------------

Now of course this is a pretty static solution.  We could improve this in so many ways like using a variable for the file name or extension, dropping in some error handling.  But this is to give you a taste of how you can automate systems that weren’t DESIGNED to automate.

If you want to have some real fun, here is a script that within Windows Powershell I took that SAME application (HANDLE.EXE) and extended it’s features into New Cmdlets via Advanced Functions.  One to show the open files, one to obtain their Process ID’s and FILE ID’s and a third to close them.  If you’re interested on where this one has gone I’ve uploaded it to the Technet Script Repository so you can download it.  With this version you can execute Cmdlets like

GET-openfile DOCX

To get the list

GET-openfile XLSX | Close-Openfile –whatif

To CAUTIOUSLY do it

GET-openfile DOCX | Close-Openfile

This can easily be turned into module as well (how I’m running on the system right now) to extend the abilities of your shell as well.  

If you’re interested more in playing with Powershell and some of your legacy applications, there is an entire series on “Hey Scripting Guy” which delves into this. 

Ah Powershell.  Where were without you before Smile

---------------

clip_image001

Sean Kearney is an infrastructure support analyst and Microsoft MVP in Window PowerShell. He is absolutely passionate about automation technologies—especially Windows PowerShell. If you say, “Powershell,” he may just break into song. Sean considers himself “just a tech at heart,” but he can often be found dabbling with most Microsoft technologies at a moment’s notice. We have also been advised to keep him at arm’s length from any open Starbucks in Seattle. We have been advised that any rumors about him ever singing for Microsoft JobsBlog are completely unfounded. Complete rubbish. Let us just keep it between you and me, OK?

Sean's contact information:
Twitter: EnergizedTech
Blog: Energized About Technology
Website: Powershell: Releasing the Power of Shell to You

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi, Sean.

    Wonder how it can be done for closing open files on a remote servers ?

    thanx,

    Nissan.

  • Absolutely

    Handle.exe is designed to be a local application. Having Powershell on the necessary server is the short term solution with Handle.exe.

    Now having said that it might be possible to have Powershell leverage other sysinternals utilities to do something similiar, have powershell as the filter calling up handle.exe remotely.

    If the server is running Windows Server 2003 R2 I would seriously look into installing the Windows Management Framework 2.0 on the destination server.

    There are additional benefits outside of this function. I leverage Powershell via PsRemoting to find which computer a user is logged into as well.

    Depending upon your configuration, I'd like to look into finding a solution for you.

    ping me at Sean at Powershell dot ca at your convenience

    Sean

    "The Energized Tech"