VBA: Downloading a file behind JavaScript link - javascript

How do you write VBA code to download a file sitting behind a JavaScript link? There are many resources on how to download a file from a specific link using VBA, however, none show how to download a file behind a JavaScript link.
In example, how do you download the file behind "Export to Spreadsheet" on this website:
https://www.vanguardinvestments.com.au/retail/ret/investments/product.html#/fundDetail/wholesale/portId=8101/assetCode=equity/?prices
Do we still declare and use urlmon?
'Declaration of API function for Office 2010+
Private Declare PtrSafe Function URLDownloadTOFile Lib "urlmon" Alias
"URLDownloadToFileA" ( _
ByVal pCaller As LongPtr, _
ByVal sZURL As String, _
ByVal szFileName As String, _
ByVal dwReserved As LongPtr, _
ByVal lpfnCB As LongPtr _
) As LongPtr
#Else
'Declaration of API function for pre Office 2010 versions
Private Declare Function URLDownloadTOFile Lib "urlmon" Alias
"URLDownloadToFileA" ( _
ByVal pCaller As Long, _
ByVal sZURL As String, _
ByVal szFileName As String, _
ByVal dwReserved As Long, _
ByVal lpfnCB As Long _
) As Long
#End If
Sub DownloadOneFile()
Dim FileURL As String
Dim DestinationFile As String
'How do you modify this to handle a javascript link?
FileURL = "https://www.vanguardinvestments.com.au/retail/ret/investments/product.html#/fundDetail/wholesale/portId=8101/assetCode=equity/?prices"
DestinationFile = "C:\VBA\prices.csv"
URLDownloadToFile 0, FileURL, DestinationFile, 0, 0
End Sub

This will fire the event. Credit to #Greedo for the principle of waiting for page to load by looping until a specified element is visible in the window. Sorry about the dreaded send keys.
Public Sub DownloadFile()
Dim objIE As InternetExplorer, currPage As HTMLDocument, url As String
url = "https://www.vanguardinvestments.com.au/retail/ret/investments/product.html#/fundDetail/wholesale/portId=8101/assetCode=equity/?prices"
Set objIE = New InternetExplorer
objIE.navigate url
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
Set currPage = objIE.document
objIE.Visible = True
Dim myDiv As HTMLDivElement: Set myDiv = currPage.getElementById("price-distribution")
Dim elemRect As IHTMLRect: Set elemRect = myDiv.getBoundingClientRect
Do Until elemRect.bottom > 0
currPage.parentWindow.scrollBy 0, 10000
Set elemRect = myDiv.getBoundingClientRect
Loop
objIE.document.getElementsByClassName("export_icon hideOnSml ng-binding")(0).FireEvent "onclick"
Application.SendKeys "%{S}"
End Sub
If necessary you might add something like the following before the send keys to ensure window is up but seems to work as is at present.
Dim objShell As Shell
Set objShell = New Shell
Application.Wait Now + TimeSerial(0, 0, 10) 'alter to give enough time for window
For Each objIE In objShell.Windows
If TypeName(objIE.document) = "HTMLDocument" Then
If InStr(objIE.document.title, "vanguard") > 0 Then
objIE.Visible = True
Exit For
End If
End If
Next objIE

Related

Get text from javascript alert box in vb.net with user32.dll

