不要在接口定义中使用 TCHAR 字符串
2009-09-29 14:37
274 查看
先上个图,July 早期版本的“运行”对话框。
其实在我最初的构想中,这个对话框的提示文字并不是系统默认的文字,而应该是我写上去的——最起码把“Windows”替换成 “July”。
但是我最后放弃了,因为我写上去的字到最后显示出来是乱码。由于 RunFileDlg 这个 API 并没有详细的文档支持,而且其时的我也没有任何逆向的能力,所以干脆把文本参数给了个 NULL,直接用默认提示算球了。
现在回想起来其实是 TCHAR 惹的祸。在当时,我从网上得到的 RunFileDlg 声明是类似这个样子:
我的开发环境是 VC6,工程设置没改过。于是一调用,乱码个球了。因为 RunFileDlg 的字符串参数实际上是 LPCWSTR,而 VC6 默认会把 LPCTSTR 定义为 LPCSTR。
我想,现在该重新审视一下 TCHAR 的意义了。它,以及 LPTSTR、LPCTSTR 都是一个 typedef,而并非一个具体的类型。它会随着编译器是否定义了 UNICODE 宏而指向真正的类型,也就是 WCHAR 或 char。换句话说,TCHAR 的确实现了 Unicode 和 ANSI 编码的兼容,但这种兼容是在源代码一级的,在编译好的 PE 可执行映像中,该是什么还是什么。考虑下面的代码:
我曾见过无数类似的代码,甚至我也这么写过。在 ANSI 的编译环境下,这么写是没问题的,但是如果定义了 UNICODE,那么这段代码就会收到编译错误,因为 lstrcpy 将会被定义成 lstrcpyW,需求的参数类型是 LPWSTR 和 LPCWSTR。
也许你会说:我自己有能力控制我的源代码,知道自己用的是 lstrcpyA 还是 lstrcpyW,你管得着么?
的确,这是你的自由,我也不会干涉你的代码风格,更不会质疑你对代码的控制力。
但是,如果你的代码要提供给别人,而且你提供的只是一个接口;甚至,这个接口跨越了 PE 边界,那你总应该负点责吧?
别让你那含混不清的 TCHAR 给你们双方都带来麻烦。
----------传说中的分隔线----------
如果我提供给你一个 .dll 和一个 .h,而这个 .h 中的接口定义是用 TCHAR 来描述的,那会发生什么事?
你分别写 A 和 W 的两套代码来测试这个 TCHAR 到底代表 char 还是 WCHAR。这麻烦了你。
你直接问我这个 TCHAR 代表什么。这麻烦了我们俩。
所以,何必呢?
其实在我最初的构想中,这个对话框的提示文字并不是系统默认的文字,而应该是我写上去的——最起码把“Windows”替换成 “July”。
但是我最后放弃了,因为我写上去的字到最后显示出来是乱码。由于 RunFileDlg 这个 API 并没有详细的文档支持,而且其时的我也没有任何逆向的能力,所以干脆把文本参数给了个 NULL,直接用默认提示算球了。
现在回想起来其实是 TCHAR 惹的祸。在当时,我从网上得到的 RunFileDlg 声明是类似这个样子:
int WINAPI RunFileDlg( HWND hwndParent, HICON hIcon, LPCTSTR pszWorkingDir, LPCTSTR pszTitle, LPCTSTR pszPrompt, DWORD dwFlags );
我的开发环境是 VC6,工程设置没改过。于是一调用,乱码个球了。因为 RunFileDlg 的字符串参数实际上是 LPCWSTR,而 VC6 默认会把 LPCTSTR 定义为 LPCSTR。
我想,现在该重新审视一下 TCHAR 的意义了。它,以及 LPTSTR、LPCTSTR 都是一个 typedef,而并非一个具体的类型。它会随着编译器是否定义了 UNICODE 宏而指向真正的类型,也就是 WCHAR 或 char。换句话说,TCHAR 的确实现了 Unicode 和 ANSI 编码的兼容,但这种兼容是在源代码一级的,在编译好的 PE 可执行映像中,该是什么还是什么。考虑下面的代码:
char str[32]; lstrcpy(str, "Hello, World");
我曾见过无数类似的代码,甚至我也这么写过。在 ANSI 的编译环境下,这么写是没问题的,但是如果定义了 UNICODE,那么这段代码就会收到编译错误,因为 lstrcpy 将会被定义成 lstrcpyW,需求的参数类型是 LPWSTR 和 LPCWSTR。
也许你会说:我自己有能力控制我的源代码,知道自己用的是 lstrcpyA 还是 lstrcpyW,你管得着么?
的确,这是你的自由,我也不会干涉你的代码风格,更不会质疑你对代码的控制力。
但是,如果你的代码要提供给别人,而且你提供的只是一个接口;甚至,这个接口跨越了 PE 边界,那你总应该负点责吧?
别让你那含混不清的 TCHAR 给你们双方都带来麻烦。
----------传说中的分隔线----------
如果我提供给你一个 .dll 和一个 .h,而这个 .h 中的接口定义是用 TCHAR 来描述的,那会发生什么事?
你分别写 A 和 W 的两套代码来测试这个 TCHAR 到底代表 char 还是 WCHAR。这麻烦了你。
你直接问我这个 TCHAR 代表什么。这麻烦了我们俩。
所以,何必呢?
相关文章推荐
- 不要在接口定义中使用 TCHAR 字符串
- 使用IFormattable接口为自定 4000 义类定义格式字符串
- 02_js中接口的定义和使用
- [转]在C#中使用IDL文件,IDL是个接口定义文件
- 【Android】Android中Serializable接口的定义和使用
- C++文本查询程序 不要定义类和智能指针管理数据 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果
- C++文本查询程序 不要定义类和智能指针管理数据 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果
- 关于asp.net(c#),webconfig中如何定义一个字符串让所有页面共同使用?
- VC++ 使用attributes定义接口
- android接口定义语言(AIDL)的使用示例
- 详解Swift中对C语言接口缓存的使用以及数组与字符串转为指针类型的方法
- 使用接口定义标准
- 为什么不要工程中不要随意使用define定义常量
- XAF-Domain Components 技术 使用接口来定义ORM业务对象
- C#字符串的定义和使用
- 不要在setTimeout中使用字符串
- [疯狂Java]泛型:泛型的定义(类、接口、对象)、使用、继承
- 不要在linux上使用java 7 Files的接口参数StandardOpenOption.DELETE_ON_CLOSE
- C++文本查询程序 不要定义类和智能指针管理数据 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果
- C++文本查询程序 不要定义类和智能指针管理数据 C++Primer练习12.28 使用vector,map,set容器保存来自文件的数据并生成查询结果