Link to home
Start Free TrialLog in
Avatar of sirbounty
sirbountyFlag for United States of America

asked on

VB.net - uploading a file to a web site

Based on the process outlined here, http://www.codeproject.com/Articles/28917/Setting-a-file-to-upload-inside-the-WebBrowser-com, I'm looking for help incorporating this into my vb.net project.
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Let's start by converting to VB.NET:

private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            HtmlDocument doc = webBrowser1.Document;
            for (int i = 0; i < doc.Forms.Count; i++)
            {
                HtmlElement form = doc.Forms[i]; // must be declared inside the loop because there's a closure
                if (form.GetAttribute("enctype").ToLower() != "multipart/form-data") { continue; }
                
                form.AttachEventHandler("onsubmit", delegate(object o, EventArgs arg)
                    {
                        FormToMultipartPostData postData = new FormToMultipartPostData(webBrowser1, form);
                        postData.SetFile("file", @"C:\windows\win.ini");
                        postData.Submit();
                    });
                form.SetAttribute("hasBrowserHandler", "1"); // expose that we have a handler to JS
            }
        }

Open in new window

SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
FormToMultipartPostData.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;


namespace FormToMultipartPostData
{
    /**
     * 
     * This is a helper class to build postData header an body with the values from 
     * the given HTML form. The purpose of this class is to attach files to 
     * the <INPUT TYPE="FILE"> fields.
     * 
     * License: Code Project Open License (CPOL)
     * (c) Kirill Hryapin, 2008
     * (c) Steven Cheng (Microsoft), 2005 (?)
     * 
     * See usage example in Form1.cs included with this distribution.
     * 
     */

    public class FormToMultipartPostData
    {
        private struct ValuePair // KeyValuePair<string, string> sounds too verbose for me
        {
            public string name;
            public string value;
            public ValuePair(string name, string value)
            {
                this.name = name;
                this.value = value;
            }
        }

        public struct RequestParameters
        {
            public byte[] data;
            public string headers;
        }

        private List<ValuePair> values = new List<ValuePair>();
        private List<ValuePair> files = new List<ValuePair>();
        private Dictionary<string, string> overloadedFiles = new Dictionary<string,string>();

        private HtmlElement form;
        private WebBrowser webbrowser;

        /**
         * In most circumstances, this constructor is better (allows to use Submit() method)
         */
        public FormToMultipartPostData(WebBrowser b, HtmlElement f)
        {
            form = f; webbrowser = b;
            GetValuesFromForm(f);
        }

        /**
         * Use this constructor if you don't want to use Submit() method
         */
        public FormToMultipartPostData(HtmlElement f)
        {
            GetValuesFromForm(f);
        }

        /**
         * Submit the form
         */
        public void Submit()
        {
            Uri url = new Uri(webbrowser.Url, form.GetAttribute("action"));
            RequestParameters req = GetEncodedPostData();
            webbrowser.Navigate(url, form.GetAttribute("target"), req.data, req.headers);
        }

        /**
         * Load values from form
         */
        private void GetValuesFromForm(HtmlElement form)
        {
            // Get values from the form
            foreach (HtmlElement child in form.All)
            {
                switch (child.TagName)
                {
                    case "INPUT":
                        switch (child.GetAttribute("type").ToUpper())
                        {
                            case "FILE":
                                AddFile(child.Name, child.GetAttribute("value"));
                                break;
                            case "CHECKBOX":
                            case "RADIO":
                                if (child.GetAttribute("checked") == "True")
                                {
                                    AddValue(child.Name, child.GetAttribute("value"));
                                }
                                break;
                            case "BUTTON":
                            case "IMAGE":
                            case "RESET":
                                break; // Ignore those?
                            default:
                                AddValue(child.Name, child.GetAttribute("value"));
                                break;
                        }
                        break;
                    case "TEXTAREA":
                    case "SELECT": // it's legal in IE to use .value with select (at least in IE versions 3 to 7)
                        AddValue(child.Name, child.GetAttribute("value"));
                        break;
                } // of "switch tagName"
            } // of "foreach form child"
        }

        private void AddValue(string name, string value)
        {
            if (name == "") return; // e.g. unnamed buttons
            values.Add(new ValuePair(name, value));
        }

        private void AddFile(string name, string value)
        {
            if (name == "") return;
            files.Add(new ValuePair(name, value));
        }

        /**
         * Set file field value [the reason why this class exist]
         */
        public void SetFile(string fieldName, string filePath)
        {
            this.overloadedFiles.Add(fieldName, filePath);
        }

        /**
         * One may need it to know whether there's specific file input
         * For example, to perform some actions (think format conversion) before uploading
         */
        public bool HasFileField(string fieldName)
        {
            foreach (ValuePair v in files) {
                if (v.name == fieldName) { return true; }
            }
            return false;
        }

        /**
         * Encode parameters 
         * Based on the code by Steven Cheng, http://bytes.com/forum/thread268661.html
         */
        public RequestParameters GetEncodedPostData()
        {
            string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

            Stream memStream = new System.IO.MemoryStream();
            byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

            string formdataTemplate = "\r\n--" + boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";
            foreach (ValuePair v in values)
            {
                string formitem = string.Format(formdataTemplate, v.name, v.value);
                byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
                memStream.Write(formitembytes, 0, formitembytes.Length);
            }
            memStream.Write(boundarybytes, 0, boundarybytes.Length);

            string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n";

            foreach (ValuePair v in files)
            {
                string filePath;

                if (overloadedFiles.ContainsKey(v.name))
                {
                    filePath = overloadedFiles[v.name];
                }
                else
                {
                    if (v.value.Length == 0) { continue; } // no file
                    filePath = v.value;
                }

                try // file can be absent or not readable
                { 
                    FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);

                    string header = string.Format(headerTemplate, v.name, filePath);
                    byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
                    memStream.Write(headerbytes, 0, headerbytes.Length);

                    byte[] buffer = new byte[1024];
                    int bytesRead = 0;
                    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        memStream.Write(buffer, 0, bytesRead);
                    }

                    memStream.Write(boundarybytes, 0, boundarybytes.Length);
                    fileStream.Close();
                }
                catch (Exception x) // no file?..
                {
                    MessageBox.Show(x.Message, "Cannot upload the file", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }

            RequestParameters result = new RequestParameters();

            memStream.Position = 0;
            result.data = new byte[memStream.Length];
            memStream.Read(result.data, 0, result.data.Length);
            memStream.Close();

            result.headers = "Content-Type: multipart/form-data; boundary=" + boundary + "\r\n" +
                             "Content-Length: " + result.data.Length + "\r\n" +
                             "\r\n";

            return result;
        }
    }
}

Open in new window

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of sirbounty

ASKER

It's the class where I got stuck - even after converting it... :(
Ah, but your version looks much better...
My code converter of choice:

http://converter.telerik.com/
Code fix recommended:
Dim postData As New FormToMultipartPostData (WebBrowser1, form)
to
Dim postData As New FormToMultipartPostData.FormToMultipartPostData(WebBrowser1, form)
Yep, I use the same converter (I think you showed me that one a few years ago ;^)
To simplify your code, you should remove the Namespace FormToMultipartPostData...End Namespace lines.  C# code had namespace, but it is not needed in VB.NET.
Ok, as-is, it doesn't seem to be applying the file path.  Fortunately with the appliance it will always be the same file.
(removing the namespace corrected the problem from above).

