C++ Qamp;A

GetKeyState, the STL String Class, Exposing C++ Objects, and More

Paul DiLascia

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

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.

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.

Figure 1 GetKeyState

//////////////////////////////////////////////////////////////// // 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).

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.

Figure 2 tstring

//////////////////////////////////////////////////////////////// // 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

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.

Figure 5 C-Style Unions

//////////////////////////////////////////////////////////////// // 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; } }; }

Figure 4 utest

//////////////////////////////////////////////////////////////// // 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;

Figure 6 CSpecialFolder

//////////////////////////////////////////////////////////////// // 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 https://www.dilascia.com.