github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/sys/windows/dll_windows.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package windows 6 7 import ( 8 "sync" 9 "sync/atomic" 10 "syscall" 11 "unsafe" 12 ) 13 14 // DLLError describes reasons for DLL load failures. 15 type DLLError struct { 16 Err error 17 ObjName string 18 Msg string 19 } 20 21 func (e *DLLError) Error() string { return e.Msg } 22 23 // Implemented in runtime/syscall_windows.goc; we provide jumps to them in our assembly file. 24 func loadlibrary(filename *uint16) (handle uintptr, err syscall.Errno) 25 func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err syscall.Errno) 26 27 // A DLL implements access to a single DLL. 28 type DLL struct { 29 Name string 30 Handle Handle 31 } 32 33 // LoadDLL loads DLL file into memory. 34 func LoadDLL(name string) (dll *DLL, err error) { 35 namep, err := UTF16PtrFromString(name) 36 if err != nil { 37 return nil, err 38 } 39 h, e := loadlibrary(namep) 40 if e != 0 { 41 return nil, &DLLError{ 42 Err: e, 43 ObjName: name, 44 Msg: "Failed to load " + name + ": " + e.Error(), 45 } 46 } 47 d := &DLL{ 48 Name: name, 49 Handle: Handle(h), 50 } 51 return d, nil 52 } 53 54 // MustLoadDLL is like LoadDLL but panics if load operation failes. 55 func MustLoadDLL(name string) *DLL { 56 d, e := LoadDLL(name) 57 if e != nil { 58 panic(e) 59 } 60 return d 61 } 62 63 // FindProc searches DLL d for procedure named name and returns *Proc 64 // if found. It returns an error if search fails. 65 func (d *DLL) FindProc(name string) (proc *Proc, err error) { 66 namep, err := BytePtrFromString(name) 67 if err != nil { 68 return nil, err 69 } 70 a, e := getprocaddress(uintptr(d.Handle), namep) 71 if e != 0 { 72 return nil, &DLLError{ 73 Err: e, 74 ObjName: name, 75 Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(), 76 } 77 } 78 p := &Proc{ 79 Dll: d, 80 Name: name, 81 addr: a, 82 } 83 return p, nil 84 } 85 86 // MustFindProc is like FindProc but panics if search fails. 87 func (d *DLL) MustFindProc(name string) *Proc { 88 p, e := d.FindProc(name) 89 if e != nil { 90 panic(e) 91 } 92 return p 93 } 94 95 // Release unloads DLL d from memory. 96 func (d *DLL) Release() (err error) { 97 return FreeLibrary(d.Handle) 98 } 99 100 // A Proc implements access to a procedure inside a DLL. 101 type Proc struct { 102 Dll *DLL 103 Name string 104 addr uintptr 105 } 106 107 // Addr returns the address of the procedure represented by p. 108 // The return value can be passed to Syscall to run the procedure. 109 func (p *Proc) Addr() uintptr { 110 return p.addr 111 } 112 113 // Call executes procedure p with arguments a. It will panic, if more then 15 arguments 114 // are supplied. 115 // 116 // The returned error is always non-nil, constructed from the result of GetLastError. 117 // Callers must inspect the primary return value to decide whether an error occurred 118 // (according to the semantics of the specific function being called) before consulting 119 // the error. The error will be guaranteed to contain windows.Errno. 120 func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { 121 switch len(a) { 122 case 0: 123 return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0) 124 case 1: 125 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0) 126 case 2: 127 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0) 128 case 3: 129 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2]) 130 case 4: 131 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0) 132 case 5: 133 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0) 134 case 6: 135 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5]) 136 case 7: 137 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0) 138 case 8: 139 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0) 140 case 9: 141 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]) 142 case 10: 143 return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0) 144 case 11: 145 return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0) 146 case 12: 147 return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]) 148 case 13: 149 return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0) 150 case 14: 151 return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0) 152 case 15: 153 return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14]) 154 default: 155 panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".") 156 } 157 return 158 } 159 160 // A LazyDLL implements access to a single DLL. 161 // It will delay the load of the DLL until the first 162 // call to its Handle method or to one of its 163 // LazyProc's Addr method. 164 type LazyDLL struct { 165 mu sync.Mutex 166 dll *DLL // non nil once DLL is loaded 167 Name string 168 } 169 170 // Load loads DLL file d.Name into memory. It returns an error if fails. 171 // Load will not try to load DLL, if it is already loaded into memory. 172 func (d *LazyDLL) Load() error { 173 // Non-racy version of: 174 // if d.dll == nil { 175 if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil { 176 d.mu.Lock() 177 defer d.mu.Unlock() 178 if d.dll == nil { 179 dll, e := LoadDLL(d.Name) 180 if e != nil { 181 return e 182 } 183 // Non-racy version of: 184 // d.dll = dll 185 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll)) 186 } 187 } 188 return nil 189 } 190 191 // mustLoad is like Load but panics if search fails. 192 func (d *LazyDLL) mustLoad() { 193 e := d.Load() 194 if e != nil { 195 panic(e) 196 } 197 } 198 199 // Handle returns d's module handle. 200 func (d *LazyDLL) Handle() uintptr { 201 d.mustLoad() 202 return uintptr(d.dll.Handle) 203 } 204 205 // NewProc returns a LazyProc for accessing the named procedure in the DLL d. 206 func (d *LazyDLL) NewProc(name string) *LazyProc { 207 return &LazyProc{l: d, Name: name} 208 } 209 210 // NewLazyDLL creates new LazyDLL associated with DLL file. 211 func NewLazyDLL(name string) *LazyDLL { 212 return &LazyDLL{Name: name} 213 } 214 215 // A LazyProc implements access to a procedure inside a LazyDLL. 216 // It delays the lookup until the Addr method is called. 217 type LazyProc struct { 218 mu sync.Mutex 219 Name string 220 l *LazyDLL 221 proc *Proc 222 } 223 224 // Find searches DLL for procedure named p.Name. It returns 225 // an error if search fails. Find will not search procedure, 226 // if it is already found and loaded into memory. 227 func (p *LazyProc) Find() error { 228 // Non-racy version of: 229 // if p.proc == nil { 230 if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil { 231 p.mu.Lock() 232 defer p.mu.Unlock() 233 if p.proc == nil { 234 e := p.l.Load() 235 if e != nil { 236 return e 237 } 238 proc, e := p.l.dll.FindProc(p.Name) 239 if e != nil { 240 return e 241 } 242 // Non-racy version of: 243 // p.proc = proc 244 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc)) 245 } 246 } 247 return nil 248 } 249 250 // mustFind is like Find but panics if search fails. 251 func (p *LazyProc) mustFind() { 252 e := p.Find() 253 if e != nil { 254 panic(e) 255 } 256 } 257 258 // Addr returns the address of the procedure represented by p. 259 // The return value can be passed to Syscall to run the procedure. 260 func (p *LazyProc) Addr() uintptr { 261 p.mustFind() 262 return p.proc.Addr() 263 } 264 265 // Call executes procedure p with arguments a. It will panic, if more then 15 arguments 266 // are supplied. 267 // 268 // The returned error is always non-nil, constructed from the result of GetLastError. 269 // Callers must inspect the primary return value to decide whether an error occurred 270 // (according to the semantics of the specific function being called) before consulting 271 // the error. The error will be guaranteed to contain windows.Errno. 272 func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { 273 p.mustFind() 274 return p.proc.Call(a...) 275 }