在Windows上使用Golang系统调用的结构?

时间:2015-11-18 00:33:46

标签: winapi go

EnumPrinters Win32函数接受参数_Out_ LPBYTE pPrinterEnum,指向已分配缓冲区的指针。在C中,它的工作原理如下:

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\n", pPrinterInfo[i].pPrinterName);
}

在Go中使用syscall而不是cgo完成同样的操作?到目前为止,这很多编译,但我不知道如何将结果字节切片强制转换为结构数组(不使用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)))

我试过这个,它成功打印了一个迭代,然后失败了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)
}

1 个答案:

答案 0 :(得分:3)

uintptr(unsafe.Pointer(&pPrnInfo))

以上代码中的两个地方都是错误的;它为您提供了一个指向切片标头的指针,而不是指向实际的支持数组。你想要这个:

uintptr(unsafe.Pointer(&pPrnInfo[0]))

(由于后备数组是连续的,因此指向后备数组的第一个元素的指针与指向后备数组本身的指针相同。)