roartechs
roartechs

Reputation: 1355

How can I get a listing of all drives on Windows using golang?

I would like my program to iterate through all drives on a Windows system and search for a particular file type. Right now, I can run the program and pass it a drive letter to start from, but I want it to search on all drives automatically. Currently, I would need to do something like this:

C:\> program.exe C:
C:\> program.exe D:
C:\> program.exe E:

I want the program to get a list of all drives and iterate through all of them without the user having to specify the drive letter. Is this possible using Go?

Similar to this question Listing All Physical Drives (Windows) but using Go instead of C.

Upvotes: 10

Views: 12352

Answers (5)

Zombo
Zombo

Reputation: 1

You can use the windows package:

package main

import (
   "golang.org/x/sys/windows"
   "strings"
   "unicode/utf16"
)

func drives() ([]string, error) {
   n, e := windows.GetLogicalDriveStrings(0, nil)
   if e != nil { return nil, e }
   a := make([]uint16, n)
   windows.GetLogicalDriveStrings(n, &a[0])
   s := string(utf16.Decode(a))
   return strings.Split(strings.TrimRight(s, "\x00"), "\x00"), nil
}

func main() {
   a, e := drives()
   if e != nil {
      panic(e)
   }
   for _, s := range a {
      println(s)
   }
}

https://pkg.go.dev/golang.org/x/sys/windows#GetLogicalDriveStrings

Upvotes: 4

nemo
nemo

Reputation: 57639

You can call the function GetLogicalDrives and match the letters according to the bit map.

Something like:

package main

import (
    "fmt"
    "syscall"
)

func main() {

    kernel32, _ := syscall.LoadLibrary("kernel32.dll")
    getLogicalDrivesHandle, _ := syscall.GetProcAddress(kernel32, "GetLogicalDrives")

    var drives []string

    if ret, _, callErr := syscall.Syscall(uintptr(getLogicalDrivesHandle), 0, 0, 0, 0); callErr != 0 {
        // handle error
    } else {
        drives = bitsToDrives(uint32(ret))
    }

    fmt.Printf("%v", drives)

}

func bitsToDrives(bitMap uint32) (drives []string) {
    availableDrives := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}

    for i := range availableDrives {
        if bitMap & 1 == 1 {
            drives = append(drives, availableDrives[i])
        }
        bitMap >>= 1
    }

    return
}

Upvotes: 14

Godineau Félicie
Godineau Félicie

Reputation: 438

You can use the gopsutil lib:

package main

import (
    "fmt"
    "github.com/shirou/gopsutil/disk"
)

func main() {
    partitions, _ := disk.Partitions(false)
    for _, partition := range partitions {
        fmt.Println(partition.Mountpoint)
    }
}

Upvotes: 10

Hinogary
Hinogary

Reputation: 462

The easist way is write own function with try to open "drive" folder mentioned by Volker.

import "os"

func getdrives() (r []string){
    for _, drive := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ"{
        f, err := os.Open(string(drive)+":\\")
        if err == nil {
            r = append(r, string(drive))
            f.Close()
        }
    }
    return
}

Upvotes: 6

Bryan Reynaert
Bryan Reynaert

Reputation: 53

Or you can call GetLogicalDriveStrings directly:

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

func main() {
    kernel32, err := syscall.LoadDLL("kernel32.dll")
    getLogicalDriveStringsHandle, err := kernel32.FindProc("GetLogicalDriveStringsA")

    buffer := [1024]byte{}
    bufferSize := uint32(len(buffer))
    drives := []string{}

    hr, _, _ := getLogicalDriveStringsHandle.Call(uintptr(unsafe.Pointer(&bufferSize)), uintptr(unsafe.Pointer(&buffer)))
    if hr == 0 {
        fmt.Print("There was an error")
    } else {
        // Deal with the buffer, you need to split it
    }
}

Upvotes: 5

Related Questions