;========================================================================================================================================== ; How to get SUBST.EXE functionality into WinBatch code? ;------------------------------------------------------------------------------------------------------------------------------------------ ; - Substitute a local folderpath by a local drive letter and remove substitution. ; - Check if a local drive is a subst drive or not. ; - Create and remove normal subst drive. ; - Create and remove persistent subst drive. ; - List all local subst drives. ; - List all local persistent subst drives. ; - List all DOS devices. ;------------------------------------------------------------------------------------------------------------------------------------------ ; Inspired by: archimede _archimede_@libero.it, Saturday, January 02, 2010 06:25 AM ; Inspired by: IFICantBYTE ificantbyte@yahoo.com.au, Sunday, January 03, 2010 10:11 PM. ; See also: http://en.wikipedia.org/wiki/Subst ; See also: http://technet.microsoft.com/en-us/library/bb491006.aspx ; ; (c) Detlev Dalitz.20100106.20111110. ;========================================================================================================================================== ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfQueryDosDevice () hdlK32 = DllLoad ("KERNEL32.DLL") intBBSizeAdd = 4096 intBBSize = 0 While @TRUE intBBSize = intBBSize + intBBSizeAdd hdlBBTargetPath = BinaryAlloc (intBBSize) If !!DllCall (hdlK32, long : "QueryDosDeviceA", lpnull, lpbinary : hdlBBTargetPath, long : intBBSize) Then Break If DllLastError () != 122 Then Break ; ERROR_INSUFFICIENT_BUFFER = 122 ; The data area passed to a system call is too small (dderror). hdlBBTargetPath = BinaryFree (hdlBBTargetPath) EndWhile BinaryEodSet (hdlBBTargetPath, intBBSize) intEod = BinaryIndexBin (hdlBBTargetPath, 0, "0000", @FWDSCAN, 1) ; Find trailing double zero byte. BinaryEodSet (hdlBBTargetPath, intEod) intCount = BinaryReplace (hdlBBTargetPath, "", @TAB, @TRUE) strListDeviceNames = BinaryPeekStr (hdlBBTargetPath, 0, intEod) hdlBBTargetPath = BinaryFree (hdlBBTargetPath) hdlK32 = DllFree (hdlK32) Return strListDeviceNames ;.......................................................................................................................................... ; This UDF "udfQueryDosDevice" returns a tab delimited string list of all existing MS-DOS device names ; by utilizing the ANSI version of the KERNEL32.DLL function "QueryDosDevice". ; ; See also: http://msdn.microsoft.com/en-us/library/aa365461(VS.85).aspx ; ; (c) Detlev Dalitz.20100104. ;.......................................................................................................................................... #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfGetDriveMounted (intRequest) strDelim1 = @TAB strDelim2 = "|" hdlK32 = DllLoad ("KERNEL32.DLL") strLocalDrives = DiskScan (intRequest) ; Request numbers see below. intDrives = ItemCount (strLocalDrives, @TAB) strListDriveMounted = "" For intDrive = 1 To intDrives strDrive = ItemExtract (intDrive, strLocalDrives, @TAB) intBBSizeAdd = 128 intBBSize = 0 While @TRUE intBBSize = intBBSize + intBBSizeAdd hdlBBTargetPath = BinaryAlloc (intBBSize) ; See note below. If !!DllCall (hdlK32, long : "QueryDosDeviceA", lpstr : strDrive, lpbinary : hdlBBTargetPath, long : intBBSize) Then Break If DllLastError () != 122 Then Break ; ERROR_INSUFFICIENT_BUFFER = 122 ; The data area passed to a system call is too small (dderror). hdlBBTargetPath = BinaryFree (hdlBBTargetPath) EndWhile BinaryEodSet (hdlBBTargetPath, intBBSize) intEod = BinaryIndexBin (hdlBBTargetPath, 0, "0000", @FWDSCAN, 1) ; Find trailing double zero byte. BinaryEodSet (hdlBBTargetPath, intEod) intCount = BinaryReplace (hdlBBTargetPath, "", @TAB, @TRUE) strList = BinaryPeekStr (hdlBBTargetPath, 0, intEod) strItem = ItemExtract (1, strList, @TAB) strListDriveMounted = ItemInsert (strDrive : strDelim2 : strItem, -1, strListDriveMounted, strDelim1) hdlBBTargetPath = BinaryFree (hdlBBTargetPath) Next hdlK32 = DllFree (hdlK32) Return strListDriveMounted ;.......................................................................................................................................... ; This UDF "udfGetDriveMounted" returns a tab delimited string list of all mounted drives ; based on the list of local fixed hard drives detected by WinBatch function DiskScan (2). ; ; Each list item is build from a sub list of two items separated by "|" pipe symbol. ; Examples: ; ListItem = "C:|\Device\HarddiskVolume1" ; ListItem = "Z:|\??\F:\TEMP\" ;.......................................................................................................................................... ; Note: ; Retrieve information about the particular MS-DOS device specified by lpDeviceName, e. g. lpstr:strDrive. ; The first null-terminated string stored into the buffer is the current mapping for the device. ; The other null-terminated strings, if any, represent undeleted prior mappings for the device. ;..........................................................................................................................................; ; The request number is a bitmask, so adding the values together (except for 0) returns all drive types specified; ; e. g. the request number 3 returns removeable drives plus local hard drives. ; Request ; 0 List of unused disk IDs ; 1 List of removable drives (floppies, zip, Jaz, memory sticks etc.) ; 2 List of local fixed (hard) drives ; 4 List of remote (network) drives ; 8 CD-ROM (32 bit) ; 16 RamDisk (32 bit) ; 32 List of persistent non-connected drives ; 64 List USB bus disk drives. (Windows 2000 and later only) ;..........................................................................................................................................; ; (c) Detlev Dalitz.20100104.20111110. ;.......................................................................................................................................... #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfGetDriveSubst (strListDriveMounted) strDelim1 = @TAB strDelim2 = "|" intDrives = ItemCount (strListDriveMounted, strDelim1) strListDriveSubst = "" For intDrive = 1 To intDrives strItem = ItemExtract (intDrive, strListDriveMounted, strDelim1) strItem2 = ItemExtract (2, strItem, strDelim2) If StrIndex (strItem2, "\??\", 1, @FWDSCAN) == 1 strItem2 = StrSub (strItem2, 5, -1) strItem2 = strItem2 : StrSub ("\", StrSub (strItem2, StrLen (strItem2), 1) != "\", 1); AddBackslash. strItem = ItemExtract (1, strItem, strDelim2) : strDelim2 : "\ ==> " : strDelim2 : strItem2 strListDriveSubst = ItemInsert (strItem, -1, strListDriveSubst, strDelim1) EndIf Next Return strListDriveSubst ;.......................................................................................................................................... ; This UDF "udfGetDriveSubst" returns a tab delimited string list of all drives, which currently substitute a folder path. ; ; Each list item is build from a sub list of three items separated by "|" pipe symbol. ; Example: ; ListItem = "Z:|\ ==> |F:\TEMP\" ; SubItem 1: Drive letter with trailing colon, e.g. "Z:" ; SubItem 2: Filler "\ ==> ", just for displaying purpose. ; SubItem 3: Folder with trailing backslash, e. g. "F:\TEMP\" ; ; (c) Detlev Dalitz.20100104. ;.......................................................................................................................................... #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfGetDriveSubstPersistent () strDelim1 = @TAB strDelim2 = "|" strRegKey = "SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices" If !RegExistKey (@REGMACHINE, strRegKey) Then Return "" strListDriveSubst = "" hdlRegKey = RegOpenKey (@REGMACHINE, strRegKey) strListDevices = RegQueryItem (hdlRegKey, "") intDevices = ItemCount (strListDevices, @TAB) For intDevice = 1 To intDevices strDevice = ItemExtract (intDevice, strListDevices, @TAB) If RegExistValue (hdlRegKey, "[" : strDevice : "]") strValue = RegQueryValue (hdlRegKey, "[" : strDevice : "]") If StrIndex (strValue, "\??\", 1, @FWDSCAN) == 1 strValue = StrSub (strValue, 5, -1) strValue = strValue : StrSub ("\", StrSub (strValue, StrLen (strValue), 1) != "\", 1); AddBackslash. strItem = strDevice : strDelim2 : "\ ==> " : strDelim2 : strValue strListDriveSubst = ItemInsert (strItem, -1, strListDriveSubst, strDelim1) EndIf EndIf Next blnResult = RegCloseKey (hdlRegKey) Return strListDriveSubst ;.......................................................................................................................................... ; This UDF "udfGetDriveSubstPersistent" returns a tab delimited string list of all drives, which substitute a folder path persistently. ; ; Each list item is build from a sub list of three items separated by "|" pipe symbol. ; Example: ; ListItem = "Z:|\ ==> |F:\TEMP\" ; SubItem 1: Drive letter with trailing colon, e.g. "Z:" ; SubItem 2: Filler "\ ==> ", just for displaying purpose. ; SubItem 3: Folder with trailing backslash, e. g. "F:\TEMP\" ; ; A persistent subst drive will be set up automatically by the operating system while boot process. ; ; (c) Detlev Dalitz.20100105. ;.......................................................................................................................................... #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfDriveSubstCreate (strDriveLetter, strFolderPath, intMode) blnResult = @TRUE Switch intMode Case 2 Case 1 strRegKey = "SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices" If !RegExistKey (@REGMACHINE, strRegKey) Then Return @FALSE hdlRegKey = RegOpenKey (@REGMACHINE, strRegKey) blnResult = RegSetValue (hdlRegKey, "[" : strDriveLetter : "]", "\??\" : strFolderPath) && RegCloseKey (hdlRegKey) If !blnresult Then Return @FALSE If intMode == 2 Then Return blnResult Case 0 Return blnResult && !!DllCall ("KERNEL32.DLL", long : "DefineDosDeviceA", long : 0, lpstr : strDriveLetter, lpstr : strFolderPath) EndSwitch ;.......................................................................................................................................... ; This UDF "udfDriveSubstCreate () substitutes a local folder by a local drive letter. ; The device name string must not have a colon as the last character, unless a drive letter is being defined, redefined, or deleted. ; For example, drive C would be the string "C:". In no case is a trailing backslash ("\") allowed. ; ; Parameter intMode: ; 0 = Create normal subst drive. ; 1 = Create normal drive together with persistent subst drive. ; 2 = Create only persistent subst drive to be activated during the next boot. ; ; Return value is @TRUE (1) for success or @FALSE (0) for failure. ;.......................................................................................................................................... #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfDriveSubstRemove (strDriveLetter, intMode) blnResult = @TRUE Switch intMode Case 2 Case 1 strRegKey = "SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices" If !RegExistKey (@REGMACHINE, strRegKey) Then Return @FALSE hdlRegKey = RegOpenKey (@REGMACHINE, strRegKey) blnResult = RegDelValue (hdlRegKey, "[" : strDriveLetter : "]") && RegCloseKey (hdlRegKey) If !blnresult Then Return @FALSE If intMode == 2 Then Return blnResult Case 0 Return blnResult && !!DllCall ("KERNEL32.DLL", long : "DefineDosDeviceA", long : 2, lpstr : strDriveLetter, lpnull) EndSwitch ;.......................................................................................................................................... ; This UDF "udfDriveSubstRemove" removes the substitution made by a previous call of udfDriveSubstCreate (strDriveLetter, strFolderpath, intMode). ; ; Parameter intMode: ; 0 = Remove normal subst drive. ; 1 = Remove normal drive together with persistent subst drive. ; 2 = Remove only persistent drive. ; ; Return value is @TRUE (1) for success or @FALSE (0) for failure. ;.......................................................................................................................................... #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfPathRoot (strString) strPathRoot = "" arrPattern = Arrayize ("?:\" : @LF : "\\*\*\" : @LF : "\", @LF) For intI = 0 To 2 If 1 == StrIndexWild (strString, arrPattern [intI], 1) strPathRoot = StrSubWild (strString, arrPattern [intI], 1) If intI == 1 Then strPathRoot = StrSub (strPathRoot, 1, StrLen (strPathRoot) - 1) Break EndIf Next Return strPathRoot ;.......................................................................................................................................... ; This UDf udfPathRoot works like SHLWAPI.DLL "PathStripToRootA". ; ; Detlev Dalitz.20090630. ;.......................................................................................................................................... #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfAddBackSlash (strString) Return strString : StrSub ("\", StrSub (strString, StrLen (strString), 1) != "\", 1) #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfMsgCreate (strDriveMount, srFolderMount, intResult) Message ("Create Subst Drive", "Set" : @LF : strDriveMount : @LF : "To" : @LF : srFolderMount : @LF : @LF : ItemExtract (1 + !!intResult, "Failure.|Success.", "|")) #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfMsgRemove (strDriveMount, intResult) Message ("Remove Subst Drive", "Remove" : @LF : strDriveMount : @LF : @LF : ItemExtract (1 + !!intResult, "Failure.|Success.", "|")) #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfMsgDriveCheck (strRootFolder, intResult) Message ("Drive Check " : strRootFolder, "Drive " : ItemExtract (1 + intResult, "virtual.|normal.", "|")) #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------------------------------------ #DefineFunction udfGetPathRoot (strFolderPath) Return udfAddBackSlash (udfPathRoot (udfAddBackSlash (strFolderPath))) #EndFunction ;------------------------------------------------------------------------------------------------------------------------------------------ ; Main. :Part1 Pause ("Subst Example, Part 1", "Substitute folder by drive, then check if drive is a subst drive or not.") ; Choose a folder, e. g. user temp folder. strFolderTest = ShortCutDir ("Local Settings", 0, 1) : "Temp" ; Any test folder, without trailing backslash. ; Get an unused drive letter from the end of the unused drives list. strDriveNew = ItemExtract (-1, DiskScan (0), @TAB) If strDriveNew == "" Then Goto Part4 ; No drive letter available, try to free one. ; Set drive letter to folder. intResult = udfDriveSubstCreate (strDriveNew, strFolderTest, 0) udfMsgCreate (strDriveNew, strFolderTest, intResult) ; Check drive (DOS DIR trick). strFolderPath = udfGetPathRoot (strFolderTest) intResult = RunShell (Environment ("COMSPEC"), '/C DIR ' : strFolderPath : ' /W | FIND "[.]"', "", @HIDDEN, @GETEXITCODE) udfMsgDriveCheck (strFolderPath, intResult) ; Check drive (DOS DIR trick). strFolderPath = udfGetPathRoot (strDriveNew) intResult = RunShell (Environment ("COMSPEC"), '/C DIR ' : strFolderPath : ' /W | FIND "[.]"', "", @HIDDEN, @GETEXITCODE) udfMsgDriveCheck (strFolderPath, intResult) ; Release drive letter. intResult = udfDriveSubstRemove (strDriveNew, 0) udfMsgRemove (strDriveNew, intResult) ;------------------------------------------------------------------------------------------------------------------------------------------ :Part2 Pause ("Subst Example, Part 2", "Substitute folder by drive, then substitute the substituted drive by another drive.") ; Choose a folder, e. g. user temp folder. strFolderTest1 = ShortCutDir ("Local Settings", 0, 1) : "Temp" ; Any test folder, without trailing backslash. ; Get an unused drive letter from the end of the unused drives list. strDriveNew1 = ItemExtract (-1, DiskScan (0), @TAB) If strDriveNew1 == "" Then Goto Part4 ; No drive letter available, try to free one. ; Set drive letter to folder. intResult = udfDriveSubstCreate (strDriveNew1, strFolderTest1, 0) udfMsgCreate (strDriveNew1, strFolderTest1, intResult) ; Choose a folder. strFolderTest2 = strDriveNew1 ; We use the new drive from just before as mountpoint. ; Get an unused drive letter from the end of the unused drives list. strDriveNew2 = ItemExtract (-1, DiskScan (0), @TAB) ; Set drive letter to folder. intResult = udfDriveSubstCreate (strDriveNew2, strFolderTest2, 0) udfMsgCreate (strDriveNew2, strFolderTest2, intResult) ; Get list of subst drives. intRequest = 2 ; List of local fixed (hard) drives. strListDrivesSubst = udfGetDriveSubst (udfGetDriveMounted (intRequest)) Message ("Subst drives", StrReplace (StrReplace (strListDrivesSubst, "|", ""), @TAB, @LF)) ;------------------------------------------------------------------------------------------------------------------------------------------ :Part3 Pause ("Subst Example, Part 3", "Substitute folder by drive, normal and persistent.") ; Choose a folder, e. g. user temp folder. strFolderTest1 = ShortCutDir ("Local Settings", 0, 1) : "Temp" ; Any test folder, without trailing backslash. ; Get an unused drive letter from the end of the unused drives list. strDriveNew1 = ItemExtract (-1, DiskScan (0), @TAB) If strDriveNew1 == "" Then Goto Part4 ; No drive letter available, try to free one. ; Set drive letter to folder. intResult = udfDriveSubstCreate (strDriveNew1, strFolderTest1, 0) udfMsgCreate (strDriveNew1, strFolderTest1, intResult) ; Get an unused drive letter from the end of the unused drives list. strDriveNew1 = ItemExtract (-1, DiskScan (0), @TAB) If strDriveNew1 == "" Then Goto Part4 ; No drive letter available, try to free one. ; Set drive letter to folder persistently. intResult = udfDriveSubstCreate (strDriveNew1, strFolderTest1, 1) udfMsgCreate (strDriveNew1, strFolderTest1, intResult) ; Get list of subst drives. intRequest = 2 ; List of local fixed (hard) drives. strListDrivesSubst = udfGetDriveSubst (udfGetDriveMounted (intRequest)) Message ("Subst drives", StrReplace (StrReplace (strListDrivesSubst, "|", ""), @TAB, @LF)) ; Get list of persistent subst drives. strListDrivesSubst = udfGetDriveSubstPersistent () Message ("Persistent subst drives", StrReplace (StrReplace (strListDrivesSubst, "|", ""), @TAB, @LF)) ;------------------------------------------------------------------------------------------------------------------------------------------ :Part4 Pause ("Subst Example, Part 4", "Create and remove only persistent subst drives.") ; Choose a folder, e. g. user temp folder. strFolderTest = ShortCutDir ("Local Settings", 0, 1) : "Temp" ; Any test folder, without trailing backslash. ; Get an unused drive letter from the end of the unused drives list. strDriveNew = ItemExtract (-1, DiskScan (0), @TAB) If strDriveNew == "" Then Goto Part5 ; No drive letter available, try to free one. ; Set drive letter to folder. intResult = udfDriveSubstCreate (strDriveNew, strFolderTest, 2) udfMsgCreate (strDriveNew, strFolderTest, intResult) ; Get list of persistent subst drives. strListDrivesSubst = udfGetDriveSubstPersistent () Message ("Persistent subst drives", StrReplace (StrReplace (strListDrivesSubst, "|", ""), @TAB, @LF)) ; Release drive letter. intResult = udfDriveSubstRemove (strDriveNew, 2) udfMsgRemove (strDriveNew, intResult) ; Get list of persistent subst drives. strListDrivesSubst = udfGetDriveSubstPersistent () Message ("Persistent subst drives", StrReplace (StrReplace (strListDrivesSubst, "|", ""), @TAB, @LF)) ;------------------------------------------------------------------------------------------------------------------------------------------ :Part5 Pause ("Subst Example, Part 5", "Remove all normal and persistent subst drives.") ; Remove all subst drives. intRequest = 2 ; List of local fixed (hard) drives. strListDrivesSubst = udfGetDriveSubst (udfGetDriveMounted (intRequest)) Message ("Subst drives", StrReplace (StrReplace (strListDrivesSubst, "|", ""), @TAB, @LF)) intDrivesSubst = ItemCount (strListDrivesSubst, @TAB) For intDrive = 1 To intDrivesSubst strItem = ItemExtract (intDrive, strListDrivesSubst, @TAB) strDriveSubst = ItemExtract (1, strItem, "|") intResult = udfDriveSubstRemove (strDriveSubst, 0) udfMsgRemove (strDriveSubst, intResult) Next ; Remove all persistent subst drives. strListDrivesSubst = udfGetDriveSubstPersistent () Message ("Persistent subst drives", StrReplace (StrReplace (strListDrivesSubst, "|", ""), @TAB, @LF)) intDrivesSubst = ItemCount (strListDrivesSubst, @TAB) For intDrive = 1 To intDrivesSubst strItem = ItemExtract (intDrive, strListDrivesSubst, @TAB) strDriveSubst = ItemExtract (1, strItem, "|") intResult = udfDriveSubstRemove (strDriveSubst, 1) udfMsgRemove (strDriveSubst, intResult) Next ;------------------------------------------------------------------------------------------------------------------------------------------ :Part6 Pause ("Subst Example, Part 6", "Get QueryDosDevice List.") ; Get a list of all Dos Devices. strFileOut = ShortCutDir ("Local Settings", 0, 1) : "Temp\DosDevAll.txt" intBytesWritten = FilePut (strFileOut, StrReplace (ItemSort (udfQueryDosDevice (), @TAB), @TAB, @CRLF)) Run (strFileOut, "") ;------------------------------------------------------------------------------------------------------------------------------------------ :CANCEL Display (2, "Subst Example", "Good Bye!") Exit ;==========================================================================================================================================