tran van hai
Trả lời 15 năm trước
Có 2 phương pháp liên kết code của chương trình ứng dụng với code của các hàm thư viện mà nó dùng: đó là phương pháp liên kết tĩnh và phương pháp liên kết động (hay còn gọi là liên kết muộn tại thời điểm chạy).
1. Trong phương pháp liên kết tĩnh, tại thời điểm dịch và liên kết chương trình, code của các hàm thư viện sẽ được trích ra từ file thư viện và được chèn trực tiếp vào file chương trình (y như do người dùng viết chúng vậy). Sau đó các lệnh gọi hàm thư viện trong chương trình ứng dụng sẽ được hiệu chỉnh lại để thể hiện sự gọi trực tiếp các hàm thư viện này (lúc này nằm cục bộ trong file chương trình ứng dụng). Ưu điểm của phương pháp này là tính đơn giản, dễ hiểu, nhưng phương pháp này có khá nhiều khuyết điểm làm nó không được dùng trong các môi trường hiện đại:
• Làm dung lượng chương trình lớn lên 1 cách không cần thiết vì có thể có nhiều hàm thư viện được nhân bản và được để vào file chương trình nhưng trong nhiều lần chạy chương trình, các hàm thư viện không được gọi lần nào (do luồng thi hành chương trình không đi qua đoạn code chứa lời gọi hàm thư viện).
• Lãng phí tài nguyên bộ nhớ ngoài (đĩa) và bộ nhớ trong (RAM) vì đoạn code của hàm thư viện đã bị nhân bản từ file thư viện và được đặt trực tiếp trong n file phần mềm có gọi hàm đó, nếu số lượng n file này là lớn thì sự lãng phí về dung lượng đĩa (để chứa n file chương trình) và dung lượng RAM (để chạy các chương trình này đồng thời) càng lớn.
• Hành vi của chương trình ứng dụng không thay đổi động khi các hàm thư viện mà nó gọi đã được nâng cấp. Thật vậy, khi file thư viện đã được nâng cấp ta phải dịch và liên kết chương trình với file thư viện mới, nếu không, ứng dụng vẫn gọi version cũ của hàm thư viện chứ không phải version hiện hành của file thư viện.
2. Trong phương pháp liên kết động, lệnh gọi hàm thư viện trong file chương trình chưa được liên kết với hàm tại thời điểm dịch. Khi chương trình chạy, mỗi khi cần phải thực thi 1 lệnh gọi hàm thư viện, hệ thống mới dừng lại, xác định file thư viện nào và nếu nó chưa được nạp vào bộ nhớ trước đó thì hệ thống sẽ nạp vào (nhưng thường được để trong không gian bộ nhớ dùng chung để có thể "share" cho nhiều chương trình gọi nó). Sau khi đã nạp file thư viện vào bộ nhớ, hệ thống mới liên kết lời gọi hàm thư viện với hàm đó rồi cho chương trình chạy tiếp. Với cách làm này, sự liên kết giữa lời gọi và hàm thư viện chỉ xảy ra 1 lần, lần đầu tiên lệnh gọi hàm được chạy. Phương pháp liên kết động có rất nhiều ưu điểm nên được sử dụng chủ yếu trong các môi trường hiện nay như Windows, Linux... Trên Windows, việc liên kết lời gọi hàm với hàm thư viện có thể được thực hiện tường minh bởi người lập trình tại vị trí xác định trong chương trình với đoạn code "template" như sau:
//định nghĩa biến pointer đến hàm thư viện
typedef int FAR (*ATMBEGINFCHANGE)(void);
ATMBEGINFCHANGE ATMBeginFontChange, ATMEndFontChange;
//load file thư viện chứa hàm cần gọi vào bộ nhớ dùng chung
hATMLIB = LoadLibrary((LPCSTR)"atmlib.dll") ;
if (hATMLIB == NULL)
MessageBox(NULL,"Load atmlib.dll Error","Error",MB_OK );
//xác định địa chỉ đầu của hàm thư viện cần gọi
ATMBeginFontChange = (ATMBEGINFCHANGE)GetProcAddress(hATMLIB, "ATMBeginFontChange");
if (ATMBeginFontChange == NULL)
MessageBox(NULL,"Khong the tim ham ATMBeginFontChange","Error",MB_OK);
...
//khi cần, gọi hàm thông qua biến pointer
ATMBeginFontChange();
Đoạn code thực hiện liên kết động 1 cách tường minh như trên khá rườm rà, một mặt gây khó khăn cho người viết, mặt khác gây khó khăn cho người đọc lại đoạn code để hiểu nó. Chính vì vậy, khi dịch file thư viện, các tool biên dịch và liên kết trên Windows như Borland C++, VC++, ... luôn tạo ra 2 file thư viện khác nhau:
• file *.dll chứa các hàm thư viện đã được dịch.
• file *.lib chứa các đoạn code thực hiện liên kết động cho từng hàm thư viện (giống như đoạn code trên).
Khi dịch và liên kết chương trình ứng dụng, chương trình liên kết sẽ dịch lời gọi hàm thư viện thành lời gọi hàm tương ứng trong file *.lib và hàm này thực hiện liên kết động với hàm thư viện. Ở đây bạn cần lưu ý là chương trình dịch dùng phương pháp liên kết tĩnh với đoạn code trong file *.lib, chỉ có đoạn code này mới thực hiện liên kết động với hàm trong file *.dll.
Một lưu ý khác là để 1 file source C++ gọi được 1 hàm nằm trong file khác (thường là thư viện), ta phải khai báo trước đặc tả sử dụng hàm cần gọi rồi sau đó mới gọi nó. Lệnh khai báo đặc tả sử dụng 1 hàm sẽ miêu tả tên hàm, danh sách từng tham số, mỗi tham số thuộc kiểu dữ liệu nào, và đặc tả chức năng của hàm đó. Thường các lệnh khai báo đặc tả hàm được để trong file *.h, file source C++ sẽ dùng lệnh "#include" để chèn nội dung file *.h vào file source. Khi dịch, các lệnh khai báo chỉ cung cấp thông tin cho chương trình dịch chứ không được dịch ra mã máy nên không hề tốn bộ nhớ cho chúng.
3. Về nguyên tắc, bạn có thể dùng kết hợp 2 phương pháp liên kết tĩnh và động trong cùng 1 phần mềm. Tuy nhiên thường người ta sẽ chọn dùng 1 phương pháp để đảm bảo tính nhất quán và dễ theo dõi.
4. Chúng tôi đã trả lời vấn đề này trên các số báo trước, xin nhắc lại WinXP có cơ chế bảo vệ tài nguyên rất chặt chẽ, các lệnh trong file ứng dụng không được phép truy xuất trực tiếp các tài nguyên của máy (thí dụ truy xuất các cổng I/O của các card chức năng). Nếu muốn, bạn phải xây dựng 1 device driver theo định dạng của WinXP, device driver này sẽ chứa các đoạn code truy xuất trực tiếp tài nguyên và nó được chạy ở cấp ưu tiên cao nhất như các đoạn code của HĐH. Chi tiết về việc xây dựng device driver được trình bày trong CD DDK (Device Development Kit).
Trong VB.NET tôi có khai báo hàm như sau:
Private Sub ShowImage(ByVal position As Integer, ByVal index As Integer)
Dim a_label As New Label
a_label.Name = "lblCell" & position.ToString
Nếu index = 0 thì xoá hình khỏi a_label
If index = 0 Then
a_label.Image = Nothing
a_label.Refresh()
Else
Select Case position
Case 0 Or 6
a_label.Image = Image.FromFile("D:\My Documents\My Projects\Visual Studio Projects\O_Quan\Images\quan1" & index.ToString & ".jpg")
a_label.Refresh()
Case 1 To 5
a_label.Image = Image.FromFile("D:\My Documents\Mư Projects\Visual Studio Projects\O_Quan\Images\" & index.ToString & "r.jpg")
a_label.Refresh()
Case 7 To 11
a_label.Image = Image.FromFile("D:\My Documents\Mư Projects\Visual Studio Projects\O_Quan\Images\" & index.ToString & "b.jpg")
a_label.Refresh()
End Select
End If
a_label.Refresh()
a_label.Update()
End Sub
Các Label lblCellposition đã có sẵn trên form. Nhưng khi chạy chương trình thì hình ảnh trên lblCellposition không có thay đổi gì cả. Xin hỏi có phải khai báo hàm trên sai ở chỗ gán "a_label.Name" không? Xin chỉ cách khắc phục.
Đúng, hơn nữa, bạn còn mắc phải 1 số lỗi khác nữa:
• lệnh Case 0 Or 6 tương đương với lệnh Case 0 vì kết quả tính biểu thức "0 Or 6" sẽ là 0. Do đó nếu muốn miêu tả 2 trường hợp 0 và 6, bạn nên dùng 2 lệnh "Case 0" và "Case 6" riêng biệt.
• Các đoạn code mà bạn thực hiện để thiết lập ảnh bitmap cho các label thực sự chỉ tác động trên đối tượng a_label mà đối tượng này được tạo động bằng chương trình và chưa liên kết với form giao diện. Lưu ý để truy xuất 1 đối tượng giao diện nào đó, bạn phải dùng biến tham khảo đến nó (thí dụ a_label). Bạn có 12 label với tên lần lượt là lblCell0 đến lblCell12, muốn truy xuất label nào, bạn phải dùng đúng tên biến tương ứng, biến này chính là thuộc tính "Name" mà bạn đã thiết lập tại thời điểm thiết kế trực quan.
Tóm lại để có thể thay đổi được ảnh bitmap của các label, bạn có thể hiệu chỉnh đoạn code của mình thành:
Private Sub ShowImage(ByVal position As Integer, ByVal index As Integer)
Dim filename As String
Xác định file chứa ảnh cần dùng
Select Case position
Case 0
filename = "D:\My Documents\My Projects\Visual Studio Projects\O_Quan\Images\quan1" & index.ToString & ".jpg"
Case 6
filename = "D:\My Documents\My Projects\Visual Studio Projects\O_Quan\Images\quan1" & index.ToString & ".jpg"
Case 1 To 5
filename = "D:\My Documents\Mư Projects\Visual Studio Projects\O_Quan\Images\" & index.ToString & "r.jpg"
Case 7 To 11
filename = "D:\My Documents\Mư Projects\Visual Studio Projects\O_Quan\Images\" & index.ToString & "b.jpg"
End Select
Select Case index
Nếu index = 0 thì xóa ảnh khỏi label
Case 0
Select Case position
Case 0
lblCell0.Image = Nothing
Case 1
lblCell1.Image = Nothing
Case 2
lblCell2.Image = Nothing
...
Case 11
lblCell11.Image = Nothing
End Select
Nếu index = 1 thì thiết lập ảnh cho label tương ứng
Case 1
Select Case position
Case 0
lblCell0.Image = Image.FromFile(filename)
Case 1
lblCell1.Image = Image.FromFile(filename)
Case 2
lblCell2.Image = Image.FromFile(filename)
...
Case 11
lblCell11.Image = Image.FromFile(filename)
End Select
End Select
End Sub
Cho hỏi trong VB.Net, làm sao để "Extract" một icon từ những file *.exe?
Bạn có thể gọi hàm API của Windows tên là ExtractIcon() để đọc icon từ 1 file khả thi nào đó (*.exe, *.dll, *.ocx...). Lưu ý 1 file khả thi có thể chứa nhiều icon khác nhau, mỗi icon được nhận dạng thông qua chỉ số từ 0 đến n. Đoạn code VB .Net sau sẽ đọc icon đầu tiên trong file *.exe (thường đó là icon miêu tả chương trình ứng dụng) rồi hiển thị nó lên ở góc trên trái của form hiện hành :
khai báo hàm API cần dùng
Private Declare Function ExtractIcon Lib "shell32.dll" Alias "ExtractIconA" (ByVal hInst As Integer, ByVal lpszExeFileName As String, ByVal nIconIndex As Integer) As System.IntPtr
khai báo biến quản lý icon
Dim hIcon As System.IntPtr
Dim ic As Icon
đọc icon đầu tiên (chỉ số 0) của file *.exe vào
hIcon = ExtractIcon(VB6.GetHInstance.ToInt32, "c:\windows\prog.exe", 0)
tạo đối tượng Icon tương ứng
ic = Icon.FromHandle(hIcon)
hiển thị icon lên góc trên trái của form hiện hành
Me.CreateGraphics.DrawIcon(ic, 0, 0)
Chúc bạn thành công!