PSUBST - Persistent SUBST Command

samintz

Scott Mintz
May 20, 2008
1,264
11
Solon, OH, USA
#1
Code:
@echo off
setlocal
set regkey=HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices
set fPersist=0
set fDel=0
set drive1=
set drive2=

do i=1 to %#
    switch "%[%i]"
    case "/p"
     set fPersist=1
    case "/d"
     set fDel=1
    case "/?" .or. "/h"
     goto :usage
    default
     iff "%drive1" == "" then
       set drive1=%[%i]
     elseiff "%drive2" == "" then
       set drive2=%[%i]
     else
       goto :usage
    endswitch
enddo

iff %fPersist == 1 .and. %fDel == 1 then
    goto :usage
endiff
iff "%drive2" != "" .and. %fDel == 1 then
    goto :usage
endiff
iff    "%drive1" == "" .and. (%fDel==1 .or. %fPersist==1) then
    goto :usage
endiff

rem Make an existing virtual drive persistent
iff "%drive1" != "" .and. "%drive2" == "" .and. %fPersist==1 then
    do d in /p subst
      setdos /x-6
     set drv=%@left[2,%d]
     set vpath=\??\%@trim[%@word[">",1,%d]]
     setdos /x+6
     iff %drv == %drive1 then
       echo Making %drv persistent
        set r=%@regset["%regkey\%drv",REG_SZ,%vpath]
        goto :display
     endiff
    enddo
    echo Error %drive1 is not a SUBST'ed drive.
    quit
endiff

rem Remove an existing drive
iff "%drive1" != "" .and. "%drive2" == "" .and. %fDel==1 then
    do d in /p subst
      setdos /x-6
     set drv=%@left[2,%d]
     setdos /x+6
     iff %drv == %drive1 then
       echo Removing %drv
        set r=%@regset["%regkey\%drv",REG_SZ,]
        subst %drv /d
        goto :display
     endiff
    enddo
    echo Error %drive1 is not a SUBST'ed drive.
    quit
endiff

rem Create a substituted drive
iff "%drive2" != "" then
    rem first try to run SUBST
    subst %drive1 %drive2
    iff %? != 0 then
     quit
    endiff
    iff %fPersist==1 then
        set r=%@regset["%regkey\%drive1",REG_SZ,\??\%@unquote[%drive2]]
    endiff
endiff

rem Default to display mappings
:display
    do d in /p subst
      setdos /x-6
     iff %@regquery["%regkey\%@left[2,%d]"] != -1 then
        echos [*] ``
     else
       echos [ ] ``
     endiff
     echo %d
      setdos /x+6
    enddo
    echo ^nItems marked with [*] are persistent
    quit
       
:usage
text
PSUBST v1.0 Associates a path with a drive letter.
Manages persistent substituted (virtual) drives.
   
PSUBST [drive1: [drive2:]path] [/P]
PSUBST drive1: /D | /P
   
    drive1:        Specifies a virtual drive to which you want to assign a path.
    [drive2:]path  Specifies a physical drive and path you want to assign to
                   a virtual drive (no trailing backslash).
    /D             Deletes a substituted (virtual) drive.
    /P             Make a substituted drive persistent
   
Type PSUBST with no parameters to display a list of current virtual drives.
endtext
quit
 
