在Windows上使用Golang的syscall时,可以使用结构体(structs)。

huangapple go评论120阅读模式
英文:

Use structs with Golang syscall on Windows?

问题

EnumPrinters Win32函数接受一个参数_Out_ LPBYTE pPrinterEnum,它是一个指向已分配缓冲区的指针。在C语言中,它的用法如下:

  1. DWORD cbNeeded, nPrinters;
  2. EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &cbNeeded, &nPrinters);
  3. BYTE *pPrnInfo = malloc(cbNeeded);
  4. EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, pPrnInfo, cbNeeded, &cbNeeded, &nPrinters);
  5. PRINTER_INFO_5 *pPrinterInfo = (PRINTER_INFO_5 *) pPrnInfo;
  6. for (int i=0; i < nPrinters; i++) {
  7. printf("pPrinterName: %s\n", pPrinterInfo[i].pPrinterName);
  8. }

在Go语言中,如何使用syscall而不是cgo来实现相同的功能?到目前为止,这部分代码可以编译,但我不知道如何将结果的字节切片转换为结构体数组(不使用cgo)。

  1. type PrinterInfo5 struct {
  2. pPrinterName *uint16
  3. pPortName *uint16
  4. attributes uint32
  5. deviceNotSelectedTimeout uint32
  6. transmissionRetryTimeout uint32
  7. }
  8. ...
  9. dll := syscall.MustLoadDLL("winspool.drv")
  10. f := dll.MustFindProc("EnumPrintersW")
  11. var cbNeeded, nPrinters uint32
  12. fmt.Println(cbNeeded, nPrinters)
  13. f.Call(PRINTER_ENUM_LOCAL, 0, 5, 0, 0, uintptr(unsafe.Pointer(&cbNeeded)), uintptr(unsafe.Pointer(&nPrinters)))
  14. fmt.Println(cbNeeded, nPrinters)
  15. var pPrnInfo []byte = make([]byte, cbNeeded)
  16. 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错误:

  1. hdr := reflect.SliceHeader{
  2. Data: uintptr(unsafe.Pointer(&pPrnInfo)),
  3. Len: int(nPrinters),
  4. Cap: int(nPrinters),
  5. }
  6. s := *(*[]PrinterInfo5)(unsafe.Pointer(&hdr))
  7. for _, t := range s {
  8. fmt.Println(t)
  9. }
英文:

The EnumPrinters Win32 function takes and argument _Out_ LPBYTE pPrinterEnum, a pointer to an allocated buffer. In C, it works like this:

  1. DWORD cbNeeded, nPrinters;
  2. EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &cbNeeded, &nPrinters);
  3. BYTE *pPrnInfo = malloc(cbNeeded);
  4. EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, pPrnInfo, cbNeeded, &cbNeeded, &nPrinters);
  5. PRINTER_INFO_5 *pPrinterInfo = (PRINTER_INFO_5 *) pPrnInfo;
  6. for (int i=0; i < nPrinters; i++) {
  7. printf("pPrinterName: %s\n", pPrinterInfo[i].pPrinterName);
  8. }

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).

  1. type PrinterInfo5 struct {
  2. pPrinterName *uint16
  3. pPortName *uint16
  4. attributes uint32
  5. deviceNotSelectedTimeout uint32
  6. transmissionRetryTimeout uint32
  7. }
  8. ...
  9. dll := syscall.MustLoadDLL("winspool.drv")
  10. f := dll.MustFindProc("EnumPrintersW")
  11. var cbNeeded, nPrinters uint32
  12. fmt.Println(cbNeeded, nPrinters)
  13. f.Call(PRINTER_ENUM_LOCAL, 0, 5, 0, 0, uintptr(unsafe.Pointer(&cbNeeded)), uintptr(unsafe.Pointer(&nPrinters)))
  14. fmt.Println(cbNeeded, nPrinters)
  15. var pPrnInfo []byte = make([]byte, cbNeeded)
  16. 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:

  1. hdr := reflect.SliceHeader{
  2. Data: uintptr(unsafe.Pointer(&pPrnInfo)),
  3. Len: int(nPrinters),
  4. Cap: int(nPrinters),
  5. }
  6. s := *(*[]PrinterInfo5)(unsafe.Pointer(&hdr))
  7. for _, t := range s {
  8. fmt.Println(t)
  9. }

答案1

得分: 4

在上面的代码中,两个地方的uintptr(unsafe.Pointer(&pPrnInfo))是错误的;它给你的是指向切片头部的指针,而不是实际的底层数组。你应该使用以下代码:

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

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

英文:
  1. 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:

  1. 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.)

huangapple
  • 本文由 发表于 2015年11月18日 08:33:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/33769766.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定