Limited types in C++ DLLs

I have started using a combination of C# and C++ DLLs (VC6) for running the computational part of my image processing tasks in C#. As it is very easy to use C++ DLLs in C# using DLLImport, I have run into a problem. It appears to me that only a limited number of types can be used in the creation of this DLL.

I start by opening the VC6 wizard and choose the Win32 DLL project. From there, I next choose the third option of a simple DLL with some exported symbols.

What I am trying to do is to try to use the string type. I have created a very simple function that returns a string of characters. My function is below:
extern "C" __declspec(dllexport) char* testStr(void);

When I use char* in the prototype, the code compiles with no problem. Next I try
extern "C++" __declspec(dllexport) char* testStr(void);

Also no problem. Next I try
extern "C" __declspec(dllexport) string  testStr(void);

I get a compilation error. Same if I do
extern "C++" __declspec(dllexport) string  testStr(void);

I have included all the headers in the declarations
#include <string>
#include <string.h>
#include "string.h"

but I still get the compiling error. Is there a way around this? I can work with the char* approach if required but I would prefer to use string and have the same/similar types in the unmanaged and managed part of my code. Is there a simple reason for this compiling error?

Can someone post the correct syntax of how this should be done?

Who is Participating?
evilrixConnect With a Mentor Senior Software Engineer (Avast)Commented:
>> extern "C" __declspec(dllexport) string  testStr(void);
The string class is a C++ object, when you use extern "C" you are explicitly saying this code is C only. This being the case, it cannot support the use of string.

>> extern "C++" __declspec(dllexport) string  testStr(void);
You either need a "using namespace std;" before the declaration or you need to use the fully qualified name (std::string). See below. Why do you need to use extern "C++" if the code is already C++?

#include <string>
extern "C++"
	__declspec(dllexport) std::string  testStr(void);

Open in new window


Not wishing to call my friend Rx wrong, can I just comment on

>> "when you use extern "C" you are explicitly saying this code is C only."

Strictly speaking that is not true: let me quote from a good site I have used:

"Defining a C++ function as extern "C", tells the compiler to generate function names and function calls that are compatible with the old "C" standard. This way a C program can call C++ programs and the other way round."
evilrixSenior Software Engineer (Avast)Commented:
Hi mrwad99. Thanks for your augmentation. My response was specifically trying not to be too technical and; rather, just convey the salient point that this gives the function C semantics. The point is that the result of trying to use a non-POD (plain of data) type in a function with C linkage is undefined. A std::string is not a POD type and is not part of the C langauge.

" 7.5 Linkage specifications

9. Linkage from C++ to objects defined in other languages and to objects defined in C + + from other languages
is implementation-defined and language-dependent. Only where the object layout strategies of two language
implementations are similar enough can such linkage be achieved."

C is different language from C++.
Cloud Class® Course: SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

aaborkarAuthor Commented:
Thanks for your response guys. The compilation works fine now. I had completely forgotten about the std namespace. I thought std was only used when cin and cout were used but I guess I was wrong.
evilrixSenior Software Engineer (Avast)Commented:
All STL entities are in the std namespace :)
aaborkarAuthor Commented:
Now the code compiles. But I have to specify the linkage as C++ and not C. If I try to compile the code as

extern "C" __declspec(dllexport) std::string inStr(void); I get the following below.

No warning if I do
extern "C++" __declspec(dllexport) std::string inStr(void); I get the following below.

Now the problems seems to be that C# cannot find entry points into a dll that has C++ linkage. But I will post this on a separate thread.
.\DLLFile.cpp(4) : warning C4190: 'inStr' has C-linkage specified, but returns UDT 'std::basic_string<_Elem,_Traits,_Ax>' which is incompatible with C

Open in new window

evilrixSenior Software Engineer (Avast)Commented:
Did you see my point above? The std::string is not a C type so trying to use it in a function that has C linkage is not portable. The reason C# can't see the functions with C++ linkage is that the ABI (Application Binary Interface) for C++ defines no standard for name mangling (the mechanism that allows you to have overloaded functions); hence you normally have to specify C linkage. The problem, as you have discovered, is that by doing so you hinder your ability to use C++ types at the API boundary. There is no easy way to get around this I'm afraid.
I was just writing up my findings of a past question I asked about passing STL object across DLL boundaries, and released that you are the same thing here, so I thought I would comment again on this.

Something I came across (and that Rx will be familiar with) is that

***you cannot safely pass STL objects across DLL boundaries. ***

You are returning a std::string from a DLL, presumably for use in a C# app/DLL.

You are asking for a major headache IMHO when you start using this code (believe me, I have been there :))

Just pass plain memory (char*) and then construct some helper object (eg std::string maybe) with the memory passed when it is received.

I found this article whilst searching: it talks about a std::map, but the point it raises about Microsoft's dodgy, sorry, "feature-rich", implementation of the STL is interesting:

An alternative to the STL is also mentioned.

You can search Google or similar for the words STL DLL boundary to find more reasons to not to do what you are doing if you like :)
evilrixSenior Software Engineer (Avast)Commented:
>> Just pass plain memory (char*) and then construct some helper object (eg std::string maybe) with the memory passed when it is received.

Just one observation. Be careful, if it's gone out of scope when it's return from the function you'll also be in hot water :) If it's not then I'd question how you're persisting it. Static type? Heap? Pass in buffer? Each has it's own potential pitfalls.
aaborkarAuthor Commented:
Well I was able to do some searching and found that the StringBuilder class in C# helps solve this issue.  StringBuilder can be used with a fixed buffer size and passed over to C++ in the form of char*. Then a simple strcpy(...) {within the buffer size} works to modify the string.

Now my next worry is to figure out how to send classes and structs from C# to C++. I have started a new thread:

since I don't want to deviate away from the current subject. Thanks for all the help guys.


I see you are new to EE, so if you feel this question has been answered, please close it by accepting the answer/s you feel best solved the problem for you.  This prevents community support getting annoyed at having to clean up after users :)
evilrixSenior Software Engineer (Avast)Commented:
BTW: mrwad99, I forgot to say it's good to see you getting stuck in there and helping out :) Nice to have you on-board as a regular expert... I'm looking forward to sharing some threads with you my friend ;)
:o) @ RX

Heh, I am afraid at the minute I just want to keep my premium services for free; I do a lot more asking than I do answering as you know!  But I did wamt to share what I had myself learnt with your help on the previous thread here, as I found it a real pain, and the source of many many cold sweats until I had a reasonable explanation as to why it was happening.  This will hopefully prevent others having that, just like many PAQs here have saved me :)
evilrixSenior Software Engineer (Avast)Commented:
>> I do a lot more asking than I do answering as you know!
Every little helps though :)

Take care my friend.

Ditto :)
evilrixSenior Software Engineer (Avast)Commented:
I feel I should point out that I'd already correctly answered the question you asked well before the answer that was selected (the very first answer in fact identifies the problem and the solution) here and whilst I don't want to take anything away from mrwad99, the first correct answer posted should be the one accepted. If any additional posts assisted you should then split the points accordingly. Is there any reason you chose to ignore the answers I provided?
aaborkarAuthor Commented:
Sorry, I was absent minded when I chose my previous answer. How do I distribute additional points to other providers?
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.