Golang调用Dll案例

前言

在家办公已经两个多星期了,目前最大的困难就是网络很差。独自一个人用golang开发调用dll的驱动程序。本来就是半桶水的我,还在为等待打开一个页面而磨平了耐心。本想依葫芦画瓢把这个驱动做了。可网上找到的案例都是一些简单的调用dll。对于各种传参、获取返回值和一些常见错误的文章太少(可能因为网络不好一些优质文章还没有点开就被关掉了)。今天ITDragon就做一个简单的葫,以广播驱动作为案例。

1.The specified module could not be found.

2.%1 is not a valid Win32 application.

3.The operation completed successfully.

4.error: unknown type name 'HWND'、'DWORD'.

5.获取dll返回的结构体

6.dll传参unsigned char* argName, struct _PlayParam* pParam

调用LCAudioThrDll 案例

ITDragon龙 先画一个葫芦。这个dll是在做广播驱动时用到,列举了其中几个有代表性的方法介绍。

package main

/*
#include <stdlib.h>
typedef struct _PlayParam
{
long hWnd; //主窗口的句柄,如果不为0,则线程有事件会向该窗口发送消息
int Priority; //优先级
int MultiGroup; //多播组号
int CastMode; //传输模式,单播,多播,广播
long IP; //ip,如果是广播和多播,此参数是源网卡的IP,如果此地址为0,则由系统决定使用哪个网卡,如果是单播,这是个目标设备的ip地址。
int Volume; //播放音量取值0~100
int Tone; //音调
int Treble; //高音频率
int Bass; //低音频率
int Treble_En; //高音增益
int Bass_En; //低音增益
unsigned short SourceType; //输入源,0为文件,1为声卡
unsigned short OptionByte; //选项字,默认为0;bit0=1 禁止重采样,bit1=1,启动监听,bit2=1,禁用解码功能(仅播放符合要求的音频文件)
int DeviceID; //音频输入ID号 1~N
int MaxBitrate; //允许最大的比特率组合,如果源文件高于此比特率,将被重压缩至此比特率。
unsigned int Option[15]; //选项
int nChannels; //采样的通道 1~2 CodecType
int nSamplesPerSec; //采样频率 8K,11.025K,22.05K,44.1K
int AudioBufferLength; //Audio数据的长度
unsigned char* AudioBuf; //Audio数据的指针
unsigned int PrivateData[128]; //私有信息,lc_init初始化后,用户不能修改里面的内容。
}PlayParam;
*/
import "C"
import (
"fmt"
"os"
"strconv"
"strings"
"syscall"
"unicode"
"unsafe"
) /*
struct _PlayParam* __stdcall lc_play_getmem (void);
int __stdcall lc_init(unsigned char* pFileName, struct _PlayParam* pParam);
int __stdcall lc_play(struct _PlayParam* pParam);
int __stdcall lc_set_volume(struct _PlayParam* pParam, char volume);
int __stdcall lc_addip (struct _PlayParam* pParam,DWORD ip);
*/ var (
lcAudioSdk, _ = syscall.LoadDLL("LCAudioThrDll.dll")
lcAudioSdkPlayGetMemFunc, _ = lcAudioSdk.FindProc("lc_play_getmem")
lcAudioSdkInitFunc, _ = lcAudioSdk.FindProc("lc_init")
lcAudioSdkPlayFunc, _ = lcAudioSdk.FindProc("lc_play")
lcAudioSdkSetVolumeFunc, _ = lcAudioSdk.FindProc("lc_set_volume")
lcAudioSdkAddIPFunc, _ = lcAudioSdk.FindProc("lc_addip")
) func main() {
filePath := `D:\upload\attachment\20200217115847582_-581698856.mp3`
if IsIllegalFile(filePath) {
return
} audioSource := C.CString(filePath)
defer C.free(unsafe.Pointer(audioSource))
var playParam *C.PlayParam
/**
step1 申请PlayParam内存
1. 无参
2. 获取并转换dll 返回结构体指针
*/
playParamMem, _, _ := lcAudioSdkPlayGetMemFunc.Call()
playParam = (*C.PlayParam)(unsafe.Pointer(playParamMem))
playParam.Volume = 80
playParam.SourceType = 0
playParam.CastMode = 0
playParam.IP = C.long(ipAddrToInt("127.0.0.1")) /**
step2 初始化客户端
1. 入参是unsigned char* 和 struct _PlayParam*
2. 获取并转换dll 返回int类型
*/
initResult, _, _ := lcAudioSdkInitFunc.Call(uintptr(unsafe.Pointer(audioSource)), uintptr(unsafe.Pointer(playParam)))
fmt.Println("lcaudio init result : ", int32(initResult)) /**
step3 播放音频
1. 入参是struct _PlayParam*
2. 获取并转换dll 返回int类型
*/
playResult, _, _ := lcAudioSdkPlayFunc.Call(uintptr(unsafe.Pointer(playParam)))
fmt.Println("lcaudio play result : ", int32(playResult)) /**
step4 调整音量
1. 入参是struct _PlayParam* 和 char (疑惑)
2. 获取并转换dll 返回int类型
*/
volumeResult, _, _ := lcAudioSdkSetVolumeFunc.Call(uintptr(unsafe.Pointer(playParam)), uintptr(90))
fmt.Println("lcaudio set volume result : ", int32(volumeResult)) /**
step5 单播模式添加IP设备
1. 入参是struct _PlayParam* 和 DWORD
2. 获取并转换dll 返回int类型
*/
addIpResult, _, _ := lcAudioSdkAddIPFunc.Call(uintptr(unsafe.Pointer(playParam)), uintptr(C.long(ipAddrToInt("192.168.0.5"))))
fmt.Println("lcaudio add ip result : ", int32(addIpResult)) } // ip地址转16进制
func ipAddrToInt(ipAddr string) int {
bits := strings.Split(ipAddr, ".")
b0, _ := strconv.Atoi(bits[0])
b1, _ := strconv.Atoi(bits[1])
b2, _ := strconv.Atoi(bits[2])
b3, _ := strconv.Atoi(bits[3])
var sum int
sum += int(b0) << 24
sum += int(b1) << 16
sum += int(b2) << 8
sum += int(b3) return sum
} // 文件校验
func IsIllegalFile(filePath string) bool {
if IsChineseChar(filePath) {
return true
}
if !PathExists(filePath) {
return true
} return false
} func IsChineseChar(str string) bool {
for _, r := range str {
if unicode.Is(unicode.Scripts["Han"], r) {
return true
}
}
return false
} func PathExists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}

