Excel VBA Retrieve Constant from Worksheet Code Fails When New Code is Written - vba

In an attempt to retrieve constants from each worksheet in some reporting workbooks I use, three years ago I wrote some code that gets included in each worksheet. Here's an example of the code:
Option Explicit
' Determine the type of worksheet
Private Const shtType As String = "CR"
Public Function GetShtType()
GetShtType = shtType
End Function
In other code that gets the values from the worksheets for processing, the following section of code is used, where 'wksToCheck' is the worksheet in question. This code is stored in a personal macro workbook, not in the workbook with the worksheet code:
' Get the sheet 'type' if it has one
On Error Resume Next
shtType = wksToCheck.GetShtType()
If Err.Number <> 0 Then
' We do not have a type
shtType = "Unknown"
Err.Clear
End If ' Err.Number...
On Error GoTo Error_BuildTemplateWbk
My problem is, I use the code above to process workbooks several times a week, and I have for the past three years. Now, I am trying to write some new code with the above block to process the report workbooks in a different way. However, when I run code with the above block now, I get a 'Method or Data Member Not Found' error on the '.GetShtType()' portion of the code. I cannot compile the code and of course, consequently, the code doesn't work. I have tried adding the worksheet code to a worksheet in the macro workbook to see if that would fix the problem. It hasn't. Does anyone have any ideas? I am running Excel 2013 on a Windows 7 PC. Any ideas?
Brian

Using late-binding, should avoid the error, Dim wksToCheck As Object, but you'll lose the intellisense.
If you're open to alternatives, you may have better luck simply using the CallByName function, or using worksheet's CustomProperties.
Using CallByName preserves backwards compatibility with your older workbooks if needed:
shtType = CallByName(wksToCheck, "GetShtType", VbMethod)
Or, using CustomProperties instead of a custom method, in your worksheets:
Private Sub Worksheet_Activate()
Const PropName$ = "ShtType"
Const ShtType$ = "CR"
On Error Resume Next
Me.CustomProperties(PropName) = ShtType$
If Err.Number = 13 Then
Me.CustomProperties.Add "PropName", ShtType
End If
End Sub
Then,
' Get the sheet 'type' if it has one
On Error Resume Next
shtType = wksToCheck.CustomProperties("ShtType")
If Err.Number = 13 Then
' We do not have a type
shtType = "Unknown"
Err.Clear
End If ' Err.Number...
On Error GoTo Error_BuildTemplateWbk

Related

Excel VBA Run-time error 438 first time through code

