如何将** C.char数据传递回调用者的缓冲区?

时间:2018-04-01 02:22:37

标签: go jna cgo

我将构建一个Go共享对象二进制文件(.DLL和.so),它将字符串传递回Java。为了计算从Go传递的C字符串,我写了这个:

package main

/*
#include <stdlib.h>
*/
import "C"
import (
    "log"
    "unsafe"
)

//export passBackHello
func passBackHello(buf **C.char) C.int {
    str := "Hello World!"
    length := len(str)
    cString := C.CString(str) // returns *C.char
    defer C.free(unsafe.Pointer(cString))
    log.Println("In passBackHello: cString:", C.GoStringN(cString, C.int(length)))
    *buf = C.CString(str) // works
    *buf = cString        // doesn't work
    log.Println("In passBackHello: buf:", C.GoStringN(*buf, C.int(length)))

    return C.int(length)
}

func main() {
    buf := make([]byte, 8192) //create my buffer
    cStrPointer := (**C.char)(unsafe.Pointer(&buf[0]))
    defer C.free(unsafe.Pointer(cStrPointer))
    lengthCint := passBackHello(cStrPointer)
    log.Println("In main: length:", int(lengthCint))
    log.Println("In main: buf:", C.GoStringN(*cStrPointer, lengthCint))
    log.Println("In main: buf:", C.GoString(*cStrPointer))
}

当我在函数*buf = C.CString(str)中使用passBackHello时,它可以正常运行:

2018/03/31 19:33:54 In passBackHello: cString: Hello World!
2018/03/31 19:33:54 In passBackHello: buf: Hello World!
2018/03/31 19:33:54 In main: length: 12
2018/03/31 19:33:54 In main: buf: Hello World!
2018/03/31 19:33:54 In main: buf: Hello World!
exit status 3221226356

当我在功能*buf = cStringPointer中使用passBackHello时,buf显示在passBackHello但不在main中:

2018/03/31 19:33:05 In passBackHello: cString: Hello World!
2018/03/31 19:33:05 In passBackHello: buf: Hello World!
2018/03/31 19:33:05 In main: length: 12
2018/03/31 19:33:05 In main: buf:  ⌂3     X☺�
2018/03/31 19:33:05 In main: buf:  ⌂3
exit status 3221226356

我需要让*buf = cStringPointer版本起作用,因为那个版本的C字符串变量为C.free。我正在运行版本go1.10.1 windows / amd64。

更新

在应用Azeem的答案和其他一些清理后,正常工作的Go代码如下所示:

//export passBackHello
func passBackHello(cStrPointer **C.char) C.int {
    str := "Hello World!"
    length := len(str)
    *cStrPointer = C.CString(str) // copies *C.char into caller's buffer
    log.Println("In passBackHello *cStrPointer:", C.GoStringN(*cStrPointer, C.int(length)))
    return C.int(length)
}

func main() {
    buf := make([]byte, 8192) //create my buffer
    cStrPointer := (**C.char)(unsafe.Pointer(&buf[0]))
    defer C.free(unsafe.Pointer(cStrPointer))
    lengthCint := passBackHello(cStrPointer)
    log.Println("In main: length:", int(lengthCint))
    log.Println("In main: *cStrPointer:", C.GoStringN(*cStrPointer, lengthCint))
    log.Println("In main: *cStrPointer:", C.GoString(*cStrPointer))
}

只是为了完成Java调用者看起来像这样:

// allocate a void**
final PointerByReference decCStringPointer = new PointerByReference();
// call the C function
Integer decLength = gpg.passBackHello(decCStringPointer);
// extract the void* that was allocated in C
final Pointer p = decCStringPointer.getValue();
// extract the null-terminated string from the Pointer
final String decValue = p.getString(0);
System.out.printf("decrypted length: %d\n", decLength);
System.out.printf("decrypted value: %s\n", decValue);

看起来缓冲区在C内存中。如何从Java中解放出来?

1 个答案:

答案 0 :(得分:1)

根据C.CString函数的文档:

// Go string to C string
// The C string is allocated in the C heap using malloc.
// It is the caller's responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
func C.CString(string) *C.char

您不需要在功能中释放它。它是调用者(即你的情况下的main函数)应该释放它。

defer C.free(unsafe.Pointer(cString))中移除passBackHello行,它应该有效!

注意
您不需要该缓冲区(即buf),因为它是正在返回的指针并且已经分配了内存。如果需要,您可以将它复制到缓冲区中并释放指针。