Golang調用Dll案例

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,打印的錯誤信息是操作已完成???😂😂😂。ITDragon龍用着龜速的網絡在git上看到了這個issue,但是已經被關閉了,說的是已經修復了.......可我用的go version 是1.13.1 。沒有深究這個問題,畢竟工作要緊。最後是直接忽略第三個參數,通過dll方法的返回值來判斷是否成功。

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

在定義結構體時,遇到了兩個無法識別的類型。查閱了一些文檔和dll使用說明文檔。我ITDragon龍用long類型代替了,結果是沒問題。

5. 獲取dll返回的結構體

如果返回值是結構體指針,採用:(*C.自定義結構體)(unsafe.Pointer(返回值))

如果返回值是結構體,採用:(*C.自定義結構體)(unsafe.Pointer(&返回值))

詳情可以參考調用LCAudioThrDll 案例

我的疑惑

ITDragon龍可能連半桶水都不是,從接觸dll開發到現在只有幾天。因爲項目進度的問題,很多細節都沒有時間去了解,只要能跑起來就行。想到後續還會有其他dll需要調用,先整理一個葫蘆,後續再畫。

1. 爲什麼dll需要的參數是unsigned char*,定義C.CString 可以正常執行

2. 爲什麼dll需要的參數是char,定義golang的int類型可以正常執行

3. 爲什麼dll需要的參數是DWORD,定義golang的int類型可以正常執行

下面是我瞭解的類型轉換,先挖再填!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章