Don Reba
Don Reba

Reputation: 14051

Passing an STL string to a C++ DLL from a C++/CLI app

I have a C++/CLI project using OpenCV. I compiled this version of OpenCV in VS 2010 myself and I can use it in unmanaged projects without an issue — the trouble started when I tried to use it in a managed one.

The function of interest is cv::imread(std::string&, int). Simply calling it from a managed module did not work at all, producing <invalid pointer> on the receiving end. I was sort of expecting it. After all, managed code has its own std::string implementation.

Things got a little more interesting when I created a separate C++ file, removed CLI support from its module, and placed my code in it. Now, imread was getting a valid pointer, but its contents were scrambled. Apparently, the string I was passing it contained the string pointer offset by 4 bytes, but it expected it to be at the 0 offset.

The unmanaged module is using the same CRT DLL as OpenCV and has all options set to the values appropriate for normal OpenCV use. Why would it have a different string layout? I am lost.

Sample code:

#include <opencv/cv.h>
#include <opencv/highgui.h>

#include <string>

using namespace cv;
using namespace std;

void Run()
{
    string path("C:\\Users\\Don Reba\\Pictures\\Merlin 1D.jpg");

    Mat image(imread(path, CV_LOAD_IMAGE_GRAYSCALE));
    imwrite("image.jpg", image);
}

Upvotes: 2

Views: 1960

Answers (4)

Don Reba
Don Reba

Reputation: 14051

The problem is that Visual Studio 2010 uses different toolsets for C++ and C++/CLI projects by default. This is why STL classes have different layouts despite identical settings. To fix the problem, make sure that Configuration Properties / General / Platform Toolset is set to v100 in the C++/CLI project.

Upvotes: 0

Markus Schumann
Markus Schumann

Reputation: 8244

You shouldn't (can't) pass a std::string& between different modules (DLLs) unless you are sure that all your modules were compiled the same way (release vs. debug etc).

For example: if you compile one DLL in release and another as debug - then the memory layout of std::string is likely to be different. Other compiler settings may influence the memory layout as well.

Try this - compile the code below as release vs. debug and run it. In debug you get 32 in relase 28.

#include <iostream>
#include <string>

int main()
{
   std::cout << "sizeof(std::string) : " << sizeof(std::string) << std::endl;

   return 0;
}

I suggest not to cross module boundaries using std::string.

Upvotes: 2

Chen
Chen

Reputation: 1672

Short answer: Yes, you can pass a STL string to a native C++ DLL from a C++/CLI app, if you use the same compiler settings for the DLL and the C++/CLI app.

Code:

#include <msclr/marshal_cppstd.h> // header for marshal utilities

...

String^ path = "C:\\Users\\Don Reba\\Pictures\\Merlin 1D.jpg"; // Managed string
std::string s = msclr::interop::marshal_as<std::string>(path); // To stl string
cv::imread(s, CV_LOAD_IMAGE_GRAYSCALE);

See this page for more details: http://msdn.microsoft.com/en-us/library/bb384865.aspx

Upvotes: 0

DarkWanderer
DarkWanderer

Reputation: 8866

Answering the question in the title: no, you can't directly marshal std::string from managed to unmanaged code. See answers to another SO question on the reasons. Main reason is that std::string is a template and not a "real" type.

Basically, you need to write a small unmanaged module which provides simple wrappers for the openCV functions, getting rid of STL types. With your example function, it can be as simple as that:

declspec(__dllexport) imread(char* c, int i) {
    string s = c;
    cv::imread(s, i);
}

As for the problem with the string offset... Try creating a separate project, with "Unmanaged" type from the beginning. Switching the project to managed and back can produce a mess with project settings, having unpredictable consequences - at least, I've hit such pits twice...

Upvotes: 5

Related Questions