2016-02-06 3 views
3

Пытается вызвать функцию GetVolumeInformation от golang. Хотите получить имя тома.golang, вызов GetVolumeInformation winapi function

Использование спецификации API-х:

BOOL WINAPI GetVolumeInformation(
    _In_opt_ LPCTSTR lpRootPathName, 
    _Out_opt_ LPTSTR lpVolumeNameBuffer, 
    _In_  DWORD nVolumeNameSize, 
    _Out_opt_ LPDWORD lpVolumeSerialNumber, 
    _Out_opt_ LPDWORD lpMaximumComponentLength, 
    _Out_opt_ LPDWORD lpFileSystemFlags, 
    _Out_opt_ LPTSTR lpFileSystemNameBuffer, 
    _In_  DWORD nFileSystemNameSize 
); 

Используйте код:

// test 
package main 

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

func main() { 
    var lpRootPathName = "C:\\" 
    var lpVolumeNameBuffer string 
    var nVolumeNameSize uint64 
    var lpVolumeSerialNumber uint64 
    var lpMaximumComponentLength uint64 
    var lpFileSystemFlags uint64 
    var lpFileSystemNameBuffer string 
    var nFileSystemNameSize uint32 

    kernel32, _ := syscall.LoadLibrary("kernel32.dll") 
    getVolume, _ := syscall.GetProcAddress(kernel32, "GetVolumeInformationW") 

    var nargs uintptr = 8 
    ret, _, callErr := syscall.Syscall9(uintptr(getVolume), 
     nargs, 
     uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpRootPathName))), 
     uintptr(unsafe.Pointer(&lpVolumeNameBuffer)), 
     uintptr(unsafe.Pointer(&nVolumeNameSize)), 
     uintptr(unsafe.Pointer(&lpVolumeSerialNumber)), 
     uintptr(unsafe.Pointer(&lpMaximumComponentLength)), 
     uintptr(unsafe.Pointer(&lpFileSystemFlags)), 
     uintptr(unsafe.Pointer(&lpFileSystemNameBuffer)), 
     uintptr(unsafe.Pointer(&nFileSystemNameSize)), 
     0) 
    fmt.Println(ret, callErr, lpVolumeNameBuffer) 
} 

... и, наконец, ошибка :(

unexpected fault address 0xffffffffffffffff 
fatal error: fault 
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x456b11] 

Не понимаю и не могу Google» d help с вызовом функций winapi и returng string в результате.

Спасибо.

+0

Вы не можете передать '* string' к функциям Windows API, и ожидать, что Go волшебно заполнить все в; вам нужно выделить буфер 'uint16' и передать это вместо. – andlabs

+0

Вы правы. Спасибо. Я изменил тип 'lpVolumeNameBuffer' от строки до uint16. И измените печать на строку (lpVolumeNameBuffer). Он возвращает только один байт, но не строку. – Khoz

+0

Конечно, потому что теперь вы просите только один байт. Обычно вы делаете фрагмент и передаете указатель на его первый элемент. – andlabs

ответ

0

Package unsafe

Пакет содержит небезопасные операции, что шаг по безопасности типа из программ Go.

type Pointer

type Pointer *ArbitraryType 

Указатель представляет собой указатель на произвольный тип. Существует четыре специальных операций, доступных для типа Pointer, которые недоступны. для других типов.

1) Значение указателя любого типа может быть преобразовано в указатель.

2) Указатель может быть преобразован в значение указателя любого типа.

3) Uintptr можно преобразовать в указатель.

4) Указатель может быть преобразован в uintptr.

Указатель позволяет программе побеждать систему типов и читать и записывать произвольную память. Его следует использовать с особой осторожностью.

Вы не учли предупреждения о том, что unsafe.Pointer «следует использовать с особой осторожностью».

Попробуйте это:

package main 

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

func main() { 
    var RootPathName = `C:\` 
    var VolumeNameBuffer = make([]uint16, syscall.MAX_PATH+1) 
    var nVolumeNameSize = uint32(len(VolumeNameBuffer)) 
    var VolumeSerialNumber uint32 
    var MaximumComponentLength uint32 
    var FileSystemFlags uint32 
    var FileSystemNameBuffer = make([]uint16, 255) 
    var nFileSystemNameSize uint32 = syscall.MAX_PATH + 1 

    kernel32, _ := syscall.LoadLibrary("kernel32.dll") 
    getVolume, _ := syscall.GetProcAddress(kernel32, "GetVolumeInformationW") 

    var nargs uintptr = 8 
    ret, _, callErr := syscall.Syscall9(uintptr(getVolume), 
     nargs, 
     uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(RootPathName))), 
     uintptr(unsafe.Pointer(&VolumeNameBuffer[0])), 
     uintptr(nVolumeNameSize), 
     uintptr(unsafe.Pointer(&VolumeSerialNumber)), 
     uintptr(unsafe.Pointer(&MaximumComponentLength)), 
     uintptr(unsafe.Pointer(&FileSystemFlags)), 
     uintptr(unsafe.Pointer(&FileSystemNameBuffer[0])), 
     uintptr(nFileSystemNameSize), 
     0) 
    fmt.Println(ret, callErr, syscall.UTF16ToString(VolumeNameBuffer)) 
} 
+0

Спасибо за ответ и пример хорошей практики. Оно работает. Небольшой правильный источник.Rootname должно быть с двойной косой чертой: var RootPathName = 'C: \\' – Khoz

+0

@Khoz: Не верно. Это строковый литерал. [Строковые литералы] (https://golang.org/ref/spec#String_literals): «Строковые литералы являются символьными последовательностями между обратными кавычками, как в' foo'. В кавычках может отображаться любой символ, кроме обратной цитаты. Значение строкового литерала является строкой, состоящей из неинтерпретированных (неявно UTF-8-кодированных) символов между кавычками, в частности, обратные косые черты не имеют особого значения, а строка может содержать символы новой строки ». – peterSO

+0

Использование 'unsafe.Pointer' - единственный способ использовать системные вызовы/winapi и для определенных аспектов производительности, он явно не пропустил предупреждение. – OneOfOne

0

Я не знаю точной проблемы, с которой вы сталкиваетесь, но я думаю, что это вероятно потому, что вы не используете функции в https://github.com/golang/go/blob/master/src/syscall/syscall_windows.go, связанные с преобразованием из формата, который выходит из ядра, в то, что нужно Go. Посмотрите на других абонентов в UTF16ToString, например, в env_windows.go, чтобы узнать, как они используются.

Смежные вопросы