When terminating internet explorer within VBA I'm using the following code sourced (more or less) from stackoverflow:
Sub IE_Sledgehammer()
Dim objWMI As Object, objProcess As Object, objProcesses As Object
Set objWMI = GetObject("winmgmts://.")
Set objProcesses = objWMI.ExecQuery( _
"SELECT * FROM Win32_Process WHERE Name = 'iexplore.exe'")
MsgBox "iexplore.exe processes = " & objProcesses.Count
For Each objProcess In objProcesses
Call objProcess.Terminate
Next
Set objProcesses = Nothing: Set objWMI = Nothing
End sub
The number of processes returned is "2" (which is correct) but in the first iteration of the For loop, when the first iexplore.exe is terminated that also terminates the second process and so in the second loop iteration a 'not found' error occurs. I understand that this is because closing one iexplorer.exe process closes all iexplore.exe tasks.
My question is, how can I reference and terminate only the first 'objprocess' in the objprocesses list OR force the For loop to exit after one iteration?
Do you mean you want to close the first tab or IE window?
I suggest you create an object of Shell and loop through IE windows.
You can close the first IE window and exit the loop or you can match the title of the IE window and close that specific IE window.
You need to add the reference to Microsoft Shell Controls and Automation and Microsoft Internet Controls.
Code:
Sub demo()
Dim oShellWin As Shell
Dim oWin As Object
Dim objIE As InternetExplorer
Set oShellWin = New Shell
For Each objIE In oShellWin.Windows
If TypeName(objIE.Document) = "HTMLDocument" Then
'Debug.Print objIE.Document.Title
If objIE.Document.Title = "Bing" Then 'User can modify the title here...
objIE.Quit
Exit For
End If
End If
Next objIE
End Sub
Output:
Further, you can try to modify the code as per your requirement.
Related
I have an Excel application which collects information via a form based interface. This is used to;
Fill values in the workbook
A procedure opens a Word document (template essentially) and names the file according to rules, based
on some of the input data. (Okay to this point)
The idea then, is to transfer collected information (from the Excel app driving this process) to
the same Word document opened and named.
Specifically, I intend to populate a number of uniquely named ActiveX textboxes with document.
*** This is where I fail miserably.
I have enabled the "Microsoft Word 16.0 Object Library" under references in the MSExcel VBA environment.
Given that I know the name/title of the content control (ActiveX textbox is a 'content control' isn't it?). The code below is a simplified example, if it works for the example I should be able to sort out the broader document:
Sub trial()
Dim Word As Word.Application
Dim wdDoc As Word.Document
On error resume next
Set Word = New Word.Application
Set wdDoc = Word.Documents.Open("G:\CAPS Management Tool\Customer.docm")
Word.Application.Visible = True
Dim cc As Object
Set cc = ActiveDocument.SelectContentControlsByTitle(txt_PersonName) 'txt_PersonName is the control name
cc.Range.Text = "SUCCESS" 'Run-time error 438
'Object does not support property or method
Set cc = ActiveDocument.SelectContentControlsByTitle(txt_Address) 'txt_Address is the control name
cc.Range.Text = "SUCCESS" 'Run-time error 438
'Object does not support property or method
End Sub
Anybody able to assist? There are a lot of text boxes in the Word document I wish to plug in to.
Thanks in advance.
OK, so I kept digging (I don't like accepting defeat) and found that my entire premise was wrong! ActiveX controls in Word are considered "InlineShapes" not "ContentControls". But the results I was reading in the internet searches had me confused (didn't claim to be the sharpest tool in the shed).
Once I realised this, some more digging provided the answer (see below).
So, first to list the 3 controls in my document (and their index) with the following Sub
Sub ListActiveXControls()
Dim i As Integer
i = 1
Do Until i > ActiveDocument.InlineShapes.Count
Debug.Print ActiveDocument.InlineShapes(i).OLEFormat.Object.Name & " Control Index = " & i
i = i + 1
Loop
End Sub
Now moving to EXCEL, I used the following:
Sub trial()
Dim Word As Word.Application
Dim wdDoc As Word.Document
Set Word = New Word.Application
Set wdDoc = Word.Documents.Open("G:\CAPS Management Tool\Customer.docm")
Word.Application.Visible = True
debug.print "ActiveDocument Name is : " & ActiveDocument.Name
' Result = Nothing
' Allowing the code to continue without the pause caused the operation to fail
Application.Wait (Now + TimeValue("0:00:10")) ' See text below, would not work without pause
wdDoc.Activate
' Begin set ActiveX control values. In this instance,
' The first line corresponds to 'Textbox1'
ActiveDocument.InlineShapes(1).OLEFormat.Object.Text = "Success"
' The second line corresponds to 'Textbox2'
ActiveDocument.InlineShapes(2).OLEFormat.Object.Text = "Success"
' The third line corresponds to 'ChkBox1'
ActiveDocument.InlineShapes(3).OLEFormat.Object.Value = True
End Sub
For some reason, without the 'Wait' command, the operation fails. Stepping through, if there is no pause, the ActiveDocument seems to be null, no idea why. It occurs with a document with 2 ActiveX controls or 165 ActiveX controls and the wait required seems to
be 10 secs on my PC. Incidentally, setting almost 150 control values was only seconds, once the wait period was completed.
If anyone knows why the 'Wait' is seemingly required, I'd be interested to know!
Here are a few tips to help you work this out.
Don't use On Error Resume Next unless you really need it. And then when you do use it, re-enable error catching with On Error Goto 0 as quickly as possible. Otherwise you'll miss many errors in your code and it will be hard to tell what's happening.
Don't use the name Word as a variable name. It is reserved for the Word.Application and will only confuse the compiler (and you).
The control titles are text strings, so you must enclose them in double-quotes.
I've thrown in a bonus Sub that gives you a quick method to either open a new Word application instance or attach to an existing application instance. You'll find that (especially during debugging) there will be dozens of Word exe's opened and running.
The example code below also breaks the assignment of your "SUCCESS" value to the control into a separate Sub. It's in this short Sub that using On Error Resume Next is appropriate -- and isolated from the rest of the logic -- limiting its scope.
Option Explicit
Sub trial()
Dim wdApp As Word.Application
Dim wdDoc As Word.Document
Set wdApp = AttachToMSWordApplication
Set wdDoc = wdApp.Documents.Open("C:\Temp\Customer.docm")
wdApp.Application.Visible = True
TextToControl wdDoc, "txt_PersonName", "SUCCESS"
TextToControl wdDoc, "txt_Address", "SUCCESS"
End Sub
Private Sub TextToControl(ByRef doc As Word.Document, _
ByVal title As String, _
ByVal value As String)
Dim cc As ContentControl
On Error Resume Next
Set cc = doc.SelectContentControlsByTitle(title).Item(1)
If Not cc Is Nothing Then
cc.Range.Text = value
Else
Debug.Print "ERROR: could not find control titled '" & title & "'"
'--- you could also raise an error here to be handled by the caller
End If
End Sub
Public Function AttachToMSWordApplication() As Word.Application
'--- finds an existing and running instance of MS Word, or starts
' the application if one is not already running
Dim msApp As Word.Application
On Error Resume Next
Set msApp = GetObject(, "Word.Application")
If Err > 0 Then
'--- we have to start one
' an exception will be raised if the application is not installed
Set msApp = CreateObject("Word.Application")
End If
Set AttachToMSWordApplication = msApp
End Function
Dim objList As WbemScripting.SWbemObjectSet
Set objList = GetObject("winmgmts:").ExecQuery("select * from win32_process where name='iexplore.exe'")
This code returns a collection of SWbemObjectEx objects
relating to all the running processes "iexplore.exe" (as seen in task manager).
I read on the web that I can run the method .Terminate of these objects to kill them.
However, neither the Locals window while in breakmode nor the Object Browser for the "SWbemObjectEx" class, nor the official doc at https://learn.microsoft.com/it-ch/windows/win32/wmisdk/swbemobjectex, show this method .Terminate.
And what surprises me is that it works, although not for all the objects...
Why? and how could I see all these hidden(?) methods for this class?
try this code:
Option Explicit
Sub funTaskTerminate()
Dim objTask As Object
Dim objProcesses As Object
Dim objProcess As Object
' Set the object for Task Manager
Set objTask = GetObject("winmgmts:")
' Set the object for all the processes in query
Set objProcesses = objTask.ExecQuery("select * from win32_process where name='iexplore.exe'")
' Loop for all processes in query
For Each objProcess In objProcesses
' In my test, there were 3 processes, but the first Terminate () killed all the others, therefore,
' in the second Terminate () the process no longer existed,
' which caused an error, so the use of "Resume Next".
'------------------------------------------
On Error Resume Next
Call objProcess.Terminate ' Terminate the process
On Error GoTo 0
Next
Set objTask = Nothing
Set objProcesses = Nothing
Set objproces = Nothing
End Sub
The .Terminate method is part of the Win32_Process class. Put a breakpoint onCall objProcess.Terminateand add a watch onobjProcess. That will show you a.Methods_collection, where second item is.Terminate.
Also note the.Properties_collection to get infos on that process (e.g handle).
This sub prints the collections to the immediate window, just call it inside the loop throughobjProcesses, e.g.
...
For Each objProcess In objProcesses
PrintPropertiesAndMethods objProcess
...
Private Sub PrintPropertiesAndMethods(Process As WbemScripting.SWbemObjectEx)
With Process
Debug.Print vbCrLf & "Properties_ collection:"
Dim Prop As WbemScripting.SWbemProperty
For Each Prop In .Properties_
With Prop
Debug.Print .Name & " " & .Value
End With
Next
Debug.Print vbCrLf & "Methods_ collection:"
Dim Method As WbemScripting.SWbemMethod
For Each Method In .Methods_
With Prop
Debug.Print Method.Name
End With
Next
End With
End Sub
You have to be carefull when terminateiexploror.exeas it has a main process (x64 - C:\Program Files\Internet Explorer), that creates a child process (x86 - C:\Program Files (x86)\Internet Explorer) for each tab (check in task-manager). If you terminate the main process, all childs will terminate too. Too get a second main process for testing, use Run as Admin.
There are many articles dealing with finding the existing IE sessions and then working with them. For some reason, they don't work for me.
I have the following code which creates IE 11 sessions. Sometimes, I manually terminated the VBA code. That left the IE session as an orphan.
Set IE = GetObject("InternetExplorer.Application")
If IE Is Nothing Then Set IE = CreateObject("INTERNETEXPLORER.APPLICATION")
With IE
.Visible = True
.Navigate URL
I have found many articles with all sorts of code to find the existing IE sessions but they don't work for me. The following code is an example.
Sub test_is()
Dim IE As SHDocVw.InternetExplorer
On Error Resume Next
Set IE = GetObject(, "InternetExplorer.Application")
Debug.Print Err.Description
On Error GoTo 0
If IE Is Nothing Then
Set IE = CreateObject("InternetExplorer.Application")
If IE Is Nothing Then
Debug.Print "error getting IE"
Else
IE.Quit
End If
End If
End Sub
When it got to "Set IE = GetObject(, "InternetExplorer.Application")", Debug.Print reported that "ActiveX component can't create object
".
As the code went on, an IE session was created and then quit. That's the only IE session quit. The other IE sessions which had been created by other code stayed intact. I have tried at least six codes I found on the NET. They work for some people but never work for me.
The following is another example:
Sub Find_IE_Windows()
Dim Shell As Object
Dim IE As Object
Set Shell = CreateObject("Shell.Application")
For Each IE In Shell.Windows
If TypeName(IE.Document) = "HTMLDocument" Then
MsgBox "Internet Explorer window found:" & vbCrLf & _
"LocationURL=" & IE.LocationURL & vbCrLf & _
"LocationName=" & IE.LocationName
IE.Quit
End If
Next
End Sub
In my case, the Shell.Windows.Count was zero. So, the code went from the For loop directly to End Sub.
In other cases, Shell.Windows.Count were 2 when I had excel, IE 11 and MS Edge running. And those two shell windows were File Explorer sessions, not Excel, not Edge, not IE 11.
I have Microsoft Internet Controls, Microsoft HTML Object Library, Microsoft Shell Control and Automation all checked. Am I missing some other library?
The only code that works is this one but it stops MS Edge from getting online. So, can't use it.
Sub IE_Sledgehammer()
Dim objWMI As Object, objProcess As Object, objProcesses As Object
Set objWMI = GetObject("winmgmts://.")
Set objProcesses = objWMI.ExecQuery( _
"SELECT * FROM Win32_Process WHERE Name = 'iexplore.exe'")
For Each objProcess In objProcesses
Call objProcess.Terminate
Next
Set objProcesses = Nothing: Set objWMI = Nothing
End Sub
I'm running Windows 10 Pro and Excel 2010.
I'm writing an Excel macro that opens up a Word document and looks for a CommandButton object, by Name. When it finds the object, it tries to check if it has a picture associated with it. It seems to be locating the object, but dies a "catastrophic" death when I try to reference the handle of the picture. I've done this before and looking to see if the picture's handle is zero has worked for me. Not sure what's up here, maybe someone else can see what I'm missing?
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Open(strFileName)
objWord.Visible = True
Set cmdSignatureButton = fncGetCommandButtonByName("NameOfCommandButtonImLookingFor", objDoc)
MsgBox "h=" & cmdSignatureButton.Picture.Handle
' It dies here, giving the error:
' Runtime error -2147418113 (8000ffff)
' Automation error
' Catastrophic failure
Private Function fncGetCommandButtonByName(strName As String, objDoc As Word.Document)
Dim obj As Object
Dim i As Integer
For i = objDoc.InlineShapes.Count To 1 Step -1
With objDoc.InlineShapes(i)
If .Type = 5 Then
If .OLEFormat.Object.Name = strName Then
Set fncGetCommandButtonByName = .OLEFormat.Object
MsgBox "Found the Command Button object" ' Seems to find the CommandButton object here
Exit Function
End If
End If
End With
Next
End Function
I was able to get this functioning without an issue. You may want to step through the code to see if the document is fully loaded first.
Here's the code that's working for me, edited to match the format of the original question posed.
Dim objWord As Object: Set objWord = CreateObject("Word.Application")
Dim objDoc As Object: Set objDoc = objWord.Documents.Open(strFileName)
objWord.Visible = True
Dim cmdSignatureButton As Object
Set cmdSignatureButton = fncGetCommandButtonByName("CommandButton1", objDoc)
If Not cmdSignatureButton Is Nothing Then
'Do something when it isn't nothing
MsgBox "h=" & cmdSignatureButton.Picture.Handle
Else
'Something here
End If
Private Function fncGetCommandButtonByName(strName As String, objDoc As Word.Document) As Object
Dim i As Integer
For i = objDoc.InlineShapes.Count To 1 Step -1
With objDoc.InlineShapes(i)
If .Type = 5 Then
If .OLEFormat.Object.Name = strName Then
Set fncGetCommandButtonByName = .OLEFormat.Object
Exit Function
End If
End If
End With
Next
Set fncGetCommandButtonByName = Nothing 'set it equal to nothing when it fails
End Function
If you are still receiving that error, I'm thinking it may have something to do with the picture not being fully loaded. If so, I'd add some error handling to catch that error and process a retry a second later to see if the picture's handle is available.
Here's what I get when I run that code:
OK, I think I have an approach, at least. I moved on to my next problem, which is very similar. In this case, I am looking for images within Command Buttons within an Excel spreadsheet, but I'm doing so from Access. Instead of trying to jump through hoops and get Access VBA to interrogate the Excel file, I put a Public Function into the Excel file that Access calls. Excel has no problem checking the button for an image, so it just returns the answer for me.
Had to figure out how to Run Public Functions, but that was easy enough. Thanks for the feedback, Ryan. Still not sure why yours worked and mine didn't, but at least I got around it.
I've been running a query for a while now getting data from a webpage. After numerous runs it has decided to stop working, and I've traced the issue back to the ie.document object - it never returns anything.
When compiling my project I see that the "Document" element of ie returns an error of "Applicaiton-defined or Object-defined error" - even before I navigate to a webpage. Also some other elements return this error also - namely "Status Text" and "Type"
The link contains a screenshot of my error:
https://www.dropbox.com/s/wcxxep8my10nu8h/vba%20ie%20document.jpg?dl=0
In case that doesn't work here a scaled back version of the code I'm running
Sub getCard()
Dim ie As InternetExplorer
Dim url1 As String
url1 = "google.com"
Set ie = New InternetExplorer
ie.Visible = True
ie.Navigate url1
WaitBrowserQuiet ie
End Sub
Sub WaitBrowserQuiet(objIE As InternetExplorer)
Do While objIE.Busy Or objIE.ReadyState <> READYSTATE_COMPLETE
DoEvents
Loop
End Sub
As soon as I get to the "Set ie = New InternetExplorer" part of the code is when the ie object is created and I see the errors. If I do happen to navigate to webpage, then the ie.document object is empty.
I've searched around and tried a few things to stop this happening - restarted my computer, run "ie.quit" and "Set ie = Nothing", reset my Internet Explorer, etc... Nothing seems to work.
It seems like it may be a deeper issue given I'm getting an error message even before navigating to a webpage. Hope someone knows how to stop the error.
Your URL is URL1, try changing that, or just putting the URL in there.
In your code you have the object "ie" locally defined in the sub getCard and when this sub finishes,so goes the binding. Also changing from private to public internet zones can remove the binding to that object. What I rather do is use a global object appIE and then when it runs into such an error I catch the error (if TypeName(appIE) = "Object" Then FindIE) and find the object again with this sub:
Sub FindIE() 'Needs reference to "Microsoft Shell Controls And Automation" from VBA->Tools->References
Dim sh
Dim eachIE
Dim SearchUntil
SearchUntil = Now() + 20 / 24 / 60 / 60 'Allow to search for 20 seconds otherwise it interrupts search
Do
Set sh = New Shell32.Shell
For Each eachIE In sh.Windows
If InStr(1, eachIE.LocationURL, ServerAddress) Then
Set appIE = eachIE
'IE.Visible = False 'This is here because in some environments, the new process defaults to Visible.
'Exit Do
End If
Next eachIE
If TypeName(appIE) <> "Object" Then
If InStr(1, appIE.LocationURL, ServerAddress) > 0 Or SearchUntil < Now() Then Exit Do
End If
Loop
Set eachIE = Nothing
Set sh = Nothing
End Sub
This code contains parts of other people here from stackoverflow, but I forgot who to credit for some essential parts of the code. Sorry.