Hy guys!
Please help me out;
I'm trying to get the text from an javascript alert box with user32.dll API but i can't figure out the lpszClass name.
Please someone help me out and sorry if it's a dumb question.
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function FindWindowEx(hwndParent As IntPtr, hwndChildAfter As IntPtr, lpszClass As String, lpszWindow As String) As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="FindWindow", SetLastError:=True)> _
Private Shared Function FindWindow(lpClassName As String, lpWindowName As String) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(hWnd As IntPtr, Msg As UInt32, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As System.Text.StringBuilder, ByVal cch As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function GetWindowTextLength(ByVal hwnd As IntPtr) As Integer
End Function
Private Function Form1_Deactivate(sender As Object, e As EventArgs) Handles Me.Deactivate As String
Dim hwnd As IntPtr = FindWindow("#32770", "Mensagem da página da web")
'hwnd = FindWindowEx(hwnd, IntPtr.Zero, "<NEED TO KNOW WHAT TO PUT HERE", Nothing)
Dim length As Integer = GetWindowTextLength(hwnd)
Dim sb As New System.Text.StringBuilder("", length + 1)
GetWindowText(hwnd, sb, sb.Capacity)
return sb.ToString()
End Function
Fellow Windows API noob here, but here's how I did something similar:
VisualStudio comes with a tool called Spy++ (it's under the Tools menu) that lists every "window" on the system along with the relevant information. The information you are looking for is shown right after the window title:
In my case, I needed to find multiple children with the same class, so I ended up using the EnumChildWindows function instead of FindWindowEx. Here's my code in Java (using JNA):
User32 u32 = User32.INSTANCE;
WinDef.HWND window = u32.FindWindow("#32770", "Control Center Login");
List<WinDef.HWND> windows = new ArrayList<>();
u32.EnumChildWindows(window, (child, data) -> {
char[] name = new char[256];
u32.GetClassName(child, name, 256);
String nameStr = new String(name).trim();
if(nameStr.equals("Edit")) {
windows.add(child);
}
return true;
}, null);
I hope you have not been waiting for this answer for the past 2 years, but if someone else finds this question, here's a way to do it :)

Downloading File from IE using VBA

I am currently working on a VBA code that retrieves the top file from this website (http://infopost.bwpmlp.com/Posting/default.aspx?Mode=Display&Id=27&tspid=100000). I am able to click on the button using Javascript in my code, and I am able to click on open after the download is kicked off. However, I am having trouble saving the file. Because the workbook is being pulled from a website there really isn't a way to set it to the active workbook that I can think of. Currently when I do ActiveWorkbook.SaveAs the code is saving the blank workbook that I am testing the code out of. The file I downloaded seems to not open until the entire code is done running even after I try putting in breaks. Anyone have any ideas? My code is below. Thanks!
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 Texas_Gas()
Application.DisplayAlerts = True
Dim ie As Object
Dim IeHandle As Long, FileDownloadHandle As Long, OpenButtonHandle As Long, IePopupBarHandle As Long
Dim AutoMode As Boolean, FileDownloadClassicPopup As Boolean, DownloadComplete As Boolean
Dim Timeout As Date
Dim strSPICE As String, strLink As String
Dim PopupGap As Integer, i As Integer
Set ie = CreateObject("InternetExplorer.Application")
DownloadComplete = False
FileDownloadClassicPopup = False
FileDownloadHandle = 0
IePopupBarHandle = 0
With ie
.Visible = True
.navigate "http://infopost.bwpmlp.com/Posting/default.aspx? Mode=Display&Id=27&tspid=100000"
Do While .Busy: DoEvents: Loop
Do While .readyState <> 4: DoEvents: Loop
End With
ie.document.parentWindow.execScript "javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(""dgITMatrix:0:lnkBtnDownload"", """", true, """", """", false, true))"
Dim o As IUIAutomation
Dim e As IUIAutomationElement
Dim iCnd As IUIAutomationCondition
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)
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
ActiveWorkbook.SaveAs "I:\Cap_Rel\raw_scrapes\Texas_Gas_Transmission\parsed\Texas_Gas_Transmission_CapRel" & Format(Date - 1, "yyyymmdd") & "MACRO", FileFormat:=xlCSV
End Sub

On Click event not firing from code behind

In my code below , my ("OnClick") is not firing. Does anyone know why?
e.Row.Cells(1).Attributes("OnClick") = "window.location.href='MachineSweepLite.aspx?AreaID='" _
+ GridView1.DataKeys(e.Row.RowIndex).Values("ID").ToString()
Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
e.Row.Cells(1).Attributes("onmouseover") = "this.style.cursor='hand';this.style.textDecoration='underline';"
e.Row.Cells(1).Attributes("onmouseout") = "this.style.textDecoration='none';"
Dim Index As Integer = e.Row.RowIndex
e.Row.Cells(1).Attributes("OnClick") = "window.location.href='MachineSweepLite.aspx?AreaID='" + GridView1.DataKeys(e.Row.RowIndex).Values("ID").ToString
End If
End Sub
Edit above.
You have a error in your attribute, as you are adding the value of the ID after the closing single quote...
For example, if the ID was 12, you're sending this to the browser...
window.location.href='MachineSweepLite.aspx?AreaID='12
Note that the 12 is not part of the URL.
You should have the following instead...
e.Row.Cells(1).Attributes("onclick") =
string.Format("window.location.href='MachineSweepLite.aspx?AreaID={0}';",
GridView1.DataKeys(e.Row.RowIndex).Values("ID").ToString())
Also note, as of .NET 4.0 it is unnecessary to have the _ character when spanning over multiple lines.
e.Row.Attributes("onClick") = "CallFunction('" & Convert.ToString(GridView1.DataKeys(e.Row.RowIndex).Values("ID")) & "');"
JS code:
function CallFunction(val)
{
// do your login here
}

Xml.XmlDataDocument() obsolete message

I am running a simple program in which you enter a user number into a text box, click the submit button, and then the program is supposed to go to a database look up the number you entered and display that rows information. Simple enough.
The problem is I keep getting the error that Xml.XmlDataDocument() is obsolete. I've googled this issue, which led me to here, but the replacements suggested do not work within my program.
Also, I have not studied VB and this is for an XML class.
I've double checked my code for any errors and do not see anything. But, I could be missing the forest for the trees. Would like to have another set of eyes take a look at my code to see if I've missed something, or to offer up a replacement for the Xml.XmlDataDocument() line.
Thank you in advance for any help you can offer.
Here is the code I am using:
Javascript for the onClick event
<script language="javascript" type="text/javascript">
function btnSearch_onclick() {
var docSubmit = new ActiveXObject("MSXML2.DOMDocument");
docSubmit.loadXML("<?xml version='1.0'?><request><customerID>" + txtCustID.value + "</customerID></request>")
var objSocket = new ActiveXObject("MSXML2.XMLHTTP");
objSocket.open("POST", "Lookup.aspx", false)
objSocket.send(docSubmit)
alert(objSocket.responseXML.xml)
lblFirstName.innerHTML = objSocket.responseXML.selectSingleNode("//FirstName").firstChild.nodeValue
lblLastName.innerHTML = objSocket.responseXML.selectSingleNode("//LastName").firstChild.nodeValue
lblAddress.innerHTML = objSocket.responseXML.selectSingleNode("//Address").firstChild.nodeValue
lblCity.innerHTML = objSocket.responseXML.selectSingleNode("//City").firstChild.nodeValue
lblState.innerHTML = objSocket.responseXML.selectSingleNode("//State").firstChild.nodeValue
lblZip.innerHTML = objSocket.responseXML.selectSingleNode("//Zip").firstChild.nodeValue
}
</script>
And here is the VB code:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim docReceived As New System.Xml.XmlDataDocument()
docReceived.Load(Request.InputStream)
Dim CustomerID = docReceived.SelectSingleNode("//customerID").FirstChild.Value
Dim myConnection As New System.Data.OleDb.OleDbConnection
Dim myConnectionString As String
myConnectionString = "Provider = Microsoft.Jet.OLEDB.4.0;Data Source = " & _
Server.MapPath("customer.mbd")
myConnection.ConnectionString = myConnectionString
Dim strSQL As String
strSQL = "Select * From CustomerInfo where CustomerID = " & CustomerID
Dim myAdapter As New System.Data.OleDb.OleDbDataAdapter(strSQL, myConnection)
Dim myDataSet As New System.Data.DataSet("CustomerInfo")
Dim intRecords As Integer
intRecords = myAdapter.Fill(myDataSet, "Customer")
Response.ContentType = "text/xml"
If intRecords > 0 Then
myDataSet.WriteXml(Response.OutputStream)
Else
Response.Write("<?xml version='1.0'?><customer><FirstName>Not Found</FirstName><LastName>***</LastName><Address>***</Address><City>***</City><State>***</State><Zip>***</Zip><Phone>***</Phone><Email>***</Email></customer>")
End If
myDataSet.WriteXml(Response.OutputStream)
myConnection.Close()
myAdapter.Dispose()
myConnection.Dispose()
End Sub
XmlDataSet is obsolete. As you can see on the msdn it may even be removed in the next version of the .NET Framework ([ObsoleteAttribute("XmlDataDocument class will be removed in a future release.")]
). In your case I don't think you need it at all. The easies fix seems to be to use just XmlDocument. I believe you won't have to change anything else in your code but this line
Dim docReceived As New System.Xml.XmlDataDocument()
to:
Dim docReceived As New System.Xml.XmlDocument()

Perform code-behind Function/Sub from javascript (VB.NET)

In my project, there are 3 user controls; BasicContact, BasicDetail and ActionTime. They are in EditOrder.aspx page.
There is "ReportDate" property (Date type) in BasicContact, RadioButtonList "rdl_Priority" (with integer value from "prio_id" field) in BasicDetail and "CheckDate" property in ActionTime. The ReportDate value is derived from txt_ReportDate and txt_ReportTime and CheckDate value is derived from txt_CheckDate and txt_CheckTime.
My objective is to calculate Checkdate after I add date into ReportDate and click on radiobuttonlist. Checkdate will be calculated from ReportDate (date) + SLAHour (hour, get from "GetSLAHour" method where input is prio_id) and then set text in txt_CheckDate and txt_CheckTime.
Right now, I was be able to complete this task using Postback. I create custom event and raise it in RadioButtonList selectedIndexchanged method. After that, event is handled in code behind of EditOrder page. Here are my code.
BasicDetail - RadioButtonList
<asp:RadioButtonList ID="rdl_Priority" runat="server" RepeatDirection="Horizontal" AutoPostBack="true" />
BasicDetail - codeBehind
Public Event priorityClicked As System.EventHandler
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
If Not IsPostBack Then SetupList()
End Sub
Private Sub SetupList()
Dim ctx As New StsDataContext
With rdl_Priority
Dim Result = (From r In ctx.Priorities Order By r.display_order)
If Result.Count > 0 Then
.DataTextField = "prio_name"
.DataValueField = "prio_id"
.DataSource = Result
.DataBind()
Else
lbl_Priority.Visible = False
rdl_Priority.Visible = False
End If
End With
End Sub
Protected Sub rdl_Priority_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles rdl_Priority.SelectedIndexChanged
RaiseEvent priorityClicked(sender, e)
End Sub
EditOrder - codeBehind
Private Sub BasicDetail_priorityClicked(ByVal sender As Object, ByVal e As System.EventArgs) Handles BasicDetail.priorityClicked
Dim reportDate As Date? = BasicContact.ReportDate
Dim SLAHour As Integer? = GetSLAHour(BasicDetail.PriorityId)
If reportDate.HasValue AndAlso SLAHour.HasValue Then
ActionTime.CheckDate = CDate(reportDate).AddHours(CDbl(SLAHour))
End If
End Sub
However, I don't want the page to be refreshed (no postback). I don't know how to call the function or sub from javascript. I have tried PageMethod but it cause error in runtime saying that the method is not supported. Anyway, if there is a better way than calling code-behind from javascript, please let me know.
Thanks in advance
Ok Sorry, here is my solution
Since for now I used PageMethod to solve this problem, I don't need raise event function from code behind anymore so I deleted all the codes I have posted to ask my own question.
First, I added javascript function to each item in RadioButtonList in BasicDetail code behind
BasicDetail - Code behind
Private Sub rdl_Priority_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles rdl_Priority.DataBound
For Each li As ListItem In rdl_Priority.Items
Dim slaHour As Integer? = GetSLAHour(li.Value)
li.Attributes.Add("onclick", "return CalCheckDate(" & If(slaHour.HasValue, CStr(slaHour), "null") & ");")
Next
End Sub
This "CalCheckDate" function added to each button is implemented in EditOrder page (user control's parent page)
EditOrder.aspx
<script type="text/javascript">
function CalCheckDate(hour) {
var hid_ServId = document.getElementById('<%=hid_ServId.ClientID%>');
var txt_reportDate = document.getElementById('<%=BasicContact.ReportDateTextName%>');
var txt_reportTime = document.getElementById('<%=BasicContact.ReportTimeTextName%>');
PageMethods.GetCheckDateTime(hid_ServId.value, txt_reportDate.value, txt_reportTime.value, hour, OnGetCheckDateComplete, OnGetCheckDateError);
}
function OnGetCheckDateComplete(result) {
var txt_checkDate = document.getElementById('<%=ActionTime.CheckDateTextName%>');
var txt_checkTime = document.getElementById('<%=ActionTime.CheckTimeTextName%>');
var chkDateTime = result.split(" ");
txt_checkDate.value = chkDateTime[0];
txt_checkTime.value = chkDateTime[1];
}
function OnGetCheckDateError(e)
{
alert(e._message);
}
</script>
Child control within user control, such as txt_ReportDate in BasicContact, can be derived by creating property in BasicContact as follows:
BasicContact - Code Behind
Public ReadOnly Property ReportDateTextName() As String
Get
Return txt_ReportDate.ClientID
End Get
End Property
This property is used in "CalCheckDate" function to get its value and pass it to PageMethod function. Other controls can be derived in the same way.
Last step is coding PageMethods function, "GetCheckDateTime", in EditOrder code behind
EditOrder - Code Behind
<System.Web.Services.WebMethod()> _
Public Shared Function GetCheckDateTime(ByVal servId As String, ByVal ReportDate As String, ByVal ReportTime As String, ByVal hour As String) As String
Dim checkDate As String, checkTime As String
'************************************************************************
'Calculate check and time date from input ReportDate, ReportTime and hour
'************************************************************************
Return checkDate & " " & checkTime
End Function
The result is returned to "OnGetCheckDateComplete" function in javascript (or "OnGetCheckDateError" if there is an exception). Here, I split the String and use its value to set text in textbox.

Categories

Resources