Add values to a list in vba - excel

Feel like this should be really simple but. I have an excel program that picks a value from a list, searches for a file in our system, and outputs if the file is there or not. Works great if I use:
set missing_file = createobject("System.collections.arraylist")
missing_file.add "Missing file"
However that requires .net 3.5 to be loaded on everyone's computers which I guess is too difficult for people to do.
Ive tried using
dim missing_file as New List(of string)
but excel errors out with a "compile error expected end of statement"
Is there an alternative in the .net 4 that is similar to Arraylist or a different way to add things to a list of indeterminate length?

Related

Excel VBA hard crash on ModifyAppliesToRange with no error message

I'm getting a strange problem in Excel and VBA.
I'm calling ModifyAppliesToRange on various FormatCondition, along with quite a lot of other FormatCondition related stuff.
It works fine for a while (about 2-300 calls) and then for no reason I can see my Excel (and the VBA development environment) will shut down with no error from either my error handler or excel, no notification, nothing except a 'pause' where nothing happens until it crashes. This happens whether I'm running it normally or with a breakpoint and Step.
Depending on the exact contents of the WorkSheet's FormatConditions the exact number of times it works seems to vary. But if the starting condition is the same then the point of failure is the same (i.e. it fails on the same FormatCondition). Code failing is:
myCF.ModifyAppliesToRange Union(range1, range2)
where myCF As FormatCondition
I tried
Dim rng As Range
Set rng = Union(range1, range2)
myCF.ModifyAppliesToRange rng
I checked: myCF, range1, range2 and rng are all valid as far as I can tell, and show valid .Address strings (for the ranges). For example, it will fail on range1.Address="$DO$9:$GN$39,$BD$8:$BD$39", range2.Address="$CI$9:$DN$39", and the resulting rng.Address="$BD$8:$BD$39,$CI$9:$GN$39"
So I tried
Set rng = Union(Range(CFFrom.AppliesTo.Address), Range(CFTo.AppliesTo.Address))
Same problem.
On occasion (under different start conditions) it will get past the above ModifyAppliesToRange but the moment I look at myCF.AppliesTo.Address the same crash happens - whether hovering a mouse over it, or showing it in Watches, or displaying it in Immediate. Yet rng.Address gives no such problem.
Yet it also seems to work just fine for 2-300 times.
I suspect a memory problem (but Excel is not using much memory or CPU, there's plenty of empty memory on the computer, I've used Option Explicit, and so on) or a hard limit or bug in Excel
Any ideas as to why this might be happening?
Any ideas as to how I can find out what is shutting down Excel?
NOTE: To give the exact way to reproduce it I'd have to post my entire spreadsheet and code, which I'm reluctant to do. Still I might be able to send one-on-one if need be.
I've encountered this problem as well. It does seem to be an Excel bug to be honest.
However, according to this Technet forum post, it seems that Excel changes the format conditions collection on the fly when updating conditions. Seeing as that might lead to nasty bugs when processing all conditions in a loop (like I did), I first rewrote my code so that it first collects all conditions to process in a Collection, and then loops over those. That still gave me Excel crashes; possibly the condition objects themselves also get updated (created/destroyed) behind the scenes mid-way, causing stale pointers to deleted VBA objects?
Not trusting Excel to keep the order identical either, I then rewrote my code to keep looping over all conditions, finding the first to update and just restarting the loop, until not a single condition was left unprocessed. So basically like:
Dim stillBusy As Boolean
Do
stillBusy = False
Dim nextCondition As FormatCondition
For Each nextCondition In myRange.FormatConditions
Dim newAreas As Range
Set newAreas = ResizeAreas(nextCondition.AppliesTo, myRange.Rows.Count)
If Not AreasEqual(nextCondition.AppliesTo, newAreas) Then
Call nextCondition.ModifyAppliesToRange(newAreas)
stillBusy = True
Exit For
End If
Next
Loop While stillBusy
And while this is super inefficient, it... miraculously works?! (for now). Hopefully it also works for anyone else?
Btw: ResizeAreas and AreasEqual are some of my own helper functions; ResizeAreas transforms e.g. [A1:A3,C1:D3] to [A1:A6,C1:D6], and AreasEqual looks if the given two ranges are composed of the exact same cells ([A1:A2,B1:B2] and [A1:B2] = True)

