Asked By Jeff
14-Sep-07 08:58 AM

I'm sure there is a better way to do this, but this is what I came up
with:
param( [string]$filePath = $( throw "You must provide a path to a COM
server." ), [switch]$ShowProgress )
if ( !( Test-Path "$( Get-Location)\PathShortener.dll" ) )
{
$code = @'
using System;
using System.Text;
using System.Runtime.InteropServices;
public class PathShortener
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto,
SetLastError = true)]
public static extern int GetShortPathName(
[MarshalAs( UnmanagedType.LPTStr )] string lpszLongPath,
[MarshalAs( UnmanagedType.LPTStr )] StringBuilder
lpszShortPath,
uint cchBuffer);
public static string GetShortPath( string longPath )
{
string shortPath = longPath;
StringBuilder shortPathBuilder = new StringBuilder(260);
int result = GetShortPathName( longPath,
shortPathBuilder,
(uint)shortPathBuilder.Capacity );
if ( result > 0 )
{
shortPath = shortPathBuilder.ToString();
}
return shortPath;
}
}
'@
$cSharpCodeProvider = New-Object
Microsoft.CSharp.CSharpCodeProvider
$parameters = New-Object
System.CodeDom.Compiler.CompilerParameters
$parameters.GenerateInMemory = $true
$parameters.GenerateExecutable = $false
$parameters.OutputAssembly = "$( Get-Location)\PathShortener.dll"
$compilerResults =
$cSharpCodeProvider.CompileAssemblyFromSource( $parameters, $code )
if ( $results.Errors.Count -gt 0 )
{
Throw "Could not compile PathShortener class"
}
}
[System.Reflection.Assembly]::LoadFile( "$( Get-Location)
\PathShortener.dll" ) | Out-Null
$longPath = $filePath.ToLower()
$shortPath = [PathShortener]::GetShortPath( $filePath ).ToLower()
if ( ( New-Object System.IO.FileInfo $longPath ).Extension.ToLower() -
eq ".exe" )
{
$serverKeys = Get-Item HKLM:\SOFTWARE\Classes\CLSID\*
\LocalServer32
}
else
{
$serverKeys = Get-Item HKLM:\SOFTWARE\Classes\CLSID\*
\InProcServer32
}
$serverCount = $serverKeys.Length
$serverProgress = 0;
foreach ( $serverKey in $serverKeys )
{
if ( $ShowProgress )
{
Write-Progress -Activity "Searching servers for
'$filePath'..." `
-Status "Progress:" `
-PercentComplete ( ( $serverProgress++ /
$serverCount ) * 100 )
}
if ( $serverKey.GetValue( "" ) )
{
$foundPath = $serverKey.GetValue( "" ).ToLower() -replace '"'
if ( $foundPath -eq $longPath -or $foundPath -eq $shortPath )
{
if ( $serverKey.PSPath -match "\\([^\\]+)\\[^\\]+$" )
{
$clsid = $matches[ 1 ].ToLower()
$clsidKeys = Get-Item HKLM:\SOFTWARE\Classes\*\CLSID
$clsidCount = $clsidKeys.Length
$clsidProgress = 0
foreach ( $clsidKey in $clsidKeys )
{
if ( $ShowProgress )
{
Write-Progress -Activity "Searching CLSIDs for
'$clsid'..." `
-Status "Progress:" `
-PercentComplete
( ( $clsidProgress++ / $clsidCount ) * 100 )`
-Id 1
}
if ( $clsidKey.GetValue( "" ) -and
$clsidKey.GetValue( "" ).ToLower() -eq $clsid )
{
if ( $clsidKey.PSPath -match "\\([^\\]+)\\CLSID
$" )
{
$matches[ 1 ]
}
}
}
}
}
}
}
Use it like this:
PSH$ .\Get-ProgId.ps1 'C:\Program Files\Internet Explorer
\iexplore.exe' -ShowProgress
This attempts to list all ProgIds associated with the file passed in
as a parameter. I say "attempts" because I am sure I missed
something. I had to put the GetShortPathName call in because short
paths are used quite often in the registry. It is really slow, but it
seems to do the trick. I added a switch parameter that determines
whether progress is displayed, since Write-Progress calls slow it down
so much more.
Jeff