• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    golang调用c实现的dll接口细节分享

    目的

    本篇文章主要介绍golang在调用c实现的dll时,具体的一些方式。比如值传递、参数传递、指针等等的一些使用。

    一、dll的代码

    c实现的dll代码:

    hello.h

    #ifndef _HELLO_H_
    #define _HELLO_H_
    #include stdio.h>
    #define HELLO_EXPORTS
    #ifdef HELLO_EXPORTS
    #define EXPORTS_API extern "C" __declspec(dllexport)
    #else
    #define EXPORTS_API extern "C" __declspec(dllimport)
    #endif // HELLO_EXPORTS
    EXPORTS_API int add(int left, int right);
    EXPORTS_API void show(char* ptr, int nLen);
    EXPORTS_API char* change(char* ptr, int nLen);
    EXPORTS_API void callByReference(int nLen);
    EXPORTS_API void callByPtr(int* nLen);
    #endif //_HELLO_H_
    

    hello.cpp

    #include "hello.h"
    int add(int left, int right)
    {
     return left + right;
    }
    void show(char* ptr,int nLen)
    {
     printf("> -------------------\n> Pass `pointer` and `int` data:\n");
     printf(">> %s, %d\n", ptr,nLen);
    }
    char* change(char* ptr, int nLen)
    {
     if (!ptr || 0 > nLen)
      return nullptr;
     printf("> -------------------\n> Pass `pointer` and `int` data:\n");
     printf("> src strings: %s\n",ptr);
     ptr[1] = 'a';
     printf("> modify strings: %s\n", ptr);
     return ptr;
    }
    void callByReference(int nLen)
    {
     nLen = 100;
    }
    void callByPtr(int* nLen)
    {
     *nLen = 1000;
    }

    生成一个名为c2plusdll.dll的动态库文件,位于我的路径:E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll

    二、golang的调用代码

    编写调用dll的代码:

    package main
    import (
     "fmt"
     "strconv"
     "syscall"
     "unsafe"
    )
    func main() {
     call()
    }
    func IntPtr(n int) uintptr {
     return uintptr(n)
    }
    func Int2IntPtr(n int) uintptr {
     return uintptr(unsafe.Pointer(n))
    }
    func IntPtr2Ptr(n *int) uintptr {
     return uintptr(unsafe.Pointer(n))
    }
    func BytePtr(s []byte) uintptr {
     return uintptr(unsafe.Pointer(s[0]))
    }
    func call() error {
     left := 4
     right := 5
     err := Add(left, right)
     if err != nil {
      fmt.Println("Error:", err)
      return err
     }
     str := []byte("this is a test msg!")
     err = Show(str, len(str))
     if err != nil {
      fmt.Println("Error:", err)
      return err
     }
     err = Change_bytes(str, len(str))
     if err != nil {
      fmt.Println("Error:", err)
      return err
     }
     n := 0
     err = Call_PassByValue(n)
     if err != nil {
      fmt.Println("Error:", err)
      return err
     }
     fmt.Println("> Call_PassByValue(n)的结果为 n=" + strconv.Itoa(n) + ",期待输出 100")
     n = 0
     err = Call_PassByPtr(n)
     if err != nil {
      fmt.Println("Error:", err)
      return err
     }
     fmt.Println("> Call_PassByPtr(n)的结果为 n=" + strconv.Itoa(n) + ",期待输出 1000")
     return nil
    }
    func Add(left, right int) error {
     dllPath := "E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll"
     handle, err := syscall.LoadLibrary(dllPath)
     if err != nil {
      fmt.Printf("Error: %s\n", err)
      return err
     }
     defer syscall.FreeLibrary(handle)
     add, err := syscall.GetProcAddress(handle, "add")
     if err != nil {
      fmt.Printf("Error: %s\n", err)
      return err
     }
     ret, _, _ := syscall.Syscall(add, 2, IntPtr(left), IntPtr(right), 0)
     if err != nil {
      fmt.Printf("Error: %s\n", err)
     }
     fmt.Println("> Add(4,5)的结果为:", ret)
     return nil
    }
    func Show(str []byte, l int) error {
     dllPath := "E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll"
     handle := syscall.NewLazyDLL(dllPath)
     show := handle.NewProc("show")
     show.Call(BytePtr(str), IntPtr(l))
     return nil
    }
    func Change_bytes(str []byte, l int) error {
     dllPath := "E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll"
     handle := syscall.NewLazyDLL(dllPath)
     change := handle.NewProc("change")
     change.Call(BytePtr(str), IntPtr(l))
     return nil
    }
    func Call_PassByValue(n int) error {
     dllPath := "E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll"
     handle := syscall.NewLazyDLL(dllPath)
     test := handle.NewProc("callByReference")
     test.Call(Int2IntPtr(n))
     return nil
    }
    func Call_PassByPtr(n *int) error {
     dllPath := "E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll"
     handle := syscall.NewLazyDLL(dllPath)
     test := handle.NewProc("callByPtr")
     test.Call(IntPtr2Ptr(n))
     return nil
    }
    

    三、结果分析

    运行的结果:

    从上图中可以看到:

    1、当值传递时并没有修改传入的值;只有指针传递时修改了传入的值。

    2、指针传递时golang侧使用的是byte切片

    四、结论

    1、需要修改参数的值时,必须使用指针类型

    func Call_PassByPtr(n *int) error{
    return nil
    }

    2、需要修改指针的内容时,必须使用指针类型

    func Change_bytes(str []byte, l int) error {
     return nil
    }

    3、golang传递指针给c接口函数时,必须使用[] byte的类型,不能使用string类型

    func Show(str []byte, l int) error {
     dllPath := "E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll"
     handle := syscall.NewLazyDLL(dllPath)
     show := handle.NewProc("show")
     show.Call(BytePtr(str), IntPtr(l))
     return nil
    }
    

    4、golang调用c接口时有三种方式:

    (1)使用syscall.LoadLibrary(dllPath)函数加载dll,syscall.Syscall(...)函数调用具体的函数接口,如下:

    func Add(left, right int) error {
     dllPath := "E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll"
     handle, err := syscall.LoadLibrary(dllPath)
     if err != nil {
      fmt.Printf("Error: %s\n", err)
      return err
     }
     defer syscall.FreeLibrary(handle)
     add, err := syscall.GetProcAddress(handle, "add")
     if err != nil {
      fmt.Printf("Error: %s\n", err)
      return err
     }
     ret, _, _ := syscall.Syscall(add, 2, IntPtr(left), IntPtr(right), 0)
     if err != nil {
      fmt.Printf("Error: %s\n", err)
     }
     fmt.Println("> Add(4,5)的结果为:", ret)
     return nil
    }
    

    (2)使用syscall.NewLazyDLL()加载dll,使用接口函数.Call(uintptr类型的参数)来调用函数

    func Call_PassByPtr(n *int) error {
     dllPath := "E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll"
     handle := syscall.NewLazyDLL(dllPath)
     test := handle.NewProc("callByPtr")
     test.Call(IntPtr2Ptr(n))
     return nil
    }

    (3)使用syscall.MustLoadDLL(dllPath)加载dll,函数接口函数.Call(参数列表)调用

    func Call_PassByValue(n int) error {
     dllPath := "E:\\Code\\vs2015_project\\demo\\x64\\Release\\c2plusdll.dll"
     handle := syscall.MustLoadDLL(dllPath)
     callByReference := handle.MustFindProc("callByReference")
     ret, _, err := callByReference.Call(IntPtr(n))
     if err != nil {
      fmt.Println("DllTestDef的运算结果为:", ret)
     }
     return nil
    }

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。

    您可能感兴趣的文章:
    • Golang中switch语句和select语句的用法教程
    • Golang 编译成DLL文件的操作
    • Golang如何调用windows下的dll动态库中的函数
    • golang实践-第三方包为私有库的配置方案
    • 完美解决golang go get私有仓库的问题
    • golang gopm get -g -v 无法获取第三方库的解决方案
    • Golang: 内建容器的用法
    • golang switch语句的灵活写法介绍
    上一篇:Golang如何调用windows下的dll动态库中的函数
    下一篇:Golang 编译成DLL文件的操作
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    golang调用c实现的dll接口细节分享 golang,调用,实现,的,dll,接口,