Archive: Next free drive letter


Next free drive letter
I need to add a network drive for the next available drive letter.

I saw a post from Junior Member scatlin how to add drive Q: as a mapped network drive with the command
Exec '"net" "use" "Q:" "\\server\directory" ...'

However first I would like to *find* the next drive letter not in use. If I do it with Q: and the user has already mapped Q:, my user is in trouble (the installation will either overwrite what he has or abort, both of which are undesirable). I searched in the forum and help file of NSIS, but came up empty handed.

I could perhaps use {GetDrives} with some tedious looping. But I don't want to reinvent the wheel ...


"net use * \\server\share" will use the next free drive letter.


Thanks, Anders! This works fine (kind of circumventing the problem) but now I need to know for the rest of my script which letter was assigned?


What is the output of that command?

Stu


Here is a small part of my NSIS script:

Exec '"net" "use" "*" "\\PM2000\ICIS5"'
...
; Configure ODBC data source GMS Central
WriteRegStr HKCU "Software\ODBC\ODBC.INI\ODBC Data Sources" "$Crop-CENTRAL-GMS" "Microsoft Access Driver (*.mdb)"
WriteRegStr HKCU "Software\ODBC\ODBC.INI\$Crop-CENTRAL-GMS" "DBQ" "X:\Database\$Crop\Central\$Crop-GMS.mdb"
WriteRegStr HKCU "Software\ODBC\ODBC.INI\$Crop-CENTRAL-GMS" "Driver" "$SYSDIR\odbcjt32.dll"
...

I'm setting up ODBC data sources to point to the newly created mapped network drive.
Now I would like to set X:\ to Q:\ above if Q:\ was the drive that was assigned in the net-command.


Like I said, does that command tell you anything when you execute it such as the drive that was mapped?

Stu


Oh, with "that command" you mean the "net use" command. Silly me. Didn't know the "net use" until I got it from scatlin and Anders.

Here is some documentation from Microsoft though:

http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/net_use.mspx?mfr=true
http://www.microsoft.com/resources/d....mspx?mfr=true

As far as I can tell, there is no output from the command, e.g. telling me which drive letter was assigned. I can use a bare "net use" command to get a list of connected drives, but that NSIS' GetDrives can also do, and the problem with that is that the files in the new drive could be identical to files in for instance C:\ or D:\ also (same subdirectories), and my script should not mix up these instances.


I guess you could compare the old list of drives with the new list to find out which one was added.
You could use NSISArray for that.

Stu


Here's the output of the net command that Stu is asking for:

C:\Documents and Settings\Owner>net use * \\laptop\scc_d
Drive Z: is now connected to \\laptop\scc_d.

The command completed successfully.

I should point out that drive letter Z was not the 'next free drive letter' available to my way of thinking - my system only uses C (hard drive) and D (CD-ROM).

Don

Thank you. Something like this should get it.


nsExec::ExecToStack 'net use * "\\PM2000\ICIS5"'
Pop $R0
StrCpy $R0 $R0 2 6

$R0 will contain Z:

You might want to do some validation checks on it though (i.e. check that it isn't empty!)

Stu

Thanks for the feedback. I could never get the command nsExec::ExecToStack 'net use * "\\PM2000\ICIS5"' to work nor my first attempt Exec '"net" "use" "\\PM2000\ICIS5" ">out.txt"' where out.txt would be empty. Instead Exec '"net" "use" ">out.txt"' worked fine and produced a listing of drives as expected, but didn't help me of course.

Instead I opted for the NSISArray solution that Stu outlined. Here is the solution for anyone interested (sorry if I goofed up some command, I'm a newbie to NSIS):


!include "NSISArray.nsh"
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!include "WordFunc.nsh"
!include "TextFunc.nsh"
!insertmacro GetDrives
!insertmacro WordReplace
!insertmacro LineRead
Var /Global MappedDrive
${Array} "MyArray" 30 50
${ArrayFunc} Write
${ArrayFunc} Read
...
; Assign a mapped drive from \\PM2000\ICIS5
; automatically and put that in ODBC sources
; If it couldn't be assigned, then C:\ICIS5\... will be used
StrCpy $MappedDrive "C:\ICIS5"
; Check first if it was already assigned to some network
; drive, then recover that
${GetDrives} "NET" "RecoverMappedDrive"
; It wasn't found, so try to add it, recovering which
; drive letter was assigned
${If} $MappedDrive == "C:\ICIS5"
${MyArray->Init}
StrCpy $R1 0
${GetDrives} "NET" "SaveMappedDrives"
ExecWait '"net" "use" "*" "\\PM2000\ICIS5" "/persistent:yes"'
StrCpy $R1 0
${GetDrives} "NET" "GetMappedDriveLetter"
${MyArray->Delete}
${EndIf}
; Use of MappedDrive, which will either contain X: or C:\ICIS5
WriteRegStr HKCU "Software\ODBC\ODBC.INI\$Crop-CENTRAL-GMS" "DBQ" \
"$MappedDrive\Database\$Crop\Central\$Crop-GMS.mdb"
SectionEnd

; Check if X:\EXES\ICIS32.DLL exists (which it will if the
; network directory was already mapped to X: )
Function RecoverMappedDrive
IfFileExists $9EXES\ICIS32.DLL 0 +3
StrCpy $MappedDrive $9 1
StrCpy $MappedDrive "$MappedDrive:"
Push $0
FunctionEnd

Function SaveMappedDrives
${MyArray->Write} $R1 $9
IntOp $R1 $R1 + 1
Push $0
FunctionEnd

Function GetMappedDriveLetter
${MyArray->Read} $R2 $R1
IntOp $R1 $R1 + 1
${If} $MappedDrive == "C:\ICIS5"
StrCmp $9 $R2 +3 0
StrCpy $MappedDrive $9 1
StrCpy $MappedDrive "$MappedDrive:"
${EndIf}
Push $0
FunctionEnd

I'm sorry, this code works much better :p


ReadEnvStr $R0 COMSPEC
nsExec::ExecToStack '$R0 /C net use * "\\PC-01\Drive-C\NewPCTemp"'
Pop $R0

StrCmp $R0 0 +4
Pop $R0
DetailPrint $R0
Abort

Pop $R0
StrCpy $MappedDrive $R0 2 6


Stu

There might still be a problem with the exact location of the drive in the string, when using e.g. Spanish Windows or another regional Windows version. Perhaps this can be mended best by searching for ':' in the string, because that probably is independent of region (well, don't know about chinese!), so the drive letter will be just before ':'.

Thanks for the alternative. My solution had the added benefit of introducing me to NSISarray that I think will be very useful for me in the future.


Good point. Let me see what I can do.
I just think the other solution is a bit dodgy because it's not direct.

Stu


Here we are.


ReadEnvStr $R0 COMSPEC
nsExec::ExecToStack '$R0 /C net use * "\\PC-01\Drive-C\NewPCTemp"'
Pop $R0

StrCmp $R0 0 +4
Pop $R0
DetailPrint $R0
Abort

Pop $R0
StrCpy $R1 0
StrLen $R3 $R0

StrCpy $R2 $R0 1 $R1
StrCmp $R1 $R3 0 +3
DetailPrint "Error: Mapped network drive could not be located."
Abort
IntOp $R1 $R1 + 1
StrCmp $R2 : 0 -5
IntOp $R1 $R1 - 2

StrCpy $MappedDrive $R0 2 $R1


Stu

Wow! Is there nothing NSIS can't do? Returning the output from a command prompt! I've used comspec, command.com and cmd.exe in vbScript. So I hope this isn't wrong. All credit goes to Stu for the solution. I noticed with cmd.exe the /c switch halts the script, and since you're running the commandline via another process, I don't know if you need to close the dos box. It may not even open. I also tried comspec with /k which is supposed to keep the window open and it doesn't.


nsExec hides the command prompt window and it has to wait for the execution to complete to get the exit code and output.

This code will work if the output contains unicode characters as well.

%COMSPEC% is what you should use because command prompt can be cmd.exe or command.com depending on the operating system.

Stu


Yes, NSIS is versatile :)

I tried your new code (Stu) and it works fine in my desktop computer, even with a Spanish Windows version. So just when I thought the discussion was over, I stumbled over a case where my old version would work better. Perhaps Stu's code can be mended too to handle this case:

I was trying to install on a Linux machine (VMWare) that has Windows XP as a subsystem (don't know the right term), and that Windows subsystem didn't allow the connection to the network drive without (LAN) username and password. So my installation with Stu's code just failed, while the old code "worked" to my surprise, because the old DOS window would then ask for username and password from the command prompt, and entering these, in fact *did* connect the network drive.

I realize the "net use /username:<> /password:<>" is possible, but then I only have this kind of problem in 5% of my company's computers, the rest will connect automatically, and then it would be tedious in the 95% of cases to ask the user for LAN username and password. One drawback to the DOS window is that it is a bit ugly and distracting user attention, looks non-professional.


If it fails then perhaps you could ask for a username and password (which you would have to write a small plugin for).

I'll have a look at not using cmd.exe.

Stu


Hmm it seems to be ok now I get 0 on success without using cmd.exe.


nsExec::ExecToStack 'net use * "\\PC-01\Drive-C\NewPCTempo"'
Pop $R0

StrCmp $R0 0 +4
Pop $R0
DetailPrint $R0
Abort

Pop $R0
StrCpy $R1 0
StrLen $R3 $R0

StrCpy $R2 $R0 1 $R1
StrCmp $R1 $R3 0 +3
DetailPrint "Error: Mapped network drive could not be located."
Abort
IntOp $R1 $R1 + 1
StrCmp $R2 : 0 -5
IntOp $R1 $R1 - 2

StrCpy $MappedDrive $R0 2 $R1


Stu