Я пытаюсь получить короткое имя файла из длинного имени файла, но у меня проблема с кодом С#. Код VB.Net:
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
Я преобразовал эту функцию в С#:
[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();
}
Но я не могу получить работу с версией С#. Я что-то упустил или что не так?
Объявление VB восходит к VB6, это довольно неуместно для языка .NET. Хотя маркерщик P/Invoke позволяет неуправляемому шифрованию кода в строку, он вызывает случайный сбой из-за интернирования строк. Вы также действительно хотите использовать версию Unicode, чтобы не получить неожиданный перевод символов. И вы хотите сделать что-то значимое, если функция не удалась. Здесь моя версия:
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);
Возможно, из-за CharSet/Marshaling, помните также, что строка является imutable. Попробуйте следующее:
[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)
/// <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();
}
pinvoke.net перечисляет импорт:
[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 - отличная ссылка на эти типы проблем, где вы не совсем уверены, как будет переводиться певчий.
Это изменит ваш код так:
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();
}