枚举windows下的串口(2)

用go来写.

step.1 测量一个对象的大小
import “unsafe”
var uno uintptr // 这个类型是存放proc-address的
fmt.Printf(“%v\n”, unsafe.Sizeof(uno))

step.2 加载我们的handler
1) 采用syscall.LoadLibrary syscall.GetProcAddr syscall.FreeLibrary
我们的入口不在kernel32.dll上面, 所以若取得proc地址之后却FreeLibrary, 那么syscall.Syscall6对其调用会失败.
屏幕上打印出一堆地址和寄存器, 没有报告到底是什么错误. (只需要把LoadLibrary后面defer FreeLibrary去掉即可调用成功)
2) 还是采用MustLoadDLL然后用sys.DLL的方法MustFindProc 就不会有Free的烦恼.
调用的话参数还异常简单

step.3 内存布局 从C struct 到golang struct 从我们的EnumPorts来看没有任何对齐的问题. (全是64bit一个域)
(问题正好在这里。 查了半天, 发现我们应该保证在golang中以相同的方式遍历buffer, 第一个前提就是struct的尺寸大小的一致! golang都是64中运行的. 用file查看go build之后的. 想必go run的运行时也是x64…懒 就不看了.)
unsafe.Sizeof一下一个struct对比大小。 这个正确了, 几本就找到解了.

step.4 显示字符串. syscall.UTF16ToString 需要一个[]uint16的slice
这个没问题 比如有 ptr *uint16 (先转array再转slice, 后者会产生一个slice头部的拷贝)
那么 (*[1<<30]uint16)(unsafe.Pointer(ptr))[:]
syscall.UTF16ToString([]uint16)string

package main

import (
    "fmt"
    "reflect"
    "syscall"
    "unsafe"
)

var (
    nEnumPortsW *syscall.Proc
)

/* definition in C
typedef struct _PORT_INFO_2 {
  LPTSTR pPortName;
  LPTSTR pMonitorName;
  LPTSTR pDescription;
  DWORD  fPortType;
  DWORD  Reserved;
} PORT_INFO_2, *PPORT_INFO_2;
*/

// This is x64
// If you need to test against a C version, be sure to make it x64
type PortInfo2 struct {
    pPortName    *uint16
    pMonitorName *uint16
    pDescription *uint16
    fPortType    uint32
    reserved     uint32
}

func init() {
    wp := syscall.MustLoadDLL("Winspool.drv")
    nEnumPortsW = wp.MustFindProc("EnumPortsW")

    //var pi PortInfo2
    //fmt.Printf("Size of obj is: %v\n", unsafe.Sizeof(pi))
}

func toStr(str *uint16) string {
    strSlice := (*[1 << 30]uint16)(unsafe.Pointer(str))[:]
    return syscall.UTF16ToString(strSlice)
}

func main() {
    var cbNeeded, cReturned uint32
    nEnumPortsW.Call(
        0,                 // null
        2,                 // level
        0,                 // return buffer.
        uintptr(cbNeeded), // 0.
        uintptr(unsafe.Pointer(&cbNeeded)),
        uintptr(unsafe.Pointer(&cReturned)),
    )
    //nEnumPorts(NULL, 2, (LPBYTE)pPort, pcbNeeded, &pcbNeeded, &pcReturned);
    // fmt.Printf("cb: %v\n", cbNeeded)
    // fmt.Printf("cReturned: %v\n", cReturned)

    buffer := make([]byte, cbNeeded)
    nEnumPortsW.Call(
        0,
        2,
        uintptr(unsafe.Pointer(&buffer[0])),
        uintptr(cbNeeded), //now we have
        uintptr(unsafe.Pointer(&cbNeeded)),
        uintptr(unsafe.Pointer(&cReturned)),
    )

    hdr := reflect.SliceHeader{
        Data: uintptr(unsafe.Pointer(&buffer[0])),
        Len:  int(cReturned),
        Cap:  int(cReturned),
    }
    pInfos := *(*[]PortInfo2)(unsafe.Pointer(&hdr))
    pInfos = pInfos[:] // you can even copy all.
    for _, t := range pInfos {
        // And the length of array is 1<<30. Pretty sure unsafe.
        // First make the pointer an array (with length of inifnite)
        // Then make the array into an slice ( cast with `[:]' )
        fmt.Printf("%v - %v - %v\n", toStr(t.pPortName),
            toStr(t.pMonitorName),
            toStr(t.pDescription),
        )
    }
}

参考链接:
https://stackoverflow.com/questions/38509347/sized-data-load-in-golang-getting-uint16-into-a-uint8-slice
https://stackoverflow.com/questions/28886616/convert-array-to-slice-in-go
https://talks.golang.org/2012/10things.slide#15
https://hacpai.com/article/1446417162636?m=0
https://github.com/golang/go/wiki/WindowsDLLs

最重要的是下面这个链接:
https://stackoverflow.com/questions/33769766/use-structs-with-golang-syscall-on-windows

注意, 使用连续内存时应该跳过头部。 也就是&buffer和&buffer[0]是不同的.

package main

import (
    "fmt"
)

func main(){
    buffer:=make([]byte,1024)
    fmt.Printf("&buffer: %p\n", &buffer)
    fmt.Printf("&buffer[0]: %p\n", &buffer[0])
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值