go生成DLL #
go写dll动态库
package main
import (
"net"
)
//必须导入
import "C"
//编译成动态库也是必须的
func main() {}
//export Interfaces
func Interfaces(list []string, retlen *int) string {
interf, err := net.InterfaceAddrs()
if err != nil {
return err.Error()
}
for i, v := range interf {
if i >= len(list) {
break
}
list[i] = v.String()
}
*retlen = len(list)
return ""
}
go build -ldflags "-s -w" -buildmode=c-shared -o net.dll main.go
执行后,在同级目录下生成了net.h和net.dll文件
C语言中使用
#include <stdio.h>
#include<stdlib.h>
#include <string.h>
#include "net.h"
int main()
{
GoString ret;
GoSlice slice;
slice.len=10;
slice.cap=0;
slice.data=calloc(10,sizeof(GoString));
GoInt retlen=0;
ret=Interfaces(slice,&retlen);
if (ret.n != 0)
{
char* retc = calloc(ret.n+1,sizeof(char));
memcpy(retc,ret.p,ret.n);
printf("Return value:%s\n", retc);
free(retc);
retc=NULL;
}
GoString* st=(GoString*)(slice.data);
for (int i=0;i<retlen;i++)
{
printf("%s\n", st[i].p);
}
free(slice.data);
slice.data=NULL;
return 0;
}
C/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;
}
GO使用C++代码 #
Go不能直接使用C++代码,要把C++代码转化为C
hello.h
#include <stdio.h>
extern int add(int left, int right);
extern void show(char* ptr, int nLen);
extern char* change(char* ptr, int nLen);
extern void callByReference(int& nLen);
extern void callByPtr(int* nLen);
修改hello.cpp代码
return nullptr; //nullptr是c++代码
void callByReference(int& nLen) //int& 是c++代码
main.go
//#include "hello.h"
import "C"
import "fmt"
func main() {
r,_ := C.add(C.int(12),C.int(34))
fmt.Println(int(r))
}
//运行
env CGO_ENABLED=1 go build
Go 调用DLL库 #
上面c++代码生成dll
g++ hello.cpp -fPIC -shared -o libc2plus.dll
转化支撑函数
//int => uintptr
func IntPtr(n int) uintptr {
return uintptr(n)
}
//int -> *int -> unsafe.pointer -> uintptr
func Int2IntPtr(n int) uintptr {
return uintptr(unsafe.Pointer(&n))
}
//*int -> unsafe.pointer -> uintptr
func IntPtr2Ptr(n *int) uintptr {
return uintptr(unsafe.Pointer(n))
}
//[]byte -> *[]byte -> unsafe.pointer -> uintptr
func BytePtr(s []byte) uintptr {
return uintptr(unsafe.Pointer(&s[0]))
}
//ret为uintptr临时变量,不能使用*(*int)(unsafe.Pointer(ret))去获取
func PtrInt(r uintptr) int {
return int(r)
}
获取DLL地址、错误提示函数
const (
DLLPATH = "libc2plus.dll"
)
func GetDllPath() string {
path, err := filepath.Abs(DLLPATH)
abort("GetDLLPATH",err)
return path
}
func abort(fn string, err error) {
if err != nil {
panic(fn + err.Error())
}
}
通过syscall.LoadLibrary获取和调用DLL #
调用c++的add函数
func add(a,b int) int {
fmt.Println(GetDllPath())
handle,err := syscall.LoadLibrary(GetDllPath())
abort("add syscall.LoadLibrary",err)
defer syscall.FreeLibrary(handle)
proc, err := syscall.GetProcAddress(handle, "add")
abort("add syscall.GetProcAddress",err)
if proc == 0 {
abort("add syscall.GetProcAddress",errors.New("proce address is null"))
}
if ret, _, callErr := syscall.Syscall(proc, 2, IntPtr(a), IntPtr(b), 0); callErr != 0 {
panic("add syscall.Syscall"+ callErr.Error())
} else {
//ret为uintptr临时变量,不能使用*(*int)(unsafe.Pointer(ret))去获取
return PtrInt(ret)
}
return 0
}
通过syscall.NewLazyDLL调用 #
调用c++的show函数,通常调用都使用该方式
func show(str []byte, len int) {
dll := syscall.NewLazyDLL(GetDllPath())
show := dll.NewProc("show")
show.Call(BytePtr(str),IntPtr(len))
}
调用c++的callByPtr函数
func passByPtr(n *int) {
dll := syscall.NewLazyDLL(GetDllPath())
show := dll.NewProc("callByPtr")
show.Call(IntPtr2Ptr(n))
}
通过 syscall.MustLoadDL调用 #
调用c++语言的change函数
func change(str []byte, len int) {
handle := syscall.MustLoadDLL(GetDllPath())
change := handle.MustFindProc("change")
_, _, _ = change.Call(BytePtr(str),IntPtr(len))
defer handle.Release()
}
通过window.NewLazySystemDLL调用 #
调用c++的callByReference值传递引用
func passByValue(n int) {
handle := windows.NewLazySystemDLL(GetDllPath())
proc := handle.NewProc("callByReference")
_, _, _ = proc.Call(Int2IntPtr(n))
fmt.Println()
}
//传递的是地址就能获取到n的变化
func passByValue1(n *int) {
handle := windows.NewLazySystemDLL(GetDllPath())
proc := handle.NewProc("callByReference")
_, _, _ = proc.Call(IntPtr2Ptr(n))
fmt.Println()
}
主函数 #
func main() {
sum := add(12,34)
fmt.Println("call c++ add",sum)
str := []byte("function execute")
show(str,len(str))
change(str,len(str))
i := 0
passByValue(i)
fmt.Printf("pass by value %d\n",i)
passByValue1(&i)
fmt.Printf("pass by value1 %d\n",i)
passByPtr(&i)
fmt.Printf("pass by pointer %d\n",i)
}
输出
call c++ add 46 //add
pass by value 0 //passByValue
pass by value1 100 //passByValue1
pass by pointer 1000 //passByPtr
> -------------------
> Pass `pointer` and `int` data: //show
>> function execute, 16
> -------------------
> Pass `pointer` and `int` data: //change
> src strings: function execute
> modify strings: fanction execute
注意 #
1、c++中的char*,go中要通过[]byte传递参数地址,不能使用string
2、环境变量中要设置CGO_ENABLED=1
3、调用其它DLL,如果是386交叉编译,要加上参数GOARCH=386;CGO_ENABLED=1
不加会报错 %1 is not a valid Win32 application”
4、要加入import “C”,要不报错
package shadu/hello: C++ source files not allowed when not using cgo or SWIG: hello.cpp