Cập nhật dữ liệu Module, File từ xa qua Internet

Ngày Mới

Thành viên tích cực
1. Đặt vấn đề
Mỗi khi chúng ta viết một chương trình hay một Tool cho người khác sử dụng. Mỗi khi có vấn đề, ta thường phải viết lại Code trong Module của Tool hay chương trình đó và gởi cho người sử dụng. Nghĩa là, nếu người sử dụng không có kiến thức về VBA thì họ buộc phải xóa chương trình cũ, tải lại chương trình mới của chúng ta. Thật là phiền toái.

Hôm nay, Tôi sẽ giới thiệu có các bạn một cách giúp chúng ta có thể cập nhật được chương trình thông qua Internet. Nó sẽ giúp các bạn có thể cập nhật File, Module từ xa mà người dùng không cần phải có thao tác nào trong VBA

2. Nội dung
2.1. Nguyên tắc cốt lõi

Chương trình sẽ dùng đường Links bạn gắn sẵn để Download một tiệp tin về và đọc kiểm tra tiệp tin đó để xác định 2 điều:
- Phiên bản hiện tại có phải phiên bản mới nhất chưa?
- Download chương trình mới, Module mới... nếu chưa phải phiên bản mới.

2.2. Cách tiến hành
2.2.1. Các Function

Chúng ta sẽ dùng 2 hàm API URLDownloadToFile InternetGetConnectedState
- Hàm URLDownloadToFile: Dùng để download file vào đường dẫn cho trước
- Hàm InternetGetConnectedState: Kiểm tra kết nối Internet ( )
Mã:
Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" (ByVal pCaller As LongPtr, ByVal szURL As String, ByVal szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As LongPtr) As LongPtr
Private Declare PtrSafe Function InternetGetConnectedState Lib "wininet.dll" (lpdwFlags As LongPtr, ByVal dwReserved As Long) As Boolean
'//DOWNLOAD FILE TU DUONG DAN CHO TRUOC
Public Function DownloadFile(URL As String, SavePath As String) As Boolean
    #If VBA7 Then
        Dim myResult As LongPtr
    #Else
        Dim myResult As Long
    #End If

    myResult = URLDownloadToFile(0, URL, SavePath, 0, 0)
    DownloadFile = (myResult = 0)
End Function

'//KIEM TRA KET NOI INTERNET
Public Function GetInternetConnectedState() As Boolean
  GetInternetConnectedState = InternetGetConnectedState(0&, 0&)
End Function
2.2.2. Các thủ tục
Set 2 biến là URL và Path của file cần tải về. Các bạn cần lưu ý, URL phải là dạng Direct link. Để có được link dạng này, các bạn làm theo hướng dẫn bên dưới

Ta sẽ xét hướng dẫn đối với Google Drive:
Mở file cần lấy Link dưới dạng đường dẫn > Mở bằng > Direct Link Creator > Share All and Get Direct Links
Bạn cần đăng nhập để thấy hình ảnh

Đây là giao diện trong Google Drive

Bạn cần đăng nhập để thấy hình ảnh

Đây là giao diện trong Direct Link Creator

Mã:
'Dây là URL ví dụ, các bạn có thể thay đổi thành URL của mình
LinksDownload = "https://docs.google.com/uc?export=download&id=1ZUKzhxKOixsuyydtW6IillWHMmHHtg8z"
PathDownload = Environ("Temp") & "UpdateAuto.txt"
Kiểm tra, đảm bảo kết nối Internet
Mã:
If GetInternetConnectedState = False Then
    Msgbox "Please connect to the internet", vbOk + vbInformation
    Exit Sub
