Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Start single process in vba 1

Status
Not open for further replies.

patriciaxxx

Programmer
Jan 30, 2012
277
0
0
GB
I have the following code which loops through one or more files and copys audio mp3 from mp4 files using ffmpeg. The code works but the problem is it opens a new ffmpeg window process for each file it processes and this is not good.

Can any one show me how to do this in just one process ie one ffmpeg window opening up, better still one hidden window?

Dim ffmpegpath As String
ffmpegpath = CurrentProject.path & "\"
Dim wsh As Object
Set wsh = VBA.CreateObject("WScript.Shell")

Dim i As Long
Dim convertedFile As String
For i = 1 To UBound(sourcePaths)
convertedFile = Me.Text0 & "\" & Left(Mid(sourcePaths(i), InStrRev(sourcePaths(i), "\") + 1), InStrRev(Mid(sourcePaths(i), InStrRev(sourcePaths(i), "\") + 1), ".") - 1) & ".mp3"
wsh.Run """" & ffmpegpath & "ffmpeg.exe"" -i """ & sourcePaths(i) & """ -q:a 0 -map a """ & convertedFile & """"
Next i

On the same not but a different approach the following line in a bat file file does process many files but it only works if you click the bat file, i can't make it work from a vba fuction? ie my code above?

for %%a in (*.mp4) do ffmpeg -i "%%a" -q:a 0 -map a "files\%%~na.mp3"
 
No idea why you decided to try and launch ffmpeg through the Windows Scripting Host. VBA has it's own method for running commands, a method which has the exact feature you are looking for - the ability to minimise or hide the window:

Code:
[blue]Dim ffmpegpath As String
ffmpegpath = CurrentProject.Path & "\" [green]' not really necessary when using Shell command - unless, of course, you have previously changed the current working directory for some reason[/green] 

Dim i As Long
Dim convertedFile As String
For i = 1 To UBound(SourcePaths)
    convertedFile = Me.Text0 & "\" & Left(Mid(SourcePaths(i), InStrRev(SourcePaths(i), "\") + 1), InStrRev(Mid(SourcePaths(i), InStrRev(SourcePaths(i), "\") + 1), ".") - 1) & ".mp3"
    Shell """" & ffmpegpath & "ffmpeg.exe"" -i """ & SourcePaths(i) & """ -q:a 0 -map a """ & convertedFile & """", vbHide
Next i[/blue]

Running your bat file is as simple as:

Code:
[blue]    ChDir CurrentProject.Path [green]' again, not strictly necessary[/green]
    Shell "patriciaxxx.bat", vbHide[/blue]

 
Hello strongm,

Thank you for your comments.

I tried the Shell with vbHide and it does do that but the bigger problem which I need solving is it opens an ffmpeg process (and window if I remove the vbHide) for each file I select, so if I select 50 files I have all those processes running which is no good?

Can you help?

The reason I posted the bat line of code was because it does process many files in a single process and I wondered if that was the sort of code I needed in my VBA, ie what would the VBA equivalent be of the bat line of code (with my variables in it) and would that then do the trick). As for running it from VBA I don’t know how to parse my variables to it and it doesn’t seem to want to run from the folder it sits in when called from VBA, it does run when you click on it in that folder though.
 
> so if I select 50 files I have all those processes running which is no good?

ffmpeg is a single-threaded command-line application. This means it can only process one file at a time. The advantage of Shell is that it can launch multiple instances of ffmpeg at pretty much the same time. Of course, since ffmpeg is single-instance this means that you end up with multiple instances running, one for each file you are trying to convert. If you use vbHide the user has no idea that this is the case, though. Which is what I thought you were trying to achieve.. There is no way that you can use VBA to make ffmpeg multinstance.
Oh,m you might say, but the batch file only has one instance. Well, yes - because a) the ffmpeg process gets hidden inside the single commandline process and b) it can only process 1 file at a time (so there is indeed only one instance running).

So, perhaps you could be clearer about what you actually want to achieve
 
I would like to achieve in VBA what the bat file is doing ie process 1 file at a time so there is only one instance running.

