Has ANYONE gotten C++ .NET libraries to work?

T

Tommy Vercetti

With Visual Studio .NET 2003, I create a new project -> C++ -> Class
library (.NET).

The instant I include a C++ header file such as:

#include <string>

or any other standard C++ header I can think of, the project won't build:

Linking...
LINK : error LNK2020: unresolved token (0A000006) _CxxThrowException
LINK : error LNK2020: unresolved token (0A000017) delete
LINK : fatal error LNK1120: 2 unresolved externals


I've done lots of Internet searches and found many others who have
encountered this exact problem but none have found a solution. Among
suggested solutions that I've tried:
- Changing the Runtime Library option in Project Properties to
"Multi-threaded Debug DLL" from "Multi-threaded Debug". This didn't help.
- Explicitly adding LIBCPMTD.LIB. Didn't help; I assume this is done
automatically.

I don't have these problems with Managed C++ console and windows
applications. Those seem fine. But Managed C++ class libraries seem to
be simply broken.

This seems like a very large blatant bug. I can't even find official
"known bug" type info on Microsoft's site. I've found a knowledge base
article that loosely describes this bug for the 2002 version of Visual
Studio but the article suggests that it would be fixed in subsequent
versions (such as 2003 which I'm using).

Ultimately my goal is to wrap existing C++ code in a .NET library
interface. All the code will compile under the Managed C++ compiler.
 
G

Guest

Yes, I was able to duplicate your problem. It was very simple to fix it, all
I had to do was move the #include <string> line to the very top of the
program, before including stdafx.h and the others. Try this and let us know
if it now compiles for you.

mosimu
 
J

Jeff F

mosimu said:
Yes, I was able to duplicate your problem. It was very simple to fix
it, all I had to do was move the #include <string> line to the very
top of the program, before including stdafx.h and the others. Try
this and let us know if it now compiles for you.

If using precompiled headers, this merely masks the problem by ingoring
anything before the #include "stdafx.h". Was std::string used in your test
case?

Jeff Flinn
 
T

Tommy Vercetti

Jeff, you are absolutely right; moving includes before the stdafx.h will
just ignore them so it's not really fixing anything.

After ripping my hair out all yesterday, I believe I've identified the
issue:

http://support.microsoft.com/default.aspx?scid=kb;en-us;814472

This is the "Mixed DLL Loading Problem" and is apparently related to
using both managed (.NET) DLLs and unmanaged (regular C++) DLLs withing
the same DLL project.

That is a very confusing name for this problem. It sounds like some
obscure specific issue but it's not; *ALL* MC++ class library projects
will fit this description. With very rare exception, all MC++ projects
use the .NET runtime (which is a managed DLL) and the regular C++
runtime (which is an unmanaged DLL).

If you follow the list of steps on that page, you can get around the
build errors however there are more problems. It sounds like the
global/static variable initialization isn't 100% functional. I haven't
got all my code compiling yet, but that doesn't sound good. The code I'm
using makes plenty of use of static variables.

The Microsoft note says that they left these errors in place so that
people are aware of the other less obvious runtime problems. I'm looking
forward to it.

This is the part of programming that I hate. Sorry to vent. Hope this
helps out others in the same boat.
 
I

Ioannis Vranos

Tommy said:
Jeff, you are absolutely right; moving includes before the stdafx.h will
just ignore them so it's not really fixing anything.

After ripping my hair out all yesterday, I believe I've identified the
issue:

http://support.microsoft.com/default.aspx?scid=kb;en-us;814472

This is the "Mixed DLL Loading Problem" and is apparently related to
using both managed (.NET) DLLs and unmanaged (regular C++) DLLs withing
the same DLL project.

That is a very confusing name for this problem. It sounds like some
obscure specific issue but it's not; *ALL* MC++ class library projects
will fit this description. With very rare exception, all MC++ projects
use the .NET runtime (which is a managed DLL) and the regular C++
runtime (which is an unmanaged DLL).

If you follow the list of steps on that page, you can get around the
build errors however there are more problems. It sounds like the
global/static variable initialization isn't 100% functional. I haven't
got all my code compiling yet, but that doesn't sound good. The code I'm
using makes plenty of use of static variables.

The Microsoft note says that they left these errors in place so that
people are aware of the other less obvious runtime problems. I'm looking
forward to it.

This is the part of programming that I hate. Sorry to vent. Hope this
helps out others in the same boat.


Well, if you can't use the standard library facilities in managed dlls
then you may use the .NET ones. Consider System::String and even
System::StringBuilder (a string type that you can modify and is similar
to std::string).
 
J

Jeff F

Ioannis said:
Well, if you can't use the standard library facilities in managed dlls
then you may use the .NET ones. Consider System::String and even
System::StringBuilder (a string type that you can modify and is
similar to std::string).

Why not drop the .NET libraries? :) So much for BIll Gates commitment to
true interoperability, eh?

Jeff Flinn
 
I

Ioannis Vranos

Jeff said:
Why not drop the .NET libraries? :) So much for BIll Gates commitment to
true interoperability, eh?


Yes, I agree with your notion, that mixed code should work with .NET
dlls too, as it works for executables. For C++ standard libraries at least.


Or better, make their entire C++ implementation managed. After all, CLR
is the platform here. So for example:


#include <iostream>


int main()
{
std::cout<<"Hello world!\n";
}


should compile with /clr:safe sometime in the future.


Initially, with "STL .NET", I think std::basic_string should be included
too.

All C++ programmers use std::string in managed code, myself included.
 
T

Tommy Vercetti

If you follow the list of steps on that page, you can get around the
build errors however there are more problems. It sounds like the
global/static variable initialization isn't 100% functional. I haven't
got all my code compiling yet, but that doesn't sound good. The code I'm
using makes plenty of use of static variables.

Unfortunately, this kills it. static/global variables really don't work
correctly. Both the standard C++ library and boost and the custom code
that I am trying to package in a .NET DLL all encounter problems due to
this bug.

To summarize, EXEs are fine but Managed C++ DLLs really aren't ready for
use. From my investigation, people are using this for some very special
case projects but for most practical purposes it's not in usable form.

I guess the next best thing is to return to rusty old COM or raw DLL
format. Neither of those solutions are desireable but they should work.
 
T

Tommy Vercetti

Ioannis said:
Well, if you can't use the standard library facilities in managed dlls
then you may use the .NET ones. Consider System::String and even
System::StringBuilder (a string type that you can modify and is similar
to std::string).

That would involve rewriting large quantities of C++/STL/Boost code
which isn't a practical option.

If I was doing a full rewrite or was starting from scratch I wouldn't
use Managed C++. I would use C# or maybe even Java.
 
G

Guest

We use that at work.
Desipte the seemingly negative knowledge-base article, it works pretty well,
also using the standard template library.

We make only two modifications to the default project-settings, e.g.
debug-mode:
1) C/C++ code-generation: "Multi-threaded Debug DLL (/MDd)"
2) Linker input: "nochkclr.obj mscoree.lib msvcrtd.lib" (only msvcrtd.lib
differs from the defaults)

Just to be sure, I tested at home with the example below. An when calling
CppClass::Main from a C#-program, I get the correct output.

CppNetLib.cpp:

#include "stdafx.h"

#include "CppNetLib.h"
#include <string>
#include <vector>
#include <iostream>

static char* MyGlobalString = "Hello, World!";

class MyClass
{
public:
static int X;
};

int MyClass::X = 47;

namespace CppNetLib
{
void CppClass::Main()
{
String* msg = new String(MyGlobalString);
Console::WriteLine("MyGlobalString: {0}",msg);
Console::WriteLine("X: {0}",__box(MyClass::X));

// Using std-lib

std::vector<int> vecInt;
vecInt.push_back(1);
vecInt.push_back(2);
vecInt.push_back(3);

std::vector<int>::iterator it = vecInt.begin();

for ( ; it != vecInt.end(); it++)
{
std::cout << *it << " ";
}

std::cout << std::endl;
}
}

CppNetLib.h:

using System;
using CppNetLib;

namespace TetCS
{
class Class1
{
//[STAThread]
static void Main(string[] args)
{
CppClass.Main();
}
}
}
 
G

Guest

OK, static (__nogc) C++ class-initialisers need a call to
__crt_dll_initialize() to work.
Add e.g. a static class-initialiser to all __gc C++ classes to check for
init of CRT.
Revised example:

// CppNetLib.h
#pragma once
extern void InitCRT();
using namespace System;

namespace CppNetLib
{
public __gc class CppClass
{
public:
static void Main();

private:
static CppClass() { InitCRT(); };
};

public __gc class CppUtil
{
public:
static void Main();

private:
static CppUtil() { InitCRT(); };
};
}


// CppNetLib.cpp
#include "stdafx.h"
#include "_vcclrit.h"
#include <string>
#include <vector>
#include <iostream>
#include "CppNetLib.h"

void InitCRT()
{
static bool isInitialized = false;
if (!isInitialized)
{
__crt_dll_initialize();
isInitialized = true;
std::cout << "InitCRT called\n";
}
}

class MyClass
{
public:
MyClass();

static int X;
int m_Y;
};

int MyClass::X = 47;

MyClass::MyClass()
{
m_Y = 74;
}

static MyClass myclass;

namespace CppNetLib
{
void CppClass::Main()
{
// Using std-lib
std::vector<int> vecInt;
vecInt.push_back(1);
vecInt.push_back(2);
vecInt.push_back(3);

std::vector<int>::iterator it = vecInt.begin();

for ( ; it != vecInt.end(); it++)
std::cout << *it << " ";

std::cout << std::endl;

// Testing static class-initializers
std::cout << "CppClass::Main: "
<< myclass.m_Y << std::endl;
}

void CppUtil::Main()
{
// Testing static class-initializers
std::cout << "CppUtil::Main: "
<< myclass.m_Y << std::endl;
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top