Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles

John Papa tackles questions about calling services from Silverlight 2 applications.

John Papa

MSDN Magazine November 2008

...

Read more!

This month we explain how pseudo variables and format specifiers provide a wealth of information for use in debugging.

Kenny Kerr

MSDN Magazine December 2008

...

Read more!

Matt Milner takes a look at some of the challenges and techniques related to testing Windows Workflow Foundation activities, workflows, and associated components.

Matt Milner

MSDN Magazine November 2008

...

Read more!

Our security experts present 10 vulnerable pieces of code. Your mission is to find the holes (a.k.a. bad security practices) in the code.

Michael Howard and Bryan Sullivan

MSDN Magazine November 2008

...

Read more!

The CLR team takes a look inside the System.Globalization namespace to explain how to handle data formats for proper localization and globalization.

Melitta Andersen

MSDN Magazine November 2008

...

Read more!

Also by this Author

Paul DiLascia

MSDN Magazine December 2005

...

Read more!

Paul DiLascia

MSDN Magazine April 2006

...

Read more!

This month Paul DiLascia codes some Microsoft Office-style dialog box features.

Paul DiLascia

MSDN Magazine August 2006

...

Read more!

Paul DiLascia

MSDN Magazine May 2005

...

Read more!

Paul DiLascia

MSDN Magazine September 2005

...

Read more!

Popular Articles

C# 2.0 introduces a wealth of exiting new features, such as generics, iterators, partial classes and anonymous methods. While generics are the most talked-about feature especially for former classic C++ developers, the rest of the new features are important additions to your .NET development arsenal, enhancing power and improving overall productivity. This article is dedicated to all the new C# 2.0 capabilities besides generics to give you a good overall picture of the upcoming features.

Juval Lowy

MSDN ...

Read more!

This article presents an overview of the motivation behind new techniques that decompose problems into independent pieces for optimal use of parallel programming.

David Callahan

MSDN Magazine October 2008

...

Read more!

When incorporating the ASP.NET DataGrid control into your Web apps, common operations such as paging, sorting, editing, and deleting data require more effort than you might like to expend. But all that is about to change. The GridView control--the successor to the DataGrid-- extends the DataGrid's functionality it in a number of ways. First, it fully supports data source components and can automatically handle data operations, such as paging, sorting, and editing, as long as its bound data source object supports these capabilities. In addition, ...

Read more!

Ray Djajadinata

MSDN Magazine May 2007

...

Read more!

Here we describe some of the more common challenges to concurrent programming and present advice for coping with them in your software.

Joe Duffy

MSDN Magazine October 2008

...

Read more!

Our Blog

Windows Workflow Foundation (WF) imposes some restrictions on the developer authoring programs that target it. But in return WF offers a powerful, flexible, and extensible set of runtime services such as support for long-running code.

In the December 2008 issue of MSDN Magazine, Josh Lane provides some best practices to consider ...

Read more!

Silverlight and SharePoint provide a simple, yet powerful, infrastructure for building intranet and extranet applications with sophisticated user interface designs and interactions.

In the November 2008 issue of MSDN Magazine, Steve Fox and Paul Stubbs demonstrate how to build a SharePoint Web Part as a wrapper for a Silverlight application.

...

Read more!

Choosing the best alternative is a common task in software development and testing. A group of beta users may need to choose the best user interface from a set of prototypes. Or imagine the members of an open source project voting for a policy.

In the November 2008 issue of MSDN Magazine, Dr. James McCaffrey describes five of the ...

Read more!

Silverlight provides a browser interoperability layer that allows managed code to access the document object model (DOM) of the underlying page. At the same time, JavaScript code running in the page can access the XAML content of the plug-in and even make modifications.

In the November 2008 issue of MSDN Magazine, Dino Esposito discusses the ...

Read more!

Windows Presentation Foundation (WPF) adds functionality to the Microsoft .NET Framework so that you actually can reliably keep bound controls synchronized with their data sources.

In the December 2008 issue of MSDN Magazine, Ken Getz demonstrates how to use the ObservableCollection class provided by WPF to keep bound controls in ...

Read more!

New information has been added to this article since publication.
Refer to the Editor's Update below.

C++ Q&A
GetKeyState, the STL String Class, Exposing C++ Objects, and More
Paul DiLascia