End If
Xóa file đã tải về tránh bị lỗi trùng lặp
Mã:
If FSO.FileExists(PathDownload) Then FSO.DeleteFile (PathDownload)
Nếu tải file thành công, sẽ mở file lên. Đọc và ghi nhận giá trị của 3 dòng đầu tiên vào 3 biến xác định(strVersion, strNameModule, strLinkModule)
- strVersion: Số phiên bản
- strNameModule: Tên File, Module cần tải về
- strLinkModule: URL File, Module cần tải về
Kiểm tra, so sánh với số phiên bản cũ. Tôi đang mặc định để số phiên bản cũ nằm ở ô A1 tại Sheet1
Nếu phát hiện phiên bản mới hơn, Tiến hành thông báo và tải File, Module mới về. Bên dưới code, tôi đang giả sử là nội dung cần Update là cập nhật dữ liệu mới trong Module nên sẽ cần thêm thao tác xóa, nạp Module mới (đoạn code số 48).
Mã:
If DownloadFile(LinksDownload, PathDownload) Then

    '//MO FILE UPTEDATE
    i = 1
    Open PathDownload For Input As #1
    While Not EOF(1)
        Line Input #1, TxtRow

        '//NAP CAC GIA TRI DOC DUOC TU TXT VAO BIEN
        If i = 1 Then
            lngInItr = InStr(TxtRow, "Version: ")
            If lngInItr <> 0 Then strVersion = Replace(TxtRow, "Version: ", "")
        ElseIf i = 2 Then
            lngInItr = InStr(TxtRow, "Name Module: ")
            If lngInItr <> 0 Then strNameModule = Replace(TxtRow, "Name Module: ", "")
        ElseIf i = 3 Then
            lngInItr = InStr(TxtRow, "Links: ")
            If lngInItr <> 0 Then strLinkModule = Replace(TxtRow, "Links: ", "")
        End If

    i = i + 1
    Wend
    Close #1

    With Sheets("Sheet1").Range("A1")

        '//KIEM TRA VERSION
        If .Value >= CDbl(strVersion) Then
            MsgBox "You are using the latest version: " & .Value
            Exit Sub
        Else
 
            '//NEU LA PHIEN BAN CU THI UPDATE
            If MsgBox("Last version: " & .Value & vbNewLine & "New version: " & strVersion & vbNewLine & "Do you want to update to new version?", vbOKCancel + vbQuestion) <> vbOK Then Exit Sub
 
            '//SET DUONG DAN FILE
            PathDownloadMod = Environ("Temp") & strNameModule
 
            '//BAY LOI
            If FSO.FileExists(PathDownloadMod) Then FSO.DeleteFile (PathDownloadMod)  'Neu file *.bas ton tai thi xoa no di
 
            '//BAY LOI
            If DownloadFile(strLinkModule, PathDownloadMod) = False Then
                MsgUni UniVBA("Error download")
                Exit Sub
            Else
                '//UPDATE
                Call Update(PathDownloadMod)
                .Value = CDbl(strVersion)
            End If

        End If
    End With
End If
Bạn cần đăng nhập để thấy hình ảnh

(Đây là giao diện file UpdateAuto.txt)

Tại đoạn code số 48, ta sẽ gọi một thủ mới để nạp Module mới.
Quy trình xóa, nạp Module mới (In nghiên là có hoặc không): Lấy tên Module mới > [Đổi tên Module cũ] > Nạp Module mới > [Xóa Module cũ] > [Chạy lệnh gì đó] > Thông báo
Lệnh chạy gì đó: lệnh này tôi đang mặc định với tên thủ tục là "ActionUpdate", các bạn có thể sửa lại
Mã:
Sub Update(ByRef vPath As Variant)
Dim vPathUpdate, sNameModule, Buf, CacheMod As Variant
Dim strLineTMP As String
Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")

CacheMod = "CacheMode"

'CHON FILE UPDATE
vPathUpdate = vPath

'LAY TEN MODULE FILE .BAS DUOC CHON
Open vPathUpdate For Input As #1

While Not EOF(1)
    Line Input #1, Buf
    strLineTMP = InStr(Buf, "Attribute VB_Name = """)
    If strLineTMP <> 0 Then sNameModule = Right(Replace(Buf, "Attribute VB_Name = """, ""), Len(Replace(Buf, "Attribute VB_Name = """, "")) - 1)
