亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

獲取固定驅動器列表

獲取固定驅動器列表

Go
開滿天機 2022-06-13 16:28:21
如何僅獲取物理驅動器的所有掛載點的列表?我看到這里有一個類似的答案,但這列出了所有掛載點,包括網絡共享。
查看完整描述

2 回答

?
慕斯709654

TA貢獻1840條經驗 獲得超5個贊

好的,我決定拋開我的 Win32 API 編程技能,準備一個解決方案。


基于您提到的線程的la腳方法的解決方案如下:


package main


import (

    "errors"

    "fmt"

    "log"

    "syscall"

    "unsafe"

)


var (

    kernel32 = syscall.NewLazyDLL("kernel32.dll")


    getDriveTypeWProc = kernel32.NewProc("GetDriveTypeW")

)


func getDriveType(rootPathName []uint16) (int, error) {

    rc, _, _ := getDriveTypeWProc.Call(

        uintptr(unsafe.Pointer(&rootPathName[0])),

    )


    dt := int(rc)


    if dt == driveUnknown || dt == driveNoRootDir {

        return -1, driveTypeErrors[dt]

    }


    return dt, nil

}


var (

    errUnknownDriveType = errors.New("unknown drive type")

    errNoRootDir        = errors.New("invalid root drive path")


    driveTypeErrors = [...]error{

        0: errUnknownDriveType,

        1: errNoRootDir,

    }

)


const (

    driveUnknown = iota

    driveNoRootDir


    driveRemovable

    driveFixed

    driveRemote

    driveCDROM

    driveRamdisk

)


func getFixedDOSDrives() ([]string, error) {

    var drive = [4]uint16{

        1: ':',

        2: '\\',

    }


    var drives []string


    for c := 'A'; c <= 'Z'; c++ {

        drive[0] = uint16(c)

        dt, err := getDriveType(drive[:])


        if err != nil {

            if err == errNoRootDir {

                continue

            }

            return nil, fmt.Errorf("error getting type of: %s: %s",

                syscall.UTF16ToString(drive[:]), err)

        }


        if dt != driveFixed {

            continue

        }


        drives = append(drives, syscall.UTF16ToString(drive[:]))

    }


    return drives, nil

}


func main() {

    drives, err := getFixedDOSDrives()

    if err != nil {

        log.Fatal(err)

    }

    for _, drive := range drives {

        log.Println(drive)

    }

}

按盒子運行(在 Wine 4.0 下)我得到:


tmp$ GOOS=windows go build drvs.go 

tmp$ wine64 ./drvs.exe

0009:fixme:process:SetProcessPriorityBoost (0xffffffffffffffff,1): stub

2020/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 文檔,這應該是有啟發性的。


查看完整回答
反對 回復 2022-06-13
?
Cats萌萌

TA貢獻1805條經驗 獲得超9個贊

基于 Eryk Sun 在評論中建議的改進方法。


編碼

package main


import (

    "errors"

    "log"

    "strings"

    "syscall"

    "unsafe"

)


func main() {

    mounts, err := getFixedDriveMounts()

    if err != nil {

        log.Fatal(err)

    }

    for _, m := range mounts {

        log.Println("volume:", m.volume,

            "mounts:", strings.Join(m.mounts, ", "))

    }

}


var (

    kernel32 = syscall.NewLazyDLL("kernel32.dll")


    findFirstVolumeWProc = kernel32.NewProc("FindFirstVolumeW")

    findNextVolumeWProc  = kernel32.NewProc("FindNextVolumeW")

    findVolumeCloseProc  = kernel32.NewProc("FindVolumeClose")


    getVolumePathNamesForVolumeNameWProc = kernel32.NewProc("GetVolumePathNamesForVolumeNameW")


    getDriveTypeWProc = kernel32.NewProc("GetDriveTypeW")

)


const guidBufLen = syscall.MAX_PATH + 1


func findFirstVolume() (uintptr, []uint16, error) {

    const invalidHandleValue = ^uintptr(0)


    guid := make([]uint16, guidBufLen)


    handle, _, err := findFirstVolumeWProc.Call(

        uintptr(unsafe.Pointer(&guid[0])),

        uintptr(guidBufLen*2),

    )


    if handle == invalidHandleValue {

        return invalidHandleValue, nil, err

    }


    return handle, guid, nil

}


func findNextVolume(handle uintptr) ([]uint16, bool, error) {

    const noMoreFiles = 18


    guid := make([]uint16, guidBufLen)


    rc, _, err := findNextVolumeWProc.Call(

        handle,

        uintptr(unsafe.Pointer(&guid[0])),

        uintptr(guidBufLen*2),

    )


    if rc == 1 {

        return guid, true, nil

    }


    if err.(syscall.Errno) == noMoreFiles {

        return nil, false, nil

    }

    return nil, false, err

}


func findVolumeClose(handle uintptr) error {

    ok, _, err := findVolumeCloseProc.Call(handle)

    if ok == 0 {

        return err

    }


    return nil

}


func getVolumePathNamesForVolumeName(volName []uint16) ([][]uint16, error) {

    const (

        errorMoreData = 234

        NUL           = 0x0000

    )


    var (

        pathNamesLen uint32

        pathNames    []uint16

    )


    pathNamesLen = 2

    for {

        pathNames = make([]uint16, pathNamesLen)

        pathNamesLen *= 2


        rc, _, err := getVolumePathNamesForVolumeNameWProc.Call(

            uintptr(unsafe.Pointer(&volName[0])),

            uintptr(unsafe.Pointer(&pathNames[0])),

            uintptr(pathNamesLen),

            uintptr(unsafe.Pointer(&pathNamesLen)),

        )


        if rc == 0 {

            if err.(syscall.Errno) == errorMoreData {

                continue

            }


            return nil, err

        }


        pathNames = pathNames[:pathNamesLen]

        break

    }


    var out [][]uint16

    i := 0

    for j, c := range pathNames {

        if c == NUL && i < j {

            out = append(out, pathNames[i:j+1])

            i = j + 1

        }

    }

    return out, nil

}


