I am going to build a Go shared object binary (.DLL and .so) that passes back a string to Java. To work out the C string passing from Go I wrote this:
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))
}
When I use *buf = C.CString(str)
in function passBackHello
it works:
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
When I use *buf = cStringPointer
in function passBackHello
it shows in buf
while in passBackHello
but not in 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
I need to get the *buf = cStringPointer
version to work because that one has the C.free
of the C string variable. I am running go version go1.10.1 windows/amd64.
UPDATE
After applying Azeem's answer and some other cleanup the working Go code looks like this:
//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))
}
And just to be complete the Java caller looks like this:
// 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);
It looks like the buffer is in C memory. How do I free it from Java?
According to the documentation of C.CString
function:
// 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
You don't need to free it in your function. It is the caller (i.e. main
function in your case) that is supposed to free it.
Remove the line defer C.free(unsafe.Pointer(cString))
from your passBackHello
and it should work!
NOTE:
You don't need that buffer (i.e. buf
) because it's the pointer that is being returned and the memory is already allocated. You may copy it in your buffer if required and free the pointer.