Wend

Close #1

'DOI TEN MODULE CU
On Error Resume Next
ThisWorkbook.VBProject.VBComponents(sNameModule).Name = CacheMod
On Error GoTo 0

'NAP MODLE MOI
Workbooks(ThisWorkbook.Name).VBProject.VBComponents.Import (vPathUpdate)

'XOA MODULE CU
On Error Resume Next
ThisWorkbook.VBProject.VBComponents.Remove ThisWorkbook.VBProject.VBComponents(CacheMod)

'CHAY LENH SETUP
Application.Run "'" & ThisWorkbook.Name & "'!" & "ActionUpdate"
On Error GoTo 0

'THONG BAO HOAN THANH
Msgbox "Completed" & FSO.GetBaseName(vPathUpdate), vbInformation + vbOKOnly

End Sub
File ví dụ các bạn có thể tải về

3. Tổng kết
Những gì các bạn vừa đọc phía trên chỉ là một ví dụ nhỏ trong phương pháp cập nhật dữ liệu từ xa qua Internet. Nguyên tắc cốt lõi vẫn là tải file về từ URL cho trước và lấy dữ liệu từ đó. Theo nguyên tắc này, các bạn chỉ cần đưa file cập nhật lên Google Drive theo các phiên bản, sau đó thông báo cho người dùng nhấn nút cập nhật là xong. Điều quan trọng là bạn phải giữ được URL cố định này trong Google Drive, hãy lưu nó ở một Folder riêng và lưu giữ nó. Cách này có thể tùy biến rất nhiều cách khác nhau tùy theo nhu cầu riêng.

Trên đây là những gì được tôi tổng hợp được trong quá trình công tác công việc thực tế. Chính vì lý do đó nên sơ suất trong Code là khó tránh khỏi, rất mong nhận được những phản hồi và đóng góp từ các bạn!
 
Sửa lần cuối:

Nguyen Kha Nam

Thành viên mới
Chủ đề khá hay nhưng trong thực tế ứng dụng vẫn còn những hạn chế.
Ví dụ đối với công ty mình nhiều máy tính không được kết nối internet do nhu cầu công việc...
Do vậy có thể để các file ..txt,bas.. các file chưa code này ở một nơi nào đó trong mạng lan, ví dụ như máy chủ , hoặc 1 thư mục ở máy nào đó ở chế độ share để từ các máy khác có thể cập nhật được không bạn?
 

Ngày Mới

Thành viên tích cực
Về vấn đề này mình chưa thử, nhưng qua vài đường Google thì mình thấy việc kết nối với các mạng LAN hay Folder Share là hoàn toàn có thể làm được.
Một ví dụ khi Google:
 

Nguyen Kha Nam

Thành viên mới
Vậy nếu có điều kiện mong bạn tiếp tục phát triển chủ đề rộng thêm để có một ứng dụng hữu ích, bạn nhé.
Như là mình muốn:
Sau khi chạy một đoạn code nào đó.
Nó sẽ kiểm tra xem đoạn code này có cần cập nhật hay không thông qua một giá trị nào đó, ví dụ một ô a1 trong sheets"check_code" là 0 thì nó sẽ cập nhật lại đoạn code này với qua tập tin nào đó như: //192.168.252.11/Update_Code.txt
đó chỉ là một mô phỏng đơn giản còn trong thực tế có file đó mà xó nhiều: module/class/userform/sub/function..

Thì sẽ xử lý thế nào.
Hihi mình không đòi hỏi gì chỉ là thấy vấn đề như vậy nên đưa ý kiến thôi mong bạn đừng nghĩ nhiều nhé.
Chúc bạn luôn thành công.
 

vbano1

