8

I want to import an unmanaged C++ DLL and call a function that takes stringstream as a parameter. In C#, there is no stringstream class, so can anyone tell me how to call such a function from a C# program?

user186246
  • 1,857
  • 9
  • 29
  • 45

5 Answers5

12

You should not expose templated objects via a DLL, period.

Templated objects (e.g. almost everything in std::) become inlined. So in this way, your DLL gets its own private copy of the implementation. The module calling your DLL will also get its own private implementation of stringstream. Passing between them means you are inadvertently weaving two unrelated implementations together. For many projects, if you are using the exact same build settings, it's probably no problem.

But even if you use the same compiler, and mix a release DLL with a debug EXE, you will find stack / heap corruption and other harder-to-find problems.

And that's only using your DLL from another unmanaged C++ exe/dll. Crossing then the lines to .NET is even more of a problem.

The solution is to change your DLL's interface to something that plays friendly across DLL bounds. Either COM (you could use IStream for example), or just a C-style interface like the winapi.

tenfour
  • 36,141
  • 15
  • 83
  • 142
  • I tried to import a function in unmanaged C++ that takes std:string as parameter into the C# program and got the following runtime error: "Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. at Win32API.processRequestWithStream(String& inputString, String& outputString) at Win32API.Main()" - What could be the cause for this error? – user186246 Jan 20 '11 at 08:29
  • 1
    `std::string` is a `typedef` of `std::basic_string`, i.e., it's just as templated as `stringstream`. – Max Lybbert Jan 20 '11 at 08:38
  • 1
    And what about the various smart pointers? If I take this literally, this means one shouldn't expose non-trivial C++ across DLL bounds. –  Jan 22 '11 at 19:43
  • 1
    the point is about inlined code. There are ways to export C++ objects no problem, because of the fact that the implementation has ONE home. Templated (or any inlined) implementations get many independent implementations, and do not mix. Smart pointers included. But the objects they POINT to, not necessarily. – tenfour Jan 23 '11 at 08:31
  • I'm pretty sure that the VS define _USER_DLL automatically exports the `basic_string` family. See http://support.microsoft.com/kb/168958. – KitsuneYMG Jan 27 '11 at 02:24
  • You're telling me. I don't know how many times I have had to fix a crash at work due to one component not defining `_SECURE_SCL=0`... – Ed S. Oct 24 '11 at 20:53
3

If you can modify the C++ dll, export a plain string version. Otherwise you have to build a managed C++ wrapper project, import the other C++ dll, export it as a managed function, and call that from your C# code. C++ interop really sucks.

fejesjoco
  • 11,763
  • 3
  • 35
  • 65
3

I'm afraid that you're going to have to create your own StringStream class in C# in order to be able to consume the functions exported from that DLL. As you mentioned, the .NET Framework doesn't provide any similar class out of the box.

The easiest way is probably to wrap the StringBuilder class provided by the .NET Framework such that it can function as a stream. See this blog post for a further explanation and some sample code.

A similar question was also answered in MSDN Magazine: http://msdn.microsoft.com/en-us/magazine/cc163768.aspx. You might find some of the hints and/or sample code given there useful.

malat
  • 12,152
  • 13
  • 89
  • 158
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
1

You are trying to bind native C++ code to managed code in C#. Best way of doing that in general is to introduce middle layer in managed C++ that will provide interface to calls from C#.

0

Create an Wrapper dll in c, or c++ that exposes an friendly call to that function. It's the better way.

for example an

BOOL getString(TCHAR * myreturnString, DWORD *size);
Sorcerer86pt
  • 427
  • 9
  • 21