VBA - Compile Error: Expected Function or Variable - vba

To start, I am not a programmer. Im just someone trying to make a document that has an "invoice" number that increases on the document that goes up every time I print the document. I found a macro code online but I keep getting the
Compile error
I'll attach a snap shot of what im getting with the piece that keeps screwing up high lighted.
Is there something I'm doing wrong?

To expand on braX's answer...
That's the syntax for assigning the return value of a Function or Property Get member - namely, you are assigning to the procedure's identifier:
Public Function GetTotallyRandomNumber() As Long
GetTotallyRandomNumber = 4
End Function
Seems you mean to have a local variable named SerialNumber, however VBA already knows this identifier as the name of a Sub procedure named SerialNumber, and because a Sub procedure doesn't return anything, it can't legally be assigned like this.
Declare a local variable inside the procedure's scope, before the illegal assignment:
Dim SerialNumber As String
SerialNumber = System.PrivateProfileString(...)
And then your code will work... however I wouldn't recommend using the exact same name as the procedure.
My recommendation would be to name the local variable SerialNumber, and to rename the Sub procedure so that its name starts with a verb. Procedures do something, they're actions: find a meaningful name that describes what it does, and go with that.
Naming is hard though - if you can't find a simple name that describes what your procedure does, it's probably because it's doing too many things. Split it up into smaller, more focused procedures.
Public Sub PrintActiveDocumentAndAddSerialNumberBookmark()

You are treating a Function like a Subroutine. Subroutines do not return values.
If you want the routine to return a value, then change Sub to Function (at the top)
If you are not wanting it to return anything, then choose a different variable name instead of SerialNumber, or change the name of the Subroutine.
You cannot use a variable name that is the same as the name of the Subroutine.

Related

Variable in a form won't keep its value after being used in the call to another form

