VBA: test if workbook is nothing - vba

Edit: My real question is how to test if object was set that was instantiated. I am not really looking to "correct" my code. Its just an example.
I have a function that returns a workbook:
Edit: Added code
Sub GetWb() as Workbook
Application.DisplayAlerts = False
Application.EnableEvents = False
On Error Resume Next
Set wM = Application.Workbooks.Open("Z:\somepath.xlsm", ReadOnly:=True)
Application.EnableEvents = True
Application.DisplayAlerts = True
On Error GoTo 0
end sub
In another sub I want to check if that object was set properly by the function. I usually do something like this with objects generally:
dim w as Workbook
set w = GetWb
if w is nothing then
debug.print "no workbook"
else
debug.print "workbook"
end if
However, the is nothing test does not work because the object is instantiated, but was not set so it is something, not nothing.
I have resorted to this ugly solution, which works fine:
dim w as Workbook
set w = GetWb
on error goto someerrorhandling
if w.name = "" then
end if
on error goto 0
'other code here
someerrorhandling:
msgbox "no workbook"
In other words, I check a property of the object to force an error, or not. There must be a better/cleaner way.
I checked and this link states that the way I am doing it is the best way:
VBA: Conditional - Is Nothing

change your error handling for GetWB so it returns nothing incase of error, also use Function instead of sub.
Function GetWb() As Workbook
Application.DisplayAlerts = False
Application.EnableEvents = False
On Error GoTo errHandler:
Set GetWb = Application.Workbooks.Open("Z:\somepath.xlsm", ReadOnly:=True)
Application.EnableEvents = True
Application.DisplayAlerts = True
errHandler:
If Err.Number <> 0 Then
Set GetWb = Nothing
Application.EnableEvents = True
Application.DisplayAlerts = True
End If
End Function

Related

Automation Error - Unspecified Error (Runtime Error -2147467259)

I need some help. I am new to Excel VBA. I am trying to create a userform for stock inventory records and I been geting the automation error -2147467259. My problem is that the code works but after a few mouse clicks (10 or more) or after long usage, I keep getting this error. My code:
Private Sub cbPickID_Change()
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim tbl_issuance As ListObject
Set tbl_issuance = shIssuance.ListObjects("tblIssuance")
If Not tbl_issuance.DataBodyRange Is Nothing Then
tbl_issuance.DataBodyRange.Delete
End If
Dim tbl_pick As ListObject
Set tbl_pick = shPickList.ListObjects("tblPickList")
On Error GoTo ErrDetect
With tbl_pick.DataBodyRange
.AutoFilter field:=1, Criteria1:=Me.cbPickID.Value
End With
Dim pick_row As Long
pick_row = shPickList.Range("A" & Application.Rows.Count).End(xlUp).Row
shPickList.Range("A3:L" & pick_row).SpecialCells(xlCellTypeVisible).Copy
shIssuance.Range("A3").PasteSpecial (xlPasteValuesAndNumberFormats)
tbl_pick.AutoFilter.ShowAllData
Application.CutCopyMode = False
Dim issued_row As Long
issued_row = shIssuance.Range("A" & Application.Rows.Count).End(xlUp).Row
With Me.lbPickList
.ColumnHeads = True
.ColumnCount = 12
.ColumnWidths = ("40,40,40,110,0,45,40,60,90,0,0,0")
.RowSource = shIssuance.Range("A3:L" & issued_row).Address
End With
ErrDetect:
If Err.Number = 1004 Then
MsgBox "No records found!"
Exit Sub
End If
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
When I click debug, the error point at this
tbl_issuance.DataBodyRange.Delete
all my reference are in the same file. if I want to use the Excel VBA again, I need to close all Excel file and re-open them again.
any advice is highly appreciated.

Find out if excel is in edit mode from word-vba

