可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a third party library which has a class where the constructor takes a std::wstring
.
The constructor is defined by the third party like this in the header file:
Something(const std::wstring &theString);
My header file has this:
extern "C" __declspec(dllexport) ThirdParty::Something* createSomething(const std::wstring &theString);
My implementation is like this:
ThirdParty::Something* Bridge::createSomething(const std::wstring &theString) {
return new ThirdParty::Something(theString);
}
Now in my C# example program I have:
[DllImport("Bridge.dll", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Unicode)]
public static extern IntPtr createSomething(StringBuilder theString);
When I now try to call this like:
IntPtr ip = createSomething(new StringBuilder("foo"));
I get an AccessViolationException
. When I use String
instead of StringBuilder
I get a SEHException
.
What am I missing or doing incorrectly?
EDIT when I just return 0
in the createSomething
function I get a StackImbalanceException
when using String
.
回答1:
Having STL classes at DLL interface boundaries is very fragile and highly constraining, for example you must pay attention that both the DLL and its clients are built with the same C++ compiler version, with the same switches, link with the same flavor of the CRT, etc.
Moreover, it doesn't work "out of the box" between native C++ and C#.
For C++ and .NET interop, I'd suggest you using C++/CLI to build a bridging layer between your native C++ code components and your .NET C# code.
If you choose to do so, to marshal strings between native C++ code (e.g. using std::wstring
) and .NET code (using .NET's managed String
s), you can use the wrappers built by Microsoft and listed here:
Overview of Marshaling in C++
For example, to convert from a .NET managed string to a native C++ std::wstring
, you may want to use code like this:
#include <string>
#include <msclr\marshal_cppstd.h>
System::String^ managedString = "Connie";
std::wstring nativeString
= msclr::interop::marshal_as<std::wstring>(managedString);
回答2:
I don't believe the C++ ABI is supported in the .Net marshaller out the box.
You would need to marshal the .Net String to a wchar_t*
and then create the std::wstring
on the native side.
Alternatively you could use the C++/CLI (assuming msvc) to mediate the two for you (via marshal_as
), it understands the .Net String and has marshaller to marshal it the std::wstring
. Microsoft provides several standard marshalers, see their overview here.
My experience has generally been that the C++/CLI stub is neater and easier in these situations (your mileage will vary here), else you could try to get the third party library to be provided with a plain C-style API for you.
Your sample code hints at a Bridge
piece of code that may already be under your control. Consider keeping the interface in the bridge as simple as possible (built in types, PODs etc.) and it should simplify the integration of the third party library.
It is also worth noting that, if you are going to link against the C++ library of the third party, is to use the same compiler, settings, calling conventions and the runtime etc. as they do, else you will still run into ABI issues. It isn't always a good idea to provide external libraries with export C++ interfaces (including the STL).
Basically there are a few ways to chain these pieces of code together, you will have to pick one that suits the tools chains being used.
回答3:
The solution was much, much, MUCH simpler! I just forgot __stdcall
and need to use wchar_t
.
My header entry now looks like:
extern "C" __declspec(dllexport) ThirdParty::Something* __stdcall createSomething(wchar_t* theString);
So does the implementation:
ThirdParty::Something* __stdcall Bridge::createSomething(wchar_t* theString) {
std::wstring theWString = std::wstring(theString);
return new ThirdParty::Something(theWString);
}
Now I can pass in String
s and StringBuilder
s.