填坑

1. The specified module could not be found.

在执行syscall.LoadDLL时报错。如果报这个错,可以考虑从两个方面找问题:

第一:有可能是dll路径不对。

第二:有可能是当前dll所需要的其他dll丢失。

第一种情况很好解决,换一个全英文路径试一试。第二种情况需要借助DependenciesGui工具查找dll的依赖项。不推荐用depends22这个工具。将缺失的dll下载并放在当前dll同一层目录即可(或者放在系统目录),只要黄色感叹号消失即可。

2. %1 is not a valid Win32 application.

一般是在64位下执行32位的dll会出现这种情况,配置编译环境即可。GOARCH=386;CGO_ENABLED=1

3. The operation completed successfully.

在执行.Call()方法会返回三个参数。其中第三个参数就是error。并且这个error始终不为nil,打印的错误信息是操作已完成???

Golang调用Dll案例的更多相关文章

  1. golang调用c++的dll库文件

    最近使用golang调用c++的dll库文件,简单了解了一下,特作此笔记:一.DLL 的编制与具体的编程语言及编译器无关 dll分com的dll和动态dll,Com组件dll:不管是何种语言写的都可以 ...

  2. Golang调用windows下的dll动态库中的函数

    Golang调用windows下的dll动态库中的函数 使用syscall调用. package main import ( "fmt" "syscall" & ...

  3. Golang调用windows下的dll动态库中的函数 Golang 编译成 DLL 文件

    Golang调用windows下的dll动态库中的函数 package main import ( "fmt" "syscall" "time&quo ...

  4. Windows平台Go调用DLL的坑

    最近的项目中,使用了GO来开发一些服务中转程序.业务比较简单,但是有一些业务需要复用原有C++开发的代码.而在WINDOWS,用CGO方式来集成C/C++代码并不是太方便.所以用DLL把C++的代码封 ...

  5. Windows平台Go调用DLL的坑(居然有这么多没听过的名词)

    最近的项目中,使用了GO来开发一些服务中转程序.业务比较简单,但是有一些业务需要复用原有C++开发的代码.而在WINDOWS,用CGO方式来集成C/C++代码并不是太方便.所以用DLL把C++的代码封 ...

  6. 全面总结: Golang 调用 C/C++,例子式教程

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  7. C# 远程调用实现案例

    C#远程调用实现案例 2007年11月19日 13:45:00 阅读数:6012 C#实现远程调用主要用到“System.Runtime.Remoting”这个东西.下面从三个方面给于源码实例. ·服 ...

  8. C#生成DLL,在Unity中导入/调用DLL

    网上搜了一些DLL的创建.编写.使用的学习资料,感觉比较的凌乱.或是复杂抽象,或是关键地方一笔带过,不是很适合萌新.于是决定还是图文记录一下该过程,尽量精简而又明确. 学习资料: https://do ...

  9. Golang 调用 C/C++,例子式教程

    大部分人学习或者使用某样东西,喜欢在直观上看到动手后的结果,才会有继续下去的兴趣. 前言: Golang 调用 C/C++ 的教程网上很多,就我目前所看到的,个人见解就是比较乱,坑也很多.希望本文能在 ...

随机推荐

  1. 原生javascript实现选项卡(基础版)

    一.实现原理 1.主要运用“排他思想”,在设置当前元素前,先把相应元素恢复到默认状态 2.给相应元素添加下标的应用 二.代码展示 <!DOCTYPE html> <html> ...

  2. Spring Boot2 系列教程 (四) | 集成 Swagger2 构建强大的 RESTful API 文档

    前言 快过年了,不知道你们啥时候放年假,忙不忙.反正我是挺闲的,所以有时间写 blog.今天给你们带来 SpringBoot 集成 Swagger2 的教程. 什么是 Swagger2 Swagger ...

  3. 初识 ST 表

    推荐博客 : https://blog.csdn.net/BerryKanry/article/details/70177006 ST表通常用于RMQ问题中,询问某个区间的最值这类问题中 ST表的核心 ...

  4. [洛谷P4178] Tree (点分治模板)

    题目略了吧,就是一棵树上有多少个点对之间的距离 \(\leq k\) \(n \leq 40000\) 算法 首先有一个 \(O(n^2)\) 的做法,枚举每一个点为起点,\(dfs\) 一遍可知其它 ...

  5. 洛谷P3645 [APIO2015]雅加达的摩天楼

    题目描述 印尼首都雅加达市有 N 座摩天楼,它们排列成一条直线,我们从左到右依次将它们编号为 0 到 N − 1.除了这 NN 座摩天楼外,雅加达市没有其他摩天楼. 有 M 只叫做 “doge” 的神 ...

  6. Redhat下如何查看nvidia显卡的工作状况

    安装完毕nvidia显卡驱动后,可以使用命令来查看显卡的工作状况,命令如下: nvidia-smi 输入上述命令后,显示界面如下 安装nvidia显卡驱动的步骤,请参照驱动安装cuda和cudnn.

  7. selenium,xpath路径中引入变量

    比如,我需要获取每一条微博的阅读数,总不可能所有微博都找出xpath,然后获取阅读数 找规律 “//*[@id='Pl_Official_MyProfileFeed__20']/div/div[2]/ ...

  8. Quantitative Trading with R(一):两个简单的策略

    下面是两个使用R中的Quantstrat包进行策略构建的例子,都是对600550.ss.600192.ss.600152.ss.600644.ss.600885.ss.600151.ss六只股票进行投 ...

  9. android开发实战-记账本APP(二)

    继昨天的开发,继续完成今天的内容. (一)开始构建一些业务逻辑,开始构建记账本的添加一笔记账的功能. ①对fab按钮的click时间进行修改,创建一个AlertDialog.Builder对象,因此我 ...

  10. springboot之整合基本的jdbc并操作Mysql数据库

    对于数据访问层,无论是SQL还是NOSQL,springboot默认采用整合spring data方式进行统一处理,添加大量自动配置,屏蔽了许多设置,引入各种xxxTemplate,xxxReposi ...