Sunday, May 30, 2010

programs on accessing databases

Accessing Databases


12.1 Introduction

The .NET Framework uses ADO.NET to access databases. ADO.NET is a set of

managed classes within the .NET Framework. It operates on a disconnected data

access model. When an application requests data a new database connection is

created and destroyed when the request is completed. A disconnected data access

model is frugal with resources, which is desirable.

One important advantage of ADO.NET is that information is stored and transferred

in XML.

ADO.NET allows access to many different databases. There are two basic types of

connections. SQLClient is used for Microsoft’s SQL Server and OLEDB is used for

all other database formats.

In order to use SQL with SQL Server or SQL Server Express the following namespace

must be specified.

using System.Data.SqlClient;

In order to use OLEDB the following namespace must be specified.

using System.Data.OleDb;

When designing a database, the correct datatypes must be chosen. Note the following.

• Choosing an incorect datatype can degrade performance, because of data conversion.

• Choosing too small a datatype cannot meet the system need and at some point

your system may become useless.

• Choosing too large a datatype can waste space and increase capital expenses.

156

12.1. INTRODUCTION 157

Accessing a database requires that a .NET Data Provider is set up. A Data Provider

consists of

• Connection

• Command

• DataReader

• DataAdapter

A Dataset can be understood as a virtual database table that resides in RAM. A

DataSet can contain a set of tables with all the metadata necessary to represent

the structure of the original database. It can be manipulated and updated independantly

of the database. A DataSet is not always required.

The Connection creates the actual connection to the database. The Connection

object contains all the information needed to open the connection, for example the

userid and password. A Connection String is required to connect to a database.

The easiest way to find the correct connection string for a DBMS is to search the

Web. A good site to start with is http://www.connectionstrings.com.

In order to open and close a database connection the Connection methods Open()

and Close() are used respectively.

The Command executes an instruction against the database. For example, this

could be a SQL query or a stored procedure. There are three options for executing

a Command.

1. ExecuteReader: Used for accessing data. Can return multiple rows. Readonly,

forward-only.

2. ExecuteScalar: Used to retrieve a value from a single field.

3. ExecuteNonQuery: Used for data manipulation, such as Delete, Update and

Insert.

A DataReader is used for fast and efficient forward-reading, read-only database

access. It returns individual rows directly to the application. The data is not cached.

In the case that the data is read-only and rarely updated, then a DataAdapter can

be used. A DataAdapter handles retrieving and updating data. It is a middle-layer

between the database (Data Provider) and the disconnected DataSet. It decouples

the DataSet from the database and allows a single DataSet to represent more than

one database. The data adapter fills a DataSet object when reading the data and

writes in a single batch when writing changes back to the data base.

158 CHAPTER 12. ACCESSING DATABASES

An important control is the SqlDataSource. It allows connections to most relational

databases. The default provider is for Microsoft SQL Server, but providers

for other databases are provided. For example, for Oracle.

An important concept is that of Data-Bound controls. They provide a link

between the data source controls and the user. Any data source control can be

selected and linked to any data-bound controls.

12.2 Examples

The following program shows how to connect to a Microsoft SQL Server 2005 or

Sql Express database and retrieve some basic information about the server. Care

must be taken is creating an appropriate connection string (CON STRING in these

examples). Knowledge of SQL is required.

using System;

using System.Data;

using System.Data.SqlClient;

using System.Text;

namespace csDB

{

class OpenSQLDB

{

public const string CON_STRING = "Data Source=mySqlDB;Initial

Catalog=dbTest;User ID=JoeUser;Password=User1234";

static void Main(string[] args)

{

Console.WriteLine("Connecting to db\n");

SqlConnection connection = new SqlConnection(CON_STRING);

connection.Open();

Console.WriteLine("Server Version: {0}",

connection.ServerVersion);

Console.WriteLine("Database: {0}",

connection.Database);

Console.WriteLine("Data Source: {0}",

connection.DataSource);

Console.WriteLine("Stats Enabled: {0}",

connection.StatisticsEnabled);

connection.Close();

} // end Main

} // end class

} // end namespace

12.2. EXAMPLES 159

Embedding the user ID and password, as in the above code, is not a good idea; a

change in the database means the code will have to be modified and recompiled. In

addition, it is not secure. ASP.NET solves this problem by using the web.config file.

The following is an extract from a web.config file.







value="server=mySqlDB.newdb,net;initial

catalog=dbTest;user id=JoeUser;pwd=User1234;"/>



...

<\configuration>

The following line of code fetches the connection string from web.config.

string CON_STRING=ConfigurationSettings.Appsettings["DBConnect"];

The basic method of retrieving data using a Data Reader is as follows. A standard

SQL query is used and submitted to the database via a SqlCommand object. In the

case of data retrieval the SELECT statement is used.

SqlCommand cmDB = new SqlCommand();

cmDB.Connection = connection ;

cmDB.CommandText = "SELECT * FROM tblCustomer";

SqlDataReader myReader = cmDB.ExecuteReader();

while(myReader.Read())

{

// Do something with the data

// In the following code, the myReader[0]

// accesses the FIRST field of data in the

// the selected record. myReader[1] then

// accesses the SECOND field.

// ---------------------------------------

// string strTmp = myReader[0].ToString() +

// " " + myReader[1].ToString();

}

myReader.Close();

A wildcard search can be achieved as follows. The assumption is that a TextBox

called txtSearch is used to input the search parameters. A Trim() is used to remove

whitespace.

160 CHAPTER 12. ACCESSING DATABASES

string strSearch =

"SELECT * FROM tblCustomer where fldCustName LIKE ’" +

TextBox1.Text.Trim() + "%’"; // SQL uses a % for wildcard

myReader = cmDB.ExecuteReader();

The following code shows how to access particular fields using a Data Reader.

// Access SQL EXPRESS db and retrieve data and put field

// values into variables

string CON_STRING =

"Data Source=ejd\\sqlexpress;" +

"Initial Catalog=dbCustomer;Integrated Security=True";

string strSQL =

"SELECT * FROM tblCustomer WHERE" +

" fldCustSurname = ’Hardy’ AND fldCustName = ’Frank’";

int id; string strName, strSurname; float fltCred, fltBal;

SqlConnection connection = new SqlConnection(CON_STRING);

connection.Open();

SqlCommand cmDB = new SqlCommand();

cmDB.Connection = connection;

cmDB.CommandText = strSQL;

SqlDataReader rdr = cmDB.ExecuteReader();

while (rdr.Read())

{

id = (int) rdr["fldCustomerID"];

strName = (string) rdr["fldCustName"];

// etc etc

TextBox1.Text = id + " " + strName;

}

rdr.Close();

connection.Close();

The following code shows how to insert a row into a database, using a SQL INSERT

command.

String CON_STRING

= @"Data Source=mname\sqlexpress;" +

"Initial Catalog=dbCustomer" +

";Integrated Security=True";

SqlConnection connection = new SqlConnection(CON_STRING);

12.2. EXAMPLES 161

connection.Open();

String MyString

= @"INSERT INTO tblCustomer(fldCustName, fldCustSurname)" +

"VALUES(’Angus’, ’MacBeth’)";

SqlCommand MyCmd = new SqlCommand(MyString, connection);

MyCmd.ExecuteScalar();

connection.Close();

It is likely that the C# programmer will have a need to extract some data from a

TextBox, either on a Windows or Web Form, and insert that value into a table. In

the case of wanting to insert the values from two TextBoxes called txtFirstName

and txtSurname, you would have the following statement in C#.

String MyString

= @"INSERT INTO tblCustomer(fldCustName, fldCustSurname)"

+ "VALUES(’"

+ txtFirstName.Text.Trim()

+ "’, ’"

+ txtSurname.Text.Trim()

+ "’)";

The following code shows how to edit a row in a database, using a SQL UPDATE

command.

string CON_STRING =

@"Data Source=mname\sqlexpress;" +

"Initial Catalog=dbCustomer;Integrated Security=True";

SqlConnection connection = new SqlConnection(CON_STRING);

connection.Open();

String MyString =

"UPDATE tblCustomer SET fldCustCred = ’1000’ " +

"WHERE fldCustName = ’Angus’";

SqlCommand MyCmd = new SqlCommand(MyString, connection);

MyCmd.ExecuteNonQuery();

connection.Close();

The following code shows how to delete data in a database, using a SQL DELETE

command. This program uses a TRY... CATCH block for exception handling.

string CON_STRING =

"Data Source=mname\\sqlexpress;" +

"Initial Catalog=dbCustomer;" +

"Integrated Security=True";

162 CHAPTER 12. ACCESSING DATABASES

SqlConnection connection = new SqlConnection(CON_STRING);

try

{

connection.Open();

String MyString = @"DELETE FROM tblCustomer " +

"WHERE fldCustName = ’Greg’";

SqlCommand MyCmd = new SqlCommand(MyString, connection);

MyCmd.ExecuteScalar();

}

catch (SqlException sqlexp)

{

// SqlException catches SQL specific exceptions

// Use it when accessing SQL databases

}

catch (Exception ex)

{

TextBox1.Text = ex.Message;

}

finally

{

// The following code checks if the database is still open

// If it is, then it is closed

if(connection.State == ConnectionState.Open) connection.Close();

}

programs on remoting

Remoting


11.1 Introduction

Object running in one Virtual Machine (VM) should be able to access (call functions

on) objects that are in a different Virtual Machine. Usually this means the two

processes are running on two different computers. To set up remoting we proceed

as follows:

Instantiate target object (on server)

Setup our channels

Register our object (on server)

Get a reference to object (on client)

Call method on server from the client

For example on the Server side:

public class Server {

public static void Main() {

Target t = new Target();

RemotingServices.Marshal(t,"me");

IChannel c = new HttpChannel(8080);

ChannelServices.RegisterChannel(c);

Console.WriteLine("Server ready. ");

Console.ReadLine();

}

}

147

148 CHAPTER 11. REMOTING

On the Client side we have

public class Client {

public static void Main() {

string url = "http://localhost:8080/me";

Type to = typeof(Target);

Target t = (Target) RemotingServices.Connect(to,url);

Console.WriteLine("Connected to server.");

try {

string msg = Console.ReadLine();

t.sendMessage(msg);

} catch(Exception e) {

Console.WriteLine("Error " + e.Message);

}

}

}

A shared class (i.e. it is on the Client and Server side) is

public class Target : System.MarshalByRefObject

{

public void sendMessage(string msg)

{ System.Console.WriteLine(msg); }

}

Reference types passed via remoting must be serializable or marshal-by-reference.

Serializable == pass-by-value

Copy of object is made.

MarshalByRefObject == pass-by-reference

proxy object is passed.

An example for the second case is:

public class Counter : MarshalByRefObject {

int count = 0;

public void incCount() { count++; }

public int getCount() { return count; }

}

An example for the first case is:

11.1. INTRODUCTION 149

[Serializable]

public class Counter {

int count = 0;

public void incCount() { count++; }

public int getCount() { return count; }

}

We have to import:

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Http; // for Http

using System.Runtime.Remoting.Channels.Tcp; // for Tcp

Shared objects must be available to both the client and the server. Make a separate

assembly for it

csc /t:library Shared.cs

csc /r:Shared.dll Server.cs

csc /r:Shared.dll Client.cs

There are three main items in the process of remoting. The Interface, the Client

and the Server. The Interface is a .dll file (generated from a .cs file) which is on

the Client and Server side. The Server provides an implementation of the Interface

and makes it available to call. The Client calls the Server using the Interface.

150 CHAPTER 11. REMOTING

Example. The client provides a string to the server and the server returns the

length of the string.

We first create an interface with the file MyInterface.cs.

// MyInterface.cs

public interface MyInterface

{

int FunctionOne(string str);

}

MyInterface.cs is on the Server and Client side. We compile to MyInterface.dll

on the Client and Server side with

csc /t:library MyInterface.cs

On the Server side we now create our class which we invoke from a remote machine

(client). RemoteObject.cs is on the Server side and is the implementation of

MyInterface.cs.

// RemoteObject.cs

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

public class MyRemoteClass : MarshalByRefObject, MyInterface

{

public int FunctionOne(string str)

{

return str.Length; // length of string

}

}

Any class derived from MarshalByRefObject allows remote clients to invoke its

methods. Now on the Server side we compile

csc /t:library /r:MyInterface.dll RemoteObject.cs

This creates the file RemoteObject.dll on the Server side.

11.1. INTRODUCTION 151

Next we provide our Server on the Server side.

// ServerTcp.cs

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

public class MyServer

{

public static void Main()

{

TcpChannel m_TcpChan = new TcpChannel(9999);

ChannelServices.RegisterChannel(m_TcpChan,false);

RemotingConfiguration.RegisterWellKnownServiceType(

Type.GetType("MyRemoteClass,RemoteObject"),

"FirstRemote",WellKnownObjectMode.SingleCall);

System.Console.WriteLine("Press ENTER to quit");

System.Console.ReadLine();

} // end Main

} // end class MyServer

First we create a TcpChannel object which we use to transport messages across our

remoting boundary. We select 9999 as the TCP port to listen on. This could cause

problems with firewalls. We use

ChannelServices.RegisterChannel

to register our TcpChannel with the channel services. Then we use

RemoteConfiguration.RegisterWellKnownServiceType

to register our RemoteClass object as a well-known type. Now this object can be

remoted.

We are using the string FirstRemote here which will be used as part of the URL

that the client uses to access the remote object. We use SingleCall mode here

which means a new instance is created for each remote call.

Remark. One could have also used Singleton in which case one object would have

been instantiated and used by all connecting clients.

Next we generate the exe file for ServerTcp via

csc /r:RemoteObject.dll ServerTcp.cs

152 CHAPTER 11. REMOTING

Now we write our Client program ClientTcp.cs on the client side.

// ClientTcp.cs

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

public class MyClient

{

public static void Main()

{

TcpChannel m_TcpChan = new TcpChannel();

ChannelServices.RegisterChannel(m_TcpChan,false);

MyInterface m_RemoteObject =

(MyInterface) Activator.GetObject(typeof(MyInterface),

"tcp://192.168.0.3:9999/FirstRemote");

Console.WriteLine(m_RemoteObject.FunctionOne("willi hans steeb"));

// for LocalHost

// "tcp://LocalHost:9999/FirstRemote");

} // end Main

} // end class MyClient

Just as in the Server we have a TcpChannel object, though in this case we do not

have to specify a port. We also use

ChannelService.RegisterChannel

to register the channel. We use

Activator.GetObject

to obtain an instance of the MyInterface object. The IP address of the remote

machine (Server) is 192.168.0.3 with the port 9999. The string FirstRemote

which forms part of the URL was that we passed to

RemotingConfiguration.RegisterWellKnownServiceType

on the Server.

To obtain the ClientTcp.exe file we run

csc /r:MyInterface.dll ClientTcp.cs

11.1. INTRODUCTION 153

To run everything on the Server side we enter the exe file at the command line

ServerTcp

Then on the Client side we enter the exe file at the command line

ClientTcp

Then we are provide with the length of the string. In the present case 16.

154 CHAPTER 11. REMOTING

Instead of using Tcp we can also use Http. Then the files ServerTcp.cs and

ClientTcp.cs are replaced by ServerHttp.cs and ClientHttp.cs shown below.

// ServerHttp.cs

// on Server side

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Http;

public class MyServer

{

public static void Main()

{

HttpChannel chan = new HttpChannel(8080);

ChannelServices.RegisterChannel(chan,false);

RemotingConfiguration.RegisterWellKnownServiceType(

Type.GetType("MyRemoteClass,RemoteObject"),

"FirstRemote",WellKnownObjectMode.SingleCall);

System.Console.WriteLine("Press ENTER to quit");

System.Console.ReadLine();

} // end Main

} // end class MyServer

11.1. INTRODUCTION 155

// Client.cs

// on client side

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Http;

public class MyClient

{

public static void Main()

{

HttpChannel chan = new HttpChannel();

ChannelServices.RegisterChannel(chan,false);

MyInterface m_RemoteObject =

(MyInterface) Activator.GetObject(typeof(MyInterface),

"http://152.106.41.42:8080/FirstRemote");

Console.WriteLine(m_RemoteObject.FunctionOne("willi hans"));

// for LocalHost

// "tcp://LocalHost:9999/FirstRemote");

} // end Main

} // end class MyClient

programs on user datagram protocol

10.3 User Datagram Protocol


UDP is not very reliable (but fast) connectionless protocol.

Example. The UDPServer1.cs file is given by

// UDPServer1.cs

using System;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Configuration;

class ServerUDP

{

public static void Main()

{

UdpClient udpc = new UdpClient(2055);

Console.WriteLine("Server started servicing on port 2055");

IPEndPoint ep = null;

while(true)

{

byte[] rdata = udpc.Receive(ref ep);

string name = Encoding.ASCII.GetString(rdata);

string job = ConfigurationManager.AppSettings[name];

if(job==null) job = "No such employee";

byte[] sdata = Encoding.ASCII.GetBytes(job);

udpc.Send(sdata,sdata.Length,ep);

} // end while

} // end Main

}

The xml-file UDPServer1.exe.config is given by

















146 CHAPTER 10. SOCKETS PROGRAMMING

The UDPClient1.cs file is given by

// UDPClient1.cs

using System;

using System.Net;

using System.Net.Sockets;

using System.Text;

class ClientUDP

{

public static void Main()

{

UdpClient udpc = new UdpClient("152.106.40.84",2055);

IPEndPoint ep = null;

while(true)

{

Console.Write("Name: ");

string name = Console.ReadLine();

if(name == "") break;

byte[] sdata = Encoding.ASCII.GetBytes(name);

udpc.Send(sdata,sdata.Length);

byte[] rdata = udpc.Receive(ref ep);

string job = Encoding.ASCII.GetString(rdata);

Console.WriteLine("job = " + job);

} // end while

} // end Main

}

