The EnumPrinters
Win32 function takes and argument _Out_ LPBYTE pPrinterEnum
, a pointer to an allocated buffer. In C, it works like this:
DWORD cbNeeded, nPrinters;
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &cbNeeded, &nPrinters);
BYTE *pPrnInfo = malloc(cbNeeded);
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, pPrnInfo, cbNeeded, &cbNeeded, &nPrinters);
PRINTER_INFO_5 *pPrinterInfo = (PRINTER_INFO_5 *) pPrnInfo;
for (int i=0; i < nPrinters; i++) {
printf("pPrinterName: %s
", pPrinterInfo[i].pPrinterName);
}
How is the same accomplished in Go, using syscall
instead of cgo? So far, this much compiles, but I don't know how to cast the resulting byte slice to an array of structs (without using cgo).
type PrinterInfo5 struct {
pPrinterName *uint16
pPortName *uint16
attributes uint32
deviceNotSelectedTimeout uint32
transmissionRetryTimeout uint32
}
...
dll := syscall.MustLoadDLL("winspool.drv")
f := dll.MustFindProc("EnumPrintersW")
var cbNeeded, nPrinters uint32
fmt.Println(cbNeeded, nPrinters)
f.Call(PRINTER_ENUM_LOCAL, 0, 5, 0, 0, uintptr(unsafe.Pointer(&cbNeeded)), uintptr(unsafe.Pointer(&nPrinters)))
fmt.Println(cbNeeded, nPrinters)
var pPrnInfo []byte = make([]byte, cbNeeded)
f.Call(PRINTER_ENUM_LOCAL, 0, 5, uintptr(unsafe.Pointer(&pPrnInfo)), uintptr(cbNeeded), uintptr(unsafe.Pointer(&cbNeeded)), uintptr(unsafe.Pointer(&nPrinters)))
I have tried this, which prints one iteration successfully, then fails with fatal error: heapBitsBulkBarrier: unaligned arguments
:
hdr := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(&pPrnInfo)),
Len: int(nPrinters),
Cap: int(nPrinters),
}
s := *(*[]PrinterInfo5)(unsafe.Pointer(&hdr))
for _, t := range s {
fmt.Println(t)
}