If it can’t be done in VBA with Shell and Loops then I could use the bat file if it will launch from the folder the mdb sits in and if it will accept the path variables I currently use ie ffmpeg.exe and files in and out?
 
>so there is only one instance running

there isn't only one instance running. There is one instance of the command line process, which then hosts multiple serial instances of ffmpeg (i.e. one after the other).

Sure you can emulate what the batch file does - but I am trying to understand why multiple serial instances are preferable to invisible multiple concurrent instances (the former will be slower), before even thinking of providing further direction on this.

 
Let me explain it like this:

When I run the VBA I currently have with or without vbHide and selecting 50 files then 50 processes start in the TaskBar and the computer slows down.

When I run the bat file by clicking it in the folder (because I don’t know how to launch it from VBA with my variables, which was one of the things I was asking) then the one process starts and the computer doesn’t slow down.

I just wanted a robust way of processing my files using ffmpeg and VBA, my way seemed to me to be far from that and I thought you might have a better VBA or VBA / bat approach.
 
Excellent - a much better description. Thanks.

But odd. vbHide should NOT show anything on the task bar. That's the whole point. It certainly shows nothing on the task bar on two XP machines and one W7 machine that I have tested it on.

It will slow your machine down, though, yes. So, if you want to run the instances one at a time you need to shell each one and then wait for it to run ... what we might call ShellAndWait. A ccouple of examples of ShellAndWait functions (basically drop in replacements for the VBA Shell function) can be found here: thread222-1393439
 
Hello strongm,

Oops, I meant Task Manager.

I’m very grateful for your help, advice and the examples that you pointed out to me.

With your help I now have the following code which needs two things sorting.

First is your ExecCmd function, can it be modified to hide the ffmpeg window form showing?

And second, I tried to implement a progress bar but it progresses along in big chunks representing each file that is processed. How can I make it run smoothly as it does when I use the original Shell code?

Code:
Dim i As Long
Dim convertedFile As String
        Me.boxPct.Visible = True
        inti = 1 'Set to 1 not 0 for progress bar to increment correctly through to 100 percent complete.


