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  }