DanielHsH
DanielHsH

Reputation: 4453

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* ???
    }
}

Upvotes: 8

Views: 19691

Answers (3)

Soleil
Soleil

Reputation: 7287

If you need more speed, you also can do an unsafe read:

unsafe string GetString(IntPtr ptr) => new string((char*)ptr); // local function

IntPtr charPtr = f1();
var s = GetString(charPtr);

When you add the unsafe method, VS will ask you if you want to allow unsafe code in the project: answer yes. Or manually "project properties/build/allow unsafe code".

Upvotes: 1

simonc
simonc

Reputation: 42165

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);
}

Upvotes: 8

Thach Van
Thach Van

Reputation: 1539

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);

Upvotes: 14

Related Questions