How to check until file exist - vba

Is there any way to check until file exists in VBA.
what I am trying to do is, making vba call asynch.
Now after I run
wshShell.Run """" & SFilename & """" & s
I want to check until file exists like this
Wait until fso.fileexists("file")
Msgbox "file is now available"
End wait!!!
is there any way in vba?
I am using word vba.

You can do it like this:
Do
If fso.FileExists("file") Then
Exit Do
End If
DoEvents 'Prevents Excel from being unresponsive
Application.Wait Now + TimeValue("0:00:01") 'wait for one second
Loop
MsgBox "file available", vbOKOnly, ""
Although this is surely not the best method
Instead of using Application.Wait, you can use sleep:
Sleep 1000 '1 Second
but you need to add this to your code to be able to use it:
#If VBA7 Then
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr) 'For 64 Bit Systems
#Else
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds as Long) 'For 32 Bit Systems
#End If

You need a timeout to avoid an endless loop. This function returns true if it found the file and false if timeout was reached.
Option Explicit
Const timeoutSeconds As Long = 128
Private localFSO As Object
Public Function FSO() As Object
If localFSO Is Nothing Then Set localFSO = CreateObject("Scripting.FileSystemObject")
Set FSO = localFSO
End Function
Public Function WaitForFileToExist(ByVal theFileName As String) As Boolean
Dim timeElapsed As Single
Dim startTime As Single
startTime = Timer
Do
If FSO.FileExists(theFileName) Then
WaitForFileToExist = True
Exit Do
End If
DoEvents
Application.Wait Now + TimeValue("0:00:01")
timeElapsed = Timer - startTime
Loop Until timeElapsed > timeoutSeconds
End Function

Related

Implementing a File Picker dialog box in Outlook VBA

So you're creating an Outlook macro that prompts the users to select file(s) - but you can't quite get it right. Hopefully this will help.
There seems to be a number of related questions, but I'm consolidating everything here and showing what worked for me in the end.
Outlook Application.FileDialog not found
Where is Outlook's save FileDialog?
How to implement Application.FileDialog using Outlook VBA?
FilePicker in Macro opens dialogbox in background
The most annoying thing for me was the fact that once you've implemented a workaround, the File Dialog will open in the background whenever you're not running the code from VBE directly.
Right out of the gate, the Outlook Application doesn't support VBA FileDialog object. Theoretically Outlook itself supports this since you can do File > Save As and File > Open & Export...but you can't simply call the object from VBA.
For my project - I have a sub that replaces tokens with user input, but I wanted to give folks the option of picking which Template to open. I'd recommend reading up on the FileDialog object itself as there are several helpful examples in the Microsoft documentation.
There are a number of options, but below are the 2 main workarounds I've found. I prefer the first method as it doesn't require adding a reference - meaning that the macro(s) can be more easily shared without compilation errors.
Method 1: No References Needed (hopefully)
#If VBA7 Then
Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal win As LongPtr) As LongPtr
#Else
Private Declare Function SetForegroundWindow Lib "user32" (ByVal win As Long) As Long
#End If
Option Explicit
Sub CreateEmailUsingSelectedTemplate()
Dim xlApp As Object
Set xlApp = CreateObject("Excel.Application")
'MsgBox "The top-level window handle is: " & xlApp.hWnd
Dim fd As Office.FileDialog
Set fd = xlApp.FileDialog(msoFileDialogFilePicker)
Dim vrtSelectedItem As Variant
SetForegroundWindow (xlApp.hWnd)
With fd
.InitialFileName = Environ("APPDATA") & "\Microsoft\Templates\"
.Filters.Add "All Files", "*.*", 1
.Filters.Add "Templates", "*.oft", 2
.FilterIndex = 2
If .Show = -1 Then
For Each vrtSelectedItem In .SelectedItems
MsgBox "Selected item's path: " & vrtSelectedItem
'FindAndReplaceTokens CStr(vrtSelectedItem)
Next vrtSelectedItem
Else 'If the user presses Cancel...
MsgBox "Hit cancel instead of Accept"
Exit Sub
End If
End With
End Sub
Method 2: Early Binding
See FilePicker in Macro opens dialogbox in background and File dialog box not displaying on top and not visable
'Set reference to 'Microsoft Excel XX Object Library' in
'Tools > References
#If VBA7 Then
Private Declare PtrSafe Function FindWindowA Lib "user32" (ByVal class As String, ByVal caption As String) As LongPtr
Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal win As LongPtr) As LongPtr
#Else
Private Declare Function FindWindowA Lib "user32" (ByVal class As String, ByVal caption As String) As Long
Private Declare Function SetForegroundWindow Lib "user32" (ByVal win As Long) As Long
#End If
Option Explicit
Sub ShowDialogBox()
Dim fd As Office.FileDialog
Dim xlApp As Excel.Application
Dim hxl As LongPtr
Dim vrtSelectedItem As Variant
Set xlApp = New Excel.Application
Set fd = xlApp.FileDialog(msoFileDialogFilePicker)
hxl = FindWindowA("XLMAIN", "EXCEL")
If Not IsNull(hxl) Then
SetForegroundWindow (hxl)
End If
If fd.Show = -1 Then
For Each vrtSelectedItem In fd.SelectedItems
MsgBox "Selected item's path: " & vrtSelectedItem
'Put your code here
Next vrtSelectedItem
Else
MsgBox "User hit cancel"
Exit Sub
'Do something different here
End If
End Sub

