Reputation: 317
I have a C# app that calls a C++ DLL.
In C#, I have code as follows:
[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
public extern static String Func1(String arg);
......
String arg = "test text";
String retstring = Func1(arg);
In CPP, I have the function defined as follows:
extern "C"
{
__declspec(dllexport) LPWSTR Func1(LPWSTR arg)
{
....
LPWSTR ret1 = L"1?2?3?4?5";
LPWSTR ret2 = SomeActualFunction(arg);
retturn ret1; // return ret2;
}
}
If I return ret1 in C++'s Func1(), all works fine. And in VS2008's memory window, I can see correct Unicode binaries. In C++ the binaries of ret1 is
"31 00 3f 00 32 00 3f 00 33 00 3f 00 34 00 3f 00 35 00"
, and in C# the binaries of retstring is
"28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00 31 00 3f 00 32 00 3f 00 33 00 3f 00 34 00 3f 00 35 00"
. I think the C# binaries
"28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00"
are the header of System.String type.
What's more, if I add the following line just before return in CPP code, I can get correct retstring in C# code too:
ret2 = L"1?2?3?4?5";
But when I return ret2 in the C++ DLL, the returned string ret in C# seems to be corrupted. The binaries in the C++ DLL are correct Unicode on my inspection. But the binaries of retstring in the C# code are
"28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00 dd dd dd dd dd dd dd dd dd dd dd dd ....".
I can only notice ret2 is longer than ret1 - ret2 has some hundreds of WCHARs.
Any ideas? Thx in advance.
Upvotes: 6
Views: 2901
Reputation: 1102
Here is a link for your reference should you need to marshal other data types UPDATED LINK FOR .NET 4.0 https://msdn.microsoft.com/en-us/library/sak564ww(v=vs.100).aspx
Upvotes: 0
Reputation: 923
I think I got this working: A C# call to a C++ DLL, returns a string to C#. I will share what I think are the things that make it work:
In C#
using System.Runtime.InteropServices;
[DllImport(
@"C:\Users\Ron\Documents\Visual Studio 2013\ etc. ...(The whole path)... my.dll,
CallingConvention = CallingConvention.Cdecl
)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string cpp_brand_files(string home_dir, string xml_lic);
And the call later in C#:
string a_str = cpp_brand_files(home_dir, xml_license);
In C++ DLL:
#using <mscorlib.dll>
using namespace System;
extern "C" __declspec(dllexport)
wchar_t* cpp_brand_files(char* home_dir, char* xml_lic);
And further down in C++:
wchar_t* cpp_brand_files(char* home_dir, char* xml_lic)
{
BSTR samp = ::SysAllocString( L"This is the long, long string." );
return samp;
}
AND ALSO, compile the C++ DLL with /cli (Common Language Runtime Support) in the configuration properties. Right click the C++ project, then Properties -> Configuration Properties -> General -> Common Language Runtime Support.
So that's what I think are the bare bones of what makes it work (returning a string).
I am using Visual Studio 2013 RC. Experts, please comment on what is extra or missing.
Upvotes: 0
Reputation: 1959
I've uploaded a sample code on SkyDrive (http://cid-48a119f5ed65483e.office.live.com/self.aspx/.Public/MarshalString.zip). It demonstrates how to pass a managed string to unmanaged code and how to manipulate it.
Upvotes: 0
Reputation: 613501
I would always use a BSTR
for this because it makes the responsibility of memory allocation/deallocation transparent.
C++
#include <comutil.h>
BSTR GetSomeText()
{
return ::SysAllocString(L"Greetings from the native world!");
}
C#
[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();
You don't say how your strings are being allocated, but as soon as you use a dynamically allocated string you need to tackle that issue. The great thing about BSTR
is that it uses the shared COM allocator which enables the C# marshaller to deallocate the string with the same allocator as the C++ code that allocated it.
Upvotes: 8
Reputation: 178179
Is your func __cdecl or __stdcall calling convention? IIRC, the default is for C# is __stdcall
, but the default for C++ is __cdecl
. Try adding CallingConvention=CallingConvention.Cdecl
.
Another possibility: Since you say returning a static string works, is the pointer returned by SomeActualFunction
still valid? If it pointed to a local buffer in that function, it would no longer be valid after returning from the function.
Upvotes: 1
Reputation: 43331
So, if you return the pointer to hard-coded string L"1?2?3?4?5", everything is OK. But if you return SomeActualFunction, the answer is incorrect. Maybe C++ code is incorrect? How does SomeActualFunction work? For example, it may return LPWSTR from some stack-allocated object which is destroyed at this time. Try to test this with C++ client first.
Upvotes: 0
Reputation: 3145
[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPStr)]
public extern static String Func1(String arg);
or
[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
public extern static IntPtr Func1(String arg);
// In your calling code
string result = Marshal.PtrToStringUni(Func1("somestring"));
Upvotes: 5