Old 14th January 2017, 08:37   #1
Pawel
Moderator
 
Pawel's Avatar
 
Join Date: Aug 2004
Location: Poland
Posts: 507
Send a message via ICQ to Pawel
Get available disk space of Network drive

I am writing my own Directory Page, based on nsDialogs.
Everything is fine but I have few problems:

I am creating a button that user can choose directory:

PHP Code:
    ${NSD_CreateButton265u 49u 60u 14u "Browse..."
    
Pop $BUTTON
    
${NSD_OnClick$BUTTON .OnClick_BROWSE_BUTTON 
Now, lets add some action... I am using SelectFolderDialog control.


PHP Code:
Function .OnClick_BROWSE_BUTTON

    NsDialogs
::SelectFolderDialog "Select directory" "C:\"
    Pop $0

    
${If} $0 == error
        Abort
    
${Else}
        ; $0 <--- this is our new directory
    
${EndIf}

FunctionEnd 
Seems to be easy, but...
Here are the problems:

1. We don't want to see Network drives (\\SOMETHING) in select folder dialog. How to create SelectFolderDialog control without Network drives?
2. We want to see network drives (\\SOMETHING) in select folder dialog. But how to get size of this (available disk space of network disk)?



To get disk space of local disk I use this code:
(I also check if drive is local or removable etc, but it is not related to my issues (System::Call "kernel32::GetDriveType(t R0) i.R1"))

PHP Code:
    ; $<-- Our directory path
    
FirstI get drive root of selected directory
    
${GetRoot} $0 $R0 ; -> $C:\My Path\Test$R0 C:
    ; 
SoI have got, for example $R0 'C:'
    
    
Now I get drive available disk space (bytes)
    
ClearErrors
    
${DriveSpace"$R0\" "/D=/S=B" $R1
    ; I have got available disk space in 
$R1
    
${If} ${Errors}
        StrCpy 
$R1 0
        MessageBox MB_OK "
Drive Space$R1 -> SOME ERROR"
        Abort
    
${EndIf}
    
    ;MessageBox MB_OK "
Drive Space$R1
This works fine. But when I try to use this for Network directory on network drive it returns nowthing...
What am I doing wrong? Maybe ${GetRoot} fails? For sure ${DriveSpace} need a drive letter and get something like '\\blablabl\...
If someone could show some working example I would be happy
I am sure I am missing something...

I forgot to mention - NSIS directory page (built-in) do it correctly!

Regards,
-Pawel
Pawel is offline   Reply With Quote
Old 14th January 2017, 10:50   #2
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,764
You should not use ${GetRoot} because volumes can be mounted in subfolders! On Vista+ you can also create symlinks to network shares.

You could try calling SHBrowseForFolder with the System plug-in and using the BIF_DONTGOBELOWDOMAIN flag.

Call GetDiskFreeSpaceEx to get the size. (NSIS calls it in a loop, removing the last component and backslash until the function returns success or the string is at the root drive letter)

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 14th January 2017, 13:47   #3
Pawel
Moderator
 
Pawel's Avatar
 
Join Date: Aug 2004
Location: Poland
Posts: 507
Send a message via ICQ to Pawel
@Anders,
Thanks for tips!

I would like to sum up things...
I am correctly get available disk space for local drives, right?
(assuming that I only use drive types like: DRIVE_REMOVABLE and DRIVE_FIXED)

Quote:
Originally Posted by Anders View Post
You should not use ${GetRoot} because volumes can be mounted in subfolders! On Vista+ you can also create symlinks to network shares.
Yes. And as I understand this applies for network drives (not local).

Quote:
Originally Posted by Anders View Post
You could try calling SHBrowseForFolder with the System plug-in and using the BIF_DONTGOBELOWDOMAIN flag.
Ah, right! Will try this.

Quote:
Originally Posted by Anders View Post
Call GetDiskFreeSpaceEx to get the size. (NSIS calls it in a loop, removing the last component and backslash until the function returns success or the string is at the root drive letter)
Will try this. You wrote NSIS calls it in a loop - where can I find code for this - in NSIS sources or in some contrib nsh files (available in installed NSIS)?

Again, thank you for your help.
Pawel is offline   Reply With Quote
Old 14th January 2017, 14:21   #4
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,764
You don't seem to understand, the drive letters are irrelevant. If I type "c:\Stuff\YourApp" in the directory box that does not mean it is really going to be installed on volume c:, the "c:\Stuff" directory could be redirected to d:, a network location or a volume could be mounted directly there! Windows has supported this since Windows 2000.

The loop should work like this:
Quote:
GetDiskFreeSpaceEx("c:\Stuff\YourApp") and stop if success
GetDiskFreeSpaceEx("c:\Stuff\") and stop if success
GetDiskFreeSpaceEx("c:\") and stop if success
error, not a valid path
Alternatively you can call GetVolumePathName to find the true location but even then you are not guaranteed to get a drive letter or UNC share because a volume can be mounted in a directory and have no assigned drive letter.

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 14th January 2017, 14:24   #5
Pawel
Moderator
 
Pawel's Avatar
 
Join Date: Aug 2004
Location: Poland
Posts: 507
Send a message via ICQ to Pawel
OK. Will read, analyze and code it in best way
-Pawel

Ps: I only want to add, that in my installer user can not change path manually - he need to use BrowseForFolder dialog - it means I don't have to validate path, as I am sure the path is correct and exists.
Only problem I have was network drives (directories, like \\path\to\directory).
I am gonna do 2 installation modes: normal (full system integration - user can install files in local drives or removable - not on network or ram drive etc) and portable (no system integration, no system directories, no shortcuts, etc - user can install files where he wants (not on system directories), also on network directories).

I need to think twice about it to make it work correctly. If I didn't understand you well, sorry... i am still learning
-Pawel
Pawel is offline   Reply With Quote
Old 15th January 2017, 00:04   #6
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,764
If the path starts with "\\" or the drive letter type is network then you can probably just block it but you should still use GetDiskFreeSpaceEx in a loop to get the size.

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 15th January 2017, 10:28   #7
Pawel
Moderator
 
Pawel's Avatar
 
Join Date: Aug 2004
Location: Poland
Posts: 507
Send a message via ICQ to Pawel
Yup. As I know that user can not brake the path (he has to use Browse Dialog which will return correct path or error) I only check if the path is '\\' or X:\ . All other possibilities are blocked.
Pawel is offline   Reply With Quote
Old 15th January 2017, 11:49   #8
Pawel
Moderator
 
Pawel's Avatar
 
Join Date: Aug 2004
Location: Poland
Posts: 507
Send a message via ICQ to Pawel
Here is the pseudo code...
Would you change anything?

Macro ${GET_AVAILABLE_DISK_SPACE} gets Text control handle (with directory path chosen by Browse Button) and a handle to label control where result will be displayed...
In macro, I check path in loop if exists (it seems that existing path need to be passed to GetDiskFreeSpaceEx()), then get size and display it on label control.

Those all MessageBox's of course are only for debug...

PHP Code:
Usage
Get available disk space ("DIR_CONTROL" "OUTPUT_CONTROL")
${
GET_AVAILABLE_DISK_SPACE"$DIR_EDIT_HANDLE" "$OUTPUT_LABEL_HANDLE"

/*===================================================================
; GET_AVAILABLE_DISK_SPACE
; This macro returns available disk space and set text on control
===================================================================*/
!define GET_DISK_FREE_SPACE "kernel32::GetDiskFreeSpaceEx(t, *l, *l, *l) i"
!define GET_AVAILABLE_DISK_SPACE "!insertmacro GET_AVAILABLE_DISK_SPACE"
!macro GET_AVAILABLE_DISK_SPACE "DIR_CONTROL" "OUTPUT_CONTROL" 

    
Get Current Install Directory
    
;${NSD_GetText"${DIR_CONTROL}$R0 ; -> C:\Program Files (x86)\Test
    
;MessageBox MB_OK "DIR: $R0"

    
DEBUG
    
Below path is wrong (only first part is correct loop will eliminate wrong part)
    
StrCpy $"C:\Program Files (x86)\Stas\Jas\Kasia\Basia\Asia\"
    MessageBox MB_OK "
INPUT PATH: $0"
    
    ; Check Path (loop until path exists)
    
${Do}
        ; Remove backslash
        Push "
$0"
        Call REMOVE_BACKSLASH
        Pop "
$0"

        
${IfNot} ${FileExists} "$0\*.*"
            ; Go one level up
            
${GetParent} "$0" "$0"
        
${EndIf}

        ; Break if path to short
        StrLen 
$R0 "$0"
        
${If} $R0 < 1
            MessageBox MB_OK "
PATH TO SHORT: $0"
            
${Break}
        
${EndIf}
 
        MessageBox MB_OK "
LOOP TEST PATH: $0"
    
${LoopUntil} ${FileExists} "$0\*.*"

    MessageBox MB_OK "
FINAL PATH: $0"

    ; Get available disk space
    System::Call "
${GET_DISK_FREE_SPACE}(r0,.,.,.r1)"
    MessageBox MB_OK "
Drive Space: $1"

    
${If} "$1" L= "0"
        ; ERROR
        MessageBox MB_OK "
Drive Space: $-> ERROR!"
        StrCpy $1 "
0 bytes"
    
${Else}
        ; OK
        ; Format Size
        MessageBox MB_OK "
Drive Space: $1 bytes"
    
${EndIf}

    ; Set Control Text
    ShowWindow 
${OUTPUT_CONTROL} ${SW_HIDE}
    
${NSD_SetText} ${OUTPUT_CONTROL} "$1"
    ShowWindow 
${OUTPUT_CONTROL} ${SW_SHOW}

!macroend

Function REMOVE_BACKSLASH

    Exch $0
    Push $1
    
    StrCpy $1 $0 "" -1
    StrCmp $1 "
\" 0 +2
    StrCpy $0 $0 -1

    Pop $1
    Exch $0
    
FunctionEnd 
Pawel is offline   Reply With Quote
Old 15th January 2017, 18:46   #9
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,764
You don't have to check with ${FileExists}, just call GetDiskFreeSpaceEx and check the return value. Stop if GetDiskFreeSpaceEx succeeds or if you removed all folders and are at the drive root and it still fails (because the drive letter is invalid etc.).

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 15th January 2017, 18:50   #10
Pawel
Moderator
 
Pawel's Avatar
 
Join Date: Aug 2004
Location: Poland
Posts: 507
Send a message via ICQ to Pawel
${FileExists} -> Yes, but I add not existing directory, which have to be removed (GetDiskFreeSpaceEx fails, if path is invalid). It works nice. I paste here only pseudo code... When I am sure the path is correct and exists I check the size -> if size is > 0 it is ok, if not then I know it fails). Tested on win10 and win7, works very nice.
Pawel is offline   Reply With Quote
Old 15th January 2017, 19:21   #11
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,764
You have to check the return value from GetDiskFreeSpaceEx no matter what, you cannot just look at the size. Usually the results of a function are undefined if the function fails. Look at the return value first, then look at the size!

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 15th January 2017, 19:58   #12
Pawel
Moderator
 
Pawel's Avatar
 
Join Date: Aug 2004
Location: Poland
Posts: 507
Send a message via ICQ to Pawel
Yes. I am checking if the return value is zero or not.

GetDiskFreeSpaceEx function
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero (0). To get extended error information, call GetLastError.
Pawel is offline   Reply With Quote
Old 15th January 2017, 21:54   #13
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,764
You are not checking the return value in the code you posted.

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 15th January 2017, 21:57   #14
Pawel
Moderator
 
Pawel's Avatar
 
Join Date: Aug 2004
Location: Poland
Posts: 507
Send a message via ICQ to Pawel
I wrote:
System::Call "${GET_DISK_FREE_SPACE}(r0,.,.,.r1)"
MessageBox MB_OK "Drive Space: $1"

${If} "$1" L= "0"
...

Am I doing it wrong?
Pawel is offline   Reply With Quote
Old 16th January 2017, 00:21   #15
Anders
Moderator
 
Anders's Avatar
 
Join Date: Jun 2002
Location: ${NSISDIR}
Posts: 4,764
Quote:
Originally Posted by Pawel View Post


Am I doing it wrong?
Yes.

System::Call "${GET_DISK_FREE_SPACE}(r0,.,.,.r1).r9" and then check $9 <> 0

IntOp $PostCount $PostCount + 1
Anders is offline   Reply With Quote
Old 16th January 2017, 08:33   #16
Pawel
Moderator
 
Pawel's Avatar
 
Join Date: Aug 2004
Location: Poland
Posts: 507
Send a message via ICQ to Pawel
Yup. Fixed. thx.
...
Todo: Read system plugin documentation and then again, and again
Pawel is offline   Reply With Quote
Reply
Go Back   Winamp & SHOUTcast Forums > Developer Center > NSIS Discussion

Tags
disk, drive, space

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump