Program stops working on 2nd runthrough - vba

Visual Basic Problem:
Hello Friends,
my problem is pretty complex (although the solution is most likely an easy one):
I've written a small application in Visual Basic. It's a small time management system that has 4 simple functions (on separate buttons):
"Start Work:"
Fetch the current date from System, compare it to a list of dates in an excel table, set current row to the row with the fitting date, and enter the current time in the appropriate cell.
The other functions are "stop work", "start break" and "stop break" and work pretty much the same way.
The application is started by a button embedded in the form and works - so far so good. If I however, start VBA's own debugger and then start the Program again, it fails, because the function that sniffs the correct date out of the list of dates fails to find the correct value. At this point I'm pretty much out of ideas (especially since this is my first VBA project) so I'd be really glad if someone could give me a pointer in the right direction.
Here's the function fetching the date:
Function get_date(time As Date) 'findet das aktuelle Datum in Spalte 2 (Datum)
Dim findDate As Range
On Error Resume Next
Set findDate = Columns(2).Find(Date)
Err.Clear
On Error GoTo 0
If findDate Is Nothing Then
MsgBox "Current Date not on Active Form!"
Else: MsgBox "Current Date is " & Date
MsgBox findDate
Exit Function
End If
End Function
and the function setting the row to the row with the current date
Function get_row(time As Date)
Dim rngSearch As Range, rngFound As Range
Set rngSearch = Range("B5:B18")
Set rngFound = rngSearch.Find(What:=Date, LookIn:=xlValues, LookAt:=xlPart)
If rngFound Is Nothing Then
MsgBox "Aktuelles Datum nicht gefunden - Terminplan erweitern"
Else
get_row = rngFound.row
End If
End Function
As I mentioned both those functionalities work perfectly fine on the first start-up of the form but fail to find (and thus return) a value if I debug and start the Makro again.
The program also has a real time running clock on a timer - maybe that's a factor? I'm totally lost to be honest.
Any criticisms towards the code and how I tackled certain problems are also very welcome - this is my first VBA app ever.
Update:
As requested here's a screenshot of what I assume to be the search ranges:
I'm using the 1904 date system, but changing back to 1900 didn't have any effect.
If anyone is interested, I uploaded the whole Project into my github Stechuhr.xlsm
The relevant file is "Stechuhr.xlsm"
Any further help will be greatly appreciated.
Edit: To clarify - The program stops working, once it has been paused and then resumed. I suspect that the timer function is somehow responsible for this - will do further testing.
Update 2:
As I have been unable to fix this, I simplified my problem as suggested:
Option Explicit
Public Function FindDateRowInColB(TargetDate As Date, TargetSheet As Worksheet) As Long
Dim FoundRange As Range
Set FoundRange = TargetSheet.Columns(2).Find(What:=TargetDate, LookIn:=xlValues, LookAt:=xlPart)
If FoundRange Is Nothing Then
FindDateRowInColB = 0
Else
FindDateRowInColB = FoundRange.Row
End If
End Function
'Test Button 1
Private Sub Test_button_Click()
Dim TestTime As Date
Dim TestSheet As Worksheet
Dim TestRow As Long
Dim pdat_Datum As Date
pdat_Datum = Date
'set references
Set TestSheet = ThisWorkbook.Worksheets("Tabelle1")
'this is our test assertion
TestRow = FindDateRowInColB(pdat_Datum, TestSheet)
'short if statement to display a message based on test results
MsgBox ("TestRow =" & TestRow)
End Sub
' Test Button 2
Private Sub TestButton_2_Click()
Dim active_row As Integer
Dim pdat_aktuellesDatum As Date
Dim TestSheet As Worksheet
pdat_aktuellesDatum = Date
Set TestSheet = ThisWorkbook.Worksheets("Tabelle1")
'set references
Set TestSheet = ThisWorkbook.Worksheets("Tabelle1")
active_row = FindDateRowInColB(pdat_aktuellesDatum, TestSheet)
MsgBox ("Die passende Reihe zum heutigen Datum ist " & active_row)
End Sub
There's the code. It's simply one function and 2 buttons to test it. However, it has the very same problems as my original code - works fine on first startup, but if I ever pause the program and start it again, FindDateRowInColB always returns a value of null. I suppose there might be some memory management issue at work.
If anyone has further input, it will be greatly appreciated.

