Click here to Skip to main content
Click here to Skip to main content

Best gotchas of C++/CLI

By , 1 May 2013
 
Prize winner in Competition "Best C++ article of August 2012"

Best gotchas in C++/CLI

What is IJW and C++/CLI ? Why this article ?

For managed developers, C++/CLI allow them to take advantage of native C/C++ code in the .NET world.

For unmanaged developers, C++/CLI allow them to take advantage of managed .NET code in the native world.

ITW is the acronym for It just work. Most of the time, it is used by clueless developers that want to feel smart -...ok I am one of those Smile | <img src= " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" /> -, and that only mean they used C++/CLI and not pInvoke to interact with native code.

Why do I call them clueless ? Because it IJDNWTW (It Just Does Not Work That Way), and you have to know current C++ practices and internals to code correctly... Thus the need of this article.

Why would you need to use C++/CLI ? because pInvoke is also difficult to get it right, and require you to write grunt wrapper code to interact with the unmanaged API.
pInvoke is the right solution when you have only 2 or 3 unmanaged call without complex structures in your application... More than that, C++/CLI will save you time but only if you are well advised.

Rebirth of C++/CLI

In Visual Studio 2010, Microsoft stopped intellisense support on C++/CLI.

C++ is hard enough with intellisense, so without it, C++/CLI was useless, and classic C# pInvoke code was much easier to write.

And then Visual Studio 2012, re-enabled it. I think that the strategy of Microsoft is first to bring C++ developers on the metro plateform, then smoothly bring them to managed world... That's just speculation though. After all, they did that with VB6 developers with VB.NET, and it worked...

This is your story my son...

Last days, I made an article around HttpSysManager, a tool that made heavy use on the unmanaged HTTP Server API.

My code worked, and I had a simple goal : Get rid of this awful pInvoke code, and use C++/CLI instead.
My unit tests was done, so it was just really a matter to move managed classes to C++/CLI, and call unmanaged API without the pInvoke layer.

This is a story about my journey, the good, the bad and the ugly.

Here is from what I started.

Simple enough, a WPF project, with all my interop code, and a test project.

Here was the classes of HttpSysManager, and what I wanted to do with it :

As you can see, the only class that will left managed is HttpSysManager, this is the facade used accross my codebase and unit tests.

Most of the code will be deleted, because most of the code was using pInvoke. With C++/CLI I will be able to call unmanaged API directly.

So I opened my project in Visual studio 2012, and create a new C++/CLI library project called HttpSysManager.Native.

I don't understand, most of this stuff... my way with dealing with the unknow is to delete it. Because it is only when things crash that we can learn something new.

Now the compiler yell at me error C1083: Cannot open precompiled header file:DebugHttpSysManager.Native.pch': No such file or directory.

I don't know what is a precompiled header, I deleted it, and now it wants it... I've gone in my project property, and found the settings Precompiled Header setting, I changed the Use (/Yu) setting to Not Using Precompiled Headers.

Then, I removed the #include in AssemblyInfo.cpp, and it compiled ! Great now I'm the master of the situation.

Along the way, I notice the parameter General -> Plateform Toolset set to Visual Studio 11 (2012).
It corresponds to the compiler, linker, and VC++ runtime libraries used by your project. I set it to Visual Studio 10 (2010), because I don't want to rely on a RC version.

Now, I add a reference from my managed project to my native project and...

A reference to 'HttpSysManager.Native (Visual Studio 2010)' could not be added. An assembly must have a 'dll' or 'exe' extension in order to be referenced.

Nevermind... just a bug on VS2012 RC. The workaround I found on internet is to reference the .dll instead of the project.
I did not like it because when I will compile HttpSysManager in Release mode, it will pick the debug dll of HttpSysManager.Native, and it will not compile the dependent project automatically, if that is not alreaedy done !

I little hack in the HttpSysManager.csproj fix that problem, at the expense of an annoying warning message in the error list.

<ItemGroup>
    <ProjectReference Include="..\HttpSysManager.Native\HttpSysManager.Native.vcxproj"></ProjectReference>
    <Reference Include="System" />
    <Reference Include="System.Core" />

Let's try to create a new managed class in HttpSysManager.Native and reference it in HttpSysManager.

public ref class Test //In Source.cpp
{
};

Ok I can reference it, I can instantiate Test...

I run it and... *boom* BadImageFormatException.

Maybe I need to address the warning : There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference HttpSysManager.Native "x86".

Ok that mean that my VC++ project is x86, and my managed project AnyCPU... but wait !

Gotcha number 1 : AnyCpu mode does not exist for C++/CLI assembly

I don't want to release two different binaries x86 and x64, so I just changed HttpSysManager plateform to x86, which is also supported in x64 OS with virtualization (I will talk about that soon).

Ok, now it works !

Let's run it on another computer, without visual studio...

*boom* it crashed and burn in flames !

Ok maybe I forgot a dependency ?

procmon.exe helps me :

Ok, I missed msvcr100D.dll, but, like a popular coffee machine maker would say : what else ?

A cool tool to see dependencies of your native library is Dependency Walker, I runned it on HttpSysManager.Native.dll.

All dll are part of windows, except msvcp100D and msvcr100D.

Why do I need them ? This research brings me to an old concept I forgot on C/C++ linkers and linking in general...

Static linking, load time linking, runtime linking

Do you remember this popular program ?

#include<stdio.h>
void main()
{
   printf("hello world !");
}

Most of you remember that stdio.h contains the declaration of printf.

But where is the actual implementation ? Response : It depends.

Static linking

With static linking, the implementation is in a .lib file, and merged at compile time to your .exe, or .dll.
With static linking, your program does not have any dependency at runtime, at the expense of its size.

In the .NET world, we use ILMerge for such thing.
In the .NET world .lib files does not exists, only other managed .dll can be merged inside another .exe or .dll.

Load time linking

