Background code to see how long the spreadsheet has been open - excel

I want to evaluate how long the spreadsheet has been open and once it hits a certain time (say an hour), warn the user that it will automatically close in 15 mins.
I have a tab called Reference where I can put auto-open code to log when they opened it but I want the spreadsheet to check unprompted at certain intervals how long the sheet has been open.
I want to force a close where a user has accidentally left a spreadsheet open so it's important it isn't assessed based on user input.
Note - I don't care if they lose work, I just want to close the spreadsheet.

To schedule the call of a routine, use Application.OnTime. To ensure that the routine is scheduled to "one hour after opening", create a Workbook-Open sub (in ThisWorkbook-Module) and put something like this into it:
Private Sub Workbook_Open()
Application.OnTime DateAdd("h", 1, Now), "InformUser"
End Sub
This will call the Sub InformUser one hour after the Workbook was opened. In this routine you could add another call to OnTime. However, you need to be a little bit careful how to inform the user. If you simple use MsgBox but the user is not reacting to that, the MsgBox-Window stays active and as it is a modal window, the code will not continue to run and the OnTime-procedure will never be triggered. It's not easy to create a MsgBox with a TimeOut (see Display a message box with a timeout value), so maybe create a simple form and show it Non-Modal.
Sub InformUser()
InfoForm.Show modal:=False
Application.OnTime DateAdd("n", 15, Now), "ShutDown" ' "n": Minutes ("m" stands for months)
End Sub
Sub ShutDown
ThisWorkbook.Close SaveChanges:=True ' Or "False" if you don't want to save - use on your own risk...
End Sub

Related

What is "normal" Excel behavior of macro called by Application.OnTime during cell edit?

I am a VBA beginner trying to determine the "normal" behavior of excel during a timed call to a macro using Application.OnTime. In my code (below) I am calling ActiveWorkbook.RefreshAll but I think this question may be relevant to the timed call to any macro. As a newbie, I want to understand the behavior and limits of VBA so that I can determine if VBA is the right tool or if I should use another solution in certain cases (perhaps python / openpyxl).
My set up: I am using Microsoft Excel 365 (annual subscription) and the Account page says I am using Version 2006 (Build 13001.20384 Click-to-Run). I have a single workbook containing a single Sheet1 which contains the formulas to import stock data using the built in feature under the Excel Data tab. I have several rows (one stock per row) of columnar data corresponding to the stock fields such as ticker symbol, price, etc.
To automatically refresh the data, I borrowed & modified code to refresh the data every minute. The code works fine. The "issue" I have is that between refreshes, if I start editing any cell on the sheet and continue to do so during the time when the next automatic call to the refresh should occur, it indefinitely delays the execution of said call until the moment I complete (or cancel) the cell edit. When it does the refresh upon my edit completion that refresh does seem to work correctly.
If I start a cell edit and do not complete it for several minutes then only a single refresh occurs when I complete (or cancel) the edit, even though several intervals may have passed (it does not makeup/catchup the missed/delayed calls).
This behavior is OK with me in this application, but I would like to understand if this is considered "normal" behavior, and if I should expect it to always behave this way. Perhaps this question extends beyond a refresh call but also to any timed call to any macro that may be delayed as a result of a cell edit in progress.
Here is my code.
This is in a file called VBA-Test-02-StockData.xlsm under ThisWorkbook (Code):
Private Sub Workbook_Activate()
'ActiveWindow.WindowState = xlMaximized
Application.Speech.Speak ("Activating stock workbook")
Call AutoRefresh
End Sub
This is in the same xlsm file under Module1 (Code):
Sub RefreshStockData()
Application.Speech.Speak ("Refreshing stock data")
ActiveWorkbook.RefreshAll
End Sub
Sub AutoRefresh()
Call RefreshStockData
Application.OnTime Now + TimeValue("00:01:00"), "AutoRefresh"
End Sub

How to run VBA Excel macro at a specific time and then every 2 hours thereafter

I would like a VBA Excel macro to run every morning # 8:30AM and then every 2 hours thereafter with the final one # 4:30pm (10:30am, 12:30pm, 2:30pm,4:30pm).
I have the following two scripts so far, however cannot seem to figure out how to have it start # 8:30AM:
Private Sub Workbook_Open()
Call SetTimeToRun
End Sub
Private Sub SetTimeToRun()
Application.OnTime Now() + TimeValue("02:00:00"), "LiveBook"
End Sub
If you want to run it a specific amount eg 3 times then simply do:
Application.OnTime Now() + TimeValue("02:00:00"), "LiveBook"
Application.OnTime Now() + TimeValue("04:00:00"), "LiveBook"
Application.OnTime Now() + TimeValue("06:00:00"), "LiveBook"
If you want to run it continously every 2 hours without limitation then you need to add
Call SetTimeToRun
to your procedure LiveBook. For Example
Public Sub LiveBook()
Call SetTimeToRun
'your code here …
End Sub
So everytime LiveBook runs it initiates the next run in 2 hours.
But note that the workbook needs to be open all the time in that machine otherwise the timed procedures will not run.
If you need to run a macro at a specific times like 8:30 am, 10:30 am. I recommend to use the windows scheduler to run a VB script or something that opens the workbook, runs the macro and closes the workbook. But note that no one else can use the workbook or the scheduler will fail to open the workbook (only one person can open a workbook at a time for editing).
Also see How to set recurring schedule for xlsm file using Windows Task Scheduler.

Wait via Do Events works in standard module but not userform

Background/issue: I created a userform so that when a value lower than $250 is entered in a specific cell, the userform prompts the user for a password. The password is then supplied by a supervisor to allow the value. The issue is that the value entered by the user will be a guess. These users mess with values until the value they enter gets them the number they're looking for (separate formula that's dependent upon the low $250 amount). So after the user attempts their first number, they're likely going to need to enter it up to 10ish times. Currently the userform would require the supervisor to enter the password each time the value is changed. I'm hoping to edit my code so that either A) the password can be "retained" if you will for a certain length of time or B) allow the opportunity to enter an amount lower than $250 a set amount of times without the userform launching each time. I tried the number attempt originally and was unsuccessful. Honestly a time option would be best so hopefully someone can help. I've tried multiple ways of waiting (application.wait), sleep using lib kernel32, and a few different do events. From what I've gathered, the do events might be my best option as what I need it to do is allow the user to change values and not have the PC prevent the user from working (change said value though I suppose the user could still perform other actions.)
I've tried putting different the different wait/sleep/loop subs in a standard module and call them but I've also tried putting them directly in the forms module but neither way works. If the sub is in a standard module, Excel doesn't let the user do anything (it acts as application.wait) but if I perform it in the form module, it allows the user to select cells but not edit them until the loop/userform sub is complete. No errors are given in any of this, it just doesn't let users make multiple attempts at the value under $250. One last tidbit...I can run the Wait sub on a standard module and work in the workbook while it continues firing; just can't get it to work with the userform.
Public Sub CommandButton1_Click()
Dim newTime As Date
If tbPassword.Value = "password" Then
me.hide 'Form would stay up so I thought hiding it would fix issue but didn't
call wait 'loop to allow an amount of time before PS has to be reentered
Else
MsgBox "Password is incorrect."
Sheets("Rate Calculator v8").Range("K19") = ""
Application.Goto Sheets("Rate Calculator v8").Range("K19")
End If
Unload me
End Sub
Sub Wait()
Start = Timer
Do While Timer < Start + 60
DoEvents
Loop
End Sub
I got a solution that works but not as you like it to work and I will tell you why it shouldn't work like you want it to work.
First the solution:
Put this code in "ThisWorkbook"-Module:
Option Explicit
Public Endt
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Call Modul1.TimeBeforeReentry
End Sub
Side note here: You may want to use another event then Workbook_SheetChange but it does the job for me here. The disadvantage is that it also triggers with everything else that changes the a sheet.
Then put this code in a module1:
Sub TimeBeforeReentry()
If ThisWorkbook.Endt > Timer Then
Else
UserForm1.Show
If UserForm1.Passwordfield.Value = "password" Then
UserForm1.Passwordfield.Value = ""
ThisWorkbook.Endt = Timer + 60
Else
MsgBox "Password is incorrect."
Worksheets(1).Range("K19") = ""
Application.Goto Worksheets(1).Range("K19")
End If
End If
End Sub
And finally in the userform:
Private Sub CommandButton1_Click()
Me.Hide
End Sub
And now the explanation and why you shouldn't put everything into the userform.
The public variable Endt is in thisworkbook and will be saved there (unless there is an error!). As soon as the event is triggered, in our case workbook_SheetChange, the module will be called. Here is the whole logic and this is good. Because you do not want to make "smartUI" the problem with logic in userforms is that it is nearly impossible to maintain and to adjust to changes. I learned this the hard way believe me.. I am not an expert on this topic but read these links
Smart UI
Model View Presenter as alternative to Smart UI
The Sub TimeBeforeReentry then checks the time and shows the userform accordingly. If the password is entered the Endt is set and if the next event is triggered within Endt then the password does not have to be reentered. Otherwise the password will be needed to reentered.
The userform is just a data interchange device it has nothing to do with logic it just holds data and delivers it. That's what UI is for!

Running macros at scheduled time when pc is locked

Background: Windows 7, Office 2010
I currently have 2 macros running by buttons, one macro calculates and extracts data and the other macro selects a specific range and sends an email to a specified email.
May I know how I can get this 2 macros to run at a scheduled time when my pc is locked - i.e. (on 'switch user' screen)?
Thank you and I sincerely appreciate any help I can get!
Sub tempo()
tps = Now + TimeValue("00:01:00") 'your refresh rate
Application.OnTime tps, "message_ctrl"
End Sub
Sub message_ctrl()
Call Module1.test 'your macro
Call tempo 'this just relaunch schedule when test() finished
End Sub
Here exemple to refresh every minutes, but you can define day or anything.
You need to start once tempo() or message_ctrl() to start cycle. Maybe at workbook_open.
The key here is the Application.OnTime function.

Close an excel workbook opened by another user on the same computer using VBA

I have been working on an excel document for making employee schedules for my work, using excel 2010. One issue that we constantly have is that a user may have the workbook open and forget to close it before they leave. They are still logged into the computer (a shared computer) but that user profile is locked, and another user logs in. The file in use is stored on a network drive, so it could be locked on different computers, or the same computer the current user that wants to access it is using. Of course, if the workbook was left open, the new user can't make changes. I was wondering if there is a way to add some code to the document so I could put a button that would close the instance of the workbook that is still open by another user, perhaps giving the current user the option to save it or not, then close and reopen the workbook that the current user opened so they can gain access to make changes? Let me know if I need to clarify anything for you. Thank you!
You can do some trick. The main idea is to close workbook if nobody has changed the selection of cells (i.e. select A1 or another cell) in example for 10 minutes.
1) add this code to the vba module:
Public lastSelectionChange As Date
Sub closeWB()
If DateDiff("n", lastSelectionChange, Now) > 10 Then
ThisWorkbook.Close SaveChanges:=True
Else
Application.OnTime Now + TimeSerial(0, 10, 0), "closeWb"
End If
End Sub
2) add following code to the ThisWorkbook module:
Private Sub Workbook_Open()
lastSelectionChange = Now
closeWB
End Sub
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
lastSelectionChange = Now
End Sub
Function closeWb will called every 10 minutes (Application.OnTime Now + TimeSerial(0, 10, 0), "closeWb" do this) and check if last selection change was over 10 minutes ago. If so, then close wb.
That would be a very serious security issue in te operation system. Being able to interact with another users' files and running programs.
However, you could have the current user do a "Save-as" or create a copy and continue from there
I think I have a solution.
You place a timer code for e.g. every 30s which searches a specific folder for a specific file/file name.
If the file doesn't exist, then do nothing.
If the file exists then it closes the workbook without saving changes.
To start the shut down procedure on a peers PC that has this workbook open then you create a separate macro that creates that specific file and deletes it just before shutdown.
You have to make sure that the shutdown procedure doesn't work for read only copies of the workbook. Alternatively you could manually paste the file there and delete once the master workbook is closed.

Resources