• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 965
  • Last Modified:

Same question How to automate user input on a web page

I'm back again with the same question except I have got a bit further. I am automating a daily task for the company I work for where they sign in to a web page (User name and Password), then on the next page the put in Client ID and password then they go to another page (Everything up to here works perfectly) On this page is where I am stuck, I couldn't find how to type to the textbox the path to the file I wanted to upload. Thanks to the LearnedOne I discovered DOM Inspector and found the path to the Textbox but I still cannot automate typing in to it. (I should say there is a Browse button beside it and the 2 seemed to go together as pair.
When I went through the webpage with DOM inspector and click on the textbox with Find control it highlights both the Textbox and Browse button.
If I try SetAttribute("value","c:\File.doc"). It does nothing
but If I use Invokemember("click") it opens the file Browser but I don't know how to automate that.
Can anybody put me in he right direction, I have been working on this for a few weeks now and this is finally near the end (If I can get this going)
WB.Navigate("https://WEbURL", False)
        'While WB.ReadyState <> WebBrowserReadyState.Complete
        '    Application.DoEvents() 'Process all other windows messages in the queue
        'End While
        Wait_IIS()
 
        Dim User As HtmlElement
        Dim pass As HtmlElement
        Dim Xid As HtmlElement
        Dim submit As HtmlElement
        User = WB.Document.Window.Frames(1).Document.GetElementById("logonForm")
        User.Document.GetElementsByTagName("Table")
        User = User.Document.GetElementsByTagName("Table")(0)
        User.Document.GetElementById("oid")
        User = User.Document.GetElementById("sid")
        pass = User.Document.GetElementById("pass")
        Xid = User.Document.GetElementById("id")
        submit = User.Document.GetElementById("submit")
 
        User.SetAttribute("value", My.MySettings.Default.SCR1_LOGON.Split("#")(0))
        pass.SetAttribute("value", My.MySettings.Default.SCR1_LOGON.Split("#")(1))
        id.SetAttribute("value", My.MySettings.Default.SCR1_LOGON.Split("#")(2))
        submit.InvokeMember("click")
        blnuser = True
The part above works perfectly but when I get to the next screen which is where you upload the file
 
 
 Dim hDoc As HtmlElement
            Dim input As HtmlElement
 
            hDoc = WB.Document.Window.Document.GetElementById("AgencyLinkHm")
            hDoc = hDoc.Document.GetElementsByTagName("table")(0)            
            input = hDoc.Document.GetElementById("UpFileName")
            input.SetAttribute("value", "File.doc")
This doesn't work but if I change input.SetAttribute("value", "File.doc") with input.InvokeMember("click") it opens the file browser

Open in new window

0
Niall292
Asked:
Niall292
  • 11
  • 9
1 Solution
 
_Katka_Commented:
Hi, I've solved similar question once.. I was sending the input feedback directly to the browser. Unfortunately, I'm unable to find the code (as it was long time ago). But basically, what you need is to find a window of the process, and the send the message to it (PostMessage) with either keyboard strokes or mouse clicks. The conversion from string -> keyboard strokes is pretty intuitive. As you're sending the automated input directly to that particular window, there can be no mistake when someone is interacting with other windows, so it can run even in background. I'll try to find that sample somewhere. Gimme some feedback if you know what to do, or if you do want some example. :)

regards,
Kate
0
 
Niall292Author Commented:
I don't like using Keystrokes as Ithink it is too flakey and doesn't always type what you want. I would like to send the input directly to the browser just lke the way I put the user name and Password etc at the start.
but please givee me an example of what you did so I can look at it.

Thanks
0
 
_Katka_Commented:
Ok, I'll try to find it. I just noticed you're calling:

input.InvokeMember("click")

Isn't there some other button (like submit) there ?

submit.InvokeMember("click")