You could create a lightweight test environment for your functions to make sure they are returning what you'd expect for a few conditions. Here is an example function for finding a date match in column B along with two tests:
Option Explicit
Public Function FindDateRowInColB(TargetDate As Date, TargetSheet As Worksheet) As Long
Dim FoundRange As Range
Set FoundRange = TargetSheet.Columns(2).Find(What:=TargetDate, LookIn:=xlValues, LookAt:=xlPart)
If FoundRange Is Nothing Then
FindDateRowInColB = 0
Else
FindDateRowInColB = FoundRange.Row
End If
End Function
Sub TestFindDateRowFunctionSuccess()
Dim TestTime As Date
Dim TestSheet As Worksheet
Dim TestRow As Long
'set references
Set TestSheet = ThisWorkbook.Worksheets("Sheet1")
TestTime = "4/22/2014"
'this is our test assertion
TestRow = FindDateRowInColB(TestTime, TestSheet)
'short if statement to display a message based on test results
If TestRow = 3 Then
MsgBox ("Test passed! Identified 4/22/2014 in row 3")
Else
MsgBox ("Test failed! Did not identify 4/22/2014 in row 3")
End If
End Sub
Sub TestFindDateRowFunctionFailure()
Dim TestTime As Date
Dim TestSheet As Worksheet
Dim TestRow As Long
'set references
Set TestSheet = ThisWorkbook.Worksheets("Sheet1")
TestTime = "4/1/2014"
'this is our test assertion
TestRow = FindDateRowInColB(TestTime, TestSheet)
'short if statement to display a message based on test results
If TestRow = 0 Then
MsgBox ("Test passed! Date 4/1/2014 was not found so 0 was returned")
Else
MsgBox ("Test failed! Date 4/1/2014 was identified somewhere")
End If
End Sub

Related

Finding a value in the range

I am writing a subroutine that looks through a range of cells starting in cell A1 (the range is 1 column wide) containing String values. My sub first finds the entire range and assign it to a Range variable "theForest" to help make searching easier. Then, it looks through each cell in the range until it finds the word “Edward”. If he is found or not, it display the result in a message (stating that he was or was not found).
The code I have so far is this:
With Range("A1")
'this will help find the entire range, since it is only one column I will search by going down
theForest = Range(.Offset(0,0), .End(xlDown)).Select
Dim cell As Range
For Each cell In theForest
If InStr(Edward) Then
Msgbox"He was found"
Else
Msgbox"He was not found sorry"
End If
Next cell
End With
However I am getting numerous errors upon running the program and I think the issue is with the
theForest = Range(.Offset(0,0), .End(xlDown.)).Select
line of code. I would appreciate any guidance into this simple code.
Thank you :)
EDIT: Here is some new code I have come up with:
Dim isFound As Boolean
isFound = False
With Range("A1")
For i = 1 to 500
If .Offset(1,0).Value = "Edward" Then
isFound = True
Exit For
End If
Next
End With
If isFound = True Then
Msgbox " Edward was found"
Else
MsgBox "Edward was not found"
End if
Then again it does not include finding the entire range and assiging it to the range variable theForest.
Dim theForest as Range, f as Range
Set theForest = ActiveSheet.Range(ActiveSheet.Range("A1"), _
ActiveSheet.Range("A1").End(xlDown))
Set f = theForest.Find("Edward", lookat:=xlWhole)
If Not f Is Nothing Then
Msgbox"He was found"
Else
Msgbox"He was not found sorry"
End If

Subtract two ranges and clear the contents from result

