Đầu tiên, chúng ta sẽ thảo luận về các vấn đề và các yêu cầu mà bạn nên xem xét khi bắt đầy viết các file DLL của riêng bạn.
Các loại DLLs
Khi bạn nạp một DLL vào trong một ứng dụng, có hai phương pháp liên kết cho phép bạn gọi chức năng exported dll
Hai phương pháp liên kết là:
1. thời gian nạp liên kết động (load-time dynamic linking)
2. thời gian chạy liên kết động (run-time dynamic linking).
load-time dynamic linking (Vì 1 số lý do về ngữ nghĩa mình sẽ để nguyên nghĩa tiếng anh của khái niệm để giữ đúng ý của câu.)
Trong load-time dynamic linking, một ứng dụng sẽ có một lời gọi rõ ràng đến các chức năng DLL exported để sử dụng như là một chức năng của chương trình chính. Để sử dụng load-time dynamic linking, ta cung cấp một file header (.h) và một file thư viện import - import library (lib) khi biên dịch và liên kết các ứng dụng. Khi bạn làm điều này, các mối liên kết sẽ cung cấp các hệ thống với các thông tin cần thiết để tải các DLL và sử dụng các chức năng DLL exported tại thời gian tải.
-> Tức là trước khi sử dụng phải include file dll trước
run-time dynamic linking
Trong run-time dynamic linking, một ứng dụng gọi một trong hai chức năng LoadLibrary hoặc các chức năng LoadLibraryEx để nạp DLL khi chạy. Sau khi DLL được nạp thành công, bạn sử dụng hàm GetProcAddress, để có được địa chỉ của hàm DLL exported mà bạn muốn gọi. Khi bạn sử run-time dynamic linking, bạn không cần import một tập tin thư viện trước khi sử dụng.
-> Tức là gọi trực tiếp file dll trong code, xử dụng và giải phóng sau thời điểm gọi.
Danh sách sau đây mô tả các tiêu chuẩn áp dụng cho việc lựa chọn giữa load-time dynamic linking và run-time dynamic linking
-
Hiệu suất khởi động - Nếu hiệu suất khởi động ban đầu của ứng dụng là rất quan trọng, bạn nên sử dụng run-time dynamic linking.
-
Dễ dàng sử dụng - Trong thời gian load-time dynamic linking, các chức năng DLL expost giống như chức năng của địa phương. Nó giúp bạn gọi các chức năng một cách dễ dàng.
-
Ứng dụng logic - Trong thời gian chạy liên kết động, một ứng dụng có thể phân nhánh để tải module khác nhau theo yêu cầu. Điều này rất quan trọng khi bạn phát triển các phiên bản đa ngôn ngữ.
Các DLL Entry Point
Khi bạn tạo một DLL, bạn có thể tùy chọn chỉ định một chức năng Entry Point. Các chức năng entry point sẽ được gọi trong quy trình hoặc đề tự gắn mình vào các DLL hoặc tách mình khỏi các DLL. Bạn có thể sử dụng chức năng Entry Point để khởi tạo hoặc phá hủy các cấu trúc dữ liệu theo yêu cầu của các DLL.
Ngoài ra, nếu các ứng dụng là đa luồng, bạn có thể sử dụng thread lưu trữ địa phương (TLS) để cấp phát bộ nhớ đó là riêng đến mỗi thread trong các chức năng entry point. Code sau đây là một ví dụ của hàm Entry Point vào DLL.
BOOL APIENTRY DllMain( HANDLE hModule, DLLDWORD ul_reason_for_call, LPVOID lpReserved ) // Dành riêng
{
//HANDLE hModule - Xử lý các module
//LPVOID lpReserved - Dành riêng
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACHED:
// Một quá trình nạp DLL.
break;
case DLL_THREAD_ATTACHED:
// Một quá trình tạo ra một luồng mới.
break;
case DLL_THREAD_DETACH:
// Một luồng thoát thông thường.
break;
case DLL_PROCESS_DETACH:
// Một quá trình unload các DLL.
break;
}
return TRUE;
}
Khi chức năng Entry Poin trả về giá trị FALSE, ứng dụng sẽ không bắt đầu nếu bạn đang sử dụng load-time dynamic linking. Nếu bạn đang sử dụng run-time dynamic linking, chỉ có các DLL cá nhân sẽ không tải.
Các chức năng Entry Poin nên chỉ thực hiện nhiệm vụ khởi tạo đơn giản và không nên gọi bất kỳ DLL tải hoặc chấm dứt các chức năng khác. Ví dụ, trong các chức năng nhập điểm, bạn không nên trực tiếp hoặc gián tiếp gọi chức năng LoadLibrary hoặc các chức năng LoadLibraryEx. Ngoài ra, bạn không nên gọi chức năng FreeLibrary khi quá trình được chấm dứt.
CẢNH BÁO - Trong các ứng dụng đa luồng, đảm bảo rằng quyền truy cập vào các dữ liệu toàn cục DLL được đồng bộ (luồng an toàn) để tránh có thể mất mát dữ liệu. Để làm điều này, sử dụng TLS để cung cấp dữ liệu duy nhất cho mỗi thread.
export function DLL
Để export chức năng DLL, bạn có thể thêm một từ khóa chức năng đến các chức năng DLL export, tạo ra một định nghĩa module (.def) tập tin liệt kê các chức năng DLL export.
-
Để sử dụng một từ khóa chức năng, bạn phải khai báo cho từng chức năng mà bạn muốn export với từ khóa sau đây - __declspec(dllexport)
-
Để sử dụng chức năng DLL trong các ứng dụng, bạn phải khai báo cho từng chức năng mà bạn muốn inport với từ khóa sau đây -__declspec(dllimport)
Thông thường, bạn sẽ sử dụng một tập tin tiêu đề có xác định tuyên bố và một ifdef tuyên bố để tách các báo cáo xuất khẩu và các tuyên bố nhập khẩu.
Bạn cũng có thể sử dụng một tập tin định nghĩa module để khai báo các chức năng DLL export. Khi bạn sử dụng một tập tin định nghĩa module, bạn không cần phải thêm từ khóa chức năng đến các chức năng DLL export. Trong các tập tin định nghĩa module, bạn khai báo các LIBRARY và tuyên bố export lệnh nào cho DLL. Các mã sau đây là một ví dụ về một tập tin định nghĩa.
// SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS
HelloWorld
Viết một DLL mẫu
Trong Microsoft Visual C ++ 6.0, bạn có thể tạo một DLL bằng cách chọn một trong hai loại dự án Win32 Dynamic-Link Library hoặc MFC AppWizard (dll).
Các đoạn mã sau đây là một ví dụ của một DLL đã được tạo ra trong Visual C ++ bằng cách sử dụng các loại dự án Win32 Dynamic-Link Library.
// SampleDLL.cpp
#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
return TRUE;
}
void HelloWorld()
{
MessageBox( NULL, TEXT("Hello World"),
TEXT("In a DLL"), MB_OK);
}
// File: SampleDLL.h
//
#ifndef INDLL_H
#define INDLL_H
#ifdef EXPORTING_DLL
extern __declspec(dllexport) void HelloWorld() ;
#else
extern __declspec(dllimport) void HelloWorld() ;
#endif
#endif
Gọi một DLL mẫu
Các mã sau đây là một ví dụ về một dự án ứng dụng Win32 mà gọi hàm DLL xuất khẩu trong SampleDLL DLL.
// SampleApp.cpp
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HelloWorld();
return 0;
}
Chú ý - Trong thời gian load-time dynamic linking, bạn phải liên kết các thư viện inporrt SampleDLL.lib được tạo ra khi bạn xây dựng dự án SampleDLL.
Trong run-time dynamic linking, bạn sử dụng mã số đó là tương tự như mã dưới đây để gọi SampleDLL.dll export function DLL.
...
typedef VOID (*DLLPROC) (LPTSTR);
...
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;
hinstDLL = LoadLibrary("sampleDLL.dll");
if (hinstDLL != NULL)
{
HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
if (HelloWorld != NULL)
(HelloWorld);
fFreeDLL = FreeLibrary(hinstDLL);
}
...
Khi bạn biên dịch và liên kết các ứng dụng SampleDLL, Windows tìm kiếm hệ điều hành cho các SampleDLL DLL trong các địa điểm sau theo thứ tự này -
1. Các thư mục ứng dụng
2. Các thư mục hiện tại
3. Các thư mục hệ thống Windows (các GetSystemDirectory chức năng trả về đường dẫn của thư mục hệ thống Windows).
4. Các thư mục Windows (các GetWindowsDirectory chức năng trả về đường dẫn của thư mục Windows).