DLL - Cách viết
Đầu tiên, chúng tôi 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 phát triển các tệp DLL của riêng mình.
Các loại DLL
Khi bạn tải một DLL trong một ứng dụng, hai phương pháp liên kết cho phép bạn gọi các hàm DLL đã xuất. Hai phương pháp liên kết là -
- liên kết động trong thời gian tải và
- liên kết động thời gian chạy.
Liên kết động thời gian tải
Trong liên kết động thời gian tải, một ứng dụng thực hiện các lệnh gọi rõ ràng đến các hàm DLL đã xuất như các hàm cục bộ. Để sử dụng liên kết động trong thời gian tải, hãy cung cấp tệp tiêu đề (.h) và tệp thư viện nhập (.lib), khi bạn biên dịch và liên kết ứng dụng. Khi bạn thực hiện việc này, trình liên kết sẽ cung cấp cho hệ thống thông tin cần thiết để tải DLL và giải quyết các vị trí chức năng DLL đã xuất tại thời điểm tải.
Liên kết động thời gian chạy
Trong liên kết động thời gian chạy, một ứng dụng gọi hàm LoadLibrary hoặc hàm LoadLibraryEx để tải DLL trong thời gian chạy. Sau khi tệp DLL được tải thành công, bạn sử dụng hàm GetProcAddress để lấy địa chỉ của hàm DLL đã xuất mà bạn muốn gọi. Khi bạn sử dụng liên kết động thời gian chạy, bạn không cần tệp thư viện nhập.
Danh sách sau đây mô tả các tiêu chí ứng dụng để lựa chọn giữa liên kết động trong thời gian tải và liên kết động trong thời gian chạy -
Startup performance - Nếu hiệu suất khởi động ban đầu của ứng dụng là quan trọng, bạn nên sử dụng liên kết động thời gian chạy.
Ease of use- Trong liên kết động thời gian tải, các hàm DLL được xuất giống như các hàm cục bộ. Nó giúp bạn gọi các chức năng này một cách dễ dàng.
Application logic- Trong liên kết động thời gian chạy, một ứng dụng có thể phân nhánh để tải các mô-đun 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ữ.
Điểm vào DLL
Khi bạn tạo một DLL, bạn có thể tùy chọn chỉ định một chức năng điểm nhập. Hàm entry point được gọi khi các tiến trình hoặc luồng tự gắn vào DLL hoặc tự tách khỏi DLL. Bạn có thể sử dụng hàm điểm nhập để khởi tạo hoặc phá hủy cấu trúc dữ liệu theo yêu cầu của DLL.
Ngoài ra, nếu ứng dụng đa luồng, bạn có thể sử dụng lưu trữ cục bộ luồng (TLS) để cấp phát bộ nhớ riêng cho từng luồng trong hàm điểm nhập. Đoạn mã sau đây là một ví dụ về hàm điểm nhập DLL.
BOOL APIENTRY DllMain(
HANDLE hModule, // Handle to DLL module
DWORD ul_reason_for_call,
LPVOID lpReserved ) // Reserved
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACHED:
// A process is loading the DLL.
break;
case DLL_THREAD_ATTACHED:
// A process is creating a new thread.
break;
case DLL_THREAD_DETACH:
// A thread exits normally.
break;
case DLL_PROCESS_DETACH:
// A process unloads the DLL.
break;
}
return TRUE;
}
Khi hàm điểm nhập trả về giá trị FALSE, ứng dụng sẽ không khởi động nếu bạn đang sử dụng liên kết động thời gian tải. Nếu bạn đang sử dụng liên kết động thời gian chạy, chỉ DLL riêng lẻ sẽ không tải.
Hàm điểm nhập chỉ nên thực hiện các tác vụ khởi tạo đơn giản và không được gọi bất kỳ hàm tải hoặc kết thúc DLL nào khác. Ví dụ: trong hàm điểm nhập, bạn không nên trực tiếp hoặc gián tiếp gọiLoadLibrary chức năng hoặc LoadLibraryExchức năng. Ngoài ra, bạn không nên gọiFreeLibrary hoạt động khi quá trình kết thúc.
WARNING- Trong các ứng dụng đa luồng, đảm bảo rằng quyền truy cập vào dữ liệu toàn cầu DLL được đồng bộ hóa (an toàn luồng) để tránh dữ liệu có thể bị hỏng. Để thực hiện việc này, hãy sử dụng TLS để cung cấp dữ liệu duy nhất cho mỗi luồng.
Xuất các hàm DLL
Để xuất các hàm DLL, bạn có thể thêm từ khóa hàm vào các hàm DLL đã xuất hoặc tạo tệp định nghĩa mô-đun (.def) liệt kê các hàm DLL đã xuất.
Để sử dụng từ khóa hàm, bạn phải khai báo từng hàm mà bạn muốn xuất với từ khóa sau:
__declspec(dllexport)
Để sử dụng các hàm DLL đã xuất trong ứng dụng, bạn phải khai báo từng hàm mà bạn muốn nhập với từ khóa sau:
__declspec(dllimport)
Thông thường, bạn sẽ sử dụng một tệp tiêu đề có define tuyên bố và một ifdef câu lệnh để tách câu lệnh xuất và câu lệnh nhập.
Bạn cũng có thể sử dụng tệp định nghĩa mô-đun để khai báo các hàm DLL đã xuất. Khi bạn sử dụng tệp định nghĩa mô-đun, bạn không phải thêm từ khóa hàm vào các hàm DLL đã xuất. Trong tệp định nghĩa mô-đun, bạn khai báoLIBRARY tuyên bố và EXPORTScâu lệnh cho DLL. Đoạn mã sau là một ví dụ về tệp đị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 DLL bằng cách chọn Win32 Dynamic-Link Library loại dự án hoặc MFC AppWizard (dll) Loại dự án.
Đoạn mã sau là một ví dụ về DLL được tạo trong Visual C ++ bằng cách sử dụng loại dự án Thư viện liên kết động Win32.
// 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
Đoạn mã sau là một ví dụ về dự án Ứng dụng Win32 gọi hàm DLL đã xuất trong DLL SampleDLL.
// SampleApp.cpp
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HelloWorld();
return 0;
}
NOTE - Trong liên kết động thời gian tải, bạn phải liên kết thư viện nhập SampleDLL.lib được tạo khi bạn xây dựng dự án SampleDLL.
Trong liên kết động thời gian chạy, bạn sử dụng mã tương tự như mã sau để gọi hàm DLL đã xuất SampleDLL.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 ứng dụng SampleDLL, hệ điều hành Windows sẽ tìm kiếm SampleDLL DLL ở các vị trí sau theo thứ tự này:
Thư mục ứng dụng
Thư mục hiện tại
Thư mục hệ thống Windows ( GetSystemDirectory hàm trả về đường dẫn của thư mục hệ thống Windows).
Thư mục Windows ( GetWindowsDirectory hàm trả về đường dẫn của thư mục Windows).