How to delay a macro that runs after sending email?

I have the below code for Outlook 365 which will run a macro after sending an email.
How do I modify this to delay the macro 10 seconds after clicking send, and how do I limit this code to my exchange account email which is the default email account?
Option Explicit
Private Sub Application_ItemSend(ByVal Item As Object, Cancel As Boolean)
With Item
Call BatchResendEmailsMacro
End With
End Sub
Outlook doesn't have a timer function but you can use Appointment or Task Reminders to trigger macros. Set up an Application_Reminder macro that will do something when a reminder fires. To limit it to running when specific reminders fire, use an If statement to look for words in the subject or a specific category.
If you want the macro to fire a specified time after you restart Outlook, use an Application_Startup macro to create the appointment. Read more about that in the Running Outlook Macros on a Schedule article.
Also you may consider using Windows API functions such as SetTimer and KillTimer. Outlook VBA - Run a code every half an hour page provides a sample code (for example, that is for Windows x64):
Declare PtrSafe Function SetTimer Lib "user32" (ByVal hwnd As LongLong, ByVal nIDEvent As LongLong, ByVal uElapse As LongLong, ByVal lpTimerfunc As LongLong) As LongLong
Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongLong, ByVal nIDEvent As LongLong) As LongLong
Public TimerID As LongLong 'Need a timer ID to eventually turn off the timer. If the timer ID <> 0 then the timer is running
Public Sub TriggerTimer(ByVal hwnd As Long, ByVal uMsg As Long, ByVal idevent As Long, ByVal Systime As Long)
MsgBox "The TriggerTimer function has been automatically called!"
End Sub
Public Sub DeactivateTimer()
Dim lSuccess As LongLong
lSuccess = KillTimer(0, TimerID)
If lSuccess = 0 Then
MsgBox "The timer failed to deactivate."
Else
TimerID = 0
End If
End Sub
Public Sub ActivateTimer(ByVal nMinutes As Long)
nMinutes = nMinutes * 1000 * 60
'The SetTimer call accepts milliseconds, so convert to minutes
If TimerID <> 0 Then Call DeactivateTimer
'Check to see if timer is running before call to SetTimer
TimerID = SetTimer(0, 0, nMinutes, AddressOf TriggerTimer)
If TimerID = 0 Then
MsgBox "The timer failed to activate."
End If
End Sub
You could trigger the code with the ItemAdd event on the Sent Items folder.
Option Explicit
' In the ThisOutlookSession module
Private WithEvents sentItems As Items
Private Sub Application_Startup()
Dim sentItemsFolder As Folder
' default Sent Items folder
Set sentItemsFolder = Session.GetDefaultFolder(olFolderSentMail)
Set sentItems = sentItemsFolder.Items
End Sub
Private Sub sentItems_ItemAdd(ByVal item As Object)
Dim waitTime As Long
Dim waitDiff As Long
Dim delay As Date
Dim waitStart As Date
waitTime = 10 ' in seconds
Debug.Print vbCr & "Wait start: " & Now
waitStart = Now
delay = DateAdd("s", waitTime, waitStart)
Debug.Print "Wait until: " & delay
Do Until Now >= delay
DoEvents
Loop
Debug.Print "Wait end..: " & Now
waitDiff = DateDiff("s", waitStart, Now)
Debug.Print waitDiff & " seconds delay."
Debug.Print "Call BatchResendEmailsMacro"
'Call BatchResendEmailsMacro
Debug.Print "Done."
End Sub
Private Sub test()
sentItems_ItemAdd ActiveInspector.currentItem
End Sub

Determine if the computer is locked

