2012-11-04 26 views
8

Câu chuyện dài về những gì tôi đang cố gắng đạt được

Tôi đang làm việc trên một chương trình tự động tải DLL dưới dạng plugin. Tôi đang biên dịch chương trình bằng Microsoft Visual C++ 2008. Tuy nhiên, chúng ta hãy giả định rằng bất kỳ phiên bản Visual C++ nào mà Qt hoạt động sẽ được hỗ trợ. Cách bố trí thư mục chương trình sau:C++: Tệp kê khai và tải động DLL từ thư mục khác nhau

| plugins/ 
| plugin1.dll 
| plugin2.dll 
| QtCore4.dll 
| QtGui4.dll 
| program.exe 

program.exe phát hiện ra tất cả các plugin file DLL, thực hiện LoadLibrary() trên họ và gọi một chức năng chữ ký nhất định để tìm hiểu xem nó thực sự là một plugin hay không. Điều này hoạt động khá tốt trên các máy tính có vcredist cho MSVC90 cài đặt. Đương nhiên, để làm cho chương trình hoạt động trên tất cả các máy tính tôi phải phân phối lại nó với các tệp msvc * .dll và với tệp kê khai thích hợp. Qt DLLs cũng yêu cầu redist để chạy.

Bây giờ, tôi đã thiết lập cmake để tự động sao chép qua các tệp DLL tái tạo thích hợp và tệp kê khai tùy thuộc vào phiên bản Visual Studio đã chọn. Vì mục đích đơn giản, hãy tiếp tục giả sử rằng tôi đang làm việc với MSVC90. Khi redist được sao chép vào thư mục chương trình bố trí trông như thế này:

| plugins/ 
| plugin1.dll 
| plugin2.dll 
| QtCore4.dll 
| QtGui4.dll 
| msvcm90.dll 
| msvcp90.dll 
| msvcr90.dll 
| Microsoft.VC90.CRT.manifest (I'm also aware that this file is bugged in VS2008) 
| program.exe 

Về lỗi trong file manifest: http://www.cmake.org/pipermail/cmake/2008-September/023822.html

Vấn đề

Chương trình với cách bố trí này hiện đang làm việc trên các máy tính không được cài đặt lại, nhưng các plugin không được tải. Để tải các plugin tải xuống, tôi phải thực hiện một trong các thao tác sau:

  1. Sao chép tệp kê khai vào thư mục plugins/. Xóa tất cả các tham chiếu đến tệp msvc * .dll khỏi tệp kê khai. Điều này hoạt động nhưng nó không tốt đẹp vì tôi phải hỗ trợ các phiên bản khác nhau của các tệp kê khai đã chỉnh sửa tùy thuộc vào phiên bản MSVC đã sử dụng. Ngoài ra, tôi không có ý tưởng nếu điều này sẽ không phá vỡ với Visual Studio khác hơn 2008.
  2. Sao chép toàn bộ redist để plugins/ thư mục. Điều này không yêu cầu bất kỳ sửa đổi nào đối với tệp kê khai, nhưng bây giờ program.exe ngớ ngẩn cố gắng tải các tệp msvc * .dll cho rằng chúng là các plugin. Đương nhiên, điều này không thành công một cách duyên dáng nên không có hại lớn nào được thực hiện. Nhược điểm khác là kích thước của gói chương trình tăng hơn 1 MB. Cả hai vấn đề này đều là thứ mà tôi có thể sống cùng.
  3. Biên dịch plugin bằng công tắc/MT. Kiểm tra ngắn gọn đã chỉ ra rằng điều này thực sự hoạt động nhưng tôi không chắc chắn nếu nó sẽ không phá vỡ bất cứ điều gì trong tương lai nếu cả hai Qt và program.exe là/MD.

Câu hỏi (s)

giải pháp tốt nhất là gì? Giải pháp chính xác là gì? Nếu có nhiều hơn một giải pháp đúng thì đó là phương pháp hay nhất? Tôi có phải là người đầu tiên cố gắng làm điều này không?

Cập nhật 1 (18 tháng 11 năm 2012)

Trong khi câu hỏi vẫn chưa được giải tôi quyết định đi cho con đường là nguyên nhân gây đau đầu nhất. Cho đến bây giờ tôi đã sử dụng giải pháp số 1 và tôi quyết định gắn bó với nó. Nếu CMake phát hiện rằng người dùng đang sử dụng một phiên bản MSVC khác với năm 2008, nó sẽ hiển thị một thông báo cảnh báo cho biết rằng bao bì tự động không được hỗ trợ đầy đủ.

+1

Tại sao bạn không liên kết tĩnh thời gian chạy cho ứng dụng của bạn và cho các plugin của bạn? –

+0

Qt đã yêu cầu vcredist vì vậy tôi sẽ phải bao gồm nó anyway. Bên cạnh đó có vấn đề với phân bổ tài nguyên khi tài nguyên được cấp phát trong một thực thể được liên kết tĩnh và sau đó được phát hành ở một thực thể khác - nó không an toàn và chương trình có thể bị lỗi. Tôi không biết làm thế nào điều này sẽ hành xử với Qt nếu Qt là/MD. – ZalewaPL

+1

Tôi nghĩ bạn nên để các tác giả của các plugin quyết định cách họ muốn liên kết với thư viện thời gian chạy và không thử và buộc họ liên kết động với một phiên bản cụ thể của thời gian chạy. Đối với các vấn đề phân bổ mà bạn đã đề cập, nếu bạn đang gọi vào plugin để phân bổ tài nguyên, thì bạn nên gọi lại plugin để giải phóng tài nguyên. –

Trả lời

0

Nếu hệ điều hành mục tiêu của bạn có _WIN32_WINNT> = 0x0502 hơn bạn có thể sử dụng chức năng

SetDllDirectory() 

trước khi bạn tải các plug-in.

Đặt đường dẫn đến thư mục chương trình chính.

Các ghi đè cuộc gọi hệ thống tải theo thứ tự:

  1. Thư mục mà từ đó các ứng dụng nạp.
  2. Thư mục được chỉ định bởi đường dẫn trong cuộc gọi SetDllDirectory().

Vì vậy, bạn có thể gọi hàm sau khi ứng dụng khởi động. Nó là an toàn trong mọi trường hợp. Chúc may mắn!

+0

Dường như không hoạt động. – ZalewaPL

0

Bạn có thể tạo liên kết cứng tới các tệp VC với hàm CreateHardLink() trong quá trình cài đặt. Với phương thức (1) mà bạn đã mô tả, có thể có một số vấn đề với các bản sao khác nhau của các dll VCRT. Hardlinks hoặc SetDllDirectory() có vẻ là giải pháp tốt nhất.

Không kết hợp trong một liên kết tĩnh và động đơn lẻ với MSVCRT - LUÔN LUÔN cung cấp cho bạn các sự cố!

1

Bạn có thể cung cấp đường dẫn tệp đầy đủ cho "LoadLibrary", để bạn có thể tải các plugin của mình bằng đường dẫn của chúng. Tôi đã sử dụng bố trí này chính xác để tải nhiều phiên bản của cùng một thư viện từ các thư mục con của dll hiện trong Visual Studio 2005.

Trước tiên bạn cần để có được những con đường hiện tại của dll hiện tại sử dụng:

static LPSTR strDLLPath1 = new TCHAR[_MAX_PATH+1]; 
::GetModuleFileName((HINSTANCE)&__ImageBase, strDLLPath1, _MAX_PATH); 

Mặc dù nếu program.exe của bạn đã phát hiện ra các tệp plugin này, tôi cho rằng bạn đã có quyền truy cập vào đường dẫn đầy đủ của chúng.