Another alternative, is load time linking. This means that the windows loader will fetch dependencies only when the DLL, or EXE are loading into your program.

These dependencies are specified in the header of the DLL or EXE file. (we call that the PE header)

It is what we have seen with Dependency walker.

At compile time, the C/C++ linker always need .lib because it needs to solve your code's references by giving them an implementation... even if the real implementation is resolved at load time ... So the trick is to give to the linker .lib files with empty implementations, these are only stubs that will be replaced at load time. These .lib file are within the Windows SDK.

All the real implementations are in dll files, loaded at load time.

Thus, the executable size is smaller and your program is much more modular... But deployment is harder.

Dynamic linking

The last linking type, but not the least, is runtime linking, that is the equivalent that what we do in .NET with Assembly.Load... We don't need any .lib at compile type, but the dll need to be present at runtime during LoadLibrary.

#include <Windows.h>

typedef int (*PPrintf)(const char * _Format, ...);
//Function pointer type declaration, in .NET we call that a delegate => delegate int PPrintf(const char* format, params object[] args)

int main()
{
    PPrintf printf; //Declare a function pointer
    HMODULE module = LoadLibrary(L"MSVCR100D.DLL"); //Load library => Assembly.Load("...dll");
    printf = (PPrintf)GetProcAddress(module, "printf"); //Get address of printf
    printf("hello world");
    return 0;
}

Back to my problem

How can I know, in the previous code snippet, that MSVCR100D.dll has the implementation of printf ?

Well... every .dll export their functions, they have an export table that map a function name to their address in the dll, you can see this table with dependency walker.

GetProcAddress just use this information, add the DLL base address, and return the address.(base address = where the dll was loaded in virtual memory of the process)

In the .NET world, all .NET assemblies are loaded at JIT time, this means that if you inspect a normal .NET assembly, you will not see your managed dll with dependency walker.

Maybe now you understand why my application crash on another computer... the problem is just that HttpSysManager.Native link the C Runtime Library (printf and all basic functions) at load time !

So I tried to set it up to static linking to remove dependencies (Multi-threaded Debug)... Except that it does not work in C++/CLI ! (Correction : static linking works but not on the C Runtime Library -printf,memset,memcpy and the likes)

Thus gotcha number 2 : With C++/CLI, forget static linking on the C Runtime Library, and embrace VC++ redistribuable dlls. (In C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\redist)

I don't want to create an installer, and ask my user to install Microsoft Visual C++ 2010 Redistributable Package (x86) so the solution was to copy these .dll in the output folder at compile time with this piece of msbuild code at the end of my C++/CLI project (.vcxproj)

<Target Name="AfterBuild">
        <ItemGroup>
            <VCRedist Include="$(VCInstallDir)redist\$(VCRedistDir)*.dll"></VCRedist>
        </ItemGroup>
        <Copy SourceFiles="@(VCRedist)" DestinationFolder="$(OutputPath)"></Copy>
</Target>

$(VCInstallDir) is the install directory of your visual studio tool set. I know it existed, because lots of dialog box in vcxproj' configuration show all the list:

For that, I have gone in Project properties/Linker/Input/Additional dependencies, and clicked on the arrow then edit.

$(VCRedistDir) is a folder specific to my environment I needed to specify. The path for the C Runtime Library in x86 Debug mode is

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <CLRSupport>true</CLRSupport>
    <CharacterSet>Unicode</CharacterSet>
    <VCRedistDir>Debug_NonRedist\x86\Microsoft.VC100.DebugCRT\</VCRedistDir>
</PropertyGroup>

This PropertyGroup is only used in Debug|Win32 (x86).

So in summary, in x86 debug mode with vs 2010 toolset, $(VCInstallDir)redist\$(VCRedistDir)*.dll, is equal to this files C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\redist\*.dll.

For details, I already wrote a lot on MSBuild.

But wait, that is not all : Gotcha number 3 : C++/CLI output dir is SOLUTION_DIRECTORY/Debug|Release, as opposed to PROJECT_DIRECTORY/bin/Debug|Release for managed projects.

Using a project reference from my managed project to the native one did not copied the C Runtime Library dlls from the native's project directory.
The solution was to change the output directory of my managed project to ../Debug|Release as opposed to the default bin/Debug|Release...

Now it works fine on my other computer... Few... and I did not start coding yet !

Debug heap, heap, hooray!

A common problem with native/managed interaction, is that the managed world use System.String whereas the native world use char* and whchar* (ansi and unicode strings).

The common solution is to use IntPtr Marshall::StringToHGlobalAnsi|Unicode(String managedStr)to allocate native string from managed one, and then free the allocation after that... Here is a simplified version.

#include <stdio.h>
using namespace System;
using namespace System::Runtime::InteropServices;

void main()
{
    String^ line = Console::ReadLine();
    char* cStr = (char*)Marshal::StringToHGlobalAnsi(line).ToPointer();
    printf(cStr);
    delete cStr; //or (delete[] or free())
}

Thanks for trying, but that's not my day !

This problem brought me within the memory management of windows !

In this journey I met VirtualAlloc, GlobalAlloc, RtlAllocateHeap, and the CRT (C Runtime Time) Debug Heap.

At the lowest level, Windows can only request whole memory pages to the memory manager, each page have the same fixed size (you can see it with GetSystemInfo).
And you can use VirtualAlloc, to allocate at the page level. Your question is now : So how is it possible that in my program, I can allocate at the byte level ? The response is : thanks to a data structure called the heap. And your program create its own heap called the global heap at the start of your program with RtlCreateHeap.

In normal case, every malloc, or new or free or delete are just call to the underlying Heap API on the global heap. (RtlAllocateHeap, and RtlFreeHeap)

Marshal::StringToHGlobalAnsi means : allocate an ANSI native string from the Global Heap, so why my code crashes like that ?

The answer is... The CRT (C Runtime Time) Debug Heap !

