C++ At Work

Dialog Templates, RegexTest

Paul DiLascia

Code download available at:CAtWork0508.exe(279 KB)

Q I'd like to create a dialog-based app (the main window is a dialog itself) using MFC and C++. I don't want to use a resource (.rc) file, but I want to create the dialog dynamically in memory. I sifted through MSDN® and found some clues, but no code examples. I know there are DLGTEMPLATE and DLGITEMTEMPLATE structures and the function InitModalIndirect, which probably is used to create the modal dialog, but I have no idea where to start. How can I create a dialog dynamically without using a resource file?

Q I'd like to create a dialog-based app (the main window is a dialog itself) using MFC and C++. I don't want to use a resource (.rc) file, but I want to create the dialog dynamically in memory. I sifted through MSDN® and found some clues, but no code examples. I know there are DLGTEMPLATE and DLGITEMTEMPLATE structures and the function InitModalIndirect, which probably is used to create the modal dialog, but I have no idea where to start. How can I create a dialog dynamically without using a resource file?

Thomas Zeitlberger

A Creating a dialog on the fly is theoretically straightforward but practically perilous. Just create the proper structures in memory and then call one of the Indirect dialog creation functions: CDialog::CreateIndirect to create a modeless dialog or CDialog::InitModalIndirect to create a modal one (then call DoModal to run it). These correspond to the Win32® API functions ::CreateDialogIndirect and ::DialogBoxIndirect. Whichever method you use, you have to pass a pointer to your dialog template in memory.

A Creating a dialog on the fly is theoretically straightforward but practically perilous. Just create the proper structures in memory and then call one of the Indirect dialog creation functions: CDialog::CreateIndirect to create a modeless dialog or CDialog::InitModalIndirect to create a modal one (then call DoModal to run it). These correspond to the Win32® API functions ::CreateDialogIndirect and ::DialogBoxIndirect. Whichever method you use, you have to pass a pointer to your dialog template in memory.

Creating the dialog template is conceptually simple: just build and initialize the structures in memory. The details are a bit tricky because the structures are quirky and you have to get everything just right. If you're off by even 1 byte, your app could spiral into neverland with no clue where the problem lies. It's also frustrating to calculate the positions and sizes of child controls since dialogs don't use pixels; they use dialog units, which depend on your dialog's font.

Figure 1 String Dialog

Figure 1** String Dialog **

A full exposition of dialog templates that covers every kind of control and dialog is beyond the scope of this column. But I can at least offer a simple example. I wrote a class called CStringDialog that displays a dialog like the one shown in Figure 1 to request a string from the user. To use it, all you have to do is instantiate and then call Init and DoModal:

