github.com/jwijenbergh/purego@v0.0.0-20240126093400-70ff3a61df13/syscall_windows.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2022 The Ebitengine Authors 3 4 package purego 5 6 import ( 7 "reflect" 8 "sync" 9 "syscall" 10 _ "unsafe" // only for go:linkname 11 12 "golang.org/x/sys/windows" 13 ) 14 15 // maxCb is the maximum number of callbacks 16 const maxCB = 1024 17 18 var syscall15XABI0 uintptr 19 20 type syscall15Args struct { 21 fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr 22 f1, f2, f3, f4, f5, f6, f7, f8 uintptr 23 r1, r2, err uintptr 24 } 25 26 func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { 27 r1, r2, errno := syscall.Syscall15(fn, 15, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) 28 return r1, r2, uintptr(errno) 29 } 30 31 // NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention. 32 // This is useful when interoperating with Windows code requiring callbacks. The argument is expected to be a 33 // function with one uintptr-sized result. The function must not have arguments with size larger than the 34 // size of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory 35 // allocated for these callbacks is never released. Between NewCallback and NewCallbackCDecl, at least 1024 36 // callbacks can always be created. Although this function is similiar to the darwin version it may act 37 // differently. 38 func NewCallback(fn interface{}) uintptr { 39 val := reflect.ValueOf(fn) 40 if val.Kind() != reflect.Func { 41 panic("purego: the type must be a function but was not") 42 } 43 if val.IsNil() { 44 panic("purego: function must not be nil") 45 } 46 return syscall.NewCallback(fn) 47 } 48 49 // NewCallbackFnPtr converts a Go function pointer to a function pointer conforming to the stdcall calling convention. 50 // This is useful when interoperating with C code requiring callbacks. The argument is expected to be a 51 // function with one uintptr-sized result. The function must not have arguments with size larger than the 52 // size of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory 53 // allocated for these callbacks is never released. Between NewCallback and NewCallbackCDecl, at least 1024 54 // callbacks can always be created. Although this function is similiar to the darwin version it may act 55 // differently. 56 // 57 // Calling this function multiple times with the same function pointer will return the originally created callback 58 // reference, reducing live callback pressure. 59 func NewCallbackFnPtr(fnptr interface{}) uintptr { 60 val := reflect.ValueOf(fnptr) 61 if val.IsNil() { 62 panic("purego: function must not be nil") 63 } 64 if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Func { 65 panic("purego: the type must be a function pointer but was not") 66 } 67 68 // Re-use callback to function pointer if available 69 if addr, ok := getCallbackByFnPtr(val); ok { 70 return addr 71 } 72 73 addr := syscall.NewCallback(val.Elem().Interface()) 74 75 cbs.lock.Lock() 76 cbs.knownFnPtr[val.Pointer()] = addr 77 cbs.lock.Unlock() 78 return addr 79 } 80 81 var cbs = struct { 82 lock sync.RWMutex 83 knownFnPtr map[uintptr]uintptr // maps function pointers to callback addresses 84 }{ 85 knownFnPtr: make(map[uintptr]uintptr, maxCB), 86 } 87 88 //go:linkname openLibrary openLibrary 89 func openLibrary(name string) (uintptr, error) { 90 handle, err := windows.LoadLibrary(name) 91 return uintptr(handle), err 92 } 93 94 func loadSymbol(handle uintptr, name string) (uintptr, error) { 95 return windows.GetProcAddress(windows.Handle(handle), name) 96 } 97 98 func getCallbackByFnPtr(val reflect.Value) (uintptr, bool) { 99 cbs.lock.RLock() 100 defer cbs.lock.RUnlock() 101 addr, ok := cbs.knownFnPtr[val.Pointer()] 102 return addr, ok 103 }