Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Last time, we looked at how easy it is to add managed code to your existing C++ application. (Call managed code from your C++ code)
The sample below shows a more substantial C++ program which
- Liberally intermixes both native and managed objects in C++ code for demo purposes.
- Reads the registry recursively using native C++ code to call Windows API
- Stores strings in both a native STL vector and a managed List<String> to demonstrate using these techniques
- Also stores strings in a TreeViewItem
- Uses Windows Presentation Foundation (WPF) to display the registry data in a TreeView
- Subscribes to a WPF event Window->OnLoaded
We’ll start by creating a new C++ project.
File->New->Project->C++ General->Empty Project CppClr
Source->Files->Right-Click->Add New C++ File “CppClr”
Paste in the code below
Now change the project type to be Windows (not Console) and set the new entry point:
// Project->Properties
// set Linker->System->SubSystem Windows (/SUBSYSTEM:WINDOWS)
// Set Linker->Advance->entry point = mymain
Because we’re using WPF, we must set our main thread to be Single Threaded Apartment (STA). To do this for a C++/CLI project, we need to define our own entry point that gets called before the default entry point. One way to do this is to define a custom entry point “MyMain” that
- is marked with the STA attribute
- initializes COM as STA
- then calls mainCRTStartup to initialize the C Runtime library and calls our “main” program
If we comment out the STA attribute line, we get exceptions like:
An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationCore.dll Additional information: The calling thread must be STA, because many UI components require this.
<code>
/*
File->New->Project->C++ General->Empty Project CppClr
Source->Files->Right-Click->Add New C++ File
// Project->Properties
// set Linker->System->SubSystem Windows (/SUBSYSTEM:WINDOWS)
// Set Linker->Advance->entry point = mymain
*/
// CppClr.cpp : main project file.
//#include "stdafx.h"
#include "objbase.h"
#include "atlbase.h"
#include "string"
#include "vector"
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Controls;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
using namespace std;
extern "C" void mainCRTStartup();
public ref class MyWindow : Window
{
String ^_strReg;
int _nTotal;
public:
MyWindow()
{
_strReg = gcnew String(
L"Software\\Microsoft\\VisualStudio"
);
Title = _strReg;
Height = 500;
Width = 500;
// subscribe to managed event
this->Loaded +=
gcnew RoutedEventHandler(
this,
&MyWindow::OnLoaded);
}
void OnLoaded(Object ^Sender, RoutedEventArgs ^e)
{
// create a new treeview
auto tv = gcnew TreeView();
// get the content
List<String^>^ lst = GetRegKeys(_strReg, tv);
// add content to the form
this->Content = tv;
// set the title
this->Title =
String::Format(
"# reg keys {0} {1}",
_nTotal,
_strReg);
}
//GetRegKeys is a native method that
// gets the reg keys, adds them to the passed in
// ItemsControl
// both TreeView and TreeViewItem are ItemsControls
// The parameters are both managed
// Even though not used, return a generic List<String>
// to demonstrate usage.
List<String^>^ GetRegKeys(
// a managed string
String ^ strReg,
ItemsControl ^itemsControl
)
{
// create a vector. Can't mix native/managed
// (can't have a vector of a System.String)
vector<wstring> vecKeys;
// create a generic list<string>
auto lst = gcnew List<String ^>();
// convert the Managed string to an IntPtr
IntPtr wstrReg =
Marshal::StringToHGlobalUni(strReg);
// CRegKey is an ATL class,
// with a dtor to close the key
CRegKey regkey;
DWORD dwResult = regkey.Open(
HKEY_CURRENT_USER,
(WCHAR *) wstrReg.ToPointer(), //IntPtr to WCHAR
KEY_READ);
// don't leak this guy
Marshal::FreeHGlobal(wstrReg);
if (ERROR_SUCCESS == dwResult)
{
for (int nIndex = 0; ; nIndex++)
{
wstring strKey(200, L'\0'); // native string
DWORD nLen = strKey.length();
// enumerate the key
if (ERROR_SUCCESS !=
regkey.EnumKey(nIndex, &strKey[0], &nLen))
{
break;
}
// add the native string to the native vector
vecKeys.push_back(strKey);
// create a managed string and add it to the list
String ^ str = gcnew String(strKey.data());
lst->Add(str);
//create & store item in WPF TreeViewItem
auto tvItem = gcnew TreeViewItem();
itemsControl->Items->Add(tvItem);
auto childLst = GetRegKeys(
strReg + "\\" + str,
tvItem
); // recur
// set the text
tvItem->Header = strReg+"\\"+ str;
tvItem->IsExpanded = true;
}
}
_nTotal += lst->Count;
return lst;
}
};
// we need a new entry point
// so we can set the STAThread attribute on
// the main thread
[System::STAThread]
int mymain() //the new entry point so we can set STAThread
{
//If we need COM, Init COM as single model apartment
//HRESULT hr = CoInitializeEx(0,COINIT_APARTMENTTHREADED);
//Initialize the CRT,
// which also calls our "main" program
mainCRTStartup();
//uninit
//CoUninitialize();
return 0;
}
// this main is called from CRuntime mainCRTStartup
int main()
{
Console::WriteLine(L"Hello World");
// create a new WPF Window
auto win = gcnew MyWindow();
// show it modally
win->ShowDialog();
return 0;
}
</code>