C# .NET Programming

Monday, April 03, 2006

If Dilbert used .NET

Thank you Paul Mooney, I love this kind of promotional stuff.

400+ Differences sure put a smile on my face this morning, after billing 50+ hours last week and working all weekend.

I'm not in need of faster coding tools (I'm already on Visual Studio 2005 of course), I just need fewer contracts.

I especially like video #14, Difference # 308. I can relate to that one ;-)

Friday, February 10, 2006

Missing Commands in Visual Studio 2005

So for a little fun, the other day, I was checking out the Visual Studio 2005 Starter Kits. And I couldn't help but be impressed with the new *.VSI installer packages that provide automated Template setup for your local Visual Studio install.

I was so impressed actually, I wanted to learn how to make a VSI package myself. So I visited How to: Create Starter Kits.

As explained via the above link, a VSI package is quite simple to build - being just a specially crafted selection of files archived into a *.ZIP file, with the extension renamed to *.VSI so that Visual Studio recognizes it for special handling as a Template installer.

But what got me was, at one point in the tutorial of How to: Create Project Templates using the Export Template Wizard, it states:

Step 4. On the File menu, click Export Template. The Export Template wizard opens.

Well guess what? My Visual Studio 2005 Professional (final) didn't have that menu command - it was missing :-].

Vs2005customizemenu0

I'm just going like, WTF!? So I started searching about a bit, then it occured to me to check out the ever handy "Tools / Customize..." options in Visual Studio, like it exists in most Microsoft products.

And what do ya know, I found it hiding in the File group as it should be, so I just dragged it from the Customize dialog onto the File pull-down menu, and...

Vs2005customizemenu

Voila, my missing menu command was where it should be (or there abouts).

Vs2005customizemenu2

Of course, now I'm wondering what other little gem of a command is hidden from me? The whole thing seems kinda strange, maybe it was something I did during install? I don't know...

Monday, December 12, 2005

Error Deploying ASP.NET 2.0 Application

Recently I rebuilt one of my primary servers (and redeployed it with a new co-lo host here in Calgary), so admittedly I haven't worked through all the configuration quirks yet (but that's no excuse).

The new Server is running Windows 2003 with IIS 6.0 and has several ASP.NET 1.1 applications.

Well yesterday, I deployed a new website for some friends and a good cause. And had decided to create the website as my first little ASP.NET 2.0 web application project. I set it up using basic default settings in the IIS Console. The website crashed upon first load of course, displaying a general Server Error message. I then quickly discovered some of my other ASP.NET 1.1 applications where hanging, not loading at all, with no error message - just hanging.

The general Server Error message I got suggested looking at the Event Viewer, where I found the following message.

Iiseventviewer

"It is not possible to run two different versions of ASP.NET in the same IIS process."

Although it doesn't say it directly, I suspected the process the message was referring to was the Application Pools.

I created a new Application Pool in the IIS Console (with default settings) and named it "Version 2 Pool", and then went to the properties dialog of the IIS Console for my ASP.NET 2.0 application and changed the Application Pool assignment.

Iischangeapppool

This fixed my ASP.NET 2.0 website, but my other ASP.NET 1.1 websites that were sharing the "DefaultAppPool" Application Pool were still not loading.

Back in the Application Pool section of the IIS Console I simply tried recycling "DefaultAppPool" to clear whatever was preventing the sites from loading. This did the trick.

Iisrecycleapppool

What's the lesson to be learned here? Pay more attention to the importance of the Application Pools, and not just to avoid errors like this - but to take advantage of the them to improve performance and reliability.

Thanks for reading.

Thursday, September 15, 2005

Mimic Unknown Connection String in .NET Assembly

Recently a web hosting company contacted me for a small job to help them deploy an ASP.NET 1.1 web application for them, for one of their new customers. The host primarily uses Linux boxens, but had recently acquired a number of MS Windows machines and so of course were a little new to .NET.

