Excel bug message box output writes on cell - vba

On one of my Excel projects I have a "saving check" feature that if the workbook has not been saved in the past 25 min it will prompt user to take some action (MsgBox).
This method invokes Application.OnTime to schedule next alert and is being rescheduled once is saved.
Today we saw a bug where the contents of the message box (and additional text with "Microsoft Excel" & "OK") were printed out to a worksheet, overwriting cell contents.
According to the user no macro was running in the background and the only action being taken was switching between worksheets. Those sheets have no event triggered code.
I though OnTime was safe method but that bug just freaked me out.
I have done some research trying to find documentation about this issue but couldn't. I believe it must be a low prob event that might happens once in a lifetime.
Any comments about if I should I keep this code or wipe it out?
Many thanks in advance.
' EDIT 1 - Adding code being called by the OnTime Function
Public Sub NeedToBeSaved()
' ======================================================================
' Description : Periodically checks if the workbook needs to be saved.
'
' Comments : AfterSave method takes care of scheduling new check.
' Refresh it only if user has not saved
' ======================================================================
Dim nme As Name
Dim dteLastSaved As Date
Set nme = ThisWorkbook.Names(gsRNGNAME_LASTSAVED)
dteLastSaved = Mid$(nme.value, 2)
' Debug Log
' ---------
Debug.Print "----------------------"
Debug.Print "Saving Periodic Check"
Debug.Print "----------------------"
Debug.Print "Worbook is saved:" & vbTab & ThisWorkbook.saved
Debug.Print "Last time :" & vbTab & dteLastSaved
If (ThisWorkbook.saved = False) Then
MsgBox "File has not been saved in the last " & glAPP_SAVED_FREQ & _
" minutes, please consider saving your changes. " & _
vbCrLf & "(To prevent this message open in read-only mode)"
Call SaveOnTimeCheck
Else
Debug.Print "Not rescheduled"
End If
End Sub

This is the most likely scenario to me:
Imagine someone does some copy paste actions in your workbook. He doesn't look on the screen but on his keyboard because he's not familiar with typing blind. The MsgBox pops up (he doesn't notice, still eyes on keybord). He accidentally copies the content of the MsgBox ctrl+c and pastes it into the workbook.
This adds an additional "Microsoft Excel" and lines "---" and "OK" to the message like below:
---------------------------
Microsoft Excel
---------------------------
Your MsgBox message here …
---------------------------
OK
---------------------------
You can easily reproduce this yourself by pressing ctrl+c while a MsgBox is up. And pasting it into Excel afterwards.
There are 2 possible reasons for this:
The manual copy paste scenario I described above (most likely).
Alternatively there was a macro running in the background doing copy paste actions while the MsgBox was up (I'm not 100 % sure but I think this is even not possible, but if so you should be able to reproduce the issue).

Related

Excel VBA Self-Destruct Switch

I have had my first "Client-took-my-work-and-then-ghosted-me-without-paying" experience. For the future, I want to put in a killswitch, disguised as a regular macro, which makes the whole thing unusable. That way, even if they hire someone to crack the password and remove my "Your trial has expired..." check, a normal-looking macro (Something like "Fix_Sheet_Formatting") would be easily overlooked and run, destroying everything and saving the changes.
However, that leaves the VBA... We're talking a full purge here, so everything must go. I'll figure out how to do all of this on my own, I just don't want to waste time pursuing something that isn't possible:
Can VBA code delete itself from a workbook while running or does it have to be deleted from a macro on another workbook? And would deleting the code cause the macro to stop running, or can I have it delete everything except a nasty MsgBox after everything is done?
I'll leave the comments to debating whether or not this is a good idea (IMHO it probably isn't). As to the question itself, of course it's possible, and won't interrupt macro execution:
Public Sub Example()
Dim proj As Object
Set proj = ThisWorkbook.VBProject
Dim comp As Object
For Each comp In proj.VBComponents
With comp.CodeModule
.DeleteLines 1, .CountOfLines
If comp.Name = "ThisWorkbook" Then
.InsertLines 1, "Private Sub Workbook_Open()" & vbCrLf & _
vbTab & "MsgBox ""Where's my money, ##$%&!?""" & vbCrLf & _
"End Sub"
End If
End With
Next
ThisWorkbook.Save
End Sub
Obfuscation is an exercise for the reader.
Put a loop with due date as your payment data . Inside write code to get macro into infinity loop or to delete code.
If payment happens on time then just comment above piece of code or else just wait client come back to you on next day defined in loop.

Excel VBA: Undo all normal Excel-changes since last save, then run macro and save

I'm having serious trouble solving this issue:
I have an Excel Workbook (2010 or later) with a Macro that HAS TO run BeforeClose. It deletes internal data and saves this change: (no problem so far)
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim xlSheet As Worksheet
For Each xlSheet In Sheets
If xlSheet.Name = "internalDataSource" Then
MsgBox "Internal data will be deleted from the workbook. Changes are saved."
Application.DisplayAlerts = False
xlSheet.Delete
ThisWorkbook.Save
Application.DisplayAlerts = True
End If
Next xlSheet
End Sub
My Problem:
the user should be able to change something. Now if the user changes something he usually gets asked, if he wants to save the changes. In this version there is no choice, but saving his changes. currently they are always saved.
How can I get the user to be able to say "No, don't save my changes." but still run the macro and save the deletion of internal data afterwards?
I'm desperately looking for a VBA-command to revert all changes to the last saved state. Then I would simply run the Macro from above and save my deletion.
Thanks for any help I get!
This could be done by saving a copy of the workbook each time a normal save is done, move the internal data to the copy, and rename the copy as the original. There are few options for how to do this specifically, I'll post some suggestions here to flush out my answer:
Saving a copy could be placed in the Workbook_BeforeSave event.
Renaming the copy to the original could be done in the Workbook_BeforeClose procedure you have listed here.
To run a messagebox past the user, you may consider editing the MsgBox you have listed to something like:
If (MsgBox("You are attempting to exit." & vbNewLine _
& "All changes since the last save will be lost." & vbNewLine _
& "Okay to proceed?", 308) = vbNo) Then
Cancel = True
End If
Of course, not "all changes since the last save will be lost" because your internal data will be saved.

Excel VBA copy cells on key press

I'm using VBA to dynamically change the worksheet while typing into another cell. To do so, I've been using the API code that is found here:
Excel track keypresses
So the Sub Sheet_Keypress describes the desired action upon pressing a key.
However, I've been running into problems with the following:
Private Sub Sheet_KeyPress(ByVal KeyAscii As Integer, _
ByVal KeyCode As Integer, _
ByVal Target As Range, _
Cancel As Boolean)
Dim Col As String
Col = Chr(KeyAscii)
Worksheets(1).Range("G" & 4 & ":G" & 6).Value = _
Worksheets(1).Range(Col & 1 & ":" & Col & 3).Value
End Sub
When I go back to the sheet and type somewhere not in rows 1-3, the first keypress does fine. However, the second keypress is not recorded, and a further key gives Error 1004. What exactly is causing this error and is it possible to avoid it?
The problem appears to stem from the fact that Excel locks the worksheets from being changed whilst you are in "edit mode" (i.e. editing a value in a cell) until after you exit edit mode by pressing Enter or Escape.
So in your scenario, it looks like that when you first press a key it is registered fine and triggers your Sheet_Keypress, however, immediately after this, Excel goes into "edit mode" and then your subsequent call to Worksheets(1).Range("G" & 4 & ":G" & 6).Value fails as Excel locks the sheet. Nevertheless, Excel still registers the key press, and you can see this if, for example you change the last row of your routine to something like Application.StatusBar = Col.
As to ways around this, you would need to write code in such a way that it kicked Excel out of "edit mode" just after the key press. Unfortunately, there is no built-in way to do this, people have come up with various ways to do this (e.g. here)
There is a simpler solution, however, there are a couple of caveats: the value typed in will not display in the Excel sheet, and you will only be able to type a single value before your formula is updated (so no good for > column AA).
What you can do is to put the line Cancel = True into your sub above, and this will prevent the key press from going into Excel and thus activating the edit mode.

SaveAs command does not work, but SaveCopyAs does

I am having a lot of difficulty getting a SaveAs command to work properly. For a local hospital, there are patient charts which are created from a template file, in which patient data is entered after which it is manually renamed (using save-As) and then copied to another location as a backup. The template is re-used over and over again.
The goal of my code is this to automate this process. Therefore I want to save to two different locations, starting from a template file. The template file should not be overwritten. In the template, a user sets the department name and bed number in cell K1 and N1 , repectively. These fields determine the folder and filename within that folder.
When the save button is pressed, my code starts to run. I use SaveCopyAs to save the backup file and after that I want to use SaveAs to save to my primary folder. SaveAs should set this new file to be my working file, therefore not overwriting my template. At least this is what I believe...
THE PROBLEM: When running SaveAs, Excel crashes (without any clear error message). The strange thing (to me) is that is does not crash when I replace SaveAs with SaveCopyAs.
THE QUESTION: Why does Excel crash at this point? Is there a way to fix or avoid this behaviour? I cannot find a suitable solution that does not alter my template. Any help or suggestions are more than welcome.
The code below is placed in my "ThisWorkbook" folder and is executed every time I click the "save"-button.
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
department = Range("K1").Value 'Name of department: CHIC, THIC, ICB or NCIC
bedNumber = Range("N1").Value 'bednumber or roomnumber: Bed 1. Bed 2 or Room 1, Room 2.
newFileName = department & "\" & bedNumber & ".xls"
If IsEmpty(department) Then
MsgBox "You haven't entered a department. Please try again."
ElseIf IsEmpty(bedNumber) Then
MsgBox "You haven't entered a bed or room number. Please try again."
Else
ActiveWorkbook.SaveCopyAs "C:\myBackupFolder\" + newFileName
End If
ActiveWorkbook.SaveAs "C:\myPrimaryFolder\" + newFileName 'Doesn't work
'ActiveWorkbook.SaveCopyAs "C:\myPrimaryFolder\" + newFileName 'Does work, but I end up with a messed up template!
End Sub
As well as setting Cancel = True to prevent the default save-behaviour, add:
Application.EnableEvents = False
ActiveWorkbook.SaveAs "C:\myPrimaryFolder\" + newFileName 'Doesn't work
Application.EnableEvents = True
to prevent the same procedure being called again (and again..). This is probably why it crashes.

How to make Excel VBA Automation Execute for All End Users

I wrote the following code so that when an Excel spreadsheet is closed it will update its name with the current date and time:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If ThisWorkbook.Name = "Name_Last Opened-" & Format(Date, "MM-DD-YYYY") & _
"_" & Format(Time, "HH.MM") & ".xls" Then
Else
ThisWorkbook.SaveAs Filename:="\\C:\... Name_Last Opened-" & _
Format(Date, "MM-DD-YYYY") & "_" & Format(Time, "HH.MM") & ".xls"
FName = Sheets("Name").Range("D1").Text
Kill FName
End If
End Sub
Private Sub Workbook_Open()
Range("A1").Select
ActiveCell.FormulaR1C1 = ThisWorkbook.Name
End Sub
Additionally, the code is located within VBAProject(Name of file), under MS Excel Object - ThisWorkbook.
This code works perfectly for me or the workstation that it was created on; however, it does not execute for anyone who opens it on their worstation. Would anyone know how to get the code to execute whenever the spreadsheet is opened and closed from any computer, not just mine?
Thank you,
DFM
It's possible that Excel's security settings aren't allowing other people's computers to run the script that could be interpreted as risky malware. Perhaps you changed your security settings so long ago that you forgot about it. See if you can modify another user's security settings to see if that will make the macro execute on the workbook close.
"Would anyone know how to get the code to execute whenever the spreadsheet is opened and closed from any computer, not just mine?"
I don't think it can be done with 100% certainty unless you can ensure that every possible user will have macro security set such that your macro can execute.
Assuming you can get past that one, you should perhaps check that the users all have the worksheet in the same hard-coded path on C:\ that you seem to be using. What happens if they open the workbook from a different location?
Also:
FName = Sheets("Name").Range("D1").Text
is getting a value from one place and
Range("A1").Select
ActiveCell.FormulaR1C1 = ThisWorkbook.Name
is putting it in another.
I think I'd try something like the following (which assumes from your code that you actually only want to change the file name if it has not changed since the minute of the current time changed):
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim dateTime As String
Dim oldPath As String
Dim newPath As String
dateTime = Format(Now, "MM-DD-YYYY_HH.MM") ' Format the while thing in one string - once
With ThisWorkbook
oldPath = .FullName ' what is it called now, and where did it come from?
newPath = .Path & "\" & "Name_Last Opened-" & dateTime & ".xls" ' what should it be called now?
If oldPath <> newPath Then ' only do something if not saved in last minute - is that what you really want?
.SaveAs Filename:=newPath
Kill oldPath
End If
End With
End Sub
Date() function needs administrator access to run.. so if your user is a non admin, then it will fail. Instead use now(). Most of the times this is some thing which we usually forget as we(people developing the tool) have admin access over our PC's
Fundamentally, you cannot ensure that all users will a) have a macro security setting of low or medium, and b) if set to medium, enable them when the file is opened.
Creating your own certificate would seem like the obvious answer, but in practice I find that the resultant messages and warnings are even more confusing/frightening for some end users, leading to much the same situation as with macro security. Third-party certificates avoid this, but are $$$ and almost surely overkill for an Excel workbook in a corporate environment.
What I've done where I need users to have VBA enabled is to set all sheets to xlveryhidden on save, except a custom locked sheet that only has a note saying macros must be enabled and a brief guide on how to do this. This sheet is hidden and the others restored by the workbook's workbook_open procedure, something that of course will not fire if VBA is disabled.