Skip to content

Latest commit

 

History

History
273 lines (210 loc) · 7.72 KB

sample_087.md

File metadata and controls

273 lines (210 loc) · 7.72 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.

MSDN article How to create and manipulate NTFS junction points covers the usage of Mountvol.exe, Linkd.exe and Delrp.exe utilities.How to create and manipulate NTFS junction points

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}\