「Excel / 使用VBA執行ffmpeg合併圖檔與音檔成為影音檔」的第4篇
說明如何調用Windows API ,讓VBA內建的shell也可以等待程式碼執行
程式碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
Private Const SYNCHRONIZE = &H100000 Private Const INFINITE = &HFFFFFFFF #If Win64 Then Private Declare PtrSafe Function OpenProcess Lib "kernel32" _ (ByVal dwDesiredAccess As LongPtr, ByVal bInheritHandle As LongPtr, ByVal dwProcessId As LongPtr) As LongPtr Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As LongPtr Private Declare PtrSafe Function WaitForSingleObject Lib "kernel32" _ (ByVal hHandle As LongPtr, ByVal dwMilliseconds As LongPtr) As LongPtr #Else Private Declare Function OpenProcess Lib "kernel32" _ (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long Private Declare Function WaitForSingleObject Lib "kernel32" _ (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long #End If ' 修改 Public Sub creatVideo2() r = Sheets(1).Range("B1").End(xlDown).Row If r = 1048576 Then Exit Sub End If For i = 2 To r If Sheets(1).Range("A" & i).Value <> "◎" Then If Sheets(1).Range("B" & i).Value <> "" And Sheets(1).Range("C" & i).Value <> "" Then On Error GoTo CleanUp Application.DisplayAlerts = False Application.ScreenUpdating = False Dim windowStyle As Integer: windowStyle = 3 Dim wavName As String, mp4Name As String, imgPath As String Dim n As Long, s As String Dim pId As LongPtr, pHnd As LongPtr wavName = Sheets(1).Range("C" & i).Value n = InStr(1, wavName, ".", vbTextCompare) mp4Name = Mid(wavName, 1, n - 1) & ".mp4" imgPath = Sheets(1).Range("B" & i).Value Debug.Print mp4Name ' 如果用環境參數在WScript.Shell會無法執行 ' ffmpegFile = "C:\Users\trico\Desktop\ffmpeg\bin\ffmpeg.exe" ffmpegFile = "C:\Users\edu\Desktop\yt-dlp\ffmpeg\bin\ffmpeg.exe" ' 建立 ffmpeg 指令 s = ffmpegFile & " -framerate 1 -i " & imgPath & " -i " & Chr(34) & wavName & Chr(34) & " -f mp4 -c:v libx264 -pix_fmt yuv420p " & Chr(34) & mp4Name & Chr(34) Debug.Print s ' 執行並等待 ffmpeg 完成 pId = Shell(s, windowStyle) pHnd = OpenProcess(SYNCHRONIZE, 0, pId) If pHnd <> 0 Then WaitForSingleObject pHnd, INFINITE CloseHandle pHnd Debug.Print "輸出:" & mp4Name Sheets(1).Range("A" & i).Value = "◎" Sheets(1).Range("D" & i).Value = mp4Name End If End If End If Next CleanUp: Application.DisplayAlerts = True Application.ScreenUpdating = True End Sub |
#1-2
定義16位元的常數SYNCHRONIZE用於OpenProcess()、INFINITE用於WaitForSingleObject()
#4-16
調用kernel32的3個函式(Function) OpenProcess、CloseHandle、WaitForSingleObje
#59-69
利用shell的回傳值,如果執行成功會有工作識別碼(Process Id)
再利用windows API來偵測這個工作識別碼所代表的程式的執行狀態
呼叫 OpenProcess API 取得 Process Handle(pHnd), 然後再利用 Process Handle 呼叫 WaitForSingleObject, 即可等待被 Shell 執行的程式執行完畢 (第2個參數設定為INFINITE), 才繼續向下執行(關閉當前程序)
詳細說明可以參考這篇文章「Shell & Wait 的程式怎麼寫?」
函式的參數值可以參考這篇文章「【VB6|第27期】如何在VB6中使用Shell函数实现同步执行」