Since I don't quite understand the code, I'm not sure what I should be looking for.  Could it be that the SetFile method is looking for "file" and it's actually labeled something different on the page?  Or am I looking in the wrong direction?
Not sure if this is needed/relevant, but this is what I find in the page source for that browse section:

<input type="file" 
id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload" 
name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload" 
size="40" 
class="formField" />


<tbody><tr><td>
<a href="#" onclick="return oamSubmitForm('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:downloadMsgLockConfig');" id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:downloadMsgLockConfig">Download current configuration</a>
</td><td>

Open in new window

What does the <form> element look like?
What do you need from it?
Shows a Document: {System.Windows.Forms.HtmlDocument}
DomElement: {[object]}
Several "Event" options - all objects as well...
Have an ID, which appears to be the same as above, an inner-html & text, as well as outer of the same.
Wait - at this point, I haven't sent a click to the Browse button - I'm thinking I should do that first and then proceed with the above?  
I'll try that now...
Nope - that just 'leaves' it at the "Choose File to Upload" dialogue box...
I'm wondering if the problem is that the box where the file name ends up, is greyed out - and you cannot type in it - you must choose the browse button to upload.
Still possible with this code?
There is a problem with the VB.NET conversion here:

form.AttachEventHandler("onsubmit", delegate(object o, EventArgs arg)
{
     FormToMultipartPostData postData = new FormToMultipartPostData(webBrowser1, form);
     postData.SetFile("file", @"C:\windows\win.ini");
     postData.Submit();
});

The delegate function is known as an anonymous function.  It attaches new code to the onsubmit event handler.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
"What does the <form> element look like?"
I need to see the <form> element in the HTML, not from your code.

Like this:
<form action="demo_form.asp" method="get">
I don't see a form action...
<form id="zdmSummaryDetailsSubview:relContentViewpc1:zdmTabsForm2" name="zdmSummaryDetailsSubview:relContentViewpc1:zdmTabsForm2" method="post" action="/console/services/zdm/index.jsf" enctype="application/x-www-form-urlencoded"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="wizardButtonBox"><tbody><tr><td></td></tr></tbody></table><input type="hidden" name="zdmSummaryDetailsSubview:relContentViewpc1:zdmTabsForm2_SUBMIT" value="1" /><input type="hidden" name="javax.faces.ViewState" value="rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAN0AAFhcHQAFy9zZXJ2aWNlcy96ZG0vaW5kZXguanNw" /></form>

Open in new window


Should
form.AttachEventHandler("onsubmit", AddressOf OnSubmit)
be
form.AttachEventHandler("oamSubmitForm", AddressOf OnSubmit) ? ( see prior post)
Do I need to publicly declare 'form' for OnSubmit to use?  Or pass it as an argument somehow?
As-is, it's not hitting the onsubmit sub...