I'm trying to subtract RangeA - RangeA+offset to get a new range. After this i need to clear all the values within it. My problem is that the variable columnrange is empty and i'm unable to realize what i'm doing wrong.
Dim rng1 As String
Dim rangeA As Range
Dim columnrange As Range
Dim clearrange As Range
rng1 = TextBoxA.Value
If Not IsNull(RangeboxA.Value) Then
On Error Resume Next
Set rangeA = Sheets("Plan1").Range(RangeboxA.Value)
rangeA.Select
Selection.Copy
rangeA.Offset(0, rng1).Select
ActiveSheet.Paste
columnrange = rangeA.Resize(rangeA.Rows.Count, rangeA.Columns.Count + rng1).Value
columnrange.Select
On Error Resume Next
If rangeA Is Nothing Then MsgBox "Verificar informação A"
End If
This code moves a user-defined range by a user-defined amount.
Sub RemoveRangeOverlap()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Plan1")
Dim rngOffset As Integer
Dim rangeA As Range, rangeB As Range
Dim cellRange() As String
On Error GoTo ErrHandle
rngOffset = CInt(TextBoxA.Value)
If RangeBoxA.Value <> "" Then
Set rangeA = ws.Range(RangeBoxA.Value) 'Set old range
cellRange = Split(CStr(RangeBoxA.Value), ":") 'Set start/ending cells
ReDim Preserve cellRange(LBound(cellRange) To UBound(cellRange))
Set rangeB = ws.Range(ws.Range(cellRange(0)).Offset(0, rngOffset), _
ws.Range(cellRange(1)).Offset(0, rngOffset)) 'set new range
rangeA.Copy rangeB 'copy new range
Application.CutCopyMode = xlCopy 'remove marching ants
If rangeA.Columns.Count <= rngOffset Then 'remove old values
rangeA.Clear
Else: ws.Range(ws.Range(cellRange(0)), _
ws.Range(cellRange(1)).Offset(0, rngOffset - rangeA.Columns.Count)).Clear
End If
Else: MsgBox "Missing target range input.", vbCritical, "Insufficient Data"
End If
ErrHandle:
If Err.Number = 438 Then
MsgBox "Invalid range format in range input box." & vbNewLine & _
"Proper range format example: A1:A1", vbCritical, "Error 438"
ElseIf Err.Number = 13 Then
MsgBox "Only numbers may be input as the range offset amount", _
vbCritical, "Error 13: Type Mis-match"
ElseIf Err.Number = 5 Then Exit Sub
Else: Err.Raise Err.Number
End If
End Sub
How the code works:
The first thing we have set up is information control from user-defined values. To accomplish this (which can also be done with If Then statements to prevent the errors from ever occurring in the first place) I've included an error handling line at the end. We know what 3 errors we expect to get depending on what the user provides us with.
Error 438 will occur if the user tries to set RangeBoxA's value as a non-range value.
Error 13 will occur if the user tries to input anything that isn't a number as the offset value.
Error 5 will occur because I'm bad at error handling and I'm not sure why it's occuring.. It loops my error statement at the end after whichever error is thrown (being a non-vba error).
Next we split up the range supplied by the user into two 'cells'. Using this we can apply some simple math to show where the copy destination will be as well as delete the proper amount of old range values.
If the number of columns is greater than the user supplied offset, then the new and old ranges will overlap. Some simple math will remove the old cells while preserving the new one's
If the number of columns is less than the user supplied offset, delete all of the old cells because they won't be overlapping.
Let me know if this works for you.

VBA Record date of row change in specific column

I'm trying to automatically update the "Updated" column of an excel spreadsheet when any cell of that specific row changes to today's date. I was able to do this by hard-coding where the "Updated" column header would be, however, it is now necessary to search for that column header as it may move.
The code I am trying to implement works but immediately gives me the error Automation error - The object invoked has disconnected from it's clients.
Any help would be appreciated. Here is the code I have currently:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:DX")) Is Nothing Then
Dim f As Range
Set f = ActiveSheet.Range("A1:DD1").Find("Updated", lookat:=xlWhole)
' f.Row = Range(Target).Row
If Not f Is Nothing Then
Range(Split(f.Address, "$")(1) & Target.Row).Value = Now
Else
MsgBox "'Updated' header not found!"
End If
End If
End Sub
You got into an endless loop.
Try this:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:DX")) Is Nothing Then
Dim f As Range
Set f = ActiveSheet.Range("A1:DD1").Find("Updated", lookat:=xlWhole)
' f.Row = Range(Target).Row
If f Is Nothing Then
MsgBox "'Updated' header not found!"
ElseIf Intersect(Target, f.EntireColumn) Is Nothing Then
Intersect(Target.EntireRow, f.EntireColumn).Value = Now
' Else
' MsgBox "We entered this function again because the row above updated the Updated column", vbInformation, "False alarm"
End If
End If
End Sub
To understand what happens,
Uncomment the else and MsgBox
Put a breakpoint on the MsgBox
When you hit it, press [ctrl]-L
In a case such as this, I run into far fewer problems when I simply loop through the available cells to find the column header. Using the .Find method also works, but is less "tunable" to my needs in a custom application.
Public Function FindColumn(header As String) As Long
Dim lastCol As Long
Dim headerCol As Long
Dim i As Long
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("VTO2 Labor")
lastCol = sh.Cells(1, sh.Columns.Count).End(xlToLeft).Column
headerCol = 0
For i = 1 To lastCol
If sh.Cells(1, i).Value = header Then
headerCol = i
End If
Next i
FindColumn = headerCol
End Function
It isn't clear on whether the Updated column header could be in row 1 or if it will always be in row 1, just not in the same location.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:DX")) Is Nothing Then
On Error GoTo bm_SafeExit
'TURN OFF EVENTS IF YOU ARE GOING TO WRITE A VALUE INTO THE WORKSHEET!
Application.EnableEvents = False
Dim uCol As Long, f As Range
If Application.CountIf(Rows(1), "updated") Then
uCol = Application.Match("updated", Rows(1), 0)
For Each f In Intersect(Target, Range("A:DX"))
If f.Row > 1 Then _
Cells(f.Row, uCol) = Now
Next f
Else
MsgBox "'Updated' header not found!"
End If
End If
bm_SafeExit:
Application.EnableEvents = True
End Sub
That should survive multiple updates (e.g. when pasting values). The problem I see is that is the Updated column is being shifted around, presumably through inserting columns or the like, then the change routine is going to run.