It seems to me you're triggering the "..." (ellipse) button instead. If there's no submit button I guess the only way to simulate the enter key is via posting a message (althought I agree it's a bit dirty).

regard,
Kate
0
Windows Server 2016: All you need to know

Learn about Hyper-V features that increase functionality and usability of Microsoft Windows Server 2016. Also, throughout this eBook, you’ll find some basic PowerShell examples that will help you leverage the scripts in your environments!

 
_Katka_Commented:
I maybe found out safer way to invoke a keypress:

WB.Focus();
SendKeys.Send(Environment.NewLine);

Try this and tell me if it works and/or the results are satisfying. Otherwise I'll try the messy (unsafe) way.

regards,
Kate
0
 
Niall292Author Commented:
There is a submit button as well but I am trying to get the text into the textbox before pressing it usually I would just use something like id.SetAttribute("value", "whatever")
I can't understand why the SetAttribute is not working on this textbox when it already worked on 5 otther textboxes on the same site

but I have never been happy with Keypress especially on web apps sometimes it would send multiple keystrokes and sometimes it would leave stroke out.
0
 
_Katka_Commented:
Usually it is caused by input control being readonly (the user is prohibited to write to it). This will also disable the possibility of sending keystrokes, so we can rule that out. I'd guess edit is filled by some javascript function. As it seems you will probably need to run that function and proceed with that upload dialog.

Do you have some source code of that page available ?

regards,
Kate
0
 
Niall292Author Commented:
Katka,
Thanks for the help up to now and I will get you the Souce code tomorrow or late today. (Its 2am here)
I should tell you I can type in to the text box but can not automate it
0
 
Niall292Author Commented:
This is a copy of the source
Source.txt
0
 
_Katka_Commented:
Hi again, I've reviewed the code and the browse input is indeed readonly (at least in IE, Firefox, well not under Opera anyway). As you cannot change the type of the FILE input; that would be a security issue; I'd consider my previous solution or I'd consider the direct comunication with server.

I.option

A modified "original" solution (just to have something):

1) call the "click" event; that will open a dialog
2) find the dialog by the name, if there're multiple I'd check for a child process (yeah I know)
3) I'd try to simulate a keyboard input + enter key (NewLine)
4) call the "submit"

II. option

Direct communication would require to rewrite your logic so you're communicating with the server directly via HttpRequest and HttpResponse. Which has many advantages but requires a reversed proxy (to eavesdrop on the server's communication). Basically you'll listen what is send to a server when user browses file and submits the data and then you'll make program that will send the data automatically. It's much cleaner; no hacking of html forms; and also no input simulation. It's also much faster and independent of client browser.

Tell me your choice ? (If you'll choose 2nd option I'll help you to grasp the idea)

regards,
Kate
0
 
Niall292Author Commented:
I love the 2nd idea but I would need help to grasp the idea.
So if you don't mind I would love to take you up on your offer.

Niall
0
 
_Katka_Commented:
Hi, there's list of prerequisities which you'll need:

1) first of all download some http analyzer, I'd prefer this one:

homepage - http://www.ieinspector.com/httpanalyzer/
trial version download - http://www.ieinspector.com/httpanalyzer/downloadV4/HttpAnalyzerFullV4_Trial.exe

Note: It will install a proxy, and also IE add-in, so you can watch the web requests/responses as they go from within a user-friendly GUI. Be aware that it's still a proxy and it filters the traffic so be sure to uninstall it when not needed anymore; or atleast disable it.

2) Start IE (an alternative browser can be also monitored with standalone application) and press Alt+Shift+F2 (or click in menu View -> Explorer bars -> IEAnalyzer) and the GUI should appear at the botom. Now navigate to a page you want to analyze and watch as the requests/responses are filling in to a GUI. There are two tabs (as in add-ins tabcontrol) which we're interested in; the "Header" tab and "Cookies" tab. I hope your page doesn't use cookies as it'd be much easier without the need to maintain the session cookies.