For i = 1 To UBound(sourcePaths)
    convertedFile = Me.Text0 & "\" & Left(Mid(sourcePaths(i), InStrRev(sourcePaths(i), "\") + 1), InStrRev(Mid(sourcePaths(i), InStrRev(sourcePaths(i), "\") + 1), ".") - 1) & ".mp3"
    
   ExecCmd """" & ffmpegpath & "ffmpeg.exe"" -i """ & sourcePaths(i) & """ -q:a 0 -map a """ & convertedFile & """"
            dblPct = inti / j 'COUNT OF SELECTED FILES>       'Calculate progress bar percent.
            Me.boxPct.Width = Me.boxWhole.Width * dblPct 'Increment the progress bar.
          DoEvents
            inti = inti + 1

Next i
 
Did you know there is a built in progress meter in MS Access?

Code:
' Used for progress meter
Dim Progress As Variant 

' Initialise progress meter
Progress = SysCmd(acSysCmdInitMeter, "Converting " &  UBound(sourcePaths) & " File(s), Please Wait...", UBound(sourcePaths))

For i = 1 To UBound(sourcePaths)

     ' Your other code '

     Progress = SysCmd(acSysCmdUpdateMeter, i)
     DoEvents

Next i

' remove progress meter
Progress = SysCmd(acSysCmdRemoveMeter)

Something like that, you will then get the green built in progress bar in the bottom right of the access app [bigsmile]

"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"
Free Electronic Dance Music
 
Hello 1DMF

Thank you, I did know about that one but I particularly wanted to get the one I have running like I mention here:

I tried to implement a progress bar but it progresses along in big chunks representing each file that is processed. How can I make it run smoothly as it does when I use the original Shell code?

And strongm’s function running like this

How can your ExecCmd function be modified to hide the ffmpeg window form showing?
 
How can I make it run smoothly as it does when I use the original Shell code?

Didn't the original shell code open multiple instances, rather than wait for each instance to finish?

That may be what gave you the 'illusion of smoothness', if you only update on each iteration when the instance has ran (which I understood is what you are trying to achieve), then it will be in block increments as each file is individually processed.



"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"
Free Electronic Dance Music
 
>can it be modified to hide the ffmpeg window form showing?

Sure. My ExecCmd function that you are using is a somewhat cutdown version of my extended ShellAndWait function that can do rather a lot more than the typical ShellAndWait - but is often overkill for most ShellAndWait functions (to be honest I think I might have elected to use a version of guitardave78's ShellAndWait from the thread I linked). However, if you want to use it here is a minor edit that works pretty much like the VB Shell function, in that you can tell it how you want the window to appear, using the same function parameters(i.e vbMinimizedFocus etc):

Code:
[blue]Option Explicit

Private Type PROCESS_INFORMATION
        hProcess As Long
        hThread As Long
        dwProcessId As Long
        dwThreadId As Long
End Type

Private Type STARTUPINFO
        cb As Long
        lpReserved As String
        lpDesktop As String
        lpTitle As String
        dwX As Long
        dwY As Long
        dwXSize As Long
        dwYSize As Long
        dwXCountChars As Long
        dwYCountChars As Long
        dwFillAttribute As Long
        dwFlags As Long
        wShowWindow As Integer
        cbReserved2 As Integer
        lpReserved2 As Long
        hStdInput As Long
        hStdOutput As Long
        hStdError As Long
End Type

Private Const NORMAL_PRIORITY_CLASS = &H20
Private Const INFINITE = &HFFFF

Private Const WAIT_OBJECT_0 = 0

Private Declare Function CreateProcessA Lib "kernel32" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long

[b][COLOR=#EF2929]Private Const STARTF_USESHOWWINDOW = &H1&[/color]
[/b]
Public Sub ExecCmd(ByVal cmdline As String[COLOR=#EF2929][b], Optional WindowStyle As VbAppWinStyle = vbMinimizedFocus[/b][/color])
    Dim proc As PROCESS_INFORMATION
    Dim start As STARTUPINFO
    Dim ret As Long
    Dim hProc As Long

    [COLOR=#4E9A06]' Initialize the STARTUPINFO structure:[/color]
    start.cb = Len(start)
      
    [b][COLOR=#EF2929]start.dwFlags = STARTF_USESHOWWINDOW
    start.wShowWindow = WindowStyle[/color][/b]  
  
    [COLOR=#4E9A06]' Start the shelled application:
[/color]    hProc = CreateProcessA(vbNullString, cmdline, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc)

    If hProc <> 0& Then WaitForInputIdle hProc, INFINITE

    [COLOR=#4E9A06]' Wait for the shelled application to finish:
[/color]    ret = -1 [COLOR=#4E9A06]' can't leave as default because that is same value as WAIT_OBJECT_0[/color]
    Do Until ret = WAIT_OBJECT_0
        ret = WaitForSingleObject(proc.hProcess, 10)
        DoEvents
    Loop

    ret = CloseHandle(proc.hProcess)
End Sub[/blue]
 
Mike,

Is there a way of putting a hook into the shell progress while it is waiting, that could be used to create a smoother progress meter?

Does ffmpeg have any progress hooks itself?

1DMF

"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"
Free Electronic Dance Music
 
>Does ffmpeg have any progress hooks itself?

Not that I am aware of.

> used to create a smoother progress meter?

Given the above, no, since we cannot see how far through the process ffmpeg has got for each individual file, so you can't improve the granularity. What we could do instead is an activity meter (and I've previously explained to patricia how this might be achieved for any process)
 
Thanks Mike, as I thought.

What we could do instead is an activity meter (and I've previously explained to patricia how this might be achieved for any process)
What thread is that on? This is something that interest me, so was asking at a tangent to Patricia - didn't mean to thread hijack - sorry.

1DMF.

"In complete darkness we are all the same, it is only our knowledge and wisdom that separates us, don't let your eyes deceive you."

"If a shortcut was meant to be easy, it wouldn't be a shortcut, it would be the way!"
Free Electronic Dance Music
 
Thank you strongm and 1DMF

The edit to your ExecCmd function works just fine, so thank you for that.

Both your comments on the smooth running progress meter make sense to me and I thought as much myself but wanted to be sure I wasn’t missing something.

Your suggestion to use an activity meter is good and I do have code which I have posted below.

Code:
Option Compare Database
Option Explicit

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Sub cmdStart_Click()
StartActivity
Call Workarounds
StopActivity
End Sub

'This is only here to set up the label to display the way we want.
Private Sub Form_Open(Cancel As Integer)
With boxWhole
  boxPct.Move .Left, .top + 45, 0#, .Height '- .BorderWidth * 2
End With
'lblStatus.BackStyle = 1
'lblStatus.BackColor = &H7D491F
End Sub
 
Private Sub StartActivity()
'lblStatus.Width = 0
boxWhole.Visible = True
boxPct.Visible = True
Me.TimerInterval = 5
End Sub

Private Sub StopActivity()
'lblStatus.Width = 0
boxWhole.Visible = False
boxPct.Visible = False
Me.TimerInterval = 0
End Sub

Private Sub Form_Timer()
Static LeftPos As Double
Dim BlockWidth As Long

BlockWidth = 570 + 120 '- Frame4.BorderWidth * 2
LeftPos = (LeftPos + (boxWhole.Width - BlockWidth) / 100) Mod (boxWhole.Width - BlockWidth)
boxPct.Left = boxWhole.Left + 60 + LeftPos
boxPct.Width = BlockWidth
End Sub

Private Sub Workarounds()
Dim lp As Long
For lp = 1 To 500 'Change this to change how long Workarounds runs for.
  Sleep 5
  DoEvents 'Defer to OS to allow Form timer to do something.
Next
End Sub

This code currently works from a form with a command button and two labels for the meter, it uses the forms Timer and when you click the command button the activity runs and in this scenario this is a working example.

Can you show me how I would implement it into the loop below?

Code:
Dim i As Long
Dim convertedFile As String
For i = 1 To UBound(SourcePaths)
    convertedFile = Me.Text0 & "\" & Left(Mid(SourcePaths(i), InStrRev(SourcePaths(i), "\") + 1), InStrRev(Mid(SourcePaths(i), InStrRev(SourcePaths(i), "\") + 1), ".") - 1) & ".mp3"
    ExecCmd """" & ffmpegpath & "ffmpeg.exe"" -i """ & sourcePaths(i) & """ -q:a 0 -map a """ & convertedFile & """", vbHide