I'm a novice self-taught VBA programmer knowing just enough augment Excel/Access files here and there. I have a mysterious 438 error that only popped up when a coworker made a copy of my workbook (Excel 2013 .xlsm) and e-mailed it to someone.
When the file is opened, I get a run time 438 error when setting a variable in a module to a ActiveX combobox on a sheet. If I hit end and rerun the Sub, it works without issue.
Module1:
Option Private Module
Option Explicit
Public EventsDisabled As Boolean
Public ListBox1Index As Integer
Public cMyListBox As MSForms.ListBox
Public cMyComboBox As MSForms.Combobox
Public WB As String
Sub InitVariables()
Stop '//for breaking the code on Excel open.
WB = ActiveWorkbook.Name
Set cMyListBox = Workbooks(WB).Worksheets("Equipment").Listbox1
Set cMyComboBox = Workbooks(WB).Worksheets("Equipment").Combobox1 '//438 here
End Sub
Sub PopulateListBox() '//Fills list box with data from data sheet + 1 blank
Dim y As Integer
If WB = "" Then InitVariables
ListBox1Index = cMyListBox.ListBoxIndex
With Workbooks(WB).Worksheets("Equipment-Data")
y = 3
Do While .Cells(y, 1).Value <> ""
y = y + 1
Loop
End With
Call DisableEvents
cMyListBox.ListFillRange = "'Equipment-Data'!A3:A" & y
cMyListBox.ListIndex = ListBox1Index
cMyListBox.Height = 549.75
Call EnableEvents
End Sub
...
PopulateListBox is called in the Worksheet_activate sub of the "Equipment" sheet.
All my code was in the "Equipment" sheet until I read that was bad form and moved it to Module1. That broke all my listbox and combobox code but based on the answer in this post I created the InitVariables Sub and got it working.
I initially called InitVariables once from Workbook_open but added the If WB="" check after WB lost its value once clicking around different workbooks that were open at the same time. I'm sure this stems from improper use of Private/Public/Global variables (I've tried understanding this with limited success) but I don't think this is related to the 438 error.
On startup (opening Excel file from Windows Explorer with no instances of Excel running), if I add a watch to cMyComboBox after the code breaks at "Stop" and then step through (F8), it sets cMyComboBox properly without error. Context of the watch does not seem to affect whether or not it prevents the error. If I just start stepping or comment out the Stop line then I get the 438 when it goes to set cMyComboBox.
If I add "On Error Resume Next" to the InitVariables then I don't error and the project "works" because InitVariables ends up getting called again before the cMyComboBox variable is needed and the sub always seems to work fine the second time. I'd rather avoid yet-another-hack in my code if I can.
Matt
Instead of On Error Resume Next, implement an actual handler - here this would be a "retry loop"; we prevent an infinite loop by capping the number of attempts:
Sub InitVariables()
Dim attempts As Long
On Error GoTo ErrHandler
DoEvents ' give Excel a shot at finishing whatever it's doing
Set cMyListBox = ActiveWorkbook.Worksheets("Equipment").Listbox1
Set cMyComboBox = ActiveWorkbook.Worksheets("Equipment").Combobox1
On Error GoTo 0
Exit Sub
ErrHandler:
If Err.Number = 438 And attempts < 10 Then
DoEvents
attempts = attempts + 1
Resume 'try the assignment again
Else
Err.Raise Err.Number 'otherwise rethrow the error
End If
End Sub
Resume resumes execution on the exact same instruction that caused the error.
Notice the DoEvents calls; this makes Excel resume doing whatever it was doing, e.g. loading ActiveX controls; it's possible the DoEvents alone fixes the problem and that the whole retry loop becomes moot, too... but better safe than sorry.
That said, I'd seriously consider another design that doesn't rely so heavily on what appears to be global variables and state.

Error from passing workbook object to another function (Run-time "Automation error")

I have a set of functions that is meant to read data from various worksheets in a separate workbook. The main routine uses a loop to go through various sheet names and within the function two levels down, it looks into the workbook to grab data. I am getting runtime error '-2147023174' (800706ba) Automation error at various points in this function, and am wondering where the error lies.
Sub mainRoutine()
'opens workbook and runs makeFile function on a loop
Dim wbOBJ As Workbook, oFile As integer
Set wbOBJ = Workbooks.Open(filePath, ReadOnly:=True)
For k = 1 To 20
makeFile myBox.List(k), wbOBJ
Next k
wbOBJ.Saved = True
wbOBJ.Close
End Sub
Function makeFile(ShtName As String, ByRef wbOBJ as Workbook)
'calls various procedures from which to collect data
'and passes workbook object along
debug.print wbOBJ.Sheets(1).Cells(1,1).Value
printInfoA(ShtName)
printInfoB(ShtName, wbOBJ)
End Function
Function printInfoB(ShtName as String, ByRef wbOBJ as Workbook)
'assigns object to a sheet inside the other workbook, collects data
Dim wsOBJ As Worksheet
Set wsOBJ = wbOBJ.Sheets(ShtName)
For j = 1 to 10
Thisworkbook.Sheets(1).Cells(j,1) = wsObj.Cells(j, 1)
Next j
End Function
Even in the makeFile function, the error pops up on the debug.print line, even though the debug.print works, and produces the correct value.
If I suppress that line, the same error will instead occur in the mainRoutine on the wbOBJ.Saved = True line. What is causing these errors?
(Note that I originally tried to organize it this way, instead of having the workbook open and close everytime I ran the printInfoB function, as I am trying to increase speed.)

Why does assigning a reference in my spreadsheet sometimes work and sometimes not?