We include an xml-file (UDPServer1.exe.config) on the Server side we can query

from the Client. On the Server side we first compile

csc UDPServer1.cs

Next we compile the Client side

csc UDPClient1.cs

Then on the Server side we start running the exe-file UDPServer1 and finally we

start the exe-file UDPClient1 on the Client side.

programs on sockets

Sockets Programming


10.1 Introduction

Most interprocess communication uses the client server model. These terms refer to

the two processes which will communicating with each other. One of the processes,

the client, connects to the other process, the server, typically to make a request for

information. A good analogy is a person who makes a phone call to another person.

The client needs to know of the existence of and the address of the server, but the

server does not need to know the address of (or even the existence of) the client

prior to the connection being established. Once a connection is established, both

sides can send and receive information.

The system calls for establishing a connection are different for the client and the

server, but both involve the basic construct of a socket. A socket is one end of an

interprocess communication channel. The two processes each establish their own

socket.

The steps for establishing a socket on the client side are:

1. Create a socket

2. Connect the socket to the address of the server

3. Send and receive data

The steps for establishing a socket on the server side are:

1. Create a socket

2. Bind the socket to an address. For the server socket on the Internet, an address

consists of a port number on the host machine.

3. Listen for connections.

4. Accept a connection. This call typically blocks until a client connects with the

server.

137

138 CHAPTER 10. SOCKETS PROGRAMMING

5. Send and receive data.

Thus a socket is a communication mechanism. A socket is normally identified by an

integer which may be called the socket descriptor. The socket mechanism was first

introduced in the 4.2 BSD Unix system in 1983 in conjunction with the TCP/IP

protocols that first appeared in the 4.1 BSD Unix system in late 1981.

Thus besides the IPAddress we also need a port number (2 bytes) which is arbitrary

except for the well know port numbers associated with popular applications.

Formally a socket is defined by a group of four numbers. There are

1) The remote host identification number or address (IPAddress)

2) The remote host port number

3) The local host identification number or address (IPAddress)

4) The local host port number

10.2 Transmission Control Protocol

TCP is reliable connection oriented protocol.

Example 1. When the server program is run it will indicate at which IPAddress

it is running and the port it is listening to. Now run the client program so that we

establish a connection with the server. When the connection is established the server

will display the IPAddress and port from where it has accepted the connection. The

client will ask for the string which is to be transmitted to the server. The server on

reciept of the string will display it, send an acknowledgement which will be received

by the client.

10.2. TRANSMISSION CONTROL PROTOCOL 139

// Server1.cs

using System;

using System.Text;

using System.Net;

using System.Net.Sockets;

class Server1