I am trying to find out from word if excel is in edit mode, I looked at this thread and tried to modify it, but it does not work if you have excel in edit mode, then run it, then exit edit mode rerun it still says it is edit mode:
'*********************************************************
'********* define if we need to close excel after sub is done
'***********************************************************
Public Function setExcelObject(ByRef oXLApp As Object) As Boolean
On Error Resume Next
Set oXLApp = GetObject(, "Excel.Application")
If oXLApp Is Nothing Then
Set oXLApp = CreateObject("Excel.Application")
End If
setExcelObject = IsInEditMode(oXLApp)
End Function
Public Function IsInEditMode(ByRef exapp As Object) As Boolean
If exapp.Interactive = False Then
IsInEditMode = False
Else
On Error GoTo terminate
exapp.Interactive = False
exapp.Interactive = True
IsInEditMode = False
End If
Exit Function
terminate:
IsInEditMode = True
Exit Function
End Function
Note: It also takes awful long(15s) to figure out that it is in edit mode...
Here is a working code:
'**********************************************************************
'********* See if we can open excel, true is Yes we can work with excel
'**********************************************************************
Public Function setExcelObject(ByRef oXLApp As Object) As Boolean
On Error Resume Next
Set oXLApp = GetObject(, "Excel.Application")
If oXLApp Is Nothing Then
Set oXLApp = CreateObject("Excel.Application")
End If
setExcelObject = Not IsInEditMode(oXLApp)
If setExcelObject = False Then Set oXLApp = Nothing
End Function
' *****************************************************************
' **************** Check if excel is in edit mode ****************
'*****************************************************************
Public Function IsInEditMode(ByRef exapp As Object) As Boolean
On Error GoTo terminate
exapp.Interactive = False
exapp.Interactive = True
IsInEditMode = False
Exit Function
terminate:
IsInEditMode = True
Exit Function
End Function
' *************************************************************
' *************** Check if excel is open, true, means we should not close excel after we are done.....
'*****************************************************************
Function ExcelOpen() As Boolean
ExcelOpen = FindWindow("XLMAIN", vbNullString)
End Function
The above code I then call from several procedure like this:
' Get excel object
If Not FileHandling.setExcelObject(oXLApp) Then
failMessage = "You are editing a cell in excel, stop doing that!"
GoTo terminate
End If
' check if we need to close after
closeExcelMy = FileHandling.ExcelOpen
'See if we can open workbook
If Not FileHandling.GetWorkbook(wbName, oXLApp, xlApp) Then
failMessage = "Failed to open workbook"
GoTo terminate
End If
oXLApp.Visible = True

Excel VBA Issue

Using the following code to auto upper two columns,
Private Sub Worksheet_Change(ByVal Target As Range)
If Not (Application.Intersect(Target, Range("C2:C5000", "P3:P5000")) _
Is Nothing) Then
With Target
Application.EnableEvents = False
.Value = UCase(.Value)
Application.EnableEvents = True
End With
End If
End Sub
Works perfectly, the problem is, if a user selects multiple cells, and hits delete, it errors, then the user hits End and the function no longer works. protected. Run-time error 13, type mismatch.
Doesn't matter if the cell is empty or not, still get the error.
Thanks in advance.
The answer of #ScottHoltzman solves the issue of the current problem, where an error is raised when you apply UCASE to an Array. When the Target range has more than one cell its .Value is an array, and UCase does not accept an array parameter.
Your routine will exit this line (.Value = UCase(.Value)) and will miss the next line that resets Application.EnableEvents = True. After that, you end up working with events disabled, so all your event handling routines will stop working, not only this one (in case you had other such routines).
To avoid these situations the good approach is to implement proper error handling in event handlers, following this structure
Sub my_Handler()
On Error Goto Cleanup
Application.EnableEvents = False: Application.ScreenUpdating = False ' etc..
''''''''''''''''''''''''''''''''''
'
' normal code of the routine here
'
''''''''''''''''''''''''''''''''''
Cleanup:
if Err.Number <> 0 Then MsgBox Err.Description
Application.EnableEvents = True, Application.ScreenUpdating = True ' etc..
End Sub
To apply it to your routine:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Cleanup
Application.EnableEvents = False: Application.ScreenUpdating = False ' etc..
If Not (Application.Intersect(Target, Range("C2:P5000")) Is Nothing) Then
Target.value = UCase(Target.value)
End If
Cleanup:
If Err.Number <> 0 Then msgBox Err.Description
Application.EnableEvents = True: Application.ScreenUpdating = True ' etc..
End Sub
Importantly, don't use this structure automatically for all you routines, only Event handlers or eventually macros ythat you would invoke from the GUI. Other routines are usually called from these handlers or macros, so you can write them normally.
I tried putting this in a comment to the answer, but was too long, so sorry..
#a-s-h #a.s.h
This one worked the best, with a slight modification. Thanks!
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Cleanup
Application.EnableEvents = False: Application.ScreenUpdating = False ' etc..
If Not (Application.Intersect(Target, Range("C2:C5000", "P3:P5000")) Is Nothing) Then
Target.Value = UCase(Target.Value)
End If
Cleanup:
If Err.Number <> 0 Then GoTo EndLine
EndLine:
Application.EnableEvents = True: Application.ScreenUpdating = True ' etc..
End Sub
Performs uppercase, and deletes multiples at once without any errors, or MsgBox's.
If they are selecting multiple cells then my thinking is that you would want to use SelectionChange macro instead, like this
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
If ((Target.Address = Target.EntireRow.Address Or _
Target.Address = Target.EntireColumn.Address)) Then Exit Sub
Application.EnableEvents = False
If Not (Application.Intersect(Target, Range("C2:C5000", "P3:P5000")) _
Is Nothing) Then
On Error GoTo endItAll
For Each aCell In Target.Cells
Range(aCell.Address) = UCase(Range(aCell.Address))
Next aCell
End If
endItAll:
Application.EnableEvents = True
SecondsElapsed = Round(Timer - StartTime, 2)
Debug.Print SecondsElapsed
End Sub
Or you could change it back to the worksheet_Change macro like below and it will not error if the user selects multiple cells or deletes cells without causing an error. The error handler is there - Like in A.S.H. 's solution, but I haven't yet seen it needed in my testing.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Not (Application.Intersect(Target, Range("C1:C5000", "D1:D5000")) _
Is Nothing) Then
On Error GoTo endItAll
For Each aCell In Target.Cells
Range(aCell.Address) = UCase(Range(aCell.Address))
Next aCell
End If
endItAll:
Application.EnableEvents = True
End Sub
Account for multiple cells this way:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not (Application.Intersect(Target, Range("C2:C5000", "P3:P5000")) _
Is Nothing) Then
Dim rCell as Range
Application.EnableEvents = False
For each rCell in Target
rCell.Value = UCase(rCell.Value)
Next
Application.EnableEvents = True
End If
End Sub

Why is this "Delete Method of Range class failed" error popping up?

I am trying to figure out why this "Delete Method of Range Class" error is popping up. It does not always occur when this macro runs, which makes it all the more perplexing.
Can anybody explain this?
Sub ResetSheet()
If WindowsOS Then
'*******************************************************************************************************'
'Resets the Data sheet. Called by the resetSheetButton procedure (located in module: m7_Macros1_5). '
'Also called by the OkCommandButton_Click procedure in the OnOpenUserForm form. '
'*******************************************************************************************************'
Application.EnableEvents = False
Sheet4.Visible = True
Sheet4.Activate
Sheet4.Select
Sheet4.Rows("2:101").Select
Selection.Copy
'TC Edit
Sheet1.Activate
Sheet1.Range("A2").PasteSpecial (xlPasteAll)
'Sheet1.Paste
Sheets("Data").Select
Sheet1.Rows("102:10000").EntireRow.Delete
Sheet4.Visible = False
'TC Edit 2.0 - Adding code to reset the exception checkboxes
If WindowsOS Then
Call resetCheckBoxes
End If
Application.EnableEvents = True
This is the macro code that causes the error (sometimes)
This is the error pop-up
try with below simplified code
Sub ResetSheet()
'If WindowsOS Then
Application.EnableEvents = False
With Worksheets("Sheet4")
.Visible = True
.Rows("2:101").Copy Worksheets("Sheet1").Range("A2")
End With
With Worksheets("Sheet1")
.Rows("102:101").EntireRow.Delete
End With
Worksheets("Sheet4").Visible = False
If windowsOS Then
Call resetCheckBoxes
End If
Application.EnableEvents = True
End Sub

