跳轉到內容

Windows 程式設計/資源指令碼參考

來自華夏公益教科書,自由的教科書,構建自由的世界

本附錄頁面將嘗試列出不同型別的資源,並嘗試展示如何使用這些資源。

通用結構

[編輯 | 編輯原始碼]

資源指令碼檔案是人類可讀的文字檔案,以 ANSI 或 Unicode(更嚴格地說,帶位元組順序標記 (BOM) 的 UTF-16)格式儲存。為了在 ANSI 格式中混合不同語言,存在一個奇特的 #pragma 用於在語言之間切換內碼表。Unicode、#pragma 切換和 LANGUAGE 語句僅在 Win32 中受支援。

一個典型的較小的檔案可能如下所示

#include <windows.h>
#define IDC_STATIC -1

100 ICON "ProgIcon.ico"

10 MENU
{		// or BEGIN
 POPUP "&File"
 {
  MENUITEM "&Exit",IDCANCEL
 }
}		// or END

使用花括號或 BEGIN/END 取決於您的偏好。舊的、浪費空間的風格是 BEGIN/END 對,來自 MacOS 和 Win16 API 呼叫的 Pascal 遺產。C 程式設計師通常更喜歡花括號。Visual Studio 資源編輯器始終生成 BEGIN/END 對,以及大量維護內容。

從一些標頭檔案包含和 #define 語句開始,每個資源都被包含為以下兩種形式之一

id_of_resource  resource_type  [memory management flags]  "filename"

id_of_resource  resource_type  [memory management flags]
BEGIN
 subsequent data
END

此規則的例外是

  • LANGUAGE 語句,可以放置在幾乎任何地方(僅限 Win32)
  • DIALOGVERSIONINFO 資源,在標題行和 BEGIN 之間有額外的語句
  • STRINGTABLE 資源,其中沒有資源 ID 在關鍵字之前。相反,每個字串都以一個 ID 為字首

id_of_resourceresource_type 可以是字串或數字。沒有引號!數字是首選的識別方法。所有預定義的資源型別都是數字。但要注意!如果您對資源編譯器使用未知 ID(例如 Visual Studio 6 的 MANIFEST),您將不會收到錯誤或警告,並且資源將以字串資源型別構建。Windows XP 使用 ExecProcess() 不會找到該預期清單,並且您的程式將以舊的視覺化風格顯示。

還支援使用 #if / #ifdef / #endif 進行條件編譯。

ID 的表示式僅限於非常簡單的數學運算,不允許使用布林運算子。

資源被編譯成一個三級目錄結構

  1. 資源型別(MENU、DIALOG 等)
  2. 資源 ID(在資源型別之前的數字 - 對於 STRINGTABLE,最多 16 個字串組的 ID)
  3. 資源語言(當前活動語言,由命令列選項或 LANGUAGE 語句給出;僅限 Win32)

以下資料的具體內容取決於實際的資源型別。通常,它是二進位制的。

對任意資源的二進位制資料的讀取訪問使用

FindResource()		// get a handle
LoadResource()		// get the binary size
LockResource()		// get a pointer; Win32: This is a simple macro, Win16: This is a function call.
...			// do something
UnlockResource()	// Win32: This is a do-nothing macro, Win16: This is a function call.
FreeResource()		// release

因為點陣圖、圖示、游標、對話方塊、字串表和選單資源沒有正式文件化,並且解析起來有點困難,所以程式設計師應該使用專門的資源載入函式來載入這些資源型別。請參閱下面這些型別的描述和示例。

識別符號

[編輯 | 編輯原始碼]

識別符號通常以特定方式命名,雖然讀者和所有程式設計師都可以自由更改此命名方案。這只是一個建議。識別符號通常以字首 “ID” 開頭,後面跟著一個字母表示識別符號的型別

  • IDS: 字串資源
  • IDM: 選單資源
  • IDC: 命令識別符號
  • IDD: 對話方塊資源
  • IDA: 加速鍵表資源
  • IDI: 圖示或點陣圖資源
  • IDB: 點陣圖資源
  • ID: 自定義資源,或不常見的資源型別。

有時,選單中的命令識別符號會以 “IDM_” 字首開頭,以區分來自其他來源的命令。

沒有必要使用符號識別符號。在某些情況下,識別符號會使訪問對話方塊或選單中按數字排列的控制元件變得複雜。無論如何,識別符號不會幫助非英語程式設計師閱讀軟體原始碼。數字永遠不需要翻譯。而且識別符號和數字都需要解釋。

ID 允許在 0..65535 範圍內,並在 1..32767 範圍內優先使用。

此關鍵字具有不同的範圍

  • 本地(對於一個資源),如果位於資源行下方,例如
21 MENU
LANGUAGE 7,1 // or, LANG_GERMAN, SUBLANG_GERMAN
{
 POPUP "&Datei"  // = "&File"
 ...

此語言適用於該選單

  • 全域性(對於所有後續資源),如果位於其他位置

與語言無關的資源,如與文化無關的圖示、版本資訊和清單,應始終設定為 LANGUAGE 0,0(或更詳細地說,LANG_NEUTRAL,SUBLANG_NEUTRAL)。

注意始終檢查影像是否與文化相關!一個典型的錯誤是用於主電源供電筆記型電腦的插頭符號:它無疑顯示了一個美國插頭,即使是在歐洲也是如此。顯然,包含字母或文字的影像與文化相關。

記憶體管理標誌

[編輯 | 編輯原始碼]

從 Win16 遺產中有一些記憶體管理標誌,例如 MOVEABLE、FIXED 等。請參閱 LocalAlloc() 瞭解一些標誌。

DISCARDABLE

[編輯 | 編輯原始碼]

資源在程式執行時載入到記憶體中。但是,如果資源沒有被使用,並且 Windows 不立即需要它們,資源可以選擇從記憶體中解除安裝,直到需要為止。要指定可以從記憶體中解除安裝未使用的資源,您可以將 DISCARDABLE 關鍵字與資源一起列出。DISCARDABLE 資源允許更有效地使用記憶體,但如果需要從磁碟載入它們,則會減慢程式速度。

DISCARDABLE 關鍵字對於 32 位 Windows 被忽略,但為了相容性而保留。[1] 32 位資源永遠不會被載入,而是被對映到記憶體中。

圖示可以使用 ICON 關鍵字儲存在資原始檔中。以下是用資源指令碼中使用圖示的通用示例

IDI_ICON<n> ICON [DISCARDABLE] "iconfile.ico"

Windows 資源管理器將使用指令碼中的第一個圖示來顯示二進位制可執行檔案。例如,如果我們載入兩個圖示,如下所示

IDI_ICON1 ICON DISCARDABLE "icon1.ico"
IDI_ICON2 ICON DISCARDABLE "icon2.ico"

並且我們在相應的 resource.h 中這樣定義我們的宏

#define IDI_ICON1 1
#define IDI_ICON2 2

可執行檔案將以 icon1.ico 作為其圖示。

要從可執行模組載入圖示,假設我們有一個指向模組的例項控制代碼(以下示例中的 hInst),我們可以這樣獲取指向圖示的控制代碼

HICON hIcon;
hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1));

這將返回指向與識別符號 “IDI_ICON1” 關聯的圖示的控制代碼。圖示識別符號通常以 “IDI_” 為字首,表示 “圖示的 ID”。

LoadIcon() 函式的第二個引數是指向字串的指標。字串指標是 32 位值。但是,如果最高 16 位全部為零,Windows 將把該值視為資源編號,而不是字串。為了在字串和 16 位整數之間進行轉換,Microsoft 提供了 MAKEINTRESOURCE 宏。類似地,我們可以使用字串來定義我們的圖示

MYICON1 ICON DISCARDABLE "icon1.ico"

並且我們可以按名稱載入此字串

HICON hIcon;
hIcon = LoadIcon(hInst, "MYICON1");

資源的字串識別符號不區分大小寫。

WNDCLASSEX 具有指向兩個圖示的控制代碼值:一個大圖示和一個小圖示。小圖示是左上角使用的圖示。小圖示通常為 16 畫素見方。大圖示通常為 32 畫素見方。如果未提供小圖示控制代碼,則大圖示將縮小以適合。

如果 LoadIcon() 函式使用 NULL 例項控制代碼提供,Windows 將提供一個預設圖示供使用。

最近,Win32 API 提供了 LoadImage 函式,用於從單個函式載入圖示、點陣圖和滑鼠游標。您可以在 MSDN 上找到有關此函式的更多資訊。

在內部,圖示儲存在數字資源型別 RT_ICON == 3 下,並分組在 RT_GROUP_ICON == 14 下。

點陣圖

[編輯 | 編輯原始碼]

點陣圖可以像資原始檔中的圖示一樣載入。

(bitmap ID or name) BITMAP [DISCARDABLE] "bitmapfile.bmp"

點陣圖可以使用名為 **LoadBitmap** 的函式訪問(同樣,Win32 API 的新版本更喜歡使用 **LoadImage** 載入點陣圖、圖示或游標)。LoadBitmap 返回一個 HBITMAP 控制代碼型別。

HBITMAP hBmp;
hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));

或者,如果我們已命名點陣圖資源

hBmp = LoadBitmap(hInst, "MyBitmapRes");

點陣圖是大型資源,如果 Windows 無法將點陣圖載入到記憶體中(或如果 ID 或名稱值無效),該函式將返回 NULL 值。請確保在使用控制代碼之前測試此值。

必須透過將控制代碼傳遞給 DeleteObject() 函式來從記憶體中解除安裝點陣圖。您可以在 MSDN 上找到有關此方面的更多資訊。

點陣圖識別符號通常使用 “IDB_” 字首,表示它是點陣圖的 ID。

在內部,點陣圖儲存在數字資源型別 RT_BITMAP == 2 下。

滑鼠游標

[編輯 | 編輯原始碼]

滑鼠游標的指定方式類似於圖示和點陣圖,並且使用 **LoadCursor** 函式載入。

在內部,游標儲存在數字資源型別 RT_CURSOR == 1 下,並分組在 RT_GROUP_CURSOR == 12 下。

對於任何暗示二進位制資料的資源,建議使用帶有“檔名”的外部檔案。但是,大多數資源編譯器允許以這種方式將二進位制資料內聯到資原始檔

42 ICON
{
 123,4567,0x89AB,0xCDEF
 '\x01','\x23',"ajx"
}

使用此規則,源於 Win16 遺產

  • 數字(十進位制或十六進位制)連續儲存為 16 位小端量(未對齊)
  • 字元儲存為 8 位量

字串表

[編輯 | 編輯原始碼]

一個資源指令碼可以包含多個字串表,儘管這沒有必要:這些表沒有區別(即它們被合併),並且任何表中的每個字串物件都必須具有唯一的識別符號。字串表中的字串也不得使用名稱,而必須使用數字識別符號。畢竟,用字串來訪問字串是沒有意義的,對吧?

這是一個通用的字串表。

STRINGTABLE DISCARDABLE
BEGIN
   IDS_STRING1, "This is my first string"
   IDS_STRING2, "This is my second string"
   ...
END

重要的是要注意,在 BEGIN 和 END 關鍵字的位置,程式設計師也可以使用更類似於 C 的大括號,如下所示。

STRINGTABLE DISCARDABLE
{
   IDS_STRING1, "This is my first string"
   IDS_STRING2, "This is my second string"
   ...
}

有些人更喜歡其中一個,但它們對資源編譯器來說都是一樣的。

可以使用 **LoadString** 函式載入字串。LoadString 比 LoadBitmap 或 LoadIcon 函式更復雜。

int LoadString(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax);

hInstance 引數,如我們所知,是包含字串的模組的例項控制代碼。uID 引數包含我們嘗試訪問的字串編號。lpBuffer 是將接收字串的字元陣列變數,而 nBufferMax 編號告訴 Windows 可以載入的最大字元數。此計數是安全預防措施,因此請確保不要允許 Windows 將字元資料寫入字串末尾以外的位置。MSDN 在此函式的頁面上顯示了一個很大的警告,程式設計師必須注意此警告。 msdn

Windows 將在字串寫入緩衝區後自動以零結尾。LoadString 將返回實際寫入字串中的字元數,以防字元數少於允許的最大字元數。如果此返回值為 0,則字串資源不存在或無法載入。

字串可以在中間包含 “\0”。由於字串儲存為計數字符串,因此 LoadString 返回儲存的字元數,包括中間的零。但是,大多數資源編輯器都會在處理此類字串時失敗。

在內部,字串表儲存在數字資源型別 RT_STRING == 6 下,最多分組為 16 個相鄰 ID。

加速鍵

[編輯 | 編輯原始碼]

鍵盤加速鍵是幾乎每個 Windows 應用程式的常見部分,因此,最好透過將它們放入資源指令碼中來簡化建立加速鍵的工作。以下是如何建立加速鍵表。

(Accelerator Table ID or name) ACCELERATORS [DISCARDABLE]
BEGIN
   (key combination), (Command ID)
   ...
END

鍵組合使用字串文字字元(例如,“A”)或虛擬鍵程式碼值來指定。以下是一些示例。

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE
BEGIN
   "A", IDA_ACTION_A //Shift+A
END

現在,當按下 “Shift+A” 鍵組合時,您的視窗過程將收到 WM_COMMAND 訊息,訊息的 WPARAM 欄位中將包含值 IDA_ACTION_A。

如果我們想使用 “Alt” 鍵或 “Ctrl” 鍵的組合,我們可以分別使用 ALT 和 CONTROL 關鍵字。

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE
BEGIN
   "a", IDA_ACTION_A, ALT          //Alt+A
   "b", IDA_ACTION_B, CONTROL      //Ctrl+B
   "c", IDA_ACTION_C, ALT, CONTROL //Alt+Ctrl+A
END

此外,我們可以使用 “^” 符號來表示 CONTROL 鍵程式碼。

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE
BEGIN
   "^a", IDA_ACTION_A //Control+A
END

同樣,如果我們想成為超級駭客,我們可以直接使用 ASCII 程式碼。

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE
BEGIN
   65, IDA_ACTION_A, ASCII //65 = "A", Shift+A
END

或者,我們可以使用虛擬鍵程式碼識別符號來引用鍵(包括非字母數字鍵),方法是使用 VIRTKEY 識別符號。

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE
BEGIN
   VK_F12, IDA_ACTION_F12, VIRTKEY             //press the "F12 Key"
   VK_DELETE, IDA_ACTION_DEL, VIRTKEY, CONTROL //Ctrl+Delete
END

現在,如果我們使加速鍵與選單命令對應,當我們按下加速鍵時,選單命令將被點亮。也就是說,除非我們在加速鍵表中指定 “NOINVERT” 關鍵字,否則選單將被點亮。

IDA_ACCEL_TABLE ACCELERATORS DISCARDABLE
BEGIN
   "A", IDA_ACTION_A, NOINVERT //Shift+A (non inverted menu selection)
END

要載入加速鍵表,我們需要使用 **LoadAccelerators** 函式,如下所示。

HACCEL hAccel;
hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDA_ACCEL_TABLE));

同樣,我們也可以為資源指定字串名稱,並使用該字串來載入該表。

使用加速鍵時,我們需要修改訊息迴圈以攔截按鍵訊息,並根據加速鍵表規則將它們轉換為命令訊息。我們使用 **TranslateAccelerator** 函式來攔截按鍵訊息,並將它們轉換為命令訊息,如下所示。

while ( (Result = GetMessage(&msg, NULL, 0, 0)) != 0)
{
    if (Result == -1)
    {
        // error handling
    }    
    else
    {
        if (!TranslateAccelerator(hwnd, haccel, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }    
    }    
}   

此外,如果我們正在編寫 MDI 應用程式,我們需要攔截子視窗的加速鍵訊息,我們還需要使用 **TranslateMDISysAccel** 函式。

while ( (Result = GetMessage(&msg, NULL, 0, 0)) != 0)
{
    if (Result == -1)
    {
        // error handling
    }    
    else
    {
        if (  !TranslateMDISysAccel(hwndClient, &msg)
           && !TranslateAccelerator(hwndFrame, haccel, &msg) )
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }    
    }    
}   

其中 “hwndFrame” 是框架視窗的控制代碼,而 “hwndClient” 是 MDI 客戶端視窗的控制代碼。

在內部,加速鍵儲存在數字資源型別 RT_ACCELERATOR == 9 下。

可以使用 MENU 關鍵字在資源指令碼中定義選單。選單中包含兩種型別的專案,即頂級 “POPUP” 選單專案和二級 “MENUITEM” 專案。它們在選單中的定義方式如下。

(ID or name) MENU [DISCARDABLE]
BEGIN
   POPUP "File"
   POPUP "Edit"
   BEGIN
      MENUITEM "Copy", IDM_EDIT_COPY
      MENUITEM "Paste", IDM_EDIT_PASTE
   END
   ...
 END

我們在此處包含了一些示例,以便您可以看到 POPUP 和 MENUITEM 之間的區別。當我們有一個 ID 為 ID_MENU 的選單時,我們可以像這樣將其載入到程式中。

HMENU hmenu;
hmenu = LoadMenu(hInst, MAKEINTRESOURCE(ID_MENU));

