syscall_windows.go RawHistoryFolder
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
3
4package purego
5
6import (
7 "errors"
8 "reflect"
9 "sync"
10 "syscall"
11 "unsafe"
12)
13
14var syscallXABI0 uintptr
15
16func syscall_syscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
17 r1, r2, errno := syscall.SyscallN(fn, args...)
18 return r1, r2, uintptr(errno)
19}
20
21// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention.
22// This is useful when interoperating with Windows code requiring callbacks. The argument is expected to be a
23// function with one uintptr-sized result. The function must not have arguments with size larger than the
24// size of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory
25// allocated for these callbacks is never released. Between NewCallback and NewCallbackCDecl, at least 1024
26// callbacks can always be created. Although this function is similar to the darwin version it may act
27// differently.
28//
29// Every call to NewCallback creates a new callback even for the same function value, so passing a Go callback to C
30// inside a loop (e.g. a qsort comparator) keeps consuming callbacks and eventually panics once they are exhausted.
31// The same happens when a func value is passed to a C function, as [RegisterFunc] creates a new callback for each
32// call. Create the callback once with NewCallback and reuse the returned pointer instead.
33func NewCallback(fn any) uintptr {
34 isCDecl := false
35 ty := reflect.TypeOf(fn)
36 if ty == nil || ty.Kind() != reflect.Func {
37 panic("purego: the type must be a function but was not")
38 }
39 for i := range ty.NumIn() {
40 in := ty.In(i)
41 if !in.AssignableTo(reflect.TypeFor[CDecl]()) {
42 continue
43 }
44 if i != 0 {
45 panic("purego: CDecl must be the first argument")
46 }
47 isCDecl = true
48 }
49 if isCDecl {
50 return syscall.NewCallbackCDecl(fn)
51 }
52 return syscall.NewCallback(fn)
53}
54
55// CallbackArgs is the integer-class argument block of one C call into a
56// NewCallbackInts callback. It is only valid during the callback.
57type CallbackArgs struct{}
58
59// Int returns the i-th integer-class argument (0-based), following the platform C ABI.
60func (a *CallbackArgs) Int(i int) uintptr {
61 panic("purego: NewCallbackInts is not supported on windows")
62}
63
64// NewCallbackInts returns a C function pointer for an integer-class callback.
65// It is not supported on Windows.
66func NewCallbackInts(fn func(a *CallbackArgs) uintptr) uintptr {
67 panic("purego: NewCallbackInts is not supported on windows")
68}
69
70// NewCallbackFnPtr converts a Go function pointer to a Windows callback and reuses an existing callback when possible.
71func NewCallbackFnPtr(fnPtr any) uintptr {
72 val := reflect.ValueOf(fnPtr)
73 if val.IsNil() {
74 panic("purego: function must not be nil")
75 }
76 if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Func {
77 panic("purego: the type must be a function pointer but was not")
78 }
79 if addr, ok := getCallbackByFnPtr(val); ok {
80 return addr
81 }
82 addr := NewCallback(val.Elem().Interface())
83 cbs.lock.Lock()
84 cbs.knownFnPtr[val.Pointer()] = addr
85 cbs.lock.Unlock()
86 return addr
87}
88
89// UnrefCallback is unsupported on Windows because runtime callback slots cannot be reclaimed.
90func UnrefCallback(cb uintptr) error {
91 if cb == 0 {
92 return errors.New("callback not found")
93 }
94 return errors.New("purego: unreferencing callbacks is unsupported on windows")
95}
96
97// UnrefCallbackFnPtr is unsupported on Windows because runtime callback slots cannot be reclaimed.
98func UnrefCallbackFnPtr(fnPtr any) error {
99 val := reflect.ValueOf(fnPtr)
100 if val.IsNil() {
101 panic("purego: function must not be nil")
102 }
103 if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Func {
104 panic("purego: the type must be a function pointer but was not")
105 }
106 return errors.New("purego: unreferencing callbacks is unsupported on windows")
107}
108
109// maxCb is the maximum number of tracked Windows callback function pointers.
110const maxCB = 1024
111
112var cbs = struct {
113 lock sync.RWMutex
114 knownFnPtr map[uintptr]uintptr
115}{
116 knownFnPtr: make(map[uintptr]uintptr, maxCB),
117}
118
119func getCallbackByFnPtr(val reflect.Value) (uintptr, bool) {
120 cbs.lock.RLock()
121 defer cbs.lock.RUnlock()
122 addr, ok := cbs.knownFnPtr[val.Pointer()]
123 return addr, ok
124}
125
126func loadSymbol(handle uintptr, name string) (uintptr, error) {
127 return syscall.GetProcAddress(syscall.Handle(handle), name)
128}
129
130// callbackMaxFrame is a stub definition.
131const callbackMaxFrame = 0
132
133func callbackArgFromStack(argsBase unsafe.Pointer, stackSlot int, stackByteOffset *uintptr, inType reflect.Type) reflect.Value {
134 panic("purego: callbackArgFromStack should not be called on windows")
135}