System Programming with .NET

1   This Course

Instructor:
Course web page:
http://lipn.univ-paris13.fr/~rodriguez/teach/dotnet/2014-15/
Exercises and project:
Available here.
Evaluation:
  • Continuous assessment
  • Code review on Friday 5 December
  • Submission of your code by the end of the month

1.1   Syllabus

  • Multithreaded programming
  • Sockets
  • Accessing the registry
  • Event logs
  • Windows Services

2   Multithreaded Programming

All (almost all, actually) you will find here has been extracted from the MSDN. A good starting point is the section Threading Objects and Features of the .NET Framework Development Guide.

2.1   Introduction

2.2   Thread Creation, Destruction, Generalities

  • As a general overview, see the Managed Threading Basics section of the guide
  • Threads are created via delegates, or parametrized delegates (so as to pass data to the thread)
  • On single-processor machines, threads are executed during time slots of 20ms
  • Executing Thread.Sleep (0) context-switches to a different executable thread and terminates the time slot given to this one
  • Exceptions in thread generally terminate the process, with the exceptions mentioned here
  • Do handle exceptions on threads, as they can have unexpected consequences if your threads synchronize in any way
  • Foreground and background threads and the IsBackground property
  • Pausing and Resuming Threads

On a more operational note, see the following:

2.3   Thread States

  • Main article
  • A thread can be in more than one state at a time (!!)

2.4   Synchronization

  • A good conceptual overview to the topic

  • We can divide all synchronization primitives in two classes:

    Locking:

    This is guaranteeing mutual exclusion, only one thread (or a specific number of them) access one resource at a time.

    Primitives: lock (statement), Monitor, Mutex, SpinLock, ReaderWriterLock, Semaphore

    Signaling:

    This is informing of a specific condition from one thread to another, or to several ones.

    Primitives: EventWaitHandle and friends (auto reset, manual reset), Mutex, Semaphore, Barriers. All classes but Barrier derive from WaitHandle.

2.4.1   Monitors (locking)

  • The Monitor class is a static class, that is, all methods are static
  • See the Remarks section for the Monitor class.

There are four important operations:

System.Threading.Monitor.Enter (obj);
System.Threading.Monitor.Exit (obj);
System.Threading.Monitor.Wait (obj);
System.Threading.Monitor.Pulse (obj);

Enter and Exit lock and unlock threads calling these functions with the same object, like if the object was a semaphore or a mutex in the Phtreads library.

Wait and Pulse make possible that one thread that had acquired the monitor temporarily releases it, allowing another thread to acquire it. When that second thread acquires the monitor and calls Pulse, the first thread is scheduled to re-acquire the monitor, which may happen as soon as the second thread releases the monitor (calling Exit). Calls tu Pulse do not accumulate, that is, they are not remembered. If a thread calls Pulse and no thread is Wait-ing, the call is lost, and next time a thread calls Wait it may wait forever.

2.4.2   Wait Handles (signaling and locking)

  • Conceptual overview here
  • All signaling primitives (EventWaitHandle, AutoResetEvent, Mutex...) derivate from the class WaitHandle !!
  • Encapsulates Win32 synchronization handle
  • Can be less portable and less efficient in terms of resource requirements than monitors/locks
  • It is abstract, so it cannot be instantiated

Important methods:

handle.WaitOne ()
System.Threading.WaitHandle.WaitAll (WaitHandle [])
System.Threading.WaitHandle.WaitAny (WaitHandle [])

Derived classes differ in thread affinity (who can signal/release the wait handle, the thread that got it, or anyone?); mutexes have thread affinity, others have not

2.4.3   EventWaitHandle, AutoResetEvent, ManualResetEvent (signaling, but also locking)

All the three classes inherit from WaitHandle. We focus on EventWaitHandle, as the behaviour of AutoResetEvent and ManualResetEvent can be explained in terms of EventWaitHandle.

We can create an EventWaitHandle as follows:

h = new EventWaitHandle (false or true, EventResetMode.AutoReset or EventResetMode.ManualReset);
  • The object h is like a traffic semaphore, it can be red or green
  • Initialized to false (red) or true (green)
  • It can work in two modes:
    • AutoReset: Each car that crosses the semaphore (a thread calling WaitOne), turns the semaphore to red
    • ManualReset: Whenever the semaphore is green, every car (thread) can cross freely (WaitOne does not block). If you want to stop cars, you need to manually turn the semaphore red (call the method Reset)
  • Method Set() turns the semaphore to green
  • Method Reset() turns the semaphore to red
  • Method WaitOne() lets the thread go if the semaphore is in green, and blocks if in red