Although the customer (a non-profit community organization in the US) thought they had all the "source-code" for their specialized web application, a quick review of the files showed just the compiled assemblies and content files (*.aspx, *.htm, etc...), and of course the SQL Server *.mdf and *.ldf files. The code-behind files were missing, and apparently no other copy for the original files are available (let alone any documentation).

It was unfortunate the source was not available, but at least the immediate need of deployment shouldn't be a problem - except, an editable database connection string wasn't to be found in the standard place (namely the Web.config file). For some unknown reason, the original developer choose to hard-code the database connection string - now safely hiding in one or more places in the compiled assembly in the /bin folder.

Okay, so how do you deploy an ASP.NET web app with a hard-coded connection string? When you have no idea what that string may contain!

Enter Lutz Roeder's Reflector for .NET. After a quick download and a simple loading of the compiled *.dll file, I was able to navigate the Class hierarchy of the application. Luckily, the original developer had at least build the app with a common data object which provided a single location for the connection string (removed from screen-shot).

Reflectorsample

Once I knew the connection string of the application and the database, it was quite simple to mimic the expected properties of the connection.

  • The web application needed to point to a specific server URL address for the database, which of course didn't exist on the hosting company's network. But a simple text edit of the local HOSTS file of the webserver fixed that (look in the %windir%\system32\drivers\etc folder). The HOSTS files basically gives you simple DNS alias control for the local machine.
  • Then, the database side was simple, by ensuring the SQL Server database name matched what was in the connection string, and the expected SQL Login account was created with the given password.

All-in-all it was a satisfying little job (a few hours), because I find trouble-shooting quite rewarding, and I've never had to go to quite this level of sleuthing before for an ASP.NET app.

Thank you Lutz!

Tuesday, August 16, 2005

Anatomy of a ProgressBar for ASP.NET

Note: Code is .NET 1.1

Here is the repost of my previous article and code for implementing a ProgressBar for ASP.NET 1.1 - to highlight why it's not such a simple thing to do.

A ProgressBar in a Windows Form application

First, to understand the challenge of building a Progress Bar Control for a Web application, let's examine how one works in a Windows Forms application for .NET.

Generally speaking, a Windows Forms application will execute within the environment of a single computer. Most Windows Forms applications will also typically only run within a single thread, where the GUI and business logic (behind the scenes) is sharing the same memory space, as accessible objects and classes. Because of this kind of setup, it is relatively easy for the business logic code to report the state of a given execution path to a visible element in the GUI, like a ProgressBar.

The code for the executing business logic simply references the ProgressBar object, while it is running, and "sets" an incremental value for the ProgressBar to indicate progress.

The properties and method of a ProgressBar to take note of, are: Minimum, Maximum, Value (and/or Increment(int)). By repeatedly supplying an incremental Value between Minimum and Maximum, the ProgressBar will graphically indicate where that Value is in relation to Minimum and Maximum.

Simplistically speaking, that continuous setting of information in response to code execution is possible because all the objects and code involved is executing within the confines of the same computer (same memory space, same thread, etc…), where the business logic code and GUI elements are easily accessible to each other.

A ProgressBar in an ASP.NET application

But now consider the web. We've all heard it before, and the fact still remains: the Internet is "stateless" - but why is that important when it comes to implementing a ProgressBar on a Webform?

Because, the business logic code of an ASP.NET application only executes on the Web server, while the active GUI elements of the same application resides in a Web browser (on a separate computer, with separate memory space using separate processor threads, etc…).

This disconnected relationship within a Web application is, for the most part, the opposite of what is happening in many desktop applications (which is why a Windows Forms application is sometimes considered "stateful". Tightly-coupled is another term).

And because a Web application is "stateless" (a.k.a. loosely-coupled), having business logic code communicate with a ProgressBar in the GUI is not easily possible.

What does it mean to have State?

To have State in an application is to imply that two or more objects can understand information about each other, and that they are accessible to each other.

