⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 10. 菜单及其它资源.txt

📁 本书介绍了在Microsoft Windows 98、Microsoft Windows NT 4.0和Windows NT 5.0下程序写作的方法
💻 TXT
📖 第 1 页 / 共 5 页
字号:
在掌握这些知识之后,让我们看一看使用图示的详细情况。

取得图示句柄


如果您仔细阅读ICONDEMO.RC和RESOURCE.H文件,会看到由Developer Studio产生用于维护文件的一些标记。然而,当编译资源描述档时,只有少数几行是重要的。这些从ICONDEMO.RC和RESOURCE.H文件中摘录下来的关键部分被列在程序10-2中。

ICONDEMO.RC (摘录)
        
//Microsoft Developer Studio generated resource script.
        
#include "resource.h"
        
#include "afxres.h"
        
/////////////////////////////////////////////////////////////////////////////
        
// Icon
        
IDI_ICON                           ICON    DISCARDABLE     "icondemo.ico"
        
RESOURCE.H (摘录)
        
// Microsoft Developer Studio generated include file.
        
// Used by IconDemo.rc
        
#define IDI_ICON       101
        
程序10-2 ICONDEMO.RC和RESOURCE.H文件的摘录
 

程序10-2所显示的ICONDEMO.RC和RESOURCE.H文件与您在普通的文字编辑器中手动建立的很相似,80年代的Windows程序写作者就是这样做的。唯一不同的是AFXRES.H,它是个表头文件,包含了在建立由机器产生的MFC项目时由Developer Studio使用的常用标识符。在本书中,我们不会用到AFXRES.H。

ICONDEMO.RC中的这行

IDI_ICON ICON DISCARDABLE "icondemo.ico"
        
是资源描述档的ICON叙述。该图示有一个数值标识符IDI_ICON,等于101。由Developer Studio添加的DISCARDABLE关键词指出,必要时Windows可以从内存中丢弃图标,以获得额外的空间。之后不需要程序任何特定的操作,Windows就能够重新加载图示。DISCARDABLE属性是内定的,不需要指定。只有在名称和目录路径包含空格时,Developer Studio才将文件名加上引号。

当资源编译程序将编译的资源储存在ICONDEMO.RES中,并且由连结程序将资源添加到ICONDEMO.EXE中以后,该资源就可以经由一个资源型态(RT_ICON)和一个标识符(IDI_ICON或101)来标识。程序可以通过呼叫LoadIcon函数取得此图示的句柄:

hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON)) ;
        
请注意ICONDEMO在两个地方呼叫这个函数,一次在定义窗口类别时,另一次在窗口消息处理程序中取得图标的句柄用于绘制。LoadIcon传回HICON型态的值,它是图示的句柄。

LoadIcon的第一个参数,是指出资源来自哪个文件的执行实体句柄。使用hInstance表示它来自程序自己的.EXE文件。LoadIcon的第二个参数实际上被定义为指向字符串的指针。待会将会看到,可以使用字符串而不是用数值标识符标识资源。宏MAKEINTRESOURCE(把整数转换成资源字符串)生成指向非数字的指针,如下所示:

#define MAKEINTRESOURCE(i)  (LPTSTR) ((DWORD) ((WORD) (i)))
        
LoadIcon知道,如果第二个参数的高字组为0,那么低字组就为图示的数值标识符。图标的标识符必须为16位值。

本书前面的范例程序使用了预先定义的图示:

LoadIcon (NULL, IDI_APPLICATION) ;
        
hInstance参数被设定为NULL,因此Windows知道这是预先定义的图示。IDI_APPLICATION也在WINUSER.H中用MAKEINTRESOURCE定义:

#define IDI_APPLICATION MAKEINTRESOURCE(32512)
        
LoadIcon的第二个参数带来了一个有趣的问题:图标的标识符能可以为字符串吗?答案是可以。方法如下:在 Developer Studio中,在ICONDEMO项目的文件列表上,选择 IDONDEMO.RC。您会看到顶端为「IconDemo Resource」的树状结构,然后是资源型态「Icon」,再下来是「IDI_ICON」。如果用鼠标右键单击图标标识符,并从菜单上选择「 Properties」,您就能改变ID。实际上,您可以把名称放在引号内将其更改为字符串。我用这种方法指定资源名称,并在本书的其它地方也使用该方法。

我喜欢为图示(以及一些其它资源)使用文字名称,因为名称可以是程序的名称。例如,假定文件被命名为MYPROG。如果您使用「Icon Properties」对话框将图标的ID指定为「MyProg」(包括引号),资源描述档将包含下列叙述:

MYPROG ICON DISCARDABLE myprog.ico
        
然而,在RESOURCE.H中并没有#define叙述,来指出MYPROG是数值标识符。资源描述文件将假定MYPROG是字符串标识符。

