I am running a program that uses 5 macros and lots of formulas. Some of the macros I have asked for your help on here. After putting the program together there is alot of lag. I mean if we delete a line we have to wait 1 to 2 minutes for it do the that process. Any ideas on what I should be looking at? I know this sounds somewhat vague but I am at a loss of where to starting looking to resolve the lag. Do I look at computer, server, or program?
From Visual Basic Concepts (part of Help)
You can make your Visual Basic applications run faster by optimizing the way Visual Basic resolves object references. The speed with which Visual Basic handles object references can be affected by:
Whether or not the ActiveX component has been implemented as an in-process server or an out-of-process server.
Whether an object reference is early-bound or late-bound.
In general, if a component has been implemented as part of an executable file (.exe file), it is an out-of-process server and runs in its own process. If it has been implemented as a dynamic-link library, it is an in-process server and runs in the same process as the client application.
Applications that use in-process servers usually run faster than those that use out-of-process servers because the application doesn't have to cross process boundaries to use an object's properties, methods, and events. For more information about in-process and out-of-process servers, see "In-Process and Out-of-Process Servers."
Object references are early-bound if they use object variables declared as variables of a specific class. Object references are late-bound if they use object variables declared as variables of the generic Object class. Object references that use early-bound variables usually run faster than those that use late-bound variables.
See this link from a Microsoft person. This is excel specific rather than VBA. Autocalc and other calc options/screenupdating etc.
http://blogs.office.com/2009/03/12/excel-vba-performance-coding-best-practices/
Minimise Dots
So if you are interested in performance minimise dots (each dot is a lookup), especially in loops.
There are two ways. One is to set objects to the lowest object if you are going to access more than once.
eg (slower)
set xlapp = CreateObject("Excel.Application")
msgbox xlapp.worksheets(0).name
(faster because you omitt a dot every time you use the object)
set xlapp = CreateObject("Excel.Application")
set wsheet = xlapp.worksheets(0)
msgbox wsheet.name
The second way is with statement. You can only have one with active at a time.
This skips 100 lookups.
with wsheet
For x = 1 to 100
msgbox .name
Next
end with
String Concatination
And don't join strings one character at a time. See this from a VBScript programmer. It requires 50,000 bytes and many allocation and deallocation to make a 100 character string.
http://blogs.msdn.com/b/ericlippert/archive/2003/10/20/53248.aspx
Related
I am new to LINQ and especially PLINQ. I was playing around in ms office interop and was quite surprised to find that when using PLINQ my code got significantly (depending on the query up to double the speed) faster even though COM should marshal all my parallel calls to a single thread (https://learn.microsoft.com/en-us/visualstudio/vsto/threading-support-in-office?view=vs-2019).
Now I am confused on what might be going on here. I have not run into com server is busy or similar exceptions even on very large workbooks.
Dim oWb as Excel.Workbook
oWb.Worksheets.Cast(Of Excel.Worksheet).AsParallel().Where(Function(x) x.Names.Count > 0).SelectMany(Function(x) x.Names.Cast(Of Excel.Name)).Where(Function(y) y.Name.StartsWith("abc"))
The regular approach here is to loop all worksheets and on all worksheets loop the .Names collection.
I know that a Workbook.Names property also exists, this code is just to show a query as an example.
I read that one can use PLINQ queries in Excel (https://devblogs.microsoft.com/pfxteam/plinq-and-office-add-ins/) but I don't know if the same applies for interop.
My question: can this kind of parallel query be used with office interop nowadays and is it safe to do so?
It is not safe to access STA based Office applications from multiple threads. Otherwise, the host application, for example, Outlook may throw an exception if it detects cross-thread calls. I'd recommend extracting the data from the Excel object model and only then run PLINQ queries or any secondary threads on this data in parallel. And it doesn't depend on the interop technology itself, instead, on the COM server implementation - whether it is STA or MTA.
I've noticed that some members of the Stack Overflow community will use Set Object = Nothing in closing procedures. I was able to find why this is useful for instances of Access, but no answer has been satisfying when it comes to doing this for Excel, so my question is
What are the benefits of setting objects to Nothing in VBA?
In the sample code below, is setting my objects ws and Test equal to Nothing a waste of space? Else, if doing so is in fact good practice, why?
Dim ws as Worksheet
Dim Test as Range
Set ws = Sheets(“Sheet1”)
Set Test = ws.Range(“A1”)
'Utilize Test variable (copy, paste, etc)
Set Test = Nothing
Set ws = Nothing
Exit Sub
If this was managed .NET code (which is garbage-collected), you'd have to Release every single COM object you ever accessed, lest the host process (EXCEL.EXE) would likely remain running in the background, consuming memory and unable to completely tear down.
But this is VBA code (which is reference-counted), moreover VBA code that uses objects that the host application controls - these objects will die when the host application shuts down, and when that happens the VBA execution context is long gone.
In other words, all these Set ... = Nothing instructions are completely redundant.
In some specific cases, when you're dealing with a 3rd-party API / type library, it's possible that objects don't entirely clean up. For example you might be creating an Access.Application instance, and find that a "ghost" ACCESS.EXE process remains open in Task Manager well after Excel exited: that's a sign that you're leaking an object reference somehow, somewhere, and Set ... = Nothing can help prevent that.
However I wouldn't recommend systematically nulling all object references like that. Only when not doing it causes a problem. And even then, it's going to be one or two objects dragging everything down, not all of them. If ACCESS.EXE shuts down properly, there's no reason to clutter up your code with such instructions.
Avoiding storing object references in global state helps, too. If everything is local, in theory all objects involved are destroyed as soon as the local scope exits.
I have two processes that operate on a single Excel file.
The first process creates the Excel instance and opens/creates and activates a workbook and worksheet. The second process makes entries to the spreadsheet. The first process passes the Excel handle as an output parameter to the second process but, when the second process attempts to interact with the workbook, a "Given key not in dictionary" error occurs.
I speculate that the handle is just a means for a process to distinguish between Excel instances to which it is connected and the second process needs to connect to the Excel instance opened by the first process. The Excel VBO contains Attach and Attach Worksheet pages that may provide this functionality, but I cannot find any instructions or documentation for the Excel VBO. There may be more than one Excel instance open and I'm not sure how to refer to the correct instance.
Is my assumption that I need to connect the second process to the Excel instance opened by the first process correct? If so, how do I do this? If not, can anyone tell me what causes the dictionary error and how I can address it?
handles are kept track of internally in memory by a particular instance of the MS Excel VBO. They are not shared between instances of the VBO.
Given the above, and assuming your code is set up exactly as you've described (two distinct processes), this is expected behavior: the instance of the MS Excel VBO that holds the handle for the instance of Excel you're attempting to interact with is purged from memory at the end of the process.
Regarding the "Attach" functionality and associated documentation: most all out-of-the-box VBOs do have documentation available, and is always accessible by clicking the "i" button as emphasized in my screenshot below:
Clicking this pops an Internet Explorer window with the documentation for the particular object you have set in the "Business Object" field of this window. In this case, the MS Excel VBO action "Attach" has the following description:
1.3 Attach
Back-compatible link to 'Open Instance'. This opens the first running
instance of Excel found and links to it in this object. Returns:
- handle : Number : An integer with which the instance opened can be
identified.
- Enable Events : Flag : Indicates that events should be
enabled / disabled on the attached instance - defaulted to True
In your particular use case, this may be a viable action. In most cases/designs (esp. including considerations for resiliency), it should be considered that the automated solution may inadvertently attach to another Excel instance (if one is present). As such, you might want to consider re-factoring your process design to create and interact with the Excel instance within the same Blue Prism process. If you need logical separation of the code that launches Excel & handles the processing, you might consider using individual Pages and page references as opposed to separate processes altogether.
The last point above lends nicely to your assumption regarding the use of handle. At the risk of being redundant: your assumption itself is correct, but you might want to consider a slight re-design to your processes. It's unlikely that the optimal design of a given Blue Prism process would open an instance of Excel in one process, and not interact with it until another process.
I have the following code in a Form Module in an xlsm:
Public Sub DoModal() ' Note: Called by LaunchHelper which is associated with F12 in Workbook_Open
Dim ewsActive As Worksheet: Set ewsActive = ThisWorkbook.ActiveSheet
If ewsActive Is Overview Then ' Overview is the CodeName of one of the Worksheets
Set ewsOverview = ewsActive ' This is a Private variable in the Form Module
ShowSafe
End If
End Sub
Private Sub ShowSafe() ' Calling Show directly caused random crashes, I concluded that these were caused by that the Form was not loaded into memory, this function makes sure, it is in memory before calling Show
Dim blnLoaded As Boolean: blnLoaded = False
Dim objForm As Object: For Each objForm In UserForms
If objForm.name = Me.name Then
blnLoaded = True
Exit For
End If
Next objForm
If blnLoaded = False Then
Load Me
End If
Show
End Sub
As far as I know:
VBA is single-threaded, it can never happen that two Subs or Functions are executed in parallel (e.g. while processing Worksheet_Activate, Worksheet_SelectionChange is also called, and the order of executing the statements inside those two functions is undetermined).
However, if we look at Excel+VBA as a system, then it is multi-threaded because Excel and VBA run in parallel (and Excel is multi-threaded itself, in addition).
My problem is:
I see a race condition in DoModal, because checking and acting is not atomic, and the following scenario is possible:
(1) The condition ewsActive Is Overview is evaluated as true, so we start executing the branch inside the If block.
(2) The execution is taken over by Excel, where the user switches to a different Worksheet.
(3) ShowSafe is called while we the ActiveSheet is not Overview but something else.
Do you agree with my reasoning? Is it correct that in spite of the checking, the Form might show up on a different Worksheet? So far, I have not succeded in making this error happen but I would like to know if it was possible in theory, even if it has only a low chance.
Where can I find more information about problems and guarantees related to multi-threading in VBA?
For the user to switch the ActiveSheet, Excel needs to process User Events. Experienced VBA programmers usually let Excel process user events by explicitly calling DoEvents, because Excel's GUI freezes/starves for CPU while VBA code is running. This suggests that the race condition you describe is extremely unlikely, if possible at all.
However, this non-concurrency between Excel's GUI and its VBA runtime is only know by practice. I didn't find official documentation that formally confirms that there's absolutely zero chance for Excel to process user events while VBA code is running (without explicitly yielding). We only know by practice that this chance is "almost zero".
For this reason I would say that your suggested race condition is, though extremely unlikely, theoretically possible until we have an official statement or documentation that rules out that concurrency.
p.s. I consider this question as of academic interest only, and the code of illustrative purpose only, because obviously there no relevance for a form to "check" whether it is actually loaded.
I'm not seeing what you're trying to do. Why does it matter what sheet is active when you show your user form? If it needs to be Worksheets("overview") then just Activate it on form Load event? It won't hurt if it's already active.
Similarly if you aren't sure if it's loaded then call Load just before your Show
Finally you're referring to the user form as Me - which implies that you're running this from the user form itself. So this code will not run unless the form is loaded into memory, so ShowSafe is pointless code.
Is there any way to share an object reference (not value) across multiple VBS applications? It seems like an out-of-process COM object might work, but that puts me in way over my head, and I'd like to know if I'm barking up the wrong tree before I spend a week pounding my head on it.
The background: I'm forced by the product I'm using to communicate with my database using a bunch of small vb scripts, each called independently (there's no way around this). This means dozens of individual connections per minute to the database (one connection per script). Rather than flog it this way (constantly establishing new connections), I'd love to figure out if there's a way for a standalone program to define and open the ADO Connection object, and then have that standalone program share the Connection object with all the little vb scripts (so that connection pooling kicks in).
Thanks for your consideration.
As far as I'm aware VB script allows passing parameters by reference. I assume it also allows the references to be returned from functions.
I think your idea may work, and is probably worth a try.
Create a COM Application or library using a language such as VB (as opposed to VBScript) or Delphi which could as you suggest connect to the Database and hold an ADODB connection? Then define a method on that object that is exposed via COM that returns the ADODB connection as an OLEVariant or simple Variant from a function
Something Like
function getConnection() as Object
I really don't know if this will work but it should.