• Thứ Năm, 11/11/2004 15:20 (GMT+7)

    “Nhấn và xem” với VB6


    Trong công việc cũng như trong học tập, chắc không ít lần bạn phải tiếp xúc với tài liệu nước ngoài và tự điển trên máy tính là trợ thủ đắc lực không thể thiếu. Khả năng 'nhấn và xem' (hay 'click and see') - tra nghĩa từ ngay trong tài liệu là tính năng rất cần thiết. Bài viết này sẽ hướng dẫn bạn xây dựng một chương trình dạng 'nhấn và xem' với VB6. Mã nguồn chương trình có thể tải về ở website của Tạp chí TGVT.

    Nguyên tắc hoạt động

    Chương trình được đặt tên là CnS. Để có thể tra từ trong các ứng dụng khác (MS Word, Arobat Reader, IE,...), chúng ta phải 'bắt' - câu móc hệ thống - để phát hiện khi người dùng nhấn chuột (dĩ nhiên chương trình của chúng ta phải chạy nền), sau đó sao chụp từ vị trí con trỏ chuột vào bộ nhớ và gửi về cho CnS xử lý. CnS sẽ so sánh từ này với CSDL từ điển có sẵn và hiển thị form thông tin ngay vị trí từ muốn tra. CnS chạy nền, đặt biểu tượng ở khay hệ thống.

    Khởi tạo chương trình

    Để bắt đầu, bạn tạo Standard Project mới, form1 mặc định được tạo. Vào menu Project/Add Form để thêm form2, Project/Add module để thêm module mới. Đặt tên form1=frmMain (Visible=false, Caption= 'Click and See'), form2=frmPopup (form hiển thị thông tin, BoderStyle=0), module1=mHook. Vào Project/Properties chọn Startup Object là Sub Main.

    Tạo một file CSDL trong Access gồm 1 bảng (WordsTable) và trong bảng này tạo 9 trường: Words, Display, Pronunciation, Noun, Verbs, Adjective, Preposition, Adverb và Other. Lưu với tên EV.mdb cùng thư mục với CnS.

    Phần FRMMAIN

    Trong frmMain bạn dùng MenuEditor tạo một menu cha (Caption: tùy ý, name: mnuCnS,bỏ chọn ô Visible) và 2 menu con: 1(Caption: &Return to Program, name: mnuR), 2(Caption: &Exit Program, name: mnuE). Nhấn Ctrl+T để vào hộp thoại Components Control, click chọn và thêm lên form:

    1. Microsoft ADO Data Control 6.0(OLEDB) (Name:ADO)

    2. Microsoft DataGrid Control 6.0(OLEDB) (Name:DG, Enable=False, DataSource = ADO)

    3. Microsoft SysInfo Control 6.0 (Name: SI)

    Đối tượng DG kết hợp với ADO sẽ hiển thị và cho phép bạn hiệu chỉnh trực tiếp lên CSDL (Edit Mode).

    Thêm lên form 5 CommandButton:

    1.Caption: &Edit Mode, Name:cmdE

    2. Caption: &Add Record, Name: cmdAR, Enable=false

    3. Caption: &Delete Record, Name: cmdDR,Enable=false

    4. Caption: &Return to Systray, Name: cmdRT

    5. Caption: &Exit, Name: cmdExit

    Ngoài ra, bạn thêm 2 Image để hiển thị trạng thái 'Enable' (hoạt động) và 'Disable' (tắt) chương trình ở khay hệ thống.

    Sub đảm nhiệm việc tạo icon ở khay hệ thống:

    Private Sub AddToSysTray()

      TrayI.cbSize = Len(TrayI)

      TrayI.hWnd = Me.hWnd 'lấy handle của frmMain

      TrayI.uId = 1&

      TrayI.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE

      TrayI.ucallbackMessage = WM_LBUTTONDOWN

      TrayI.hIcon = imgIcon(1).Picture 'Icon hiển thị trạng thái Enable

      TrayI.szTip = 'Click and See® Program-Enable' & Chr$(0) 'Chr$(0)- định dạng lại tooltiptext

      Shell_NotifyIcon NIM_ADD, TrayI 'tạo Icon

      Me.Hide 'Ẩn frmMain đi

    End Sub

                        và gỡ bỏ icon khỏi khay hệ thống:

    Private Sub RemoveFromSystray()

      TrayI.hWnd = Me.hWnd
     
      TrayI.uId = 1&

      TrayI.cbSize = Len(TrayI)

      Shell_NotifyIcon NIM_DELETE, TrayI 'hủy bỏ icon

      UnhookWindowsHookEx hHook 'không câu móc hệ thống

    End Sub

                         Thủ tục form_load:

    Private Sub Form_Load()

      AddToSysTray 'xuống khay hệ thống

      bState = 1 'trạng thái của chương trình là Enable

      Edit_Mode = 0 'không cho phép hiệu chỉnh DataGrid

      'thực hiện câu móc chuột hệ thống:

      hHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf MouseProc, App.hInstance, 0)

      ConnectStr = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' & App.Path & '\EV.mdb;Persist

      Security Info=False' 'chuỗi ConnectString của ADO

      ADO.ConnectionString = ConnectStr

      ADO.RecordSource = RecordSourceStr

      ADO.Refresh

      ADO.Recordset.Sort = 'Words' 'sắp xếp theo trường Words

    End Sub

                          Sự kiện xử lý nhấn chuột lên form (thực ra là nhấn lên Icon ở khay hệ thống):

    Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

    Msg = X / Screen.TwipsPerPixelX 'lấy tọa độ tương ứng với từng thông báo

    Select Case Msg

    Case WM_RBUTTONUP 'sau khi nhấn chuột phải

    Me.PopupMenu mnuCnS 'hiển thị menu Popup

    Case WM_LBUTTONDOWN 'nhấn chuột trái

     bState = (bState = 0)

     Select Case Abs(bState)

     Case 0 'Disable chương trình

       TrayI.hIcon = imgIcon(0).Picture

       TrayI.szTip = 'Click and See Pro gram-Disable' & Chr$(0)

           'không câu móc

       UnhookWindowsHookEx hHook

    Case 1 'Enable chương trình

       TrayI.szTip = 'Click and See Pro gram®-Enable' & Chr$(0)

       TrayI.hIcon = imgIcon(1).Picture

        'câu móc lại

        hHook = SetWindowsHookEx(WH_MOUSE_LL, AddressOf MouseProc, App.hInstance, 0)

    End Select

    Shell_NotifyIcon NIM_MODIFY, TrayI 'biến đổi thông tin Icon

    End Select

    End Sub

    Khi tìm từ, nếu có DG sẽ trích duy nhất từ đó trong CSDL và hiển thị ở dòng đầu tiên. Để biết từ cần tìm có trong CSDL hay không ta dựa vào thuộc tính Text của cột đầu tiên:

    Public Function FindWord(ByVal sWord As String) As Boolean

    On Error GoTo errTrap

    'câu truy vấn, không có khoảng cách trắng giữa dấu nháy đơn và dấu nháy kép

    ADO.RecordSource = RecordSourceStr & ' where [Words]= ' ' & sWord & ' ' '

    ADO.Refresh

    FindWord = IIf(DG.Columns(0).Text <> vbNullString, True, False)

    Exit Function

    errTrap:

    Select Case Err.Number

       Case 6160 'lỗi không tìm thấy

          FindWord = False 'không tìm thấy

    End Select

    End Function

    Phần FRMPOPUP

    Chọn BorderStyle cho form=0-None, Backcolor=Tooltip, Width=2000, Height=3075. Thêm một Shape Control (mặc định là hình chữ nhật, Top=0, Left=0, Width=2000, Height=3075) làm đường biên cho form, một CommandButton (Name:cmdClose) để tạm thời giấu cửa sổ frmPopup, và một vbalRichEditControl (Name:rtfMain, BackColor:vbWhite, Border=True).

    Chú ý, trước khi sử dụng OCX vbalRichEditControl (có cung cấp kèm theo mã nguồn hoặc có thể dùng RichTextBox), bạn phải chép nó vào thư mục Windows/System32, nhấn <Start>-Run-cmd, từ dấu nhắc DOS di chuyển đến thư mục System32, gõ 'regsvr32 RichEditControl.ocx' nhấn Enter. Đồng thời cũng đăng kí thêm thư viện SSubtmr6.dll (có cung cấp kèm theo mã nguồn). vbalRichEditControl là một RichTextBox cấp cao, tôi đã tích hợp thêm TOM (Text Object Model) và các tính năng khác nên rất hữu ích. Vào hộp thoại Add Components (Ctrl+T) chọn và thêm lên form Microsoft Direct Text-to-Speech (file xvoice.dll, Name=TTS ) để thêm chức năng phát âm cho chương trình. Thêm một Command Button (Name:cmdSpk) để khi nhấn vào đó sẽ phát âm.

    Private Sub cmdSpeak_Click()

      TTS.Speak str4Speak 'str4Speak là một biến kiểu chuỗi toàn cục khai báo trong module để lưu giữ chuỗi cần phát âm

    End sub

    Có những từ khác nhau nhưng nghĩa giống nhau, ta có thể tham khảo chéo bằng cách nhấn đúp vào từ đó trong rtfMain.

    Private Sub rtfMain_MouseUp(X As Single, Y As Single, Shift As Integer)

    If rtfMain.SelectedText <> vbNullString Then'tìm từ được chọn và hiển thị nghĩa

    mHook.FindWord rtfMain.SelectedText 'sub FindWord trong module mHook

    End If

    End Sub

    Câu móc chương trình

    Để câu móc vào các sự kiện của hệ thống, bạn phải sử dụng 3 API CallNextHookEx, SetWindowsHookEx,UnhookWindowsHookEx (cẩn thận khi làm việc với hook, mã nguồn của bạn phải chính xác, nếu không lúc debug chỉ cần một lỗi nhỏ là VB đóng lại ngay).

    Phần quan trọng nhất của chương trình, hàm 'bắt' chuột:

    Public Function MouseProc(ByVal nCode As Integer, ByVal wParam As Long, ByVal lParam As Long) As Long

    On Error Goto errTrap

    If nCode < 0 Then

    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)

    Else

    If (GetKeyState(VK_CONTROL) And &HF000000) And wParam = WM_LBUTTONDOWN Then

    SendKeys '^c'

    SetPos frmPopup

    strDisplay = Trim(Clipboard.GetText)

    FindWord strDisplay

    End If

    MouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)

    End If

    Exit Function

    errTrap:

      Select Case Err.Number

       Case 521 'lỗi Can't open clipboard

       DoEvents 'chờ 1 chút

    End Select

    Nếu giá trị nCode<0 (ta sử dụng WH_MOUSE_LL) thì ta sẽ gọi tiếp hàm CallNextHookEx (hàm này rất quan trọng, nếu không có chương trình sẽ crash), ngược lại ta sẽ kiểm tra xem người dùng có nhấn Ctrl+Left_Mouse không? ('phím nóng' khi tra từ, bạn có thể sử dụng các phím nóng khác). Nếu phải, thì sao chụp vùng text đang chọn và định vị trí cho frmPopup hiện ra để hiển thị thông tin về từ bằng hàm SetPos:

    Private Sub SetPos(ByRef frm As frmPopup)

    Dim SI As SysInfo

    Set SI = frmMain.SI

    'sử dụng SysInfo để xác định vùng làm việc vì vùng làm việc không phải lúc nào cũng đầy màn hình.

    'Chẳng hạn như bạn kéo thanh taskbar lên cao hơn thì vùng làm việc đã thay đổi.

    GetCursorPos pnt 'lấy vị trí con trỏ hiện tại

    frm.Top = IIf(pnt.Y * 15 + frm.Height >= SI.WorkAreaHeight, SI.WorkAreaHeight - frm.Height, pnt.Y * 15)

    frm.Left = IIf(pnt.X * 15 + frm.Width >= SI.WorkAreaWidth, SI.WorkAreaWidth - frm.Width, pnt.X * 15)
    frm.Show

    BringWindowToTop frm.hWnd 'cho ở trên các cửa sổ khác

    End Sub

    Biến strDisplay sẽ lấy nội dung từ đã được đưa vào bộ nhớ và kiểm tra xem từ có trong CSDL hay không, nếu có thì đưa vào rtfMain của frmPopup bằng Sub AddWord:

    Private Sub AddWord(ByRef rtfText As vbalRichEdit, ByVal sDisplay As String, _

    ByVal sPro As String, ByVal sN As String, ByVal sV As String, _

    ByVal sAdj As String, ByVal sPrep As String, ByVal sAdv As String, _

    ByVal sOther As String)

    'các đối số sDisplay, sPro, sV,... để chỉ nội dung các trường

    Dim cStrW As String

    Static nCount As Long

    a = Array('{danh từ}', '{động từ}', '{tính từ}', '{giới từ}', '{trạng từ}', '{thể loại khác}') 'tên tiếng Việt khi thêm vào rtfText ứng với 6 trường cuối

    sA = Array(sN, sV, sAdj, sPrep, sAdv, sOther)

    ResetRTF rtfText 'sub ResetRTF thiết lập các thuộc tính mặc định cho rtfText

    rtfText.SelText = sDisplay & ' ' & sPro & Chr(13) 'Hiển thị từ và cách phát âm

    'sub HighLight để tô màu cho các từ

    HighLight rtfText, sDisplay, vbBlue, True 'từ tô màu xanh và đậm

    HighLight rtfText, sPro, RGB(125, 0, 0) 'cách phát âm tô màu nâu

    'vòng lặp để kiểm tra trường

    For i = 0 To 5

    If Len(sA(i)) <> 0 Then

    'nếu trường khác rỗng

    rtfText.SelText = a(i) & Chr(13) 'thêm tên trường tương ứng tiếng Việt và kí tự xuống dòng

    ExtractWord rtfText, sA(i) 'trích nghĩa của từ trong trường

    HighLight rtfText, a(i),vbRed

    'tô màu đỏ cho tên tiếng Việt của trường

    End If

    Next

    rtfText.SelStart = 0 'đưa con trỏ về đầu

    rtfText.ReadOnly = True 'không cho phép hiệu chỉnh rtfMain

    End Sub

    Cách hiển thị từ và nghĩa tôi dựa vào tự điển của English Study 4. Tôi xin nói qua về cách tổ chức từ điển chứa từ. CSDL gồm 9 trường, trong đó có 3 trường cần có đầy đủ thông tin là Words, Display, Pronunciation; 6 trường còn lại có thể có hoặc không. Trong mỗi trường, nếu từ có nhiều nghĩa thì các nghĩa cách nhau bởi dấu '/', trước câu ví dụ phải có dấu '*'. Chương trình ở đây không đủ thông minh để nhận ra từ loại nên phải dùng vòng lặp kiểm tra trong 6 trường cuối, nếu trường rỗng thì bỏ qua, ngược lại thì thêm tên của trường (theo tiếng Việt tương ứng) và trích nội dung của trường vào rtfMain. Ví dụ như chữ a, chỉ có trường Noun= 'Chữ cái đầu tiên/ một, chỉ một/nốt thứ sáu trong gam đô trưởng, nốt la*A sharp: la thăng*A flat:la giáng' thì khi gọi AddWord sẽ thêm vào rtfMain:

    A [ei]

    {danh từ}

    1.Chữ cái đầu tiên

    2.một, chỉ một

    3.nốt thứ sáu trong gam đô trưởng, nốt la

    vd:

    A sharp:la thăng

    A flat: la giáng

    Sub sau sẽ đảm nhiệm việc trích từ:

    Private Sub ExtractWord(ByRef rtfText As vbalRichEdit, ByVal sStr As String)

    Dim hOneMean As Boolean

    Dim SymbolDivPos As Long

    Dim SymbolStarPos As Long

    Dim n As Long, m As Long, i As Integer

    Dim sArray(0 To 100) As String

    Dim sField As String, sChar As String, sExample As String

    hOneMean = IIf(InStr(sStr, '/') = 0, True, False) 'từ có một nghĩa?

    sField = IIf(Right(sStr, 1) <> '/', sStr & '/', sStr) 'chuỗi trong mỗi trường, thêm '/' vào cuối chuỗi

    'để CnS không bỏ qua nghĩa cuối

    While (InStr(sField, '/') <> 0)

    n = n + 1

    'tăng số hiển thị trước mỗi nghĩa

    m = -1

    SymbolDivPos = InStr(sField, '/') 'vị trí của '/' trong sFiled

    sChar = Left(sField, SymbolDivPos - 1) ' mỗi nghĩa trong sFiled

    If InStr(sChar, '*') <> 0 Then 'nghĩa này có ví dụ?

    sExample = Mid(sChar, InStr(sChar, '*') + 1) & '*' 'chuỗi ví dụ

    While (InStr(sExample, '*') <> 0)

    SymbolStarPos = InStr(sExample, '*') 'vị trí của '*'

    m = m + 1 'số ví dụ

    sArray(m) = Left(sExample, SymbolStarPos - 1) 'trích mỗi ví dụ vào mảng

    sExample = Mid(sExample, SymbolStarPos + 1) ' cắt bỏ ví dụ đã trích

    Wend

    sChar = Left(sChar, InStr(sChar, '*') - 1) 'cắt bỏ chuỗi ví dụ

    End If

    rtfText.SelText = Space(2) & IIf(hOneMean, vbNullString, n & '.') _
    & sChar & Chr(13) 'ghi mỗi nghĩa vào rtfText

    HighLight rtfText, sChar, RGB(0, 125, 0) 'và tô màu

    If m <> -1 Then 'mỗi nghĩa có ví dụ?

    rtfText.SelText = Space(5) & 'Ví dụ:' & Chr(13)

    HighLight rtfText, 'Ví dụ:', RGB(217, 0, 217) 'tô màu hồng

    For i = 0 To m

    rtfText.SelText = Space(7) & sArray(i) & Chr(13) 'ghi ví dụ vào rtfText

    HighLight rtfText, sArray(i), RGB(125, 0, 0) 'và tô màu nâu

    Next

    End If

    sField = Mid(sField, SymbolDivPos + 1) 'không để vòng lặp vô tận
    Wend

    End Sub

    Đầu tiên, ta kiểm tra từ có một nghĩa hay nhiều nghĩa. Gán biến cField=sStr(nội dung của trường) và thêm vào bên phải kí tự '/' nếu chưa có. Sau đó sử dụng 2 vòng lặp While để trích từng nghĩa và câu ví dụ.

    Private Sub HighLight(ByRef rtfText As vbalRichEdit, ByVal sStr As String, _

    ByVal oColor As Long, Optional bBold As Boolean = False)

    rtfText.FindText sStr, , , , lStart, lEnd

    rtfText.TextDocument.Range(lStart, lEnd).Font.ForeColor = oColor

    rtfText.TextDocument.Range(lStart, lEnd).Font.Bold = bBold

    End Sub

    Xin nói thêm về cách dùng vbalRichEditControl và TOM, OCX này lấy từ trang vbaccelerator.com (đúng như tên của nó, bạn sẽ tìm được những thứ rất hữu ích về VB trên trang web này). Phương thức FindText của rtfMain tìm chuỗi sStr và lưu vị trí bắt đầu của chuỗi vào biến lStart, vị trí kết thúc vào biến lEnd. Thuộc tính TextDocument chỉ TOM, Range(lStart,lEnd) chỉ vùng được chọn từ vị trí lStart đến lEnd, Font xác lập các giá trị của Font cho vùng được chọn như Shadow, Emboss, Animation...

    Trở lại với hàm 'bắt' chuột của ta, nếu từ không tìm thấy thì hiển thị thông báo trong rtfMain của frmPopup:

    Private Sub DisplayNoSug(rtfText As vbalRichEdit, ByVal sStr As String)

      ResetRTF rtfText

      rtfText.SelText = sStr & Chr(13) 'hiển thị từ không có nghĩa này

      HighLight rtfText,sStr,vbBlue,True 'tô màu và đậm

      rtfText.SelText = ' Không tìm thấy từ này' & Chr(13) & ' trong từ điển' 'câu thông báo

      HighLight rtfText, ' Không tìm thấy từ này' & Chr(13) & ' trong tự điển',vbRed 'tô đỏ câu thông báo

      rtfText.ReadOnly = True 'không cho phép hiệu chỉnh rtfMain

      str4Speak = '' 'không thể phát âm từ này

    End Sub

                      Và thêm 2 hàm không thể thiếu:

    Private Sub ResetRTF(ByRef rtfText As vbalRichEdit)

      rtfText.ReadOnly = False

      rtfText.Text = ''

      rtfText.SelectAll

      rtfText.FontBold = False

      rtfText.FontColour = vbBlack
    End Sub

    Public Sub FindWord(ByVal sWordFind As String)

    Dim sFind As String

     sFind = Trim(sWordFind)

       With frmMain

          If .FindWord(sFind) Then 'nếu từ có trong cơ sở dữ liệu

                  AddWord frmPopup.rtfMain, .DG.Columns(1).Text, .DG.Columns(2).Text, 
    Columns(3).Text, .DG.Columns(4).Text, .DG.Columns(5).Text, .DG.Columns(6).Text, .DG.Columns(7).Text, .DG.Columns(8).Text
    str4Speak = strDisplay 'chuỗi phát âm

         Else

               DisplayNoSug frmPopup.rtfMain, sFind 'không tìm thấy từ

          End If

       End With

    End Sub
                        Hàm chính khởi động chương trình:

    Sub Main()

    If FindWindow(vbNullString, 'Click and See') = 0 Then'nếu chương trình chưa có ở khay hệ thống

    Load frmMain 'thì khởi động frmMain

    End If

    End Sub

    Tới đây chương trình của bạn đã hoàn tất. Việc còn lại hơi nặng nhọc là nhập từ vào CSDL. Bạn có thể dùng định dạng từ điển đã nêu trong TGVT A tháng 9/2004; tuy nhiên theo tôi, định dạng này hơi phức tạp vì phải xác định vị trí offset và chiều dài các nghĩa mỗi lần thêm từ mới.

     

    NÓI THÊM VỀ ĐIỀU KHIỂN MÁY TÍNH TỪ XA

     

     

    Sau khi lắp đặt thành công bộ điều khiển từ xa theo bài viết trên TGVT - PCWVN A tháng 11/2002 của tác giả Nguyễn Việt Hà, tôi nảy ra ý tưởng sau: Khi sử dụng điều khiển từ xa của tivi, lúc nhấn nút trên bộ điều khiển thì một đèn led ở tivi phát sáng và tôi muốn thực hiện tương tự với bộ điều khiển máy tính.
    Để thực hiện được chức năng này tôi tách một phần tín hiệu từ chân OUT của mắt nhận hồng ngoại, rồi đưa tín hiệu đó qua một transitor khuếch đại tín hiệu, dùng tín hiệu này để đóng/ngắt một transitor khác có chức năng như một công tắc đóng/mở đèn led.
    Ở đây tôi sử dụng hai đèn led: LED2 mầu xanh có chức năng báo hệ thống đang hoạt động (led này luôn sáng), LED1 mầu vàng có chức năng nháy sáng khi có tín hiệu từ bộ điều khiển từ xa và mắt nhận hồng ngoại nhận được.
    Linh kiện
    Chúng ta sử dụng các linh kiện thông dụng như transitor, điện trở, led (diot quang).
    Transitor: T1, T2 loại C828
    Điện trở: R1 3K3, R2 10K
    Led: LED1 vàng, LED2 xanh
    Chú ý
    Khi lắp mạch, các bạn cần chú ý xác định đúng các chân của transitor và led. Về nguồn, các bạn có thể sử dụng nguồn bên trong máy tính, ở đây tôi lấy nguồn từ dây đỏ (+5V) và dây đen (0V) của jack cấp nguồn cho ổ cứng (jack này gồm 4 dây: dây đỏ +5V, 2 dây đen 0V [GND], dây vàng +12V).ÿ
    Mai Văn Hoàn
    Email: maihoan2004@yahoo.com

     

     

     

    Lưu ý, trong mã nguồn có một số chi tiết không có trong bài viết (phục vụ cho hiệu quả giao diện chương trình) và vị trí thư mục của các control có thể khác trên máy bạn nên khi chạy VB6 có thể thông báo không tìm thấy, bạn mở file *.vbp bằng NotePad và sửa địa chỉ hoặc nhấn 'Yes' khi hỏi có tiếp tục hay không khi các control không tìm thấy. Lúc này các control sẽ thay thế bằng PictureBox, bạn xóa các PictureBox này và thay thế bằng các control tương ứng.

    Dịch chương trình ra file *.exe và cho chạy nền, vào tài liệu bất kì như pdf chẳng hạn, nhấn đúp vào từ không biết, nhấn Ctrl+Left_Mouse và chiêm ngưỡng thành quả của mình. Chúc bạn thành công!ÿ

    Nguyễn Quốc Việt
    Bamby084@hotmail.com
     
     

    ID: A0410_125
    File đính kèm