Tạo thư viện DLL ở Delphi. Tạo và sử dụng thư viện liên kết động (DLL) trong Delphi



Giới thiệu

Do sự phát triển nhanh chóng của công nghệ lập trình, ngày càng có nhiều người phải đối mặt với vấn đề tăng cường khả năng của chương trình của họ. Bài viết này được dành riêng cho vấn đề này, cụ thể là lập trình DLL ở Borland Delphi. Ngoài ra, vì chúng tôi sẽ đề cập đến các vấn đề liên quan đến việc sử dụng DLL nên chúng tôi cũng sẽ đề cập đến việc nhập các chức năng từ các DLL nước ngoài (bao gồm cả các hệ thống, tức là WinAPI).

Khu vực ứng dụng DLL

Vì vậy, tại sao các DLL lại cần thiết và chúng được sử dụng ở đâu?.. Hãy liệt kê một số lĩnh vực ứng dụng của chúng:

Thư viện riêng biệt Chứa các chức năng bổ sung hữu ích cho lập trình viên. Ví dụ: các hàm làm việc với chuỗi hoặc các thư viện phức tạp để chuyển đổi hình ảnh. Kho lưu trữ tài nguyên DLL có thể lưu trữ không chỉ các chương trình và chức năng mà còn tất cả các loại tài nguyên - biểu tượng, hình ảnh, mảng chuỗi, menu, v.v. Thư viện hỗ trợ Ví dụ bao gồm thư viện của các gói nổi tiếng như: DirectX, ICQAPI (API cho ICQ), OpenGL, v.v. Các phần của chương trình Ví dụ: cửa sổ chương trình (biểu mẫu), v.v. có thể được lưu trữ trong một tệp DLL. Plugin Đây là nơi chứa đựng những suy nghĩ thực sự của một lập trình viên! Plugin là phần bổ sung cho chương trình nhằm mở rộng khả năng của nó. Ví dụ: trong bài viết này chúng ta sẽ xem xét lý thuyết tạo plugin cho chương trình của riêng bạn. Một tài nguyên chia sẻ DLL (Thư viện liên kết động) có thể được sử dụng bởi một số chương trình hoặc quy trình cùng một lúc (cái gọi là chia sẻ - tài nguyên được chia sẻ)

Mô tả ngắn gọn về các chức năng và kỹ thuật làm việc với DLL

Vậy bạn cần sử dụng những kỹ thuật và chức năng nào để làm việc với DLL? Hãy xem xét hai phương pháp nhập hàm từ thư viện:

1 chiều. Ràng buộc một DLL vào một chương trình.

Đây là phương pháp đơn giản và dễ dàng nhất để sử dụng các hàm được nhập từ DLL. Tuy nhiên (và điều này cần lưu ý) phương pháp này có một nhược điểm rất đáng kể - nếu không tìm thấy thư viện mà chương trình sử dụng, thì chương trình sẽ không khởi động, đưa ra lỗi và báo cáo rằng không tìm thấy tài nguyên DLL. Và thư viện sẽ được tìm kiếm: trong thư mục hiện tại, trong thư mục chương trình, trong thư mục WINDOWS\SYSTEM, v.v. Vì vậy, đầu tiên, dạng chung của kỹ thuật này:


Tên hàm (hoặc Tên thủ tục) tên của hàm (hoặc thủ tục) sẽ được sử dụng trong chương trình của bạn; Par1, Par2,... tên các tham số hàm hoặc thủ tục; Par1Type, Par2Type,... các loại tham số hàm hoặc thủ tục (ví dụ: Integer); Kiểu trả về ReturnType (chỉ chức năng); lệnh stdcall, phải khớp chính xác với lệnh được sử dụng trong chính DLL; lệnh "DLLNAME.DLL" bên ngoài chỉ định tên của DLL bên ngoài mà từ đó hàm hoặc thủ tục này sẽ được nhập (trong trường hợp này là DLLNAME.DLL); Chỉ thị name "FunctionName" ("ProcedureName") chỉ định tên chính xác của hàm trong chính tệp DLL. Đây là một lệnh tùy chọn cho phép sử dụng một hàm trong một chương trình có tên khác với tên thật (có trong thư viện); chỉ mục Lệnh FunctionIndex (ProcedureIndex) cho biết số chỉ mục của hàm hoặc thủ tục trong một DLL. Đây cũng là một chỉ thị tùy chọn.

Đây là một phương pháp phức tạp hơn nhiều nhưng cũng thanh lịch hơn. Nó không có nhược điểm của phương pháp đầu tiên. Điều khó chịu duy nhất là số lượng mã cần thiết để triển khai kỹ thuật này và khó khăn là hàm được nhập từ DLL chỉ có thể truy cập được khi DLL này được tải và nằm trong bộ nhớ... Bạn có thể xem ví dụ bên dưới, nhưng hiện tại - mô tả ngắn gọn về các hàm WinAPI được phương pháp này sử dụng:

LoadLibrary(LibFileName: PChar) Tải thư viện đã chỉ định LibFileName vào bộ nhớ. Nếu thành công, hàm trả về một điều khiển (THandle) cho DLL trong bộ nhớ. GetProcAddress(Module: THandle; ProcName: PChar) đọc địa chỉ của hàm thư viện đã xuất. Nếu thành công, hàm trả về một điều khiển (TFarProc) cho hàm trong tệp DLL đã tải. FreeLibrary(LibModule: THandle) vô hiệu hóa LibModule và giải phóng bộ nhớ liên kết với nó. Cần lưu ý rằng sau khi gọi thủ tục này, các chức năng của thư viện này sẽ không còn nữa.

Thực hành và ví dụ

Chà, bây giờ là lúc đưa ra một vài ví dụ về việc sử dụng các phương pháp và kỹ thuật trên:

Ví dụ 1. Liên kết DLL với chương trình


Bây giờ điều tương tự, nhưng theo cách thứ hai - với tải động:


Ghi chú:

Bạn nên hạn chế sử dụng kiểu chuỗi trong các hàm thư viện vì Có vấn đề với việc "chia sẻ bộ nhớ" khi sử dụng nó. Bạn có thể đọc thêm về điều này (bằng tiếng Anh) trong văn bản của dự án DLL trống mà Delphi tạo ra (Tệp -> Mới -> DLL). Vì vậy, tốt hơn hết bạn nên sử dụng PChar và sau đó chuyển đổi nó thành chuỗi bằng hàm StrPas nếu cần thiết.

Chà, bây giờ chúng ta hãy nhìn vào chính DLL:

Ví dụ 3. Nguồn của dự án MYDLL.DPR


Đặt tài nguyên và biểu mẫu trong DLL

Một DLL không chỉ có thể chứa các hàm mà còn cả con trỏ, hình ảnh, biểu tượng, menu và chuỗi văn bản. Chúng tôi sẽ không tập trung vào điều này. Tôi sẽ chỉ lưu ý rằng để tải một tài nguyên, bạn cần tải DLL, sau đó, sau khi nhận được bộ mô tả của nó, hãy tải chính tài nguyên đó bằng hàm thích hợp (LoadIcon, LoadCursor, v.v.). Trong phần này, chúng tôi sẽ chỉ đề cập ngắn gọn về vị trí của các cửa sổ ứng dụng (tức là các biểu mẫu trong Delphi) trong DLL.

Để thực hiện việc này, bạn cần tạo một DLL mới và thêm một biểu mẫu mới vào đó (Tệp -> Mới -> DLL, rồi Tệp -> Biểu mẫu mới). Tiếp theo, nếu biểu mẫu là một hộp thoại (dạng modal (bsDialog)), thì hãy thêm hàm sau vào DLL (giả sử biểu mẫu có tên là Form1 và lớp của nó là TForm1):

Ví dụ 4: Đặt biểu mẫu trong DLL


Nếu bạn cần đặt một biểu mẫu không theo phương thức trong một DLL, thì bạn cần tạo hai hàm - mở và đóng biểu mẫu. Trong trường hợp này, bạn cần buộc DLL ghi nhớ phần xử lý của biểu mẫu này.

Tạo plugin

Ở đây chúng tôi sẽ không xem xét chi tiết các plugin, bởi vì... Các ví dụ đã đưa ra ở trên sẽ giúp bạn dễ dàng hiểu được phần quan trọng nhất của lập trình DLL. Hãy để tôi nhắc bạn rằng plugin là một phần bổ sung cho chương trình nhằm mở rộng khả năng của nó. Đồng thời, bản thân chương trình phải cung cấp sự hiện diện của những phần bổ sung đó và cho phép chúng hoàn thành mục đích của mình.

Ví dụ: để tạo một plugin cho trình chỉnh sửa đồ họa có thể thực hiện chuyển đổi hình ảnh, bạn cần cung cấp ít nhất hai hàm trong plugin (và theo đó, gọi các hàm này trong chương trình) - một hàm sẽ trả về tên của plugin (và/hoặc loại của nó) để thêm plugin này vào menu (hoặc thanh công cụ), cộng với chức năng chính - truyền và nhận hình ảnh. Những thứ kia. đầu tiên, chương trình tìm kiếm các plugin, sau đó với mỗi plugin được tìm thấy, nó gọi chức năng nhận dạng của mình bằng một tên được xác định nghiêm ngặt (ví dụ: GetPluginName) và thêm mục mong muốn vào menu, sau đó, nếu người dùng đã chọn mục này, nó sẽ gọi hàm thứ hai, chuyển hình ảnh đầu vào (hoặc tên tệp, chứa hình ảnh này) và hàm này lần lượt xử lý hình ảnh và trả về nó ở dạng mới (hoặc tên tệp có hình ảnh mới). Đó là bản chất của plugin... :-)

Lời kết

Bài viết này trình bày những kiến ​​thức cơ bản về cách sử dụng và tạo DLL trong Borland Delphi. Nếu bạn có thắc mắc, hãy gửi cho tôi qua email:

Các đồng nghiệp thân mến!

Trong bài viết này tôi sẽ cố gắng trả lời các câu hỏi: Thế nào là DLL? Nó dùng để làm gì? Và cách tạo và sử dụng DLL với sự giúp đỡ Delphi.

DLL là gì?

Thư viện liên kết động hoặc viết tắt DLL là một thư viện chứa một tập hợp dữ liệu hoặc hàm để sử dụng trong các chương trình khác.

Các lĩnh vực sử dụng:

  • Lưu trữ tài nguyên: biểu tượng, âm thanh, con trỏ, v.v. Chúng tôi tiết kiệm kích thước của các tệp thực thi bằng cách kết hợp các tài nguyên vào một thư viện duy nhất.
  • Vị trí của các mô-đun chương trình riêng lẻ và các hình thức giao diện. Chúng tôi có cơ hội cập nhật một phần ứng dụng và với kết nối động, có thể cập nhật các mô-đun mà không cần khởi động lại chương trình chính.
  • Sử dụng làm plugin (PlugIn). Chúng tôi cung cấp cơ hội mở rộng chức năng của ứng dụng mà không cần viết lại mã chương trình chính.
  • Thư viện có thể được sử dụng bằng các ngôn ngữ lập trình khác nhau, bất kể nó được viết bằng ngôn ngữ nào.

Tạo DLL của riêng bạn.

Để tạo thư viện, hãy vào menu Tài liệu -> Khác và lựa chọn Dự án Delphi -> Thư viện liên kết động.
Văn bản mã có mẫu để tạo thư viện sẽ mở ra:

Dự án Thư viện1; sử dụng System.SysUtils, System.Classes; ($R *.res) bắt đầu kết thúc.

Vị trí tài nguyên

Hãy thêm một biểu tượng vào thư viện mà chúng ta sẽ sử dụng sau này trong chương trình chính.
Cách thêm tài nguyên vào dự án được mô tả chi tiết trong bài viết.

Hãy thêm biểu mẫu “Giới thiệu về chương trình”.
Nhấp chuột Tài liệu -> Mới -> Mẫu VCL. Hãy thêm văn bản và nút "OK":

Hãy thêm một chức năng sẽ hiển thị tiêu chuẩn Hộp tin nhắn với một câu hỏi, các nút “Có”, “Không” và có kết quả ở dạng ĐÚNG VẬY hoặc SAI.

Hàm YesNoDlg(const Câu hỏi: PChar): boolean; stdcall; bắt đầu Kết quả:= (MessageBox(0, Câu hỏi, "Xác nhận", MB_YESNO + MB_ICONQUESTION) = ID_YES); kết thúc;

Chú ý đến từ khóa cuộc gọi std. Nó xác định chính xác cách các tham số và kết quả sẽ được truyền khi gọi hàm. Bạn có thể đọc thêm trên trang wiki Thỏa thuận gọi điện.

Chúng tôi cũng sẽ thêm một quy trình sẽ được sử dụng làm phần bổ trợ cho chương trình chính. Nút này sẽ thêm nút “Giới thiệu về chương trình” vào menu chương trình chính để mở cửa sổ mà chúng tôi đã tạo.
Mã thủ tục:

Thủ tục PlugIn(Mẫu: TForm); stdcall; var i: số nguyên; mi: TMenuItem; bắt đầu cho i:= 0 đến Form.ComponentCount - 1 bắt đầu nếu (Form.Components[i].ClassName = "TMenuItem") và (Form.Components[i].Name = "miHelp") thì bắt đầu mi:= TMenuItem .Create(Form.Components[i]); mi.Caption:= "Giới thiệu về chương trình"; mi.OnClick:= fmAbout.onAboutButtonClick; TMenuItem(Form.Components[i]).Add(mi); Lối ra; kết thúc; kết thúc; kết thúc;

Hình thức của chương trình được chuyển tới thủ tục dưới dạng tham số. Chúng tôi kiểm tra xem có mục menu nào có tên “ miHelp"và nếu tìm thấy menu, hãy thêm nút của chúng tôi vào đó.

Bây giờ chúng tôi sẽ chỉ ra những chức năng và thủ tục nào có thể được sử dụng từ thư viện của chúng tôi.
Hãy thêm dòng:

Xuất PlugIn, YesNoDlg;

Hãy biên dịch hàm Dự án -> Xây dựng hoặc sử dụng phím nóng Shift+F9.
Nếu không có lỗi trong mã thì một tệp có phần mở rộng sẽ xuất hiện trong thư mục dự án DLL.

Bây giờ hãy chuyển sang tạo một ứng dụng sẽ sử dụng thư viện của chúng tôi.

Hãy tạo một biểu mẫu trong ứng dụng Chủ yếu trong đó chúng tôi sẽ thêm một thành phần với các nút sau: Chương trình -> Thoát và Trợ giúp. Đối với cái cuối cùng, hãy đặt tên - miHelp:

Hãy chuyển sang mã mẫu.

Chức năng từ thư viện DLL có thể được kết nối theo hai cách:
Tĩnh— thư viện được kết nối khi chương trình bắt đầu. Nếu không tìm thấy thư viện hoặc tên hàm, chương trình sẽ báo lỗi và không chạy.
Năng động— thư viện được kết nối ngay trước lệnh gọi hàm hoặc sau một sự kiện cụ thể.

Hãy xem xét phương thức kết nối tĩnh:

Gõ TfmMain = class(TForm) MainMenu: TMainMenu; miProgram: TMenuItem; miExit: TMenuItem; miHelp: TMenuItem; thủ tục FormCreate(Người gửi: TObject); thủ tục miExitClick(Người gửi: TObject); mục đích chung riêng tư; thủ tục PlugIn(Form: TForm); stdcall; bên ngoài "SampleDLL.dll"; var...

Từ khóa bên ngoài cho biết chức năng này được kết nối từ thư viện bên ngoài.

Dành cho sự kiện onTạo biểu mẫu, thêm lệnh gọi thủ tục Cắm vào:

Thủ tục TfmMain.FormCreate(Người gửi: TObject); bắt đầu PlugIn(Tự); kết thúc;

Là một tham số Hình thức chúng tôi chuyển biểu mẫu hiện tại cho thủ tục (từ khóa Bản thân).

Khi khởi động chương trình, chúng ta nên có mục “Giới thiệu về chương trình” trong phần “Trợ giúp” của menu chính.

Hãy chuyển sang phương pháp kết nối động.

Chúng ta sẽ cần ba chức năng WinApi:

TảiThư viện
Tải thư viện vào bộ nhớ máy tính. Kết quả là nó trả về một con trỏ tới thư viện trong bộ nhớ. Trong trường hợp có lỗi, nó sẽ trả về 0.

LoadLibrary(lpLibFileName: LPCWSTR): HMODULE;

lpLibTên tệp- Tên tập tin thư viện.

Nhận địa chỉ Proc
Tìm một hàm trong thư viện theo tên. Kết quả sẽ là một con trỏ hàm. Nếu không tìm thấy chức năng, nó sẽ trả về không.

GetProcAddress(hModule: HMODULE; lpProcName: LPCSTR): FARPROC;

hMô-đun
lpProcName- Tên chức năng.

Thư viện miễn phí
Gỡ bỏ thư viện khỏi bộ nhớ của máy tính. Kết quả sẽ là True nếu thành công và False nếu có lỗi.

FreeLibrary(hLibModule: HMODULE): BOOL;

hLibModule— Con trỏ tới thư viện đã tải.

Bây giờ, bằng cách sử dụng phương thức động, chúng ta sẽ lấy tài nguyên, biểu tượng của mình và thêm lệnh gọi hàm vào nút “Thoát” CóKhôngDlgđể xác nhận chương trình đã đóng.
Thêm mã vào cùng một sự kiện onCreate:

Thủ tục TfmMain.FormCreate(Người gửi: TObject); var DLLHandle: THandle; bắt đầu PlugIn(Tự); DLLHandle:= LoadLibrary("SampleDLL.dll"); nếu DLLHandle = 0 thì raise Exception.Create("Không thể bao gồm thư viện 'SampleDLL'!"); thử Self.Icon.LoadFromResourceName(DLLHandle, "my_icon"); cuối cùng là FreeLibrary(DLLHandle); kết thúc; kết thúc;

Và đối với sự kiện trong một cái nhấp chuột mục menu "Thoát":

Quy trình TfmMain.miExitClick(Người gửi: TObject); var DLLHandle: THandle; Dlg: function(const Câu hỏi: PChar): boolean; stdcall; bắt đầu DLLHandle:= LoadLibrary("SampleDLL.dll"); nếu DLLHandle = 0 thì raise Exception.Create("Không thể bao gồm thư viện 'SampleDLL'!"); thử @Dlg:= GetProcAddress(DLLHandle, "YesNoDlg"); nếu không được chỉ định(@Dlg) thì raise Exception.Create("Không tìm thấy hàm có tên "YesNoDlg" trong thư viện "SampleDLL"!"); nếu Dlg("Thoát khỏi chương trình?") thì Đóng; cuối cùng là FreeLibrary(DLLHandle); kết thúc; kết thúc;

Nếu bạn đã viết chính xác mọi thứ, thì sau khi khởi động chương trình, biểu tượng biểu mẫu sẽ thay đổi, thêm nút “Giới thiệu về chương trình”, khi nhấp vào nó, biểu mẫu sẽ hiển thị Về và khi bạn nhấn nút thoát, chương trình sẽ yêu cầu xác nhận: “Thoát chương trình?”

Tôi hy vọng bạn thấy ví dụ nhỏ này về việc sử dụng các tính năng này hữu ích. DLL thư viện.
Các nguồn dự án có thể được tải xuống.

Do sự phát triển nhanh chóng của công nghệ lập trình, ngày càng có nhiều người phải đối mặt với vấn đề tăng cường khả năng của chương trình của họ. Bài viết này được dành riêng cho vấn đề này, cụ thể là lập trình DLL ở Borland Delphi. Ngoài ra, vì chúng tôi sẽ đề cập đến các vấn đề liên quan đến việc sử dụng DLL nên chúng tôi cũng sẽ đề cập đến việc nhập các chức năng từ các DLL nước ngoài (bao gồm cả các hệ thống, tức là WinAPI).

Khu vực ứng dụng DLL

Vì vậy, tại sao các DLL lại cần thiết và chúng được sử dụng ở đâu?.. Hãy liệt kê một số lĩnh vực ứng dụng của chúng:

  • Thư viện riêng lẻ, chứa các hàm bổ sung hữu ích cho người lập trình. Ví dụ: các hàm làm việc với chuỗi hoặc các thư viện phức tạp để chuyển đổi hình ảnh.
  • Lưu trữ tài nguyên. Một DLL có thể lưu trữ không chỉ các chương trình và chức năng mà còn tất cả các loại tài nguyên - biểu tượng, hình ảnh, mảng chuỗi, menu, v.v.
  • Thư viện hỗ trợ. Ví dụ: chúng ta có thể trích dẫn thư viện của các gói nổi tiếng như: DirectX, ICQAPI(API cho ICQ), OpenGL vân vân.
  • Các phần của chương trình. Ví dụ: bạn có thể lưu trữ các cửa sổ chương trình (biểu mẫu), v.v. trong một tệp DLL.
  • bổ sung(Bổ sung). - Đây là nơi chứa đựng những suy nghĩ thực sự của một lập trình viên! Plugin là phần bổ sung cho chương trình nhằm mở rộng khả năng của nó. Ví dụ: trong bài viết này chúng ta sẽ xem xét lý thuyết tạo plugin cho chương trình của riêng bạn.
  • Tài nguyên được chia sẻ. DLL( Thư viện liên kết động) có thể được sử dụng bởi một số chương trình hoặc quy trình cùng một lúc (cái gọi là. chia sẻ- tài nguyên được chia sẻ)

Mô tả ngắn gọn về các chức năng và kỹ thuật làm việc với DLL

Vậy bạn cần sử dụng những kỹ thuật và chức năng nào để làm việc với DLL? Hãy xem xét hai phương pháp nhập hàm từ thư viện:

1 chiều. Ràng buộc một DLL vào một chương trình.Đây là phương pháp đơn giản và dễ dàng nhất để sử dụng các hàm được nhập từ DLL. Tuy nhiên (và điều này cần lưu ý) phương pháp này có một nhược điểm rất đáng kể - nếu không tìm thấy thư viện mà chương trình sử dụng, thì chương trình sẽ không khởi động, đưa ra lỗi và báo cáo rằng không tìm thấy tài nguyên DLL. Và thư viện sẽ được tìm kiếm: trong thư mục hiện tại, trong thư mục chương trình, trong thư mục WINDOWS\SYSTEM, v.v.
Vì vậy, đầu tiên, dạng chung của kỹ thuật này:

thực hiện
...
chức năng FunctionName(Par1: Par1Type; Par2: Par2Type; ...): ReturnType; cuộc gọi std; bên ngoài"DLLNAME.DLL" tên"Tên chức năng" mục lục Func Index;
// hoặc (nếu không phải là hàm mà là thủ tục):
thủ tục Tên thủ tục(Par1: Par1Type; Par2: Par2Type; ...); cuộc gọi std; bên ngoài"DLLNAME.DLL" tên"Tên thủ tục" mục lục Proc Index;

Đây: Tên chức năng(hoặc Tên thủ tục) - tên của hàm (hoặc thủ tục) sẽ được sử dụng trong chương trình của bạn;
Par1, Par2, ...- tên của các tham số hàm hoặc thủ tục;
Par1Type, Par2Type, ...- các loại tham số hàm hoặc thủ tục (ví dụ: số nguyên);
Kiểu trả về- kiểu giá trị trả về (chỉ dành cho hàm);
cuộc gọi std- một lệnh phải khớp chính xác với lệnh được sử dụng trong chính DLL;
bên ngoài "DLLNAME.DLL"- một lệnh chỉ ra tên của DLL bên ngoài mà hàm hoặc thủ tục đã cho sẽ được nhập từ đó (trong trường hợp này - DLLNAME.DLL);
tên "Tên hàm" ("Tên thủ tục")- một lệnh chỉ ra tên chính xác của hàm trong chính DLL. Đây là một lệnh tùy chọn cho phép sử dụng một hàm trong một chương trình có tên khác với tên thật (có trong thư viện);
chỉ số FunctionIndex(ProcedureIndex)- một lệnh chỉ ra số thứ tự của một hàm hoặc thủ tục trong một DLL. Đây cũng là một chỉ thị tùy chọn.

Phương pháp 2. Tải động các DLL.Đây là một phương pháp phức tạp hơn nhiều nhưng cũng thanh lịch hơn. Nó không có nhược điểm của phương pháp đầu tiên. Điều khó chịu duy nhất là số lượng mã cần thiết để triển khai kỹ thuật này và khó khăn là hàm được nhập từ DLL chỉ có thể truy cập được khi DLL này được tải và nằm trong bộ nhớ... Bạn có thể xem ví dụ bên dưới, nhưng hiện tại - mô tả ngắn gọn về các hàm WinAPI được phương pháp này sử dụng:

TảiThư viện(Tên tệp Lib: PChar) - tải thư viện đã chỉ định LibFileName vào bộ nhớ. Khi hoàn thành thành công, hàm trả về một điều khiển ( Tay cầm) DLL trong bộ nhớ.
Nhận địa chỉ Proc(Mô-đun: Tay cầm; Tên Proc: PChar) - đọc địa chỉ của hàm thư viện đã xuất. Khi hoàn thành thành công, hàm trả về một điều khiển ( TFaProc) trong tệp DLL đã tải.
Thư viện miễn phí(Mô-đun Lib: Tay cầm) - vô hiệu hóa LibModule và giải phóng bộ nhớ được liên kết với nó. Cần lưu ý rằng sau khi gọi thủ tục này, các chức năng của thư viện này sẽ không còn nữa.

Thực hành và ví dụ

Chà, bây giờ là lúc đưa ra một vài ví dụ về việc sử dụng các phương pháp và kỹ thuật trên:

Bây giờ điều tương tự, nhưng theo cách thứ hai - với tải động:

(...Đây là tiêu đề tệp và định nghĩa của biểu mẫu TForm1 và mẫu Form1 của nó)

var
Mẫu1: TForm1;
GetSimpleText: chức năng(LangRus: Boolean): PChar;
LibHandle: THandle;

thủ tục Button1Click(Người gửi: TObject);
bắt đầu
(“Làm sạch” địa chỉ hàm khỏi “bụi bẩn”)
@GetSimpleText:= nil;
(Chúng tôi đang cố tải thư viện)
LibHandle:= LoadLibrary("MYDLL.DLL");
(Nếu mọi thứ đều ổn)
nếu LibHandle >= 32 thì bắt đầu
(...sau đó chúng tôi đang cố gắng lấy địa chỉ của hàm trong thư viện)
@GetSimpleText:= GetProcAddress(LibHandle,"GetSimpleText");
(Nếu mọi thứ ở đây đều ổn)
nếu @GetSimpleText<>không thì
(... sau đó gọi hàm này và hiển thị kết quả)
ShowMessage(StrPas(GetSimpleText(True)));
kết thúc;
(Và đừng quên giải phóng bộ nhớ và dỡ bỏ DLL)
Thư viện miễn phí (LibHandle);
kết thúc;

GHI CHÚ : Bạn nên hạn chế sử dụng kiểu chuỗi trong các hàm thư viện vì Có vấn đề với việc "chia sẻ bộ nhớ" khi sử dụng nó. Bạn có thể đọc thêm về điều này (bằng tiếng Anh) trong văn bản của dự án DLL trống mà Delphi tạo ra (Tệp -> Mới -> DLL). Vì vậy, tốt hơn hết bạn nên sử dụng PChar và sau đó chuyển đổi nó thành chuỗi bằng hàm StrPas nếu cần thiết.

Chà, bây giờ chúng ta hãy nhìn vào chính DLL:

Đặt tài nguyên và biểu mẫu trong DLL

Một DLL không chỉ có thể chứa các hàm mà còn cả con trỏ, hình ảnh, biểu tượng, menu và chuỗi văn bản. Chúng tôi sẽ không tập trung vào điều này. Tôi sẽ chỉ lưu ý rằng để tải một tài nguyên, bạn cần tải DLL, sau đó, sau khi nhận được bộ mô tả của nó, hãy tải chính tài nguyên đó bằng hàm thích hợp (LoadIcon, LoadCursor, v.v.). Trong phần này, chúng tôi sẽ chỉ đề cập ngắn gọn về vị trí của các cửa sổ ứng dụng (tức là các biểu mẫu trong Delphi) trong DLL.

Để thực hiện việc này, bạn cần tạo một DLL mới và thêm một biểu mẫu mới vào đó (Tệp -> Mới -> DLL, rồi Tệp -> Biểu mẫu mới). Tiếp theo, nếu biểu mẫu là một hộp thoại (dạng modal (bsDialog)), thì hãy thêm hàm sau vào DLL (giả sử biểu mẫu có tên là Form1 và lớp của nó là TForm1):

Nếu bạn cần đặt một biểu mẫu không theo phương thức trong một DLL, thì bạn cần tạo hai hàm - mở và đóng biểu mẫu. Trong trường hợp này, bạn cần buộc DLL ghi nhớ phần xử lý của biểu mẫu này.

Tạo plugin

Ở đây chúng tôi sẽ không xem xét chi tiết các plugin, bởi vì... Các ví dụ đã đưa ra ở trên sẽ giúp bạn dễ dàng hiểu được phần quan trọng nhất của lập trình DLL. Hãy để tôi nhắc bạn rằng plugin là một phần bổ sung cho chương trình nhằm mở rộng khả năng của nó. Đồng thời, bản thân chương trình phải cung cấp sự hiện diện của những phần bổ sung đó và cho phép chúng hoàn thành mục đích của mình.

Ví dụ: để tạo một plugin cho trình chỉnh sửa đồ họa có thể thực hiện chuyển đổi hình ảnh, bạn cần cung cấp ít nhất hai hàm trong plugin (và theo đó, gọi các hàm này trong chương trình) - một hàm sẽ trả về tên của plugin (và/hoặc loại của nó) để thêm plugin này vào menu (hoặc thanh công cụ), cộng với chức năng chính - truyền và nhận hình ảnh. Những thứ kia. đầu tiên, chương trình tìm kiếm các plugin, sau đó với mỗi plugin được tìm thấy, nó gọi chức năng nhận dạng của mình bằng một tên được xác định nghiêm ngặt (ví dụ: GetPluginName) và thêm mục mong muốn vào menu, sau đó, nếu người dùng đã chọn mục này, nó sẽ gọi hàm thứ hai, chuyển hình ảnh đầu vào (hoặc tên tệp, chứa hình ảnh này) và hàm này lần lượt xử lý hình ảnh và trả về nó ở dạng mới (hoặc tên tệp có hình ảnh mới). Đó là bản chất của plugin... :-)

Lời kết

Bài viết này trình bày những kiến ​​thức cơ bản về cách sử dụng và tạo DLL trong Borland Delphi. Nếu bạn có thắc mắc, hãy gửi cho tôi qua email: [email được bảo vệ], và thậm chí tốt hơn - hãy viết trong hội nghị trên trang này để những người dùng khác có thể xem câu hỏi của bạn và cố gắng trả lời nó!

Karikh Nikolay. Vùng Moscow, Zhukovsky

Thư viện DLL cho phép bạn kết hợp mã có thể sử dụng lại thành một tổng thể. Các hàm từ thư viện DLL có thể được liên kết động trong thời gian chạy, trái ngược với các hàm từ các gói Delphi, được liên kết tĩnh ở giai đoạn biên dịch của ứng dụng.

Để tạo thư viện DLL, trước tiên bạn cần thực thi lệnh menu File|New|Other và chọn phần tử DLL Wizard trên trang Mới của hộp thoại Mục mới.

Trình hướng dẫn DLL sẽ tự động tạo một mẫu trống cho DLL. Không giống như mô-đun thông thường bắt đầu bằng từ khóa đơn vị, mô-đun thư viện DLL bắt đầu bằng từ khóa thư viện. Phần sử dụng của mô-đun DLL chỉ yêu cầu bao gồm hai gói: SysUtils và Class.

Tạo một hàm DLL bao gồm một số bước:

1. Đầu tiên, trong phần triển khai mô-đun, bạn phải nhập chữ ký hàm và lập trình mã mà hàm thực thi.

3. Cuối cùng, một hàm được cho là không chỉ được sử dụng bên trong mô-đun mà còn được gọi từ các ứng dụng khác, phải được khai báo là có thể xuất được trong phần xuất.

Các hàm từ DLL có thể được gọi cả từ các ứng dụng được phát triển trong Delphi và từ các ứng dụng được viết bằng các ngôn ngữ lập trình khác, chẳng hạn như C++.

Thứ tự phân bổ bộ nhớ cho các tham số và giải phóng nó là khác nhau đối với các ngôn ngữ lập trình khác nhau. Để tránh lỗi thời gian chạy, việc khai báo hàm trong DLL và khai báo hàm trong ứng dụng phải sử dụng cùng một cơ chế truyền tham số. Khi khai báo một thủ tục hoặc hàm, có thể chỉ định một trong các cơ chế sau để truyền tham số:

Phương thức truyền tham số được biểu thị bằng dấu chấm phẩy sau phần mô tả hàm. Ví dụ:

hàm F1(X, Y, Z: Real]: Real; stdcall;.

Các phương thức truyền tham số khác nhau xác định thứ tự truyền tham số (trái sang phải hoặc phải sang trái) và cũng cho biết ai sẽ giải phóng bộ nhớ ngăn xếp (thủ tục được gọi hoặc thủ tục gọi). Khi sử dụng DLL làm thành phần được gọi từ các ứng dụng bằng các ngôn ngữ lập trình khác, bạn nên sử dụng công cụ sửa đổi cuộc gọi thích hợp. Đối với các ứng dụng C++, công cụ sửa đổi cuộc gọi stdcall được sử dụng.

Để một hàm được mô tả trong DLL có thể được gọi từ một ứng dụng khác, hàm đó phải được xuất. Danh sách tất cả các chức năng được xuất được chỉ định trong phần xuất, được phân tách bằng dấu phẩy

và kết thúc bằng dấu chấm phẩy. Chức năng xuất có thể được thực hiện theo ba cách:

Theo tên hàm được sử dụng trong DLL;

Theo tên hàm được chỉ định làm tên xuất;

Theo chỉ số được gán cho hàm.

Để gán chỉ mục cho hàm, cần chỉ định chỉ mục trong phần xuất sau tên hàm bằng từ khóa chỉ mục.

Để hàm xuất được gọi bằng tên khác với tên dùng trong thư viện DLL, ở phần xuất sau tên hàm bạn phải ghi rõ tên từ khóa và tên xuất mới cho hàm này.

DLL - thư viện không phải là mô-đun thực thi. Để có được mã của nó, chỉ cần biên dịch dự án là đủ.

dự án thư viện;

SysUtils, Lớp học;

hàm F1(X, Y: Số nguyên): Số nguyên; stdcall;

Liên kết tĩnh thư viện DLL

DLL có thể được liên kết tĩnh hoặc động. Khi bạn bao gồm một DLL, nó sẽ được tải vào bộ nhớ ứng dụng.

Với kết nối tĩnh, DLL được tải một lần khi ứng dụng khởi động. Trong suốt quá trình thực thi của ứng dụng, tên của hàm được nhập từ một DLL được liên kết tĩnh trỏ đến cùng một hàm (điểm vào DLL) trong cùng một DLL. Tất cả các hàm từ DLL sẽ được sử dụng ban đầu trong ứng dụng phải được khai báo là bên ngoài. Trong trường hợp này, bạn nên chỉ định, nếu cần, một công cụ sửa đổi cuộc gọi. Nếu một hàm được gọi theo chỉ mục thì nó phải được đặt tên được sử dụng trong ứng dụng và chỉ mục của hàm trong DLL.

Việc khai báo các hàm bên ngoài được thực hiện trong phần triển khai trước khi sử dụng các hàm này.

Tuyên bố của bên ngoài chức năng với từ khóa bên ngoài chỉ định rằng liên kết tĩnh sẽ được sử dụng.

TForml = lớp(TForm)

Biên tập: TEdit; [Trường để nhập giá trị đầu tiên)

Chỉnh sửa2: TEdit; (Trường để nhập giá trị thứ hai)

Chỉnh sửa3: Chỉnh sửa; (Trường hiển thị kết quả

thực thi một chức năng từ một DLL)

Nútl: TButton; (Một cuộc gọi được thực hiện đối với chức năng được sử dụng theo tên)

Nút2: Nút; [Một lệnh gọi hàm được thực hiện bằng cách sử dụng chỉ mục)

thủ tục ButtonlClickfSender: TObject);

thủ tục Button2Click(Người gửi: TObject);

(Khai báo riêng)

(Tuyên bố công khai)

(Khai báo hàm xuất)

hàm Fl(i: Số nguyên; j: Số nguyên): Số nguyên; stdcall;

bên ngoài "Projectl.dll";

hàm F2 (i: Số nguyên; j: Số nguyên): Số nguyên; stdcall;

bên ngoài "chỉ mục Projectl.dll 2;

thủ tục TForml.ButtonlClick(Người gửi: TObject);

(Gọi hàm xuất)

Edit3.Text:=IntToStr(Fl(StrToInt(Editl.Text),StrToInt(Edit2.Text) ));

thủ tục TForml.Button2Click(Người gửi: TObject);

Edit3.Text:=JntToStr(F2(StrToInt(Editl.Text),StrToInt(Edit2.Text)));

Liên kết động thư viện DLL

Không giống như liên kết tĩnh một DLL xảy ra khi ứng dụng tải, liên kết động một DLL có thể được thực hiện tại bất kỳ thời điểm nào trong quá trình thực thi chương trình. Sau khi gọi một hàm từ DLL, bạn có thể tắt nó. Khi sử dụng đồng thời nhiều DLL, điều này giúp tiết kiệm bộ nhớ đáng kể. Các hàm API của Windows được sử dụng để kết nối động thư viện DLL. API Windows -đây là tập hợp các hàm tiêu chuẩn được sử dụng để thực hiện tương tác với hệ điều hành.

Khi gọi một hàm từ một DLL được liên kết động, thay vì xác định tên hàm là bên ngoài trong trường hợp liên kết tĩnh, bạn nên xác định một loại mới tương ứng với loại của hàm được gọi và tạo một biến thuộc loại đó.

Để thực hiện lệnh gọi hàm từ DLL liên kết động, hãy làm theo các bước sau:

1. Tạo một loại mới. tương ứng với kiểu của hàm được gọi (tên kiểu mới có thể nhập sau phần kiểu).

Ví dụ:

TMyFl=function(i,j:Integer):Số nguyên; stdcall;

2. Trong phần var của phần giao diện của mô-đun, tạo một biến kiểu hàm đã tạo. Ví dụ: MyFl: TMyFl;

3. Trước khi tải thư viện DLL, hãy khai báo một biến kiểu Integer, biến này sẽ chứa bộ mô tả của thư viện đang được kết nối.

4. Gọi phương thức LoadLibrary để tải DLL. Ví dụ; h:=LoadLibrary("Projectl.dll");

5. Kiểm tra xem kết nối thư viện có thành công không. Nếu tên DLL không chính xác hoặc không tìm thấy DLL, hàm LoadLibrary sẽ trả về 0.

6. Nếu bạn kết nối thành công thư viện DLL, tiếp theo bạn cần lấy địa chỉ hàm. Để thực hiện việc này, hàm API Windows GetProcAddress được sử dụng, các tham số của hàm này là bộ mô tả thư viện DLL và tên của hàm được kết nối. Ví dụ: @MyFl: =GetProcAddress(h, "Fl");

7. Nếu địa chỉ hàm được nhận, thì giá trị của địa chỉ (trong ví dụ @MyFl của chúng tôi) không được bằng 0.

8. Tại thời điểm này, bạn có thể gọi một hàm từ một DLL được liên kết động.

9. Để giải phóng và do đó dỡ bỏ DLL, hãy gọi phương thức FreeLibrary để vô hiệu hóa DLL.

Windows, Tin nhắn, SysUtils, Biến thể, Lớp, Đồ họa,

Điều khiển, Biểu mẫu, Hộp thoại, StdCtrls;

TForml = lớp(TForm)

Nút3: Nút;

thủ tục Button3Click

thủ tục TForml.Button3Click(Người gửi: TObject);

h:=LoadLibrary("Projectl.dll");

nếu h<>0 thì

@MyFl:=GetProcAddress(h,"Fl");

nếu @MyFl<>không thì

Edit3.Text:=IntToStr(MyFl(StrToInt(Editl.Text),

StrToInt(Edit2.Text)));

Sử dụng DLL để gọi các hộp thoại phương thức chung.

Kết quả của việc thực hiện một thủ tục từ thư viện DLL có thể là hiển thị một số hộp thoại phương thức. Để thực hiện việc này, hãy tạo một đối tượng biểu mẫu trong phương thức đã xuất, hiển thị nó dưới dạng hộp thoại phương thức, sau đó xóa đối tượng biểu mẫu. Trong trường hợp này, chính biểu mẫu sẽ gọi phương thức Đóng để kết thúc hộp thoại.

Để tạo một biểu mẫu, hãy sử dụng phương thức Create, phương thức này được truyền dưới dạng tham số một con trỏ tới biểu mẫu gốc - biểu mẫu của ứng dụng gọi điện. Tham số này được truyền cho hàm DLL được gọi.

dự án thư viện;

Unitl_DLL trong "Unitl_DLL.pas" (Forml);

thủ tục MyModalForm (var Z:Integer ;F:TForm1); stdcall;

Form1:=TForml.Create(F);

(Tham số F được truyền khi gọi thủ tục và chứa một con trỏ

sang biểu mẫu gốc - biểu mẫu của ứng dụng gọi điện)

Môi trường lập trình Delphi cung cấp các công cụ tích hợp để tạo nhanh các tệp DLL.

Hãy tạo một thư viện chứa hàm cụ thể

GetArea(a, b, c: REAL): THỰC.

Hàm này lấy đầu vào là độ dài các cạnh của tam giác. Hàm trả về diện tích của tam giác đã cho:

p:=(a+b+c)/2;

Kết quả:=SQRT(p*(p-a)*(p-b)*(p-c))

Khởi động Delphi, và sau đó chúng ta hành động một cách khác thường. Chọn các mục menu FileNôiở đó, trong cửa sổ mở trên tab Nôinhấp vào biểu tượng DLL Wthằn lằn. ( thuật toán phụ thuộc vào phiên bản)

Điều này tạo ra một tệp DLL mẫu. Nó rất giống với một mô-đun thông thường (đơn vị) Delphi, chỉ bắt đầu với toán tửThư viện. Lưu dự án dưới tên mà DLL sẽ được cung cấp trong tương lai, chẳng hạn như:Có được một. TênNhận diện tíchkhông thể sử dụng được - nó đã bị tên hàm chiếm giữ.

Bây giờ sau khi người vận hànhSỬ DỤNGchúng ta viết văn bản cho hàm của mình, nhưng có một số thay đổi trong tiêu đề:

CHỨC NĂNG GetArea(a, b, c:REAL):REAL;xuất khẩu;

Từ khóa XUẤT chỉ ra rằng chức năng này có thể xuất được và sẽ hiển thị từ các chương trình bên ngoài.

Sau văn bản của hàm chúng ta sẽ thêm

Nhận diện tích;

Câu lệnh EXPORTS liệt kê tất cả các thủ tục và hàm được xuất từ ​​thư viện. Đây là một loại danh mục của thư viện của chúng tôi.

Không thể chạy thư viện; nó chỉ có thể được biên dịch. Để thực hiện việc này, hãy chọn mục menu Dự án → Xây dựng. Nếu mọi thứ được thực hiện chính xác, một tệp có tên geta.dll sẽ được tạo trên đĩa trong thư mục hiện tại. Đây là thư viện của chúng tôi.

Lưu ý quan trọng: Có một sự tinh tế nhất định khi truyền tham số kiểu STRING cho các thủ tục và hàm có trong thư viện.

Để có thể truyền các tham số của loại STRING, bạn sẽ phải chỉ định kết nối của mô-đun ShareMem trong câu lệnh USES của cả thư viện và chương trình gọi nó, và thậm chí để mô-đun này đứng đầu trong danh sách . Hơn nữa, cùng với thư viện, bạn sẽ phải bao gồm tệp borlndmm.dll (nó được bao gồm trong bản phân phối Delphi). Rất dễ để tránh tình trạng này: đối với các tham số kiểu văn bản, bạn nên sử dụng kiểu dữ liệu ShortString (đây là một chuỗi thông thường nhưng dài tối đa 255 ký tự) và PChar (một con trỏ tới một chuỗi văn bản).

Gọi dll

Có hai cách để gọi các thủ tục và hàm từ một DLL. Trong trường hợp đầu tiên, chúng tôi biết trước, ở giai đoạn phát triển chương trình, chúng tôi sẽ kết nối với DLL nào (thường là chúng tôi tự tạo DLL này). Trong trường hợp thứ hai, chúng tôi kết nối với một thư viện tùy ý, bao gồm cả thư viện “nước ngoài”.

Liên kết tĩnh

Để thực hiện phương pháp đầu tiên, được gọi là liên kết tĩnh, tạo một ứng dụng thông thường mới, đặt ba trường nhập LabeledEdit1...LabeledEdit3, một nút và thành phần Tlabel trên biểu mẫu. Sau câu lệnh THỰC HIỆN, thêm một dòng nhập hàm GetArea từ thư viện geta.dll:

Hàm GetArea(a,b,c:real):REAL; XA; BÊN NGOÀI "geta";

Từ EXTERNAL chỉ ra rằng phần thân của hàm này nằm trong thư viện với tên được chỉ định và từ FAR chỉ định việc sử dụng các địa chỉ 4 byte “dài”, điều này cần thiết vì chương trình gọi nằm trên một trang của bộ nhớ và thư viện DLL nằm trên một thư viện khác. Tất nhiên, tệp geta.dll phải được đặt trong cùng thư mục chứa tất cả các tệp của ứng dụng hiện tại.

Trong trình xử lý bấm nút, bạn cần tạo một mảng và chuyển nó vào hàm thư viện và hiển thị kết quả trên màn hình:

thủ tục TForm1.Button1Click(Người gửi: TObject);