Mapping NT Device Names to Drive Letters and vice-versa

Device Names and Drive Letters

This tip demonstrates how you convert between MS-DOS style drive letters and NT-style device names. This can be useful if you're working with an API that exposes an NT device name, such as IMAPI for CD Burning.

About Device Names

Under Windows NT, devices like hard disks, serial ports have an internal Windows NT name typically of the form \Device\[Name][Number]. These can optionally be mapped by the Windows NT Object Mapper onto the legacy MS-DOS style drive letters like A:, C:, LPT1: etc. Programs typically cannot use the device names directly, but you can map to and from the device name and mapped names using the code presented here.

Device Name Functions

There are two functions used in this sample:

  • QueryDosDeviceW
    Takes a DOS-style name and returns the NT device name.
  • GetLogicalDriveStringsA
    Returns the list of all drive strings present on the system.

QueryDosDevice takes the DOS name, without the trailing backslash, and puts the equivalent device name into a buffer. The buffer is terminated with two trailing nulls (the reason for this is that the lpDeviceName parameter can be set to null, in which case all device names are returned in a null delimited list). The following code demonstrates how to use the function in VB:

Private Declare Function QueryDosDeviceW Lib "kernel32.dll" ( _
    ByVal lpDeviceName As Long, _
    ByVal lpTargetPath As Long, _
    ByVal ucchMax As Long _
    ) As Long
Private Const MAX_PATH = 260

Public Function GetNtDeviceNameForDrive( _
   ByVal sDrive As String) As String
Dim bDrive() As Byte
Dim bResult() As Byte
Dim lR As Long
Dim sDeviceName As String

   If Right(sDrive, 1) = "\" Then
      If Len(sDrive) > 1 Then
         sDrive = Left(sDrive, Len(sDrive) - 1)
      End If
   End If
   bDrive = sDrive
   ReDim Preserve bDrive(0 To UBound(bDrive) + 2) As Byte
   ReDim bResult(0 To MAX_PATH * 2 + 1) As Byte
   lR = QueryDosDeviceW(VarPtr(bDrive(0)), VarPtr(bResult(0)), MAX_PATH)
   If (lR > 2) Then
      sDeviceName = bResult
      sDeviceName = Left(sDeviceName, lR - 2)
      GetNtDeviceNameForDrive = sDeviceName
   End If
End Function

There is no direct function to perform a reverse mapping (i.e. determine a DOS drive name from an NT device name). However, you can do the same thing by looping through the attached drives and getting the device name for each one. Strangely VB does not include a means of listing drives (although I suppose you could use the shell scripting object or even the DriveListBox control) but the list isn't hard to get with the GetLogicalDriveStrings function. This takes a buffer and fills it with a null-delimited list of drive names, completing the list with two terminating nulls. Calling the function with a null buffer causes it to return the size of the buffer needed to hold the entire list:

Private Declare Function GetLogicalDriveStringsA Lib "kernel32" ( _
    ByVal nBufferLength As Long, lpBuffer As Any) As Long

Public Function GetDrives() As Collection
Dim colDrives As New Collection
Dim lSize As Long
Dim lR As Long
Dim iLastPos As Long
Dim iPos As Long
Dim sDrive As String
Dim sDriveStrings As String

   lSize = GetLogicalDriveStringsA(0, ByVal 0&)
   sDriveStrings = String(lSize + 1, 0)
   lR = GetLogicalDriveStringsA(lSize, ByVal sDriveStrings)
   iLastPos = 1
      iPos = InStr(iLastPos, sDriveStrings, vbNullChar)
      If Not (iPos = 0) Then
         sDrive = Mid$(sDriveStrings, iLastPos, iPos - iLastPos)
         iLastPos = iPos + 1
         sDrive = Mid$(sDriveStrings, iLastPos)
      End If
      If Len(sDrive) > 0 Then
         colDrives.Add sDrive
      End If
   Loop While Not (iPos = 0)
   Set GetDrives = colDrives
End Function

Using this list we can get the device name for a drive just by looping through the drive list until there's a match:

Public Function GetDriveForNtDeviceName( _
   ByVal sDeviceName As String) As String
Dim sFoundDrive As String
Dim colDrives As Collection
Dim vDrive As Variant

   For Each vDrive In GetDrives()
      If (GetNtDeviceNameForDrive(vDrive) = sDeviceName) Then
         sFoundDrive = vDrive
         Exit For
      End If
   GetDriveForNtDeviceName = sFoundDrive
End Function

Wrap Up

This sample demonstrates how to map to and from NT Device names and the equivalent drive letters under Windows NT. This function is normally not needed, but can be useful when working with an API that works at this level, for example the IMAPI.