AutoResetEvent and ManualResetEvent are classes deriving from EventWaitHandle and behave like the latter one, as if the instance of EventWaitHandle had been constructing passing the appropriate value to the second parameter of the constructor

2.4.4   CountdownEvent (signaling)

Consider the following code:

CountdownEvent cde = new CountdownEvent (12)
  • It creates a CountdownEvent
  • This is an object that behaves like a traffic semaphore with two integer variables associated to it:
    • the CurrentCount, it will act as a countdown counter, and
    • the InitialCount, the 12 passed to the constructor.
  • The value of InitialCount can only be modified on the constructor
  • You can modify the value of CurrentCount with
    • Signal(), decrements by 1
    • AddCount(), increments by 1
    • Reset(), sets it to the value of InitialCount
  • A CountdownEvent has a WaitHandle associated to it, as well.
  • When CurrentCount is 0, it sets the handle, that is, the semaphore becomes green.
  • Otherwise the handle is closed (semaphore red)
  • To block until the CurrentCount becomes 0, call the instance method Wait()
  • If the handle is set (the CurrentCount is 0), calling Signal or AddCount will raise an exception. You can only call Reset.

2.4.5   Other Synchronization Primitives

2.5   The Thread Pool

  • Interesting information in the Remarks section of the ThreadPool Class
  • System.Threading.ThreadPool is static
  • Threadpool.QueueUserWorkItem is the routine to execute start a thread
  • You may pass an object to the function; the thread's function will receive it in its (single) argument (it will receive null if you did not provide an argument to QueueUserWorkItem)
  • All threads are background threads

2.6   Timers

See this article on the development guide.

3   Network Programming

As before, he will heaviliy rely on the section Network Programming in the .NET Framework of the .NET Framework Development Guide.

Namespace System.Net.Sockets.

3.1   Sockets

We focus on connective sockets, using TCP. The first step is creating the socket.

3.1.1   The Client Side

3.1.2   The Server Side

While the client usually only establishes one connection at a time, the server needs to deal with potentially several ones. As in probably any other language you know, we have a select function for checking the status of several threads and blocking until we need to do some work.

The static method Socket.Select takes three lists of (references to) Socket instances and can block until there is something to read (first list), something to write (second list) or an error was reported (third list). A call to Select will return when at least one socket is ready to do work with it, which we can check with the Poll method of the Socket class.

If a socket is a listening socket, i.e., it is being used to accept new clients, place it in the reading list. If a client has arrived, and Accept will return immediately with its associated Socket, then the Poll method will return true when invoked with the SelectMode.SelectRead switch.

3.1.3   UDP Sockets

  • The client do not need to call Connect, it can rather send data using the SendTo and ReceiveFrom methods from the Socket class, which allow to directly provide the destination address
  • If, however, it calls Connect on a UDP socket, incoming packages from a different IP address and port will be discarded.
  • They client may also want to Bind the socket to an specific local IP and port before sending data
  • As for the server, there is no need to call Listen (as you are not accepting connections), just create the socket and bind it to a local IP and port

3.2   Above the Sockets interface: TCP and UDP Services

The classes TcpClient, TcpListener, and UdpClient help you opening data connections over TCP and UDP, as well as accepting incoming TCP connections. They abstract away the details of setting up and using sockets and provide a simplified access to the network.

Their use is straightforward, see

4   The Windows Registry

The Windows registry is a database where applications can store settings, configuration, and in general information that shall be preserved after the application is closed.

It offers an interface to store and retrieve information with a tree-like structure, where each branch of the tree is technically called a key and each node of the tree can store zero or more pairs of the form

(name, value)

For instance, the well known application Notepad seems to store some settings under the key

KEY_CURRENT_USER\Software\Microsoft\Notepad

where we find a number of name-value pairs storing certain settings, such as:

("IfFaceName", "Fixedsys")
("iWindowPosX", 0x00000012)
("iWindowPosY", 0x0000002f)

You can visualize (and modify!) the Windows registry using the tool regedit.exe (you may want to type the name of the program at the Start > Execute dialog).

In .NET, the two main classes to access the registry are the Registry class and the RegistryKey class, both located within the namespace Microsoft.Win32.

Important entry points to the MSDN documentation:

Two examples:

object obj;
obj = Registry.GetValue("HKEY_CURRENT_USER\\Software\\WinRAR\\ArcHistory", "1", null);

RegistryKey k;

k = Registry.CurrentUser;

Console.WriteLine (k);
Console.WriteLine (k.Name);
Console.WriteLine (k.ValueCount);
Console.WriteLine (k.SubKeyCount);

foreach (var s in k.GetValueNames()) Console.WriteLine(s);
foreach (var s in k.GetSubKeyNames()) Console.WriteLine(s);

There are several standard entry points to the registry, they correspond to the nodes of the tree hanging just below the root of the tree (see the static fields of class Registry).

Also, each value in a name-value pair has a type associated to it (see the method Registry.SetValue and the enumeration RegistryValueKind).

5   Windows Event Logs

The Windows event log is a place where both Windows and applications running on top of it record important software and hardware events. Each entry in the log contains informations such as the machine where it was generated, the generation time, a source, and of course a text message.

The source identifies, conceptually, the application originating the event, although a source does not need to designate an application. Both the log where the event will be stored as well as the source need to be created within the system well before events can be logged.

Several standard event logs are already present in the machine. In particular, the log Application will be of interest for us. You can view the contents of existing event logs using the Event Viewer (Start > Control Panel > System and Security > Administrative Tools > Event Viewer, administrative permission will be required). Many informations about the existing event logs are stored at the registry, see here.

It is possible to create new event logs and sources. This should be done well before you start using the log, as Windows apparently needs some time to update its files (...). Ideally this should be done during the installation of your application.

In .NET, we can

6   Windows Services

A Windows service is a notion analog to that of a daemon in Unix: a long-running process, executing in background, and offering some service to someone else. See this introduction to service applications from the MSDN library.

Unlike a Unix daemon, which often only offers one service, a single Windows program can contain several Windows services. They must be installed on the server before they run. That is, we need to carry out some specific procedure by which the Service Control Manager (SCM) will get to know the executable binary where our services are contained and the services themselves. Once this is done, we will be able to start, stop, or pause the service from the SCM.

Services, thus, function differently from regular applications. The overall procedure to write and run a service coded in .NET is as follows:

  1. A service will be a class inheriting from ServiceBase. You will override the methods OnStart, OnStop and optionally OnPause and OnContinue.
  2. The Main method of your program will instantiate all services in your program and pass each of them to the static method ServiceBase.Run.
  3. The binary program needs to define an installer class, inheriting from System.Configuration.Install.Installer. Within its constructor, construct and initialize instances of the classes ServiceProcessInstaller and ServiceInstaller. These will tell the SCM how to invoke, respectively, the binary program containing the service, and the services themselves.
  4. To install and uninstall the service, use the tool installutil.exe. Administrative rights are required.

Here you have a minimal example (warning, I didn't compile it!):

using System.ServiceProcess;
using System.Configuration.Install;

static class Program {
 static void Main() {
       ServiceBase.Run (new PingService ());
 }
}

public class PingService : ServiceBase {
 public PingService() {
       ServiceName = "PingService";
       CanPauseAndContinue = true;
 }

 protected override void OnStart(string[] args) {
       // do something
 }

 protected override void OnStop() {
       // do something
 }

 protected override void OnPause() {
       // do something
 }

 protected override void OnContinue() {
       // do something
 }
}

/* for the install utility to recognize Setup as a valid installer */
[RunInstaller(true)]
public class Setup : System.Configuration.Install.Installer {
 private ServiceProcessInstaller proc_inst;
 private ServiceInstaller serv_inst;

 public Setup() {
       proc_inst = new ServiceProcessInstaller();
       serv_inst = new ServiceInstaller();

       proc_inst.Account = ServiceAccount.LocalSystem;
       proc_inst.Password = null;
       proc_inst.Username = null;

       serv_inst.Description = "A long description of your service";
       serv_inst.DisplayName = "The name you will see in the SCM";
       serv_inst.ServiceName = "PingService";
       serv_inst.StartType = ServiceStartMode.Automatic;

       Installers.Add (proc_inst);
       Installers.Add (serv_inst);
 }
}

Several good entry points to the subject on the MSDN library:

Finally, to start the service, open the SCM (Start > Programs > Administrative Tools > Services), search for your service (the column "Name" is the DisplayName property of the ServiceInstaller object created during installation) and click on Start, Stop (or Pause). You can also manually do it from code, using the ServiceController class.