2009-02-10 121 views
31

是否有可能將資源構建到靜態庫中,並通過簡單地鏈接庫來重用它們?靜態庫中的VC++資源

我主要考慮在庫中調用函數,然後訪問資源的情況。

回答

22

它可以完成,但它是非常痛苦的:你不能通過簡單地鏈接靜態庫來實現。

請考慮:資源被嵌入到EXE或DLL中。當靜態庫中的某些代碼調用(例如)LoadIcon時,它將從它所鏈接的EXE或DLL中獲取資源。

所以,如果你的靜態庫要求的資源可用,你有兩個選擇:

  1. 你可以有圖書館建立他們的飛行,然後用(例如)CreateDialogIndirect。參見Raymond Chen的"Building a dialog template at run-time"
  2. 您可以將它們作爲簡單數組(即char my_dialog_resource[] = { .... };)嵌入庫中,然後使用(例如)CreateDialogIndirect。您可能需要查找(或寫入)將.RES文件轉換爲.CPP文件的實用程序。
  3. 您可以使用資源腳本(.RC文件)和相應的頭文件運送LIB文件。然後你們#include他們相關。您需要爲LIB使用一系列資源ID,以免它們與主EXE或DLL相沖突。這是MFC用作靜態庫時的功能。或者您可以使用字符串資源ID(這不適用於STRINGTABLE資源)。
  4. 您的靜態庫可以附帶單獨的資源DLL。
+1

其實我已經做了你的選項2,它不是那麼難。你也需要爲.res文件格式構建一個解析器,這是有據可查的,不是太糟糕。 – slicedlime 2009-08-14 19:31:16

+1

@Roger Lipscombe你能看出爲什麼Dimitri C.的答案不是一個好主意嗎? – DataGraham 2012-01-17 21:47:36

+1

因爲.RES文件需要確保它不與資源ID衝突。我的#3有同樣的問題,除了你可以用一些預處理器魔法來減輕它。 Dimitri的答案不能做到這一點。 – 2012-01-18 09:49:02

1

我不這麼認爲。靜態庫沒有它自己的HINSTANCE。它的代碼是在鏈接它的DLL或EXE的上下文中執行的。這就是爲什麼你要從靜態庫的代碼中加載的所有資源都是封裝在DLL/EXE中的。

儘管有它自己的地址空間,但我用DLL做了這種資源重用,並且可以使用DLL的HINSTANCE調用LoadResource。

0

推薦的方法是提供一個dll的資源連同你的庫。

10

我剛剛通過這個MS Visual Studio編譯器。我們正在將一些遺留項目從DLL轉換爲靜態庫。這些DLL中有幾個嵌入了對話框或字符串資源。我能夠通過「TEXTINCLUDE」機制將這些DLL的.RC腳本編譯到主應用程序的RC腳本文件中,並將它們編譯到我們的主應用程序中。我發現通過直接編輯RC文件來做到這一點最簡單,但Visual Studio也提供了一個稍微更「魔法」的機制。其他編譯器的實現很可能不同。


要直接操縱的主要RC腳本:

0.1。在「2 TEXTINCLUDE」部分中,包含定義庫的資源ID的頭文件。語法是

2 TEXTINCLUDE 
BEGIN 
    "#include ""my_first_lib_header.h""\r\n" 
    "#include ""my_second_lib_header.h""\0" 
END 

.2。在「3 TEXTINCLUDE」部分中,包含庫中的RC腳本。

3 TEXTINCLUDE 
BEGIN 
    "#include ""my_first_library.rc""\r\n" 
    "#include ""my_second_library.rc""\0" 
END 

步驟3和4應該自動發生,但我發現它是更可靠的,只是進入他們自己,而不是依賴於微軟的資源腳本編譯器來處理後事。

.3。將您的庫資源定義的頭文件添加到只讀符號列表中。該列表通常靠近文件的頂部。

#define APSTUDIO_READONLY_SYMBOLS 
#include "my_first_lib_header.h" 
#include "my_second_lib_header.h" 
#undef APSTUDIO_READONLY_SYMBOLS 

.4。在APSTUDIO_INVOKED部分包含您的庫的RC腳本。這通常在文件的底部。

#ifndef APSTUDIO_INVOKED 
#include "my_first_library.rc" 
#include "my_second_library.rc" 
#endif 

