Hey, Scripting Guy! How to Query the Contents of a Special Folder on a Windows 7 Computer?

Hey, Scripting Guy! How to Query the Contents of a Special Folder on a Windows 7 Computer?

  • Comments 1
  • Likes

 

Bookmark and Share

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! I need to query for the contents of a special folder on my Windows 7 computer. I would like to use Windows PowerShell to do this. I know I can use the Get-ChildItem cmdlet to return the contents of a folder, but I am not sure how to do this with a special folder, other than typing a long path that would change depending on who is logged on the computer. Am I out of luck? On the other hand, can this be easily done?

-- RC

 

Hey, Scripting Guy! AnswerHello RC,

Microsoft Scripting Guy Ed Wilson here You are in luck because we recently had hundreds of scripts written that accomplish exactly what you describe. Querying the contents of special folders was one of the events in the 2010 Scripting Games, so there are many scripts that illustrate different techniques for this task. The event was Beginner Event 7; the task was to list administrative tools that are installed on a computer. The scripting language of choice here is Windows PowerShell, but you could also use VBScript. The expert commentators have an example of using Windows PowerShell and using VBScript to list the administrative tools on a computer.

The BEG7_RCC.ps1 script is an interesting script. One of the things I like about it is that it displays the number of administrative tools that are installed on the computer, as well as numbering each of the tools in the printout. The results of running the BEG7_RCC.ps1 script are shown in the following image.

Image of results of running BEG7_RCC.ps1 script

The BEG7_RCC.ps1 script uses cls to clear the screen. CLS was the command typed in a DOS prompt to clear the screen, but in Windows PowerShell it is an alias for Clear-Host function. Clear-Host is a function, and not a cmdlet. This is shown here:

PS C:\Users\ed.NWTRADERS> Get-Alias cls
CommandType Name Definition
----------- ---- ----------
Alias cls Clear-Host
__________________________________________________________________________________________________
PS C:\Users\ed.NWTRADERS> Get-Command clear-host
CommandType Name Definition
----------- ---- ----------
Function Clear-Host $space = New-Object System.Management.A...

In general, I do not like to use aliases in a script, so I would change cls to Clear-Host. The script next initializes $i to 1. This is required to ensure an accurate count of the number of tools and to provide a starting point for the tool numbering.

The variable $admintoolsNamespace is set to 47, which is the ShellSpecialFolderConstant value associated with a special folder. The ShellSpecialFolderConstants enumeration has actually been superseded by the Constant Special Item ID List (CSIDL) constants. The CSIDL system was actually replaced with KNOWNFOLDERID values in Windows Vista. Using a variable to hold the number associated with the special folder is a good idea because it makes the script easier to read.

The shell.application COM object is a powerful object that provides many methods. One is the namespace method used here to connect to the special folder. The namespace method returns a folder object, which is shown here:

PS C:\> $admin = (new-object -com shell.application).namespace(47)
PS C:\> $admin
Title : Administrative Tools
Application : System.__ComObject
Parent :
ParentFolder : System.__ComObject
Self : System.__ComObject
OfflineStatus :
HaveToShowWebViewBarricade : False
ShowWebViewBarricade : False
PS C:\> $admin | gm
TypeName: System.__ComObject#{a7ae5f64-c4d7-4d7f-9307-4d24ee54b841}
Name MemberType Definition
---- ---------- ----------
CopyHere Method void CopyHere (Variant, Variant)
DismissedWebViewBarricade Method void DismissedWebViewBarricade ()
GetDetailsOf Method string GetDetailsOf (Variant, int)
Items Method FolderItems Items ()
MoveHere Method void MoveHere (Variant, Variant)
NewFolder Method void NewFolder (string, Variant)
ParseName Method FolderItem ParseName (string)
Synchronize Method void Synchronize ()
Application Property IDispatch Application () {get}
HaveToShowWebViewBarricade Property bool HaveToShowWebViewBarricade () {get}
OfflineStatus Property int OfflineStatus () {get}
Parent Property IDispatch Parent () {get}
ParentFolder Property Folder ParentFolder () {get}
Self Property FolderItem Self () {get}
ShowWebViewBarricade Property bool ShowWebViewBarricade () {get} {set}
Title Property string Title () {get}

One of the methods is the items method that returns a collection of items that reside in the namespace. The name property is returned to provide the list of administrative tools. The complete BEG7_RCC.ps1 script is shown here.

BEG7_RCC.ps1

cls
#Counter for tools
$i = 1
#Namespace constant for Administrative Tools
$admintoolsNamespace = 47
$admin = (new-object -com shell.application).namespace($admintoolsNamespace)
$adminTools = $admin.items()
#Output results
Write-Host "Number of administrative tools installed: "$adminTools.Count
Write-Host "---------------------------------------------"
$adminTools | foreach-object {write-host $i":" $_.Name; $i++}
Write-Host "---------------------------------------------"

The script can be written in a single line:

$i=1;(new-object -com shell.application).namespace(47).items()| % {"$i $($_.name)";$i++}

When the one-line command runs, it produces output similar to what is shown in the following image.

Image of output of one-line script

You can produce a list of a large number of namespace names by using the title property that was found earlier when using the Get-Member cmdlet. The command is shown here:

PS C:\> 1..500|%{(new-object -com shell.application).namespace($_).title}
The Internet
Programs
All Control Panel Items
Printers
My Documents
Favorites
Startup
Recent Items
SendTo
Recycle Bin
Start Menu
My Music
My Videos
Desktop
Computer
Network
Network Shortcuts
Fonts
Templates
Start Menu
Programs
<list truncated>

Though this is interesting, it does not provide the namespaces and the number that produced it. I decided the idea was pretty cool, and wrote a script that lists the ID number, the name, and the path to the special folder. It writes the information to a text file. The script is shown here.

Get-SpecialFolders.ps1

$file = "c:\fso\csidl.txt"
$rtn = New-Object psobject -Property @{"Value"=$null; "name" = $null; "path"=$null}
1..500 |
ForEach-Object {
$rtn.Value = $_
$rtn.name = (new-object -com shell.application ).namespace($_).title
$rtn.path = (new-object -com shell.application ).namespace($_).self.path
if($rtn.name -ne $null)
{
"$($rtn.value) $($rtn.name) $($rtn.path)" | Out-File -FilePath $file -Append
}
}
notepad $file

When the Get-SpecialFolders.ps1 script runs, it produces a text file similar to the one seen in the following image.

Image of text file produced by Get-SpecialFolders.ps1 script

RC, that is all there is to using special folders to produce a list of administrative tools. The 2010 Scripting Games Wrap-Up Week 2 will continue tomorrow.

 

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson and Craig Liebendorfer, Scripting Guys

 

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

    Is there a way to use KNOWNFOLDERIDs in vbscripts? I found:

    social.technet.microsoft.com/.../936b2915-14df-424b-8c30-490b42e86773

    Where someone says:

    "For an object to be accessible as an ActiveX (COM) automation object, it must implement the IDispatch interface. WSH-based scripts only support automation objects.

    IKnownFolder inherits from IUnknown, and I don't see any mention of it having an IDispatch interface. Thus, IKnownFolder would not be accessible from a VBScript WSH script."

    With VBscript, what is the alternative to hardcoding a KNOWNFOLDERD which doesn't have CSIDL? For example AppData/LocalLow has FOLDERID_LocalAppDataLow, but no CSIDL equivalent.