先上个图,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 代表什么。这麻烦了我们俩。
所以,何必呢?