DLL - Hướng dẫn nhanh
Liên kết động là cơ chế liên kết các ứng dụng với thư viện tại thời điểm chạy. Các thư viện vẫn nằm trong tệp riêng của chúng và không được sao chép vào tệp thực thi của ứng dụng. Các DLL liên kết đến một ứng dụng khi ứng dụng được chạy, thay vì khi nó được tạo. Các tệp DLL có thể chứa các liên kết đến các tệp DLL khác.
Nhiều lần, các tệp DLL được đặt trong các tệp có phần mở rộng khác nhau như .EXE, .DRV hoặc .DLL.
Ưu điểm của DLL
Dưới đây là một số lợi ích của việc có tệp DLL.
Sử dụng ít tài nguyên hơn
Các tệp DLL không được tải vào RAM cùng với chương trình chính; chúng không chiếm không gian trừ khi được yêu cầu. Khi cần tệp DLL, nó sẽ được tải và chạy. Ví dụ: miễn là người dùng Microsoft Word đang chỉnh sửa tài liệu, tệp DLL của máy in không cần thiết trong RAM. Nếu người dùng quyết định in tài liệu, thì ứng dụng Word sẽ làm cho tệp DLL của máy in được tải và chạy.
Thúc đẩy kiến trúc mô-đun
DLL giúp thúc đẩy phát triển các chương trình mô-đun. Nó giúp bạn phát triển các chương trình lớn yêu cầu nhiều phiên bản ngôn ngữ hoặc một chương trình yêu cầu kiến trúc mô-đun. Một ví dụ về chương trình mô-đun là một chương trình kế toán có nhiều mô-đun có thể được tải động tại thời điểm chạy.
Hỗ trợ triển khai và cài đặt dễ dàng
Khi một chức năng trong DLL cần cập nhật hoặc sửa lỗi, việc triển khai và cài đặt DLL không yêu cầu chương trình phải được liên kết lại với DLL. Ngoài ra, nếu nhiều chương trình sử dụng cùng một DLL, thì tất cả chúng đều được hưởng lợi từ bản cập nhật hoặc bản sửa lỗi. Sự cố này có thể xảy ra thường xuyên hơn khi bạn sử dụng DLL của bên thứ ba thường xuyên được cập nhật hoặc khắc phục.
Các ứng dụng và DLL có thể tự động liên kết với các DLL khác, nếu liên kết DLL được chỉ định trong phần IMPORTS của tệp định nghĩa mô-đun như một phần của trình biên dịch. Ngoài ra, bạn có thể tải chúng một cách rõ ràng bằng chức năng Windows LoadLibrary.
Tệp DLL quan trọng
COMDLG32.DLL - Điều khiển các hộp thoại.
GDI32.DLL - Chứa nhiều chức năng để vẽ đồ họa, hiển thị văn bản và quản lý phông chữ.
KERNEL32.DLL - Chứa hàng trăm chức năng để quản lý bộ nhớ và các quy trình khác nhau.
USER32.DLL- Chứa nhiều chức năng giao diện người dùng. Tham gia vào việc tạo ra các cửa sổ chương trình và tương tác của chúng với nhau.
Đầ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, hãy đả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).
Để sử dụng DLL, nó phải được đăng ký bằng cách nhập các tham chiếu thích hợp vào Registry. Đôi khi xảy ra trường hợp tham chiếu Registry bị hỏng và các chức năng của DLL không thể sử dụng được nữa. DLL có thể được đăng ký lại bằng cách mở Start-Run và nhập lệnh sau:
regsvr32 somefile.dll
Lệnh này giả định rằng somefile.dll nằm trong một thư mục hoặc thư mục nằm trong PATH. Nếu không, đường dẫn đầy đủ cho DLL phải được sử dụng. Một tệp DLL cũng có thể được hủy đăng ký bằng cách sử dụng công tắc "/ u" như hình dưới đây.
regsvr32 /u somefile.dll
Điều này có thể được sử dụng để bật và tắt một dịch vụ.
Một số công cụ có sẵn để giúp bạn khắc phục sự cố DLL. Một số trong số chúng được thảo luận dưới đây.
Người đi phụ thuộc
Công cụ Dependency Walker (depends.exe) có thể quét đệ quy tất cả các DLL phụ thuộc được chương trình sử dụng. Khi bạn mở một chương trình trong Dependency Walker, Dependency Walker thực hiện các kiểm tra sau:
- Kiểm tra các tệp DLL bị thiếu.
- Kiểm tra các tệp chương trình hoặc DLL không hợp lệ.
- Kiểm tra xem chức năng nhập và chức năng xuất có khớp nhau không.
- Kiểm tra lỗi phụ thuộc vòng tròn.
- Kiểm tra các mô-đun không hợp lệ vì các mô-đun này dành cho một hệ điều hành khác.
Bằng cách sử dụng Dependency Walker, bạn có thể ghi lại tất cả các DLL mà một chương trình sử dụng. Nó có thể giúp ngăn ngừa và khắc phục các sự cố DLL có thể xảy ra trong tương lai. Dependency Walker nằm trong thư mục sau khi bạn cài đặt Microsoft Visual Studio 6.0:
drive\Program Files\Microsoft Visual Studio\Common\Tools
DLL Universal Problem Solver
Công cụ DLL Universal Problem Solver (DUPS) được sử dụng để kiểm tra, so sánh, lập tài liệu và hiển thị thông tin DLL. Danh sách sau đây mô tả các tiện ích tạo nên công cụ DUPS:
Dlister.exe - Tiện ích này liệt kê tất cả các tệp DLL trên máy tính và ghi thông tin vào tệp văn bản hoặc tệp cơ sở dữ liệu.
Dcomp.exe - Tiện ích này so sánh các tệp DLL được liệt kê trong hai tệp văn bản và tạo ra tệp văn bản thứ ba chứa sự khác biệt.
Dtxt2DB.exe - Tiện ích này tải các tệp văn bản được tạo bằng cách sử dụng tiện ích Dlister.exe và tiện ích Dcomp.exe vào cơ sở dữ liệu dllHell.
DlgDtxt2DB.exe - Tiện ích này cung cấp phiên bản giao diện người dùng đồ họa (GUI) của tiện ích Dtxt2DB.exe.
Hãy ghi nhớ các mẹo sau khi viết DLL:
Sử dụng quy ước gọi phù hợp (C hoặc stdcall).
Hãy lưu ý về thứ tự chính xác của các đối số được truyền vào hàm.
KHÔNG BAO GIỜ thay đổi kích thước mảng hoặc nối các chuỗi bằng cách sử dụng các đối số được truyền trực tiếp cho một hàm. Hãy nhớ rằng, các tham số bạn truyền là dữ liệu LabVIEW. Việc thay đổi kích thước mảng hoặc chuỗi có thể dẫn đến sự cố do ghi đè dữ liệu khác được lưu trữ trong bộ nhớ LabVIEW. Bạn CÓ THỂ thay đổi kích thước mảng hoặc nối chuỗi nếu bạn vượt qua Trình xử lý Mảng LabVIEW hoặc Xử lý Chuỗi LabVIEW và đang sử dụng trình biên dịch Visual C ++ hoặc trình biên dịch Symantec để biên dịch DLL của bạn.
Trong khi chuyển chuỗi cho một hàm, hãy chọn đúng loại chuỗi để chuyển. C hoặc Pascal hoặc chuỗi LabVIEW Xử lý.
Chuỗi Pascal được giới hạn độ dài 255 ký tự.
C chuỗi được kết thúc NULL. Nếu hàm DLL của bạn trả về dữ liệu số ở định dạng chuỗi nhị phân (ví dụ: qua GPIB hoặc cổng nối tiếp), hàm này có thể trả về giá trị NULL như một phần của chuỗi dữ liệu. Trong những trường hợp như vậy, việc chuyển các mảng số nguyên ngắn (8-bit) là đáng tin cậy nhất.
Nếu bạn đang làm việc với mảng hoặc chuỗi dữ liệu, LUÔN LUÔN chuyển một bộ đệm hoặc mảng đủ lớn để chứa bất kỳ kết quả nào được đặt trong bộ đệm bởi hàm trừ khi bạn chuyển chúng dưới dạng xử lý LabVIEW, trong trường hợp đó, bạn có thể thay đổi kích thước chúng bằng CIN các chức năng trong trình biên dịch Visual C ++ hoặc Symantec.
Liệt kê các hàm DLL trong phần EXPORTS của tệp định nghĩa mô-đun nếu bạn đang sử dụng _stdcall.
Liệt kê các hàm DLL mà các ứng dụng khác gọi trong phần EXPORTS tệp định nghĩa mô-đun hoặc để bao gồm từ khóa _declspec (dllexport) trong khai báo hàm.
Nếu bạn sử dụng trình biên dịch C ++, hãy xuất các hàm với câu lệnh extern .C. {} Trong tệp tiêu đề của bạn để ngăn việc sai tên.
Nếu bạn đang viết DLL của riêng mình, bạn không nên biên dịch lại DLL trong khi DLL được tải vào bộ nhớ bởi ứng dụng khác. Trước khi biên dịch lại DLL, hãy đảm bảo rằng tất cả các ứng dụng sử dụng DLL cụ thể đó đều được dỡ bỏ khỏi bộ nhớ. Nó đảm bảo rằng bản thân DLL không được tải vào bộ nhớ. Bạn có thể không xây dựng lại chính xác nếu bạn quên điều này và trình biên dịch của bạn không cảnh báo bạn.
Kiểm tra các tệp DLL của bạn bằng một chương trình khác để đảm bảo rằng chức năng (và DLL) hoạt động chính xác. Kiểm tra nó bằng trình gỡ lỗi của trình biên dịch của bạn hoặc một chương trình C đơn giản mà bạn có thể gọi một hàm trong DLL sẽ giúp bạn xác định xem liệu những khó khăn có thể xảy ra có liên quan đến DLL hoặc LabVIEW hay không.
Chúng ta đã biết cách viết một DLL và cách tạo chương trình "Hello World". Ví dụ đó chắc hẳn đã cho bạn một ý tưởng về khái niệm cơ bản của việc tạo một DLL.
Ở đây, chúng tôi sẽ đưa ra mô tả về việc tạo DLL bằng Delphi, Borland C ++ và VC ++.
Hãy để chúng tôi lấy từng ví dụ một.
Cách viết và gọi DLL trong Delphi
Tạo DLL từ Borland C ++ Builder IDE
Tạo DLL trong Microsoft Visual C ++ 6.0