枚举windows下的串口(2)

2017-12-06 18:52:06来源:CSDN作者:J0KER人点击

分享

用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 mainimport (    "fmt"    "reflect"    "syscall"    "unsafe")var (    nEnumPortsW *syscall.Proc)/* definition in Ctypedef 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 x64type 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 main() {    var cbNeeded uintptr = 0    var cReturned uintptr = 0    nEnumPortsW.Call(        0,        // null        2,        // level        0,        // return buffer.        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])),        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))    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 `[:]' )        rawS := (*[1 << 30]uint16)(unsafe.Pointer(t.pPortName))[:]        deviceName := syscall.UTF16ToString(rawS)        fmt.Printf("%v/n", deviceName)    }}

参考链接:
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 mainimport (    "fmt")func main(){    buffer:=make([]byte,1024)    fmt.Printf("&buffer: %p/n", &buffer)    fmt.Printf("&buffer[0]: %p/n", &buffer[0])}

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台