go嵌入C及调用C的动态库和静态库

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wyy626562203/article/details/83788861

go嵌入C及调用C的动态库和静态库

go版本:go 1.11
环境:ubuntu(涉及到window调用动态库)

目前Go语言有2套编译器:GC和gccgo。其中GC提供的cgo支持C语言,gccgo支持C/C++。
此外,SWIG从2.0.1之后也对go语言提供支持,可以支持C++的类和回调。Go官方提供cmd/go命令,
可以很好的支持cgo。

go源码中嵌入C代码

c代码需要用///* */注释,并且和import "C"之间不能有空行,必须紧接其后。

//cgotest.go
package main 

//#include <stdio.h>
//#include <stdlib.h>
/*
void Hello(char *str) {
    printf("%s\n", str);
}
*/
import "C" 

import "unsafe"
func main() {
    s := "Hello Cgo"
    cs := C.CString(s)
    C.Hello(cs)
    C.free(unsafe.Pointer(cs))
}

编译运行

go run cgotest.go

go源码中调用C动态库

链接进主程序方式

编写动态库测试代码

//test.h                                                                             
#ifndef __TEST_H 
#define __TEST_H 
#ifdef __cplusplus
extern "C" {
#endif
extern int Value; 
extern void sayHello();
extern int add(int a, int b);
#ifdef __cplusplus 
} 
#endif
#endif
//test.c
#include <stdio.h> 
int Value = 123456;
void sayHello()
{
    printf("this is a c library.");
}

int add(int a, int b)
{
    return a + b;
}

编译动态库

gcc -fPIC -shared -o libtest.so test.c

配置环境

如果不把动态库路径添加到LD_LIBRARY_PATH中,当运行go程序时提示找不到动态库。你也可以直接将动态库拷贝到/lib目录下,为了不污染到/lib,这里使用配置环境变量来达到测试的目的。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wyy/goproject/src/cgotest/clib

编写go测试程序

通过CFLAGS配置编译选项(头文件),通过LDFLAGS来链接指定目录下的动态库。注意import "C"是紧挨着注释的,没有空行。​

//testlib.go
package main
/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. -ltest
#include <stdio.h>
#include <stdlib.h>
#include "test.h"
*/
import "C"

import "unsafe"
import "fmt"

func main() {
    fmt.Println("==c library test ==")
    fmt.Printf("rannum:%x\n", C.random())

    cs := C.CString("Hello world")
    defer C.free(unsafe.Pointer(cs))
    C.fputs(cs, (*C.FILE)(C.stdout))
    //C.free(unsafe.Pointer(cs))
    C.fflush((*C.FILE)(C.stdout))
    
    fmt.Println("");
    fmt.Println(C.Value)
    C.sayHello()
}

编译运行

go run testlib.go

输出结果

==c library test ==
rannum:6b8b4567
Hello world
123456

这里你会发现调用sayHello后没有输出this is a c library.

如果没有换行,buffer中的数据不会立即输出。在go调用C的测试程序验证了没有fflush,stdout不会显示输出信息。平时在运行C程序的时候,C的运行环境自动做了fflush的动作。所以将动态库中的printf("this is a c library.");改为printf("this is a c library.\n");

加载动态库文件方式

对于动态库的调用还有一种方式,通过c来调用dll,无需将动态库添加到lib下,也不需要配置环境变量。

package main

/*
#cgo LDFLAGS: -ldl
#include "dlfcn.h"
#include <stdio.h>
int  add_c(int a,int b){
        int ret = 0;
        char* func_name="add";
        char *lib_path = "libtest.so";
        void* libc;
        int (*add)(int _a,int _b);
        if(libc = dlopen(lib_path,RTLD_LAZY))
        {
                add = dlsym(libc, func_name);
                ret =(*add)(a,b);
                dlclose(libc);
        }
        return ret;
}
*/
import "C"

import "fmt"

func main() {
        fmt.Println(C.add_c(10,20))
}

window下加载动态库文件

介绍了三种方法

package main
import(
	"fmt"
	"syscall"
	"time"
	"unsafe"
)


func StrPtr(s string) uintptr {
	return uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(s)))
}

// windows下的第一种DLL方法调用
func MessageBox(caption, text string, style uintptr) (result int) {
	user32, _  := syscall.LoadLibrary("user32.dll")
	messageBox, _ := syscall.GetProcAddress(user32, "MessageBoxW")
	ret, _, callErr := syscall.Syscall9(messageBox,
		4,
		0,
		StrPtr(text),
		StrPtr(caption),
		style,
		0, 0, 0, 0, 0)
	if callErr != 0 {
		panic("Call MessageBox failed: " + callErr.Error())
	}
	result = int(ret)
	defer syscall.FreeLibrary(user32)
	return
}

// windows下的第二种DLL方法调用
func ShowMessage2(title, text string) {
	user32 := syscall.NewLazyDLL("user32.dll")
	MessageBoxW := user32.NewProc("MessageBoxW")
	MessageBoxW.Call(uintptr(0), StrPtr(text), StrPtr(title), uintptr(0))
}


// windows下的第三种DLL方法调用
func ShowMessage3(title, text string) {
	user32,_ := syscall.LoadDLL("user32.dll")
	MessageBoxW,_ := user32.FindProc("MessageBoxW")
	MessageBoxW.Call(uintptr(0), StrPtr(text), StrPtr(title), uintptr(0))
}

func main() {

	num := MessageBox("第一种", "windows下的第一种DLL方法调用", 0x00000003)

	fmt.Printf("Get Retrun Value Before MessageBox Invoked: %d\n", num)
	ShowMessage2("第二种", "windows下的第二种DLL方法调用")

	ShowMessage3("第三种", "windows下的第三种DLL方法调用")

	time.Sleep(3 * time.Second)

	fmt.Printf("test finish...")
}

go源码中调用C链接库

例子源码和上面的一样

编译静态库

gcc -c test.c
ar -rv libtest.a test.o

编译运行

go run testlib.go

go源码中调用C++链接库

一般使用c包装c++在给go调用

猜你喜欢

转载自blog.csdn.net/wyy626562203/article/details/83788861