I'm trying to call WinHttpGetIEProxyConfigForCurrentUser function to get the automatically detected IE proxy settings. It accepts an inout struct parameter according to documentation. I'm using following code:
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(&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
}
It looks like the call is successful, settings is not nil, but as soon as I access it I get panic. Here's the output:
1 The operation completed successfully.
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x1 pc=0x4d2bb4]
                You need to pass a pointer to your allocated struct, which you already created with the new function. Remove the extra & from the syscall; uintptr(unsafe.Pointer(settings))
You also need to have a struct that has the same layout as the C struct expected by the syscall. The struct definition looks like:
typedef struct {
  BOOL   fAutoDetect;
  LPWSTR lpszAutoConfigUrl;
  LPWSTR lpszProxy;
  LPWSTR lpszProxyBypass;
} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
Which should translate to
type WINHTTP_CURRENT_USER_IE_PROXY_CONFIG struct {
    fAutoDetect       bool
    lpszAutoConfigUrl *uint16
    lpszProxy         *uint16
    lpszProxyBypass   *uint16
}
Each of those LPWSTR fields is going to be a null-terminated, 16bit/char string. For windows you can generally use the syscall UTF16 functions, but here we first need to convert the *uint16 to a []uint16 slice, then decode that slice to a utf8 string. There function that the syscall pakcage uses to do this isn't exported, but we can easily copy that:
// 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 && 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))
}
Or use the exported version that was recently added to golang.org/x/sys/windows, windows.UTF16PtrToString.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With