一、Windows的图标、光标、字符串和自订资源—字符串资源
把字符串当成资源的观念一开始可能令人觉得诡异。因为我们在使用原始码中定义为变量的一般字符串时,并没有碰到任何问题。
字符串资源主要是为了让程序转换成其它语言时更为方便。正如后面两章中将看到的一样,菜单和对话框也是资源描述文件的一部分。如果使用字符串资源而不是将字符串直接放入原始码中,那么程序所使用的所有文字将在同一文件-资源描述档中。如果转换了资源描述文件中的文字,那么建立程序的另一种语言版本所需做的一切就是重新连结程序。这种方法比重新组织原始码安全得多(然而,除了下一个范例程序,我在本书的其它程序中不使用字符串表,原因是字符串表使程序代码看起来更为模糊和复杂)。
您可以在「Insert」菜单中选择「Resource」,再选择「 String Table」,建立一个字符串表。字符串会显示在屏幕右边的列表中。通过双击字符串就可以选中它。针对每个字符串,您可以指定标识符和字符串的内容。
在资源描述中,字符串显示在一个多行的叙述中,如下所示:
STRINGTABLE DISCARDABLE BEGIN IDS_STRING1, "character string 1" IDS_STRING2, "character string 2" 其它字符串定义 END
如果您在替早期版本的Windows写程序,并在文字编辑器中手动建立这个字符串表(用Developer Studio来做这件事当然更容易得多了),您可以用左右大括号代替BEGIN和END叙述。
资源描述可以包含多个字符串表,但是每个ID必须唯一表示一个字符串。每个字符串占一行,最多4097个字符。\t可以作为制表符,\n则作为linefeed字符号。DrawText和MessageBox函数能够识别这些控制符号。
您的程序可以使用LoadString呼叫把字符串复制到程序数据段的缓冲区中:
LoadString (hInstance, id, szBuffer, iMaxLength) ;
参数id是ID,它加在资源描述文件中每个字符串的前面;szBuffer是指向接收字符串的字符数组的指针;iMaxLength是送入szBuffer中的最大字符数。函数传回字符串中的字符数。
每个字符串前面的ID一般是定义在表头文件中的宏标识符。许多Windows程序写作者使用前缀IDS_ 来表示字符串的ID。有时,文件名称或其它信息需要在字符串显示时插入到字符串中。在这种情况下,您可以将C的格式化字符放入字符串,并把它用于wsprintf中作为一个格式化字符串。
所有资源文字-包括字符串表中的文字-以Unicode格式储存在.RES编译资源文件以及最终的.EXE文件中。LoadStringW函数直接加载Unicode文字。LoadStringA函数(仅在Windows 98下有效)完成由Unicode到本地代码页的文字转换。
让我们来看一个程序,它使用三个字符串,在消息框中显示三条错误信息。RESOURCE.H表头文件为这些信息定义了三个标识符:
#define IDS_FILENOTFOUND 1 #define IDS_FILETOOBIG 2 #define IDS_FILEREADONLY 3
资源描述文件具有此字符串表:
STRINGTABLE BEGIN IDS_FILENOTFOUND, "File %s not found." IDS_FILETOOBIG, "File %s too large to edit." IDS_FILEREADONLY, "File %s is read-only." END
C原始码文件也包含这个表头文件,并定义了一个显示消息框的函数(我假定szAppName是一个包含程序名称的整体变量)。
OkMessage (HWND hwnd, int iErrorNumber, TCHAR *szFileName) { TCHAR szFormat [40] ; TCHAR szBuffer [60] ; LoadString (hInst, iErrorNumber, szFormat, 40) ; wsprintf (szBuffer, szFormat, szFilename) ; return MessageBox ( hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION) ; }
为了显示包含「file not found」信息的消息框,程序呼叫:
OkMessage (hwnd, IDS_FILENOTFOUND, szFileName) ;