by Klaus Graefensteiner
24. June 2010 12:45
Introduction
I have been busy lately with writing lots of PowerShell scripts. One of the problems that gave me major headaches was passing path names that contain spaces like for example “C:\Program Files\My Tools\Make Me Rich.exe” from a PowerShell script to a console application. It seems like some tools can handle blanks and other’s don’t. I didn’t spent to much time doing a proper analysis, but this seemed to be an issue of the command line parsing routing that is being used in each of the utilities. I gave up trying to figure out how to pass the quotation marks to the console application and decided to go a different route.
Figure 1: Big rubber ducky
I created a little script that converts long path names to the short 8.3 DOS format before I passed it to the console application. This seemed to work.
To round this little script up I also added the reverse function to the library that would take a short 8.3 DOS file name and resolve it to its long path name.
By the way the P/Invoke code was copied from http://pinvoke.com.
The Script
$DebugPreference = "continue"
function Load-FileSystemHelper()
{
$Code =
@"
using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Globalization;
public class FileSystemHelper
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetShortPathName(
[MarshalAs(UnmanagedType.LPTStr)] string path,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath,
int shortPathLength);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.U4)]
private static extern int GetLongPathName(
[MarshalAs(UnmanagedType.LPTStr)]
string lpszShortPath,
[MarshalAs(UnmanagedType.LPTStr)]
StringBuilder lpszLongPath,
[MarshalAs(UnmanagedType.U4)]
int cchBuffer);
public static string GetShortPathName(string path)
{
StringBuilder shortPath = new StringBuilder(500);
if (0 == GetShortPathName(path, shortPath, shortPath.Capacity))
{
if (Marshal.GetLastWin32Error() == 2)
{
throw new Exception("File does not exist!");
}
else
{
throw new Exception("GetLastError returned: " + Marshal.GetLastWin32Error());
}
}
return shortPath.ToString();
}
public static string GetLongPathName(string shortPath)
{
if (String.IsNullOrEmpty(shortPath))
{
return shortPath;
}
StringBuilder builder = new StringBuilder(255);
int result = GetLongPathName(shortPath, builder, builder.Capacity);
if (result > 0 && result < builder.Capacity)
{
return builder.ToString(0, result);
}
else
{
if (result > 0)
{
builder = new StringBuilder(result);
result = GetLongPathName(shortPath, builder, builder.Capacity);
return builder.ToString(0, result);
}
else
{
throw new FileNotFoundException(
string.Format(
CultureInfo.CurrentCulture,
null,
shortPath),
shortPath);
}
}
}
}
"@
Add-Type -TypeDefinition $Code
}
function Get-DOSPathFromLongName([string] $Path)
{
Load-FileSystemHelper
$DOSPath = [FileSystemHelper]::GetShortPathName($Path)
Write-Debug $DOSPath
return $DOSPath
}
function Get-LongNameFromDOSPath([string] $Path)
{
Load-FileSystemHelper
$LongPath = [FileSystemHelper]::GetLongPathName($Path)
Write-Debug $LongPath
return $LongPath
}
$DOSPath = Get-DOSPathFromLongName -Path "C:\Program Files (x86)\"
Explorer $DOSPath
$LongPath = Get-LongNameFromDOSPath -Path $DOSPath
Explorer $LongPath
The Download
The script can be downloaded here: ShortAndLongPathNames.zip
Ausblick
PowerShell gets an A+ for .NET interoperability. Seriously!