你也可以做這一切通過Visual Studio IDE中自動完成,但我發現它時,我預期它並不總是適用。

  1. 在Visual Studio中打開「資源視圖」窗口。
  2. 右鍵單擊主應用程序的資源文件,然後從上下文菜單中選擇「資源包含...」。
  3. 在標有「只讀符號指令」的框中,添加用於爲庫定義資源ID的.h文件的include語句。
  4. 在標有「編譯時指令」的框中,爲庫的.rc腳本添加包含語句。
  5. 點擊確定。您可能還需要手動觸發RC腳本編譯,以確保它發生。

如果您圖書館的資源腳本引用磁盤上的所有文件(文本文件,圖標文件等),你需要確保主應用程序項目知道在哪裏可以找到他們。您可以將這些文件複製到應用程序可以找到它們的地方,也可以在編譯器設置中添加額外的包含路徑。

要添加附加包含路徑:

  1. 打開的屬性對話框中的主應用程序。
  2. 從左側導航窗格中選擇「Configuration Properties/Resources/General」。
  3. 在屬性列表中,在「其他包含目錄」旁邊輸入任何相關路徑。
48

你需要做的在Visual C靜態庫使用的資源(圖片,對話,等...)++(2008年)的唯一事情,就是包括靜態庫的關聯.RES文件您項目。這可以在「項目設置/鏈接器/輸入/附加依賴項」中完成。

使用此解決方案,靜態庫的資源將打包到.exe中,因此您不需要額外的DLL。令人遺憾的是,Visual Studio沒有像.lib文件那樣自動包含.res文件(當使用「項目依賴性」特性時),但我認爲這個小小的額外步驟是可以接受的。

我已經爲這個解決方案尋找了很長時間,現在讓我驚訝的是這很簡單。唯一的問題是它完全沒有記錄。

0

按照Visual Studio 2010中,微軟的開發工具顯然無法正常靜態庫內部處理編譯資源數據在所有。

要分發已編譯的資源文件(.res文件),你有兩個選擇:

  1. 分別分發.res文件,並指示客戶端代碼與他們聯繫;
  2. 使用cvtres幾個.res文件合併成一個單一的對象(.obj)文件,並單獨提供。

請注意,您不能在使用cvtres創建的對象文件中進行讀取。如果提供了多個目標文件,lib抱怨好像給出了多個.res文件;如果提供了單個對象文件,lib不會發出抱怨,但鏈接器只是簡單地忽略lib文件中的嵌入式資源數據。

可能出現這種情況,即強制鏈接器讀取並鏈接資源數據中的libbed(帶有一些命令行選項,節操作等),因爲資源數據確實可用圖書館(如dumpbin揭示)。到目前爲止,我還沒有找到解決方案,除非有人願意破解開發工具,否則比這個簡單的解決方案更好的東西可能是不值得的。

在靜態庫(在這種情況下,靜態庫)中發佈資源數據的唯一方法是分別分配資源並將它們顯式鏈接到客戶端代碼中。如果使用cvtres,則可以將分佈式資源文件的數量減少爲1個。

-2

當使用以下的方法,任何資源(在該示例中,圖標)可被用作靜態庫和此類文庫的組成部分可以通過任何類型的應用程序一起使用,包括一控制檯一個(其沒有任何資源細分)。

  1. 圖標轉換爲BYTE的靜態數組。 bin2c可用於此。
  2. 數據被轉換成一個HICON句柄。這裏是我如何做到的:

    HICON GetIcon() 
    { 
        DWORD dwTmp; 
        int offset; 
        HANDLE hFile; 
        HICON hIcon = NULL; 
        offset = LookupIconIdFromDirectoryEx(s_byIconData, TRUE, 0, 0, LR_DEFAULTCOLOR); 
        if (offset != 0) 
        { 
         hIcon = CreateIconFromResourceEx(s_byIconData + offset, 0, TRUE, 0x00030000, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE); 
        } 
        return hIcon; 
    } 
    
  3. GetIcon用來代替LoadIcon。 而不是調用的:

m_hIcon = ::LoadIcon(hInstanceIcon, MAKEINTRESOURCE(pXMB->nIcon));

然後調用

m_hIcon = GetIcon()