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