Auto login windows security window - vb.net

I try to fill up automatically the name and the password for a windows security window, basically the page is opening after a couple of seconds with the window but the fields for user and password are empty. This is the code I have so far, any help is appreciate. Thank you.
This is where I am now:
Dim oProcess As New Process()
Dim psi As New ProcessStartInfo
psi.FileName = "arp"
psi.Arguments = "-a"
psi.UseShellExecute = False
psi.RedirectStandardOutput = True
psi.WindowStyle = ProcessWindowStyle.Hidden
psi.CreateNoWindow = True
Dim p As Process = Process.Start(psi)
Dim s As String = p.StandardOutput.ReadToEnd()
s = s.Split(" ".ToCharArray)(1)
WebBrowser1.Navigate("http://user:pass#" + (s) + ":34655/")
It works but I get from cmd only the first ip(192.2.2.2), if I don't use the arp command I don't see the ip of the device.
Basically I need to get the ip from this cmd output from third row:
"interface: 192.2.2.2 ----0xb//////
internet address- physical address---type///////
134.45.455.455.4(this ip) mac address dynamic//////
etc etc"/////
Thank you

You need to be more specific.
If this site you are connecting to uses Basic authentication, you are presented with a System InputBox with standard fields and a button control.
If it uses WebForm LogIn authentication, you are presented with a Web Form
with semi-standard Form Elements to fill in.
In the former case, it is quite simple to perform an authentication throu WebRequest/HttpWebResponse or, if you really need to use a WebBrowser control,
to setup its .Navigate property to achieve the same result.
If your case is the latter, since you are using a WebBrowser control to navigate to an URI, you don't really need any PInvoke at all.
Mind that WebBrowser.Document contains the HtmlDocument that describes the HTML page.
It also lets you access all the object that the page defines.
So, if you know in advance which Site you are connecting to (ie, you need to perform this operation
for this one Site only), you can use the .DocumentCompleted Event of your WebBrowser and set
your parameters directly from there:
Consider:
["UserName"], ["Password"], ["LoginButton"]
as the IDs of the Html Elements that represent an <input> Textbox, an <input> Password,
and a <input> Button that forwards the form parameters in a .Click Event .
So, read from the Html Page the IDs of the elements in the form and just insert your values:
In WebBrowser1_DocumentCompleted:
WebBrowser1.Document.All(["UserName"]).SetAttribute("Value", "UserID")
WebBrowser1.Document.GetElementById(["Password"]).SetAttribute("Value", "Password")
WebBrowser1.Document.All(["LoginButton"]).InvokeMember("Click")
I'm using .Document.All and .Document.GetElementById to show that there are two different
methods to accomplish the same task.
If you don't know in advance what the content of the Html Page is, you need to perform a simple parsing of the page (using the HtmlDocument Object), retrieve the elements of the form and then repeat the
previous task.
As I said, you need to be more specific.
If you have questions, comment and I will edit my answer.

Related

Unable to let my script keep clicking on Load more button using IE