3) After you have your data filtered. That means you've performed a request to the web page and also the it's response. The web-request is data (in one or more packets) which your browser sends to a webpage and the web-response is the answer (again data) by that website. The "Header" tab contains all the data we need to build our web-request in C#. So go to your website (while the analyzer is started) fill that webpage with some data; browse for a file and then stop the analyzer and save or otherwise copy out the "Header" tab data for all the the rows. Be sure to clear the analyzer data before trying this out so there're only data we're interested in. So start IE wait for it to finish the initial default homepage to load. Show IEAnalyzer and clear ("Clear" button) any possible contents before trying your site.

I'll stop here and ask you to prepare that situation so we can continue. Please post here the result of your request and response so we can analyze it together and form the C# code to make it automatized.

Note: Don't forget to scramble or strip all the possible username/passwords or other sensitive data (usually cookie values).

regards,
Kate
0
 
Niall292Author Commented:
Ok This what I have so far. When I open the first logon page the There is nothing in the headers tab or the Cookies tab.
When I put the pasword in and press submit something flashes in the Header tab but then it goes blan again.
When I enter the second username and Password on the second screen and go to the third scren where I need to enter the path to the file. The Header tab now has the info attached in it. Unfortunately I cannot test the upload file part because I can only do it at a certain time each day but hopefully I will get a chance to try it tomorrow

Header.txt
0
 
_Katka_Commented:
Hi, I've made a little sample program to get you more familiar with this approach.

It will make a request to a www.rapidshare.com and retrieves your session ID.
Then it uses this ID to upload a specificed file and recieves the result of the operation and also your download link.

I've tried to comment it heavily, but feel free to ask. If you'll get more specific data on your website, we can forge more specific requests and response analysis.

regards,
Kate

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
 
namespace DirectWebAccessProof
{
    public class Program
    {
        // this is the constant for a website we're contacting
        private const String STR_WebSite = "http://www.rapidshare.com/";
 
        // this is a list of accepted data formats
        private const String STR_AcceptString = "text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1";
 
        // this is specially used by Rapidshare (RS), but usually it's used by the uploaders (a boundary may change depending on the server)
        private const String STR_ContentType = "multipart/form-data; boundary={0}";
 
        // the file we'll be uploading
        private const String STR_UploadFile = @"C:\test.txt";
 
        // Mozzila impostor string (we're behaving like a Mozzila)
        private const String STR_UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; EmbeddedWB 14.52 from: http://www.bsalsa.com/ EmbeddedWB 14,52; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.21022; .NET CLR 1.1.4322; .NET CLR 3.5.30729; .NET CLR 3.0.30618; WWTClient2; FDM; OfficeLiveConnector.1.3; OfficeLivePatch.0.0)";
 
        // the container for possible cookies (not used on RS)
        private static CookieContainer cookieContainer;
 
        static void Main(string[] args)
        {
            // initializes the cookie container
            cookieContainer = new CookieContainer();
 
            // GET method - retrieves the Google's website html code
            HttpWebRequest initialRequest = InitialRequest();
            String uploadUriText = InitialResponse(initialRequest);
 
            // POST method - posts a upload into the input edit
            HttpWebRequest uploadRequest = UploadRequest(uploadUriText);
            UploadResponse(uploadRequest);
        }
 
        private static HttpWebRequest InitialRequest()
        {
            HttpWebRequest result = null;
 
            // creates the URI format out of the web address
            Uri initialUri = new Uri(STR_WebSite);
 
            try
            {
                // prepares the request for the RS website server
                result = (HttpWebRequest) WebRequest.Create(initialUri);
                result.Accept = STR_AcceptString;
                result.ContentType = "application/x-www-form-urlencoded";
                result.CookieContainer = cookieContainer;
                result.KeepAlive = true;
                result.Method = "GET";
                result.Proxy = null;
                result.Referer = STR_WebSite;
            }
            catch (Exception ex)
            {
                // rethrow your own exception
                throw;
            }
 
            return result;
        }
 
        private static String InitialResponse(WebRequest initialRequest)
        {
            HttpWebResponse initialResponse = null;
            Stream initialResponseStream = null;
 
            try
            {
                // retrieves the RS server response to our request (equivalent to navigating browser to http://www.rapidshare.com)
                initialResponse = (HttpWebResponse) initialRequest.GetResponse();
 
                // retrieves the stream of data which is poured at us from RS server
                initialResponseStream = initialResponse.GetResponseStream();
 
                // reads the data stream
                Byte[] responseData = ReadStreamToByte(initialResponseStream);
 
                // converts it to a string (in fact it's a HTML)
                String responseString = Encoding.ASCII.GetString(responseData);
                
                //
                // THIS IS RAPIDSHARE SPECIFIC!
                //
 
                // find the post action which gives us next request uri, post id
                Int32 actionIndex = responseString.IndexOf("action=\"") + 8;
                String uploadUriText = string.Empty;
 
                while (responseString[actionIndex] != '"')
                {
                    uploadUriText += responseString[actionIndex];
                    actionIndex++;
                }
 
                return uploadUriText;
            }
            catch (Exception ex)
            {
                // rethrow your own exception
                throw;
            }
            finally
            {
                // closes the reponse (otherwise it'll stay open)
                if (initialResponse != null)
                {
                    initialResponse.Close();
                }
 
                // closes and disposes the data stream (also needed)
                if (initialResponseStream != null)
                {
                    initialResponseStream.Flush();
                    initialResponseStream.Close();
                    initialResponseStream.Dispose();
                }
            }
        }
 
        private static HttpWebRequest UploadRequest(String uploadUriText)
        {
            HttpWebRequest result = null;
            Stream uploadRequestStream = null;
 
            // creates the URI format out of the web address we've retrieved from the first response (that action url which also contains RS identificator)
            Uri uploadUri = new Uri(uploadUriText);
            
            // RS specific; builds a separator for the post data
            String boundaryString = string.Format("-----------------------------{0}", DateTime.Now.Ticks.ToString("x"));
 
            // builds the data request (header + file's data)
            Byte[] postData = BuildPostData(boundaryString);
 
            try
            {
                // prepares another request; this time it's a POST (that means it's sending our own data to a server and not only the request)
                result = (HttpWebRequest) WebRequest.Create(uploadUri);
                result.Accept = STR_AcceptString;
                result.ContentLength = postData.Length;
                result.ContentType = string.Format(STR_ContentType, boundaryString);
                result.CookieContainer = cookieContainer;
                result.KeepAlive = true;
                result.Method = "POST";
                result.Referer = STR_WebSite;
                result.UserAgent = STR_UserAgent;
 
                // uploads our data to the server (header + file contents)
                uploadRequestStream = result.GetRequestStream();
                uploadRequestStream.Write(postData, 0, postData.Length);
                uploadRequestStream.Flush();
                uploadRequestStream.Close();
            }
            catch (Exception ex)
            {
                // rethrow your own exception
            }
            finally
            {
                // again releases our data stream
                if (uploadRequestStream != null)
                {
                    uploadRequestStream.Flush();
                    uploadRequestStream.Close();
                    uploadRequestStream.Dispose();
                }
            }
 
            return result;
        }
 
        private static void UploadResponse(WebRequest uploadRequest)
        {
            HttpWebResponse uploadResponse = null;
            Stream uploadResponseStream = null;
 
            try
            {
                // finally we're retrieving the post-upload response (should contain the link to a file)
                uploadResponse = (HttpWebResponse) uploadRequest.GetResponse();
                uploadResponseStream = uploadResponse.GetResponseStream();
                Byte[] responseData = ReadStreamToByte(uploadResponseStream);
 
                // if you'll check this out in watches (+ html view) it should show you the preview of the RS page after uploading
                String responseString = Encoding.ASCII.GetString(responseData);
 
                // we check for the random text on the successful RS upload screen and if it's present the upload was a success
                if (responseString.Contains("Your file has been saved"))
                {
                    Console.WriteLine("Successfully uploaded '{0}' file!", STR_UploadFile);
 
                    // just for the fun we'll read the download link of the uploaded file (we can check the integrity/contents of the file)
                    Int32 downloadLinkIndex = responseString.IndexOf("class=\"downloadlink\"") + 21;
                    String downloadLinkText = string.Empty;
 
                    while (responseString[downloadLinkIndex] != '<')
                    {
                        downloadLinkText += responseString[downloadLinkIndex];
                        downloadLinkIndex++;
                    }
 
                    Console.WriteLine("Download link: {0}", downloadLinkText);
                    Console.ReadKey();
                }
                else
                {
                    Console.Write("Something failed! Probably RS changed something and we're out-dated. :/");
                }
            }
            catch (Exception ex)
            {
                // rethrow your own exception
            }
            finally
            {
                // ..and again releases response and response stream
                if (uploadResponse != null)
                {
                    uploadResponse.Close();
                }
 
                if (uploadResponseStream != null)
                {
                    uploadResponseStream.Flush();
                    uploadResponseStream.Close();
                    uploadResponseStream.Dispose();
                }
            }
        }
 
        private static Byte[] BuildPostData(String boundary)
        {
            // this is a special RS data pack which tells the RS website we're uploading a file, it's contents and also we clicked on the upload picture (x,y coordinates)
            StringBuilder result = new StringBuilder();
            result.AppendLine(boundary);
            result.AppendLine(string.Format("Content-Disposition: form-data; name=\"filecontent\"; filename=\"{0}\"", STR_UploadFile));
            result.AppendLine("Content-Type: text/plain");
            result.AppendLine();
            result.AppendLine(File.ReadAllText(STR_UploadFile));
            result.AppendLine(boundary);
            result.AppendLine("Content-Disposition: form-data; name=\"u.x\"");
            result.AppendLine();
            result.AppendLine("53");
            result.AppendLine(boundary);
            result.AppendLine("Content-Disposition: form-data; name=\"u.y\"");
            result.AppendLine();
            result.AppendLine("47");
            result.AppendLine(boundary + "--");
            return Encoding.ASCII.GetBytes(result.ToString());
        }
 
        private static Byte[] ReadStreamToByte(Stream initialResponseStream)
        {
            // just reads the response stream to a array of byte (should be done faster, but I picked just basic option here)
 
            List<Byte> result = new List<Byte>();
            Int32 offset = 0;
            Int32 value = -2;
 
            while (value != -1)
            {
                value = initialResponseStream.ReadByte();
                result.Add((Byte) value);
                offset++;
            }
 
            return result.ToArray();
        }
    }
}

Open in new window

0
 
_Katka_Commented:
Sorry for some typos, it was late when I posted it yesterday.

Here are the method explained:

InitialRequest - similar to a browser going to a website www.rapidshare.com
InitialResponse - similar to a browser displaying the contents of that website

UploadRequest - simulates you clicking the browse for a file, choosing one, and pressing the upload button
UploadResponse - similar to a browser displaying the message after you've uploaded the file

I split the methods so it's more evident, how this works. Basically you'll do a particular request, it has to be one which target website understands and it'll give you a response with some data. You'll analyze the data and you'll create another request, or in a case of the browser you let the user to interact with the html and perform the events of the html elements.

regards,
Kate
0
 
Niall292Author Commented:
Katka,
I just want to let you know I haven't fallen off the face of the planet just under alot of pressure with other projectss but hope to try this in the next day or 2.

Sorry for the delay
0
 
_Katka_Commented:
Hi, no problem I'm actually on the vaccation in Marroco. So no stress there. :D

regards,
Kate
0
 
Niall292Author Commented:
I would prefer not to close this question. It is just alot of other projects have came up and I need time to get back to this
0
 
_Katka_Commented:
I have no objection to that, just please make it before my retirement (some 30 years into the future), as I can't promise to hold that information in that age. :D

regards,
Kate
0
 
Niall292Author Commented:
I'm sorry it has taken me so long to get back to this and honestly I still have not finished it but I do think this is definitely sending me in the right direction
0
 
_Katka_Commented:
It's ok, you can always ask another question to fine-tune it to your needs.

best regards,
Kate
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

  • 11
  • 9
Tackle projects and never again get stuck behind a technical roadblock.
Join Now