英文:
Use structs with Golang syscall on Windows?
问题
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)
}
英文:
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\n", 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)
}
答案1
得分: 4
在上面的代码中,两个地方的uintptr(unsafe.Pointer(&pPrnInfo))
是错误的;它给你的是指向切片头部的指针,而不是实际的底层数组。你应该使用以下代码:
uintptr(unsafe.Pointer(&pPrnInfo[0]))
(由于底层数组是连续的,指向底层数组第一个元素的指针与指向底层数组本身的指针是相同的。)
英文:
uintptr(unsafe.Pointer(&pPrnInfo))
in both places in the code above is wrong; it gives you a pointer to the slice header, not to the actual backing array. You want this instead:
uintptr(unsafe.Pointer(&pPrnInfo[0]))
(Since the backing array is contiguous, a pointer to the first element of the backing array is the same as a pointer to the backing array itself.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论