tags:

views:

89

answers:

3

I have a dynamic link library written in VC++6. I wrote some code with VC++2005 which calls the native VC++6 library. Whenever I pass std::string to the native library, the result is always garbage. However, this does not happen if I pass other types like char *, int, etc. Any ideal what is causing this?

The following code illustrates this.

// VC++ 6 Code

class __declspec(dllexport) VC6
{
public:
VC6();
void DoSomething(const std::string &s);
}

VC6()::VC6() {}

void VC6::DoSomething(const std::string &s)
{
std::cout << s; // Resulting output on screen is garbage
}

// VC++ 2005 Code

void VC2005::DoSomething()
{

  VC6 *vc6 = new VC6();

  std::string s("Test String");  
  vc6->DoSomething(s);  

  delete vc6;

}

+3  A: 

Classes such as std::string aren't necessarily defined the same way in every version of the runtime library (even though they have the same name), so you shouldn't mix the libraries this way. On the other hand, types such as int and char* are the same for a given platform so you can pass them.

In your example, it's better to pass the string as a (pointer,size) pair or simply as a null-terminated string.

Edit: forgot to mention the obvious solution of using the same compiler version. Do this if you want to pass objects around.

Amnon
A: 

Don't do this. It doesn't work.

C++ does not define a fixed ABI, so you can't in general pass non-POD types between libraries or translation units compiled by different compilers.

In your case, VC6 and VC8 have different definitions of std::string (and the compilers may also insert different padding and other changes), and the result is garbage, and/or unpredictable behavior and crashes.

If you need to pass data to a VC6 DLL (a better option might be to recompile that code under a sane compiler), you have to stick to types where you can be sure it'll work. That means 1) POD types (either built-in primitives such as char*, or C structs containing only POD types), or COM objects.

jalf
Even POD is not safe to pass around. Bit fields and unions are likely candidates to be troublesome, but even the packing of a simple struct could change from compiler to compiler. You'd hope that byte ordering would not change, but it legally it could.
Stephen Nutt
The reason why I need to call VC6 from VC8 is because I do not have access to the source code for the library written in VC6 but I need to convert it to managed code so that C# can call the library. The problem is that the VC6 library heavily makes use of std::vector and std::string and there is no way of marshaling these types into c# directly. So what I need is a bridge between VC6 and C# and this is the reason why I choose to come up with a VC8 wrapper for VC6.You mentioned about COM objects. Can you give me some examples on how I can use COM object to achieve the above?
Lopper
Stephen Nutt: True, but with POD types, you can generally nail it down without too much trouble, with packing pragmas and the like. With non-POD types, you've pretty much lost from the outset.
jalf
A: 

You can write a wrapper DLL with a C interface. A C++/CLI wrapper may be needed if p-invoke alone can not handle the interop. Writing a COM server in ATL is probably a better choice to provide an object oriented interface in native code and to avoid writing another wrapper DLL in C++/CLI).

Sheng Jiang 蒋晟