您现在的位置是:网站首页> Go语言

Go动态调用dll

摘要

VC写一个 DLL

// TestDll.cpp : Defines the entry point for the DLL application.

//


#include "stdafx.h"


BOOL APIENTRY DllMain( HANDLE hModule, 

                       DWORD  ul_reason_for_call, 

                       LPVOID lpReserved

)

{

    return TRUE;

}


int __stdcall myadd(int a,int b)

{

  return a+b;

}

Go中动态调用

package main


import (

"fmt"

"syscall"

)


func main() {

fmt.Println("你好Hello World!")

addfile, err := syscall.LoadLibrary("TestDll.dll") //动态库加载

if err != nil {

fmt.Printf("cgo:err:%v\n", err)

}

myaddcall, _ := syscall.GetProcAddress(addfile, "myadd")

var nargs uintptr = 3

ret, _, callErr := syscall.Syscall(uintptr(myaddcall), nargs, 5, 3, 0)

if callErr != 0 {

fmt.Printf("call myaddcall:%v\n", callErr)

}

fmt.Printf("myadd:%v\n", ret)

}


syscall.Syscall系列方法

syscall.Syscall syscall.Syscall6 syscall.Syscall9 syscall.Syscall12 syscall.Syscall15

分别对应 3个/6个/9个/12个/15个参数或以下的调用

syscall.Syscall(trap, nargs, a1, a2, a3)

第二个参数, nargs 即参数的个数,一旦传错, 轻则调用失败,重者直接APPCARSH


多余的参数, 用0代替

调用示例

获取磁盘空间

//首先,准备输入参数, GetDiskFreeSpaceEx需要4个参数, 可查MSDNdir := "C:"lpFreeBytesAvailable := int64(0) 

//注意类型需要跟API的类型相符lpTotalNumberOfBytes := int64(0)lpTotalNumberOfFreeBytes := int64(0)

//获取方法的引用kernel32, err := syscall.LoadLibrary("Kernel32.dll") 

// 严格来说需要加上 defer syscall.FreeLibrary(kernel32)

// GetDiskFreeSpaceEx, err := syscall.GetProcAddress(syscall.Handle(kernel32), "GetDiskFreeSpaceExW")

//执行之. 因为有4个参数,故取Syscall6才能放得下. 最后2个参数,自然就是0了r, _, errno := syscall.Syscall6(uintptr(GetDiskFreeSpaceEx), 4,

            uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("C:"))),

            uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),

            uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),

            uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)), 0, 0)

            // 注意, errno并非error接口的, 不可能是nil// 而且,根据MSDN的说明,返回值为0就fail, 不为0就是成功if r != 0 {

    log.Printf("Free %dmb", lpTotalNumberOfFreeBytes/1024/1024)}



简单点的方式? 用syscall.Call

跟Syscall系列一样, Call方法最多15个参数. 这里用来Must开头的方法, 如不存在,会panic.

 h := syscall.MustLoadDLL("kernel32.dll")

    c := h.MustFindProc("GetDiskFreeSpaceExW")

    lpFreeBytesAvailable := int64(0)

    lpTotalNumberOfBytes := int64(0)

    lpTotalNumberOfFreeBytes := int64(0)

    r2, _, err := c.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("F:"))),

        uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),

        uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),

        uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))

    if r2 != 0 {

        log.Println(r2, err, lpFreeBytesAvailable/1024/1024)


调用例子:

var (

    //    kernel32, _        = syscall.LoadLibrary("kernel32.dll")

    //    getModuleHandle, _ = syscall.GetProcAddress(kernel32, "GetModuleHandleW")


    user32, _     = syscall.LoadLibrary("user32.dll")

    messageBox, _ = syscall.GetProcAddress(user32, "MessageBoxW")

)


func IntPtr(n int) uintptr {

    return uintptr(n)

}


func StrPtr(s string) uintptr {

    return uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s)))

}


func MessageBox(caption, text string, style uintptr) (result int) {

    ret, _, callErr := syscall.Syscall9(uintptr(messageBox),

        4,

        0,

        StrPtr(text),

        StrPtr(caption),

        style,

        0, 0, 0, 0, 0)

    if callErr != 0 {

        abort("Call MessageBox", callErr)

    }

    result = int(ret)

    return

}


Top