Here's my current documentcompleted sub:
Private Sub WebBrowser1_DocumentCompleted(sender As Object, e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
        If e.Url.AbsolutePath.ToLower.Contains("login") Then Exit Sub
        If ServiceTabClicked Then
            If ZDMConfigTabClicked Then
'                Dim browseButton As mshtml.HTMLInputElement = helper.GetHtmlElementById(Of mshtml.HTMLInputElement)("zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload")
'                browseButton.click()
                Dim doc As HtmlDocument = WebBrowser1.Document
                For i As Integer = 0 To doc.Forms.Count - 1
                    Form = doc.Forms(i)
                    If form.GetAttribute("enctype").ToLower() <> "multipart/form-data" Then Continue For
                    form.AttachEventHandler("onsubmit", AddressOf OnSubmit)
                    form.SetAttribute("hasBrowserHandler", "1")
                Next
            Else
                Dim zdmCfgTab As mshtml.HTMLInputElement = helper.GetHtmlElementById(Of mshtml.HTMLInputElement)("zdmSummaryDetailsSubview:relContentViewpc1:configZDMTabbedPane.2") '"zdmSummaryDetailsSubview:relContentViewpc1:zdmTabsForm:bcServiceZDM")
                '"zdmSummaryDetailsSubview:relContentViewpc1:configZDMTabbedPane.2"
                zdmCfgTab.click()
                ZDMConfigTabClicked = True
            End If
        Else
            VSite.Path = "console/services/zdm/index.jsf"
            WebBrowser1.Navigate(VSite.Uri.ToString)
            ServiceTabClicked = True
        End If
    End Sub

Open in new window

Don't know if this helps, but before the upload this is what I see for the 'file':
<input type="file" id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload" name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload" size="40" class="formField" />

Open in new window

After:
<INPUT id=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload
 class=formField 
  value="\\SERVER\PATH\FILE.CSV" 
  size=40 
  type=file 
name=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload>

Open in new window

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ok, that makes it a little bit clearer for me, thanks.
But (a) does that eliminate the need for me to locate the browse button and 'click' it?  and (b) I've tried it both ways and neither route seems to be doing anything.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Yes, there is a link to download the current config (it has never really worked when I tried it), but we just append to an existing external config file sitting on a share.

The way I'm interpretting this is that we've setup an event hanlder for when the page attempts to submit the file, but there doesn't seem to be a way to actually submit the file, or is that what clicking the browse button should trigger?  Because that seems to move away from the scope of the page and generate a popup dialogue that's no longer associated with the web page.  Should this event handler be able to manipulate that dialogue?

Could it be that the event handler is named incorrectly?
Is the "hasBrowserHandler" attribute still applicable (I'm not sure what the benefit is for that)?
I did try putting a breakpoint at OnSubmit, but it doesn't hit it.  I also adjusted the code to set the handler before clicking the browse button:
Dim browseButton As mshtml.HTMLInputElement = helper.GetHtmlElementById(Of mshtml.HTMLInputElement)("zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload")

Dim doc As HtmlDocument = WebBrowser1.Document
  For i As Integer = 0 To doc.Forms.Count - 1
    form = doc.Forms(i)
    If form.GetAttribute("enctype").ToLower() <> "multipart/form-data" Then Continue For
    form.AttachEventHandler("onsubmit", AddressOf OnSubmit)
    form.SetAttribute("hasBrowserHandler", "1")
  Next
  browseButton.click()
End If

Open in new window


I can attach the full page code if that would help.  I'm afriad I just don't know enough to know what else to look for (but I'm still trying - and learning! :^)
I just noticed something in the code that you posted:

If form.GetAttribute("enctype").ToLower() <> "multipart/form-data" Then Continue For

Open in new window


This means if the enctype attribute is not "multipart/form-data", then skip.

The form enctype is here:

enctype="application/x-www-form-urlencoded"
I had hopes, but no, still nothing.

Looking at the source, the only enctype before the browse button is showing multipart/form-data.  Is that not the relevant one?
<table width="920"><tbody><tr><td><form id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm" name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm" method="post" action="/console/services/zdm/index.jsf"
enctype="multipart/form-data"><div class="leftPaddedSection"><div class="form"><table border="0" cellspacing="2" width="100%"><tbody><tr><td class="formLabelNoWrap">Default Domain *</td><td class=""><input id="defaultDomain" name="defaultDomain" type="text" value="COMPANY.com" size="30" class="formField" /></td></tr><tr><td class="formLabelNoWrap">Message Format</td><td class=""><select id="msgFormat" name="msgFormat" size="1" class="formField">      <option value="V2">Version 2</option>      <option value="V3" selected="selected">Version 3</option></select></td></tr><tr><td class="formLabelNoWrap"><nobr>Session Timeout Period (minutes) *</nobr></td><td class="">
<input id="sessionTimeout" name="sessionTimeout" type="text" value="20" size="12" class="formField" /></td></tr><tr><td class="formLabelNoWrap">Identity Timeout Policy</td><td class=""><select id="identityTimeoutPolicy" name="identityTimeoutPolicy" size="1" class="formField" onchange="submit()">      <option value="OFF">Disabled</option>      <option value="NEVER">Enabled - Never Times Out</option>      <option value="TIMEOUT" selected="selected">Enabled - With Timeout Period</option></select></td></tr><tr><td class="formLabelNoWrap">Identity Timeout Period</td><td class=""><input id="identityTimeout" name="identityTimeout" type="text" value="12" size="12" class="formField" /> <select id="identityTimeoutUnit" name="identityTimeoutUnit" size="1" class="formField">      <option value="HOUR" selected="selected">Hour(s)</option>      <option value="DAY">Day(s)</option>      <option value="WEEK">Week(s)</option></select></td></tr><tr><td class="formLabelNoWrap"><span class="formLabelTop">Message Locking Configuration</span></td>
Thinking I may be misunderstanding (again)...should I be focusing on the Save button and making sure that submit gets overridden with the file details? (I have a new question open for that one, since it's an anchor element and I've been unable to get the click event working for that)
So many details, so little time...

Please show me the HTML elements for the <input type="file> for the upload, and the associated <form> element.
I pulled this block which includes the options before (setting I don't change), the browse button and the save button/link:
<form id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm"   name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm" 
  method="post" 
  action="/console/services/zdm/index.jsf" 
  enctype="multipart/form-data">
<div class="leftPaddedSection"><div class="form">
  <table border="0" cellspacing="2" width="100%">
   <tbody><tr><td class="formLabelNoWrap">Default Domain *</td><td class="">
     <input id="defaultDomain" name="defaultDomain" type="text" value="companyname.com" size="30" class="formField" />
	</td></tr><tr>
	<td class="formLabelNoWrap">Message Format</td>
	<td class="">
       <select 
	 id="msgFormat" 
	 name="msgFormat" 
 	 size="1" 
	 class="formField">	
	 <option value="V2">Version 2</option>	
	 <option value="V3" selected="selected">Version 3</option>
       </select>
       </td>
       </tr>
       <tr>
       <td class="formLabelNoWrap"><nobr>Session Timeout Period (minutes) *</nobr></td>
       <td class="">
       <input 
	 id="sessionTimeout" 
	 name="sessionTimeout" 
	 type="text" 
	 value="20" 
	 size="12" 
	 class="formField" />
       </td>
       </tr>
       <tr><td class="formLabelNoWrap">Identity Timeout Policy</td>
       <td class="">
       <select 
	 id="identityTimeoutPolicy" 
	 name="identityTimeoutPolicy" 
	 size="1" 
	 class="formField" 
	 onchange="submit()">	
	 <option value="OFF">Disabled</option>	
	 <option value="NEVER">Enabled - Never Times Out</option>	
	 <option value="TIMEOUT" selected="selected">Enabled - With Timeout Period</option>
       </select>
       </td></tr>
       <tr><td class="formLabelNoWrap">Identity Timeout Period</td>
	<td class="">
	<input id="identityTimeout" name="identityTimeout" type="text" value="12" size="12" class="formField" /> 
	<select id="identityTimeoutUnit" name="identityTimeoutUnit" size="1" class="formField">	
	<option value="HOUR" selected="selected">Hour(s)</option>	
	<option value="DAY">Day(s)</option>	
	<option value="WEEK">Week(s)</option></select></td></tr>
	<tr>
	<td class="formLabelNoWrap">
	<span class="formLabelTop">Message Locking Configuration</span>
	</td>
	<td class="">
	<br/>
	<span class="formTip">Browse to select a CSV file, then click the Save button to upload it.</span>
	<table border="0" cellpadding="2" cellspacing="0">
	<tbody>
	<tr>
	<td>
	<input type="file" 
	  id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload"   	  name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload" 
	  size="40" 
	  class="formField" />
	</td>
	<td></td>
	</tr>
  </tbody>
</table>
<table border="0" cellpadding="3" cellspacing="0">
<tbody>
<tr><td>
<a href="#" onclick="return oamSubmitForm('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:downloadMsgLockConfig');" id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:downloadMsgLockConfig">Download current configuration</a></td><td> </td><td><a href="#" onclick="var cf = function(){if (!confirm('Are you sure you want to delete all message locking filters?')) return false};var oamSF = function(){return oamSubmitForm('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:clearMsgLockConfig');};return (cf()==false)? false : oamSF();" id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:clearMsgLockConfig">Clear current configuration</a></td></tr></tbody></table></td></tr></tbody></table></div></div><br/><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td>

<div class="ButtonRowR">
  <table class='ButtonWrapper'>
    <tr>
      <td>
        <a href="#" 
	  onclick="return oamSubmitForm	('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig');" 	  id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig" class="ButtonLeft">
	  <div class='ButtonRight'>
 	    <div class='ButtonMiddle'>Save</div>
	  </div>
	</a>
      </td>
    </tr>
  </table>
</div>
</td>
</tr>
</tbody>
</table>

Open in new window

We need to focus here:

<div class="ButtonRowR">
  <table class='ButtonWrapper'>
    <tr>
      <td>
       <a href="#"
        onclick="return oamSubmitForm      ('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig');"         id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig" class="ButtonLeft">
       <div class='ButtonRight'>
           <div class='ButtonMiddle'>Save</div>
        </div>
      </a>
      </td>
    </tr>
  </table>
</div>

Is that the relevant anchor that you can't get click() to work?
Yes, but I think it's because of the function that it's trying to execute.
Four parameters required: (formName, linkId, target, params)
Changed this line (to onclick)
 form.AttachEventHandler("onclick", AddressOf OnSubmit)
and it hit the handler...although it still popped up a script error (yes/no prompt)
Stepping through the code shows:
? child.Name
"zdmSummaryDetailsSubview:footerViewpc1:footerFormpc1_SUBMIT"
? child.GetAttribute("value")
"1"

? child.Name
"javax.faces.ViewState"
? child.GetAttribute("value")
"rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kN5YnxBzKWwCAAB4cAAAAAN0AAE1cHQAFx9zZXJ2aWNlcy43ZG0vaW5kZXguanNw"

(this from the Case Else - I don't really need to catch these)

It never hit the input, file case element.
Once it went through I fired off the remainder and it did add the attribute, but then bombed at

result.headers = (Convert.ToString("Content-Type: multipart/form-data; boundary=") & boundary) + vbCr & vbLf + "Content-Length: " + result.data.Length + vbCr & vbLf + vbCr & vbLf

with a conversion error.
I think we're getting closer?
Try this:
result.headers = "Content-Type: multipart/form-data; boundary=" & boundary & vbCr & vbLf & "Content-Length: " & result.data.Length & vbCr & vbLf & vbCr & vbLf

Open in new window

Well, it prevented the coversion error, but with the script error still there it doesn't seem to be submitting the file.
I did try changing the reference to the input file to set that attribute for "file" to the location of the file.  Didn't seem to change anything there either.
I'll try turning the script error suppresion back on (left it off so I could get past the security alert, but now I've installed the cert)...
It seems to loop through the doccompleted sub multiple times after I click the save button.
No script errors this time, but about the 3rd time it hits the completed sub, it doesn't find the browse button, so errors on the missing object.  And the page now looks like it didn't completely load.
I let the sub finish out and tried a refresh, but it's still not uploading  the file.
I know when it has new config data, because there's a section on the header that states an update to all nodes is needed...
At this point I can't assume that this line was reached:

webbrowser.Navigate(url, form.GetAttribute("target"), req.data, req.headers)

Open in new window

And, we shouldn't assume that this line is reached:

postData.Submit()

Open in new window

Do you reach this line?

Private Sub AddFile(name As String, value As String)
                  If name = "" Then
                        Return
                  End If
                 files.Add(New ValuePair(name, value))
            End Sub
What is "result.data.Length" here?

result.headers = "Content-Type: multipart/form-data; boundary=" & boundary & vbCr & vbLf & "Content-Length: " & result.data.Length & vbCr & vbLf & vbCr & vbLf
First two comments - yes, I hit breakpoints at both (though after the script error).
No to the AddFile sub - it never hits that case element.
result.data.length is 416 at the line you posted last, but for the webbrowser.navigate, the url appears correct, but the form attribute "target" is empty...
1) The "target" attribute is not specified, and that's OK, since you don't need it (target frame).

2) It should call AddFile here:

Select Case child.TagName
    Case "INPUT"
          Select Case child.GetAttribute("type").ToUpper()
            Case "FILE"
                 AddFile(child.Name, child.GetAttribute("value"))
                  Exit Select
                  Case "CHECKBOX", "RADIO"
...
I'll step through it again and see what the tagname is - pretty sure it kept hitting the case else...
First pass to GetValuesFromForm,
child.TagName = "A" so it skips, next it's "INPUT" but "type" attribute is 'hidden',
so it hits the case else and adds "hidden" with a value of '1'
Third time it's "INPUT" with 'hidden' and a value of a really long string of seemingly random characters.
It doesn't return after this.
It needs to find the <input type="file"> element.

<input type="file"         id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload"           name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload"
        size="40"
        class="formField" />
      </td>
And for the GetEncodedPostData, the values (2 of them) are:
? values(0)
{Voltage_Test.FormToMultipartPostData.ValuePair}
    name: "zdmSummaryDetailsSubview:footerViewpc1:footerFormpc1_SUBMIT"
    value: "1"
? values(1)
{Voltage_Test.FormToMultipartPostData.ValuePair}
    name: "javax.faces.ViewState"
    value: "rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAN0AAE1cHQAFy9zZXJ2aWNlcy96ZG0vaW5kZXguanNw"
I presume I need to leave this?
Changed this line (to onclick)
 form.AttachEventHandler("onclick", AddressOf OnSubmit)
It wasn't hitting the handler otherwise... Or is there some other event I should be looking for?
According to the sample project, you need to attach an event handler to the "onsubmit" event:

form.AttachEventHandler("onsubmit", ...

When the event doesn't get raised, it is because there is no submit.  That should happen on the <input type="file"> element, which we are having a problem finding.
I think this is why it's not making sense to me...
The only 'event' that I see in the source is when the save button is clicked.  There is no action shown for the browse button.  Are we assuming we can technically skip that procedure and just ensure that the file value gets populated correctly and then submit via the save button?  Or should I focus on actually clicking the browse button and that somehow is inserting the file into the popup dialogue?
This is pretty complex, so let's review:

1) Attach an event handler to the "onsubmit" event.

2) The <Save> anchor, when clicked, raises the "submit" event.

3) Find an <input type="file"> HTML element.

