I'm running a short experiment to see how difficult it would be to display some WPF windows from a native C++ (MFC) application. After seeing this and later this, I've decided to use a C++/CLI wrapper DLL around a .NET class library .DLL.
[native C++ .exe] -> [C++/CLI wrapper .lib] -> [C# Class Library .dll]
This, supposedly, will allow my native C++ application to remain unmanaged and I will not need to use the /clr compile option.
I then got my sandbox code working after jumping some hurdles needed to display the window and start the dispatcher. A native console application begins, displays a WPF window allowing the user to enter a number, and on closing the window the number is returned and written to the console output.
Now, I've used CLI in the past for the reverse (calling unmanaged code from managed) but going in this direction has left me a little puzzled.
After reading some material on how CLR is intialized for a .NET executable, such as the discussion about the CLR "process" in this SO answer's comments and how the Execution Engine is boot strapped, I'm wondering when and how all of this happens in a situation where the main executable is not managed or associated with .NET? Also, what is the "lifetime" of the CLR's components - if I make a single method call does the garbage collector and JIT compiler get initialized and destroyed? Do they live with my process until it exits? Does this affect my native application in any way?
The code for my test project looks something like the following, and works fine as far as I can tell.
ConsoleApplication (.exe)
CLIWrapper.lib added to "AdditionalDependencies"
mainfile.cpp
#include "CLIInterface.h"
int main()
{
APIWrapper wrapper;
int rint = wrapper.GetAnInteger();
char buffer[100];
snprintf(buffer, sizeof(buffer), "Value: %d\n", rint);
std::cout << buffer;
}
CLIInterface.h
class APIWrapperPrivate;
class APIWrapper
{
private:
APIWrapperPrivate* _private;
public:
APIWrapper();
~APIWrapper();
int GetAnInteger();
};
CLIWrapper (.dll)
CLIWrapper.cpp
#include <msclr\auto_gcroot.h>
#using "..\CSharpLibrary\bin\Debug\CSharpLibrary.dll"
using namespace System;
using namespace System::Runtime::InteropServices;
class APIWrapperPrivate
{
public:
msclr::auto_gcroot<CSharpLibrary::TheCSharpInterface^> theCSharpInterface;
};
class __declspec(dllexport) APIWrapper
{
private:
APIWrapperPrivate* _private;
public:
APIWrapper()
{
_private = new APIWrapperPrivate();
_private->theCSharpInterface = gcnew CSharpLibrary ::TheCSharpInterface();
}
~APIWrapper()
{
delete _private;
}
int GetAnInteger()
{
return _private->theCSharpInterface->GetAnItegerValue();
}
};
CSharpLibrary (.dll)
TheCSharpInterface.cs
public class TheCSharpInterface
{
public int GetAnItegerValue()
{
int anIntegerValue;
ThreadStart ThreadProc = new ThreadStart(() =>
{
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
SomeWindow window = new SomeWindow();
window.Closed += (sender, e) =>
{
anIntegerValue = window.Power;
Dispatcher.CurrentDispatcher.InvokeShutdown();
};
window.Show();
Dispatcher.Run();
});
Thread t = new Thread(ThreadProc);
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
t.Join();
return this.anIntegerValue;
}
}
SomeWindow.xaml
A wpf window with an integer slider control