Pages

Advertisement

Wednesday, October 10, 2007

Multithreading Using C#

 

Multithreading has always been a helpful friend of a programmer . Multithreading increases the response time of an application .  Multithreading can be implemented by using namespace system.threading  .

Why Multithreading is required ?

> For this I will give U a simple example : Assume U have a company and U have only one assistance and 0 workers then what will happen the complete workload of company will be on your assistance , the assistance then take much time to do everything one by one , Which will affect U in turn .. your application also runs in the same manner if U have only single thread Your execution and your processing will be very slow which may also hangs your application . So Multithreading distributes specified process to run in another thread  an wont affect the normal processing of your application ..

Lets have a little demo for this .

Demo - 1

 

using System;
using System.Threading;
public class MyThread
{
    public static void Thread1()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Thread1 {0}", i);
        }
    }
    public static void Thread2()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Thread2 {0}", i);
        }
    }
}
 
public class MyClass
{
    public static void Main()
    {
        Console.WriteLine("Before start thread");
        Thread tid1 = new Thread(new ThreadStart(MyThread.Thread1));
        Thread tid2 = new Thread(new ThreadStart(MyThread.Thread2));
        tid1.Start();
        tid2.Start();
    }
}


Explanation :



In this Demo u have 2 static methods Thread1 and Thread2 . To make a thread we are making a object class named Thread.



The constructor of this class takes a reference of a ThreadStart class. This constructor
can send two types of exceptions; ArgumentNullException when the parameter is
a null reference or a Security Exception when program does not have permission to
create thread.

 



The parameter of the Thread class is reference to a ThreadStart class.
ThreadStart class points to the method that should be executed first when a
thread is started. The parameter is the name of the function, which is considered as
a thread function. Thread1 is a static function so we give it with the name of class
name without making an object of the class. The thread starts execution with
Start() method of the Thread class. The output of this program is



Before start thread
Thread1 0
Thread1 1
Thread1 2
Thread1 3
Thread1 4
Thread1 5
Thread1 6
Thread1 7
Thread1 8
Thread1 9
Thread2 0
Thread2 1
Thread2 2
Thread2 3
Thread2 4
Thread2 5
Thread2 6
Thread2 7
Thread2 8
Thread2 9



This is how we using multithreading in our application ...



Technorati Tags: , ,

Friday, October 5, 2007

Improving BitTorrent Download Speeds

1.0 Why are my downloads going slow?

- 1.1 Reason 1: Your ISP is limiting your download speed
- 1.3 Reason 2: You have a “NAT Error”
--> 1.3.1 How do I know if I have this problem?

2.0 Fixing a NAT error

- 2.1 Switch off UPnP
- 2.2 Configure Your XP Firewall (ICF: Internet Connection Firewall)
--> 2.2.1 Configure ICF in SP1
--> 2.2.2 Configure ICF in SP2
- 2.3 Configure Your Router
- 2.4 Configure Microsoft Internet Connection Sharing (ICS)
- 2.5 Configure Your Firewall.

3.0 Multiple BitTorrent users on a LAN
4.0 Other things that could be wrong


1.0 Why are my downloads going SOOO slow?

1.1 Reason 1: Your ISP is limiting your download speed

Some ISPs are limiting download speeds by controlling the bandwidth allocated to traffic using the default BitTorrent ports, which are 6881 to 6999.

This is not just a problem for those who have nasty ISPs, it affects everybody. To avoid ISPs from damaging the efficiency of a torrent with their controls, some stringent tracker owners are blocking users trying to connect on the standard ports.

So whether your ISP is limiting you or not, all BitTorrent users should stop using the default ports.

For simplicity, this guide recommends the use of port 16881. Adding a 1 to the start is simple administratively, and is in a range unlikely to be used by other programs.

You then need to set up your client to use the new port.

1) AZUREUS
Tools -> Options -> Connection
-> Add a “1” before the current listen port number. For example, 6881 will now be 16881.

2) uTorrent
Options -> Preferences -> Connection
-> Type 16881 for the port number for incoming connections. Untick the box for Randomize Port.

Whatever client you use, make sure you update anywhere else you have entered the port numbers to account for the change.

By completing this section, you have helped to avoid problems later down the line, but this will not solve the majority of slow downloads. Therefore, move on to reason 2.


1.2 Reason 2: You have a “NAT Error”

BitTorrent works on a credit system. By uploading parts of a file to other people, they allow you to download parts of the file from them. This is BitTorrents anti-leech measure. However, if you have a problem with your NAT (anything between you and the internet, such as a router or firewall), you will not get credit for what you are uploading. The only downloading that you can do is from people who upload a small amount to you in the hope that you will return the favour. Since their client will not recognise your response if you have a NAT problem, then they will stop uploading to you shortly after. This is what is causing your problem. You may also notice that torrent speeds go very high for 5 minutes, before slowing back down to 0-5kb/s.


1.2.1 How do I know if I have this problem?

You might not have a problem at all. Before continuing with this guide, leave your torrent running for at least ten minutes. If the speed hasn't picked up, stop and restart the torrent. If that fails, try at least two other torrents. If you are still having problems, continue on...

Start to download a file using your chosen client.