I have a macro that send me a text from outlook when a meeting notification pops up. I would like to figure out a way to make that macro only run if I am not at my computer. I have looked for a way to pull my status from Skype for Business, determine if the PC is locked or not, and see if a smart card is inserted. All without much luck. Looking for a simple solution that works in VBA.
I used the code from here Determine if application is running with Excel
Function IsProcessRunning(process As String)
Dim objList As Object
Set objList = GetObject("winmgmts:") _
.ExecQuery("select * from win32_process where name='" & process & "'")
If objList.Count > 0 Then
IsProcessRunning = True
Else
IsProcessRunning = False
End If
End Function
Based on the answer here In Python 3, how can I tell if Windows is locked?
I called
IsProcessRunning("LogonUI.exe")
and it seems to work.
Maybe this is of any help
Option Explicit
Private Declare Function SwitchDesktop Lib "User32" (ByVal hDesktop As Long) As Long
Private Declare Function OpenDesktop Lib "User32" Alias "OpenDesktopA" (ByVal lpszDesktop As String, ByVal dwFlags As Long, ByVal fInherit As Long, ByVal dwDesiredAccess As Long) As Long
Private Declare Function CloseDesktop Lib "User32" (ByVal hDesktop As Long) As Long
Private Const DESKTOP_SWITCHDESKTOP As Long = &H100
Function desktopLocked() As String
Dim p_lngHwnd As Long
Dim p_lngRtn As Long
Dim p_lngErr As Long
Dim System As String
p_lngHwnd = OpenDesktop(lpszDesktop:="Default", dwFlags:=0, fInherit:=False, dwDesiredAccess:=DESKTOP_SWITCHDESKTOP)
If p_lngHwnd = 0 Then
System = "Error"
Else
p_lngRtn = SwitchDesktop(hDesktop:=p_lngHwnd)
p_lngErr = Err.LastDllError
If p_lngRtn = 0 Then
If p_lngErr = 0 Then
System = "Locked"
Else
System = "Error"
End If
Else
System = "Unlocked"
End If
p_lngHwnd = CloseDesktop(p_lngHwnd)
End If
desktopLocked = System
End Function
Update: Example how one could use the function above
Option Explicit
#If VBA7 Then
Declare PtrSafe Function LockWorkStation Lib "user32.dll" () As Long
#Else
Declare Function LockWorkStation Lib "user32.dll" () As Long
#End If
Dim iTimerSet As Double
Public Sub SaveAndClose()
If desktopLocked = "Locked" Then
ThisWorkbook.Close True
Else
iTimerSet = Now + TimeValue("00:00:03")
Application.OnTime iTimerSet, "SaveAndClose"
End If
End Sub
Sub LockPC()
SaveAndClose
LockWorkStation
End Sub
Just run LockPC and wait 3 seconds before you unlock the workstation. The file has been closed in the meantime.

Pause Outlook for a given amount of time

I'm trying to run Outlook code 10 seconds after an email is received.
I tried using application.wait but it appears that you cannot do this with Outlook.
How do I pause Outlook for a given amount of time?
You can create a Sub that will mimic the Application.Wait, something like.
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
'For 64-Bit
'Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Sub Pause(intSeconds As Variant)
' Comments: Waits for a specified number of seconds
' Params : intSeconds Number of seconds to wait
' Source : Total Visual SourceBook
On Error GoTo PROC_ERR
Dim datTime As Date
datTime = DateAdd("s", intSeconds, Now)
Do
' Yield to other programs (better than using DoEvents which eats up all the CPU cycles)
Sleep 100
DoEvents
Loop Until Now >= datTime
PROC_EXIT:
Exit Sub
PROC_ERR:
MsgBox "Error: " & Err.Number & ". " & Err.Description, , "Pause Method"
Resume PROC_EXIT
End Sub
To call this you could use Pause 3
Here's a simple way:
T0 = Now + TimeValue("0:00:10")
Do Until Now > T0
Loop
Throw a DoEvents in this and it'll be okay
T0 = Now + TimeValue("00:00:10")
Do Until Now > 10
DoEvents
Loop
I'm not sure the need for complicated function..
Try This:
#If VBA7 Then
'Code is running VBA7 (2010 or later).
#If Win64 Then
'Code is running in 64-bit version of Microsoft Office.
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#Else
'Code is running in 32-bit version of Microsoft Office.
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If
#Else
'Code is running VBA6 (2007 or earlier).
#End If
Sub Test()
Debug.Print Now
Sleep 10000
Debug.Print Now
End Sub

VBA code that closes excel after inactivity and can run with other macros

Hello I am looking for code that will close excel after a specified amount of time that the user has been inactive. I have code online for this problem but it does not run correctly with the other macros in my workbook. I need code that can run with other macros and will close after user inactivity. Any help would be greatly appreciated!
Call macro for the inactivity counter at the end of each macro. Exit inactivity counter macro using global variable passing 0 at the beginning of each macro. Sample code with keyboard only inactivity control:
Public br As Integer
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Byte) As Integer
Sub brea()
br = 0
'do stuff here
Call test
End Sub
Sub test()
br = 1
Dim t As Integer
t = 0
While t < 15
t = t + 1
Sleep (1000)
For i = 0 To 255
ret = GetAsyncKeyState(i)
If ret <> 0 Then t = 0
Next
Application.StatusBar = t
If br = 0 Then GoTo Ends
DoEvents
Wend
Application.Quit
Ends:
End Sub