3

I've been trying to follow z505/goDLL repo and came across a big problem. The method won't work returning for strings not I'm able to read the output variable of the result.

This is the code I'm using so far (Go) (full code https://play.golang.org/p/Yfg85DCeMLh)

//export PrintHello2
func PrintHello2(Input *C.char, Output **C.char) int32 {
    fmt.Println(C.GoString(Input))
    *Output = C.CString(fmt.Sprintf("From DLL: Hello, %s!\n", C.GoString(Input)))
    fmt.Println("Message: ", C.GoString(*Output))
    return 1
}

//export PrintHello3
func PrintHello3(Input *C.char, Output *int32) int32 {
    fmt.Println(C.GoString(Input))
    *Output = 3
    fmt.Println("Value: ", *Output)
    return 0
}

C# testing code

class Program
{
    [DllImport("goDLL.dll", CharSet = CharSet.Unicode)]
    public static extern int PrintHello2(byte[] data, ref byte[] output);

    [DllImport("goDLL.dll", CharSet = CharSet.Unicode)]
    public static extern int PrintHello3(byte[] data, ref int output);

    static void Main(string[] args)
    {
        string res = "demo";
        byte[] output = null;
        Int32 refVal = 0;

        Console.WriteLine("PrintHello3 Returns: " + PrintHello3(Encoding.UTF8.GetBytes(res), ref refVal));
        Console.WriteLine("Ref Val changed to: " + refVal + "\n");
        Console.WriteLine("PrintHello2 Returns: " + PrintHello2(Encoding.UTF8.GetBytes(res), ref output));
        Console.WriteLine("Ref Val changed to: " + Encoding.UTF8.GetString(output) + "\n");
    }
}

Expected output result:

C:\tmp\DLL>ConsoleApp.exe
demo
Value:  3
PrintHello3 Returns: 0
Ref Val changed to: 3

demo
Message:  From DLL: Hello, demo!
PrintHello2 Returns: 1
Ref Val changed to: From DLL: Hello, demo!

Current result:

C:\tmp\DLL>ConsoleApp.exe
demo
Value:  3
PrintHello3 Returns: 0
Ref Val changed to: 3

demo
Message:  From DLL: Hello, demo!

No panic visible, no error found. Just incomplete output thx

12
  • What does is it mean that all you "can read from C# is a pointer address"? What do expect to happen? What "doesn't work at all" in PrintHello1? Commented Jan 11, 2018 at 13:50
  • On PrintHello1 just after printing "Message: ..." the function seems to panic without any error. It causes the c# app to quit without any returned value. For PrintHello2 I was expecting the Output variable to have the string, it's empty. This doesn't happen with PrintHello3 for instance like in the following example that actually works as expected play.golang.org/p/F5PNcwHnXj4 Commented Jan 11, 2018 at 13:56
  • Please show the actual error output, and what you expect to happen. In PrintHello2, does the Message: line not show any output? If you're expecting the Output *C.char changes to be visible outside of the function, then you need to pass in a **C.char, otherwise you're just overwriting the pointer value in the function scope. Commented Jan 11, 2018 at 14:01
  • Please show the code calling these two function. Commented Jan 11, 2018 at 14:03
  • Go code with **C.char: play.golang.org/p/B_FwFjsaWt0 C# code: pastebin.com/jZk0sEw5 Commented Jan 11, 2018 at 14:19

1 Answer 1

7

Anyway, after some time trying and error"ing", this is the solution

Go

//export PrintHello2
func PrintHello2(Input *C.char, Output **C.char) int32 {
    *Output = C.CString(fmt.Sprintf("From DLL: Hello, %s!\n", C.GoString(Input)))
    return int32(len(C.GoString(*Output)))
}

//export PrintHello4
func PrintHello4(Input *C.char) *C.char{
    return C.CString(fmt.Sprintf("From DLL: Hello, %s!\n", C.GoString(Input)))
}

C#

class Program
{
    [DllImport("goDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    public static extern int PrintHello2([In] byte[] data, ref IntPtr output);

    [DllImport("goDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    public static extern IntPtr PrintHello4(byte[] data);

    static void Main(string[] args)
    {
        string res = "demo";
        IntPtr output= IntPtr.Zero;
        var a = PrintHello4(Encoding.UTF8.GetBytes(res));

        Console.WriteLine("PrintHello4 Returns: " + Marshal.PtrToStringAnsi(a));
        var i = PrintHello2(Encoding.UTF8.GetBytes(res), ref output);

        Console.WriteLine("PrintHello2 Returns: " + i);
        Console.WriteLine("Ref Val changed to: " + Marshal.PtrToStringAnsi(output, i));
    }
}

It will probably be used by many others that might be trying to accomplish the same as i am.

Thanks for the tips and pacience JimB

Sign up to request clarification or add additional context in comments.

4 Comments

is it necessary to call C.free() for the C.CString?
Thank you! It will free the c mem automatically when gc, by creating a go object to wrap the allocated memory. But in this scenario, mem is used as return value. It seems to be a fatal error when the caller is still using the mem but gc occurs in golang.
True, but this case is just a test about sharing a memory address to pass a value. You would need to return de value and not the pointer itself. The example shown isn't efficient and it does not have any propper validation. You better create a decent wrapper and implement it the right way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.