In debug mode, malloc, delete, free, new does not allocate in the Global Heap, but in a special debug heap for debugging purpose.
This heap will directly tell you want you free the same memory block twice ! Very handy, you can use DbgInt.h with the debug heap api to check its state at runtime for debug configurations !

In summary : My code crash because StringToHGlobalAnsi allocate memory from the Global Heap, but, my native project compiled in debug mode delete and alloc memory on the CRT Debug Heap !
Here is how to fix that.

#include <stdio.h>
using namespace System;
using namespace System::Runtime::InteropServices;

void main()
{
    String^ line = Console::ReadLine();
    char* cStr = (char*)Marshal::StringToHGlobalAnsi(line).ToPointer();
    printf(cStr);
    Marshal::FreeHGlobal(IntPtr(cStr));
} 

Note that my previous code would work perfectly fine in Release mode.

Update: Thanks to CodeProject, I always learn better way to do things Smile | <img src= " />

Here is another way to do the same things easily:

#include <stdio.h>
#include <msclr/marshal.h>

using namespace System;
using namespace msclr::interop;
using namespace System::Runtime::InteropServices;

void main()
{
    String^ line = Console::ReadLine();
    marshal_context context;
    const char* cStr = context.marshal_as(line);
    printf(cStr); //Marshal_Context free things correctly when out of scope.
}

You can even see in the implementation that for wchar_t (unicode char) it allocates with Marshall::StringHGlobalUni and free with Marshal::FreeGlobal. (Thanks to Espen Harlinn for the trick. For more information go to Overview of Marshalling in C++.)

Gotcha number 4 : In Debug mode, you are not using the same native heap than .NET Marshal class

You can check everything I tell you by yourself with API montitor. (By the way this tool is great to reverse engineer other programs and get WINAPI functions !)

You did not touch me : I am invisible

Another gotcha involve template classes take this code :

template<class T>
public ref class Base
{
public: void MethodBase()
        {}
};

public ref class Derived : Base<wchar_t>
{
};

public ref class Derived : Base<wchar_t>
{
};

And now, I try to use it in my managed project:

Derived derived = new Derived();
derived.MethodBase();

And here is the pretty error : 'Derived' does not contain a definition for 'MethodBase' and no extension method 'MethodBase' accepting a first argument of type 'Derived' could be found.

Ok, I ILSpy my native assembly and see that :

First question : Why there is 2 non generic Base class instead of one generic Base<T> class ?

Second question : Why I can't see my MethodBase method ?

The problem is that I am using a template and not a generic... The difference is that templates are classes generated at compile time !

So why not use a generic ?

generic<class T>
public ref class Base
{
public: void MethodBase()
        {}
};

And the problem is : gotcha number 5 you can't use generic with generic native type parameter... This line give me a compilation error because _GUID is a native type...

public ref class Derived2 : Base<_GUID>
{
};

Ok, nevermind, I don't care !
Now question two : Why can't I see my MethodBase method ?

Well here is why : You cannot access templates defined in a referenced assembly with C++/CLI language syntax, but you can use reflection. If a template is not instantiated, it’s not emitted in the metadata. If a template is instantiated, only referenced member functions will appear in metadata.

Solution 1 : makes MethodBase virtual. (Thanks Espen Harlinn)

generic<class T>
public ref class Base
{
public: virtual void MethodBase()
        {}
};

Solution 2 : references MethodBase in the native code, and somehow call it from accessible code.

template<class T>
public ref class Base
{
private: void _DummyAndUselessMethod() //Necessary and Useless method
         {
             if(false)
             {
                MethodBase(); //Never got called, but now it will appear in metadata
             }
         }
public: Base()
        {
            _DummyAndUselessMethod(); //Necessary and Useless call
        }
public: void MethodBase()
        {
        }
};

That explain why we can see in HttpSysManager.Native such code:

    template<class TSet>
