7

I've set up a project in Visual Studio 2010 to write unit tests against an existing MFC DLL. I'm using a single-header unit test framework, and linked to the MFC DLL's lib wrapper from the unit test project. I'm trying to construct a class that takes a std::wstring in it's constructor. Here's what my test looks like:

TEST_CASE("MyProject/MyTest", "Do the test.")
{
    MockDbService mockDbService;
    Foobar foo(L"{F00DFACE-FEED-DEAD-BEEF-C0FFEEDECADE}", mockDbService);

    foo.loadObject();

    REQUIRE(mockDbService.getMethodInvokeCount("query()") >= 1);
}

Where Foobar is the class exported from the MFC DLL under test. However, the test framework reports an unexpected exception. I tracked it down to std::wstring's copy constructor when copying the string to Foobar's constructor. The MSVC debugger reports the source string as <Bad Ptr>.

I created a dummy constructor, Foobar::Foobar(long num, IDbService& db) and all the values (including the IDbService&) come across just fine.

Both the MFC DLL and my unit test EXE are sharing a property sheet which should keep the compiler flags equivalent. I'm building and running the test in debug mode. Any ideas why the std::wstring can't copy across the DLL?

Bret Kuhns
  • 4,034
  • 5
  • 31
  • 43
  • 2
    Are you dynamically linking both the EXE and the DLL with the debug version of the CRT? (`/MDd`) – Mr.C64 Mar 02 '13 at 18:37
  • 1
    @Mr.C64 Wow, that was it. My unit test project was using `/MD` and the MFC DLL was `/MDd`. I'd love a brief explanation as an answer so I can understand it; and I'll accept it. Thanks! – Bret Kuhns Mar 02 '13 at 18:41
  • I added a brief explanation. Basically, I think the problem in your case is that debug-build's `std::wstring` has a different implementation than release-build's `std::wstring`. – Mr.C64 Mar 02 '13 at 19:27

1 Answers1

12

You should check that both the EXE and the DLL are dynamically linked with the same debug CRT (/MDd compiler option). Make sure that also other settings like _HAS_ITERATOR_DEBUGGING are the same for both the EXE and the DLL.

(A shortcut could be to just use const wchar_t* instead of std::wstring at the class interface, and just build a std::wstring from the raw pointer inside constructor's body).

EDIT: You confirmed that CRT mismatch (i.e. EXE built with /MD vs. DLL built with /MDd) was the problem. The fact is that the same class name std::wstring means two different classes in debug builds (/MDd) and in release builds (/MD). In fact, in debug builds there can be additional mechanics inside the class implementation to help debugging; this mechanics can introduce inefficiencies, so it's removed in release builds. So, the internal structure of debug build's std::wstring is different from release build's std::wstring (e.g. if you try to print the raw sizeof of std::wstring instances, you can find different numbers in release builds and debug builds). So, the EXE built with /MD was expecting release-build's std::wstring; instead the DLL built with /MDd was expecting debug-build's std::wstring: there is a mismatch between these two expectations (one module is expecting class X but the other module is giving class Y) and so you have a crash.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • That makes sense. Thank you. In that case, my problem is exactly that which I found earlier: http://stackoverflow.com/questions/2322095/why-does-this-program-crash-passing-of-stdstring-between-dlls?rq=1 But both my projects were built in Debug so I didn't think it was related. According to what you're saying, I was using the release build of the CRT even with debug projects. Thanks again. – Bret Kuhns Mar 02 '13 at 20:18
  • 1
    Yes, `/MD` is dynamic linking to release CRT DLL; `/MDd` is dynamic linking to debug CRT DLL. – Mr.C64 Mar 02 '13 at 20:51
  • I also hit with: HAS_ITERATOR_DEBUGGING=0 in one project and not in the other ( once we started using google tests ) – Derek Jun 30 '22 at 13:31