func getDriveType(rootPathName []uint16) (int, error) {

    rc, _, _ := getDriveTypeWProc.Call(

        uintptr(unsafe.Pointer(&rootPathName[0])),

    )


    dt := int(rc)


    if dt == driveUnknown || dt == driveNoRootDir {

        return -1, driveTypeErrors[dt]

    }


    return dt, nil

}


var (

    errUnknownDriveType = errors.New("unknown drive type")

    errNoRootDir        = errors.New("invalid root drive path")


    driveTypeErrors = [...]error{

        0: errUnknownDriveType,

        1: errNoRootDir,

    }

)


const (

    driveUnknown = iota

    driveNoRootDir


    driveRemovable

    driveFixed

    driveRemote

    driveCDROM

    driveRamdisk


    driveLastKnownType = driveRamdisk

)


type fixedDriveVolume struct {

    volName          string

    mountedPathnames []string

}


type fixedVolumeMounts struct {

    volume string

    mounts []string

}


func getFixedDriveMounts() ([]fixedVolumeMounts, error) {

    var out []fixedVolumeMounts


    err := enumVolumes(func(guid []uint16) error {

        mounts, err := maybeGetFixedVolumeMounts(guid)

        if err != nil {

            return err

        }

        if len(mounts) > 0 {

            out = append(out, fixedVolumeMounts{

                volume: syscall.UTF16ToString(guid),

                mounts: LPSTRsToStrings(mounts),

            })

        }

        return nil

    })


    if err != nil {

        return nil, err

    }


    return out, nil

}


func enumVolumes(handleVolume func(guid []uint16) error) error {

    handle, guid, err := findFirstVolume()

    if err != nil {

        return err

    }

    defer func() {

        err = findVolumeClose(handle)

    }()


    if err := handleVolume(guid); err != nil {

        return err

    }


    for {

        guid, more, err := findNextVolume(handle)

        if err != nil {

            return err

        }


        if !more {

            break

        }


        if err := handleVolume(guid); err != nil {

            return err

        }

    }


    return nil

}


func maybeGetFixedVolumeMounts(guid []uint16) ([][]uint16, error) {

    paths, err := getVolumePathNamesForVolumeName(guid)

    if err != nil {

        return nil, err

    }


    if len(paths) == 0 {

        return nil, nil

    }


    var lastErr error

    for _, path := range paths {

        dt, err := getDriveType(path)

        if err == nil {

            if dt == driveFixed {

                return paths, nil

            }

            return nil, nil

        }

        lastErr = err

    }


    return nil, lastErr

}


func LPSTRsToStrings(in [][]uint16) []string {

    if len(in) == 0 {

        return nil

    }


    out := make([]string, len(in))

    for i, s := range in {

        out[i] = syscall.UTF16ToString(s)

    }


    return out

}

(這是此代碼的要點。)


在具有 4 個驅動器的 Wine 4.0(使用`winecfg 配置)下,我有:


tmp$ GOOS=windows go build fvs.go

tmp$ wine64 ./fvs.exe 

0009:fixme:process:SetProcessPriorityBoost (0xffffffffffffffff,1): stub

2020/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:\

代碼滾動如下:

  1. 枚舉系統中的所有(不是 DOS“驅動器”)。

  2. 對于每個卷,它會查詢掛載該卷的路徑名列表(如果有)。

  3. 對于每個這樣的路徑名,它會嘗試獲取它的類型——看看它是否是固定的。

好的一面

正如我在另一個答案中所述,這種方法的好處是,該代碼應該能夠檢測到非驅動器安裝——也就是說,作為目錄安裝的卷,而不是 DOS 設備。

缺點

  • 代碼比較復雜。

  • 結果,它實際上產生了一個兩級結構(深度為 2 的樹):一個固定卷的列表,每個卷都包含其掛載的列表。
    唯一明顯的問題是它可能更難處理,但你可以自由地偽造它,以便它返回一個平面的坐騎列表。

可能的問題

剛才我看到了兩個,不幸的是我沒有容易訪問的運行 Windows 的機器來檢查。

第一個問題是我希望調用kernel32!GetDriveTypeW對卷名起作用——那些\\?\Volume{169203c7-20c7-4ca6-aaec-19a806b9b81e}\由卷枚舉 API 調用返回的樣式的東西,但事實并非如此——總是返回DRIVE_UNKNOWN錯誤代碼。
因此,在我的代碼中,我什至沒有嘗試在卷名上調用它,而是直接通過 查詢卷的安裝kernel32!GetVolumePathNamesForVolumeNameW,然后嘗試獲取它們的驅動器類型。

我不知道為什么它會這樣工作??赡堋皇强赡堋@是我用來測試的 Wine 4.0 中的一個錯誤,但不太可能。

另一個問題是我不知道如何在Wine下創建一個新的非驅動卷掛載,老實說我沒有時間去了解。因此,目錄掛載也可能kernel32!GetDriveTypeW失?。ú⑶覂H適用于“DOS 驅動器”掛載)。
MSDN 對這些問題保持沉默,所以我只是不知道它應該如何表現。如果我是你,我可能會在 Windows 機器上進行一些測試;-)


查看完整回答
反對 回復 2022-06-13
  • 2 回答
  • 0 關注
  • 91 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號