.NET allows you to expose components as COM and consume them from unmanaged code. There are many references on how to this (and you can only start with MSDN), and I will not talk about that part. What I want to explain here is something different. Suppose you have this interface:

[Guid("2F8433FE-4771-4037-B6B2-ED5F6585ED04")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IAccounts
{
      [DispId(1)]
      string[] GetUsers();
}

Method GetUsers() returns an array on string representing the user names. But what if you also wanted the user passwords or addresses? Since this is exposed as COM, you cannot return an array of User. But you can return multiple arrays of string. So, how would you deal with out string[]? This is what I want to show you in this tutorial.

This is a .NET interface exposed to COM. It has two methods, GetUsers() that returns an array of string representing user names, and GetUsers2() that returns an array of strings as an output parameters and a bool as return type, indicating whether any user was found.

namespace SampleLibrary
{
   [Guid("2F8433FE-4771-4037-B6B2-ED5F6585ED04")]
   [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
   public interface IAccounts
   {
      [DispId(1)]
      string[] GetUsers();

      [DispId(2)]
      bool GetUsers2(out string [] users);
   }
}

And this is the implementation:

namespace SampleLibrary
{
   [Guid("C4713144-5D29-4c65-BF9C-188B1B7CD2B6")]
   [ClassInterface(ClassInterfaceType.None)]
   [ProgId("SampleLibrary.DataQuery")]
   public class Accounts : IAccounts
   {
      List< string > m_users;

      public Accounts()
      {
         m_users = new List< string > {
            "marius.bancila",
            "john.doe",
            "anna.kepler"
         };
      }

      #region IDataQuery Members

      public string[] GetUsers()
      {
         return m_users.ToArray();
      }

      public bool GetUsers2(out string[] users)
      {
         users = m_users.ToArray();

         return users.Length > 0;
      }

      #endregion
   }
}

Note: If you are trying this example make sure you set the ComVisible attribute to true, either for each type or per assembly (in AssemblyInfo.cs)

[assembly: ComVisible(true)]

Second, you have to check the “Register for COM interop” setting in the Build page of the project properties.

The first thing to do in C++ is importing the .TLB file that was generated by regasm.exe.

#import "SampleLibrary.tlb"
using namespace SampleLibrary;

If we look in the .TLB file, we can see how the IAccounts interface looks like:

struct __declspec(uuid("2f8433fe-4771-4037-b6b2-ed5f6585ed04"))
IAccounts : IDispatch
{
    //
    // Wrapper methods for error-handling
    //

    // Methods:
    SAFEARRAY * GetUsers ( );
    VARIANT_BOOL GetUsers2 (
        SAFEARRAY * * users );
};

The following C++ functions, GetUsers1() retrieves the users users list using method GetUsers() from IAccounts. It puts the users in a CStringArray (notice that this container does not have an assignment operator, so the only way to return such an array is with a reference in the parameters list).

void GetUsers1(CStringArray& arrUsers)
{
   IAccountsPtr pAccounts(__uuidof(Accounts));

   SAFEARRAY* sarrUsers = pAccounts->GetUsers();

   _variant_t varUsers;
   varUsers.parray = sarrUsers;
   varUsers.vt = VT_ARRAY | VT_BSTR;

   UnpackBstrArray(varUsers, arrUsers);
   SafeArrayDestroy(sarrUsers);

   pAccounts->Release();
}

UnpackBstrArray() is a function (see below) that extracts the elements of a SAFEARRAY and adds them to a CStringArray.

Function GetUsers2() uses the second method, GetUsers2() from IAccounts. This needs the address of a pointer to a SAFEARRAY (i.e. SAFEARRAY**) that will hold the values returned by the COM method. This time we have to create an empty SAFEARRAY and then pass its address to the COM method. The rest is similar to the previous case.

void GetUsers2(CStringArray& arrUsers)
{
   IAccountsPtr pAccounts(__uuidof(Accounts));

   SAFEARRAYBOUND aDim[1];
   aDim[0].lLbound = 0;
   aDim[0].cElements = 0;

   SAFEARRAY* sarrUsers = SafeArrayCreate(VT_BSTR, 1, aDim);

   VARIANT_BOOL ret = pAccounts->GetUsers2(&sarrUsers);
   if(ret != VARIANT_FALSE)
   {
      _variant_t varUsers;
      varUsers.parray = sarrUsers;
      varUsers.vt = VT_ARRAY | VT_BSTR;
      UnpackBstrArray(varUsers, arrUsers);
   }

   SafeArrayDestroy(sarrUsers);

   pAccounts->Release();
}

The helper method UnpackBstrArray() used previous looks like this:

void UnpackBstrArrayHelper(VARIANT* pvarArrayIn, CStringArray* pstrarrValues)
{
   if (!pstrarrValues || !pvarArrayIn || pvarArrayIn->vt == VT_EMPTY)
      return;

   pstrarrValues->RemoveAll();

   VARIANT* pvarArray = pvarArrayIn;
   SAFEARRAY* parrValues = NULL;

   SAFEARRAYBOUND arrayBounds[1];
   arrayBounds[0].lLbound = 0;
   arrayBounds[0].cElements = 0;

   if((pvarArray->vt & (VT_VARIANT|VT_BYREF|VT_ARRAY)) == (VT_VARIANT|VT_BYREF) &&
      NULL != pvarArray->pvarVal &&
      (pvarArray->pvarVal->vt & VT_ARRAY))
   {
      pvarArray = pvarArray->pvarVal;
   }

   if (pvarArray->vt & VT_ARRAY)
   {
      if (VT_BYREF & pvarArray->vt)
         parrValues = *pvarArray->pparray;
      else
         parrValues = pvarArray->parray;
   }
   else
      return;

   if (parrValues != NULL)
   {
      HRESULT hr = SafeArrayGetLBound(parrValues, 1, &arrayBounds[0].lLbound);
      hr = SafeArrayGetUBound(parrValues, 1, (long*)&arrayBounds[0].cElements);
      arrayBounds[0].cElements -= arrayBounds[0].lLbound;
      arrayBounds[0].cElements += 1;
   }

   if (arrayBounds[0].cElements > 0)
   {
      for (ULONG i = 0; i < arrayBounds[0].cElements; i++)
      {
         LONG lIndex = (LONG)i;
         CString strValue = _T("");

         VARTYPE vType;
         BSTR bstrItem;

         ::SafeArrayGetVartype(parrValues, &vType);
         HRESULT hr = ::SafeArrayGetElement(parrValues, &lIndex, &bstrItem);

         if(SUCCEEDED(hr))
         {
            switch(vType)
            {
            case VT_BSTR:
               strValue = (LPCTSTR)bstrItem;
               break;
            }

            ::SysFreeString(bstrItem);
         }

         pstrarrValues->Add(strValue);
      }
   }
}

void UnpackBstrArray( const _variant_t &var, CStringArray &strarrValues  )
{
   UnpackBstrArrayHelper( &(VARIANT)const_cast< _variant_t & >(var), &strarrValues );
}

Attached you can find a demo project (C# and C++) with the complete example show in this tutorial.

output SAFEARRAY** example (319)

, , , , , Hits for this post: 19450 .

MSDN Code Gallery made available an update for the Windows API Code Pack for .NET Framework 3.5 (or above), a library that provides access to some Window 7 features and some existing features in previous operating systems. It includes:

  • Windows 7 Taskbar Jump Lists, Icon Overlay, Progress Bar, Tabbed Thumbnails, and Thumbnail Toolbars.
  • Known Folders, Windows 7 Libraries, non-file system containers, and a hierarchy of Shell Namespace entities.
  • Windows 7 Explorer Browser Control.
  • Shell property system.
  • Windows Vista and Windows 7 Common File Dialogs, including custom controls.
  • Windows Vista and Windows 7 Task Dialogs.
  • Direct3D 11.0, Direct3D 10.1/10.0, DXGI 1.0/1.1, Direct2D 1.0, DirectWrite, Windows Imaging Component (WIC) APIs. (DirectWrite and WIC have partial support)
  • Sensor Platform APIs
  • Extended Linguistic Services APIs
  • Power Management APIs
  • Application Restart and Recovery APIs
  • Network List Manager APIs
  • ommand Link control and System defined Shell icons.

The requirements for using this library are:

  • .NET Framework 3.5
  • Windows 7 RC (some features work on previous operating systems too)
  • DirectX features have dependency on Windows SDK for Windows 7 RC and March 2009 release of DirectX SDK

You can download the Code Pack library from here.

, , , Hits for this post: 12868 .

Have you tried to print colored text in a console or put the text at a specific position? You know then that STL or CRT does not provide that functionality.
Windows API however, contains a set of functions for working with console windows. The list of these functions is available in MSDN.

However, to simplify the use of these functions I have created a template, wrapper class that provides methods and overloaded operators to:

  • change the color for text and background
  • set the cursor position
  • write to the console
  • read from the console

The class is called basic_console, and is a template class. There are two specializations, one for ANSI, called console, and one for UNICODE, called wconsole.

typedef basic_console< char, std::char_traits< char >> console;
typedef basic_console< wchar_t, std::char_traits< wchar_t >> wconsole;

You can change the position on both horizontal and vertical or on only one of the two:

   void GoTo(int x, int y)
   void GoToX(int x)
   void GoToY(int y)
   console_pos GetConsolePos()

There is also an overloaded operator for changing the position, specified with a console_pos structure.

	_Myt& operator<<(const console_pos& pos)

For the color, you can change the text and background color, using one of these functions:

   void SetForeColor(text_color::Color crText)
   void SetBackColor(bg_color::Color crBack)
   void SetColors(text_color::Color crText, bg_color::Color crBack)
   void SetColors(WORD colors)
   void SetColors(WORD crText, WORD crBack)
   void UseDefaultColors()

   _Myt& operator<<(const console_colors& col)

Number of colors are limited, but we can combine them to produce more. Windows defines separate flags for the text and background colors.
I have created two classes text_color and back_color to define mnemonics for colors based on these flags.

class text_color
{
public:
	enum Color
	{
		Black = 0,
		White = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED,
		Blue = FOREGROUND_BLUE,
		Green = FOREGROUND_GREEN,
		Red = FOREGROUND_RED,
		Yellow = FOREGROUND_RED | FOREGROUND_GREEN,
		Magenta = FOREGROUND_RED | FOREGROUND_BLUE,
		LightWhite = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY,
		LightBlue = FOREGROUND_BLUE | FOREGROUND_INTENSITY,
		LightGreen = FOREGROUND_GREEN | FOREGROUND_INTENSITY,
		LightRed = FOREGROUND_RED | FOREGROUND_INTENSITY,
		LightYellow = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
		LightMagenta = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
	};
};

class bg_color
{
public:
	enum Color
	{
		Black = 0,
		White = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED,
		Blue = BACKGROUND_BLUE,
		Green = BACKGROUND_GREEN,
		Red = BACKGROUND_RED,
		Yellow = BACKGROUND_RED | BACKGROUND_GREEN,
		Magenta = BACKGROUND_RED | BACKGROUND_BLUE,
		LightWhite = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY,
		LightBlue = BACKGROUND_BLUE | BACKGROUND_INTENSITY,
		LightGreen = BACKGROUND_GREEN | BACKGROUND_INTENSITY,
		LightRed = BACKGROUND_RED | BACKGROUND_INTENSITY,
		LightYellow = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY,
		LightMagenta = BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY,
	};
};

Let's see several examples. The first one, shows how to print text with different colors. Moreover, the colored text is always displayed starting with column 40.

int main()
{
   console con;

   con.SetForeColor(text_color::White);
   con << "Processing with files";
   con.GoToX(40);
   con << "OK" << "n";

   con.SetForeColor(text_color::White);
   con << "Looking for templates";
   con.GoToX(40);
   con.SetForeColor(text_color::LightYellow);
   con << "Not found" << "n";

   con.SetForeColor(text_color::White);
   con << "Creating new account";
   con.GoToX(40);
   con.SetForeColor(text_color::LightRed);
   con << "Failed" << "n";

   return 0;
}

Here is the output for this program

A second example shows how to print numbers starting from 1 to N and back to 1 to form a rhomb.

int main()
{
   console con;

   int depth;
   con << "Depth (1-9)? ";
   con >> depth;

   int middle = 20;
   for(int i = 1; i <= depth; ++i)
   {
      con.GoToX(middle-i+1);
      for(int j = 1; j<=i; ++j)
      {
         con << i << " ";
      }
      con << "n";
   }
   for(int i = depth-1; i >=1; --i)
   {
      con.GoToX(middle-i+1);
      for(int j = 1; j<=i; ++j)
      {
         con << i << " ";
      }
      con << "n";
   }

   return 0;
}

And here is the output, when the selected depth is 9:

The same can be achived using the overloaded operator<< for console_pos.

int main()
{
   console con;

   int depth;
   con << "Depth (1-9)? ";
   con >> depth;

   int x = 20;
   int y = 2;
   for(int i = 1; i <= depth; ++i)
   {
      con << console_pos(x-i+1, y++);
      for(int j = 1; j<=i; ++j)
      {
         con << i << " ";
      }
   }
   for(int i = depth-1; i >=1; --i)
   {
      con << console_pos(x-i+1, y++);
      for(int j = 1; j<=i; ++j)
      {
         con << i << " ";
      }
   }

   return 0;
}

A third and last examples shows how to read a matrix on rows and columns.

int main()
{
   console con;

   int rows;
   int cols;

   con << "rows: "; con >> rows;
   con << "cols: "; con >> cols;

   std::vector< int > numbers;

   for(int i = 0; i < rows; ++i)
   {
      for(int j = 0; j < cols; ++j)
      {
         con.GoTo(j*4, i+2);
         con << "[_]";
      }
   }
   int val;
   for(int i = 0; i < rows; ++i)
   {
      for(int j = 0; j < cols; ++j)
      {
         con.GoTo(j*4+1, i+2);
         con >> val;
         numbers.push_back(val);
      }
   }

   return 0;
}

Of course, this basic_console class does not carry all the possible functionality you can imagine, but it should be good enough for basic operations.

Here is the file with the source code.

, Hits for this post: 9406 .

The beta version of the MFC Feature Pack (the extension to MFC 9 from Visual Studio 2008) contains an extended dialog class implementation, CDialogEx. What this brings new, publicly, is the ability to set the background of the dialog box to a color or an image.

A first new method allows to set a background color.

void SetBackgroundColor(COLORREF color, BOOL bRepaint = TRUE);

Dialog box with yellow background.

A second overloaded method allows to set an image on the background.

void SetBackgroundImage(
    HBITMAP hBitmap,
    BackgroundLocation location = BACKGR_TILE,
    BOOL bAutoDestroy = TRUE,
    BOOL bRepaint = TRUE);

BOOL SetBackgroundImage(
    UINT uiBmpResId,
    BackgroundLocation location = BACKGR_TILE,
    BOOL bRepaint = TRUE);

You can use it like this:

BOOL CDialogDemoDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// ...

	HBITMAP bmp = ::LoadBitmap(AfxGetResourceHandle(), MAKEINTRESOURCE(IDB_BITMAP_LOGO));
	SetBackgroundImage(bmp, BACKGR_TILE);

	return TRUE;
}

Dialog box with image on the background

What is very weird is that the only styles for the image are these:

enum BackgroundLocation
{
	BACKGR_TILE,
	BACKGR_TOPLEFT,
	BACKGR_TOPRIGHT,
	BACKGR_BOTTOMLEFT,
	BACKGR_BOTTOMRIGHT,
};

This implementation lacks two basic styles: Center and Stretch. I really don’t understand why they were not implemented. It’s much more likely to need an image centered or stretched that aligned at the bottom-left, for instance. Thus, I don’t see how good this class is if I have to override the entire painting myself if I need that functionality.

Here is an implementation of a dialog class that supports those styles.

Hits for this post: 19561 .

At the beginning of January, Microsoft released a beta version of MFC Feature Pack, an exstension to the MFC shipped with Visual Studio 2008. This feature pack allows developers to create applications with the look and feel of Office, Visual Studio and Internet Explorer. MFC application can now support:

  • Office Ribbon
  • Office 2003, XP and 2007 look and feel
  • docking toolbars and panes in the Visual Studio style
  • customizable toolbars and menus
  • advanced GUI controls
  • advaced MDI tabs and groups

I have published an article on www.codeguru.com about enabling Office 2007 style on a MDI application. The article is called MFC Feature Pack: An Introduction. I encourage you to read it. A Romanian version will be available soon at www.codexpert.ro.

, , , , , Hits for this post: 30397 .

Recently Microsoft launched a program called DreamSpark addressed to students from all over the world (though currently the program is available only in 10 countries) that allows them to used for free software created by Microsoft. This includes:

  • development tools:
    • Visual Studio 2005 Professional Edition
    • Visual Studio 2008 Professional Edition
    • XNA Game Studio 2.0, 12-month free Academic membership in the XNA Creators Club
  • design tools:
    • Expression Studio (Expression Web, Expression Blend, Expression Design, Expression Media)
  • platforms:
    • SQL Server 2005 Developer Edition
    • Windows Server Standard Edition

To take part in the program you must be at least 18 years old and be a student in one of the countries where it is available. You also have to have your student status verified each year. You can read more about the program on its home page. You can also watch a video with Bill Gates announcing the program.

Unfortunately, for Romanian students, the program is not yet available. If you want to read more about reasons and the programs available for Romanians read Todi Pruteanu’s blog.

Hits for this post: 8706 .

I’ve seen this question many times asked in forums. Unfortunatelly, not all the answers are ok. A a good wait to find the available COM ports is by using function GetDefaultCommConfig, that returns the default configuration for a specified communication device. Following code shows how to use it:

#include 
#include  

// this can be defined in a separate file
#ifdef _UNICODE
  #define tstring std::wstring
#else
  #define tstring std::string
#endif   

void DetectComPorts(std::vector< tstring >& ports, size_t upperLimit = 128)
{
  for(size_t i=1; i<=upperLimit; i++)
  {
	TCHAR strPort[32] = {0};
	_stprintf(strPort, _T("\\\\.\\COM%d"), i);   

	DWORD dwSize = 0;
	LPCOMMCONFIG lpCC = (LPCOMMCONFIG) new BYTE[1];
	BOOL ret = GetDefaultCommConfig(strPort, lpCC, &dwSize);
	delete [] lpCC;	   

	lpCC = (LPCOMMCONFIG) new BYTE[dwSize];
	ret = GetDefaultCommConfig(strPort, lpCC, &dwSize);
	delete [] lpCC;   

	if(ret) ports.push_back(strPort);
  }
}

Here is an example of how to use this function:

int _tmain(int argc, _TCHAR* argv[])
{
  std::vector< tstring > ports;
  DetectComPorts(ports);   

  for(std::vector< tstring >::const_iterator it = ports.begin(); it != ports.end(); ++it)
  {
	std::cout << *it << std::endl;
  }   

  return 0;
}
Hits for this post: 10926 .

I read a very interesting article written in 2004 by Joel Spolsky, called How Microsoft Lost the API War. It shares my view that latelly, Microsoft has released simply too many programs and technologies and we can no longer cope with them. They have to take a break (and fix problems in the current releases) and give us a break too. Though a few years old, I find it very actual, and I encourage you to read it.

One funny thing that I read in the article was a problem of the famous SimCity game, which crashed on the beta release of Windows 95, because it accessed memory after it was freed. So Microsoft changed memory handling, introducing a special check for the program that was running, and if SimCity was found it allowed it to access memory after it was freed. :)

Hits for this post: 7676 .

Recently I have updated my article published on CodeGuru about determining Windows version and edition. The update offers support for determining the Windows Vista and Windows Server 2008 edition installed on a machine.

Since both these new operating systems have the same major and minor version returned by GetVersionEx, i.e. 6 for major and 0 for minor, a first differentiation between the two is done based on the product type returned by the same function: 1 is used for Vista and 3 for Server 2008. To identify the edition a new function available in Vista and Server 2008 must be used: GetProductInfo. Based on the major and minor version of the product and the major and minor version of the service pack, it returns a number identifying the edition type. The article explains how to differentiate between Windows Vista Starter, Vista Home Basic, Vista Home Premium, Vista Enterprise, Vista Business and Vista Ultimate, as well as different editions of Windows Server 2008, such as Standard, Datacenter, Enterprise or Webserver.

The article offers source code that can be integrated in any C++ application, as well as a demo application.

Hits for this post: 9439 .

Visual Studio 2008 (formely code-named ‘Orcas’) has several important updates for VC++ and MFC. Among them the possibility to create syslink controls, command or split buttons and network address controls. In this post I will show how you can work with the syslink control. The control provides a way to embed hypertext links in a window. It is actually a window that renders marked-up text just as hyperlinks in a web browser. Multiple links can be put in a single control, and are accessed by a zero-based index.

Currently it supports the anchor tag (<A>) with the HREF and ID attributes. HREF is used to specify a URL of any protocol (http, ftp, mailto, etc.). On the other hand ID specifies an unique name within the control, associated with an individual link.

The content is available in the toolbar, so you can simply drag and drop syslink controls onto you dialog template.

Syslink controls on dialog

You can specify the text, including the anchor tag from the properties page, or you can use the SetWindowText function to set it at run-time.

GetDlgItem(IDC_SYSLINK1)->SetWindowText(
   L"Visit my web site"
   L" and check my blog.");



You have to handle the NM_CLICK notification, check which link was clicked and take the appropriate action:

BEGIN_MESSAGE_MAP(CMFCDemoDlg, CDialog)
	ON_NOTIFY(NM_CLICK, IDC_SYSLINK1, &CMFCDemoDlg::OnNMClickSyslink1)
END_MESSAGE_MAP()   

void CMFCDemoDlg::OnNMClickSyslink1(NMHDR *pNMHDR, LRESULT *pResult)
{
	PNMLINK pNMLink = (PNMLINK) pNMHDR;   

	if (wcscmp(pNMLink->item.szUrl, WEB_SITE) == 0)
	{
		ShellExecuteW(NULL, L"open", pNMLink->item.szUrl, NULL, NULL, SW_SHOWNORMAL);
	}
	else if(wcscmp(pNMLink->item.szUrl, BLOG_LINK) == 0)
	{
		ShellExecuteW(NULL, L"open", pNMLink->item.szUrl, NULL, NULL, SW_SHOWNORMAL);
	}   

	*pResult = 0;
}

In MFC 9.0 (version that will ship with Visual Studio 2008) class CLinkCtrl is a wrapper over the Windows API for working with the syslink control.

You can associate an instance of CLinkCtrl with a syslink control through the DDX mechanism:

void CMFCDemoDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_SYSLINK2, Link2);
}

In my demo application that you can download from here I used a second syslink with an ID attribute. Each time the link is clicked it increments a counter, which is shown. For that I created two helper functions first, one to generate part of the text and the second to set the text to the link control:

CString CMFCDemoDlg::GetClickText() const
{
	CString str;
	str.Format(L"clicked %d times", Clicks);
	return str;
}   

void CMFCDemoDlg::SetLink2Text()
{
	Link2.SetWindowText(L"Link was " + GetClickText() + L"");
}

When handling the NM_CLICK notification, I checked the szID member of structure LITEM and took the appropriate action:

void CMFCDemoDlg::OnNMClickSyslink2(NMHDR *pNMHDR, LRESULT *pResult)
{
	PNMLINK pNMLink = (PNMLINK) pNMHDR;   

	if (wcscmp(pNMLink->item.szID, L"clicked") == 0)
	{
		Clicks++;
		SetLink2Text();
	}   

	*pResult = 0;
}

The result is shown here:

Syslink controls on dialog

Hopefully the sample code that I put together will help you work with CLinkCtrl and the syslink control in VS 2008.

Hits for this post: 18469 .