獲得此控制代碼後,我們可以將其傳遞給 CreateWindow 函式,並將其應用於我們的視窗。

當選擇選單項時,主機程式會收到 WM_COMMAND 訊息,WPARAM 引數中包含選單項識別符號。如果我們有一個基本的視窗過程 switch-case 語句,我們可以看到如下所示。

case WM_COMMAND:
   switch(WPARAM)
   {
       case IDM_EDIT_COPY:
          //handle this action
          break;
       case IDM_EDIT_PASTE:
          //handle this action
          break;
   }
   break;

在選單中,如果我們想將選單項與加速鍵關聯,我們可以將其定義如下。

ID_MENU MENU DISCARDABLE
BEGIN
   POPUP "File"
   POPUP "Edit"
   BEGIN
      MENUITEM "&Copy", IDM_EDIT_COPY
      MENUITEM "&Paste", IDM_EDIT_PASTE
   END
   ...
 END

請注意,我們如何在 “Copy” 中的 “C” 和 “Paste” 中的 “P” 前面放置了和號 (&)。這意味著這些字母將被下劃線,但更重要的是,如果按下了加速鍵組合,選單中的這些項將被突出顯示(除非加速鍵表中指定了 NOINVERT 標籤)。如果在 POPUP 選單項前面放置了一個和號,則按 ALT+該字母將彈出該選單。例如,讓我們定義我們的選單。

ID_MENU MENU DISCARDABLE
BEGIN
   POPUP "&File"
   POPUP "&Edit"
   BEGIN
      MENUITEM "Copy", IDM_EDIT_COPY
      MENUITEM "Paste", IDM_EDIT_PASTE
   END
   ...
 END

現在,如果我們按下 ALT+F,我們將開啟 File 選單,如果我們按下 ALT+E,它將開啟 Edit 選單。對於只輸入一個額外字元,這真是很不錯的功能。

在內部,選單儲存在數字資源型別 RT_MENU == 4 下。

版本資訊

[編輯 | 編輯原始碼]

程式可以在資源指令碼中包含有關其版本及其作者的某些資訊。此版本資訊會在您在 Windows 中右鍵單擊可執行檔案並單擊 “屬性” 時顯示。在屬性對話方塊中,此資訊顯示在 “版本” 選項卡上。

BLOCK "StringFileInfo"
  BEGIN
      BLOCK "040904E4"
      BEGIN
          VALUE "CompanyName",      "My Company.\0"
          VALUE "FileDescription",  "A Win32 program."
          VALUE "FileVersion",      "1.0.0.0\0"
          VALUE "ProductName",      "The product name.\0"
          VALUE "ProductVersion",   "1.0\0"
          VALUE "LegalCopyright",   "My Company.\0"
      END
  END
  BLOCK "VarFileInfo"
  BEGIN
      VALUE "Translation", 0x409, 1252
  END

在內部,VersionInfo 儲存在數字資源型別 RT_VERSION == 16 下。它是在 Windows 3 中引入的。

對話方塊

[編輯 | 編輯原始碼]

對話方塊資源遵循一般模式。

(Dialog ID or name) DIALOG [DISCARDABLE] x, y, width, height
TITLE "(dialog box title)"
[CLASS "(class name)"]
FONT "(font name)"
BEGIN
   ...
END

如果對話方塊沒有與類關聯,則無需填寫 CLASS 欄位。所有列為用引號括起來的字串都 *必須用資源指令碼中的引號括起來*,否則會發生錯誤。然後,對話方塊中的各個專案在 BEGIN 和 END 標籤之間指定。

在內部,對話方塊儲存在數字資源型別 RT_DIALOG == 5 下。

通用控制元件

[編輯 | 編輯原始碼]

CONTROL classname,windowname,id,left,top,width,height,windowflags

特定按鈕

[編輯 | 編輯原始碼]

編輯框

[編輯 | 編輯原始碼]

清單資源包含使用 UTF-8 編碼的 XML 描述檔案,用於描述作業系統和 DLL 依賴關係。通常,該資源指示使用 Windows XP 版本 6.0 的 comctl32.dll,以對標準和通用控制元件使用 Luna 樣式。

在內部,清單儲存在數字資源型別 RT_MANIFEST == 24 下。它是在 Windows 4.1 中引入的。

使用者型別資源

[編輯 | 編輯原始碼]

使用者型別資源應使用一些更大的資源型別識別符號,或 RT_RCDATA == 10。

華夏公益教科書