State management in Web applications (for all modern platforms or languages) has progressed in a huge way with the addition many years ago of Session-cookies. And, the advent of ASP.NET and ViewState is arguably the biggest advance in State Management for Web applications since Session-cookies, but we're still not out of the "stateless" woods yet.

Although State Management and ViewState for Web applications is not the primary focus of this article, it is important to understand that the "state" relationship between the Web server and the Web browser is entirely one-sided. Meaning, the server is only aware of the browser, when the browser permits it.

And because of this one-sided "state" relationship, business logic code executing on the server is not readily capable of communicating progress to the GUI.

In a Web application, the browser is always the starting point. Generally speaking, the browser always initiates the starting "request" to the server for a webpage, static resource or a dynamic function. The server, in turn, "responds" by sending some content back in some form or another.

This transaction (a "request" resulting in a "response") is possible with the aid of HyperText Transport Protocol (HTTP). The important point to note here is that the server never starts this conversation. A server cannot "push" response content (or a request for that matter) to a browser unbidden. The internet was not designed for this type of connected state, and for good reason (not covered in this article).

Note: Do not confuse the HTTP "keep-alive" instruction with State Management in this context, this is something different (again, not covered in this article).

Session-cookies provide a mechanism for a server to recognize a browser request as belonging to a specific individual visitor to a website. Session-cookies are how Session Scope variables (stored on the server) can always be available to a specific User of your application. A Session-cookie is the "linkage" for associating a specific browser with specific variables in memory on the server.

ViewState is another mechanism, in addition to Session-cookies, to help link information between data processed and required on the server, with specific GUI elements and fields in a browser. And although the hidden ViewState data in a Webform is created by the server, it is only useful again to the server upon a subsequent "request" from the browser.

So, between Session-cookies and ViewState, an ASP.NET Web application is able to communicate more information between its disconnected parts (namely the GUI and server-side business logic) to ultimately provide a semblance of "state". This is one of the key advantages of ASP.NET over other Web development platforms and languages.

Butthere is still an accessibility break down in our State Management, because connectivity between business logic on the server and the GUI is still entirely dependant on the browser initiating communication (keyword: accessibility).

Given this brief lesson on "state", we should now understand more about the lack of accessibility between our business logic code and the GUI of a Web application. So then, how do you "set" or "push" real-time progress information from the server to the browser to run a ProgressBar? With effort and a new model for receiving and displaying progress read on.

Why even have a ProgressBar for your Webform?

Why? To manage a browser request that results in a long-running process on the server, until the server can send a finished response back.

And why is managing a long-running response important for a Webform? Two reasons:

  1. To give the User a visual indicator that something is happening, that the application is not suddenly broken.
  2. Most modern browsers will timeout after a short period and not wait for the response regardless if it may still potentially show up.

Note: Reason 2 is directly the result of the internet's "stateless" nature. A web browser inherently has no intimate knowledge of a server's availability, and visa versa.

In an ideal world, every request to the server from a Webform will result in a near instant response. This way the User is never left wanting or guessing what is happening.

But as fast as ASP.NET is, eventually a User will request a server-side function that will potentially take a long time to complete. Having a long-running function is not necessarily a bad thing, but not informing the User can be.

Prevent Browser Timeout with HTTP Instructions

In addition to the challenge of one-sided "state" management in a Web application (described above), a long-running server-side process will typically result in the browser simply not wanting to wait. This is a hard-coded fact of most modern browsers.

A browser "request" is typically an HTTP GET or POST command, and a typical "response" from a server is some other HTTP instructions generally followed by some content (usually HTML).

So how do you instruct a browser to wait longer than usual? With some special HTTP response instructions, of course.

HTTP response instructions typically contain general information about the contents the browser is about to receive from the server, such as format, language, caching and size but nothing about how long the content might take to arrive. With such a response, the browser just expects the content will all arrive in a timely manner, else it times out.

Following is a typical HTTP response:

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Fri, 08 Jul 2005 21:15:24 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 1.1.4322
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 9819

(html appears here)...

So what HTTP instructions do you use to tell a browser to wait on a long running process? AND manage a ProgressBar display for the user? It begins with the following response:

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Fri, 08 Jul 2005 21:21:56 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 1.1.4322
Transfer-Encoding: chunked
Cache-Control: private
Content-Type: text/html; charset=utf-8

1
<
4
html
1
>
1
<
4
body
1
>
...

The main difference in HTTP response instructions, from the special one we need from the first "typical" response is the following:

  • The addition of Transfer-Encoding: chunked
  • No total Content-Length:
  • The contents (after the empty line, a.k.a. two carriage-returns) is now broken into pieces surrounded by line length numbers.

By sending special chunked instructions to the browser we can make it wait almost indefinitely. As well, in the contents of the same response output we can feed incremental instructions (as it becomes available on the server) in the form of JavaScript commands embedded in HTML.

So, how do you send these kinds of HTTP instructions and JavaScript commands, and run a ProgressBar, using ASP.NET?

Prevent Browser Timeout and display a ProgressBar in ASP.NET

If you're still following along, the question of why Visual Studio .NET doesn't ship with a ProgressBar Control for ASP.NET Webforms (like it does for Windows Forms), should be more apparent.

A ProgressBar for Webforms cannot operate the same as for Windows Forms because of "state", and lack thereof. The solution requires a slightly different processing model as provided by the chunked HTTP instructions described above.

The processing model of an ASP.NET ProgressBar can be similar to Windows Forms, except in how the server will "feed" the incremental values to the browser.

Therefore, my ASP.NET ProgressBar solution involves breaking the Control into two reusable parts:

  1. ProgressClient (for the browser, as a User Control)
  2. ProgressServer (for the server, as a Class).
Progressbarproject

The Visual Studio .NET 2003 Source-code

Without going into too much detail with the source-code (which you'd rather read yourself I'm sure), this solution, written in C#, comes complete with a two ProgressBar demo implementation examples.

One simple demo (waiting on a sleepy thread in a for loop), and one slightly more complex demo (transferring a file from a one website to another, and displaying the progress).

Each example comprises two Webforms:

  1. The first Webform (Demo*.aspx) is for the User to trigger the long-running process (and subsequently display its results). This Webform contains the ProgressClient User Control.
  2. And the second Webform (MyWorkerCode*.aspx) contains the worker code that will execute on the long-running process, and report its progress in real-time. This Webform uses the ProgressServer Class to communicate with the ProgressClient.

Setup the ProgressClient

In the code-behind of the Demo1.aspx Webform, the following code highlights how to manage the properties of the ProgressClient.

private void Page_Load(object sender, System.EventArgs e)
{
    progressClient1.StartButtonText = "Call Sleepy Thread";
    progressClient1.StartButtonTextWhenFinished = "Reset Webform for Demo";
    progressClient1.StartButtonCommandWhenFinished = "location.href = 'Demo1.aspx'";
    progressClient1.WorkerWebformUrl = "MyWorkerCode1.aspx";
    progressClient1.NoticeBeforeWorkerStart = "Click to start.";
    progressClient1.NoticeAfterWorkerComplete = "Thread completed.";
}

How the ProgressClient Runs

When the Webform is displayed in the browser, it should look like the following.

Progressbarstart

With the help of a non-visible HTML IFRAME element, and some embedded client-side JavaScript functions, the request to the server is initiated, which displays the ProgressBar. And as the server responds with incremental information, via those special HTTP instructions, the graphical ProgressBar indicator changes.

Note: The call to the worker code on the server is initiated without refreshing (redisplaying) the Webform.

Progressbarrunning

Upon reaching the 100% increment from the server, the ProgressBar disappears and resets to start over, or to allow starting a different command.

Progressbarfinished

Setup the ProgressServer

Following is a simple example to highlight how easy it should be to integrate reporting progress information with your server-side code.

private void Page_Load(object sender, System.EventArgs e)
{

    // invoke the server half of the progressbar user control
    ProgressServer progressServer = new ProgressServer();

    try{
        // start output for ProgressBar
        progressServer.Begin();
        progressServer.Maximum = 100;

        for(int i = 1; i <= 100; i++){

            // notify webform of progress
            progressServer.Increment(i, String.Format("Loop {0} of 100...", i.ToString()));

            // purposely create a pause in the loop
            System.Threading.Thread.Sleep(50);

        }
        // end output for ProgressBar
        progressServer.End();

    }catch(ThreadAbortException){
        // do nothing because Response.End() was called.
    }catch(Exception error){
        // send error information to client
        progressServer.SendMessageAndReset(error.ToString());
    }

}

How the ProgressServer Works

As described above in the HTTP sections, the chunked command is the first instruction for the browser, this is managed in the ProgressServer Method Begin(), which initializes an HtmlTextWriter tied to the underlying Response.Output Stream. Following is the C# code:

public void Begin(){
    // create the unbuffered output
    // for beginning of step actions
    htmlOutput = new HtmlTextWriter(context.Response.Output);
    context.Response.Buffer = false;
    htmlOutput.WriteFullBeginTag("html");
    htmlOutput.WriteFullBeginTag("body");
}

Note: The intrinsic Response.Buffer property (false) is what provides the workings to issue the Transfer-Encoding: chunked instructions.

Next, the Increment() Method is about sending the incremental information to the ProgressClient to advance the graphical indicator, and as a bonus also send a text message to accompany it. This is achieved by repeadtedly calling the Flush() Method on the HtmlTextWriter and the underlying Response.Output Stream. The Method includes divisor logic to manage the increment sent in percentages (when the Maximum property is greater than the default 100), so never more than 100 commands are sent from the server to the browser. This is useful in the case of measuring progress based on the number of bytes of when moving or copying a large file (see Demo2 in the source code).

public void Increment(int increment, string message){
    // manage increment work in percentages only
    // because the visual display of the progress bar
    // will only reach to 100% in width
    double progressNow = 0;
    if(maximum > 10000){
        progressNow = Math.Floor(((increment/100) * 100) / (maximum/100));
    }else if(maximum > 1000){
        progressNow = Math.Floor(((increment/10) * 100) / (maximum/10));
    }else{
        progressNow = Math.Floor((increment * 100) / maximum);
    }
    if(progressNow > progressCounter){
        // ensure the output message text is not empty
        if(message == null || message.Trim() == String.Empty){
            message = "(message is empty)";
        }
        // percentage value has incremented by a whole number
        // equal or between 1 and 100
        // so send progress report to client
        htmlOutput.WriteFullBeginTag("script");
        htmlOutput.Write(String.Format("window.parent.SetProgress({0}, '{1}');", progressNow.ToString(), message.Replace("\\", "\\\\").Replace("'", "\\'").Replace(Environment.NewLine, "\\n")));
        htmlOutput.WriteEndTag("script");
        htmlOutput.Flush();

        // increment counter for next percentage increment
        progressCounter++;
    }
}

Lastly, to clean up the chunked output sent from the server, is the overloaded End() Method, where one Method can send an optional message prompt to the ProgressClient, which may be helpful to send special information about the end of whatever long-running process just completed.

The call to Response.End() ensures no other HTML content from the original *.aspx file is sent, to reduce unnecessary (and unseen) output.

public void End(){
    End(null);
}
public void End(string message){
    // insert ending message prompt
    if(message != null){
        if(message.Trim() == String.Empty){
            message = "(empty string)";
        }
        htmlOutput.WriteFullBeginTag("script");
        htmlOutput.Write(String.Format("window.parent.Say('{0}');", message.Replace("\\", "\\\\").Replace("'", "\\'").Replace(Environment.NewLine, "\\n")));
        htmlOutput.WriteEndTag("script");
    }
    // end of progress output
    htmlOutput.WriteEndTag("body");
    htmlOutput.WriteEndTag("html");
    htmlOutput.Flush();
    context.Response.End();
}

Client Options

In my implementation, one of my main objectives was to display real-time information from the server, without refreshing the browser continuely, or even once.

The ProgressClient (in the browser) is using a seemingly hidden HTML IFRAME to manage receiving increment instructions from the server. Although this method may suggest it is not entirely cross-browser, it does work for newer versions of Mozilla and Firefox, and obviously all versions of MS Internet Explorer. Using an IFRAME allows the initial call to the server-side worker code by simply calling the Webform to have it load in the IFRAME (so the current Webform is not refreshed). Then the increment instructions can arrive using HTML and JavaScript at their leasure. These JavaScript commands simply reference the ProgressClient objects and functions by appending window.parent... to each command.

Other options for calling the server without refreshing the current Webform in the browser are:

  1. Dynamic manipulation of an HTML SCRIPT tag.
  2. Utilizing the XMLHttpRequest object, also known lately as Ajax (what a dumb term).

Manipulating an HTML SCRIPT tag works by dynamically assigning a new value for the SRC attribute. This can permit dynamic calls to a server without affecting the load state of a Webform in the browser. In turn, the returning server output can contain pure client-side JavaScript code or function calls (all in context to the current webpage). But...unfortunately this "feature" only works well in MS Internet Explorer (very gracefully I might add). Mozilla and Firefox appears to exhibit some odd behavior when you attempt this (undoubtedly a security feature, maybe?).

Using XMLHttpRequest (a.k.a. XMLHTTP or Remote Scripting) is another option, that is lately cross-browser and a wonderful solution to many browser to server challenages - but in this case has some limitations. The behavior of XMLHttpRequest and it's readystate property implies that it also wants to receive server responses in a "complete" timely manner. Although there are some tricks that can be applied to XMLHttpRequest to get it to receive response content in a delayed context, I did not find this approach very cross-browser friendly.

In Summary

Of course, there may be cases where this type of real-time progress reporting from the server is not entirely practical. Some examples would be where the beginning of the long-running process does not even start for some time (like when waiting on results from a Database query before beginning other processing). In which case, you need to handle sending at least a starting increment to show something and maybe a message that says "Stand by".

Another example where this type of progress reporting may need a different model, is when initiating a new asynchronous process on the server.  Reporting from a separate thread has different challenges, but throwing a while loop (and some logic to reset the ProgressBar) in to the mix may at least provide some running feedback that could be helpful, until the separate thread finishes.

A Composite ProgressBar Control for ASP.NET

This article presented just one way of implementing a ProgressBar Control for ASP.NET Webforms, by simply utilizing a custom User Control and a separate Class file. This approach of course has some limitation. But mainly I was hoping a User Control would also be helpful to some, because my example is also limited in extended functionality, and that at least this way others could make their own modifications more easily.

Please send me or post your feedback, about any implementation challenages you have, or feature suggestions, so that I may better prepare a distributable (more feature rich) version of this Webform ProgressBar in the near future, as a Composite Control.

Thank you for reading and thank you for the complimentry comments posted with the original positing.

DemoWebProgressBar.zip DemoWebProgressBarVB.zip

Friday, March 11, 2005

Read an INI File with C# (Managed)

Note: Code is .NET 1.1

When deploying Console or Windows Service applications, I use InstallShield which comes with this nice simple feature of dynamically creating INI files during install. Although .NET applications can utilize an app.config file very easily - sometimes an INI file is what you have to work with.

Following is a simple Console application to demonstrate reading an *.ini file, and assigning the keywords from each section to some private fields. These fields can be used during the operation of your application.

Here's the INI file and code.

[APPLICATIONINFO]
APPNAME=Hello World!
STARTVALUE=3
[LOCATIONS]
INSTALLDIR=C:\Program Files\My Program
DATABASEDIR=C:\Program Files\My Program\My Data

using System;
using System.IO;

namespace DemoINIFileReading
{
    ///
    /// Assign the INI keywords to fields, but not much else.
    ///
    class DemoReading
    {

        private string ini_APPNAME = null;
        private byte ini_STARTVALUE = 0;
        private string ini_INSTALLDIR = null;
        private string ini_DATABASEDIR = null;

        ///
        /// The main entry point for the application.
        ///
        [STAThread]
        static void Main(string[] args)
        {
            // prepare myself
            DemoReading demo = new DemoReading();
            // read my ini file
            demo.read_ini_settings();
            // do my stuff
            Environment.ExitCode = demo.run_application();
        }

        private void read_ini_settings(){
            // get the configuration file
            FileInfo config_file = new FileInfo(String.Format("{0}\\DemoFile.ini", Directory.GetCurrentDirectory()));
            if(config_file.Exists){
                // read configuration values
                // Create an instance of StreamReader to read from a file.
                // The using statement also closes the StreamReader.
                using(StreamReader stream_reader = new StreamReader(config_file.FullName)) {
                    string section = "[]";
                    string line;
                    // Read lines from the file until the end of
                    // the file is reached.
                    while((line = stream_reader.ReadLine()) != null){
                        // set the current section name
                        if(line.StartsWith("[") && line.EndsWith("]") && line != section){
                            section = line.ToUpper();
                        }
                        if(section == "[APPLICATIONINFO]"){
                            // assign keywords from this section
                            if(line.ToUpper().StartsWith("APPNAME=") && line.Length > 8){
                                ini_APPNAME = line.Substring(8);
                            }else if(line.ToUpper().StartsWith("STARTVALUE=") && line.Length > 11){
                                ini_STARTVALUE = byte.Parse(line.Substring(11));
                            }

                        }else if(section == "[LOCATIONS]"){
                            // assign keywords from this section
                            if(line.ToUpper().StartsWith("INSTALLDIR=") && line.Length > 11){
                                ini_INSTALLDIR = line.Substring(11);
                            }else if(line.ToUpper().StartsWith("DATABASEDIR=") && line.Length > 12){
                                ini_DATABASEDIR = line.Substring(12);
                            }
                        }
                    }
                }
            }else{
                throw new Exception("My INI file is missing.");
            }
        }

        private int run_application(){
            // do stuff here
            // and utilize my ini settings
            Console.Write(ini_APPNAME);
            return 1;
        }
    }
}

I find I do this quite often. I hope this is useful.

DemoINIFileReading.zip

Friday, January 21, 2005

File Download with ProgressBar for Windows Forms

Note: Code is .NET 1.1

For a previous web project of mine, I built a web-based application update component that permitted the Webmaster to initiate software and database updates through a browser with a few button clicks (no manual file copy, etc...).

This of course involves having some sort of file download routine to copy the Update package from my server to a customer's server - and be managed through a Webform.

Downloading files in .NET is simple as a few lines of code using the WebClient or WebRequest and WebResponse Classes - but I also want to have a progress bar in the Webform.

Anyway, after much Googling for some sample code that included progress bars, the best I could find was some VB.NET examples for Windows Forms applications.

I found some working VB.NET code to download (credit to Mike McIntyre) from DevCity.NET. I transposed this VB.NET sample into C# (sample below), from which I'll base my ASP.NET implementation on (for another article) - allowing of course for how HTTP will push progress information to the Webform.

The credit also goes to this forum posting, where I think most of code originated. I just put it into a working Windows Forms application written in C#.

Downloadprogressbardemo

Download VS.NET 2003 C# Project - DemoDownloadProgressBar.zip (28.29 KB)