Code download available at: CQA0408.exe (234 KB)
Browse the Code Online

Q I want users to hold down the Control key while double-clicking on my program's icon to have the program start up in a particular way. Neither ::GetCommandLine nor __argc give any indication that the key was pressed; nor does there seem to be any way to do this in MFC using CCommandLineInfo. Is there a way to find this out, say by polling the key?
Q I want users to hold down the Control key while double-clicking on my program's icon to have the program start up in a particular way. Neither ::GetCommandLine nor __argc give any indication that the key was pressed; nor does there seem to be any way to do this in MFC using CCommandLineInfo. Is there a way to find this out, say by polling the key?
Dean Jones

A Yes, it's very simple. All you have to do is call GetKeyState. This function returns the status of a virtual key at the time the current message you are processing was sent. The status can be up, down, or toggled. Toggled is for keys like Caps and Shift Lock, which alternate states. For ordinary keys like the Control key (VK_CONTROL), the high-order sign bit of the state is 1 if the key is down.
A Yes, it's very simple. All you have to do is call GetKeyState. This function returns the status of a virtual key at the time the current message you are processing was sent. The status can be up, down, or toggled. Toggled is for keys like Caps and Shift Lock, which alternate states. For ordinary keys like the Control key (VK_CONTROL), the high-order sign bit of the state is 1 if the key is down.
Many apps use Control+F8 as the special key to launch in restore mode. For example, if your app lets users customize their workspaces, starting with Control+F8 might restore the workspace to its factory default settings. Just make sure you ask the user to confirm first. Even better would be to save the customizations in separate INI files, so users have a chance of recovering them. In any case, to check for the Control key when your app starts, you'd write something like this:
if (GetKeyState(VK_CONTROL)<0) {
  // enter special mode
}
Figure 1 shows a snippet from a sample MFC app you can download from the link at the top of this article that displays a message box and beeps if the user presses Ctl+F8 while starting the app. If you just want to check for the Control key, leave out the test for VK_F8.
////////////////////////////////////////////////////////////////
// MSDN Magazine — August 2004
// 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 on Windows XP. Tab size=3.
//
// CtlTest illustrates how to test for a special control-key combination 
// when your program starts.

#include "StdAfx.h"
#include "MainFrm.h"
#include "StatLink.h"
#include "resource.h"

class CMyApp : public CWinApp {
public:
   virtual BOOL InitInstance();
   •••
} theApp;

BOOL CMyApp::InitInstance()
{
   BOOL bCtl = GetKeyState(VK_CONTROL)<0;
   BOOL bF8  = GetKeyState(VK_F8)<0;
   if (bCtl && bF8) {
      if (AfxMessageBox(_T("Enter special startup mode?"),
                        MB_YESNO)==IDYES)
         // Enter special startup mode here.
         MessageBeep(0);
   }
   •••
}

Q I use the Standard Template Library (STL) std::string class very often in my C++ programs, but I have a problem when it comes to Unicode. When using regular C-style strings I can use TCHAR and the _T macro to write code that compiles for either Unicode or ASCII, but I always find it difficult to get this ASCII/Unicode combination working with the STL string class. Do you have any suggestions?
Q I use the Standard Template Library (STL) std::string class very often in my C++ programs, but I have a problem when it comes to Unicode. When using regular C-style strings I can use TCHAR and the _T macro to write code that compiles for either Unicode or ASCII, but I always find it difficult to get this ASCII/Unicode combination working with the STL string class. Do you have any suggestions?
Naren J.

A Sure. It's easy, once you know how TCHAR and _T work. The basic idea is that TCHAR is either char or wchar_t, depending on the value of _UNICODE:
// abridged from tchar.h
#ifdef  _UNICODE
typedef wchar_t TCHAR;
#define __T(x) L ## x
#else
typedef char TCHAR;
#define __T(x) x
#endif
A Sure. It's easy, once you know how TCHAR and _T work. The basic idea is that TCHAR is either char or wchar_t, depending on the value of _UNICODE:
// abridged from tchar.h
#ifdef  _UNICODE
typedef wchar_t TCHAR;
#define __T(x) L ## x
#else
typedef char TCHAR;
#define __T(x) x
#endif
When you choose Unicode as the character set in your project settings, the compiler compiles with _UNICODE defined. If you select MBCS (Multi-Byte Character Sets), the compiler builds without _UNICODE. Everything hinges on the value of _UNICODE. Similarly, every Windows® API function that uses char pointers has an A (ASCII) and a W (Wide/Unicode) version, with the real version defined to one of these, based on the value of _UNICODE:
#ifdef UNICODE
#define CreateFile CreateFileW
#else
#define CreateFile CreateFileA
#endif
Likewise, there's _tprintf and _tscanf for printf and scanf. All the 't' versions use TCHARs instead of chars. So how can you apply all this to std::string? Easy. STL already has a wstring class that uses wide characters (defined in the file xstring). Both string and wstring are typedef-ed as template classes using basic_string, which lets you create a string class using any character type. Here's how STL defines string and wstring:
// (from include/xstring)
typedef basic_string<char, 
  char_traits<char>, allocator<char> >
  string;
typedef basic_string<wchar_t, 
  char_traits<wchar_t>, allocator<wchar_t> > 
  wstring;
The templates are parameterized by the underlying character type (char or wchar_t), so all you need for a TCHAR version is to mimic the definitions using TCHAR:
typedef basic_string<TCHAR, 
  char_traits<TCHAR>, 
  allocator<TCHAR> > 
  tstring;
Now you have a tstring that's based on TCHAR—that is, either char or wchar_t, depending on the value of _UNICODE. I'm showing you this to point out how STL uses basic_string to implement strings based on any underlying character type. Defining a new typedef isn't the most efficient way to solve your problem. A better way is to simply #define tstring to either string or wstring, like so:
#ifdef _UNICODE
#define tstring wstring
#else
#define tstring string
#endif
This is better because STL already defines string and wstring, so why use templates to create another string class that's the same as one of these, just to call it tstring? You can use #define to define tstring to string or wstring, which will save you from creating another template class (though compilers are getting so smart these days it wouldn't surprise me if the duplicate class were discarded).
[ Editor's Update - 7/30/2004: typedef does not create a new class, but rather introduces a scoped name for a type. A typedef never defines a new type.] In any case, once you have tstring, you can write code like this:
   tstring s = _T("Hello, world");
   _tprintf(_T("s =%s\n"), s.c_str());
The method basic_string::c_str returns a const pointer to the underlying character type; in this case, that character type is either const char* or const wchar_t*.
Figure 2 shows a simple program I wrote that illustrates tstring. It writes "Hello, world" to a file and reports how many bytes were written. I set the project up so it uses Unicode for the Debug build and MBCS for the Release build. You can compile both builds and run them to compare the results. Figure 3 shows a sample run.
////////////////////////////////////////////////////////////////
// MSDN Magazine — August 2004
// 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 on Windows XP. Tab size=3.
//

// TSTRING shows how to implement a tstring class that uses STL string or
// wstrings depending on the setting of _UNICODE, similar to TCHAR, 
// _tprintf and all the other "t" versions of functions in the C runtime.
//
// To see the difference, compile both debug and release versions. The 
// debug version uses Unicode; the release uses MBCS. Then run each 
// program and compare the output files.
//
#include "stdafx.h"
#include "resource.h"

using namespace std;

// tstring is either string or wstring, depending on _UNICODE.
// This works too, but may produce an extra class:
//
//   typedef basic_string<TCHAR, char_traits<TCHAR>, 
//      allocator<TCHAR> > tstring;
//
#ifdef _UNICODE
#define tstring wstring
#else
#define tstring string
#endif

static void WriteString(HANDLE f, LPCTSTR lpsz, int len);

void _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
   // process args
   if (argc != 2) {
      _tprintf(_T("Usage: tstring [filename]\n"));
      _tprintf(_T("       writes test message to [filename]\n"));
      return;
   }

   // CreateFile will create Unicode or MBCS string
   // depending on value of _UNICODE.
   LPCTSTR filename = argv[1];
   HANDLE f = CreateFile(filename, ...);
   if (f!=INVALID_HANDLE_VALUE) 
   {
      if (GetFileType(f) == FILE_TYPE_DISK)
      {
         // create STL tstring
         tstring s = _T("Hello, world");
         WriteString(f, s.c_str(), s.length());
      } else {
         tprintf(_T("ERROR: the specified file '%s' is not a disk file\n"),
                 filename);
      }
      CloseHandle(f); // close file
   } else {
      _tprintf(_T("ERROR: can't open '%s'\n"), filename);
   }
}

////////////////
// write string to file. 
//
void WriteString(HANDLE f, LPCTSTR lpsz, int len)
{
   DWORD nWrite = len * sizeof(TCHAR);
   DWORD nActual;
   if (WriteFile(f, lpsz, nWrite, &nActual, NULL)) {
      // display results.
      _tprintf(_T("%d bytes written\n sizeof(TCHAR)=%d\n"), nActual, 
                  sizeof(TCHAR));
   } else {
      _tprintf(_T("ERROR %d writing\n"), GetLastError());
   }
}
Figure 3 tstring in Action 
By the way, MFC's CString is now married to ATL so that both MFC and ATL use the same string implementation. The combined implementation uses a template class called CStringT that works like STL's basic_string in the sense that it lets you create a CString class based on any underlying character type. The MFC include file afxstr.h defines three string types, like so:
typedef ATL::CStringT<wchar_t, 
  StrTraitMFC<wchar_t>> CStringW;
typedef ATL::CStringT<char, 
  StrTraitMFC<char>> CStringA;
typedef ATL::CStringT<TCHAR, 
  StrTraitMFC<TCHAR>> CString;
CStringW, CStringA, and CString are just what you would expect: wide, ASCII, and TCHAR versions of CString.
So which is better, STL or CStrings? Both classes are fine, and you should use whichever you like best. One issue to consider is which libraries you want to link with and whether you're already using ATL/MFC or not. From a coding perspective, I prefer CString for two features. First, you can initialize a CString from either wide or char strings:
CString s1 = "foo";
CString s2 = _T("bar");
Both initializations work because CString silently performs whatever conversions are necessary. With STL strings, you can't initialize a tstring without using _T() because you can't initialize a wstring from a char* or vice versa. The other feature I like about CString is its automatic conversion operator to LPCTSTR, which lets you write the following:
   CString s;
   LPCTSTR lpsz = s;
With STL, on the other hand, you have to explicitly call c_str. This is really nit-picking and some would even argue it's better to know when you're performing a conversion. For example, CStrings can get you in trouble with functions that use C-style variable arguments (varargs), such as printf:
   printf("s=%s\n", s); // Error: thinks s is char*
   printf("s=%s\n", (LPCTSTR)s); // required
Without the cast you can get garbage results because printf expects s to be char*. I'm sure many readers have made this error. Preventing this sort of mishap is no doubt one reason the designers of STL chose not to provide a conversion operator, insisting instead that you invoke c_str. In general, the STL folks tend to be a little more academic and purist types, whereas the Redmontonians are a little more practical and loosey-goosey. Hey, whatever. The practical differences between std::string and CString are slim.

Q I'm trying to expose my C++ library to C# and the .NET Framework using managed extensions and interop. One of my classes has a union in it, but .NET doesn't seem to like that:
class MyClass {
  union {
    int i;
    double d;
  };
};
Q I'm trying to expose my C++ library to C# and the .NET Framework using managed extensions and interop. One of my classes has a union in it, but .NET doesn't seem to like that:
class MyClass {
  union {
    int i;
    double d;
  };
};
The point of the union is to save space because I know the int and double are never used at the same time. Also, I have a lot of code that already references the union, and I prefer not to change it. How can I expose this class to .NET-compliant languages? Do I have to make each value in the union a separate member, or use methods instead?
John Bunt

A You could take either of those approaches, but you don't have to. In .NET interop, there's always a way to expose your C++ objects—well, almost always. The common language runtime (CLR) doesn't understand unions, but you can use an ordinary __value struct with some special voodoo to tell it where your members go. The magic attributes are StructLayout and FieldOffset. In managed C++, it looks like this:
[StructLayout(LayoutKind::Explicit)]
public __value struct MyUnion {
  [FieldOffset(0)] int i;
  [FieldOffset(0)] double d;
};
This tells the CLR that the integer i and the double d are both at offset zero (that is, the first items) in your struct, which makes them overlap and, in effect, the struct is a union. You can then use MyUnion in your __gc class, like so:
public __gc class CUnionClass {
public:
  // can access this directly because it's public
  MyUnion uval;
};
A You could take either of those approaches, but you don't have to. In .NET interop, there's always a way to expose your C++ objects—well, almost always. The common language runtime (CLR) doesn't understand unions, but you can use an ordinary __value struct with some special voodoo to tell it where your members go. The magic attributes are StructLayout and FieldOffset. In managed C++, it looks like this:
[StructLayout(LayoutKind::Explicit)]
public __value struct MyUnion {
  [FieldOffset(0)] int i;
  [FieldOffset(0)] double d;
};
This tells the CLR that the integer i and the double d are both at offset zero (that is, the first items) in your struct, which makes them overlap and, in effect, the struct is a union. You can then use MyUnion in your __gc class, like so:
public __gc class CUnionClass {
public:
  // can access this directly because it's public
  MyUnion uval;
};
With CUnionClass defined, you can access the members i and d directly through uval from any .NET-compliant language. In C# it looks like the following code snippet:
CUnionClass obj = new CUnionClass();
obj.uval.i = 17;
obj.uval.d = 3.14159
I wrote all this up in a little program Called MCUnion that implements a managed C++ library with a CUnionClass like the one shown earlier, and a C# program utest that tests it (see Figure 4 and Figure 5). CUnionClass shows how to add properties for the union members, so you can access the values using obj.i and obj.d instead of obj.uval.i and obj.uval.d. This may or may not be what you want, depending on your design. If you like, you could make uval private/protected, so clients must use the properties. This would completely hide the union nature of uval. The test program accesses i and d both ways—through the union (uval) itself and through the properties i and d.
////////////////////////////////////////////////////////////////
// MSDN Magazine — August 2004
// 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 on Windows XP. Tab size=3.
//

// This module shows how to export a C-style union as a managed __value
// struct using StructLayout and FieldOffset. To compile this program and 
// the C# test program that uses it, cd to this directory and type
//
// NMAKE
//
#using <mscorlib.dll>

using namespace System;
using namespace System::Runtime::InteropServices;

// Handy definitions to save typing ugly __'s.
#define DOTNET __gc
#define PROPERTY __property
#define VALUE    __value

namespace MCLib {

//////////////////
// Here's a typical C/C++ union:
//
// union {
//    int i;
//    double d;
// };
//
// And here's how to implement it for consumption by .NET:
//
[StructLayout(LayoutKind::Explicit)]
public VALUE struct MyUnion {
   [FieldOffset(0)] int i;
   [FieldOffset(0)] double d;
};

// This exported class contains the union as a member.
// The C# program test.cs shows how to use it.
//
public DOTNET class CUnionClass {
public:
   // can access this directly because it's public
   MyUnion uval;

   // additional access through properties
   PROPERTY int  get_i()      { return uval.i; }
   PROPERTY void set_i(int i) { uval.i = i; }

   PROPERTY double get_d()    { return uval.d; }
   PROPERTY void   set_d(double d) { uval.d = d; }
};

}
////////////////////////////////////////////////////////////////
// MSDN Magazine — August 2004
// 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 on Windows XP. Tab size=3.
//
// This C# program tests the union defined in MCLib.cpp.
//
using System;
using MCLib; // home-brew MC++ class lib

class MyApp {
   // main entry point
   [STAThread]
   static int Main(string[] args) {
      CUnionClass obj = new CUnionClass();

      // Note that this code accesses the embedded union both directly
      // through obj.val and indirectly through the properties i and d.
      obj.uval.i = 17;
      Console.WriteLine("Set i=17:");
      Console.WriteLine("    i={0}", obj.i);
      Console.WriteLine("    d={0}", obj.d);

      obj.uval.d = 3.14159;
      Console.WriteLine();
      Console.WriteLine("Set d=3.14159:");
      Console.WriteLine("    i={0}", obj.i);
      Console.WriteLine("    d={0}", obj.d);
      return 0;
   }
}

