Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+3
-3
@@ -50,8 +50,8 @@ func (bs *BSTR) String() string {
|
||||
return windows.UTF16ToString(bs.toUTF16())
|
||||
}
|
||||
|
||||
// toUTF16 is unsafe for general use because it returns a pointer that is
|
||||
// not managed by the Go GC.
|
||||
// toUTF16 is unsafe for general use because it returns a slice referencing a
|
||||
// pointer that is not managed by the Go GC.
|
||||
func (bs *BSTR) toUTF16() []uint16 {
|
||||
return unsafe.Slice(bs.toUTF16Ptr(), bs.Len())
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func (bs *BSTR) toUTF16Ptr() *uint16 {
|
||||
return (*uint16)(unsafe.Pointer(*bs))
|
||||
}
|
||||
|
||||
// ToUTF16 returns the contents of bs as C-style string pointer to UTF-16 code units.
|
||||
// ToUTF16Ptr returns the contents of bs as C-style string pointer to UTF-16 code units.
|
||||
func (bs *BSTR) ToUTF16Ptr() *uint16 {
|
||||
return unsafe.SliceData(bs.ToUTF16())
|
||||
}
|
||||
|
||||
+4
-4
@@ -47,24 +47,24 @@ var (
|
||||
)
|
||||
|
||||
func sysAllocString(str *uint16) (ret BSTR) {
|
||||
r0, _, _ := syscall.Syscall(procSysAllocString.Addr(), 1, uintptr(unsafe.Pointer(str)), 0, 0)
|
||||
r0, _, _ := syscall.SyscallN(procSysAllocString.Addr(), uintptr(unsafe.Pointer(str)))
|
||||
ret = BSTR(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func sysAllocStringLen(str *uint16, strLen uint32) (ret BSTR) {
|
||||
r0, _, _ := syscall.Syscall(procSysAllocStringLen.Addr(), 2, uintptr(unsafe.Pointer(str)), uintptr(strLen), 0)
|
||||
r0, _, _ := syscall.SyscallN(procSysAllocStringLen.Addr(), uintptr(unsafe.Pointer(str)), uintptr(strLen))
|
||||
ret = BSTR(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func sysFreeString(bstr BSTR) {
|
||||
syscall.Syscall(procSysFreeString.Addr(), 1, uintptr(bstr), 0, 0)
|
||||
syscall.SyscallN(procSysFreeString.Addr(), uintptr(bstr))
|
||||
return
|
||||
}
|
||||
|
||||
func sysStringLen(bstr BSTR) (ret uint32) {
|
||||
r0, _, _ := syscall.Syscall(procSysStringLen.Addr(), 1, uintptr(bstr), 0, 0)
|
||||
r0, _, _ := syscall.SyscallN(procSysStringLen.Addr(), uintptr(bstr))
|
||||
ret = uint32(r0)
|
||||
return
|
||||
}
|
||||
+1
-2
@@ -20,6 +20,5 @@ package com
|
||||
// Technically this proc is __cdecl, but since it has 0 args this doesn't matter
|
||||
//sys setOaNoCache() = oleaut32.SetOaNoCache
|
||||
|
||||
// For the following two functions we use IUnknownABI instead of IStreamABI because it makes the callsites cleaner.
|
||||
// The following function uses IUnknownABI instead of IStreamABI because it makes the callsite cleaner.
|
||||
//sys shCreateMemStream(pInit *byte, cbInit uint32) (stream *IUnknownABI) = shlwapi.SHCreateMemStream
|
||||
//sys createStreamOnHGlobal(hglobal internal.HGLOBAL, deleteOnRelease bool, stream **IUnknownABI) (hr wingoes.HRESULT) = ole32.CreateStreamOnHGlobal
|
||||
-206
@@ -13,7 +13,6 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/dblohm7/wingoes"
|
||||
"github.com/dblohm7/wingoes/internal"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -179,103 +178,6 @@ func (o SequentialStream) Write(b []byte) (int, error) {
|
||||
return p.Write(b)
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) Seek(offset int64, whence int) (n int64, _ error) {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[5]
|
||||
|
||||
if runtime.GOARCH == "386" {
|
||||
words := (*[2]uintptr)(unsafe.Pointer(&offset))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
words[0],
|
||||
words[1],
|
||||
uintptr(uint32(whence)),
|
||||
uintptr(unsafe.Pointer(&n)),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
} else {
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(offset),
|
||||
uintptr(uint32(whence)),
|
||||
uintptr(unsafe.Pointer(&n)),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
}
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return 0, e
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) SetSize(newSize uint64) error {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[6]
|
||||
|
||||
if runtime.GOARCH == "386" {
|
||||
words := (*[2]uintptr)(unsafe.Pointer(&newSize))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
words[0],
|
||||
words[1],
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
} else {
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(newSize),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
}
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) CopyTo(dest *IStreamABI, numBytesToCopy uint64) (bytesRead, bytesWritten uint64, _ error) {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[7]
|
||||
|
||||
if runtime.GOARCH == "386" {
|
||||
words := (*[2]uintptr)(unsafe.Pointer(&numBytesToCopy))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(unsafe.Pointer(dest)),
|
||||
words[0],
|
||||
words[1],
|
||||
uintptr(unsafe.Pointer(&bytesRead)),
|
||||
uintptr(unsafe.Pointer(&bytesWritten)),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
} else {
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(unsafe.Pointer(dest)),
|
||||
uintptr(numBytesToCopy),
|
||||
uintptr(unsafe.Pointer(&bytesRead)),
|
||||
uintptr(unsafe.Pointer(&bytesWritten)),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
}
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return bytesRead, bytesWritten, e
|
||||
}
|
||||
|
||||
return bytesRead, bytesWritten, nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) Commit(flags STGC) error {
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[8]
|
||||
|
||||
@@ -306,76 +208,6 @@ func (abi *IStreamABI) Revert() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) LockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[10]
|
||||
|
||||
if runtime.GOARCH == "386" {
|
||||
oWords := (*[2]uintptr)(unsafe.Pointer(&offset))
|
||||
nWords := (*[2]uintptr)(unsafe.Pointer(&numBytes))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
oWords[0],
|
||||
oWords[1],
|
||||
nWords[0],
|
||||
nWords[1],
|
||||
uintptr(lockType),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
} else {
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(offset),
|
||||
uintptr(numBytes),
|
||||
uintptr(lockType),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
}
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) UnlockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[11]
|
||||
|
||||
if runtime.GOARCH == "386" {
|
||||
oWords := (*[2]uintptr)(unsafe.Pointer(&offset))
|
||||
nWords := (*[2]uintptr)(unsafe.Pointer(&numBytes))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
oWords[0],
|
||||
oWords[1],
|
||||
nWords[0],
|
||||
nWords[1],
|
||||
uintptr(lockType),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
} else {
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(offset),
|
||||
uintptr(numBytes),
|
||||
uintptr(lockType),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
}
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) Stat(flags STATFLAG) (*STATSTG, error) {
|
||||
result := new(STATSTG)
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[12]
|
||||
@@ -493,19 +325,10 @@ const hrE_OUTOFMEMORY = wingoes.HRESULT(-((0x8007000E ^ 0xFFFFFFFF) + 1))
|
||||
// copy of initialBytes. Its seek pointer is guaranteed to reference the
|
||||
// beginning of the stream.
|
||||
func NewMemoryStream(initialBytes []byte) (result Stream, _ error) {
|
||||
return newMemoryStreamInternal(initialBytes, false)
|
||||
}
|
||||
|
||||
func newMemoryStreamInternal(initialBytes []byte, forceLegacy bool) (result Stream, _ error) {
|
||||
if len(initialBytes) > maxStreamRWLen {
|
||||
return result, wingoes.ErrorFromHRESULT(hrE_OUTOFMEMORY)
|
||||
}
|
||||
|
||||
// SHCreateMemStream exists on Win7 but is not safe for us to use until Win8.
|
||||
if forceLegacy || !wingoes.IsWin8OrGreater() {
|
||||
return newMemoryStreamLegacy(initialBytes)
|
||||
}
|
||||
|
||||
var base *byte
|
||||
var length uint32
|
||||
if l := uint32(len(initialBytes)); l > 0 {
|
||||
@@ -525,32 +348,3 @@ func newMemoryStreamInternal(initialBytes []byte, forceLegacy bool) (result Stre
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func newMemoryStreamLegacy(initialBytes []byte) (result Stream, _ error) {
|
||||
ppstream := NewABIReceiver()
|
||||
hr := createStreamOnHGlobal(internal.HGLOBAL(0), true, ppstream)
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return result, e
|
||||
}
|
||||
|
||||
obj := result.Make(ppstream).(Stream)
|
||||
|
||||
if err := obj.SetSize(uint64(len(initialBytes))); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
if len(initialBytes) == 0 {
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
_, err := obj.Write(initialBytes)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
if _, err := obj.Seek(0, io.SeekStart); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2023 Tailscale Inc & AUTHORS. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"math"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/dblohm7/wingoes"
|
||||
)
|
||||
|
||||
const maxStreamRWLen = math.MaxInt32
|
||||
|
||||
func (abi *IStreamABI) Seek(offset int64, whence int) (n int64, _ error) {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[5]
|
||||
|
||||
words := (*[2]uintptr)(unsafe.Pointer(&offset))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
words[0],
|
||||
words[1],
|
||||
uintptr(uint32(whence)),
|
||||
uintptr(unsafe.Pointer(&n)),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return 0, e
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) SetSize(newSize uint64) error {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[6]
|
||||
|
||||
words := (*[2]uintptr)(unsafe.Pointer(&newSize))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
words[0],
|
||||
words[1],
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) CopyTo(dest *IStreamABI, numBytesToCopy uint64) (bytesRead, bytesWritten uint64, _ error) {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[7]
|
||||
|
||||
words := (*[2]uintptr)(unsafe.Pointer(&numBytesToCopy))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(unsafe.Pointer(dest)),
|
||||
words[0],
|
||||
words[1],
|
||||
uintptr(unsafe.Pointer(&bytesRead)),
|
||||
uintptr(unsafe.Pointer(&bytesWritten)),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return bytesRead, bytesWritten, e
|
||||
}
|
||||
|
||||
return bytesRead, bytesWritten, nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) LockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[10]
|
||||
|
||||
oWords := (*[2]uintptr)(unsafe.Pointer(&offset))
|
||||
nWords := (*[2]uintptr)(unsafe.Pointer(&numBytes))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
oWords[0],
|
||||
oWords[1],
|
||||
nWords[0],
|
||||
nWords[1],
|
||||
uintptr(lockType),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) UnlockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[11]
|
||||
|
||||
oWords := (*[2]uintptr)(unsafe.Pointer(&offset))
|
||||
nWords := (*[2]uintptr)(unsafe.Pointer(&numBytes))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
oWords[0],
|
||||
oWords[1],
|
||||
nWords[0],
|
||||
nWords[1],
|
||||
uintptr(lockType),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+103
@@ -8,6 +8,109 @@ package com
|
||||
|
||||
import (
|
||||
"math"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/dblohm7/wingoes"
|
||||
)
|
||||
|
||||
const maxStreamRWLen = math.MaxUint32
|
||||
|
||||
func (abi *IStreamABI) Seek(offset int64, whence int) (n int64, _ error) {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[5]
|
||||
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(offset),
|
||||
uintptr(uint32(whence)),
|
||||
uintptr(unsafe.Pointer(&n)),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return 0, e
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) SetSize(newSize uint64) error {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[6]
|
||||
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(newSize),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) CopyTo(dest *IStreamABI, numBytesToCopy uint64) (bytesRead, bytesWritten uint64, _ error) {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[7]
|
||||
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(unsafe.Pointer(dest)),
|
||||
uintptr(numBytesToCopy),
|
||||
uintptr(unsafe.Pointer(&bytesRead)),
|
||||
uintptr(unsafe.Pointer(&bytesWritten)),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return bytesRead, bytesWritten, e
|
||||
}
|
||||
|
||||
return bytesRead, bytesWritten, nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) LockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[10]
|
||||
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(offset),
|
||||
uintptr(numBytes),
|
||||
uintptr(lockType),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (abi *IStreamABI) UnlockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
|
||||
var hr wingoes.HRESULT
|
||||
method := unsafe.Slice(abi.Vtbl, 14)[11]
|
||||
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
method,
|
||||
uintptr(unsafe.Pointer(abi)),
|
||||
uintptr(offset),
|
||||
uintptr(numBytes),
|
||||
uintptr(lockType),
|
||||
)
|
||||
hr = wingoes.HRESULT(rc)
|
||||
|
||||
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2024 Tailscale Inc & AUTHORS. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package com
|
||||
|
||||
func getTooBigSlice() []byte {
|
||||
return nil
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2024 Tailscale Inc & AUTHORS. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows && !386
|
||||
|
||||
package com
|
||||
|
||||
func getTooBigSlice() []byte {
|
||||
return make([]byte, maxStreamRWLen+1)
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) 2023 Tailscale Inc & AUTHORS. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package com
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
const maxStreamRWLen = math.MaxInt32
|
||||
+14
-26
@@ -7,7 +7,6 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/dblohm7/wingoes"
|
||||
"github.com/dblohm7/wingoes/internal"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -44,63 +43,52 @@ var (
|
||||
modoleaut32 = windows.NewLazySystemDLL("oleaut32.dll")
|
||||
modshlwapi = windows.NewLazySystemDLL("shlwapi.dll")
|
||||
|
||||
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
|
||||
procCoGetApartmentType = modole32.NewProc("CoGetApartmentType")
|
||||
procCoIncrementMTAUsage = modole32.NewProc("CoIncrementMTAUsage")
|
||||
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
|
||||
procCoInitializeSecurity = modole32.NewProc("CoInitializeSecurity")
|
||||
procCreateStreamOnHGlobal = modole32.NewProc("CreateStreamOnHGlobal")
|
||||
procSetOaNoCache = modoleaut32.NewProc("SetOaNoCache")
|
||||
procSHCreateMemStream = modshlwapi.NewProc("SHCreateMemStream")
|
||||
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
|
||||
procCoGetApartmentType = modole32.NewProc("CoGetApartmentType")
|
||||
procCoIncrementMTAUsage = modole32.NewProc("CoIncrementMTAUsage")
|
||||
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
|
||||
procCoInitializeSecurity = modole32.NewProc("CoInitializeSecurity")
|
||||
procSetOaNoCache = modoleaut32.NewProc("SetOaNoCache")
|
||||
procSHCreateMemStream = modshlwapi.NewProc("SHCreateMemStream")
|
||||
)
|
||||
|
||||
func coCreateInstance(clsid *CLSID, unkOuter *IUnknownABI, clsctx coCLSCTX, iid *IID, ppv **IUnknownABI) (hr wingoes.HRESULT) {
|
||||
r0, _, _ := syscall.Syscall6(procCoCreateInstance.Addr(), 5, uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(unkOuter)), uintptr(clsctx), uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(ppv)), 0)
|
||||
r0, _, _ := syscall.SyscallN(procCoCreateInstance.Addr(), uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(unkOuter)), uintptr(clsctx), uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(ppv)))
|
||||
hr = wingoes.HRESULT(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func coGetApartmentType(aptType *coAPTTYPE, qual *coAPTTYPEQUALIFIER) (hr wingoes.HRESULT) {
|
||||
r0, _, _ := syscall.Syscall(procCoGetApartmentType.Addr(), 2, uintptr(unsafe.Pointer(aptType)), uintptr(unsafe.Pointer(qual)), 0)
|
||||
r0, _, _ := syscall.SyscallN(procCoGetApartmentType.Addr(), uintptr(unsafe.Pointer(aptType)), uintptr(unsafe.Pointer(qual)))
|
||||
hr = wingoes.HRESULT(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func coIncrementMTAUsage(cookie *coMTAUsageCookie) (hr wingoes.HRESULT) {
|
||||
r0, _, _ := syscall.Syscall(procCoIncrementMTAUsage.Addr(), 1, uintptr(unsafe.Pointer(cookie)), 0, 0)
|
||||
r0, _, _ := syscall.SyscallN(procCoIncrementMTAUsage.Addr(), uintptr(unsafe.Pointer(cookie)))
|
||||
hr = wingoes.HRESULT(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func coInitializeEx(reserved uintptr, flags uint32) (hr wingoes.HRESULT) {
|
||||
r0, _, _ := syscall.Syscall(procCoInitializeEx.Addr(), 2, uintptr(reserved), uintptr(flags), 0)
|
||||
r0, _, _ := syscall.SyscallN(procCoInitializeEx.Addr(), uintptr(reserved), uintptr(flags))
|
||||
hr = wingoes.HRESULT(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func coInitializeSecurity(sd *windows.SECURITY_DESCRIPTOR, authSvcLen int32, authSvc *soleAuthenticationService, reserved1 uintptr, authnLevel rpcAuthnLevel, impLevel rpcImpersonationLevel, authList *soleAuthenticationList, capabilities authCapabilities, reserved2 uintptr) (hr wingoes.HRESULT) {
|
||||
r0, _, _ := syscall.Syscall9(procCoInitializeSecurity.Addr(), 9, uintptr(unsafe.Pointer(sd)), uintptr(authSvcLen), uintptr(unsafe.Pointer(authSvc)), uintptr(reserved1), uintptr(authnLevel), uintptr(impLevel), uintptr(unsafe.Pointer(authList)), uintptr(capabilities), uintptr(reserved2))
|
||||
hr = wingoes.HRESULT(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func createStreamOnHGlobal(hglobal internal.HGLOBAL, deleteOnRelease bool, stream **IUnknownABI) (hr wingoes.HRESULT) {
|
||||
var _p0 uint32
|
||||
if deleteOnRelease {
|
||||
_p0 = 1
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procCreateStreamOnHGlobal.Addr(), 3, uintptr(hglobal), uintptr(_p0), uintptr(unsafe.Pointer(stream)))
|
||||
r0, _, _ := syscall.SyscallN(procCoInitializeSecurity.Addr(), uintptr(unsafe.Pointer(sd)), uintptr(authSvcLen), uintptr(unsafe.Pointer(authSvc)), uintptr(reserved1), uintptr(authnLevel), uintptr(impLevel), uintptr(unsafe.Pointer(authList)), uintptr(capabilities), uintptr(reserved2))
|
||||
hr = wingoes.HRESULT(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func setOaNoCache() {
|
||||
syscall.Syscall(procSetOaNoCache.Addr(), 0, 0, 0, 0)
|
||||
syscall.SyscallN(procSetOaNoCache.Addr())
|
||||
return
|
||||
}
|
||||
|
||||
func shCreateMemStream(pInit *byte, cbInit uint32) (stream *IUnknownABI) {
|
||||
r0, _, _ := syscall.Syscall(procSHCreateMemStream.Addr(), 2, uintptr(unsafe.Pointer(pInit)), uintptr(cbInit), 0)
|
||||
r0, _, _ := syscall.SyscallN(procSHCreateMemStream.Addr(), uintptr(unsafe.Pointer(pInit)), uintptr(cbInit))
|
||||
stream = (*IUnknownABI)(unsafe.Pointer(r0))
|
||||
return
|
||||
}
|
||||
+19
-10
@@ -27,7 +27,6 @@ const (
|
||||
hrE_NOTIMPL = HRESULT(-((0x80004001 ^ 0xFFFFFFFF) + 1))
|
||||
hrE_POINTER = HRESULT(-((0x80004003 ^ 0xFFFFFFFF) + 1))
|
||||
hrE_UNEXPECTED = HRESULT(-((0x8000FFFF ^ 0xFFFFFFFF) + 1))
|
||||
hrTYPE_E_WRONGTYPEKIND = HRESULT(-((0x8002802A ^ 0xFFFFFFFF) + 1))
|
||||
)
|
||||
|
||||
// S_FALSE is a peculiar HRESULT value which means that the call executed
|
||||
@@ -83,6 +82,10 @@ func (hr HRESULT) String() string {
|
||||
return fmt.Sprintf("0x%08X", uint32(hr))
|
||||
}
|
||||
|
||||
func (hr HRESULT) GoString() string {
|
||||
return fmt.Sprintf("%T(0x%08X)", hr, uint32(hr))
|
||||
}
|
||||
|
||||
func (hr HRESULT) isNT() bool {
|
||||
return (hr & (hrCustomerBit | hrFacilityNTBit)) == hrFacilityNTBit
|
||||
}
|
||||
@@ -152,20 +155,26 @@ func ErrorFromHRESULT(hr HRESULT) Error {
|
||||
return Error(hr)
|
||||
}
|
||||
|
||||
// SupportedErrorTypes is a type constraint describing the possible types
|
||||
// that may be used as inputs to [NewError].
|
||||
type SupportedErrorTypes interface {
|
||||
Error | HRESULT | windows.Errno | windows.NTStatus
|
||||
}
|
||||
|
||||
// NewError converts e into an Error if e's type is supported. It returns
|
||||
// both the Error and a bool indicating whether the conversion was successful.
|
||||
func NewError(e any) (Error, bool) {
|
||||
switch v := e.(type) {
|
||||
func NewError[E SupportedErrorTypes](e E) (Error) {
|
||||
switch v := any(e).(type) {
|
||||
case Error:
|
||||
return v, true
|
||||
return v
|
||||
case windows.NTStatus:
|
||||
return ErrorFromNTStatus(v), true
|
||||
return ErrorFromNTStatus(v)
|
||||
case windows.Errno:
|
||||
return ErrorFromErrno(v), true
|
||||
return ErrorFromErrno(v)
|
||||
case HRESULT:
|
||||
return ErrorFromHRESULT(v), true
|
||||
return ErrorFromHRESULT(v)
|
||||
default:
|
||||
return ErrorFromHRESULT(hrTYPE_E_WRONGTYPEKIND), false
|
||||
panic(fmt.Sprintf("Unsupported error type %T", v))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +230,7 @@ func (e Error) toErrno(f errnoFailHandler) windows.Errno {
|
||||
// AsError converts the Error to a windows.Errno, but panics if not possible.
|
||||
func (e Error) AsErrno() windows.Errno {
|
||||
handler := func(hr HRESULT) windows.Errno {
|
||||
panic(fmt.Sprintf("wingoes.Error: Called AsErrno on a non-convertable HRESULT 0x%08X", uint32(hr)))
|
||||
panic(fmt.Sprintf("wingoes.Error: Called AsErrno on a non-convertable %#v", hr))
|
||||
return windows.ERROR_UNIDENTIFIED_ERROR
|
||||
}
|
||||
|
||||
@@ -247,7 +256,7 @@ func (e Error) toNTStatus(f ntStatusFailHandler) windows.NTStatus {
|
||||
// AsNTStatus converts the Error to a windows.NTStatus, but panics if not possible.
|
||||
func (e Error) AsNTStatus() windows.NTStatus {
|
||||
handler := func(hr HRESULT) windows.NTStatus {
|
||||
panic(fmt.Sprintf("windows.Error: Called AsNTStatus on a non-NTSTATUS HRESULT 0x%08X", uint32(hr)))
|
||||
panic(fmt.Sprintf("windows.Error: Called AsNTStatus on a non-NTSTATUS %#v", hr))
|
||||
return windows.STATUS_UNSUCCESSFUL
|
||||
}
|
||||
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) 2023 Tailscale Inc & AUTHORS. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type HGLOBAL windows.Handle
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build windows
|
||||
|
||||
package wingoes
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go mksyscall.go
|
||||
//go:generate go run golang.org/x/tools/cmd/goimports -w zsyscall_windows.go
|
||||
|
||||
// Appending a '?' to this declaration because it's very, very deprecated, in
|
||||
// the (admittedly unlikely) chance it actually gets removed in a future version
|
||||
// of Windows.
|
||||
//sys getVersionEx(osv *_OSVERSIONINFOEX) (err error) [int32(failretval)==0] = kernel32.GetVersionExW?
|
||||
+195
-66
@@ -9,27 +9,33 @@ package wingoes
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
var (
|
||||
verOnce sync.Once
|
||||
verInfo osVersionInfo // must access via getVersionInfo()
|
||||
const (
|
||||
_VER_MINORVERSION = 0x0000001
|
||||
_VER_MAJORVERSION = 0x0000002
|
||||
_VER_BUILDNUMBER = 0x0000004
|
||||
_VER_PLATFORMID = 0x0000008
|
||||
_VER_SERVICEPACKMINOR = 0x0000010
|
||||
_VER_SERVICEPACKMAJOR = 0x0000020
|
||||
_VER_SUITENAME = 0x0000040
|
||||
_VER_PRODUCT_TYPE = 0x0000080
|
||||
)
|
||||
|
||||
// osVersionInfo is more compact than windows.OsVersionInfoEx, which contains
|
||||
// extraneous information.
|
||||
type osVersionInfo struct {
|
||||
major uint32
|
||||
minor uint32
|
||||
build uint32
|
||||
servicePack uint16
|
||||
str string
|
||||
isDC bool
|
||||
isServer bool
|
||||
}
|
||||
const (
|
||||
_VER_EQUAL = 1
|
||||
_VER_GREATER = 2
|
||||
_VER_GREATER_EQUAL = 3
|
||||
_VER_LESS = 4
|
||||
_VER_LESS_EQUAL = 5
|
||||
_VER_AND = 6
|
||||
_VER_OR = 7
|
||||
)
|
||||
|
||||
const (
|
||||
_VER_NT_WORKSTATION = 1
|
||||
@@ -37,29 +43,124 @@ const (
|
||||
_VER_NT_SERVER = 3
|
||||
)
|
||||
|
||||
func getVersionInfo() *osVersionInfo {
|
||||
verOnce.Do(func() {
|
||||
osv := windows.RtlGetVersion()
|
||||
verInfo = osVersionInfo{
|
||||
major: osv.MajorVersion,
|
||||
minor: osv.MinorVersion,
|
||||
build: osv.BuildNumber,
|
||||
servicePack: osv.ServicePackMajor,
|
||||
str: fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.BuildNumber),
|
||||
isDC: osv.ProductType == _VER_NT_DOMAIN_CONTROLLER,
|
||||
// The definition in x/sys/windows doesn't export the OSVersionInfoSize field,
|
||||
// which we need to set before making API calls.
|
||||
type _OSVERSIONINFOEX struct {
|
||||
OSVersionInfoSize uint32
|
||||
MajorVersion uint32
|
||||
MinorVersion uint32
|
||||
BuildNumber uint32
|
||||
PlatformId uint32
|
||||
CSDVersion [128]uint16
|
||||
ServicePackMajor uint16
|
||||
ServicePackMinor uint16
|
||||
SuiteMask uint16
|
||||
ProductType byte
|
||||
_ byte // Reserved
|
||||
}
|
||||
|
||||
type osVersionInfo struct {
|
||||
fallbackBuildMin atomic.Uint32
|
||||
fallbackBuildMax atomic.Uint32
|
||||
build uint32
|
||||
str string
|
||||
isDC bool
|
||||
isServer bool
|
||||
useFallback bool
|
||||
}
|
||||
|
||||
var (
|
||||
// We cannot use mksyscall for these because they pass uint64 by value on
|
||||
// 32-bit CPU architectures.
|
||||
procVerSetConditionMask = modkernel32.NewProc("VerSetConditionMask")
|
||||
procVerifyVersionInfo = modkernel32.NewProc("VerifyVersionInfoW")
|
||||
verOnce sync.Once
|
||||
verInfo osVersionInfo // must access via getVersionInfo()
|
||||
)
|
||||
|
||||
func getVersionInfoInternal() (*_OSVERSIONINFOEX, error) {
|
||||
osv := _OSVERSIONINFOEX{
|
||||
OSVersionInfoSize: uint32(unsafe.Sizeof(_OSVERSIONINFOEX{})),
|
||||
}
|
||||
// Using GetVersionEx to account for app manifest.
|
||||
if err := getVersionEx(&osv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &osv, nil
|
||||
}
|
||||
|
||||
func (osv *osVersionInfo) initFlags() {
|
||||
if isDC, err := verQueryProductType(_VER_NT_DOMAIN_CONTROLLER); err == nil {
|
||||
osv.isDC = isDC
|
||||
if isDC {
|
||||
// Domain Controllers are also implicitly servers.
|
||||
isServer: osv.ProductType == _VER_NT_DOMAIN_CONTROLLER || osv.ProductType == _VER_NT_SERVER,
|
||||
osv.isServer = true
|
||||
return
|
||||
}
|
||||
// UBR is only available on Windows 10 and 11 (MajorVersion == 10).
|
||||
if osv.MajorVersion == 10 {
|
||||
if ubr, err := getUBR(); err == nil {
|
||||
verInfo.str = fmt.Sprintf("%s.%d", verInfo.str, ubr)
|
||||
}
|
||||
}
|
||||
|
||||
if isServer, err := verQueryProductType(_VER_NT_SERVER); err == nil {
|
||||
osv.isServer = isServer
|
||||
}
|
||||
}
|
||||
|
||||
func (osv *osVersionInfo) initFallback() {
|
||||
osv.initFlags()
|
||||
osv.useFallback = true
|
||||
}
|
||||
|
||||
func verCheckManifest(osv *_OSVERSIONINFOEX) bool {
|
||||
major, minor, build := windows.RtlGetNtVersionNumbers()
|
||||
return major == osv.MajorVersion && minor == osv.MinorVersion && build == osv.BuildNumber
|
||||
}
|
||||
|
||||
func (osv *osVersionInfo) init() {
|
||||
osvx, err := getVersionInfoInternal()
|
||||
if err != nil {
|
||||
osv.initFallback()
|
||||
return
|
||||
}
|
||||
|
||||
// We only support 10.0.x.x
|
||||
if osvx.MajorVersion != 10 || osvx.MinorVersion != 0 {
|
||||
if verCheckManifest(osvx) {
|
||||
panic("change in versioning scheme -- package must be updated to interpret")
|
||||
} else {
|
||||
panic("incoherent Windows version -- missing/outdated manifest?")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
*osv = osVersionInfo{
|
||||
build: osvx.BuildNumber,
|
||||
str: versionString(osvx),
|
||||
isDC: osvx.ProductType == _VER_NT_DOMAIN_CONTROLLER,
|
||||
// Domain Controllers are also implicitly servers.
|
||||
isServer: osvx.ProductType == _VER_NT_DOMAIN_CONTROLLER || osvx.ProductType == _VER_NT_SERVER,
|
||||
}
|
||||
}
|
||||
|
||||
func getVersionInfo() *osVersionInfo {
|
||||
verOnce.Do(verInfo.init)
|
||||
return &verInfo
|
||||
}
|
||||
|
||||
func versionString(osv *_OSVERSIONINFOEX) string {
|
||||
fmtstr := "%d.%d.%d.%d"
|
||||
vers := append(make([]any, 0, 4),
|
||||
uint(osv.MajorVersion),
|
||||
uint(osv.MinorVersion),
|
||||
uint(osv.BuildNumber),
|
||||
)
|
||||
|
||||
if ubr, err := getUBR(); err == nil {
|
||||
vers = append(vers, uint(ubr))
|
||||
} else {
|
||||
fmtstr = fmtstr[:len(fmtstr)-3]
|
||||
}
|
||||
|
||||
return fmt.Sprintf(fmtstr, vers...)
|
||||
}
|
||||
|
||||
// getUBR returns the "update build revision," ie. the fourth component of the
|
||||
// version string found on Windows 10 and Windows 11 systems.
|
||||
func getUBR() (uint32, error) {
|
||||
@@ -82,8 +183,7 @@ func getUBR() (uint32, error) {
|
||||
}
|
||||
|
||||
// GetOSVersionString returns the Windows version of the current machine in
|
||||
// dotted-decimal form. The version string contains 3 components on Windows 7
|
||||
// and 8.x, and 4 components on Windows 10 and 11.
|
||||
// dotted-decimal form.
|
||||
func GetOSVersionString() string {
|
||||
return getVersionInfo().String()
|
||||
}
|
||||
@@ -100,31 +200,6 @@ func IsWinDomainController() bool {
|
||||
return getVersionInfo().isDC
|
||||
}
|
||||
|
||||
// IsWin7SP1OrGreater returns true when running on Windows 7 SP1 or newer.
|
||||
func IsWin7SP1OrGreater() bool {
|
||||
if IsWin8OrGreater() {
|
||||
return true
|
||||
}
|
||||
|
||||
vi := getVersionInfo()
|
||||
return vi.major == 6 && vi.minor == 1 && vi.servicePack > 0
|
||||
}
|
||||
|
||||
// IsWin8OrGreater returns true when running on Windows 8.0 or newer.
|
||||
func IsWin8OrGreater() bool {
|
||||
return getVersionInfo().isVersionOrGreater(6, 2, 0)
|
||||
}
|
||||
|
||||
// IsWin8Point1OrGreater returns true when running on Windows 8.1 or newer.
|
||||
func IsWin8Point1OrGreater() bool {
|
||||
return getVersionInfo().isVersionOrGreater(6, 3, 0)
|
||||
}
|
||||
|
||||
// IsWin10OrGreater returns true when running on any build of Windows 10 or newer.
|
||||
func IsWin10OrGreater() bool {
|
||||
return getVersionInfo().major >= 10
|
||||
}
|
||||
|
||||
// Win10BuildConstant encodes build numbers for the various editions of Windows 10,
|
||||
// for use with IsWin10BuildOrGreater.
|
||||
type Win10BuildConstant uint32
|
||||
@@ -133,6 +208,7 @@ const (
|
||||
Win10BuildRTM = Win10BuildConstant(10240)
|
||||
Win10Build1511 = Win10BuildConstant(10586)
|
||||
Win10Build1607 = Win10BuildConstant(14393)
|
||||
WinServer2016 = Win10Build1607
|
||||
Win10BuildAnniversary = Win10Build1607
|
||||
Win10Build1703 = Win10BuildConstant(15063)
|
||||
Win10BuildCreators = Win10Build1703
|
||||
@@ -140,6 +216,7 @@ const (
|
||||
Win10BuildFallCreators = Win10Build1709
|
||||
Win10Build1803 = Win10BuildConstant(17134)
|
||||
Win10Build1809 = Win10BuildConstant(17763)
|
||||
WinServer2019 = Win10Build1809
|
||||
Win10Build1903 = Win10BuildConstant(18362)
|
||||
Win10Build1909 = Win10BuildConstant(18363)
|
||||
Win10Build2004 = Win10BuildConstant(19041)
|
||||
@@ -147,6 +224,7 @@ const (
|
||||
Win10Build21H1 = Win10BuildConstant(19043)
|
||||
Win10Build21H2 = Win10BuildConstant(19044)
|
||||
Win10Build22H2 = Win10BuildConstant(19045)
|
||||
WinServer2022 = Win10BuildConstant(20348)
|
||||
)
|
||||
|
||||
// IsWin10BuildOrGreater returns true when running on the specified Windows 10
|
||||
@@ -163,6 +241,10 @@ const (
|
||||
Win11BuildRTM = Win11BuildConstant(22000)
|
||||
Win11Build22H2 = Win11BuildConstant(22621)
|
||||
Win11Build23H2 = Win11BuildConstant(22631)
|
||||
Win11Build24H2 = Win11BuildConstant(26100)
|
||||
WinServer2025 = Win11Build24H2
|
||||
Win11Build25H2 = Win11BuildConstant(26200)
|
||||
Win11Build26H1 = Win11BuildConstant(28000)
|
||||
)
|
||||
|
||||
// IsWin11OrGreater returns true when running on any release of Windows 11,
|
||||
@@ -184,16 +266,63 @@ func (osv *osVersionInfo) String() string {
|
||||
}
|
||||
|
||||
func (osv *osVersionInfo) isWin10BuildOrGreater(build uint32) bool {
|
||||
return osv.isVersionOrGreater(10, 0, build)
|
||||
if !osv.useFallback {
|
||||
return osv.build >= build
|
||||
}
|
||||
|
||||
if osv.fallbackBuildMin.Load() >= build {
|
||||
return true
|
||||
}
|
||||
|
||||
if build >= osv.fallbackBuildMax.Load() {
|
||||
return false
|
||||
}
|
||||
|
||||
result, err := verQueryBuild(10, 0, build)
|
||||
if err == nil {
|
||||
if result {
|
||||
osv.fallbackBuildMin.Store(build)
|
||||
} else {
|
||||
osv.fallbackBuildMax.Store(build)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (osv *osVersionInfo) isVersionOrGreater(major, minor, build uint32) bool {
|
||||
return isVerGE(osv.major, major, osv.minor, minor, osv.build, build)
|
||||
func verQueryBuild(major, minor, build uint32) (bool, error) {
|
||||
var condMask uint64
|
||||
condMask = verSetConditionMask(condMask, _VER_MAJORVERSION, _VER_GREATER_EQUAL)
|
||||
condMask = verSetConditionMask(condMask, _VER_MINORVERSION, _VER_GREATER_EQUAL)
|
||||
condMask = verSetConditionMask(condMask, _VER_BUILDNUMBER, _VER_GREATER_EQUAL)
|
||||
|
||||
typeMask := uint32(_VER_MAJORVERSION | _VER_MINORVERSION | _VER_BUILDNUMBER)
|
||||
|
||||
osv := _OSVERSIONINFOEX{
|
||||
MajorVersion: major,
|
||||
MinorVersion: minor,
|
||||
BuildNumber: build,
|
||||
}
|
||||
|
||||
return verVerify(&osv, typeMask, condMask)
|
||||
}
|
||||
|
||||
func isVerGE(lmajor, rmajor, lminor, rminor, lbuild, rbuild uint32) bool {
|
||||
return lmajor > rmajor ||
|
||||
lmajor == rmajor &&
|
||||
(lminor > rminor ||
|
||||
lminor == rminor && lbuild >= rbuild)
|
||||
func verQueryProductType(wantProdType byte) (bool, error) {
|
||||
condMask := verSetConditionMask(0, _VER_PRODUCT_TYPE, _VER_EQUAL)
|
||||
osv := _OSVERSIONINFOEX{
|
||||
ProductType: wantProdType,
|
||||
}
|
||||
return verVerify(&osv, _VER_PRODUCT_TYPE, condMask)
|
||||
}
|
||||
|
||||
func verVerify(osv *_OSVERSIONINFOEX, typeMask uint32, condMask uint64) (bool, error) {
|
||||
osv.OSVersionInfoSize = uint32(unsafe.Sizeof(_OSVERSIONINFOEX{}))
|
||||
err := verifyVersionInfo(osv, typeMask, condMask)
|
||||
switch err {
|
||||
case nil:
|
||||
return true, nil
|
||||
case windows.ERROR_OLD_WIN_VERSION:
|
||||
return false, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build windows
|
||||
|
||||
package wingoes
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func verSetConditionMask(conditionMask uint64, typeMask uint32, condition byte) uint64 {
|
||||
words := (*[2]uintptr)(unsafe.Pointer(&conditionMask))
|
||||
r0, r1, _ := syscall.SyscallN(
|
||||
procVerSetConditionMask.Addr(),
|
||||
words[0],
|
||||
words[1],
|
||||
uintptr(typeMask),
|
||||
uintptr(condition),
|
||||
)
|
||||
return uint64(r0) | (uint64(r1) << 32)
|
||||
}
|
||||
|
||||
func verifyVersionInfo(versionInformation *_OSVERSIONINFOEX, typeMask uint32, conditionMask uint64) (err error) {
|
||||
words := (*[2]uintptr)(unsafe.Pointer(&conditionMask))
|
||||
rc, _, e := syscall.SyscallN(
|
||||
procVerifyVersionInfo.Addr(),
|
||||
uintptr(unsafe.Pointer(versionInformation)),
|
||||
uintptr(typeMask),
|
||||
words[0],
|
||||
words[1],
|
||||
)
|
||||
if int32(rc) == 0 {
|
||||
err = e
|
||||
}
|
||||
return err
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build windows && !386
|
||||
|
||||
package wingoes
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func verSetConditionMask(conditionMask uint64, typeMask uint32, condition byte) uint64 {
|
||||
r0, _, _ := syscall.SyscallN(
|
||||
procVerSetConditionMask.Addr(),
|
||||
uintptr(conditionMask),
|
||||
uintptr(typeMask),
|
||||
uintptr(condition),
|
||||
)
|
||||
return uint64(r0)
|
||||
}
|
||||
|
||||
func verifyVersionInfo(versionInformation *_OSVERSIONINFOEX, typeMask uint32, conditionMask uint64) (err error) {
|
||||
rc, _, e := syscall.SyscallN(
|
||||
procVerifyVersionInfo.Addr(),
|
||||
uintptr(unsafe.Pointer(versionInformation)),
|
||||
uintptr(typeMask),
|
||||
uintptr(conditionMask),
|
||||
)
|
||||
if int32(rc) == 0 {
|
||||
err = e
|
||||
}
|
||||
return err
|
||||
}
|
||||
+79
-38
@@ -13,6 +13,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/bits"
|
||||
"os"
|
||||
"reflect"
|
||||
@@ -83,10 +84,14 @@ func (s *SectionHeader) NameString() string {
|
||||
return string(s.Name[:])
|
||||
}
|
||||
|
||||
type peReader interface {
|
||||
io.Closer
|
||||
type peReaderBase interface {
|
||||
io.ReaderAt
|
||||
io.ReadSeeker
|
||||
}
|
||||
|
||||
type peReader interface {
|
||||
peReaderBase
|
||||
io.Closer
|
||||
Base() uintptr
|
||||
Limit() uintptr
|
||||
}
|
||||
@@ -136,7 +141,7 @@ func checkMagic(oh OptionalHeader, machine uint16) bool {
|
||||
case dpe.IMAGE_FILE_MACHINE_AMD64, dpe.IMAGE_FILE_MACHINE_ARM64:
|
||||
expectedMagic = 0x020B
|
||||
default:
|
||||
panic("unsupported machine")
|
||||
panic(fmt.Sprintf("unsupported machine 0x%04X", machine))
|
||||
}
|
||||
|
||||
return oh.GetMagic() == expectedMagic
|
||||
@@ -147,22 +152,32 @@ type peBounds struct {
|
||||
limit uintptr
|
||||
}
|
||||
|
||||
func (peb *peBounds) Base() uintptr {
|
||||
return peb.base
|
||||
}
|
||||
|
||||
func (peb *peBounds) Limit() uintptr {
|
||||
return peb.limit
|
||||
}
|
||||
|
||||
type peFile struct {
|
||||
*os.File
|
||||
peReaderBase
|
||||
peBounds
|
||||
}
|
||||
|
||||
func (pef *peFile) Base() uintptr {
|
||||
return pef.base
|
||||
}
|
||||
func (pef *peFile) Close() error {
|
||||
if pef.peReaderBase == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pef *peFile) Limit() uintptr {
|
||||
if pef.limit == 0 {
|
||||
if fi, err := pef.Stat(); err == nil {
|
||||
pef.limit = uintptr(fi.Size())
|
||||
if cl, ok := pef.peReaderBase.(io.Closer); ok {
|
||||
if err := cl.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return pef.limit
|
||||
|
||||
pef.peReaderBase = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type peModule struct {
|
||||
@@ -171,14 +186,6 @@ type peModule struct {
|
||||
modLock uintptr
|
||||
}
|
||||
|
||||
func (pei *peModule) Base() uintptr {
|
||||
return pei.base
|
||||
}
|
||||
|
||||
func (pei *peModule) Limit() uintptr {
|
||||
return pei.limit
|
||||
}
|
||||
|
||||
// NewPEFromFileName opens a PE binary located at filename and parses its PE
|
||||
// headers. Upon success it returns a non-nil *PEHeaders, otherwise it returns a
|
||||
// nil *PEHeaders and a non-nil error.
|
||||
@@ -193,8 +200,18 @@ func NewPEFromFileName(filename string) (*PEHeaders, error) {
|
||||
}
|
||||
|
||||
func newPEFromFile(f *os.File) (*PEHeaders, error) {
|
||||
// peBounds base is 0, limit is loaded lazily
|
||||
pef := &peFile{File: f}
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newPEFromReaderBase(f, uintptr(fi.Size()))
|
||||
}
|
||||
|
||||
func newPEFromReaderBase(rb peReaderBase, size uintptr) (*PEHeaders, error) {
|
||||
// peBounds base is 0, limit is size
|
||||
pef := &peFile{peReaderBase: rb, peBounds: peBounds{limit: size}}
|
||||
peh, err := loadHeaders(pef)
|
||||
if err != nil {
|
||||
pef.Close()
|
||||
@@ -204,6 +221,15 @@ func newPEFromFile(f *os.File) (*PEHeaders, error) {
|
||||
return peh, nil
|
||||
}
|
||||
|
||||
// NewPEFromBlob parses the headers from contents of a PE file stored in blob.
|
||||
// The blob must contain the contents of a file, not a module. Upon success it
|
||||
// returns a non-nil *PEHeaders, otherwise it returns a nil *PEHeaders and a
|
||||
// non-nil error.
|
||||
// Call Close() on the returned *PEHeaders when it is no longer needed.
|
||||
func NewPEFromBlob(blob []byte) (*PEHeaders, error) {
|
||||
return newPEFromReaderBase(bytes.NewReader(blob), uintptr(len(blob)))
|
||||
}
|
||||
|
||||
// Close frees any resources that were opened when peh was created.
|
||||
func (peh *PEHeaders) Close() error {
|
||||
return peh.r.Close()
|
||||
@@ -213,17 +239,20 @@ type rvaType interface {
|
||||
~int8 | ~int16 | ~int32 | ~uint8 | ~uint16 | ~uint32
|
||||
}
|
||||
|
||||
// addOffset ensures that, if off is negative, it does not underflow base.
|
||||
func addOffset[O rvaType](base uintptr, off O) uintptr {
|
||||
// addOffset ensures that off neither overflows nor underflows base.
|
||||
func addOffset[O rvaType](base uintptr, off O) (addr uintptr, ok bool) {
|
||||
if off >= 0 {
|
||||
return base + uintptr(off)
|
||||
if upoff := uintptr(off); upoff <= (math.MaxUint - base) {
|
||||
return base + upoff, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
negation := uintptr(-off)
|
||||
if negation >= base {
|
||||
return 0
|
||||
return 0, false
|
||||
}
|
||||
return base - negation
|
||||
return base - negation, true
|
||||
}
|
||||
|
||||
func binaryRead(r io.Reader, data any) (err error) {
|
||||
@@ -254,9 +283,12 @@ func readStruct[T any, R rvaType](r peReader, rva R) (*T, error) {
|
||||
|
||||
return result, nil
|
||||
case *peModule:
|
||||
addr := addOffset(r.Base(), rva)
|
||||
szT := unsafe.Sizeof(*((*T)(nil)))
|
||||
if addr+szT >= v.Limit() {
|
||||
addr, ok := addOffset(r.Base(), rva)
|
||||
if !ok {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
szT := uint32(unsafe.Sizeof(*((*T)(nil))))
|
||||
if addr2, ok := addOffset(addr, szT); !ok || addr2 >= v.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
@@ -285,9 +317,12 @@ func readStructArray[T any, R rvaType](r peReader, rva R, count int) ([]T, error
|
||||
|
||||
return result, nil
|
||||
case *peModule:
|
||||
addr := addOffset(r.Base(), rva)
|
||||
szT := reflect.ArrayOf(count, reflect.TypeOf((*T)(nil)).Elem()).Size()
|
||||
if addr+szT >= v.Limit() {
|
||||
addr, ok := addOffset(r.Base(), rva)
|
||||
if !ok {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
szT := uint32(reflect.ArrayOf(count, reflect.TypeFor[T]()).Size())
|
||||
if addr2, ok := addOffset(addr, szT); !ok || addr2 >= v.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
@@ -330,7 +365,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
|
||||
if e_lfanew <= 0 {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
if addOffset(r.Base(), e_lfanew) >= r.Limit() {
|
||||
if addr, ok := addOffset(r.Base(), e_lfanew); !ok || addr >= r.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
@@ -352,7 +387,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
|
||||
|
||||
// Read the file header
|
||||
fileHeaderOffset := uint32(e_lfanew) + uint32(unsafe.Sizeof(pe))
|
||||
if r.Base()+uintptr(fileHeaderOffset) >= r.Limit() {
|
||||
if addr, ok := addOffset(r.Base(), fileHeaderOffset); !ok || addr >= r.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
@@ -368,7 +403,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
|
||||
|
||||
// Read the optional header
|
||||
optionalHeaderOffset := uint32(fileHeaderOffset) + uint32(unsafe.Sizeof(*fileHeader))
|
||||
if r.Base()+uintptr(optionalHeaderOffset) >= r.Limit() {
|
||||
if addr, ok := addOffset(r.Base(), optionalHeaderOffset); !ok || addr >= r.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
@@ -402,7 +437,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
|
||||
|
||||
// Read in the section table
|
||||
sectionTableOffset := optionalHeaderOffset + uint32(fileHeader.SizeOfOptionalHeader)
|
||||
if r.Base()+uintptr(sectionTableOffset) >= r.Limit() {
|
||||
if addr, ok := addOffset(r.Base(), sectionTableOffset); !ok || addr >= r.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
@@ -474,6 +509,8 @@ const (
|
||||
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
|
||||
)
|
||||
|
||||
const _IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
|
||||
|
||||
// DataDirectoryEntry returns information from nfo's data directory at index idx.
|
||||
// The type of the return value depends on the value of idx. Most values for idx
|
||||
// currently return the DataDirectoryEntry itself, however it will return more
|
||||
@@ -486,9 +523,13 @@ const (
|
||||
// sophisticated return values, so be careful to structure your type assertions
|
||||
// accordingly.
|
||||
func (nfo *PEHeaders) DataDirectoryEntry(idx DataDirectoryIndex) (any, error) {
|
||||
if ii := int(idx); ii < 0 || ii >= _IMAGE_NUMBEROF_DIRECTORY_ENTRIES {
|
||||
return nil, ErrIndexOutOfRange
|
||||
}
|
||||
|
||||
dd := nfo.optionalHeader.GetDataDirectory()
|
||||
if int(idx) >= len(dd) {
|
||||
return nil, ErrIndexOutOfRange
|
||||
return nil, ErrNotPresent
|
||||
}
|
||||
|
||||
dde := dd[idx]
|
||||
|
||||
+15
-1
@@ -13,7 +13,17 @@ import (
|
||||
)
|
||||
|
||||
func (pei *peModule) Close() error {
|
||||
return windows.FreeLibrary(windows.Handle(pei.modLock))
|
||||
if pei.modLock == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := windows.FreeLibrary(windows.Handle(pei.modLock)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pei.Reader = nil
|
||||
pei.modLock = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewPEFromBaseAddressAndSize parses the headers in a PE binary loaded
|
||||
@@ -22,6 +32,10 @@ func (pei *peModule) Close() error {
|
||||
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
|
||||
// *PEHeaders and a non-nil error.
|
||||
func NewPEFromBaseAddressAndSize(baseAddr uintptr, size uint32) (*PEHeaders, error) {
|
||||
if baseAddr == 0 || size == 0 {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
// Grab a strong reference to the module until we're done with it.
|
||||
var modLock windows.Handle
|
||||
if err := windows.GetModuleHandleEx(
|
||||
|
||||
+1
-2
@@ -48,7 +48,6 @@ const (
|
||||
langEnUS = 0x0409
|
||||
codePageUTF16LE = 0x04B0
|
||||
langNeutral = 0
|
||||
codePageNeutral = 0
|
||||
)
|
||||
|
||||
// NewVersionInfo extracts any VERSIONINFO resource from filepath, parses its
|
||||
@@ -111,7 +110,7 @@ func (vi *VersionInfo) maybeLoadTranslationIDs() {
|
||||
},
|
||||
langAndCodePage{
|
||||
language: langNeutral,
|
||||
codePage: codePageNeutral,
|
||||
codePage: codePageUTF16LE,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -8,11 +8,15 @@ package wingoes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Infinite is the [time.Duration] equivalent of [windows.INFINITE].
|
||||
const Infinite = time.Duration(math.MaxInt64)
|
||||
|
||||
var (
|
||||
// ErrDurationOutOfRange means that a time.Duration is too large to be able
|
||||
// to be specified as a valid Win32 timeout value.
|
||||
@@ -21,6 +25,10 @@ var (
|
||||
|
||||
// DurationToTimeoutMilliseconds converts d into a timeout usable by Win32 APIs.
|
||||
func DurationToTimeoutMilliseconds(d time.Duration) (uint32, error) {
|
||||
if d == Infinite {
|
||||
return windows.INFINITE, nil
|
||||
}
|
||||
|
||||
millis := d.Milliseconds()
|
||||
if millis >= windows.INFINITE {
|
||||
return 0, ErrDurationOutOfRange
|
||||
|
||||
+63
@@ -7,9 +7,14 @@
|
||||
package wingoes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
_ "golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
// UserSIDs contains pointers to the SIDs for a user and their primary group.
|
||||
@@ -78,3 +83,61 @@ func getTokenInfoFixedLen[T any](token windows.Token, infoClass uint32) (result
|
||||
err := windows.GetTokenInformation(token, infoClass, (*byte)(unsafe.Pointer(&result)), uint32(unsafe.Sizeof(result)), &actualLen)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// MultiSZDecode decodes a UTF-16 buffer containing either a [registry.MULTI_SZ]
|
||||
// value or a Win32 environment block, and returns the contents as a slice of
|
||||
// strings. It returns an empty slice if the value is empty, or nil if buf
|
||||
// cannot be decoded.
|
||||
func MultiSZDecode(buf []uint16) []string {
|
||||
if buf == nil {
|
||||
// Technically an empty value, but not a failure.
|
||||
return []string{}
|
||||
}
|
||||
|
||||
const doubleNulLen = 2
|
||||
if len(buf) < doubleNulLen {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We cannot rely on the len(buf) to determine the end of the data; registry
|
||||
// APIs treat MULTI_SZ strings as double-nul-terminated C-style strings.
|
||||
doubleNulIdx := int(-1)
|
||||
fl := len(buf) - 1
|
||||
for i, v := range buf {
|
||||
if v == 0 && i < fl && buf[i+1] == 0 {
|
||||
doubleNulIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if doubleNulIdx < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
str := string(utf16.Decode(buf[:doubleNulIdx]))
|
||||
if len(str) == 0 {
|
||||
// Technically an empty value, but not a failure.
|
||||
return []string{}
|
||||
}
|
||||
|
||||
return strings.Split(str, "\x00")
|
||||
}
|
||||
|
||||
// MultiSZEncode encodes strs into the format utilized by [registry.MULTI_SZ]
|
||||
// values and Win32 environment blocks. strs may be nil or empty, but it must
|
||||
// not contain any empty strings.
|
||||
func MultiSZEncode(strs []string) ([]uint16, error) {
|
||||
var buf strings.Builder
|
||||
for _, v := range strs {
|
||||
if len(v) == 0 {
|
||||
return nil, fmt.Errorf("MultiSZEncode: %w: empty strings not permitted", os.ErrInvalid)
|
||||
}
|
||||
buf.WriteString(v)
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
if buf.Len() == 0 {
|
||||
// So that we end with a double-nul in the empty case
|
||||
buf.WriteByte(0)
|
||||
}
|
||||
buf.WriteByte(0)
|
||||
return utf16.Encode([]rune(buf.String())), nil
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package wingoes
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
|
||||
procGetVersionExW = modkernel32.NewProc("GetVersionExW")
|
||||
)
|
||||
|
||||
func getVersionEx(osv *_OSVERSIONINFOEX) (err error) {
|
||||
err = procGetVersionExW.Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r1, _, e1 := syscall.SyscallN(procGetVersionExW.Addr(), uintptr(unsafe.Pointer(osv)))
|
||||
if int32(r1) == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in new issue
Block a user