2013/01/21

[C#] Windows 目錄的長路徑與短路徑轉換

因為 Windows 的目錄/檔名允許空白字元, 所以造成寫命令列指令(ex: java)時往往要用雙引號(")包裝路徑, 以避免空白字元分隔了參數.
不過最近用了一個 SVG 轉圖的 Java 套件(SVG Rasterizer), 卻不接受雙引號框住的路徑. 反而要將路徑轉為無空白字元的短路徑才行.

在 C# 中要做到長短路徑互換, 可透過 DllImport 的方式來使用以下兩個 Windows 的函式:
GetShortPathNameGetLongPathName.

GetShortPathName 的使用範例如下:

// 透過 DllImport 呼叫 Windows API
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetShortPathName(
    [MarshalAs(UnmanagedType.LPTStr)] string path,
    [MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath,
    int shortPathLength
);

static void Main(string[] args) {
  // 給定一個長路徑+檔名的字串
  string longPath = @"C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 4\thirdpartynotices.rtf";
  // 因為 Windows 本身有路徑長度的限制, 所以給 255 即可
  StringBuilder sbPath = new StringBuilder(255);
  // 此函式會回傳轉換後的短路徑字串長度
  // , 如果轉換失敗會傳回0 
  int result = GetShortPathName(longPath, sbPath, sbPath.Capacity);
  // 可用 sbPath.ToString(0, result) 
  // 或 sbPath.ToString() 取得轉換後的結果
  string shortPath = sbPath.ToString();
  Console.WriteLine("result={0}, path={1}", result, shortPath);
}
結果如下:
result=47, path=C:\PROGRA~2\MICROS~2.NET\ASP~4.NET\THIRDP~1.RTF

GetLongPathName 的使用範例如下:

// 透過 DllImport 呼叫 Windows API
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetLongPathName(
    [MarshalAs(UnmanagedType.LPTStr)] string path,
    [MarshalAs(UnmanagedType.LPTStr)] StringBuilder longPath,
    int longPathLength
);

static void Main(string[] args) {
  // 給定一個短路徑+檔名的字串
  string shortPath = @"C:\PROGRA~2\MICROS~2.NET\ASP~4.NET\THIRDP~1.RTF";
  // 因為 Windows 本身有路徑長度的限制, 所以給 255 即可
  StringBuilder sbPath = new StringBuilder(255);
  // 此函式會回傳轉換後的長路徑字串長度
  // , 如果轉換失敗會傳回0 
  int result = GetLongPathName(shortPath, sbPath, sbPath.Capacity);
  // 可用 sbPath.ToString(0, result) 
  // 或 sbPath.ToString() 取得轉換後的結果
  string longPath = sbPath.ToString();
  Console.WriteLine("result={0}, path={1}", result, longPath);
}
結果如下:
result=76, path=C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 4\thirdpartynotices.rtf

由於回傳值是處理過後的字串長度, 所以可從其值是否大於 0 來判斷是否有轉換成功.
如果是拿一個實際不存在的路徑去轉換, 就會產生傳回 0 的情況.

經過這樣的函式將路徑轉為短路徑後, 在使用 SVG Rasterizer 做批次的轉圖時, 就可使用以下的指令:
(C:\Imgs\2013_01_17_19_14_54\3508 轉換為  C:\Imgs\2013_0~3\3508)

java -jar .\batik-rasterizer.jar -d output -m image/png  -w 1535 C:\Imgs\2013_0~3\3508\*.txt
不然用 "C:\Imgs\2013_01_17_19_14_54\3508\*.txt" 做參數, 會傳回 NullPointerException
Exception in thread "main" java.lang.NullPointerException   at org.apache.batik.apps.rasterizer.SVGConverterURLSource.(SVGConverterURLSource.java:56)
  at org.apache.batik.apps.rasterizer.SVGConverter.computeSources(SVGConverter.java:798)
  at org.apache.batik.apps.rasterizer.SVGConverter.execute(SVGConverter.java:677)
  at org.apache.batik.apps.rasterizer.Main.execute(Main.java:938)
  at org.apache.batik.apps.rasterizer.Main.main(Main.java:992)

沒有留言:

張貼留言