public ref class ManagedSet
{
    //If I don't use that, metadata for these methods are not generated (http://msdn.microsoft.com/en-us/library/ms177213.aspx)
private: void ShowToManagedCode()
         {
             if(false)
             {
                 Add();
                 Update();
                 Delete();
                 ConfigId = HttpServiceConfigCache;
             }
         }

Thus Gotcha number 6 : You have to reference and somehow call methods from template class to use them in managed code.

You can't instantiate me !

This is about two gotchas that combine their strenght together to do another bigger gotcha...

Let's see a simple and innocent code like this one :

namespace TestCLI {

#pragma managed(push, off)
    public class MyNativeClass
    {
    public:
        MyNativeClass(){printf("constructor called");};
        ~MyNativeClass(){printf("deleted");};

    };

void InitGlobal()
{
    static MyNativeClass globalObject;
}

#pragma managed(pop)


    public ref class MyManagedClass
    {
    public: 
        MyManagedClass()
        {
            InitGlobal();
        };
    };
}

Thanks to Petro Protsyk, I found that this code was a culprit of vicious crash at the end of the program... Here is my console app calling this code.

static void Main(string[] args)
{
    new TestCLI.MyManagedClass();
}

Run it annnnd... it crashes when the program closes.

0xC0020001: The string binding is invalid !

The workaround ? Simple enough... You just have to make the function InitGlobal managed. (I moved the #pragma managed(pop) up)

namespace TestCLI {

#pragma managed(push, off)
    public class MyNativeClass
    {
    public:
        MyNativeClass(){printf("constructor called");};
        ~MyNativeClass(){printf("deleted");};

    };

#pragma managed(pop)
void InitGlobal()
{
    static MyNativeClass globalObject;
}




    public ref class MyManagedClass
    {
    public: 
        MyManagedClass()
        {
            InitGlobal();
        };
    };
}

Another workaround, is to move the static native object to class scope.

namespace TestCLI {

#pragma managed(push, off)
    public class MyNativeClass
    {
    public:
        MyNativeClass(){printf("constructor called");};
        ~MyNativeClass(){printf("deleted");};
        static MyNativeClass globalObject;
    };
MyNativeClass MyNativeClass::globalObject;

void InitGlobal()
{
    
}

#pragma managed(pop)

    public ref class MyManagedClass
    {
    public: 
        MyManagedClass()
        {
            InitGlobal();
        };
    };
}

Gotcha number 7 : You can't instantiate a static global native object with a destructor from a native function or method...

That would be good, as long as your code is running in the default AppDomain... Because if you don't as in the following code :

static void callback()
{
    new TestCLI.MyManagedClass();
}
static void Main(string[] args)
{
    var domain = AppDomain.CreateDomain("te");
    domain.DoCallBack(callback);
    AppDomain.Unload(domain);
}

It crashes !

Gotcha number 8 : You can't instantiate a static global native object with destructor from a managed method if you are not in the default app domain !!!

Now you can combine Gotcha number 8 and Gotcha number 7 together and you get the ubber gotcha !

Gotcha number 9 : You can't instantiate a static native object with destructor in any way if your code does not execute in the default app domain !

Tarmik found a way to overcome Gotcha 9 and 7 ! The problem seems to be that the process call static object's destructor when the CLR is always freed. It seems that the C++/CLI library is unloaded by the CLR, but its static variables' destructors are called by the C Runtime Library just after.

Anyway, the solution is clever, you just need to call destructors before the C Runtime Lib, but before CLR close. For this, Tarmik took the code crtdll.c and reverse engineered it to produce the following snippet : It calls destructors, and remove them from the destructor's list.

In the following code, CrtDestroyStatics is called when winform close the application. (System::Windows::Forms::Application::ApplicationExit) You can call it elsewhere depending on your situation.

Update :  for MSVCRT10.dll, check bendenajones'comment.

/*
    Typically all custom classes written as globals / static variables must be brought to managed code side - using for example
    code like this:
 
    // Contains static variables, must be known to CLR. (http://www.codeproject.com/Articles/442784/Best-gotchas-of-Cplusplus-CLI)
    #ifdef _MANAGED
        #pragma managed(push, on)
    #endif
    
        static MyClass g_myglobal;
 
    #ifdef _MANAGED
        #pragma managed(pop)
    #endif
 
    same rule applies with functions with static variables.
    
    If you check shutdown sequence of dll/exe - 
        The program '[4940] TestVM.exe: Managed' has exited with code 0 (0x0).
        The program '[4940] TestVM.exe: Native' has exited with code 0 (0x0).
 
    So typically it shutdowns managed code first, and after that native code.
 
    crtdll.c does all the dirty work on executing native destructors on globals.
    
    If native code somehow references managed memory - you'll get
        0xC0020001: The string binding is invalid
    exception during shutdown.
    
    This code tries to make the same trick as crtdll.c, only before that one executes - 
    during managed code shutdown.
    
    crtdll.c will not execute same destructors over again, because they have been cleared from
    crt registration list (__onexitbegin / __onexitend).
*/
 
#ifdef _MANAGED     // Works only with managed C++
#pragma once
#pragma managed(push, on)
#include lt;stdlib.h>     //free

void CrtDestroyStatics(void);
 

void OnApplicationExit( System::Object^, System::EventArgs^)
{
    CrtDestroyStatics();
}
 
class CRegisterManagedShutdown
{
public:
    CRegisterManagedShutdown()
    {
        System::Windows::Forms::Application::ApplicationExit += gcnew System::EventHandler( &OnApplicationExit );
    }
}gCRegisterManagedShutdown;
 

//
//  http://msdn.microsoft.com/en-us/library/system.windows.forms.application.applicationexit.aspx#Y0
//
//C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src\internal.h(41):
typedef void (__cdecl *_PVFV)(void);
extern "C" void * __cdecl _decode_pointer(void *);
extern "C" void * __cdecl _encoded_null();
 
// crtdll.c
extern "C" _PVFV *__onexitbegin;
extern "C"  _PVFV *__onexitend;
 

 

 
void CrtDestroyStatics(void)
{
    _PVFV * onexitbegin = (_PVFV *)_decode_pointer(__onexitbegin);
    if (onexitbegin)
    {
        _PVFV * onexitend = (_PVFV *)_decode_pointer(__onexitend);
        _PVFV function_to_call = NULL;
 
        /* save the start and end for later comparison */
        _PVFV * onexitbegin_saved = onexitbegin;
        _PVFV * onexitend_saved = onexitend;
 
        while (1)
        {
            _PVFV * onexitbegin_new = NULL;
            _PVFV * onexitend_new = NULL;
 
            /* find the last valid function pointer to call. */
            while (--onexitend >= onexitbegin && (*onexitend == NULL || *onexitend == _encoded_null()))
            {
                /* keep going backwards. */
            }
 
            if (onexitend < onexitbegin)
            {
                /* there are no more valid entries in the list, we are done. */
                break;
            }
 
            /* cache the function to call. */
            function_to_call = (_PVFV)_decode_pointer(*onexitend);
 
            /* mark the function pointer as visited. */
            *onexitend = (_PVFV)_encoded_null();
 
            /* call the function, which can eventually change __onexitbegin and __onexitend */
            (*function_to_call)();
 
            onexitbegin_new = (_PVFV *)_decode_pointer(__onexitbegin);
            onexitend_new = (_PVFV *)_decode_pointer(__onexitend);
 
            if ( ( onexitbegin_saved != onexitbegin_new ) || ( onexitend_saved != onexitend_new ) )
            {
                /* reset only if either start or end has changed */
                 önexitbegin = onexitbegin_saved = onexitbegin_new;
                 önexitend = onexitend_saved = onexitend_new;
            }
            
            break;
        }
        /*
        * free the block holding onexit table to
        * avoid memory leaks.  Also zero the ptr
        * variables so that they are clearly cleaned up.
        */
 
        free ( onexitbegin ) ;
 
        __onexitbegin = __onexitend = (_PVFV *)_encoded_null();
    }
 
} //CrtDestroyStatics

 
#pragma managed(pop)
#endif

 Conclusion and summary

  • Gotcha number 1 : AnyCpu mode does not exist for C++/CLI assembly.
  • Gotcha number 2 : With C++/CLI, forget static linking on C Runtime Library, and embrace VC++ redistribuable dlls. (In C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\redist).
  • Gotcha number 3 : C++/CLI output dir is SOLUTION_DIRECTORY/Debug|Release, as opposed to PROJECT_DIRECTORY/bin/Debug|Release for managed projects.
  • Gotcha number 4 : In Debug mode, you are not using the same native heap than .NET Marshal class.
  • Gotcha number 5 : you can't use generic with generic native type parameter.
  • Gotcha number 6 : You have to reference and somehow call methods from template class to use them in managed code.
  • Gotcha number 7 : You can't instantiate a static global native object with a destructor from a native function or method...
  • Gotcha number 8 : You can't instantiate a static global native object with destructor from a managed method if you are not in the default app domain.
  • Gotcha number 9 : You can't instantiate a static global native object with destructor in any way if your code does not execute in the default app domain !

Don't get me wrong, C++/CLI is awesome... My code is now simpler and has no memory leaks thanks to the RAII principle. However be careful, it is not as easy as it seems, and bring you its load of challenge !

History

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Nicolas Dorier
Software Developer Freelance
France France
Member
I develop to make you forget what's between you and your work.
 
I teach and, with delight, you'll see that the best castles are done with the dumbest concepts.

Curiosity is the best teacher.
 
If you are interested for working with me, this way Smile | :)

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionVery nicememberCIDev2 May '13 - 5:52 
Well written and illustrated with plenty of useful info.
Just because the code works, it doesn't mean that it is good code.

AnswerRe: Very nicememberNicolas Dorier2 May '13 - 6:51 
Thanks ! Smile | :)
GeneralI give you a 5 on C++/CLI topic, but...professionalShawn-USA1 May '13 - 19:12 
I disagree that you are saying "because pInvoke is also difficult to get it right, and require you to write grunt wrapper code to interact with the unmanaged API". That is only because Microsoft does not provide such a tool to let us automatically generate PInvoke code either for C style DLL or C++ DLL containing exported classes. C++/CLI is actually a glue-language for calling native DLL from .NET, MS could have invented such a tool for C# as well long time ago.

GeneralRe: I give you a 5 on C++/CLI topic, but...memberNicolas Dorier1 May '13 - 21:18 
I've seen such tool. (pInvoke.net created one if I remember)
However, I don't entirely agree, you are creating a tool for pInvoke maybe you can give me a good reply on the problem I have with pInvoke.
 
The problem with pInvoke, is that most example are using IntPtr with complex marshaling code to get their structure right. You can say that we can use real pointer to make in more easy in .NET, and I agree, since that's what I do most of the time. But still the code is more complicated than C/C++. (well... that's relative, if a good tool existed, I would prefer such tool than bringing C/C++ CLI with all these gotchas)
 
Microsoft did not provide such a tool because they can't.
The problem is that DLLs have no information about parameters and types contained in it.
Such information can only be found in PDB or .h files, or through reverse engineering.
 
Are you saying that your tool is parsing .h ?
If that's the case, your tool's release is worth checking.
GeneralRe: I give you a 5 on C++/CLI topic, but...professionalShawn-USA1 May '13 - 21:46 
The tool I wrote is used to build .NET Wrapper for C++ DLL exporting C++ Classes. So it is different from any other tools you have seen so far. The one created by Pinvoke.net is only for C style DLL exporting just methods. There are no other tools which can generate .NET wrapper for C++ DLL without developer's involvement of big effort.
 
Marshaling is complicated, I have 15+ years experience of coding C++, even I would have difficulty of working on something like std:vector<std:vector<std::string>> myself in C++ Code, I guess it will be an issue in C++/CLI as well, but the tool will correctly marshal this template class to a right C# wrapper class without issues, because the tool can progressively marshal std::string first, then std::vector<std::string>, and then std::vector<std::vector<std::string>>.
 
Microsoft's Visual C++ can retrieve the type information from the C++ header files, no other third party compilers or tools can do the same to match exactly what their compiler parse the header files into. It is no exception to our tools, we will require their compiler to do the same, but it is done automatically by the tools.
 
Since the tool needs to validate the C++ dll and header files to make sure all the necessary files are gathered, it does require MS Visual C++ installed, otherwise, calling into the C++ DLL is not even guaranteed, right? Without that requirement, you don't even know if the C++ header files are exactly the files which were used to build the DLL.

GeneralRe: I give you a 5 on C++/CLI topic, but...memberNicolas Dorier1 May '13 - 21:57 
Amazing work !!!!
I'm looking forward to test it.
When will it be shipped ? From your link[^], you don't seem to have shipped it yet.
I'll follow your blog to not miss it.
GeneralRe: I give you a 5 on C++/CLI topic, but...professionalShawn-USA2 May '13 - 5:29 
Yes. It is not ready to ship it yet. I am still busy working on it. Thank you for being interested in it.

GeneralRe: I give you a 5 on C++/CLI topic, but...memberDewey2 May '13 - 13:45 
That wasn't the question!
 
He asked "When will it be shipped ?"
 
I don't know is a valid answer, but you gave none!
GeneralRe: I give you a 5 on C++/CLI topic, but...professionalShawn-USA2 May '13 - 14:18 
Hi, Dewey,
 
There are many ways to answer a question, You have every right to answer the question while being polite when you were asked. For me, I prefer answering that way, especially he asked about my product, I am still busy working on it, I have no time to think about when I will be delivering it because I care much more about the quality of the product instead of when I can release it.
 
I am so sorry that you were bothered by my answer. Thanks for paying attention to my comments though.