I've created a script in vba using IE to keep clicking on the Load more hits button located at the bottom of a webpage until there is no such button is left.
Here is how my script can populate that button: In the site's landing page there is a dropdown named Type. The script can click on that Type to unfold the dropdown then it clicks on some corporate bond checkbox among the options. Finally, it clicks on the apply button to populate the data. However, that load more hits button can be visible at the bottom now.
My script can follow almost all the steps exactly what I described above. The only thing I am struggling to solve is that the script seems to get stuck after clicking on that button 3/4 times.
How can I rectify my script to keep clicking on that Load more hits button until there is no such button is left?
Website link
I've tried so far:
Sub ExhaustLoadMore()
Dim IE As New InternetExplorer, I As Long
Dim Html As HTMLDocument, post As Object, elem As Object
Dim CheckBox As Object, btnSelect As Object
With IE
.Visible = True
.navigate "https://www.boerse-stuttgart.de/en/tools/product-search/bonds"
While .Busy Or .readyState < 4: DoEvents: Wend
Set Html = .document
Do: Loop Until Html.querySelectorAll(".bsg-loader-ring__item").Length = 0
Html.querySelector("#bsg-filters-btn-bgs-filter-3").Click
Do: Set CheckBox = Html.querySelector("#bsg-checkbox-3053"): DoEvents: Loop While CheckBox Is Nothing
CheckBox.Click
Set btnSelect = Html.querySelector("#bsg-filters-menu-bgs-filter-3 .bsg-btn__label")
Do: Loop While btnSelect.innerText = "Close"
btnSelect.Click
Do: Loop Until Html.querySelectorAll(".bsg-loader-ring__item").Length = 0
Do: Set elem = Html.querySelector(".bsg-table__tr td"): DoEvents: Loop While elem Is Nothing
Do
Set post = Html.querySelector(".bsg-searchlist__load-more button.bsg-btn--juna")
If Not post Is Nothing Then
post.ScrollIntoView
post.Click
Application.Wait Now + TimeValue("00:00:05")
Else: Exit Do
End If
Loop
End With
End Sub
I've tried with selenium but that seems to be way slower. However, it keeps clicking on the load more button after a long wait in between even when no hardcoded wait within it. In case of selenium: I wish to have any solution which might help reduce it's execution time.
Sub ExhaustLoadMore()
Const Url$ = "https://www.boerse-stuttgart.de/en/tools/product-search/bonds"
Dim driver As New ChromeDriver, elem As Object, post As Object
With driver
.get Url
Do: Loop Until .FindElementsByCss(".bsg-loader-ring__item").count = 0
.FindElementByCss("#bsg-filters-btn-bgs-filter-3", timeOut:=10000).Click
.FindElementByXPath("//label[contains(.,'Corporate Bond')]", timeOut:=10000).Click
.FindElementByXPath("//*[#id='bsg-filters-menu-bgs-filter-3']//button", timeOut:=10000).Click
Do: Loop Until .FindElementsByCss(".bsg-loader-ring__item").count = 0
Set elem = .FindElementByCss(".bsg-table__tr td", timeOut:=10000)
Do
Set post = .FindElementByCss(".bsg-searchlist__load-more button.bsg-btn--juna", timeOut:=10000)
If Not post Is Nothing Then
post.ScrollIntoView
.ExecuteScript "arguments[0].click();", post
Do: Loop Until .FindElementsByCss("p.bsg-searchlist__info--load-more").count = 0
Else: Exit Do
End If
Loop
Stop
End With
End Sub
I have studied a bit your website, and since I could not say all of this into a single comment I have decided to post an answer (even though it doesn't provide with a concrete solution, but just with an "answer" and maybe some tips).
The answer to your question
How can I rectify my script to keep clicking on that Load more hits button until there is no such button is left?
Unfortunately, it's just not your fault. The website you are targeting is working through WebSocket communication between the web client (your browser) and the web server providing with the prices you are trying to scrape. You can see it as follows:
Imagine it like this:
When you first load your webpage, the web socket is initialized and the first request is sent (Web client: "Hey server, give me the first X results", Web server: "Sure, here you go").
Every time you click on the "Load more results" button, the Web client (important: re-using the same WS connection) keeps on asking for X new results to the web server.
So, the communication keeps on going on for some time. At some point, out of your control, it happens that the web socket just dies. It's enough to look at the JavaScript console while clicking on the "Load more results" button: you will see the request going through until at some point you don't just see a NullPointerException raised:
If you click on the last line of the stack before the exception, you will see that it's because of the web socket:
The error speaks clearly: cannot read .send() on null, meaning that _ws (the web socket) is gone.
Starting from now, you can forget about your website. When you click on the button "Load more results", the web client will ask the web socket to deliver the new request to the web server, but the web socket is gone so goodbye communications between the two, and so (unfortunately) goodbye the rest of your data.
You can verify this by just going a bit upper in the stack:
As you can see above, we have:
A message logged in the console saying "performSearch params ...") just before posting the new data request
The post of the new data request
A message logged in the console saying "performed search with result ...") just after posting the new data request
While the web socket is still alive, everytime you click on "Load more results" you will see these two messages in the console (with other messages in between printed over the rest of their code):
However, after the first crash of the web socket, no matter how many times you try to click on the button you will only get the first message (web client sends the request) but never will get the second message (request gets lost in the void):
Please note this corresponds to your behavior observed in VBA:
the script seems to get stuck after clicking on that button 3/4 times.
It doesn't get stuck, actually your script keeps on executing correctly. It's the website that times out.
I have tried to figure out why the web socket crashes, but no luck. It just seems a timeout (I've had this a lot more while debugging their JavaScript, so my breakpoints were causing the timeout) but I can't make you sure it's the only cause. Since you're not controlling the process between the web client and the web server, all you can do is to hope that it doesn't timeout.
Also, I believe using Selenium automatically sets some longer timeouts (because of the long execution time) and this somehow allows you to keep the web socket more tolerant with respect to the timeouts.
The only way I found to restore the connection after a crash of the web socket is completely reload the web page and restart the process from scratch.
My suggestions
I think you might go with building an XHR request and sending through JavaScript, because their API (through which the web client/web socket deliver the request to the web server) is pretty exposed in their front-end code.
If you open their file FinderAPI.js, you will see they've left the endpoints and API configurations harcoded:
var FinderAPI = {
store: null,
state: null,
finderEndpoint: '/api/v1/bsg/etp/finder/list',
bidAskEndpoint: '/api/v1/prices/bidAsk/get',
instrumentNameEndpoint: '/api/products/ProductTypeMapping/InstrumentNames',
nameMappingEndpoint: '/api/v1/bsg/general/namemapping/list',
apiConfig: false,
initialize: function initialize(store, finderEndpoint) {
var apiConfig = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
this.store = store;
this.state = store.getState();
this.apiConfig = apiConfig;
this.finderEndpoint = finderEndpoint;
},
This means you know the URL to which you should send your POST request.
A request also requires a Bearer Token to be validated by the server. Lucky you, they have also forgot to protect their tokens providing (GORSH) a GET end point to get the token:
End-point: https://www.boerse-stuttgart.de/api/products
Response:
{"AuthenticationToken":"JgACxn2DfHceHL33uJhNj34qSnlTZu4+hAUACGc49UcjUhmLutN6sqcktr/T634vaPVcNzJ8sHBvKvWz","Host":"frontgate.mdgms.com"}
You'll just have to play around with the website a little bit to figure out what is the body of your POST request, then create a new XmlHttpRequest and send those values inside it to retrieve the prices directly in your VBA without opening the webpage and robotic-scraping.
I suggest you start with a breakpoint on the file FinderAPI.js, line 66 (the line of code is this.post(this.finderEndpoint, params), params should lead you to the body of the request - I remember you can print the object as string with JSON.stringify(params)).
Also, please note that they use a pagination of 50 results each time, even though their API supports up to 500 of them. In other words, if you get to sweep the value 500 (instead of 50) into their pagination property sent to the API for the request:
... then you will get 500 results per time instead of 50, so reducing by 10 the time your code will spend scraping the webpage in case you decide not to go deeper into the XHR solution.
Could you try to change
Do
Set post = Html.querySelector(".bsg-searchlist__load-more button.bsg-btn--juna")
If Not post Is Nothing Then
post.ScrollIntoView
post.Click
Application.Wait Now + TimeValue("00:00:05")
Else: Exit Do
End If
Loop
to:
Set post = Html.querySelector(".bsg-searchlist__load-more button.bsg-btn--juna")
If Not post Is Nothing Then
post.ScrollIntoView
While Not post Is Nothing
Debug.Print "Clicking"
post.Click
Application.Wait Now + TimeValue("00:00:05")
Wend
Debug.Print "Exited Click"
End If
(untested)

VB.NET Screenshot Application Window and Send

I have built an application that obtains details from a web service and displays them in a GUI for the agent to see.
I've had instances of problems occuring, sometimes an exception due to a change in how the data is received due to the provider and unfortunately no user lets me know this occurs, they just click through and pay no attention to the error.
I've built a custom form to capture the error and then email it to me with some details like username etc. etc.
I would ideally LIKE to capture the screen of the application as well, much like an ALT+PRINTSCRN so I can see what the application looks like at the time of error as well.
How possible is this?
Assuming WinForm, I've done this before:
Public Sub SaveAsImage(frm As Form)
'Dim fileName As String = "sth.png"
'define fileName
Dim format As ImageFormat = ImageFormat.Png
Dim image = New Bitmap(frm.Width, frm.Height)
Using g As Graphics = Graphics.FromImage(image)
g.CopyFromScreen(frm.Location, New Point(0, 0), frm.Size)
End Using
image.Save(fileName, format)
End Sub
When being called, it will capture the current screen in the area defined by frm and save to a file.

Using vbscript to log on to internet provider webpage (data in html table)

I'm sure that this is a repeat post, but I've been unable to find exactly what I need. I currently live on a college campus where I need to enter a username and password to use the internet. I'm trying to automate the process (using .bat files) so I can run a server application on startup without ever pressing a key. Unfortunately, I keep getting an error at line 9 char 9: "Object does not support this property or method: 'getElementByID'" I tried replacing .getElementByID with .getElementByName, but it didn't make a difference.
Call Main
Function Main
Set IE = WScript.CreateObject("InternetExplorer.Application", "IE_")
IE.Visible = True
IE.Navigate "https://caserver.jbu.edu/auth/perfigo_weblogin.jsp"
Wait IE
With IE.Document
.getElementByID("username").value = "name"
.getElementByID("password").value = "password"
.getElementByID("tx_voputilities_pi1[sign_in]")(0).Submit
End With
End Function
Sub Wait(IE)
Do
WScript.Sleep 500
Loop While IE.ReadyState < 4 And IE.Busy
End Sub
I think that the problem has to do with the webpage. The "username" and "password" fields are elements inside a table. I couldn't find an example webpage with similar properties, so I'm at a loss. I'm not sure if the authentication page can be viewed from off-campus, so I attached a picture with some of the HTML (or at least the elements list)
My research so far:
VBScript to Launch a website login
IE 9 error getElementbyId: Object required
VBScript get contents of html table text input field
VBS website login script - "Object required" error
Like I said, I'm using batch files to start a computer. I wanted this to be a quick (and, yes, crude) solution, but there may be a better way to automate webpage logon. If there is a [quick] better way, feel free to point me in the correct direction.
Your problem may be that they don't use id, but name instead.
There is no function getElementByName, but there is getElementsByname
You could try
.getElementsByName("username")[0].value = "name"
.getElementsByName("password")[0].value = "password"
(I should probably note that this assumes the first element with these names is the one you are looking for - names do not have to be unique hence the array returned and the lack of a getElementByName function, whereas id is unique)
As a further note, your problem is not that the fields are in a table - getting an element from the DOM with either getElementById or getElementsByName do not take positioning into account, only that the element exists somewhere within the parent you are running the function on.

opening up web browser from winform

Done quite a bit of looking but not finding what i need. From a win form i'd like to open up a web browser passing in a url. But i need to provide authentication while doing this. I tried just using a system.diagnostics.process.start("http://userid:pw#site") but that does not work. Was hoping someone could lend a hand.
thanks
shannon
Using the tip.. here is what i have...
Dim m As New System.Security.SecureString
Dim pw As String = "mypassword"
For Each c As Char In pw
m.AppendChar(c)
Next
Dim pis As ProcessStartInfo = New ProcessStartInfo("http://test/pagegoingafter.aspx")
With pis
.UserName = "userid"
.Password = m
.UseShellExecute = False
End With
Process.Start(pis)
I'm getting a logon failure: unknown user name or password.
it's seems strange to me.. but if i do in firefox http://userid:mypassword#test/pagegoingafter.aspx i can get to my page. If i do the same thing in IE 8... no joy.
so is there anything else that can be done to get IE to work.. cause i'm thinking that would allow the above code to work as well.
You can provide credentials to the process.
See this overload to Process.Start - it takes a username, password and domain.
There are other alternatives - see this blog post.

how to open a vb.application from another vb.application with parameters

i have 2 vb applications. this is the code for the first one which when a button is clicked it will check if the other application is already open. If not, it'll open that application -
Dim sComputer As String
sComputer = Environ("COMPUTERNAME")
Dim LocalByName As Process() = Process.GetProcessesByName("ticket.prices", sComputer)
If LocalByName.Length = 0 Then
System.Diagnostics.Process.Start("http://ticket.prices.application")
End If
this runs fine. but what i need is that the customerid on the application 1 that is calling application 2, should be transfered while opening app 2.
e.g -
Customer 10001 screen is open on app 1. When i click open app 2, the above code runs and opens app 2. how do i make app 2 open to customer 10001 screen. Is there any way to pass parameters while opening app 2 in System.Diagnostics.Process.Start ?
Use the version of 'Process.Start' that takes 2 strings, the second being the commandline parameters. See here for details.
You want the ProcessStartInfo class, or use the Start method taking to strings. ProcessStartInfo gives you a lot of options about how to start your program, which often comes in handy. Its good to get familiar with it.
Dim info as New ProcessStartInfo()
info.Arguments = "10001"
info.FileName = "exename"
Dim LocalByName as New Process()
LocalByName.StartInfo = info
LocalByName.Start()
Getting the arguments in the new program is accomplished via Environment.GetCommandLineArgs()
For Each arg As String In Environment.GetCommandLineArgs()
Console.WriteLine(arg)
Next arg
It looks like what you ultimately want to accomplish is getting the currently selected row from App 1 and passing that to the second program, though. Is this correct? That opens a whole new ball of wax involving interprocess communication.
EDIT: The simplest way to get the selected edit would be to write the id out to a text file. You have to be careful when doing this because if you just write System.IO.File.WriteAllText("selectedrow.txt", "123"), you'll write to the app's startup path directory. You'll want to get the exe's current path as below
Dim u as New Uri(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)
dim exepath as String = System.IO.Path.GetDirectoryName(u.LocalPath)
dim fullPath as String = System.IO.Path.Combine(exepath, "selectedrow.txt")
System.IO.File.WriteAllText(fullpath, "123")
This will overwrite the text in the file every time you change rows. You want to wrap this in a try/catch block so as not to crash the program. Make sure you log the errors; don't just swallow them. To read the data, you just do
dim id as string = System.IO.File.ReadAllText(PathToFileYoureWritingToInTheOtherProgram)
in the other program.
This isn't necessarily the best way to go about things, but its the simplest way I know of off the top of my head.
You might could look at MessageQueues if you a better solution, but as long as you're not changing selected rows every 100ms, writing the file should work fine.