PSUBST - Persistent SUBST Command

samintz

Scott Mintz
May 20, 2008
1,368
12
Solon, OH, USA
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
 
May 20, 2008
9,899
71
Syracuse, NY, USA
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?
 
May 20, 2008
9,899
71
Syracuse, NY, USA
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,368
12
Solon, OH, USA
That's kind of nifty. Any idea what API SUBST uses? It's too bad it doesn't display those mappings.
 

samintz

Scott Mintz
May 20, 2008
1,368
12
Solon, OH, USA
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?
 

Charles Dye

Super Moderator
Staff member
May 20, 2008
3,966
53
Albuquerque, NM
prospero.unm.edu
I guess you write a plugin.... It might be handy if @WINAPI and @CAPI could interface with binary buffers allocated by @BALLOC, but I suspect that would be a major-update type feature to add.
 

samintz

Scott Mintz
May 20, 2008
1,368
12
Solon, OH, USA
This looks promising:
Code:
do d in /c ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 (echo %d:  %@winapi[kernel32.dll,QueryDosDeviceW,"%d:",BUFFER,16384])|ffind /vkm /t"\??\"
 

samintz

Scott Mintz
May 20, 2008
1,368
12
Solon, OH, USA
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

May 20, 2008
9,899
71
Syracuse, NY, USA
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.