Sử dụng DLL và Windows API
Các hộp thoại thông dụng Dưới đây là một hộp thoại mở tập tin Hộp thoại này không chỉ tiết kiệm thời gian cho chúng ta, mà còn tạo nên một giao diện người sử dụng gần gũi và thống nhất với môi trường Windows ...
Các hộp thoại thông dụng
Dưới đây là một hộp thoại mở tập tin
Hộp thoại này không chỉ tiết kiệm thời gian cho chúng ta, mà còn tạo nên một giao diện người sử dụng gần gũi và thống nhất với môi trường Windows
Hộp thoại này do Windows cung cấp, nằm trong thư viện WindowsSystemcomdlg32.Dll. Tập tin này chứa các đoạn chương trình tạo ra các hộp thoại thông dụng khác nhau. Do tập hợp lại trong một thư mục dùng chung là Windowssystem, nó cho phép mọi ứng dụng Windows có quyền truy cập và thậm chí bản thân Windows cũng vậy.
Các tập tin DLL này được biên dịch vớI C/C++.
![](/pictures/picfullsizes/2018/05/24/dhq1527153541.jpg)
Thư viện liên kết động
Đối với các ngông ngữ lập trình cổ điển như C, khi biên dịch chương trình, ta có một tập tin EXE duy nhất có thể được thi hành mà không cần bất cứ tập tin nào khác (tập tin.vbp của Visual Basic không phảI trường hợp này, vì nó chỉ chạy trong môi trường Visual Basic). Toàn bộ chương trình cần thiết được chứa hết trong một tập tin EXE. Tuy nhiên, cũng có rất nhiều thư viện C được dùng rộng rãi. Vấn đề là làm sao sử dụng lạI các đoạn chương trình viết sẵn trong chương trình của ta. Đó chính là liên kết (link). Có hai loạI liên kết : liên kết tĩnh ( static link) và liên kết động (dynalic link).
Liên kết tĩnh
Cung cấp một kết nối nền vững giữa chương trình và module viết sẵn ngay lúc thiết kế; tương tự ta tạo module trong Visual Basic và gọi thủ tục trong đó, chỉ khác là liên kết tĩnh thì chứa bên ngoài Visual Basic. Tuy nhiên, để sử dụng liên kết tĩnh, ta cần copy phần chương trình viết sẵn của thư viện vào tập tin chương trình khi biên dịch. Từ đó trở đi, chúng trở thành một phần của chương trình và bị khoá chặt với chương trình.
Liên kết động
Là giải pháp linh hoạt hơn liên kết tĩnh. Tập tin thư viện bên ngoài không bị ràng buộc với bên ngoài. Nó chứa ở một nơi sao cho tập tin EXE có thể tìm ra và gửi thông điệp cho nó. Khi thi hành, các thông điệp này là những cuộc gọi đến các hàm/thủ tục, yêu cầu phần chương trình nào đó của DLL được thi hành.
Các DLL của Visual Basic
Có lẽ Visual Basic là một minh hoạ cho việc sử dụng DLL. Vào thư mục Windowssystem, ta sẽ thấy một loạt các tập tin cấu tạo nên cơ chế thi hành của VB.
Ví dụ, VB5DB.DLL chứa chương trình kiên kết với DAO (đối tượngtruycậpdữ liệu) lúc thi hành để ứng dụng có thể tìm kiếm các cơ sở dữ liệu cục bộ.
Khi ta xây dựng một ứng dụng cơ sở dữ liệu và biên dịch nó, tập tin EXE không biết
Gì về cơ sở dữ liệu. Thay vào đó, nó sử dụng một số đoạn chương trình của vb cho phép nạp thư viện vb5db.dll lúc thi hành và gọi các hàm trong đó.
Thế mạnh của DLL
- Nhất quán: người sử dụng ưa chuộng Windows vì nó không ít thì nhiều có một giao diện người sử dụng phổ biến cho mọi ứng dụng. Ví dụ các hộp thoại thông dụng, các menu, thanh công cụ của Office97… Nghĩa là có những đoạn chương trình chung để tạo ra chúng.
- Dễ bảo trì: Những thay đổI hoặc bổ sung nếu có sẽ thể hiện trên mọI ứng dụng.
- Tập tin EXE nhỏ hơn: Do một phần công việc chứa ở nơi khác, và không gắn kết “cứng nhắc” như liên kết tĩnh, kích cỡ tập tin EXE được giảm nhỏ.
Chỉ có điều là DLL còn chứa nhiều phần khác, không chỉ là những gì chương trình của ta cần.
Cấu trúc Windows
DLL là nền tảng của thiết kế Windows. Windows thực chất là tập hợp các DLL để các ứng dụng khác nhau có thể dùng chung. Bên trong các DLL này là hàng trăm hàm/thủ tục. Ta gọi chúng là WindowsAPI.
Giao diện lập trình ứng dụng (Application Programmer’s Interface) là tập hợp các hàm/thủ tục có sẵn của Windows. Chúng gẫn gũi với ngôn ngữ C/C++ hơn.
Visual Basic được thiết kế theo kiểu che bớt các công việc bên dướI hệ thống. Phần lớn các cuộc goi đến các hàm API được lồng trong các dạng lệnh Visual Basic, từ khoá, phương thức và thuộc tính. Chúng sẽ được thông dịch thành WinAPIbên trong của Visual Basic.
Tuy nhiên, vẫn có một số hàm API mà Visual Basic không có phần tương đương.
Ví dụ, Visual Basic chuẩn không có cách nào cho người lập trình điều khiển hệ thống multimedia của Windows, nhưng với winapi, ta có thể đạt được kết quả. Hiểu rõ WINAPI, ta có thể khám phá những năng lực tiềm tàng của chúng.
Lớp bọc API và các điều khiển hiệu chỉnh
Điều khiển hiệu chỉnh (OCX hay activex) bản thân chúng là những lớp bọc API, chúng chuyển giao các chức năng theo kiểu Visual Basic một cách thân thiện.
Điều khiển ActiveXvà OLEAutomationServersđưa chương trình vào các đề án mà không cần phảI có một DLL thực sự.
Ta cũng có thể gọI API trong các module lớp, nghĩa là đưa năng lực API vào đốI tượng Visual Basic.
Tìm kiếm API
Ta có thể tìm các API thông qua tập tin Trợgiúp(Help) của Visual Basic, qua sách tra cứu
Trình duyệt API (TextAPI Viewer)
Được cung cấp sẵn khi cài Visual Basic. Khi ta cần tra cứu cú pháp chính xác của hàm API, ta dùng TextAPIViewer. Tuy nhiên, để có thông tin chi tiết hơn như hàm API làm gì, truyền tham số gì, trả về giá trị gì, ta cần có quyển sách tra cứu.
Ngoài ra, chương trình này còn cho phép copy nội dung API đến clipboardđể dán vào chương trình.
Các DLL của Windows
Các API được tổ chức trong bốn DLL chính của Windows:
a. KERNEL32:
Là DLL chính, đảm nhiệm quản lý bộ nhớ, thưc hiện chức năng đa nhiệm và những hàm ảnh hưởng trực tiếp đến hoạt động của Windows.
b. USER32:
Thư viện quản lý Windows. Thư viện này chứa các hàm xử lý menu, định giờ, truyền tin, tập tin và nhiều phần không được hiển thị khác của Windows.
c. GDI32:
Giao diện thiết bị đồ hoạ (Graphics Device Interface). Thư viện này cung cấp các hàm vẽ trên màn hình, cũng như kiểm tra phần biểu mẫu nào cần vẽ lại.
d. WINNM:
Cung cấp các hàm multimedia để xử lý âm thanh, nhạc, video thời gian thực, lấy mẫu, v.v… Nó là DLL 32 bit. (Thư viện 16 bit tên là MMSYSTEM)
Ta có thể tìm các tập tin này trong thư mục Windowssystem. Ngoài ra, còn có các DLL nhỏ hơn, cũng được dùng phổ biến để cung cấp các dịch vụ đặc biệt cho ứng dụng.
Trên đây là các tên DLL 32 bit. Phiên bản VB4 là bản cuối cùng còn hỗ trợ 16 bit.
Gọi API
Gọi API không khác gì với gọi hàm/ thủ tục trong module của đề án. Ví dụ ta có thủ tục:
Public sub FindText(obiDataControl as Control, _ SFilename as String) ‘ Code to implement function here End sub
Để gọi thủ tục ta dùng :
FindText datTitles, “Titles”
Chỉ có điều API là một thủ tục không chỉ nằm ngoài module mà còn nằm ngoài Visual Basic.
Khai báo một cuộc gọI API:
Trước khi dùng hàm của DLL, ta cần khai báo hàm đó. Visual Basic cần biết:
- Tên hàm / thủ tục.
- Tập tin DLL chứa nó.
- Tham số truyền.
- Kiểu dữ liệu truyền về nếu là hàm
Khai báo API tương tự khai báo hằng/ thủ tục thông thường. Ta vẫn bắt đầu bằng từ khoá Sub/Function, chỉ khác là trước đó phảI có từ khoá Declare.
Ví dụ mẫu - Tạo cửa sổ nhấp nháy bằng cách gọi API
1. Tạo đề án chuẩn mớI
2. Vẽ điều khiển định giờ (timer) trên biểu mẫu và định thuộc tính
Interval là 10. Nó sẽ gây ra một sự kiện timer mỗI 10 mi-li-giây.
Biểu tượng điều khiển Timer trên hộp công cụ
3. Nhấn đúp lên cửa sổ này để mở Cửa sổ Code
Private Sub Timer1_Timer() Dim nReturnValue As Integer nReturnValue = Flash(Form1.hWnd, True) End Sub
4. Khai báo hàm Flash trong GeneralDeclarations:
Private Declare Function Flash Lib "User32"_ Alias "FlashWindow"_ (ByVal hWnd As Long,_ ByVal bInvert As Long) As Long
5. Thi hành chương trình. Khi biểu mẫu xuất hiện, tiêu đề của nó nhấp nháy.
Mặc dù ta thấy chương trình này rất đơn giản, nhưng nếu viết bằng các hàm Visual Basic thông thường, nó rất phức tạp và tốn rất nhiều chương trình.
Từ khoá Declarebáo VB biết đây là khai báo một hàm của DLL.
Sau Declarelà từ khoá Subhay Function, cho biết đây là thủ tục hay hàm. Ta chỉ có một trong hai lựa chọn.
Từ khoá Lib cho biết tên DLL đang chứa hàm/ thủ tục đó. Ở đây là thư viện User32. Từ khoá Alias cho biết tên thực sự của thủ tục / hàm trong thư viện. Nó có thể khác vớI tên ta khai báo trước từ khoá Lib.
Cuối cùng là khai báo các tham sổ truyền, cùng vớI kiểu dữ liệu hàm trả về.
Ở đây tham số được truyền là :
(ByVal hWnd As Long, ByVal bInvert As Long) As Long
Tham số đầu, hWnd,là “handle”, xác định cửa sổ cần nhấp nháy. Tham số thứ hai, bInvertlà giá trị Boolean. Nếu bInvertđược truyền vào có giá trị True, thanh tiêu đề sẽ nhấp nháy. Để trả về trạng thái đầu, ta phảI gọI lạI lần nữa, vớI bInvertmang giá trị False.
Với nhiều hàm API, tên Alias trùng với tên thực. Khi đó Visual Basic sẽ tự động loại bỏ phần Alias. Ví dụ:
Private Declare Function FlashWindow Lib "User32"_ Alias "FlashWindow"_ (ByVal hWnd As Long,_ ByVal bInvert As Long) As Long
Visual Basic sẽ đổi thành:
Private Declare Function FlashWindow Lib "User32"_ (ByVal hWnd As Long,_ ByVal bInvert As Long) As Long
Tuy nhiên một số có tên không hợp lệ đốI vớI Visual Basic, như _lopen, một số khác có nhiều phiên bản, ví dụ có ký tự A và W ở cuối tên. Nói chung, tốt nhất nên dùng tên thực của API. Một số lập trình viên dùng Aliasđể thay thế tên hàm, hoặc thậm chí khai báo hai tên cho hai phiên bản hàm để nhận các tham số truyền khác nhau.
nReturnValue = Flash(Form1.hWnd, True) Sau khi khai báo hàm API, ta có thể gọI API như một hàm hoặc thủ tục Visual Basic thông thường. GọI Flash là gọI đến API trong DLL, và ta lưu giá trị trả về trong biến nReturnValue.
Đối với các hàm thông thường, ta có thể không cần sử dụng giá trị trả về của hàm. Tuy nhiên, ta vẫn cần chứa giá trị trả về vào một biến dù ta không có ý định sử dụng nó. Phần lớn API trả về mã lỗi kiểu số, và ta có thể dùng nó để kiểm tra mọi việc có hoạt động chính xác hay không.
Trong thực tế, bỏ qua giá trị trả về không chỉ là lườI biếng mà còn thực sự nguy hiểm nếu ta đang gọI nhiều API.
Sử dụng API sai có thể dẫn đến treo Windows, nếu không nói là treo máy. Khi làm việc vớI các API phức tạp, như những hàm cần cấp phát nhiều vùng nhớ và tài nguyên hệ thống. Không nên bắt chiếc các lập trình viên cẩu thả bỏ qua các giá trị trả về. Vì hàm DLL nằm ngoài ứng dụng, chúng tự kiểm tra lỗi – ta chỉ biết có sai sót thông qua giá trị trả về.
Handle
Lấy biểu mẫu làm ví dụ. Windows dùng một cấu trúc để lưu giữ thông tin của biểu mẫu. Thông tin này đồng nhất với thông tin chứa trong cửa sổ Properties. Windows chứa cấu trúc của từng cửa sổ trong một danh sách dài gồm các cấu trúc dữ liệu liên quan đến mọi cửa sổ của mọi chương trình đang chạy. Để xác định cấu trúc nào thuộc cửa sổ nào, nó dùng handle. Nó không dùng tên biểu mẫu vì tên cũng là một thuộc tính của biểu mẫu. Handle chính là số ID của một đối tượng trong Windows.
Khi ta bắt đầu dùng API, nhất là những API có xử lý với biểu mẫu, ta sẽ thường xuyên làm việc với handle. Visual Basic chứa handle như một thuộc tính chỉ được đọc, có thể dùng làm tham số truyền cho những hàm của Windows khi cần.
Thuộc tính này gọi là hWnd(handleđếnmộtcửasổ), chỉ có thể truy cập lúc thi hành. Mặc dù nó không mang ý nghĩa trong chương trình, nhưng nó có thể được đọc, và truyền như một tham số đến API. Các API có liên quan hiển thị cửa sổ sẽ cần tham số hWndđể biết chính xác cửa sổ mà nó cần xử lý.
Khai báo tham số truyền
Điểm quan trọng trong khai báo tham số truyền cho API là từ khoá Byval.
Với chương trình thông thường, nếu truyền giá trị cho hàm, Visual Basic biết rằng nó chỉ xử lý với bản sao của tham số.
Function Square(Byval Number as Double) as Double
Một cách khác để truyền tham số là truyền tham chiếu. tham số truyền là biến chứ không phải là bản sao của nó. Do đó nếu hàm thay đổi tham số, các thay đổi này sẽ ảnh hưởng lên biến truyền vào. Nếu không chỉ rõ Byval, VB sẽ tự động xem đó là truyền tham chiếu.
Nếu là hàm hoặc thủ tục do ta viết, nếu có sai sót dothiếu Byval, hậu quả không nghiêm trọng, Windows không bị treo.
Tuy nhiên, với các DLL, tình hình nguy hiểm hơn nhiều. Nếu ta quên Byval, VB tự động truyền một con trỏ đến biến. Nó cho biết địa chỉ của biến trên vùng nhớ. Sau đó hàm này đến địa chỉ đó và lấy giá trị về.
Nếu một hàm của DLL chờ một kết quả trong khoảng từ 0 đến 3, và ta truyền một biến tham chiếu, giá trị thực sự truyền vào có thể là 1002342, là địa chỉ vùng nhớ của biến. Hàm này sẽ xử lý số 1002342 thay vì số thuộc khoảng (0-3), kết quả là hệ thống treo.
Không hề có thông báo lỗi ở đây; ta chỉ biết được API bị lỗi khi hệ thống rối loạn ,và treo cứng. Một trong những kinh nghiệm khi làm việc với API là lưu lại. Vì chúng ,ta đang mạo hiểm ra ngoài vùng an toàn của Visual Basic, khi bị lỗi, hệ thống treo và ta mất hết dữ liệu. Luôn luôn lưu đề án trước khi chạy đoạn chương trình goin API.
Từ menu Tools, chọn Options để mở hộp thoại Options. Chọn tab Environment, đánh dấu vào tuỳ chọn SaveChanges.
Sử dụng lớp với API
Sử dụng riêng lẽ từng hàm API sẽ gây khó khăn cho những người đọc chương trình nếu họ không phải là người lập trình ban đầu, nhất là đối với các ứng dụng lớn.
Giải pháp của Visual Basic 6 là chuyển các API thành các lớp (các điều khiển ActiveX). Từng API có thể xếp vào những nhóm tuỳ thuộc lĩnh vực nó xử lý. Các nhóm này có thể chuyển thành các lớp của Visual Basic. Ví dụ, tạo một lớp có các chức năng về multimedia của các API về lĩnh vực này.
Lớp multimedia
Lớp này chứa một bộ các lệnh multimedia thông dụng. Khi một đối tượng được tạo từ lớp, nó mang những chức năng tương tự một điều khiển – có thể xem hay quy định thuộc tính, các phương thức. Nó che đi các lệnh gọi API.
Các phương thức mà lớp này hỗ trợ:
Phương t hức | Mô tả |
MmOpen | Mở tập tin (video, âm thanh, nhạc, v.v...) chuẩn bị Play |
MmClose | Đóng tập tin đang mở, ngăn cấm hoạt động Play |
MmPause | Dừng Play trên tập tin hiện hành |
MmStop | Dừng hẳn Play |
MmSeek | Tìm một vị trí trong tập tin |
MmPlay | Play tập tin đang mở, phát ra âm thanh trong loa |
Các phương thức này là những hàm riêng rẽ trong lớp MMedia.cls và cho phép sử dụng các API theo nhiều cách.
Sau đây là các thủ tục thuộc tính trong tập tin nguồn:
Thuộc tính | Mô tả |
Filename | Tên của tập tin đang mở |
Length | Chiều dài của tập tin đang mở |
Position | Vị trí hiện hành trong tập tin – ta có thể kết hợp với thuộc tínhLengthđể hiển thị trạng thái Play |
Satus | Một từ cho biết trạng thái tập tin (Play,dừngtạm,dừnghẳn,v.v...) |
Wait | Nếu là True, chương trình sẽ chờ đến khi Playxong mới làmtiếp. Nếu là False, nó thi hành theo kiểu đa nhiệm |
Ví dụ mẫu - Sử dụng lớp Multimedia
1) Mở tập tin TestMM.vbp
2) Điều chỉnh kích cỡ biểu mẫu chính và vẽ một nút lệnh và một điều khiẻn hộp thoại thông dụng:
Thiết kế biểu mẫu
Nếu không thấy điều khiển hộp thoại thông dụng trên hộp công cụ, từ menu Project, chọn Components, và chọn vào hộp đánh dấu “Microsoft Common Dialog Control 6.0”.
3) Để hộp thoại (thông dụng) bật ra khi nhấn vào nút lệnh, ta xử lý sự kiện Click trên nút lệnh bằng cách gõ vào:
Private Sub Command1_Click() With CommonDialog1 .Filter = "WaveAudio (*.wav)|*.wav|Midi (*.mid)|*.mid|Video files (*.avi)|*.avi" .FilterIndex = 0 .ShowOpen End With End Sub
4) Chạy chương trình và nhấn nút lệnh, ta sẽ thấy hộp thoại mở tập tin quen thuộc xuất hiện cho ta chọn tập tin multimedia:
![](/pictures/picfullsizes/2018/05/24/vop1527153541.jpg)
5) Kế tiếp ta chuyển lớp multimedia thành một đối tượng.
Private Sub Command1_Click() Dim Multimedia As New MMedia With CommonDialog1 .Filter = "WaveAudio (*.wav)|*.wav|Midi (*.mid)|*.mid|Video files (*.avi)|*.avi" .FilterIndex = 0 .ShowOpen End With If CommonDialog1.Filename <> "" Then Multimedia.mmOpen CommonDialog1.Filename Multimedia.mmPlay End If End Sub
Thi hành chương trình. Tìm một tập tin multimedia trên đĩa cứng (thường chứa trong thư mục WindowsMedia) và play.
Lưu ý rằng để play các tập tin âm thanh như WAVvà MID, ta cần có cardâm thanh trên máy .
Trong dòng đầu của sự kiện click, ta tạo một đối tượng multimedia dẫn xuất từ lớp MMedia. Đây là bước chuyển từ một lớp sang một đối tượng.
Private Sub Command1_Click() Dim Multimedia As New MMedia
Bốn dòng kế sử dụng đối tượng multimedia để mở tập tin dùng phương thức mmOpenvà Play bằng phương thức mmPlay.
If CommonDialog1.Filename <> "" Then Multimedia.mmOpen CommonDialog1.Filename Multimedia.mmPlay End If
Tạo lớp bao bọc các API làm vấn đề đơn giản hơn. Nếu lớp này được đem thưưng mại hoá, người sử dụng nó sẽ không cần phải hiểu về API, họ chỉ cần biết cách thức hoạt động của lớp mà thôi.
Tìm hiểu lớp Multimedia
Windows có nhiều phân hệ, mỗi phân hệ đảm nhiệm một chức năng nhất định. Một trong những phần này là MCI. MCI là tên gọi tắt của Multimedia Control Interface, cung cấp một giải pháp độc lập với thiết bị để sử dụng các tính năng của Windows thông qua chương trình.
Khi viết chương trình trò chơi trên DOS, ta phải xử lý với nhiều chuẩn card âm thanh và hình ảnh khác nhau. Tính năng độc lập với thiết bị, và các chương trình điều khiển thiết bị cung cấp bởi Windows cho phép ta làm việc với bất kỳ cardâm thanh, hình ảnh nào với cùng chương trình, miễn là chúng được hỗ trợ bởi Windows.
MCI cung cấp lớp đệm giữa lập trình viên và các thiết bị dùng xử lý dữ liệu multimedianhư các card âm thanh, hình ảnh.
MCI sẽ làm việc với các chương trình điều khiển thiết bị của Windows, và cuối cùng là phần cứng multimedia. Lập trình viên, yêu cầu MCI dùng hàm API mciSendString. Lệnh này sau đó được gọi xuống chương trình điều khiển thiết bị, ta không cần quan tâm.
MCI là một đối tượng độc lập. Nó có thể được lập trình và có ngôn ngữ lập trình riêng. Khi ta dùng mciSendString, ta đang lập trình MCI.
Sử dụng mciSendString
Cú pháp của mciSendString:
<ResultCode> = mciSendString(“<Command>”, _ <ReturnString>, <ReturnLength>, <CallbackHadle>)
<ResultCode> là một số long integer, và thay đổi tuỳ theo dòng lệnh.
<Command> đặt trong đấu trích dẫn, phải là một từ dưới dạng chuỗi ký tự và là lệnh gửi đến MCI; như là Play để play một tập tin, Open để mơe tập tin, v.v...
Một số lệnh MCI trả về một chuỗi ký tự. Lệnh Status trả về một chuỗi cho biết tập tin dừng hẳn (Stopped), hay đang chơi (Playing), hay dừng tạm (Pause), v.v...
API cần biết bao nhiêu dữ liệu được chứa trong biến chuỗi, tham số kế tiếp là chiều dài chuỗi. Do đó, nếu ta phát lệnh đến MCI trả về một chuỗi, ta phải truyền một biến chuỗi có chiều dài nhất định và cho biết chiều dài của nó:
Dim sReturnString As String * 255 Dim nReturn As Long nReturn = mciSendString("status waveaudio mode",_ sReturnString, 255, 0)
Thêm * 255 vào khai báo sReturnStringcho biết chiều dài của nó là 255.
Sử dụng hàm Callback trong Visual Basic
Hàm Callbackthực ra chỉ áp dụng cho C/C++, Delphi, hay một số ngôn ngữ biên dịch cấp thấp, không dùng với Visual Basic. Tuy nhiên, VB6 cho phép ta sử dụng hàm Callbackmà không cần thêm các chương trình phụ đặc biệt như trong các phiên bản trước.
Khi ta dùng API, chương trình của ta không thể nào biết được điều gì đang xảy ra khi hàm đang chạy. Ta phải chờ đến khi nó kết thúc, và kiểm tra giá trị trả về. Ý tưởng của hàm Callbacklà một API mà khi chạy, nó có thể gọi đến một hàm hoặc thủ tục của chương trình ta đang viết.
Ta phải tạo một hàm Public ở trong một module chương trình của Visual Basic, với các tham số truyền cần thiết của API. Sau đó, khi gọi API, ta gửi một con trỏ (pointer) - địa chỉ vùng nhớ của hàm Callback. Ta phải dùng toán tử mới AddressOf:
nResult = someAPIFunction(ParamOne, ParamTwo, _ AddressOfMyCallback)
Khi API chạy, nó gọi mmọt hàm trong chương trình của chúng ta và gửi các tham ,số cần thiết. Thường nó được được dùng để cập nhật thanh trạng thái, lấy danh sách font hệ thống, và các công việc khác.
Như đã nói, chúng ta sẽ không nói thêm về các hàm Callback. Các hàm này làm phức tạp hơn cho chương trình và nhiều khi làm treo hệ thống. Tuy nhiên, trợ giúp của Visual Basic sẽ cung cấp một số ví dụ nếu bạn uốn tìm hiểu kỹ hơn.
Mở tập tin Media
Ta gửi tên tập tin cho lệnh Open để mở tập tin. Đây là tên chuẩn như: C:Video.avi.
Open <filename> Type <typestring> Alias <aname> ... ... ‘Issue command to do something to the file ... ... Close <aname>
Sau từ khoá Typelà kiểu tập tin. Kiểu chuẩn của Windows là WaveAudiođối với tâp tin WAV, AVIVideođối với AVI, và Sequencer đối với MID.
Alias dùng để thay thế tên tạp tin mở:
Open c:video.avi Type AVIVideo Alias Peter
Nếu ta gửi dòng lệnh này đến MCI bằng MCISenString, nó yêu cầu MCI mở tập tin C:video.avinhư một tập tin video của Microsoft, và nó sẽ dùng tên Peter để chỉ ra tập tin này.
Mỗi lần mở tập tin, lệnh MCI có thể dùng bí danh để chơi tập tin, dừng hẳn hay tạm dừng, hoặc hiển thị trạng thái, v.v... Ví dụ:
Play Peter Pause Peter Stop Peter
Sau đó, ta cần đóng tập tin bằng cách gửi lệnh Close, theo sau là bí danh của tập
nReturn = mciSendString(“Close Peter”, “”,0,0)
Hiển thị trạng thái và vị trí của tập tin Multimedia
1. Mở đề án TestMM.vbp
2. Chúng ta sẽ thêm một số điều khiển để xem thuộc tính Status và Position của lớp MMedia. Thêm một điều khiển thanh diễn tiến (ProgressBar), một nhãn, một điều khiển định giờ (timer):
Nếu không thấy điều khiển ProgressBar trên hộp công cụ, từ menu Project, chọn Components, chọn vào hộp đánh dấu “Microsoft Windows Common Controls 6.0 ”.
3. Mở cửa sổ Properties của điều khiển Timer, đổi thuộc tính Enabled thành False, và Interval là 500. Xoá Caption của điều khiển nhãn.
4. Nhấn đúp chuột lên nút lệnh để mở sự kiện Click:
![](/pictures/picfullsizes/2018/05/24/mfy1527153541.jpg)
Private Sub Command1_Click() ... ... If CommonDialog1.Filename <> "" Then Multimedia.Wait = False Multimedia.mmOpen CommonDialog1.Filename ProgressBar1.Value = 0 ProgressBar1.Max= Multimedia.Length Timer1.Enabled = True Multimedia.mmPlay End If End Sub
5. Trở về biểu mẫu, nhấn đúp chuột lên điều khiển Timer1 để mở sự kiện Timer.
Private Sub Timer1_Timer() ProgressBar1.Value = Multimedia.Position Label1 = "Status: " & Multimedia.Status If ProgressBar1.Value = ProgressBar1.Max Then Multimedia.mmClose Timer1.Enabled = False End If End Sub
Có một vấn đề nhỏ. Ta đã định nghĩa biến chỉ đến instance của lớp MMedia trong hàm sự kiện command1_Click(). Bây giờ chúng ta lại muốn chỉ đến nó từ trong Timer1_Timer().
GHI CHÚ Bạn sẽ được giải thích khái niêm instance trong chương 13 - Lập trình hướng đối tượng
6. Trong sự kiện Click của nút lệnh, chọn dòng khao báo biến Mutilmedia, nhấn phím Ctrl-X để cắt nó vào Clipboard và xoá nó khỏi sự kiện Command1_Click. Sau đó, chọn vào danh sách (General) trong cửa sổ Code, nhấn phím Ctrl –V để dán nó vào vùng General Declarations. Biến khai báo đặt trong vùng này sẽ là biến toàn cục đối với biểu mẫu này.
7. Thi hành chương trình. Nhấn nút “Load and Play a file”, và chọn một tậptin AVI, ví dụ tập tin video “Welcome to windows 95”.
8. Ta sẽ thấy thanh diễn tiến cho thấy bao nhiêu phần trăm của tập tin đang chơi. Khi video kết thúc ta thấy kết quả hiển thị: Stopped.
Khi ta mới nhấn nút lệnh, chương trình thiết lập các khởi tạo cho thuộc tính trước khi chơi tập tin:
If CommonDialog1.Filename <> "" Then Multimedia.Wait = False Multimedia.mmOpen CommonDialog1.Filename ProgressBar1.Value = 0 ProgressBar1.Max = Multimedia.Length Timer1.Enabled = True Multimedia.mmPlay End If
Đối tượng multimedia có thuôc tính tên là Wait. Thuộc tính này quyết định chương trình có tiếp tục thi hành (đa nhiệm) trong khi chơi tập tin, hay phải dừng và chờ đến đến khi nó hoàn tất. Phương thức mmPlay theo dõi giá trị của biến bWait. Nếu biến này có giá trị True, nó thêm Wait vào lệnh gọi mciSendString:
Public Sub mmPlay() Dim nReturn As Long If bWait Then nReturn = mciSendString("Play " &_ sAlias & " wait", "", 0, 0) Else nReturn = mciSendString("Play " & sAlias,"", 0, 0) End If End Sub
Làm sao biết giá trị bWait ? Nhắc lại rằng ta có thể cung cấp các hàm thuộc tính cho phép đọc hoặc quy định giá trị của biến nội bộ:
Property Get Wait() As Boolean ' Routine to return the value of the object's wait property. Wait = bWait
End Property
Property Let Wait(bWaitValue As Boolean) 'Routinetosetthevalueoftheobject's wait property bWait = bWaitValue
End Property
Bước kế là mở tập tin ta muốn chơi. Ta dùng phương thức mmOpen để mở tập tin.
Trước hết, ta khai báo một cặp biến cục bộ để giữ giá trị tạm thời.
Public Sub mmOpen(ByVal sTheFile As String) Dim nReturn As Long Dim sType As String If sAlias <> "" Then mmClose End If Select Case UCase$(Right$(sTheFile, 3)) Case "WAV" sType = "Waveaudio" Case "AVI" sType = "AviVideo" Case "MID" sType = "Sequencer" Case Else Exit Sub End Select sAlias = Right$(sTheFile, 3) & Minute(Now) IfInStr(sTheFile, " ") Then sTheFile = Chr(34) & sTheFile & Chr(34) End if nReturn =mciSendString("Open " & sTheFile _ & "ALIAS " &sAlias& " TYPE " & sType _ & " wait", "", 0, 0) End Sub
Trước hết, hàm mmOpenkiểm tra biến ở mức module gọi là sAlias.
If sAlias <> “” then mmClose End if
Làm việc với MCI, ta nên dùng bí danh cho từng tập tin mở. Ở đây lớp MMedia thiết lập một tên cho bí danh và chứa bí danh vào biến sAlias. Khi ta tiếp tục mở một tập tin kế tiếp bằng mmOpen, hoặc chỉ ra thuộc tính tên tập tin, chương trình kiểm tra điều này và gọi một thủ tục khác để đóng tập tin thứ nhất. Đóng tập tin khi ta cần giải phóng vùng nhớ và tăng tốc độ chơi tập tin.
Cấu trúc Select Casekiểm tra từng loại tập tin.
Lệnh waitcho phép chương trình tiếp tuch chạy cho đến khi nạp thành công. Nếu không có wait, trên một máy nhanh với đĩa cứng chậm, có thể có vấn đề. Ta có thể cố chơi tập tin trước khi nó được nạp xong, đơn giản bởi vì chương trình chạy nhanh hơn đĩa cứng. Lưu ý rằng nó không giống thuộc tính Waittrước đây để điều khiển chương trình tiếp tục chạy khi tập tin đang chơi chứ không phải đang nạp.
Dùng mciSendStringđể lấy hoặc quy định chiều dài. Thuộc tính Lengthcủa lớp MMediachỉ có tính chất được phép đọc mà thôi, và ta không cung cấp hàm Property Let.
Property Get Length() As Single Dim nReturn As Long, nLength As Integer Dim sLength As String * 255 If sAlias = "" Then Length = 0 Exit Property End If nReturn = mciSendString("Status " & sAlias _ & length",Length, 255, 0) nLength = InStr(sLength, Chr$(0)) Length = Val(Left$(sLength, nLength - 1)) End Property
Trước hết sAliasđược kiểm tra xem tập tin có đang mở hay không ? Nếu chưa mở, giá trị trả về từ thủ tục thuộc tính là 0. Nếu tập tin mở rồi, lệnh Status Length của MCI được dùng.
Ta không lo về cách tính chiều dài tập tin, vì đơn vị đo phù hợp với thanh diễn tiến.
Lệnh Status là lệnh MCI đặc biệt, có thể kết nối với các từ khoá như Length, Position,Modeđể xác định các thông tin về tập tin hiện hành. Nó trả về các thông tin này trong một chuỗi ký tự có chiều dài nhất định được truyền vào mciSendString. Trong ví dụ này chuỗi trả về là sLength và dài 255 ký tự.
Dĩ nhiên, nó không luôn chứa 255 ký tự trả về từ lệnh Status. Dùng hàm InStr để cắt bỏ các ký tự 0 lấp đầy khoảng trống.
Chiều dài chứa trong chuỗi được trích chuỗi và chuyển đổi sang kiểu số trước khi gán cho Length.
Lệnh StatusPositioncó thể được gọi nhiều lần để xác định vị trí hiện hành của tập tin đang chơi:
Property Get Position() As Single Dim nReturn As Integer, nLength As Integer Dim sPosition As String * 255 If sAlias = "" Then Exit Property nReturn = mciSendString("Status " & sAlias _ & " position", sPosition, 255, 0) nLength = InStr(sPosition, Chr$(0)) Position = Val(Left$(sPosition, nLength - 1)) End Property
Thay vì gửi Status Length, ta gửi Status Position.
Lấy trạng thái hiện hà n h
Để lấy chuỗi ký tự trạng thái còn gọi là mode, ta truy vấn thuộc tính Statuscủa lớp. Ta cũng sử dụng hàm thuộc tính Property Get hầu đồng nhất với thuộc tính Position trên đây. Chỉ khác là ta gửi Status Mode thay vì Status Length hay Status Positioncho
mciSendString. Dĩ nhiên, không cần chuyển đổi sang kiểu số:
... nReturn = mciSendString("Status " & sAlias & _ " mode", sStatus, 255, 0) nLength = InStr(sStatus, Chr$(0)) Status = Left$(sStatus, nLength - 1) ...
Trở lại sự kiện Command1_Click. Cho tới giờ, ta đã định nghĩa thuộc tính Wait., mở tập tin, thiết lập thanh diễn tiến. Trước khi chơi tập tin, ta quy định Timer. Sau đó, ,ta chơi tập tin bằng cách gọi phương thức mmPlaycủa đối tượng Multimedia.
If CommonDialog1.Filename <> "" Then Multimedia.Wait = False Multimedia.mmOpen CommonDialog1.Filename ProgressBar1.Value = 0 ProgressBar1.Max = Multimedia.Length Timer1.Enabled = True Multimedia.mmPlay End If
Trước hết, kiểm tra tập tin mở thông qua biến sAlias; sau đó nếu thoả điều kiện, nó thi hành lệnh Play của MCI.
Public Sub mmPlay() Dim nReturn As Long If sAlias = "" Then Exit Sub If bWait Then nReturn=mciSendString("Play"&_ sAlias & " wait", "", 0, 0) Else nReturn = mciSendString("Play " & _ sAlias, "", 0, 0) End If End Sub
Công việc sau cùng là cập nhật thanh trạng thái và nhãn trên biểu mẫu, khi tập tin đang chơi. Trước khichơi tập tin, đặt điều khiển Timervới Interval là 500. Vậy nó sẽ kích hoạt đếm mỗi nửa giây. Khi đó, đoạn chương trình sau được thi hành:
Private Sub Timer1_Timer() ProgressBar1.Value = Multimedia.Position Label1 = "Status: " & Multimedia.Status If ProgressBar1.Value = ProgressBar1.Max Then Multimedia.mmClose Timer1.Enabled = False End If End Sub
Cuối cùng cần phải ngưng lại khi đạt đến cuối tập tin. Có thể thực hiện điều này bằng cách so sánh giá trị hiện hành và giá trị Max của thanh diễn tiến. Khi chúng bằng nhau, tập tin được đóng bằng phương thức mmClose. Sau đó, cấm Timer để ngăn hàm này chạy cho đến khi mở tập tin khác.
Lệnh | Mô tả |
Play | Chơi một tập tin |
Pause | Tạm dừng chơi, sẵn sàng bắt đầu mọi lúc |
Stop | Dừng hẳn - cần chuyển đến một vị trí nào đó để tiếp tục chơi |
Seek | Theo sau là một con số, chuyển đến vị trí trong tập tin |
Status Mode | Trả về một chuỗi ký tự thể hiện trạng thái tập tin(đang choi, đangmở, tạm dừng, dừng hẳn....) |
Status Position | Trả về vị trí tập tin mà playback đã đạt đến |
Status Length | Trả về chiều dài tập tin và hỗ trợ để dưa con số trả về từ StatusPosition vào một ngữ cảnh có ý nghĩa nào đó. |
Close | Đóng tập tin và giải phóng vùng nhớ nó chiếm trước đó |
Ngoài ra MCI còn hỗ trợ một số lệnh khác và một số lệnh đặc biệt cho mỗi định dạng tập tin.