Q I'm writing a DirectX® screen saver and need to obtain a list of JPG, BMP, GIF, and TGA files from the user's My Pictures directory as a default before they learn where the screen saver settings are and can load them on their own. I have no problem setting the image textures into DirectX, but I'm afraid the My Pictures directory will be different for each user. On my machine it's "C:\Documents and Settings\Administrator\My Documents\My Pictures". Is there a simple way to get the location of my pictures?
Q I'm writing a DirectX® screen saver and need to obtain a list of JPG, BMP, GIF, and TGA files from the user's My Pictures directory as a default before they learn where the screen saver settings are and can load them on their own. I have no problem setting the image textures into DirectX, but I'm afraid the My Pictures directory will be different for each user. On my machine it's "C:\Documents and Settings\Administrator\My Documents\My Pictures". Is there a simple way to get the location of my pictures?
Bruce MacFarlane

A Indeed, there is. The function you want is SHGetSpecialFolderPath, which gets the location of a special folder using a predefined ID like CSIDL_MYPICTURES, defined in ShlObj.h along with a bunch of other shell stuff. For example:
TCHAR buf[MAX_PATH];
SHGetSpecialFolderPath(NULL, // HWND
  buf,
  CSIDL_MYPICTURES,
  NULL); // don't create
A Indeed, there is. The function you want is SHGetSpecialFolderPath, which gets the location of a special folder using a predefined ID like CSIDL_MYPICTURES, defined in ShlObj.h along with a bunch of other shell stuff. For example:
TCHAR buf[MAX_PATH];
SHGetSpecialFolderPath(NULL, // HWND
  buf,
  CSIDL_MYPICTURES,
  NULL); // don't create
You should always use SHGetSpecialFolderPath to get the path name of a special folder (as opposed to probing the registry directly) because it's guaranteed to work across all versions of Windows, including future ones even if the Redmondtonians ever decide to change the registry keys where the special folder path names are stored. For Windows 2000 and Windows XP, SHGetSpecialFolderPath lives in shell32.dll. Older versions of Windows such as Windows 9x and Windows NT® don't have SHGetSpecialFolderPath, but Microsoft provides it in a special DLL, SHFOLDER.DLL, which you're free to distribute with your app.
In fact, the official documentation from Redmond says, "Software vendors are encouraged to redistribute [SHFOLDER.DLL] as much as possible to enable this support on Windows operating systems prior to Windows 2000." The only catch is if you're targeting older versions of the Windows operating system, but building on Windows 2000 or Windows XP, you must explicitly link with SHFOLDER.DLL; otherwise the linker will grab SHGetSpecialFolderPath from Shell32.dll.
Since this is a C++ column, I wrote a little class called CSpecialFolder (see Figure 6) that's derived from CString and automatically calls SHGetSpecialFolderPath. To use it, all you have to write is:
CSpecialFolder mypics(CSIDL_MYPICTURES);
LPCTSTR lpszPath = mypics;
////////////////////////////////////////////////////////////////
// MSDN Magazine — August 2004
// 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 on Windows XP. Tab size=3.
//

// Class to get the path name of a special folder. To use:
//
//    CSpecialFolder sf(CSIDL_MYPICTURES)
//
// Now the string holds the path name.
//
class CSpecialFolder : public CString
{
protected:
   BOOL GetSpecialFolder(int nFolder) {
      Empty();
      BOOL bRet = SHGetSpecialFolderPath(NULL, GetBuffer(MAX_PATH), 
                                         nFolder, FALSE);
      ReleaseBuffer();
      return bRet;
   }

public:
   CSpecialFolder(int nFolder) {
      GetSpecialFolder(nFolder);
   }
   CSpecialFolder& operator=(int nFolder) {
      GetSpecialFolder(nFolder);
   }
};
The assignment works because CSpecialFolder is derived from CString, which has an implicit conversion operator to LPCTSTR. CSpecialFolder is available in the download, along with a test program that displays the path names of all the special folders that have CSIDL_XXX IDs defined in ShlObj.h. This includes familiar folders like Favorites, Fonts, Programs, History, and AppData—as well as some strange ones like CSIDL_BITBUCKET (Recycle Bin), CSIDL_INTERNET (path to Microsoft® Internet Explorer icon, I believe), and CSIDL_SYSTEMX86 (x86 system directory on RISC/Alpha systems for Windows 2000).

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


Paul DiLascia is a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). Paul can be reached at http://www.dilascia.com.

Page view tracker