Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling c++ dll functions returning char* from c#. Cannot use DllImport()

I have a c++ dll with 2 functions

const char* getVersionText1(void);
void        getVersionText2(const char** res);

Both function return a string describing the version of the product. First function returns it as const char* (which means it allocates and deal locates it internally) and the second one gets a pointer to char* and just sets it to point to a char* describing the version.

I want to call those functions from C# and display the text. I cant use [dllimport ...] style since the order of the calling of functions is important. I first call constructor than getVersion and finally the destructor. So dll must be loaded to memory first.

Can you please post the few lines of codes that print the text for both functions. I am new to C#, so sorry if you find my code problematic I tried

static class NativeMethods{
    [DllImport("kernel32.dll")]
    public static extern IntPtr LoadLibrary(string dllToLoad);
    [DllImport("kernel32.dll")]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
    [DllImport("kernel32.dll")]
    public static extern bool FreeLibrary(IntPtr hModule);
}
class Program{
    // Define function pointers to entires in DLL
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate IntPtr getVersionText1();
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void getVersionText2(IntPtr );
    static void Main(string[] args){
        // Pointers to functions of DLL.
        getVersionText1 f1;
        getVersionText2 f2;
        IntPtr pDll = NativeMethods.LoadLibrary("p:\\my.dll");
        IntPtr pAddressOfFunctionToCall;
        pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "getVersionText1 ");
        f1 = (getVersionText1)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(getVersionText1));
        pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "getVersionText2 ");
        f2 = (getVersionText2)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(getVersionText2));

        // Here I don't know what to do. It doesn't work ????
        IntPtr verText = f1();
        String s = Marshal.PtrToStringAuto(verText);
        Console.WriteLine(s);   // Doesnt work
        // And how to use the second function. How do I sent a pointer to char* ???
    }
}
like image 350
DanielHsH Avatar asked Feb 19 '13 08:02

DanielHsH


2 Answers

If the C++ library function returns char*, C# code will treat it as IntPtr and Marshal.PtrToStringAnsi() will convert it into C# string.

IntPtr verText = f1();
string s = Marshal.PtrToStringAnsi(verText);
Console.WriteLine(s);
like image 164
Thach Van Avatar answered Nov 02 '22 20:11

Thach Van


Here's a way of making your calls. There may be shorter alternatives that perform the utf8/string conversions automatically.

[DllImport("mydll.dll")]
public static extern IntPtr getVersionText1(void);
public string getVersionTextConst()
{
    IntPtr ptr = getVersionText1();
    // assume returned string is utf-8 encoded
    return PtrToStringUtf8(ptr);
}

[DllImport("mydll.dll")]
public static extern void getVersionText2(out IntPtr res);
public string getVersionTextConst()
{
    IntPtr ptr;
    getVersionText2(ptr);
    // assume returned string is utf-8 encoded
    String str = PtrToStringUtf8(ptr);
    // call native DLL function to free ptr
    // if no function exists, pinvoke C runtime's free()
    return str;
}

private static string PtrToStringUtf8(IntPtr ptr) // aPtr is nul-terminated
{
    if (ptr == IntPtr.Zero)
        return "";
    int len = 0;
    while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0)
        len++;
    if (len == 0)
        return "";
    byte[] array = new byte[len];
    System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, len);
    return System.Text.Encoding.UTF8.GetString(array);
}
like image 45
simonc Avatar answered Nov 02 '22 20:11

simonc