Possible syntax mistake? "Expecting =" In a Sub call from a UserForm - excel

I want to call a Sub I declared at it gives a compilation error saying that it expects a =.
The Sub call is in a UserForm_Initialize event procedure.
The code is as follows.
In a module:
Public Sub FillCb(Ar() As String, Cb As ComboBox)
Cb.Clear
For I = 1 To Application.CountA(Ar)
Cb.AddItem (Ar(I))
Next I
End Sub
In the UserForm code:
Private Sub UserForm_Initialize()
LblDate.Caption = Date
FillCb(LibrosNoPrestados, CbLibro)
End Sub
This code is giving me error.
I analized the code line by line using the debugger and commenting the las line inside the Initialize event, and it works fine up to that point. The error is thrown at compile time in the
FillCb(LibrosNoPrestados, CbLibro)
The rest of the code is not needed here since as I said it works fine, but the syntax in that last line must be wrong and I can't see the mistake.

A VBA "feature". If you are calling a sub routine without the "Call" keyword then don't use parentheses, if you use the "Call" keyword then you need the parentheses.
Eg
Call FillCb(LibrosNoPrestados, CbLibro)
Or
FillCb LibrosNoPrestados, CbLibro
Here's Microsoft's documentation:
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/call-statement

Related

How to call a procedure depending on a cell value

Please Help,
I have written different procedures on different worksheet in a Workbook. I want to call a particular procedure depending on the specific cell value in a worksheet.
I tried with defining a variable
Example:
Private Sub CommandButton1_Click()
pbk = Me.Range("L1").Value
Call pbk
End Sub
but I'm getting errors like this:
Microsoft Visual Basic for Applications
Compile error:
Expected Sub, Function, or Property
value Range L1 is changing as per contents it has array of total 15 contents, so I have written 15 procedures different. i just need to call each procedure depending on the value of 'L1'.
Its total 15 Procedures, i can write with the IF condition like this:
Private Sub CommandButton1_Click()
pbk = Me.Range("L1").Value
If pbk = "PBK_Kirim" Then
Call PBK_Kirim
End If
If pbk = "PBK_ke_Masa" Then
Call PBK_ke_Masa
End If
'and so on
End Sub
but it will be too bulky so i'm trying for some easy method. thanks before
I agree with Rory, use "run pbk". Only other alternative to clean it up without using run that I can think of is build another sub routine passing in the pbk value and using select case for the different calls.

Cannot change color of a shape in Excel

