1

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.

4
  • 1
    You'll need some C++/CLI and/or some pure C++ (you can choose). You can't create C++ objects from C#. Commented Jun 23, 2015 at 9:25
  • I tried to follow this tutorial: codeproject.com/Articles/18032/How-to-Marshal-a-C-Class and there they do what I am trying here. Commented Jun 23, 2015 at 9:26
  • And in general linking against a third party C++ library is a mess... If you use the same compiler with the same options of the C++ library then there aren't problems... Otherwise it is bad... Commented Jun 23, 2015 at 9:27
  • That tutorial doesn't create a std::string or a std::wstring. And both solutions require creating a C/C++ bridge, where the bridge doesn't accept C++ objects... Commented Jun 23, 2015 at 9:29

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

The main problem for linking against a third party DLL is the mixing of versions of C++ runtime... see for example siomsystems.com/mixing-visual-studio-versions See the Passing C/C++ Objects (Non-Primitive Data Objects) chapter.
Outside the the .Net string marshalling, there are a host of additional C++ issues at play. The assumption is that they have the same compiler etc. as the vendor. Good point, worth adding to the answer.
2

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 Strings), 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 Comments

It is important to note that using C++/CLI won't solve the problem described in the first paragraph. Precondition for using C++/CLI for interop is that there is no version mismatch.
@xanatos: You're right. I think the best "isolation" is obtained building COM components (which does have a learning curve), or DLLs with a pure-C interface.
-2

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 Strings and StringBuilders.

1 Comment

if on x86 , then try __cdecl calling convention

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.