#2
Scott, the first example I found with Google (didn't try it) looks like this
Code:
"Z:"="\\??\\C:\\Documents and Settings\\All Users\\Shared Documents"
Your code uses only single backslashes. So, what's correct here?
 
#3
You had it right, Scott. I was looking at a .REG script and when a .REG is imported, "\\" is changed to "\".

Did you know 0-9 can be used as drive names? They don't show up in the SUBST command, but they work.
Code:
v:\> subst 4: g:\tc17

v:\> subst
P:\: => L:\projects
S:\: => C:\Windows\system32
T:\: => H:\temp
U:\: => G:\uty
V:\: => H:\workplace
W:\: => C:\Windows

v:\> echo %@truename[4:]
G:\tc17\

v:\> 4:\

4:\> dir /k /m tcc*
2015-01-19  10:39  175,512  tcc.exe
2013-01-09  12:05  1,002  tccbatch.btm
2013-04-03  08:10  1,170  tcchere.btm
2014-11-26  17:02  1,281  TCCHereEx.btm
2014-10-25  22:09  2,353  tcconly.ini
2013-01-09  12:07  1,273  tcctabhere.btm
 

samintz

Scott Mintz
May 20, 2008
1,264
11
Solon, OH, USA
#6
It looks like SUBST uses QueryDosDeviceW() and DefineDosDeviceW().

I tried to experiment with @WINAPI to call QueryDosDeviceW but it only echos a single drive letter.
Code:
echo %@winapi[kernel32.dll,QueryDosDeviceW,0,BUFFER,16384]
P:
How do you get the full contents of the buffer?
 

samintz

Scott Mintz
May 20, 2008
1,264
11
Solon, OH, USA
#9
Here is a SUBST-less version that uses the native Win32 API's to define, query, and remove DOS drive mappings.
Code:
@echo off
setlocal
set regkey=HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices
set fPersist=0
set fDel=0
set drive1=
set drive2=
function DefineDosDevice=`%@winapi[kernel32.dll,DefineDosDeviceW,1,"%1","%2"]`
function RemoveDosDevice=`%@winapi[kernel32.dll,DefineDosDeviceW,2,"%1",0]`
function QueryDosDevice=`%@winapi[kernel32.dll,QueryDosDeviceW,"%1",BUFFER,1024]`

do i=1 to %#
    switch "%[%i]"
    case "/p"
     set fPersist=1
    case "/d"
     set fDel=1
    case "/?" .or. "/h"
     goto :usage
    default
     iff "%drive1" == "" then
       set drive1=%[%i]
     elseiff "%drive2" == "" then
       set drive2=%[%i]
     else
       echo Invalid parameter - %[%i]^n
       goto :usage
    endswitch
enddo

iff %fPersist == 1 .and. %fDel == 1 then
    echo Invalid parameter - cannot specify both /P and /D^n
    goto :usage
elseiff "%drive2" != "" .and. %fDel == 1 then
    echo Invalid parameter - /D^n
    goto :usage
elseiff    "%drive1" == "" .and. (%fDel == 1 .or. %fPersist == 1) then
    echo Syntax error - missing drive1:^n
    goto :usage
elseiff    "%drive1" != "" .and. "%drive2" == "" .and. %fDel == 0 .and. %fPersist == 0 then
    echo Syntax error - missing option or [drive2:]path^n
    goto :usage
endiff

rem Make an existing virtual drive persistent
iff "%drive2" == "" .and. %fPersist==1 then
    set d=%@QueryDosDevice[%drive1]
    iff %@index["%d",\??\] != -1 then
        set drv=%drive1
        set vpath=%d
        echo Making %drv persistent
        set r=%@regset["%regkey\%drv",REG_SZ,%vpath]
        goto :display
    endiff
    echo Error %drive1 is not a PSUBST'ed drive.
    quit
endiff

rem Remove an existing drive
iff %fDel==1 then
    set d=%@QueryDosDevice[%drive1]
    iff %@index["%d",\??\] != -1 then
        echo Removing %drive1
        set r=%@regset["%regkey\%drive1",REG_SZ,]
        set r=%@RemoveDosDevice[%drive1]
        goto :display
    endiff
    echo Error %drive1 is not a PSUBST'ed drive.
    quit
endiff

rem Create a substituted drive
iff "%drive2" != "" then
    rem make sure it is not already mapped
    set d=%@QueryDosDevice[%drive1]
    iff %@index["%d",\??\] != -1 then
     echo Drive %drive1 is already PSUBSTed
     quit
    endiff
    iff %@DefineDosDevice[%drive1,\??\%@unquote[%drive2]] != 1 then
     echo.
     goto :usage
    endiff
    iff %fPersist==1 then
        set r=%@regset["%regkey\%drive1",REG_SZ,\??\%@unquote[%drive2]]
    endiff
endiff

rem Default to display mappings
:display
    do l in /c ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 
     set d=%@QueryDosDevice[%l:]
     if %@index["%d",\??\] == -1 ITERATE
     iff %@regquery["%regkey\%l:"] != -1 then
        echos [*] ``
     else
       echos [ ] ``
     endiff
     echo %l:  %@right[-4,%d]
    enddo
    echo ^nItems marked with [*] are persistent
    quit
       
:usage
text
PSUBST v1.0 Associates a path with a drive letter.
Manages persistent substituted (virtual) drives.
   
PSUBST [drive1: [drive2:]path [/P]]
PSUBST drive1: /D | /P
   
    drive1:        Specifies a virtual drive to which you want to assign a path.
    [drive2:]path  Specifies a physical drive and path you want to assign to
                   a virtual drive (no trailing backslash).
    /D             Deletes a substituted (virtual) drive.
    /P             Make a substituted drive persistent
   
Type PSUBST with no parameters to display a list of current virtual drives.
endtext
quit
 

Attachments

#10
This looks promising:
Code:
do d in /c ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 (echo %d:  %@winapi[kernel32.dll,QueryDosDeviceW,"%d:",BUFFER,16384])|ffind /vkm /t"\??\"
I imagine that's exactly what SUBST.EXE does, but, since 0-9 are not documented as allowable, it omits them. I wonder if any other single characters work. ... Yes indeed, I just tried "-:" as a drive letter with SUBST.EXE and it worked! "~:" works too ... could be a good one for one's profile directory.