VBA: excel is closing, no error generated

I have a macro which I run on many file. The goal is to define a source and copy the value inside my file. It works fine for 30 source files but I recently have one that makes my excel crash, no error message nothing.
Here the code:
'dimensioning of the variables
'range and workbook
Dim Target_Area As Range
Dim Account_Number, Account_Description, Debit, Credit As Range
Dim General_Balance As Workbook
Dim Transform_file As Workbook
Dim Source_Range As Range
'technical var
Dim LastCell As Range
Dim LastCellNumber As Long
Dim Array_Position As Integer
Dim Worksheet_general_balance As Long
Dim Links As Variant
Dim address As String
'var used to adapt to the different trial balance
Dim startline, account_column, description_column, debit_column, credit_column As Integer
Dim column_to_test As String
Dim Target_Column(0 To 3) As Integer
'setting the variables
address = "blabla"
startline = 5
account_column = 1
description_column = 2
debit_column = 3
credit_column = 4
column_to_test = "A"
Target_Column(0) = 1
Target_Column(1) = 4
Target_Column(2) = 5
Target_Column(3) = 6
Worksheet_general_balance = 1
Set Transform_file = ActiveWorkbook
Set General_Balance = Workbooks.Open(address)
With General_Balance.Worksheets(Worksheet_general_balance)
Set LastCell = .Cells(.Rows.Count, column_to_test).End(xlUp)
LastCellNumber = LastCell.Row
End With
MsgBox "General TB sheet name: " & General_Balance.Worksheets(Worksheet_general_balance).Name
'3. save the required range from the source file
General_Balance.Worksheets(Worksheet_general_balance).Activate
Set Account_Number = General_Balance.Worksheets(Worksheet_general_balance).Range(Cells(startline, account_column), Cells(LastCellNumber, account_column))
Set Account_Description = General_Balance.Worksheets(Worksheet_general_balance).Range(Cells(startline, description_column), Cells(LastCellNumber, description_column))
Set Debit = General_Balance.Worksheets(Worksheet_general_balance).Range(Cells(startline, debit_column), Cells(LastCellNumber, debit_column))
Set Credit = General_Balance.Worksheets(Worksheet_general_balance).Range(Cells(startline, credit_column), Cells(LastCellNumber, credit_column))
'copying the value to the file
Transform_file.Activate
Transform_file.Worksheets("general balance").Range(Cells(6, Target_Column(0)), Cells(LastCellNumber - startline + 6, Target_Column(0))).Value = Account_Number.Value
Transform_file.Worksheets("general balance").Range(Cells(6, Target_Column(1)), Cells(LastCellNumber - startline + 6, Target_Column(1))).Value = Account_Description.Value
'up to this point, everything works well
'THE FOLLOWING TWO LINES EITHER ONE OF THEM MAKE EXCEL CRASH
Transform_file.Worksheets("general balance").Range(Cells(6, Target_Column(2)), Cells(LastCellNumber - startline + 6, Target_Column(2))).Value = Debit.Value
Transform_file.Worksheets("general balance").Range(Cells(6, Target_Column(3)), Cells(LastCellNumber - startline + 6, Target_Column(3))).Value = Credit.Value
General_Balance.Close
If I replace the range name Debit or Credit by Account_Number for example, the macro will finish, so i guess it's not about the destination.
I tried to put this code:
For Each cell In Debit.Cells
MsgBox cell.Value
Next cell
Before the problematic lines, and it goes through all the cells without any problems.
I can't find any reason why it's not working... any idea ?
First I think you should add some On Error to your code, including a
MsgBox Err.Description,,Err.Number.
My first guess is that you are trying to write to an already open/locked file.
Sub test()
On Error GoTo Hell
'Do lots of things
'...
Adios:
Exit Sub
Hell:
MsgBox Err.Description, vbCritical, "Error " & Err.Number
Resume Adios
Resume
End Sub
With the above sample, when you get the message box, press Ctrl+Break, move the yellow dot from the resume Adios to the Resume line, then press F8. Now you are on the line that caused the error.
Another way is to start your Sub in debug mode, and press F8 until it crashes (and remember where that was !).

