一、Windows的图标、光标、字符串和自订资源—自订的资源
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) { TCHAR szAppName [16], szCaption [64], szErrMsg [64] ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; LoadString ( hInstance, IDS_APPNAME, szAppName, sizeof (szAppName) / sizeof (TCHAR)) ; LoadString ( hInstance, IDS_CAPTION, szCaption, sizeof (szCaption) / sizeof (TCHAR)) ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (hInstance, szAppName) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { LoadStringA (hInstance, IDS_APPNAME, (char *) szAppName, sizeof (szAppName)) ; LoadStringA (hInstance, IDS_ERRMSG, (char *) szErrMsg, sizeof (szErrMsg)) ; MessageBoxA (NULL, (char *) szErrMsg, (char *) szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, szCaption, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static char * pText ; static HGLOBAL hResource ; static HWND hScroll ; static int iPosition, cxChar, cyChar, cyClient, iNumLines, xScroll ; HDC hdc ; PAINTSTRUCT ps ; RECT rect ; TEXTMETRIC tm ; switch (message) { case WM_CREATE : hdc = GetDC (hwnd) ; GetTextMetrics (hdc, &tm) ; cxChar = tm.tmAveCharWidth ; cyChar = tm.tmHeight + tm.tmExternalLeading ; ReleaseDC (hwnd, hdc) ; xScroll = GetSystemMetrics (SM_CXVSCROLL) ; hScroll = CreateWindow (TEXT ("scrollbar"), NULL, WS_CHILD | WS_VISIBLE | SBS_VERT, 0, 0, 0, 0, hwnd, (HMENU) 1, hInst, NULL) ; hResource = LoadResource (hInst, FindResource (hInst, TEXT ("AnnabelLee"), TEXT ("TEXT"))) ; pText = (char *) LockResource (hResource) ; iNumLines = 0 ; while (*pText != '\\' && *pText != '\0') { if (*pText == '\n') iNumLines ++ ; pText = AnsiNext (pText) ; } *pText = '\0' ; SetScrollRange (hScroll, SB_CTL, 0, iNumLines, FALSE) ; SetScrollPos (hScroll, SB_CTL, 0, FALSE) ; return 0 ; case WM_SIZE : MoveWindow (hScroll, LOWORD (lParam) - xScroll, 0, xScroll, cyClient = HIWORD (lParam), TRUE) ; SetFocus (hwnd) ; return 0 ; case WM_SETFOCUS : SetFocus (hScroll) ; return 0 ; case WM_VSCROLL : switch (wParam) { case SB_TOP : iPosition = 0 ; break ; case SB_BOTTOM : iPosition = iNumLines ; break ; case SB_LINEUP : iPosition -= 1 ; break ; case SB_LINEDOWN : iPosition += 1 ; break ; case SB_PAGEUP : iPosition -= cyClient / cyChar ; break ; case SB_PAGEDOWN : iPosition += cyClient / cyChar ; break ; case SB_THUMBPOSITION : iPosition = LOWORD (lParam) ; break ; } iPosition = max (0, min (iPosition, iNumLines)) ; if (iPosition != GetScrollPos (hScroll, SB_CTL)) { SetScrollPos (hScroll, SB_CTL, iPosition, TRUE) ; InvalidateRect (hwnd, NULL, TRUE) ; } return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; pText = (char *) LockResource (hResource) ; GetClientRect (hwnd, &rect) ; rect.left += cxChar ; rect.top += cyChar * (1 - iPosition) ; DrawTextA (hdc, pText, -1, &rect, DT_EXTERNALLEADING) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : FreeResource (hResource) ; PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
POEPOEM.RC (摘录) //Microsoft Developer Studio generated resource script. #include "resource.h" #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// // TEXT ANNABELLEE TEXT DISCARDABLE "poepoem.txt" ///////////////////////////////////////////////////////////////////////////// // Icon POEPOEM ICON DISCARDABLE "poepoem.ico" ///////////////////////////////////////////////////////////////////////////// // String Table STRINGTABLE DISCARDABLE BEGIN IDS_APPNAME "PoePoem" IDS_CAPTION """Annabel Lee"" by Edgar Allan Poe" IDS_ERRMSG "This program requires Windows NT!" END
RESOURCE.H (摘录) // Microsoft Developer Studio generated include file. // Used by PoePoem.rc #define IDS_APPNAME 1 #define IDS_CAPTION 2 #define IDS_ERRMSG 3
POEPOEM.TXT It was many and many a year ago, In a kingdom by the sea, That a maiden there lived whom you may know By the name of Annabel Lee; And this maiden she lived with no other thought Than to love and be loved by me. I was a child and she was a child In this kingdom by the sea, But we loved with a love that was more than love -- I and my Annabel Lee -- With a love that the winged seraphs of Heaven Coveted her and me. And this was the reason that, long ago, In this kingdom by the sea, A wind blew out of a cloud, chilling My beautiful Annabel Lee; So that her highborn kinsmen came And bore her away from me, To shut her up in a sepulchre In this kingdom by the sea. The angels, not half so happy in Heaven, Went envying her and me -- Yes! that was the reason (as all men know, In this kingdom by the sea) That the wind came out of the cloud by night, Chilling and killing my Annabel Lee. But our love it was stronger by far than the love Of those who were older than we -- Of many far wiser than we -- And neither the angels in Heaven above Nor the demons down under the sea Can ever dissever my soul from the soul Of the beautiful Annabel Lee: For the moon never beams, without bringing me dreams Of the beautiful Annabel Lee; And the stars never rise, but I feel the bright eyes Of the beautiful Annabel Lee: And so, all the night-tide, I lie down by the side Of my darling -- my darling -- my life and my bride, In her sepulchre there by the sea -- In her tomb by the sounding sea. [May, 1849] \
POEPOEM.ICO |
在POEPOEM.RC资源描述档中,使用者定义的资源被定义为TEXT型态,取名为AnnabelLee:
ANNABELLEE TEXT POEPOEM.TXT
在WndProc处理WM_CREATE时,使用FindResource和LoadResource取得资源句柄。使用LockResource锁定资源,并且使用一个小程序将文件末尾的反斜线(\)换成0,这有利于后面WM_PAINT消息处理期间使用的DrawText函数。
注意,这里使用的是子窗口的滚动条,而不是窗口滚动条,这是因为子窗口滚动条有一个自动的键盘接口,因此在POEPOEM中没有处理WM_KEYDOWN。
POEPOEM还使用三个字符串,它们的ID在RESOURCE.H表头文件中定义。在程序的开始,IDS_APPNAME和IDS_CAPTIONPOEPOEM字符串由LoadString加载内存:
LoadString (hInstance, IDS_APPNAME, szAppName, sizeof (szAppName) / sizeof (TCHAR)) ; LoadString (hInstance, IDS_CAPTION, szCaption, sizeof (szCaption) / sizeof (TCHAR)) ;
注意RegisterClass前面的两个呼叫。如果您在Windows 98下执行Unicode版本的POEPOEM,这两个呼叫就都会失败。因此,LoadStringA比LoadStringW要复杂得多(LoadStringA必须将资源字符串由Unicode转化为ANSI,而LoadStringW仅是直接加载它),LoadStringW在Windows 98下不被支持。这意味着在Windows 98下,当RegisterClassW函数失败时,MessageBoxW函数(Windows 98支持)就不能使用LoadStringW加载程序的字符串。由于这个原因,程序使用LoadStringA加载IDS_APPNAME和IDS_ERRMSG字符串,并使用MessageBoxA显示自订的消息框:
if (!RegisterClass (&wndclass)) { LoadStringA (hInstance, IDS_APPNAME, (char *) szAppName, sizeof (szAppName)) ; LoadStringA (hInstance, IDS_ERRMSG, (char *) szErrMsg, sizeof (szErrMsg)) ; MessageBoxA (NULL, (char *) szErrMsg, (char *) szAppName, MB_ICONERROR) ; return 0 ; }
注意,TCHAR字符串变量是指向char的指针。
既然我们已经定义了用于POEPOEM的所有字符串资源,那么翻译者将程序转换成外语版本就很容易了。当然,它们将不得不翻译「Annabel Lee」这个名字-我想,这会是一项困难得多的工作。