本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

379 lines
11KB

  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. package windows
  5. import (
  6. "sync"
  7. "sync/atomic"
  8. "syscall"
  9. "unsafe"
  10. )
  11. // DLLError describes reasons for DLL load failures.
  12. type DLLError struct {
  13. Err error
  14. ObjName string
  15. Msg string
  16. }
  17. func (e *DLLError) Error() string { return e.Msg }
  18. // Implemented in runtime/syscall_windows.goc; we provide jumps to them in our assembly file.
  19. func loadlibrary(filename *uint16) (handle uintptr, err syscall.Errno)
  20. func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err syscall.Errno)
  21. // A DLL implements access to a single DLL.
  22. type DLL struct {
  23. Name string
  24. Handle Handle
  25. }
  26. // LoadDLL loads DLL file into memory.
  27. //
  28. // Warning: using LoadDLL without an absolute path name is subject to
  29. // DLL preloading attacks. To safely load a system DLL, use LazyDLL
  30. // with System set to true, or use LoadLibraryEx directly.
  31. func LoadDLL(name string) (dll *DLL, err error) {
  32. namep, err := UTF16PtrFromString(name)
  33. if err != nil {
  34. return nil, err
  35. }
  36. h, e := loadlibrary(namep)
  37. if e != 0 {
  38. return nil, &DLLError{
  39. Err: e,
  40. ObjName: name,
  41. Msg: "Failed to load " + name + ": " + e.Error(),
  42. }
  43. }
  44. d := &DLL{
  45. Name: name,
  46. Handle: Handle(h),
  47. }
  48. return d, nil
  49. }
  50. // MustLoadDLL is like LoadDLL but panics if load operation failes.
  51. func MustLoadDLL(name string) *DLL {
  52. d, e := LoadDLL(name)
  53. if e != nil {
  54. panic(e)
  55. }
  56. return d
  57. }
  58. // FindProc searches DLL d for procedure named name and returns *Proc
  59. // if found. It returns an error if search fails.
  60. func (d *DLL) FindProc(name string) (proc *Proc, err error) {
  61. namep, err := BytePtrFromString(name)
  62. if err != nil {
  63. return nil, err
  64. }
  65. a, e := getprocaddress(uintptr(d.Handle), namep)
  66. if e != 0 {
  67. return nil, &DLLError{
  68. Err: e,
  69. ObjName: name,
  70. Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
  71. }
  72. }
  73. p := &Proc{
  74. Dll: d,
  75. Name: name,
  76. addr: a,
  77. }
  78. return p, nil
  79. }
  80. // MustFindProc is like FindProc but panics if search fails.
  81. func (d *DLL) MustFindProc(name string) *Proc {
  82. p, e := d.FindProc(name)
  83. if e != nil {
  84. panic(e)
  85. }
  86. return p
  87. }
  88. // Release unloads DLL d from memory.
  89. func (d *DLL) Release() (err error) {
  90. return FreeLibrary(d.Handle)
  91. }
  92. // A Proc implements access to a procedure inside a DLL.
  93. type Proc struct {
  94. Dll *DLL
  95. Name string
  96. addr uintptr
  97. }
  98. // Addr returns the address of the procedure represented by p.
  99. // The return value can be passed to Syscall to run the procedure.
  100. func (p *Proc) Addr() uintptr {
  101. return p.addr
  102. }
  103. //go:uintptrescapes
  104. // Call executes procedure p with arguments a. It will panic, if more than 15 arguments
  105. // are supplied.
  106. //
  107. // The returned error is always non-nil, constructed from the result of GetLastError.
  108. // Callers must inspect the primary return value to decide whether an error occurred
  109. // (according to the semantics of the specific function being called) before consulting
  110. // the error. The error will be guaranteed to contain windows.Errno.
  111. func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
  112. switch len(a) {
  113. case 0:
  114. return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
  115. case 1:
  116. return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
  117. case 2:
  118. return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
  119. case 3:
  120. return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
  121. case 4:
  122. return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
  123. case 5:
  124. return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
  125. case 6:
  126. return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
  127. case 7:
  128. return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
  129. case 8:
  130. 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)
  131. case 9:
  132. 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])
  133. case 10:
  134. 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)
  135. case 11:
  136. 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)
  137. case 12:
  138. 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])
  139. case 13:
  140. 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)
  141. case 14:
  142. 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)
  143. case 15:
  144. 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])
  145. default:
  146. panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
  147. }
  148. }
  149. // A LazyDLL implements access to a single DLL.
  150. // It will delay the load of the DLL until the first
  151. // call to its Handle method or to one of its
  152. // LazyProc's Addr method.
  153. type LazyDLL struct {
  154. Name string
  155. // System determines whether the DLL must be loaded from the
  156. // Windows System directory, bypassing the normal DLL search
  157. // path.
  158. System bool
  159. mu sync.Mutex
  160. dll *DLL // non nil once DLL is loaded
  161. }
  162. // Load loads DLL file d.Name into memory. It returns an error if fails.
  163. // Load will not try to load DLL, if it is already loaded into memory.
  164. func (d *LazyDLL) Load() error {
  165. // Non-racy version of:
  166. // if d.dll != nil {
  167. if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil {
  168. return nil
  169. }
  170. d.mu.Lock()
  171. defer d.mu.Unlock()
  172. if d.dll != nil {
  173. return nil
  174. }
  175. // kernel32.dll is special, since it's where LoadLibraryEx comes from.
  176. // The kernel already special-cases its name, so it's always
  177. // loaded from system32.
  178. var dll *DLL
  179. var err error
  180. if d.Name == "kernel32.dll" {
  181. dll, err = LoadDLL(d.Name)
  182. } else {
  183. dll, err = loadLibraryEx(d.Name, d.System)
  184. }
  185. if err != nil {
  186. return err
  187. }
  188. // Non-racy version of:
  189. // d.dll = dll
  190. atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
  191. return nil
  192. }
  193. // mustLoad is like Load but panics if search fails.
  194. func (d *LazyDLL) mustLoad() {
  195. e := d.Load()
  196. if e != nil {
  197. panic(e)
  198. }
  199. }
  200. // Handle returns d's module handle.
  201. func (d *LazyDLL) Handle() uintptr {
  202. d.mustLoad()
  203. return uintptr(d.dll.Handle)
  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. // NewLazyDLL creates new LazyDLL associated with DLL file.
  210. func NewLazyDLL(name string) *LazyDLL {
  211. return &LazyDLL{Name: name}
  212. }
  213. // NewLazySystemDLL is like NewLazyDLL, but will only
  214. // search Windows System directory for the DLL if name is
  215. // a base name (like "advapi32.dll").
  216. func NewLazySystemDLL(name string) *LazyDLL {
  217. return &LazyDLL{Name: name, System: true}
  218. }
  219. // A LazyProc implements access to a procedure inside a LazyDLL.
  220. // It delays the lookup until the Addr method is called.
  221. type LazyProc struct {
  222. Name string
  223. mu sync.Mutex
  224. l *LazyDLL
  225. proc *Proc
  226. }
  227. // Find searches DLL for procedure named p.Name. It returns
  228. // an error if search fails. Find will not search procedure,
  229. // if it is already found and loaded into memory.
  230. func (p *LazyProc) Find() error {
  231. // Non-racy version of:
  232. // if p.proc == nil {
  233. if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
  234. p.mu.Lock()
  235. defer p.mu.Unlock()
  236. if p.proc == nil {
  237. e := p.l.Load()
  238. if e != nil {
  239. return e
  240. }
  241. proc, e := p.l.dll.FindProc(p.Name)
  242. if e != nil {
  243. return e
  244. }
  245. // Non-racy version of:
  246. // p.proc = proc
  247. atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
  248. }
  249. }
  250. return nil
  251. }
  252. // mustFind is like Find but panics if search fails.
  253. func (p *LazyProc) mustFind() {
  254. e := p.Find()
  255. if e != nil {
  256. panic(e)
  257. }
  258. }
  259. // Addr returns the address of the procedure represented by p.
  260. // The return value can be passed to Syscall to run the procedure.
  261. // It will panic if the procedure cannot be found.
  262. func (p *LazyProc) Addr() uintptr {
  263. p.mustFind()
  264. return p.proc.Addr()
  265. }
  266. //go:uintptrescapes
  267. // Call executes procedure p with arguments a. It will panic, if more than 15 arguments
  268. // are supplied. It will also panic if the procedure cannot be found.
  269. //
  270. // The returned error is always non-nil, constructed from the result of GetLastError.
  271. // Callers must inspect the primary return value to decide whether an error occurred
  272. // (according to the semantics of the specific function being called) before consulting
  273. // the error. The error will be guaranteed to contain windows.Errno.
  274. func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
  275. p.mustFind()
  276. return p.proc.Call(a...)
  277. }
  278. var canDoSearchSystem32Once struct {
  279. sync.Once
  280. v bool
  281. }
  282. func initCanDoSearchSystem32() {
  283. // https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
  284. // "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
  285. // Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
  286. // systems that have KB2533623 installed. To determine whether the
  287. // flags are available, use GetProcAddress to get the address of the
  288. // AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
  289. // function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
  290. // flags can be used with LoadLibraryEx."
  291. canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil)
  292. }
  293. func canDoSearchSystem32() bool {
  294. canDoSearchSystem32Once.Do(initCanDoSearchSystem32)
  295. return canDoSearchSystem32Once.v
  296. }
  297. func isBaseName(name string) bool {
  298. for _, c := range name {
  299. if c == ':' || c == '/' || c == '\\' {
  300. return false
  301. }
  302. }
  303. return true
  304. }
  305. // loadLibraryEx wraps the Windows LoadLibraryEx function.
  306. //
  307. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
  308. //
  309. // If name is not an absolute path, LoadLibraryEx searches for the DLL
  310. // in a variety of automatic locations unless constrained by flags.
  311. // See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx
  312. func loadLibraryEx(name string, system bool) (*DLL, error) {
  313. loadDLL := name
  314. var flags uintptr
  315. if system {
  316. if canDoSearchSystem32() {
  317. const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
  318. flags = LOAD_LIBRARY_SEARCH_SYSTEM32
  319. } else if isBaseName(name) {
  320. // WindowsXP or unpatched Windows machine
  321. // trying to load "foo.dll" out of the system
  322. // folder, but LoadLibraryEx doesn't support
  323. // that yet on their system, so emulate it.
  324. systemdir, err := GetSystemDirectory()
  325. if err != nil {
  326. return nil, err
  327. }
  328. loadDLL = systemdir + "\\" + name
  329. }
  330. }
  331. h, err := LoadLibraryEx(loadDLL, 0, flags)
  332. if err != nil {
  333. return nil, err
  334. }
  335. return &DLL{Name: name, Handle: h}, nil
  336. }
  337. type errString string
  338. func (s errString) Error() string { return string(s) }
上海开阖软件有限公司 沪ICP备12045867号-1