CStringDialog dlg; dlg.Init(_T("Hi"), _T("Please enter your name:")); if (dlg.DoModal()==IDOK) { CString name = dlg.m_str; // do something with it... }

Figure 3 Dialog Template

Figure 3** Dialog Template **

CStringDialog looks and acts like any other CDialog-derived class based on a dialog resource, but it builds its dialog on the fly in memory using templates.

So what exactly is a dialog template, anyway? A dialog template is a memory structure that describes the dialog. What makes the template complex and error-prone is that it's not a simple fixed-length struct like CREATESTRUCT or WNDCLASS. It's a variable-length structure that is comprised of a fixed-length DLGTEMPLATE followed by an array of DLGITEMTEMPLATE structs, one for each item/control in your dialog. Both the DLGTEMPLATE and DLGITEMTEMPLATE have extra variable-length fields that come after the C struct. Figure 2 shows the structures and Figure 3 illustrates the whole layout. Dialog templates resemble something from an assembly language programming manual so now's the time to don your grunge-retardant hazmat suit. I'll take it one step at a time, starting with DLGTEMPLATE.

Figure 2 DlgTemplate.cpp

// one of these for the dialog typedef struct { DWORD style; DWORD dwExtendedStyle; WORD cdit; short x; short y; short cx; short cy; // variable-length stuff follows } DLGTEMPLATE; // one of these for each dialog item (control) typedef struct { DWORD style; DWORD dwExtendedStyle; short x; short y; short cx; short cy; WORD id; // variable-length stuff follows } DLGITEMTEMPLATE;

Assuming you've allocated a memory block large enough to hold your entire dialog template, the first thing to do is fill out the fields in the DLGTEMPLATE. This is the easy part:

WORD* pTempl = new WORD[1024]; DLGTEMPLATE& dt = *((DLGTEMPLATE*)pTempl); dt.style = WS_POPUPWINDOW|DS_MODALFRAME|WS_DLGFRAME; dt.cdit = 3; // # dlg items dt.x = 100; // in dlg units // etc.

The DLGTEMPLATE fields are self-explanatory, so I won't elaborate further. Immediately following the DLGTEMPLATE come three variable-length fields: the menu, dialog class, and caption. Each must fall on a WORD boundary. Each can be a null-terminated Unicode string identifying either the name of a MENU resource, the class name to use for the dialog, or the caption. In addition, the menu and class name can have the special value 0xFFFF followed by a 16-bit ID—either the menu resource ID or the ordinal of a predefined system window class. In almost all cases you should use 0x0000 (null string) for the class, which tells Windows® to use the default dialog class (#32770). Most dialogs don't have a menu, so the menu will also be 0x0000 (null string). In code, it looks like this:

*pTempl++ = 0; // menu (none) *pTempl++ = 0; // dialog class (use standard)

The caption comes next, a null-terminated Unicode string:

USES_CONVERSION; LPCWSTR wszText = T2W(_T("My Dialog")); wcscpy((WCHAR*)pTempl, wszText); pTempl += wcslen(wszText)+1;

This code works for Unicode or ASCII builds, since with _UNICODE defined, T2W is a no-op. Don't forget to increment the pointer to your template-in-progress (pTempl) to the next WORD after the string. If your dialog has the DS_SETFONT style, there's a fourth field: the 16-bit font size followed by the Unicode font name, for example "Verdana".

Finally, I should point out that there's an extended version of the dialog template, DLGTEMPLATEEX, that lets you specify even more fields like the point size and weight, whether to use italics, the character set, and typeface name. See the documentation for more information; I'll only describe the simple versions here since you can always set the font more easily by calling SetFont in your OnInitDialog handler. (There's also a DLGITEMTEMPLATEEX for items.)

So much for DLGTEMPLATE. Next, the controls. Each control in your dialog is described by an item template (DLGITEMTEMPLATE), which must fall on a DWORD boundary:

pTempl = AlignDWORD(pTempl); DLGITEMTEMPLATE& it = *((DLGITEMTEMPLATE*)pTempl); it.x = 0; it.y = 0; // etc.

As with DLGTEMPLATE, the DLGITEMTEMPLATE struct is followed by three variable-length fields. In this case they are the class name, text, and creation data. The class name is again either a null-terminated Unicode string specifying the window class name (for example, "SysListView32" or "MyFancyControl"), or the value 0xFFFF followed by one of the special atom codes, that are shown in Figure 4, for standard predefined system controls. For example, the following code shows how to create a static text control:

// class immediately after DLGITEMTEMPLATE *pTempl++ = 0xFFFF; // next WORD is atom: *pTempl++ = 0x0082; // static control

Figure 4 Atom Codes

Code Control Type
0x0080 Button
0x0081 Edit
0x0082 Static
0x0083 List box
0x0084 Scroll bar
0x0085 Combo box

Following the class name comes the title. It can be either a Unicode string or the special value 0xFFFF followed by a 16-bit resource ID. You also can use the 0xFFFF + ID format to specify an icon or bitmap for a static control with the SS_ICON or SS_BITMAP style. CStringDialog uses the string form to create its prompt:

USES_CONVERSION; LPCWSTR wszTest = T2W(_T("My Dialog")); int maxlen = /* don't overflow! */ wcsncpy((WCHAR*)pTempl, wszText, maxlen); pTempl += wcslen(wszText)+1;

Finally, the "creation data" can be whatever you want. The first WORD is the length of the data, or zero if none. Windows passes a pointer to your data as LPARAM to WM_INITDIALOG (modal dialogs) or WM_CREATE (modeless). I don't recommend using creation data here since it's much easier to add whatever data members you want to your dialog class and initialize them with your dialog's constructor or OnInitDialog handler. But you still have to supply a zero WORD to tell Windows there's no creation data:

*pTempl++ = 0; // no creation data

Once you've completely specified the DLGITEMTEMPLATE, you can do it again for the next control, and the next, and so on for each control in your dialog. Just make sure DLGTEMPLATE::cdit specifies the total number of controls. To simplify the process of building dialog templates (and make it less error prone) I wrote a helper class, CDlgTemplateBuilder. CStringDialog uses it to build its dialog one step at a time:

// in CStringDialog::Init CDlgTemplateBuilder& dtb = m_dtb; DLGTEMPLATE* pTempl = dtb.Begin(...); dtb.AddItem(...); dtb.AddItem(...); dtb.AddItem(...); InitModalIndirect(pTempl, ...);

I've obscured the details to highlight the main idea: you call Begin once, then AddItem for each control. CDlgTemplateBuilder::Begin builds the DLGTEMPLATE and each call to AddItem builds another DLGITEMTEMPLATE. CDlgTemplateBuilder builds the template in its own memory buffer and automatically increments DLGTEMPLATE::cdit (the item count) every time you add another item. CDlgTemplateBuilder has helper functions AlignDWORD and AddText to ensure the proper alignments and perform the proper string conversions. I leave you to download and ponder the source from the MSDN Magazine Web site.

I mentioned that dialogs use dialog units, not pixels. Both DLGTEMPLATE and DLGITEMTEMPLATE have x, y, cx, and cy members to specify the position and size of the dialog or item. These values are in dialog units. Each horizontal dialog unit is one-quarter of a base unit, and each vertical unit is one-eighth of a base unit. A base unit is the average width and height of a character in the dialog and depends on the dialog's font. If this seems like a real pain, it is, but in fairness the idea is laudable: dialog units make your dialog's appearance independent of its font. So whether you use a large font or a small one, the relative positions of all the controls are the same and everything should look correct. Windows has a special function called MapDialogRect to convert dialog units to pixels; amazingly, there's no function to convert the other way, which is what you need to build a template—but you can use the following formulas:

CSize base = ::GetDialogBaseUnits(); xDlg = MulDiv(xPixel, 4, base.cx); yDlg = MulDiv(yPixel, 8, base.cy);

For CStringDialog, I was too lazy to do all this. Instead, I experimented to find the right values to get a dialog like the one shown in Figure 1. A more sophisticated implementation would check the length of the prompt or allow the caller to specify the dimensions. If dealing with dialog units makes your brain ache, you can create your controls using zeroes for size and position, then implement an OnSize handler that moves them to their proper pixel places. Your dialog may have to post itself a WM_SIZE message from OnInitDialog to make sure the controls are positioned properly when the dialog first starts.

Finally, how did I get CStringDialog to display the question mark in Figure 1? CStringDialog::Init lets you specify the icon for your prompt. The default is IDI_QUESTION. But IDI_QUESTION is a built-in system icon, not an icon from the application's resource file. If you specify a resource ID in your dialog template, Windows expects it to be in the resource file. So how did I get Windows to use the system icon instead?

By fooling it, of course. CStringDialog checks the icon resource ID for a value greater than IDI_APPLICATION, the first system icon ID. If the icon ID is in the system ID range, CStringDialog loads it by calling ::LoadIcon with hInstance set to NULL (for system icons) and saves the HICON loaded in a data member m_hIcon. CStringDialog then constructs the dialog template using the 0xFFFF + nResID format with nResID=0. This causes Windows to create a static icon with no actual icon. CStringDialog then sets the icon in its OnInitDialog handler:

// in CStringDialog::OnInitDialog() if (m_hIcon) { CStatic* pStatic = (CStatic*)GetDlgItem(IDICON); pStatic->SetIcon(m_hIcon); }

The upshot is that you can pass any of the IDI_XXX icon IDs to CStringDialog::Init. You can still use your own icon as long as its ID is below IDI_APPLICATION = 32512. Download the source for details.

Q It's quite a coincidence that you should write about regex DDV validation (see the April 2005 column) since I had to write the same thing recently. Why would you wrap a .NET library and add that dependency (and all the baggage of a wrapper library) when there's a compact and free regex library included with Visual Studio® .NET that becomes available to your MFC app by including a single header file, atlrx.h? It's not 100 percent standard syntax, but I'd much rather live with that than add a .NET Framework dependency.

Q It's quite a coincidence that you should write about regex DDV validation (see the April 2005 column) since I had to write the same thing recently. Why would you wrap a .NET library and add that dependency (and all the baggage of a wrapper library) when there's a compact and free regex library included with Visual Studio® .NET that becomes available to your MFC app by including a single header file, atlrx.h? It's not 100 percent standard syntax, but I'd much rather live with that than add a .NET Framework dependency.

Gil Rosin

A Well lock me in a straitjacket and spray me with silly string! I didn't even know the Active Template Library (ATL) had a regex class. Well, it just goes to show Windows has more stuff than even a supposed guru can ever possibly hope to know. Indeed, you're right, ATL does provide a regex implementation!

A Well lock me in a straitjacket and spray me with silly string! I didn't even know the Active Template Library (ATL) had a regex class. Well, it just goes to show Windows has more stuff than even a supposed guru can ever possibly hope to know. Indeed, you're right, ATL does provide a regex implementation!

But first, let me correct some impressions about the .NET Framework. I know a lot of you are reluctant to add such a dependency to your apps because you fear code bloat, and I was reluctant too, at first. But using the .NET Framework may not be as bad as you think. While managed apps definitely take a performance hit at startup, believe it or not, once the Framework is loaded, Microsoft® intermediate language (MSIL) code can run even faster than native EXEs. That's because the just-in-time (JIT) compiler is capable of some really hairy performance optimizations. And while applications targeting older versions of Windows like Windows 98 and Windows NT® can't assume the Framework exists, forcing you to install it yourself (see "Using Visual Studio .NET 2003 to Redistribute the .NET Framework" at Using Visual Studio .NET 2003 to Redistribute the .NET Framework, or google "dotnetfx.exe"), newer and future versions of Windows come with the Framework pre-installed. As the .NET Framework becomes more ubiquitous, and as its performance continues to improve, the extra cost (both performance and installation) of calling the Framework should shrink to the vanishing point.

As for my wrapper library, the "baggage" is really just a hair-thin layer of syntactic sugar designed to make the wrappers compile. The most they add is one extra function call since each wrapper object is no more than a handle to the managed object. As I pointed out in my April article (see "Wrappers: Use Our ManWrap Library to Get the Best of .NET in Native C++ Code"), the function call is insignificant for most applications. Also, I chose regex only as an example; my main purpose was to create a general mechanism to wrap any Framework class. And finally, you only need wrappers if you want to stick with an older version of the complier that doesn't support /clr, or you want to avoid it for some reason. Otherwise, toss the wrappers and call the Framework directly through the managed extensions.

Now that I've cleared that up, I must confess I was quite excited to learn the Active Template Library has a regex class, even though I was embarrassed I didn't already know. The first thing I did when I got your e-mail was port the test programs from the ManWrap article to use ATL instead of the .NET library. I wanted to see how easy it would be. I encountered some minor snafus, but overall the process was fairly straightforward.

The Active Template Library implementation of regex is primitive compared to the .NET Framework version, but for many purposes, it does the job fine. ATL uses two template classes: CAtlRegExp, which holds a regular expression; and CAtlREMatchContext, which holds matches. These templates are parameterized by another class that describes the character traits (for example, ASCII, WCHAR, or multibyte). In practice you can ignore this since the ATL templates provide default character traits CAtlRECharTraits based on your settings for _UNICODE:

// in atlrx.h #ifndef _UNICODE typedef CAtlRECharTraitsA CAtlRECharTraits; #else typedef CAtlRECharTraitsW CAtlRECharTraits; #endif template <class CharTraits=CAtlRECharTraits> class CAtlRegExp; // forward declaration

In effect, all the ATL regex stuff uses TCHARs by default. So to create a regular expression in ATL, you'd write:

CAtlRegExp<> re; re.Parse("a+b+");

This parses the regular expression into internal structures, so you can then call Match with a CAtlREMatchContext to get the matches, as shown here:

CAtlREMatchContext<> mc; re.Match("aaabbx", &mc);

CAtlREMatchContext is a little primitive, at least compared to the Framework regex class and other more full-blown implementations. It has a member m_Match that's a MatchGroup, which holds the start and end of the match:

struct MatchGroup { const RECHAR *szStart; const RECHAR *szEnd; };

Here RECHAR is whatever character type is defined in the character traits; in practice it's the same as TCHAR if you use the default traits. CAtlREMatchContext can also find matching subgroups within an input string. After calling Match, mc.m_uNumGroups holds the number of matching subgroups and you can call GetMatch(i, ...) to get the ith subgroup match. One of the weird things about ATL regular expressions is that they use curly braces to denote groups, not the standard parentheses. For example:

CAtlRegExp<> re; re.Parse("{a+}{b+}"); re.Match("aaabbx", &mc);

This would find a single match ("aaabb") with two groups ("aaa" and "bb"). ATL will match normal paren groups, but you can't find the individual sub-matches unless you use curly braces.

CAtlRegExp and CAtlREMatchContext do the job, but they're a little cumbersome to use. For example, to find all the matches, you have to repeatedly call Match with the szEnd pointer from the previous match as the start of the next input string. It's not rocket science, but why should you have to keep track of state when you want to quickly find all the matches? So I encapsulated everything in a single class, CRegex, that makes programming easier. Figure 5 shows the main header file and Figure 6 shows my RegexTest program, from the April ManWrap article, ported to ATL. As with the original program, you enter a regular expression and input string, and RegexTest displays the matches and groups. It uses CRegex to iterate the matches like so:

CRegex re(/* regex */); re.SetInput(/* input */); while (re.NextMatch()) { int offset=0; CString match = re.GetMatch(&offset); ... }

Figure 5 Regex.h

//////////////////////////////////////////////////////////////// // MSDN Magazine — August 2005 // If this code works, it was written by Paul DiLascia. // If not, I don't know who wrote it. // Compiles with Visual Studio .NET 2003 (V7.1) on Windows XP. Tab size=3. // #pragma once #include <atlrx.h> // ATL regex using namespace std; ////////////////// // CRegex makes ATL regular expressions a little more usable. // class CRegex : public CAtlRegExp<> { protected: CString m_re; // the regular expression BOOL m_bCaseSensitive; // case sensitive? CAtlREMatchContext<> m_mc; // internal ATL match context LPCTSTR m_szIn; // original input string LPCTSTR m_szNext; // next character to search // helper to extract string from ATL MatchGroup CString GetMGString(const CAtlREMatchContext<>::MatchGroup& mg); public: REParseError m_err; // current ATL parse error, if any // helper function to get error name from error code static LPCTSTR GetErrorName(REParseError err); CRegex() : m_err(REPARSE_ERROR_OK) { } CRegex(LPCTSTR szRE, BOOL bCaseSensitive=TRUE) { m_err = Parse(szRE, bCaseSensitive); } CRegex(const CRegex& r) { *this = r; } ~CRegex() { } // only copy Regex and case flag, not dynamic state—then reparse const CRegex& operator= (const CRegex& r) { m_re = r.m_re; m_bCaseSensitive = r.m_bCaseSensitive; if (!m_re.IsEmpty()) m_err = Parse(m_re, m_bCaseSensitive); return *this; } // convert to string: return RE operator LPCTSTR() const { return m_re; } // Parse RE: reset internal state and return error code. REParseError Parse(LPCTSTR szRE, BOOL bCaseSensitive=TRUE) { m_re = szRE; m_szIn = m_szNext = NULL; return CAtlRegExp<>::Parse(szRE, bCaseSensitive); } // Set input string. Use this with NextMatch to find all matches. void SetInput(LPCTSTR szIn) { m_szIn = m_szNext = szIn; } // Find next match after calling SetInput BOOL NextMatch() { return CAtlRegExp<>::Match(m_szNext, &m_mc, &m_szNext); } // Find a single match in input string. BOOL Match(LPCTSTR szIn, LPCTSTR* ppszEnd=NULL) { if (szIn==NULL || *szIn==0) return FALSE; SetInput(szIn); return CAtlRegExp<>::Match(szIn, &m_mc, ppszEnd); } // Get current match; optional arg returns offset in input string. CString GetMatch(int* pOffset=NULL) const { if (pOffset) *pOffset = (int)(m_mc.m_Match.szStart - m_szIn); return GetMGString(m_mc.m_Match); } // Get number of groups. In ATL syntax, groups are marked with {}. UINT GetNumGroups() const { return m_mc.m_uNumGroups; } // Get nth match group. Optional arg returns offset into input string. CString GetGroup(int nIndex, int* pOffset=NULL) const { CAtlREMatchContext<>::MatchGroup mg; const_cast<CRegex*>(this)->m_mc.GetMatch(nIndex, &mg); if (pOffset) *pOffset = (int)(mg.szStart - m_szIn); return GetMGString(mg); } };

The CRegex constructor calls Parse automatically (you should check m_err for errors), and if you want the individual group matches, you can call CRegex::GetNumGroups to get the total number of groups for the current match and CRegex::GetGroup to get each group match. Figure 7 shows these details. Internally, CRegex has its own CAtlRegExp and CAtlREMatchContext objects, and keeps track of where it is in the input string—so you don't have to.

Figure 7 FormatResults.cpp

CString FormatResults(CString sRegex, CString sInput) { if (sRegex.IsEmpty()) return errbeep(_T("Please enter a regular expression!")); if (sInput.IsEmpty()) return errbeep(_T("Please enter an input string!")); CString result; CString temp; CRegex re(sRegex); if (re.m_err!=REPARSE_ERROR_OK) { result.Format(_T("Oops! Error parsing regex: %s\n"), re.GetErrorName()); MessageBeep(0); return result; } LPCTSTR szInput = sInput; int count=0; re.SetInput(szInput); while (re.NextMatch()) { int offset=0; CString match = re.GetMatch(&offset); temp.Format(_T("Match at %d: %s\n"), offset, match); result += temp; temp.Format(_T(" Number of groups: %d\n"), re.GetNumGroups()); result += temp; UINT nGroups = re.GetNumGroups(); for (UINT m=0; m<nGroups; m++) { CString group = re.GetGroup(m, &offset); temp.Format(_T(" Group %d at %d: %s\n"), m, offset, group); result += temp; } count++; } if (count<=0) result.Format(_T("No match!")); return result; }

Figure 6 RegexTest in Action

Figure 6** RegexTest in Action **

I also added a few other useful features. The Active Template Library has no regex replace function, but it wasn't hard to add one. As with the Framework regex class, CRegex::Replace comes in normal and static flavors, so you can call it with or without a CRegex object. For example, the main dialog in RegexTest uses the static version to convert linefeeds (\n) to CRLFs ("\r\n"), like so:

static CString LF2CRLF(LPCTSTR lpsz) { return CRegex::Replace(lpsz,_T("\n"),_T("\r\n"),TRUE); }

What could be easier? Without CRegex, this would be dozens of lines of code using CAtlRegExp directly. It just goes to show how effective you can be when you take the time to build higher layers of abstraction on top of the basic building blocks the Redmondtonians provide. This is the essence of layered design. I even added a dynamic Replace function similar to the one in the .NET Framework so you can use a callback function to implement a dynamic replace algorithm like the one in my word-scrambler program (WordMess) from the April ManWrap article. Finally, I added Split so you can split a string into an array of substrings using a regular expression as the separator. For example:

vector<CString> strs = CRegex::Split("one,two,three",",");

This returns a vector of three substrings: "one", "two", and "three". Implementing Split is mostly a matter of string manipulation, but once you've written the code, it's yours to use for all eternity. I leave you to download and ponder the source for details.

If you decide to use ATL regular expressions or my CRegex encapsulation of them, be careful: the ATL version of regular expressions is a simplified, nonstandard implementation. I've already mentioned the use of curly braces instead of parens. The ATL also uses nonstandard meta characters (for example, \b instead of \s for whitespace) and you can't use meta characters within ranges ([\a\d] won't work). See Figure 8 for a list of special characters. Read the documentation carefully and watch out for bugs. Complex regular expressions don't always work, and you need #pragma to suppress a couple of level 4 compiler warnings (see the code for details). But if all you need is some basic pattern-matching to validate user input, ATL can get the job done.

Figure 8 CAtlRegExp Special Characters

Abbrev. Matches
\a Any alphanumeric character: ([a-zA-Z0-9])
\b White space (blank): ([ \\t])
\c Any alphabetic character: ([a-zA-Z])
\d Any decimal digit: ([0-9])
\h Any hexadecimal digit: ([0-9a-fA-F])
\n Newline: (\r|(\r?\n))
\q A quoted string: (\"[^\"]*\")|(\'[^\']*\')
\w A simple word: ([a-zA-Z]+)
\z An integer: ([0-9]+)

Before parting, I should point out to C++ fans that regular expressions are slated to become part of the Standard Template Library. The implementation will most likely be based on the current boost implementation available from boost.org.

Happy programming!

Send your questions and comments for Paul to  cppqa@microsoft.com.

Paul DiLascia is a freelance software consultant and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). In his spare time, Paul develops PixieLib, an MFC class library available from his Web site, www.dilascia.com.