[Tool Ex Test]Thực thi và lấy kết quả MS-DOS từ VBA

tuhocvba

Administrator
Thành viên BQT
MS-DOS là môi trường hoàn toàn khác VBA. Tuy nhiên có những việc chúng ta không thể làm bằng VBA thì MS-DOS lại làm được.
Tôi ví dụ, công ty bạn có 2000 nhân viên. Bạn cần tạo ra 2000 folder, mỗi folder ứng với 1 nhân viên, thiết định quyền full control cho nhân viên đó.
Trong trường hợp này, ta sẽ kết hợp VBA và MS-DOS.
VBA tạo ra được 2000 folder, sau đó VBA gọi MS-DOS để chạy thiết định quyền full control.
Trên đây là ví dụ tôi đã làm trong thực tế. Bây giờ chúng ta vào nội dung bài dịch.

Liên qua tới chủ đề này, chúng ta có hai vấn đề cần quan tâm. Một là, làm thế nào để khởi động được MS-DOS từ VBA. Chúng ta không thể dùng Shell được.
Vấn đề thứ hai là, làm thế nào để lấy được kết quả đang hiển thị trên MS-DOS.

Hai vấn đề trên sẽ được giải quyết bằng WSH(Windows Scripting Host) . Phương thức Exec của WshScriptExec Object của WSH có thể khởi động được MS-DOS.
Hơn thế nữa, nó còn giúp ta lấy được kết quả hiển thị trên màn hình DOS.
Đoạn code dưới đây sẽ hiển thị toàn bộ file có trong ổ C:
Mã:
Sub Sample1()
    Dim WSH, wExec, sCmd As String, Result As String
    Set WSH = CreateObject("WScript.Shell")         ''(1)
    sCmd = "dir C:\"                                ''(2)
    Set wExec = WSH.Exec("%ComSpec% /c " & sCmd)    ''(3)
    Do While wExec.Status = 0                       ''(4)
        DoEvents
    Loop
    Result = wExec.StdOut.ReadAll                   ''(5)
    MsgBox Result
    Set wExec = Nothing
    Set WSH = Nothing
End Sub
Bạn cần đăng nhập để thấy đính kèm


(Còn nữa)
 

giaiphapvba

Administrator
Thành viên BQT
(1): Tạo tham chiếu tới WSH.
(2): Dòng lệnh thực thi trong MS-DOS.
(3):Thực thi phương thức Exec. Do lệnh Shell khác nhau giữa hệ thống 9x và hệ thống 2000, vì vậy bằng cách viết %ComSpec%, nó sẽ được xác định tự động. Khi thực thi nó sẽ trả về một đối tượng WshScriptExec Object, do đó nó sẽ lưu trữ vào một biến.
Việc thực thi lệnh MS-DOS có thể sẽ mất thời gian, chúng ta phải chờ cho tới khi MS-DOS hoàn thành xong. Chú ý MS-DOS chạy độc lập với VBA, cũng tương tự như khi chúng ta dùng VBA thao tác với web, web cũng hoạt động độc lập với VBA. Vì vậy chúng ta cần vòng lặp (4) mục đích là chờ cho tới khi nào MS-DOS xử lý xong.
Để đoán biết khi nào MS-DOS hoàn thành, nó sẽ dựa vào thuộc tính Status của WshScriptExec Object.
Để lấy kết quả hiển thị trên MS-DOS, ta sẽ dùng thuộc tính StdOut. Ta lưu toàn bộ thông tin thuộc tính này vào trong biến Result. Sau cùng chúng ta giải phóng biến đối tượng (Object).
Kết quả này hoàn toàn giống với kết quả hiển thị trên MS-DOS.
Bạn cần đăng nhập để thấy đính kèm


(Còn nữa)
Nguồn:
 

tuhocvba

Administrator
Thành viên BQT
Hôm qua có việc cần dùng tới cái này, nhưng test thử thấy vô tác dụng.
Cụ thể mình muốn chạy hai dòng lệnh sau:
Mã:
cd Đường_dẫn_thư_mục_hiện_hành
pdflatex tên_file.tex
Mục đích là để biên dịch file Latex thành file PDF. Nhưng đều thất bại. Từ đó mình phải đi đường vòng, đó là tạo ra một file .CMD. File này chứa nội dung như trên. Rồi cho macro dùng shell để chạy file .cmd này. Qua đây chia sẻ với anh em cách thực thi file .CMD
Nhưng cách này thì mình chưa kiểm tra được khi nào CMD chạy xong. Bởi vì DOS và VBA lúc này là độc lập.