{

public static void Main()

{

try

{

// use local IP address and use the same in the client

IPAddress ipAd = IPAddress.Parse("152.106.40.84");

TcpListener listener = new TcpListener(ipAd,2055);

// start listening at the specified port 2055

listener.Start();

Console.WriteLine("Server is running at port 2055...");

Console.WriteLine("The local End point is: " + listener.LocalEndpoint);

Console.WriteLine("Waiting for a connection...");

Socket s = listener.AcceptSocket();

Console.WriteLine("Connection accepted from " + s.RemoteEndPoint);

byte[] b = new byte[100];

int k = s.Receive(b);

Console.WriteLine("Received...");

for(int i=0;i

Console.Write(Convert.ToChar(b[i]));

ASCIIEncoding asen = new ASCIIEncoding();

s.Send(asen.GetBytes("The string was received by the server."));

Console.WriteLine("\nSent Acknowledgement");

// clean up

s.Close();

listener.Stop();

} // end try

catch(Exception e)

{

Console.WriteLine("Error... " + e.StackTrace);

} // end catch

} // end Main

} // end class Server1

140 CHAPTER 10. SOCKETS PROGRAMMING

// Client1.cs

using System;

using System.IO;

using System.Text;

using System.Net;

using System.Net.Sockets;

class Client1

{

public static void Main()

{

try

{

TcpClient tcpclnt = new TcpClient();

Console.WriteLine("Connecting...");

tcpclnt.Connect("152.106.40.84",2055);

Console.WriteLine("Connected");

Console.Write("Enter the string to be transmitted: ");

String str = Console.ReadLine();

Stream stm = tcpclnt.GetStream();

ASCIIEncoding asen = new ASCIIEncoding();

byte[] ba = asen.GetBytes(str);

Console.WriteLine("Transmitting...");

stm.Write(ba,0,ba.Length);

byte[] bb = new byte[100];

int k = stm.Read(bb,0,100);

for(int i=0;i

Console.Write(Convert.ToChar(bb[i]));

// clean up

tcpclnt.Close();

} // end try

catch(Exception e)

{

Console.WriteLine("Error... " + e.StackTrace);

} // end catch

} // end Main

} // end class Client1

10.2. TRANSMISSION CONTROL PROTOCOL 141

Example 2. In our second example we include an xml-file (Server2.exe.config)

on the Server side we can query from the Client. On the Server side we first compile

csc /D:LOG Server2.cs

The code between #if LOG and endif will be added by the compiler only if the symbol

LOG is defined during compilation (conditional compilation). Next we compile

the Client side

csc Client2.cs

Then on the Server side we start running the exe-file Server2 and finally we start

the exe-file Client2 on the Client side.

// Server2.cs

using System;

using System.Threading;

using System.IO;

using System.Net;

using System.Net.Sockets;

using System.Configuration;

class EmployeeTCPServer

{

static TcpListener listener;

const int LIMIT = 5; // 5 concurrent clients

public static void Main()

{

IPAddress ipAd = IPAddress.Parse("152.106.40.84");

listener = new TcpListener(ipAd,2055);

listener.Start();

#if LOG

Console.WriteLine("Server mounted,listening to port 2055");

#endif

for(int i=0;i

{

Thread t = new Thread(new ThreadStart(Service));

t.Start();

} // end for loop

} // end Main

public static void Service()

{

while(true)

142 CHAPTER 10. SOCKETS PROGRAMMING

{

Socket soc = listener.AcceptSocket();

#if LOG

Console.WriteLine("Connected: {0}",soc.RemoteEndPoint);

#endif

try

{

Stream s = new NetworkStream(soc);

StreamReader sr = new StreamReader(s);

StreamWriter sw = new StreamWriter(s);

sw.AutoFlush = true; // enable automatic flushing

sw.WriteLine("{0} Employees available",ConfigurationManager.AppSettings.Count);

while(true)

{

string name = sr.ReadLine();

if(name == ""

name == null) break;

string job = ConfigurationManager.AppSettings[name];

if(job == null) job = "No such employee";

sw.WriteLine(job);

} // end while

s.Close();

} // end try

catch(Exception e)

{

#if LOG

Console.WriteLine(e.Message);

#endif

} // end catch

#if LOG

Console.WriteLine("Disconnected: {0}",soc.RemoteEndPoint);

#endif

soc.Close();

}

}

} // end class Server2

10.2. TRANSMISSION CONTROL PROTOCOL 143

The file (xml-file) Server2.exe.config is given by















programs on processes and threads

Processes and Threads


9.1 Processes

A process in its simplest form is a running application. The Process class provides

access to local and remote processes and enables us to start and stop local system

processes.

The Process.Start() method starts a process resource by specifying the name of

a document or application file and associates the resource with a new process component.

Thus we can start other applications from within our application.

The program below launches notepad.exe, winhlp32.exe and asynchronously counts

up from 0 to 100001, then destroys the notepad.exe process using the Kill()

method. It counts again from 0 to 2000 and then kills the winhlp32.exe process.

Each process has an Id number.

// process1.cs

using System;

using System.Diagnostics;

class Processtest

{

public static void Main()

{

Process p = Process.Start("notepad.exe");

string name = p.ProcessName;

int id = p.Id;

DateTime started = p.StartTime;

Console.WriteLine("name = " + name);

Console.WriteLine("id = " + id);

Console.WriteLine("StartTime = " + started);

114

9.2. THREADS 115

Process w = Process.Start("winhlp32.exe");

for(int i=0;i<100001;i++) Console.WriteLine(i);

p.Kill();

for(int j=0;j<2000;j++) Console.WriteLine(j);

w.Kill();

}

}

If we also want to load a file (for example process2.cs) with notepad.exe we

change this line to

Process p = Process.Start("notepad.exe","c:\\csharp\\process2.cs");

The detect process completion we use the method

WaitForExit()

9.2 Threads

9.2.1 Introduction

A thread is an execution stream within a process. A thread is also called a lightweight

process. It has it own execution stack, local variables, and program counter. There

may be more than one thread in a process. Threads are part of the same process

which execute concurrently. In .NET Base Class Library (BCL) the System.Threading

namespace provides various classes for executing and controlling threads.

The following program Threads0.cs is the simple example. We create a Thread

instance by passing it an object of ThreadStart delegate which contains a reference

to our ThreadJob method. Thus the code creates a new thread which runs the

ThreadJob method, and starts it. That thread counts from 0 to 1 while the main

thread counts from 0 to 5.

// Threads0.cs

using System;

using System.Threading;

public class Threads0

{

static void Main()

{

ThreadStart job = new ThreadStart(ThreadJob);

Thread thread = new Thread(job);

116 CHAPTER 9. PROCESSES AND THREADS

thread.Start();

int i = 0;

while(i < 6)

{

Console.WriteLine("Main thread: {0}",i);

i++;

} // end while

} // end Main

static void ThreadJob()

{

int i = 0;

while(i < 2)

{

Console.WriteLine("Other thread: {0}",i);

i++;

} // end while

} // end ThreadJob

}

An output could be

Main thread: 0

Main thread: 1

Main thread: 2

Main thread: 3

Main thread: 4

Main thread: 5

Other thread: 0

Other thread: 1

At another run the output could be

Other thread: 0

Other thread: 1

Main thread: 0

Main thread: 1

Main thread: 2

Main thread: 3

Main thread: 4

Main thread: 5

9.2.2 Background Thread

In the program threadtest2.cs the Thread t is set to the background thread

9.2. THREADS 117

t.IsBackgound = true;

The thread will terminate when the application terminates.

// threadtest2.cs

using System;

using System.Threading; // for Thread class

class ThreadTest2

{

public static void SayHello()

{

for(int i=1;;i++)

{

Console.WriteLine("Hello {0}",i);

}

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(SayHello));

t.IsBackground = true;

t.Start();

for(int i=1;i<1001;i++)

{

Console.WriteLine("Bye {0}",i);

}

}

}

9.2.3 Sleep Method

Once a thread has been started it is often useful for that thread to pause for a

fixed period of time. Calling Thread.Sleep causes the thread to immediately

block for a fixed number of milliseconds. The Sleep method takes as a parameter

a timeout, which is the number of milliseconds that the thread should remain

blocked. The Sleep method is called when a thread wants to put itself to

sleep. One thread cannot call Sleep on another thread. Calling Thread.Sleep(0)

causes a thread to yield the remainder of its timeslice to another thread. Calling

Thread.Sleep(Timeout.Infinite) causes a thread to sleep until it is interrupted

by another thread that calls Thread.Interrupt. A thread can also be paused

by calling Thread.Suspend. When a thread calls Thread.Suspend on itself, the

call blocks until the thread is resumed by another thread. When one thread calls

Thread.Suspend on another thread, the call is a non-blocking call that causes the

118 CHAPTER 9. PROCESSES AND THREADS

other thread to pause.

The following program Threads0.cs is the simple example. We create a Thread

instance by passing it an object of ThreadStart delegate which contains a reference

to our ThreadJob method. Thus the code creates a new thread which runs the

ThreadJob method, and starts it. That thread counts from 0 to 9 fairly fast (about

twice a second) while the main thread counts from 0 to 4 fairly slowly (about once a

second). The way they count at different speeds is by each of them including a call

to Thread.Sleep(), which just makes the current thread sleep (do nothing) for the

specified period of time (milliseconds). Between each count in the main thread we

sleep for 1000ms, and between each count in the other thread we sleep for 500ms.

// ThreadsSleep.cs

using System;

using System.Threading;

public class ThreadsSleep

{

static void Main()

{

ThreadStart job = new ThreadStart(ThreadJob);

Thread thread = new Thread(job);

thread.Start();

int i = 0;

while(i < 5)

{

Console.WriteLine("Main thread: {0}",i);

Thread.Sleep(1000);

i++;

} // end while

} // end Main

static void ThreadJob()

{

int i = 0;

while(i < 10)

{

Console.WriteLine("Other thread: {0}",i);

Thread.Sleep(500);

i++;

} // end while

} // end ThreadJob

}

9.2. THREADS 119

A typical output could be

Main thread: 0

Other thread: 0

Other thread: 1

Main thread: 1

Other thread: 2

Other thread: 3

Other thread: 4

Main thread: 2

Other thread: 5

Main thread: 3

Other thread: 6

Other thread: 7

Main thread: 4

Other thread: 8

Other thread: 9

At another run this could change.

9.2.4 Join Methods

The method Join() blocks the calling thread until a thread terminates.

// joiningthread.cs

using System;

using System.Threading;

class JoiningThread

{

public static void Run()

{

for(int i=1;i<3;i++)

Console.WriteLine("Hello {0}",i);

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(Run));

t.Start();

for(int i=1;i<6;i++)

Console.WriteLine("Welcome {0}",i);

t.Join();

Console.WriteLine("Goodbye");

}

120 CHAPTER 9. PROCESSES AND THREADS

}

The output is

Welcome 1

Welcome 2

Welcome 3

Welcome 4

Welcome 5

Hello 1

Hello 2

9.3 Monitor

While one thread is in a read/increment/write operation, no other threads can try

to do the same thing. This is where monitors come in. Every object in .NET has

a monitor associated with it. A thread can enter (or acquire) a monitor only if no

other thread has currently “got” it. Once a thread has acquired a monitor, it can

acquire it more times, or exit (or release) it. The monitor is only available to other

threads again once it has exited as many times as it was entered. If a thread tries

to acquire a monitor which is owened by another thread, it will block until it is able

to acquire it. There may be more than one thread trying to acquire the monitor,

in which case when the current owner thread releases it for the last time, only one

of the threads will acquire it - the other one will have to wait for the new owner to

release it too. The Pulse(object) method notifies a thread in the waiting queue

of a change in the locked object’s state. The method Wait(object) waits for the

Monitor Pulse.

// waitingthread.cs

using System;

using System.Threading;

class WaitingThread

{

static object obj = new object();

public static void thread1()

{

for(int i=1;i<6;i++)

Console.WriteLine("Welcome: {0}",i);

Monitor.Enter(obj);

Monitor.Pulse(obj);

Monitor.Exit(obj);

}

9.4. SYNCHRONIZATION 121

public static void thread2()

{

for(int i=1;i<15;i++)

Console.WriteLine("Hello: {0}",i);

Monitor.Enter(obj);

Monitor.Pulse(obj);

Monitor.Exit(obj);

}

public static void thread3()

{

for(int i=1;i<8;i++)

Console.WriteLine("Good Night: {0}",i);

Monitor.Enter(obj);

Monitor.Wait(obj);

Monitor.Pulse(obj);

Monitor.Exit(obj);

}

public static void Main()

{

ThreadStart job1 = new ThreadStart(thread1);

ThreadStart job2 = new ThreadStart(thread2);

ThreadStart job3 = new ThreadStart(thread3);

Thread t1 = new Thread(job1);

Thread t2 = new Thread(job2);

Thread t3 = new Thread(job3);

t1.Start();

t2.Start();

t3.Start();

}

}

If thread3 runs last after thread1 and thread2 have completed it freeze at

Good Night: 7

9.4 Synchronization

When two or more threads share a common resource access needs to be serialized in a

process called synchronization. Synchronization is done with the lock() operation.

The first program is without the lock operation. In the second program the lock

operation is introduced.

122 CHAPTER 9. PROCESSES AND THREADS

// syncthreads1.cs

using System;

using System.Threading;

class Account

{

private double balance = 5000;

public void Withdraw(double amount)

{

Console.WriteLine("WITHDRAWING {0}",amount);

if(amount > balance) throw new Exception("INSUFFICIENT FUNDS");

Thread.Sleep(10); // do other stuff

balance -= amount;

Console.WriteLine("BALANCE {0}",balance);

}

}

class SymnThread

{

static Account acc = new Account();

public static void Run()

{

acc.Withdraw(3000);

}

public static void Main()

{

new Thread(new ThreadStart(Run)).Start();

acc.Withdraw(3000);

}

}

Now we introduce the lock.

// syncthreads2.cs

using System;

using System.Threading;

class Account

{

private double balance = 5000;

9.5. DEADLOCK 123

public void Withdraw(double amount)

{

Console.WriteLine("WITHDRAWING {0}",amount);

lock(this)

{

if(amount > balance) throw new Exception("INSUFFICIENT FUNDS");

Thread.Sleep(10); // do other stuff

balance -= amount;

}

Console.WriteLine("BALANCE {0}",balance);

}

}

class SymnThread

{

static Account acc = new Account();

public static void Run()

{

acc.Withdraw(3000);

}

public static void Main()

{

new Thread(new ThreadStart(Run)).Start();

acc.Withdraw(3000);

}

}

9.5 Deadlock

The second major problem of multi-threading is that of deadlocks. Simply put, this

is when two threads each holds a monitor that the other one wants. Each blocks,

waiting for the monitor that it’s waiting for to be released - and so the monitors are

never released, and the application hangs (or at least those threads involved in the

deadlock hang). An example is given below.

// Deadlock.cs

using System;

using System.Threading;

public class Deadlock

{

124 CHAPTER 9. PROCESSES AND THREADS

static readonly object firstLock = new object();

static readonly object secondLock = new object();

static void ThreadJob()

{

Console.WriteLine("\t\t\t\tLocking firstLock");

lock(firstLock)

{

Console.WriteLine("\t\t\t\tLocked firstLock");

// Wait until we are fairly sure the first thread

// has grabbed secondlock

Thread.Sleep(1000);

Console.WriteLine("\t\t\t\tLocking secondLock");

lock(secondLock)

{

Console.WriteLine("\t\t\t\tLocked secondLock");

}

Console.WriteLine("\t\t\t\tReleased secondLock");

}

Console.WriteLine("\t\t\t\tReleased firstLock");

} // end method ThreadJob

public static void Main()

{

new Thread(new ThreadStart(ThreadJob)).Start();

// wait until we are fairly sure the other thread

// has grabbed firstlock

Thread.Sleep(500);

Console.WriteLine("Locking secondLock");

lock(secondLock)

{

Console.WriteLine("Locked secondLock");

Console.WriteLine("Locking firstLock");

lock(firstLock)

{

Console.WriteLine("Locked firstLock");

}

Console.WriteLine("Released firstLock");

}

Console.WriteLine("Released secondLock");

} // end Main

} // end class

9.6. INTERLOCKED CLASS 125

9.6 Interlocked Class

An operation is atomic if it is indivisible - in other words, nothing else can happen

in the middle. Thus with an atomic write, we cannot have another thread reading

the value half way through the write, and ending up “seeing” half of the old value

and half of the new value. Sinilarly, with an atomic read, we cannot have another

thread changing the value half way through the read, ending up with a value which

is neither the old nor the new value. For example, for a long (64 bits) on a 32 bit

machine, if one thread is changing the value from 0 to 0x0123456789ABCDEF, there

is no guarantee that another thread will not see the value as 0x0123456700000000

or 0x0000000089ABCDEF.

The Interlocked class provides a set of methods for performing atomic changes: exchanges

(optionally performing a comparison first), increments and decrements. The

Exchange and CompareExchange methods act on a variables of type int, object,

or float; the Increment and Decrement methods act on variables of type int and

long.

// interlocked.cs

using System;

using System.Threading;

public class MyInterlocked

{

static long count = 0;

public static void Main()

{

ThreadStart job = new ThreadStart(ThreadJob);

Thread thread = new Thread(job);

thread.Start();

for(long i=0;i<5;i++)

{

Interlocked.Increment(ref count);

Console.WriteLine("I am in for loop in main");

}

thread.Join();

Console.WriteLine("final count: {0}",count);

} // end Main

static void ThreadJob()

{

long i = 0;

126 CHAPTER 9. PROCESSES AND THREADS

while(i < 5)

{

Interlocked.Increment(ref count);

Console.WriteLine("I am in ThreadJob");

i++;

}

} // end ThreadJob

}

First the for loop in Main will run to the end and then the while loop will be done.

9.7 Thread Pooling

We can use thread pooling to make much more efficient use of multiple threads,

depending on our application. Many applications use multiple threads, but often

those threads spend a great deal of time in the sleeping state waiting for an event

to occur. Other threads might enter a sleeping state and be awakend only periodically

to poll for a change or update status information before going to sleep again.

Using thread pooling provides our application with a pool of worker threads that

are managed by the system, allowing us to concentrate on application tasks rather

than thread management. An example is given below.

// ThreadsSum2.cs

using System;

using System.Threading;

class ThreadsSum

{

public static void Sum1(Object o)

{

int sum1 = 0;

int[] a1 = (int[]) o;

for(int i=0;i

{

sum1 += a1[i];

}

Console.WriteLine("sum1 = " + sum1);

}

public static void Sum2(Object o)

{

int sum2 = 0;

int[] a2 = (int[]) o;

for(int i=0;i

9.8. THREADING IN WINDOWS FORMS 127

{

sum2 += a2[i];

}

Console.WriteLine("sum2 = " + sum2);

}

public static void Main()

{

int[] a1 = { 2, 3, 4 };

int[] a2 = { 4, 5, 7 };

if(ThreadPool.QueueUserWorkItem(new WaitCallback(Sum1),a1))

Console.WriteLine("Sum1 queued");

if(ThreadPool.QueueUserWorkItem(new WaitCallback(Sum2),a2))

Console.WriteLine("Sum2 queued");

Thread.Sleep(10); // Give other threads a turn

}

}

9.8 Threading in Windows Forms

How to handle threading in a UI? There are two rules for Windows Forms:

1) Never invoke any method or property on a control created on another thread other

than Invoke, BeginInvoke, EndInvoke or CreateGraphics, and InvokeRequired.

Each control is effectively bound to a thread which runs its message pump. If we

try to access or change anything in the UI (for example changing the Text property)

from a different thread, we run a risk of our program hanging or misbehaving

in other ways. We may get away with it in some cases. Fortunately, the Invoke,

BeginInvoke and EndInvoke methods have been provided so that we can ask the

UI thread to call a method in a safe manner.

2) Never execute a long-running piece of code in the UI thread. If our code is running

in the UI thread, that means no other code is running in that thread. That means

we won’t receive events, our controls won’t be repainted, etc. We can execute longrunning

code and periodically call Application.DoEvents(). It means we have

to consider re-entrancy issues etc, which are harder to diagnose and fix than ”normal”

threading problems. We have to judge when to call DoEvents, and we can’t

use anything which might block (network access, for instance) without risking an

unresponsive UI. There are message pumping issues in terms of COM objects as well.

If we have a piece of long-running code which we need to execute, we need to create

a new thread (or use a thread pool thread if we prefer) to execute it on, and make

128 CHAPTER 9. PROCESSES AND THREADS

sure it doesn’t directly try to update the UI with its results. The thread creation

part is the same as any other threading problem. It is interesting going the other

way - invoking a method on the UI thread in order to update the UI. There are two

different ways of invoking a method on the UI thread, one synchronous (Invoke)

and one asynchronous (BeginInvoke). They work in much the same way - we specify

a delegate and (optionally) some arguments, and a message goes on the queue

for the UI thread to process. If we use Invoke, the current thread will block until

the delegate has been executed. If we use BeginInvoke, the call will return immediately.

If we need to get the return value of a delegate invoked asynchronously, we

can use EndInvoke with the IAsyncResult returned by BeginInvoke to wait until

the delegate has completed and fetch the return value.

There are two options when working out how to get information between the various

threads involved. The first option is to have state in the class itself, setting it in

one thread, retrieving and processing it in the other (updating the display in the UI

thread, for example). The second option is to pass the information as parameters

in the delegate. Using state somewhere is necessary if we are creating a new thread

rather than using the thread pool - but that doesn’t mean we have to use state to

return information to the UI. However, creating a delegate with lots of parameters

feels clumsy, and is in some ways less efficient than using a simple MethodInvoker

or EventHandler delegate. These two delegates are treated in a special (fast) manner

by Invoke and BeginInvoke. MethodInvoker is just a delegate which takes no

parameters and returns no value (like ThreadStart), and EventHandler takes two

parameters (a sender and an EventArgs parameter and returns no value. However if

we pass an EventHandler delegate to Invoke or BeginInvoke then even if we specify

parameters ourself, they are ignored - when the method is invoked, the sender will

be the control we have invoked it with, and the EventArgs will be EventArgs.Empty.

Here is an example which shows several of the above concepts.

// ThreadingForms.cs

using System;

using System.Threading;

using System.Windows.Forms;

using System.Drawing;

public class Test : Form

{

delegate void StringParameterDelegate(string value);

Label statusIndicator;

Label counter;

Button button;

readonly object stateLock = new object();

9.8. THREADING IN WINDOWS FORMS 129

int target;

int currentCount;

Random rng = new Random();

Test()

{

Size = new Size(180,120);

Text = "Test";

Label lbl = new Label();

lbl.Text = "Status:";

lbl.Size = new Size(50,20);

lbl.Location = new Point(10,10);

Controls.Add(lbl);

lbl = new Label();

lbl.Text = "Count:";

lbl.Size = new Size(50,20);

lbl.Location = new Point(10,34);

Controls.Add(lbl);

statusIndicator = new Label();

statusIndicator.Size = new Size(100,20);

statusIndicator.Location = new Point(70,10);

Controls.Add(statusIndicator);

counter = new Label();

counter.Size = new Size(100,20);

counter.Location = new Point(70,34);

Controls.Add(counter);

button = new Button();

button.Text = "Run";

button.Size = new Size(50,20);

button.Location = new Point(10,58);

Controls.Add(button);

button.Click += new EventHandler(StartThread);

}

void StartThread(object sender,EventArgs e)

{

button.Enabled = false;

lock(stateLock)

{

130 CHAPTER 9. PROCESSES AND THREADS

target = rng.Next(100);

}

Thread t = new Thread(new ThreadStart(ThreadJob));

t.IsBackground = true;

t.Start();

}

void ThreadJob()

{

MethodInvoker updateCounterDelegate = new MethodInvoker(UpdateCount);

int localTarget;

lock(stateLock)

{

localTarget = target;

}

UpdateStatus("Starting");

lock(stateLock)

{

currentCount = 0;

}

Invoke(updateCounterDelegate);

// Pause before starting

Thread.Sleep(500);

UpdateStatus("Counting");

for(int i=0;i

{

lock(stateLock)

{

currentCount = i;

}

// Synchronously show the counter

Invoke(updateCounterDelegate);

Thread.Sleep(100);

}

UpdateStatus("Finished");

Invoke(new MethodInvoker(EnableButton));

}

void UpdateStatus(string value)

{

if(InvokeRequired)

{

// We’re not in the UI thread, so we need to call BeginInvoke

BeginInvoke(new StringParameterDelegate(UpdateStatus),new object[]{value});

9.8. THREADING IN WINDOWS FORMS 131

return;

}

// Must be on the UI thread if we’ve got this far

statusIndicator.Text = value;

}

void UpdateCount()

{

int tmpCount;

lock(stateLock)

{

tmpCount = currentCount;

}

counter.Text = tmpCount.ToString();

}

void EnableButton()

{

button.Enabled = true;

}

static void Main()

{

Application.Run(new Test());

}

}

State is used to tell the worker thread what number to count up to. A delegate taking

a parameter is used to ask the UI to update the status label. The worker thread’s

principal method actually just calls UpdateStatus, which uses InvokeRequired

to detect whether or not it needs to ”change thread”. If it does, it then calls

BeginInvoke to execute the same method again from the UI thread. This is quite

a common way of making a method which interacts with the UI thread-safe. The

choice of BeginInvoke rather than Invoke here was just to demonstrate how to

invoke a method asynchronously. In real code, we would decide based on whether

we needed to block to wait for the access to the UI to complete before continuing

or not. It is quite rare to actually require UI access to complete first, so we should

use BeginInvoke instead of Invoke. Another approach might be to have a property

which did the appropriate invoking when necessary. It is easier to use from the

client code, but slightly harder work in that we would either have to have another

method anyway, or get the MethodInfo for the property setter in order to construct

the delegate to invoke. In this case we actually know that BeginInvoke is required

because we are running in the worker thread anyway. We do not call EndInvoke after

the BeginInvoke. Unlike all other asynchronous methods we do not need to call

EndInvoke unless we need the return value of the delegate’s method. BeginInvoke

132 CHAPTER 9. PROCESSES AND THREADS

is also different to all of the other asynchronous methods as it doesn’t cause the

delegate to be run on a thread pool thread. State is used again to tell the UI thread

how far we have counted so far. We use a MethodInvoker delegate to execute

UpdateCount. We call this using Invoke to make sure that it executes on the UI

thread. This time there’s no attempt to detect whether or not an Invoke is required.

If we call BeginInvoke it will have a different effect than calling the method directly

as it will occur later, rather than in the current execution flow, of course. Again,

we actually know that we need to call Invoke here anyway. A button is provided to

let the user start the thread. It is disabled while the thread is running, and another

MethodInvoker delegate is used to enable the button again afterwards. All state

which is shared between threads (the current count and the target) is accessed in

locks in the way described earlier. We spend as little time as possible in the lock, not

updating the UI or anything else while holding the lock. This probably doesn’t make

too much difference here. It would be disastrous to still have the lock in the worker

thread when synchronously invoking UpdateCount - the UI thread would then try

to acquire the lock as well, and we end up with deadlock. The worker thread is

set to be a background thread (IsBackground=true;) so that when the UI thread

exits, the whole application finishes. In other cases where we have a thread which

should keep running even after the UI thread has quit, we need to be careful not

to call Invoke or BeginInvoke when the UI thread is no longer running - we will

either block permanently (waiting for the message to be taken off the queue, with

nothing actually looking at messages) or receive an exception.

9.9 Asynchronous Programming Model

When a caller invokes a method, the call is synchronous that is the caller has to

wait for the method to return before the remaining code can be executed. .NET has

an inbuilt support for asynchronous method invocation. Here the caller can issue a

request for invocation of a method and concurreently execute the remaining code.

For every delegate declared in an assembly the compiler emits a class (subclass of

System.MulticastDelegate) with Invoke, BeginInvoke and EndInvoke methods.

For example, consider a delegate declared as

delegate int MyWorker(char c,int m);

The compiler will emit the MyWorker class

class MyWorker : System.MulticastDelegate

{

public int Invoke(char c,int m);

public System.IAsyncResult BeginInvoke(char c,int m,System.AsyncCallback cb,

object asyncState);

public int EndInvoke(System.IAsyncResult result);

}

9.10. TIMERS 133

The BeginInvoke and EndInvoke methods can be used for asynchronous invocation

of a method pointed by MyWorker delegate. In the program below the method

DoWork (it displays character c m number of times) is invoked asynchronously with

character ’+’, in the next statement this method is directly invoked with character

’*’. Both ’+’ and ’*’ are displayed (1000 times each) on the Console simultaneously.

As an asynchronous call executes within its own background thread, we have

used Console.Read() to pause the main thread.

// Asynchronous.cs

using System;

delegate int MyWorker(char c,int m);

class AsncTest

{

static MyWorker worker;

static int DoWork(char c,int m)

{

int t = Environment.TickCount; // returns number of milliseconds

// elapsed since the system started

for(int i=1;i<=m;i++) Console.Write(c);

return (Environment.TickCount - t);

}

public static void Main()

{

Console.WriteLine("Start");

worker = new MyWorker(DoWork);

worker.BeginInvoke(’+’,1000,null,null); // asynchronous call

DoWork(’*’,1000); // synchronous call

Console.Read(); // pause until user presses a key

}

}

9.10 Timers

There are various different timers available in .NET, each of which basically calls

a delegate after a certain amount of time has passed. All the timers implement

IDispossible. so we have to make sure to dispose when we are not using them

anymore.

// Timers.cs

134 CHAPTER 9. PROCESSES AND THREADS

using System;

using System.Threading;

public class Timers

{

public static void Main()

{

Console.WriteLine("Started at {0:HH:mm:ss.fff}",DateTime.Now);

// Start in three seconds, then fire every second

using(Timer timer = new Timer(new TimerCallback(Tick),null,3000,1000))

{

// wait for 10 seconds

Thread.Sleep(10000);

// then go slow for another 10 seconds

timer.Change(0,2000);

Thread.Sleep(10000);

}

// the timerm will now have been disposed automatically due

// to the using statement so there won’t be any other threads running

} // end Main()

static void Tick(object state)

{

Console.WriteLine("Ticked at {0:HH:mm:ss.fff}",DateTime.Now);

}

}

9.11 Interrupt and Abort

There are two methods in the Thread class which are used for stopping threads -

Abort and Interrupt. Calling Thread.Abort aborts that thread as soon as possible.

Aborting a thread which is executing unmanaged code has no effect until the CLR

gets control again. Calling Thread.Interrupt is similar, but less drastic. This

causes a ThreadInterruptedException exception to be thrown the next time the

thread enters the WaitSleepJoin state, or immediately if the thread is already in

that state.

// Interruptthread.cs

using System;

using System.Threading;

class SleepingThread

{

public static void Run()

9.11. INTERRUPT AND ABORT 135

{

for(int i=1;i<6;i++)

Console.WriteLine("Welcome {0}",i);

try

{

Thread.Sleep(5000);

}

catch(ThreadInterruptedException e)

{

Console.WriteLine("Sleep Interrupted");

}

Console.WriteLine("Goodbye");

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(Run));

t.Start();

for(int i=1;i<16;i++)

Console.WriteLine("Hello {0}",i);

t.Interrupt();

}

}

The program threadtest1.cs uses the Abort method.

// threadtest1.cs

using System;

using System.Threading; // for Thread class

class ThreadTest1

{

public static void SayHello()

{

for(int i=1;;i++)

{

Console.WriteLine("Hello {0}",i);

}

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(SayHello));

136 CHAPTER 9. PROCESSES AND THREADS

t.Start();

for(int i=1;i<1001;i++)

{

Console.WriteLine("Bye {0}",i);

}

t.Abort();

}

}

programs on events

Events


Event handling is familiar to any developer who has programmed graphical user interfaces

(GUI). When a user interacts with a GUI control (e.g., clicking a button on

a form), one or more methods are executed in response to the above event. Events

can also be generated without user interactions. Event handlers are methods in an

object that are executed in response to some events occuring in the application. To

understand the event handling model of the .NET framework, we need to unterstand

the concept of delegate. A delegate in C# allows us to pass methods of one

class to objects to other classes that can call those methods. We can pass method

m in class A, wrapped in a delegate, to class B and class B will be able to call

method m. We can pass both static and instance methods.

An event handler in C# is a delegate with a special signature, given below

public delegate void MyEventHandler(object sender,MyEventArgs e)

The first parameter (sender) in the above declaration specifies the object that fired

the event. The second parameter (e) holds data that can be used in the event handler.

The class MyEventArgs is derived from the class EventArgs. EventArgs is the

base class of more specialized classes, like MouseEventArgs, ListChangedEventArgs,

etc.

Thus an event is a notification sent by a sender object to a listener object. The listener

registers an event handler method with the sender. When the event occurs the

sender invokes event handler methods of all its registered listerners. The event handler

is registered by adding +=. The -= operator removes the handler from the event.

As an example we set up two classes A and B to see how this event handling mechanism

works in .NET framework. The delegates require that we define methods with

the exact same signature as that of the delegate declaration. Class A will provide

event handlers (methods with the same signature as that of the event declaration).

It will create the delegate objects and hook up the event handler. Class A will then

pass the delegate objects to class B. When an event occurs in class B, it will execute

the event handler method of class B.

110

111

// MyHandler.cs

using System;

// Step 1. Create delegate object

public delegate void MyHandler1(object sender,MyEventArgs e);

public delegate void MyHandler2(object sender,MyEventArgs e);

// Step 2. Create event handler methods

class A

{

public const string m_id = "Class A";

public void OnHandler1(object sender,MyEventArgs e)

{

Console.WriteLine("I am in OnHandler1 and MyEventArgs is {0}",e.m_id);

}

public void OnHandler2(object sender,MyEventArgs e)

{

Console.WriteLine("I am in OnHandler2 and MyEventArgs is {0}",e.m_id);

}

// Step 3. Create delegates, plug in the handler and register

// with the object that will fire the events

public A(B b)

{

MyHandler1 d1 = new MyHandler1(OnHandler1);

MyHandler2 d2 = new MyHandler2(OnHandler2);

b.Event1 += d1;

b.Event2 += d2;

}

} // end class A

// Step 4. Call the encapsulated method through the delegate (fires events)

class B

{

public event MyHandler1 Event1;

public event MyHandler2 Event2;

public void FireEvent1(MyEventArgs e)

{

if(Event1 != null) { Event1(this,e); }

}

public void FireEvent2(MyEventArgs e)

{

if(Event2 != null) { Event2(this,e); }

112 CHAPTER 8. EVENTS

}

} // end class B

public class MyEventArgs

{

public string m_id;

} // end class MyEventArgs

class Driver

{

public static void Main()

{

B b = new B();

A a = new A(b);

MyEventArgs e1 = new MyEventArgs();

MyEventArgs e2 = new MyEventArgs();

e1.m_id = "Event args for event 1";

e2.m_id = "Event args for event 2";

b.FireEvent1(e1);

b.FireEvent2(e2);

} // end Main

}

The next program shows GUI event handling in C#. We create two buttons each of

them fires an event.

// evthand.cs

using System;

using System.Windows.Forms;

using System.Drawing;

class MyForm : Form

{

MyForm()

{

// button1 top left corner

Button button1 = new Button();

button1.Text = "Button";

button1.Click += new EventHandler(HandleClick1);

Controls.Add(button1);

Button button2 = new Button();

button2.Location = new Point(40,40);

button2.Size = new Size(60,40);

113

button2.Text = "New Button";

button2.Click += new EventHandler(HandleClick2);

Controls.Add(button2);

}

void HandleClick1(object sender,EventArgs e)

{

MessageBox.Show("The click event fire!");

}

void HandleClick2(object sender,EventArgs e)

{

Console.WriteLine("Console click fire!");

}

public static void Main()

{

Application.Run(new MyForm());

}

}

programs on graphics

Graphics


7.1 Drawing Methods

The Windows.Forms namespace contains classes for creating Windows based applications.

In this namespace we find the Form class and many other controls that can

be added to forms to create user interfaces. The System.Drawing namespace provides

access to GDI + basic graphics functionality. The Graphics object provides

methods for drawing a variety of lines and shapes. Simple or complex shapes can

be rendered in solid or transparent colors, or using user-defined gradient or image

textures. Lines, open curves, and outline shapes are created using a Pen object. To

fill in an area, such as a rectangle or a closed curve, a Brush object is required. The

drawing methods are

DrawString(), DrawLine(), DrawRectangle(),

DrawEllipse(), DrawPie(), DrawPolygon(),

DrawArc()

and DrawImage(). Using DrawImage() we can display an image.

The method DrawString() takes five arguments

DrawString(string,Font,Brush,int X,int Y)

The first argument is a string, i.e. the text we want to display. The last two

arguments is the position where we put the string. The method DrawEllipse() is

given by

DrawEllipse(System.Drawing.Pen,float x,float y,float width,float height)

Every method in the Graphics class has to be accessed by creating an object of that

class. We can easily update the above program to render other graphical shapes like

Rectangle, Ellipse, etc. All we have to do is to apply the relevant methods appropriately.

96

7.1. DRAWING METHODS 97

Using the Pen class we can specify colour of the border and also the thickness. The

Pen class is applied for drawing shapes. The Brush is applied for filling shapes such

as SolidBrush and HatchStyleBrush.

The default Graphics unit is Pixel. By applying the PageUnit property, we can

change the unit of measurement to Inch and Millimeter.

// HelloGraphics.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class Hello : Form

{

public Hello()

{

this.Paint += new PaintEventHandler(f1_paint);

}

private void f1_paint(object sender,PaintEventArgs e)

{

Graphics g = e.Graphics;

g.DrawString("Hello C#",new Font("Verdana",20),new SolidBrush(Color.Tomato),

40,40);

g.DrawRectangle(new Pen(Color.Pink,3),20,20,150,100);

}

public static void Main()

{

Application.Run(new Hello());

}

}

The method

DrawPolygon(System.Drawing.Pen,new Point[]{

new Point(x,y),new Point(x,y),

new Point(x,y),new Point(x,y),

new Point(x,y),new Point(x,y)});

draws a polygon for a given set of points. The following program shows how do

draw two triangles using this method.

98 CHAPTER 7. GRAPHICS

// MyPolygon.cs

using System;

using System.Drawing;

using System.Windows.Forms;

class Triangle : Form

{

public Triangle()

{

this.Paint += new PaintEventHandler(draw_triangle);

}

public void draw_triangle(Object sender,PaintEventArgs e)

{

Graphics g = e.Graphics;

Pen pen1 = new Pen(Color.Blue,2);

Pen pen2 = new Pen(Color.Green,2);

g.DrawPolygon(pen1,new Point[]{new Point(150,50),

new Point(50,150),new Point(250,150)});

g.DrawPolygon(pen2,new Point[]{new Point(50,155),

new Point(250,155),new Point(250,255)});

}

public static void Main()

{

Application.Run(new Triangle());

}

}

The following program shows how to use

FillRectangle(Brush,float x,float y,float width,float height);

FillEllipse(Brush,float x,float y, float width,float height);

FillPie(Brush,float x,float y,float width,float height,float angleX,float angleY);

// Fill.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class Fill : Form

{

public Fill()

7.2. COLOR CLASS 99

{

this.Paint += new PaintEventHandler(fillpaint);

}

private void fillpaint(object sender,PaintEventArgs e)

{

Graphics g = e.Graphics;

g.FillRectangle(new SolidBrush(Color.Red),15,15,100,150);

g.FillEllipse(new SolidBrush(Color.Blue),50,50,150,120);

g.FillPie(new SolidBrush(Color.Yellow),200,200,40,40,0,90);

}

public static void Main()

{

Application.Run(new Fill());

}

}

7.2 Color Class

The Color class provides “constants” for common colors such as White, Black,

Blue, Red, Green, Pink. To create a Color structure from the specified 8-bit Color

(red,green,blue) we call for example

Color myColor = Color.FromArgb(0,255,125);

The alpha-value is implicit 255 (no transparency). To create a Color structure from

the four ARGB component (alpha,red,green,blue) we call for example

Color myColor = Color.FromArgb(51,255,0,0);

Alpha is also known as transparency where 255 is totally solid and 0 is totally

transparent.

// draw.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class Draw : Form

{

public Draw() { } // default constructor

protected override void OnPaint(PaintEventArgs e)

100 CHAPTER 7. GRAPHICS

{

FontFamily fontFamily = new FontFamily("Times New Roman");

Font font = new Font(fontFamily,24,FontStyle.Bold,GraphicsUnit.Pixel);

PointF pointF = new PointF(30,10);

SolidBrush solidbrush =

new SolidBrush(Color.FromArgb(51,255,0,0));

e.Graphics.DrawString ("Hello",font,solidbrush,pointF);

Pen myPen = new Pen(Color.Red);

myPen.Width = 50;

e.Graphics.DrawEllipse(myPen,new Rectangle(33,45,40,50));

e.Graphics.DrawLine(myPen,1,1,45,65);

e.Graphics.DrawBezier(myPen,15,15,30,30,45,30,87,20);

}

public static void Main()

{

Application.Run(new Draw());

}

}

In C# the user can choose a color by applying the ColorDialog class appropriatly.

First we have to create an object of ColorDialog class

ColorDialog cd = new ColorDialog();

An example is given in the followsing program

// ColorD.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class ColorD : Form

{

Button b = new Button();

TextBox tb = new TextBox();

ColorDialog clg = new ColorDialog();

public ColorD()

{

b.Click += new EventHandler(b_click);

b.Text = "OK";

tb.Location = new Point(50,50);

this.Controls.Add(b);

7.3. BUTTON AND EVENTHANDLER 101

this.Controls.Add(tb);

}

public void b_click(object sender,EventArgs e)

{

clg.ShowDialog();

tb.BackColor = clg.Color;

}

public static void Main(string[] args)

{

Application.Run(new ColorD());

}

}

7.3 Button and EventHandler

The Button class defines a Click event of type EventHandler. Inside the Button

class, the Click member is exactly like a private field of type EventHandler. However,

outside the Button class, the Click member can only be used on the left-hand

side of the += and -= operators. The operator += adds a handler for the event, and

the -= operator removes a handler for the event.

// Winhello.cs

using System;

using System.Windows.Forms;

using System.ComponentModel;

using System.Drawing;

class WinHello : Form

{

private Button bnclick;

public WinHello()

{

Text = "Hello World";

Size = new Size(400,400);

bnclick = new Button();

bnclick.Text = "Click.Me";

bnclick.Size = new Size(60,24);

bnclick.Location = new Point(20,60);

bnclick.Click += new EventHandler(bnclick_Click);

Controls.Add(bnclick);

Closing += new CancelEventHandler(WinHello_Closing);

102 CHAPTER 7. GRAPHICS

}

private void bnclick_Click(object sender,EventArgs ev)

{

MessageBox.Show("Hello Egoli!!!!","Button Clicked",

MessageBoxButtons.OK,MessageBoxIcon.Information);

}

private void WinHello_Closing(object sender,CancelEventArgs ev)

{

if(MessageBox.Show("Are you sure?","Confirm exit",

MessageBoxButtons.YesNo,MessageBoxIcon.Question)

== DialogResult.No) ev.Cancel = true;

}

// Initialize the main thread

// using Single Threaded Apartment (STA) model

public static void Main()

{

Application.Run(new WinHello());

}

}

We can create a Font selection dialog box using the FontDialog class. The following

program gives an example

// Fonts.cs

using System;

using System.Drawing;

using System.Windows.Forms;

public class Fonts : Form

{

Button b = new Button();

TextBox tb = new TextBox();

FontDialog flg = new FontDialog();

public Fonts()

{

b.Click += new EventHandler(b_click);

b.Text = "OK";

tb.Location = new Point(50,50);

this.Controls.Add(b);

this.Controls.Add(tb);

7.4. DISPLAYING IMAGES 103

}

public void b_click(object sender,EventArgs e)

{

flg.ShowDialog();

tb.Font = flg.Font;

}

public static void Main(string[] args)

{

Application.Run(new Fonts());

}

}

7.4 Displaying Images

Next we provide a program that displays images using the method DrawImage. The

file formats could be bmp, gif or jpeg.

// mydrawim.cs

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

public class Texturedbru : Form

{

public Texturedbru()

{

this.Text = "Using Texture Brushes";

this.Paint += new PaintEventHandler(Text_bru);

}

public void Text_bru(object sender,PaintEventArgs e)

{

Graphics g = e.Graphics;

Image bgimage = new Bitmap("forest.bmp");

g.DrawImage(bgimage,20,20,1000,600);

}

public static void Main()

{

Application.Run(new Texturedbru());

104 CHAPTER 7. GRAPHICS

}

}

Another option to display the picture is

// textbru.cs

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

public class Texturedbru : Form

{

Brush bgbrush;

public Texturedbru()

{

this.Text = "Using Texture Brushes";

Image bgimage = new Bitmap("forest.bmp");

bgbrush = new TextureBrush(bgimage);

this.Paint += new PaintEventHandler(Text_bru);

}

public void Text_bru(object sender,PaintEventArgs e )

{

Graphics g = e.Graphics;

g.FillEllipse(bgbrush,50,50,500,300);

g.FillEllipse(bgbrush,150,150,450,300);

g.FillRectangle(bgbrush,350,450,100,130);

}

public static void Main()

{

Application.Run(new Texturedbru());

}

}

7.5 Overriding OnPaint

The next program shows how to override OnPaint(). We create a Button and

clicking on it switches the color of the circle from red to blue and vice versa.

// MyOnPaint.cs

7.5. OVERRIDING ONPAINT 105

using System;

using System.Drawing;

using System.Windows.Forms;

class Draw : Form

{

private SolidBrush b = new SolidBrush(Color.Red);

public Draw()

{

Button button1 = new Button();

button1.Text = "Click Me";

button1.Location = new Point(210,220);

button1.Click += new EventHandler(HandleClick);

Controls.Add(button1);

}

protected override void OnPaint(PaintEventArgs e)

{

int diameter = 200;

int x = 50;

int y = 30;

if(b.Color == Color.Blue)

{

b.Color = Color.Red;

}

else

{

b.Color = Color.Blue;

}

e.Graphics.FillEllipse(b,x,y,diameter,diameter);

} // end OnPaint

void HandleClick(object sender,EventArgs e)

{

this.Refresh();

}

public static void Main()

{

Application.Run(new Draw());

}

}

106 CHAPTER 7. GRAPHICS

Another example we consider a rotation line. We call the Invalidate() method to

force a repaint at the end of the frame.

// Rotate.cs

using System.Windows.Forms;

using System.Drawing;

using System.Drawing.Drawing2D;

using System;

class TimerVector : Form

{

float rot;

public static void Main()

{

Application.Run(new TimerVector());

}

private void TimerEvent(Object myObject,EventArgs myEventArgs)

{

Invalidate();

}

protected override void OnPaint(PaintEventArgs pea)

{

Graphics g = pea.Graphics;

GraphicsState gs = g.Save();

Pen myPen = new Pen(Color.Black,3);

float x1 = ClientSize.Width/4;

float y1 = ClientSize.Height/4;

float x2 = (ClientSize.Width*3)/4;

float y2 = (ClientSize.Height*3)/4;

float centerx = ClientSize.Width/2;

float centery = ClientSize.Height/2;

g.TranslateTransform(-centerx,-centery);

g.RotateTransform(rot,MatrixOrder.Append);

g.TranslateTransform(centerx,centery,MatrixOrder.Append);

g.SmoothingMode = SmoothingMode.AntiAlias;

g.DrawLine(myPen,x1,y1,x2,y2);

g.Restore(gs);

rot = (rot + 1)%360;

}

public TimerVector()

{

7.5. OVERRIDING ONPAINT 107

rot = 0.0f;

Timer t = new Timer();

t.Interval = 100;

t.Tick += new EventHandler(TimerEvent);

t.Start();

}

}

Another example is

// Flicker.cs

using System;

using System.Drawing;

using System.Windows.Forms;

class Flicker : Form

{

private Timer t = new Timer();

private Size playerSize = new Size(50,50);

private Point playerPosition = new Point(0,0);

public Flicker()

{

ClientSize = new Size(500,500);

SetStyle(ControlStyles.DoubleBuffer,true);

SetStyle(ControlStyles.UserPaint,true);

SetStyle(ControlStyles.AllPaintingInWmPaint,true);

t.Interval = 40;

t.Tick += new EventHandler(TimerOnTick);

t.Enabled = true;

this.KeyDown += new KeyEventHandler(OnKeyPress);

}

private void TimerOnTick(object sender,EventArgs e)

{

this.Refresh();

this.Text = DateTime.Now.ToString();

this.Text += " " + this.PlayerPosition.ToString();

}

private Point PlayerPosition

{

get { return this.playerPosition; }

108 CHAPTER 7. GRAPHICS

set

{

if(value.X < 0)

{

this.playerPosition.X = this.ClientSize.Width-this.playerSize.Width;

}

else if(value.X+this.playerSize.Width > this.ClientSize.Width)

{

this.playerPosition.X = 0;

}

else

{

this.playerPosition.X = value.X;

}

if(value.Y < 0)

{

this.playerPosition.Y = this.ClientSize.Height-this.playerSize.Height;

}

else if(value.Y+this.playerSize.Height > this.ClientSize.Height)

{

this.playerPosition.Y = 0;

}

else

{

this.playerPosition.Y = value.Y;

}

}

}

private void OnKeyPress(object sender,KeyEventArgs e)

{

if(e.KeyValue == 37)

{

this.PlayerPosition =

new Point(this.PlayerPosition.X-this.playerSize.Width,this.PlayerPosition.Y);

}

if(e.KeyValue == 38)

{

this.PlayerPosition =

new Point(this.PlayerPosition.X,this.PlayerPosition.Y-this.playerSize.Width);

}

if(e.KeyValue == 39)

{

this.PlayerPosition =

7.5. OVERRIDING ONPAINT 109

new Point(this.PlayerPosition.X+this.playerSize.Height,this.PlayerPosition.Y);

}

if(e.KeyValue == 40)

{

this.PlayerPosition =

new Point(this.PlayerPosition.X,this.PlayerPosition.Y+this.playerSize.Height);

}

}

protected override void OnPaint(PaintEventArgs e)

{

e.Graphics.FillRectangle(new SolidBrush(Color.Red),this.PlayerPosition.X,

this.playerPosition.Y,this.playerSize.Width,

this.playerSize.Height);

}

public static void Main()

{

Application.Run(new Flicker());

}

}