2009-02-10 36 views
31

Có thể xây dựng tài nguyên vào một thư viện tĩnh và tái sử dụng chúng bằng cách liên kết đơn giản với thư viện không?VC++ tài nguyên trong một thư viện tĩnh

Tôi chủ yếu suy nghĩ về trường hợp bạn gọi một hàm trong thư viện mà lần lượt truy cập tài nguyên.

Trả lời

22

Nó có thể được thực hiện, nhưng nó khá đau đớn: Bạn không thể làm điều đó bằng cách đơn giản là liên kết với thư viện tĩnh.

Hãy xem xét điều này: tài nguyên được nhúng trong EXE hoặc DLL. Khi một số mã trong các cuộc gọi thư viện tĩnh (ví dụ) LoadIcon, nó sẽ lấy các tài nguyên từ EXE hoặc DLL mà nó được liên kết với.

Vì vậy, nếu thư viện tĩnh của bạn đòi hỏi phải có nguồn lực có sẵn, bạn đã có một vài lựa chọn:

  1. Bạn có thể có thư viện xây dựng chúng một cách nhanh chóng, và sau đó sử dụng (ví dụ) CreateDialogIndirect. Xem Raymond Chen's "Building a dialog template at run-time".
  2. Bạn có thể nhúng chúng vào thư viện dưới dạng mảng đơn giản (ví dụ: char my_dialog_resource[] = { .... }; và sau đó sử dụng (ví dụ: CreateDialogIndirect. Có thể bạn sẽ cần tìm (hoặc viết) một tiện ích chuyển đổi từ các tệp .RES thành .CPP tệp.
  3. Bạn có thể gửi tệp LIB bằng tập lệnh tài nguyên (.RC tệp) và tệp tiêu đề tương ứng. Sau đó, bạn #include chúng là có liên quan. Bạn sẽ cần phải đặt trước một loạt các ID tài nguyên để LIB sử dụng, do đó chúng không xung đột với các tệp của EXE hoặc DLL chính. Đây là những gì MFC làm khi được sử dụng như một thư viện tĩnh. Hoặc bạn có thể sử dụng ID tài nguyên chuỗi (điều này không hoạt động cho các tài nguyên STRINGTABLE).
  4. Thư viện tĩnh của bạn có thể gửi cùng với một tài nguyên DLL riêng biệt.
+1

Tôi đã thực sự thực hiện lựa chọn của bạn 2, và nó không phải là tất cả những gì khó khăn. Bạn cũng cần phải xây dựng một trình phân tích cú pháp cho định dạng tệp .res, cũng được ghi lại và không quá khủng khiếp. – slicedlime

+1

@Roger Lipscombe Bạn có thấy bất kỳ lý do gì khiến câu trả lời của Dimitri C. không phải là một ý tưởng hay không? – DataGraham

+1

Vì tệp .RES cần đảm bảo rằng nó không xung đột với ID tài nguyên. # 3 của tôi có cùng một vấn đề, ngoại trừ việc bạn có thể giảm thiểu nó với một số ma thuật tiền xử lý. Bạn không thể làm điều này với câu trả lời của Dimitri. –

1

Tôi không nghĩ vậy. Thư viện tĩnh không có HINSTANCE riêng. Mã của nó được thực hiện trong ngữ cảnh của DLL hoặc EXE liên kết nó. Đó là lý do tại sao tất cả các tài nguyên mà bạn sẽ cố gắng tải từ mã của thư viện tĩnh sẽ là của nó kèm theo DLL/EXE.

Tôi đã làm loại tài nguyên đó sử dụng lại với một DLL mặc dù, theo như nó có không gian địa chỉ riêng của nó, và bạn có thể gọi LoadResource với HINSTANCE của DLL.

0

Cách được khuyến nghị là cung cấp một dll với các tài nguyên cùng với thư viện của bạn.

10

Tôi vừa thực hiện việc này với trình biên dịch MS Visual Studio. Chúng tôi đã chuyển đổi một số dự án kế thừa từ các tệp DLL thành các thư viện tĩnh. Một vài trong số các DLL này có tài nguyên hộp thoại hoặc chuỗi được nhúng trong chúng. Tôi đã có thể biên dịch các tập lệnh .RC cho các tệp DLL này vào ứng dụng chính của chúng tôi bằng cách đưa chúng vào tệp tập lệnh RC của ứng dụng chính thông qua cơ chế "TEXTINCLUDE". Tôi tìm thấy nó dễ nhất để làm điều này bằng cách chỉnh sửa các tập tin RC trực tiếp, nhưng Visual Studio cung cấp một chút "wizardy" cơ chế là tốt. Việc triển khai có nhiều khả năng khác nhau trong các trình biên dịch khác.


Để thao tác RC kịch bản chính trực tiếp:

.1. Trong phần "2 TEXTINCLUDE", bao gồm tệp tiêu đề xác định ID tài nguyên cho thư viện của bạn. Cú pháp là

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

.2. Trong phần "3 TEXTINCLUDE", bao gồm tập lệnh RC từ thư viện của bạn.

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

Bước 3 và 4 sẽ tự động diễn ra, nhưng tôi thấy đáng tin cậy hơn khi chỉ nhập chúng, thay vì phụ thuộc vào trình biên dịch tập lệnh tài nguyên của Microsoft để xử lý mọi thứ.

.3. Thêm tệp tiêu đề với tài nguyên thư viện của bạn xác định danh sách ký hiệu chỉ đọc. Danh sách này thường nằm gần đầu tệp.

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

.4. Bao gồm tập lệnh RC của thư viện của bạn trong phần APSTUDIO_INVOKED. Điều này thường ở cuối tệp.

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

Bạn cũng có thể làm tất cả điều này tự động thông qua IDE visual studio, nhưng tôi thấy nó không luôn luôn áp dụng khi tôi mong đợi nó đến.

  1. Mở cửa sổ "Chế độ xem tài nguyên" trong Visual Studio.
  2. Nhấp chuột phải vào tệp tài nguyên của ứng dụng chính của bạn và chọn "Bao gồm tài nguyên ..." từ trình đơn ngữ cảnh.
  3. Trong hộp có nhãn "Chỉ thị biểu tượng chỉ đọc", hãy thêm câu lệnh bao gồm cho tệp .h xác định ID tài nguyên cho thư viện của bạn.
  4. Trong hộp có nhãn "Chỉ thị thời gian biên dịch", hãy thêm câu lệnh bao gồm cho tập lệnh .rc của thư viện của bạn.
  5. Nhấp vào OK. Bạn cũng có thể muốn kích hoạt trình biên dịch tập lệnh RC theo cách thủ công, để đảm bảo nó xảy ra.

Nếu kịch bản tài nguyên của thư viện của bạn tham chiếu bất kỳ tập tin trên đĩa (file văn bản, biểu tượng tập tin, vv), bạn sẽ cần phải chắc chắn rằng các dự án ứng dụng chính biết nơi để tìm thấy chúng. Bạn có thể sao chép các tệp này vào một nơi nào đó mà ứng dụng của bạn có thể tìm thấy chúng hoặc bạn có thể thêm một đường dẫn bổ sung bao gồm trong các thiết lập trình biên dịch.

Để thêm một bổ sung bao gồm đường dẫn:

  1. Mở hộp thoại thuộc tính cho các ứng dụng chính của bạn.
  2. Chọn "Thuộc tính cấu hình/Tài nguyên/Chung" từ ngăn điều hướng bên trái.
  3. Trong danh sách thuộc tính, hãy nhập bất kỳ đường dẫn thích hợp nào bên cạnh "Thư mục bao gồm bổ sung".
+1

Cảm ơn! Điều này đã cứu ngày của tôi! Mặc dù tôi đã có thể bỏ qua sự bao gồm các tiêu đề trên VS2010. Nó làm việc sau khi tôi chỉ cần thêm hai mục cho các tập tin .rc. – lowglider

48

Điều duy nhất bạn cần phải làm gì để sử dụng tài nguyên (hình ảnh, hộp thoại, vv ...) trong một thư viện tĩnh trong Visual C++ (2008), là bao gồm res liên thư viện tĩnh của tập tin tại của bạn dự án. Điều này có thể được thực hiện tại "Thiết lập dự án/Linker/Input/phụ thuộc bổ sung".

Với giải pháp này, tài nguyên của thư viện tĩnh được đóng gói vào .exe, vì vậy bạn không cần thêm DLL. Đáng tiếc, Visual Studio không bao gồm tệp .res tự động như nó cho tệp .lib (khi sử dụng "phụ thuộc dự án" -feature), nhưng tôi nghĩ rằng bước bổ sung nhỏ này là chấp nhận được.

Tôi đã tìm kiếm một thời gian rất dài cho giải pháp này, và bây giờ nó làm tôi ngạc nhiên nó là đơn giản. Vấn đề duy nhất là nó hoàn toàn không có giấy tờ.

+1

Điều này có lẽ không phải là câu hỏi ban đầu được yêu cầu, nhưng điều này đã cứu ngày của tôi! +1 – kizzx2

+0

Làm việc tuyệt vời cho tôi! @ kizzx2 Tôi đồng ý không chính xác những gì các câu hỏi được yêu cầu, nhưng tôi nghĩ rằng nó hoàn thành mục tiêu cuối cùng. – DataGraham

+0

Bất kỳ ý tưởng làm thế nào điều này được thực hiện trong một makefile viết tay? Tôi đang bỏ lỡ một tùy chọn biên dịch tài liệu hoặc pragma ở đây. – Lothar

0

Theo Visual Studio 2010, các công cụ phát triển của Microsoft dường như không thể xử lý đúng cách dữ liệu tài nguyên được biên dịch bên trong các thư viện tĩnh.

Để phân phối một tập tin tài nguyên biên dịch (một file .res), bạn có hai lựa chọn:

  1. Phát .res file riêng biệt, và chỉ thị cho mã khách hàng để liên kết chống lại họ;
  2. Sử dụng cvtres để hợp nhất một số .res tệp vào một tệp đối tượng đơn lẻ (.obj) và cung cấp riêng cho nó.

Lưu ý rằng bạn không thể lib trong các tệp đối tượng được tạo với cvtres. Nếu nhiều tệp đối tượng được cung cấp, lib than phiền như thể nhiều tệp .res đã được cung cấp; nếu một tệp đối tượng được cung cấp, lib không phàn nàn, nhưng trình liên kết chỉ đơn giản bỏ qua dữ liệu tài nguyên được nhúng trong tệp lib. Có thể là trường hợp có một cách để buộc trình liên kết đọc và liên kết phần mềm libbed trong dữ liệu tài nguyên (với một số tùy chọn dòng lệnh, thao tác phần và vv), vì dữ liệu tài nguyên thực sự có sẵn trong thư viện (như dumpbin tiết lộ). Cho đến nay, tôi đã không tìm thấy một giải pháp, và, trừ khi một người sẵn sàng để hack các công cụ phát triển, bất cứ điều gì tốt hơn so với giải pháp đơn giản này có lẽ không có giá trị nỗ lực.

Cách duy nhất để gửi dữ liệu tài nguyên trong thư viện tĩnh (trong trường hợp này, với thư viện tĩnh) là phân phối tài nguyên một cách riêng biệt và liên kết rõ ràng chúng trong mã khách hàng. Sử dụng cvtres có thể giảm số lượng tệp tài nguyên được phân phối xuống một tệp, nếu bạn có nhiều tệp.

-2

Khi sử dụng phương pháp sau, bất kỳ tài nguyên nào (trong ví dụ này, biểu tượng) có thể được sử dụng như một phần không thể tách rời của thư viện tĩnh và có thể sử dụng bất kỳ loại ứng dụng nào không có bất kỳ phân đoạn tài nguyên nào).

  1. Biểu tượng được chuyển thành một mảng tĩnh của BYTE. bin2c có thể được sử dụng cho điều đó.
  2. Dữ liệu được chuyển đổi thành bộ xử lý HICON. Đây là cách tôi đã làm điều đó:

    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 được sử dụng thay cho LoadIcon. Thay vì gọi:

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

Sau đó gọi

m_hIcon = GetIcon() 
Các vấn đề liên quan