Skip to content

Latest commit

 

History

History
274 lines (210 loc) · 7.45 KB

File metadata and controls

274 lines (210 loc) · 7.45 KB

Home

Enumerating Volumes and Volume Mounting Points (NTFS)

Before you begin:

After the volume mount points have been established, they are maintained through computer restarts automatically.

The picture shows local CDDRIVE device mounted to D:\mnt\cddrive directory.

Handle this set of functions with care. Do not unmount root directories like C:, unless you know what you are doing.

LOCAL oVolumes As TVolumes, oVolume As TVolume, oMountPoint  
oVolumes = CREATEOBJECT("TVolumes")  

*!*	? "[" + oVolumes.PointToVolume("Z") + "]"  
*!*	? oVolumes.MountVolume("d:\mnt\cddrive\", "E")  
*!*	? oVolumes.UnmountVolume("d:\mnt\cddrive\")  

FOR EACH oVolume IN oVolumes.volumes  
	WITH oVolume  
		?.volumename, .volumename1,;  
			.serialnumber, .drvletter  

		FOR EACH oMountPoint IN .mountpoints  
			? oMountPoint  
		NEXT  
	ENDWITH  
NEXT

Code:

DEFINE CLASS TVolumes As Session
#DEFINE INVALID_HANDLE_VALUE -1
#DEFINE MAX_PATH 260
	volumes=NULL

PROCEDURE Init
	THIS.declare
	THIS.volumes = CREATEOBJECT("Collection")
	THIS.EnumVolumes

PROCEDURE MountVolume(cMountPath, cDriveLetter)
* works similar to MOUNTVOL.EXE utility
* usage: MountVolume("d:\mnt\cddrive\", "E")
* everything is strictly local
* Directory "d:\mnt\cddrive\" must exist
* CDROM must exist with logical drive letter E assigned
* if successful, the CDROM can be accessed via the path on D:

	LOCAL cVolume
	cVolume = THIS.PointToVolume(cDriveLetter)
	IF EMPTY(cVolume)
		RETURN .F.
	ENDIF
RETURN (SetVolumeMountPoint(cMountPath, cVolume) <> 0)

PROCEDURE UnmountVolume(cMountPath)
* a trailing backslash is required
	IF LEN(cMountPath) <= 3
	* I suggest do not unmount root directories
	* like C:\, D:\ and so on
		RETURN .F.
	ENDIF
RETURN (DeleteVolumeMountPoint(cMountPath) <> 0)

FUNCTION PointToVolume(cDriveLetter) As String
* returns a gibberish for a given logical drive letter
* example: "D" --> \\?\Volume{...}\
	LOCAL cBuffer
	cBuffer = REPLICATE(CHR(0), MAX_PATH)
	= GetVolumeNameForVolumeMountPoint(cDriveLetter + ":\",;
		@cBuffer, LEN(cBuffer))
RETURN STRTRAN(cBuffer, CHR(0), "")

PROCEDURE EnumVolumes
	DO WHILE THIS.volumes.Count > 0
		THIS.volumes.Remove(1)
	ENDDO

	LOCAL hFind, cBuffer

	cBuffer = REPLICATE(CHR(0), MAX_PATH+1)
	hFind = FindFirstVolume(@cBuffer, LEN(cBuffer))

	IF hFind = INVALID_HANDLE_VALUE
		RETURN
	ENDIF

	DO WHILE .T.
		LOCAL oVolume As TVolume

		oVolume = CREATEOBJECT("TVolume",;
			STRTRAN(cBuffer, CHR(0), ""))

		THIS.volumes.Add(oVolume)
		oVolume=NULL

		cBuffer = REPLICATE(CHR(0), MAX_PATH+1)
		IF FindNextVolume(hFind, @cBuffer, LEN(cBuffer)) = 0
			EXIT
		ENDIF
	ENDDO
	= FindVolumeClose(hFind)

PROCEDURE declare
	DECLARE INTEGER SetVolumeMountPoint IN kernel32;
		STRING lpszVolumeMountPoint, STRING lpszVolumeName

	DECLARE INTEGER DeleteVolumeMountPoint IN kernel32;
		STRING lpszVolumeMountPoint

	DECLARE INTEGER GetVolumeNameForVolumeMountPoint IN kernel32;
		STRING lpszVolumeMountPoint, STRING @lpszVolumeName,;
		LONG cchBufferLength

	DECLARE INTEGER GetVolumePathNamesForVolumeName IN kernel32;
		STRING lpszVolumeName, STRING @lpszVolumePathNames,;
		LONG cchBufferLength, LONG @lpcchReturnLength

	DECLARE INTEGER FindFirstVolume IN kernel32;
		STRING lpszVolumeName, LONG cchBufferLength

	DECLARE INTEGER FindVolumeClose IN kernel32;
		INTEGER hFindVolume

	DECLARE INTEGER FindNextVolume IN kernel32;
		INTEGER hFindVolume, STRING @lpszVolumeName,;
		LONG cchBufferLength

	DECLARE INTEGER FindFirstVolumeMountPoint IN kernel32;
		STRING lpszRootPathName, STRING @lpszVolumeMountPoint,;
		LONG cchBufferLength

	DECLARE INTEGER FindNextVolumeMountPoint IN kernel32;
		INTEGER hFindVolumeMountPoint, STRING @lpszVolumeMountPoint,;
		LONG cchBufferLength

	DECLARE INTEGER FindVolumeMountPointClose IN kernel32;
		INTEGER hFindVolumeMountPoint

	DECLARE INTEGER GetVolumeInformation IN kernel32;
		STRING lpRootPathName, STRING @lpVolumeNameBuffer,;
		LONG nVolumeNameSize, LONG @lpVolumeSerialNumber,;
		LONG @lpMaximumComponentLength, LONG @lpFlags,;
		STRING @lpFileSystemNameBuffer, LONG nFileSystemNameSize

ENDDEFINE

DEFINE CLASS TVolume As Session
	volumename=""
	filesystemname=""
	volumename1=""
	serialnumber=""
	maxfilenamelen=0
	mountpoints=NULL
	pathnames=NULL
	drvletter=""

PROCEDURE Init(cName)
	THIS.volumename = m.cName
	THIS.GetInfo

	THIS.pathnames = CREATEOBJECT("Collection")
	THIS.EnumPathNames

	THIS.mountpoints = CREATEOBJECT("Collection")
	THIS.EnumMountPoints

PROCEDURE EnumPathNames
	THIS.drvletter=""
	DO WHILE THIS.pathnames.Count > 0
		THIS.pathnames.Remove(1)
	ENDDO
	
	LOCAL cBuffer, nBufsize, nCount, nIndex
	nBufsize=0
	cBuffer = REPLICATE(CHR(0), 0x4000)

	GetVolumePathNamesForVolumeName(THIS.volumename,;
		@cBuffer, LEN(cBuffer), @nBufsize)

	cBuffer = SUBSTR(cBuffer, 1, AT(CHR(0)+CHR(0), cBuffer))

	nCount = ALINES(arrPathNames, cBuffer, .T., CHR(0))
	THIS.drvletter = arrPathNames[1]
	FOR nIndex=1 TO nCount
		THIS.pathnames.Add(arrPathNames[nIndex])
	NEXT
	RELEASE arrPathNames

PROCEDURE GetInfo
	LOCAL cVNBuffer, nSNBuffer, nMaxCmpLen,;
		nFlags, cOSBuffer, nResult

	STORE REPLICATE(CHR(0), MAX_PATH+1) TO cVNBuffer, cOSBuffer
	STORE 0 TO nSNBuffer, nMaxCmpLen, nFlags

	nResult = GetVolumeInformation(THIS.volumename,;
		@cVNBuffer, LEN(cVNBuffer), @nSNBuffer,;
		@nMaxCmpLen, @nFlags, @cOSBuffer, LEN(cOSBuffer))

	IF nResult <> 0
		THIS.filesystemname=STRTRAN(cOSBuffer, CHR(0),"")
		THIS.volumename1=STRTRAN(cVNBuffer, CHR(0),"")
		THIS.serialnumber=TRANSFORM(nSNBuffer, "@0")
		THIS.maxfilenamelen=nMaxCmpLen
	ENDIF

PROCEDURE EnumMountPoints
	DO WHILE THIS.mountpoints.Count > 0
		THIS.mountpoints.Remove(1)
	ENDDO

	LOCAL hFind, cBuffer

	cBuffer = REPLICATE(CHR(0), MAX_PATH+1)
	hFind = FindFirstVolumeMountPoint(THIS.volumename,;
		@cBuffer, LEN(cBuffer))

	IF hFind = INVALID_HANDLE_VALUE
	* 18=ERROR_NO_MORE_FILES
	* 21=ERROR_NOT_READY
	* 123=ERROR_INVALID_NAME
		RETURN
	ENDIF

	DO WHILE .T.
		THIS.mountpoints.Add(STRTRAN(cBuffer, CHR(0), ""))

		cBuffer = REPLICATE(CHR(0), MAX_PATH+1)
		IF FindNextVolumeMountPoint(hFind,;
			@cBuffer, LEN(cBuffer)) = 0
			EXIT
		ENDIF
	ENDDO
	= FindVolumeMountPointClose(hFind)

ENDDEFINE  

Listed functions:

DeleteVolumeMountPoint
FindFirstVolume
FindFirstVolumeMountPoint
FindNextVolume
FindNextVolumeMountPoint
FindVolumeClose
FindVolumeMountPointClose
GetVolumeInformation
GetVolumeNameForVolumeMountPoint
GetVolumePathNamesForVolumeName
SetVolumeMountPoint

Comment:

Related Registry keys:
HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2


Function GetVolumeNameForVolumeMountPoint returns the volume name for a given drive letter. Same do the Volume names enumeration calls. The volume names, I think, can be used to uniquely identify computers. Example:

\?\Volume{9da8b072-8130-22d6-ff8f-806d6172699f}\