I am dealing with a web form text field that will initiate a search on its contents, when "Enter" is pressed.
I know how to initiate all of the other event listeners but I am unable to get the press "Enter" event to trigger. It is not listed with the other events. i.e. onchange, onclick, onblur
I am using the CreateObject("Shell.Application") as the parent object in Excel VBA to control an existing IE browser.
I tried sendkeys but have trouble with what VBA focuses on. It types in my IDE or on the spreadsheet itself.
It is not a public site. It is an input tag with Events (blur, change, focus, keydown, mousedown, mousemove).
With Tex_Box
.focus
.keydown
.innertext = Field_Text
.change
.focus
.blur
End With
So I found that stackoverflow.com/questions/11655591/… describes how to provide Internet Explorer with focus before Sendkeys are used well. However, "how" was merely part of the puzzle, the other part was finding "what" needed focus before the program would invoke the sendkey "~"(Enter). Due to an unknown number of sendkey "{TAB}"'s that would have taken to land the cursor on the desired field, I created a loop that would continuously iterate through all the text fields until the partial-string "focus" was present in the attribute titled classname of the desired text box. Once that occurs, the program would have a marker to let it know to invoke the sendkey "~."
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Declare Function SetForegroundWindow Lib "user32" (ByVal HWND As Long) As Long
Dim HWNDSrc As Long
text_again:
With DOCK_Tex_Box
.focus
.keydown
.innertext = Field_Text
.change
Sleep (500)
.focus
.blur
HWNDSrc = IE.HWND
For i = 1 To 100
DoEvents
SetForegroundWindow HWNDSrc
DoEvents
Application.SendKeys ("{TAB}"), True
DoEvents
Sleep 1000
If InStr(.classname, "prompt") > 0 Then 'If the for loop goes to quickly the field loses focus and then the text clears out.
DoEvents
Sleep 500
GoTo text_again:
End If
If InStr(.classname, "focus") > 0 Then 'Text field has focus
Sleep 1000
Exit For
End If
Next
End With
Application.SendKeys ("~"), True
DoEvents
Sleep 500
Related
I'm trying to automate the process of stock prices and then verify those prices with a screenshot from Yahoo Finance and pasting in onto a excel sheet.
I have completed the first task of auto pulling adjusted closing stock prices but I need the last step of verifying said prices by automating the process of going to yahoo finance and taking a screenshot of the price that day. The script I have so far succeeds with pulling the website up on internet explorer it even takes a screen shot of the window as well and pastes it but it does it incorrectly.
Issues:
1) Pastes about five screenshots when I only need one.
2) does not wait for the window to fully load before taking said screenshot.
3) I also want to just take a small part of the page.
Extra info: I am using two monitors
What I want:
What happens:
Option Explicit
Private Const SW_SHOWMAXIMIZED = 3
'Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal _
bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const VK_SNAPSHOT = &H2C
Sub Screenshot()
Dim IEapp As Object
Dim WebUrl As String
'Delcaring internet explorer as web application
Set IEapp = CreateObject("InternetExplorer.Application")
WebUrl = "https://finance.yahoo.com/quote/TWLO/history?p=TWLO"
With IEapp
.Silent = True 'No Pop-ups
.Visible = True 'Set InternetExplorer to Visible
.Navigate WebUrl 'Load web page
Do While .busy
DoEvents
keybd_event VK_SNAPSHOT, 1, 0, 0
ActiveSheet.Paste
Loop
End With
End Sub
Thank you in advance!
Here you are using snapshot inside do while loop it will perform action until its ready state is complete
While .Busy Or .ReadyState <> 4: DoEvents: Wend
keybd_event VK_SNAPSHOT, 1, 0, 0
ActiveSheet.Paste
you have mentioned need to get small part of page please explain further
I am trying to write a script that will auto save and close an excel file if there is computer (not just excel) inactivity after so long. A message box warning to the user would also be a nice feature that I would like to include. I found some code that seems like it would meet my needs perfectly (http://www.vbaexpress.com/forum/showthread.php?33711-Solved-Possible-for-excel-to-detect-inactivity-at-pc), but I can't seem to get it to work right. I have put this code in a module, but it gets hung up on the very first line (Private Declare Sub GetLastInputInfo Lib "USER32" (ByRef plii As LASTINPUTINFO)) saying "Compile Error: User defined type not defined." I have enabled Microsoft ActiveX Data Objects 6.1 Library, but still get the same result. I'm fairly new to VBA so I can't read someone else's code very well so sorry in advance if I'm missing something simple.
Private Declare Sub GetLastInputInfo Lib "USER32" (ByRef plii As LASTINPUTINFO)
Function IdleTime() As Single
Dim a As LASTINPUTINFO
a.cbSize = LenB(a)
GetLastInputInfo a
IdleTime = (GetTickCount - a.dwTime) / 1000
End Function
Public Sub CloseDownFile()
On Error Resume Next
If IdleTime > 30 Then
Application.StatusBar = "Inactive File Closed: " & ThisWorkbook.Name
ThisWorkbook.Close SaveChanges:=True
Else
CloseDownTime = Now + TimeValue("00:00:30") ' change as needed
Application.OnTime CloseDownTime, "CloseDownFile"
End If
End Sub
Sub Test_MsgBoxWait()
Dim rc As Long
rc = MsgBoxWait("UserName", "Is your computer user name " & _
Environ("username") & "?" & vbLf & _
"I will wait 5 seconds for your response.", 1, 2) '4+32
Select Case rc
Case 6
MsgBox "Congratulations, you are correct."
Case 7
MsgBox "I am sorry, that is incorrect." & vbLf & _
"Your computer username is " & Environ("username") & "."
Case Else
MsgBox "The return code was: " & rc
End Select
End Sub
'Function MsgBoxWait(strTitle As String, strText As String, _
nType As Integer, nSecondsToWait As Integer)
Function MsgBoxWait(strTitle As String, strText As String, _
nType As Long, nSecondsToWait As Integer)
Dim ws As Object, rc As Long
Set ws = CreateObject("WScript.Shell")
rc = ws.Popup(strText, nSecondsToWait, strTitle, nType)
Set ws = Nothing
MsgBoxWait = rc
End Function
'Arguments
'Object
'WshShell object.
'strText
'String value containing the text you want to appear in the pop-up message box.
'nSecondsToWait
'Numeric value indicating the maximum length of time (in seconds) you want the pop-up message box displayed.
'strTitle
'String value containing the text you want to appear as the title of the pop-up message box.
'nType
'Numeric value indicating the type of buttons and icons you want in the pop-up message box. These determine how the message box is used.
'IntButton //not used but returned as result of MsgBoxWait().
'Integer value indicating the number of the button the user clicked to dismiss the message box. This is the value returned by the Popup method.
'Remarks
'The Popup method displays a message box regardless of which host executable file is running (WScript.exe or CScript.exe). If
' nSecondsToWaitis equals zero (the default), the pop-up message box remains visible until closed by the user. If
' nSecondsToWaitis is greater than zero, the pop-up message box closes after nSecondsToWait seconds. If you do not supply
' the argument strTitle, the title of the pop-up message box defaults to "Windows Script Host." The meaning of nType is the
' same as in the Microsoft Win32® application programming interface MessageBox function. The following tables show the
' values and their meanings. You can combine values in these tables.
'
'Note To display text properly in RTL languages such as Hebrew or Arabic, add hex &h00100000 (decimal 1048576) to the nType parameter.
'Button Types
'
'Value Description
'0 Show OK button.
'1 Show OK and Cancel buttons.
'2 Show Abort, Retry, and Ignore buttons.
'3 Show Yes, No, and Cancel buttons.
'4 Show Yes and No buttons.
'5 Show Retry and Cancel buttons.
'
'Icon Types
'
'Value Description
'16 Show "Stop Mark" icon.
'32 Show "Question Mark" icon.
'48 Show "Exclamation Mark" icon.
'64 Show "Information Mark" icon.
'
'The previous two tables do not cover all values for nType. For a complete list, see the Microsoft Win32 documentation.
'
'The return value intButton denotes the number of the button that the user clicked. If the user does not click a button before nSecondsToWait seconds, intButton is set to -1.
'
'Value Description
'1 OK Button
'2 Cancel Button
'3 Abort Button
'4 Retry Button
'5 Ignore Button
'6 Yes Button
'7 No Button
'
' Note: intButton is not used here. The value for intButton is returned to from the Function.
You just need to declare LASTINPUTINFO type:
Private Type LASTINPUTINFO
cbSize As Long
dwTime As Long
End Type
Private Declare Sub GetLastInputInfo Lib "user32" (ByRef plii As LASTINPUTINFO)
Private Declare Function GetTickCount Lib "kernel32" () As Long
Function IdleTime() As Single
Dim a As LASTINPUTINFO
a.cbSize = LenB(a)
GetLastInputInfo a
IdleTime = (GetTickCount - a.dwTime) / 1000
End Function
I'm looping through a list of csv files from a website which I want to open and extract the data from. The reason I'm using VBA is because the specific files I need to open varies day per day, and those specific file references is available only in an excel view. The second reason is because I've already made a similar scraping application in vba, so I already had half the code.
The end user of the application doesn't need clean or fast code, just that it works, because checking these files manually now is a daily chore of 2 hours per day.
So far I'm already logged into the website where the secondairy files are stored (This website has no API so I'm scraping it) and I'm opening those files by letting the code click the buttons. The code then clicks the export button, which opens the dialog box to
open, save (dropdown), cancel
I just want to open and extract the data then close, so I'm using the code from VBA Internet Explorer Automation - How to Select "Open" When Downloading a File . It feels like my code is bugging on this part...
Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
Dim ie As InternetExplorer
Dim h As LongPtr
Function Download()
Dim o As IUIAutomation
Dim e As IUIAutomationElement
Set o = New CUIAutomation
h = ie.Hwnd
h = FindWindowEx(h, 0, "Frame Notification Bar", vbNullString)
If h = 0 Then Exit Function
Set e = o.ElementFromHandle(ByVal h)
Dim iCnd As IUIAutomationCondition
Set iCnd = o.CreatePropertyCondition(UIA_NamePropertyId, "Open")
Dim Button As IUIAutomationElement
Set Button = e.FindFirst(TreeScope_Subtree, iCnd)
Dim InvokePattern As IUIAutomationInvokePattern
Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke
End Function
before I invoke this function I check the amount of workbooks and make excel wait upto 20 seconds until another workbook is opened. I do that with this code
xnum1 = Application.Workbooks.Count
Download
t = Now
tStop = t + TimeValue("00:00:20") 'Adjust the TimeValue as needed "hh:mm:ss"
Do Until t = tStop Or Application.Workbooks.Count > xnuml
DoEvents
t = Now
Loop
However it seems that even with this wait time, excel doesn't want to open the file during every loop. If I run it step by step, it does open the file, when I let it run on it's own, it doesn't.
After I downloaded the file I export the data to the main file and then close it, using Application.DisplayAlerts to ensure no dialog box prevents the closing of the file. But because the code bugs in the download I've had instances where the file opened too late and then it didn't close, and because they all have the same filename I made an extra safegaurd in the main code:
xnum1 = Application.Workbooks.Count
For y = 1 To xnum1
If Left(Application.Workbooks(y).Name, 10) = "export.csv" Then
Application.Workbooks(y).Close
End If
Next
I can't seem to figure out why excel isn't opening the files properly and then closing them properly... Seems like the code just runs, goes into error mode because the file didn't open in the second run, although no error is given by the code itself even when I step through it.
My gut is telling me that this Download function is where the problem lies, but I can't point out how to fix it...
I've also tried with sendkeys %{O}, the shortkey to click open, but this also didn't open the file. When I manually click alt + O it does open the file...
Any suggestions?
I am trying to insert a pause into Excel VBA code. The following code generates a compile error: "Invalid Qualifier".
The function Pause() is trying to combine the Application.Wait method with DoEvents to get the best of both worlds; Excel will continue to process keystrokes and mouse clicks while not eating up CPU resources as it waits.
Background:
http://www.fmsinc.com/microsoftaccess/modules/examples/avoiddoevents.asp
http://www.cpearson.com/excel/WaitFunctions.aspx
http://vbadud.blogspot.com/2008/03/sleep-function-in-excel-vba.html
Public Declare Function GetTickCount Lib "kernel32" () As Long
' A DoEvents loop uses CPU power.
' Application.Wait suspends all keyboard and mouse actions.
' This function is a hybrid to combine the best of both worlds.
Public Sub Pause(Optional Timeout As Single = 5)
Dim EndTime
EndTime = GetEndTick(Timeout)
Do While GetTickCount() <= EndTime
DoEvents
Application.Wait DateAdd("s", 0.1, Now)
Loop
End Sub
' Timeout is in seconds.
Function GetEndTick(Timeout)
GetEndTick = GetTickCount() + Timeout * 1000
End Function
To answer my question for some reason the Application object is not being recognized. I used the following workaround successfully:
Worksheets.Application.Wait DateAdd("s", 0.1, Now)
However I have discovered that the Worksheets.Application.Wait takes up CPU resources which defeats the purpose. Instead here is a working Pause function:
#If VBA7 Then
' Insert PtrSafe for Excel 64 as per http://stackoverflow.com/a/5507370/2529619
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
#Else
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function GetTickCount Lib "kernel32" () As Long
#End If
' "Pause" code execution for "Timeout" seconds.
'
' Excel will continue to process the keyboard and mouse clicks at least once a second.
' This Pause function will not take up lots of CPU resources.
' 2015.02.04 -- Chaim Gewirtz
Public Sub Pause(Optional Timeout As Single = 5)
Dim EndTick
' There are 1000 'ticks' in a second.
EndTick = GetTickCount() + Timeout * 1000
Do While GetTickCount() <= EndTick
' Process keyboard and mouse events while waiting.
DoEvents
' If there is at least one more second to pause then...
If EndTick - GetTickCount() > 1000 Then
' "Wait" for one second without using CPU resources.
Sleep 1000
End If
Loop
End Sub
Visual Basic - all variations - use end of line as the end of statement marker. This contrasts with C# where semi-colon is used as a statement terminator, and with Delphi where semi-colon is used as a statement separator.
So, this line:
Application.Wait DateAdd("s", 0.1, Now)
is treated as a single statement, and it doesn't make sense. The best that the compiler can do is to think that you're wanting to use Application.Wait as a qualifier, but it doesn't understand it.
Change the code to this:
Application.Wait
DateAdd("s", 0.1, Now)
and it should at least execute. Though, whether it's good practice is another question.
I am writing a macro to download a csv file from my company's internal website.
For many reasons I can't use any xmlhttp objects. The macro will download the file. The problem is Internet Explorer 9 prompts the user with Open, Save, and Cancel buttons.
While in IE, Alt+Shift+S will save the download, but I can't get the Sendkeys "%+s" method from Excel VBA to work.
Here is the relevant code:
Function followLinkByText(thetext As String) As Boolean
'clicks the first link that has the specified text
Dim alink As Variant
'Loops through every anchor in HTML document until specified text is found
' then clicks the link
For Each alink In ie.document.Links
If alink.innerHTML = thetext Then
alink.Click
'waitForLoad
Application.Wait Now + TimeValue("00:00:01")
Application.SendKeys "%+s", True
followLinkByText = True
Exit Function
End If
Next
End Function
Like I mentioned in my comments, The Info Security bar makes it difficult to interact with the File Download Window.
An alternative is to use the webbrowser control and then passing the URL to it. But the main problem with this method is that you cannot have the webbrowser in the same Excel Instance. Once the File Download window pops up your entire VBA Macro will come to a standstill till the time you do not dispose it off.
Here is an alternative. Here is a small exe that I created in VB6 which will pop up the File Download window bypassing the IE Info Security Bar. And once the File Download window pops up, you can interact with it using the APIs as shown in my blog article.
Let's take an example to see on how we interact with this vb6 exe file.
Create a module in Excel and paste this code.
IMPORTANT NOTE: Since you didn't give me any URL, I am taking a Static URL. Please replace it with your link. Now depending upon the link that you specify, you might see the one of these two download windows. Based on the download window that you see you will have to find the window handles based on the pic shown below. More details on the blog link that I gave.
Download the file attached and save it in say C:\. If you save it in any other location then amend that in the Shell statement below.
Sub Sample()
Dim sUrl As String
sUrl = "http://spreadsheetpage.com/downloads/xl/king-james-bible.xlsm"
Shell "C:\FDL.exe " & sUrl, vbNormalFocus
End Sub
SNAPSHOT
FILE: The file can be downloaded here.
You may try this as it is worked for me on IE 11:
Copy file C:\Windows\System32\UIAutomationCore.dll file to users Documents i.e C:\Users\admin\Documents then add reference UIAutomationClient to your macro file.
Paste below code in your module:
Option Explicit
Dim ie As InternetExplorer
Dim h As LongPtr
Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
Sub Download()
Dim o As IUIAutomation
Dim e As IUIAutomationElement
Set o = New CUIAutomation
h = ie.Hwnd
h = FindWindowEx(h, 0, "Frame Notification Bar", vbNullString)
If h = 0 Then Exit Sub
Set e = o.ElementFromHandle(ByVal h)
Dim iCnd As IUIAutomationCondition
Set iCnd = o.CreatePropertyCondition(UIA_NamePropertyId, "Save")
Dim Button As IUIAutomationElement
Set Button = e.FindFirst(TreeScope_Subtree, iCnd)
Dim InvokePattern As IUIAutomationInvokePattern
Set InvokePattern = Button.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke
End Sub
Try at your end.
I think I came up with a simpler solution: when the download bar appears in IE9, just by-pass it by displaying the "real" Download Pop Up window. The shortcut is "CTRL+J". All you have to do next is click on "Save" or "Open". There might be pretty ways to do it, but I simply send a key sequence to move the focus on desired option and then press enter.
Here is the code:
' Wait for download bar to appear
Application.Wait (Now + TimeValue("0:00:04"))
' Sending CTRL+J to open download pop-up
SendKeys "^j"
' Wait for download popup to appear
Application.Wait (Now + TimeValue("0:00:02"))
' Sending keys sequence to click on "Save" button
SendKeys "{RIGHT}{RIGHT}{RIGHT}~"
Your Application.Sendkeys just needs a tweak. Below is the code I am using so it is tested on IE11. This is for Alt+S with no Shift which is the keyboard shortcut in IE11. Let me know if this doesn't work and you need help adding the Shift back in.
Application.SendKeys "%{S}", True