github.com/zhongdalu/gf@v1.0.0/third/golang.org/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 // 35 // Warning: using LoadDLL without an absolute path name is subject to 36 // DLL preloading attacks. To safely load a system DLL, use LazyDLL 37 // with System set to true, or use LoadLibraryEx directly. 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 failes. 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 if e != 0 { 76 return nil, &DLLError{ 77 Err: e, 78 ObjName: name, 79 Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(), 80 } 81 } 82 p := &Proc{ 83 Dll: d, 84 Name: name, 85 addr: a, 86 } 87 return p, nil 88 } 89 90 // MustFindProc is like FindProc but panics if search fails. 91 func (d *DLL) MustFindProc(name string) *Proc { 92 p, e := d.FindProc(name) 93 if e != nil { 94 panic(e) 95 } 96 return p 97 } 98 99 // Release unloads DLL d from memory. 100 func (d *DLL) Release() (err error) { 101 return FreeLibrary(d.Handle) 102 } 103 104 // A Proc implements access to a procedure inside a DLL. 105 type Proc struct { 106 Dll *DLL 107 Name string 108 addr uintptr 109 } 110 111 // Addr returns the address of the procedure represented by p. 112 // The return value can be passed to Syscall to run the procedure. 113 func (p *Proc) Addr() uintptr { 114 return p.addr 115 } 116 117 //go:uintptrescapes 118 119 // Call executes procedure p with arguments a. It will panic, if more than 15 arguments 120 // are supplied. 121 // 122 // The returned error is always non-nil, constructed from the result of GetLastError. 123 // Callers must inspect the primary return value to decide whether an error occurred 124 // (according to the semantics of the specific function being called) before consulting 125 // the error. The error will be guaranteed to contain windows.Errno. 126 func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { 127 switch len(a) { 128 case 0: 129 return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0) 130 case 1: 131 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0) 132 case 2: 133 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0) 134 case 3: 135 return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2]) 136 case 4: 137 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0) 138 case 5: 139 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0) 140 case 6: 141 return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5]) 142 case 7: 143 return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0) 144 case 8: 145 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) 146 case 9: 147 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]) 148 case 10: 149 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) 150 case 11: 151 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) 152 case 12: 153 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]) 154 case 13: 155 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) 156 case 14: 157 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) 158 case 15: 159 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]) 160 default: 161 panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".") 162 } 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 Name string 171 172 // System determines whether the DLL must be loaded from the 173 // Windows System directory, bypassing the normal DLL search 174 // path. 175 System bool 176 177 mu sync.Mutex 178 dll *DLL // non nil once DLL is loaded 179 } 180 181 // Load loads DLL file d.Name into memory. It returns an error if fails. 182 // Load will not try to load DLL, if it is already loaded into memory. 183 func (d *LazyDLL) Load() error { 184 // Non-racy version of: 185 // if d.dll != nil { 186 if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil { 187 return nil 188 } 189 d.mu.Lock() 190 defer d.mu.Unlock() 191 if d.dll != nil { 192 return nil 193 } 194 195 // kernel32.dll is special, since it's where LoadLibraryEx comes from. 196 // The kernel already special-cases its name, so it's always 197 // loaded from system32. 198 var dll *DLL 199 var err error 200 if d.Name == "kernel32.dll" { 201 dll, err = LoadDLL(d.Name) 202 } else { 203 dll, err = loadLibraryEx(d.Name, d.System) 204 } 205 if err != nil { 206 return err 207 } 208 209 // Non-racy version of: 210 // d.dll = dll 211 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll)) 212 return nil 213 } 214 215 // mustLoad is like Load but panics if search fails. 216 func (d *LazyDLL) mustLoad() { 217 e := d.Load() 218 if e != nil { 219 panic(e) 220 } 221 } 222 223 // Handle returns d's module handle. 224 func (d *LazyDLL) Handle() uintptr { 225 d.mustLoad() 226 return uintptr(d.dll.Handle) 227 } 228 229 // NewProc returns a LazyProc for accessing the named procedure in the DLL d. 230 func (d *LazyDLL) NewProc(name string) *LazyProc { 231 return &LazyProc{l: d, Name: name} 232 } 233 234 // NewLazyDLL creates new LazyDLL associated with DLL file. 235 func NewLazyDLL(name string) *LazyDLL { 236 return &LazyDLL{Name: name} 237 } 238 239 // NewLazySystemDLL is like NewLazyDLL, but will only 240 // search Windows System directory for the DLL if name is 241 // a base name (like "advapi32.dll"). 242 func NewLazySystemDLL(name string) *LazyDLL { 243 return &LazyDLL{Name: name, System: true} 244 } 245 246 // A LazyProc implements access to a procedure inside a LazyDLL. 247 // It delays the lookup until the Addr method is called. 248 type LazyProc struct { 249 Name string 250 251 mu sync.Mutex 252 l *LazyDLL 253 proc *Proc 254 } 255 256 // Find searches DLL for procedure named p.Name. It returns 257 // an error if search fails. Find will not search procedure, 258 // if it is already found and loaded into memory. 259 func (p *LazyProc) Find() error { 260 // Non-racy version of: 261 // if p.proc == nil { 262 if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil { 263 p.mu.Lock() 264 defer p.mu.Unlock() 265 if p.proc == nil { 266 e := p.l.Load() 267 if e != nil { 268 return e 269 } 270 proc, e := p.l.dll.FindProc(p.Name) 271 if e != nil { 272 return e 273 } 274 // Non-racy version of: 275 // p.proc = proc 276 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc)) 277 } 278 } 279 return nil 280 } 281 282 // mustFind is like Find but panics if search fails. 283 func (p *LazyProc) mustFind() { 284 e := p.Find() 285 if e != nil { 286 panic(e) 287 } 288 } 289 290 // Addr returns the address of the procedure represented by p. 291 // The return value can be passed to Syscall to run the procedure. 292 // It will panic if the procedure cannot be found. 293 func (p *LazyProc) Addr() uintptr { 294 p.mustFind() 295 return p.proc.Addr() 296 } 297 298 //go:uintptrescapes 299 300 // Call executes procedure p with arguments a. It will panic, if more than 15 arguments 301 // are supplied. It will also panic if the procedure cannot be found. 302 // 303 // The returned error is always non-nil, constructed from the result of GetLastError. 304 // Callers must inspect the primary return value to decide whether an error occurred 305 // (according to the semantics of the specific function being called) before consulting 306 // the error. The error will be guaranteed to contain windows.Errno. 307 func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { 308 p.mustFind() 309 return p.proc.Call(a...) 310 } 311 312 var canDoSearchSystem32Once struct { 313 sync.Once 314 v bool 315 } 316 317 func initCanDoSearchSystem32() { 318 // https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says: 319 // "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows 320 // Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on 321 // systems that have KB2533623 installed. To determine whether the 322 // flags are available, use GetProcAddress to get the address of the 323 // AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories 324 // function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_* 325 // flags can be used with LoadLibraryEx." 326 canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil) 327 } 328 329 func canDoSearchSystem32() bool { 330 canDoSearchSystem32Once.Do(initCanDoSearchSystem32) 331 return canDoSearchSystem32Once.v 332 } 333 334 func isBaseName(name string) bool { 335 for _, c := range name { 336 if c == ':' || c == '/' || c == '\\' { 337 return false 338 } 339 } 340 return true 341 } 342 343 // loadLibraryEx wraps the Windows LoadLibraryEx function. 344 // 345 // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx 346 // 347 // If name is not an absolute path, LoadLibraryEx searches for the DLL 348 // in a variety of automatic locations unless constrained by flags. 349 // See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx 350 func loadLibraryEx(name string, system bool) (*DLL, error) { 351 loadDLL := name 352 var flags uintptr 353 if system { 354 if canDoSearchSystem32() { 355 const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 356 flags = LOAD_LIBRARY_SEARCH_SYSTEM32 357 } else if isBaseName(name) { 358 // WindowsXP or unpatched Windows machine 359 // trying to load "foo.dll" out of the system 360 // folder, but LoadLibraryEx doesn't support 361 // that yet on their system, so emulate it. 362 windir, _ := Getenv("WINDIR") // old var; apparently works on XP 363 if windir == "" { 364 return nil, errString("%WINDIR% not defined") 365 } 366 loadDLL = windir + "\\System32\\" + name 367 } 368 } 369 h, err := LoadLibraryEx(loadDLL, 0, flags) 370 if err != nil { 371 return nil, err 372 } 373 return &DLL{Name: name, Handle: h}, nil 374 } 375 376 type errString string 377 378 func (s errString) Error() string { return string(s) }