4) When the "onsubmit" event is raised, handle it.

5) Let JavaScript know that the client-side event was handled by external handler:
form.SetAttribute("hasBrowserHandler", "1")

6) The FormToMultipartPostData.Submit method uses a POST to get the file bytes, and upload to the server.

All these steps need to work correctly, or the upload will not occur.
Putting it back to onsubmit doesn't do anything.
I grab the id for the save button, click it, get the script error, then it just kind of sits there... :(

The only onsubmit that I see in the source is within the page function:
function oamSubmitForm(formName, linkId, target, params) {
	var clearFn = 'clearFormHiddenParams_'+formName.replace(/-/g, '\$:').replace(/:/g,'_')
    if (typeof window[clearFn] =='function') {
        window[clearFn](formName)
    }
    var form = document.forms[formName]
    var agentString = navigator.userAgent.toLowerCase()
    if (agentString.indexOf('msie') != -1) {
        if (!(agentString.indexOf('ppc') != -1 && agentString.indexOf('windows ce') != -1 && version >= 4.0)) {
            window.external.AutoCompleteSaveForm(form)
        }
    }
    var oldTarget = form.target
    if(target != null) {
        form.target=target
    }
    if((typeof params!='undefined') && params != null) {
        for(var i=0, param; (param = params[i]); i++) {
            oamSetHiddenInput(formName,param[0], param[1])
        }
    }
    oamSetHiddenInput(formName,formName +':'+'_idcl',linkId)
    if(form.onsubmit) {
        var result=form.onsubmit()
        if((typeof result=='undefined')||result) {
            try {
                form.submit()
            } catch(e) {
            }
        }
    }else {
        try{
            form.submit()
        }catch(e){
        }
    }
    form.target=oldTarget
    if((typeof params!='undefined') && params != null) {
        for(var i=0, param; (param = params[i]); i++) {
            oamClearHiddenInput(formName,param[0], param[1])
        }
    }
    oamClearHiddenInput(formName,formName +':'+'_idcl',linkId)
    return false
}

Open in new window

Perhaps a silly, certainly ignorant, question - case doesn't matter with 'onsubmit' does it?
So I shouldn't be focused on the onclick listed under the anchor tag?
<a href="#" 
	onclick="return 
	oamSubmitForm ('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig');" 
	id="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig" 
	class="ButtonLeft">
	<div class="ButtonRight"><div class="ButtonMiddle">Save</div></div>
</a>

Open in new window

"case doesn't matter with 'onsubmit' does it?"
That is never a silly question when working with C# and Javascript, but as a VB.NET programmer it makes sense that you think it is silly.  Case does matter in most cases.

This line will submit the page:
form.submit()

If you have attached to the correct handler, then the event should be handled by the VB.NET code.
And this is the only instance I see window.external, within that function above (window.external.AutoCompleteSaveForm(form)) which is where the script error is thrown immediately after clicking save.
This line will submit the page:
form.submit()
So use that rather than Savebutton.click()?
"So I shouldn't be focused on the onclick listed under the anchor tag?"
Yes, you should be focused on that, since it calls the oamSubmitForm, which in turn calls the form.submit(), which raises the "onsubmit" event, which is handled by the code.

Knee bone connected to the thigh bone...
Thigh bone connected to the hip bone...
submit is not a member of system.windows.forms.htmlelement.
Trying form.raiseevent instead...
Gah - raiseevent gives me "Value does not fall within the expected range." :(
While those were valiant attempts, they will not get you where you need to be.  I believe that it should be possible if we follow the code example project from CodeProject, we just need to get all the steps to work as expected.
Ok, so backing up to your outline above...only step #1 is definitely completed/working.
I'm unsure that the save anchor being clicked is raising the submit event.  Based on what I'm seeing, it is not, or at least it's throwing a script error first that may or may not be interfering.  Suppressing the script errors doesn't seem to do anything.

I'm going to step through the forms.count loop and see if I can determine any uniqueness there.
I also moved the form.raiseevent("submit") within this loop, after the hasBrowserHandler is set.
? doc.forms(0).getattribute("enctype") & vbTab & doc.forms(0).id
"application/x-www-form-urlencoded zdmSummaryDetailsSubview:headerViewpc1:headerFormpc1"

? doc.forms(1).getattribute("enctype") & vbTab & doc.forms(1).id
"application/x-www-form-urlencoded zdmSummaryDetailsSubview:headerViewpc1:globalActionsFormpc1"

? doc.forms(2).getattribute("enctype") & vbTab & doc.forms(2).id
"application/x-www-form-urlencoded zdmSummaryDetailsSubview:nav1Viewpc1:navServicesFormpc1"

? doc.forms(3).getattribute("enctype") & vbTab & doc.forms(3).id
"application/x-www-form-urlencoded zdmSummaryDetailsSubview:nav2Viewpc1:nav2ServiceZDMFormpc1"

? doc.forms(4).getattribute("enctype") & vbTab & doc.forms(4).id
"application/x-www-form-urlencoded zdmSummaryDetailsSubview:relContentViewpc1:zdmTabsForm"

? doc.forms(5).getattribute("enctype") & vbTab & doc.forms(5).id
"application/x-www-form-urlencoded "

? doc.forms(6).getattribute("enctype") & vbTab & doc.forms(6).id
"multipart/form-data zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm"

? doc.forms(7).getattribute("enctype") & vbTab & doc.forms(7).id
"application/x-www-form-urlencoded zdmSummaryDetailsSubview:relContentViewpc1:zdmTabsForm2"

? doc.forms(8).getattribute("enctype") & vbTab & doc.forms(8).id
"application/x-www-form-urlencoded zdmSummaryDetailsSubview:footerViewpc1:footerFormpc1"

So, it appears that element 6 is the one I want, and the only one with multipart/form-data.
The inner/outer html shows:

"  <FORM id=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm encType=multipart/form-data method=post name=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm action=/console/services/zdm/index.jsf file="\\hfdp-spd-020\email\voltagepoc\message locking\msglocking.csv"><DIV class=leftPaddedSection>  <DIV class=form>  <TABLE border=0 cellSpacing=2 width="100%">  <TBODY>  <TR>  <TD class=formLabelNoWrap>Default Domain *</TD>  <TD><INPUT id=defaultDomain class=formField value=aetna.com size=30 name=defaultDomain></TD></TR>  <TR>  <TD class=formLabelNoWrap>Message Format</TD>  <TD><SELECT id=msgFormat class=formField size=1 name=msgFormat> <OPTION value=V2>Version 2</OPTION> <OPTION selected value=V3>Version 3</OPTION></SELECT></TD></TR>  <TR>  <TD class=formLabelNoWrap><NOBR>Session Timeout Period (minutes) *</NOBR></TD>  <TD><INPUT id=sessionTimeout class=formField value=20 size=12 name=sessionTimeout></TD></TR>  <TR>  <TD class=
formLabelNoWrap>Identity Timeout Policy</TD>  <TD><SELECT id=identityTimeoutPolicy class=formField onchange=submit() size=1 name=identityTimeoutPolicy> <OPTION value=OFF>Disabled</OPTION> <OPTION value=NEVER>Enabled - Never Times Out</OPTION> <OPTION selected value=TIMEOUT>Enabled - With Timeout Period</OPTION></SELECT></TD></TR>  <TR>  <TD class=formLabelNoWrap>Identity Timeout Period</TD>  <TD><INPUT id=identityTimeout class=formField value=12 size=12 name=identityTimeout> <SELECT id=identityTimeoutUnit class=formField size=1 name=identityTimeoutUnit> <OPTION selected value=HOUR>Hour(s)</OPTION> <OPTION value=DAY>Day(s)</OPTION> <OPTION value=WEEK>Week(s)</OPTION></SELECT></TD></TR>  <TR>  <TD class=formLabelNoWrap><SPAN class=formLabelTop>Message Locking Configuration</SPAN></TD>  <TD><BR><SPAN class=formTip>Browse to select a CSV file, then click the Save button to upload it.</SPAN>  <TABLE border=0 cellSpacing=0 cellPadding=2>  <TBODY>  <TR>  <TD><INPUT id=zdmSummaryDetailsSubview:relContentViewpc1:tabZDM
Service:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload class=formField size=40 type=file name=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload></TD>  <TD></TD></TR></TBODY></TABLE>  <TABLE border=0 cellSpacing=0 cellPadding=3>  <TBODY>  <TR>  <TD><A id=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:downloadMsgLockConfig onclick="return oamSubmitForm('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:downloadMsgLockConfig');" href="#">Download current configuration</A></TD>  <TD></TD>  <TD><A id=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:clearMsgLockConfig onclick="var cf = function(){if (!confirm('Are you sure you want to delete all message locking filters?')) return false};var oamSF = function(){return oam
SubmitForm('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:clearMsgLockConfig');};return (cf()==false)? false : oamSF();" href="#">Clear current configuration</A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></DIV></DIV><BR>  <TABLE border=0 cellSpacing=0 cellPadding=0 width="100%">  <TBODY>  <TR>  <TD>  <DIV class=ButtonRowR>  <TABLE class=ButtonWrapper>  <TBODY>  <TR>  <TD><A id=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig class=ButtonLeft onclick="return oamSubmitForm('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig');" href="#">  <DIV class=ButtonRight>  <DIV class=ButtonMiddle>Save</DIV></DIV></A></TD></TR></TBODY></TABLE></DIV></
TD></TR></TBODY></TABLE><INPUT value=1 type=hidden name=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm_SUBMIT><INPUT value=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAN0AAE1cHQAFy9zZXJ2aWNlcy96ZG0vaW5kZXguanNw type=hidden name=javax.faces.ViewState></FORM>"

Open in new window


Within there, I see the INPUT file type:
<INPUT id=zdmSummaryDetailsSubview:relContentViewpc1:tabZDM
Service:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload class=formField size=40 type=file name=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload>

and I also see the save anchor element:
<A id=zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig class=ButtonLeft onclick="return oamSubmitForm('zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm','zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig');" href="#">

So since I'm clicking the anchor element, I need to somehow associate the INPUT file type to reference the external file, correct?

In the form loop, it hits i=6, as expected, attaches the event handler to "onsubmit", sets the attribute "hasBrowserHandler" to 1, and that's where I attempt the form.RaiseEvent("submit").  Hitting that line throws an ArgumentException was unhandled by user code: Value does not fall within the expected range.

<sigh> - It was all so 'easy' compared to this... :\
Took a step even further back, just to see if I could get 'any' success clicking an anchor.
So, immediately after logon, I grab the anchor for the Service tab and try to click, but it's coming up as 'nothing' when choosing HTMLAnchorElement.  Could it be that these should be defined as something besides an anchor? :\
Or could it be that it's supposed to reference the 'name' value?  I notice some have id & name, some only have ID... :\
Don't know why I had such trouble locating the Service tab, but once I did, by stepping throuh to the GetHtmlElementById, I was able to locate it.  Once clicked, however, and this is only changing tabs, it barfed on the window.external script error again.  Looks like that's expecting a value each time that function is called.
I'm guessing this part of the function is ensuring it's not a mobile device, and attempting to enable autocomplete?  Though I don't know why, or how to disable that, it appears to be the only source of this window.external in the source.
if (agentString.indexOf('msie') != -1) {
        if (!(agentString.indexOf('ppc') != -1 && agentString.indexOf('windows ce') != -1 && version >= 4.0)) {
            window.external.AutoCompleteSaveForm(form)
        }
    }
I'm stumped. :(

I even tried having my code 'wait' till I populated the file field manually, and follow up with a save click, and it still throws the script error for window.external...

I'll upload the source, in case that helps trigger anything on your end.
zdm.txt
You need to pass the correct User Agent string in the headers, that includes "msie" in the request headers, since it really is Internet Explorer, which is why it barfs on the AutoCompleteSaveForm.

Changing the User Agent in a web browser control
http://www.lukepaynesoftware.com/articles/programming-tutorials/changing-the-user-agent-in-a-web-browser-control/

Remember that Fiddler comment?  You can look at what Internet Explorer passes as the user agent.
I went back and 'fiddled' with fiddler - still couldn't get it to grab anything, though it does state that some policies might prevent that.
I'll have a look at the user agent.
User error with fiddler...under my admin account it captures, so that's sorted....
I see upon logon, the user agent is set to this:

User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; MS-RTC LM 8)
That Javascript goes is looking for "msie", which is "MSIE 8.0".
Although I'm seeing a lot more in fiddler now, I'm not sure how to implement it.
The user agent method above indicates to use .navigate to pass the user agent, but if I'm grabbing an anchor reference, I don't have .navigate.