QuestionFix #9 Updated for MSVCRT10memberbendenajones15 Apr '13 - 7:22 
MS changed the function prototype and name used to clean up pointers...
 
#ifdef _MANAGED // Works only with managed C++
#pragma once
#pragma managed(push, on)
#include //free
 
void CrtDestroyStatics(void);
 

//
// http://msdn.microsoft.com/en-us/library/system.windows.forms.application.applicationexit.aspx#Y0
//
//C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src\internal.h(41):
typedef void (__cdecl *_PVFV)(void);
WINBASEAPI __out_opt PVOID WINAPI DecodePointer(void *);
extern "C" void * __cdecl _encoded_null();

// crtdll.c
extern "C" _PVFV *__onexitbegin;
extern "C" _PVFV *__onexitend;
 
void CrtDestroyStatics(void)
{
_PVFV * onexitbegin = (_PVFV *)DecodePointer(__onexitbegin);
if (onexitbegin)
{
_PVFV * onexitend = (_PVFV *)DecodePointer(__onexitend);
_PVFV function_to_call = NULL;

/* save the start and end for later comparison */
_PVFV * onexitbegin_saved = onexitbegin;
_PVFV * onexitend_saved = onexitend;

while (1)
{
_PVFV * onexitbegin_new = NULL;
_PVFV * onexitend_new = NULL;

/* find the last valid function pointer to call. */
while (--onexitend >= onexitbegin && (*onexitend == NULL || *onexitend == _encoded_null()))
{
/* keep going backwards. */
}

if (onexitend < onexitbegin)
{
/* there are no more valid entries in the list, we are done. */
break;
}

/* cache the function to call. */
function_to_call = (_PVFV)DecodePointer(*onexitend);

/* mark the function pointer as visited. */
*onexitend = (_PVFV)_encoded_null();

/* call the function, which can eventually change __onexitbegin and __onexitend */
(*function_to_call)();

onexitbegin_new = (_PVFV *)DecodePointer(__onexitbegin);
onexitend_new = (_PVFV *)DecodePointer(__onexitend);

if ( ( onexitbegin_saved != onexitbegin_new ) || ( onexitend_saved != onexitend_new ) )
{
/* reset only if either start or end has changed */
onexitbegin = onexitbegin_saved = onexitbegin_new;
önexitend = onexitend_saved = onexitend_new;
}

break;
}
/*
* free the block holding onexit table to
* avoid memory leaks. Also zero the ptr
* variables so that they are clearly cleaned up.
*/

free ( onexitbegin ) ;

__onexitbegin = __onexitend = (_PVFV *)_encoded_null();
}

} //CrtDestroyStatics
 

#pragma managed(pop)
#endif
AnswerRe: Fix #9 Updated for MSVCRT10memberNicolas Dorier1 May '13 - 21:27 
Thanks, good to know, I updated the article to point to your comment.
QuestionThe string binding is invalidmemberPetro Protsyk25 Sep '12 - 1:59 
Very often when using mixed mode assemblies (the one than contain native and managed code) you will see this exception at application exit (0xC0020001: The string binding is invalid).
 
One of the possible causes is static variable. Visual C++ runtime registers destructor with atexit function. Often such destructor is called after CLR runtime shutdown in DllMain and you will likely to see this exception. I have struggled a lot with such kind of issues while exposing native libraries to .NET.
AnswerRe: The string binding is invalidmemberNicolas Dorier25 Sep '12 - 4:38 
Sound interesting ! Do you have any code sample to reproduce this problem on C++/CLI ?
I will update this article.
GeneralRe: The string binding is invalidmemberPetro Protsyk26 Sep '12 - 5:49 
Create C++/CLI Assembly project. Create two cpp files:
 
File1.cpp:
        #pragma unmanaged
        namespace CodeLib {
    class NativeClass
    {
    public:
        NativeClass() { }
        ~NativeClass() { }
    };
 
    bool NativeMethod()
    {
        static NativeClass staticNativeObject;
        return true;
    }
        }
 
File2.cpp:
namespace CodeLib {
    bool NativeMethod();
    public ref class Class1
    {
    public:
        Class1()
        {
            NativeMethod();
        }
    };
}
 
Create C# Console project, reference the assembly
 
class Program {
    static void Main(string[] args) {
        new CodeLib.Class1();
    }
}
 
You should see application crash at shutdown.
GeneralRe: The string binding is invalidmemberNicolas Dorier26 Sep '12 - 8:02 
Holy cow !
 
Your gotcha : You can't instantiate a static native object with a constructor from a native function or method...
 
A workaround is to use a managed function or method to inititate it.
 
It makes no sense since the native method should not register anything that have to do with the CLR...
 
But then, I found another huge gotcha : You can't instantiate a static native object with destructor from a managed method if you are not in the default app domain !!!
 
By combining these two gotchas you have the following ubber gotcha of death.
 
Ubber gotcha : You can't instantiate a static native object with destructor in any way if your code does not execute in the default app domain !!!!
 
Any workaround you used is welcome, I'm going to update this article.
GeneralRe: The string binding is invalidmemberNicolas Dorier26 Sep '12 - 8:34 
I just updated my article to include your gotcha... I must admit that is the most annoying one. This is completely insane.
GeneralRe: The string binding is invalidmemberlenznerf27 Sep '12 - 4:20 
I just updated my article to include your gotcha...
Thank - you! This is the first place where I found a short explanation for what I was experiencing: "You can't instantiate a static native object with a destructor from a native function or method." Now that's exactly my problem!
 
I must admit that is the most annoying one. This is completely insane.
What was even more annoying to me is the fact that I couldn't find anything about this restriction in any article/blog I read about the migration from native C++ to CLI. In the end I realized that there are potential problems with DLL initialization and static variables, but this one little sentence made it clear to me.
 
