VBA how to determine which application is being used - excel

I have a VBA function I use in MS Access and MS Excel. When used in MS Excel I use Application.Volatile but when it is placed or used in MS Access it will not compile. Is there a way to make this line interchangeable without having to delete it when placed in MS Access?
Thank you,
Fred

You can ask for the name of the application:
If Application.Name = "Microsoft Access" then
'Do Nothing ......Or whatever you need to do.
ElseIf Application.Name = "Microsoft Excel" then
Application.Run "Application.Volatile"
End If

From Help.
Visual Basic for Applications Reference
CallByName Function
Executes a method of an object, or sets or returns a property of an object.
Syntax
CallByName(object, procname, calltype,[args()])
The CallByName function syntax has these named arguments:
Part Description
object Required; Variant (Object). The name of the object on which the function will be executed.
procname Required; Variant (String). A string expression containing the name of a property or method of the object.
calltype Required; Constant. A constant of type vbCallType representing the type of procedure being called.
args() Optional: Variant (Array).
Remarks
The CallByName function is used to get or set a property, or invoke a method at run time using a string name.
In the following example, the first line uses CallByName to set the MousePointer property of a text box, the second line gets the value of the MousePointer property, and the third line invokes the Move method to move the text box:
CallByName Text1, "MousePointer", vbLet, vbCrosshair
Result = CallByName (Text1, "MousePointer", vbGet)
CallByName Text1, "Move", vbMethod, 100, 100
Send feedback to MSDN.Look here for MSDN Online resources.

Related

Find / replace text in embedded word object code stopped working

I have used this code successfully to replace content in an embedded word object from excel. I copied the code for a new excel file but now it doesn't work. It opens the file but doesn't replace although I can see that it IS finding the right text and replacement text. I'm kind of lost as to what is happening.
Dim strFindText As Range
Dim strReplaceText As Range
Dim nSplitItem As Long
Set strFindText = ActiveWorkbook.Worksheets("Utilisation Form").Range("c11:c20")
Set strReplaceText = ActiveWorkbook.Worksheets("Utilisation Form").Range("a11:a20")
nSplitItem = strFindText.Count
Debug.Print strFindText.Item(0)
For Each sh In ThisWorkbook.Sheets("Utilisation Form").Shapes
If sh.Name <> "Object 1" Then sh.Delete
Next
Set urobj = ThisWorkbook.Sheets("Utilisation Form").OLEObjects("Object 1")
Set wordtemp = urobj.Duplicate
wordtemp.Verb Verb:=xlOpen
Set wordtemp2 = wordtemp.Object
For x = 1 To nSplitItem
With wordtemp2.Content.Find
.Forward = True
.Text = strFindText.Item(x)
.ClearFormatting
.Replacement.Text = strReplaceText.Item(x)
.Execute Replace:=wdReplaceAll
End With
Next x
End Sub
Thanks for the support
When the early-binding technology is used in the code you need to add a corresponding COM reference to be able to use data types. Otherwise, you need to declare everything from the Word object model as Object in the code and use the late-binding technology.
To use early binding on an object, you need to know what its v-table looks like. In Visual Basic, you can do this by adding a reference to a type library that describes the object, its interface (v-table), and all the functions that can be called on the object. Once that is done, you can declare an object as being a certain type, then set and use that object using the v-table. For example, if you wanted to Automate Microsoft Office Excel using early binding, you would add a reference to the Microsoft Excel X.0 Object Library from the Project|References dialog, and then declare your variable as being of the type Excel.Application. From then on, all calls made to your object variable would be early bound.
Read more about that in the Using early binding and late binding in Automation article.

VBA UserForm gives run-time error 91 for one of its parameters