1. Anh chị em thử chạy code #1 xem được không rồi báo lại mình biết với.
2. Có cách nào biết được khi nào DOS chạy xong thì anh chị em giới thiệu trong topic này luôn nhé.
 
Anh chia sẻ code đi.
Em tìm cũng chưa thấy bài nào cho thấy họ biết khi nào command line kết thúc để cho VBA thực thi các lệnh tiếp theo sau đó.
 
D

Deleted member 1294

Guest
Mình nghĩ thế này để biết khi nào command line chạy xong ta có thể cho command line chạy một lệnh đơn giản như xuất file text nào đó và nhiệm vụ VBA xác định file text đó có tồn tại không ! Nếu tồn tại => chạy xong và ngược lại .
 

tuhocvba

Administrator
Thành viên BQT
Mình nghĩ thế này để biết khi nào command line chạy xong ta có thể cho command line chạy một lệnh đơn giản như xuất file text nào đó và nhiệm vụ VBA xác định file text đó có tồn tại không ! Nếu tồn tại => chạy xong và ngược lại .
Ý tưởng hay nhỉ, để mình thử xem.
Cho mình hỏi chút:
MSDOS thực thi lệnh:
-Lệnh 1
-Lệnh 2
-...
-Lệnh tạo file txt

Giả sử bị lỗi ở lệnh 2 thì các lệnh ở dưới có được thực thi không ấy nhỉ?
 
D

Deleted member 1294

Guest
@tuhocvba vì vậy các lệnh trên phải chuẩn rồi nếu lỗi thì bẫy lỗi .
 

tuhocvba

Administrator
Thành viên BQT
@tuhocvba vì vậy các lệnh trên phải chuẩn rồi nếu lỗi thì bẫy lỗi .
Bẫy lỗi nói rõ hơn đi @USA_Covid19 . Kết quả thử nghiệm:
Mã:
cd C:\VBA\
pdflatex a.tex
fsutil file createnew thvba.txt 1
Dòng 1: Chuyển con trỏ về thư mục hiện hành.
Dòng 2: Là lệnh chuyển pdf của Latex, cố tình tạo ra lỗi
Dòng 3: Là lệnh tạo file txt
Kết quả là dòng 2 bị lỗi nên lệnh dòng 3 không thực hiện được.

Ý tưởng là dùng vòng lặp để kiểm tra file thvba.txt có tồn tại hay không, ví dụ kiểm tra trong 20s. Cái không thích trong ý tưởng này là con số 20s. Nói cách khác, là vẫn chưa biết được chính xác thời điểm chạy MSDOS kết thúc.
 

tuhocvba

Administrator
Thành viên BQT
Anh chia sẻ code đi.
Mã:
Dim N As Long
Open lk & "\Makex.cmd" For Output As #N
Print #N, "rem ********** Bat dau chay chuong trinh **********" 'Comment'
Print #N, "Nội dung lệnh 1"
Print #N, "Nội dung lệnh 2"
Print #N, "rem ********** Ket thuc **********"
Close #N

Shell str_Path & "\Makex.cmd", vbNormalFocus
Application.Wait (Now + TimeValue("0:0:20"))
 
Đoạn code này em tìm được:
Cái hay là: Nó sẽ đợi tới khi DOS chạy xong, hoặc là nếu có lỗi, người dùng tắt DOS đi, thì nó mới chạy tiếp.
Mã:
Sub test()
    Dim wsh As Object
    Set wsh = VBA.CreateObject("WScript.Shell")
    Dim waitOnReturn As Boolean: waitOnReturn = True
    Dim windowStyle As Integer: windowStyle = 1
    Dim errorCode As Integer
    wsh.Run "D:\VBA\1.bat", windowStyle, waitOnReturn
    MsgBox "OK"
End Sub
Nguồn:
 
B

bvtvba