However, I did get a successful "service tab" click/navigate using:

WebBrowser1.Navigate(Site.Uri.ToString, "_self", Nothing, "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; MS-RTC LM 8)")

Would I just replace the .click method with a navigate to the href="#"?  So long as I can locate and modify the file= attribute?
I looked at the HTML text that you sent me, but I wouldn't be able to set up a mock web site without the Javascript that is referenced:

<script language="JavaScript" type="text/javascript" src="/console/scripts/helpPopup.js?5.1.2"></script>
<script language="JavaScript" type="text/javascript" src="/console/scripts/position.js?5.1.2"></script>
<script language="JavaScript" type="text/javascript" src="/console/scripts/utils.js?5.1.2"></script>
<script language="JavaScript" type="text/javascript" src="/console/scripts/progress.js?5.1.2"></script>
<script language="JavaScript" type="text/javascript" src="/console/scripts/ieflicker.js?5.1.2"></script>
<script language="JavaScript" type="text/javascript" src="/console/scripts/events.js?5.1.2"></script>

You could use developer tools on the web browser, and capture the Javascript text, and send me the files, and I could set up a web site with that HTML, and see if I can't get an upload.
I don't know if that opens up a new can of worms or not...you make it sound so simple, and yet... ;^)

I think I might be able to play with Fiddler over the weekend and see if I can come up with at least something helpful.  I tried navigating to the "#" reference link, but didn't work.

For the 'data' that is submitted from the csv, I do see this in fiddler:
Content-Disposition: form-data; name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload"; filename="\\unc_path\filename.csv"
Content-Type: application/vnd.ms-excel

Open in new window

Sheesh - I don't know if I'm close or not.
After submitting save, adding the file info to the header, I got

Problem accessing /%23. Reason:
    Not Found

Haha.  I hope I don't break something. :^)
That sounds like an HTTP 404 error, which means "Resource not found".   Is this a Javascript error?

I found the Javascript that I was looking for, embedded in the HTML:

function oamSubmitForm(formName, linkId, target, params) {
        var clearFn = 'clearFormHiddenParams_' + formName.replace(/-/g, '\$:').replace(/:/g, '_');
        if (typeof window[clearFn] == 'function') {
            window[clearFn](formName);
        }
        var form = document.forms[formName];
        var agentString = navigator.userAgent.toLowerCase();
        if (agentString.indexOf('msie') != -1) {
            if (!(agentString.indexOf('ppc') != -1 && agentString.indexOf('windows ce') != -1 && version >= 4.0)) {
                window.external.AutoCompleteSaveForm(form);
            }
        }
        var oldTarget = form.target;
        if (target != null) {
            form.target = target;
        }
        if ((typeof params != 'undefined') && params != null) {
            for (var i = 0, param;
                (param = params[i]); i++) {
                oamSetHiddenInput(formName, param[0], param[1]);
            }
        }
        oamSetHiddenInput(formName, formName + ':' + '_idcl', linkId);
        if (form.onsubmit) {
            var result = form.onsubmit();
            if ((typeof result == 'undefined') || result) {
                try {
                    form.submit();
                } catch (e) {}
            }
        } else {
            try {
                form.submit();
            } catch (e) {}
        }
        form.target = oldTarget;
        if ((typeof params != 'undefined') && params != null) {
            for (var i = 0, param;
                (param = params[i]); i++) {
                oamClearHiddenInput(formName, param[0], param[1]);
            }
        }
        oamClearHiddenInput(formName, formName + ':' + '_idcl', linkId);
        return false;
    }

Open in new window

%23 = # in URL encoding.
Without CSS, this is what I see:

User generated image
If I attach a file, and then click on the Save link, then I get this error:

User generated image
.jsf extensions are related to the Java FacesServlet.  It looks like it is trying to show a Home page.
Well that's a pretty good representation of the site. :^)
Are you using IE?  When I ran it from chrome it looked more like that.  IE showed the save button on the right and uses 'browse' instead of 'choose file'.

I thought index.jsf was the home page. :\
I don't like IE, so I always use Chrome as my browser of choice.

The anchor tags have an href="#", which means scroll to the top of the page:

Example:
<a href="#" onclick="var cf = function(){window.open('/console/help/Vg_securemail_console_help.htm', 'VgSMConsoleHelp', 'height=500,width=700,status,toolbar=0,menubar=0,status=0,scrollbars,resizable'); return false;}

I would like to understand how that error comes up.  Is it a Javascript error?  Can you attach a screen shot?
I am wondering if "zdm" refers to this:

Voltage SecureMail Cloud
https://www.voltage.com/vsn/login-pages.htm

Zero Download Messenger (ZDM);
Indeed it does.  We are using their secure mail product.  
We currently have to manually append to this csv file to lock a message.
This would allow the customer to get immediate message locking rather than wait on a tech to see the email and respond...

Which error did you want a screenshot of?
The /%23 not found error.
Sure, it was kind of bland, but I'll upload it...
Oh, I looked at the code again, and it was one attempt I had made to change the uri path to simply "#".
When I used the debugger to view the site's path value, it was "%23" - that must be where that came from...
Yeah, it's no longer appearing...was apparently just a mistake I made yesterday testing.
Was trying to use the .navigate with the useragent and the data I saw from fiddler.
Clearly I don't know what I'm doing...

 WebBrowser1.Navigate("#", "_self", Nothing, UserAgent & vbCrLf & "Content-Disposition: form-data; name=" & Chr(34) & "zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload" & Chr(34) & "; filename=" & Chr(34) & "\\UNCpath\lockfile.csv" & Chr(34) & vbCrLf & "Content-Type: application/vnd.ms-excel")
This is why the %23, and I tried the .navigate with the suggestion, but it didn't seem to work - went back to the first service page. :\
http://social.msdn.microsoft.com/Forums/en-US/11459b7a-1ee1-4a54-a4fb-76967ee22c96/webbrowser-control-how-to-navigate-to-an-anchor-new-uri-conversion-issue-23?forum=netfxcompact

Seems like my problem now is that I can navigate using the useragent, but I can't "click" with it...
Would that be the first time that you navigate to a URL?  I would think that you need to send it on every navigate.  Another possibility might be to create a custom WebBrowser class, and in the BeforeNavigate, set the user agent.

public class ExtendedWebBrowser : WebBrowser
{
    bool renavigating = false;

    public string UserAgent { get; set; }

    public ExtendedWebBrowser()
    {
        DocumentCompleted += SetupBrowser;

        //this will cause SetupBrowser to run (we need a document object)
        Navigate("about:blank");
    }

    void SetupBrowser(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        DocumentCompleted -= SetupBrowser;
        SHDocVw.WebBrowser xBrowser = (SHDocVw.WebBrowser)ActiveXInstance;
        xBrowser.BeforeNavigate2 += BeforeNavigate;
        DocumentCompleted += PageLoaded;
    }

    void PageLoaded(object sender, WebBrowserDocumentCompletedEventArgs e)
    {

    }

    void BeforeNavigate(object pDisp, ref object url, ref object flags, ref object targetFrameName,
        ref object postData, ref object headers, ref bool cancel)
    {
        if (!string.IsNullOrEmpty(UserAgent))
        {
            if (!renavigating)
            {
                headers += string.Format("User-Agent: {0}\r\n", UserAgent);
                renavigating = true;
                cancel = true;
                Navigate((string)url, (string)targetFrameName, (byte[])postData, (string)headers);
            }
            else
            {
                renavigating = false;
            }
        }
    }
}

Open in new window

VB.NET:

Public Class ExtendedWebBrowser
	Inherits WebBrowser
	Private renavigating As Boolean = False

	Public Property UserAgent() As String

	Public Sub New()
		AddHandler DocumentCompleted, AddressOf SetupBrowser

		'this will cause SetupBrowser to run (we need a document object)
		Navigate("about:blank")
	End Sub

	Private Sub SetupBrowser(sender As Object, e As WebBrowserDocumentCompletedEventArgs)
		RemoveHandler DocumentCompleted, AddressOf SetupBrowser
		Dim xBrowser As SHDocVw.WebBrowser = DirectCast(ActiveXInstance, SHDocVw.WebBrowser)
		AddHandler xBrowser.BeforeNavigate2, AddressOf BeforeNavigate
		AddHandler DocumentCompleted, AddressOf PageLoaded
	End Sub

	Private Sub PageLoaded(sender As Object, e As WebBrowserDocumentCompletedEventArgs)

	End Sub

	Private Sub BeforeNavigate(pDisp As Object, ByRef url As Object, ByRef flags As Object, ByRef targetFrameName As Object, ByRef postData As Object, ByRef headers As Object, _
		ByRef cancel As Boolean)
		If Not String.IsNullOrEmpty(UserAgent) Then
			If Not renavigating Then
				headers += String.Format("User-Agent: {0}" & vbCr & vbLf, UserAgent)
				renavigating = True
				cancel = True
				Navigate(url.ToString(), targetFrameName.ToString(), DirectCast(postData, Byte()), headers.ToString())
			Else
				renavigating = False
			End If
		End If
	End Sub
End Class

Open in new window

Hmm, that seems promising.  I'll try to implement it... haha.
Would I need to move my documentcompleted sub into there as well?
No, your code could stand as it is, since you are just extending the WebBrowser class to add additional behavior.  That class would have a UserAgent property, that you set once, and would be passed to the web site every time before the control needed to navigate.
BTW, what version of Visual Studio are you working with?
2010
SHDocVw.Webbrosser is not defined - I need to add reference to that dll, yes?  Cause that's what I'm doing unless you tell me otherwise. :^)
And I change my designer to
Me.Webbrowser1 = New ExtendedWebBrowser, yes?
I don't really see much of a change, other than I did remove the user agent from my doccompleted sub and that's working...

Here's my sub - maybe I've simply set something up bogus...