在C程序中,使用LoadIcon函数来取得图示句柄。您可能已经有了表示程序名的字符串:

static TCHAR szAppName [] = TEXT ("MyProg") ;
        
这意味着程序可以使用叙述:

hIcon = LoadIcon (hInstance, szAppName) ;
        
来加载图标,这比宏MAKEINTRESOURCE更清晰一些。

但是如果您确实想用数字来命名,那么您可以用数字代替标识符或字符串。在「Icon Properties」对话框中,在ID栏中输入数字。资源描述档将有一个类似下面的ICON叙述:

125 ICON DISCARDABLE myprog.ico
        
可以使用两种方法之一引用图示。明显易读的方式是:

hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (125)) ;
        
另一个不易阅读的方式是:

hIcon = LoadIcon (hInstance, TEXT ("#125")) ;
        
Windows识别初始字符#作为ASCII形式中字符数值的开头。

在程序中使用图标


虽然Windows以几种方式用图标来代表程序,但是许多Windows程序仅在用WNDCLASS结构和RegisterClass定义窗口类别时指定一个图示。如我们所看到的,这样作用得很好,尤其当图示文件包含标准和较小的图像大小时,更是如此。Windows在显示图标图像时,它会在图示文件中选择最合适的图像大小。

RegisterClass有一个改进版本叫做RegisterClassEx,它使用名为WNDCLASSEX的结构。WNDCLASSEX有两个附加的字段:cbSize和hIconSm。cbSize字段指出了WNDCLASSEX结构的大小,假设hIconSm被设定为小图标的图标句柄。这样,在WNDCLASSEX结构中,您可以设定与两个图示文件相关的两个图示句柄-一个用于标准图示,一个用于小图示。

有这种必要吗?没有。正如我们看到的,Windows已经从单个图示文件中提取了大小合适的图标图像。RegisterClassEx似乎没有RegisterClass聪明。如果hIconSm字段使用了包含多个图像的图标文件,则只有第一个图像能被利用。它可能是标准大小的图示,使用时才被缩小。RegisterClassEx似乎是为了使用多个图标图像而设计的,每个图像只包含一种图标大小。因为现在可以将多个图示大小包括在同一个图示文件中,所以我建议使用WNDCLASS和RegisterClass。

如果您想在程序执行的时候,动态地更改程序的图标,可以使用SetClassLong来达到目的。例如,如果您有与标识符IDI_ALTICON相关的第二个图示文件,则您可以使用以下的叙述将其切换到那个图示:

SetClassLong (hwnd, GCL_HICON,
        
    LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ALTICON))) ;
        
如果不想储存程序图标的句柄,但要使用DrawIcon函数在别处显示它,可以使用GetClassLong获得句柄。例如:

DrawIcon (hdc, x, y, GetClassLong (hwnd, GCL_HICON)) ;
        
在Windows文件的某些部分,LoadIcon被称为「过时的」,并推荐使用LoadImage(LoadIcon在/Platform SDK/User Interface Services/Resources/Icons中说明,LoadImage在/Platform SDK/User Interface Services/Resources/Resources中说明)。当然LoadImage更为灵活,但它没有LoadIcon简单。您会注意到,在ICONDEMO中对同一个图示呼叫了LoadIcon两次。这不会产生问题,也没有使用额外的内存。LoadIcon是取得句柄但不需要清除句柄的少数几个函数之一。实际上有一个DestroyIcon函数,但它与CreateIcon、CreateIconIndirect和CreateIconFromResource连在一起使用。这些函数使程序能够动态地建立图标图像。

使用自订光标


在程序中使用自订的鼠标光标与使用自订的图示相似,只是大多数程序写作者总是使用Windows提供的光标。自订游标一般为单色,大小为32×32图素。在Developer Studio中建立光标与建立图标的方法相同(从「Insert」菜单上选择「 Resource」,然后单击「Cursor」),但不要忘记定义热点。

可以在对象类别定义中设定自订光标,叙述为:

wndclass.hCursor = LoadCursor (hInstance, MAKEINTRESOURCE (IDC_CURSOR)) ;
        
如果光标用文字名称定义,则为:

wndclass.hCursor = LoadCursor (hInstance, szCursor) ;
        
每当鼠标位于根据这个类别建立的窗口上时,就会显示与IDC_CURSOR或szCursor相对应的鼠标光标。

如果使用了子窗口,那么您可能希望光标随着所在窗口的不同而有所区别。如果程序为这些子窗口定义了窗口类别,就可以在每个窗口类别中适当地设定hCursor字段,让每个窗口类别使用不同的光标。如果使用了预先定义的子窗口控件,就可以使用以下方法改变窗口类别的hCursor字段:

SetClassLong (hwndChild, GCL_HCURSOR,
        
    LoadCursor (hInstance, TEXT ("childcursor")) ;
        
如果您将显示区域划分为较小的逻辑区域而不使用子窗口,就可以使用SetCursor来改变鼠标光标:

SetCursor (hCursor) ;
        
在处理WM_MOUSEMOVE消息处理期间,您应该呼叫SetCursor;否则,当光标移动时,Windows将使用窗口类别中定义的光标来重画光标。文件指出,如果没有改变光标,则SetCursor速度将会很快。

字符串资源


把字符串当成资源的观念一开始可能令人觉得诡异。因为我们在使用原始码中定义为变量的一般字符串时,并没有碰到任何问题。

字符串资源主要是为了让程序转换成其它语言时更为方便。正如后面两章中将看到的一样,菜单和对话框也是资源描述文件的一部分。如果使用字符串资源而不是将字符串直接放入原始码中,那么程序所使用的所有文字将在同一文件-资源描述档中。如果转换了资源描述文件中的文字,那么建立程序的另一种语言版本所需做的一切就是重新连结程序。这种方法比重新组织原始码安全得多(然而,除了下一个范例程序,我在本书的其它程序中不使用字符串表,原因是字符串表使程序代码看起来更为模糊和复杂)。

您可以在「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) ;
        
自订的资源


Windows也定义了「自订资源」,这又称为「使用者定义的资源」(使用者就是您-程序写作者,而不是那个使用您程序的幸运者)。自订资源让连结.EXE文件中的各种数据更为方便,对取得程序中的数据也是如此。资料可以是您需要的任何格式。程序用于存取自订资源的Windows函数促使Windows将数据加载内存并传回指向它的指标。然后您就可以对程序做任何操作。您会发现对于储存和存取各种自己的数据,这要比把数据储存在外部文件中,再使用文件输入函数存取它要方便得多。

例如,您有一个文件叫做BINDATA.BIN,它包含程序需要显示的一些数据。您可以选择这个文件的格式。如果在MYPROG项目中有MYPROG.RC资源描述档,您就可以在Developer Studio中从「Insert」菜单中选择「Resource」并按「 Custom」按钮,来建立自订的资源。键入表示资源的名称:例如,BINTYPE。然后,Developer Studio会生成资源名称(在这种情况下是IDR_BINTYPE1)并显示让您输入二进制数据的窗口。但是您不必输入什么,用鼠标右键单击IDR_BINTYPE1名称,并选择 Properties,然后就可以输入一个文件名称:例如,BINDATA.BIN。

资源描述档就会包含以下的一行叙述:

IDR_BINTYPE1 BINTYPE BINDATA.BIN
        
除了我们刚刚生成的BINTYPET资源型态外,这个叙述与ICONDEMO中的ICON叙述一样。有了图示后,您可以对资源名称使用文字的名称,而不是数字的标识符。

当您编译并连结程序,整个BINDATA.BIN文件会被并入MYPROG.EXE文件中。

在程序的初始化(比如,在处理WM_CREATE消息时)期间,您可以获得资源的句柄:

hResource = LoadResource (hInstance,
        
                   FindResource (hInstance, TEXT ("BINTYPE"),
        
                                                                 MAKEINTRESOURCE (IDR_BINTYPE1))) ;
        
变量hResource定义为HGLOBAL型态,它是指向内存区块的句柄。不管它的名称是什么,LoadResource不会立即将资源加载内存。把LoadResource和FindResource函数如上例般合在一起使用,在实质上就类似于LoadIcon和LoadCursor函数的做法。事实上,LoadIcon和LoadCursor函数就用到了LoadResource和FindResource函数。

当您需要存取文字时,呼叫LockResource:

pData = LockResource (hResource) ;
        
LockResource将资源加载内存(如果还没有加载的话),然后它会传回一个指向资源的指标。当结束对资源的使用时,您可以从内存中释放它:

FreeResource (hResource) ;
        
当您的程序终止时,也会释放资源,即使您没有呼叫FreeResource.。

让我们看一个使用三种资源-一个图标、一个字符串表和一个自订的资源-的范例程序。程序10-3所示的POEPOEM程序在其显示区域显示Edgar Allan Poe的「Annabel Lee」文字。自订的资源是文件POEPOEM.TXT,它包含了一段诗文,此文本文件以反斜线(\)结束。

程序10-3  POEPOEM
        
POEPOEM.C
        
/*---------------------------------------------------------------------------
        
  POEPOEM.C -- Demonstrates Custom Resource
        
                                   (c) Charles Petzold, 1998
        
----------------------------------------------------------------------------*/
        
#include <windows.h>
        
#include "resource.h"
        

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
        
HINSTANCE hInst ;
        
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
        
                                          PSTR szCmdLine, int iCmdShow)
        
{
        

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -