I need to read a specific binary data format (https://www.usna.edu/Users/oceano/pguth/md_help/html/BT_file_format.htm). Go seems to be able to do so quite nicely:
// ...
f, _ := os.Open(filename)
var data struct {
Indicator [10]byte
Columns [4]byte
Rows [4]byte
DataSize [4]byte
UTMFlag [2]byte
UTMZone [2]byte
LeftExtend [4]byte
RightExtend [4]byte
BottomExtend [4]byte
TopExtend [4]byte
FloatingPointFlag [2]byte
}
_ = binary.Read(f, binary.LittleEndian, &data)
// ...
That seems to work since spew.dump(data.Indicator) for example return the correct data. What I do not understand is how to cast fixed slices like [2]byte to an integer I can actually work with. Any suggestions?
Declare the fields with fixed size numeric types:
var data struct {
Indicator [10]byte
Columns uint32
Rows uint32
DataSize uint32
UTMFlag uint16
UTMZone uint16
LeftExtend uint32
RightExtend uint32
BottomExtend uint32
TopExtend uint32
FloatingPointFlag uint16
}
I used unsigned integers here, but it's also OK to use signed integers. Use the type that matches the data.
https://play.golang.org/p/95yqMAYsWVR
Related
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)
I'm new to golang and trying to figure out the correct way of casting a block of bytes to the correct struct. All structs start with two bytes that dictate the layout of the remaining bytes. In C I would point to the beginning of the block of memory and cast it as a simple struct that only contained those two bytes (X below) but here I get an invalid type assertion. I'm probably way off base here any help you be appreciated.
package main
import (
"fmt"
)
type A struct {
tag byte
ver byte
data1 int
data2 int
data3 int
}
type B struct {
tag byte
ver byte
data1 float32
}
type X struct {
tag byte
ver byte
}
func main() {
var a A
a.tag = 1
a.ver = 1
x := a.(X)
fmt.Printf("%d,%d", x.tag, x.ver)
}
Edit
In short I just want to create a custom method on type Foo that is the only reason why I want to perform the cast. If the solutions are very complex I will just create functions instead of methods I guess. I was just curios.
playground link
Here's my solution. It involves a few different tips:
Embed the shared struct in the individual structs.
Use encoding/binary package to load bytes into structs.
Fill header struct with first two bytes, then make a decision on which subtype to make and fill.
Always use fixed length int types for this kind of thing.
Your field names must be UpperCase to be fillable from encoding/binary
This is a pretty brittle way to manage marshalling.unmarshalling of data, but I'm sure you know that.
Here's my solution:
package main
import (
"bytes"
"encoding/binary"
"fmt"
"log"
)
type A struct {
X
Data1 int32
Data2 int32
Data3 int32
}
type B struct {
X
Data1 int32
}
type X struct {
Tag byte
Ver byte
}
func main() {
var err error
data := []byte{1, 1, 0, 0, 0, 42}
hdr := X{}
err = binary.Read(bytes.NewReader(data[:2]), binary.BigEndian, &hdr)
if err != nil {
log.Fatal(err)
}
fmt.Println(hdr.Tag, hdr.Ver)
if hdr.Tag == 1 {
b := B{}
err = binary.Read(bytes.NewReader(data), binary.BigEndian, &b)
if err != nil {
log.Fatal(err)
}
fmt.Println(b.Data1)
}
}
playground link
Go generally tries to discourage C-like memory fiddling as it leads to memory leaks, incorrect behavior, and security vulnerabilities unless extraordinary caution and testing are applied. That doesn't mean it's impossible though; in fact, the aptly-named unsafe.Pointer is exposed for exactly this purpose. Use it with caution.
I have the following byte array:
buf := make([]byte, 1)
var value int8
value = 45
buf[0] = value // cannot use type int8 as type []byte in assignment
And when I want to put a char value into the byte array I get the error that I cannot use type int8 as type []byte in assignment. What's wrong? How do I do this?
The issue you're having their is that although int8 and byte are roughly equivalent, they're not the same type. Go is a little stricter about this than, say, PHP (which isn't strict about much). You can get around this by explicitly casting the value to byte:
buf := make([]byte, 1)
var value int8
value = 45
buf[0] = byte(value) // cast int8 to byte
Try this:
buf := make([]byte, 1)
var value int8
value = 45
buf[0] = byte(value)
UPDATE: Took out the code converting negative numbers to positive ones. It appears that byte(...) already does this conversion in current versions of Go.
I've got a C function that fills a C struct:
typedef struct {
char name[8];
}
I need to copy data into Go lang struct that has the same content:
type sData struct {
Name [8]byte
}
The structure has parameters of multiple sizes: 4, 12, 32 so it would be nice to have a function that deals with more than just one size.
thanks
To make this a little more generic, you can decompose the C char array to a *C.char, then use unsafe.Pointer to cast it back to an array.
func charToBytes(dest []byte, src *C.char) {
n := len(dest)
copy(dest, (*(*[1024]byte)(unsafe.Pointer(src)))[:n:n])
}
Or maybe a little easier
func charToBytes(src *C.char, sz int) []byte {
dest := make([]byte, sz)
copy(dest, (*(*[1024]byte)(unsafe.Pointer(src)))[:sz:sz])
return dest
}