VBA noob here.
I am developing a workbook which makes use of an external dll from my client.
In a sheet I have a button that, when pressed, starts a routine which makes a shape orange, then calls the API and finally makes the shape black.
Misteriously, it works only 'few times'.
The following code resides in a sub within a module:
Dim shapeToFlash As String
shapeToFlash = "SHAPE " & sheetName
Worksheets("GTE HOME").Shapes(shapeToFlash).Fill.ForeColor.SchemeColor = 53
Worksheets("HOME").Shapes(shapeToFlash).Fill.ForeColor.SchemeColor = 53
// API CALL (kind of long operation ~ 3/4 seconds)
Worksheets("GTE HOME").Shapes(shapeToFlash).Fill.ForeColor.SchemeColor = 0
Worksheets("HOME").Shapes(shapeToFlash).Fill.ForeColor.SchemeColor = 0
I cannot share details about the API. I simply declare with the traditional sintax:
#If Win64 Then
Private Declare PtrSafe Function IMB_set_value _
Lib "path/API.dll" () As Long
#Else
Private Declare Function IMB_set_value _
Lib "path/API.dll" () As Long
and works perfectly.
The problem is that the first SchemeColor (to 53) does not work meaning that the API is called and the second SchemeColor too (the shape turns black). Just to test, I tried to comment the second SchemeColor (to 0) and I noticed that in this case the shape turns orange AFTER the API is called! That suggested me Excel create a sort of priority queue of the commands to be executed and that the API call is performed before the first SchemeColor: that clearly messes with my flow. Is there a way to force Excel to execute immediately an operation? Is there another reason for the fail?
P.S.: I have executed the first SchemeColor lines of code separately and works nicely so I suppose the code is correct.
P.P.S.: I have also tried using RGB instead of SchemeColor, with the same result.
Try this
Sub InitiateLongRunningOperation()
Dim Argument as String
HighlightShape
Argument = "Argument Value"
Application.OnTime Now, "'LongRunningOperation """ & Argument & """'"
End Sub
Sub HighlightShape()
Worksheets(1).Shapes(1).Fill.ForeColor.SchemeColor = 53
End Sub
Sub LongRunningOperation(AnArgument As String)
Debug.Print AnArgument
' Replace the line below with your API call
Application.Wait Now + TimeValue("0:00:03")
Application.OnTime Now, "ResetShape"
End Sub
Sub ResetShape()
Worksheets(1).Shapes(1).Fill.ForeColor.SchemeColor = 0
End Sub
It works with Application.OnTime to start the chain of events without waiting for all of it to end before updating.
I have changed some of your code to make it easier to reproduce, but I think you will be able to follow it quite easy.

How to declare Global Variables in Excel VBA to be visible across the Workbook

I have a question about global scope and have abstracted the problem into a simple example:
In an Excel Workbook:
In Sheet1 I have two(2) buttons.
The first is labeled SetMe and is linked to a subroutine in Sheet1's module:
Sheet1 code:
Option Explicit
Sub setMe()
Global1 = "Hello"
End Sub
The second is labeled ShowMe and is linked to a subroutine in ThisWorkbook's module:
ThisWorkbook code:
Option Explicit
Public Global1 As String
Debug.Print("Hello")
Sub showMe()
Debug.Print (Global1)
End Sub
Clicking on SetMe produces a compiler error: variable not defined.
When I create a separate module and move the declaration of Global1 into it everything works.
So my question is:
Everything I have read says that Global variables, declared at the top of a module, outside of any code should be visible to all modules in the project. Clearly this is not the case.
Unless my understanding of Module is not correct.
The objects Sheet1, Sheet2, ThisWorkbook,... that come with a workbook: are these not modules capable of declaring variables at global scope?
Or is the only place one can declare a global, in a separate module of type Modules.
Your question is:
are these not modules capable of declaring variables at global scope?
Answer: YES, they are "capable"
The only point is that references to global variables in ThisWorkbook or a Sheet module have to be fully qualified (i.e., referred to as ThisWorkbook.Global1, e.g.)
References to global variables in a standard module have to be fully qualified only in case of ambiguity (e.g., if there is more than one standard module defining a variable with name Global1, and you mean to use it in a third module).
For instance, place in Sheet1 code
Public glob_sh1 As String
Sub test_sh1()
Debug.Print (glob_mod)
Debug.Print (ThisWorkbook.glob_this)
Debug.Print (Sheet1.glob_sh1)
End Sub
place in ThisWorkbook code
Public glob_this As String
Sub test_this()
Debug.Print (glob_mod)
Debug.Print (ThisWorkbook.glob_this)
Debug.Print (Sheet1.glob_sh1)
End Sub
and in a Standard Module code
Public glob_mod As String
Sub test_mod()
glob_mod = "glob_mod"
ThisWorkbook.glob_this = "glob_this"
Sheet1.glob_sh1 = "glob_sh1"
Debug.Print (glob_mod)
Debug.Print (ThisWorkbook.glob_this)
Debug.Print (Sheet1.glob_sh1)
End Sub
All three subs work fine.
PS1: This answer is based essentially on info from here. It is much worth reading (from the great Chip Pearson).
PS2: Your line Debug.Print ("Hello") will give you the compile error Invalid outside procedure.
PS3: You could (partly) check your code with Debug -> Compile VBAProject in the VB editor. All compile errors will pop.
PS4: Check also Put Excel-VBA code in module or sheet?.
PS5: You might be not able to declare a global variable in, say, Sheet1, and use it in code from other workbook (reading http://msdn.microsoft.com/en-us/library/office/gg264241%28v=office.15%29.aspx#sectionSection0; I did not test this point, so this issue is yet to be confirmed as such). But you do not mean to do that in your example, anyway.
PS6: There are several cases that lead to ambiguity in case of not fully qualifying global variables. You may tinker a little to find them. They are compile errors.
You can do the following to learn/test the concept:
Open new Excel Workbook and in Excel VBA editor right-click on Modules->Insert->Module
In newly added Module1 add the declaration; Public Global1 As String
in Worksheet VBA Module Sheet1(Sheet1) put the code snippet:
Sub setMe()
Global1 = "Hello"
End Sub
in Worksheet VBA Module Sheet2(Sheet2) put the code snippet:
Sub showMe()
Debug.Print (Global1)
End Sub
Run in sequence Sub setMe() and then Sub showMe() to test the global visibility/accessibility of the var Global1
Hope this will help.

Excel VBA - Use an existing string in called sub

I'm pretty new to this so apologies in advance
I'm half way through a userform in Excel and I'm trying to cut some fat off my code by using Call - I have 12 buttons that all do the same thing, the only difference is that each buttons sub is dependant on the buttons caption. My problem is that I can't figure out a way to use a String I've already declared in the Buttons Sub, then use it in the called Sub. I know you can do it, but my googling skills have failed me :(
Please could someone show me how to do this? Hope that all makes sense...
Here is a very small snippet of my code, but you get the jist:
Public Sub CommandButton4_Click()
Dim Name As String
Name = CommandButton4.Caption
Call Sort1
End Sub`
And the other one (Also tried this as function for the sake of trial and error)
Public Sub Sort1(Name As String)
Label11.Caption = Name
Sheets(Name).Select
End Sub
What you're referring to is passing an argument to another subroutine or function. Let's say you want to use a function a lot of times to get the first letter of a string. A sample of this is:
Function LeftOne(StrSample As String) As String
LeftOne = Left(StrSample, 1)
End Function
The above function can be used inside another function or subroutine provided you meet its requirement: StrSample. By declaring StrSample As String in the arguments field of the function, you are basically requiring that any calls to this should require a string to be passed to it. Anything else would throw an error.
The full line LeftOne(StrSample As String) As String can be read as: "I am function LeftOne. Pass me a string and I'll return to you a string after doing something with it." Note that the name StrSample is an arbitrary name.
Anyway, calling the above is as simple as:
Sub MsgInABox()
Dim StrToFeed As String
StrToFeed = "BK201"
MsgBox LeftOne(StrToFeed) 'Returns B.
End Sub
In your example, if you want to pass Name to Sort1, your attempt is absolutely correct.
Let us know if this helps.
You hat to give your sort1 procedure the parameter name.
call sort1(name)
or
call sort1(CommandButton4.Caption)

VBA: What is causing this string argument passed to ParamArray to get changed to a number (that looks suspiciously like a pointer)?

FINAL EDIT: It does indeed appear to be a compiler bug - see the accepted answer.
Using VBA within Excel 2007, I have the following code in 'Class1':
Option Explicit
Public Function strange(dummy As String, ParamArray pa())
Debug.Print pa(LBound(pa))
End Function
Public Sub not_strange(dummy As String, ParamArray pa())
Debug.Print pa(LBound(pa))
End Sub
Public Function also_not_strange(ParamArray pa())
Debug.Print pa(LBound(pa))
End Function
and some mode code in a module:
Option Explicit
Public Function not_strange_either(dummy As String, ParamArray pa())
Debug.Print pa(LBound(pa))
End Function
Public Sub outer(v)
Dim c As Class1
Set c = New Class1
Call c.strange("", v(LBound(v)))
Call c.not_strange("", v(LBound(v)))
Call c.also_not_strange(v(LBound(v)))
Call not_strange_either("", v(LBound(v)))
End Sub
If call 'outer' from the Immediate window like this:
call outer(array("a"))
I get back output that seems strange:
102085832
a
a
a
It seems to matter whether the called routine is in a class module or not, whether it is a Sub or a Function, and whether or not there is an initial argument. Am I missing something about how VBA is supposed to work? Any ideas?
The strange number changes from run to run. I say "looks suspiciously like a pointer" because if I call this:
Public Sub outer2(v)
Dim c As Class1
Set c = New Class1
Dim ind As Long
For ind = LBound(v) To UBound(v)
Call c.strange("", v(ind))
Next ind
End Sub
like so:
call outer2(array("a","b","c"))
I get back output like:
101788312
101788328
101788344
It's the increment by 16 that makes me suspicious, but I really don't know. Also, passing a value, say by calling:
Call c.strange("", CStr(v(ind)))
works just fine.
EDIT: A little more info...If I assign the return value from 'c.strange' to something instead of throwing it away, I get the same behavior:
Public Sub outer3(v)
Dim c As Class1
Set c = New Class1
Dim x
x = c.strange("", v(LBound(v)))
Call c.not_strange("", v(LBound(v)))
Call c.also_not_strange(v(LBound(v)))
Call not_strange_either("", v(LBound(v)))
End Sub
Interestingly, if I call my test routines as above, with an argument that results from calling 'Array', the supposed-pointer value changes. However, if I call it like this:
call outer([{1,2,3}])
I get back the same number, even if I make the call repeatedly. (The number changes if I switch to another app in Windows, like my browser.) So, now I'm intrigued that the Excel evaluator (invoked with the brackets) seemingly caches its results...
Now this is awesome.
Reproduced on office 2003.
Looks like a compiler bug.
The problem is in this line:
Call c.strange("", v(LBound(v)))
Here the compiler creates a Variant that holds a 1D array of Variant's, the only element of which is a pointer instead of the value. This pointer then goes to the strange function which actually is not strange, it only prints the Variant\Long value passed to it.
This trick brings the compiler sanity back:
Call c.strange("", (v(LBound(v))))
EDIT
Yes, this magic number is a pointer to the VARIANT structure which is supposed to be passed to the strange method. The first field of which is 8, which is vbString, and the data field contains a pointer to the actual string "a".
Therefore, it is definitely a compiler bug... Yet another VB compiler bug in regard of arrays ;)

Resources