Path: blob/main/vendor/golang.org/x/sys/windows/dll_windows.go
2880 views
// Copyright 2011 The Go Authors. All rights reserved.1// Use of this source code is governed by a BSD-style2// license that can be found in the LICENSE file.34package windows56import (7"sync"8"sync/atomic"9"syscall"10"unsafe"11)1213// We need to use LoadLibrary and GetProcAddress from the Go runtime, because14// the these symbols are loaded by the system linker and are required to15// dynamically load additional symbols. Note that in the Go runtime, these16// return syscall.Handle and syscall.Errno, but these are the same, in fact,17// as windows.Handle and windows.Errno, and we intend to keep these the same.1819//go:linkname syscall_loadlibrary syscall.loadlibrary20func syscall_loadlibrary(filename *uint16) (handle Handle, err Errno)2122//go:linkname syscall_getprocaddress syscall.getprocaddress23func syscall_getprocaddress(handle Handle, procname *uint8) (proc uintptr, err Errno)2425// DLLError describes reasons for DLL load failures.26type DLLError struct {27Err error28ObjName string29Msg string30}3132func (e *DLLError) Error() string { return e.Msg }3334func (e *DLLError) Unwrap() error { return e.Err }3536// A DLL implements access to a single DLL.37type DLL struct {38Name string39Handle Handle40}4142// LoadDLL loads DLL file into memory.43//44// Warning: using LoadDLL without an absolute path name is subject to45// DLL preloading attacks. To safely load a system DLL, use [NewLazySystemDLL],46// or use [LoadLibraryEx] directly.47func LoadDLL(name string) (dll *DLL, err error) {48namep, err := UTF16PtrFromString(name)49if err != nil {50return nil, err51}52h, e := syscall_loadlibrary(namep)53if e != 0 {54return nil, &DLLError{55Err: e,56ObjName: name,57Msg: "Failed to load " + name + ": " + e.Error(),58}59}60d := &DLL{61Name: name,62Handle: h,63}64return d, nil65}6667// MustLoadDLL is like LoadDLL but panics if load operation fails.68func MustLoadDLL(name string) *DLL {69d, e := LoadDLL(name)70if e != nil {71panic(e)72}73return d74}7576// FindProc searches DLL d for procedure named name and returns *Proc77// if found. It returns an error if search fails.78func (d *DLL) FindProc(name string) (proc *Proc, err error) {79namep, err := BytePtrFromString(name)80if err != nil {81return nil, err82}83a, e := syscall_getprocaddress(d.Handle, namep)84if e != 0 {85return nil, &DLLError{86Err: e,87ObjName: name,88Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),89}90}91p := &Proc{92Dll: d,93Name: name,94addr: a,95}96return p, nil97}9899// MustFindProc is like FindProc but panics if search fails.100func (d *DLL) MustFindProc(name string) *Proc {101p, e := d.FindProc(name)102if e != nil {103panic(e)104}105return p106}107108// FindProcByOrdinal searches DLL d for procedure by ordinal and returns *Proc109// if found. It returns an error if search fails.110func (d *DLL) FindProcByOrdinal(ordinal uintptr) (proc *Proc, err error) {111a, e := GetProcAddressByOrdinal(d.Handle, ordinal)112name := "#" + itoa(int(ordinal))113if e != nil {114return nil, &DLLError{115Err: e,116ObjName: name,117Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),118}119}120p := &Proc{121Dll: d,122Name: name,123addr: a,124}125return p, nil126}127128// MustFindProcByOrdinal is like FindProcByOrdinal but panics if search fails.129func (d *DLL) MustFindProcByOrdinal(ordinal uintptr) *Proc {130p, e := d.FindProcByOrdinal(ordinal)131if e != nil {132panic(e)133}134return p135}136137// Release unloads DLL d from memory.138func (d *DLL) Release() (err error) {139return FreeLibrary(d.Handle)140}141142// A Proc implements access to a procedure inside a DLL.143type Proc struct {144Dll *DLL145Name string146addr uintptr147}148149// Addr returns the address of the procedure represented by p.150// The return value can be passed to Syscall to run the procedure.151func (p *Proc) Addr() uintptr {152return p.addr153}154155//go:uintptrescapes156157// Call executes procedure p with arguments a. It will panic, if more than 15 arguments158// are supplied.159//160// The returned error is always non-nil, constructed from the result of GetLastError.161// Callers must inspect the primary return value to decide whether an error occurred162// (according to the semantics of the specific function being called) before consulting163// the error. The error will be guaranteed to contain windows.Errno.164func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {165switch len(a) {166case 0:167return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)168case 1:169return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)170case 2:171return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)172case 3:173return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])174case 4:175return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)176case 5:177return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)178case 6:179return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])180case 7:181return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)182case 8:183return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)184case 9:185return 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])186case 10:187return 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)188case 11:189return 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)190case 12:191return 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])192case 13:193return 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)194case 14:195return 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)196case 15:197return 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])198default:199panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")200}201}202203// A LazyDLL implements access to a single DLL.204// It will delay the load of the DLL until the first205// call to its Handle method or to one of its206// LazyProc's Addr method.207type LazyDLL struct {208Name string209210// System determines whether the DLL must be loaded from the211// Windows System directory, bypassing the normal DLL search212// path.213System bool214215mu sync.Mutex216dll *DLL // non nil once DLL is loaded217}218219// Load loads DLL file d.Name into memory. It returns an error if fails.220// Load will not try to load DLL, if it is already loaded into memory.221func (d *LazyDLL) Load() error {222// Non-racy version of:223// if d.dll != nil {224if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil {225return nil226}227d.mu.Lock()228defer d.mu.Unlock()229if d.dll != nil {230return nil231}232233// kernel32.dll is special, since it's where LoadLibraryEx comes from.234// The kernel already special-cases its name, so it's always235// loaded from system32.236var dll *DLL237var err error238if d.Name == "kernel32.dll" {239dll, err = LoadDLL(d.Name)240} else {241dll, err = loadLibraryEx(d.Name, d.System)242}243if err != nil {244return err245}246247// Non-racy version of:248// d.dll = dll249atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))250return nil251}252253// mustLoad is like Load but panics if search fails.254func (d *LazyDLL) mustLoad() {255e := d.Load()256if e != nil {257panic(e)258}259}260261// Handle returns d's module handle.262func (d *LazyDLL) Handle() uintptr {263d.mustLoad()264return uintptr(d.dll.Handle)265}266267// NewProc returns a LazyProc for accessing the named procedure in the DLL d.268func (d *LazyDLL) NewProc(name string) *LazyProc {269return &LazyProc{l: d, Name: name}270}271272// NewLazyDLL creates new LazyDLL associated with DLL file.273//274// Warning: using NewLazyDLL without an absolute path name is subject to275// DLL preloading attacks. To safely load a system DLL, use [NewLazySystemDLL].276func NewLazyDLL(name string) *LazyDLL {277return &LazyDLL{Name: name}278}279280// NewLazySystemDLL is like NewLazyDLL, but will only281// search Windows System directory for the DLL if name is282// a base name (like "advapi32.dll").283func NewLazySystemDLL(name string) *LazyDLL {284return &LazyDLL{Name: name, System: true}285}286287// A LazyProc implements access to a procedure inside a LazyDLL.288// It delays the lookup until the Addr method is called.289type LazyProc struct {290Name string291292mu sync.Mutex293l *LazyDLL294proc *Proc295}296297// Find searches DLL for procedure named p.Name. It returns298// an error if search fails. Find will not search procedure,299// if it is already found and loaded into memory.300func (p *LazyProc) Find() error {301// Non-racy version of:302// if p.proc == nil {303if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {304p.mu.Lock()305defer p.mu.Unlock()306if p.proc == nil {307e := p.l.Load()308if e != nil {309return e310}311proc, e := p.l.dll.FindProc(p.Name)312if e != nil {313return e314}315// Non-racy version of:316// p.proc = proc317atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))318}319}320return nil321}322323// mustFind is like Find but panics if search fails.324func (p *LazyProc) mustFind() {325e := p.Find()326if e != nil {327panic(e)328}329}330331// Addr returns the address of the procedure represented by p.332// The return value can be passed to Syscall to run the procedure.333// It will panic if the procedure cannot be found.334func (p *LazyProc) Addr() uintptr {335p.mustFind()336return p.proc.Addr()337}338339//go:uintptrescapes340341// Call executes procedure p with arguments a. It will panic, if more than 15 arguments342// are supplied. It will also panic if the procedure cannot be found.343//344// The returned error is always non-nil, constructed from the result of GetLastError.345// Callers must inspect the primary return value to decide whether an error occurred346// (according to the semantics of the specific function being called) before consulting347// the error. The error will be guaranteed to contain windows.Errno.348func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {349p.mustFind()350return p.proc.Call(a...)351}352353var canDoSearchSystem32Once struct {354sync.Once355v bool356}357358func initCanDoSearchSystem32() {359// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:360// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows361// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on362// systems that have KB2533623 installed. To determine whether the363// flags are available, use GetProcAddress to get the address of the364// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories365// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*366// flags can be used with LoadLibraryEx."367canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil)368}369370func canDoSearchSystem32() bool {371canDoSearchSystem32Once.Do(initCanDoSearchSystem32)372return canDoSearchSystem32Once.v373}374375func isBaseName(name string) bool {376for _, c := range name {377if c == ':' || c == '/' || c == '\\' {378return false379}380}381return true382}383384// loadLibraryEx wraps the Windows LoadLibraryEx function.385//386// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx387//388// If name is not an absolute path, LoadLibraryEx searches for the DLL389// in a variety of automatic locations unless constrained by flags.390// See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx391func loadLibraryEx(name string, system bool) (*DLL, error) {392loadDLL := name393var flags uintptr394if system {395if canDoSearchSystem32() {396flags = LOAD_LIBRARY_SEARCH_SYSTEM32397} else if isBaseName(name) {398// WindowsXP or unpatched Windows machine399// trying to load "foo.dll" out of the system400// folder, but LoadLibraryEx doesn't support401// that yet on their system, so emulate it.402systemdir, err := GetSystemDirectory()403if err != nil {404return nil, err405}406loadDLL = systemdir + "\\" + name407}408}409h, err := LoadLibraryEx(loadDLL, 0, flags)410if err != nil {411return nil, err412}413return &DLL{Name: name, Handle: h}, nil414}415416417