SMod
Thành viên BQT
Tai sao phải phức tạp nạp với xóa Module làm gì nhỉ.
Bởi vì một chương trình như Excel thì rất nhẹ, thường cỡ 1-2Mb đã là nhiều. Vì vậy chỉ cần đọc thông tin version từ sever và so sánh với version hiện tại, nếu phát hiện phiên bản đã cũ thì thông báo cho người dùng biết đã có phiên bản mới hơn, có thể download tại ...
Bạn cần đăng nhập để thấy đính kèm


Trên sever tôi đặt file txt ghi thông tin phiên bản mới nhất. Như vậy link file txt này là cố định.
Mỗi lần có phiên bản mới thì tôi cập nhật nội dung cho file txt này. Ví dụ nội dung là: ver4.0
Trên sever tôi tạo một folder, link folder này là cố định.
Mỗi lần có phiên bản macro mới thì tôi đặt file macro mới vào folder này.

Như vậy trên máy tính của các bạn, macro sẽ đọc file txt trên sever và so sánh phiên bản xem phiên bản đang dùng có phải là mới nhất không.
Nếu phát hiện phiên bản mới hơn thì đưa ra thông báo, người dùng click vào thông báo thì tự động mở ra đường link folder chứa file macro mới nhất.
Việc download một file excel cỡ vài Mb thì nhanh lắm. Vì vậy làm như trên, tôi nghĩ là đủ rồi.
 

Ngày Mới

Thành viên tích cực
@vbano1 Mỗi người sẽ có các tính chất công việc khác nhau bạn ạ. Trong phạm vi công việc của mình, File Excel của tôi đc phân mảng rất nhiều phòng ban nhập liệu vào đó, việc Download một file excel mới là không khả thi trong trường hợp của tôi.
 

tuhocvba

Administrator
Thành viên BQT
@Ngày Mới : Nên nói rõ cái tính chất công việc của ban. Sơ đồ hoá cho dễ hiểu .Tại sao nhiều phòng ban thì không khả thi ?Tôi đọc vẫn không hiểu lý do thực sư.
 

nhg

Thành viên mới
Mình nghĩ cập nhật module phù hợp hơn vì nếu tải file mới xuống, dữ liệu đã nhập trong file cũ ko còn nữa, có nhiều dữ liệu rất phức tạp, chuyển từ file cũ sang file mới ko hề đơn giản
 

tuhocvba

Administrator
Thành viên BQT
Mình nghĩ cập nhật module phù hợp hơn vì nếu tải file mới xuống, dữ liệu đã nhập trong file cũ ko còn nữa, có nhiều dữ liệu rất phức tạp, chuyển từ file cũ sang file mới ko hề đơn giản
Hoàn toàn không hiểu gì cả.
Khi bạn nâng cấp macro thì phải tạo ra file macro mới dựa trên phiên bản cũ. Ý kiến của vbano1 là trao cho người dùng file macro mới ấy thay vì phải cập nhật từng module nạp vào file cũ trên máy tính. Vậy thì phức tạp ở chỗ nào.
Hãy mô hình hóa bằng sơ đồ câu chuyện của bạn để mọi người hiểu rõ chỗ phức tạp ấy là chỗ nào, phức tạp như thế nào.
 

vbano1

SMod
Thành viên BQT
@nhg : Hãy nêu rõ bằng hình ảnh để mọi người hình dung xem vấn đề phức tạp là gì.
Nếu như bạn mô tả thì có vẻ như file Tool cũng là nơi lưu dữ liệu. Vậy thì phải xem lại thiết kế của bạn. Tại sao dữ liệu và file tool lại cùng một file. Trong trường hợp này tại sao bạn không xây dựng Tool dưới dạng addin? Có khó khăn gì à?

Tóm lại, cách trình bày vấn đề không rõ ràng, sẽ khiến mọi người rất khó hình dung vấn đề thưc sự là gì. Vì vậy mong muốn các bạn hãy sơ đồ hóa câu chuyện của mình, nên sử dụng hình ảnh mô tả để mọi người dễ hình dung.
 
Top