Next i
 
Stiull find it difficult to believe that you describe yourself as aprogrammer, Patricia. You seem more like a handyman, simply sticking bits and pieces together. The following might be one way of achieving what you want

Code:
[blue]Public Sub ExecCmd(ByVal cmdline As String, Optional WindowStyle As VbAppWinStyle = vbMinimizedFocus)
    Dim proc As PROCESS_INFORMATION
    Dim start As STARTUPINFO
    Dim ret As Long
    Dim hProc As Long

    [green]' Initialize the STARTUPINFO structure:[/green]
    start.cb = Len(start)
      
    start.dwFlags = STARTF_USESHOWWINDOW
    start.wShowWindow = WindowStyle  
  
    [green]' Start the shelled application:[/green]
    hProc = CreateProcessA(vbNullString, cmdline, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc)

    If hProc <> 0& Then WaitForInputIdle hProc, INFINITE

    [green]' Wait for the shelled application to finish:[/green]
    ret = -1 [green]' can't leave as default because that is same value as WAIT_OBJECT_0[/green]
    [b][COLOR=#EF2929]StartActivity[/color][/b]
    Do Until ret = WAIT_OBJECT_0
        ret = WaitForSingleObject(proc.hProcess, 10)
        DoEvents
    Loop
    [b][COLOR=#EF2929]StopActivity[/color][/b]

    ret = CloseHandle(proc.hProcess)
End Sub[/blue]

 
Hello Mike,

I guess we’re all handymen of sorts!
Some are just handier than others.

I call myself a programmer because I developed websites using FrontPage, somewhat redundant now I know, but hey that’s what I did.

I do appreciate your help but could do without the frequent quips.

My activity meter code used the form Timer and I was curious if there was a better method available for the code I’m using here but I see your suggesting the Timer.

Thank you for your help.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top