分类: C/C++
2008-04-23 21:53:54
C Q&A 专栏...
原著:Paul DiLascia
翻译:
原文出处:
原代码下载: (234KB)
我想让用户双击程序图标时按住 Control 键,以一种特殊的方式来启动程序。 但::GetCommandLine 和__argc 均没有任何反应,用 MFC 中的 CCommandLineInfo 似乎也是如此。有没有一种方法可以解决这个问题呢?
if (GetKeyState(VK_CONTROL)<0) { // enter special mode }Figure 1 给出了一个基于 MFC 的示例程序代码段,你可以通过本文顶端的链接进行下载,如果用户在启动程序的时候按下Ctrl F8,它将显示一个消息框,并且 发出蜂鸣声。如果你只是想检查 Control键,可以忽略对 VK_F8 键的测试。
// abridged from tchar.h #ifdef _UNICODE typedef wchar_t TCHAR; #define __T(x) L ## x #else typedef char TCHAR; #define __T(x) x #endif当你在工程设置中选择 Unicode 字符集时,编译器会用 _UNICODE 定义进行编译。如果你选择MBCS(多字节字符集),则编译器将不会带 _UNICODE 定义 。一切取决于_UNICODE 的值。同样,每一个使用字符指针的 Windows API 函数会有一个 A(ASCII) 和一个 W(Wide/Unicode) 版本,这些版本的 实际定义也是根据 _UNICODE 的值来决定:
#ifdef UNICODE #define CreateFile CreateFileW #else #define CreateFile CreateFileA #endif同样,_tprintf 和 _tscanf 对应于 printf 和 scanf。所有带"t"的版本使用 TCHARs 取代了chars。那么怎样把以上的这些应用到 std::string 上呢?很简单。STL已经有一个使用宽字符定义的wstring类 (在 xstring 头文件中定义)。string 和 wstring 均是使用 typedef 定义的模板类,基于 basic_string, 用它可以创建任何字符类型的字符串类。以下就是 STL 定义的 string 和 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;模板被潜在的字符类型(char 或 wchar_t)参数化,因此,对于 TCHAR 版本,所要做的就是使用 TCHAR 来模仿定义。
typedef basic_string< TCHAR, char_traits< TCHAR >, allocator< TCHAR > > tstring;现在便有了一个 tstring,它基于 TCHAR——也就是说,它要么是 char,要么是 wchar_t,这取决于 _UNICODE 的值。 以上示范并指出了 STL 是怎样使用 basic_string 来实现基于任何类型的字符串的。定义一个新的 typedef 并不是解决此问题最有效的方法。一个更好的方法是基于 string 和wstring 来简单 地定义 tstring,如下:
#ifdef _UNICODE #define tstring wstring #else #define tstring string #endif这个方法之所以更好,是因为 STL 中已经定义了 string 和 wstring,那为什么还要使用模板来定义一个新的和其中之一一样的字符串类呢? 暂且叫它 tstring。可以用 #define 将 tstring 定义为 string 和 wstring,这样可以避免创建另外一个模板类( 虽然当今的编译器非常智能,如果它把该副本类丢弃,我一点也不奇怪)。[编辑更新-2004/07/30:typedef 不创建新类,只是为某个类型引入限定范围的名称,typedef 决不会定义一个新的类型]。不管怎样,一旦定义了 tstring,便可以像下面这样编码:
tstring s = _T("Hello, world"); _tprintf(_T("s =%s\n"), s.c_str());basic_string::c_str 方法返回一个指向潜在字符类型的常量指针;在这里,该字符类型要么是const char*,要么是 const wchar_t*。
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 和 CString 正是你所期望的:CString 的宽字符,ASCII 和 TCHAR 版本。
CString s1 = "foo"; CString s2 = _T("bar");这两个初始化都正常工作,因为 CString 自己进行了所有必要的转换。使用 STL 字符串,你必须使用_T()对 tstring 进行初始化,因为你 无法通过一个char*初始化一个wstring,反之亦然。
CString s; LPCTSTR lpsz = s;另一方面,使用 STL 必须显式调用 c_str 来完成这种转换。这确实有点挑剔,某些人会争辩说,这样能更好地了解何时进行转换。比如, 在C风格可变参数的函数中使用 CString 可能会有麻烦,像 printf:
printf("s=%s\n", s); // 错误 printf("s=%s\n", (LPCTSTR)s); // 必需的没有强制类型转换的话,得到的是一些垃圾结果,因为 printf 希望 s 是 char*。我敢肯定很多读者都犯过这种错误。防止这种灾祸是 STL 设计者不提供转换操作符的一个毋庸置疑的理由。而是坚持要你调用 c_str。一般来讲,喜欢使用 STL 家伙趋向于理论和学究气,而 Redmontonians(译者:指微软)的大佬们则更注重实用和散漫。嘿,不管怎样,std::string 和 CString 之间的实用差别是微不足道的。
class MyClass { union { int i; double d; }; };使用联合旨在节约空间,因为我知道int和double是绝对不可能同时使用的。同时,我的很多代码都引用了此联合类型,而且我不想更改它们。请问我怎样把这个类暴露给.Net呢?我是不是必须把联合中的每个值分别定义为成员变量,或者使用一个成员方法?
[StructLayout(LayoutKind::Explicit)] public __value struct MyUnion { [FieldOffset(0)] int i; [FieldOffset(0)] double d; };这段代码告诉 CLR,整数i和浮点数d均处于结构的零偏移处(也就是说是第一项),这样便使它们交迭,其效果就是把一个结构变成了联合。这样便可以在 __gc 类中 使用 MyUnion,像这样:
public __gc class CUnionClass { public: // 可以直接存取,因为它是 public 类型 MyUnion uval; };有了 CUnionClass 的定义,便可以在任何 .Net 语言中通过 uval 直接存取成员i和d。在C#中,它看起来像下面这样:
CUnionClass obj = new CUnionClass(); obj.uval.i = 17; obj.uval.d = 3.14159我写了一个名为 MCUnion 的小程序,它实现了一个托管C 库,它包含前面所示的 CUnionClass,还有一个用于测试这个C 库的 C# 程序 utest(参见 Figure 4和 Figure 5)。CUnionClass 示范了如何为联合成员添加属性,这样你就可以通过 obj.i 和 obj.d,而不是 obj.uval.i 和 obj.uval.d 来 存取值。依照你的设计,这可能是,也可能不是你所想要的结果。如果你愿意,你可以将 uval 设置为 private 或者protected 类型,这样客户端就必须使用属性。这将完全隐藏 uval 的联合 实质特性。测试程序通过联合本身和属性 i 和 d 两种方式都可以存取 i 和 d。
TCHAR buf[MAX_PATH]; SHGetSpecialFolderPath(NULL, // HWND buf, CSIDL_MYPICTURES, NULL); // don''t create应该总是使用 SHGetSpecialFolderPath 获得专用文件夹的名称(而不是直接搜寻注册表),因为它保证可以在所有版本的 Windows 系统中工作, 包括未来的版本,即便微软的大佬们修改存储专用文件夹路径的注册表键值。对于 Windows 2000 和 Windows XP来说,SHGetSpecialFolderPath 在shell32.dll中。而 Windows 9x 和 Windows NT 等较旧版本不含 SHGetSpecialFolderPath,但Microsoft 通过一个专门的 DLL 提供——SHFOLDER.DLL,你可以 随自己的应用程序免费分发这个DLL文件。
CSpecialFolder mypics(CSIDL_MYPICTURES); LPCTSTR lpszPath = mypics;这样赋值是行得通的,因为 CSpecialFolder 从 CString 派生而来,它含有一个隐式的到 LPCTSTR 的转换操作符。CSpecialFolder 可以 从下载包中得到,附带有一个测试程序,它可以显示所有在 ShlObj.h 文件中有 CSIDL_XXX 定义的专用文件夹路径名称。其中包含大家熟悉的文件夹,如 :Favorites(收藏夹),Fonts(字体),Programs(程序),History(历史),AppData(应用程序数据)——以及一些 奇怪的文件夹,比如:CSIDL_BITBUCKET(回收站),CSIDL_INTERNET(我想是指 Microsoft IE图标的路径),还有 CSIDL_SYSTEMX86(在 RISC/Alpha For Windows 2000 上,x86 的系统目录)。
向 Paul 提问和评论请发到 cppqa@microsoft.com.