There's one question left, however: After fixing the issue with a static variable in my project, I'm not getting the "The string binding is invalid" exception at program shutdown anymore. Can I be sure that I will not encounter the problem again, possibly on a client machine, with another static variable? I.e., is the behavior in question really deterministic, as one MS article suggested? (Can't find the article anymore).
 
PS: Thumbs up for the article! Liked reading it.
I don't understand, most of this stuff... my way with dealing with the unknow is to delete it. Because it is only when things crash that we can learn something new.
Smile | :)
GeneralRe: The string binding is invalidmemberNicolas Dorier27 Sep '12 - 4:32 
Thanks for the thumb Smile | :)
 
From my experience with this problem, it is deterministic.
 
However, like I said in gotcha 8, there is another gotcha when your code in not running in the default app domain. (if it is hosted on IIS for example)
 
It seems this problem does not happen if the static variable is in a class scope. I don't understand why yet.
 
How did you fixed your issue ? I suggested to make the method managed (gotcha 7), but this workaround would not work in another app domain (gotcha 8)
GeneralRe: The string binding is invalid [modified]memberlenznerf27 Sep '12 - 20:29 
What I am doing:
 
We have a native code base. Two executables, a few DLLs and some static libraries. Altogether there are roundabout 20 separate VS-projects. We want to step away from C++/MFC limitations and constraints and open up for .NET.
 
First, I was using IJW-approach, simply compiling everything with /clr (after some minor changes in the project settings). But it turned out - IDNW. I think it can be done, but I need to adjust the __declcpec(dllexport) stuff. And there are some ugly global/static variables in some of the DLLs. Anyway, I decided to start on a smaller base, and tried to only compile one of the executables with /clr. There was one nasty linker issue with a method called "GetClassName()" in a class in one of the DLLs, which was used by the now managed code in the app. Naming it "GetClassName_()" solved the issue. Everything was nice thereafter. Projects compiled, application started - but at shutdown I ran into the "The string binding is invalid" problem. With the help of your article I tracked it down to the following:
SRegistry& SRegistry::Get() 
{
    static SRegistry instance; // SRegistry has a dtor!

    return instance;
}
Just to test if this causes the problem I made it:
SRegistry& SRegistry::Get() 
{
    static SRegistry* instance = NULL;
 
    if (!instance)
        instance = new SRegistry();
    
    return *instance;
}
This worked (= did not lead to the error), but of course leaves me with memory leaks. So I haven't actually fixed the problem - my previous posting was wrong in this respect. But I'm going to make the
static SRegistry* instance
a class member, and add methods to create and destroy the instance (from the managed code).
 
If you are right in your assumption:
Nicolas Dorier wrote:
It seems this problem does not happen if the static variable is in a class scope.

then this should be the solution.
 
Update: The class scope instance did indeed solve the problem - fixed!

modified 28 Sep '12 - 2:59.

GeneralRe: The string binding is invalidmemberNicolas Dorier27 Sep '12 - 22:40 
How do you initialize your static variable in class scope ?
If you do just that in your class
 
static MyNativeClass globalObject;
 
globalObject is never initialized, so constructor is never called.
 
If you do that in some method :
 
MyNativeClass::globalObject = MyNativeClass();
It create an object that destruct at the end of the method scope.
 
I don't code a lot in C++, but I don't manage to use this static variable in my code.
I have this error.
 
Error	1	error LNK2020: unresolved token (0A000399) "public: static class TestCLI::MyNativeClass const TestCLI::MyNativeClass::globalObject" (?globalObject@MyNativeClass@TestCLI@@2V12@B)	C:\Users\NICO\Documents\Visual Studio 2012\Projects\TestCLI\TestCLI\TestCLI.obj	TestCLI
 
for this call
 
MyNativeClass a = MyNativeClass::globalObject;
 
I just want to copy my globalObject.
GeneralRe: The string binding is invalidmemberlenznerf28 Sep '12 - 0:20 
Nicolas Dorier wrote:
How do you initialize your static variable in class scope ?

You really have to instantiate this member explicitly, like so:
// MyClass.h

class MyClass
{
    static int MyStaticMember;
};
// MyClass.cpp

int MyClass::MyStaticMember = 1;
In case you have a getter for the static member - like I have for the single static instance - you can put the static instance in the scope of the getter member function:
// MyClass.h

class MyClass
{
    static int GetStatic();
};
// MyClass.cpp

int MyClass::GetStatic()
{
    static int MyStatic = 0;
 
    return MyStatic;
}
As far as I know, this is common practice, and works well in C++.
 
MyClass::MyStaticMember is like a global variable, and gets instantiated on program startup (ctor called), and destroyed on shutdown (dtor called). I am not quite sure about MyStatic, though: When does it get initialized? On first function call? And destroyed? Like MyClass::MyStaticMember? There must be a difference, as the time of the dtor call caused the original problem.
GeneralRe: The string binding is invalidmemberNicolas Dorier28 Sep '12 - 2:00 
the static variable in method is initialized when the method is called.
 
However, this is interesting that it must exist a difference between the dtor registration of global vs static variable.
 
Not sure yet what it is, anyway thanks I update the article with this piece of code.
GeneralRe: The string binding is invalidmemberTarmik1 Oct '12 - 8:03 
Hi !
 
After playing around, hitting head into walls and etc - I've came across quite nice solution.
 
#pragma managed(push, on) does help, however - you need to locate all instances of static variables, also in functions. Additional interesting thing is that static gets initialized during function call - if you have some func1() function, which is not called normally - everything works fine, till it gets executed - and voila - you have back same problem.
 
One thing which I have noticed what additional trace which came after .exe execution -
The program '[4940] TestVM.exe: Managed' has exited with code 0 (0x0).
The program '[4940] TestVM.exe: Native' has exited with code 0 (0x0).
 
So first managed code goes down - then native. But during native shutdown - destructor for some reason tried to access managed ram space - and voila - you have problem.
I don't know why it tries to access - I guess it has something to do with C# reflection.
 
