[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetVolumePathNamesForVolumeNameW(string lpszVolumeName,
char [] lpszVolumePathNames, uint cchBuferLength,
ref UInt32 lpcchReturnLength);
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetVolumePathNamesForVolumeNameW(
[MarshalAs(UnmanagedType.LPWStr)]
string lpszVolumeName,
[MarshalAs(UnmanagedType.LPWStr)]
string lpszVolumePathNames,
uint cchBuferLength,
ref UInt32 lpcchReturnLength);
Declare Function GetVolumePathNamesForVolumeName Lib "kernel32.dll" (TODO) As TODO
Imports System.Runtime.InteropServices
Partial Public Class Win32Methods
'''Return Type: BOOL->int
'''param0: LPCTSTR->LPCWSTR->WCHAR*
'''param1: LPTSTR->LPWSTR->WCHAR*
'''param2: DWORD->unsigned int
'''param3: PDWORD->DWORD*
<DllImportAttribute("kernel32.dll", _
SetLastError:=True, _
EntryPoint:="GetVolumePathNamesForVolumeNameW")> _
Public Shared Function GetVolumePathNamesForVolumeNameW(
<[In]()> <MarshalAs(UnmanagedType.LPTStr)>
ByVal sVolumeName As String, _
<MarshalAs(UnmanagedType.LPWStr)> _
ByVal lpBuffer As IntPtr, _
ByVal uintBufferLen As UInteger, _
<Out()> ByRef uintReturnLen As UInteger) _
As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
End Class
None.
Do you know one? Please contribute it!
The VB Code was generated by the tool at http://clrinterop.codeplex.com/releases/view/14120. I believe this to be the same tool referenced at http://blogs.msdn.com/b/vbteam/archive/2008/03/14/making-pinvoke-easy.aspx. The StringBuilder limitation appears to apply to this code as well, and it will only return the first path/mountpoint found, but the method does work inside that limitation. I will post updated code as soon as I code and test.
Updated: VB code now uses IntPtr instead of StringBuilder - see sample below
Using StringBuilder for the buffer does not work since this function returns the names as a bunch of strings separated by the null character ('\0'). So StringBuilder would only report the first item. Therefore we store it in a char array, which can later be converted to a bunch of strings.
Please add some!
UInt32 dwRequired = 0;
char[] buffer = new char[260];
if (!GetVolumePathNamesForVolumeName(volumeName, buffer, (uint)buffer.Length, ref dwRequired))
{
// Not enough room in buffer perhaps? Try a bigger one
buffer = new char[dwRequired];
if (!GetVolumePathNamesForVolumeName(volumeName, buffer, (uint)buffer.Length, ref dwRequired))
throw new Win32Exception();
}
public List<string> GetMountPointsForVolume(string volumeDeviceName)
{
List<string> result = new List<string>();
// GetVolumePathNamesForVolumeName is only available on Windows XP/2003 and above
int osVersionMajor = Environment.OSVersion.Version.Major;
int osVersionMinor = Environment.OSVersion.Version.Minor;
if (osVersionMajor < 5 || (osVersionMajor == 5 && osVersionMinor < 1))
{
return result;
}
try
{
uint lpcchReturnLength = 0;
string buffer = "";
GetVolumePathNamesForVolumeNameW(volumeDeviceName, buffer, (uint)buffer.Length, ref lpcchReturnLength);
if (lpcchReturnLength == 0)
{
return result;
}
buffer = new string(new char[lpcchReturnLength]);
if (!GetVolumePathNamesForVolumeNameW(volumeDeviceName, buffer, lpcchReturnLength, ref lpcchReturnLength))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
string[] mounts = buffer.Split('\0');
foreach (string mount in mounts)
{
if (mount.Length > 0)
{
result.Add(mount);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return result;
}
Private Sub ListMountPoints(Optional strVolumeName As String = "")
' if no Volume GUID passed to sub, use this one (corresponds to my USB stick)
If strVolumeName = "" Then
strVolumeName = "\\?\Volume{9a615499-414c-11e0-bd72-78e7d1722cbc}\"
End If
' make this a static in order to stop race condition/thread lock - suspect garbage collector
Static lpBuffer As IntPtr = Marshal.AllocHGlobal(1)
Dim uintBufferLen As UInteger = 0, uintBuffReqLen As UInteger = 0
Const ERROR_MORE_DATA = 234, ERROR_FILE_NOT_FOUND = 2
Dim retval As Boolean = False
Try
' call function to get Required Buffer Length first
retval = Win32Methods.GetMountPointsIntPtr(strVolumeName, lpBuffer, uintBufferLen, uintBuffReqLen)
If Not retval Then
Dim errVal As Integer = Marshal.GetLastWin32Error()
If errVal = ERROR_MORE_DATA Then
'MsgBox("The last Win32 error was: ERROR_MORE_DATA")
ElseIf errVal = ERROR_FILE_NOT_FOUND Then
MsgBox("File not found, did you remember to plug in the USB drive?")
Exit Sub
Else
MsgBox("Error! The last Win32 error was: " + CStr(errVal))
End If
End If
' now set the buffer length
uintBufferLen = uintBuffReqLen
'zero out the length indicator
uintBuffReqLen = 0
Catch ex As Exception
Dim errVal As Integer = Runtime.InteropServices.Marshal.GetLastWin32Error()
MsgBox(ex.Message + ". The last Win32 error was: " + CStr(errVal))
End Try
' now call the function again with the right buffer length
lpBuffer = Runtime.InteropServices.Marshal.ReAllocHGlobal(lpBuffer, CType(uintBuffReqLen, IntPtr))
' uintBufferLen = uintBuffReqLen
uintBuffReqLen = 0
Try
If CBool(Not (Win32Methods.GetMountPointsIntPtr(strVolumeName, lpBuffer, uintBufferLen, uintBuffReqLen))) Then
Dim errVal As Integer = Runtime.InteropServices.Marshal.GetLastWin32Error()
MsgBox("The last Win32 error was: " + CStr(errVal))
End If
Catch ex As Exception
Dim errVal As Integer = Runtime.InteropServices.Marshal.GetLastWin32Error()
MsgBox(ex.Message + ". The last Win32 error was: " + CStr(errVal))
End Try
Dim strOutput As String = Runtime.InteropServices.Marshal.PtrToStringUni(lpBuffer, CInt(uintBuffReqLen))
Dim tmp As Integer = InStr(strOutput, vbNullChar)
strOutput = Strings.Replace(strOutput, vbNullChar, vbCrLf, , , vbBinaryCompare)
' Free the buffer.
Runtime.InteropServices.Marshal.FreeHGlobal(lpBuffer)
lpBuffer = IntPtr.Zero
MsgBox(strVolumeName + vbCrLf + " has the following mountpoints: " + vbCrLf + strOutput.ToString)
End Sub