TN035:搭配 Visual C++ 使用多個資源檔和標頭檔
注意
下列技術提示自其納入線上文件以來,未曾更新。 因此,有些程序和主題可能已過期或不正確。 如需最新資訊,建議您在線上文件索引中搜尋相關的主題。
本提示說明 Visual C++ 資源編輯器如何支援在單一專案內共用或跨多項專案共用的多個資源檔和標頭檔,以及如何善加運用該支援。 這份提示將回答下列問題:
何時可能想要將專案分割成多個資源檔及/或標頭檔,以及其執行方式
如何在兩
.RC
個檔案之間共用通用頭.H
檔如何將專案資源分割成多個
.RC
檔案您 (和 工具) 如何管理 、
.CPP
和.H
檔案之間的.RC
組建相依性
您應該注意,如果您將額外的資源檔新增至專案,ClassWizard 將無法辨識新增檔案中的資源。
這份提示是定型解答,針對上述問題回應的內容如下:
Visual C++ 如何管理資源檔和標頭檔 的概觀,概述 Visual C++ 中的 Resource Set Includes 命令如何讓您在同一個專案中使用多個資源檔和標頭檔。
AppWizard 建立
.RC
的分析和.H
檔案 會查看 AppWizard 所建立應用程式的多個資源和標頭檔。 這些檔案可以在您需要將其他資源檔和標頭檔加入至專案時,做為這些檔案的良好典範。包含其他標頭檔 描述您可能想要包含多個標頭檔的位置,並提供如何執行此動作的詳細資料。
在兩
.RC
個檔案 之間共用標頭檔說明如何在不同專案中的多個.RC
檔案之間共用一個標頭檔,或可能在同一個專案中共用。在相同的專案中 使用多個資源檔描述您可能想要將專案分解成多個
.RC
檔案的位置,並提供如何執行此動作的詳細資料。強制執行不可編輯的 Visual C++ 檔案 說明如何確定 Visual C++ 不會編輯和無意中重新格式化自訂資源。
管理多個 Visual C++編輯
.RC
檔案 共用的符號說明如何跨多個.RC
檔案共用相同的符號,以及如何避免指派重複的識別碼數值。管理 、 和 檔案 之間的
.RC
相依性說明 Visual C++ 如何避免不必要的重新編譯.CPP
相依于資源符號.H
檔的檔案。.CPP
Visual C++ 如何管理集合包含資訊 ,提供有關 Visual C++ 如何追蹤多個 (巢狀)
.RC
檔案和檔案所.RC
包含多個標頭檔的技術詳細資料。
Visual C++ 如何管理資源檔和標頭檔的概觀
Visual C++ 會將單 .RC
一資源檔和對應的 .H
標頭檔作為緊密結合的檔案組合來管理。 當您在檔案中 .RC
編輯和儲存資源時,會間接編輯並儲存對應 .H
檔案中的符號。 雖然您可以一次開啟和編輯多個 .RC
檔案(使用 Visual C++的 MDI 使用者介面),但對於任何您間接編輯一個對應標頭檔的任何指定 .RC
檔案。
[資源檢視的資源包含] 對話方塊
若要存取 [ 資源包含 ],請開啟 [ 資源檢視 ],然後以滑鼠右鍵按一下 .RC
檔案,然後選取 [資源包含 ]。
符號標頭檔
根據預設,Visual C++ 一律會命名對應的標頭檔,不論資源檔 RESOURCE.H
的名稱為何(例如 )。 MYAPP.RC
Visual C++ 中 [ 資源包含 ] 對話方塊的 [符號標頭檔: ] 區段可讓您變更此標頭檔的名稱。 在區段的編輯方塊中輸入新的檔案名。
注意
資源檔未位於與檔案相同的目錄中 .RC
,必須前面加上逸出 -'\' 的相對路徑,才能正確讀取。
唯讀符號指示詞
雖然 Visual C++ 只會編輯任何指定 .RC
檔案的一個標頭檔,但 Visual C++ 支援參考其他唯讀標頭檔中定義的符號。 [ 資源包含 ] 對話方塊中的 [唯讀符號指示詞:] 區段 可讓您將任意數目的其他唯讀標頭檔指定為唯讀符號指示詞。 「唯讀」限制表示當您在檔案中 .RC
新增資源時,可以使用唯讀標頭檔中定義的符號。 不過,如果您刪除資源,符號仍會保留在唯讀標頭檔中定義。 您無法變更指派給唯讀符號的數值。
編譯時期指示詞
Visual C++ 也支援資源檔的巢狀結構,其中一個 .RC
檔案會使用 指示詞包含在另一個 #include
檔案內。 當您使用 Visual C++ 編輯指定的 .RC
檔案時,不會顯示內含檔案中的任何資源。 但是當您編譯檔案 .RC
時,也會編譯包含的檔案。 [資源包含 ] 對話方塊中的 [ 編譯時間指示詞: ] 區段 可讓您指定要包含為 Compile-Time 指示詞的 .RC
任意數目檔案。
請注意,如果您讀入 Visual C++ 時 .RC
,包含另一個 .RC
未指定為 Compile-Time 指示詞的檔案,會發生什麼事。 當您將先前使用文字編輯器手動維護的檔案帶入 Visual C++ .RC
時,可能會發生這種情況。 當 Visual C++ 讀取包含 .RC
的檔案時,它會將包含的資源合併到父 .RC
檔案中。 當您儲存父 .RC
檔案時, #include
語句實際上會由包含的資源取代。 如果您不想要發生此合併,您應該先從父檔案中移除 #include
語句,再 將它讀入 Visual C++;然後使用 Visual C++,將相同的 #include
語句加回編譯時間指示 詞。 .RC
Visual C++ 會將上述三種集合包含資訊(符號標頭檔、唯讀符號指示詞和編譯時間指示詞)儲存在 .RC
#include
指示 詞和資源 中 TEXTINCLUDE
。 Visual C++ 管理集合包含資訊 中 會說明資源 TEXTINCLUDE
,這是您通常不需要處理的實作詳細資料。
分析 AppWizard 建立 .RC
和 .H
檔案
檢查 AppWizard 產生的應用程式程式碼可讓您深入了解 Visual C++ 如何管理多個資源檔和標頭檔。 以下所檢查的程式碼摘錄來自 MYAPP
使用預設選項由 AppWizard 所產生的應用程式。
AppWizard 建立的應用程式會使用多個資源檔和多個標頭檔,如下圖摘要所示:
RESOURCE.H AFXRES.H
\ /
\ /
MYAPP.RC
|
|
RES\MYAPP.RC2
AFXRES.RC
AFXPRINT.RC
您可以使用 Visual C++ [File/Set Includes] 命令,檢視這些檔案關聯性。
MYAPP.RC
您使用 Visual C++ 編輯的應用程式資源檔。
RESOURCE.H
是應用程式特定的標頭檔。 它一律由 AppWizard 命名 RESOURCE.H
,與 Visual C++的預設標頭檔命名一致。 #include
此標頭檔的 是資源檔 ( MYAPP.RC
):
//Microsoft Visual C++ generated resource script
//
#include "resource.h"
RES\MYAPP.RC2
包含不會由 Visual C++ 編輯的資源,但會包含在最終編譯 .EXE
的檔案中。 由於 Visual C++ 可以編輯任何標準資源,包括版本資源 (這個版本中的新功能),因此 AppWizard 預設不會建立此類資源。 AppWizard 會產生空白檔案,以備您想要自行在這個檔案中加入自訂格式的資源。
如果您使用自訂格式化的資源,您可以使用 Visual C++ 文字編輯器將其新增至 RES\MYAPP.RC2
並加以編輯。
AFXRES.RC
和 AFXPRINT.RC
包含架構特定功能所需的標準資源。 如同 RES\MYAPP.RC2
,這兩個架構提供的資源檔會包含在 的 MYAPP.RC
結尾,而且會在 [集合包含] 對話方塊的 [編譯時間指示詞] 中指定。 因此,在 Visual C++ 中編輯時,您不會直接檢視或編輯 MYAPP.RC
這些架構資源,但是這些資源會編譯成應用程式的二進位 .RES
檔和最終 .EXE
檔案。 如需標準架構資源的詳細資訊,包括修改它們的程式,請參閱 技術附注 23 。
AFXRES.H
定義標準符號,例如 ID_FILE_NEW
架構所使用的 ,並特別用於 AFXRES.RC
。 AFXRES.H
也會使用 #include
包含 WINRES.H
,其中包含 Visual C++ 產生的 .RC
檔案和 AFXRES.RC
所需的子集 WINDOWS.H
。 當您編輯應用程式資源檔 ( MYAPP.RC
) 時,可以使用 中 AFXRES.H
定義的符號。 例如, ID_FILE_NEW
用於 File
New
檔案功能表資源中的 MYAPP.RC
功能表項目。 您無法變更或刪除這些架構定義的符號。
包含其他標頭檔
AppWizard 建立的應用程式只包含兩個標頭檔: RESOURCE.H
和 AFXRES.H
。 只有 RESOURCE.H
應用程式專屬。 您可能需要在下列情況中包含其他唯讀標頭檔:
標頭檔是由外部來源提供,或者您想要在多個專案之間或相同專案的多個組件之間共用標頭檔。
標頭檔具有您不希望 Visual C++ 在儲存檔案時變更或篩選掉的格式和批註。 例如,您或許想要保留使用符號算術的 #define:
#define RED 0
#define BLUE 1
#define GREEN 2
#define ID_COLOR_BUTTON 1001
#define ID_RED_BUTTON (ID_COLOR_BUTTON + RED)
#define ID_BLUE_BUTTON (ID_COLOR_BUTTON + BLUE)
#define ID_GREEN_BUTTON (ID_COLOR_BUTTON + GREEN)
您可以使用 Resource Includes 命令,將語句指定為第二個唯讀符號指示詞,以包含 #include
其他唯讀標頭檔 ,如下列所示:
#include "afxres.h"
#include "second.h"
新的檔案關聯性圖表,現在看起來像這樣:
AFXRES.H
RESOURCE.H SECOND.H
\ /
\ /
MYAPP.RC
|
|
RES\MYAPP.RC2
AFXRES.RC
AFXPRINT.RC
在兩 .RC
個檔案之間共用標頭檔
您可能想要在不同專案中的兩 .RC
個檔案之間共用標頭檔,或可能共用相同的專案。 若要這樣做,請將上述的唯讀指示詞技術套用至這兩個 .RC
檔案。 在兩 .RC
個檔案適用于不同應用程式(不同專案)的情況下,下圖說明結果:
RESOURCE.H AFXRES.H RESOURCE.H
(for MYAPP1) SECOND.H (for MYAPP2)
\ / \ /
\ / \ /
MYAPP1.RC MYAPP2.RC
/ \ / \
/ \ / \
RES\MYAPP1.RC2 AFXRES.RC RES\MYAPP2.RC2
AFXPRINT.RC
下面將討論第二個標頭檔由相同應用程式 (專案) 中的兩個 .RC
檔案共用的情況。
在相同的專案中使用多個資源檔
Visual C++ 和資源編譯器透過 #include
指示詞支援相同專案中的多個 .RC
檔案,這些指示詞包含另一個 .RC
檔案。 這會允許多重巢狀。 有各種理由將專案的資源分割成多個 .RC
檔案:
如果您將資源分割成多個
.RC
檔案,就能更輕鬆地管理多個專案小組成員之間的大量資源。 如果您使用原始檔控制管理套件來簽出檔案和簽入變更,請將資源分割成多個.RC
檔案,可讓您更精細地控制管理資源的變更。如果您想要針對資源部分使用預處理器指示詞,例如
#ifdef
、#endif
和#define
,您必須將它們隔離在資源編譯器所編譯的唯讀資源中。元件
.RC
檔案在 Visual C++ 中載入並儲存的速度會比一個複合.RC
檔案更快。如果您想要以人類可讀取的形式維護具有文字編輯器的資源,您應該將其保留在檔案中
.RC
,與 Visual C++ 編輯分開。如果您需要將使用者定義的資源保留在另一個特製化資料編輯器可解譯的二進位或文字表單中,則您應該將它保留在個別
.RC
的檔案中,讓 Visual C++ 不會將格式變更為十六進位資料。.WAV
MFC 進階概念範例 SPEAKN 中的 (sound) 檔案資源是很好的範例。
您可以在 [設定包含] 對話方塊中的 SECOND.RC
[編譯時間指示詞] 中包含:
#include "res\myapp.rc2" // non-Visual C++ edited resources
#include "second.rc" // THE SECOND .RC FILE
#include "afxres.rc" // Standard components
#include "afxprint.rc" // printing/print preview resources
結果如下圖所示:
RESOURCE.H AFXRES.H
\ /
\ /
MYAPP.RC
|
|
RES\MYAPP.RC2
SECOND.RC
AFXRES.RC
AFXPRINT.RC
使用 Compile-Time 指示詞,您可以將 Visual C++可編輯和不可編輯的資源組織成多個 .RC
檔案,其中 main MYAPP.RC
不會 #include
執行其他 .RC
檔案。 如果您使用 Visual Studio C++ 專案 .MAK
檔,則應該在專案中包含主要 .RC
檔案,讓所有包含的資源都與您的應用程式一起編譯。
強制執行不可編輯的 Visual C++ 檔案
AppWizard 建立 RES\MYAPP.RC2
的檔案是一個檔案範例,其中包含您不想不小心讀取到 Visual C++ 的資源,然後以遺失格式資訊來回寫。 若要防範此問題,請將下列幾行放在檔案開頭 RES\MYAPP.RC2
:
#ifdef APSTUDIO_INVOKED
#error this file is not editable by Visual C++
#endif //APSTUDIO_INVOKED
當 Visual C++ 編譯檔案時 .RC
,它會同時定義 APSTUDIO_INVOKED
和 RC_INVOKED
。 如果 AppWizard 建立的檔案結構已損毀,且 Visual C++ 會讀取上述 #error 行,則會報告嚴重錯誤並中止讀取 .RC
檔案。
管理多個 Visual C++編輯 .RC
檔案共用的符號
當您將資源分割成 .RC
多個您想要在 Visual C++ 中個別編輯的檔案時,就會發生兩個問題:
您可能想要跨多個
.RC
檔案共用相同的符號。您需要協助 Visual C++ 避免將相同的 ID 數值指派給不同資源 (符號)。
下圖說明處理第一個問題的組織 .RC
和 .H
檔案:
MYAPP.RC
/ \
/ \
MYSTRS.H / MYSHARED.H \ MYMENUS.H
\ / / \ \ \
\ / / \ \ \
MYSTRS.RC MYMENUS.RC
在此範例中,字串資源會保留在一個資源檔中, MYSTRS.RC
而功能表則保留在另一個 資源檔中。 MYMENUS.RC
有些符號 (例如命令的符號) 可能需要在兩個檔案之間共用。 例如, ID_TOOLS_SPELL
可能是 [工具] 功能表中 [拼字] 專案的功能表命令識別碼;它也可能是架構在應用程式主視窗狀態列中所顯示之命令提示字元的字串識別碼。
符號 ID_TOOLS_SPELL
會保留在共用標頭檔中。 MYSHARED.H
您可以使用文字編輯器手動維護此共用標頭檔;Visual C++ 不會直接編輯它。 在兩個資源檔和 MYSTRS.RC
MYMENUS.RC
中,您可以使用 Resource Includes 命令,在 的唯讀指示 MYAPP.RC
詞中指定 #include "MYSHARED.H"
,如先前所述。
在您嘗試使用它來識別任何資源之前,最方便預期您會共用的符號。 將符號新增至共用標頭檔,如果您尚未在檔案的唯讀指示詞 .RC
中包含共用標頭檔,請在使用符號之前執行此動作。 如果您未預期以這種方式共用符號,則必須手動(使用文字編輯器)將符號的 #define 語句從 MYMENUS.H
MYSHARED.H
移至 ,再將其用於 MYSTRS.RC
。
當您管理多個 .RC
檔案中的符號時,也必須協助 Visual C++ 避免將相同的識別碼數值指派給不同的資源(符號)。 針對任何指定的 .RC
檔案,Visual C++ 會以累加方式在四個識別碼網域中指派識別碼。 在編輯會話之間,Visual C++ 會追蹤檔案符號標頭檔中 .RC
每個網域中指派的最後一個識別碼。 以下是空白 (new) .RC
檔案的值: APS_NEXT
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
_APS_NEXT_RESOURCE_VALUE
是下一個符號值,將用於對話方塊資源、功能表資源等等。 資源符號值的有效範圍是 1 至 0x6FFF。
_APS_NEXT_COMMAND_VALUE
是下一個將用於命令識別的符號值。 命令符號值的有效範圍是 0x8000 至 0xDFFF。
_APS_NEXT_CONTROL_VALUE
是下一個將用於對話方塊控制項的符號值。 對話方塊控制項符號值的有效範圍是 8 至 0xDFFF。
_APS_NEXT_SYMED_VALUE
是當您使用 [符號瀏覽器] 中的 [新增] 命令手動指派符號值時,將會發出的下一個符號值。
Visual C++ 從建立新 .RC
檔案時的最低合法值略高的值開始。 AppWizard 也會將這些值初始化為一些較適合 MFC 應用程式的值。 如需識別碼值範圍的詳細資訊,請參閱 技術附注 20 。
現在,每當您建立新的資源檔,即使在相同的專案中,Visual C++ 也會定義相同的 _APS_NEXT_
值。 這表示,如果您在兩個不同的 .RC
檔案中新增多個對話方塊,很可能將相同的 #define 值指派給不同的對話。 例如, IDD_MY_DLG1
在第一個 .RC
檔案中,可能會指派與第二 .RC
個檔案相同的數位 101 IDD_MY_DLG2
。
若要避免此問題,您應該針對個別 .RC
檔案中的四個識別碼定義域,保留個別的數值範圍。 在您開始新增資源之前 ,請先手動更新每個 .RC
檔案 中的值, _APS_NEXT
以設定範圍。 例如,如果第一個 .RC
檔案使用預設值 _APS_NEXT
,您可能會想要將下列 _APS_NEXT
值指派給第二個 .RC
檔案:
#define _APS_NEXT_RESOURCE_VALUE 2000
#define _APS_NEXT_COMMAND_VALUE 42000
#define _APS_NEXT_CONTROL_VALUE 2000
#define _APS_NEXT_SYMED_VALUE 2000
當然,Visual C++ 仍然可以在第一個 .RC
檔案中指派這麼多識別碼,讓數值開始重迭保留給第二 .RC
個檔案的識別碼。 您應該保留足夠大的範圍,以免發生此衝突。
管理 、 .CPP
和 .H
檔案之間的 .RC
相依性
當 Visual C++ 儲存檔 .RC
案時,也會將符號變更儲存至對應的 RESOURCE.H
檔案。 參考檔案中資源的任何 .CPP
檔案都必須用來 #include
包含 RESOURCE.H
檔案,通常來自專案的主要頭 .RC
檔。 因為開發環境的內部專案管理會掃描原始程式檔中的標頭相依性,因此這種包含會導致不良副作用。 每次在 Visual C++ 中新增符號時, .CPP
所有具有 #include "RESOURCE.H"
指示詞的檔案都必須重新編譯。
Visual C++ 會藉由包含下列批註作為檔案的第一行 RESOURCE.H
來規避相依性 RESOURCE.H
:
//{{NO_DEPENDENCIES}}
開發環境會忽略 的變更 RESOURCE.H
來解譯此批註,讓相依 .CPP
檔案不需要重新編譯。
Visual C++ 一律會在儲存檔案時,將 //{{NO_DEPENDENCIES}}
批註行新增至 .RC
檔案。 在某些情況下,規避建 RESOURCE.H
置相依性可能會導致在連結時未偵測到的執行時間錯誤。 例如,如果您使用符號瀏覽器來變更指派給資源符號的數值,則如果 .CPP
參考資源的檔案未重新編譯,將無法正確找到資源,並在應用程式執行時間載入資源。 在這種情況下,您應該明確地重新編譯 .CPP
任何您知道的檔案會受到 中符號變更 RESOURCE.H
的影響,或選取 [ 全部 重建]。 如果您需要經常變更特定資源群組的符號值,您可能會發現將這些符號分成個別唯讀標頭檔更為方便且更安全,如上一節 包含其他頭 檔中所述。
Visual C++ 如何管理集合包含資訊
如前所述,[File] 功能表 [Set Includes] 命令可讓您指定三種類型的資訊:
符號標頭檔
唯讀符號指示詞
編譯時期指示詞
下表描述 Visual C++ 如何在檔案中 .RC
維護這項資訊。 您不需要此資訊才能使用 Visual C++,但它可能會增強您的瞭解,以便更自信地使用 Set Includes 功能。
上述三種類型的 Set Include 資訊會以兩種形式儲存在檔案中 .RC
:(1) 作為 #include
或資源編譯器可解譯的其他指示詞,而 (2) 則儲存為只能由 Visual C++ 解譯的特殊 TEXTINCLUDE
資源。
資源的目的是 TEXTINCLUDE
要安全地將 [包含] 資訊儲存在 Visual C++的 [集合包含 ] 對話方塊中的表單中。 TEXTINCLUDE
是 Visual C++ 所定義的資源類型 。 Visual C++ 可辨識具有資源識別碼 1、2 和 3 的三個特定 TEXTINCLUDE
資源:
TEXTINCLUDE 資源識別碼 |
Set Includes 資訊的類型 |
---|---|
1 | 符號標頭檔 |
2 | 唯讀符號指示詞 |
3 | 編譯時期指示詞 |
這三種類型的 Set Includes 資訊都說明 AppWizard 所建立的預設 MYAPP.RC
和 RESOURCE.H
檔案,如下所述。 RC 語法需要和 區塊之間的 END
BEGIN
額外 \0
和 ""
標記,才能分別指定零個終止字串和雙引號字元。
符號標頭檔
資源編譯器解譯的符號標頭檔資訊形式只是語句 #include
:
#include "resource.h"
對應的 TEXTINCLUDE
資源為:
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
唯讀符號指示詞
唯讀符號指示詞會包含在資源編譯器可解譯的下列表單頂端 MYAPP.RC
:
#include "afxres.h"
對應的 TEXTINCLUDE
資源為:
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
編譯時期指示詞
編譯時間指示詞會 MYAPP.RC
包含在資源編譯器可解譯的下列格式結尾:
#ifndef APSTUDIO_INVOKED
///////////////////////
//
// From TEXTINCLUDE 3
//
#include "res\myapp.rc2" // non-Visual C++ edited resources
#include "afxres.rc" // Standard components
#include "afxprint.rc" // printing/print preview resources
#endif // not APSTUDIO_INVOKED
指示 #ifndef APSTUDIO_INVOKED
詞會指示 Visual C++ 略過 Compile-Time 指示詞。
對應的 TEXTINCLUDE
資源為:
3 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""res\myapp.rc2"" // non-Visual C++ edited resources\r\n"
"\r\n"
"#include ""afxres.rc"" // Standard components\r\n"
"#include ""afxprint.rc"" // printing/print preview resources\r\n"
"\0"
END