I am trying to create multiple instances of the same modeless UserForm in excel-VBA, with parameters, through a Sub.
I can make it work with two of the three parameters I want to assign, but the third one keeps returning me
"Run-time Error '91': Object variable or With block variable not set"
and I can't figure out why.
It may be an obvious typo that I didn't see, but I really can't point out the problem.
Here is my code:
Sub AskToClose(targetWksht As Worksheet, targetRow As Integer, test As String)
Dim newInstanceOfMe As Object
Set newInstanceOfMe = UserForms.Add("MOVE_TO_CLOSED") 'MOVE_TO_CLOSED is the name of my UserForm
newInstanceOfMe.targetWksht = targetWksht 'If I comment this line it works just fine, otherwise Run-time error 91
newInstanceOfMe.targetRow = targetRow
newInstanceOfMe.test = test
newInstanceOfMe.Show vbModeless
End Sub
_____________________________________________________________________
Sub test()
Dim openWksht As Worksheet
Set openWksht = Worksheets("OPEN WO") 'The worksheet exists and works just fine everywhere else
Call AskToClose(openWksht, 2, "test 2")
Call AskToClose(openWksht, 3, "test 3")
Call AskToClose(openWksht, 4, "test 4")
Set openWksht = Nothing 'I tried to comment this line just in case, same result...
End Sub
_____________________________________________________________________
'My MOVE_TO_CLOSED UserForm parameters
Public targetWksht As Worksheet
Public targetRow As Integer
Public test As String
newInstanceOfMe.targetWksht = targetWksht
This statement produces an error-level code quality inspection result for Value Required inspection in Rubberduck (an open-source VBIDE add-in project I manage). The inspection explains the situation as follows:
Object used where a value is required
The VBA compiler does not raise an error if an object is used in a place that requires a value type and the object's declared type does not have a suitable default member. Under almost all circumstances, this leads to a run-time error 91 'Object or With block variable not set' or 438 'Object doesn't support this property or method' depending on whether the object has the value 'Nothing' or not, which is harder to detect and indicates a bug.
There are two types of assignments in VBA: value assignment (Let), and reference assignment (Set). The Let keyword is redundant/optional/obsolete for value assignments:
Dim foo As Long
foo = 2 + 2
Let foo = 2 + 2 '<~ exactly equivalent to the above
So unless the Set keyword is present, VBA attempts to make a value assignment. If the object has a default member, that might just work - the VBA specs define how let-coercion mechanisms make that happen. That's how you can assign a Range to a value:
Sheet1.Range("A1") = 42
That's implicitly assigning to Range.Value, via an implicit member call to Range.[_Default], a hidden property of the Range class.
If the right-hand side of the assignment was also an object, then let-coercion would be happening on both sides of the operator:
Sheet1.Range("A1") = Sheet1.Range("B1") '<~ implicit default member calls galore!
Sheet1.Range("A1").Value = Sheet1.Range("B1").Value
But we're not looking at a Range here, we're looking at a UserForm, and a UserForm does not have a default member, so let-coercion can't happen... but the compiler won't validate that, so the code gets to run anyway... and blows up at run-time instead.
So, we're looking at a Let assignment with both sides holding an object reference, for a class type that doesn't define a default member.
Something.SomeObject = someOtherObject
But VBA doesn't care that there's no default member - because there's no Set keyword, it tries as hard as it can to do what you told it to do, and coerce these objects into values... and fails, obviously.
If Something.SomeObject (left-hand side) is Nothing, then the let-coercion attempt will try to invoke the inexistent default member -- but since the object reference is Nothing, the call is invalid, and error 91 is raised.
If Something.SomeObject is already holding a valid object reference, then the let-coercion attempt will go one step further, and fail with error 438 because there's no default member to invoke.
If Something.SomeObject has a default member (and the reference isn't Nothing), then the value assignment succeeds, no error is raised... but no object reference was assigned, and this might be a subtle bug!
Adding a Set keyword makes the assignment a reference assignment, and now everything works fine.

Set CheckBox Control dim as a UserForm CheckBox

I am attempting to create a class module that builds on top of a checkbox control. Within the Class Module I want to point at the checkbox in the userform. Although, when I try to fill the CheckBox object with one of the checkboxes in the userform I get a type-mismatch since calling on the checkbox gives back it's state instead of the entire object. Is there a way to get the entire object?
I have tried
set myCheckBox = makeMyCheckBox(Me.CheckBox1)
and
set myCheckBox = makeMyCheckBox(Me.CheckBox1.Object)
where makeMyCheckBox is a function that takes in a CheckBox object and creates a new MyCheckBox object.
'Within my userform's code
Dim myCheckBoxes(1 to 2) As MyCheckBox 'MyCheckBox is my class module
Private Sub UserForm_Initialize()
set myCheckBoxes(1) = makeMyCheckBox(me.CheckBox1)'<--Error Type Mismatch
End Sub
Private Function makeMyCheckBox(c As CheckBox) As MyCheckBox
Dim myChck As MyCheckBox
Set myChck = New MyCheckBox
myChck.init c 'takes in a CheckBox and fills its internal CheckBox object
Set makeMyCheckBox= myChck
End Function
I expect Me.CheckBox1 to be a CheckBox object.
Me.CheckBox1 outputs the checkbox's state when I look in debug (true/false)
I get--
Run-time error '13':
Type Mismatch
I get a type-mismatch since calling on the checkbox gives back it's state instead of the entire object. Is there a way to get the entire object?
Wrong assumption, you're getting the "entire object", but the object you're getting isn't implementing the interface you're expecting, hence the type mismatch.
You need to qualify your MSForms types explicitly with the MSForms library, like this:
Private Function makeMyCheckBox(ByVal c As MSForms.CheckBox) As MyCheckBox
Otherwise the unqualified CheckBox identifier / type name is referring to Excel.CheckBox, because the host application's object model (the Excel library) always has a higher priority than the referenced MSForms library, in the project references dialog:
This is excruciatingly hard to discover in a vanilla VBE. With Rubberduck you just place the caret on CheckBox and it tells you where it's coming from:
Without any add-ins, you kind of have to guess what the actual type is, because Shift+F2 (which normally takes you to the definition in the Object Browser) is useless for this - all you get is a message saying "the identifier under the cursor is not recognized".
Disclaimer: I manage the Rubberduck open-source project.

Defining cross-module variables with values

I'm developing a bunch of Excel Macros for making my life easier. One part of different macros is inserting a picture into sheets. For this reason, I would like to save the path to the images in a global location and then access it via a variable (so that I don't have to manually adjust the paths in every macro if it changes). I use one module per macro
In my own module "Variables" I defined a variable as Public or Global and then assigned a value via a sub. If I now access this variable via another module, I get an empty MsgBox.
For test purposes I use a string which I want to display via an MsgBox.
Modul 1:
Public test As String
Sub variablen()
test = "String for Test "
End Sub
Modul 2:
Public Sub testpublic()
MsgBox (test)
End Sub
I recommend to use a constant instead of a variable:
Module 1
Option Explicit
Public Const MyPath As String = "C:\Temp"
Module 2
Option Explicit
Public Sub ShowPath()
MsgBox MyPath
End Sub
I also recommend to activate Option Explicit: In the VBA editor go to Tools › Options › Require Variable Declaration.
If you do it like you did test is empty until it was initialized by running the procedure variablen first. If you use Public Const no initialization is required.
so that I don't have to manually adjust the paths in every macro if it changes
If it ever needs to change, then it semantically isn't a Const. The key to writing code that you don't constantly need to modify is to separate the code from the data.
A file path that sometimes needs to change can be seen as some kind of configuration setting.
Have a module that is able to read the settings from wherever they are, and return the value of a setting given some key.
The settings themselves can live on a (hidden?) worksheet, in a ListObject table with Key and Value columns, and looked up with INDEX+MATCH functions (using the early-bound WorksheetFunction functions will throw run-time errors given a non-existing key string):
Option Explicit
Public Function GetSettingValue(ByVal key As String) As Variant
With SettingsSheet.ListObjects(1)
GetSettingValue = Application.WorksheetFunction.Index( _
.ListColumns("Value").DataBodyRange, _
Application.WorksheetFunction.Match(key, .ListColumns("Key").DataBodyRange, 0))
End With
End Function
The Variant will retain the subtype of the Value, so for a String value you get a Variant/String; for a Date value you get a Variant/Date, for a numeric value you get a Variant/Double, and for a TRUE/FALSE value you get a Variant/Boolean.
Now when the file path needs to change, your code does not:
Dim path As String
path = GetSettingValue("ImageFolderPath")
And if you need more settings, you have no code to add, either:
Dim otherThing As String
otherThing = GetSettingValue("OtherThing")
All you need to do is to make sure the string keys being used match the contents of the Key column in your SettingsSheet.

String declaration error in if else

I have Camp as string. When I write this code, I get an error:
*Me.BoatDesc =< the expression you entered refer to an object that is close*
Here is my code
private Sub Save_Click()
Dim Camp As String
If Me.BoatDesc = "Camp" Then
Me.Amount = Me.Amount * 12
End If
Correct me if I am wrong.
You are using VBA, not VB.Net. Here are some notes
Here is a simple form, it will be open when the code is run. The code will be run by clicking Save. Note that the default for an MS Access bound form is to save, so you might like to use a different name.
This is the form in design view, note that there is a control named BoatDesc and another named Amount, as can only be seen from the property sheet.
The save button have an [Event Procedure], which is the code.
Note that the code belongs to Form2, the form I am working with, and the words Option Explicit appear at the top. This means I cannot have unnamed variables, so it is much harder to get the names wrong.
This is the code to be run by the save button.
Option Compare Database
Option Explicit
Private Sub Save_Click()
''Do not mix up strings, variables, controls and fields
''If you want to know if a control, BoatDesc, equals
''the string "camp", you do not need this
''Dim Camp As String
If Me.BoatDesc = "Camp" Then
Me.Amount = Me.Amount * 12
End If
End Sub

Resources