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

Eugene Lisitsky
  • 12,113
  • 5
  • 38
  • 59
Alexandre Dias
  • 101
  • 1
  • 6
  • 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`? – JimB Jan 11 '18 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 https://play.golang.org/p/F5PNcwHnXj4 – Alexandre Dias Jan 11 '18 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. – JimB Jan 11 '18 at 14:01
  • Please show the code **calling** these two function. – leaf bebop Jan 11 '18 at 14:03
  • Go code with **C.char: https://play.golang.org/p/B_FwFjsaWt0 C# code: https://pastebin.com/jZk0sEw5 – Alexandre Dias Jan 11 '18 at 14:19
  • You seem to have C# and Go code. Why the C language tag? – Gerhardh Jan 11 '18 at 14:19
  • Because I'm using also cgo c code and i'm not sure if it's related or not: https://github.com/z505/goDLL/blob/master/goDLL.c – Alexandre Dias Jan 11 '18 at 14:21
  • Update the question, show the output you expect, and the errors you encountered. If it's a stack trace, indicate precisely which line caused the panic. See [ask], and how to create a [mcve]. – JimB Jan 11 '18 at 14:25
  • Done editing as suggested, thanks – Alexandre Dias Jan 11 '18 at 14:39
  • I'm not really familiar with C#, but I doubt a `byte[]` can be transparently used as a `**C.char`. – JimB Jan 11 '18 at 15:28
  • Exactly my thoughts, but what would you suggest to be used if not even string is accepted? – Alexandre Dias Jan 11 '18 at 15:43
  • I'm not sure what the type in C# is going to be, but google turns up a number of resources. Some of these may be of help: https://gist.github.com/esskar/3779066, https://social.msdn.microsoft.com/Forums/vstudio/en-US/8d80d81d-84f0-4cba-8d65-37463d089215/call-c-dll-from-c-passing-char-?forum=clr, https://stackoverflow.com/questions/39987435/pass-char-to-c-dll-from-c-sharp-string, etc. – JimB Jan 11 '18 at 15:53

1 Answers1

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

Alexandre Dias
  • 101
  • 1
  • 6
  • is it necessary to call `C.free()` for the `C.CString`? – Yang May 23 '19 at 08:54
  • I believe you're refering to this post https://stackoverflow.com/questions/9537948/garbage-collection-and-cgo – Alexandre Dias May 24 '19 at 08:46
  • 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. – Yang May 24 '19 at 09:33
  • 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. – Alexandre Dias May 24 '19 at 12:46