Dynamic Macro That Auto Updates Graph Scale Misfiring When File Opens

I have an issue that makes no sense to me.
Option Explicit
Private Sub Worksheet_Calculate()
Dim Chtob As ChartObject
Dim wks As Worksheet
Set wks = ActiveSheet
On Error GoTo Terminate
For Each Chtob In ActiveSheet.ChartObjects
With Chtob.Chart
If wks.Range("$G$2").Value <> .Axes(xlCategory).MaximumScale Then
.Axes(xlCategory).MaximumScale = wks.Range("$G$2").Value
End If
If wks.Range("$C$2").Value <> .Axes(xlCategory).MinimumScale Then
.Axes(xlCategory).MinimumScale = wks.Range("$C$2").Value
End If
If wks.Range("$G$2").Value <> .Axes(xlCategory, xlSecondary).MaximumScale Then
.Axes(xlCategory, xlSecondary).MaximumScale = wks.Range("$G$2").Value
End If
If wks.Range("$C$2").Value <> .Axes(xlCategory, xlSecondary).MinimumScale Then
.Axes(xlCategory, xlSecondary).MinimumScale = wks.Range("$C$2").Value
End If
End With
Next
Exit Sub
Terminate:
MsgBox "Storm Event Not Valid, Please check if such event number exists"
End
Exit Sub
This macro is used on a tab that has two charts. When a certain cell changes the macro updates the graph scale. This tab will be then duplicated numerous times to show different time events.
The issue arises when someone else tries to open this file. The moment the file is open they get the error to pop up as many times as the amount of tabs created. This for some reason causes a different tab with a different graph to reset it's x scale. This different tab does not have the dynamic macro attached to it and no other macros are being used.
I want to say that a different version of Excel might be part of the problem, but there are times when this doesn't happen.
The way it should work is when somebody enters the wrong value in cell B2 the macro can't execute. So instead of going into debug, one gets an error message. So I need the error portion of the macro to be there.
I should mention that the tab also has another dynamic macro that automatically renames the tab name if the same cell changes.
Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$B$2" Then
If Not Target.HasFormula Then
If Not Target.Value = vbNullString Then
On Error GoTo ErrHandler
ActiveSheet.Name = "Event" & " " & Target.Value
End If
End If
End If
Exit Sub
ErrHandler:
MsgBox "Error " & Err & ":" & Error(Err)
On Error GoTo 0
End Sub
Thanks to comments made by Scott, my issue has never poped up again
I just changed Set wks = Activesheet to Set wks = Me and then changed all of the wks to Me in the script
Option Explicit
Private Sub Worksheet_Calculate()
Dim Chtob As ChartObject
Dim wks As Worksheet
Set wks = Me
On Error GoTo Terminate
For Each Chtob In Me.ChartObjects
With Chtob.Chart
If wks.Range("$G$2").Value <> .Axes(xlCategory).MaximumScale Then
.Axes(xlCategory).MaximumScale = wks.Range("$G$2").Value
End If
If wks.Range("$C$2").Value <> .Axes(xlCategory).MinimumScale Then
.Axes(xlCategory).MinimumScale = wks.Range("$C$2").Value
End If
If wks.Range("$G$2").Value <> .Axes(xlCategory, xlSecondary).MaximumScale Then
.Axes(xlCategory, xlSecondary).MaximumScale = wks.Range("$G$2").Value
End If
If wks.Range("$C$2").Value <> .Axes(xlCategory, xlSecondary).MinimumScale Then
.Axes(xlCategory, xlSecondary).MinimumScale = wks.Range("$C$2").Value
End If
End With
Next
Exit Sub
Terminate:
MsgBox "Storm Event Not Valid, Please check if such event number exists"
End
End Sub