Anyway - I have overrided to manually call native destructors during managed code shutdown.
I have borrowed some code from crtdll.c.
 
There is still a risk that managed code tries to access native variables during shutdown -
but at least this was not raised as a problem to me. Also it's possible to "upgrade" code to manually check what destructor to invoke during managed and what during native shutdown - but then again - you have those pragma's still. Smile | :)
 
Here is my code snipet:
 
/*
    Typically all custom classes written as globals / static variables must be brought to managed code side - using for example
    code like this:
 
    // Contains static variables, must be known to CLR. (http://www.codeproject.com/Articles/442784/Best-gotchas-of-Cplusplus-CLI)
    #ifdef _MANAGED
        #pragma managed(push, on)
    #endif
    
        static MyClass g_myglobal;
 
    #ifdef _MANAGED
        #pragma managed(pop)
    #endif
 
    same rule applies with functions with static variables.
    
    If you check shutdown sequence of dll/exe - 
        The program '[4940] TestVM.exe: Managed' has exited with code 0 (0x0).
        The program '[4940] TestVM.exe: Native' has exited with code 0 (0x0).
 
    So typically it shutdowns managed code first, and after that native code.
 
    crtdll.c does all the dirty work on executing native destructors on globals.
    
    If native code somehow references managed memory - you'll get
        0xC0020001: The string binding is invalid
    exception during shutdown.
    
    This code tries to make the same trick as crtdll.c, only before that one executes - 
    during managed code shutdown.
    
    crtdll.c will not execute same destructors over again, because they have been cleared from
    crt registration list (__onexitbegin / __onexitend).
*/
 
#ifdef _MANAGED     // Works only with managed C++
#pragma once
#pragma managed(push, on)
#include <stdlib.h>     //free

void CrtDestroyStatics(void);
 

void OnApplicationExit( System::Object^, System::EventArgs^)
{
    CrtDestroyStatics();
}
 
class CRegisterManagedShutdown
{
public:
    CRegisterManagedShutdown()
    {
        System::Windows::Forms::Application::ApplicationExit += gcnew System::EventHandler( &OnApplicationExit );
    }
}gCRegisterManagedShutdown;
 

//
//  http://msdn.microsoft.com/en-us/library/system.windows.forms.application.applicationexit.aspx#Y0
//
//C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src\internal.h(41):
typedef void (__cdecl *_PVFV)(void);
extern "C" void * __cdecl _decode_pointer(void *);
extern "C" void * __cdecl _encoded_null();
 
// crtdll.c
extern "C" _PVFV *__onexitbegin;
extern "C"  _PVFV *__onexitend;
 

 

 
void CrtDestroyStatics(void)
{
    _PVFV * onexitbegin = (_PVFV *)_decode_pointer(__onexitbegin);
    if (onexitbegin)
    {
        _PVFV * onexitend = (_PVFV *)_decode_pointer(__onexitend);
        _PVFV function_to_call = NULL;
 
        /* save the start and end for later comparison */
        _PVFV * onexitbegin_saved = onexitbegin;
        _PVFV * onexitend_saved = onexitend;
 
        while (1)
        {
            _PVFV * onexitbegin_new = NULL;
            _PVFV * onexitend_new = NULL;
 
            /* find the last valid function pointer to call. */
            while (--onexitend >= onexitbegin && (*onexitend == NULL || *onexitend == _encoded_null()))
            {
                /* keep going backwards. */
            }
 
            if (onexitend < onexitbegin)
            {
                /* there are no more valid entries in the list, we are done. */
                break;
            }
 
            /* cache the function to call. */
            function_to_call = (_PVFV)_decode_pointer(*onexitend);
 
            /* mark the function pointer as visited. */
            *onexitend = (_PVFV)_encoded_null();
 
            /* call the function, which can eventually change __onexitbegin and __onexitend */
            (*function_to_call)();
 
            onexitbegin_new = (_PVFV *)_decode_pointer(__onexitbegin);
            onexitend_new = (_PVFV *)_decode_pointer(__onexitend);
 
            if ( ( onexitbegin_saved != onexitbegin_new ) || ( onexitend_saved != onexitend_new ) )
            {
                /* reset only if either start or end has changed */
                onexitbegin = onexitbegin_saved = onexitbegin_new;
                onexitend = onexitend_saved = onexitend_new;
            }
            
            break;
        }
        /*
        * free the block holding onexit table to
        * avoid memory leaks.  Also zero the ptr
        * variables so that they are clearly cleaned up.
        */
 
        free ( onexitbegin ) ;
 
        __onexitbegin = __onexitend = (_PVFV *)_encoded_null();
    }
 
} //CrtDestroyStatics

 
#pragma managed(pop)
#endif
ewfsdasdf

GeneralRe: The string binding is invalidmemberNicolas Dorier1 Oct '12 - 11:40 
That's impressive, thanks, I will upadte this article.
Can you tell us how you get this solution ? Knowing how we should have find this solution ourselves is also very interesting.
 
I will update my article with both : The solution and how you found it, digging in the lower layer is cool.
GeneralRe: The string binding is invalidmemberTarmik9 Oct '12 - 5:47 
Quote:
Can you tell us how you get this solution ?

 
After a long long debug sessions - for every failed case you need to restart visual studio for some reason, and once I had to even log off - because I was not able to kill or terminate zombie process (with admin priviledges) - tries task manager and far manager.
Best way to catch crt shutdown is to place a breakpoint into some class destructor and then from call stack you can find out who called you - that's crt. And set breakpoints into crt itself.
 
After that I've tried to export same symbols as CRT uses, and copy pasted crt terminate code from crt itself - with some of modifications to get it into compilable state.
Smile | :)
 
There is still a risk that at some point of time you will need to have native in destructors, but I did not had such need currently.
ewfsdasdf

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 2 May 2013
Article Copyright 2012 by Nicolas Dorier
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid