• Thứ Ba, 13/04/2010 15:00 (GMT+7)

    Lập trình: Dùng VB.Net đọc “serial number” đĩa cứng

    Nguyễn Văn Hiệp
    Ở phần “Trả lời thư” trên tạp chí số báo tháng 02/2008, chúng tôi đã giới thiệu đến bạn đọc quy trình xây dựng ứng dụng và mã lệnh (code) được viết bằng VB 6.0, cho phép truy xuất thông tin của các ổ đĩa trên máy. Theo yêu cầu của nhiều bạn đọc, bài viết này giới thiệu cách viết code bằng VB .Net 2005 để truy xuất thông tin của các ổ đĩa.

    Nói chung, chúng ta có thể dùng hàm API Windows có tên là DeviceIoControl() để truy xuất các thông tin vật lý của ổ đĩa (cứng, USB...) như Model number, Serial number, Firmware revision... Các hằng và kiểu dữ liệu phục vụ cho việc truy xuất thông tin vật lý của ổ đĩa được định nghĩa trong bộ DDK (Device development Kit).

    1. Chạy Visual Studio .Net, chọn menu File.New project. Khi cửa sổ "New Project" hiển thị, bạn duyệt tìm mục "Visual Basic" trong cửa sổ "Project types", chọn mục "Windows" con của mục "Visual Basic", chọn icon "Windows Application" rồi chọn button Ok để tạo Project mới theo qui định.

    2. Khi cửa sổ hiển thị Form thiết kế trống ban đầu, bạn hãy thiết kế Form gồm 4 đối tượng: 1 label, 1 textbox, 1 button và 1 ListBox như hình dưới đây. Hãy thiết lập thuộc tính (Name) cho textbox là txtDrive, cho button là btnStart, cho ListBox là lstInfo.

    3. Nhấn đúp chuột vào button "Xem thong tin" để tạo thủ tục xử lý sự kiện click chuột trên nó. Khi cửa sổ code của Form hiển thị, bạn hiệu chỉnh nội dung đơn giản ban đầu thành đoạn code VB .Net dưới đây:

    Option Strict On
    Option Explicit On
    Imports System.Runtime.InteropServices
    Public Class Form1
    'code cho Form1
    'định nghĩa các hằng cần dùng cho hàm CreateFile
    Private Const FILE_SHARE_READ As Short = &H1S
    Private Const FILE_SHARE_WRITE As Short = &H2S
    Private Const GENERIC_READ As Integer = &H80000000
    Private Const GENERIC_WRITE As Integer = &H40000000
    Private Const OPEN_EXISTING As Short = 3
    Private Const CREATE_NEW As Short = 1
    'định nghĩa các hằng, các kiểu cần dùng cho hàm DeviceIOControl
    'các thông tin này được lấy từ bộ DDK
    Private Const DFP_RECEIVE_DRIVE_DATA As Integer = &H7C088
    Private Enum HDINFO

    HD_MODEL_NUMBER
    HD_SERIAL_NUMBER
    HD_FIRMWARE_REVISION

    End Enum

    Private Structure IDEREGS

    Dim bFeaturesReg As Byte
    Dim bSectorCountReg As Byte
    Dim bSectorNumberReg As Byte
    Dim bCylLowReg As Byte
    Dim bCylHighReg As Byte
    Dim bDriveHeadReg As Byte
    Dim bCommandReg As Byte
    Dim bReserved As Byte

    End Structure

    Private Structure SENDCMDINPARAMS

    Dim cBufferSize As Int32
    Dim irDriveRegs As IDEREGS
    Dim bDriveNumber As Byte
    Dim bReserved1 As Byte
    Dim bReserved2 As Byte
    Dim bReserved3 As Byte
    Dim dwReserved1 As Int32
    Dim dwReserved2 As Int32
    Dim dwReserved3 As Int32
    Dim dwReserved4 As Int32

    End Structure

    Private Structure DRIVERSTATUS
    Dim bDriveError As Byte
    Dim bIDEStatus As Byte
    Dim bReserved1 As Byte
    Dim bReserved2 As Byte
    Dim dwReserved1 As Int32
    Dim dwReserved2 As Int32

    End Structure

    'không cần dùng kiểu SENDCMDOUTPARAMS như version VB 6.0
    'khai báo các hàm API cần dùng

    Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer, ByVal lpSecurityAttributes As Integer, ByVal dwCreationDisposition As Integer, ByVal dwFlagsAndAttributes As Integer, ByVal hTemplateFile As Integer) As Integer

    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Integer) As Integer

    Private Declare Function DeviceIoControl Lib "kernel32" (ByVal hDevice As Integer, ByVal dwIoControlCode As Integer, ByVal lpInBuffer As IntPtr, ByVal nInBufferSize As Integer, <Out()> ByVal lpOutBuffer As IntPtr, ByVal nOutBufferSize As Integer, ByRef lpBytesReturned As Integer, ByVal lpOverlapped As Integer) As Integer

    'định nghĩa các biến cần dùng

    Dim bin As New SENDCMDINPARAMS

    'định nghĩa hàm GetHDInfo để đọc thöng tin vật lý của disk

    Private Function GetHDInfo(ByRef Drive As Byte, ByRef hdi As HDINFO) As String

    'định nghĩa các biến cần dùng

    Dim hdh As Integer
    Dim br As Integer
    Dim ix As Integer
    Dim hddfr As Integer
    Dim hddln As Integer
    Dim s As String

    Select Case hdi 'kiểm tra mã chức năng

    Case HDINFO.HD_MODEL_NUMBER
    hddfr = 55 'vị trí đầu của chuỗi ModelNumber
    hddln = 40 'độ dài chuỗi ModelNumber

    Case HDINFO.HD_SERIAL_NUMBER
    hddfr = 21 'vị trí đầu của chuỗi SerialNumber
    hddln = 20 'độ dài chuỗi SerialNumber

    Case HDINFO.HD_FIRMWARE_REVISION
    hddfr = 47 'vị trí đầu của chuỗi FirmwareRevision
    hddln = 8 'độ dài chuỗi FirmwareRevision

    Case Else

    Err.Raise(10001, "Mã chức năng không đúng")

    End Select

    'tạo file nhận dạng ổ đĩa cần đọc thông tin

    hdh = CreateFile("\\.\PhysicalDrive" & Drive, GENERIC_READ + GENERIC_WRITE, FILE_SHARE_READ + FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)

    'kiểm tra việc tạo file

    If hdh = 0 Then
    Err.Raise(10003, , "Gặp lỗi khi gọi CreateFile")
    End If

    'thiết lập các thông số input

    With bin
    .bDriveNumber = Drive
    .cBufferSize = 512
    With .irDriveRegs
    If (Drive And 1) <> 0 Then
    .bDriveHeadReg = &HB0S
    Else
    .bDriveHeadReg = &HA0S
    End If
    .bCommandReg = &HECS
    .bSectorCountReg = 1
    .bSectorNumberReg = 1

    End With
    End With

    'gọi hàm DeviceIoControl để đọc thông tin đĩa

    Dim pbout As IntPtr = Marshal.AllocHGlobal(528)
    Dim pbin As IntPtr = Marshal.AllocHGlobal(Len(bin))
    Marshal.StructureToPtr(bin, pbin, False)
    DeviceIoControl(hdh, DFP_RECEIVE_DRIVE_DATA, pbin, Len(bin), pbout, 528, br, 0)

    'copy kết quả từ bộ nhớ "unmanaged" về bộ nhớ managed

    Dim bBuffer(0 To 512) As Byte
    For ix = 1 To 512
    bBuffer(ix) = Marshal.ReadByte(pbout, ix + 15)
    Next

    'rút trích thông tin cần truy xuất
    s = ""

    For ix = hddfr To hddfr + hddln - 1 Step 2
    If bBuffer(ix + 1) = 0 Then Exit For
    s = s & Chr(bBuffer(ix + 1))

    If bBuffer(ix) = 0 Then Exit For
    s = s & Chr(bBuffer(ix))

    Next ix

    GetHDInfo = Trim(s)

    'đóng handle disk
    CloseHandle(hdh)

    End Function

    'thủ tục xử lý sự kiện click chuột trên button

    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click

    Dim Drive As Byte
    Drive = CByte(txtDrive.Text)
    lstInfo.Items.Clear()
    lstInfo.Items.Add("Current drive: " & Drive)
    lstInfo.Items.Add("")

    'đọc và hiển thị các thông tin của đĩa

    lstInfo.Items.Add("Model number: " & GetHDInfo(Drive, HDINFO.HD_MODEL_NUMBER))
    lstInfo.Items.Add("Serial number: " & GetHDInfo(Drive, HDINFO.HD_SERIAL_NUMBER))
    lstInfo.Items.Add("Firmware Revision: " & GetHDInfo(Drive, HDINFO.HD_FIRMWARE_REVISION))

    End Sub

    End Class

    4. Chọn menu Debug.Start Debuging để dịch và chạy chương trình, nếu bạn nhập đúng đoạn code trên thì chương trình sẽ chạy đúng. Bạn thử nhập chỉ số 0 (nhận dạng ổ đĩa cứng đầu tiên) rồi nhấn button "Xem thong tin", thông tin về đĩa đầu tiên sẽ được hiển thị. Nếu bạn có đĩa cứng khác và/hoặc đĩa USB, bạn hãy nhập chỉ số 1, 2... rồi nhấn button "Xem thong tin" để xem thông tin về ổ đĩa tương ứng.

    Bạn có thể copy Project VB .Net quản lý ứng dụng hiển thị thông tin về ổ đĩa được trình bày ở phần trả lời này (có tên là VBNetDiskInfo) trên website TGVT.

    Nguyễn Văn Hiệp

    ID: A1003_92