Private Sub WebBrowser1_DocumentCompleted(sender As Object, e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
        If e.Url.AbsolutePath.ToLower.Contains("login") Then Exit Sub 'still at the logon page
        If ServiceTabClicked Then
            If ZDMConfigTabClicked Then
                If SaveButtonClicked Then
                    'read form to find 'success' or 'error/else' 
                    'todo: once file successfully uploaded, form displays results in red/green
                Else
                    Dim filename As String = "\\URLPath\LockFile.csv"
                    Dim browseButton As mshtml.HTMLInputElement = helper.GetHtmlElementById(Of mshtml.HTMLInputElement)("zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload")
                    browseButton.setAttribute("filename", filename) 'something I've tried - doesn't work
                    Dim SaveButton As mshtml.HTMLAnchorElement = helper.GetHtmlElementById(Of mshtml.HTMLAnchorElement)("zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig")

                    Dim doc As HtmlDocument = WebBrowser1.Document
                    For i As Integer = 0 To doc.Forms.Count - 1
                        ' must be declared inside the loop because there's a closure
                        form = doc.Forms(i)
                        If form.GetAttribute("enctype").ToLower() <> "multipart/form-data" Then Continue For
                        form.AttachEventHandler("onsubmit", AddressOf OnSubmit)
                        ' form.AttachEventHandler("onclick", AddressOf OnSubmit)
                        form.SetAttribute("hasBrowserHandler", "1")
                        'have also found this elsewhere as form.SetAttribute("hasBrowserHandlers", "true") but doesn't seem to change anything
                    Next
                    SaveButtonClicked = True 'technically it's not at this point
                    'VoltageSite.Path += "#" 'this was throwing my %23 error
                    WebBrowser1.Navigate(VoltageSite.Uri.ToString) ' not working
                    'SaveButton.click() ' still throws the script error :(
                End If
            Else
                Dim zdmCfgTab As mshtml.HTMLInputElement = helper.GetHtmlElementById(Of mshtml.HTMLInputElement)("zdmSummaryDetailsSubview:relContentViewpc1:configZDMTabbedPane.2")
                zdmCfgTab.click()
                zdmCfgTab = Nothing
                ZDMConfigTabClicked = True
            End If
        Else
            VoltageSite.Path = "console/services/zdm/index.jsf"
            WebBrowser1.Navigate(VoltageSite.Uri.ToString) ', "_self", Nothing, UserAgent)
            'WebBrowser1.Navigate(VoltageSite.Uri.ToString)
            ServiceTabClicked = True
        End If
    End Sub

Open in new window

Which edition of Visual Studio 2010 do you have?  Do you have the ability to record Coded UI tests?

How to: Create a Coded UI Test
http://msdn.microsoft.com/en-us/library/dd286681(v=vs.100).aspx

Using Visual Studio Premium or Visual Studio Ultimate, you can create a coded UI test that can test that the user interface for an application functions correctly
If I don't, I can probably get permissions to install another edition...I'll check and get back to you.
Thanks.
Well apparently 'permission' will be very hard to come by. :(
Nonetheless, I'm still testing.  I'm going to run through fiddler once more and try to build a new project from the ground up using what I find there.  Hopefully the new/fresh perspective will help.
I was curious what edition you were using after some out-of-the-box thinking.  With Coded UI unit tests, you can record a test, generate some code, and play it back with different arguments, which might get you what you need.
I have VS 2010 Professional :(
I've walked through recreating the entire thing from scratch...though I didn't use mshtml this time.
Everything works up until that point. :(

In Fiddler, I can see that it's setting the input, type=file, value to the path of the csv.
Yet, when I try to use setattribute against that, it remains blank.  I'm guessing because that field is essentially 'locked' unless using the browse button, so I'm wondering if I'm back to trying to track down the handle of the file dialogue...
Yep, once I programmatically clicked the browse button and selected the file, it's holding the "value" attribute... ugh.
Apparenlty this is 'by design'... http://www.cs.tut.fi/~jkorpela/forms/file.html#value
<sigh>
"Yet, when I try to use setattribute against that, it remains blank."
Can you explain what you mean by that?  

You can't set the value attribute for the <input type="file">, since it is a security violation.  This whole exercise has been detecting the submit, and posting the file.  I am lost why that didn't work as expected.
I think I know why - I had previously been focused on grabbing the button ID.
Re-reading that site I've modified that to grabbing the correct form ID and now it's working through the loop and located the FILE element...stay tuned.
I don't know if I'm more hard-headed or more ignorant, but I think much of this is starting to make more sense to me...

So, here's what I'm seeing now:
Still throws the script error (window.external) on the button click, but I don't know how the useragent can be added to a click event (like we had with the navigate method).

It runs through the GetValusFromForm - it does locate the "FILE" element, but obviously it's blank.
But it includes several of the other settings that it's passing.
It returns to the event handler, and sets the file (I changed this to "filename" since it's actually showing in fiddler as filename).

Right before postData.Submit(), I examine the data.
I have 8 values (the aforementioned settings) listed in PostData.values (count=8).
I have a files (count=1) property as well.
For PostData.Files(0) I have
name:"zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload"
value: ""

I would have expected that to be the path of the file... :(
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
As it continues through to GetEncodedPostData(),
headerTemplate is equal to
"Content-Disposition: form-data; name="{0}"; filename="{1}"  Content-Type: application/octet-stream    "

Since the file path was empty, I forced it in that same routine, and it got to the point where it read the file and added it to the memStream, but I couldn't tell once it hit the navigate if it worked. (Hope that makes sense)
Tried hard coding it in there, but it didn't seem to take the file.
I'll run through again after I grab a bite and turn fiddler back on to try to see if I can find the difference.

Meanwhile, do you know of a way to have the .Click() procedure include the UserAgent?  Not sure if that sorts it, or if I need to ensure that it's added in this additional class...just thinking outloud.
"but I couldn't tell once it hit the navigate if it worked. (Hope that makes sense)"

There are two indications that I could think of:

1) Error
2) The file didn't upload
Running it through fiddler, I have 4 warnings:
Content-length mismatch; Request Header indicated 17,185 bytes, but client sent 0 bytes (twice)
Failed to obtain requestbody.System.IO.InvalidDataException The request body did not contain the specified number of bytes.  Got 0, expected 17185 (twice)
But, fidder does show the same body info:

Content-Disposition: form-data; name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:msglockfileupload"; filename="My CSV File!"
Content-Type: application/octet-stream
I'm finding the reference for hasBrowserHandler to be hasBrowserHandlers and instead of a value of 1, examples show a value of "true".
I'm not sure if setting attributes for both versions is a bad idea or not...so I've been flip-flopping them.
Not sure if that's eliminated my script error, or my change to
form.RaiseEvent("onsubmit")...  
But, running fiddler again, it almost seems like it's only pushing part of the file.  The details of that field don't scroll all the way down as if it's been truncated.  I'm thinking maybe it has something to do with that error I'm seeing repeatedly about Content-length mismatch...
Looking through the working session vs non-working session today.
I see the definition passed for content-type was application/octet-stream, but the
working session shows it as "vnd.ms-excel", so I've changed that in the code.

These other things, I'm unsure of, but pointing out what I see as far as differences:
Obviously these lines are different, but I think they're unique per session:
------------------------------8d183179e024d12
However, I did see the automated version had one additional hyphen, so I shortened that in the code.

The non working session has an empty/line feed at the top.

Both have 'form-data' for defaultDomain, msgFormat, sessionTimeout, identityTimeoutPolicy,
identityTimeout, and identityTimeoutUnit - values all correct.

Removing that, they both have
Content-Disposition: form-data; name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm_SUBMIT"; (value of 1)

but these are in different places within the session data (not sure if that matters?)

The working session has one additional form-data info, that I don't see in the non-working session:

Content-Disposition: form-data; name="zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:_idcl"

zdmSummaryDetailsSubview:relContentViewpc1:tabZDMService:tabZDMServiceSettings:zdmDetailsForm:saveZDMSessionConfig
-----------------------------7de1684362508--


Finally, this may be nit-picking, but the working session shows this line:
Content-Disposition: form-data; name="javax.faces.ViewState"
The non-working has a trailing semi-colon (;).
Oh, and Fiddler is now down to 2 complaints, instead of 4 about the content length mismatch...
Just let me know when I can help, because you are outside the boundaries now...
I'll just go ahead and close this one.  You've helped me a lot.  Thanks!