Reputation: 237
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:
char* error_string
, is error_string *byte
in my GO struct correct?
[]byte
or something else?error_string
field?
fmt.Println(err.error_string)
prints the memory addressfmt.Println(*err.error_string)
prints always "101"Upvotes: 1
Views: 515
Reputation: 492
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)
Upvotes: 2