views:

161

answers:

3

I'm trying to get get the short filename from a long filename but I'm having problem in c# code. VB.Net code is:

Declare Function GetShortPathName Lib "kernel32" _
    Alias "GetShortPathNameA" (ByVal lpszLongPath As String, _
    ByVal lpszShortPath As String, ByVal cchBuffer As Long) As Long

Public Function GetShortName(ByVal sLongFileName As String) As String
    Dim lRetVal As Long, sShortPathName As String, iLen As Integer
    'Set up buffer area for API function call return
    sShortPathName = Space(255)
    iLen = Len(sShortPathName)

    'Call the function
    lRetVal = GetShortPathName(sLongFileName, sShortPathName, iLen)
    'Strip away unwanted characters.
     GetShortName = Left(sShortPathName, lRetVal)
End Function

I've converted this function to c#:

[DllImport("kernel32", EntryPoint = "GetShortPathNameA")]
static extern long GetShortPathName(string lpszLongPath, string lpszShortPath, long cchBuffer);

public string GetShortName(string sLongFileName)
{
    long lRetVal;
    string sShortPathName;
    int iLen;

    // Set up buffer area for API function call return
    sShortPathName = new String(' ', 1024);
    iLen = sShortPathName.Length;

    // Call the function
    lRetVal = GetShortPathName(sLongFileName, sShortPathName, iLen);

    // Strip away unwanted characters.
    return sShortPathName.Trim();
}

But I cant get the c# version work. Am I missing something or what is wrong?

+3  A: 

Maybe because of CharSet/Marshaling, remember also that string are imutable. Try the following :

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError=true)]
static extern uint GetShortPathName(
   [MarshalAs(UnmanagedType.LPTStr)]
   string lpszLongPath,
   [MarshalAs(UnmanagedType.LPTStr)]
   StringBuilder lpszShortPath,
   uint cchBuffer);

(From pinvoke)

/// <summary>
/// The ToLongPathNameToShortPathName function retrieves the short path form of a specified long input path
/// </summary>
/// <param name="longName">The long name path</param>
/// <returns>A short name path string</returns>
public static string ToShortPathName(string longName)
{
    uint bufferSize = 256;

    // don´t allocate stringbuilder here but outside of the function for fast access
    StringBuilder shortNameBuffer = new StringBuilder((int)bufferSize);

    uint result = GetShortPathName(longName, shortNameBuffer, bufferSize);

    return shortNameBuffer.ToString();
}
Guillaume
+2  A: 

pinvoke.net lists the import at such:

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError=true)]
static extern uint GetShortPathName(
   [MarshalAs(UnmanagedType.LPTStr)]
   string lpszLongPath,
   [MarshalAs(UnmanagedType.LPTStr)]
   StringBuilder lpszShortPath,
   uint cchBuffer);

pinvoke.net is a great reference for these types of issues where you're not exactly sure how the singature would translate.

That would change your code like so:

public static string GetShortName(string sLongFileName)
{
    long lRetVal;
    int iLen = 1024;
    StringBuilder sShortPathName = new StringBuilder(iLen);

    // Call the function
    lRetVal = GetShortPathName(sLongFileName, sShortPathName, iLen);

    // Strip away unwanted characters.
    return sShortPathName.ToString();
}
Joe Doyle
+2  A: 

The VB declaration dates back to VB6, it is quite inappropriate for a .NET language. Although the P/Invoke marshaller will allow unmanaged code scribbling into a string, it causes random failure due to string interning. You also really want to use the Unicode version so you don't get unexpected character translation. And you want to do something meaningful if the function fails. Here's my version:

public static string GetShortName(string sLongFileName) {
  var buffer = new StringBuilder(259);
  int len = GetShortPathName(sLongFileName, buffer, buffer.Capacity);
  if (len == 0) throw new System.ComponentModel.Win32Exception();
  return buffer.ToString();
}

[DllImport("kernel32", EntryPoint = "GetShortPathName", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetShortPathName(string longPath, StringBuilder shortPath, int bufSize);
Hans Passant