STL字符串类与UNICODE及其它
副标题#e#
我想让用户双击措施图标时按住 Control 键,以一种非凡的方法来启动措施。 但::GetCommandLine 和__argc 均没有任何回响,用 MFC 中的 CCommandLineInfo 好像也是如此。有没有一种要领可以办理这个问题呢?
有,很是简朴。你所要做的就是挪用 GetKeyState。当你正在处理惩罚的当前动静被发送时, 该函数返回虚拟键的状态。这个状态大概是弹起,按下,可能套索钉。套索钉用于大写锁定( Caps)和转换锁(Shift Lock),它们可以转换状态。对付一般的 键,如节制键(VK_CONTROL),假如键被按下,则其状态的高位标识位为 1。
很多的应用措施利用 Control+F8 作为非凡键来启动规复模式。好比,假如应用措施答允用户 定制事情间,那么Control+F8就可以将其规复到初始的默认配置,只是在规复之前必然要让用户举办确认。做的更好一点的话,你可以在单独的INI文件中生存用户的配置,这样用户 有时机规复它们。不管奈何,要想在措施启动时查抄 Control 键,你可以像下面这样写:
if (GetKeyState(VK_CONTROL)<0)
{
// enter special mode
}
Figure 1 给出了一个基于 MFC 的示例措施代码段,你可以通过本文顶端的链接举办下载,假如用户在启动措施的时候按下Ctrl+F8,它将显示一个动静框,而且 发出蜂鸣声。假如你只是想查抄 Control键,可以忽略对 VK_F8 键的测试。
我常常在 C++ 措施中利用尺度模板库(STL)的 std::string 类,但在 利用 Unicode 时遇到了问题。在利用通例 C 气势气魄的字符串时,我可以利用 TCHAR 和 _T 宏,这样针对 Unicode 或 ASCII 均可以举办编译,但我 老是发明这种ASCII/Unicode的团结很难与 STL 的 string 类一起利用。你有什么好的发起吗?
是的,一旦知道 TCHAR 和_T 是如何事情的,那么这个问题很简朴。根基思想是 TCHAR 要么是char,要么是 wchar_t,这取决于 _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
当你在工程配置中选择 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;
#p#副标题#e#
此刻便有了一个 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*。
#p#分页标题#e#
Figure 2 是一个简朴的示范措施,举例说明白 tstring 的用法。它将“Hello,world”写入一个文件,并陈诉写了几多个字节。我对 工程举办了配置,以便用 Unicode 生成 debug 版本,用 MBCS 生成 Release 版本。你可以别离举办编译/生成并运行措施,然后较量功效。Figure 3 显示了例子的运行环境。
Figure 3 运行中的 tstring
顺便说一下,MFC 和 ATL 此刻已经联婚,以便都利用沟通的字符串实现。团结后的实现利用一个叫做 CStringT 的模板类,这在某种意义上 ,其机制雷同 STL 的 basic_string,用它可以按照任何潜在的字符范例来建设 CString 类。在 MFC 包括文件 afxstr.h 中界说了三种字符 串范例,如下:
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 版本。
那么,哪一个更好,STL 照旧 CStirng?两者都很好,你可以选择你最喜欢的一个。但有一个问题要思量到:就是你想要链接哪个库,以及你是否已经在利用 MFC/ATL。从编码 的角度来看,我更喜欢 CString 的两个特性:
其一是无论利用宽字符照旧char,都可以很利便地对 CString 举办初始化。
CString s1 = "foo";
CString s2 = _T("bar");
这两个初始化都正常事情,因为 CString 本身举办了所有须要的转换。利用 STL 字符串,你必需利用_T()对 tstring 举办初始化,因为你 无法通过一个char*初始化一个wstring,反之亦然。
其二是 CString 对 LPCTSTR 的自动转换操纵,你可以像下面这样编码:
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 之间的实用不同是微不敷道的。
我正在试图用托管扩展和互用性(interop)向 C# 和 .Net 框架袒露我的 C++ 库。我的一个类中含有一个连系(union)范例,但 .Net 好像并不支持这种范例:
class MyClass
{
union
{
int i;
double d;
};
};
利用连系旨在节省空间,因为我知道int和double是绝对不行能同时利用的。同时,我的许多代码都引用了此连系范例,并且我不想变动它们。请问我奈何把这个类袒露给.Net呢?我是不是必需把连系中的每个值别离界说为成员变量,可能利用一个成员要领?
你可以利用这些要领中的任意一个,但不必然非要这么做。在 .Net 的互用性机制中,总有 步伐来很好地袒露C++工具——这么说吧,险些总有步伐。民众语言运行时(CLR)不能领略连系范例,但可以用某些非凡能力的通例 __value struct 来汇报 它成员在那边。这个神奇的属性就是 StructLayout 和FieldOffset。在托管 C++ 中,它看起来象这样:
[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
#p#分页标题#e#
我写了一个名为 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。
我正在写一个 DirectX 屏保,需要在用户举办屏保配置之前,将从用户 My Pictures 目次下得到JPG,BMP,GIF 和 TGA 文件列表 作为一种默认配置并自行加载它们。将图像纹理配置到 DirectX 中没有什么问题,但我有点担忧的就是差异的用户其 My Pictures 目次大概会不 同。在我的呆板上,这个路径为“C:\Documents and Settings\Administrator\My Documents\My Pictures”。有没有一个简朴的要领得到 My Pictures 的位置呢?
是的,有一个简朴的要领。你需要的函数是 SHGetSpecialFolderPath,它可以通过一个预界说的ID,如 CSIDL_MYPICTURES 来找到 对应的专用文件夹,该函数被界说在 ShlObj.h中,个中还界说了许多其它的外壳元素。好比:
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文件。
事实上,来自 Redmond 的官方文档如是说:“勉励软件供给商尽大概多地从头分发 SHFOLDER.DLL 文件,以便支持 Windows 2000 以前 各个版本。”独一需要留意的是:假如你的应用措施是面向旧版本的 Windows 操纵系统,可是在 Windows 2000 或 Windows XP上生成的, 那么你必需显式的链接 SHFOLDER.DLL;不然链接器将从 Shell32.dll 中获得 SHGetSpecialFolderPath。
既然这是一个 C++ 专栏,所以我写了一个叫做 CSpecialFolder 的小类(拜见 Figure 6),它从 CString 派生,并会自动挪用 SHGetSpecialFolderPath。 其利用要领如下:
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 的系统目次)。
本文配套源码