Passing Strings from C# to C++ Dll and Back - Minimal Example

Passing strings from C# to C++ DLL and back -- minimal example

You cannot pass a C++ std::string across an interop boundary. You cannot create one of those in your C# code. So your code can never work.

You need to use interop friendly types at the interop boundary. For instance, null-terminated arrays of characters. That works well when you allocate and deallocate the memory in the same module. So, it's simple enough when passing data from C# to C++.

C++

void foo(const char *str)
{
// do something with str
}

C#

[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(string str);

....

foo("bar");

In the other direction you would typically expect the caller to allocate the buffer, into which the callee can write:

C++

void foo(char *str, int len)
{
// write no more than len characters into str
}

C#

[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(StringBuilder str, int len);

....

StringBuilder sb = new StringBuilder(10);
foo(sb, sb.Capacity);

Wrong behaviour when passing string from c# to c dll

In C# ulong is a 64 bit type. The PID parameter is DWORD which is a 32 bit parameter.

Change your C# parameter declaration from ulong to uint to fix the problem.

How to pass strings from C# to C++ (and from C++ to C#) using DLLImport?

Passing string from C# to C++ should be straight forward. PInvoke will manage the conversion for you.

Geting string from C++ to C# can be done using a StringBuilder. You need to get the length of the string in order to create a buffer of the correct size.

Here are two examples of a well known Win32 API:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
public static string GetText(IntPtr hWnd)
{
// Allocate correct string length first
int length = GetWindowTextLength(hWnd);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText(hWnd, sb, sb.Capacity);
return sb.ToString();
}


[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetWindowText(IntPtr hwnd, String lpString);
SetWindowText(Process.GetCurrentProcess().MainWindowHandle, "Amazing!");

How to use string as parameter in c# functions imported from c++ DLL?

On the C++ side, change from string to const char*. Leave the C# side be. That's what the default expectation is for P/Invoke when it sees a C# string on the declaration side.

Passing an array of strings from C# to C++ DLL function and fill it in the DLL and get back

To make this current approach work you'd need to pass StringBuilder instances rather than string. That's because the data is flowing from caller to callee. The strings are out parameters. And that means that the caller has to allocate the buffers for each string, and know how large the buffers need to be.

It's much easier to use BSTR here. This allows you to allocate the strings in the native code, and have them deallocated in the managed code. That's because BSTR is allocated on the shared COM heap, and the p/invoke marshaller understands them. Making this slight change means that the caller does not need to know how large the strings are up front.

The code would look like this:

SAMPLEDLL_API BOOL InitExecution(BSTR* names, int count)
{
for (int i = 0 ; i < count; i++)
names[i] = SysAllocStr(...);
return TRUE;
}

And on the C# side you write it like this:

[DllImport(@"mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool InitExecution(
[Out] IntPtr[] names,
int count
);

And then you've got a bit of work to marshal from BSTR to C# string.

IntPtr[] namePtrs = new IntPtr[count];
InitExecution(namePtrs, namePtrs.Length);
string[] names = new string[namePtrs.Length];
for (int i = 0; i < namePtrs.Length; i++)
{
names[i] = Marshal.PtrToStringBSTR(namePtrs[i]);
Marshal.FreeBSTR(namePtrs[i]);
}

pass char * to C DLL from C# string

Update your P/Invoke declaration of your external function as such:

[DllImport("dork.dll")]
public static extern int CopyFunc([MarshalAs( UnmanagedType.LPStr )]string a, [MarshalAs( UnmanagedType.LPStr )] string b);

int GetFuncVal(string src, string dest)
{
return(CopyFunc(dest,src));
}


Related Topics



Leave a reply



Submit