Introduction
The main intention of this page is to build some kind of archive I can use (and share) for future reference. It is a compilation of various
code excerpts in Managed C++ (also called C++/CLI), accumulated during some years of writing software in this language.
Some code excerpts are quite basic, some more complicated. Many topics took quite some research, these
include using delegates, intercepting USB devices, TCP communication, using client database, Shell Extensions and integrating a propriety library.
All the code has been used and experimentally proven to work - of course this doesn't mean that the solutions
are the best ones, so please feel free to comment.
It is certainly not my intention to give a complete rendition on managed C++. Others have done this (and can
do it much better than I) - see the link section at the end of this page
for some interesting artices and websites.
All the code has been written using the Visual Studio Express 2008 IDE. No fancy extensions such as MFC or ATL are used. C++/CLI is
very powerful since it incorporates native C++ and C, thus facilitating integration with embedded systems.
When doing research on C++/CLI, you may encounter the old syntax of C++/CLI which you'll recognise by its multiple underscores at the start of
many keywords, such __property and __gc. This has now been replaced by the new, much easier to read syntax.
Delegate Mechanism
Delegates are really type-safe, object oriented function pointers. For more on
function pointers in C and C++, go to Function Pointers in C and C++.
Since we are in managed C++,
the delegate also needs to take into account that we are working with handles instead of pointers,
the location of which are not fixed.
Methods are a little more complicated than functions. When a method is invoked
on an instance, 2 pointers are required: a pointer to the location
of that method in its class and a (hidden) pointer to the particular instance the method
is called upon. This instance contains the state information that is most
likely needed. Delegates encapsulate both these requirements. Like function pointers, they
reference a method to enable any functionality you want at runtime.
A delegate can be assigned any other delegate where the method signatures match.
The following excerpt uses a delegate in order to access a GUI component from another
thread:
// delegate function to write to textbox
delegate void writeToTB_del(String^ str);
void myForm::WriteToTB(String^ str)
{
if(!myForm->textBox1->InvokeRequired){
myForm->textBox1->Text = str;
}
else{
writeToTB_del^ thisDelegate = gcnew writeToTB_del(this,&myForm::WriteToTB);
this->Invoke(thisDelegate, str);
}
}
If you didn't use a delegate in the above situation, you would get an error claiming something like Component accessed
from a thread other than the one that created it.
You also use delegates when a class needs to access a form that already includes your class - which would otherwise cause
circular referencing:
////////////
// Main.h //
////////////
#include "Other.h"
...
namespace ns
{
class Main
{
private:
Other^ other;
String^ HandleFunction(String^ str, int mode);
...
}
}
//////////////
// Main.cpp //
//////////////
...
void StartOther()
{
other = gcnew Other();
other->E_other += gcnew EH_other(this, &Main::HandleFunction);
}
String ^ Main::HandleFunction(String ^str, int mode)
{
...
this->AppendFormTextbox(str); //access a form control
}
delegate void threadTask(String^ str);
void Main::AppendFormTextbox(String^ str)
{
if(this->someTextbox->InvokeRequired == false){
this->someTextbox->AppendText(str);
}
else{
threadTask^ thisDelegate = gcnew treadTask(this, &Main::AppendFormTextbox);
this->invoke(thisDelegate,str);
}
}
/////////////
// Other.h //
/////////////
namespace ns
{
public delegate String^ EH_other(String^ str, int i);
class cl
{
public:
event EH_other^ other;
...
}
}
///////////////
// Other.cpp //
///////////////
...
{
...
E_other("blah blah", 2);
...
}
...
Main can include Other, but to avoid circular referencing, Other cannot include Main.
An extra delegate mechanism (like the one described
in the first part) is included in case a form element is accessed from another thread.
back to index
USB Mass Storage Device Capture
When an application has to capture a USB Mass Storage Device it also needs to surpress the notification
window that the operating system pulls up to notify you that a USB device has been plugged in. Cancelling
this window uses the Windows QueryCancelAutoPlay message.
Capturing USB devices is handled at a lower level, so the software needs to override the
Windows Procedure. The links and references section
list the details on the book Programming Windows(5th edition), the universally claimed authority
on the subject of windows programming.
using namespace System::Security::Permissions; // to override WndProc()
// ------------------------------------------------------------------------------------------
// METHOD: WndProc
// Overrides Control::WndProc(Message)
// The WindowProc function is an application defined function that processes messages sent to
// a window. The WNDPROC type defines a pointer to this callback function. WindowProc is a
// placeholder for the application defined function name.
// ------------------------------------------------------------------------------------------
[SecurityPermission(SecurityAction::Demand, Flags=SecurityPermissionFlag::UnmanagedCode)]
void ThisForm::WndProc( Message% m)
{
//Call the base first, otherwise the values you set later will be lost
__super::WndProc(m);
//find these definitions in dbt.h
const int DBT_DEVICEARRIVAL = 0x8000; // New device detected
const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // Device Removed
const int DBT_DEVTYP_VOLUME = 0x00000002; // Device is a volume
// Register WindowMessage to make sure Windows sends the "QueryCancelAutoPlay" message to the form
// Application window needs to be in the foreground to receive this message!!
if (queryCancelAutoPlay == 0){
queryCancelAutoPlay = RegisterWindowMessage("QueryCancelAutoPlay");
}
//if the window message id equals the QueryCancelAutoPlay message id
if ((int)m.Msg == queryCancelAutoPlay){
m.Result = (IntPtr)1;
}
switch (m.Msg){
case WM_DEVICECHANGE:
switch(m.WParam.ToInt32()){
case DBT_DEVICEARRIVAL:
{
int devType = Marshal::ReadInt32(m.LParam,4); // read from unmanaged memory
if(devType == DBT_DEVTYP_VOLUME){
DEV_BROADCAST_VOLUME vol;
vol = (DEV_BROADCAST_VOLUME)Marshal::PtrToStructure(m.LParam,VI_MAIN::Form1::DEV_BROADCAST_VOLUME::typeid);
//
// DO YOUR STUFF HERE
//
} // END if(devType == DBT_DEVTYP_VOLUME)
} // END case DBT_DEVICEARRIVAL:
break;
case DBT_DEVICEREMOVECOMPLETE:
//Debug::WriteLine("Device Removal detected");
break;
} // END SWITCH
break;
} // END SWITCH
}
back to index
Spawning your default email client with attachments
To spawn your default email client, a simple line of code like the following can do the deed:
ShellExecute( hwndYourWindow,
"open",
"mailto:user@domain.com?subject=Subject Here",
NULL, NULL, SW_SHOW );
But opening the email client with attachments ready to go is more difficult.
We will use a Shell Extension - which is a COM object that adds
some kind of functionality to the Windows shell (Explorer).
You can observe what we are trying to do here by
right clicking on a folder and selecting Send to → Mail recipient.
See the following references for more clarification on the code to follow:
There are two major techniques involved here: One involves the use of a COM object, which is initialised with
CoInitialize(NULL)
The other is simulating a drop to 'drop' the attachments into the default email client window.
To get a feel of what COM is about I recommend Michael Dunn's article
Introduction to COM - What It Is and How to Use It. It is concise and very clear - even a 'clodger' like me can understand it.
I take no credit for this code, I just managed to make it work for C++/CLI - most of the credit goes to long-time MS employee
Raymond Chen. His blogs
at Old new thing, the source of most links above, gives a fascinating insight
into windows programming. Old new thing is a subsidiary (if that's the right word) of the Microsoft blog site ,
where many of its employees blog to a public audience.
#include <windows.h>
#include <shlobj.h> // DROPFILES
#include <stdlib.h> // _dupenv_s
using namespace System;
// This simple function takes a path and gets a shell UI object from it.
// We convert the path to a pidl with SHParseDisplayName, then bind to the
// pidl's parent with SHBindToParent, then ask the parent for the UI object
// of the child with IShellFolder::GetUIObjectOf
HRESULT GetUIObjectOfFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void **ppv)
{
*ppv = NULL;
HRESULT hr;
LPITEMIDLIST pidl;
SFGAOF sfgao;
// Translates a Shell namespace object's display name into an item identifier list
// and returns the attributes of the object. This function is the preferred method
// to convert a string to a PIDL.
hr = SHParseDisplayName(pszPath, NULL, &pidl,SFGAO_CANCOPY,&sfgao);
if(SUCCEEDED(hr)){
IShellFolder *psf;
LPCITEMIDLIST pidlChild;
if (SUCCEEDED(hr = SHBindToParent(pidl, IID_IShellFolder,(void**)&psf, &pidlChild))) {
hr = psf->GetUIObjectOf(hwnd, 1, &pidlChild, riid, NULL, ppv);
psf->Release();
}
CoTaskMemFree(pidl);
}
return hr;
}
// This is the method that actually gets called with the path to the filename to attach
int OpenClientWithAttachments(System::String^ pathToFile)
{
// convert managed string to wchar_t*
// ref http://msdn.microsoft.com/en-us/library/d1ae6tz5%28VS.80%29.aspx
// Pin memory so GC can't move it while native function is called
pin_ptr<const wchar_t> wch = PtrToStringChars(pathToFile);
const wchar_t* attachmentsStr = wch;
// convert the path to the mapi file also
pin_ptr<const wchar_t> wch = PtrToStringChars(GetUserEmailSendToPath());
const wchar_t* mapiPath = wch;
if (SUCCEEDED(CoInitialize(NULL))) {
{ // don't remove scope brackets!!
// IDataObject: Enables data transfer and notification of changes in data
// Any object that can receive data, calls the methods in the IDataObject interface.
IDataObject *pdto; // attachment object
if (SUCCEEDED(GetUIObjectOfFile(NULL, attachmentsStr, IID_IDataObject, (void**)&pdto))) {
IDropTarget *pdt; // target email client application
if (SUCCEEDED(GetUIObjectOfFile(NULL, mapiPath, IID_IDropTarget, (void**)&pdt))) {
POINTL pt = { 0, 0 };
DWORD dwEffect = DROPEFFECT_COPY | DROPEFFECT_LINK;
if (SUCCEEDED(pdt->DragEnter(pdto, MK_LBUTTON, pt, &dwEffect))) {
dwEffect &= DROPEFFECT_COPY | DROPEFFECT_LINK;
if (dwEffect) {
// DROPEFFECT dwEffect return should be 1 after the following line
pdt->Drop(pdto, MK_LBUTTON, pt, &dwEffect);
}
else {
pdt->DragLeave();
}
}
pdt->Release();
}
pdto->Release();
}
} // ensure p is destructed before the CoUninit
CoUninitialize();
}
return 0;
}
#undef GetEnvironmentVariable // otherwise the windows API maps this to GetEnvironmentVariableW()
String^ GetUserEmailSendToPath()
{
String^ tagPath = "";
// TODO: read http://blogs.msdn.com/b/oldnewthing/archive/2003/11/03/55532.aspx
// suggests using SHGetSpecialFolderLocation() or Environment::GetFolderPath()
String^ userProfilePath = System::Environment::GetEnvironmentVariable("USERPROFILE");
// get OS
String^ thisVersion = System::Environment::OSVersion->ToString();
if(thisVersion->Contains("XP")){
tagPath = "\\SendTo\\Mail Recipient.MAPIMail";
}
else if(thisVersion->Contains("NT")){ // vista and windows 7
tagPath = "\\AppData\\Roaming\\Microsoft\\Windows\\SendTo\\Mail Recipient.MAPIMail";
}
else{
return String::Empty;
}
String^ fullPath = String::Concat(userProfilePath, tagPath);
return fullPath;
}
back to index
Integrating a client database using OleDbDataAdaptor
A client database is a database that is implemented on the client's computer, not on a
server. The app comes integrated with a local database included. There are various ways of
doing this, but I opted to use an Access database since, being MS, it integrates well with Visual
Studio. There's ample documentation about how to do this. Before integrating MS Access into
Visual Studio, make sure you read about the database engine being used: The
MS Jet Engine. To access the data gotten from several rows in a table:
DataSet^ ds = retrieveDS(query); // local method that retreives data from a table
if(ds != nullptr && ds->Tables["YourTable"]->Rows->Count != 0){
DataRowCollection^ dr = ds->Tables["YourTable"]->Rows;
for each(DataRow ^adr in dr){
String^ str = adr[0]->ToString();
SomeObject^ thisObject = (SomeObject)adr[1];
...
}
}
When retrieving data from just one row, you don't need a DataRowCollection:
DataSet^ ds = retrieveDS(query); // local method that retreives data from a table
if(ds != nullptr && ds->Tables["YourTable"]->Rows->Count != 0){
DataRow^ dr = ds->Tables["YourTable"]->Rows[0];
// make sure it isn't empty as would be the case when starting new DB
if(dr[0]!= DBNull::Value){
String^ str = adr[0]->ToString();
SomeObject^ thisObject = (SomeObject)adr[1];
...
}
}
public:
static DataSet^ retrieveDS(String^ strAccessSelect, String^ tableName)
{
String ^strAccessConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=<path to database file>";
DataSet^ ds = gcnew DataSet();
ds->Tables->Add(tableName);
// Create Access objects:
OleDbConnection ^accessConn = gcnew OleDbConnection(strAccessConn);
OleDbCommand ^accessCommand = gcnew OleDbCommand(strAccessSelect,accessConn);
OleDbDataAdapter ^dataAdapter = gcnew OleDbDataAdapter(accessCommand);
try{
accessConn->Open();
dataAdapter->Fill(ds,tableName);
}
catch(Exception ^e){
Debug::WriteLine(e->Message);
return nullptr;
}
finally{
accessConn->Close();
}
return ds;
}
back to index
Integrating a client database using OleDbDataReader
Here's a code excerpt using a OleDbDataReader, it also shows how to reformat a DateTime:
String ^ strConnection = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=yourPath\\yourDatabase.mdb";
String ^ strQuery = "SELECT Last_Name, Initials, First_Name, Mobile, Email, DOB, Address FROM VI_Patient";
oleDbConnection = gcnew OleDbConnection(strConnection);
//sqlConnection = gcnew SqlConnection(strConnection);
oleDbConnection->Open();
//sqlConnection->Open();
oleDbCommand = gcnew OleDbCommand(strQuery, oleDbConnection);
//sqlCommand = gcnew SqlCommand(strQuery, sqlConnection);
oleDbDataReader = oleDbCommand->ExecuteReader();
//sqlReader = sqlCommand->ExecuteReader();
//while (sqlReader->Read()){
while(oleDbDataReader->Read()){
//Reformat dateTime
String ^ thisDate = String::Empty;
if (oleDbDataReader->GetFieldType(5)->ToString() =="System.DateTime"){
if (oleDbDataReader[5] != System::DBNull::Value){
DateTime^ dt = (DateTime^)oleDbDataReader[5];
thisDate = dt->ToString("dd MMM yyyy");
}
}
array<System::Object^>^ row = { oleDbDataReader[0], oleDbDataReader[1], oleDbDataReader[2],
oleDbDataReader[3], oleDbDataReader[4], thisDate, oleDbDataReader[6] };
this->dataGridView1->Rows->Add(row);
}
back to index
The 'Microsoft.Jet.OLEDB.4.0' provider is not registered on the local machine ERROR
This thread provides solutions for the said error. One solution that addresses the error in relation to the
Visual Studio Express edition has proven to work in my case:
There is no 64 bit Jet version of the database engine.
For those people with a Visual Studio Express Edition that want to compile in x86
(since VS Express doesn't expose the option) do the following:
- In Windows Explorer, right-click your project file (*.csproj, *.vbproj, etc..)
and Open With Notepad (the project file is an XML document)
- Find the section <PropertyGroup Condition=" '$(Configuration)|$(Platform)'
== 'Release|AnyCPU' "> and <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- Find (or add) the element <PlatformTarget>x86</PlatformTarget>
- Save and close Notepad
- Reload your project and compile
back to index
Using the DateTime type
Get the system's DateTime:
//get computer's time
System::DateTime now = System::DateTime::Now;
An example showing how to reformat a DateTime once you get it from a DataSet's DataRow:
DataRowCollection ^dra1 = ds1->Tables["aTable"]->Rows; //get the row
for each(DataRow ^dr1 in dra1){
String^ thisDate = ((DateTime)dr1[0]).ToString("dd MMM yyyy");
...
}
And to convert from a string to a DateTime:
DateTime^ thisDate = gcnew DateTime();
thisDate = Convert::ToDateTime(dr[0]);
Alternatively, you could use Split() and Convert::ToInt32() to convert text fields to ints, and use the
values in the DateTime constructor:
DateTime^ dt = gcnew DateTime(yearField, monthField, dayField, hourField, minuteField, secondField);
To use DateTime in a query (in this case for an Access database):
// use datetime in a query (for Access database) - note the hash tags
String^ query = "SELECT MAX(someColumn) From someTable WHERE someField = "
+ aValue
+ " AND someDateTimeField = #"
+ someDateTime->ToString("MM'/'dd'/'yy")
+ "#";
If you can, use a DateTimePicker. It simplifies things and ensures you can't get situations where
dates don't exist or are wrongly formatted.
Converting a DateTime to a String:
String ^dob = dateTimePicker1->Value.ToString("dd/MM/yyyy"); // D.O.B.
Getting the DateTime from the dateTimePicker:
DateTime dob = patient->dob_dt = dateTimePicker1->Value;
You may want to have the option of a blank value in the DateTimePicker because the user may
choose not to select a datetime. The following code could be used in a Checkbox event:
// blank datetimepicker (checkbox ticked event)
dateTimePicker1->Format = DateTimePickerFormat::Custom;
dateTimePicker1->CustomFormat = " ";
// revert back to format (checkbox not ticked event)
dateTimePicker1->Format = DateTimePickerFormat::Custom;
dateTimePicker1->CustomFormat = "dd/MMM/yyyy";
back to index
Unlocking an image from its picturebox
When an image is allocated to a picturebox, the component tends to lock this image resulting
in an error when trying to erase this image from the pictureBox. A way around this is to create
a temporary copy:
// using automatic storage semantics
Image^ i = Image::FromFile("pathToImage"); //open the file
Image^ t = gcnew Bitmap(i->Width, i->Height); //create temporary
Graphics^ g = Graphics::FromImage(t); //get graphics
g->DrawImage(i,0,0,i->Width, i->Height);
i->~Image();
pb->Image = t;
pb->SizeMode = PictureBoxSizeMode::StretchImage; //adjust image size to picturebox
back to index
Write metadata to a JPEG file
Following example writes metadata to the UserComments,
DateTimeOriginal and CreateDate filetags in the JPEG (EXIF) Header.
Refer to reading and writing metadata and
propertyitem documentation
public:
static bool WriteMetaDataToJpeg(DateTime someDateTime, String^ pathFromImg, String^ pathToImg)
{
Image^ im = Image::FromFile(pathFromImg); //open the file
// WRITE DATA TO USERCOMMENT DATATAG
// ---------------------------------
int userCommentsId = 0x9286;
String^ imgData = //"ASCII Your Heading Text: "
+ " someName=" + someValue
+ " | anotherName=" + anotherValue
...;
PropertyItem^ pi = im->PropertyItems[0];
SetProperty(pi, userCommentsId, imgData);
im->SetPropertyItem(pi);
// WRITE DATE TO DATETIMEORIGINAL FILETAG
// --------------------------------------
// reformat date
String ^ sDate = someDateTime->ToString("yyyy:MM:dd HH:mm:ss");
int datetimeOriginalId = 0x9003;
PropertyItem^ pi2 = im->PropertyItems[0];
SetProperty(pi2, datetimeOriginalId, sDate);
im->SetPropertyItem(pi2);
// WRITE DATE TO CREATEDATE FILETAG
// --------------------------------
int createDateId = 0x9004;
PropertyItem^ pi3 = im->PropertyItems[0];
SetProperty(pi3, createDateId, sDate);
im->SetPropertyItem(pi3);
// SAVE FILE
// ---------
try{
im->Save(pathToImg);
}
catch (Exception^ e){
Debug::WriteLine(e->Message);
return false;
}
im->~Image();
return true;
}
// for ASCII property items only
// Exif 2.2 requires that ASCII property items terminate with a null (0x00).
private:
static void SetProperty(Imaging::PropertyItem ^prop, int iId, String ^ sTxt)
{
int iLen = sTxt->Length + 1;
array<unsigned char,1>^ c = gcnew array<unsigned char,1>(iLen);
for (int i = 0; i < iLen - 1; i++){
c[i] = sTxt[i];
}
c[iLen -1] = 0x00;
prop->Id = iId;
prop->Type = 2;
prop->Value = c;
prop->Len = iLen;
}
back to index
Reading metadata from a JPEG file
This example reads the UserComments field. To read metadata from a JPEG, the GetProperty() method is used:
public:
static String^ ReadMetaDataFromJpeg(String^ pathToImg)
{
String^ s;
int userCommentsId = 0x9286;
Image^ im = Image::FromFile(pathToImg);
try{
PropertyItem^ thisPropertyItem = im->GetPropertyItem(userCommentsId);
int iLen = thisPropertyItem->Len;
array^ c = gcnew array(iLen);
c = thisPropertyItem->Value;
System::Text::Encoding^ enc = System::Text::Encoding::UTF8;
s = enc->GetString(c);
return s;
}
catch (Exception^ e){
return String::Empty;
}
}
back to index
The Singleton
The Singleton is one of the simplest design patterns based on the
mathematical concept of a Singleton (a set with only one element).
It ensures that only one instance
of a class is realised in order to coordinate actions accross a system.
For that reason it is often frowned upon as being ... GASP ...
anti Object Oriented! Nevertheless I've found it quite useful in situations
where only one object is needed - for example you may want to establish just
one object that implements networking functionality for other objects to
access. The Singleton structure works as follows:
//File1.h
public ref class File1
{
public:
static File1 file1Instance;
static File1^ getInstance();
...
}
//File1.cpp
#include "Form1.h" // can do
File1^ getInstance();
{
return %file1Instance;
}
//Form1.h
#include "File1.h"
public ref class Form1 : ...
{
public:
File1^ file1;
...
}
// Form1.cpp
...
file1 = File1->getInstance();
file1-> ...
...
back to index
Passing a form's handle
This is just an example to show that a form can simply pass
a handle to itself for the called object to access:
// Form1
...
Form2 ^ form2 = gcnew Form2(this);
form2->Show();
...
// Form2
private Form1^ pForm1;
Form2(Form1^ pf)
{
pForm1 = pf;
}
SomeMethod()
{
pForm1-> ...
...
}
back to index
Passing a pointer by reference
The argument in a function such as
fn(XmlDocument ^xdoc)
will result in the fn doing its thing to a local copy of xdoc. However,
if you want to do things to the actual object, you need to pass a reference to a reference.
To do this use the following syntax:
fn(XmlDocument %^xdoc)
The calling syntax stays the same.
In standard C++, to modify a pointer passed as an argument, the function would be
fn(SomeObject **pp)
and the caller would use
fn(&pp)
back to index
Printing formatted strings
While you can print formatted strings on the Console, such as
Console::WriteLine(A->ToString("R", ",", "{", "},\n", "}\n"));
there is no such facility for printing to the Debug output using Debug::WriteLine(),
instead use Debug::Print(), e.g.
Debug::Print("contents of {0}: ", filename);
back to index
Deploying a Visual Studio Express Project
I had quite a few issues with deploying a visual Studio 2008 project so my application could be
installed on all PC systems from the lowly XP 32 bit SP3 to the Windows 7 64 bit. Finally had
success after porting the whole project to Visual Studio 2010 and deploying it with
Framework 4.0 (which has a 40 MB footprint) and the VS C++ Redistributable Package.
These 2 packages together with a Propriety DLL installer were successfully integrated
into one installer package, NSIS.
A side note on Visual Studio Express 2010: IT HAS NO INTELLISENSE! So if you're used
to working with intellisense it comes as quite a shock to realise how much you actually
rely on it.
NSIS (Nullsoft Scriptable Install System)
is a professional open source system to create Windows installers. At first, its script language
seems a bit tedius, but it is very powerful and there are many examples on the net. I plan to
cover NSIS in a separate chapter...
back to index
Links and References
My main source of information is the
MSDN Website. The Library section is a good API reference.
Programming Windows(5th edition) by Charles Petzold (ISBN 13: 978-1-57231-995-0) is the universal bible on the fascinating topic of windows
programming. Almost 1500 pages, but surprisingly lucid.
The Functionx website has a series of practical tutorials.
The freely available book Expert C++/CLI
is a good read, especially if you want to delve a little deeper into the subject.
Manning's ebook C++/CLI in Action used to be freely available, but not anymore it seems.