以海康威视摄像头sdk来试用一下go的sycall

现需要用go写一个运行在win上的常驻服务的客户端,现准备用go来试一下。该客户端会集成多个商家的动态库,故试一下syscall

最新进度

我尼玛,被虐哭了

哈哈哈哈

慢慢补吧

我尼玛,被

有哪些坑

返回string的问题

海康头文件原型

1
2
// 根据错误号返回错误信息
NET_DVR_API char* __stdcall NET_DVR_GetErrorMsg(LONG *pErrorNo = NULL);

直接说返回值,看到char*,第一反应就是指针;看到char第一反应就是byte

于是:

1
2
3
4
5
6
7
8
9
10
11
12
13

// 大概是这个样子
func NET_DVR_GetErrorMsg(errNo *uint) []byte {

h := syscall.NewLazyDLL(DllPath)
proc := h.NewProc("NET_DVR_GetErrorMsg")
n, _, _ := proc.Call(
uintptr(unsafe.Pointer(errNo)))

// 这里用slice会报错,所以要用array,须指定长度
msg := *(*[100]byte)(unsafe.Pointer(n))
return string(msg)
}

这样做,也能得到字符串。但是,由于不知道长度,就会把多的字符串赋值进去

后来知道了cgo,上面有一些和c类型的对应,所以试了一下,完美解决

1
2
3
4
5
6
7
8
9
10
11
12
import "C"

func NET_DVR_GetErrorMsg(errNo *uint) string {

h := syscall.NewLazyDLL(DllPath)
proc := h.NewProc("NET_DVR_GetErrorMsg")
n, _, _ := proc.Call(
uintptr(unsafe.Pointer(errNo)))

msg := (*C.char)(unsafe.Pointer(n))
return C.GoString(msg)
}

看吧,就最后两句不一样

exit 3221225477

也就是0xc0000005问题

这是个什么问题?不知道

为什么会有这个问题?不知道

怎么解决这个问题?不知道

我只在设置回调时遇到过这个问题,如果您遇到了,可以对号入座

1
2
3
// 来自头文件 HCNetSDK.h 
typedef BOOL (CALLBACK *MSGCallBack_V31)(LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser);
NET_DVR_API BOOL __stdcall NET_DVR_SetDVRMessageCallBack_V31(MSGCallBack_V31 fMessageCallBack, void* pUser);

在go中这么搞

1
2
3
4
5
6
7
8
9
10
11
12
func NET_DVR_SetDVRMessageCallBack_V31(msgCallback callback.MSGCallBack_V31,pUser unsafe.Pointer) bool {
h := syscall.NewLazyDLL(DllPath)
proc := h.NewProc("NET_DVR_SetDVRMessageCallBack_V31")

n, _, _ := proc.Call(
// syscall.NewCallbackCDecl(msgCallback),
syscall.NewCallback(msgCallback),
uintptr(pUser),
)

return boolRes(n)
}

可以看到上面代码里面我注释了一句,syscall.NewCallbackCDecl

对,就是这一句错了,原因就是两个方法对c函数的约定不同

syscall.NewCallback 调的c函数要符合__stdcall调用约定

syscall.NewCallbackCDecl 调的c函数要符合__cdecl调用约定

在海康的头文件里就能看到貌似所有的函数都是__stdcall的,以头文件中NET_DVR_SetDVRMessageCallBack_V31为例

1
2
// 设置回调,来自头文件 HCNetSDK.h 
NET_DVR_API BOOL __stdcall NET_DVR_SetDVRMessageCallBack_V31(MSGCallBack_V31 fMessageCallBack, void* pUser);

总之,如果遇到这个问题,可以考虑一下这个解决方法

一个习惯性问题的坑

我接触的几种语言,默认情况下对象都是用指针传递的,这已经变成习惯了

然后,go的struct也是当成class用的

所以一般的struct变量都会赋一个指针,也不会想太多

1
2
3
4
5
type DemoModel struct{

}

var demoModel *DemoModel

然后,海康的某些struct里的属性为另一个struct,比如:

1
2
3
4
5
6
7
8
// 检测结果,来自头文件 HCNetSDK.h 
typedef struct tagNET_ITS_PLATE_RESULT
{
...
NET_DVR_PLATE_INFO struPlateInfo; //车牌信息结构
NET_DVR_VEHICLE_INFO struVehicleInfo; //车辆信息
...
}NET_ITS_PLATE_RESULT, *LPNET_ITS_PLATE_RESULT;

头文件中,属性为值而非指针,然而我下意识的,写上 *

1
2
3
4
type NET_ITS_PLATE_RESULT struct {
struPlateInfo *NET_DVR_PLATE_INFO
struVehicleInfo *NET_DVR_VEHICLE_INFO
}

使用的时候,这些struct字段的值就为nil

这破问题花了我好多时间,真是找不到原因

自己写c++代码来测试(因为我C++代码加了*)都是正常

百度谷歌查了好久,也没找到原因

最后,不知道在排查哪个问题的时候,又看了下头文件,然后说了句卧槽,终于结束了这次debug

再来个问题

struct 里面的 byte[] 指针问题

后面写,现在远程写的,网不好

调私库的问题

这个与syscall 无关,但既然遇到了,就先记在这里

要做以下事

  1. 声明私库的网站
    比如这样
    go env -w GOPRIVATE=gitlab.com
  2. 一般情况,我pc有私库的密钥,用ssh格式的话,不用输密码,则可以转一下格式
    比如这样
    git config --global url.git@gitlab.com:.insteadOf https://gitlab.com/

细节后面补

搜到的问题

关于syscall.callback的个数限制问题

貌似为2000

这里其实我不知道是什么意思

遇到再说

总结

虽然还没写完,但可以先把总结写了

  1. 调用动态库时,有条件就多看看头文件
    理论上说,正确性: 头文件 > 文档 > 其它语言写的demo
0%