AZUREUS
Wait a while for the torrent to get started; this may take up to five minutes.
Look in the “Health” column. If it is showing a yellow spot, then you need to configure your firewall/NAT/router. If it is Green then all is OK. Your slow downloads are being caused by something else.
A full list of the different colours can be seen [http://azureus.sourceforge.net/faq.php#21]here[/url].

Azureus can also check your configuration for you.
Select “Tools” -> “NAT / Firewall Test”
The default TCP listen port is 6881. If you have changed the listening port, enter the new port in the “Incoming TCP Listening Port.”
Press “Test”
If the result is “NAT Error,” please continue with the guide.
If the result is “OK”, then your slow speeds are being caused by something else.

PLEASE NOTE: The Azureus checker can report a false NAT error if you are running PeerGuardian or Protowall. Make sure these are disabled before using the Azureus wizard.

uTORRENT
Wait a while for the torrent to get started; this may take up to five minutes.
On the status bar, look for either a yellow warning triangle, or a green dot. If there is a yellow warning triangle, then you need to configure your firewall/NAT/router. If it is green then all is OK and your slow downloads are being caused by something else.
uTorrent can also check your configuration for you.
Select “Options” -> “Speed Guide…”
Check the current port is set to the port used in the first section, i.e. 16881.
Click “Test if port is forwarded properly”.
If you get an error, then continue to follow this guide. If the port is correct set up, then skip to the section 4.0 – “Other things that could be wrong”.


2.0 It’s showing yellow / warning triangle, I have a NAT error – what do I do?

You need to configure your computer and/or network to accept incoming BitTorrent port connections. The tracker port is 6969. If this port is not correctly configured, downloads will not start at all. The download/upload ports used in this guide are 16881-16889. It is these ports that are causing your problems.


2.1 Disable UPnP

Universal Plug and Play (UPnP), it is a feature that works with some compatible routers to open the required ports automatically. However, some routers, even if they are compatible with UPnP, have problems with this.

AZUREUS
Tools -> Options
Expand "Plugins" on the left hand tree.
Select "UPnP"
Deselect "Enable UPnP"

uTORRENT
Options -> Preferences -> Connection
Deselect “Enable UPnP port mapping”.


2.2 Configure Your XP Firewall (ICF: Internet Connection Firewall)

Please go to the appropriate sub-section.
- “Configure ICF in SP1” if you have NOT installed Service Pack 2
- “Configure ICF in SP2” if you have installed Service Pack 2


2.2.1 Configure ICF in SP1

Open up your “Network Connections”. Right click on your internet connection or LAN connection and select “Properties”.
->Select the “Advance” tab
->If the “Internet Connection Firewall” is not ticked, then this is not your problem. Research into whether you need the XP firewall or not and jump to "Configure Your Router" below. If it is ticked, please continue:
->Click “Settings…”
->On the “Services” tab press “Add…”
->Description of service = BitTorrent
Name or IP address of the computer hosting this service on your network = 127.0.0.1 (this means "the local machine.")
External Port number for this service = 16881
Internal Port number for this service = 16881
TCP/UDP = TCP
-> Press OK.

-> With uTorrent, make sure Options -> Preferences -> Connection -> "Port used for incoming connections” is equal to 16881.

-> With AZUREUS, make sure that "Tools -> Options -> Connection -> Incoming TCP Listen Port" is equal to 16881.

Check if the torrent/health has gone green. If not, move on to section 2.2.


2.2.2 Configure ICF in SP2

-> Open your Windows Control Panel and select “Security Centre.”
-> Scroll down to the “Manage security settings for:” section and select “Firewall”
-> Decide whether you want to keep the firewall on or off. Make an informed decision based on more than the information provided by Microsoft. If you decide to switch it off, then do so and move onto section 2.2. Otherwise, continue this section.
-> Select the “Exceptions” tab.
-> Select “Add Port…”
-> Name: = BitTorrent (or anything of your choice)
-> Port number: = 16881
-> TCP/UDP = TCP
-> Press OK.

-> With uTorrent, make sure Options -> Preferences -> Connection -> "Port used for incoming connections” is equal to 16881.

-> With AZUREUS, make sure that "Tools -> Options -> Connection -> Incoming TCP Listen Port" is equal to 16881.

Restart the client and wait five minutes. Check if the health has gone green or that the warning triangle has vanished. If not, move on to section 2.3.


2.3 Configure Your Router


If you are on a LAN with a router or hardware firewall, you may need to configure it. You need to forward BitTorrent’s connection ports to your specific PC on the LAN.

If your connection is shared using Microsoft Internet Connection Sharing (ICS), go to section 2.3.

There are too many routers to cover in this guide, so it is time to head to Google. Search for port forwarding, BitTorrent and the name of your router. Lots of manufacturers provide specific information on their sites. A generic guide to port forwarding for most routers can be found at PortForward.com.

-> With uTorrent, make sure Options -> Preferences -> Connection -> "Port used for incoming connections” is equal to 16881.

-> With AZUREUS, forward port 16881. Make sure this matches "Tools -> Options -> Connection -> "Incoming TCP Listen Port"

Restart the client and wait five minutes. Check if the health has gone green or that the warning triangle has vanished. If not, move on to section 2.4.


2.4 Configure Microsoft Internet Connection Sharing (ICS)

If you are sharing an internet connection using ICS, then you will need to forward the BitTorrent ports.
Windows 2000 users, click here for information on forwarding (mapping) ports.

Other users:
Shock Systems used to provide a useful tool called “ICS Configuration” for changing the settings of ICS to allow full BitTorrent access to a second computer.

-> Install the program on the serving computer and run it
-> Click “+ Add”
-> Name = [whatever you want, e.g. BitTorrent]
-> Target = Local IP of machine running BitTorrent, e.g. 192.168.0.2
-> Description = [whatever you want, e.g. “allow BitTorrent for 192.168.0.2”]
-> On the “Ports” tab, slect “+ Add”
-> Select “Port Range” from the drop-down box
-> Enter “16881” and “16889” into the relative boxes
-> TCP/UDP = TCP
-> Data translation = NORMAL
-> Press “OK”, Press “Enable”, Press “OK”.
-> Close everything and reboot the PC.
The settings will be saved in your registry. There is no need to open ICS Configuration with ICS.

-> With uTorrent, make sure Options -> Preferences -> Connection -> "Port used for incoming connections” is equal to 16881.

->With AZUREUS, forward port 16881. Make sure this matches "Tools -> Options -> Connection -> Incoming TCP Listen Port"

Restart the client and wait five minutes. Check if the health has gone green or that the warning triangle has vanished. If not, move on to section 2.5.


2.5 Configure Your Firewall

As with routers, there are too many to cover here, so time to head to Google. Unlike with other sections to this guide, ports only have to be opened, rather than forwarded.

With both AZUREUS and uTorrent, open port 16881.


3.0 Multiple BitTorrent users on a LAN

The same port can not be forwarded to different computers, so decide who gets which ports, for example:
192.168.0.2 = 16881
192.168.0.3 = 16882 etc.

Set up your router or ICS to forward the ports to the chosen computers.

Now you need to set up the BitTorrent Clients:

AZUREUS
Each computer only needs one port forwarded to it.
-> Tools -> Options
-> "Connection" on the side menu
-> On “Incoming TCP Listen Port” enter the port number that is being forwarded to that computer. Taking from the above example, computer 192.168.0.3 would enter 16884.
-> Repeat for all the users

uTORRENT
Options -> Preferences -> Connection -> is equal to 16881.
-> Set "Port used for incoming connections” to the port number that is being forwarded to that computer. Taking from the above example, computer 192.168.0.3 would enter 16884.
-> Press “OK”
-> Repeat for all users.


4.0 Other things that could be wrong

Try limiting your upload if you have ADSL/DSL. Sometimes BitTorrent can try to upload so much that it floods your upload, making it difficult for incoming connections. Slyck.com explains further:

It might seem strange to hear that your upload speed can directly affect your download speed, but it can, and quite dramatically. The reason behind it is based on the way the Internet works. When you download a file (or in this case, chunks of a file), your computer sends a tiny message back to the source computer, called an ACKnowledgement packet. These ACK packets tell the source computer that chunk #1 has arrived OK, please send #2. When #2 is down, it sends another, and so on. The source computer will not send #3 until it gets the ACK from #2. It uses a small amount of your upload bandwidth to do this. This works fine when downloading files off the Internet, but with P2P, you tend to upload as well.

This is especially true with BitTorrent – because you upload as well. If done efficiently, downloads can be very fast. The problem lies with the fact that you upload *so much* that the ACK's do not get sent out fast enough, which slows your download. The simplest way to solve this is to limit your upload bandwidth. 80% of the maximum is usually recommended, but I find I can set it to 90% with no adverse effects. This gives your ACKs a bit of breathing space to get out with.
CCSDUDE makes this recommendation on limiting your upload:

"Try 11kbs up as your limit on a 128k upload package...double it for 256/1.5mb lines and keep going up as you hit 384/ect. That seems to be the sweet spot on BT. Enough to keep everyone happy whilst not slowing your downloads down or slowing your surfing.
Some ISPs recognise BitTorrent traffic and then limit it. Simply switching to Azureus or uTorrent will mean that you traffic will be encrypted between compatible clients. Without giving technical details, this will stop ISPs from recognising the traffic as being from BitTorrent."

Improving BitTorrent Download Speeds

Technorati Tags: , , ,

Monday, October 1, 2007

Who Else Wants To Make Money With Adsense?

Fact number 1: Kids in high school are making thousands of dollars every month with Adsense.

Fact number 2: Housewives, retirees, mom and dads, who are just staying at home and have never made a dime on the internet have created full-time incomes by simply placing Adsense ads on their web site or blog.
These are just some of the “super Adsense earners”. You may have already heard about their story for they are among the few who are on their way to making millions worth of cash just by promoting Adsense sites.


Anyone, any age and gender can become money generating Adsense publishers as long as they what it takes. How does one go about this Internet advertising?
Writing articles for Adsense is the way to do it. Using the right keywords in your articles and having Google ads on a certain site has become the most profitable way of marketing that anybody can get into. No experience and level of education needed. If you are not using this strategy, or may not be aware of it in the first place, chances are you may be losing thousands of dollars worth of extra income and still do not know it yet.


This is one of the many reasons why writing original quality content articles is now the latest in marketing buzz. Content and links. When combined together becomes a really powerful tool to a successful web site and richer individuals. Many internet marketing professionals are already aware of the value of an original quality content and how using keywords can drive targeted traffic into their sites from the search engines.


So why don’t all these web site owners write and submit their own articles if that is what is important?
The simple and understandable answer is that it takes time to write articles, submit them and get targeted traffic to their websites. That is why they get the services of those who can spares sometime to write the articles that would cater to their site purpose but still turn out as a good quality and unique piece of work.


To get into the Adsense marketing business and start earning some good cash, ask yourself. Did you enjoy writing when you were in school? If you answer yes to this question, you already have an initial advantage over most internet marketing business owners that wants to make money online and doing it at home.


With the boom in the Adsense market comes the need for sites to want fresh, quality and original keyword rich content. This way, web site owners can have a steady supply of articles with the proper keywords that they relate to their site contents. The result of this is seen in the sites page rank when indexed by the search engines. Which, in turn, gets moreAdsense ads to show above, below or next to the article on their website with targeted traffic.
What do people have to do?


Write quality and original content, keyword or phrase rich articles with links to your website in the resource box. Then build a website or web page with targeted keyword or phrase rich original content for the targeted traffic that originates from the articles you wrote. Finally, you will have a Google Adsense ads that are targeted to your keyword or phrase rich original content site where visitors will get to visit when they come looking for information.


A win-win situation if you think more about it. A favor for persons looking for quality content and information. For the persons writing the original content articles. And the person with the quality original content rich website. Of course, the search engines and its advertisers are getting targeted traffic and sales but so what? As long as you are getting something in your favor, it does not really matter what the others are getting for themselves.


So who else wants to start earning money with Adsense. You. Everyone. Anybody. Internet marketing has many opportunities wide open for this people. Writing articles and using Adsense for your kind of internet marketing strategy is one sure way of getting a

piece of that action and cash.
Better not be left behind the many making millions already.

BACKLINK

Sunday, September 30, 2007

Reverse Engineering To Learn .NET Better

Introduction

The Microsoft .NET Framework is a new, exciting programming platform for Windows application developers (and potentially for developers on other Operating Systems, as we shall see). Right from the outset .NET has offered a large volume of functionality, both in terms of the underlying CLR and also the accompanying class library that is used by all .NET applications.

Becoming familiar with .NET can be achieved by making use of the mass of documentation in the .NET Framework SDK. Of course there is also a mass of third party documentation covering various aspects of the .NET Framework available in books and online on the Internet.

However it is sometimes said that a programmer can become most familiar with a system if they know exactly how it works. This can perhaps be best achieved if you have access to the underlying source of the system. Take, for example, C and C++ compilers and the Delphi compiler. These 3GL programming languages come supplied with the full source to their entire run-time library (RTL) as well as any class libraries they may use. Having full source code means any question as to the behaviour or implementation of any library feature can be readily resolved by looking at the pertinent source files.

Of course .NET does not ship with its source, but there are various tricks we can use in an attempt to overcome that hurdle, and get the same results as if we did have access to it. This is the remit of reverse engineering and this paper looks at various approaches that we can take in reverse engineering aspects of .NET, simply to understand its behaviour and operation better.

The Options Available To Us

Most reverse engineering options that are open to us stem from one of the key features of managed executable files: metadata. We'll look at the list of reverse engineering options and then look in detail at each of them. But first we'll go off on a slight tangent.

CLI Implementations

Note that I used the phrase managed executable files, rather than .NET executable files? This was an intentional choice and was intended to emphasise the fact that the Microsoft .NET Framework is not the only platform that can execute managed executables. You should be aware that the Microsoft .NET is one particular implementation of an ECMA (European Computer Manufacturers Association) standard.

ECMA 335 is the standard for the Common Language Infrastructure, or CLI . The Microsoft .NET Framework is one example of an implementation of the CLI (it implements the CLI as well as a whole host of additional tools and classes).

The CLI defines the fact that managed executables use the Portable Executable (PE) File Format, as used by Win32 executables. The PE files contain a standard header and then contain metadata and IL (Intermediate Language) code in special sections of the file. The IL code represents the functionality in the file, which will be compiled into native machine instructions prior to execution (usually) with the JIT (just in time) compiler. A module is an example of such a file. An assembly is one or more modules combined with additional metadata called a manifest, which names and describes the assembly, and lists assemblies it depends upon.

It therefore follows that any implementation of the CLI supports assemblies in the PE format.

At the time of writing there are five implementations of the CLI in existence or under development:

  1. Microsoft .NET Framework . This was the initial CLI implementation and it supports Windows platforms (Windows 98, Windows Me, Windows 2000, Windows XP, Windows Server 2003). It is freely downloadable in binary form (source code is not supplied). You can get just the redistributable version (suitable for deploying to machines to execute managed executables against) or the SDK (which includes additional tools, documentation and developer support). At the time of writing the current version of the .NET Framework is v1.1, which supersedes version v1.0 SP2.

  2. Microsoft .NET Compact Framework (. This is a CLI implementation for running on small devices that run Windows CE .NET . The implementation is much more lightweight than the full desktop .NET Framework and is tuned for the limited memory and storage of small devices. At the time of writing the current version of the .NET Compact Framework is v1.0 and can be used to develop application in conjunction with Visual Studio .NET 2003.

  3. Microsoft Shared Source CLI or SSCLI, codenamed Rotor . This is an implementation of the CLI (along with other parts of the Microsoft .NET Framework) that can run on multiple platforms. The supported platforms are Windows XP, FreeBSD 4.7 and Mac OS X 10.2, but it should also work fine on Windows 2000 and earlier versions of FreeBSD. You can freely download the entire source for SSCLI (over 3,000,000 lines of code) for non-commercial purposes.

  4. Mono, which is a project sponsored by and mainly developed by Ximian and runs on Linux and Windows. This is an implementation of the CLI, but which also endeavours to implement various other parts of Microsoft's .NET Framework such as ASP.NET, ADO.NET and VB.NET (called Basic.NET). It will also endeavour to get some level of support for WinForms for GUI applications. At the time of writing the current version is Mono 0.24, but version 1.0 is expected in Q4, 2003.

  5. Portable.NET from the DotGNU project . While the initial target platform was GNU/Linux, it is also known to run under Windows, Solaris, NetBSD, FreeBSD, and MacOS X. The runtime engine has been tested on the x86, PowerPC, ARM, Sparc, PARISC, s309, Alpha, and IA-64 processors. At the time of writing Portable.NET 0.5.6 was the current version.

Self-describing Assemblies

Getting back to the point made at the start of the previous section, assemblies are rich with metadata, making it easy to peer into them and identify the structure of what's inside. You can identify all the namespaces, classes, their methods, fields and properties, structures, enumerations and so on using a mechanism in .NET called reflection.

There are a variety of utilities that use reflection to show the structure and content of .NET assemblies and we'll bump into them as we look at the various reverse engineering options.

IL Examination

The only thing not directly exposed through metadata is the IL code itself. However this hurdle is easily surmountable with existing code and utilities available to read and display IL opcodes directly from a PE file.

High-level Language Decompilers

As well as being able to look at the low level IL code, which may not be palatable to many programmers, there are various tools which will decompile this IL to a high level language, such as C#, VB.NET or C++ with Managed Extensions. These tools examine the IL to identify patterns that allow them to substitute high level statements in their place.

Source Code

The various non-commercial implementations of the CLI all ship with source code. The most interesting example is Microsoft's own Shared Source CLI. This project started from the commercial .NET code base, although there have been multitudes of changes and some subsystems have been entirely reworked.

That notwithstanding, these source bases often offer a fascinating and educational insight into the working (or potential working) of the Microsoft .NET Framework. In many cases, the use of a high level language decompiler in conjunction with the SSCLI source can be a very productive pairing.

Examining The Assembly IL

Let's start off with (almost) the simplest C# application we can build, the classic Hello World application.


using System;

namespace Hello
{
public class HelloClass
{
public static void Main()
{
Console.WriteLine("Hello world");
}
}
}

After looking at results we get with this simple example, we'll turn our attention to looking at an already implemented method in the .NET Framework Class Library, namely the System.Collections.ArrayList class's InsertRange method. This method has been selected reasonably arbitrarily, although it is an example of a method that performs a reasonable amount of work.

IL Disassembler

The .NET Framework comes with a utility that takes source files containing IL code and metadata directives and compiles them into either modules or assemblies. The utility, ilasm.exe, is called the IL Assembler.

When you install the .NET Framework SDK (or Microsoft Visual Studio .NET or Borland C#Builder, which both include the Framework SDK) you get another utility called ildasm.exe, the IL Disassembler. As the name suggests this tool takes a compiled module/assembly and shows you the IL/metadata that constitutes it.

So rich is the IL code and metadata combination that you can use this utility to produce IL source code files that can be run through ilasm.exe to recreate a fully functioning binary. This process is called round tripping and emphasises the lack of ambiguity in the IL/metadata found in every managed executable.

The IL Disassembler operates in two modes, GUI and console. You can tell it which assembly to disassemble by passing the name on the command-line. If you tell it to generate IL source files using the /OUT command-line parameter it operates as a console application. It also runs as a console application if you pass the /TEXT parameter.

If you just pass the assembly name (or no parameters at all) it launches as a GUI app, which is often more convenient for browsing. When launched with no parameters you can use the File | Open menu (or Ctrl+O) to choose an assembly to disassemble, or alternatively drag a file onto the UI from Windows Explorer.

Running ildasm.exe on the simple Hello World application produces this.

As you can see, the tree view shows the assembly manifest and the simple namespace at the top level. In the namespace is our single class and within the class you can see its single static method, Main, as well as a reference to some other internal class elements. These include the instance constructor (.ctor), which is never used in this simple case as we do not construct an instance of HelloClass. The first item is some metadata to indicate the class does not require a class constructor (sometimes called a type initialiser), which is a method that automatically executes before the class is used, to initialise static fields.

Note that by default ILDasm will display all members of a class. If you only want, for example, public and protected (family) members displayed you can use the appropriate items on the View menu or invoke ildasm.exe with the /VIS=PUB+FAM command-line switch.

Double-clicking on any item in the tree view (or selecting it and pressing Enter) shows you the implementation of the item. The Main method shows up like this:

Since the original C# code was trivial, this should be readily understandable. The Hello world string is loaded onto the stack and the System.Console.WriteLine method is called, using the string on the stack as its parameter.

So we see that simple code is evidently readable enough. However a nice touch we can add is to get the disassembly to include the corresponding source code lines as comments just before the disassembled IL, assuming the assembly was compiled with debug information and the source is available. The View | Source menu item does this or you can use the /SOU command-line switch to do the job.

Now what about something a little more taxing, such as the InsertRange method of the ArrayList class? You can locate this method by running ildasm.exe on the mscorlib.dll assembly. You can find this file in the $(windir)\Microsoft.NET\Framework\$(version) directory, where windir is an environment variable that points to your Windows installation directory and version is a fictitious environment variable that equates to v1.0.3705 for .NET 1.0 and v1.1.4322 for .NET 1.1.

This screenshot shows how to locate the right class within the namespaces. First look in the System namespace, then in the nested System.Collections namespace. Then scroll down the methods until you bump into InsertRange.

Because of the fact that there are several statements in this method you will find the IL code quite verbose. The following listing shows the code:






.method public hidebysig newslot virtual 
        instance void  InsertRange(int32 index,
                                   class System.Collections.ICollection c) cil managed
{
  // Code size       228 (0xe4)
  .maxstack  6
  .locals (int32 V_0)
  IL_0000:  ldarg.2
  IL_0001:  brtrue.s   IL_0018
  IL_0003:  ldstr      "c"
  IL_0008:  ldstr      "ArgumentNull_Collection"
  IL_000d:  call       string System.Environment::GetResourceString(string)
  IL_0012:  newobj     instance void System.ArgumentNullException::.ctor(string,
                                                                         string)
  IL_0017:  throw
  IL_0018:  ldarg.1
  IL_0019:  ldc.i4.0
  IL_001a:  blt.s      IL_0025
  IL_001c:  ldarg.1
  IL_001d:  ldarg.0
  IL_001e:  ldfld      int32 System.Collections.ArrayList::_size
  IL_0023:  ble.s      IL_003a
  IL_0025:  ldstr      "index"
  IL_002a:  ldstr      "ArgumentOutOfRange_Index"
  IL_002f:  call       string System.Environment::GetResourceString(string)
  IL_0034:  newobj     instance void System.ArgumentOutOfRangeException::.ctor(string,
                                                                               string)
  IL_0039:  throw
  IL_003a:  ldarg.2
  IL_003b:  callvirt   instance int32 System.Collections.ICollection::get_Count()
  IL_0040:  stloc.0
  IL_0041:  ldloc.0
  IL_0042:  ldc.i4.0
  IL_0043:  ble        IL_00e3
  IL_0048:  ldarg.0
  IL_0049:  ldarg.0
  IL_004a:  ldfld      int32 System.Collections.ArrayList::_size
  IL_004f:  ldloc.0
  IL_0050:  add
  IL_0051:  call       instance void System.Collections.ArrayList::EnsureCapacity(int32)
  IL_0056:  ldarg.1
  IL_0057:  ldarg.0
  IL_0058:  ldfld      int32 System.Collections.ArrayList::_size
  IL_005d:  bge.s      IL_007c
  IL_005f:  ldarg.0
  IL_0060:  ldfld      object[] System.Collections.ArrayList::_items
  IL_0065:  ldarg.1
  IL_0066:  ldarg.0
  IL_0067:  ldfld      object[] System.Collections.ArrayList::_items
  IL_006c:  ldarg.1
  IL_006d:  ldloc.0
  IL_006e:  add
  IL_006f:  ldarg.0
  IL_0070:  ldfld      int32 System.Collections.ArrayList::_size
  IL_0075:  ldarg.1
  IL_0076:  sub
  IL_0077:  call       void System.Array::Copy(class System.Array,
                                               int32,
                                               class System.Array,
                                               int32,
                                               int32)
  IL_007c:  ldarg.0
  IL_007d:  ldarg.2
  IL_007e:  callvirt   instance object System.Collections.ICollection::get_SyncRoot()
  IL_0083:  bne.un.s   IL_00ba
  IL_0085:  ldarg.0
  IL_0086:  ldfld      object[] System.Collections.ArrayList::_items
  IL_008b:  ldc.i4.0
  IL_008c:  ldarg.0
  IL_008d:  ldfld      object[] System.Collections.ArrayList::_items
  IL_0092:  ldarg.1
  IL_0093:  ldarg.1
  IL_0094:  call       void System.Array::Copy(class System.Array,
                                               int32,
                                               class System.Array,
                                               int32,
                                               int32)
  IL_0099:  ldarg.0
  IL_009a:  ldfld      object[] System.Collections.ArrayList::_items
  IL_009f:  ldarg.1
  IL_00a0:  ldloc.0
  IL_00a1:  add
  IL_00a2:  ldarg.0
  IL_00a3:  ldfld      object[] System.Collections.ArrayList::_items
  IL_00a8:  ldarg.1
  IL_00a9:  ldc.i4.2
  IL_00aa:  mul
  IL_00ab:  ldarg.0
  IL_00ac:  ldfld      int32 System.Collections.ArrayList::_size
  IL_00b1:  ldarg.1
  IL_00b2:  sub
  IL_00b3:  call       void System.Array::Copy(class System.Array,
                                               int32,
                                               class System.Array,
                                               int32,
                                               int32)
  IL_00b8:  br.s       IL_00c7
  IL_00ba:  ldarg.2
  IL_00bb:  ldarg.0
  IL_00bc:  ldfld      object[] System.Collections.ArrayList::_items
  IL_00c1:  ldarg.1
  IL_00c2:  callvirt   instance void System.Collections.ICollection::CopyTo(class System.Array,
                                                                            int32)
  IL_00c7:  ldarg.0
  IL_00c8:  dup
  IL_00c9:  ldfld      int32 System.Collections.ArrayList::_size
  IL_00ce:  ldloc.0
  IL_00cf:  add
  IL_00d0:  stfld      int32 System.Collections.ArrayList::_size
  IL_00d5:  ldarg.0
  IL_00d6:  dup
  IL_00d7:  ldfld      int32 System.Collections.ArrayList::_version
  IL_00dc:  ldc.i4.1
  IL_00dd:  add
  IL_00de:  stfld      int32 System.Collections.ArrayList::_version
  IL_00e3:  ret
} // end of method ArrayList::InsertRange


Of course it's much less intelligible now we have more code, but with the metadata and IL documentation, which can be found in Partitions II and III of the CLI specification respectively or Inside Microsoft .NET IL Assembler we could still work it out. However it would take rather longer than most of us would be prepared for.

Reflector


Lutz Roeder is a developer working at Microsoft and he has his own personal Web site. There you can find a popular tool called Reflector, which is at version 3.0.0.1 at the time of writing. You can load assemblies into Reflector either using the File | Open... menu (or Ctrl+O) or by dragging them onto the UI from Windows Explorer.

Reflector does a similar job to the IL Disassembler in that it displays information about an assembly by reflecting across the metadata. However one key difference is that it only shows you public and protected items by default (you can change this in the options: View | Options...).

When you select a method it will display the method signature at the bottom of the main window in C# syntax by default, although you can switch it to show Visual Basic.NET syntax in the Languages menu. You can disassemble a method by selecting it and then choosing Tools | Disassembler (or by pressing Enter). Since the IL code in a given assembly is fixed, you will get much the same results from any tool:



However as you might be able to see, there are additional facilities in Reflector over ildasm.exe, such as the lists of base types and descendant types, a list of dependencies for the assembly, the ability to search for types in an assembly and a call tree. Also, the disassembly window explains the IL instructions using tooltips when you pause your mouse over them. These all make Reflector a useful tool to have available.

Borland Type Browser


Borland's new C#Builder development tool also includes a reflection browser and IL disassembler called Reflection.exe. By default the tool appears to simply be a reflection browser but it can be enticed into offering IL disassembly by adding a simple registry entry.

In the registry key HKEY_CURRENT_USER\Software\Borland\BDS\1.0\Globals a string value called ShowILDissassembly with a value of 1 will mean an extra Code page is displayed when looking at a method. Note carefully the spelling of ShowILDissassembly before creating the value in the registry.



The SSCLI IL Disassembler Source


The Microsoft SSCLI download is supplied as a massive source tree. It contains the source to the class libraries, the C# compiler the IL assembler and the IL disassembler and many other interesting bits and pieces. The IL disassembler is functionally identical to the commercial .NET Framework version except that it does not offer a GUI interface (SSCLI has no GUI support; it is just for building console applications).

If the SSCLI installation directory is referred to as $(ROTOR_DIR) then the ildasm.exe source is located in $(ROTOR_DIR)\clr\src\ildasm. You can peruse and learn from this source base to see how IL disassembly can be performed. If you need to build a custom IL viewer and can read C++ this would be a good place to start perusing.

You can use the Visual Studio .NET debugger to step through the SSCLI IL disassembler in order to understand how it operates, assuming you have built the SSCLI source base appropriately. If not, you will need to follow the SSCLI instructions in order to get a checked (full debug, optimisations on) or fast checked (full debug info, optimisations off) build of SSCLI first. Your best bet for debugging would be the checked build.

In principle, building SSCLI is actually straightforward thanks to the excellent configuration tools and files used in the source tree. You need to first install a Perl 5.6 implementation, for example ActivePerl (see Reference 11). You also need Visual Studio.NET installed before you can proceed. Installing SSCLI itself is simply a matter of extracting the files from the compressed tarball (i.e. a gzip compressed tar file) they are supplied in. WinZip or WinRar should do the trick.

In a command prompt window change to the SSCLI root installation directory and setup the SSCLI environment. The env.bat batch file will do this, and it takes parameters to set up for checked, fast checked or free mode. Once set up, your command prompt environment will have a variety of environment variables pointing to parts of the SSCLI directory tree, including ROTOR_DIR, which does indeed point to the main installation directory.

To set up the environment for checked mode execute:


env checked


Next you invoke the build process by executing:


buildall.cmd


As you might expect, the build process can be rather lengthy, depending on your hardware, but it should get there in the end.

With SSCLI built you will find ildasm.exe in $(TARGETCOMPLUSSDK)\bin. TARGETCOMPLUSSDK is another environment variable, which is equivalent to $(ROTOR_DIR)\build\v1.x86chk.rotor\sdk in the checked environment.

You can set up a Visual Studio.NET solution for ildasm.exe as follows:





  1. Choose File | Open Solution...





  2. Change the Files of type: entry to Executable files (*.exe)





  3. Locate the SSCLI ildasm.exe in $(TARGETCOMPLUSSDK)\bin





  4. Select the Solution Explorer with View | Solution Explorer or (Ctrl+Alt+L)





  5. Right-click the solution node and choose Properties





  6. Select Common Properties node, then the Debug Source Files node and add in the path to the ildasm source and also to the common SSCLI include files:





  7. View the project properties with Project | Properties





  8. In the Configuration Properties, Debugging node set use the Command Arguments entry to set up the ildasm.exe command-line arguments to refer to the assembly you wish to disassemble





  9. Optionally set the Working Directory option to point to the directory housing the specified assembly





  10. Save the solution using File | Save ildasm.sln (or Ctrl+S)





  11. Close Visual Studio.NET





  12. Create a batch file in the directory containing the ildasm.sln file that looks like this, substituting your SSCLI directory path as appropriate:


    REM Modify this line depending on where SSCLI is installed
    call c:\Tools\sscli\env checked

    REM Modify this line depending on where Visual Studio.NET is installed
    start C:\Tools\VS.NET\Common7\IDE\devenv.exe ildasm.sln


Now you can double-click the batch file and Visual Studio.NET will be launched in an SSCLI checked mode environment and will load your solution. You can then start debugging it as you would with your normal applications:



Lutz Roeder's IL Reader Class


Another option for writing your own IL displaying utility would be to use a helper class made available by Lutz Roeder . The ILReader class and its selection of helper classes are supplied in a C# source file accompanied by an example program that shows them in use.

The ILReader class relies on the calling program having access to the type whose methods require disassembling. The constructor takes two parameters: the target type's module and a class implementing the locally defined IAssemblyLoader interface. This interface defines two methods that the ILReader calls in order to load the assembly that implements the target type, Load and LoadFrom. The example program defines the trivial AssemblyLoader class with simple implementations.

Having constructed an ILReader you call its GetMethodBody method for any method you need to disassemble. GetMethodBody takes a MethodBase descendant (such as MethodInfo), which can be accessed through Type.GetMethod or Type.GetMethods, and returns a MethodBody object whose members provide all the information you require.



The sample program disassembles the System.Object class.

Obfuscators


There is an argument that suggests that programmers entering into the .NET world are opening their applications up to the eyes of the world. The rich metadata along with the type rich IL code allows any managed code in an assembly to be disassembled back to readable IL and there is the worry about intellectual copyright issues with your algorithms on public display.

This is true enough; managed code can indeed be disassembled. However this is not a new problem. Java byte code can also be decompiled in much the same way and this did not impede the acceptance of the Java programming language. JavaScript code in Web pages that produces nice effects can be directly read. Even standard Win32 applications can be readily disassembled by various utilities. Of course, Intel x86 machine code is not as well structured and is more troublesome to correctly and completely disassemble, but the principle holds true.

IL is easier to disassemble because of the richness of the metadata in the assembly, which is there because it makes various internal operations much easier, not because it has to be. The general advice if this is an issue to you is to make use of an obfuscator.

Microsoft Visual Studio.NET 2003 ships with an obfuscator called Dotfuscator from PreEmptive Solutions (installed into PreEmptive Solutions\Dotfuscator Community Edition directory under the main Visual Studio installation directory). Dotfuscator ships in two versions, the Professional Edition and the Community Edition. The latter is the cut-down version that ships with Visual Studio.NET 2003 and basically renames your classes and methods with unhelpful choices of names, whilst the former has many additional features and comes with a price tag. You can find out more about Dotfuscator from the links at Reference 12.

Borland C#Builder ships with a version of another obfuscator called Demeanor for .NET from Wise Owl. This product holds a good reputation in obfuscation circles and you can find out more from the link at . Demeanor is available in an Enterprise Edition, which you pay money for, or the Personal Edition, as supplied with C#Builder. The Personal Edition does simple renaming of methods and classes whilst the Enterprise Edition has additional features that make it an attractive purchase.

Reconstituting The original Source



Viewing IL is all well and good, but despite its completeness, it is still not very readable. Various tools have surfaced which allow you to reconstitute high level statements from the IL in the assembly. They analyse the IL instruction sequences and use this information to build up a representation of the original source code in a given high-level language (some tools support this decompiling process with various high level language targets).

Anakrino / Exemplar


One of the first products to offer decompilation support was Exemplar by Jay Freeman (aka saurik). Exemplar is a command-line tool, which has been superseded by Anakrino, his GUI version (anakrino is a Greek word meaning to examine). You can find these tools at the URL listed in . The current version at the time of writing is 1.0.0.1.

Anakrino doesn't (currently) support drag and drop from Windows Explorer so you must open assemblies with the File | Open... menu item or by pressing Ctrl+O. Drilling down to a method allows you to see its high-level representation in either C# (by default) or managed C++ (if you select Dialect | MC++). Here is the Main method in our trivial test case displayed in C#.



By contrast, this is what it looks like in Managed C++:



This utility is very helpful, but has a number of "quirks". If you switch language (dialect) whilst a method is displayed, the decompiled version is not updated. You must select a method then reselect the previous method to see it decompiled into the new language. Also, as you can in the screenshots above, the bottom pane (coloured light yellow) seems to be unused at present. The equivalent panel in Reflector displays the methods signature/prototype.

When you download Anakrino, it includes the command-line Exemplar tool. You can use this to disassemble our test case assembly as shown here.



Moving onto the ArrayList method, we get much more interesting results here. Anakrino produces this listing as the C# source for the InsertRange method:


public virtual void InsertRange(int index, ICollection c) {
int local0;

if (c == null)
throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
if (index < 0 || index > this._size)
throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
local0 = c.Count;
if (local0 > 0) {
this.EnsureCapacity(this._size + local0);
if (index < this._size)
Array.Copy(this._items, index, this._items, index + local0, this._size - index);
if (this == c.SyncRoot) {
Array.Copy(this._items, 0, this._items, index, index);
Array.Copy(this._items, index + local0, this._items, index * 2, this._size - index);
}
else
c.CopyTo(this._items, index);
this._size = this._size + local0;
this._version = this._version + 1;
}
}


As you can see this is much more meaningful that the pure IL code behind it. All we have lacking here is sensible names for the local variables along with any comments that might have helped us understand the intent of the code. However even without those you can see this is substantially easier to work with now.

Reflector


We have already looked at Reflector in the context of an IL disassembler but from version 3 it also offers high-level language decompilation support. The currently supported languages are C# and VB.NET (selectable in the Languages menu). Decompiling a selected method is much the same as disassembling it. Disassembly is attained by Tools | Disassembler (or Enter), whilst you decompile a method with Tools | Decompiler (or Space).

The results when decompiling the Hello World method are the same as with Anakrino. The following listing is what we get when decompiling ArrayList.InsertRange.


public virtual void InsertRange(int index, ICollection c)
{
int num1;
if (c == null)
{
throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
}
if ((index < 0) || (index > this._size))
{
throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
}
num1 = c.Count;
if (num1 > 0)
{
this.EnsureCapacity((this._size + num1));
if (index < this._size)
{
Array.Copy(this._items, index, this._items, (index + num1), (this._size - index));
}
if (this == c.SyncRoot)
{
Array.Copy(this._items, 0, this._items, index, index);
Array.Copy(this._items, (index + num1),
this._items, (index * 2), (this._size - index));
}
else
{
c.CopyTo(this._items, index);
}
this._size = (this._size + num1);
this._version = (this._version + 1);
}
}


Again, much the same as Anakrino; the only differences are subtle and include:





  • a different local variable name (num1 instead of local0)





  • different brace layout conventions; Anakroni puts the opening brace at the end of a line of code whilst Reflector puts them on their own line, in the same column as the previous line of code started





  • explicit brackets around each condition, such as index < 0



LSW DotNet-Reflection-Browser


DotNet-Reflection-Browser is a commercial utility from Lesser-Software that offers reflection browsing and decompilation support in the style of a SmallTalk System Browser (see Reference 15). It has various other features that are not relevant here; for our purposes you should know that it decompiles to C# or MSIL (i.e. it decompiles and disassembles).



Running it against the InsertRange method produces this code:


public virtual void InsertRange (int index, ICollection c)
{
if (c == null)
{
throw new ArgumentNullException ("c", Environment.GetResourceString ("ArgumentNull_Collection"));
}

if ((index < 0) || (index > this._size))
{
throw new ArgumentOutOfRangeException ("index", Environment.GetResourceString ("ArgumentOutOfRange_Index"));
}

int i = c.Count;
if (i > 0)
{
this.EnsureCapacity ((this._size + i));
if (index < this._size)
{
Array.Copy (this._items, index, this._items, (index + i), (this._size - index));
}

if (this == c.SyncRoot)
{
Array.Copy (this._items, 0, this._items, index, index);
Array.Copy (this._items, (index + i), this._items, (index * 2), (this._size - index));
}
else
{
c.CopyTo (this._items, index);
}

this._size += i;
this._version++;
}

return;
}


Again it is much the same as we have seen before, although there a couple of slight differences:





  • Local variables are declared at the latest point (just before they are required) instead of right at the start





  • Variable incrementing is represented with the ++ operator





  • Explicit return statements are always used to exit methods, even at the end of the method implementation





  • explicit brackets around each condition, such as index < 0



Alternative (And Sometimes Identical) Source Implementations



As detailed earlier there are various implementations of the CLI of which Microsoft's .NET Framework is the most well known and successful. However Microsoft's Shared Source CLI is worth paying some close attention to. This implementation started its life from the same code base that .NET has grown from and, whilst parts of it have been changed for various reasons (such as to aid porting to other platforms or to help keep certain algorithms in the domain of the Microsoft engineers), much of it is still a direct representation of code in the commercial platform.

This is, of course, not true of the other non-Microsoft implementations, but it can still be very fruitful delving into their source trees.

SSCLI


Assuming the SSCLI has been installed into a directory referred to as $(ROTOR_DIR) the source for the ArrayList class can be found in $(ROTOR_DIR)\clr\src\bcl\system\collections\arraylist.cs. The method we are looking at is InsertRange, and the source code for it looks like this:


// Inserts the elements of the given collection at a given index. If
// required, the capacity of the list is increased to twice the previous
// capacity or the new size, whichever is larger. Ranges may be added
// to the end of the list by setting index to the ArrayList's size.
//
/// <include file='doc\ArrayList.uex' path='docs/doc[@for="ArrayList.InsertRange"]/*' />
public virtual void InsertRange(int index, ICollection c) {
if (c==null)
throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
if (index < 0 || index > _size) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
int count = c.Count;
if (count > 0) {
EnsureCapacity(_size + count);
if (index < _size) {
Array.Copy(_items, index, _items, index + count, _size - index);
}
// Hack hack hack
// If we're inserting a ArrayList into itself, we want to be able to deal with that.
if (this == c.SyncRoot) {
// Copy first part of _items to insert location
Array.Copy(_items, 0, _items, index, index);
// Copy last part of _items back to inserted location
Array.Copy(_items, index+count, _items, index*2, _size-index);
}
else
c.CopyTo(_items, index);
_size += count;
_version++;
}
}


Some key things to note about this code are:





  • it is exactly the same logic as decompiled by Anakrino, Reflector and LSW-DNRB





  • the local variable has a descriptive name





  • there are comments describing what goes on in the code



In cases where the SSCLI still uses the same code as .NET, any effort in working out the gaps left by the decompilers is completely avoided. It is usually worth decompiling with one of the tools, then comparing with the equivalent source in SSCLI to see if the shared source file can be used as a reference to the .NET implementation.

Mono


The Mono implementation is based on reading the CLI specification and implementing the described behaviour. Therefore you don't learn anything about the inner workings of .NET by browsing Mono source, but it does give you an option to see alternative ways to implement the behaviour as outlined in the ECMA CLI specification.

If the Mono 0.24 source files are installed in $(MONO) then the ArrayList source will be found in $(MONO)\mcs-0.24\class\corlib\System.Collections\ArrayList.cs. The InsertRange method implementation looks like this:


public override void InsertRange (int index, ICollection col) {
if (col == null)
throw new ArgumentNullException ();
if (index < 0 || index > Count)
throw new ArgumentOutOfRangeException ();
if (IsReadOnly || IsFixedSize)
throw new NotSupportedException ();

if (index == Count) {
foreach (object element in col)
list.Insert (index++, element);
}
}


As you can see, this is somewhat different in its approach, but does the same thing as far as the CLI class library specification goes.

Portable.NET


The situation with Portable.NET is much the same as with Mono with regard to its implementation. If Portable.NET 0.5.6 is installed in a directory referred to as $(PNET) then you can find the ArrayList source in $(PNET)\pnetlib-0.5.6\runtime\System\Collections\ArrayList.cs. The InsertRange method looks like this:


// Insert the contents of a collection as a range.

2.1191269304&ga_sid=1191269304&ga_hid=1507543749&flash=9&u_h=768&u_w=1024&u_ah=738&u_aw=1024&u_cd=32&u_tz=330&u_java=true" frameborder="0" width="250" scrolling="no" height="250" allowtransparency>


public virtual void InsertRange(int index, ICollection c)
{
int cCount;
IEnumerator enumerator;
if(c == null)
{
throw new ArgumentNullException("c");
}
if(index < 0 || index > count)
{
throw new ArgumentOutOfRangeException
("index", _("ArgRange_Array"));
}
cCount = c.Count;
Realloc(cCount, index);
enumerator = c.GetEnumerator();
while(enumerator.MoveNext())
{
store[index++] = enumerator.Current;
}
count += cCount;
++generation;
}




Reverse Engineering To Learn .NET Better

Technorati Tags: , , ,