Cannot use named range when it is empty

I have a named range lstVendors that refers to: =OFFSET(Data!$W$2,0,0,COUNTA(Data!$W$2:$W$400),1). I want this range to be populated when the workbook opens. I have the following code for this:
Private Sub Workbook_Open()
Application.WindowState = xlMaximized
Dim rslt()
Dim i As Integer
Dim n As Integer
Dim startRng As Range
Dim DropDown1 As DropDown
ThisWorkbook.Sheets("Dashboard").Shapes("TextBox 6").Visible = False
' Range("lstVendors").Offset(0, 0).Value = "Please Select..."
' Set DropDown1 = ThisWorkbook.Sheets("Dashboard").DropDowns("Drop Down 1")
' DropDown1.Value = 1
On Error Resume Next
If Not IsError(Range("lstVendors")) Then
Range("lstVendors").ClearContents
End If
On Error GoTo 0
rslt = Application.Run("SQLite_Query", "path/to/my/sqlite", "SELECT PROGRAM_ID FROM VENDOR;")
Set startRng = Range("lstVendors")
i = 0
For n = 2 To UBound(rslt)
Range("lstVendors").Offset(i, 0).Value = rslt(n)(0)
i = i + 1
Next n
End Sub
It errors on the Set startRng = Range("lstVendors"). I know this is because there's nothing in the range when I'm trying to set it, because if I put one entry into the named range, the set works, however, I need it populated by the sqlite query on each open as the data changes.
Any suggestions much appreciated.
Try this. You have a dynamic range that doesn't evaluate after you clear the contents. To avoid this, there are probably several ways, but easy to simply hardcode the startRange variable so that it always points to Data!$W$2 address, which is (or rather, will become) the first cell in your lstVendors range.
Private Sub Workbook_Open()
Dim rslt()
Dim i As Integer
Dim n As Integer
Dim startRng As Range
Dim DropDown1 As DropDown
Dim rngList As Range
'// Define your startRange -- always will be the first cell in your named range "lstVendors"
' hardcode the address because the dynamic range may not evalaute.
Set startRange = Sheets("Data").Range("W2")
'// Empty th lstVendors range if it exists/filled
On Error Resume Next
Range("lstVendors").Clear
On Error GoTo 0
'// Run your SQL query
rslt = Application.Run("SQLite_Query", "path/to/my/sqlite", "SELECT PROGRAM_ID FROM VENDOR;")
i = 0
'// Print results to the Worksheet, beginning in the startRange cell
For n = 2 To UBound(rslt)
'Increment from the startRange cell
startRange.Offset(i, 0).Value = rslt(n)(0)
i = i + 1
'Verify that "lstVendors" is being populated
Debug.Print Range("lstVendors").Address
Next n
End Sub
Thanks for the suggestions. Here is what I ended up doing in order to get around my problem. It involves adding something I didn't specify would be ok in my original question, so David's answer is great if what I did isn't an option. I first populated the first two cells in my named range with "Please Select..." and "All". In Sub Workbook_Open() we do this:
Private Sub Workbook_Open()
Application.WindowState = xlMaximized
Dim rslt()
Dim i As Integer
Dim n As Integer
Dim startRng As Range
Dim DropDown1 As DropDown
' Disable our not found message
ThisWorkbook.Sheets("Dashboard").Shapes("TextBox 6").Visible = False
' Set our start range to our named range
Set startRng = Range("lstVendors")
' Grab all vendor names
rslt = Application.Run("SQLite_Query", "path/to/my/sqlite", "SELECT PROGRAM_ID FROM VENDOR;")
' Print result. Skip first two rows as constants "Please Select..." and "All" are populated there
i = 2
For n = 2 To UBound(rslt)
startRng.Offset(i, 0).Value = rslt(n)(0)
i = i + 1
Next n
End Sub
Then we will create Sub Workbook_BeforeClose:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
' Disable the save changes dialog. This workbook will be locked up for display only. No need to confuse the user.
Application.DisplayAlerts = False
' Clear everything below the "Please Select..." and "All" cells in the named range
On Error Resume Next
Range("lstVendors").Offset(2, 0).ClearContents
On Error GoTo 0
' Save the changes to the named range
ThisWorkbook.Save
Application.DisplayAlerts = True
End Sub
This information is going to populate a drop down, so having Please Select and All hardcoded into the named range is acceptable for me. If that stipulation doesn't work for someone else looking at this in the future, please use David's suggestion! Thanks again!