Procedure not executing all lines of code - vba

I am currently having an issue with a VBA script. It jumps over some lines of code without any error message. The strange about it is, this behavior does not happen for all users that work with the VBA script.
The VBA script is part of an Excel workbook and is designed, to call a second procedure during the runtime. This second procedure should build a form and do some further actions (formatting, etc.). The VBA script enters the second procedure but does not execute one line out of it. Instead it just continues with the code from the main procedure.
Sub Main()
Dim oClient as Object
...
'Call procedure to create form
Call CreateForm(oClient)
...
End Sub
Sub CreateForm(ByRef oClient As Object)
Dim oForm As Object
Set oForm = oClient.VBProject.VBComponents.Add(3)
With oForm
.Properties("Caption") = "Partner Export"
.Properties("Name") = "frmPartner"
.Properties("Width") = 300
.Properties("Height") = 240
End With
...
End Sub
The code is reduced to the minimum to show the procedures. The real VBA code is way bigger and more complex- that is the reason why I split it into two procedures.
The object oClient is created in the main procedure and passed to the CreateForm procedure. After calling CreateForm, the VBA script just skips all lines of the sub without notification.
What could be the reason for this behavior? Especially with the background, that this does not appear for each user of it.

You confirmed my suspicions in the comments, and the solution is to "Trust programmatic access to VBA project object model" in the Macro Security settings. This allows VBA to refer to VBProject objects.

Related

MS Access A certain Form's Form_Load() Only invoked first time that the user views said forms, but fails to be invoked in future viewings

I have very a rudimentary understanding of Microsoft Access and VBA Code.
On my work desktop, I have Microsoft Office Professional Plus 2013 Access
I've been tasked to create a MS Access application with an Access DB.
In the Visual Basic files, I have pseudo-code behind files for the various MS Access forms.
Most of my MS Access forms files have a subroutine like the following one:
Private Sub Form_Load()
Dim db As DAO.Database
Dim qdfSelectQuery As DAO.queryDef
Dim argumentsStringArray() As String
If Len(Me.OpenArgs) > 0 Then
argumentsStringArray = Split(Me.OpenArgs, "|")
…...Blah blah initialize some variables using the argumentsStringArray Blah blah …..;
End If
End Sub
Now the "Navigation" within the application can move back and forth between different forms, and it could go back to an earlier form that was already seen/used by the user of the application.
A Particular Form's Form_Load() subroutine does get executed the very first time that said form runs within the context of a particular execution run of said MS Access Application. However, if the application navigates back to a particular form that the user has already seen then it will Not execute said Form_Load() subroutine.
Could someone please tell me what code modifications that I would have to make in order to ensure that a Particular Form's Form_Load() subroutine executes regardless of how many times the user has seen it?
The Load handler runs once in the lifetime of the object.
If you need it to run multiple times, don't reuse the same object. DoCmd.OpenForm works off the form's default instance, so unless you unload or otherwise destroy the instance, it remains loaded, and next time it's displayed it still holds whatever instance state it had when it was last dismissed.
If an Access form supports unloading (not familiar with Access, and can't test ATM), then Unload MyForm should unload it, making the Load event fire again the next time the form is referenced.
If you can New up a form and Show it like you could with a UserForm, do it - forms are objects in their own rights, and this global default instance stuff is hiding the object nature of it... making basic assumptions fail (like, a Load handler being expected to run every time the form is shown).
Alternatively, move your Load code into a new handler for the Open event, which would be invoked whenever the form is open, not merely loaded.
My solution would be the following:
Instead of:
Private Sub Form_Load()
Dim db As DAO.Database
Dim qdfSelectQuery As DAO.queryDef
Dim argumentsStringArray() As String
If Len(Me.OpenArgs) > 0 Then
argumentsStringArray = Split(Me.OpenArgs, "|")
…...Blah blah initialize some variables using the argumentsStringArray Blah blah …..;
End If
End Sub
You use:
Public Sub InitializeForm(SomeArgs As String)
Dim db As DAO.Database
Dim qdfSelectQuery As DAO.queryDef
Dim argumentsStringArray() As String
If Len(SomeArgs) > 0 Then
argumentsStringArray = Split(SomeArgs , "|")
…...Blah blah initialize some variables using the argumentsStringArray Blah blah …..;
End If
End Sub
And instead of
DoCmd.OpenForm "MyForm", OpenArgs := "Something"
You do
DoCmd.OpenForm "MyForm"
Forms!MyForm.InitializeForm "Something"
This allows way more flexibility than relying on OpenArgs, e.g. you can pass objects and strongly-typed values, return values, etc.

How can I run several macros where the Macro name begins with a specific string of characters?

I've created several custom functions which I would like to Register. Currently, I have a different procedure for which I specify the registration for each function (there's no issue with that piece). However, the only way I know of to registering all these functions is by calling each Macro by name in another procedure like this:
Sub spRegisterFunctions()
Call spRegisterCUSTOMAfunction
Call spRegisterCUSTOMBfunction
Call spRegisterCUSTOMCfunction
Call spRegisterCUSTOMDfunction
End Sub
I'm actually looking for something more dynamic so that every time I create a new function, and it's corresponding "spRegister..." procedure, I don't have to remember to add the "Call" code to the "Sub spRegisterFunction()" procedure for that specific function.
Here's an example of what attempting to do:
Sub spRegisterFunctions()
Dim mc as Macro
For Each mc in VBProject("NameOfProject").Module("NameOfModule")
If Left(mc.Name,10)="spRegister" then
Call mc
End If
Next mc
End Sub
As you can see, I'm attempting to run any macro in a specific module who's name begins with "spRegister". Obviously the code above will not work, partly because some of those objects don't even exist.
Is there any way to do this?

