猿问

获取固定驱动器列表

如何仅获取物理驱动器的所有挂载点的列表?我看到这里有一个类似的答案,但这列出了所有挂载点,包括网络共享。



开满天机
浏览 73回答 2
2回答

慕斯709654

好的,我决定抛开我的 Win32 API 编程技能,准备一个解决方案。基于您提到的线程的la脚方法的解决方案如下:package mainimport (&nbsp; &nbsp; "errors"&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "log"&nbsp; &nbsp; "syscall"&nbsp; &nbsp; "unsafe")var (&nbsp; &nbsp; kernel32 = syscall.NewLazyDLL("kernel32.dll")&nbsp; &nbsp; getDriveTypeWProc = kernel32.NewProc("GetDriveTypeW"))func getDriveType(rootPathName []uint16) (int, error) {&nbsp; &nbsp; rc, _, _ := getDriveTypeWProc.Call(&nbsp; &nbsp; &nbsp; &nbsp; uintptr(unsafe.Pointer(&rootPathName[0])),&nbsp; &nbsp; )&nbsp; &nbsp; dt := int(rc)&nbsp; &nbsp; if dt == driveUnknown || dt == driveNoRootDir {&nbsp; &nbsp; &nbsp; &nbsp; return -1, driveTypeErrors[dt]&nbsp; &nbsp; }&nbsp; &nbsp; return dt, nil}var (&nbsp; &nbsp; errUnknownDriveType = errors.New("unknown drive type")&nbsp; &nbsp; errNoRootDir&nbsp; &nbsp; &nbsp; &nbsp; = errors.New("invalid root drive path")&nbsp; &nbsp; driveTypeErrors = [...]error{&nbsp; &nbsp; &nbsp; &nbsp; 0: errUnknownDriveType,&nbsp; &nbsp; &nbsp; &nbsp; 1: errNoRootDir,&nbsp; &nbsp; })const (&nbsp; &nbsp; driveUnknown = iota&nbsp; &nbsp; driveNoRootDir&nbsp; &nbsp; driveRemovable&nbsp; &nbsp; driveFixed&nbsp; &nbsp; driveRemote&nbsp; &nbsp; driveCDROM&nbsp; &nbsp; driveRamdisk)func getFixedDOSDrives() ([]string, error) {&nbsp; &nbsp; var drive = [4]uint16{&nbsp; &nbsp; &nbsp; &nbsp; 1: ':',&nbsp; &nbsp; &nbsp; &nbsp; 2: '\\',&nbsp; &nbsp; }&nbsp; &nbsp; var drives []string&nbsp; &nbsp; for c := 'A'; c <= 'Z'; c++ {&nbsp; &nbsp; &nbsp; &nbsp; drive[0] = uint16(c)&nbsp; &nbsp; &nbsp; &nbsp; dt, err := getDriveType(drive[:])&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err == errNoRootDir {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return nil, fmt.Errorf("error getting type of: %s: %s",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; syscall.UTF16ToString(drive[:]), err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if dt != driveFixed {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; drives = append(drives, syscall.UTF16ToString(drive[:]))&nbsp; &nbsp; }&nbsp; &nbsp; return drives, nil}func main() {&nbsp; &nbsp; drives, err := getFixedDOSDrives()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; for _, drive := range drives {&nbsp; &nbsp; &nbsp; &nbsp; log.Println(drive)&nbsp; &nbsp; }}按盒子运行(在 Wine 4.0 下)我得到:tmp$ GOOS=windows go build drvs.go&nbsp;tmp$ wine64 ./drvs.exe0009:fixme:process:SetProcessPriorityBoost (0xffffffffffffffff,1): stub2020/07/06 21:06:02 C:\2020/07/06 21:06:02 D:\2020/07/06 21:06:02 X:\2020/07/06 21:06:02 Z:\(所有驱动器都使用 映射winecfg。)这种方法的问题是:即使系统中存在的 DOS 驱动器的数量远小于 ASCII 大写字母的数量,它也会执行 26 次系统调用。在当今的 Windows 系统上,驱动器可能会映射到常规目录下——很像在 POSIX 系统上,因此它根本没有 DOS 驱动器号。Eryk Sun 准确地暗示了对此应该采取的措施。我将尝试提供一个考虑到这些因素的适当(尽管更复杂)的解决方案。这是代码的要点。GetDriveTypeW文档。希望这会让您对 Win32 API 如何工作以及如何从 Go 使用它感兴趣。试着syscall在你的 Go 安装中查看包的来源——再加上 MSDN 文档,这应该是有启发性的。

Cats萌萌

基于 Eryk Sun 在评论中建议的改进方法。编码package mainimport (&nbsp; &nbsp; "errors"&nbsp; &nbsp; "log"&nbsp; &nbsp; "strings"&nbsp; &nbsp; "syscall"&nbsp; &nbsp; "unsafe")func main() {&nbsp; &nbsp; mounts, err := getFixedDriveMounts()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; for _, m := range mounts {&nbsp; &nbsp; &nbsp; &nbsp; log.Println("volume:", m.volume,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "mounts:", strings.Join(m.mounts, ", "))&nbsp; &nbsp; }}var (&nbsp; &nbsp; kernel32 = syscall.NewLazyDLL("kernel32.dll")&nbsp; &nbsp; findFirstVolumeWProc = kernel32.NewProc("FindFirstVolumeW")&nbsp; &nbsp; findNextVolumeWProc&nbsp; = kernel32.NewProc("FindNextVolumeW")&nbsp; &nbsp; findVolumeCloseProc&nbsp; = kernel32.NewProc("FindVolumeClose")&nbsp; &nbsp; getVolumePathNamesForVolumeNameWProc = kernel32.NewProc("GetVolumePathNamesForVolumeNameW")&nbsp; &nbsp; getDriveTypeWProc = kernel32.NewProc("GetDriveTypeW"))const guidBufLen = syscall.MAX_PATH + 1func findFirstVolume() (uintptr, []uint16, error) {&nbsp; &nbsp; const invalidHandleValue = ^uintptr(0)&nbsp; &nbsp; guid := make([]uint16, guidBufLen)&nbsp; &nbsp; handle, _, err := findFirstVolumeWProc.Call(&nbsp; &nbsp; &nbsp; &nbsp; uintptr(unsafe.Pointer(&guid[0])),&nbsp; &nbsp; &nbsp; &nbsp; uintptr(guidBufLen*2),&nbsp; &nbsp; )&nbsp; &nbsp; if handle == invalidHandleValue {&nbsp; &nbsp; &nbsp; &nbsp; return invalidHandleValue, nil, err&nbsp; &nbsp; }&nbsp; &nbsp; return handle, guid, nil}func findNextVolume(handle uintptr) ([]uint16, bool, error) {&nbsp; &nbsp; const noMoreFiles = 18&nbsp; &nbsp; guid := make([]uint16, guidBufLen)&nbsp; &nbsp; rc, _, err := findNextVolumeWProc.Call(&nbsp; &nbsp; &nbsp; &nbsp; handle,&nbsp; &nbsp; &nbsp; &nbsp; uintptr(unsafe.Pointer(&guid[0])),&nbsp; &nbsp; &nbsp; &nbsp; uintptr(guidBufLen*2),&nbsp; &nbsp; )&nbsp; &nbsp; if rc == 1 {&nbsp; &nbsp; &nbsp; &nbsp; return guid, true, nil&nbsp; &nbsp; }&nbsp; &nbsp; if err.(syscall.Errno) == noMoreFiles {&nbsp; &nbsp; &nbsp; &nbsp; return nil, false, nil&nbsp; &nbsp; }&nbsp; &nbsp; return nil, false, err}func findVolumeClose(handle uintptr) error {&nbsp; &nbsp; ok, _, err := findVolumeCloseProc.Call(handle)&nbsp; &nbsp; if ok == 0 {&nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; }&nbsp; &nbsp; return nil}func getVolumePathNamesForVolumeName(volName []uint16) ([][]uint16, error) {&nbsp; &nbsp; const (&nbsp; &nbsp; &nbsp; &nbsp; errorMoreData = 234&nbsp; &nbsp; &nbsp; &nbsp; NUL&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= 0x0000&nbsp; &nbsp; )&nbsp; &nbsp; var (&nbsp; &nbsp; &nbsp; &nbsp; pathNamesLen uint32&nbsp; &nbsp; &nbsp; &nbsp; pathNames&nbsp; &nbsp; []uint16&nbsp; &nbsp; )&nbsp; &nbsp; pathNamesLen = 2&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; pathNames = make([]uint16, pathNamesLen)&nbsp; &nbsp; &nbsp; &nbsp; pathNamesLen *= 2&nbsp; &nbsp; &nbsp; &nbsp; rc, _, err := getVolumePathNamesForVolumeNameWProc.Call(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uintptr(unsafe.Pointer(&volName[0])),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uintptr(unsafe.Pointer(&pathNames[0])),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uintptr(pathNamesLen),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uintptr(unsafe.Pointer(&pathNamesLen)),&nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; &nbsp; &nbsp; if rc == 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err.(syscall.Errno) == errorMoreData {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return nil, err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; pathNames = pathNames[:pathNamesLen]&nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; }&nbsp; &nbsp; var out [][]uint16&nbsp; &nbsp; i := 0&nbsp; &nbsp; for j, c := range pathNames {&nbsp; &nbsp; &nbsp; &nbsp; if c == NUL && i < j {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; out = append(out, pathNames[i:j+1])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; i = j + 1&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return out, nil}func getDriveType(rootPathName []uint16) (int, error) {&nbsp; &nbsp; rc, _, _ := getDriveTypeWProc.Call(&nbsp; &nbsp; &nbsp; &nbsp; uintptr(unsafe.Pointer(&rootPathName[0])),&nbsp; &nbsp; )&nbsp; &nbsp; dt := int(rc)&nbsp; &nbsp; if dt == driveUnknown || dt == driveNoRootDir {&nbsp; &nbsp; &nbsp; &nbsp; return -1, driveTypeErrors[dt]&nbsp; &nbsp; }&nbsp; &nbsp; return dt, nil}var (&nbsp; &nbsp; errUnknownDriveType = errors.New("unknown drive type")&nbsp; &nbsp; errNoRootDir&nbsp; &nbsp; &nbsp; &nbsp; = errors.New("invalid root drive path")&nbsp; &nbsp; driveTypeErrors = [...]error{&nbsp; &nbsp; &nbsp; &nbsp; 0: errUnknownDriveType,&nbsp; &nbsp; &nbsp; &nbsp; 1: errNoRootDir,&nbsp; &nbsp; })const (&nbsp; &nbsp; driveUnknown = iota&nbsp; &nbsp; driveNoRootDir&nbsp; &nbsp; driveRemovable&nbsp; &nbsp; driveFixed&nbsp; &nbsp; driveRemote&nbsp; &nbsp; driveCDROM&nbsp; &nbsp; driveRamdisk&nbsp; &nbsp; driveLastKnownType = driveRamdisk)type fixedDriveVolume struct {&nbsp; &nbsp; volName&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; string&nbsp; &nbsp; mountedPathnames []string}type fixedVolumeMounts struct {&nbsp; &nbsp; volume string&nbsp; &nbsp; mounts []string}func getFixedDriveMounts() ([]fixedVolumeMounts, error) {&nbsp; &nbsp; var out []fixedVolumeMounts&nbsp; &nbsp; err := enumVolumes(func(guid []uint16) error {&nbsp; &nbsp; &nbsp; &nbsp; mounts, err := maybeGetFixedVolumeMounts(guid)&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if len(mounts) > 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; out = append(out, fixedVolumeMounts{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; volume: syscall.UTF16ToString(guid),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mounts: LPSTRsToStrings(mounts),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return nil&nbsp; &nbsp; })&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return nil, err&nbsp; &nbsp; }&nbsp; &nbsp; return out, nil}func enumVolumes(handleVolume func(guid []uint16) error) error {&nbsp; &nbsp; handle, guid, err := findFirstVolume()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; }&nbsp; &nbsp; defer func() {&nbsp; &nbsp; &nbsp; &nbsp; err = findVolumeClose(handle)&nbsp; &nbsp; }()&nbsp; &nbsp; if err := handleVolume(guid); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; }&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; guid, more, err := findNextVolume(handle)&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if !more {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if err := handleVolume(guid); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return nil}func maybeGetFixedVolumeMounts(guid []uint16) ([][]uint16, error) {&nbsp; &nbsp; paths, err := getVolumePathNamesForVolumeName(guid)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return nil, err&nbsp; &nbsp; }&nbsp; &nbsp; if len(paths) == 0 {&nbsp; &nbsp; &nbsp; &nbsp; return nil, nil&nbsp; &nbsp; }&nbsp; &nbsp; var lastErr error&nbsp; &nbsp; for _, path := range paths {&nbsp; &nbsp; &nbsp; &nbsp; dt, err := getDriveType(path)&nbsp; &nbsp; &nbsp; &nbsp; if err == nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if dt == driveFixed {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return paths, nil&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return nil, nil&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; lastErr = err&nbsp; &nbsp; }&nbsp; &nbsp; return nil, lastErr}func LPSTRsToStrings(in [][]uint16) []string {&nbsp; &nbsp; if len(in) == 0 {&nbsp; &nbsp; &nbsp; &nbsp; return nil&nbsp; &nbsp; }&nbsp; &nbsp; out := make([]string, len(in))&nbsp; &nbsp; for i, s := range in {&nbsp; &nbsp; &nbsp; &nbsp; out[i] = syscall.UTF16ToString(s)&nbsp; &nbsp; }&nbsp; &nbsp; return out}(这是此代码的要点。)在具有 4 个驱动器的 Wine 4.0(使用`winecfg 配置)下,我有:tmp$ GOOS=windows go build fvs.gotmp$ wine64 ./fvs.exe&nbsp;0009:fixme:process:SetProcessPriorityBoost (0xffffffffffffffff,1): stub2020/07/09 22:48:25 volume: \\?\Volume{00000000-0000-0000-0000-000000000043}\ mounts: C:\2020/07/09 22:48:25 volume: \\?\Volume{00000000-0000-0000-0000-000000000044}\ mounts: D:\2020/07/09 22:48:25 volume: \\?\Volume{00000000-0000-0000-0000-00000000005a}\ mounts: Z:\2020/07/09 22:48:25 volume: \\?\Volume{169203c7-20c7-4ca6-aaec-19a806b9b81e}\ mounts: X:\代码滚动如下:枚举系统中的所有卷(不是 DOS“驱动器”)。对于每个卷,它会查询挂载该卷的路径名列表(如果有)。对于每个这样的路径名,它会尝试获取它的类型——看看它是否是固定的。好的一面正如我在另一个答案中所述,这种方法的好处是,该代码应该能够检测到非驱动器安装——也就是说,作为目录安装的卷,而不是 DOS 设备。缺点代码比较复杂。结果,它实际上产生了一个两级结构(深度为 2 的树):一个固定卷的列表,每个卷都包含其挂载的列表。唯一明显的问题是它可能更难处理,但你可以自由地伪造它,以便它返回一个平面的坐骑列表。可能的问题刚才我看到了两个,不幸的是我没有容易访问的运行 Windows 的机器来检查。第一个问题是我希望调用kernel32!GetDriveTypeW对卷名起作用——那些\\?\Volume{169203c7-20c7-4ca6-aaec-19a806b9b81e}\由卷枚举 API 调用返回的样式的东西,但事实并非如此——总是返回DRIVE_UNKNOWN错误代码。因此,在我的代码中,我什至没有尝试在卷名上调用它,而是直接通过 查询卷的安装kernel32!GetVolumePathNamesForVolumeNameW,然后尝试获取它们的驱动器类型。我不知道为什么它会这样工作。可能——只是可能——这是我用来测试的 Wine 4.0 中的一个错误,但不太可能。另一个问题是我不知道如何在Wine下创建一个新的非驱动卷挂载,老实说我没有时间去了解。因此,目录挂载也可能kernel32!GetDriveTypeW失败(并且仅适用于“DOS 驱动器”挂载)。MSDN 对这些问题保持沉默,所以我只是不知道它应该如何表现。如果我是你,我可能会在 Windows 机器上进行一些测试;-)
随时随地看视频慕课网APP

相关分类

Go
我要回答