Opening multiple webbrowser windows in a loop. - vba

I have an excel spreadsheet and using VBA to code functions. I am having a user enter X number entries into a spread sheet and want to be able to open X number of webbrowsers for each entry. The webbrowsers will go to specific websites. The code I have now works for one entry and I use Webbrowser1.Navigate to "CX". What i want is if there are 5 entries for Webrowswer2....5.Navigate to "CX". Is there a way to have a dynamic webbrowser?

Since you've not included any code, its a little difficult to see what you're doing so far.
If you add the "Microsoft Internet Explorer" reference to VBA you can create an array or browsers. You can do it without adding the reference using CreateObject but you wont get any intellitype help in the editor so might be harder if you don't know the control's methods etc
' Create a large array of them and initialise/destroy them as needed
Dim Browser(1 to 10) As InternetExplorer
' Init browser1
Set Browser(1) = New InternetExplorer
' Destroy browser1
Set Browser(1) = Nothing
Is there any reason you can just use one/two browsers and load the pages one after the other? Depending on what your goal is it might not help that much.

Related

SolidWorks VBA - Translating API Help into useable code

I'd like to do what feels like a fairly simple task, and I've found the specific API Help pages which should make it clear, but, I can't actually make things work.
The Key steps that I would like to achieve are:
Rename the active document
Update References to this document to accommodate new name
Save active document.
This help page shows the Usage for renaming the doc, and under the "Remarks" heading, includes links to the next two steps, mentioning them off hand as if implementing them would be easy.
https://help.solidworks.com/2020/English/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IModelDocExtension~RenameDocument.html?verRedirect=1
The trouble is, I'm a bit of a VBA beginner - usually I get by with the 'record' function, and then tidying things up from there - but undertaking the steps above manually doesn't result in anything being recorded at all for one reason or another.
Assuming I am able to pass in the item to be renamed (I'll define a variable at the start of the Sub for this e.g. swModel = swApp.ActiveDoc), and the new name (NewName = "NEW NAME HERE"), How would I translate the Help API into a Sub that I can actually run?
Two of them suggest declaring as a Function, and one as a Public Interface - I've never used these before - do these just run in a standard Module? Do I need to write a 'master Sub' to call the different functions sequentially, or could these be included directly in the sub, if they're only to be used once?
[Feeling a little lost - it's demoralizing when the help files aren't all that helpful]
Let me know if there's any more information missing that I can add to improve my question - as I said, I'm fairly new to this coding thing...
The "record" function is sometimes a good point to start but there are a lot of functions it can't recognize while you execute them manually.
The API Help is then useful to find out how to use a specific function.
In almost every example the use of a specific method (e.g. RenameDocument) is only shown abstract. There is always a instance variable which shows you the object-type needed to call this method. So you can use these in every sub you want, but beforehand need access to the specific instance objects.
For your example the RenameDocument method is called with an object of the type IModelDocExtension. First thing for you to do is to get this object and then you can call the method as described in the help article.
Under Remarks in the article you find additional information for what you maybe have to do before or after calling a method.
For your example it is mentioned that the renaming takes permanently place after saving the document.
And finally here is what you want to do with some VBA code:
Dim swApp As SldWorks.SldWorks
Dim swModel As ModelDoc2
Sub main()
' get the solidworks application object
Set swApp = Application.SldWorks
'get the current opened document object
Set swModel = swApp.ActiveDoc
' get the modeldocextension object
Dim swModelExtension As ModelDocExtension
Set swModelExtension = swModel.Extension
Dim lRet As Long
lRet = swModelExtension.RenameDocument("NEW NAME")
If lRet = swRenameDocumentError_e.swRenameDocumentError_None Then
MsgBox "success renaming"
Else
MsgBox "failed with error: " & lRet
End If
End Sub
Afterwars you have to process the return value to check for errors described in this article: https://help.solidworks.com/2020/English/api/swconst/SolidWorks.Interop.swconst~SolidWorks.Interop.swconst.swRenameDocumentError_e.html

Is there a way in VBA to iterate through specific objects on a form?

I would like to have a subroutine in VBA that conditionally changes the Enabled property of each of 20+ buttons on a form via iteration rather than code them all by hand. These buttons are named similar to tables that they process. For example: A table to process is called "CUTLIST"; its corresponding button is called "but_CUTLIST". There is another table that holds the list of tables to be processed (used for iteration purposes in other subs).
What I have so far...
Private Sub txt_DataSet_GotFocus()
Dim sqlQry as String
Dim butName As String
Dim tableList As Recordset
Dim tempTable As Recordset
Set tableList = CurrentDb.OpenRecordset("TableList") 'names of tables for user to process
tableList.MoveFirst 'this line was corrected by moving out of the loop
Do Until tableList.EOF
sqlQry = 'SQL query that determines need for the button to be enabled/disabled
Set tempTable = CurrentDb.OpenRecordset(sqlQry)
If tempTable.RecordCount > 0 Then
'begin code that eludes me
butName = "but_" & tableList!tName
Me(butName).Enabled False
'end code that eludes me
End If
tableList.MoveNext
Loop
End Sub
If I remember correctly, JavaScript is capable of calling upon objects through a variable by handling them as elements of the document's object "array." Example: this[objID]=objVal Is such a thing possible with VBA or am I just going about this all wrong?
Viewing other questions... is this what's called "reflection"? If so, then this can't be done in VBA. :(
In case more explanation helps to answer the question better... I have a utility that runs SQL queries against a pre-defined set of tables. Each table has its own button, so that the user may process a query against any of the tables as needed. Depending on circumstances happening to data beforehand, any combination of the tables may need to be queried via pressing of said buttons. Constantly referring to the log, to see what was already done, gets cumbersome after processing several data sets. So, I'd like to have the buttons individually disable themselves if they are not needed for the currently focused data set. I have another idea on how to make that happen, but making this code work would be faster and I would learn something.
I'm not an expert on VBA, but I would re-arrange the code to take advantage of the fact that you can iterate through the control collection in the user form
Something like this:
Dim ctrl as Control
For Each ctrl in UserForm1.Controls
If TypeName(ctrl) = "Button" Then
ctrl.Enabled = True
End If
Next
You can pass the button name to some other function (from this loop) to determine whether the button in question should be enabled / disabled.

Lotus Notes and VBA Checking Email Address in Public Address Books

I'm trying to figure out how to check whether an e-mail address is in the public address book by using VBA. I found some code on the Web, modified it but the code gives an error 91 at line, "Set doc = view.GetAllDocumentsByKey(ChkEmailAddr)." I think that the problem has to do with the declaration type of the variable "view" or the type of view in the line "Set view = b.GetView("People\By Internet Mail")."
I'm pretty certain that I have all of the proper references activated. "Lotus Notes Domino Objects" and "Lotus Notes Automation Classes" are chosen.
I tried to get a list of views but I couldn't figure out how to do it. Do you see the error in my code or have ideas as to what I could try to do some troubleshooting?
Sub CheckEmailAddress()
Dim books As Variant
Dim view As lotus.NotesView
'Dim view As Object
Dim doc As NotesDocumentCollection
Dim dc As NotesDocument
Dim done As Variant
Dim docarr(3, 50) As Variant
Dim ChkEmailAddr As String
ChkEmailAddr = "user#example.com"
Set Session = CreateObject("Notes.Notessession")
books = Session.AddressBooks
done = False
For Each b In books
' check every public address book,
' unless we're already done
If (b.IsPublicAddressBook) Then
Debug.Print TypeName(b)
Call b.Open("", "")
Debug.Print b.Title
' look up person's last name
' in People view of address book
Set view = b.GetView("People\By Internet Mail")
Debug.Print TypeName(view)
'Debug.Print view
'Set view = b.GetView("main")
'Debug.Print TypeName(view)
Set doc = view.GetAllDocumentsByKey(ChkEmailAddr)
' if person is found, display the phone number item
'from the Person document
If Not (doc Is Nothing) Then
For j = 0 To doc.Count
docarr(0, j) = doc.GetNthDocument(j).Items(11).Text
docarr(1, j) = doc.GetNthDocument(j).Items(93).Text
docarr(2, j) = doc.GetNthDocument(j).Items(95).Text
docarr(3, j) = doc.GetNthDocument(j).Items(14).Text
Next j
End If
End If
Next b
findEmailLotus = docarr
End Sub
I'm sorry to say this, but you have a lot of issues here. Whoever wrote that code that you started from did not know what he or she was doing.
First of all, unless you are requiring that the Notes client is running while your code is running, you should be using Lotus.NotesSession instead of Notes.NotesSession. (The former corresponds to the "Lotus Notes Domino Objects", and uses COM to talk to the Notes APIs, and the latter corresponds to the "Lotus Notes Automation Classes, and uses OLE to talk to the Notes client to talk to the APIs - hence the requirement that the client must be running.)
Secondly, you haven't mentioned what version of Lotus Notes you are dealing with, but more recent versions (8 and above) include the NotesDirectory class, which includes a LoookupNames method that would probably be a better solution for you than writing your own code to loop through address books.
Third, after doing your set view operation, you really ought to be doing an If Not view is Nothing test. That will tell you whether or not you actually have a problem opening the view.
Fourth, doc is a really bad variable name for the return value from GetAllDocumentsByKey. In 20+ years of writing Notes code, I can say that anybody reading Notes code expects the variable name doc to always refer to a single document. You are getting a NotesDocumentCollection, not a single document. Do yourself a favor and change it to docs or dc, or just about anything except doc.
Fifth, using GetNthDocument in this context is usually not recommended. It performs very badly in large collections. Even worse, however, is that you are calling it four times when you could be making only one call per iteration. Instead of a For loop, consider changing it to a call to GetFirstDocument followed by While Not doc is Nothing loop that retrieves your item values and stores them in your array, and then calls getNextDocument at the bottom of the loop.
Sixth, that code referencing .Items(11), .Items(93)... that's just plain wrong. The available items within any given document are variable because Notes is schemaless. Those item numbers will refer to different fields for different people - i.e., essentially random values. That can't possibly be what you want. You should be using getFirstItem() calls with the actual names of the items that you really want to be putting in your array. You will need to study the field names used in the Domino Directory to figure this out. I recommend NotesPeek as a good tool for exploring Notes databases and/or just opening up the Domino Directory in the Domino Designer client and looking at the Person form (and associated subforms) to figure out what you need.
As to the actual error you asked about, my guess is that by adding the recommended test of If Not view Is Nothing you will gain more information, but perhaps not enough. You haven't mentioned what your debug prints are generating, but I believe there are some cases where the title is available even if the database was not successfully opened, so I don't think you should trust that as a test of whether the call worked. In fact, you really shouldn't just be doing a Call db.open("","") call. You should be doing an If db.open("","") = true to test whether it actually worked.
For people who know lotus notes, it's trival. For those that don't, there might be an easier way using web access.
Request this address
Keep your cookies
http://server/names.nsf?login&username=MYUSERNAM&password=MYPASSWORD
Then access this url and look for a 404 status or a 200 status
http://server/names.nsf/($Users)/email#domain.com?opendocument
Of course, that requires that you have web access enabled on your server and in many cases putting your password in your code is bad, and it won't work if your servers are configured in some ways.
Before coding, test it on your server.

Excel VBA runtime error '-2147319767 (80028029)'

Hey all I have a quite weird problem with my VBA code. I use macros to add/remove userform controls programmatically. When I simply add a userform control and after that try to remove it, everything works fine. But as soon as I try to remove userform controls I just created and USED (just like userform.Show) I receive the titled error. After trying again it works ... so first question be like, what the hell is this? And second question would be how can I solve this or do I even need to solve it? Could I just run over this error with some kind of error handling?
I'm not sure what part of my code helps to understand my problem, but I assume the code to remove the userform controls could help:
Public Function delete_userform_controls(strUserForm As String)
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim iCount As Integer
Dim cnt As Control
Dim iMaxColumns As Integer
Dim lCountOfLines As Long
Set VBProj = ActiveWorkbook.VBProject
Set VBComp = VBProj.VBComponents(strUserForm)
For Each cnt In VBComp.Designer.Controls
If TypeName(cnt) = "Label" Then iMaxColumns = iMaxColumns + 1
Next cnt
With VBComp.Designer
For iCount = 0 To iMaxColumns - 1
.Controls.Remove ("label_" & iCount & "")
.Controls.Remove ("textbox_" & iCount & "")
Next
.Controls.Remove ("ok_button")
.Controls.Remove ("cancel_button")
End With
With VBComp.CodeModule
.DeleteLines 1, .CountOfLines
End With
End Function
And just in addition, if I don't user any of the userfoms I can add and remove controls as much as I want, no errors ... thx!
I suspect you are adding controls at design time, but once the userform is loaded you are trying to remove controls at runtime, which is not allowed. When you see the error for the first time and choose [End], you are resetting the VBA environment, which unloads the UserForm. (Other things happen too: all global variables lose their values.) At that point, the userform is no longer loaded, so you can delete controls.
If this is your situation, then you need to make sure the userform is loaded before adding controls. When you do userform.Show for the first time, two things happen: The form is loaded, then it is made visible. You can load it without making it visible with the .Load method. Then you can add your controls. So long as you don't unload the form with the .Unload method (or reset VBA), your controls will persist. .Hide and .Show will change the form's visibility, but won't cause controls added at runtime from being discarded.
On the other hand, if you really want to add controls at design-time so that they persist (and are saved when the workbook is saved), then you need to use .Unload to unload the form before removing controls.
in my case was some kind of corruption in the book. I work with office 365 and it has happened many many times. It crashes and somehow the workbook wont work again, because of this, or any other thing
The only way I found is using my personal computer with an excel version different from office 365 (2019 in my case). Add a blank page or remove one, and save it with a different name.
Check that the size of the new file is sliUsually it solves the issue

.Select, .Activesheet, .Activecell etc...

For this question, I refer to the post below to clarify myself:
Why is my conditional format offset when added by VBA?
In many, many posts I see these days, OP's are silently allowed to use .Activate, .Select, .Offset, etc... while they are an open door to potential bugs (most often caused by the end users).
The code is sometimes even supported.
My question: Is there one valid situation where you would use any of these statements without direct alternatives being available that catch typical bugs resulting from these stmts?
I'm referring to dynamic solutions that in my opinion are a must when developing for Excel.
Personally, in more than 6 years I can't remember a single case where I needed it; it seems always to be one of the the worst options available. In my previous company, it was a silent rule never to use it and it only made my VBA life (and that of the end user) better.
Why I create this question is because I think that it is worthful to make newcomers into VBA aware of the risks they take when using these statements (by experience proven risks when the End Users do something unexpected - in the end they don't have any affection with VBA) and to propose direct alternatives (I won't state I always did that before myself, but I feel in my gut that there is something wrong with just offering quick solutions on already bug monsters).
I believe that when silently allowed (which it automatically enhances in this case), starting VBA developers will create a growing amount of tools the wrong way (and thus also newcomers will inherit the behaviour - which they will also learn from Stack Overflow since Google returns the results they look for (!)).
If the developer is not aware why he "can" use a "select" and in which situations it is a potential bug, (s)he should never use it imho. Personally I might use the select stmt in the immediate window to do some quick checks on dynamic range definition (bug mode), but not in written code.
The result makes VBA in the end even more unpopular than it is already; the language will be made the victim in case trouble appear (yet it is imho still the "best" programming support available for the Excel and Access applications). I've seen this happen too many times in a large company where VBA is always "shit".
This is only my own honest experience.
It is not a question of being right or wrong; I am interested in hearing your point of view on the question.
I agree about Select and Activate, but not ActiveWorkbook, ActiveSheet, and ActiveCell (I agree that they are abused, but not that they should be avoided, per se). There are definitely legitimate uses for those. I have a program that automates a "fill series" that does so from the ActiveCell. My program can't predict what cells will be used; it's up the user to select it. That's part of the user interface.
However, there are three situations where I have had to use Select (now four that I read about zoom, but I don't ever use it).
Conditional Formatting. There is a work around using Application.ConvertFormula, but it's worse than just storing the selection, selecting the right cell, doing the deed, and reselecting the previous selection.
Data Validation. Same reason.
Shapes. I wish I could remember the details, but it's been too long since I've worked with Shapes. There was something I couldn't do without selecting the shape first.
Ridding code of Select and Activate is a noble fight.
There are a few methods in Excel that require Activate or ActiveSheet/ActiveWorkbook etc as I've been caught with a gotchas on occasion. The only one I can remember at the moment is the zoom property. Zoom affects only the sheet that's currently active in the window so to zoom all sheets you would need something like
Sub SetZoom()
Dim ws As Worksheet
Application.screenupdating = false
For Each ws In Worksheets
ws.Select
ActiveWindow.Zoom = 80
Next ws
Application.screenupdating = true
End Sub
You can use .Select to determine what a user's view is after running code - for example if you create a new workbook in your code, without using Activate or Select your user may not know this happens.
I frequently end a long operation creating a new workbook or other largescale data manipulations with
FinalViewWorkbook.FinalViewSheet.Range("A1").Select
Just to inform the end user about something - "oh, this created a new workbook of reports!" etc.
I think it is important in this matter to distinguish some:
Active-something: Only use this if it is absolutely necessary to know what the user is handling right now. In my experience, this is usually Data Validation or Active Sheet Detection (e.g. "Update the Sheet where the user just pressed a button").
Selection: Somewhat the same as Active, only use readingly. Userful either for Data Validation, or for gimmicks like "Interpret the cell value as path and open it in a new Explorer Window".
Select, Activate: Imho different from Selection, as it actually changes the selected Cell, Sheet etc. Never ever use this to read or write data, since it enables a user to mess up your program by just clicking. Users love to click. Only use this to Zoom (see answer by #user3357963) or clean up a view after your code has finished working (see answer by #enderland). (I'm not sure, but I think handling the PageView also requires ActiveSheet).
Select, Activate the 2nd: If you are new to VBA and are learning via Macro Recorder, you will find a lot of code generated like this:
First Range("A5").Select, then Selection.Value="NewValue". Join this to Range("A5").Value="NewValue".
Offset: Personally, I don't have a problem using .Offset() - I never encountered problems with this command. Instead, I think it's a handy way of saying "The cell next to this" without having to go through "This cell's sheet at this cell's row and column+1" every time.
In many, many posts I see these days, OP's are silently allowed to use .Activate, .Select, .Offset, etc...
I agree with this. Even though it's easier to just give the necessary answer to make a piece of code work, the use of ActiveCell.Value and the like should be discouraged. This will be much easier if there's a well explained Thread to link to, as this here is hopefully becoming :-)
From my perspective, with few exceptions, the only time you should use Select is as a user input, and only then after careful consideration of alternative design/UI requirements.
For example, I'd say it's generally not advisable to rely on Selection to let user define a Range object when this method keeps execution within the code:
Dim myRange as Range
Set myRange = Application.InputBox("Select your range", Type:=8)
However, if you need to prompt users to select a particular shape or object on the worksheet, then maybe it's better to let them make a Selection (however, this can open up a Pandora's Box of problems without good error-handling and logic to prevent undesired user actions...).
Here is an example of one such exception that I have in PowerPoint. I have some RibbonUI XML and VBA that adds buttons to the Shapes right-click context menu in PowerPoint, and adds similar buttons to the Ribbon itself. These are seamless UI that give the end-user a more "native" experience with the application -- users want to be able to right-click the chart and then run some macro procedures against that selected chart or table, etc. They don't want to press a button to open up a user form and scroll through a listbox of generic shape names or GUIDs.
The procedure code needs to examine the Selection in order to handle it properly so I can use something like below, where
Sub UpdateOrEditSelection(update As Boolean)
'This procedure invoked when user edits/updates a chart.
Dim uid As Variant
Dim sel As Selection
Dim s As Integer
Dim chartsToUpdate As Object
Dim multipleShapes As Boolean
Dim sld As Slide
Set sel = ppPres.Windows(1).Selection
If update Then
Set chartsToUpdate = CreateObject("Scripting.Dictionary")
Select Case sel.Type
Case ppSelectionShapes
For s = 1 To sel.ShapeRange.count
uid = sel.ShapeRange(s).Name
'....
'...
'..
'.
Next
Case ppSelectionSlides
For Each sld In sel.SlideRange
For s = 1 To sld.Shapes.count
uid = sld.Shapes(s).Name
'....
'...
'..
'.
Next
Next
Case ppSelectionText
s = 1
If sel.ShapeRange(s).HasTable Or sel.ShapeRange(s).HasChart Then
uid = sel.ShapeRange(s).Name
'....
'...
'..
'.
End If
End Select
'....
'...
'..
'.
Where does it come from?
The Macro Recorder. Essentially, this feature records every literal user input: scrolling, selecting, viewing, activating, default properties, etc., to the point of overkill. While this is sometimes helpful, it does encourage bad code written by people who don't know that it's bad, but I will not belabor that point which has been made here:
How to avoid using Select in Excel VBA macros
What is better, conceptually?
Program to the objects directly. If you're merely using VBA to mimic keystrokes and mouseclicks, you're doing it wrong.
Exceptions:
I've found when applying formatting to series data in charts, where Select is occasionally necessary. This seems IMO to be a bug with Excel and not a design feature.
Other applications (because VBA is not only Excel):
Word is a different animal, which relies a lot more on Selection object
In PowerPoint there are some sort of operations that can only be performed when the application and slide/shape are visible or otherwise in view. While you don't usually need to "select" anything, it does require more cumbersome code.
I found this snippet in my App:
Set tb = cht.Shapes.AddTextbox(msoTextOrientationHorizontal, ptLeft, tBoxTop, ptWidth, ptHeight)
tb.Select '<--- KEEP THIS LINE OTHERWISE TEXTBOX ALIGNMENT WILL NOT WORK ## ## ##
And this:
'PPT requires selecting the slide in order to export an image preview/jpg
sld.Select
ppPres.Windows(1).View.GotoSlide sld.SlideIndex
sld.Shapes(1).Chart.Export imgPath, ppShapeFormatJPG
And this, dealing with individual Point objects:
pt.Select
pt.format.Line.Visible = msoTrue
pt.format.Line.Visible = msoFalse
pt.MarkerSize = pt.MarkerSize + 2
This is not an exhaustive list, just some examples of exceptions that I found. While these were from PowerPoint, the charts in PowerPoint use the same object model as Excel so I would not be surprised if some of these also need to be hacked in Excel, and Word, too.
Outlook: I don't do much with Outlook, it is a lot like Word and actually uses the Word object model in the Inspector, but what little I do with Outlook does rely on things like ActiveInspector, etc.
Neither Word or PowerPoint have a "macro recorder" anymore (actually, I think Word might but it's so damn impotent as to be useless) and by the time most people do any development in other applications, they've figured most of this out already.