Using VB.NET to create a Pivot Table on an Exported Document

I have a pipe-delimited text file that I need to read, turn into a pre-formatted Excel document - complete with separate pivot table sheet - and save.
Reading the file and saving it as an Excel document are solved, but I can't seem to get the pivot table to work. I've found a variety of tutorials online, but none are actually working for me.
I've reordered the arguments in the order they present, but I get the exact same late binding error.
When VS complains and states: "Option Strict On disallows late binding", it is telling you that you are either attempting to reference some method or property on a object that it only can identify as type System.Object or perform an invalid implicit cast.
Now let's look at the problem code.
Dim ptTable As Excel.PivotTable = xlPivotSheet.PivotTables.Add(ptCache, ptLocation, "Pivot")
You previous defined:
Dim xlPivotSheet As Excel.Worksheet
Therefore, xlPivotSheet is not the culprit as VS knows it is type Excel.Worksheet.
The next logical suspect is what PivotTables returns. While you were keying in that code, you should have seen something similar to this:
Did you notice that nice little help that Intellisense gave you when you entered the code? You did enter the code, right? Or did you copy-paste an example from some internet landmine site that posts code with Option Strict Off?
Regardless, you now know that you should cast the result of xlPivotSheet.PivotTables to the Excel.PivotTables interface to enable early binding and thus use the Add Method without that nasty error.
Dim ptTable As Excel.PivotTable = DirectCast(xlPivotSheet.PivotTables,Excel.PivotTables).Add( ....
Recommended Reading: Using IntelliSense
I ended up accomplishing this by changing the problem line to the below.
Dim Table As Excel.PivotTable = xlPivotSheet.PivotTableWizard(Excel.XlPivotTableSourceType.xlDatabase,
Range,Location, "Title Here",True, True, True, False)

Correct handling of Multiple instances of Interop.Excel in VB .NET

My application requires a single Excel to be read by, lets say, 2 different functions within a class.
I have one to read the headings and save them to a .txt file, one to read the data and put it into my DataGridView
As far as I'm aware Interop.Excel is the best way to handle the excel file I will open in my program but unfortunately I'm left with some rather length repetitive code.
Each class at the moment contains the following code:
Dim exApp = New Excel.Application
Dim exWB = exApp.Workbooks.Open(myFilePath)
Dim exWS = exWB.Sheets(1)
** Some statements run here **
exApp.Quit()
exApp = Nothing
exWB = Nothing
Not much of a problem if I only have two functions but if I had like 5, this seems like a lengthy way to keep opening and closing the Excel Object.
What would be the best or more efficient way of handling the excel object to be used rather than closed and reopened?
I have tried initialising the Excel object in the class I'm using but I get an error when trying to close it from a function saying it's not allowed.
I also tried to create a CloseExcel() function which is passed ByRef the excel object to be closed, but I get the same error.
Any thoughts or discussions would be appreciated.
Many thanks!
It is much more complicated of how you are describing it.
I can give you some general points how to should write your application:
Create a wrapper class ExcelWrapper for example that will be the only class that will deal with excel. The other classes (for example the class in the business layer) will instantiate an object of type ExcelWrapper (not Excel).
This class will:
instantiate the excel.application object in the constructor
Have a method that read from the excel all what you need and returns the data to whoever call it. You could return a DataTable for example to populate the datagridview.
Have another method to read the header and return the info as string (depending of what is written in the header)
A close excel method to close the opened documents and the application
You need to implement IDisposable pattern in order to properly clean interop excel objects ,GC cannot do it since it is a unmanaged code.
For each one of the above items, you can find lot of examples in the internet.

Cannot remove code modules from a destination workbook through VBA

I am running an Excel 2010 macro that opens another workbook and removes a few code modules (a form and a BAS module). After that it re-imports them as an updated version. This is the code:
For Each x In destination_wb.VBProject.VBComponents
If LCase(x.Name) Like LCase("frmCCLogin*") Or _
LCase(x.Name) Like LCase("modCQ_test*") Then
destination_wb.VBProject.VBComponents.Remove (x)
Next
I have no problem with the import but the remove process doesn't always work as expected. For some reason the BAS (modCQ_test.bas) module is not always removed. As a result, when I re-import, a new duplicated module is created ending with a "1" (i.e. modCQ_test1.bas).
I could see that many people experienced the same problem however, none of the proposed solutions worked for me. Not sure why this is happening?
Please advise.
If you can use the exact name of the module, you can write something like:
Public Sub RemoveComponent(ByVal Book As Workbook, ByVal Name As String)
On Error Resume Next
With Book.VBProject.VBComponents
Call .Remove(.Item(Name))
End With
End Sub
If you're stuck with wildcard matching (i.e. SomeName*) you could iterate the VBComponents collection and cache the names into a collection or array or whatever and call the function above for each name matched.
Additionally, if you wish to enumerate the VBComponents collection and remove like your code sample, I recommend that you go in the reverse order.
So something like:
Public Sub RemoveComponent1(ByVal Book As Workbook, ByVal NameSearch As String)
Dim oCompS As VBComponents
Dim oComp As VBComponent
Dim i As Integer
Set oCompS = Book.VBProject.VBComponents
For i = oCompS.Count To 1 Step -1
Set oComp = oCompS(i)
If oComp.Name Like NameSearch Then Call oCompS.Remove(oComp)
Next
End Sub
Problem is resolved. This simple line of code that hides the destination workbook, fixed the duplication issue in my case:
destination_wb.Windows(1).Visible = False
After this you can remove, then add the components. No duplication will occur.
I have experienced exactly the same phenomenon and it drove me mad for weeks already.
I do check whether the Code Module had definitely been removed directly after the removal and although it had vanished from the VBE's Project View, it still exists and consequently the subsequent import creates a Code Module named xxx1.
Any of the hints given proved not to be reliable on the long run and thus are nothing but guesses. Since the phenomenon is unpredictable as mentioned you never can really tell what did the trick.
As time (some years) has passed I do now have an answer for the phenomenon and a solid and stable solution.
In order not to "cut off the branch you sit on" one will have to envoke another Workbook/VB-Project for deleting and re-importing a Component.
Even another VB-Project performing the task will have to consider that the Component is definitely removed when the code which removed it has "finished".
Conclusion: Rename, Remove, Import, all performed by a VB-Project invoked via "Run ...." will do the trick.

VBA Excel - Macscript causing problems on windows when using IIF

I'm creating a macro that opens a file that everyone has on their computer and in order to do so must know the person's username / work ID.
To get the person's work ID I've tried using the following:
sso = IIf(InStr(Application.OperatingSystem, "Windows") = 1, Environ("UserName"), _
'MacScript("(user name as string)"))
Running this on windows returns an error because of the Macscript (I think) and I'd assume the same would happen vice versa, even though the error part of the IIF is never actually accessed I'm guessing seeing as the whole line is executed this is why there is a problem, thus On Error Resume Next would not really help here.
I know this can be easily overcome by just using an if and else statement but I just want to know if I'm right / why this problem occurs and if there are any other more sophisticated ways of achieving what I want.
Thanks
The IIF function evaluates both the true and false parts, or rather it attempts to do so. There is no short-circuit. Your assumption about why it's failing (and also that you can't use an OERN) is correct. You may take a look at conditional compilation logic, if certain parts of your code will not compile on Windows (or Mac, respectively).
http://msdn.microsoft.com/en-us/library/aa240847(v=vs.60).aspx

Resources