automatic close excel workbook with a pop up message - vba

I am trying to create a macro that can automatically close the workbook within 5 minutes plus there will be a pop up reminder message at 4 mins 30 sec. I want the message box to be automatically closed in 10 seconds if user does not click the ok button. I am stuck at the point that the message box cannot close within 10 seconds. Most of my code are copied from the internet. Below are my codes:
In the workbook page:
Private Sub workbook_open()
Call settimer
End Sub
Private Sub workbook_beforeclose(cancel As Boolean)
Call stoptimer
End Sub
Private Sub workbook_sheetcalculate(ByVal sh As Object)
Call stoptimer
Call settimer
End Sub
Private Sub workbook_sheetselectionchange(ByVal sh As Object, _
ByVal target As Excel.Range)
Call stoptimer
Call settimer
End Sub
In the module
Dim downtime As Date
Sub settimer()
downtime = Now + TimeValue("00:01:00")
alerttime = downtime - TimeValue("00:00:50")
Application.OnTime Earliesttime:=alerttime, _
procedure:="alertuser", schedule:=True
Application.OnTime Earliesttime:=Downtime, _
procedure:="shutdown", schedule:=True
End Sub
Sub stoptimer()
On Error Resume Next
Application.OnTime Earliesttime:=downtime, _
procedure:="shutdown", schedule:=False
End Sub
Sub shutdown()
Application.DisplayAlerts = True
With ThisWorkbook
.Save = True
.Close
End With
End Sub
Sub alertuser()
Dim wsshell
Dim intText As Integer
Set wsshell = CreateObject("WScript.Shell")
intText = wsshell.Popup("log sheet will be closed in 30 seconds if there are no more inputs", 10, "reminder")
Set wsshell = Nothing
End Sub

You need to fully qualify your procedure name. It is not finding the procedure in question. You also had a typo and where missing the global variable alerttime. Try this:
Public downtime As Date
Public alerttime As Date
Private Sub workbook_open()
Call settimer
End Sub
Private Sub workbook_beforeclose(cancel As Boolean)
Call stoptimer
End Sub
Private Sub workbook_sheetcalculate(ByVal sh As Object)
Call stoptimer
Call settimer
End Sub
Private Sub workbook_sheetselectionchange(ByVal sh As Object, _
ByVal target As Excel.Range)
Call stoptimer
Call settimer
End Sub
Sub settimer()
downtime = Now + TimeValue("00:01:00")
alerttime = downtime - TimeValue("00:00:50")
'fully qualify your procedure name here and the procedure will run
Application.OnTime Earliesttime:=alerttime, _
procedure:="WorkbookName.xlsm!ThisWorkbook.alertuser", schedule:=True
'and here... also typo was here in downtime
Application.OnTime Earliesttime:=downtime, _
procedure:="WorkbookName.xlsm!ThisWorkbook.shutdown", schedule:=True
End Sub
Sub stoptimer()
On Error Resume Next
Application.OnTime Earliesttime:=downtime, _
procedure:="shutdown", schedule:=False
End Sub
Sub shutdown()
Application.DisplayAlerts = True
With ThisWorkbook
.Save = True
.Close
End With
End Sub
Sub alertuser()
Dim wsshell
Dim intText As Integer
Set wsshell = CreateObject("WScript.Shell")
intText = wsshell.Popup("log sheet will be closed in 30 seconds if there are no more inputs", 10, "reminder")
Set wsshell = Nothing
End Sub

You could use a userform (which you Insert into your project in the VBA editor) which looks something like this:
In the properties window I changed the forms name to formReminder to make it easier to refer to in other modules. Then, in the userform's code window I put:
Private Running As Boolean
Private Sub CommandButton1_Click()
Running = False
End Sub
Private Sub UserForm_Activate()
Dim start As Single
start = Timer
Running = True
Do While Running And Timer < start + 10
DoEvents
Loop
Unload Me
End Sub
Private Sub UserForm_Click()
Running = False
End Sub
When you run the line formReminder.Show anywhere else in the code (e.g. -- in place of where you create the popup) the form will display and show for 10 seconds (or less if you click anywhere on it) and then disappear.
While it displays it will look like this:

Thanks, John Coleman for your answer. It led me to a solution I've wanted for a long time. I took your code and converted it into a generic function that accepts parameters for the message and the number of seconds to wait.
Sub MsgBoxTimerTest()
' Test the Message box with a timer form
Dim vReturn As Variant
vReturn = MsgBoxTimerCall("MessageBox that Dissappears after n Seconds", "Hello World!", 3)
End Sub
' **************************************************************************
Function MsgBoxTimerCall(strCaption As String, strMessage As String, intSeconds As Integer)
' Show a messagebox for a while
' https://stackoverflow.com/questions/37281840/automatic-close-excel-workbook-with-a-pop-up-message
' 2016-06-21
TimerSeconds = intSeconds
msgBoxTimerForm.Caption = strCaption
msgBoxTimerForm.TextBox1.Value = strMessage
msgBoxTimerForm.Show
End Function
' **************************************************************************
' **************************************************************************
Insert this code in the form
' **************************************************************************
Private Running As Boolean
Private Sub CommandButton1_Click()
MsgBox "Yo!"
Running = False
End Sub
Private Sub UserForm_Activate()
Dim start As Single
start = Timer
Running = True
Do While Running And Timer < start + TimerSeconds
DoEvents
Loop
Unload Me
End Sub
Private Sub UserForm_Click()
Running = False
End Sub

Related

Make a button visible if a key is press down

I am trying to make a button visible only if a key (example Control) is press down.
My code:
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = 17 Then
Me.btn1.Visible = True
Else
Me.btn1.Visible = False
End If
End Sub
Private Sub Form_Load()
Me.btn1.Visible = False
End Sub
I need to have visible and active the button only when the CONTROL key is press down and the user click on btn1.
Thank you.
Update Code:
Option Compare Database
Option Explicit
Private Sub btnHide_Click()
DoCmd.Close acForm, "frmDemo", acSaveYes
End Sub
Private Sub btnFake_KeyDown(KeyCode As Integer, Shift As Integer)
On Error GoTo Err_Handler
If vbKeyControl = 17 Then
Me.btnHide.Visible = True
FormTimer
Me.btnFake.SetFocus
Me.btnHide.Visible = False
Else
Me.btnHide.Visible = False
End If
Exit_This_Sub:
Exit Sub
Err_Handler:
If Err = 2467 Then '<== Form is closed.
Resume Exit_This_Sub
Else
MsgBox "Error #: " & Err.Number & " " & Err.Description
End If
Resume Exit_This_Sub
End Sub
Private Sub Form_Load()
Me.btnHide.Visible = False
Me.btnFake.SetFocus
End Sub
Sub FormTimer()
Dim PauseTime, Start, Finish, TotalTime
PauseTime = 0.1 ' Set duration.
Start = Timer ' Set start time.
Do While Timer < Start + PauseTime
DoEvents ' Yield to other processes.
Loop
End Sub
I test it and it is working fine. Simple code.
If CONTROL is press btnHide is visible for 0.1sec.
Any ideas?

Trigger code after inactivity [duplicate]

This question already has answers here:
Automatically close Workbook after inactivity
(2 answers)
Closed 7 years ago.
I want my WB to trigger some code after inactivity time (set by me, naturally). I could only find code to close the WB after inactivity time, but I want the code to do something else, different from closing the WB. I found this code for closing the WB:
This Workbook module:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
stop_Countdown
ThisWorkbook.Save
End Sub
Private Sub Workbook_Open()
start_Countdown
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
stop_Countdown
start_Countdown
End Sub
Private Sub Workbook_SheetCalculate(ByVal Sh As Object)
stop_Countdown
start_Countdown
End Sub
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, _
ByVal Target As Excel.Range)
stop_Countdown
start_Countdown
End Sub
Regular module:
Option Explicit
Public Close_Time As Date
Sub start_Countdown()
Close_Time = Now() + TimeValue("00:00:10")
Application.OnTime Close_Time, "close_WB"
End Sub
Sub stop_Countdown()
Application.OnTime Close_Time, "close_WB", , False
End Sub
Sub close_wb()
ThisWorkbook.Close True
End Sub
In what part of the code should I introduce the events I want the WB to do, after inactivity, instead of closing the WB?
You need to make changes in the Regular module.
Instead of passing the String close_wb() in your Application.OnTime function call, you have to specify the name of the procedure containing whatever you want to perform.
Here is the code to get you started:
Option Explicit
Public Inactivity_Time As Date
Sub start_Countdown()
Inactivity_Time = Now() + TimeValue("00:00:10")
Application.OnTime Inactivity_Time, "my_procedure" ' <- Notice that I changed the name of the procedure here
End Sub
Sub stop_Countdown()
On Error Resume Next
Application.OnTime Inactivity_Time, "my_procedure", , False ' <- And here too.
On Error GoTo 0
End Sub
Sub my_procedure()
' The code to perform you event goes here
End Sub
Please check here for more details on the Application.OnTime method.
EDIT: After some test, it appears that you can't call stop_Countdown() in the Workbook_BeforeClose sub procedure: It throws an error. According to this post, in your Workbook module, you have to replace the procedure Workbook_BeforeClose with the following one:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Close_WB
End Sub
And add the following procedure:
Public Sub Close_WB()
stop_CountDown
ThisWorkbook.Close SaveChanges:=True
End Sub

VBA Word macro to stop user input

I'm writing a big macro for MS Word 2013 and one of the parts of it should be sub that prevents user from writing text; user should be able to use hotkey (eg. ctrl+q) to stop (or start) this sub (I already know how to assign a hotkey to a sub). I'm pretty new to VBA. I've googled for the answer but there's only instruction how to write such macro for Excel, but it doesn't work in Word. Is there a way to do this? How?
Thank you in advance.
Dim startTime As Single
Dim stopTime As Single
Dim timeToRun
Dim totalTime
Dim tmpTime
Dim avg As Long
Public isStart As Boolean
Public Sub hotkeyPressed() 'I wrote module to handle this'
If isStart = True Then
stopButton_Click
Else
startButton_Click
End If
End Sub
Private Sub startButton_Click()
totalTime = tmpTime
startTime = Timer
isStart = True
startButton.Enabled = False
stopButton.Enabled = True
ActiveDocument.Protect _
Type:=wdNoProtection
End Sub
Private Sub stopButton_Click()
isStart = False
stopTime = Timer
tmpTime = totalTime + tmpTime
startButton.Enabled = True
stopButton.Enabled = False
ActiveDocument.Protect _
Type:=wdAllowOnlyReading
End Sub
Private Sub Document_New()
startButton.Caption = "Start!"
stopButton.Caption = "Stop"
isStart = False
Call scheduler
ActiveDocument.Protect _
Type:=wdAllowOnlyReading
On Error GoTo Handler:
Open "KM\" + ActiveDocument.Name + ".txt" For Input As #1
Input #1, line
tmpTime = line
Close #1
Exit Sub
Handler:
tmpTime = 0
End Sub
Private Sub Document_Close()
MyFile1 = "KM\" + ActiveDocument.Name + ".txt"
fnum = FreeFile()
Open MyFile1 For Output As fnum
Print #fnum, totalTime
Close fnum
End Sub
Private Sub scheduler()
timeToRun = Now + TimeValue("00:00:01")
Application.OnTime timeToRun, "getNumberOfLetters"
End Sub
Sub getNumberOfLetters()
If isStart = True Then
numOfLetters = ActiveDocument.Characters.Count
totalTime = Timer - startTime
timeLabel.Caption = totalTime + tmpTime
charLabel.Caption = numOfLetters
setResult
End If
Call scheduler
End Sub
Private Sub setResult()
avg = 60 * numOfLetters / czasLabel.Caption
avg.Caption = avg
End Sub
Have you tried simply using the .Protect method?
Sub ProtectDocument()
ActiveDocument.Protect _
Type:=wdAllowOnlyReading
End Sub
This will prevent user input. You can unprotect the document using VBA if necessary:
Sub UnprotectDocument()
ActiveDocument.Protect _
Type:=wdNoProtection
End Sub

Creating a Macro in Excel which executes the other Macro every 2 seconds

I have a macro called UpdateMacro which updates my Database from my Excel
I want to create a macro called RepeatMacro where it executes the UpdateMacro every 2 seconds automatically and only Start and Stop Buttons are to be provided to start and Stop execution of the RepeatMacro.
How can it be done?
Google for Application.OnTime
E.g.
Dim dtNextRunTime As Date
dtNextRunTime = Now + TimeSerial(0,0,2)
Application.OnTime dtNextRunTime, "MyProcedure", True
To clear a previously set procedure, you need to save the time at which it was scheduled )e.g. dtNextRunTime above), then use:
Application.OnTime dtNextRunTime, "MyProcedure", False
Here's a sample VB module with methods StartSchedule / StopSchedule to get you going:
Private m_dtScheduledTime As Date
Private m_lDelaySeconds As Long
Private m_bIsScheduled As Boolean
Private Sub DoWork()
m_bIsScheduled = False
' ... do your work
' Reschedule at the same frequency once completed
StartSchedule m_lDelaySeconds, "DoWork"
End Sub
Public Sub StartSchedule(ByVal DelaySeconds As Long)
StopSchedule
m_lDelaySeconds = DelaySeconds
m_dtScheduledTime = Now + TimeSerial(0, 0, m_lDelaySeconds)
Application.OnTime m_dtScheduledTime, "DoWork", True
m_bIsScheduled = True
End Sub
Public Sub StopSchedule()
If m_bIsScheduled Then
Application.OnTime m_dtScheduledTime, "DoWork", False
m_bIsScheduled = False
End If
End Sub
This will run UpdateMacro every two seconds, assuming UpdateMacro takes less than two seconds to run.
Sub RepeatMacro()
Dim lastRunTime
Do
lastRunTime = Now
Range("A1") = "Last run: " & Format(lastRunTime, "hh:nn:ss")
Call UpdateMacro
DoEvents
Application.Wait lastRunTime + TimeValue("00:00:02")
Loop
End Sub
Sub UpdateMacro()
Debug.Print "running UpdateMacro"
End Sub
EDIT To start and stop RepeatMacro from your sheet, make a start button and a stop button, and put the following code in your Sheet module. Also notice that I added a DoEvents in RepeatMacro above.
Private Sub StartButton_Click()
Call RepeatMacro
End Sub
Private Sub StopButton_Click()
MsgBox "Stopped."
End
End Sub
Now just saying End is poor practice, but you get the idea.

Timer on user form in Excel VBA

I've got some old Excel VBA code where I want to run a task at regular intervals. If I were using VB6, I would have used a timer control.
I found the Application.OnTime() method, and it works well for code that's running in an Excel worksheet, but I can't make it work in a user form. The method never gets called.
How can I make Application.OnTime() call a method in a user form, or are there other ways to schedule code to run in VBA?
I found a workaround for this. If you write a method in a module that just calls a method in your user form, then you can schedule the module method using Application.OnTime().
Kind of a kludge, but it'll do unless somebody has a better suggestion.
Here's an example:
''//Here's the code that goes in the user form
Dim nextTriggerTime As Date
Private Sub UserForm_Initialize()
ScheduleNextTrigger
End Sub
Private Sub UserForm_Terminate()
Application.OnTime nextTriggerTime, "modUserformTimer.OnTimer", Schedule:=False
End Sub
Private Sub ScheduleNextTrigger()
nextTriggerTime = Now + TimeValue("00:00:01")
Application.OnTime nextTriggerTime, "modUserformTimer.OnTimer"
End Sub
Public Sub OnTimer()
''//... Trigger whatever task you want here
''//Then schedule it to run again
ScheduleNextTrigger
End Sub
''// Now the code in the modUserformTimer module
Public Sub OnTimer()
MyUserForm.OnTimer
End Sub
I needed a visible countdown timer that could stay on top of other windows and run smoothly whether making changes to the workbook, or minimizing the Excel window. So, I adapted the #don-kirkby's creative code above for my own purposes and figured I'd share the result.
                      
The code below requires creation of a module and a userform as noted in the comments, or you can download the .xlsm at the bottom of this answer.
I used the Windows Timer API for more accurate and smooth countdown (and also customizable down to ~100 millisecond timer resolution, depending on your processor. There's even a "tick tock" sound. ⏰
Insert a new module and save it as modUserFormTimer. Add two form control command buttons to the worksheet, labelled Start Timer and Stop Timer and assigned procedures btnStartTimer_Click and btnStopTimer_Click.
Option Explicit 'modUserFormTimer
Public Const showTimerForm = True 'timer runs with/without the userform showing
Public Const playTickSound = True 'tick tock (a WAV sounds could be embedded: `https:// goo.gl/ ReuUyd`)
Public Const timerDuration = "00:00:20" 'could also Insert>Object a WAV for tick or alarm
Public Const onTimerStart_MinimizeExcel = True 'minimize Excel? (countdown remains visible)
Public Const onTimerStart_MaximizeExcel = True 'maximize Excel when timer completes?
'timer could be on top of other applications; instructions here: `https:// goo.gl/ AgmWrM`
'safe for 32 or 64 bit Office:
Private Declare PtrSafe Function SetTimer Lib "User32" (ByVal hWnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
Private Declare PtrSafe Function KillTimer Lib "User32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Public Declare PtrSafe Function Beep Lib "kernel32" (ByVal dwFreq As Long, ByVal dwDuration As Long) As Long
Public schedTime As Date 'this is the "major" timer set date
Private m_TimerID As Long
Public Sub OnTimerTask()
'the procedure that runs on completion of the "major timer" (timer won't reschedule)
Unload frmTimer
''''''''''''''''''''''''''''''
MsgBox "Do Something!" ' < < < < < Do Something Here
''''''''''''''''''''''''''''''
End Sub
Public Sub btnStartTimer_Click()
schedTime = Now() + TimeValue(timerDuration)
InitTimerForm
End Sub
Public Sub btnStopTimer_Click()
'clicking the 'x' on the userform also ends the timer (disable the close button to force continue)
schedTime = 0
frmTimer.UserForm_Terminate
End Sub
Public Sub InitTimerForm()
'run this procedure to start the timer
frmTimer.OnTimer
Load frmTimer
If showTimerForm Then
If onTimerStart_MinimizeExcel Then Application.WindowState = xlMinimized
frmTimer.Show 'timer will still work if userform is hidden (could add a "hide form" option)
End If
End Sub
Public Sub StartTimer(ByVal Duration As Long)
'Begin Millisecond Timer using Windows API (called by UserForm)
If m_TimerID = 0 Then
If Duration > 0 Then
m_TimerID = SetTimer(0, 0, Duration, AddressOf TimerEvent)
If m_TimerID = 0 Then
MsgBox "Timer initialization failed!", vbCritical, "Timer"
End If
Else
MsgBox "The duration must be greater than zero.", vbCritical, "Timer"
End If
Else
MsgBox "Timer already started.", vbInformation, "Timer"
End If
End Sub
Public Sub StopTimer()
If m_TimerID <> 0 Then 'check if timer is active
KillTimer 0, m_TimerID 'it's active, so kill it
m_TimerID = 0
End If
End Sub
Private Sub TimerEvent()
'the API calls this procedure
frmTimer.OnTimer
End Sub
Next, create a userform, save it as frmTimer. Add a text box named txtCountdown. Set property ShowModal to False. Paste the following into the form's code window:
Option Explicit 'code for userform "frmTimer"
'requires a textbox named "txtCountdown" and "ShowModal" set to False.
Dim nextTriggerTime As Date
Private Sub UserForm_Initialize()
ScheduleNextTrigger
End Sub
Public Sub UserForm_Terminate()
StopTimer
If schedTime > 0 Then
schedTime = 0
End If
If onTimerStart_MaximizeExcel Then Application.WindowState = xlMaximized 'maximize excel window
Unload Me
End Sub
Private Sub ScheduleNextTrigger() 'sets the "minor" timer (for the countdown)
StartTimer (1000) 'one second
End Sub
Public Sub OnTimer()
'either update the countdown, or fire the "major" timer task
Dim secLeft As Long
If Now >= schedTime Then
OnTimerTask 'run "major" timer task
Unload Me 'close userForm (won't schedule)
Else
secLeft = CLng((schedTime - Now) * 60 * 60 * 24)
If secLeft < 60 Then 'under 1 minute (don't show mm:ss)
txtCountdown = secLeft & " sec"
Else
'update time remaining in textbox on userform
If secLeft > 60 * 60 Then
txtCountdown = Format(secLeft / 60 / 60 / 24, "hh:mm:ss")
Else 'between 59 and 1 minutes remain:
txtCountdown = Right(Format(secLeft / 60 / 60 / 24, "hh:mm:ss"), 5)
End If
End If
If playTickSound Then Beep 16000, 65 'tick sound
End If
End Sub
Download the demo .xksm. here. There are numerous ways this can be customized or adapted to specific needs. I'm going to use it to calculated and display real time statistics from a popular Q&A site in the corner of my screen...
Note that, since it contains VBA macro's, the file could may set off your virus scanner (as with any other non-local file with VBA). If you're concerned, don't download, and instead build it yourself with the information provided.)
How about moving all the code to a 'Timer' module.
Dim nextTriggerTime As Date
Dim timerActive As Boolean
Public Sub StartTimer()
If timerActive = False Then
timerActive = True
Call ScheduleNextTrigger
End If
End Sub
Public Sub StopTimer()
If timerActive = True Then
timerActive = False
Application.OnTime nextTriggerTime, "Timer.OnTimer", Schedule:=False
End If
End Sub
Private Sub ScheduleNextTrigger()
If timerActive = True Then
nextTriggerTime = Now + TimeValue("00:00:01")
Application.OnTime nextTriggerTime, "Timer.OnTimer"
End If
End Sub
Public Sub OnTimer()
Call MainForm.OnTimer
Call ScheduleNextTrigger
End Sub
Now you can call from the mainform:
call Timer.StartTimer
call Timer.StopTimer
To prevent errors, add:
Private Sub UserForm_Terminate()
Call Timer.StopTimer
End Sub
Wich will trigger:
Public Sub OnTimer()
Debug.Print "Tick"
End Sub
Thanks to user1575005 !!
Used the code in a Module to setup a Timer() process:
Dim nextTriggerTime As Date
Dim timerActive As Boolean
Public Sub StartTimer()
Debug.Print Time() & ": Start"
If timerActive = False Then
timerActive = True
Call ScheduleNextTrigger
End If
End Sub
Public Sub StopTimer()
If timerActive = True Then
timerActive = False
Application.OnTime nextTriggerTime, "OnTimer", Schedule:=False
End If
Debug.Print Time() & ": End"
End Sub
Private Sub ScheduleNextTrigger()
If timerActive = True Then
nextTriggerTime = Now + TimeValue("00:00:10")
Application.OnTime nextTriggerTime, "OnTimer"
End If
End Sub
Public Sub OnTimer()
Call bus_OnTimer
Call ScheduleNextTrigger
End Sub
Public Sub bus_OnTimer()
Debug.Print Time() & ": Tick"
Call doWhateverUwant
End Sub
Private Sub doWhateverUwant()
End Sub