]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: dll function load and calling changes
authorAlex Brainman <alex.brainman@gmail.com>
Sat, 15 Oct 2011 06:29:25 +0000 (17:29 +1100)
committerAlex Brainman <alex.brainman@gmail.com>
Sat, 15 Oct 2011 06:29:25 +0000 (17:29 +1100)
New DLL and Proc types to manage and call dll functions. These were
used to simplify syscall tests in runtime package. They were also
used to implement LazyDLL and LazyProc.

LazyProc, like Proc, now have Call function, that just a wrapper for
SyscallN. It is not as efficient as Syscall, but easier to use.

NewLazyDLL now supports non-ascii filenames.

LazyDLL and LazyProc now have Load and Find methods. These can be used
during runtime to discover if some dll functions are not present.

All dll functions now return errors that fit os.Error interface. They
also contain Windows error number.

Some of these changes are suggested by jp.

R=golang-dev, jp, rsc
CC=golang-dev
https://golang.org/cl/5272042

src/pkg/runtime/syscall_windows_test.go
src/pkg/runtime/windows/os.h
src/pkg/runtime/windows/syscall.goc
src/pkg/runtime/windows/thread.c
src/pkg/syscall/Makefile
src/pkg/syscall/dll_windows.go [new file with mode: 0644]
src/pkg/syscall/syscall_windows.go

index 32eb0533fffaaa32626e5a33e3809d3091d8590d..c99756efa4a6622bf63ffcd122c194e728eadcef 100644 (file)
@@ -10,24 +10,34 @@ import (
        "testing"
 )
 
-func TestStdCall(t *testing.T) {
-       type Rect struct {
-               left, top, right, bottom int32
-       }
+type DLL struct {
+       *syscall.DLL
+       t *testing.T
+}
 
-       h, e := syscall.LoadLibrary("user32.dll")
-       if e != 0 {
-               t.Fatal("LoadLibrary(USER32)")
+func GetDLL(t *testing.T, name string) *DLL {
+       d, e := syscall.LoadDLL(name)
+       if e != nil {
+               t.Fatal(e)
        }
-       p, e := syscall.GetProcAddress(h, "UnionRect")
-       if e != 0 {
-               t.Fatal("GetProcAddress(USER32.UnionRect)")
+       return &DLL{DLL: d, t: t}
+}
+
+func (d *DLL) Proc(name string) *syscall.Proc {
+       p, e := d.FindProc(name)
+       if e != nil {
+               d.t.Fatal(e)
        }
+       return p
+}
 
+func TestStdCall(t *testing.T) {
+       type Rect struct {
+               left, top, right, bottom int32
+       }
        res := Rect{}
        expected := Rect{1, 1, 40, 60}
-       a, _, _ := syscall.Syscall(uintptr(p),
-               3,
+       a, _, _ := GetDLL(t, "user32.dll").Proc("UnionRect").Call(
                uintptr(unsafe.Pointer(&res)),
                uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})),
                uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50})))
@@ -74,24 +84,14 @@ func Test64BitReturnStdCall(t *testing.T) {
                Reserve           byte
        }
 
-       kernel32, e := syscall.LoadLibrary("kernel32.dll")
-       if e != 0 {
-               t.Fatalf("LoadLibrary(kernel32.dll) failed: %s", syscall.Errstr(e))
-       }
-       setMask, e := syscall.GetProcAddress(kernel32, "VerSetConditionMask")
-       if e != 0 {
-               t.Fatalf("GetProcAddress(kernel32.dll, VerSetConditionMask) failed: %s", syscall.Errstr(e))
-       }
-       verifyVersion, e := syscall.GetProcAddress(kernel32, "VerifyVersionInfoW")
-       if e != 0 {
-               t.Fatalf("GetProcAddress(kernel32.dll, VerifyVersionInfoW) failed: %s", syscall.Errstr(e))
-       }
+       d := GetDLL(t, "kernel32.dll")
 
        var m1, m2 uintptr
-       m1, m2, _ = syscall.Syscall6(setMask, 4, m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL, 0, 0)
-       m1, m2, _ = syscall.Syscall6(setMask, 4, m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL, 0, 0)
-       m1, m2, _ = syscall.Syscall6(setMask, 4, m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL, 0, 0)
-       m1, m2, _ = syscall.Syscall6(setMask, 4, m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL, 0, 0)
+       VerSetConditionMask := d.Proc("VerSetConditionMask")
+       m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL)
+       m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL)
+       m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL)
+       m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL)
 
        vi := OSVersionInfoEx{
                MajorVersion:     5,
@@ -100,63 +100,42 @@ func Test64BitReturnStdCall(t *testing.T) {
                ServicePackMinor: 0,
        }
        vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi))
-       r, _, e2 := syscall.Syscall6(verifyVersion,
-               4,
+       r, _, e2 := d.Proc("VerifyVersionInfoW").Call(
                uintptr(unsafe.Pointer(&vi)),
                VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR,
-               m1, m2, 0, 0)
+               m1, m2)
        if r == 0 && e2 != ERROR_OLD_WIN_VERSION {
                t.Errorf("VerifyVersionInfo failed: (%d) %s", e2, syscall.Errstr(int(e2)))
        }
 }
 
 func TestCDecl(t *testing.T) {
-       h, e := syscall.LoadLibrary("user32.dll")
-       if e != 0 {
-               t.Fatal("LoadLibrary(USER32)")
-       }
-       p, e := syscall.GetProcAddress(h, "wsprintfA")
-       if e != 0 {
-               t.Fatal("GetProcAddress(USER32.wsprintfA)")
-       }
-
        var buf [50]byte
-       a, _, _ := syscall.Syscall6(uintptr(p),
-               5,
+       a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call(
                uintptr(unsafe.Pointer(&buf[0])),
                uintptr(unsafe.Pointer(syscall.StringBytePtr("%d %d %d"))),
-               1000, 2000, 3000, 0)
+               1000, 2000, 3000)
        if string(buf[:a]) != "1000 2000 3000" {
                t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a])
        }
 }
 
 func TestCallback(t *testing.T) {
-       h, e := syscall.LoadLibrary("user32.dll")
-       if e != 0 {
-               t.Fatal("LoadLibrary(USER32)")
-       }
-       pEnumWindows, e := syscall.GetProcAddress(h, "EnumWindows")
-       if e != 0 {
-               t.Fatal("GetProcAddress(USER32.EnumWindows)")
-       }
-       pIsWindow, e := syscall.GetProcAddress(h, "IsWindow")
-       if e != 0 {
-               t.Fatal("GetProcAddress(USER32.IsWindow)")
-       }
+       d := GetDLL(t, "user32.dll")
+       isWindows := d.Proc("IsWindow")
        counter := 0
        cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
                if lparam != 888 {
                        t.Error("lparam was not passed to callback")
                }
-               b, _, _ := syscall.Syscall(uintptr(pIsWindow), 1, uintptr(hwnd), 0, 0)
+               b, _, _ := isWindows.Call(uintptr(hwnd))
                if b == 0 {
                        t.Error("USER32.IsWindow returns FALSE")
                }
                counter++
                return 1 // continue enumeration
        })
-       a, _, _ := syscall.Syscall(uintptr(pEnumWindows), 2, cb, 888, 0)
+       a, _, _ := d.Proc("EnumWindows").Call(cb, 888)
        if a == 0 {
                t.Error("USER32.EnumWindows returns FALSE")
        }
index 21277c64bc622fcb65029383286cec0af00f99ad..2ad95d9585f17ea43e159045a21dea2d2d11c02f 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-extern void *runtime·LoadLibraryEx;
+extern void *runtime·LoadLibrary;
 extern void *runtime·GetProcAddress;
 
 // Call a Windows function with stdcall conventions,
index 68c3a4dfab08e08d241c2c050ece082673dda164..8997fd3a44dad3bc06ffbbdb17495e337ab888d7 100644 (file)
@@ -7,18 +7,21 @@ package syscall
 #include "os.h"
 #include "cgocall.h"
 
-func loadlibraryex(filename uintptr) (handle uintptr) {
-       uintptr args[3] = { filename };
+func loadlibrary(filename *uint16) (handle uintptr, err uintptr) {
        WinCall c;
 
-       c.fn = runtime·LoadLibraryEx;
-       c.n = 3;
-       c.args = &args[0];
+       c.fn = runtime·LoadLibrary;
+       c.n = 1;
+       c.args = &filename;
        runtime·cgocall(runtime·asmstdcall, &c);
        handle = c.r1;
+       if(handle == 0)
+               err = c.err;
+       else
+               err = 0;
 }
 
-func getprocaddress(handle uintptr, procname uintptr) (proc uintptr) {
+func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err uintptr) {
        WinCall c;
 
        USED(procname);
@@ -27,6 +30,10 @@ func getprocaddress(handle uintptr, procname uintptr) (proc uintptr) {
        c.args = &handle;
        runtime·cgocall(runtime·asmstdcall, &c);
        proc = c.r1;
+       if(proc == 0)
+               err = c.err;
+       else
+               err = 0;
 }
 
 func NewCallback(fn Eface) (code uintptr) {
index ff075b77dcec7206462828c240db5a81792e6671..946dea38aff5599727ac5665c717962027185909 100644 (file)
@@ -19,7 +19,7 @@
 #pragma dynimport runtime·GetStdHandle GetStdHandle "kernel32.dll"
 #pragma dynimport runtime·GetSystemInfo GetSystemInfo "kernel32.dll"
 #pragma dynimport runtime·GetThreadContext GetThreadContext "kernel32.dll"
-#pragma dynimport runtime·LoadLibraryEx LoadLibraryExA "kernel32.dll"
+#pragma dynimport runtime·LoadLibrary LoadLibraryW "kernel32.dll"
 #pragma dynimport runtime·QueryPerformanceCounter QueryPerformanceCounter "kernel32.dll"
 #pragma dynimport runtime·QueryPerformanceFrequency QueryPerformanceFrequency "kernel32.dll"
 #pragma dynimport runtime·ResumeThread ResumeThread "kernel32.dll"
@@ -45,7 +45,7 @@ extern void *runtime·GetProcAddress;
 extern void *runtime·GetStdHandle;
 extern void *runtime·GetSystemInfo;
 extern void *runtime·GetThreadContext;
-extern void *runtime·LoadLibraryEx;
+extern void *runtime·LoadLibrary;
 extern void *runtime·QueryPerformanceCounter;
 extern void *runtime·QueryPerformanceFrequency;
 extern void *runtime·ResumeThread;
index d4dbc3891ec83d3dc4e83dde60e3472662cd8024..2c4579ff082adb77bf5abb1d58680a9754186554 100644 (file)
@@ -53,6 +53,7 @@ GOFILES_openbsd=\
 
 GOFILES_windows=\
        exec_windows.go\
+       dll_windows.go\
        zerrors_windows.go\
        ztypes_windows.go\
 
diff --git a/src/pkg/syscall/dll_windows.go b/src/pkg/syscall/dll_windows.go
new file mode 100644 (file)
index 0000000..f305bba
--- /dev/null
@@ -0,0 +1,252 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall
+
+import (
+       "sync"
+)
+
+// An Error can represent any printable error condition.
+type Error interface {
+       String() string
+}
+
+// Errno is the Windows error number.
+type Errno uint64
+
+func (e Errno) String() string { return Errstr(int(e)) }
+
+// DLLError describes reasons for DLL load failures.
+type DLLError struct {
+       Errno   Errno
+       ObjName string
+       Msg     string
+}
+
+func (e *DLLError) String() string { return e.Msg }
+
+// Implemented in ../runtime/windows/syscall.goc.
+func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr)
+func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
+func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr)
+func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr)
+func loadlibrary(filename *uint16) (handle, err uintptr)
+func getprocaddress(handle uintptr, procname *uint8) (proc, err uintptr)
+
+// A DLL implements access to a single DLL.
+type DLL struct {
+       Name   string
+       Handle Handle
+}
+
+// LoadDLL loads DLL file into memory.
+func LoadDLL(name string) (dll *DLL, err Error) {
+       h, e := loadlibrary(StringToUTF16Ptr(name))
+       if e != 0 {
+               return nil, &DLLError{
+                       Errno:   Errno(e),
+                       ObjName: name,
+                       Msg:     "Failed to load " + name + ": " + Errstr(int(e)),
+               }
+       }
+       d := &DLL{
+               Name:   name,
+               Handle: Handle(h),
+       }
+       return d, nil
+}
+
+// MustLoadDLL is like LoadDLL but panics if load operation failes.
+func MustLoadDLL(name string) *DLL {
+       d, e := LoadDLL(name)
+       if e != nil {
+               panic(e)
+       }
+       return d
+}
+
+// FindProc searches DLL d for procedure named name and returns *Proc
+// if found. It returns an error if search fails.
+func (d *DLL) FindProc(name string) (proc *Proc, err Error) {
+       a, e := getprocaddress(uintptr(d.Handle), StringBytePtr(name))
+       if e != 0 {
+               return nil, &DLLError{
+                       Errno:   Errno(e),
+                       ObjName: name,
+                       Msg:     "Failed to find " + name + " procedure in " + d.Name + ": " + Errstr(int(e)),
+               }
+       }
+       p := &Proc{
+               Dll:  d,
+               Name: name,
+               addr: a,
+       }
+       return p, nil
+}
+
+// MustFindProc is like FindProc but panics if search fails.
+func (d *DLL) MustFindProc(name string) *Proc {
+       p, e := d.FindProc(name)
+       if e != nil {
+               panic(e)
+       }
+       return p
+}
+
+// Release unloads DLL d from memory.
+func (d *DLL) Release() (err Errno) {
+       return Errno(FreeLibrary(d.Handle))
+}
+
+// A Proc implements access to a procedure inside a DLL.
+type Proc struct {
+       Dll  *DLL
+       Name string
+       addr uintptr
+}
+
+// Addr returns the address of the procedure represented by p.
+// The return value can be passed to Syscall to run the procedure.
+func (p *Proc) Addr() uintptr {
+       return p.addr
+}
+
+// Call executes procedure p with arguments a.
+func (p *Proc) Call(a ...uintptr) (r1, r2, err uintptr) {
+       switch len(a) {
+       case 0:
+               return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
+       case 1:
+               return Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
+       case 2:
+               return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
+       case 3:
+               return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
+       case 4:
+               return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
+       case 5:
+               return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
+       case 6:
+               return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
+       case 7:
+               return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
+       case 8:
+               return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
+       case 9:
+               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])
+       case 10:
+               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)
+       case 11:
+               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)
+       case 12:
+               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])
+       default:
+               panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
+       }
+       return
+}
+
+// A LazyDLL implements access to a single DLL.
+// It will delay the load of the DLL until the first
+// call to its Handle method or to one of its
+// LazyProc's Addr method.
+type LazyDLL struct {
+       mu   sync.Mutex
+       dll  *DLL // non nil once DLL is loaded
+       Name string
+}
+
+// Load loads DLL file d.Name into memory. It returns an error if fails.
+// Load will not try to load DLL, if it is already loaded into memory.
+func (d *LazyDLL) Load() Error {
+       if d.dll == nil {
+               d.mu.Lock()
+               defer d.mu.Unlock()
+               if d.dll == nil {
+                       dll, e := LoadDLL(d.Name)
+                       if e != nil {
+                               return e
+                       }
+                       d.dll = dll
+               }
+       }
+       return nil
+}
+
+// mustLoad is like Load but panics if search fails.
+func (d *LazyDLL) mustLoad() {
+       e := d.Load()
+       if e != nil {
+               panic(e)
+       }
+}
+
+// Handle returns d's module handle.
+func (d *LazyDLL) Handle() uintptr {
+       d.mustLoad()
+       return uintptr(d.dll.Handle)
+}
+
+// NewProc returns a LazyProc for accessing the named procedure in the DLL d.
+func (d *LazyDLL) NewProc(name string) *LazyProc {
+       return &LazyProc{l: d, Name: name}
+}
+
+// NewLazyDLL creates new LazyDLL associated with DLL file.
+func NewLazyDLL(name string) *LazyDLL {
+       return &LazyDLL{Name: name}
+}
+
+// A LazyProc implements access to a procedure inside a LazyDLL.
+// It delays the lookup until the Addr method is called.
+type LazyProc struct {
+       mu   sync.Mutex
+       Name string
+       l    *LazyDLL
+       proc *Proc
+}
+
+// Find searches DLL for procedure named p.Name. It returns
+// an error if search fails. Find will not search procedure,
+// if it is already found and loaded into memory.
+func (p *LazyProc) Find() Error {
+       if p.proc == nil {
+               p.mu.Lock()
+               defer p.mu.Unlock()
+               if p.proc == nil {
+                       e := p.l.Load()
+                       if e != nil {
+                               return e
+                       }
+                       proc, e := p.l.dll.FindProc(p.Name)
+                       if e != nil {
+                               return e
+                       }
+                       p.proc = proc
+               }
+       }
+       return nil
+}
+
+// mustFind is like Find but panics if search fails.
+func (p *LazyProc) mustFind() {
+       e := p.Find()
+       if e != nil {
+               panic(e)
+       }
+}
+
+// Addr returns the address of the procedure represented by p.
+// The return value can be passed to Syscall to run the procedure.
+func (p *LazyProc) Addr() uintptr {
+       p.mustFind()
+       return p.proc.Addr()
+}
+
+// Call executes procedure p with arguments a.
+func (p *LazyProc) Call(a ...uintptr) (r1, r2, err uintptr) {
+       p.mustFind()
+       return p.proc.Call(a...)
+}
index 7e23381edd9a829feb27fd42b2ab4db22d6eae5f..77634bf5351bd831e639df6beb30a47fe85f8980 100644 (file)
@@ -7,7 +7,6 @@
 package syscall
 
 import (
-       "sync"
        "unsafe"
        "utf16"
 )
@@ -75,76 +74,6 @@ func UTF16ToString(s []uint16) string {
 // the UTF-8 string s, with a terminating NUL added.
 func StringToUTF16Ptr(s string) *uint16 { return &StringToUTF16(s)[0] }
 
-// dll helpers
-
-// Implemented in ../runtime/windows/syscall.goc
-func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr)
-func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
-func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr)
-func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr)
-func loadlibraryex(filename uintptr) (handle uintptr)
-func getprocaddress(handle uintptr, procname uintptr) (proc uintptr)
-
-// A LazyDLL implements access to a single DLL.
-// It will delay the load of the DLL until the first
-// call to its Handle method or to one of its
-// LazyProc's Addr method.
-type LazyDLL struct {
-       mu   sync.Mutex
-       Name string
-       h    uintptr // module handle once dll is loaded
-}
-
-// Handle returns d's module handle.
-func (d *LazyDLL) Handle() uintptr {
-       if d.h == 0 {
-               d.mu.Lock()
-               defer d.mu.Unlock()
-               if d.h == 0 {
-                       d.h = loadlibraryex(uintptr(unsafe.Pointer(StringBytePtr(d.Name))))
-                       if d.h == 0 {
-                               panic("syscall: could not LoadLibraryEx " + d.Name)
-                       }
-               }
-       }
-       return d.h
-}
-
-// NewProc returns a LazyProc for accessing the named procedure in the DLL d.
-func (d *LazyDLL) NewProc(name string) *LazyProc {
-       return &LazyProc{dll: d, Name: name}
-}
-
-// NewLazyDLL creates new LazyDLL associated with dll file.
-func NewLazyDLL(name string) *LazyDLL {
-       return &LazyDLL{Name: name}
-}
-
-// A LazyProc implements access to a procedure inside a LazyDLL.
-// It delays the lookup until the Addr method is called.
-type LazyProc struct {
-       mu   sync.Mutex
-       Name string
-       dll  *LazyDLL
-       addr uintptr
-}
-
-// Addr returns the address of the procedure represented by s.
-// The return value can be passed to Syscall to run the procedure.
-func (s *LazyProc) Addr() uintptr {
-       if s.addr == 0 {
-               s.mu.Lock()
-               defer s.mu.Unlock()
-               if s.addr == 0 {
-                       s.addr = getprocaddress(s.dll.Handle(), uintptr(unsafe.Pointer(StringBytePtr(s.Name))))
-                       if s.addr == 0 {
-                               panic("syscall: could not GetProcAddress for " + s.Name)
-                       }
-               }
-       }
-       return s.addr
-}
-
 func Getpagesize() int { return 4096 }
 
 // Converts a Go function to a function pointer conforming