Following is an excerpt of the code.

private bool download(string path_download, string path_local){

    // Declare a variable of type Boolean named result initialized to false.
    bool result = false;
    // Declare a variable of type HttpWebRequest named lHttpWebRequest.
    HttpWebRequest lHttpWebRequest;
    // Declare a variable of type HttpWebResponse named lHttpWebResponse.
    HttpWebResponse lHttpWebResponse;
    // Declare a variable of type Stream named lHttpWebResponseStream.
    Stream lHttpWebResponseStream;
    // Declare a variable of type FileStream named lFileStream.
    // Use a FileStream constructor to create a new FileStream object.
    // Assign the address (reference) of the new object
    // to the lFileStream variable.
    FileStream lFileStream = new FileStream(path_local, FileMode.Create);
    // Declare a variable of type Byte Array named byteBuffer.
    byte[] byteBuffer = new byte[999];
    // Declare a variable of type Integer named bytesRead.
    int bytesRead;

    try{
        // Instantiate the HttpWebRequest object.
        lHttpWebRequest = (HttpWebRequest)WebRequest.Create(path_download);
        // Instantiate the HttpWebRespose object.
        lHttpWebResponse = (HttpWebResponse)lHttpWebRequest.GetResponse();
        // Instantiate the ResponseStream object.
        lHttpWebResponseStream = lHttpWebRequest.GetResponse().GetResponseStream();
        // Set the ProgressBars Maximum property equal to the length of the file
        // to be downloaded.
        progressBar1.Maximum = Convert.ToInt32(lHttpWebResponse.ContentLength);
        // progress counter to control when
        // the form label is updated
        double progress_counter = 0;

        do{

            // Read up to 1000 bytes into the bytesRead array.
            bytesRead = lHttpWebResponseStream.Read(byteBuffer, 0, 999);
            // Write the bytes read to the file stream.
            lFileStream.Write(byteBuffer, 0, bytesRead);
            // If the ProgressBar's value plus bytesRead is less than the length of the file...
            if((progressBar1.Value + bytesRead) <= progressBar1.Maximum){
                // Add bytesRead to the ProgressBar's Value property.
                progressBar1.Value += bytesRead;

            }else{
                // Else files download is done so set ProgressBar's Value to the length of the file.
                progressBar1.Value = progressBar1.Maximum;
            }
            // calculate the current percentage
            double progress_now = Math.Floor(((progressBar1.Value/100) * 100) / (progressBar1.Maximum/100));
            // only upgrade the display label once per percentage increment
            if(progress_now > progress_counter){
                // Update the ProgressLabel.
                label3.Text = String.Format("{0}% of {1}kb", progress_now.ToString(), (progressBar1.Maximum/1000).ToString("#,#"));
                // update the form
                Application.DoEvents();   
                // increment the counter
                progress_counter++;
            }

        }while(bytesRead > 0);

        // Close the file and web response streams.
        lHttpWebResponseStream.Close();
        // Set result to True - download was successful.
        result = true;

    }catch(Exception download_error){
        // display the whole error
        MessageBox.Show(download_error.ToString());
    }finally{
        // Close the file and web response streams.
        lFileStream.Close();
    }
    return result;
}

I changed a few things, like the percentage math for larger files. I was testing with a 38mb file and it caused some arithmetic errors on the huge decimal values it was creating. As well, I added a percentage counter so that the Label control was only updated once for each percent increment (about 100 times), as opposed to the 38,000,000 times the control was updated on my large test file, based on the original code.

Hope this is of use...

Note: This article is a repost from my original.

DemoDownloadProgressBar.zip

Programmer for Hire

  • About Hiring Me:
  • Contact Information:
    • Name: P. Scott Cadillac
    • Phone: (902) 624-1266
    • Email: scott@xmlx.net
    • Location: Mahone Bay, Nova Scotia Canada
    • Timezone: Atlantic, ADT
  • Special Links: