There is a very nice description about loading a shared library and calling a function with the syscall package on Windows (https://github.com/golang/go/wiki/WindowsDLLs). However, the functions LoadLibrary and GetProcAddress that are used in this description are not available in the syscall package on Linux. I could not find documentation about how to do this on Linux (or Mac OS).
Thanks for help
Linux syscalls are used directly without loading a library, exactly how depends on which system call you would like to perform.
I will use the Linux syscall getpid() as an example, which returns the process ID of the calling process (our process, in this case).
package main
import (
"fmt"
"syscall"
)
func main() {
pid, _, _ := syscall.Syscall(syscall.SYS_GETPID, 0, 0, 0)
fmt.Println("process id: ", pid)
}
I capture the result of the syscall in pid, this particular call returns no errors so I use blank identifiers for the rest of the returns. Syscall returns two uintptr and 1 error.
As you can see I can also just pass in 0 for the rest of the function arguments, as I don't need to pass arguments to this syscall.
The function signature is: func Syscall(trap uintptr, nargs uintptr, a1 uintptr, a2 uintptr, a3 uintptr) (r1 uintptr, r2 uintptr, err Errno).
For more information refer to https://golang.org/pkg/syscall/
Related
As I want to access some lower-level API to do I/O operation using CreateFile function
syscall.CreateFile( name *uint16…)
While doing so I face a problem that the name parameter is of *uint16 but it should be an array ([]uint16) so that it can handle the string in the UTF-16 format. As we can see in the example provided by Microsoft -> link where TEXT macro convert the string into wchar_t array or we can say []uint16.
Thanks in advance and sorry if I said anything wrong as I’m just a toddler in this field.
(Solution 1)
func UTF16PtrFromString(s string) (*uint16, error)
Built-in encoder which returns a pointer to the UTF-16 encoding
(Solution 2)
As previously I was unaware of Solution 1 so I wrote my own function which does the exact work so you can ignore this solution
For passing the file name (string) to the sys package we have to first convert the string to an array of UTF-16 and pass the pointer of the first element
var srcUTf16 [ ]uint16 = utf16.Encode([ ]rune(src+ "\x00"))
syscall.CreateFile(&srcUTf16[0],..... )
Edit:-Adding solution
Edit:- Adding correct solution and adding Terminating NUL in solution 2.
I don't really care for Windows API function signatures Go has made, and I have written about this. So if you want, you can write your own. Make a file like this:
//go:generate mkwinsyscall -output zfile.go file.go
//sys createFile(name string, access int, mode int, sec *windows.SecurityAttributes, disp int, flag int, template int) (hand int, err error) = kernel32.CreateFileW
package main
import "golang.org/x/sys/windows"
func main() {
n, e := createFile(
"file.txt",
windows.GENERIC_READ,
0,
nil,
windows.CREATE_NEW,
windows.FILE_ATTRIBUTE_NORMAL,
0,
)
if e != nil {
panic(e)
}
println(n)
}
Then build:
go mod init file
go generate
go mod tidy
go build
I know the result works, because it returns a valid handle the first time, and invalid handle the second time (also because a file is created of course):
PS C:\> .\file.exe
336
PS C:\> .\file.exe
-1
If you want, you can edit the signature line I put above, to suit your needs.
I have a RPC type of setup where one binary (binaryA) is requesting work to be done from another binary (binaryB). They're both compiled the same way and are on the same system. I can't shell out to binaryA because the task involved involves a lot of data which would take too long to serialize and I can't use a golang plugin because the I want to be able to call functions without needing to create a special binary.
This is roughly the setup I'm trying to achieve:
binaryA compiled with go build mainA.go. Somewhere in that binary, this file is compiled:
package demo
import "fmt"
func TestFn(){
fmt.Println("binaryA")
func(){ someFn() }()
}
I want to be able to call TestFn() and that anonymous function with binaryB.
Here's what I have so far.
import (
"debug/macho"
"fmt"
"os"
"reflect"
"unsafe"
)
func main() {
filename := "binaryA"
f, _ := os.Open(filename)
defer f.Close()
mf, _ := macho.NewFile(f)
sym2Addr := make(map[string]uintptr)
for _, sym := range mf.Symtab.Syms {
if int(sym.Sect-1) >= len(mf.Sections) ||
mf.Sections[sym.Sect-1].Seg != "__TEXT" { continue }
value := uintptr(sym.Value)
sym2Addr[sym.Name] = value
}
funcType := reflect.TypeOf(func() {})
if testFnPtr, ok := sym2Addr["main.TestFn"]; ok {
TestFn := reflect.New(funcType).Elem()
p := new(uintptr)
*p = testFnPtr
*(*unsafe.Pointer)(unsafe.Pointer(TestFn.Addr().Pointer())) = unsafe.Pointer(p)
TestFn.Call([]reflect.Value{})
}
}
Using the code above I'm able to find the symbol main.TestFn but it ultimately fails with:
unexpected fault address 0x210d0fb1
fatal error: fault
[signal SIGBUS: bus error code=0x2 addr=0x210d0fb1 pc=0x210d0fb1]
goroutine 1 [running]:
runtime.throw(...)
.asdf/installs/golang/1.15/go/src/runtime/panic.go:1116
runtime.sigpanic()
.asdf/installs/golang/1.15/go/src/runtime/signal_unix.go:717
runtime.call32(...)
.asdf/installs/golang/1.15/go/src/runtime/asm_amd64.s:540
reflect.Value.call(...)
.asdf/installs/golang/1.15/go/src/reflect/value.go:475
reflect.Value.Call(...)
.asdf/installs/golang/1.15/go/src/reflect/value.go:336
main.main()
plugin_demo.go:106
runtime.main()
.asdf/installs/golang/1.15/go/src/runtime/proc.go:204
runtime.goexit()
.asdf/installs/golang/1.15/go/src/runtime/asm_amd64.s:1374
exit status 2
EDIT:
I can recompile binaryA with any build flags I want.
Is there a way to execute functions in a compiled golang binary from another binary without needing any special setup?
No, that simply is not how compiled (to machine code) programs work (in any language).
I have this piece of code which runs without returning err but simply doesn't do its job because it doesn't return the expected value.
The idea is to use SHGetSpecialFolderPath in order to retrieve the path to the Windows directory (C:\Windows for example). This api call has the following signature:
BOOL SHGetSpecialFolderPath(
HWND hwndOwner,
_Out_ LPTSTR lpszPath,
_In_ int csidl,
_In_ BOOL fCreate );
I know it is deprecated, but still available even on current Windows versions. I have to use this API because I need to support Windows versions older than Windows 7 (I know that these are old or even end of life)
This is the piece of code:
target := "XXX...XXX" // hard coded string with more than 600 characters
buffer, err := syscall.UTF16PtrFromString(target)
if err != nil {
fmt.Println("conversion of string:", err)
}
result := win.SHGetSpecialFolderPath(0, buffer, win.CSIDL_WINDOWS, false)
if err != nil {
fmt.Println("result of get folder:", err)
}
fmt.Println("folder retrieved ok: ", result)
fmt.Println("folder: ", target)
}
None of the err is set, the API call returns true but the string is unchanged:
folder retrieved ok: true
folder: XXX...XXX
The result is the same on Windows 10 x64 and on my testing VM running Windows XP SP3 (I know that XP is inherently unsafe)
I have seen examples how to use LPTRSTR with unsafe and uintptr here on SO and other sites but none of them compile on my version of golang (which is go version go1.10.1 windows/amd64, I compiled with GOARCH=386)
Approach the problem in a logical, systematic fashion.
Carefully read the Microsoft documentation for the function.
SHGetSpecialFolderPath function
Carefully read the lxn/win package documentation for the function.
package win
import "github.com/lxn/win"
func SHGetSpecialFolderPath
func SHGetSpecialFolderPath(hwndOwner HWND, lpszPath *uint16, csidl CSIDL, fCreate bool) bool
Now, using the documentation, implement the function call in Go. Go Unicode strings are UTF-8 encoded. Windows Unicode strings are UTF-16 encoded.
package main
import (
"fmt"
"syscall"
"github.com/lxn/win"
)
func main() {
buf := make([]uint16, win.MAX_PATH)
rv := win.SHGetSpecialFolderPath(win.HWND(0), &buf[0], win.CSIDL_WINDOWS, false)
fmt.Println(rv)
path := syscall.UTF16ToString(buf)
fmt.Println(path)
}
Output:
true
C:\Windows
I wanna make push subscription to Windows Event Log in Golang
How exactly should I pass a callback function?
EVT_SUBSCRIBE_CALLBACK is the pointer of function, like
typedef DWORD ( WINAPI *EVT_SUBSCRIBE_CALLBACK)(
EVT_SUBSCRIBE_NOTIFY_ACTION Action,
PVOID UserContext,
EVT_HANDLE Event
);
So, my variant looks like this:
func logCallback() syscall.Handle {
cb := func(_ uintptr, _ uintptr, _ uintptr) uint64 {
fmt.Printf("callback called %v", data)
return 0
}
ptr := syscall.NewCallback(cb)
return syscall.Handle(ptr) // type syscall.Handle uintptr
}
I get successfully subscribed handler with no errors, but it still doesn't work.
Any ideas why? Where should I look first?
When using syscall make sure the to include import "C" at the top of your file. Glad it helped you.
Is there an example or method of getting a Windows system's idle time using Go?
I've been looking at the documentation at the Golang site but I think I'm missing how to access (and use) the API to get system information including the idle time.
Go's website is hardcoded to show the documentation for the standard library packages on Linux. You will need to get godoc and run it yourself:
go get golang.org/x/tools/cmd/godoc
godoc --http=:6060
then open http://127.0.0.1:6060/ in your web browser.
Of note is package syscall, which provides facilities for accessing functions in DLLs, including UTF-16 helpers and callback generation functions.
Doing a quick recursive search of the Go tree says it doesn't have an API for GetLastInputInfo() in particular, so unless I'm missing something, you should be able to call that function from the DLL directly:
user32 := syscall.MustLoadDLL("user32.dll") // or NewLazyDLL() to defer loading
getLastInputInfo := user32.MustFindProc("GetLastInputInfo") // or NewProc() if you used NewLazyDLL()
// or you can handle the errors in the above if you want to provide some alternative
r1, _, err := getLastInputInfo.Call(uintptr(arg))
// err will always be non-nil; you need to check r1 (the return value)
if r1 == 0 { // in this case
panic("error getting last input info: " + err.Error())
}
Your case involves a structure. As far as I know, you can just recreate the structure flat (keeping fields in the same order), but you must convert any int fields in the original to int32, otherwise things will break on 64-bit Windows. Consult the Windows Data Types page on MSDN for the appropriate type equivalents. In your case, this would be
var lastInputInfo struct {
cbSize uint32
dwTime uint32
}
Because this (like so many structs in the Windows API) has a cbSize field that requires you to initialize it with the size of the struct, we must do so too:
lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
Now we just need to pass a pointer to that lastInputInfo variable to the function:
r1, _, err := getLastInputInfo.Call(
uintptr(unsafe.Pointer(&lastInputInfo)))
and just remember to import syscall and unsafe.
All args to DLL/LazyDLL.Call() are uintptr, as is the r1 return. The _ return is never used on Windows (it has to do with the ABI used).
Since I went over most of what you need to know to use the Windows API in Go that you can't gather from reading the syscall docs, I will also say (and this is irrelevant to the above question) that if a function has both ANSI and Unicode versions, you should use the Unicode versions (W suffix) and the UTF-16 conversion functions in package syscall for best results.
I think that's all the info you (or anyone, for that matter) will need to use the Windows API in Go programs.
Regarding for answer from andlabs. This is ready for use example:
import (
"time"
"unsafe"
"syscall"
"fmt"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
kernel32 = syscall.MustLoadDLL("kernel32.dll")
getLastInputInfo = user32.MustFindProc("GetLastInputInfo")
getTickCount = kernel32.MustFindProc("GetTickCount")
lastInputInfo struct {
cbSize uint32
dwTime uint32
}
)
func IdleTime() time.Duration {
lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
currentTickCount, _, _ := getTickCount.Call()
r1, _, err := getLastInputInfo.Call(uintptr(unsafe.Pointer(&lastInputInfo)))
if r1 == 0 {
panic("error getting last input info: " + err.Error())
}
return time.Duration((uint32(currentTickCount) - lastInputInfo.dwTime)) * time.Millisecond
}
func main() {
t := time.NewTicker(1 * time.Second)
for range t.C {
fmt.Println(IdleTime())
}
}
This is code print idle time every second. Try run and don't touch mouse/keyboard