Http Client Protocol Issues

If you use any of these solutions, Please let me know so I can track if any of this is useful to you! Thanks! This is an area to share observations I have made working with Http Client Protocols and the associated technologies. I currently work for the Microsoft team that supports the WinInet, WinHTTP and System.Net API's and classes associated with these technologies. This is not a replacement for Microsoft Support, but an area to discuss these technologies. These postings are provided "AS IS" with no warranties, and confer no rights. Use of included code samples are subject to the terms specified at Microsoft - Information on Terms of Use

  • How to iterate over contained controls to set a common event handler (Click) in .NET

    The requirement was to assign a click event handler to all Label Controls contained in a .NET User Control.  This is a simple technique but I found there was no good documentation on how to do this. 

    To illustrate how to do this, I created a simple Windows Form application in C# and added a new Windows Forms User Control to the project.  I called it AutoLabelClickControl.  I placed a label right on the control called ‘Control Label’.  I added a panel with a label on it called ‘Panel Label’ and then placed a panel contained in the first panel and put a label on it called ‘Panel in a Panel Label’.

    image

    The idea now is that when the control ‘AutoLabelClickControl’ is loaded, code should hook up a common click event handler for all labels.  Add a _Load event handler to your Control (not the Form) using the wizard and let’s add the necessary code:

    // This is the Common Event Handler for all Label Clicks
    private void LabelClickEventHandler(object sender, EventArgs e)
    {
        // Ensure we can typecast correctly!
        if (sender is Label)
        {
            // Do whatever you want... in this case show the Name of the control clicked
            Label aLabel = sender as Label;
            MessageBox.Show(aLabel.Name);
        }

    }

    // Walk the collection recursively and find Label Controls and wire them up
    private void FindLabelControlsInContainerAndAssignEvent(Control.ControlCollection aCollection)
    {
        foreach (Control aControl in aCollection)
        {
            // does this control contain other controls?
            if (aControl.Controls.Count > 0)
            {
                // call this function recursively!
                FindLabelControlsInContainerAndAssignEvent(aControl.Controls);
            }

            // wire up our Common Event Handler
            if (aControl is System.Windows.Forms.Label)
            {
                aControl.Click += new System.EventHandler(LabelClickEventHandler);
            }

        }
    }

    private void AutoLabelClickControl_Load(object sender, EventArgs e)
    {
        // When loading wire up the events for all contained Labels
        FindLabelControlsInContainerAndAssignEvent(this.Controls);
    }

     

    And here is the test!

    image

    Note:  Although you may think you cannot have a label contained in a label control, you can actually do this if you edit the Designer code (not recommended) and that is why I did not have an ‘else’ statement between these:

    // does this control contain other controls?
            if (aControl.Controls.Count > 0)
            {
                // call this function recursively!
                FindLabelControlsInContainerAndAssignEvent(aControl.Controls);
            }

    // LOOK – NO ELSE STATEMENT… IN THEORY A LABEL COULD CONTAIN A LABEL BUT YOU WOULD HAVE TO HACK IT IN

            // wire up our Common Event Handler
            if (aControl is System.Windows.Forms.Label)
            {
                aControl.Click += new System.EventHandler(LabelClickEventHandler);
            }

    It would be EXTREMELY weird for someone to do this but…  I have been around a while so that is why I wrote that code that way :-)

    Note:  It is up to you to apply proper error checking and try/catch blocks to this code.  See the terms of use in my Blog header.

     

    Please drop me a note if you find this useful!

  • WinHttpWriteData will fail with ERROR_INVALID_PARAMETER if you are trying to send more data than specified in WinHttpSendRequest on Windows 7 (by design)

    There are new checks in Windows 7 to prevent you from doing bad things with the WinHttp APIs.  In the sample code below, I do not get an error pre-Windows 7 but on Windows 7 the WinHttpWriteData call results in bResults being 0 and GetLastError() returns 87 (ERROR_INVALID_PARAMETER):

    LPSTR pszData = "WinHttpWriteData Example";

    LPSTR pszMoreData = "WinHttpWriteData Example";

        // Send a Request.
        if (hRequest)
            bResults = WinHttpSendRequest( hRequest,
                                           WINHTTP_NO_ADDITIONAL_HEADERS,
                                           0, pszData, strlen(pszData),
                                           strlen(pszData), 0);

        // Write data to the server.
        if (bResults)
            bResults = WinHttpWriteData( hRequest, pszMoreData,
                                         strlen(pszMoreData),
                                         &dwBytesWritten);

    Looking at a Network Monitor trace, you see something very strange in the HTTP traffic pre Windows 7:

    You see a POST - Http: Request, POST /ReadAll.asp ContentLength:  24 and the text I sent: HTTPPayloadLine: WinHttpWriteData Example

    and then the successful return code from the server -Http: Response, HTTP/1.1, Status Code = 200, URL: /ReadAll.asp

    But the following bunch of Data in the next frame! - HtmlPayload: WinHttpWriteData Example

    So the POST is being sent, then more data after the initial POST and 200 OK response from the server!

    Looking at the headers, you can see the Content-Length is set to 24 (this is what you set in WinHttpSendRequest the dwTotalLength = strlen(pszData)), and that is why the server returns 200 (because as far as it is concerned you told it you were sending only 24 bytes of data and that was what your client had sent).  However the extra 24 bytes of data gets sent by the WinHttpWriteData call!

    For Windows 7, a check was put in place internally to prevent you from making such a bad protocol violation.  Now WinHttpWriteData will return 0 and GetLastError() will indicate 87 which means that you have passed invalid data.  In this case the length of the data you intend to write exceeded what you set as the content length in WinHttpSendRequest!  The fix is for you to correct your buggy code and indicate the correct content length by setting the dwTotalLength parameter correctly in the call to WinHttpSendRequest.  In this case I can change the code to this:

     

    // Send a Request.
        if (hRequest)
            bResults = WinHttpSendRequest( hRequest,
                                           WINHTTP_NO_ADDITIONAL_HEADERS,
                                           0, pszData, strlen(pszData),
                                           strlen(pszData)+ strlen(pszMoreData), 0);

    Please drop me a comment if you found this helpful!

    Reference: http://msdn.microsoft.com/en-us/library/aa384110(VS.85).aspx

  • How To: Walkthrough Using HttpListener as an SSL Simple Server

    There seems to be no simple end-to-end walkthrough for creating a simple HttpListener based SSL Listener that includes how to configure the computer by generating and installing a Certificate for this listener.

    Basic concepts covered:

    • Write the listener code to listen for SSL requests
    • Generate a self signed Certificate
    • Configure the machine to use a Certificate

    Write the listener code to listen for SSL requests

    Here is the documentation for HttpListener: http://msdn.microsoft.com/en-us/library/system.net.httplistener.aspx

    Copy the code from the above page and paste it into a new C# Console application right under the Main() function.  Then add the System.Net namespace right under using System.Text;: using System.Net;

    Now from the Main function call the function you pasted in passing the prefixes you wish to listen to.  In my case I will listen to http requests on port 8080 when the URI ends with the ‘listener’ prefix and https requests on port 8081 again with the ‘listener’ prefix.  Every time a request is handled you need to restart the program.

    static void Main(string[] args)
           {
               string[] thePrefixes = { "http://+:8080/listener/", "https://+:8081/listener/" };
               SimpleListenerExample(thePrefixes);
           }

    The code should now compile and you can run in the debugger.  I then used Internet Explorer to try and reach the listener: http://jsan17708317:8080/listener/ and this succeeds!  However the https site does not succeed: https://jsan17708317:8081/listener/".

    The failure occurs because we have not set up a certificate for the listener to use for SSL traffic (https).  This brings us to the next two items.

    Generate a self signed Certificate

    First of all, if this is a production environment, you will want to get a server certificate issued by a Trusted Root authority.  This Post illustrates how to create a self signed certificate to test your code before you get a certificate from an authority.

    This is the documentation for MakeCert.exe : http://msdn.microsoft.com/en-us/library/bfsktky3.aspx.  You can use this program to generate a self signed Server Certificate for the application.

    Using Visual Studio 2008 you can simply use the Visual Studio 2008 command prompt from the program menu to access MakeCert.exe.

    The syntax would look something like this (replace the name of the machine ‘jsan17708317’ with yours, and see the makecert documentation for an explanation of the arguments used here):

    makecert.exe -sr LocalMachine -ss MY -a sha1 -n "CN=jsan17708317" -sky exchange -pe -eku 1.3.6.1.5.5.7.3.1

    The command should return ‘Succeeded’.

    Now you need to ensure the Root Authority is trusted.  To do this from the same command prompt you used above, type MMC.  Then choose ‘File’, ‘Add/Remove Snap-in…’

    image

    and then hit the ‘Add’ Button, select ‘Certificates’.  Click the ‘Add’ button…

    image

    Select ‘Computer account’ and then click ‘Next >’, ‘Finish’, ‘Close’ and ‘OK’.

    image

    You should them be able to expand the Tree on the left to show the Personal Certificates and find the one you just added (mine is the only one shown)…

    image

    Double click on your certificate and you will see something similar to this:

    image

    You need to resolve the Red X problem (the certificate cannot be verified up to a trusted root).

    Click on the  ‘Certification Path’ tab in this dialog, and the problem certificate (Root Agency) then on ‘View Certificate’.

    image

    You will see the problem is that this Certificate is not in the ‘Trusted Root’ store. 

      image

    To get it into the store, click the ‘Details’ Tab and then the ‘Copy to File…’ button

    image

    Hit ‘Next >’ twice then the ‘Browse’ button and save the file to your local Documents directory (name it something like ‘RootCert’) then finish up the wizard until you have successfully exported the certificate.  Now we will import the certificate into the trusted root store.

    Expand the ‘Trusted Root Certification Authorities’ tree and right click on ‘Certificates’

    image 

    From the context menu select ‘All Tasks…’ then ‘Import…’ and navigate to where you saved the ‘RootCert’ and import the certificate by completing the Wizard.

    Now you should be able to go back to your Server Certificate in the Personal Store and see that there are no problems with the Certificate (leave this dialog up for the next step):

    image

    The final step here is to get thumbprint hash for use in the next section.

    Click on the ‘Details’ tab, and scroll down to the ‘Thumbprint’ Field in the dialog and click on it.  Then sweep out the numbers below with your mouse (don’t miss any) and copy those numbers with the Ctrl+C key combination.

    image

    Open notepad and paste these numbers into it for use in the next step.

     

    Supplemental note:

    If you have IIS installed, I like the SSL Diagnostics tool instead of Makecert: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=cabea1d0-5a10-41bc-83d4-06c814265282 Instructions for generating the certificate are in the help file for this tool.  Once you run this tool it creates a temporary certificate and installs it in the local computer Trusted Root Authorities Certificate store for you!

    Configure the machine to use a Certificate

    Finally you need to configure the machine to associate the certificate you just created, with the port and ip address that the HttpListener is listening on for https requests.

    You need to obtain httpcfg.exe to do this next step.  Search Technet for the latest version of this tool for your OS.  For example, XP SP2 tools have it (Note: this is an optional component for XP so you will need to do a COMPLETE install to get this tool): http://www.microsoft.com/downloads/details.aspx?FamilyId=49AE8576-9BB9-4126-9761-BA8011FABF38&displaylang=en

    You will need to run httpcfg from the command line and supply the Thumbprint hash you obtained in the previous section but you need to remove the spaces between the numbers.  In my example I removed the spaces and the hash is: f4bb35424190e006e5476e97430c5b8136ee5da5

    This is the httpcfg command I ran to associate the cert for all IP addresses associated with my machine and port 8081 (/i 0.0.0.0:8081):

    C:\Program Files\Support Tools>httpcfg set ssl /i 0.0.0.0:8081 /h f4bb35424190e006e5476e97430c5b8136ee5da5

    When you run this you will see the result: HttpSetServiceConfiguration completed with 0.

    Now double check the SSL bindings for this computer:

    C:\Program Files\Support Tools>httpcfg query ssl
        IP                      : 0.0.0.0:8081
        Hash                    : f4bb35424190e0 6e5476e9743 c5b8136ee5da5
        Guid                    : {00000000-0000-0000-0000-000000000000}
        CertStoreName           : (null)
        CertCheckMode           : 0
        RevocationFreshnessTime : 0
        UrlRetrievalTimeout     : 0
        SslCtlIdentifier        : (null)
        SslCtlStoreName         : (null)
        Flags                   : 0
    ------------------------------------------------------------------------------

     

    You can verify that the IP shows up and the Hash you entered (note that weirdness where some of the 0 characters do not appear… I don’t know why that is).

    Now you should be able to run the above Test application and hit the SSL Site using Internet Explorer.  In my case the URL is: https://jsan17708317:8081/listener/

    Troubleshooting

    If the app does not work, first verified you did each step above correctly (did not leave out something or some step).

    Verify your certificate appears with the IP and Hash fields the correspond with what you are listening for and that the Hash is the hash you obtained previously for the certificate.

    Check with httpcfg query ssl and see if there are other listeners that may conflict for that same port.  Delete conflicts with the command http delete ssl /i x.x.x.x:8081  (replace x.x.x.x with any IP address you see that are listening on the same port).

    Supplemental Information

    This is a simple example.  You probably do not want to stop listening after you process one request.  You will need to provide a way to gracefully close the listener and handle the requests on worker threads.

    You can specify a specific IP range in HttpCfg (search MSDN for HttpCfg syntax).  In my example you can use this to bind to the ip address 10.10.3.4:
    httpcfg set ssl /i 10.10.3.4:8081 /h f4bb35424190e006e5476e97430c5b8136ee5da5

    You cannot listen for http and https traffic on the same port.

     

    I hope this gets you started with your HttpListener project and will help you troubleshoot the initial setup of your code.  Please Drop me a note if you find this useful!

  • How to get a dump for a System.Net.WebException using DebugDiag (Debug Diagnostics)

    This information is available in the help file as well.  For more options please refer to the Help Documentation include with DebugDiag.

    Install the latest Debug Diagnostics from http://download.microsoft.com (search for Debug Diagnostics once on that site)

    Configure a crash rule for all w3wp.exe processes (if this dialog does not appear simply press the ‘Add Rule…’ button).

    image

    Select ‘A specific process’

     image

    Make sure you DO NOT check ‘This process instance only’ and type in the process we wish to monitor (in this case asp.net is running in w3wp.exe).

    image

    In the ‘Advanced Configuration Dialog’ select the 'Exceptions...’ button at the bottom of this dialog:

    image

    Choose ‘Add Exception…’.  Clicking this button will bring up the Configure Exception dialog.  Use this to configure actions for a specific type of first chance exception.

    image

    In the Configure Exception Dialog, Choose 'CLR (.NET) Exception'

    image

    For the 'Net Exception Type' field: (This will only be available when choosing the E0434F4D CLR (.NET) Exception code)   You must specify the exact name of the exception in this field.  This field is case sensitive.  In our case we want System.Net.WebException

    image

    In the ‘Action Type’ pull down specify ‘Full User Dump’ and you will be able to capture the state when we have the 401 (or other) WebException in the w3wp.exe process.

    image

    The ‘Action Limit’ by default is set to 1.  This means only one dump will be created.  If you wish to catch more than one dump, increase this number (usually 3 is sufficient).  Then hit OK and proceed by hitting the ‘Save & Close’ button (note you will see the configured CLR exception now).

    image

    Finally hit the ‘Next >’ button twice and then ‘Finish’.  The rule will be activated.

    You do not need to keep the UI up for DebugDiag.  If you do keep it up you will see the ‘Userdump Count’ increase with each exception you encounter (up to the limit you set previously) and the location of the dump is listed as well.

    image

    Finally to analyze the dump, you can use DebugDiag to analyze the script.  You can use the special script I developed here to get a good picture of what the problem may be.

    Please drop me a note and let me know if this Post helped you!

  • Troubleshooting ASP.NET - The remote certificate is invalid according to the validation procedure

    This error message is caused because the process is not being able to validate the Server Certificate supplied by the Server during an HTTPS (SSL) request.  The very first troubleshooting step should be to see if the server supplied certificate and every certificate in the chain is trouble free.

    Example 1 – Root Certificate only (self signed certificate in this case)

    Step 1 – Validate the certificate, any intermediate certificates and the root certificate

    One super handy and technical tool to help you do this first step is Internet Explorer.  Simply try to hit the same URL that your ASP.NET web application tries to hit when it gets this error.  For example, type in to the browser the path to the .asmx file and see what Internet Explorer says about the certificate.

    This would be a bad sign:

    image

    If Internet Explorer has certificate problems, chances are you will have problems with the HttpWebRequest (or Web Service) call as well.  The easiest fix is to install a valid certificate for the server, the root authority and all intermediate authorities.  Then go back and verify Internet Explorer can access the https site with no errors at all.

    If you continue to the site using Internet Explorer, sometimes you can diagnose the certificate problem by viewing the certificate.  In this example the problem is spelled out for me when I typed in https://jsanders4.

    image

    So in my case, it appears that the I simply need to install the certificate in the ‘Trusted Root Certification Authorities’ store.  So indeed I do this!

    image  image  image

    But I still got the certificate error…  To avoid a long discussion about this, the problem is simple.  I typed in https://jsanders4 but note the certificate is for the full domain name of this machine.  If I instead browse to https://jsanders4.northamerica.corp.microsoft.com then I get no certificate error.  Now that I am sure I can browse ok to the site with no certificate errors using Internet Explorer.

    You would continue to solve problems with the other Certificates in the Certificate chain by using Internet Explorer until they are all resolved.  For example, perhaps the certificate is expired, or the Intermediate Authority is not in the Intermediate Certificate Authorities store (a very common one).  Once you have resolved all errors with Internet Explorer your are only half way finished.  You need to get the same information into the Local Computer store.

    Step 2 – Troubleshoot the ASP.NET problem

    I try my ASP.NET application and try to access the same site.  I get this error: ‘The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.’

    But Internet Explorer was fine…

    The next step should be to get a System.Net trace.  To do this open the Web.Config for the troubled ASP.NET application and see if there is a <configuration> section in the file.  If there is, add the contents inside the <configuration> </configuration> tags from this blog: http://blogs.msdn.com/jpsanders/archive/2009/03/24/my-favorite-system-net-trace-configuration-file-dumps-process-id-and-date-time-information.aspx.  If it does not exist, add the entire contents before the closing tag of the Web.Config.  Now edit this line:   initializeData="System.Net.trace.log"  You must ensure that the Network Service account can write this .log so change this entry to a folder that the Network Service account can write to.  For example, I created a folder c:\mylogs and assigned the Network Service account FULL privileges.  Then changed this setting to initializeData="c:\mylogs\System.Net.trace.log"

    I ran the asp.net application and saw at the end of the log file (c:\mylogs\System.Net.trace.log) this information:

    System.Net Information: 0 : [0880] SecureChannel#14701405 - Remote certificate has errors:
        ProcessId=5700
        DateTime=2009-09-16T12:54:06.5718699Z
    System.Net Information: 0 : [0880] SecureChannel#14701405 -     A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.

        ProcessId=5700
        DateTime=2009-09-16T12:54:06.5718699Z
    System.Net Information: 0 : [0880] SecureChannel#14701405 - Remote certificate was verified as invalid by the user.
        ProcessId=5700
        DateTime=2009-09-16T12:54:06.5718699Z
    System.Net.Sockets Verbose: 0 : [0880] Socket#26833123::Dispose()
        ProcessId=5700
        DateTime=2009-09-16T12:54:06.5718699Z
    System.Net Error: 0 : [0880] Exception in the HttpWebRequest#31364015:: - The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
        ProcessId=5700
        DateTime=2009-09-16T12:54:06.5718699Z

    This is the key to this particular problem:  A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.

    However I thought I just added this certificate trough Internet Explore to my Trusted Root Authorities!  In reality I simply added it so the store of the logged on user.  ASP.NET is running in the local machine context.  To resolve this, start MMC (Windows key + R and type MMC) and add the following snap-ins:

    image image image

    And ensure you see the ‘Current User’ and ‘(Local Computer)’ Certificates listed (then hit OK):

    image

    In the console expand the ‘Current User’ Trusted Root store and you see I have the certificate stored there.  However expanding the ‘(Local Computer)’ trusted root, it is NOT there:

    image image

    Simply Copy (do not Drag and drop) the jsanders4 certificate from the Current User\Trusted Root store to the (Local Computer)\Trusted Root store and retest. 

    image image

    Success!

    Example 2 – Intermediate Certificate Authorities Involved

    This next example is a bit manufactured but illustrates a problem that I have had to help solve a few times.  The trouble shooting steps remain the same, but in this case there are one or more intermediate certificates.  This intermediate certificates should be in the ‘Intermediate Certification Authorities’ store to resolve this problem Take a look at the certificate chain for https://www.microsoft.com (you do this by clicking on the padlock icon in Internet Explorer and choosing the Certification Path tab:

    image 

    For the purpose of this example, assume that the three certificates that are not highlighted all have a warning icon next to them indicating a problem.

    Step 1 – Validate the certificate, any intermediate certificates and the root certificate

    Note: For Internet Explorer there actually was no problem with the certification path.  The four certificates show no warning icons.  For this example, let’s say that the ‘GTE CyberTrust Global Root’, ‘Microsoft Internet Authority’ and ‘Microsoft Secure Server Authority’ certificates were all missing from my ‘Current User’ stores.  The steps below are contrived from this assumption.

    To fix this I add the ‘GTE CyberTrust Global Root’ cert to the ‘Trusted Root Certification Authorities’ Store and the other two certificates to the ‘Intermediate Certification Authorities’ store of the Current User.  Test with IE again and Internet Explorer shows no problem after installing the certificates.  Next when I tested the ASP.NET application I got an error (because I did not add these certificates to the same stores in the (Local Computer) store).

    Step 2 – Troubleshoot the ASP.NET problem

    From the first example I was smart enough to copy the ‘GTE CyberTrust Global Root’ certificate to the ‘(Local Computer)’ trusted root store but I still have an error!

    The ASP.NET program now has an error: System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

    Again taking the System.Net trace you see an error towards the end of the file in these entries:

    Remote certificate has errors:
    A certificate chain could not be built to a trusted root authority.
    Remote certificate was verified as invalid by the user.
    The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

    This entry is the key: ‘A certificate chain could not be built to a trusted root authority.’  This means that a certificate before the Root Authority is not in the ‘(Local Computer) Intermediate Certification Authorities’ store.  Once the two intermediate certificates are copied to the Intermediate store from the Current User intermediate store the problem is solved!

    I hope this blog helped you get to the root of your problem.  If this was helpful please leave me a comment!

  • How to get the window handle for the Internet Explorer process you created (LCIE IE 8)

    One technique people use in accessibility applications is to kick off an application and then grab the Window handle of the process to hook it or do other processing.  With loosely coupled Internet Explorer (LCIE) this is a little more difficult that simply getting the handle of the iexplore.exe process you kick off.  The reason is that there is a broker IE process and this kicks of the actual IE instance.  Look at the links at the end of the article for more information how this works.

    The technique you can use is to iterate over the processes and wait for your expected IE instance to get a valid window handle.  If there are no other IE instances running the technique is relatively simple.

     Here is a simple console application you can debug and play with to investigate this technique.

     Full listing below (Copy Code):

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using System.Threading;

    using System.Diagnostics;

     

    namespace LCIE

    {

        class Program

        {

            static bool dumpIEWindows(Process [] processes)

            {

                bool abFoundHandle = false;

                if ((processes == null)||(processes.Length == 0))

                {

                    Console.WriteLine("No IE Processes found");

                }

                else

                {

                    foreach (Process nextProcess in processes)

                    {

                        if (nextProcess.MainWindowHandle == IntPtr.Zero)

                        {

                            Console.Write("IE process with Window handle set to 0: " + nextProcess.Id.ToString());

                            Console.WriteLine(".  This could be the Broker, or the windows is not created yet");

                        }

                        else

                        {

                            abFoundHandle = true;

                            Console.WriteLine("IE process with handle: " + nextProcess.Id.ToString() + ".  Window Title: " + nextProcess.MainWindowTitle);

                        }

                    }

     

                }

                return abFoundHandle;

            }

            static void Main(string[] args)

            {

                //TODO:  Trap exceptions and take appropriate action if there is an exception

     

                Process[] arrayOfProcesses;

                Process aProcess;

                // See if there are any iexplore processes already

                arrayOfProcesses = System.Diagnostics.Process.GetProcessesByName("iexplore");

     

                if (arrayOfProcesses.Length == 0)

                {

                    // create iexplore.exe process

                    aProcess = Process.Start("iexplore.exe");

                   

                    aProcess.WaitForInputIdle();

     

                    arrayOfProcesses = System.Diagnostics.Process.GetProcessesByName("iexplore");

                }

     

                // TODO:  Add some safety valve here to abort the loop after a certain amount of time

                // or after a few iterations.

     

                while (dumpIEWindows(arrayOfProcesses) == false)

                {

     

                        // no IE Windows with a handle so we need to wait for window creation.

                        // another possibility is that the iexplore broker process has started and it

                        // did not yet create the brokered process (see LCIE).

     

                        // Take a nap...

                        Thread.Sleep(500);

                        arrayOfProcesses = System.Diagnostics.Process.GetProcessesByName("iexplore");

                }

            }

        }

    }

     

     

    You can extend this example to find the exact window created in the event there are other iexplore.exe processes running before you kick off your process simply by getting a list of PIds (Process Ids) before you create your IExplore.exe process and compare that to the one that you get after you create the process.  The process without a NULL window handle will be the correct instance.

      

    Helpful links

    IE 8 LCIE:

    http://blogs.msdn.com/ie/archive/2008/03/11/ie8-and-loosely-coupled-ie-lcie.aspx

    Controlling and configuring LCIE:

    http://blogs.msdn.com/askie/archive/2009/03/09/opening-a-new-tab-may-launch-a-new-process-with-internet-explorer-8-0.aspx

    How to determine which IE tabs goes to which Iexplore.exe process when using Internet Explorer 8:

    http://blogs.msdn.com/askie/archive/2009/03/20/how-to-i-determine-which-ie-tabs-go-to-which-iexplore-exe-process-when-using-internet-explorer-8.aspx

     

     

    Please drop me a comment if you found this Blog useful!

     

  • Using HttpWebRequest with Credential Manager

    A little know fact is that the .NET framework will use the stored credentials in the Credential Manager when accessing a network resource if the credentials exist for that particular resource (host).  I intend to clear up how this functionality works for the HttpWebRequests (you could extend this to WebService calls as well).

    In Internet Explorer, you can access a website that challenges you for credentials by filling in a username and password when the dialog is presented and asks you for authentication.  If you check ‘Remember my password’ these credentials will be stored in protected storage (Credential Manager).  You can see the stored credentials by running this command: Control Keymgr.dll.  These credentials are stored in protected storage in each user’s My folder.  You can store these credentials by using control Keymgr.dll, by using the interface available in Internet Explorer or by using C++/C and the Credentials API’s (Such as CredWrite).  If you do not check ‘Remember my password’ then these credentials are not stored in Credential Manager and .NET will not be able to use these credentials.

    The .NET framework can use the currently logged on user’s credentials to authenticate but if you have a web site that requires different credentials you can use store these credentials in Credential Manager and .NET will use these credentials instead of the currently logged on user credentials.  To illustrate this I put together a real simple sample.

    The target web server in my sample is jpsandershv2003.  I only enabled Windows Integrated Authentication in IIS on that box.  I then created a User ID JPSGuest on that IIS machine.  Finally I wrote a real simple .NET console application to create an HttpWebRequest and get the response.

       HttpWebRequest aReq;

       HttpWebResponse aResp;

            

       aReq = WebRequest.Create("http://jpsandershv2003/") as HttpWebRequest;

       aReq.UseDefaultCredentials = true;

       aResp = aReq.GetResponse() as HttpWebResponse;

       aResp.Close();

    When you run this code it will first look in the credential manager first for the credentials for attaching to jpsandershv2003 and if found use those, otherwise the Currently Logged on User credentials will be used.  In my example since I was logged on as jpsanders to my machine I expect that the credentials used should be my domain credentials jpsanders.  I turned on logging and ensured the IIS log was logging the username and sure enough, jpsanders was authenticated in IIS.

    My next goal was to see if I could change this default behavior and make it log on using the jpsandershv2003\JPSGuest user credentials.  Using the command control keymgr.dll I was able to add the credential as a Windows logon credential:


     

     


    Now when I ran the console application I saw success in the IIS log!  I was logging in now as JPSGuest.

    Taking a fiddler (http://fiddlertool.com) trace of Internet Explorer also confirmed I was using the stored credentials and these were NTLM credentials. 

    Domain: jpsandershv2003
    User: JPSGuest
    Host: JPSANDERS3

    Can I store credentials and have it use Kerberos?  In theory yes, simply type domain based credentials in.  However I could not test this because I only have one logon to my domain and you cannot save your currently logged on credentials in the Credential Manager. 

    What about basic and digest?  NO, this would be a huge security hole.  Can you tell me why (it should be obvious)?

    Note that the credentials used are independent of the PORT used.  So if I change the server to bind HTTP traffic on port 8089 then change my code to access http://jpsandershv2003:8089 the code will still access the web site using the stored credentials.

    To revert back to the interactive user, simply delete the credentials stored for jpsandershv2003.

    Further observations

    What is the second radio button for (A Web site or program credentials)?  This is for programs that are writing against the credentials API directly.  Some examples of this are Terminal Server (RDP) and Windows Live credentials.  Here are some great links about the APIs:
    http://msdn.microsoft.com/en-us/library/aa374789(VS.85).aspx
    http://msdn.microsoft.com/en-us/library/aa480470.aspx

    NOTE: Because the credentials are stored in the context of the logged on user, this technique will not work for non interactive user accounts such as the service accounts and ASP.NET applications.

     

    Please send me a quick note if you found this Blog helpful!

     

     

  • Using netsh to analyze WinInet problems in Windows 7

    There are some powerful tracing tools built into Windows 7 that can help you diagnose Internet Client issues.  This walk though shows you now to enable tracing when accessing a web site using Internet Explorer 8 or other WinInet based applications.

    Network tracing in Windows 7 (ref: http://msdn.microsoft.com/en-us/library/dd569136(VS.85).aspx)  uses Event Tracing for Windows (ETW) (ref: http://msdn.microsoft.com/en-us/library/bb968803(VS.85).aspx) to enable you to see what is happening at the WinInet, Winsock, WebIO and TCP level.  By looking at a simple example, you can see how this may be useful when analyzing http traffic.

    1. Open an elevated command prompt by going to ‘Start’, ‘All Programs’, ‘Accessories’, Right click on ‘Command Prompt’ and choose ‘Run as administrator’
    2. Type this into the command prompt: netsh trace start scenario = InternetClient level=5 tracefile=protocols.etl
    3. Open http://www.microsoft.com/protocols in Internet Explorer
    4. After that page has rendered, stop tracing by typing into the command prompt window: netsh trace stop
    5. Download and install the latest network monitor from http://www.microsoft.com/downloads/en/results.aspx?freetext=netmon&displaylang=en&stype=s_basic (version at the time of this publication is ‘Microsoft Network Monitor 3.3’)
    6. Open the trace file in netmon from c:\windows\system32\protocols.etl
    7. Install the full parsers in netmon by going to ‘Tools’, ‘Options’ then click on the ‘Parser Profiles’ tab, click on the ‘Microsoft Windows’ then choose ‘Set As Active’ and then ‘OK’ (see image below)

    image

    You should now be able to see WinInet, TCPIP and other traffic in the ‘Network Conversations’ window.  To find the conversations with ‘protocols’ in the description, type this in the display filter: description.contains("protocols") and hit the Apply icon.

    image

    Here is where you can start to see the power of using netmon to analyze this data.  You should see a description similar to: WinINet_MicrosoftWindowsWinINet:WININET_SENDREQUEST_START - Request : 0x078B3EF0, AddressName : http://www.microsoft.com/protocols

    image

    If you right click on this and choose ‘Find conversations’, ‘NetEvent’ you will see that the ‘Network Conversations’ is now highlighting the conversation associated with that request. 

    image

    Remove the Display filter now so that you are not filtering by the description anymore.  Once you do this you can see where there may be Cookie information, TCP information,  WinInet connection information, TCP information, Http response information and other internal WinInet information.

     image

    Although there seems to be a mountain of information, you can use this to see what is different between a successful and failed case fairly quickly.  The Description column is pretty good at describing what each of the frames are showing you.  This will even let you see activity down to the WinSock level!

    Try clicking on the other WinInet traffic links in the Network Conversations pane to see what other WinInet activity is occurring.  You will see images being retrieved among other activity.  This will give you  very detailed look at what WinInet is doing during the time you took the trace.

    Other tools are available to analyze the HTTP traffic.  Fiddler (ref: http://fiddlertool.com ) is an excellent tool that can also allow you to see SSL content as well as performance metrics.  You could also use Netmon directly to see network traffic.  ETW tracing lets you see into what WinInet is doing however and will allow you a finer grained troubleshooting view along with the network traffic.  You can use ETW tracing as another powerful tool in your arsenal to help with HTTP protocol troubleshooting.  You can use ETW tracing to analyze HTTPS traffic as well.  You cannot see all the content in clear text but you will notice the buffer in the WININET_HTTP_RESPONSE_HEADERS: HTTP Response Headers responses will show the beginnings of the data and show the headers in clear text.

    Let me know if this post was useful to you!

  • Writing an ISAPI Filter with .NET (Managed Code) will result in poor performance of you ASP.NET web applications

    David Wang wrote an excellent post about this in 2006:  http://blogs.msdn.com/david.wang/archive/2006/02/09/Can-I-write-an-ISAPI-Filter-using-Managed-Code.aspx

    This includes writing ISAPI code that even USES managed components.  Anything that needs to load the CLR will result in poor performance of the ISAPI and worse, result in ASP.NET not functioning correctly.

    ASP.NET loads the CLR when it initializes.  During this loading it does many things to configure the CLR.  One example is that it creates on GC thread for each processor on the system.  It also sets up some defaults for the thread pools.  When you write your ISAPI in managed code or include a COM Visible .NET component through COM in your code, your ISAPI loads the CLR.  Since only one CLR can be loaded per process, ASP.NET will not create and configure the CLR and you are left with a severely crippled system.

    This problem can also occur if you write an ISAPI Extension.  Your Extension could load the CLR runtime if you hit a static file first, initialize the CLR in the ISAPI Extension by using .NET or a COM Visible .NET object and then hit your ASP.NET application after this initialization.

    You can clearly see this in a multi processor machine if you create a filter that uses a Managed code COM component.  Without your filter in place, hit an aspx page on the site and use DebugDiag to take a dump of the w3wp.exe process.  Then analyze this dump with DebugDiag.  You will see a GC thread initialized for each processor core installed on your server.  On my server I have a quad proc CPU and I see 4 threads with this in the call stack: mscorwks!SVR::gc_heap::gc_thread_function.

    Then I created a simple ISAPI filter from one of the existing Platform SDK samples and simply instantiated a COM Visible .NET component.  I installed the ISAPI filter and re-ran my test.  As expected, I saw only 1 thread with mscorwks!SVR::gc_heap::gc_thread_function.

    This problem manifests itself not only with the GC threads but all the information set by the processModel (see http://msdn.microsoft.com/en-us/library/7w2sway1.aspx).  In theory you could come up with some sort of scheme to read the machine.config file and parse and set this information, but this is untested and definitely not supported (read: you are on your own).

    If you really are in need of writing Managed Code, you should investigate the Managed Pipeline in IIS 7 and use that http://msdn.microsoft.com/en-us/library/ms227673.aspx.  There are a couple of other workarounds you could explore like out of proc components etc... but these would all create performance issues for a Filter or Extension.

    Please let me know if this blog post was useful to you!

     

  • WinHttp Proxy configuration on Windows 2003 x64

    You may get an error similar to this when running your WinHttp application:

    Error: The server name or address could not be resolved
    Code: 80072EE7
    Source:  WinHttp.WinHttpRequest

    The familiar ProxyCfg application allows you to set the proxy settings for WinHttp.  However in this case, your  application is not picking up the proxy settings.  You verify this by setting the Proxy settings to the current user by typing "proxycfg -p myproxyhere" at the command line and you see from a Netmon trace that the proxy is not being utilized. 

    Further research shows your application is loading the 32 bit version of WinHttp.  In Windows 2003 x64, there is a directory for the 32 versions of the system components.  You need to run this version for your 32 bit application: c:\windows\sysWow64\proxycfg -p myproxyhere.  This fixes the issue.  In general, on this platform when using proxycfg.exe, you should run the version in system32 and the version in sysWow64 to ensure you set the proxy setting for both versions of WinHttp on the system.

    Side note on WinHttp COM registration on Windows 2003 x64

    One thing interesting in the default installation of the 64 bit OS is that you will notice WinHttp.dll does not live in either the system32 or the sysWow64 directories.  Also, if you look in the registry under HKCR\CLSID you will see no entry for WinHttp.WinHttpRequest.5.1.  So how does a script succeed that calls CreateObject() on your progId?  The magic here is the SideBySide (SxS) registration of WinHttp.  You will notice there are WinHttp Dlls in the C:\WINDOWS\WinSxS\ subdirectories.  You will also see in C:\WINDOWS\WinSxS\Manifests there are manifest files and the file names contain WINHTTP in them.  Without going into a ton of detail, what happens is CLSIDFromProgID will take the name of the object you are creating and based on the Bitness of the application (64 or 32) there are internal system calls to find the latest version of winhttp and use that for the creation of the object.

    That is why you cannot find the ProgID in the registry!

    Let me know if this blog helped you out by dropping me a comment please!

  • WinInet used in Thread Impersonation

    First and foremost, this is NOT supported for reasons stated in this article: http://support.microsoft.com/default.aspx/kb/238425. Primarily, problems occur because of information that is stored in the HKEY_CURRENT_USERS registry key and because of threading concerns.  Furthermore, due to security implications and the design of WinInet in Windows 7, this absolutely will not work in Windows 7.  Finally, in Vista, there is a Low IL cache as well as the normal (Medium IL) cache so this sample is ineffective at best.  This blog entry is mearly an excercise to help you understand how the Registry works with thread impersonation as I discovered when working on a WinInet problem.

    That said, if you have a service running in the Local System Account, you might be able to work around some of the problems by ensuring the registry is loaded with the currently logged on user's registry information.  If the currently logged on user has used the browser, most information should be populated in that user's registry.

    If running with UAC enabled and you attempt to enumerate the cache while running under the Local System account you will fail and GetLastError will return ERROR_INVALID_PARAMETER. This error is thrown because internally there are some registry settings missing that WinInet expects to be there for every user (again, WinInet is not designed, tested or supported when running under the System Account or when using thread impersonation).  I found however if you use thread impersonation and load the registry HKCU for the currently logged on user, you can access the cache.  I do not know if this will work in all scenarios and in future WinInet releases, but I was able to get it to work for me.  I used PsExec from Sysinternals to run my tests.

    Details
    I downloaded and installed PsExec from http://technet.microsoft.com/en-us/sysinternals/bb896649.aspx

    I created a new C++ Console application in Visual Studio and added code to:
    1. Get the Currently Logged on user token in the SessionID from where this program was launched under the SYSTEM account using WTSQueryUserToken
    2. Impersonate this user using ImpersonateLoggedOnUser
    3. Load the impersonated HKU registry in HKCU by using RegDisablePredefinedCache
    4. Iterate through the IE cache using FindFirstUrlCacheEntry and FindNextUrlCacheEntry
    5. I added code that would also show the cache if the WTSQueryUserToken failed.  This will fail if you are running the program from the command line (no PsExec).  This allows you to see the case running in the context of the current user. 

    I then added the WinInet.lib and Wtsapi32.lib to the linker input files and built the project.

    I then tested the code with PsExec using: PsExec –i –s <<full path and program name>>.  I opened a command prompt with Administrator permissions to run the tests.  An interesting use of PsExec allows you to see the cache when running as an Low IL process:  PsExec –l <<full path and program name>>.  Note that this is a different set of values because the Low IL cache is in a different location then the Medium (normal) IL cache.
    For details on protected mode cache see: http://blogs.msdn.com/ie/archive/2006/02/09/528963.aspx

    Here is the code for your enjoyment.  Of course you would need to add your code to test the success of calls, trap exceptions etc...  But it should get you started:

    C++ code listing for sample (Copy Code):

    // ImpersonateWinInet.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"
    #include <windows.h>
    #include <WinInet.h>
    #pragma comment(lib, "WinInet.lib")
    #include <Wtsapi32.h>
    #pragma comment(lib, "Wtsapi32.lib")
    #include <iostream>
    #include <conio.h> // for _getch
    int showCache()
    {
        // another gotcha... The actual cache hit here depends on the IL that the program is running under.
        // to see this run psexec with the -l option in a cmd prompt
        // Local variables
        DWORD cacheEntryInfoBufferSizeInitial = 0;
        DWORD cacheEntryInfoBufferSize = 0;
        int *cacheEntryInfoBuffer = 0;
        INTERNET_CACHE_ENTRY_INFO *internetCacheEntry;
        HANDLE enumHandle = NULL;
        BOOL returnValue =
    false;
        int aiNumEntries=0;
        DWORD dwError;
        // get the size of the buffer required
        enumHandle = FindFirstUrlCacheEntry(NULL, 0, &cacheEntryInfoBufferSizeInitial);
        if (enumHandle == NULL && ERROR_NO_MORE_ITEMS == GetLastError())
        {
            return aiNumEntries;
        }
        cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
        internetCacheEntry = (INTERNET_CACHE_ENTRY_INFO *)malloc(cacheEntryInfoBufferSize);
        enumHandle = FindFirstUrlCacheEntry(NULL, internetCacheEntry, &cacheEntryInfoBufferSizeInitial);
        if (enumHandle == NULL)
        {
            std::cout <<
    "could not get first URL entry. Error :" << GetLastError();
            // ERROR_INVALID_PARAMETER is thrown because of incorrect registry info
            return aiNumEntries;
        }
        returnValue=
    true;
        while(1)
        {
            cacheEntryInfoBufferSizeInitial = cacheEntryInfoBufferSize;
            if (returnValue)
            {
                if (internetCacheEntry != NULL)
                {
                    std::wcout<<(LPWSTR)((INTERNET_CACHE_ENTRY_INFO *)internetCacheEntry->lpszSourceUrlName)<< _T(
    "\r\n");
                    returnValue = FindNextUrlCacheEntry(enumHandle, internetCacheEntry, &cacheEntryInfoBufferSizeInitial);
                    aiNumEntries++;
                }
                else
                {
                    // this should not happen!
                    break;
                }
            }

            dwError = GetLastError();
            if (!returnValue && ERROR_NO_MORE_ITEMS == dwError)
                break; //now more items!


            // if buffer not big enough, grow it
            if (!returnValue && cacheEntryInfoBufferSizeInitial > cacheEntryInfoBufferSize)
            {
                cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
                // note test for buffer here (OOM condition)
                internetCacheEntry = (INTERNET_CACHE_ENTRY_INFO *)realloc(internetCacheEntry, cacheEntryInfoBufferSize);
                returnValue = FindNextUrlCacheEntry(enumHandle, internetCacheEntry, &cacheEntryInfoBufferSizeInitial);
            }
        }
        free(internetCacheEntry);
        std::wcout<<_T(
    "Number of entries processed: ")<<aiNumEntries<<_T("\r\n");
        return aiNumEntries;
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
        // note, this below will get the session id that this program was run from.
        DWORD dwActiveSessionId = WTS_CURRENT_SESSION;
        HANDLE hUserToken = INVALID_HANDLE_VALUE;
        DWORD dwErr=0;

        //showCache();
        BOOL bSuccess = WTSQueryUserToken(dwActiveSessionId, &hUserToken);
        if (bSuccess)
        {
            std::cout <<
    "Got User Token\r\n";
            bSuccess = ::ImpersonateLoggedOnUser(hUserToken);
            if (bSuccess)
            {
                std::cout <<
    "Impersonating\r\n";
                // This is necessary because we want to load the impersonated user registry into HKCU
                // comment this out whey running under the system acct and you will get ERROR_INVALID_PARAMETER
                if( ERROR_SUCCESS==RegDisablePredefinedCache())
                {
                    showCache();
                }
                CloseHandle(hUserToken);
            }
            else
            {
                std::cout <<
    "Failed Impersonation\r\n";
            }
        }
        else
        {
            std::cout <<
    "Did not Get User Token\r\n";
            dwErr= ::GetLastError();
            std::cout <<
    "Error" << dwErr << "\r\n";
            if (ERROR_PRIVILEGE_NOT_HELD==dwErr)
            {
                std::cout <<
    "To call WTSQueryUserToken successfully, the calling application must be running within the context of the LocalSystem account and have the SE_TCB_NAME privilege\r\n";
                std::cout <<
    "Trying in the context of the user running this program...\r\n";
                std::cout <<
    "Hit a key or enter to start";
                _getch();
                showCache();
            }
        }

        std::cout<< "Hit a key or enter to exit";
        _getch();

        return 0;
    }

     

    I hope this sample helps you out.  Please drop me a comment if you found this useful!

  • Understanding Connection Limits and New Proxy Connection Limits in WinInet and Internet Explorer

    Because of RFC 2616 section 8.1.4 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html) we have traditionally limited the number of persistent connections to 2 per server.  This is because of the strong language in this RFC: " A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy."

    Practically speaking with today's modern web servers, there is no reason for such a small number of client connections to a server.  This is especially true with the rich WebPages and WebBased applications hosted in WebPages.  Indeed, at times you can get into a situation where you are attempting to download resources for a WebPage and a script is executed to download additional data giving the appearance of a web browser.  The ability to download several resources simultaneously has huge advantages when it comes to downloading resources quickly and gives the ability to provide a more responsive web experience.

    While you could always change the number of connections WinInet (and Internet Explorer) used by default, in the past, you could not also control the number of Proxy Connections separately (also covered in RFC 2616).  There is now a setting that will allow you to set the proxy connection limit independent from the number of persistent connections to a server.

    Here is a brief Q&A I put together to help you get your head around these settings:

    What does setting the max Server and Proxy connections do?  This limits the number of possible connection at one time to a given host either direct or through a proxy.

    What happens if I try to make 4 connections and the default number of persistent connections (proxy or server) is set to 2?  Two connections will be made and two will be put in a queue to ‘wait’ for an available connection to be freed.  Once one of the first two connections gets a response, another ‘waiting’ connection will use that available connection in a FIFO manner.

    If my app has 2 requests to one host and 2 to another, does that mean only 2 connections at a time will be used total, and there will be two connections waiting?  No, this means that 2 connections per host will be used so in this example there will be no connections waiting.

    Why limit the proxy connections in WinInet to begin with?  We always have as a function of limiting the HTTP connections to the end point server.  We did this because the RFC 2616 tells us to:
    http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html  (see 8.1.4 Practical Considerations)

    Why add a new limit for the proxy connections?  In the event you need to customize the number of proxy connections.  The default is set to 4 which is enough to satisfy the defaults for WinInet which is 2 for HTTP 1.1 servers and 4 for HTTP 1.0 servers. 

    What was it before?  The value did not exist before so it is the same as the server settings.  If the proxy was Http 1.0 it was 4.  If it was http 1.1 it was 2. 

    What if I want to have more server connections, should I also set the MaxConnections to proxy to follow this?  No!  The number of proxy connections will automatically follow the MaxConnections for the server, your application should probably do the same.  In most cases (unless your proxy has some other strange requirement) it should be the same as your server connections.  If for some reason you want this different then set the MaxConnectionsPerProxy manually.  If you do not have a reason for this to be different then do not use this setting.

    How do I set the MaxConnectionsPerProxy?  Either in Code (see this blog: http://blogs.msdn.com/jpsanders/archive/2009/06/08/understanding-the-new-wininet-option-internet-option-max-conns-per-proxy.aspx) or with this registry setting:

    Important This section, method, or task contains steps that tell you how to modify the registry. However, serious problems might occur if you modify the registry incorrectly. Therefore, make sure that you follow these steps carefully. For added protection, back up the registry before you modify it. Then, you can restore the registry if a problem occurs. For more information about how to back up and restore the registry, click the following article number to view the article in the Microsoft Knowledge Base:

    322756  (http://support.microsoft.com/kb/322756/) How to back up and restore the registry in Windows

    To change the number of files that you can download through your proxy at one time to 2, follow these steps:

    1.     Start Registry Editor.

    2.     Locate the following key in the registry:
    HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings

    3.     On the Edit menu, point to New, click DWORD Value, and then add the following registry values:
    Value name: MaxConnectionsPerProxy
    Value data: 2
    Base: Decimal

    4.     Exit Registry Editor.

     

    Let me know if this article was useful to you!

  • InfoPath form and other xml Office documents do not open from Internet Explorer - Raw XML displays instead

    I came across this problem.  The key to this was that the documents opened fine when clicking on them, so I knew the Office installation itself was fine.  Some rogue application on install or removal had apparently removed the appropriate Content Type registry key so Internet Explorer was unable to associate the XML file extension with any application.  Restoring the default OS registry entry allowed everything to start functioning again.  Warning, modify the registry at your own risk.  Microsoft cannot guarantee that problems resulting from the incorrect use of Registry Editor can be solved. Use Registry Editor at your own risk.

    Windows Registry Editor Version 5.00

    [HKEY_CLASSES_ROOT\.xml]
    "Content Type"="text/xml"

    Why did this rogue deletion of this registry key affect IE but not the InfoPath application itself?

    Since all the registry entries for the application were correct the associations in the registry for opening the file are in place.  The issue is that with the 'Content Type' entry missing, Internet Explorer (more precisely Urlmon used by IE) is unable to determine what to do with this particular Content Type (XML). When this key is correct, this MIME type is found in the MIME Database registry location and the appropriate application can be invoked.

    Since Office is installed the file will be passed to MSOXMLED.EXE which will determine the correct application to invoke by looking inside the XML for the appropriate entry indicating what application that particular XML is associated with.  For example: <?mso-application progid="Word.Document"?> indicates that this is a Word Document.

    Let me know if this blog helped you by dropping me a note! 

     

  • Some .chm files do not work on Windows 7 - Process Monitor - Navigation to the webpage was canceled

    This took me a couple of minutes to find so I thought I would share this here.

    I downloaded ProcMon (process monitor) and could view the help file.  It would open but the right pane had a message: "Navigation to the webpage was canceled"  The left pane was functional however.

    I found the help file.  This is a .CHM file stored in the directory where Procmon was installed.

    To be able to use is, Right Click on the procmon.chm file and in the general tab, click on Unblock!

    Works fine now!

     Let me know if you found this useful by dropping me a note please!

  • WWSAPI samples in Windows 7 SDK for RC: "Unable to add URL to HTTP URL group."

    I like to build and run with UAC on.  When running the HttpCalculatorService example I got this error: 

     Failure: errorCode=0x80070005
    Unable to add URL to HTTP URL group.
    Access is denied.

    Running from an administrator command prompt I do not get the error!

    Obviously I do not have permissions to something.  Some investigation revealed that I need to add the user I wanted to run as to the urlacl (http://msdn.microsoft.com/en-us/library/cc307223(vs.85).aspx)

    So this is the command that allowd me to run (replace myDOMAIN\myUSERNAME with the domain and user you wish to run the Service under): netsh http add urlacl url=http://+:80/example user=myDOMAIN\myUSERNAME.

    To do this in code you could use this API: http://msdn.microsoft.com/en-us/library/aa364503(VS.85).aspx but the code your are running would need to have the necessary permissions so you are in kind of a chicken and the egg situation!

    Drop me a comment and let me know if this helped you please! 

     

More Posts Next page »

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker