#windows #winapi #networking #go #dll
#Windows #winapi #сеть #Вперед #dll
Вопрос:
Я пытаюсь вызвать WinHttpGetIEProxyConfigForCurrentUser
функцию, чтобы получить автоматически определяемые настройки прокси-сервера IE. Он принимает параметр inout struct в соответствии с документацией. Я использую следующий код:
func GetProxySettings() {
winhttp, _ := syscall.LoadLibrary("winhttp.dll")
getIEProxy, _ := syscall.GetProcAddress(winhttp, "WinHttpGetIEProxyConfigForCurrentUser")
settings := new(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG)
var nargs uintptr = 1
ret, _, callErr := syscall.Syscall(uintptr(getIEProxy), nargs, uintptr(unsafe.Pointer(amp;settings)), 0, 0)
fmt.Println(ret, callErr)
if settings != nil {
fmt.Println(settings.fAutoDetect)
fmt.Println(settings.lpszAutoConfigUrl)
fmt.Println(settings.lpszProxy)
fmt.Println(settings.lpszProxyBypass)
}
}
type WINHTTP_CURRENT_USER_IE_PROXY_CONFIG struct {
fAutoDetect bool
lpszAutoConfigUrl string
lpszProxy string
lpszProxyBypass string
}
Похоже, что вызов выполнен успешно, settings
не равен нулю, но как только я получаю к нему доступ, у меня возникает паника. Вот результат:
1 The operation completed successfully.
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x1 pc=0x4d2bb4]
Комментарии:
1. Строка Go — это не то же самое, что строка C, и в этом случае документы выглядят так, как будто поля должны быть широкими символами (?), Поэтому они, вероятно, должны быть
*uint16
вместо string . Вы также передаете указатель на указатель на системный вызов, для которого требуется только один указатель. избавьтесь от лишнегоamp;
в аргументах.2. Спасибо за совет. Как мне преобразовать
*uint16
в строку go? Я обнаружилUTF16PtrFromString
, что делаю обратное преобразование, но ничего не нужно конвертировать обратно.
Ответ №1:
Вам нужно передать указатель на выделенную вами структуру, которую вы уже создали с new
помощью функции. Удалите лишнее amp;
из системного вызова; uintptr(unsafe.Pointer(settings))
Вам также необходимо иметь структуру, которая имеет тот же макет, что и структура C, ожидаемая системным вызовом. Определение структуры выглядит следующим образом:
typedef struct {
BOOL fAutoDetect;
LPWSTR lpszAutoConfigUrl;
LPWSTR lpszProxy;
LPWSTR lpszProxyBypass;
} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
Который должен переводиться в
type WINHTTP_CURRENT_USER_IE_PROXY_CONFIG struct {
fAutoDetect bool
lpszAutoConfigUrl *uint16
lpszProxy *uint16
lpszProxyBypass *uint16
}
Каждое из этих LPWSTR
полей будет строкой с нулевым завершением, 16 бит / символ. Для Windows обычно можно использовать функции системного UTF16
вызова, но здесь нам сначала нужно преобразовать *uint16
в []uint16
фрагмент, а затем декодировать этот фрагмент в строку utf8. Функция, которую для этого использует системный вызов pakcage, не экспортируется, но мы можем легко скопировать ее:
// utf16PtrToString is like UTF16ToString, but takes *uint16
// as a parameter instead of []uint16.
// max is how many times p can be advanced looking for the null terminator.
// If max is hit, the string is truncated at that point.
func utf16PtrToString(p *uint16, max int) string {
if p == nil {
return ""
}
// Find NUL terminator.
end := unsafe.Pointer(p)
n := 0
for *(*uint16)(end) != 0 amp;amp; n < max {
end = unsafe.Pointer(uintptr(end) unsafe.Sizeof(*p))
n
}
s := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:n:n]
return string(utf16.Decode(s))
}
Или используйте экспортированную версию, которая была недавно добавлена в golang.org/x/sys/windows
, windows.UTF16PtrToString
.
Комментарии:
1. Совет профессионала: преобразование строк намного проще
syscall.UTF16ToString
.2. @Rodrigo: это не делает его намного проще, он выполняет здесь только половину работы по декодированию utf16, вам все еще нужно
*uint16
выполнить[]uint16
преобразование в, что и делает неэкспортируемаяutf16PtrToString
функция. Теперь, когда я думаю об этом, я должен просто скопировать эту функцию, поскольку она не оставляет предотвращение переполнения в качестве упражнения для читателя.
Ответ №2:
Здесь вы берете адрес адреса:
// The type of settings is *WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
settings := new(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG)
// ...
// The type of amp;settings is **WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
ret, _, callErr := syscall.Syscall(uintptr(getIEProxy), nargs, uintptr(unsafe.Pointer(amp;settings)), 0, 0)
Удалите amp;
Syscall
вызов from, и он должен работать.
Комментарии:
1. Вы правы. Однако все еще существует проблема с типом
lpszProxy
.string
кажется, это неправильно. Я пытался*uint16
, но не могу найти способ преобразовать его в строку.