Một số câu hỏi, giải đáp thiết thực về lập trình

Thảo luận trong 'Ngôn ngữ lập trình' bắt đầu bởi xuanha, 31/3/09.

  1. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Khi lang thang trên các trang web giải đáp về lập trình, bắt gặp những câu hỏi và gợi ý trả lời mang tính thiết thực đối với nhiều lập trình viên. Tôi tạo một topic riêng về vấn đề này và tổng hợp, post bài lên để mọi thần dân yêu thích môn lập trình vô đây tham khảo.


    Hỏi: Trong Visual C++ 6.0 làm thế nào hiển thị được tiếng Việt trong các menu và dialog tự tạo?

    Đáp: Có 2 trường hợp sử dụng mã tiếng Việt khác nhau:
    1. Nếu bạn dùng mã tiếng Việt nào đó chứ không phải mã Unicode (Vni, BK, ABC, Vietware...) thì việc hiển thị chuỗi tiếng Việt trong các phần tử giao diện như hộp thoại, menu... gồm các bước thao tác điển hình như sau:

    - Cài các font chữ hỗ trợ bảng mã tiếng Việt mà bạn dự định dùng vào Windows, chạy tiện ích Control Panel.Display, chọn tab Appearance, chọn button Advanced, thiết lập font chữ cho các đối tượng giao diện về font chữ hỗ trợ bảng mã của bạn. Lưu ý bạn có thể tự động hóa việc thiết lập sơ đồ font chữ cho các đối tượng giao diện bằng đoạn code trong ứng dụng của bạn. Chúng tôi đã giới thiệu đoạn code này nhiều lần trong các số báo trước.

    - Cài đặt và chạy tiện ích gõ phím tiếng Việt hỗ trợ nhập chuỗi tiếng Việt theo bảng mã mà bạn dự định dùng.

    - Mở lại/tạo mới Project VC++ chứa ứng dụng của bạn (thường thuộc loại "MFC AppWizard (exe)"), thiết kế từng đối tượng giao diện cần dùng cho ứng dụng (menu, toolbar, dialog...). Khi tạo mới đối tượng giao diện nào có thuộc tính font chữ thì hãy thiết lập lại thuộc tính này về font chữ hỗ trợ bảng mã tiếng Việt của bạn. Bạn sẽ thấy trực quan các chuỗi tiếng Việt khi chúng mới được nhập vào tại thời điểm thiết kế trực quan. Khi ứng dụng chạy, chúng cũng sẽ được hiển thị đúng y như lúc thiết kế.

    2. Nếu bạn dùng mã tiếng Việt Unicode (mỗi ký tự chiếm 2 byte ô nhớ) thì việc hiển thị chuỗi tiếng Việt trong các phần tử giao diện như hộp thoại, menu... sẽ khó khăn hơn. Lý do là môi trường thiết kế trực quan VC++ 6.0 chưa hỗ trợ việc nhập/hiển thị chuỗi văn bản tiếng Việt Unicode (mặc dù ngôn ngữ VC++ thì hỗ trợ). Qui trình xây dựng giao diện hiển thị đúng chuỗi tiếng Việt Unicode gồm các bước thao tác điển hình như sau:

    - Cài các font chữ hỗ trợ bảng mã tiếng Việt Unicode vào Windows. Lưu ý rằng Windows đã có sẵn một ít font chữ hỗ trợ được bảng mã tiếng Việt Unicode như Arial, Times New Roman, Tahoma... Ở chế độ mặc định, bản thân Windows XP đã dùng các font chữ hỗ trợ tốt mã Unicode tiếng Việt, tuy nhiên nếu muốn thiết lập lại các font chữ của bạn, hãy chạy tiện ích Control Panel.Display, chọn tab Appearance, chọn button Advanced, thiết lập font chữ cho các đối tượng giao diện về font chữ hỗ trợ mã Unicode vừa cài đặt. Lưu ý bạn có thể tự động hóa việc thiết lập sơ đồ font chữ cho các đối tượng giao diện bằng đoạn code trong ứng dụng của bạn. Chúng tôi đã giới thiệu đoạn code này nhiều lần trong các số báo trước.

    - Cài đặt và chạy tiện ích gõ phím tiếng Việt hỗ trợ nhập chuỗi tiếng Việt Unicode, nhất là bảng mã utf-8.
    - Mở lại/tạo mới Project VC++ chứa ứng dụng của bạn (thường thuộc loại "MFC AppWizard (exe)"), thiết kế từng đối tượng giao diện cần dùng cho ứng dụng (menu, toolbar, dialog...). Khi tạo mới đối tượng giao diện nào có thuộc tính font chữ thì hãy thiết lập lại thuộc tính này về font chữ hỗ trợ mã tiếng Việt Unicode. Bạn sẽ không thể nhập/hiển thị trực quan các chuỗi tiếng Việt trong lúc thiết kế trực quan hay trong khi viết code mã nguồn trong môi trường VC++ 6.0, do đó bạn hãy nhập các chuỗi không dấu.

    - Khai báo trong chương trình nguồn (ở vị trí thích hợp) các biến array chứa các chuỗi utf-8 được dùng trong các đối tượng giao diện. Hãy chuyển trình gõ phím về chế độ tạo mã utf-8 và nhập tuần tự các chuỗi cần dùng. Thí dụ, thanh menubar của ứng dụng và nội dung chi tiết của menu pop-up "File" được thiết kế như sau:

    Nhưng khi chương trình chạy, bạn muốn nó được Việt hóa như sau:
    thì bạn hãy viết đoạn code hiệu chỉnh động các chuỗi caption cho thanh menubar và cho menu pop-up "File" như sau (đoạn code dưới đây được đặt trong hàm CMainFrame::OnCreate() là tốt nhất):
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
    //đoạn code được sinh tự động bởi VC++
    ...
    //đoạn code viết thêm bởi bạn
    //khai báo các biến cần dùng
    wchar_t buf[256];
    int i;
    //khai báo các chuỗi utf-8 cho thanh menubar
    char *strMenuBar[] = {
    "Tệp",
    "Soạn thảo",
    "Xem",
    "Trợ giúp"
    };
    //xác định handle của thanh menubar
    HMENU hMainMenu=::GetMenu(m_hWnd);
    //Việt hóa thanh menubar
    for (i = 0; i < 4; i++) {
    //dịch chuỗi utf-8 thành UCS-2
    ::MultiByteToWideChar(CP_UTF8,0,strMenuBar,-1,buf,256);
    //cập nhật lại menu thứ i trong menubar
    ::ModifyMenu(hMainMenu,i, MF_BYPOSITION | MF_STRING, i, buf);
    }
    //khai báo các chuỗi utf-8 cho menu "File"
    char *strFileMenu[] = {
    "Tạo mới",
    "Mở tệp...",
    "Lưu",
    "Lưu như...",
    "-",
    " Đóng"
    };
    //xác định handle của menu "File"
    HMENU hFileMenu = ::GetSubMenu(hMainMenu,0);
    //Việt hóa menu "File"
    for (i = 0; i < 6; i++) {
    if (strcmp (strFileMenu,"-")) {
    //dịch chuỗi utf-8 thành UCS-2
    ::MultiByteToWideChar(CP_UTF8,0,strFileMenu,-1,buf,256);
    //cập nhật lại option thứ i trong menu
    ::ModifyMenu(hFileMenu,i, MF_BYPOSITION | MF_STRING, i, buf);
    }
    }
    //Việt hóa các menu & các đối tượng khác nếu cần
    ...
    return 0;
    }
    - Chọn menu Project.Settings để mở cửa sổ "Project Settings", chọn tab "C/C++", nhập thêm chuỗi ",_UNICODE" sau nội dung có sẵn trong textbox "Preprocessor definitions" để dịch chương trình chạy ở chế độ Unicode. Chọn tab Link, chọn Output trong mục Category, nhập chuỗi "wWinMainCRTStartup" vào textbox "Entry-point symbol:".

    - Dịch ứng dụng và chạy thử ứng dụng. Khi ứng dụng chạy, các chuỗi tiếng Việt Unicode trong menubar và trong menu "File" sẽ được hiển thị đúng y như mong muốn.

    Trích từ PCWorld
    1
  2. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Kiểm tra một đoạn Text nhập vào có phải là địa chỉ Email hay không


    Hỏi: Xin hướng dẫn cách kiểm tra một đoạn Text nhập vào có phải là địa chỉ Email hay không, sử dụng VB.Net.

    Đáp: Việc kiểm tra 1 chuỗi có phải là địa chỉ e-mail hợp lệ có 2 cấp độ:
    1. Kiểm tra xem chuỗi có miêu tả 1 địa chỉ e-mail đúng cú pháp không. Đây là hoạt động xử lý chuỗi. Theo cú pháp, địa chỉ e-mail sẽ có dạng:

    <account>@<server quản lý>
    trong đó <account> là tên nhận dạng account e-mail trong cục bộ server e-mail tương ứng, <account> là 1 chuỗi ký tự chữ số (a-z hay 0-9). Còn <server quản lý> là tên nhận dạng server e-mail quản lý account. Có 2 dạng miêu tả <server quản lý>: dạng địa chỉ DNS như hotmail.com, yahoo.com... và dạng địa chỉ IP như 203.47.8.56.
    Dựa vào cú pháp miêu tả account e-mail, bạn có thể duyệt từng ký tự trong chuỗi nhập vào để xem nó có thỏa cú pháp không.

    2. Kiểm tra xem địa chỉ e-mail có thật không, nghĩa là kiểm tra xem có tồn tại server e-mail theo miêu tả trong địa chỉ không, nếu tồn tại thì kiểm tra tiếp xem server đó có quản lý account được miêu tả trong địa chỉ e-mail không. Việc kiểm tra ở cấp độ 2 này được thực hiện bằng cách lập trình mạng, nối kết thử với máy server mà địa chỉ e-mail qui định; nếu nối kết thành công thì gửi 1 thông báo request đến server hỏi xem account có thật không; nếu account có thật thì Ok, nếu không thì địa chỉ e-mail cần kiểm tra là địa chỉ "ma", mặc dù đúng cú pháp nhưng không tồn tại thực tế.

    Sau đây là đoạn code VB .Net mà chúng tôi viết để demo việc kiểm tra địa chỉ e-mail theo yêu cầu của bạn. Lưu ý chúng tôi chỉ kiểm tra theo dạng địa chỉ DNS và chưa thực hiện kiểm tra cấp độ 2.

    'hàm kiểm tra địa chỉ e-mail
    Private Function IsDNSMailAddr(ByVal addr As String) As Boolean
    'khai báo một số biến cần dùng
    Dim len As Integer
    Dim i As Integer
    Dim addarr() As Char
    Dim ch As Char
    'giả định kết quả sai
    IsDNSMailAddr = False
    'tìm độ dài chuỗi
    len = addr.Length
    'đổi chuỗi sang mảng các ký tự
    addarr = addr.ToCharArray()
    i = 0
    On Error GoTo test1
    'duyệt tìm thành phần <account>
    While (i < len) And (addarr(i) <> "@"c)
    ch = addarr(i)
    If (("0"c <= ch) And (ch <= "9"c)) Or (("a"c <= ch) And (ch <= "z"c)) Then
    i = i + 1
    Else
    'đã tìm được 1 ký tự không hợp cú pháp
    Exit Function
    End If
    End While
    test1:
    i = i + 1
    'nếu hết chuỗi => không hợp cú pháp
    If i = len Then Exit Function
    On Error GoTo test2
    'duyệt tìm thành phần đầu tiên của <server>
    While (i < len) And (addarr(i) <> "."c)
    ch = addarr(i)
    If (("0"c <= ch) And (ch <= "9"c)) Or (("a"c <= ch) And (ch <= "z"c)) Then
    i = i + 1
    Else
    'đã tìm được 1 ký tự không hợp cú pháp
    Exit Function
    End If
    End While
    test2:
    i = i + 1
    'nếu hết chuỗi => không hợp cú pháp
    If i = len Then Exit Function
    On Error GoTo test3
    'duyệt tìm các thành phần còn lại của <server>
    While (i < len)
    ch = addarr(i)
    If (("0"c <= ch) And (ch <= "9"c)) Or (("a"c <= ch) And (ch <= "z"c) Or ch = "."c) Then
    i = i + 1
    Else
    Exit Function
    End If
    End While
    test3:
    'nếu chạy tới đây => hợp cú pháp
    'nếu cần hãy viết code kiểm tra xem địa chỉ có tồn tại ?
    IsMailAddr = True
    End Function
    2
  3. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Hướng dẫn viết ứng dụng đọc thông tin của ổ cứng bằng VB 6.0.

    Đáp:

    Bạn 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 ổ cứng như Model number, Serial number, Firmware revision... Các hằng và các kiểu dữ liệu phục vụ cho việc truy xuất thông tin vật lý của ổ cứng được định nghĩa trong bộ DDK (Device Development Kit). Sau đây chúng tôi giới thiệu qui trình điển hình để viết 1 ứng dụng VB 6.0 đơn giản, ứng dụng này cho phép bạn chọn ổ cứng và xem các thông tin vật lý liên quan đến đĩa cứng.

    1. Chạy VB 6.0, tạo Project dạng "Standard EXE".
    2. Khi Form của ứng dụng hiển thị, hãy thiết kế Form có dạng sau:
    Trong đó TextBox có tên là txtDrive, button có tên là btnStart, ListBox có tên là lstInfo.
    3. Ấn kép chuột vào button "Xem thông tin" để tạo thủ tục xử lý sự kiện click chuột cho button này. Khi cửa sổ soạn code của Form hiển thị, bạn hãy nhập đoạn lệnh VB 6.0 sau đây vào:

    'code cho Form1
    Option Explicit
    'định nghĩa các hằng cần dùng cho hàm CreateFile
    Private Const FILE_SHARE_READ = &H1
    Private Const FILE_SHARE_WRITE = &H2
    Private Const GENERIC_READ = &H80000000
    Private Const GENERIC_WRITE = &H40000000
    Private Const OPEN_EXISTING = 3
    Private Const CREATE_NEW = 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 = &H7C088
    Private Enum HDINFO
    HD_MODEL_NUMBER
    HD_SERIAL_NUMBER
    HD_FIRMWARE_REVISION
    End Enum
    Private Type IDEREGS
    bFeaturesReg As Byte
    bSectorCountReg As Byte
    bSectorNumberReg As Byte
    bCylLowReg As Byte
    bCylHighReg As Byte
    bDriveHeadReg As Byte
    bCommandReg As Byte
    bReserved As Byte
    End Type
    Private Type SENDCMDINPARAMS
    cBufferSize As Long
    irDriveRegs As IDEREGS
    bDriveNumber As Byte
    bReserved(1 To 3) As Byte
    dwReserved(1 To 4) As Long
    End Type
    Private Type DRIVERSTATUS
    bDriveError As Byte
    bIDEStatus As Byte
    bReserved(1 To 2) As Byte
    dwReserved(1 To 2) As Long
    End Type
    Private Type SENDCMDOUTPARAMS
    cBufferSize As Long
    DStatus As DRIVERSTATUS
    bBuffer(1 To 512) As Byte
    End Type
    'Định nghĩa các hàm API cần dùng
    Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    Private Declare Function DeviceIoControl Lib "kernel32" (ByVal hDevice As Long, ByVal dwIoControlCode As Long, lpInBuffer As Any, ByVal nInBufferSize As Long, lpOutBuffer As Any, ByVal nOutBufferSize As Long, lpBytesReturned As Long, ByVal lpOverlapped As Long) As Long
    'Định nghĩa hàm GetHDInfo để đọc thông tin vật lý disk
    Private Function GetHDInfo(Drive As Integer, hdi As HDINFO) As String
    Dim bin As SENDCMDINPARAMS
    Dim bout As SENDCMDOUTPARAMS
    Dim hdh As Long
    Dim br As Long
    Dim ix As Long
    Dim hddfr As Long
    Dim hddln As Long
    Dim s As String
    Select Case hdi 'Kiểm tra chức năng
    Case HD_MODEL_NUMBER
    hddfr = 55 'thiết lập vị trí buffer chứa ModelNumber
    hddln = 40 'thiết lập độ dài buffer chứa ModelNumber
    Case HD_SERIAL_NUMBER
    hddfr = 21 'thiết lập vị trí buffer chứa SerialNumber
    hddln = 20 'thiết lập độ dài buffer chứa SerialNumber
    Case HD_FIRMWARE_REVISION
    hddfr = 47 'thiết lập vị trí buffer chứa FirmwareRevision
    hddln = 8 'thiết lập độ dài buffer chứa FirmwareRevision
    Case Else
    Err.Raise 10001, "Illegal HD Data type" 'Báo lỗi
    End Select
    'tạo file nhận dạng ổ cứng 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, , "Error on CreateFile"
    End If
    'thiết lập các thông số cần truyền
    With bin
    .bDriveNumber = Drive
    .cBufferSize = 512
    With .irDriveRegs
    If (Drive And 1) Then
    .bDriveHeadReg = &HB0
    Else
    .bDriveHeadReg = &HA0
    End If
    .bCommandReg = &HEC
    .bSectorCountReg = 1
    .bSectorNumberReg = 1
    End With
    End With
    'gọi hàm DeviceIoControl để đọc thông tin
    DeviceIoControl hdh, DFP_RECEIVE_DRIVE_DATA, bin, Len(bin), bout, Len(bout), br, 0
    'copy thông tin cần truy xuất
    s = ""
    For ix = hddfr To hddfr + hddln - 1 Step 2
    If bout.bBuffer(ix + 1) = 0 Then Exit For
    s = s & Chr(bout.bBuffer(ix + 1))
    If bout.bBuffer(ix) = 0 Then Exit For
    s = s & Chr(bout.bBuffer(ix))
    Next ix
    GetHDInfo = Trim(s)
    'Đóng handle disk
    CloseHandle hdh
    End Function
    'thủ tục xử lý click chuột trên button Start
    Private Sub btnStart_Click()
    Dim Drive As Integer
    Drive = Val(txtDrive)
    lstInfo.Clear
    lstInfo.AddItem "Current drive: " & Drive
    lstInfo.AddItem ""
    lstInfo.AddItem "Model number: " & GetHDInfo(Drive, HD_MODEL_NUMBER)
    lstInfo.AddItem "Serial number: " & GetHDInfo(Drive, HD_SERIAL_NUMBER)
    lstInfo.AddItem "Firmware Revision: " & GetHDInfo(Drive, HD_FIRMWARE_REVISION)
    End Sub

    4. Chọn menu Run.Start để chạy thử ứng dụng, nhập 0 vào TextBox (tương ứng với đĩa cứng đầu tiên của máy), chọn button "Xem thông tin" và xem kết quả trong ListBox.
    3
  4. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Đánh số thứ tự tự động trong Access



    Hỏi: Tôi có 3 bảng dữ liệu Access sau:
    - HSDT bao gồm các trường: Mã HSDT, ten KH, dia chi ...
    - CVDT bao gồm các trường: Mã HSDT, Mã CV , ngày lập,...
    - SLDT bao gồm các trường: Mã CV, ten sp, so luong, don gia ...
    Tôi muốn đánh số tự động cho Mã CV ví dụ như :
    Mã HSDT là 001.07/HSDT thì Mã CV sẽ là 001.07-01, 001.07-02, ...
    Mã HSDT là 002.07/HSDT thì Mã CV sẽ là 002.07-01, 002.07-02, ...

    Đáp: Yêu cầu đánh số tự động cho mã CV đã rõ ràng, vậy bạn hãy phân tích yêu cầu và viết code thực hiện yêu cầu đó. Thí dụ bạn có thể dùng 1 array chứa N record, mỗi record lưu cặp dữ liệu (chuỗi nhận dạng mã HSDT, chỉ số mã CV đã dùng lần cuối tương ứng). Mỗi lần cần tạo mã CV mới cho 1 record CVDT, bạn duyệt tìm trong array record quản lý mã HSDT tương ứng, rồi lấy chỉ số mã CV đã dùng lần cuối tương ứng, tăng 1 đơn vị rồi dùng giá trị này cho record CVDT mới của bạn.

    Cụ thể đoạn code VBA sau cho phép tạo mã CV tự động theo mã HSDT:

    Option Compare Database
    ‘định nghĩa kiểu cần dùng
    Private Type HSDTRec
    HSDT As String
    CV As Integer
    End Type

    ‘định nghĩa biến cần dùng
    Dim idx As Integer
    Dim lstHSDT(0 To 200) As HSDTRec

    ‘khởi động trị ban đầu cho các biến
    Private Sub Form_Load()
    idx = -1
    End Sub

    ‘hàm tạo tự động mã CV
    Private Function TaoMaCV(HSDT As String)
    Dim maHSDT As String
    ‘lấy chuỗi bên trái dấu /
    maHSDT = Left(HSDT, InStr(1, HSDT, "/", vbBinaryCompare) - 1)
    ‘tìm xem có trong danh sách quản lý chưa
    For i = 0 To idx
    If maHSDT = lstHSDT(i).HSDT Then
    ‘nếu có rồi thì lấy chỉ số đang quản lý+1
    lstHSDT(i).CV = lstHSDT(i).CV + 1
    TaoMaCV = maHSDT & "-" & lstHSDT(i).CV
    Exit Function
    End If
    Next i
    ‘nếu chưa có, tạo mới 1 record quản lý với chỉ số bắt đầu = 1
    idx = idx + 1
    lstHSDT(idx).HSDT = maHSDT
    lstHSDT(idx).CV = 1
    TaoMaCV = maHSDT & "-" & lstHSDT(idx).CV
    End Function

    Theo PCWorld
    4
  5. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Xin hướng dẫn viết chương trình VB6 lấy dữ liệu trên bảng tính Excel (VD: họ tên, ngày sinh), sau đó hiển thị lên MSFlexGird.

    Đáp: Yêu cầu của bạn gồm 2 vấn đề chính:
    - Truy xuất nội dung từng cell trong bảng tính Excel. Hai phương pháp đơn giản, tin cậy nhất để truy xuất nội dung bảng tính Excel là: hoặc dùng các đối tượng "Excel Automation Server", hoặc dùng các đối tượng ADO để truy xuất file Excel như là cơ sở dữ liệu.

    - Xử lý đối tượng MSFlexGrid thông qua các thuộc tính, các tác vụ của nó.
    Sau đây là qui trình điển hình để xây dựng 1 ứng dụng VB 6.0 đơn giản, demo việc hiển thị vùng dữ liệu nằm trong các cell từ A1:J20 trên worksheet có tên là "Sheet1" nằm trên file Excel "c:\userData.xls" lên đối tượng MSFlexGrid của VB 6.0:

    1. Chạy VB 6.0, tạo Project mới thuộc loại "Standard EXE" (loại Project mặc định có 1 Form giao diện rỗng ban đầu).

    2. Chọn menu Project.References để hiển thị cửa sổ References. Duyệt tìm và chọn mục "Microsoft Excel x.y Object Library" để "add" các đối tượng truy xuất file Excel vào Project. Lưu ý x.y là chỉ số version của thư viện các đối tượng Excel được cài trên máy bạn.

    3. Chọn menu Project.Components để hiển thị cửa sổ Components. Duyệt tìm và đánh dấu chọn mục "Microsoft FlexGrid Controls 6.0" để "add" đối tượng MSFlexGrid vào Project.

    4. Vẽ 1 button trên Form có tên mặc định là Command1.

    5. Vẽ 1 đối tượng MSFlexGrid lên Form, thiết lập giá trị các thuộc tính sau của nó : (Name) = MyFlexGrid, AllowUserRisizing = 3 - flexRisizeBoth,

    6. Nhấn đúp chuột vào button vừa tạo để tạo thủ tục xử lý sự kiện click chuột trên button rồi viết đoạn code thực hiện việc hiển thị dữ liệu từ file Excel lên đối tượng MSFlexGrid như sau:
    Option Explicit
    'thủ tục xử lý sự kiện click chuột trên button
    Private Sub Command1_Click()
    'khai báo các biến cần dùng
    Dim oXL As Excel.Application
    Dim oWB As Excel.Workbooks
    Dim oSheet As Excel.Worksheet
    Dim rgn As Range
    Dim i As Integer, j As Integer
    'khởi động Excel và nhận đối tượng Application.
    Set oXL = CreateObject("Excel.Application")
    'xác định đối tượng quản lý file Excel.
    Set oWB = oXL.Workbooks
    'mở file "c:\userData.xls" chứa dữ liệu cần hiển thị
    oWB.Add "c:\userData.xls"
    'thiết lập biến miêu tả worksheet cần truy xuất
    Set oSheet = oWB.Item(1).Worksheets("Sheet1")
    'hiển thị nội dung trong vùng A1:J20 lên MSFlexGrid
    Set rgn = oSheet.Range("A1:J20")
    'thiết lập số hàng cho FlexGrid: 1 hàng tiêu đề + 20 hàng data
    MyFlexGrid.Rows = 21
    'thiết lập số cột cho FlexGrid: 1 cột tiêu đề + 10 cột data
    MyFlexGrid.Cols = 11
    'tạo tiêu đề các cột
    MyFlexGrid.Row = 0
    For i = 1 To 10
    MyFlexGrid.Col = i
    MyFlexGrid.Text = Chr(64 + i)
    'chỉnh giữa nội dung tiêu đề
    MyFlexGrid.CellAlignment = 4
    Next i
    'tạo tiêu đề các hàng
    MyFlexGrid.Col = 0
    For i = 1 To 20
    MyFlexGrid.Row = i
    MyFlexGrid.Text = i
    'chỉnh giữa nội dung tiêu đề
    MyFlexGrid.CellAlignment = 4
    Next i
    'lặp đọc từng cell dữ liệu và hiển thị
    For i = 1 To 20
    For j = 1 To 10
    MyFlexGrid.Row = i
    MyFlexGrid.Col = j
    MyFlexGrid.Text = rgn.Cells(i, j)
    Next j
    Next i
    'đóng đối tượng workbook (file xls)
    oWB.Item(1).Close
    'đóng ứng dụng Excel
    oXL.Quit
    End Sub
    5
  6. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Cho biết hàm API dùng trong VB6 để đổi tên file của hệ thống Windows.

    Đáp: Bạn có thể dùng hàm API có tên là MoveFile (hay MoveFileEx) để đổi tên file hay di dời file từ nơi này sang nơi khác. Bạn cũng có thể dùng trực tiếp lệnh "Name" của VB 6.0 để đổi tên file. Đoạn code VB 6.0 sau đây demo việc đổi tên file c:\OldFile.txt thành file c:\NewFile.txt:

    'khai báo hàm API cần dùng
    Private Declare Function MoveFile Lib "kernel32" Alias "MoveFileA" (ByVal lpExistingFileName As String, ByVal lpNewFileName As String) As Long
    Private Sub Command1_Click()
    MoveFile "c:\OldFile.txt", "c:\NewFile.txt"
    'hoặc dùng thằng lệnh Name như sau
    'Name "c:\OldFile.txt" As "c:\NewFile.txt"
    End Sub
    6
  7. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Tách họ và tên trong Excel


    Để tách được họ và tên trong Excel, bạn có thể sử dụng các hàm có sẵn trong Microsoft Excel để thực hiện yêu cầu. Ví dụ dưới đây là 2 hàm tách tên, họ+chữ lót.

    Function ExtractFirstName(strFullName As String) As String
    Dim strTemp As String
    Dim i As Integer
    Dim l As Integer

    strTemp = strFullName
    l = Len(strFullName)

    If l > 0 Then
    i = 0
    If InStr(1, strFullName, " ", vbTextCompare) Then
    Do
    i = i + 1
    Loop Until Mid(strFullName, l - i, 1) = " "
    strTemp = Right(strFullName, i)
    Else
    End If
    Else
    strTemp = ""
    End If

    ExtractFirstName = strTemp

    End Function

    Function ExtractLastMiddleName(strFullName As String) As String
    Dim strTemp As String
    Dim i As Integer
    Dim l As Integer

    strTemp = strFullName
    l = Len(strFullName)
    i = 0

    If InStr(1, strFullName, " ", vbTextCompare) Then
    Do
    i = i + 1
    Loop Until Mid(strFullName, l - i, 1) = " "
    strTemp = Left(strFullName, l - i)
    Else
    strTemp = ""
    End If

    ExtractLastMiddleName = strTemp

    End Function
    7
  8. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi:Xin hướng dẫn lập trình VB đọc dữ liệu của Microsoft Word và ghi thêm dữ liệu vào cuối tệp *.doc? hoặc lập trình nối 2 tệp *.doc lại với nhau thành 1 tệp.

    Đáp: Cách dễ dàng và tin cậy nhất để lập trình truy xuất nội dung các file Word là dùng các đối tượng của "Word Automation Server" như Word, Application, Document, Selection,...

    Thí dụ sau đây là qui trình xây dựng 1 ứng dụng VB 6.0 đơn giản demo việc thêm chuỗi văn bản vào đầu 1 file *.doc cũng như copy dữ liệu nằm trong file Word này sang file Word khác:

    1. Chạy VB 6.0, tạo Project mới thuộc loại "Standard EXE" (loại Project mặc định có 1 Form giao diện rỗng ban đầu).
    2. Chọn menu Project.References để hiển thị cửa sổ References. Duyệt tìm và chọn mục Microsoft Word x.y Object Library để "add" các đối tượng truy xuất file Word vào Project. Lưu ý x.y là chỉ số version của thư viện các đối tượng Word được cài trên máy bạn.
    3. Tạo 1 button trên Form có tên mặc định là Command1.
    4. Nhấn đúp chuột vào button vừa tạo để tạo thủ tục xử lý sự kiện click chuột trên button rồi viết đoạn code thực hiện copy dữ liệu từ file Word này sang file khác như sau:
    Option Explicit
    'thủ tục xử lý sự kiện click chuột trên button
    Private Sub Command1_Click()
    'khai báo các biến cần dùng
    Dim oWD As Word.Application
    Dim oWB As Word.Documents
    Dim oDoc1 As Word.Document
    Dim oDoc2 As Word.Document
    'khởi động Word và nhận đối tượng Application.
    Set oWD = CreateObject("Word.Application")
    'xác định đối tượng quản lý các file Word.
    Set oWB = oWD.Documents
    'mở file "c:\data1.xls" chứa kết quả
    oWB.Add "c:\data1.doc"
    'mở file "c:\data2.xls" chứa dữ liệu cần copy
    oWB.Add "c:\data2.doc"
    'thiết lập biến các Document cần truy xuất
    Set oDoc1 = oWB.Item(1)
    Set oDoc2 = oWB.Item(2)
    'copy nội dung từ file data2.doc vào clipboard
    oDoc2.Activate
    oWD.Selection.WholeStory
    oWD.Selection.Copy
    'chuyển sang file data1.doc
    oDoc1.Activate
    'mặc định cursor đang ở đầu file
    'thêm chuỗi văn bản vào vị trí cursor hiện hành
    oWD.Selection.TypeText Text:="Chuỗi cần thêm vào" & vbCrLf
    'dán clipboard vào vị trí cursor hiện hành
    oWD.Selection.Paste
    'cất kết quả lên file mới
    oDoc1.SaveAs "c:\data3.doc"
    'đóng các đối tượng lại
    oDoc1.Close
    oDoc2.Close
    'đóng ứng dụng Word
    oWD.Quit
    End Sub
    8
  9. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi:

    Viết code VB 6.0 để tìm các từ trong một văn bản hoặc trong một đoạn văn?

    Đáp: Nội dung tổng thể của 1 văn bản hay của 1 đoạn văn nào đó đều có thể được miêu tả bởi 1 biến kiểu String trong VB 6.0 (biến String có thể chứa tối đa 2G ký tự). Có rất nhiều giải thuật tìm từ khác nhau trong 1 đoạn văn, mỗi giải thuật có những ưu/khuyết điểm riêng và có thể áp dụng trong tình huống thích hợp. Tùy giải thuật được dùng mà đoạn code miêu tả giải thuật đó sẽ có (vì đoạn code chính là giải thuật được miêu tả bởi các lệnh của ngôn ngữ lập trình). Sau đây là đoạn code VB 6.0 demo cho 1 thuật giải tìm từ sử dụng hàm InStr() sẵn có của VB để xác định vị trí của từng từ cần tìm trong đoạn văn:
    Private Sub Command1_Click()
    'khai báo các biến cần dùng
    Dim src As String
    Dim sobj As String
    Dim sotu As Integer
    Dim pos As Integer
    'thiết lập thử giá trị của các chuỗi
    src = "Truong ta Ta hoc, nha ta Ta o"
    sobj = "ta"
    'khởi động trị ban đầu các biến
    sotu = 0
    pos = 1
    Do 'Lặp
    'bắt đầu từ vị trí pos của src, thử tìm vị trí kế của sobj
    pos = InStr(pos, src, sobj, vbTextCompare)
    'nếu không có, dừng vòng lặp
    If pos = 0 Then Exit Do
    'nếu có, tăng counter đếm
    sotu = sotu + 1
    'tăng vị trí tìm về sau từ vừa tìm được
    pos = pos + Len(sobj)
    Loop Until pos = 0
    'hiển thị kết quả tìm kiếm
    MsgBox "Số từ cần tìm là : " & sotu
    End Sub
    9
  10. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Máy có 2 card âm thanh, tôi muốn viết chương trình VB6 chơi nhạc mp3, mp4... phát bài số 1 ở card thứ nhất, bài số 2 ở card thứ 2. Xin hướng dẫn.


    Đáp: Cách tổng quát và dễ dàng nhất để chơi nhiều file multimedia thuộc nhiều định dạng khác nhau ra những card âm thanh khác nhau theo yêu cầu là lập trình dùng các đối tượng COM thuộc thành phần DirectShow trong bộ DirectX của Microsoft. Ý tưởng cơ bản của DirectShow trong việc play/record 1 nguồn multimedia là xây dựng 1 đồ thị (graph) gồm nhiều phần tử độc lập, kết nối chúng theo thứ tự thích hợp, mỗi phần tử xử lý 1 công việc trong nhiều công việc. Thí dụ 1 graph đơn giản nhất gồm 2 phần tử: phần tử đầu sẽ đọc file multimedia cần chơi, phân tích, giải mã thông tin nguồn ra dạng thô, còn phần tử thứ 2 sẽ nhận thông tin dạng thô rồi phát nó ra sound card. Như vậy để chơi 2 file mp3 và mp4 (hay 1 định dạng khác), bạn chỉ cần tạo 2 graph khác nhau, mỗi graph có 2 phần tử, phần tử thứ 2 trong mỗi graph chính là sound card nào đó mà bạn muốn. Để lập trình DirectShow và dễ dàng điều khiển chi tiết các bước chức năng, bạn nên dùng ngôn ngữ VC++ thay vì VB6.0. Để bạn có thể thấy rõ chi tiết lập trình dùng DirectShow để chơi 2 file âm thanh khác nhau ra 2 sound card khác nhau, chúng tôi đã lập trình 1 ứng dụng nhỏ viết bằng VC++, qui trình gồm các bước thao tác sau:

    1. Nếu bạn dùng Windows XP hay cũ hơn, một trong nhiều cách để có được thư viện lập trình DirectShow là download gói "Windows Server 2003 SP1 SDK Release" và cài vào máy.
    2. Chạy VC++, chọn menu File.New để hiển thị cửa sổ New. Chọn button Projects, chọn loại project MFC AppWizard(Exe), chọn vị trí chứa project (Location), nhập tên Project (thí dụ PlayMP3), chọn button OK. Khi cửa sổ "MFC AppWizard - Step 1" hiển thị, chọn option "Dialog Based" rồi button Finish để tạo Project chứa 1 Form giao diện đơn giản.
    3. Khi Form thiết kế hiển thị, bạn hãy xây dựng Form chỉ chứa 1 button đơn giản. Nhấn đúp chuột vào button duy nhất vừa tạo ra trong Form để tạo hàm xử lý sự kiện click chuột trên button này rồi viết code cho thân hàm như sau:
    void CPlayMP3Dlg::OnButton1() {
    //khai báo các biến cần dùng
    IGraphBuilder *pGraph1, *pGraph2;
    IMediaControl *pMediaControl1, *pMediaControl2;
    IMediaEvent *pEvent1, *pEvent2;
    IBaseFilter *pBaseReader1, *pBaseReader2;
    IBaseFilter *pAudioDst1, *pAudioDst2;
    HRESULT hr;
    //khởi động hệ thống quản lý COM
    CoInitialize(NULL);
    //tạo 1 graph builder để chơi file 1
    CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph1);
    pGraph1->QueryInterface(IID_IMediaControl, (void **)&pMediaControl1);
    pGraph1->QueryInterface(IID_IMediaEvent, (void **)&pEvent1);
    hr = pGraph1->AddSourceFilter(L"C:\\File1.mp3", L"", &pBaseReader1);
    hr = pGraph1->AddFilter(pBaseReader1, L"AudioSrc");
    //tìm sounddevice có tên "CyberLink Audio Renderer" (tương ứng với soundcard 1)
    if (FindSoundDevice("CyberLink Audio Renderer", &pAudioDst1)) {
    hr = pGraph1->AddFilter(pAudioDst1, L"AudioDst");
    IPin *pOutPin1, *pInpPin1;
    GetPin (pBaseReader1,PINDIR_OUTPUT, &pOutPin1);
    GetPin (pAudioDst1,PINDIR_INPUT, &pInpPin1);
    hr = pGraph1->Connect(pOutPin1, pInpPin1);
    //kích hoạt chạy các thành phần của graph.
    pMediaControl1->Run();
    }
    //tạo 1 graph builder khác để chơi file 2
    CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph2);
    pGraph2->QueryInterface(IID_IMediaControl, (void **)&pMediaControl2);
    pGraph2->QueryInterface(IID_IMediaEvent, (void **)&pEvent2);
    hr = pGraph2->AddSourceFilter(L"C:\\File2.mp3", L"", &pBaseReader2);
    hr = pGraph2->AddFilter(pBaseReader2, L"AudioSrc");
    //tìm sounddevice có tên "Realtek AC97 Audio" (tương ứng với soundcard 2)
    if (FindSoundDevice("Realtek AC97 Audio", &pAudioDst2)) {
    hr = pGraph2->AddFilter(pAudioDst2, L"AudioDst");
    IPin *pOutPin2, *pInpPin2;
    GetPin (pBaseReader2,PINDIR_OUTPUT, &pOutPin2);
    GetPin (pAudioDst2,PINDIR_INPUT, &pInpPin2);
    hr = pGraph2->Connect(pOutPin2, pInpPin2);
    //kích hoạt chạy các thành phần của graph.
    pMediaControl2->Run();
    }
    //đợi từng graph chạy xong
    long evCode;
    pEvent1->WaitForCompletion(INFINITE, &evCode);
    pEvent2->WaitForCompletion(INFINITE, &evCode);

    //dọn dẹp các đối tượng đã dùng
    pMediaControl1->Release();
    pEvent1->Release();
    pGraph1->Release();
    pMediaControl2->Release();
    pEvent2->Release();
    pGraph2->Release();
    CoUninitialize();
    }
    4. Viết thêm 2 hàm dịch vụ FindSoundDevice và GetPin vào trước hàm OnButton1 như sau:
    //hàm tìm sounddevice với tên xác định
    int FindSoundDevice (char* sname, IBaseFilter **p) {
    ICreateDevEnum *pSysDevEnum = NULL;
    IBaseFilter *pd;
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
    //tìm đối tượng thống kê các sounddevice
    IEnumMoniker *pEnumCat = NULL;
    hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEnumCat, 0);
    if (hr == S_OK) { // nếu có
    IMoniker *pMoniker;
    ULONG cFetched;
    int lPos = 0;
    //duyệt tìm từng sounddevice
    while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) {
    IPropertyBag *pPropBag;
    pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
    //xác định tên của sounddevice
    VARIANT varName;
    VariantInit(&varName);
    hr = pPropBag->Read(L"FriendlyName", &varName, 0);
    if (SUCCEEDED(hr)) {
    //so sánh với tên cần tìm
    BSTR m_bstr = varName.bstrVal;
    char sdstr[256];
    WideCharToMultiByte(CP_UTF8, 0, m_bstr, -1, sdstr, 256, NULL, NULL);
    if (strcmp(sname,sdstr)==0) {
    pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void **)&pd);
    *p = pd;
    VariantClear(&varName);
    pMoniker->Release();
    pPropBag->Release();
    pEnumCat->Release();
    pSysDevEnum->Release();
    return 1;
    }
    }
    VariantClear(&varName);
    pPropBag->Release();
    }
    pEnumCat->Release();
    }
    pSysDevEnum->Release();
    return 0;
    }

    //hàm xác định ngõ giao tiếp (pin) của 1 thành phần trong graph
    int GetPin(IBaseFilter *pBF,PIN_DIRECTION eDir,IPin** ppPin) {
    HRESULT hr = S_OK;
    IEnumPins *spPins = NULL;
    hr = pBF->EnumPins(&spPins);
    if(SUCCEEDED(hr)) {
    ULONG ulFetched = 0;
    IPin *spPin = NULL;
    while(spPins->Next(1,&spPin,&ulFetched) == S_OK) {
    PIN_DIRECTION eTmpDir;
    hr = spPin->QueryDirection(&eTmpDir);
    if(SUCCEEDED(hr)&&(eTmpDir==eDir)) {
    *ppPin = spPin;
    (*ppPin)->AddRef();
    break;
    }
    spPin = NULL;
    }
    }
    return hr;
    }

    5. Viết thêm các hàng lệnh để sử dụng thư viện DirectShow vào đầu file PlayMP3Dlg.cpp như sau:
    #include <dshow.h>
    #pragma comment(lib,"Strmiids.lib")

    6. Chọn menu Tools.Options.Directories để xem và cấu hình các thư mục thông tin được dùng cho môi trường VC6.0. Chọn mục "Include Files" rồi thêm đường dẫn chứa các file *.h của DirectShow vào danh sách (mặc định là c:\Program Files\Windows Platform SDK\Include).

    7. Chọn menu Build.Execute PlayMP3.exe để dịch và chạy chương trình vừa viết. Khi cửa sổ ứng dụng hiển thị, nhấn chuột vào button để chơi 2 file MP3 ra 2 sound card khác nhau theo yêu cầu.
    Lưu ý rằng chương trình trên đã chơi nhạc ra 2 sound card khác nhau, mỗi sound card được nhận dạng thông qua tên sounddevice có tên lần lượt là "CyberLink Audio Renderer" và "Realtek AC97 Audio". Để biết tên nhận dạng cho các sounddevice quản lý các soundcard trên máy mình, bạn có thể chạy trình Windows Media Player, chọn menu Tools.Option.Devices.Speakers, chọn button Properties rồi xem danh sách các sounddevice trong listbox "Audio device to use".
    10
  11. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Xin hướng dẫn cách viết chương trình C trên Linux, tạo 2 process producer và customer. Producer tạo ra dữ liệu ngẫu nhiên gởi cho process customer. Customer nhận và xử lí.


    Đáp
    :

    Để lập trình giải quyết vấn đề nào đó, bạn cần tìm hiểu và nắm vững giải thuật giải quyết vấn đề đó. Cụ thể bài toán Sản xuất-Tiêu dùng (Producer/Consumer) là 1 trong số ít bài toán kinh điển giới thiệu vấn đề cần phải đồng bộ hóa giữa các process và phải loại trừ tương hỗ giữa các process khi chúng đồng thời cùng truy xuất vào tài nguyên dùng chung nào đó. Giải thuật giải quyết bài toán Sản xuất-Tiêu dùng dùng kỹ thuật gửi/nhận thông báo đã được trình bày trong các sách giáo trình môn Hệ điều hành. Đây là kỹ thuật rất an toàn và tổng quát, vừa áp dụng được cho các process chạy trên cùng máy hay cho các process chạy trên các máy khác nhau. Việc hiện thực nó không phụ thuộc vào ngôn ngữ và vào HĐH. Sau đây chúng tôi giới thiệu qui trình điển hình để xây dựng 2 ứng dụng hợp thành bài toán Sản xuất-Tiêu dùng bằng ngôn ngữ VC++ của Microsoft trên Windows dùng socket để giao tiếp nhau thông qua môi trường mạng Internet. Qui trình gồm các bước sau:

    1. Chạy VC++ 6.0, chọn menu File.New để hiển thị cửa sổ New, chọn tab "Projects", chọn mục "Win32 Console Application", chọn vị trí "Location" chứa Project trên đĩa, nhập tên Project chứa ứng dụng Sản xuất (thí dụ Producer), chọn button OK. Khi cửa sổ Step 1 hiển thị, chọn checkbox "An empty project" rồi button Finish để tạo project trống ban đầu.

    2. Chọn menu File.New để hiển thị cửa sổ New, chọn tab "Files", chọn mục "C++ Source File", nhập tên file mã nguồn (producer) rồi nhấn button OK để tạo file mã nguồn trống ban đầu của ứng dụng.

    3. Khi cửa sổ mã nguồn trống ban đầu của file hiển thị, bạn nhập đoạn code cấu thành ứng dụng Producer như sau:
    //khai báo các thư viện cần dùng
    #include <winsock2.h>
    #include <stdio.h>
    #include <string.h>
    #include <conio.h>
    #include <winbase.h>

    //khai báo các biến toàn cục cần dùng
    int timesx, idx = 0;
    char sItem[256], sRequest[256];

    //hàm thực hiện việc sản xuất 1 sản phẩm
    void Produce_Item(char* Item) {
    sprintf(Item, "Sản phẩm thứ %d.\n",idx++);
    //giả lập thời gian sản xuất sản phẩm để dễ quan sát
    Sleep(timesx);
    }

    //hàm thực hiện việc thông báo chứa sản phẩm
    void Buid_Message(char* Item) {
    //tùy định dạng thông báo mà bạn viết lấy
    //ở đây chúng tôi không viết và xem thông báo chính là sản phẩm
    }

    //điểm nhập chương trình
    int main(int argc, char **argv) {
    //khai báo các biến cần dùng
    int retval, fromlen;
    SOCKADDR_IN local, from;
    WSADATA wsaData;
    SOCKET listen_socket, msgsock;
    if (argc <2) {
    printf("Nên nhập lệnh Producer <timesx>\\n\n");
    timesx = 1;
    } else timesx = atoi(argv[1]);
    printf ("Lưu ý : sản xuất mới sản phẩm tốn %d ms\n",timesx);

    //khởi động dịch vụ socket
    if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
    fprintf(stderr,"WSAStartup bị lỗi : Error %d\n",WSAGetLastError());
    WSACleanup();
    return -1;
    }

    //thiết lập các giá trị cấu hình server
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = INADDR_ANY;
    local.sin_port = htons(2048);
    //tạo socket lắng nghe của server
    listen_socket = socket(AF_INET, SOCK_STREAM,0); //TCP socket
    if (listen_socket == INVALID_SOCKET){
    fprintf(stderr,"socket() bị lỗi : Error %d\n",WSAGetLastError());
    WSACleanup();
    return -1;
    }

    if (bind(listen_socket,(LPSOCKADDR)&local,sizeof(local)) == SOCKET_ERROR) {
    fprintf(stderr,"bind() bị lỗi : Error %d\n",WSAGetLastError());
    WSACleanup();
    return -1;
    }

    //khai báo số lượng client maximum
    if (listen(listen_socket,SOMAXCONN) == SOCKET_ERROR) {
    fprintf(stderr,"listen() bị lỗi : Error %d\n",WSAGetLastError());
    WSACleanup();
    return -1;
    }

    //chờ yêu cầu kết nối từ consumer
    printf("Chờ yêu cầu kết nối từ consumer...\n");
    fromlen=sizeof(from);
    msgsock = accept(listen_socket,(struct sockaddr*)&from, &fromlen);
    if (msgsock == INVALID_SOCKET) {
    fprintf(stderr,"accept() bị lỗi : error %d\n",WSAGetLastError());
    WSACleanup();
    return -1;
    }

    //thiết lập chỉ số sản phẩm đầu tiên
    idx = 0;
    //lặp sản xuất theo nhịp điệu của client
    //nếu muốn ngừng thì user phải gỏ 1 phím nào đó
    while(!_kbhit()) {
    //sản xuất 1 sản phẩm mới
    Produce_Item(sItem);
    //chờ nhận yêu cầu sản xuất từ consumer
    printf("Chờ nhận yêu cầu sản xuất từ consumer...\n");
    retval = recv(msgsock,sRequest,256,0 );
    if (retval == SOCKET_ERROR) {
    fprintf(stderr,"recv() bị lỗi : error %d\n",WSAGetLastError());
    closesocket(msgsock);
    return -1;
    }
    //xây dựng thông báo chứa sản phẩm
    Buid_Message(sItem);
    //gởi thông báo chứa sản phẩm cho consumer dùng
    printf("Gởi thông báo chứa sản phẩm cho consumer dùng...\n\n");
    send(msgsock,sItem,sizeof(sItem),0);
    }
    closesocket(msgsock);
    return 0;
    }
    4. Chọn menu Build.Set Active Configuration để hiển thị cửa sổ cấu hình, chọn mục "Win32 Release" rồi button OK để qui định máy dịch ra file khả thi ở chế độ phân phối (không có thông tin debug bên trong hầu giảm kích thước file khả thi).

    5. Build.Rebuild All để dịch ứng dụng ra file khả thi. Với cấu hình như bước 4 thì file khả thi có tên là Producer.exe nằm trong thư mục con Release của Project.

    6. tương tự cho việc xây dựng Consumer. Chạy VC++ 6.0, chọn menu File.New để hiển thị cửa sổ New, chọn tab "Projects", chọn mục "Win32 Console Application", chọn vị trí "Location" chứa Project trên đĩa, nhập tên Project chứa ứng dụng Tiêu dùng (thí dụ Consumer), chọn button OK. Khi cửa sổ Step 1 hiển thị, chọn checkbox "An empty project" rồi button Finish để tạo project trống ban đầu.

    7. chọn menu File.New để hiển thị cửa sổ New, chọn tab "Files", chọn mục "C++ Source File", nhập tên file mã nguồn (consumer) rồi nhấn button OK để tạo file mã nguồn trống ban đầu của ứng dụng.

    8. Khi cửa sổ mã nguồn trống ban đầu của file hiển thị, bạn nhập đoạn code cấu thành ứng dụng Consumer như sau:
    //khai báo các thư viện cần dùng
    #include <winsock2.h>
    #include <stdio.h>
    #include <string.h>
    #include <conio.h>
    #include <winbase.h>
    //khai báo các biến toàn cục cần dùng
    const N = 5; //dung lượng kho chứa sản phẩm
    int timetd;
    char sItem[256], sRequest[256];

    //hàm thực hiện việc tiêu dùng 1 sản phẩm
    void Consume_Item(char* Item) {
    //giả lập thời gian tiêu dùng sản phẩm để dễ quan sát
    Sleep(timetd);
    printf("Nội dung sản phẩm nhận được : %s\n", Item);
    }
    //hàm rút trích sản phẩm từ thông báo
    void Extract_Item(char* Item) {
    //tùy định dạng thông báo mà bạn viết lấy
    //ở đây chúng tôi không viết và xem thông báo chính là sản phẩm
    }

    //điểm nhập chương trình
    int main(int argc, char **argv) {
    //khai báo các biến cần dùng
    int i, retval;
    SOCKADDR_IN ser_addr;
    WSADATA wsaData;
    SOCKET sock;
    if (argc <2) {
    printf("Nên nhập lệnh Consumer <timetd>\\n\n");
    timetd = 1;
    } else timetd = atoi(argv[1]);
    printf ("Lưu ý : sản xuất mới sản phẩm tốn %d ms\n",timetd);

    //khởi động dịch vụ socket
    if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
    fprintf(stderr,"WSAStartup bị lỗi : Error %d\n",WSAGetLastError());
    WSACleanup();
    return -1;
    }

    //tạo socket giao tiếp, nếu thất bại báo lỗi
    sock=socket(AF_INET,SOCK_STREAM,0);
    if (sock==INVALID_SOCKET) {
    fprintf(stderr,"socket() bị lỗi : Error %d\n",WSAGetLastError());
    WSACleanup();
    return -1;
    }

    // thiết lập địa chỉ giao tiếp của server
    ser_addr.sin_family=AF_INET;
    ser_addr.sin_port=htons(2048);
    ser_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    //yêu cầu nối kết tới server
    if (connect(sock,(LPSOCKADDR)&ser_addr,sizeof(ser_addr))==SOCKET_ERROR) {
    fprintf(stderr,"connect() bị lỗi : Error %d\n",WSAGetLastError());
    WSACleanup();
    return -1;
    }

    //gởi N yêu cầu sản xuất (vì kho chứa có N chỗ)
    sprintf(sRequest,"Hay san xuat them san pham moi\n");
    for (i = 0 ; i < N ; i++)
    send(sock,sRequest,sizeof(sRequest),0);
    //lặp tiêu dùng từng sản phẩm
    //nếu muốn ngừng thì user phải gỏ 1 phím nào đó
    while(!_kbhit()) {
    //chờ nhận thông báo chứa sản phẩm
    printf("Chờ nhận thông báo chứa sản phẩm từ Producer...\n");
    retval = recv(sock,sItem,256,0 );
    if (retval == SOCKET_ERROR) {
    fprintf(stderr,"recv() bị lỗi : error %d\n",WSAGetLastError());
    closesocket(sock);
    return -1;
    }
    //rút trích sản phẩm từ thông báo
    Extract_Item(sItem);
    //gởi thông báo yêu cầu sản xuất tiếp
    printf("Gởi thông báo yêu cầu sản xuất tiếp...\n");
    send(sock,sRequest,sizeof(sRequest),0);
    //tiêu dùng sản phẩm vừa nhận
    Consume_Item(sItem);
    }
    closesocket(sock);
    exit(0);
    }
    9. Chọn menu Build.Set Active Configuration để hiển thị cửa sổ cấu hình, chọn mục "Win32 Release" rồi button OK để qui định máy dịch ra file khả thi ở chế độ phân phối (không có thông tin debug bên trong hầu giảm kích thước file khả thi).

    10. Build.Rebuild All để dịch ứng dụng ra file khả thi. Với cấu hình như bước 9 thì file khả thi có tên là Consumer.exe nằm trong thư mục con Release của Project.

    Sau khi có được 2 ứng dụng Sản xuất - Tiêu dùng, bạn có thể thử chạy chúng bằng cách copy 2 file khả thi tạo được vào cùng 1 thư mục, tạo 2 cửa sổ "Command Prompt" độc lập (nên hiệu chỉnh vị trí và kích thước của 2 cửa sổ này sao cho bạn có thể thấy đồng thời cả 2 cửa sổ). Trong cửa sổ 1, bạn dùng lệnh cd để chuyển về thư mục chứa 2 file khả thi rồi chạy ứng dụng Producer bằng cách nhập dòng lệnh "producer 1", còn trong cửa sổ 2, bạn cũng dùng lệnh cd để chuyển về thư mục chứa 2 file khả thi rồi chạy ứng dụng Consumer bằng cách nhập dòng lệnh "consumer 2000" rồi quan sát tiến độ chạy của 2 ứng dụng. Lưu ý tham số kèm theo dòng lệnh producer hay consumer là thời gian được tính bằng milisecond mà trình sản xuất/tiêu dùng sẽ tốn để sản xuất/tiêu dùng 1 sản phẩm. Bạn nên thử thay đổi giá trị này thành nhiều giá trị khác và kiểm nghiệm tốc độ làm việc của từng ứng dụng.

    Lưu ý trong đoạn code Producer trên, chúng tôi cho process Producer chạy trên cùng máy với Consumer và Producer giao tiếp ở địa chỉ TCP là "127.0.0.1:2048". Tùy theo nhu cầu, bạn có quyền thay đổi địa chỉ TCP của process Producer để nó có thể làm việc ở bất kỳ máy nào và port giao tiếp nào.
    11
  12. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Xin hướng dẫn cách viết 1 chương trình bằng VB 6.0 có thể tương tác với 1 ứng dụng khác. Ví dụ, khi chạy chương trình sẽ tự động gọi file iTunes.exe, sau đó đăng nhập và nhận biết đăng nhập thành công hay thất bại từ thông báo của iTunes.


    Đáp: Trong phần mềm viết bằng ngôn ngữ VB 6.0, để kích hoạt 1 chương trình khác chạy, bạn có thể gọi hàm API của Windows có tên là WinExec(). Thí dụ, muốn chạy ứng dụng "c:\program files\itunes\itunes.exe", bạn có thể viết lệnh sau:

    WinExec """c:\program files\itunes\itunes.exe""", 1
    Sau khi ứng dụng được kích hoạt chạy bởi lệnh WinExec(), nó sẽ chạy song hành và độc lập với phần mềm của bạn. Để tương tác tự động với ứng dụng khác, bạn phải biết trình tự thao tác cụ thể mà người dùng thực hiện nhằm yêu cầu 1 chức năng nào đó của ứng dụng rồi dùng lệnh SendKeys để tạo tự động trình tự thao tác đó.
    Thí dụ sau đây là qui trình xây dựng ứng dụng VB 6.0 nhỏ demo việc kích hoạt ứng dụng iTunes rồi yêu cầu nó dừng chạy khi cần thiết.
    1. Chạy VB 6.0, tạo Project dạng "Standard EXE"
    2. Khi Form ứng dụng rỗng hiển thị, bạn hãy vẽ 2 button có tên mặc định lần lượt là Command1 và Command2. Hiệu chỉnh caption cho Command1 là "Chạy iTunes" và caption cho Command2 là "Dừng iTunes".
    3. Nhấn đúp chuột vào button Command1 để hiển thị cửa sổ soạn code cho Form rồi viết đoạn lệnh VB sau:
    Option Explicit
    'khai báo các hằng cần dùng
    Const GW_CHILD = 5
    Const GW_HWNDNEXT = 2
    'khai báo các hàm API cần dùng
    Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
    Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
    Private Declare Function GetTopWindow Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function SetFocusWnd Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long
    Private Declare Function WinExec Lib "kernel32" (ByVal lpCmdLine As String, ByVal nCmdShow As Long) As Long
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

    'thủ tục xử lý click chuột vào button Command1
    Private Sub Command1_Click()
    'kích hoạt iTunes chạy
    WinExec """c:\program files\itunes\itunes.exe""", 1
    End Sub

    'thủ tục xử lý click chuột vào button Command2
    Private Sub Command2_Click()
    Dim hwnd As Long
    Do
    'tìm handle của cửa sổ iTunes
    hwnd = FindWindow("iTunes")
    Loop Until hwnd <> 0
    'cho cửa sổ iTunes hiển thị trên cùng và làm việc với user
    SetForegroundWindow hwnd
    SetFocusWnd hwnd
    'ngủ chờ 2 giây
    Sleep (2000)
    'tạo phím Alt-F để hiển thị menu File của iTunes
    SendKeys "%f"
    'ngủ chờ 2 giây
    Sleep (2000)
    'tạo phím x để yêu cầu iTunes dừng
    SendKeys "x"
    End Sub

    'hàm tìm handle của 1 cửa sổ ứng dụng có titlebar xác định
    Private Function FindWindow(stitlebar As String) As Long
    Dim WT As String, Length As Long
    Dim hwnd As Long
    FindWindow = 0
    hwnd = GetTopWindow(0)
    Do
    WT = Space(256)
    Length = GetWindowText(hwnd, WT, 255)
    WT = Left$(WT, Length)
    If WT = stitlebar Then
    FindWindow = hwnd
    Exit Function
    End If
    hwnd = GetWindow(hwnd, GW_HWNDNEXT)
    Loop Until hwnd = 0
    End Function
    4. Chọn menu Run.Start để chạy thử ứng dụng, nhấn vào button "Chạy iTunes" để kích hoạt iTunes chạy. Sau khi iTunes đã chạy, nhấn vào button "Dừng iTunes" để yêu cầu iTunes dừng chạy.


    Tôi muốn lập trình giả lập Windows Media Player cho Pocket PC, nhưng trong .Net compact Framework không hỗ trợ điều khiển Media như trong .Net framework. Vậy phải làm sao?
    Hiện nay, Microsoft đã phân phối điều khiển "Windows Media Player 10 Mobile (MP 10 Mobile)" cho máy Pocket PC và SmartPhone, do đó bạn có thể lập trình mobile dùng điều khiển này để chơi các file multimedia.

    Tôi có 1 ứng dụng nhỏ viết bằng C# truy xuất database, không hiểu sao thêm mới thì được nhưng xoá không được, và nếu đưa chỉ mục (dòng hiện hành) đến cuối thì phát sinh lỗi.
    Ứng dụng mà bạn viết thực hiện duyệt xem/cập nhật/thêm/xóa từng record trong bảng BangChude của file database (thí dụ có tên là d:\Mydatabase.mdb), mỗi record trong bảng có 2 field: field ID (làm key) và field Chude có nội dung là chuỗi. Sau khi đọc và kiểm tra chi tiết code trong Form chủ đề của bạn được liệt kê ở trên, chúng tôi thấy có khá nhiều lỗi: lỗi về hằng chuỗi, lỗi về chỉ số record cần truy xuất trong các đoạn code... Trong đó lỗi về việc xóa record như bạn thắc mắc là do nội dung trong đối tượng DataTable và DataAdapter không nhất quán với nhau. Chúng tôi đã sửa tất cả các lỗi của bạn để chương trình có thể chạy tốt. Để cho bạn và độc giả dễ theo dõi, chúng tôi giới thiệu qui trình điển hình để xây dựng ứng dụng của bạn:
    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 C#" trong cửa sổ "Project types", chọn mục "Windows" con của "Visual C#", 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 chọn Form rồi đặt tên cho nó là frmThaoTacChuDe.
    3. Vẽ các đối tượng cần dùng cho form như sau:
    Hãy đặt tên cho 2 textbox lần lượt là tbChude và tbDhh, tên cho các button chức năng lần lượt là btLui, btTien, btCapnhat, btThemmoi, btGhi, btXoa.
    3. Nhấn đúp vào từng button để tạo hàm xử lý sự kiện click chuột vào button đó. Sau khi tạo được 6 hàm xử lý click chuột cho 6 button, bạn viết code cho từng hàm theo chức năng của nó. Cuối cùng, file source code cho Form frmThaoTacChuDe như sau:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;

    //lệnh sau rất cần để dùng các đối tượng OleDb
    using System.Data.OleDb;
    namespace WindowsApplication1 {
    public partial class frmThaoTacChuDe : Form {
    //khai báo các thuộc tính cần dùng
    private OleDbDataAdapter bdg_CHU_DE;
    private DataTable bdl_CHU_DE;
    private OleDbConnection myConnection;
    private int dhh;
    //hàm khởi tạo form
    public frmThaoTacChuDe() {
    InitializeComponent();
    bdl_CHU_DE = new DataTable();
    btGhi.Enabled = false;
    dhh = 0;
    //tạo đối tượng Connection đến database
    myConnection = new OleDbConnection();
    myConnection.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\\MyDatabase.mdb;";
    //tạo đối tượng DataAdapter
    bdg_CHU_DE = new OleDbDataAdapter("select * from BangChude", myConnection);
    //thiết lập lệnh Delete cho DataAdapter;
    bdg_CHU_DE.DeleteCommand = new System.Data.OleDb.OleDbCommand();
    bdg_CHU_DE.DeleteCommand.Connection = myConnection;
    bdg_CHU_DE.DeleteCommand = new System.Data.OleDb.OleDbCommand();
    bdg_CHU_DE.DeleteCommand.Connection = myConnection;
    bdg_CHU_DE.DeleteCommand.CommandText = "DELETE FROM `BangChude` WHERE ((`ID` = ?) AND ((? = 1 AND `Chude` IS NULL) OR (`Chude` = ?)))";
    bdg_CHU_DE.DeleteCommand.CommandType = System.Data.CommandType.Text;
    bdg_CHU_DE.DeleteCommand.Parameters.Add(new System.Data.OleDb.OleDbParameter("Original_ID", System.Data.OleDb.OleDbType.Integer, 0, System.Data.ParameterDirection.Input, ((byte)(0)), ((byte)(0)), "ID", System.Data.DataRowVersion.Original, false, null));
    bdg_CHU_DE.DeleteCommand.Parameters.Add(new System.Data.OleDb.OleDbParameter("IsNull_Chude", System.Data.OleDb.OleDbType.Integer, 0, System.Data.ParameterDirection.Input, ((byte)(0)), ((byte)(0)), "Chude", System.Data.DataRowVersion.Original, true, null));
    bdg_CHU_DE.DeleteCommand.Parameters.Add(new System.Data.OleDb.OleDbParameter("Original_Chude", System.Data.OleDb.OleDbType.VarWChar, 0, System.Data.ParameterDirection.Input, ((byte)(0)), ((byte)(0)), "Chude", System.Data.DataRowVersion.Original, false, null));
    //thiết lập lệnh Insert cho DataAdapter;
    bdg_CHU_DE.InsertCommand = new System.Data.OleDb.OleDbCommand();
    bdg_CHU_DE.InsertCommand.Connection = myConnection;
    bdg_CHU_DE.InsertCommand.CommandText = "INSERT INTO `BangChude` (`Chude`) VALUES (?)";
    bdg_CHU_DE.InsertCommand.CommandType = System.Data.CommandType.Text;
    bdg_CHU_DE.InsertCommand.Parameters.Add(new System.Data.OleDb.OleDbParameter("Chude", System.Data.OleDb.OleDbType.VarWChar, 0, System.Data.ParameterDirection.Input, ((byte)(0)), ((byte)(0)), "Chude", System.Data.DataRowVersion.Current, false, null));
    //thiết lập lệnh Update cho DataAdapter;
    bdg_CHU_DE.UpdateCommand = new System.Data.OleDb.OleDbCommand();
    bdg_CHU_DE.UpdateCommand.Connection = myConnection;
    bdg_CHU_DE.UpdateCommand.CommandText = "UPDATE `BangChude` SET `Chude` = ? WHERE ((`ID` = ?) AND ((? = 1 AND `Chude` IS NULL) OR (`Chude` = ?)))";
    bdg_CHU_DE.UpdateCommand.CommandType = System.Data.CommandType.Text;
    bdg_CHU_DE.UpdateCommand.Parameters.Add(new System.Data.OleDb.OleDbParameter("Chude", System.Data.OleDb.OleDbType.VarWChar, 0, System.Data.ParameterDirection.Input, ((byte)(0)), ((byte)(0)), "Chude", System.Data.DataRowVersion.Current, false, null));
    bdg_CHU_DE.UpdateCommand.Parameters.Add(new System.Data.OleDb.OleDbParameter("Original_ID", System.Data.OleDb.OleDbType.Integer, 0, System.Data.ParameterDirection.Input, ((byte)(0)), ((byte)(0)), "ID", System.Data.DataRowVersion.Original, false, null));
    bdg_CHU_DE.UpdateCommand.Parameters.Add(new System.Data.OleDb.OleDbParameter("IsNull_Chude", System.Data.OleDb.OleDbType.Integer, 0, System.Data.ParameterDirection.Input, ((byte)(0)), ((byte)(0)), "Chude", System.Data.DataRowVersion.Original, true, null));
    bdg_CHU_DE.UpdateCommand.Parameters.Add(new System.Data.OleDb.OleDbParameter("Original_Chude", System.Data.OleDb.OleDbType.VarWChar, 0, System.Data.ParameterDirection.Input, ((byte)(0)), ((byte)(0)), "Chude", System.Data.DataRowVersion.Original, false, null));
    bdg_CHU_DE.Fill(bdl_CHU_DE);
    hienthithongtin();
    }

    //hàm xử lý click button Lùi
    private void btLui_Click(object sender, EventArgs e) {
    dhh--;
    if (dhh == -1)
    dhh = bdl_CHU_DE.Rows.Count - 1;
    btCapnhat.Enabled = true;
    btXoa.Enabled = true;
    btGhi.Enabled = false;
    hienthithongtin();
    }

    //hàm xử lý click button Tiến
    private void btTien_Click(object sender, EventArgs e) {
    dhh++;
    if (dhh == bdl_CHU_DE.Rows.Count)
    dhh = 0;
    btCapnhat.Enabled = true;
    btXoa.Enabled = true;
    btGhi.Enabled = false;
    hienthithongtin();
    }

    //hàm xử lý click button Thêm mới
    private void btThemmoi_Click(object sender, EventArgs e){
    tbChude.Text = "";
    btCapnhat.Enabled = false;
    btXoa.Enabled = false;
    btGhi.Enabled = true;
    }

    //hàm xử lý click button Ghi
    private void btGhi_Click(object sender, EventArgs e) {
    DataRow dongdulieu;
    dongdulieu = bdl_CHU_DE.NewRow();
    bdl_CHU_DE.Rows.Add(dongdulieu);
    dhh = bdl_CHU_DE.Rows.Count - 1;
    if (tbChude.Text != "")
    bdl_CHU_DE.Rows[dhh]["Chude"] = tbChude.Text;
    try {
    btCapnhat.Enabled = true;
    btXoa.Enabled = true;
    btGhi.Enabled = false;
    bdg_CHU_DE.Update(bdl_CHU_DE);
    bdl_CHU_DE.Reset();
    bdg_CHU_DE.Fill(bdl_CHU_DE);
    hienthithongtin();
    MessageBox.Show("Ghi dữ liệu thành công!");
    }
    catch {
    MessageBox.Show("Ghi dữ liệu không thành công!");
    }
    }

    //hàm xử lý click button Xóa
    private void btXoa_Click(object sender, EventArgs e) {
    bdl_CHU_DE.Rows[dhh].Delete();
    try {
    bdg_CHU_DE.Update(bdl_CHU_DE);
    bdl_CHU_DE.Reset();
    bdg_CHU_DE.Fill(bdl_CHU_DE);
    dhh--;
    if (dhh == -1) dhh = 0;
    hienthithongtin();
    MessageBox.Show("Xóa dữ liệu thành công!");
    }
    catch {
    MessageBox.Show("Xóa dữ liệu không thành công!");
    }
    }

    //hàm hiển thị record cần xử lý hiện hành
    private void hienthithongtin() {
    tbChude.Text = bdl_CHU_DE.Rows[dhh]["Chude"].ToString();
    tbDhh.Text = dhh.ToString();
    }

    //hàm xử lý việc thay đổi chỉ số record cần xử lý
    private void tbDhh_TextChanged(object sender, EventArgs e) {
    btCapnhat.Enabled = true;
    btXoa.Enabled = true;
    btGhi.Enabled = false;
    try {
    int dong = int.Parse(tbDhh.Text.ToString());
    if ((dong < 0) (dong > bdl_CHU_DE.Rows.Count - 1)) {
    MessageBox.Show("Không tồn tại dòng này!\nXin vui lòng nhập số từ 0 -" + (bdl_CHU_DE.Rows.Count - 1) + ".");
    tbDhh.Text = dhh.ToString();
    } else {
    dhh = dong;
    hienthithongtin();
    }
    } catch {
    MessageBox.Show("Kiểu dữ liệu không hợp lệ!");
    tbDhh.Text = dhh.ToString();
    }
    }

    //hàm xử lý click button Capnhat
    private void btCapnhat_Click(object sender, EventArgs e){
    if (tbChude.Text != "")
    bdl_CHU_DE.Rows[dhh]["Chude"] = tbChude.Text;
    try {
    bdg_CHU_DE.Update(bdl_CHU_DE);
    bdl_CHU_DE.Reset();
    bdg_CHU_DE.Fill(bdl_CHU_DE);
    MessageBox.Show("Cập nhật dữ liệu thành công!");
    } catch {
    MessageBox.Show("Cập nhật dữ liệu không thành công!");
    }
    }
    }
    }
    4. Chọn menu Debug.Start Debuging để chạy thử ứng dụng. Khi cửa sổ ứng dụng hiển thị, bạn thử chọn từng button chức năng và xem phản ứng của chương trình xem có đúng theo yêu cầu của mình không? Lưu ý file database mà chương trình trên truy xuất có đường dẫn là c:\MyDatabase.mdb, trong file đã có ít nhất 1 bảng tên là BangChude.

    Cách sử dụng phần mềm Lex và đoạn mã C mà có thể từ đó xây dựng một trình biên dịch?
    Module phân tích từ vựng là 1 trong nhiều module chức năng cấu thành 1 chương trình dịch ngôn ngữ. Cách dễ dàng và nhanh chóng nhất để xây dựng module phân tích từ vựng cho 1 ngôn ngữ nào đó là dùng ngôn ngữ Lex (Lexical Analyzer Generator). File Lex chứa các biểu thức chính quy, mỗi biểu thức chính quy miêu tả 1 token cụ thể của ngôn ngữ, định dạng tổng quát của file Lex như sau:
    %{
    //các lệnh định nghĩa viết bằng C hay C++
    %}
    %%
    //các biểu thức chính quy nhận dạng các token của ngôn ngữ
    %%
    //các đoạn code C hay C++ miêu tả ứng dụng
    Để bạn hiểu cụ thể và chi tiết, chúng tôi xin giới thiệu qui trình điển hình để viết 1 chương trình dịch rất đơn giản: duyệt file mã nguồn (text only), tìm mọi từ "toi" và dịch thành "I", mọi từ "anh" và dịch thành "You", các nội dung khác của mã nguồn vẫn giữ nguyên.
    1. Dùng bất kỳ trình soạn thảo văn bản nào để viết mã nguồn Lex giải quyết yêu cầu trên như sau (rồi lưu lên file mytrans.l):
    %{
    //các lệnh định nghĩa cần dùng
    #include <stdlib.h>
    %}

    %%
    [tT][oO][iI] { printf("I");}
    [aA][nN][hH] { printf("You");}
    . { printf("%c",yytext[0]); }
    %%

    //điểm nhập của ứng dụng
    void main(int argc, char *argv[]) {
    //mở file mã nguồn
    if ((yyin = fopen(argv[1],"r")) == 0) {
    printf("Không thể mở file %s!\n",argv[1]);
    return;
    }
    //gọi hàm yylex phân tích từ vựng
    yylex();
    fclose(yyin);
    return;
    }
    Lưu ý rằng toàn bộ đặc tả Lex sẽ được dịch ra thành hàm yylex() với nhiệm vụ thực hiện đúng những gì đặc tả. Trong thí dụ trên, chúng tôi chỉ đặc tả 2 token để nhận dạng 2 từ "toi" và "anh", mỗi lần gặp từ "toi" thì theo đặc tả, chương trình sẽ xuất ra từ "I", mỗi lần gặp từ "anh" chương trình sẽ xuất ra từ "You", còn khi gặp các ký tự khác thì chương trình xuất ra ký tự gốc chứ không biến đổi gì.
    2. Sau khi đã viết xong file Lex, bạn có thể dùng tool Lex dịch nó ra file *.c tương ứng. Vì tool Lex thường chạy ở chế độ dòng lệnh, bạn hãy tạo 1 cửa sổ "Command Prompt", dùng lệnh cd để chuyển thư mục làm việc về thư mục chứa file mã nguồn Lex rồi nhập lệnh sau để dịch file Lex ra file mytrans.c:
    flex -tl mytrans.l > mytrans.c

    3. Sau khi đã có file mytrans.c, bạn có thể dùng chương trình dịch C hay C++ dịch nó ra file khả thi. Thí dụ bạn có thể dùng trình dịch BorlandC++ để dịch file mytrans.c ra file khả thi như sau:
    bcc mytrans.c

    4. Sau khi đã có file khả thi, bạn có thể dùng nó. Thí dụ, bạn hãy dùng trình soạn thảo văn bản rồi soạn thử 1 file văn bản có chứa nhiều từ "toi" và "anh" (giả sử được lưu thành file mydoc.txt). Để dịch file nguồn ra file kết quả theo yêu cầu ở trên, bạn nhập lệnh sau:
    mytrans mydoc.txt > ketqua.txt
    12
  13. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Cách tạo menu động cấp 1, 2 lấy từ cơ sở dữ liệu Access hoặc SQL 2005? Ví dụ có bảng menuchinh gồm các trường (id, tieudechinh, vitri), bảng menucap2 gồm các trường (id, tieudephu, vitri).


    Đáp: Để giải quyết việc tạo menu động như bạn yêu cầu, cần trang bị 2 kiến thức chính:

    - Kiến thức về các hàm API Windows để quản lý menu như GetMenu(), DeleteMenu(), InsertMenuItem(), CreatePopupMenu()... Thông tin cụ thể và chi tiết về cách dùng các hàm này được trình bày trong CD MSDN của Microsoft.

    - Kiến thức về việc truy xuất dữ liệu trong database, thí dụ kiến thức về việc dùng các đối tượng trong thư viện cấp cao ADO để truy xuất database.

    Ý tưởng của việc tạo menu động từ thông tin trong database có thể tóm tắt như sau:
    - Tạo 1 menu trống cho Form ứng dụng.
    - Đọc vào danh sách các record của bảng menuchinh theo thứ tự của field vitri (phải có giá trị liên tục từ 0 trở lên).
    - Lặp thêm từng menu pop-up theo thông tin của record trong danh sách, đọc vào danh sách các record của bảng menucap2 có cùng giá trị ID như phần tử menu chính hiện hành, lặp thêm từng option theo thông tin của record trong danh sách.

    Qui trình viết ứng dụng tạo menu động cụ thể sẽ phụ thuộc nhiều vào môi trường lập trình, vào ngôn ngữ lập trình được dùng. Ở đây chúng tôi xin giới thiệu qui trình điển hình để viết ứng dụng tạo menu động theo yêu cầu của bạn bằng môi trường VB 6.0. Giả sử bạn đã tạo sẵn 2 bảng menuchinh và menucap2 với nội dung theo yêu cầu và chứa chúng trong file Access c:\YourDB.mdb:

    1. Chạy VB 6.0, tạo Project ứng dụng mới theo dạng "Standard EXE". Đây là Project quản lý ứng dụng đơn giản nhất, có 1 Form trống ban đầu.

    2. Sau khi Form ứng dụng hiển thị, chọn menu Tool.Menu Editor để mở cửa sổ soạn menu cho Form. Hãy tạo 1 menubar chỉ chứa 1 menu ban đầu (thí dụ Caption=File, Name=mnuFile) rồi đóng cửa sổ soạn menu lại. Mục đích của bước này là tạo 1 menu tĩnh ban đầu cho Form chỉ chứa 1 menu pop-up trống. Việc hiệu chỉnh thông tin về menu sẽ được lập trình động trong bước kế tiếp.

    3. Chọn menu Project.References để hiển thị cửa sổ References, duyệt tìm và chọn mục "Microsoft ActiveX Data Objects 2.x Library" và chọn button Ok để thêm các đối tượng ADO của thư viện này vào Project ứng dụng.

    4. Chọn menu View.Code để mở cửa sổ soạn code của chương trình rồi nhập đoạn chương trình sau đây:

    'khai báo các hàm API cần dùng
    Private Declare Function GetMenu Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function CreatePopupMenu Lib "user32" () As Long
    Private Declare Function DeleteMenu Lib "user32" (ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Long) As Long
    Private Declare Function InsertMenuItem Lib "user32" Alias "InsertMenuItemA" (ByVal hMenu As Long, ByVal un As Long, ByVal bool As Boolean, ByRef lpcMenuItemInfo As MENUITEMINFO) As Long
    'khai báo kiểu cần dùng
    Private Type MENUITEMINFO
    cbSize As Long
    fMask As Long
    fType As Long
    fState As Long
    wID As Long
    hSubMenu As Long
    hbmpChecked As Long
    hbmpUnchecked As Long
    dwItemData As Long
    dwTypeData As String
    cch As Long
    End Type

    'khai báo hằng cần dùng
    Const MF_STRING = &H0
    Const MIIM_STRING = &H40
    Const MIIM_SUBMENU = &H4
    Const MIIM_ID = &H2
    Const MIIM_TYPE = &H10
    Const MIIM_DATA = &H20
    Const MF_BYPOSITION = &H400

    'Khai báo các biến cần dùng
    Dim MII As MENUITEMINFO
    Dim Connection1 As ADODB.Connection
    Dim Command1 As ADODB.Command
    Dim RecordSet1 As ADODB.Recordset
    Dim RecordSet2 As ADODB.Recordset

    'thủ tục khởi động Form ứng dụng
    Private Sub Form_Load()
    Dim hMenu As Long
    Dim hSubMenu As Long
    'xác định menubar của Form
    hMenu = GetMenu(Me.hwnd)
    'xóa menu pop-up đã có
    kq = DeleteMenu(hMenu, 0, MF_BYPOSITION)
    Dim MyConString As String
    MyConString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source = c:\YourDB.mdb"
    'Tạo connection tới database
    Set Connection1 = New ADODB.Connection
    Connection1.Open MyConString
    'Tạo command làm việc với database
    Set Command1 = New ADODB.Command
    Command1.ActiveConnection = Connection1
    'tạo danh sách menu cấp 1 theo thứ tự vị trí
    Command1.CommandText = "select * from menuchinh order by vitri ASC"
    Set RecordSet1 = Command1.Execute()
    'lặp thêm động từng menu pop-up trong danh sách
    While Not RecordSet1.EOF()
    'xây dựng thông tin về menu pop-up cần tạo
    MII.cbSize = Len(MII)
    MII.dwTypeData = RecordSet1.Fields("tieudechinh").Value
    MII.cch = LenB(MII.dwTypeData)
    MII.hSubMenu = CreatePopupMenu()
    MII.fMask = MIIM_ID Or MIIM_STRING Or MIIM_SUBMENU
    'thêm menu pop-up này
    ret = InsertMenuItem(hMenu, RecordSet1.Fields("vitri").Value, MF_BYPOSITION, MII)
    hSubMenu = MII.hSubMenu
    'tạo danh sách menu cấp 2 của menu pop-up hiện hành theo thứ tự vị trí
    Command1.CommandText = "select * from menucap2 where ID=" & RecordSet1.Fields("ID").Value & " order by vitri ASC"
    Set RecordSet2 = Command1.Execute()
    'lặp thêm động từng menu item trong danh sách
    While Not RecordSet2.EOF()
    'xây dựng thông tin về menu item cần tạo
    MII.cbSize = Len(MII)
    MII.dwTypeData = RecordSet2.Fields("tieudephu").Value
    MII.cch = LenB(MII.dwTypeData)
    MII.fMask = MIIM_STRING
    'thêm menu item này
    ret = InsertMenuItem(hSubMenu, RecordSet2.Fields("vitri").Value, MF_BYPOSITION, MII)
    'di chuyển đến item kế tiếp
    RecordSet2.MoveNext
    Wend
    'di chuyển đến menu pop-up kế tiếp
    RecordSet1.MoveNext
    Wend
    'đóng các đối tượng đã dùng lại
    RecordSet1.Close
    RecordSet2.Close
    Connection1.Close
    End Sub

    5. Chạy chương trình và kiểm tra kết quả xem menu được tạo ra đúng theo đặc tả trong database hay không? Lưu ý rằng đoạn code trên mới chỉ tạo động được menubar chứ chưa liên kết thủ tục xử lý sự kiện cho từng option menu.
    13
  14. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Xin hướng dẫn viết lệnh VC# 2005 truy xuất cơ sở dữ liệu Access.

    Đáp
    : Nếu muốn lập trình truy xuất CSDL Access bằng ngôn ngữ VC# (hay bất kỳ ngôn ngữ .Net nào), bạn có thể dùng các đối tượng Odbc trong Package có tên là System.Data.Odbc. Sau đây là qui trình xây dựng 1 ứng dụng VC# nhỏ demo việc nối kết với database Access, đọc các record của bảng dữ liệu và hiển thị kết quả lên ListBox của ứng dụng:

    1. Chạy Access, tạo mới (hay mở lại) 1 file database (giả sử có tên là c:\YourDB.mdb), tạo 1 bảng dữ liệu có tên là danhbadienthoai, mỗi record gồm 3 field thông tin: tenthuebao, sodienthoai, diachi đều thuộc kiểu dữ liệu text (chuỗi). Thử thêm một số record dữ liệu theo nhu cầu vào bảng danhbadienthoai.

    2. Chạy Visual Studio, chọn chức năng Create.Project, chọn loại Project Visual C#.Windows.Windows Application để tạo mới Project C#.

    3. Khi Form ứng dụng hiển thị, bạn hãy tạo 2 đối tượng: button "Xem danh bạ" có tên là btnView và ListBox có tên là lbContents như hình vẽ dưới đây.

    4. Nhấn kép chuột vào button để tạo hàm xử lý sự kiện Click chuột cho button rồi viết code cho hàm này như sau:

    //thêm lệnh using sau vào đầu file để sử dụng các đối tượng Odbc.
    using System.Data.Odbc;
    //hàm xử lý sự kiện Click trên button btnView
    private void btnView_Click(object sender, EventArgs e) {
    //tạo cầu nối tới database cần truy xuất
    OdbcConnection myConnection = new OdbcConnection("Driver={Microsoft Access Driver (*.mdb)};DBQ=c:\\YourDB.mdb");
    myConnection.Open();
    //tạo đối tượng Command thực hiện truy vấn
    OdbcCommand myCommand = new OdbcCommand();
    myCommand.Connection = myConnection;
    myCommand.CommandText = "Select * from danhbadienthoai";
    OdbcDataReader myReader;
    //thi hành lệnh truy vấn và cất kết quả vào đối tượng myReader
    myReader = myCommand.ExecuteReader();
    lbContents.Items.Clear();
    //lặp đọc từng record và hiển thị
    String buf;
    while (myReader.Read()) {
    //xây dựng chuỗi thông tin cần hiển thị
    buf = myReader.GetString(0) + ", " + myReader.GetString(1) + ", " + myReader.GetString(2);
    //hiển thị chuỗi thông tin vào ListBox
    lbContents.Items.Add(buf);
    }
    //đóng các đối tượng đã dùng
    myReader.Close();
    myConnection.Close();
    }
    5. Chạy thử ứng dụng, khi Form hiển thị hãy ấn chuột vào button để xem kết quả hiển thị.
    14
  15. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Cách viết và chạy chương trình thường trú bằng ngôn ngữ C?


    Đáp
    : Bạn cần viết đoạn code assemby sau đây ở cuối chương trình để đăng ký nó thành TSR (chương trình thường trú) trên nền MSDOS:
    //Kết thúc chương trình dạng TSR
    asm {
    mov ax,3100h
    mov dx,ProgLen
    int 21h
    };
    ProgLen là biến chứa độ lớn của ứng dụng TSR theo đơn vị tính paragraph (mỗi paragraph = 16 byte). Thí dụ, giả sử chương trình thường trú dài 32768 byte (0x8000) thì Proglen = 0x800. Lưu ý chương trình TSR thường dùng để giải quyết 1 trong các vấn đề sau:
    - Chứa các hàm chức năng của hệ thống mà ta muốn nâng cấp/hiệu chỉnh.
    - Chứa các thủ tục xử lý ngắt cho 1 hay nhiều thiết bị xuất nhập nào đó...
    Dù ở dạng nào, đoạn code thường trú phải ở trạng thái ngủ chờ sự kiện và chỉ thức chạy khi sự kiện tương ứng xảy ra.
    15
  16. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Export và Import ra tập tin text từ Access (VB)

    Đáp:

    Hiện nay các bạn yêu thích lập trình sử dụng Access là nguồn chứa dữ liệu khá phổ biến vì đơn giản, dễ quản trị và đáp ứng được yêu cầu công việc. Hôm nay chúng tôi xin giới thiệu một đoạn code để export và import ra tập tin text từ Access (VB)


    Export Text (Flat file) từ Access Ms-Access

    Option Explicit
    Public Sub Export_Table_2_TextFile()
    On Error GoTo LocalErrorHandler
    Dim dbCompany As Database
    Dim rsGeneral As Recordset
    Dim ExpGeneral As PubExpGeneral
    Dim blnTab_Text As Boolean
    Dim FullName As String
    Dim FileHandle As Byte
    Dim strFileToExport As String
    Dim chkFileExist As String

    'Give Path with File name
    FullName = E:\General ' Thu muc chua du lieu, ban co the thay doi theo nhu cau của minh

    blnTab_Text = False

    Set dbCompany = OpenDatabase(FullName)

    'Ví dụ tên bang la Company
    Set rsGeneral = dbCompany.OpenRecordset(Company, dbOpenTable)
    With ExpGeneral
    .EmpNumber = No.
    .EmpName = Name
    .EmpAddress = Address
    .EmpCity = City

    Sử dụng TAB hoăc dấu phẩy
    If blnTab_Text Then
    .Delimiter1 = Chr(9)
    .Delimiter2 = Chr(9)
    .Delimiter3 = Chr(9)
    Else
    .Delimiter1 = Chr(44)
    .Delimiter2 = Chr(44)
    .Delimiter3 = Chr(44)
    End If
    .CRLF = vbCrLf
    End With

    FileHandle = FreeFile

    'Tên tập tin
    strFileToExport = C:\Exported.txt
    chkFileExist = Dir(strFileToExport)
    If chkFileExist <> Then
    Kill strFileToExport
    End If
    Open strFileToExport For Random As FileHandle Len = Len(ExpGeneral)
    Put FileHandle, , ExpGeneral
    Do Until rsGeneral.EOF
    With ExpGeneral
    .EmpNumber = rsGeneral(EmpNo)
    .EmpName = rsGeneral(EmpName)
    .EmpAddress = rsGeneral(EmpAddress)
    .EmpCity = rsGeneral(EmpCity)
    End With
    Put FileHandle, , ExpGeneral
    rsGeneral.MoveNext
    Loop
    rsGeneral.Close
    Set rsGeneral = Nothing
    Close FileHandle
    Exit Sub

    LocalErrorHandler:
    MsgBox Error Occured : & Err.Description, , Error

    End Sub


    'Import Text vào Ms-Access

    Public Sub Import_TextFile_2_Table()
    On Error GoTo LocalErrorHandler
    Dim dbCompany As Database
    Dim rsGeneral As Recordset
    Dim FullName As String
    Dim FileHandle As Byte
    Dim ImportRecord As String
    Dim flnName As String
    Dim RowPosition As Double
    Dim EmpNumber As String
    Dim EmpName As String
    Dim EmpAddress As String
    Dim EmpCity As String
    Dim Delimiter As String


    flnName = C:\Exported.txt
    Delimiter = ,
    FileHandle = FreeFile
    Open flnName For Input As FileHandle
    Line Input #FileHandle, ImportRecord
    FullName = C:\General
    Set dbCompany = OpenDatabase(FullName)
    Set rsGeneral = dbCompany.OpenRecordset(Company, dbOpenDynaset)
    Do Until EOF(FileHandle)
    Line Input #FileHandle, ImportRecord
    RowPosition = RowPosition + 1
    EmpNumber = Trim(Mid(ImportRecord, 1, InStr(1, ImportRecord, Delimiter, 1) - 1))
    EmpName = Trim(Mid(ImportRecord, 7, 10))
    EmpAddress = Trim(Mid(ImportRecord, 18, 30))
    EmpCity = Trim(Mid(ImportRecord, 49))
    rsGeneral.AddNew
    rsGeneral(EmpNo) = EmpNumber
    rsGeneral(EmpName) = EmpName
    rsGeneral(EmpAddress) = EmpAddress
    rsGeneral(EmpCity) = EmpCity
    rsGeneral.Update
    Loop
    Close FileHandle
    rsGeneral.Close
    Set rsGeneral = Nothing
    dbCompany.Close
    Set dbCompany = Nothing
    Exit Sub
    LocalErrorHandler:
    MsgBox Error Occured : & Err.Description, , Error
    End Sub
    16
  17. xuanha

    xuanha Luôn luôn thấu hiểu Thành viên BQT Thành Viên

    Ðề: Một số câu hỏi, giải đáp thiết thực về lập trình

    Hỏi: Xin hướng dẫn lập trình Visual Basic để lấy tên những tập tin chương trình đang chạy trên máy tính.

    Đáp: Khi 1 file phần mềm chạy (do yêu cầu người dùng hay do chương trình khác kích hoạt), nó được nạp vào bộ nhớ và trở thành process. Bạn có thể dùng hàm API của Windows có tên là WTSEnumerateProcesses() để thống kê tất cả các process đang chạy trên máy, mỗi process là của file khả thi nào, từ đó quyết định xử lý chúng theo yêu cầu của mình. Sau đây chúng tôi xin giới thiệu qui trình điển hình để xây dựng ứng dụng VB 6.0 demo việc thống kê tất cả các process đang chạy và hiển thị thông tin về chúng trong một ListVew để người dùng xem:
    1. Chạy VB 6.0, tạo Project ứng dụng dạng "Standard EXE" đơn giản.
    2. Chọn menu Project.Components để hiển thị cửa sổ Components, duyệt tìm vào chọn mục Microsoft Window Common Controls 6.0 rồi Ok để thêm các control trong thư viện này vào ToolBox của Project.
    3. Thiết kế Form gồm 1 ListView có tên mặc định là ListView1 như hình 4.
    4. Chọn menu View.Code để hiển thị cửa sổ soạn mã nguồn cho form rồi viết code cho nó như sau:
    Option Explicit
    'khai báo hằng và kiểu cần dùng
    Private Const WTS_CURRENT_SERVER_HANDLE = 0&
    Private Type WTS_PROCESS_INFO
    SessionID As Long
    ProcessID As Long
    pProcessName As Long
    pUserSid As Long
    End Type

    'khai báo các hàm API cần dùng
    Private Declare Function WTSEnumerateProcesses Lib "wtsapi32.dll" Alias "WTSEnumerateProcessesA" (ByVal hServer As Long, ByVal Reserved As Long, ByVal Version As Long, ByRef ppProcessInfo As Long, ByRef pCount As Long) As Long
    Private Declare Sub WTSFreeMemory Lib "wtsapi32.dll" (ByVal pMemory As Long)
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

    'hàm tìm chuỗi từ địa chỉ bộ nhớ xác định
    Private Function GetStringFromLP(ByVal StrPtr As Long) As String
    Dim b As Byte
    Dim tempStr As String
    Dim bufferStr As String
    Dim Done As Boolean

    Done = False
    Do 'lấy từng byte và xử lý
    CopyMemory b, ByVal StrPtr, 1
    If b = 0 Then 'kết thúc chuỗi
    Done = True
    Else
    tempStr = Chr$(b)
    bufferStr = bufferStr & tempStr
    StrPtr = StrPtr + 1 'tăng pointer tới byte kế
    End If
    Loop Until Done
    GetStringFromLP = bufferStr
    End Function

    'thủ tục khởi động Form
    Private Sub Form_Load()
    ListView1.View = lvwReport
    'tạo header gồm 4 cột thông tin trên ListView
    ListView1.ColumnHeaders.Add 1, "SessionID", "Session ID"
    ListView1.ColumnHeaders.Add 2, "ProcessID", "Process ID"
    ListView1.ColumnHeaders.Add 3, "ProcessName", "Process Name"
    ListView1.ColumnHeaders.Add 4, "UserID", "User ID"
    'thống kê và hiển thị các process
    GetWTSProcesses
    End Sub

    'thủ tục thống kê và hiển thị thông tin các process
    Private Sub GetWTSProcesses()
    'khai báo các biến cần dùng
    Dim RetVal As Long
    Dim Count As Long
    Dim i As Integer
    Dim lpBuffer As Long
    Dim p As Long
    Dim udtProcInfo As WTS_PROCESS_INFO
    Dim itmAdd As ListItem

    'xóa nội dung cũ của ListView
    ListView1.ListItems.Clear
    'thống kê các process
    RetVal = WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0&, 1, lpBuffer, Count)
    If RetVal = 0 Then 'nếu thất bại
    MsgBox "Error occurred calling WTSEnumerateProcesses. " & "Check the Platform SDK error codes in the MSDN Documentation" & " for more information.", vbCritical, "Error " & Err.LastDllError
    'dừng thủ tục
    Exit Sub
    End If
    'nếu thống kê được thì lặp hiển thị từng process
    p = lpBuffer
    For i = 1 To Count
    'copy thông tin về process i vào biến udtProcInfo
    CopyMemory udtProcInfo, ByVal p, LenB(udtProcInfo)
    'thêm hàng thông tin về process i vào ListView
    Set itmAdd = ListView1.ListItems.Add(i, , CStr(udtProcInfo.SessionID))
    itmAdd.SubItems(1) = CStr(udtProcInfo.ProcessID)
    itmAdd.SubItems(2) = GetStringFromLP(udtProcInfo.pProcessName)
    itmAdd.SubItems(3) = CStr(udtProcInfo.pUserSid)
    'hiệu chỉnh p về vị trí miêu tả process kế tiếp
    p = p + LenB(udtProcInfo)
    Next i
    Set itmAdd = Nothing
    'giải phóng bộ nhớ
    WTSFreeMemory lpBuffer
    End Sub
    Lưu ý tên file khả thi của process được chứa trong trường udtProcInfo.pProcessName.
    5. Chọn menu Run.Start để chạy thử Form vừa xây dựng và xem danh sách các process đang chạy trên máy
    17

Chia sẻ trang này

Users found this page by searching for:

  1. code trong vb de chay file mp3 co trong thu muc khac

    ,
  2. c# thuộc tính e. handle to control tác dụng

    ,
  3. cach lay du lieu tu msflex xuat ra textbox

    ,
  4. get hdd infor vbnet HD_FIRMWARE_REVISION,
  5. những cau hỏi và đáp án về văn học dan gian trong chương trình phổ thông cơ sở,
  6. dua CSDL vao listbox