What happens when i call a sub vba

I'm using the Call Function to activate other Macro's, but I'm not entirly sure how VB reads this function.
(I'm using this with MS Excel 2010)
For Example:
Sub main macro ()
blabla
blabla
Call Edit
Call Save
Call HistoryLog
End Sub
This code has some If funcions, and at some point will get to "Call Edit"
Here the Macro "Sub Edit()"will be activated. But I need this code to edit data, save the data into a new file and make a history log in an other Excel sheet.
If VBA is finished with the "Sub Edit()" part how will the code continu?
Does it Calls the "Sub Save()" part?
Or do I realy require to type "Call Sub Save" at the end of the "Sub Edit()" part?
VBA will process all lines of your code in the order they are given, unless an error occurs in one of them. As it is, your code will just stop at the point of error.
Where to call Save() really depends on how you want your code to work. If you want to make sure that after every call to Edit() changes are saved, I'd call Save() within Edit(). If sometimes you want to call Edit() without calling Save() you should not put the call to Save() into Edit().
When you call a procedure from within another procedure, program control returns to the line just below the Call line. In your example, when the Edit procedure reaches an End Sub or Exit Sub line, program control will continue on the Call Save line (the line just below Call Edit).
The Call keyword is included in the language for backward compatitbility. You can call the Edit procedure like
Edit
without the Call keyword. If you like using Call, it doesn't harm anything though.

How to instantiate a vba class and call a method from vb.net?

As I guess many are, I'm sitting with an ms access application with a mixture of tables, VBA Modules and VBA Classes. I intend to migrate that application to VB.NET.
However it will take some time and I would like to make use of automation to slowly move the code to VB.NET
Now I can call regular SUB and Functions from my VB.NET application but wonder if there is a way to invoke the methods of user defined objects.
Rough example what I want to do
VBA
'Class1
Public Sub Test()
Print "Hello world"
End Sub
'Module1
Public oClass1 as Class1
Public Sub Init()
Set oClass1 = New Class1
End Sub
VB.Net
' Left out the opening of the access db
oAccess.Run("Init")
oAccess.Run("oClass1.Test())
Is it even possible?
The Application.Run method requires a string containing "The name of the Function or Sub procedure to be run" as its first argument. But "oClass1.Test" is neither.
You could work around that issue by creating another VBA procedure which wraps your oClass1.Test method, and run the wrapper procedure ...
oAccess.Run("Wrap_oClass1_Test") ' no parentheses after procedure name
Public Sub Wrap_oClass1_Test()
oClass1.Test
End Sub
I confirmed that approach worked with the rest of your sample code when called from VBScript so I believe it should also work from VB.Net.
Tim's CallByName suggestion also looks promising, but I didn't test that one.

VBA UserForm running twice when changing .Caption

I'm running a VBA macro from SolidWorks. The form doubles as an input for two types of document. In the UserForm.Initialize subroutine I'm changing the name of the UserForm's Caption depending on which document type is open. Whenever I do this though, the program reruns UserForm.Initialize, and when it's all done, it carries on from where it left of, effectively running twice.
Does anyone know a way around this bizarre behaviour? I tried putting the FormName.Caption command into its own Sub but the result is the same.
Many thanks.
I can't replicate the problem and I don't know what SolidWorks is, so that may have something to do with it. Perhaps you can post a made-up example that shows Initialize being called twice.
My guess would be that it's related to auto-instantiating variables. When you use UserForm1, you are instantiating an object variable called UserForm1 that points to an object, also called UserForm1. It's similar to using the New keyword in a Dim statement. You never defined UserForm1 (the variable), but VBA did and the first time you use it, it instantiates automatically.
You should try to use the Me keyword when working inside the userforms class module (userforms are classes just like other objects except that they have a user interface element). In the Initialize event, say
Me.Caption = "blah"
instead of
UserForm1.Caption = "blah"
It could be (just a theory that I wasn't able to prove) that the flag that gets set to say "I'm pointing to a real instance" isn't set by the time you change the Caption property, and that by using the auto-instantiating variable UserForm1, you are forcing another instantiation.
Even better, don't use auto-instantiating variables, convenient though they are (and don't use the New keyword in a Dim statement either). You can control when your variables are created and destroyed and it's a best practice. Something like this in a standard module
Sub uftst()
Dim uf As UserForm1
Set uf = New UserForm1 'you control instantiation here
'Now you can change properties before you show it
uf.Caption = "blech"
uf.Show
Set uf = Nothing 'overkill, but you control destruction here
End Sub
Note that if the ShowModal property is set to False that the code will continue to execute, so don't destroy the variable if running modeless.
As Dick suggested, you should be able to stop the behavior by making sure to use me.caption instead of Userform1.caption.
Here's a way you can replicate the issue for those who are curious:
Create a Userform (Userform1) make sure you set ShowModal to false or you won't be able to see this.
In a module add the following:
Option Explicit
Sub ShowUserForm()
Dim uf As UserForm1
Set uf = New UserForm1
End Sub
In UserForm1 list the following code:
Option Explicit
Private Sub UserForm_Initialize()
UserForm1.Caption = "I'm UserForm1!" 'This will call the Initialize method of Userform1 not Me.
Me.Caption = "I'm Me!"
Me.Show
End Sub
Run ShowUserForm. You now have two Userforms with different captions.
Incidentally, if you have an Initialize method like I displayed adding Set uf = Nothing to the ShowUserForm sub actually fails to close either form.