I have a form with a variable in it called "VigilTable." This variable gets its value from the calling string OpenArgs property.
Among other things, I use this variable in the call string when opening other forms.
But it only works the first call.
MsgBox VigilTable before the call will always show "Spring2022" or whatever on the first call but always comes up blank on succeeding calls (and I get "invalid use of NULL" when the called form attempts to extract the value from OpenArgs). The variable is dimmed as String in the General section of the form's VBA code.
So what's happening here? And can I fix it?
Thanks.
Ok, so you delcared a variable at the form level (code module) for that given form.
and we assume that say on form load, you set this varible to the OpenArgs of the form on form load.
So, say like this:
Option Compare Database
Option Explicit
Public MyTest As String
Private Sub Form_Load()
MyTest = Me.OpenArgs
End Sub
Well, I can't say having a variable helps all that much, since any and all code in that form can use me.OpenArgs.
but, do keep in mind the following:
ONLY VBA code in the form can freely use that variable. It is NOT global to the applcation, but only code in the given form.
However, other VBA code outside of the form can in fact use this variable. But ONLY as long as the form is open.
So, in the forms code, you can go;
MsgBox MyTest
But, for VBA outside of the form, then you can get use of the value like this:
Msgbox forms!cityTest.MyTest
However, do keep in mind that any un-handled error will (and does) blow out all global and local variables. So, maybe you have a un-handled error.
Of course if you compile (and deploy) a compiled accDB->accDE, then any errors does NOT re-set these local and global variables.
but, for the most part, that "value" should persist ONLY as long as the form is open, and if you close that form, then of course the values and variables for that form will go out of scope (not exist).
Now, you could consider moving the variable declare to a standard code module, and then it would be really global in nature, but for the most part, such code is not recommended, since it hard to debug, and such code is not very modular, or even easy to maintain over time.
So, this suggests that some error in VBA code is occurring, and when that does occur, then all such variables are re-set (but, the noted exception is if you compile down to an accDE - and any and all variables will thus persist - and even persist their values when VBA errors are encountered.
For a string variable, a more robust solution not influenced by any error, should be writing/reading in/from Registry. You can use the, let as say, variable (the string from Registry) from any workbook/application able to read Registry.
Declare some Public constants on top of a standard module (in the declarations area):
Public Const MyApp As String = "ExcelVar"
Public Const Sett As String = "Settings"
Public Const VigilTable As String = "VT"
Then, save the variable value from any module/form:
SaveSetting MyApp, Sett, VigilTable , "Spring2022" 'Save the string in Regisgtry
It can be read in the next way:
Dim myVal as String
myVal = GetSetting(MyApp, Sett, VigilTable , "No value") 'read the Registry
If myVal = "No value" Then MsgBox "Nothing recorded in Registry, yet": Exit Sub
Debug.print myVal
Actually, this proved not to be the the answer at all.
It was suggested that I declare my variables as constants in the Standard module but I declared them as variables. It appeared at first to work, at least through one entire session, then it ceased to work and I don't know why.
If I declare as constants instead, will I still be able to change them at-will? That matters because I re-use them with different values at different times.
I didn't do constants but declaring VigilName in the Standard module and deleting all other declarations of it fixed both problems.
While I was at it I declared several other variables that are as generally used and deleted all other declarations of them as well so that at least they'll be consistently used throughout (probably save me some troubleshooting later.
Thanks to all!

Passing arguments from Visio ShapeSheet to procedure in VBA

I have been working with Visio VBA for a couple of years. I pass arguments from the ShapeSheet of a shape to several procedures in VBA using either RUNMACRO() or CALLTHIS() Functions.
I either pass pass plain strings or the value of different formulas in the ShapeSheet. The most common argument that I pass to my procedures is the ID() of the shape.
After a patch update of Windows: https://support.microsoft.com/en-us/topic/march-15-2021-kb5001566-os-build-18363-1441-out-of-band-23c4c824-8638-43e9-a381-ff58213ae6fe, I am no longer able to pass arguments from the ShapeSheet of a Shape to my procedures in VBA. Whenever I use RUNMACRO or CALLTHIS, all the arguments that I pass, even if they are plain strings, in my procedure side everything that I get is "", blank information.
Is there a way to know if this is a bug or if this is on purpose, maybe as a security measure?
Here is a simple example procedure:
Public Function HelloWorld(Number As Integer)
If Number = 1 Then
MsgBox ("Hello World 1")
End If
If Number = 2 Then
MsgBox ("Hello World 2")
End If
End Function
And this procedure is called in the EventDblClick of a Shape:
RUNMACRO("HelloWorld(1)","Test")
When the EventDblClick is triggered the value of Number stays as null, ignoring the argument sent in the RUNMACRO function, which is a 1. Since the argument of Number in the HelloWorld() function is not optional, it triggers a Compile error message
"Argument not optional"
My programs were also recently affected by this update. Previously, I was able to structure the ‘macroname’ string in RUNMACRO (macroname [,projname_opt]) to include arguments to my functions and subroutines (as in Alexis’ HelloWorld example). However, that suddenly seems to be no longer possible. It is almost as if RUNMACRO is now modifying the ‘macroname’ string to exclude any arguments prior to making the call to the Function or Subroutine.
One workaround that I have found is to use CALLTHIS and restructure the VBA Function or Subroutine slightly as shown below. That said, it is going to be a huge hassle for me to execute this change in every cell, in every shape, in every file using the RUNMACRO formula. I’m going to have to write a separate macro just to execute this change.
Workaround:
CALLTHIS(HelloWorld,,1)
[NOTE: The two commas are intentional.]
Public Function HelloWorld(callingShape as Visio.Shape, number as Integer)
‘Insert Code Here
End Function

How to find out where a function is being called from in MS Access?

I am working on an MS Access database application that was created by someone else. There is one particular line of code (a Function) that will randomly get called and I have no idea why it is being called or what it does. I have searched (ctrl+F) the entire project for something that calls this function but I can't find it. How can I find out why this Function is being called? (See below). Thank you!
Public Function Concat(strIOSC As String, strFeature As String) As String
Static strLastIOSC As String
Static strFeatures As String
If strIOSC = strLastIOSC Then
strFeatures = strFeatures & ", " & strFeature
Else
strLastIOSC = strIOSC
strFeatures = strFeature
End If
Concat = strFeatures
End Function
If you have only searched the scripts and modules, then your scope is too narrow.
A public function like this can also be used in expressions, so you need to check queries, reports, form controls, macros, and possibly even tables if you use calculated fields. Depending on the size of the database, and how often the function is called, you can either search manually in a targeted way or possibly use a public sub to output something searchable. This sub can get you started. I think it outputs every possible location for expressions. Unfortunately, each object will have its own text file which will need to be searched separately unless you build a sub to do that too.
As for what your function does, it looks like it logs each input using the Static strLastIOSC variable, compares to the arguments passed on the second function call, and if they match it concatenates the two strFeature inputs together and outputs the result.
So basically the first argument tells the function whether this is the beginning of a new concatenation instance, or the continuation of an existing instance. The second argument is the item to be concatenated.
The Static keyword means that the value is stored even after the function runs so it can compare the last call with the current call to determine whether to add the second argument to the one saved from before, or clear the memory and prepare for a new concatenation.
Given its design, it's probably being used in a query/report/form, where strIOSC is likely a primary key field or a field in a GROUP BY.

Excel 2010 VBA: Invalid procedure call when adding data field to pivot table

I want to create multiple pivot tables by encapsulating the complexity into functions and procedures (I did already manage to create pivots with VBA, but one module for each pivot, meaning lots of repetitive code...)
I managed to create procedures and functions to create the pivot, add a filter, and a row field. But when it comes to adding a data field, I get an invalid procedure call; the strange thing however is that I get that error only when I use variables to pass info: looking at the line prodicung the error, the first line works perfectly fine, whereas I cannot get the second line running (the variables contain the correct values):
pivotName.addDataField pivotName.PivotFields("sdID"), "SD ID number", xlCount 'works fine
pivotName.addDataField pivotName.PivotFields(fieldName), fieldDescription, calcMethod 'produces Invalid procedure call error
As I am running out of ideas, any help would be highly appreciated!
Thank you very much,
Alexander
The reason for the error is due to calcMethod being declared earlier in the code as a string.
The .addDataField method accepts the following parameters:
.AddDataField(Field, Caption, Function)
The parameter Function is of the XLConsolidationFunction enum and therefore should be declared and assigned like below:
Dim calcMethod As XlConsolidationFunction
calcMethod = xlCount
Once declared and assigned as above, you can use it within your method in the following way where fieldName and fieldDescription are both strings:
pivotName.addDataField pivotName.PivotFields(fieldName), fieldDescription, calcMethod
As you can see in the comment from Gareth, the problem was to declare calcMethod (optional function) as string, not as XLConsolidatedFunction.
Thank you once again!

Is it possible to declare a public variable in vba and assign a default value?

I want to do this but it won't compile:
Public MyVariable as Integer = 123
What's the best way of achieving this?
.NET has spoiled us :)
Your declaration is not valid for VBA.
Only constants can be given a value upon application load. You declare them like so:
Public Const APOSTROPHE_KEYCODE = 222
Here's a sample declaration from one of my vba projects:
If you're looking for something where you declare a public variable and then want to initialize its value, you need to create a Workbook_Open sub and do your initialization there.
Example:
Private Sub Workbook_Open()
Dim iAnswer As Integer
InitializeListSheetDataColumns_S
HideAllMonths_S
If sheetSetupInfo.Range("D6").Value = "Enter Facility Name" Then
iAnswer = MsgBox("It appears you have not yet set up this workbook. Would you like to do so now?", vbYesNo)
If iAnswer = vbYes Then
sheetSetupInfo.Activate
sheetSetupInfo.Range("D6").Select
Exit Sub
End If
End If
Application.Calculation = xlCalculationAutomatic
sheetGeneralInfo.Activate
Load frmInfoSheet
frmInfoSheet.Show
End Sub
Make sure you declare the sub in the Workbook Object itself:
Just to offer you a different angle -
I find it's not a good idea to maintain public variables between function calls. Any variables you need to use should be stored in Subs and Functions and passed as parameters. Once the code is done running, you shouldn't expect the VBA Project to maintain the values of any variables.
The reason for this is that there is just a huge slew of things that can inadvertently reset the VBA Project while using the workbook. When this happens, any public variables get reset to 0.
If you need a value to be stored outside of your subs and functions, I highly recommend using a hidden worksheet with named ranges for any information that needs to persist.
Sure you know, but if its a constant then const MyVariable as Integer = 123 otherwise your out of luck; the variable must be assigned an initial value elsewhere.
You could:
public property get myIntegerThing() as integer
myIntegerThing= 123
end property
In a Class module then globally create it;
public cMyStuff as new MyStuffClass
So cMyStuff.myIntegerThing is available immediately.
Little-Known Fact: A named range can refer to a value instead of specific cells.
This could be leveraged to act like a "global variable", plus you can refer to the value from VBA and in a worksheet cell, and the assigned value will even persist after closing & re-opening the workbook!
To "declare" the name myVariable and assign it a value of 123:
ThisWorkbook.Names.Add "myVariable", 123
To retrieve the value (for example to display the value in a MsgBox):
MsgBox [myVariable]
Alternatively, you could refer to the name with a string: (identical result as square brackets)
MsgBox Evaluate("myVariable")
To use the value on a worksheet just use it's name in your formula as-is:
=myVariable
In fact, you could even store function expressions: (sort of like in JavaScript)
(Admittedly, I can't actually think of a situation where this would be beneficial - but I don't use them in JS either.)
ThisWorkbook.Names.Add "myDay", "=if(isodd(day(today())),""on day"",""off day"")"
Square brackets are just a shortcut for the Evaluate method. I've heard that using them is considered messy or "hacky", but I've had no issues and their use in Excel is supported by Microsoft.
There is probably also a way use the Range function to refer to these names, but I don't see any advantage so I didn't look very deeply into it.
More info:
Microsoft Office Dev Center: Names.Add method (Excel)
Microsoft Office Dev Center: Application.Evaluate method (Excel)
As told above, To declare global accessible variables you can do it outside functions preceded with the public keyword.
And, since the affectation is NOT PERMITTED outside the procedures, you can, for example, create a sub called InitGlobals that initializes your public variables, then you just call this subroutine at the beginning of your statements
Here is an example of it:
Public Coordinates(3) as Double
Public Heat as double
Public Weight as double
Sub InitGlobals()
Coordinates(1)=10.5
Coordinates(2)=22.54
Coordinates(3)=-100.5
Heat=25.5
Weight=70
End Sub
Sub MyWorkSGoesHere()
Call InitGlobals
'Now you can do your work using your global variables initialized as you wanted them to be.
End Sub
You can define the variable in General Declarations and then initialise it in the first event that fires in your environment.
Alternatively, you could create yourself a class with the relevant properties and initialise them in the Initialise method
This is what I do when I need Initialized Global Constants:
1. Add a module called Globals
2. Add Properties like this into the Globals module:
Property Get PSIStartRow() As Integer
PSIStartRow = Sheets("FOB Prices").Range("F1").Value
End Property
Property Get PSIStartCell() As String
PSIStartCell = "B" & PSIStartRow
End Property
there is one way to properly solve your question. i have the same concern with you for a long time. after searching and learning for a long time, finally i get a solution for this kind of question.
The solution is that no need to declare the variable and no need to set value to the variable, and even no need VBA code. Just need the "named range" in excel itself.
For example, the "A1" cell content is "hello, world". and we define the "A1" cell a name as "hello", that is, the "A1" cell have a name now, it's called "hello".
In VBA code, we just need use this method [hello], then we can get the "A1" value.
Sub test()
msgbox [hello]
end sub
the msgbox will show "Hello, word".
this way, we get a global variable without any declaration or assignment. it can be used in any Sub or Function.
we can define many named range in excel, and in VBA code we just use [] method to get the range value.
in fact, the [hello] is a abbreviation of the function Evaluate["Hell"], but it's more shorter.
It's been quite a while, but this may satisfy you :
Public MyVariable as Integer: MyVariable = 123
It's a bit ugly since you have to retype the variable name, but it's on one line.