Please, somebody, explain Go pointers magic
I try to call COM function
func (p *IServer) Authorize(user, pass string) error {
UserName := ole.SysAllocString(login.UserName)
defer ole.SysFreeString(UserName)
UserPsw := ole.SysAllocString(login.UserPsw)
defer ole.SysFreeString(UserPsw)
// HRESULT IServer::Authorize([in] BSTR UserName, [in] BSTR UserPsw, [out] VARIANT* SID, [out, retval] long* Result)
hr, _, _ := Call(p.VTable().Authorize,
uintptr(unsafe.Pointer(p)),
uintptr(unsafe.Pointer(UserName)),
uintptr(unsafe.Pointer(UserPsw)),
uintptr(unsafe.Pointer(sid)),
uintptr(unsafe.Pointer(&res)))
...
}
This code works well, but when I replace convertion to
UserName := syscall.StringToUTF16Ptr(login.UserName)
UserPsw := syscall.StringToUTF16Ptr(login.UserPsw)
It cause an ACCESS VIOLATION! Source from go-ole
func SysAllocString(v string) (ss *int16) {
pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
ss = (*int16)(unsafe.Pointer(pss))
return
}
What am I doing wrong?
Update: In C/C++ wchar* pointer works corretly both 386/amd64
void Authorize(IServer* p wchar* user, wchar* pass) {
p.Authorize(user, pass ....
}
SysAllocString returns BSTR type, the com type object.
typedef struct {
#ifdef _WIN64
DWORD pad;
#endif
DWORD size;
union {
char ptr[1];
WCHAR str[1];
DWORD dwptr[1];
} u; // take it as a starting point of the string
} bstr_t;
In other words, it's the same utf16 encoded string but with the prefix in the form of its size (length of the Unicode characters multiplied by the size of wchar_t (2-4 byte)). For the optimization reason, it also has padding.
Because of its floating size, it's better to use ole package rather than reinventing the wheel. If you want to implement it yourself, and wchar_t has the size of int16 (2 bytes), then you have to do the following:
(half-pseudocode, I didn't test it)
type BSTR *uint16
func SysAllocString(str string) (result BSTR) {
// DWORD == int32 == rune
const padf = "\x00" // only for 64 bit system
const sizef = "\x00"
// int32 == 4 byte
// int16 == 2 byte
const wordSize = unsafe.Sizeof(int16(0))
utf16 := utf16.Encode([]rune(padf + sizef + str))
/* pad is on index 0 and 1 */
size := &utf16[2 /* 0 for 32 bit system */]
// set "size" field as unicode charachers length multypled by size of wchar_t
*(*rune)(unsafe.Pointer(size)) = rune((len(utf16)-2) * int(wordSize))
result = BSTR(&utf16[0])
return
}
// ...
bstr := SysAllocString(login.UserName)
uintptr(unsafe.Pointer(bstr))
Related
show code:
package main
import (
"fmt"
"unsafe"
)
// copy form src/runtime/type.go
type _type struct {
size uintptr
ptrdata uintptr // size of memory prefix holding all pointers
hash uint32
tflag tflag
align uint8
fieldAlign uint8
kind uint8
equal func(unsafe.Pointer, unsafe.Pointer) bool
gcdata *byte
str nameOff
ptrToThis typeOff
}
type tflag uint8
type nameOff int32
type typeOff int32
// copy form src/runtime/runtime2.go
type eface struct {
_type *maptype
data unsafe.Pointer
}
// copy form src/runtime/type.go
type maptype struct {
typ _type
key *_type
elem *_type
bucket *_type // internal type representing a hash bucket
// function for hashing keys (ptr to key, seed) -> hash
hasher func(unsafe.Pointer, uintptr) uintptr
keysize uint8 // size of key slot
elemsize uint8 // size of elem slot
bucketsize uint16 // size of bucket
flags uint32
}
func main() {
var t interface{} = map[int]int{1: 1}
p := (*eface)(unsafe.Pointer(&t))
fmt.Println(p._type.typ.kind) // 53
}
print 53,
but you can find in src/runtime/typekind.go
const (
kindBool = 1 + iota
kindInt
kindInt8
kindInt16
kindInt32
kindInt64
kindUint
kindUint8
kindUint16
kindUint32
kindUint64
kindUintptr
kindFloat32
kindFloat64
kindComplex64
kindComplex128
kindArray
kindChan
kindFunc
kindInterface
kindMap // 21
kindPtr
kindSlice
kindString
kindStruct
kindUnsafePointer
kindDirectIface = 1 << 5
kindGCProg = 1 << 6
kindMask = (1 << 5) - 1
)
the map type is const 21. Similarly, chan is 50 instead of kindChan(18). Why?
If you look at the src/runtime/typekind.go, there is a function to check if the value is stored directly in an interface value, which applies to your case, as you are creating t of interface{} but storing a map type to it.
// isDirectIface reports whether t is stored directly in an interface value.
func isDirectIface(t *_type) bool {
return t.kind&kindDirectIface != 0
}
When using that on your value of p as isDirectIface(&p._type.typ) it returns true, because of the underlying bit value corresponding to interface type is set (kindDirectIface = 1 << 5, 32)
So in effect, the value 53 represents (decimal 21+32) a map type 21 (kindMap) stored as an interface type 32 (kindDirectIface)
I'm trying to get display information through Win32 APIs. So far I've managed EnumDisplayDevicesA just fine, but EnumDisplaySettingsA is giving me trouble.
No matter how I set the first two variables, the function returns zero (indicating failure), with no extra information on why it's failing.
Here's a cut down version of my code with just the function in question;
package main
import (
"fmt"
"syscall"
"unsafe"
)
var (
dll = syscall.MustLoadDLL("user32.dll")
enumDisplaySettings = dll.MustFindProc("EnumDisplaySettingsA")
)
type devMode struct {
dmDeviceName [32]uint16
dmSpecVersion uint16
dmDriverVersion uint16
dmSize uint16
dmDriverExtra uint16
dmFields uint32
dmOrientation int16
dmPaperSize int16
dmPaperLength int16
dmPaperWidth int16
dmScale int16
dmCopies int16
dmDefaultSource int16
dmPrintQuality int16
dmColor int16
dmDuplex int16
dmYResolution int16
dmTTOption int16
dmCollate int16
dmFormName [32]uint16
dmLogPixels uint16
dmBitsPerPel uint32
dmPelsWidth uint32
dmPelsHeight uint32
dmDisplayFlags uint32
dmDisplayFrequency uint32
dmICMMethod uint32
dmICMIntent uint32
dmMediaType uint32
dmDitherType uint32
dmReserved1 uint32
dmReserved2 uint32
dmPanningWidth uint32
dmPanningHeight uint32
}
func queryDisplaySettings() devMode {
out := devMode{}
out.dmSize = uint16(unsafe.Sizeof(out))
outptr := uintptr(unsafe.Pointer(&out))
namePtr := uintptr(unsafe.Pointer(nil))
var iModeNum uint32 = 4294967295
enumCurrentSettings := uintptr(unsafe.Pointer(&iModeNum))
ret, _, _ := enumDisplaySettings.Call(namePtr, enumCurrentSettings, outptr)
if ret == 0 {
fmt.Printf("Failed EnumDisplaySettings")
}
return out
}
func main() {
settings := queryDisplaySettings()
fmt.Printf("\n%v\n", settings.dmPelsWidth)
fmt.Printf("%v\n", settings.dmPelsHeight)
fmt.Printf("%v\n\n", settings.dmDisplayFrequency)
}
My Sources:
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaysettingsw
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-devmodea
https://github.com/JamesHovious/w32/blob/74b38b9b07b520e0f84a5eec5daada6c7b6a2471/typedef.go#L363
https://docs.rs/winapi/0.2.0/i686-pc-windows-msvc/winapi/constant.ENUM_CURRENT_SETTINGS.html
There are multiple issues with the code here.
First off your type definition of devMode is for DEVMODEW but you are calling EnumDisplaySettingsA. However you shouldn't be calling that in the first place (its the ANSI version), so use EnumDisplaySettingsW instead (UNICODE).
Next, the second parameter for EnumDisplaySettingsA/EnumDisplaySettingsW is a DWORD (uint32), however instead of passing the value, you are passing the address to it.
So replace
var iModeNum uint32 = 4294967295
enumCurrentSettings := uintptr(unsafe.Pointer(&iModeNum))
With just
iModeNum := uintptr(4294967295)
And it should all just work.
I'm using syscall.Syscall(...) to call a C method in a dll.
This is the C method signature:
SENSEI_API HSENSEI SENSEI_open(const char* sensigrafo, const char* options, SENSEI_ERR* se);
This is the SENSEI_ERR struct:
typedef struct
{
int code;
char* error_string;
} SENSEI_ERR;
In my GO program I declared a struct:
type senseiErr struct {
code int
error_string *byte
}
And tried to call the method:
var nargs uintptr = 3
var err senseiErr
ret, _, callErr := syscall.Syscall(uintptr(senseiOpen),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("en"))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(""))),
uintptr(unsafe.Pointer(&err)),
)
As you may have guessed, the SENSEI_open method fill the SENSEI_ERR argument with the code and the text of the error.
Now I need to read the content of that error.
err.code actually has the correct value.
About err.error_string I don't know. I'm new to GO and i have some questions:
Since the C struct has the field char* error_string, is error_string *byte in my GO struct correct?
Should I use []byte or something else?
How do I read the content of the error_string field?
fmt.Println(err.error_string) prints the memory address
fmt.Println(*err.error_string) prints always "101"
1) I doubt that cost char* meant to be UTF16 encoded. So all what you need is just getting raw data:
sensigrafo := "en\000" // \000 = 0 = null termination, \0 does not valid
options := "\000"
...
uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&sensigrafo))
uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&options))
// *(*unsafe.Pointer) are accessing the first field of string header:
type string struct {
data *byte
len int
}
// same with slices
// but for them there's less ugly way:
sensigrafo := []byte("en\000")
options := []byte("\000")
uintptr(unsafe.Pointer(&sensigrafo[0]))
uintptr(unsafe.Pointer(&options[0]))
2) C's int and Golang's int might have different sizeof, so this requires cgo declaration (C.int) or manual matching with random selection (try also int32, int64 if you don't want to use cgo)
type senseiErr struct {
code C.int /* Golang's int32/int64 */
error_string *byte // pointer types are same as C's void* or Golang's unsafe.Pointer
}
Wrong offset might cause error_string be empty or point to random addr.
3) To read content you have to use same methods as C does (read data until null terminated byte, considering that *byte points to first element of string), but I propose to use already implemented runtime functions:
//go:linkname gostringn runtime.gostringn
func gostringn(p *byte, l int) string
//go:linkname findnull runtime.findnull
//go:nosplit
func findnull(s *byte) int
...
error_string := gostringn(err.error_string, findnull(err.error_string))
// or cgo one:
type senseiErr struct {
code C.int
error_string *C.char
}
...
error_string := C.GoString(err.error_string)
Tries to call GetVolumeInformation function from golang. Want to get volume name.
Use spec's of api:
BOOL WINAPI GetVolumeInformation(
_In_opt_ LPCTSTR lpRootPathName,
_Out_opt_ LPTSTR lpVolumeNameBuffer,
_In_ DWORD nVolumeNameSize,
_Out_opt_ LPDWORD lpVolumeSerialNumber,
_Out_opt_ LPDWORD lpMaximumComponentLength,
_Out_opt_ LPDWORD lpFileSystemFlags,
_Out_opt_ LPTSTR lpFileSystemNameBuffer,
_In_ DWORD nFileSystemNameSize
);
Use code:
// test
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
var lpRootPathName = "C:\\"
var lpVolumeNameBuffer string
var nVolumeNameSize uint64
var lpVolumeSerialNumber uint64
var lpMaximumComponentLength uint64
var lpFileSystemFlags uint64
var lpFileSystemNameBuffer string
var nFileSystemNameSize uint32
kernel32, _ := syscall.LoadLibrary("kernel32.dll")
getVolume, _ := syscall.GetProcAddress(kernel32, "GetVolumeInformationW")
var nargs uintptr = 8
ret, _, callErr := syscall.Syscall9(uintptr(getVolume),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpRootPathName))),
uintptr(unsafe.Pointer(&lpVolumeNameBuffer)),
uintptr(unsafe.Pointer(&nVolumeNameSize)),
uintptr(unsafe.Pointer(&lpVolumeSerialNumber)),
uintptr(unsafe.Pointer(&lpMaximumComponentLength)),
uintptr(unsafe.Pointer(&lpFileSystemFlags)),
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer)),
uintptr(unsafe.Pointer(&nFileSystemNameSize)),
0)
fmt.Println(ret, callErr, lpVolumeNameBuffer)
}
... and finally have error :(
unexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x456b11]
Don't understand and google cant'd help with calling winapi functions and returng string as result.
Thank's.
Package unsafe
Package unsafe contains operations that step around the type safety of
Go programs.
type Pointer
type Pointer *ArbitraryType
Pointer represents a pointer to an arbitrary type. There are four
special operations available for type Pointer that are not available
for other types.
1) A pointer value of any type can be converted to a Pointer.
2) A Pointer can be converted to a pointer value of any type.
3) A uintptr can be converted to a Pointer.
4) A Pointer can be converted to a uintptr.
Pointer therefore allows a program to defeat the type system and read
and write arbitrary memory. It should be used with extreme care.
You failed to heed the warning that unsafe.Pointer "should be used with extreme care."
Try this:
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
var RootPathName = `C:\`
var VolumeNameBuffer = make([]uint16, syscall.MAX_PATH+1)
var nVolumeNameSize = uint32(len(VolumeNameBuffer))
var VolumeSerialNumber uint32
var MaximumComponentLength uint32
var FileSystemFlags uint32
var FileSystemNameBuffer = make([]uint16, 255)
var nFileSystemNameSize uint32 = syscall.MAX_PATH + 1
kernel32, _ := syscall.LoadLibrary("kernel32.dll")
getVolume, _ := syscall.GetProcAddress(kernel32, "GetVolumeInformationW")
var nargs uintptr = 8
ret, _, callErr := syscall.Syscall9(uintptr(getVolume),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(RootPathName))),
uintptr(unsafe.Pointer(&VolumeNameBuffer[0])),
uintptr(nVolumeNameSize),
uintptr(unsafe.Pointer(&VolumeSerialNumber)),
uintptr(unsafe.Pointer(&MaximumComponentLength)),
uintptr(unsafe.Pointer(&FileSystemFlags)),
uintptr(unsafe.Pointer(&FileSystemNameBuffer[0])),
uintptr(nFileSystemNameSize),
0)
fmt.Println(ret, callErr, syscall.UTF16ToString(VolumeNameBuffer))
}
I don't know the exact problem you are having but I think it is likely because you are not using the functions in https://github.com/golang/go/blob/master/src/syscall/syscall_windows.go related to converting from the format that comes out of the kernel to what Go needs. Look at other callers to UTF16ToString, like in env_windows.go, to see how they are used.
I want to make a wrapper for my C function, which takes pointer to C struct as parameter.
In my Go code, I tried two approaches to allocate the space for C struct:
bps := make([]_Ctype_T32_Breakpoint, max) (1)
C.MyFunc((*_Ctype_T32_Breakpoint)(unsafe.Pointer(&bps[0]), C.int(max))
bps := make([]C.struct_T32_Breakpoint, max) (2)
C.MyFunc((*C.struct_T32_Breakpoint)(unsafe.Pointer(&bps[0]), C.int(max))
For the (1) method, it works, but for (2) method, I got error message :
cannot use (*[0]byte)(unsafe.Pointer(&bps[0])) (type *[0]byte) as type *_Ctype_T32_Breakpoint in function argument
Why method (2) created type of *[0]byte instead of *C.struct_T32_Breakpoint, and it seems the cast to *C.struct_T32_Breakpoint doesn't work.
What's the difference of using method (1) and (2)?
Thanks.
Test Code
File: t32.h
#ifndef __T32_H__
#define __T32_H__
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int dword;
typedef struct t32_breakpoint {
dword address;
byte enabled;
dword type;
dword auxtype;
} T32_Breakpoint;
int T32_GetBreakpointList( int *, T32_Breakpoint*, int );
#endif /* __T32_H__ */
Fileļ¼ remote.c
#include "t32.h"
int T32_GetBreakpointList (int* numbps, T32_Breakpoint* bps, int max)
{
return 0;
}
File : t32.go
package t32
// #cgo linux,amd64 CFLAGS: -DT32HOST_LINUX_X64
// #cgo linux,386 CFLAGS: -DT32HOST_LINUX_X86
// #cgo windows,amd64 CFLAGS: -D_WIN64
// #cgo windows,386 CFLAGS: -D_WIN32
// #cgo windows CFLAGS: -fno-stack-check -fno-stack-protector -mno-stack-arg-probe
// #cgo windows LDFLAGS: -lkernel32 -luser32 -lwsock32
// #include "t32.h"
// #include <stdlib.h>
import "C"
import (
"errors"
"unsafe"
)
const (
_INVALID_U64 = 0xFFFFFFFFFFFFFFFF
_INVALID_S64 = -1
_INVALID_U32 = 0xFFFFFFFF
_INVALID_S32 = -1
_INVALID_U16 = 0xFFFF
_INVALID_S16 = -1
_INVALID_U8 = 0xFF
_INVALID_S8 = -1
)
type BreakPoint struct {
Address uint32
Enabled int8
Type uint32
Auxtype uint32
}
func GetBreakpointList(max int) (int32, []BreakPoint, error) {
var numbps int32
// bps := make([]_Ctype_T32_Breakpoint, max) // Method (1), can compile
// code, err := C.T32_GetBreakpointList((*C.int)(&numbps), (*_Ctype_T32_Breakpoint)(unsafe.Pointer(&bps[0])), C.int(max))
bps := make([]C.struct_T32_Breakpoint, max) // Method (2) can't compile
code, err := C.T32_GetBreakpointList((*C.int)(&numbps), (*C.struct_T32_Breakpoint)(unsafe.Pointer(&bps[0])), C.int(max))
if err != nil {
return _INVALID_S32, nil, err
} else if code != 0 {
return _INVALID_S32, nil, errors.New("T32_GetBreakpointList Error")
}
if numbps > 0 {
var gbps = make([]BreakPoint, numbps)
for i := 0; i < int(numbps); i++ {
gbps[i].Address = uint32(bps[i].address)
gbps[i].Auxtype = uint32(bps[i].auxtype)
gbps[i].Enabled = int8(bps[i].enabled)
gbps[i].Type = uint32(bps[i]._type)
}
return numbps, gbps, nil
}
return 0, nil, nil
}
There are two reasons that the second snippet doesn't compile.
The first is that the capitalization doesn't match. The C declaration declares struct t32_breakpoint, but the Go code refers to struct T32_Breakpoint. C is case-sensitive, but it permits creating a pointer to a struct that has not been defined. So that is what CGo thinks you are doing. This pointer can't be dereferenced, since the contents and size of the struct are not known. It's basically equivalent to a void pointer, but with stronger typing. Cgo treats it as a void pointer, translating it as *[0]byte, i.e. a pointer to a zero-sized object. But unlike C, Go doesn't allow passing a void pointer to just any function that takes a pointer. So it has a type mismatch.
The other issue is that if you want to pass a struct t32_breakpoint* instead of a T32_Breakpoint*, you will need to change your function declaration in C. The distinction between the two types is probably not significant in C, but it is in Go, since Go has stronger typing than C.