Guest
Đoạn code này em tìm được:
Cái hay là: Nó sẽ đợi tới khi DOS chạy xong, hoặc là nếu có lỗi, người dùng tắt DOS đi, thì nó mới chạy tiếp.
Mã:
Sub test()
    Dim wsh As Object
    Set wsh = VBA.CreateObject("WScript.Shell")
    Dim waitOnReturn As Boolean: waitOnReturn = True
    Dim windowStyle As Integer: windowStyle = 1
    Dim errorCode As Integer
    wsh.Run "D:\VBA\1.bat", windowStyle, waitOnReturn
    MsgBox "OK"
End Sub
Nguồn:
Hay quá. Mình thử tạo file .bat bằng thì thất bại với code trên. Không thể thực thi Run. Nhưng vào thư mục file .bat, tự click bằng tay vào file này thì thực thi được.
Vậy cần làm rõ ràng đoạn code tạo ra file .bat mới được. Sau một hồi tìm kiếm thì mình tìm được ở .
Do đó code mẫu mình đề xuất để tạo file bat như sau:
Mã:
Sub taofilebat(ByVal lk2 As String, ByVal tenfile As String)
    Dim objFSO, objFile
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    lk = ThisWorkbook.Path & Application.PathSeparator & "thvba.bat"
    Set objFile = objFSO.CreateTextFile(lk2)    'Output Path

    Dim OutputString: OutputString = ""
    
    OutputString = Lệnh 1
    OutputString = OutputString & vbNewLine & Lệnh 2
    

    objFile.Write (OutputString)

    Set objFile = Nothing
    Set objFSO = Nothing

End Sub
Quá trình test code hoàn hảo!
 

tuhocvba

Administrator
Thành viên BQT
Đoạn code này em tìm được:
Cái hay là: Nó sẽ đợi tới khi DOS chạy xong, hoặc là nếu có lỗi, người dùng tắt DOS đi, thì nó mới chạy tiếp.
Mã:
Sub test()
    Dim wsh As Object
    Set wsh = VBA.CreateObject("WScript.Shell")
    Dim waitOnReturn As Boolean: waitOnReturn = True
    Dim windowStyle As Integer: windowStyle = 1
    Dim errorCode As Integer
    wsh.Run "D:\VBA\1.bat", windowStyle, waitOnReturn
    MsgBox "OK"
End Sub
Nguồn:
Hay quá, nhưng cái link tham khảo trên phải tham khảo cho hết, để lấy đúng đoạn code cần lấy. Nếu không, khi có lỗi xảy ra ở DOS, như không biên tập được file, là chờ vô hạn luôn, treo máy. Do đó code dưới đây sẽ tối ưu hơn (vẫn lấy từ nguồn link trên).
Mã:
   Dim wsh As Object
   Set wsh = VBA.CreateObject("WScript.Shell")
   Dim waitOnReturn As Boolean: waitOnReturn = True
   Dim windowStyle As Integer: windowStyle = 1
   Dim errorCode As Integer

   errorCode = wsh.Run(link_file_bat, windowStyle, waitOnReturn)

If errorCode = 0 Then
    'Insert your code here
Else
    MsgBox "Program exited with error code " & errorCode & "."
End If
 

tuhocvba

Administrator
Thành viên BQT
Cái vụ DOS này xong rồi, nhân đây chia sẻ với các bạn một vấn đề tương tự.
Quá trình công việc:
Tạo file-> mở file
Tuy nhiên việc tạo file này độc lập với VBA cho nên đoạn code mở file sẽ bị lỗi.
Nếu như chắc chắn việc tạo ra file là không có thất bại, các bạn có thể làm như sau để duy trì trạng thái mở file liên tục cho tới khi nào thành công thì thôi:
Mã:
Sub ab()
    Dim lk2 As String
    lk2 = "C:\VBA\111.c"
    Call Openfile(lk2)
End Sub


Sub Openfile(lk2 As String)
    Dim bl  As Boolean
    
    On Error Resume Next
    
    While (bl = False)
        Err.Clear
       With CreateObject("Wscript.Shell")
        .Run lk2, 5
              
        End With
        If Err.Number = 0 Then
            bl = True
        End If
    Wend
    
    On Error GoTo 0
End Sub
 
Top