I have a few cells in my excel workbook which are available for a client to put his own values. I wanted the workbook to initialize those cells with default values. In order to do so I have a worksheet "Arkusz do makr", where I store the values.
In a module "GM" I declare a variable to reference my worksheet easier like this:
Public M As Worksheet
Then I initialize this variable and set my default values like this (in ThisWorkbook):
Private Sub Workbook_Open()
Set M = Worksheets("Arkusz do makr")
Worksheets("Values").Range("Value1") = M.Range("Value1")
Worksheets("Values").Range("Value2") = M.Range("Value2")
Worksheets("Values").Range("Value3") = M.Range("Value3") `etc
End Sub
Now sometimes this works like a charm, and sometimes, when I open the workbook I get a
Run-time error '91': Object variable or With block variable not set.
Could someone please explain this behaviour to me? Additionally I would like to ask if my approach makes sense, since I have a hard time grasping the order of events in excel as well as the range of its objects.
EDIT: Additionally I should mention that the Debug function highlights the first Worksheets... line in my code. In specific worksheets I reference the M object as well, though I thought it changes anything here...
Try to change the code of this Sub like below.
I have added a simple error handling - if there is no worksheet "Arkusze do makr" or "Values" in your workbook, warning message is displayed and default values are not copied.
You can find more comments in code.
Private Sub Workbook_Open()
Dim macrosSheet As Excel.Worksheet
Dim valuesSheet As Excel.Worksheet
'------------------------------------------------------------------
With ThisWorkbook
'This command is added to prevent VBA from throwing
'error if worksheet is not found. In such case variable
'will have Nothing as its value. Later on, we check
'the values assigned to those variables and only if both
'of them are different than Nothing the code will continue.
On Error Resume Next
Set macrosSheet = .Worksheets("Arkusz do makr")
Set valuesSheet = .Worksheets("Values")
On Error GoTo 0 'Restore default error behaviour.
End With
'Check if sheets [Values] and [Arkusz do makr] have been found.
'If any of them has not been found, a proper error message is shown.
'In such case default values are not set.
If valuesSheet Is Nothing Then
Call VBA.MsgBox("Sheet [Values] not found")
ElseIf macrosSheet Is Nothing Then
Call VBA.MsgBox("Sheet [Arkusz do makr] not found")
Else
'If both sheets are found, default values are copied
'from [Arkusz do makr] to [Values].
'Note that if there is no Range named "Value1" (or "Value2" etc.)
'in any of this worksheet, another error will be thrown.
'You can add error-handling for this case, similarly as above.
With valuesSheet
.Range("Value1") = macrosSheet.Range("Value1")
.Range("Value2") = macrosSheet.Range("Value2")
.Range("Value3") = macrosSheet.Range("Value3")
End With
End If
End Sub

Macro from Word does not work in Excel can it be modified for excel?

I realize that it is probably not supposed to work, but I have this visual basic code from a Word Macro that opens a piece of software linked to a piece of fluke equipment connectded to the computer and when the macro runs it inserts the image from the screen of the equipment into the word document, is there anyway to change this code so it does the same thing in excel?
' InsertInsertActiveScreen Module
' Function: Start FlukeView if required
' Locate position for inserting Screen
' Insert active screen at cursor position
Global Const AppName = "FlukeView ScopeMeter"
Global StartedFV90 As String
' Declare constant values
Private Const ER_NONE = 0
Private Const ER_DDE_CMD_UNK = 25
Private Const ER_DDE_NO_INIT = 26
Private Const ER_DDE_NO_CONN = 27
Private Const ER_DDE_NO_SERVER = 28
Public Sub MAIN()
Dim chan As Long
Dim Status As String
Dim wordApp As Object
Set wordApp = CreateObject("Word.Application")
' Display cursor as an hourglass
Application.Cursor = xlWait
If (wordApp.Tasks.Exists(AppName) = False) Then
' Start FlukeView as server (-s) Modification of these statement is required if
' FlukeView is installed in another directory as the QReport.dot template
Call Shell(ActiveWorkbook.Path & Application.PathSeparator & "FV90WIN.EXE -s", vbMinimizedNoFocus)
' Reminder for terminating FlukeView when closing the document
StartedFV90 = "STARTED"
Else
If (Len(StartedFV90) = 0) Then
' Reminder to prevent terminating FlukeView when closing the document
StartedFV90 = "NOT STARTED BY Fluke View Report"
End If
End If
' Setup a DDE link with FlukeView
chan = DDEInitiate(App:="FV90WIN", Topic:="FlukeView")
While (Val(DDERequest(Channel:=chan, Item:="DDEStatus")) <> ER_NONE)
' Wait until FlukeView is ready to receive commands
Wend
Call DDEExecute(Channel:=chan, String:="Connect")
DoEvents
' Transfer the active screen and place it on the clipboard
Call DDEExecute(Channel:=chan, String:="Screen")
' Read completion status
Status$ = DDERequest(Channel:=chan, Item:="DDEStatus")
If (Val(Status) = ER_NONE) Then
' locate bookmark for pasting contents
Call Selection.GoTo(What:=wdGoToBookmark, Name:="InstrumentScreen")
' Paste the contents of the clipboard into the document
Call Selection.PasteSpecial
' Convert to Inline Shape to prevent overlapping images
For Each ScreenPicture In ActiveSheet.Shapes
If ScreenPicture.Type = msoPicture Then
ScreenPicture.ConvertToInlineShape
End If
Next ScreenPicture
Else
' Error occurred
Call DDEExecute(Channel:=chan, String:="Error" + Status$)
End If
' Terminate DDE connection
Call DDETerminate(Channel:=chan)
' Restore cursor
Application.Cursor = xlDefault
End Sub
This is what i have no and i get a Run-time error '13': Type mismatch?
It's hard to know what works and what doesn't in your macro when transferred from Word to Excel. However, just looking at the code I can offer a few pointer to put you in the right direction:
Replace Global with Private for your global definitions. If you don't, Excel will give you a compile error: "Constants, fixed-length strings, arrays, user-defined types and Declare statements not allowed as Public members of object modules."
Replace:
System.Cursor = wdCursorWait
System.Cursor = wdCursorNormal
with:
Application.Cursor = xlWait
Application.Cursor = xlDefault
Excel doesn't have an ActiveDocument but an ActiveWorkbook and an ActiveSheet for the active sheet in the active workbook.
I don't think you can find an easy way to call:
Tasks.Exists()
in Excel. However, you can cheat and call into Word to do this job for you:
Dim wordApp As Object
Set wordApp = CreateObject("Word.Application")
...
wordApp.Tasks.Exists()
...
Set wordApp = Nothing ' Call this when you're done with it.
The DDEExecute signature is slightly different for Excel, but I've never used it myself so I can't tell you if this will work. The second parameter is not called Command but String in Excel. Try replacing Command:= with String:= and see if that works.
Selection.Copy will copy the selected range, which is similar to Word. But you'll need Selection.PasteSpecial to paste it. Of course, in that case, you're copying and pasting over the same thing so you'll see no different. I suggest looking at the Copy/Paste functionality of the Range object in Excel.
Anyway, that's all I can think of right now. It won't cover everything that's different between Word and Excel, but it's a start. No one here would be able to help you fully, since you're relying on the functionality of a specific program ("FlukeView" or "FV90WIN.EXE") for a lot of the work.

Excel VBA Can't access sheet on external workbook

I have created a custom function in Excel using VBA. I'm trying to get data from a different workbook using the Workbooks.Open(path) command. Here's my code:
Option Explicit
Function TestFunction() As String
mySub
TestFunction = "Success."
End Function
Sub mySub()
Dim path As String
Dim wk As Workbook
path = "C:\Users\jg\Desktop\machine_data.xlsm"
Set wk = Workbooks.Open(path)
Dim ws As Worksheet
Set ws = wk.Sheets(1)
Debug.Print ws.Range("A2")
End Sub
Sub Test()
Debug.Print (TestFunction())
End Sub
Now my problem is the following:
When I run the Sub Test() within the VBA environment from Excel everything works as planned. machine_data.xlsm gets opened and the field A2 shows up in debug.
Once I go to the workbook where I defined this module in and type =TestFunction() into a cell, I get a #VALUE!. The file also doesn't get opened.
If I comment these two lines:
Set ws = wk.Sheets(1)
Debug.Print ws.Range("A2")
the cell will show Success!, but the file still doesn't open.
What am I doing wrong? Both workbooks are .xlsm files. I am using Microsoft Office Excel 2007.
Just throw everything from mySub into the test function and if everything is successful have test function return the value of the cell. So testFunc = ws.Range("A2").
As DaveU already stated UDFs can only return values. I found a different workaround simply calling the function from within the VBA environment which lets me modify cell contents wherever I'd like.