I'm experiencing a strange issue occurring with my VBA Macros in Excel. Background is, I'm creating a template for Labour Logs to log hours that employees are on site. There is a main sheet where all the data is entered "Labour Log" and then 8 sheets, one for each of the days of the week and a Holidays sheet. There is a hidden sheet where my dynamic list and static lists are held.
The macros that are causing issues are the ones on the sheets for the days of the week. What they are doing is, searching the Labour Logs sheet for any cell with that day of the week in column B, then inserting that line on row 8 of the day sheet. This loops until all instances are entered. Example of Monday below.
Private Sub PullMondayData_Click()
Dim dc As Range
With Sheets("Labour Log") 'Reference to Labour Log Sheet
For Each dc In Intersect(.Range("B:B"), .UsedRange)
If dc.Value2 = "Monday" Then 'Search/Filter B:B for Monday
dc.Resize(1, 1).EntireRow.Copy 'Copy the row
Sheets("Monday").Rows(8).Insert Shift:=xlDown 'Insert in Row 8, shifting down
End If
Next
End With
Application.CutCopyMode = False 'Remove copy mode
End Sub
This usually works fine for Monday. I can usually hit that one multiple times without issue.
When I go back to the Labour Log sheet and change the day to any other day, I start getting errors on those sheets when hitting the macro. The macro codes is exactly the same, but with the relevant day entered in the correct spots.
The first error I usually get is:
Going to debug says error with this line:
I then stop the macro and run it again, getting the following error:
Debugging it gives this line:
After that, the only way I can close the Excel sheet is to end the process form the Task Manager, the rest of the program is locked.
From time to time, I also get a crash, and the output is this:
Problem signature:
Problem Event Name: APPCRASH
Application Name: EXCEL.EXE
Application Version: 16.0.6868.2060
Application Timestamp: 5723a711
Fault Module Name: mso20win32client.dll
Fault Module Version: 0.0.0.0
Fault Module Timestamp: 57222bc3
Exception Code: c0000005
Exception Offset: 0008c78d
OS Version: 6.1.7601.2.1.0.256.48
Locale ID: 4105
Additional information about the problem:
LCID: 1033
skulcid: 1033
I've searched online for various answers, but they don't seem to work. I've tried the following:
Clearing the Excel folder in C:\Users\User_Name\AppData\Roaming\Microsoft\Excel
Cleaning the Registry
Changning the set printer (long shot, but saw a few people say it worked)
Running another day before Monday
Running Windows Update
Moving Application.CutCopyMode = False within the If loop
My knowledge of VBA isn't great and a lot of this code was taken from searches and altered to suit my purpose, but I don't understand why it is working for the Monday sheet, but crashes on all others.
Any insight would be much appreciated.
Thanks.
too little info to actually track that error down
but, assuming you're using a Userform with many buttons to click and have proper day processed, I'd propose you some code that can possibly help you keep things cleaner and, therefore, less prone to errors
set your buttons caption property as per the relevant day
so you'll have
PullMondayData button showing "Monday" caption on it
PullTuesdayData button showing "Tuesday" caption on it
and so on
have all your Pulldaybutton "Click" event handlers code just like follows:
Private Sub PullMondayData_Click()
PullDayData (Me.ActiveControl.Caption)
End Sub
Private Sub PullTuesdayData_Click()
PullDayData (Me.ActiveControl.Caption)
End Sub
and so on
add the following sub to your Userform code pane
Private Sub PullDayData(thisDay As String)
Dim dc As Range
With Sheets("Labour Log") 'Reference to Labour Log Sheet
For Each dc In Intersect(.Range("B:B"), .UsedRange)
If dc.Value2 = thisDay Then 'Search/Filter B:B for Monday
With Sheets(thisDay).Rows(8)
.Insert Shift:=xlDown 'Insert in Row 8, shifting down
.Value = dc.Resize(1, 1).EntireRow.Value
End With
End If
Next
End With
End Sub
all what above can still greatly improved, but it'll give you at least the following benefits:
write much less code
and thus both make much less effort in maintaining it and possibly have many few errors!
not make use of copy/paste methods to both not passing through clipboard and be faster
For further effective improvements I'd suggest you the following steps:
use AutoFilter() and SpecialCells() methods of Range object to improve both control and efficency
use classes
While I agree with user3598756 that this isn't enough to give an answer with certainty, I do have some additional things to try:
I've had much better results using .CurrentRegion, than .UsedRange to get the area of data. There are exceptions, but if you have headers that can't be blank and at least one column with no blanks, it works flawlessly.
When you have more than one thing on a line causing an error, breaking it out will give you better info about crashes, so change: dc.Resize(1, 1).EntireRow.Copy into two statements: dc.Resize(1,1) and dc.EntireRow.Copy.
COM objects, and Range objects like .Rows(x) in particular, are tricky beasts. This seems to be worse in C# addons than VBA macros, but I've often seen them not update as expected when you do something to major their contents. The work around is the general case of the specific case I gave in point 2: one dot good, two dots bad. For example instead of calling Sheets("Monday").Rows(8).Insert try something like:
Dim MondaySheetRow as Range
MondaySheetRow = Sheets("Monday").Rows(8)
MondaySheetRow.Insert Shift:=xlDown
Lastly I've made a habit of not updating sheets that aren't active. Instead I activate the sheet I'm going to make changes to, make the changes, then return to the originally active sheet. This mimics the way a user would manually make the changes which also helps visually show where the macro went in the calls leading up to an error.
Related
So I'm a bit new to excel VBA, and I'm creating a macro to run on financial worksheets. I want to shift the values in the totals to the right place, as they are a column to the left of the actual data (these weren't created by a formula, they were generated by a different program and are fixed text). The shifting I managed to do just fine. The problem here is finding where the totals column is, as it varies between worksheets.
This is what I have so far.
For totalRow = 7 To 2000
With ws
If ws.Visible = True Then
If InStr(Range(totalRow, "A").Value, "Totals:") > 0 Then
Exit For
End If
End If
End With
Next totalRow
Yet for some reason, it's giving me an error when I try to run it. I know it's probably something simple I'm overlooking, because I cannot for the life of me figure out the problem. I've tried using a Do-Until loop, same issue. Is it a problem with the variables I'm using?
Several suggestions:
This is the basic problem:
Error 1004 "Application-defined or Object-defined error"
Look here for several potential issues/potential fixes:
VBA Runtime Error 1004 "Application-defined or Object-defined error" when Selecting Range
Use the VBA debugger and step through your macro a line at a time, until you find the specific object it's barfing on:
https://www.techonthenet.com/excel/macros/vba_debug2013.php
EDIT:
Having said that, I think Tim Williams's suggestion is probably spot-on:
You should always scope your Range/Cells calls with a worksheet
object, otherwise they will reference whatever happens to be the
Activesheet.
But PLEASE:
If at all possible, make the effort to learn troubleshooting tools available to you (like the debugger).
One other "useful tips" link I'd urge you to look at:
http://www.jlathamsite.com/Teach/VBA/WritingBulletProofCode.pdf
My Excel project functions properly at home (with Excel 2010), but not on two work computers (with Excel 2016) and I suspect the Worksheet_Change event is the problem.
When the user makes changes, the yellow bar (in the screenshot) should turn white again, but it is not. I am getting 2 different responses on 2 work computers.
Two things to point out in the code:
In some places I use vbColor extensions, in others I had to use a numerical code.
One computer is not firing the Worksheet_Change event at all. I would note that the change event is at the top of the code, although that shouldn't have anything to do with it.
I'd appreciate advice and detailed explanations, to help me learn.
Private Sub Worksheet_Change(ByVal Target As Range) 'Check for On-Time and Delays then change the Command Button Colors to show completed.
'Return headers to white after jump to
Range("B3:I3,O3:V3,B28:I28,O28:V28,B53:I53,O53:V53,B78:I78,O78:V78,B103:I103,O103:V103,B128:I128,O128:V128,B153:I153,O153:V153").Interior.Color = vbWhite
'Check for On Time and Delayed Trips
'Trip 1 Scan Ready
If IsEmpty(Range("L3").Value) = False Then
If Range("L3").Value > Range("I3").Value Then 'If actual is greater than Departure
'If Delayed check for a delay code
If IsEmpty(Range("L24").Value) Then 'If Delay code is missing
Range("K24:L25").Interior.Color = 16711935
CommandButton1.BackColor = 16711935
CommandButton1.ForeColor = vbBlack
Else 'If Delay Code is present check for delay time
If IsEmpty(Range("L25").Value) Then
Range("K24:L25").Interior.Color.Index = 16711935
CommandButton1.BackColor = 16711935
CommandButton1.ForeColor = vbBlack
Else
CommandButton1.BackColor = vbRed
CommandButton1.ForeColor = vbWhite
Range("K24:L25").Interior.Color = vbWhite
End If
End If
Else
'Flight was on Time
CommandButton1.BackColor = 32768 '32768 = Green
CommandButton1.ForeColor = vbWhite
Range("K24:L25").Interior.Color = vbWhite
End If
End If
There could be a number of factors causing this problem. One way to diagnose is to troubleshoot like this:
At the beginning of your procedure, right after this line:
Private Sub Worksheet_Change(ByVal Target As Range)
...add a temporary line:
MsgBox "Changed: " & Target.Address
...then go change something in your worksheet (whatever change isn't firing the event as you'd expect).
One of two things will happen:
You'll have a message box pop up, showing the cell reference of whatever was just changed.
This demonstrates that the event is firing properly, so the issue must be in your code that follows.
Or, you won't get a message box pop up. This indicates the event is not firing, which could be caused by a few possibilities:
Are macros completely disabled in the workbook? This is often done automatically on workbooks received from outside sources. Save the workbook to a trusted location on the local computer or network (rather than opening from the email). Do other sections of code run properly? When you close/re-open the file, are you given a warning about Macro Security? Also, try rebooting the computer.
Other security settings could be an issue. Have you ever run VBA on these machines? You can confirm sure code is able to run in Excels' security settings in:
File→Options→Trust Center→Trust Center Settings→Macro Settings
As well as making sure macros are enabled there, you could also check Trusted Locations in the Trust Center, and either save your document in a listed location, or add a new location. Security settings will be "reduced" for documents saved in those locations.
Is EnableEvents being intentionally disabled elsewhere in your code? If you wrote all the code, you should know whether you set EnableEvents = False at some point. Perhaps it was intentional, but it's not being re-enabled.
Remember to remove the line you added temporarily, or that MsgBox will quickly get annoying by popping up every time a change is made. :)
You say "the change event is at the top of the code". A worksheet change event will only fire if you put the code in the sheet module concerned. If you've put the code concerned in a non sheet module (e.g. "Module 1" or similar, listed under the "Modules" branch in the object explorer) then that's the problem.
Also, you really shouldn't hard-code cell references like "L3" in your VBA code, because every hard reference will require amending should you (or a user) later insert rows/columns above/to the left of these references. Instead, assign meaningful named ranges to these cells back in Excel, and use those in your VBA.
Also, when using event handlers like you're doing, you should have something like If not intersect(Target, InputRange) is nothing then... so that the code only runs if something of interest changes.
I had the same problem.
I checked everything.
Everything seemed to be proper (enabled macros, EnableEvents=True, etc).
I closed and opened Excel.
Problem persisted.
There was nothing I could do.
I restarted Windows.
Problem disappeared.
Restart took 7 minutes (with all applications closing & restarting), trying to find the cause would take much more. Maybe I could have tried to find & kill every Excel process in Task Manager.
I don't like giving people the advice "try rebooting", but well, Windows is Windows.
I had a different problem. I had saved my worksheet under a new name and then added the code for the event. To get it to work, I had to close the worksheet and reopen it. It then showed the button to enable macros and the code started to work. Hope this helps someone.
Wade
I solved it simply deactivating "Design Mode" in the "Developer" tab.
It seems if you are in "Design Mode", this event (I don't know about others) won't work.
In 2019 Excel cut copy -> paste would not work the paste options were grayed out. Online sources said update and repair the app. I updated office and the copy/paste options worked.
However the worksheet_change event stopped working. After a lot debugging with no luck, I did a hail mary and commented out the subroutine and recreated it. It WORKED!! I have absolutely no idea why or how. If anyone has thoughts please share.
I had some code in a worksheet change that wasnt firing. I also had some code in Thisworkbook upon opening and saving. Resolved the issue by putting Application.EnableEvents = True at the end of the code for workbook open and workbook save.
I have created an Excel Workbook with a lot of VBA code for a customer. The customer will provide me with data. I will import that data into the VBA laden template, Save it as an xlsm, and deliver it to the customer. I get paid by the Workbook so I need to prevent them from trying to copy new data into the existing workbook and reusing it.
How can I somehow prevent the customer from reusing a workbook by just entering in new data on the main Worksheet, then saving as a new Workbook, and getting the use of the VBA code for free. (Alternately they could copy the file in windows then enter new data on the copied version.) I need to detect a significant change in data from the initial imported data.
The data on the main sheet is fairly static (perhaps even totally static on many known columns). I'm thinking about randomly sampling some of the cell data on import (perhaps 10 random cells, or number of rows, etc.), and storing that data somewhere. If, say, 50% of the cells change data, I could just disable (or short-circuit) the public entry points in the code...or something else?
I'd like to allow for some flexibility on the part of the customer, but prevent abuse.
Is there a better way than my general idea, above?
Where could I store that data (it should be part of the sheet, but not changeable by the customer). Perhaps a hidden sheet with password locked cells?
Is there some accepted way of doing this that I'm unaware of?
Perhaps Time-expire the functionality in your code
So thank you for this question. Thank you for setting a bounty. It is a very interesting question given your desire to monetise the VBA code, as a VBA programmer I generally resign myself to not being able to monetise VBA code. It is good that you insist and I will contribute an attempt at an answer.
Firstly, let me join the chorus of answers that say VBA is easily hacked. The password protection can be broken. I would join the chorus of respondents who say you should pick a compiled language as the vessel for your code. Why not try C# or VB.NET housed in a Visual Studio Tools For Office (VSTO) .NET assembly?
VSTO will associate a compiled assembly with a workbook. This mechanism is worth knowing because if you insist on VBA we can use the same mechanism (see later). Each workbook has a CustomDocumentProperties collection where one can set custom properties (it says Document not Spreadsheet because the same can be found in Word so Document is the generalised case).
Excel will look at the workbook's CustomDocumentProperties collection and search for "_AssemblyName" and "_AssemblyLocation"; if _AssemblyName is an asterisk then it knows it needs to load a .NET/VSTO assembly, the _AssemblyLocation provides a lookup to the file to load (you'll have to dig in on this, I forget the details). Reference
Anyway, I'm reminded of VSTO CustomDocumentProperties mechanism because if you insist on using VBA then I suggest storing a value in the CustomDocumentProperties collection that helps you time expire the functionality of your code. Note do not use the BuiltInDocumentProperties("Creation Date") because it is easily identifiable; instead use a codeword, say "BlackHawk". Here is some sample code.
Sub WriteProperty()
ThisWorkbook.BuiltinDocumentProperties("Creation Date") = CDate("13/10/2016 19:15:22")
If IsEmpty(CustomDocumentPropertiesItemOERN(ThisWorkbook, "BlackHawk")) Then
Call ThisWorkbook.CustomDocumentProperties.Add("BlackHawk", False, MsoDocProperties.msoPropertyTypeDate, Now())
End If
End Sub
Function CustomDocumentPropertiesItemOERN(ByVal wb As Excel.Workbook, ByVal vKey As Variant)
On Error Resume Next
CustomDocumentPropertiesItemOERN = wb.CustomDocumentProperties.Item(vKey)
End Function
Sub ReadProperty()
Debug.Print "ThisWorkbook.BuiltinDocumentProperties(""Creation Date""):=" & ThisWorkbook.BuiltinDocumentProperties("Creation Date")
Debug.Print "CustomDocumentPropertiesItemOERN(ThisWorkbook, ""BlackHawk""):=" & CustomDocumentPropertiesItemOERN(ThisWorkbook, "BlackHawk")
End Sub
So you could set CustomDocumentProperty "BlackHawk" to the workbook's initial creation time and then allow the client to use the code for 24 hours, or even 48 hours (careful with weekends, create Friday work through to Tuesday) and then afterwards the code can refuse to operate and instead throw a message saying pay LimaNightHawk more money!
P.S. Good luck with your revenue model.
P.P.S. I read your profile, thanks for your military service.
Whatever you do it will be feasible to crack it (VBA code is easy to crack). However:
there is the contract so... that's not legal for them to do it
you can put part of the code on a FTP server and control physically what is being executed
Very nices ideas here though
Compile Excel file to EXE. Google for that.
Concern 1 seems to be basic re-use of the file. You could create a sub in the ThisWorkbook module to destroy code located in other modules in the event that save-as is selected.
Concern 2 seems to be someone hacking your password protection. A similar tactic could be employed such as using "opening the developer window" as your event instead of save as.
I have experimented with save events to log user entries with great success using the ThisWorkbook module. I am not certain how/if one could detect if the developer tab is opened.
Here's what I've done. Perhaps there is a entirely better approach, or there are tweeks to the below that will make it better:
From the VBA Tools menu > VBAProject Properties > Protection (tab), I Locked the project for viewing with a password.
I created a new "License" sheet.
This sheet is hidden from the user. If you hide the sheet via code like this:
Me.Visible = xlSheetVeryHidden
then then sheet cannot be un-hidden by the user (it requires running vba code to unhide).
On initial import I sample:
Number of imported rows
x number of randomly selected cells *from the columns I know won't/shouldn't change. (There are columns they are allowed to change freely.)
I store these on the "License" sheet in Address / Value pairs.
When the workbook is opened, the Workbook_Open event fires and then does a quick comparison of the current number of rows, and the current values for the addresses stored on the "License" sheet. I then do a percentage calculation: If the rows are more than x% different, or the number of changed values is more that y% then I
Sheets(1).Protect LOCKOUT_PASSWORD
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
There is also a method for unlocking the sheets if necessary.
This might be too simple of an answer, but sometimes we fail to think of the simplest solutions:
Why don't you simply provide the customer with only a copy of the output? You could run their data through your macro-enabled workbook, copy the output sheet into a new workbook, and then break all links so that there's no formulas, updating, or ties to your workbook.
I would create another workbook that contains the code and then reference the customers wb from that one.
I couldn't find the answer to this issue anywhere, so I do hope you guys can help me. My excel macro goes through a couple iterations of data. It autofilters a source file, takes out information, works with the data, and does so again for about 50 times - once per person. Here's some code of what I mean, all the individual submethods work just fine and are pretty damn fast:
For j = 1 To names.Count
'filter the source by name, generate sheet
FilterName names(j)
'prepare data with the necessary dates
FillMasterDates dates(), j
Dim i As Long
Dim ending As Long
ending = Sheets("Daten").Rows.End(xlDown).Row
Dim cellvalue As String
'check dates, etc
For i = 1 To ending
cellvalue = Sheets("Daten").Cells(i, 1)
If cellvalue = "" Then
Exit For
End If
ColorCell (i)
FilterDate CStr(dates(i)), names(j)
Next i
'user data has been successfully gathered, copy over to final sheet
FillColumns j
Next j
The whole code takes about 4~ seconds to run (given that I have about 2000 rows and I create a new sheet for 50~ people), which is fine. The baffling thing is that when Excel stays my active window despite using Application.ScreenUpdating = False (earlier in the macro, but still active at this point), the necessary time to run the macro goes up to a staggering 25~ seconds. Same input, same output. So to put it simply - run macro, tab out of excel - macro needs about 4-5 seconds to run. run macro but stay in excel and wait - 25 seconds.
I've tried Application.WindowState = Application.WindowState, ActiveWindow.SmallScroll, DoEvents, Application.CalculateFull(). I tried different calculation settings, but I do not really use any of the formula calculations innate to Excel - I have to use Excel as an interface because the source file is an *.xls file and the final output has to remain in this format.
If you need me to provide more code snippets to make sense of it, ask away. I've been stumped for a good two days now.
You could always try a couple more lines to disable the calculations and alerts etc.
Application.ScreenUpdating = false
Application.Calculations = xlManual
Application.DisplayAlerts = False
However if you really want to bypass all the background nonsense excel seems to go through dont access the sheet directly through a loop, this concept maybe tricky if your not used to it but its worth every bit, and will speed up your code so fast you will wonder why you never did it in the first place.
I dont have your code so ill just give an example of how it works
Dim RangeArray as Variant 'This will store your range as a values array
RangeArray = Sheet1.Range("A1:G100000").Value 'this will put the entire ranges values into the array
If Not IsArray(RangeArray) Then ExitSub 'If your range is only 1 cell it will not create an array so be careful, handle this as needed
'This Array always starts lowerbound 1, RangeArray(1,1) = First Cell
Now with this you can loop through your data and manipulate and modify the array just like you would with a cell or a range except there is no overhead, its just values and not objects .
Once you have done what you need all you need to do then is put the values back into the sheets range
Sheet1.Range("A1:G100000").value = RangeArray
And thats it, very simple and very effective, and this transfer from array to range is immediate no matter how big it is.
Just let me know if this helps
Thanks
Paul S
---------------NEW MESSAGE-------------------
You could try something which maybe a little excessive and risky, if your only getting this problem while the window is active and displayed how about making it invisible, the problem is if your code fails and you fail to trap an error it will remain invisible until you goto taskmanager and close it there.
Application.Visible = false
This should deactivate the window too (although i have never tested that)
this should simulate you hiding the window and just bring it back when your code has finished..
---------------NEW MESSAGE-------------------
Application.Windowstate = xlMinimized
This should do the trick :D, should have mentioned this first haha
I also just saw that you tried something similar, but the code is incorrect there, try this one
Seems there is some bug. Can't resolve this problem, all code is running fine and I am able to see the AutoShape is getting copied from Excel file but it is not adding it to PowerPoint. Popping up an error Run-time error '-2147188160(80048240) View.Pastespecial : Invalid Request. The specified data type is unavailable
If Range("H" & i).Value = 1 And Range("B" & i).Value = "FRONT" Then
objPPT.Presentations(1).Slides(9).Select
objPPT.ActiveWindow.View.PasteSpecial DataType:=ppPasteEnhancedMetafile
Your code will be faster and possibly more reliable if you don't rely on selecting anything:
With objPPT.Slides(9).Shapes
Set objShape = .PasteSpecial(ppPasteEnhancedMetafile)(1)
With objShape
' set coordinates and such here
End With
End With
As to why you're getting the error message, try stopping the code after you've put something on the clipbard. Then switch to PowerPoint, use Paste Special to see what paste options are available. If EMF isn't one of them, that's your problem ... you're not putting anything in EMF format on the clipboard.
I had a similar issue, but I found a different solution; it may be specific to what I was doing though.
I setup a program where I would:
(manual) Copy an entire webpage that was a report on several performance metrics
(manual) Pasted it in to excel
Run the program to extract the values I want and then clear contents of the sheet I pasted them on.
Eventually after many tests, it would fail with this same automation error when I tried to access the sheet:
Sheets("PDX Paste").Activate
I was able to activate every other sheet except that particular one, even using the index value instead of the direct name reference. After googling to no success I found out that the copy and paste from the website was also pasting invisible controls. When I found this out I had 1,300+ shapes when I only expected 1 (the button I use to trigger the program). It was actually only apparent when a glitch - presumably due to so much memory being used to store these controls - displayed for a few seconds.
I ran the following code independently and then appended it to the end of my program when I do the cleanup of the data. The code goes through the sheet and deletes any shape that isn't the same type as my button. It would have to be adapted if the shapes you want to delete are the same type as the shapes you want to keep. It also becomes simpler if you don't have any shapes to keep.
Dim wsh As Worksheet
Set wsh = ActiveSheet
Dim i As Integer
For i = wsh.Shapes.Count To 1 Step -1
If wsh.Shapes(i).Type <> wsh.Shapes("UpdateDataButton").Type Then
wsh.Shapes(i).Delete
End If
Next i
I'm not sure this would solve this problem, but hopefully this can help others and prevent loss of time figuring out what may be causing this relatively vague error message.