Sunday, May 15, 2011

Windows Terminal Services (WTS) with VB.NET – what why how

I’m luckily have opportunity to works with Windows Terminal Services (WTS). The application that my team developed is running under Citrix machine, and there is a issue related with session information of each client ~ brief introduction.

Lets look more details about Windows Terminal Services (WTS).
Windows Terminal Services is one of Remote Access Technologies that allow IT administrators to perform administrative tasks; and IT users run programs on a remote machine as if they were working locally. This component provides the ability to host multiple, simultaneous client sessions on Windows Server machine. Through terminal emulation, its allows the same set of applications to run on diverse types of desktop hardware.
For organizations that looks flexibility to deploy applications and control desktop management costs, this architecture offers an important enhancement to the traditional two or three-tier client-server architecture based on servers and full-scale personal machine.

TerminalServicesArchitecture

Picture: Windows Terminal Services Architecture

Windows Terminal Services used the Remote Desktop Protocol (RDP) to interact with user. This protocol have following features and capabilities:

  • Encryption – RDP uses RSA security’s RC4 cipher ~ a stream cipher designed to efficiently encrypt small amounts of data. RC4 is designed for secure communications over networks.
  • Bandwidth Reduction – RDP supports various mechanisms to reduce the amount of data transmitted over a network connection. Mechanisms include data compression, persistent caching of bitmaps, and caching of glyphs and fragments in RAM
  • Roaming Disconnect – A user can manually disconnect from a remote desktop session without logging off. The user is automatically reconnected to their disconnected session when he or she logs back onto the system, either from the same device or a different device. When a user's session is unexpectedly terminated by a network or client failure, the user is disconnected but not logged off.
  • Clipboard Mapping – Users can delete, copy, and paste text and graphics between applications running on the local computer and those running in a remote desktop session, and between sessions.
  • Print Redirection – Applications running within a remote desktop session can print to a printer attached to the client device.
  • Virtual Channels – By using RDP virtual channel architecture, existing applications can be augmented and new applications can be developed to add features that require communications between the client device and an application running in a remote desktop session.
  • Remote Control – Computer support staff can view and control a remote desktop session. Sharing input and display graphics between two remote desktop sessions gives a support person the ability to diagnose and resolve problems remotely.
  • Network Load Balancing.
  • Smart Card Authentication through Remote Desktop Services.

TerminalServerInterationsWithRDP

Picture: Terminal Server Interactions with RDP

The user/ client initiates a connection to the Terminal Server through TCP port 3389. The Terminal Server RDP listener thread detects the session request and creates a new RDP stack instance to handle the new session request. The listener thread hands over the incoming session to the new RDP stack instance and continues listening on the TCP port for further connection attempts. Each RDP stack is created as the client sessions are connected to handle negotiation of session configuration details.

So, how I used it to help me to solve my team issue. There is one of Windows API called Remote Desktop Services API to implement the additional functionality, and dynamically link to the WtsApi32 library. There are several functions that I used to help me solve my team issue.

  • WTSEnumerateSessions – Retrieves a list of sessions on a specified Remote Desktop (RD) Session Host server.
  • WTSFreeMemory – Frees memory allocated by a Remote Desktop Services function.
  • WTSQuerySessionInformation – Retrieves session information for the specified session on the specified Remote Desktop (RD) Session Host server. It can be used to query session information on local and remote RD Session Host servers.
  • WTSOpenServer – Opens a handle to the specified Remote Desktop Session Host server.
  • WTSCloseServer – Closes an open handle to a Remote Desktop Session Host (RD Session Host) server.

And there are another functions from “Kernel32” library, that I used. There are:

  • GetCurrentProcessId – Retrieve the current of process Id.
  • WTSGetActiveConsoleSessionId – Retrieves the Remote Desktop Services session that is currently attached to the physical console. The physical console is the monitor, keyboard, and mouse. Note that it is not necessary that Remote Desktop Services be running for this function to succeed.
  • ProcessIdToSessionId – Retrieves the Remote Desktop Services session associated with a specified process.

 

   1:      Private Declare Function ProcessIdToSessionId Lib "Kernel32.dll" Alias "ProcessIdToSessionId" (ByVal processId As Int32, ByRef sessionId As Int32) As Boolean

   2:   

   3:      Private Declare Function WTSQuerySessionInformation Lib "WtsApi32.dll" Alias "WTSQuerySessionInformationW" (ByVal hServer As IntPtr, ByVal SessionId As Int32, ByVal WTSInfoClass As Int32, <MarshalAs(UnmanagedType.LPWStr)> ByRef ppBuffer As String, ByRef pCount As Int32) As Boolean

   4:   

   5:      Private Declare Function WTSQuerySessionInformation2 Lib "WtsApi32.dll" Alias "WTSQuerySessionInformationW" (ByVal hServer As IntPtr, ByVal SessionId As Int32, ByVal WTSInfoClass As Int32, ByRef ppBuffer As IntPtr, ByRef pCount As Int32) As Boolean

   6:   

   7:      Private Declare Function WTSEnumerateSessions Lib "WtsApi32.dll" Alias "WTSEnumerateSessions" (ByVal hServer As IntPtr, <MarshalAs(UnmanagedType.U4)> ByVal Reserved As Int32, <MarshalAs(UnmanagedType.U4)> ByVal Version As Int32, ByRef pSessionInfo As IntPtr, <MarshalAs(UnmanagedType.U4)> ByVal pCount As Int32) As Int32

   8:   

   9:      Private Declare Function WTSOpenServer Lib "WtsApi32.dll" Alias "WTSOpenServer" (ByVal pServerName As String) As IntPtr

  10:   

  11:      Private Declare Function WTSGetActiveConsoleSessionId Lib "Kernel32.dll" Alias "WTSGetActiveConsoleSessionId" () As Int32

  12:   

  13:      Private Declare Sub WTSCloseServer Lib "WtsApi32.dll" Alias "WTSCloseServer" (ByVal hServer As IntPtr)

  14:   

  15:      Private Declare Sub WTSFreeMemory Lib "WtsApi32.dll" Alias "WTSFreeMemory" (ByVal pMemory As IntPtr)

  16:   

  17:      Private Declare Function GetCurrentProcessId Lib "Kernel32.dll" Alias "GetCurrentProcessId" () As Int32





And I have enumeration of WTS information, called WTSInfoClass.


   1:      Private Enum WTSInfoClass As Integer
   2:          WTSInitialProgram
   3:          WTSApplicationName
   4:          WTSWorkingDirectory
   5:          WTSOEMId
   6:          WTSSessionId
   7:          WTSUserName
   8:          WTSWinStationName
   9:          WTSDomainName
  10:          WTSConnectState
  11:          WTSClientBuildNumber
  12:          WTSClientName
  13:          WTSClientDirectory
  14:          WTSClientProductId
  15:          WTSClientHardwareId
  16:          WTSClientAddress
  17:          WTSClientDisplay
  18:          WTSClientProtocolTyep
  19:          WTSIdleTime
  20:          WTSLogonTime
  21:          WTSIncomingBytes
  22:          WTSOutgoingBytes
  23:          WTSIncomingFrames
  24:          WTSOutgoingFrames
  25:      End Enum







And below is the main method to load session information of specified server name.


   1:  Public Function LoadSessionInfo(ByVal serverName As String) As SessionInfo
   2:          Dim ptrOpenedServer As IntPtr
   3:          Dim dictSessionIds As New Dictionary(Of String, Integer)
   4:   
   5:          Try
   6:              ptrOpenedServer = WTSOpenServer(serverName)
   7:              If ptrOpenedServer = vbNull Then
   8:                  Throw New Exception("Terminal Services not running on: " & serverName)
   9:              End If
  10:   
  11:              Dim retVal, count As Int32
  12:              Dim ptrSessionInfo As IntPtr = IntPtr.Zero
  13:              count = 0
  14:              Try
  15:                  retVal = WTSEnumerateSessions(ptrOpenedServer, 0, 1, ptrSessionInfo, count)
  16:                  If retVal <> 0 Then
  17:                      Dim sessionInfos() As WTS_SESSION_INFO = New WTS_SESSION_INFO(count) {}
  18:                      Dim ptrSession As IntPtr
  19:                      For i As Integer = 0 To count - 1
  20:                          ptrSession = ptrSessionInfo.ToInt32() + (i * Marshal.SizeOf(sessionInfos(i)))
  21:                          sessionInfos(i) = CType(Marshal.PtrToStructure(ptrSession, GetType(WTS_SESSION_INFO)), WTS_SESSION_INFO)
  22:                      Next
  23:   
  24:                      WTSFreeMemory(ptrSessionInfo)
  25:   
  26:                      Dim winStationName As String
  27:                      Dim tmpArr(sessionInfos.GetUpperBound(0)) As STR_SESSION_INFO
  28:                      For i = 0 To tmpArr.GetUpperBound(0)
  29:                          tmpArr(i).SessionID = sessionInfos(i).SessionId
  30:   
  31:                          winStationName = sessionInfos(i).pWinStationName
  32:                          tmpArr(i).StationName = winStationName
  33:   
  34:                          tmpArr(i).ConnectionState = sessionInfos(i).State.ToString()
  35:   
  36:                          If Not String.IsNullOrEmpty(winStationName) Then
  37:                              If Not dictSessionIds.ContainsKey(winStationName) Then
  38:                                  dictSessionIds.Add(winStationName, sessionInfos(i).SessionId)
  39:                              End If
  40:                          End If
  41:                      Next
  42:   
  43:                      ReDim sessionInfos(-1)
  44:                  Else
  45:                      Throw New Exception("No data returned")
  46:                  End If
  47:              Catch exInner As Exception
  48:                  Throw New Exception(exInner.Message & Environment.NewLine & Marshal.GetLastWin32Error)
  49:              End Try
  50:          Catch ex As Exception
  51:              Try
  52:                  WTSCloseServer(ptrOpenedServer)
  53:                  WTSFreeMemory(ptrOpenedServer)
  54:              Catch exWtsCloseServer As Exception
  55:              End Try
  56:   
  57:              Throw ex
  58:          End Try
  59:   
  60:          Dim result As New SessionInfo
  61:   
  62:          'Get ProcessId of TS Session that executed this TS Session
  63:          Dim activeProcess As Int32 = GetCurrentProcessId()
  64:          Dim activeSession As Int32 = 0
  65:          If Not ProcessIdToSessionId(activeProcess, activeSession) Then
  66:              'Nothing
  67:          End If
  68:   
  69:          Dim strBuffer As String
  70:          Dim ptrBuffer As IntPtr
  71:          Dim returnedLength As Int32
  72:   
  73:          'Member in this list will be ignore during generic query session iteration.
  74:          Dim ignoreQuerySession As New List(Of WTSInfoClass)
  75:   
  76:          'Member in this list will use function WTSQuerySessionInformation2
  77:          '    to get their information, instead WTSQuerySessionInformation.
  78:          '    By default, use WTSQuerySessionInformation is not contains in list.
  79:          Dim querySession2 As New List(Of WTSInfoClass)
  80:          querySession2.Add(WTSInfoClass.WTSClientAddress)
  81:   
  82:          'Get Windows Station Name, as a pre-requirement for some info,
  83:          '    likes: SessionId, ClientName, ClientAddress.
  84:          If WTSQuerySessionInformation(ptrOpenedServer, activeSession, WTSInfoClass.WTSWinStationName, strBuffer, returnedLength) Then
  85:              result.WinStationName = strBuffer
  86:              ignoreQuerySession.Add(WTSInfoClass.WTSWinStationName)
  87:   
  88:              'Skip ClientAddress and ClientName if this is a console session
  89:              If String.Equals(result.WinStationName, "Console") Then
  90:                  ignoreQuerySession.Add(WTSInfoClass.WTSClientAddress)
  91:                  ignoreQuerySession.Add(WTSInfoClass.WTSClientName)
  92:              End If
  93:          End If
  94:   
  95:          'Use stationName to get the correct sessionId from dictSessionId.
  96:          If dictSessionIds.ContainsKey(result.WinStationName) Then
  97:              Try
  98:                  result.SessionId = Integer.Parse(dictSessionIds(result.WinStationName))
  99:                  ignoreQuerySession.Add(WTSInfoClass.WTSSessionId)
 100:              Catch ex As Exception
 101:              End Try
 102:          End If
 103:   
 104:          Dim wtsInfoClassValues As Array = [Enum].GetValues(GetType(WTSInfoClass))
 105:          Dim wtsInfoClassItem As WTSInfoClass
 106:          Dim index As Integer = -1
 107:          Dim querySessionResult As Boolean
 108:          While index < wtsInfoClassValues.Length - 1
 109:              index += 1
 110:              wtsInfoClassItem = wtsInfoClassValues.GetValue(index)
 111:   
 112:              If ignoreQuerySession.Contains(wtsInfoClassItem) Then Continue While
 113:   
 114:              If querySession2.Contains(wtsInfoClassItem) Then
 115:                  querySessionResult = WTSQuerySessionInformation2(ptrOpenedServer, activeSession, wtsInfoClassItem, ptrBuffer, returnedLength)
 116:              Else
 117:                  querySessionResult = WTSQuerySessionInformation(ptrOpenedServer, activeSession, wtsInfoClassItem, strBuffer, returnedLength)
 118:              End If
 119:   
 120:              If querySessionResult Then
 121:                  Select Case wtsInfoClassItem
 122:   
 123:                      'TODO: Assign to proper place
 124:                      Case WTSInfoClass.WTSClientAddress
 125:                          Dim obj As New WTS_CLIENT_ADDRESS()
 126:                          obj = CType(Marshal.PtrToStructure(ptrBuffer, obj.GetType()), WTS_CLIENT_ADDRESS)
 127:                          result.Address(2) = obj.Address(2)
 128:                          result.Address(3) = obj.Address(3)
 129:                          result.Address(4) = obj.Address(4)
 130:                          result.Address(5) = obj.Address(5)
 131:   
 132:                  End Select
 133:              End If
 134:   
 135:              If ptrBuffer <> IntPtr.Zero Then
 136:                  WTSFreeMemory(ptrBuffer)
 137:                  ptrBuffer = IntPtr.Zero
 138:              End If
 139:              strBuffer = ""
 140:          End While
 141:   
 142:          WTSCloseServer(ptrOpenedServer)
 143:   
 144:          Return result
 145:      End Function



To supported above codes, I used 3 (three) structures: WTS_SESSION_INFO, STR_SESSION_INFO, and WTS_CLIENT_ADDRESS.


   1:     <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
   2:      Private Structure WTS_SESSION_INFO
   3:          Dim SessionId As Int32 'DWORD integer
   4:          Dim pWinStationName As String ' Integer LPTSTR - Pointer to a null-terminated string containing the name of the WinStation of this session
   5:          Dim State As WTSConnectStateClass
   6:      End Structure
   7:   
   8:      Private Structure STR_SESSION_INFO
   9:          Dim SessionId As Integer
  10:          Dim StationName As String
  11:          Dim ConnectionState As String
  12:      End Structure
  13:   
  14:      <StructLayout(LayoutKind.Sequential)> _
  15:      Private Structure WTS_CLIENT_ADDRESS
  16:          Public AddressFamily As Integer
  17:          <MarshalAs(UnmanagedType.ByValArray, SizeConst:=20)> _
  18:          Public Address As Byte()
  19:      End Structure
  20:   
  21:      Public Enum WTSConnectStateClass As Integer
  22:          WTSActive
  23:          WTSConnected
  24:          WTSConnectQuery
  25:          WTSShadow
  26:          WTSDisconnected
  27:          WTSIdle
  28:          WTSListen
  29:          WTSReset
  30:          WTSDown
  31:          WTSInit
  32:      End Enum





The return value of function LoadSessionInfo is SessionInfo.
SessionInfo is the plain/ model class that hold the WTS Information (I considered enumeration WTSInfoClass for those properties).


On line-123 “TODO: Assign to proper place”, this is the place to assign WTS information to our model class ~ SessionInfo. I put sample codes that assign the information of client address (IP).


I also have logic to check either application is running on remote terminal or local terminal ~ called IsRunningLocally.


   1:   Public Function IsRunningLocally() As Boolean
   2:          Dim ptrBuffer As IntPtr = IntPtr.Zero
   3:          Dim sessionId, currentSessionId, returnedLength As Integer
   4:   
   5:          Try
   6:              WTSQuerySessionInformation2(IntPtr.Zero, -1, WTSInfoClass.WTSSessionId, ptrBuffer, returnedLength)
   7:              sessionId = Marshal.ReadInt32(ptrBuffer, returnedLength)
   8:          Catch ex As Exception
   9:              sessionId = -1
  10:          Finally
  11:              WTSFreeMemory(ptrBuffer)
  12:              ptrBuffer = IntPtr.Zero
  13:          End Try
  14:   
  15:          Try
  16:              currentSessionId = WTSGetActiveConsoleSessionId()
  17:          Catch ex As Exception
  18:              currentSessionId = -1
  19:          End Try
  20:   
  21:          If currentSessionId <> sessionId Then Return False
  22:   
  23:          Return True
  24:      End Function



References


Saturday, February 12, 2011

Find Second Highest Value in a Table Without Sub-Query

Let have a simple table to working-on and some dummy data.

SQL:
CREATE TABLE Course(
    CourseTitle nvarchar(32) NOT NULL,
    CourseCapacity int NOT NULL);

If we want to get second highest value in that table, the first idea may we have is use sub-query (there are many answers that we can find on internet). Some of those solutions:

(1) SQL:
SELECT * FROM Course c1 WHERE (2 - 1) =
    (SELECT COUNT(DISTINCT(CourseCapacity))
            FROM Course c2
            WHERE c2.CourseCapacity > c1.CourseCapacity);

(2) SQL:
SELECT TOP 1 * FROM Course WHERE
    CourseCapacity < (SELECT MAX(CourseCapacity) FROM Course)
    ORDER BY CourseCapacity DESC;

But, how if only used single query?
The first idea that I got is use JOIN. And the possible JOIN type is NATURAL JOIN for this case. But that still not answer the problem. So, I tried to JOIN the same table with condition column in table-1 less than column in table-2.

SQL:
SELECT *
    FROM Course c1, Course c2
    WHERE c1.CourseCapacity < c2.CourseCapacity;

Results:
image
Above result, show to us that capacity of right part is less than left part. And the highest value of right part is the second highest of the table (MPP ~ 13). GOT IT!

So, the next step is modify the previous value with add TOP and ORDER BY clauses. The objective is do ORDER BY the above result according to right part of column CourseCapacity and then select the TOP 1 row.

SQL:
SELECT TOP 1 c1.*
    FROM Course c1, Course c2
    WHERE c1.CourseCapacity < c2.CourseCapacity
    ORDER BY c1.CourseCapacity DESC;

Result:
image

Case closed Smile

Thursday, September 16, 2010

Represent a Superclass/Subclass Relationship

For each superclass/ subclass relationship in ER-diagrams, we identify the superclass entity as parent entity and the subclass entity as the child entity.

There are various options on how to represent those relationship as one or more relations. The selection of the most appropriate option is dependent on a number of factors such as the disjointness and participation constraints on that relationship.

For example: Staff can have one or more position on the same time in the company. The available positions are Staff, Manager, Salesman, Interviewer, and Head of Branch. It’s also possible in the future, company adding new position.

There are many solution for establish that relationship:

  1. Create only single relation with one or more discriminators to distinguish the type of each tuple.
  2. Create two relations where one relation for superclass and one relation for all subclasses with one or more discriminators to distinguish the type of each tuple.
  3. Create many relations where one relation for each combined superclass/ subclass.
  4. Create many relations where one relations for superclass and one for each subclass.

Here, I’m want to offering another idea for establish that relationship, “Bitwise Operation Attribute”. In computer programming, a bitwise operation operates on one or two bit patterns or binary numerals at the level of their individual bits.

In this case, we use AND bitwise operation.

Table Position
image

Table Employee
image

How to getting the position for employee S102 ?

  • Do bitwise operations in T-SQL for getting the list of position;
  • Do natural join.

image

T-SQL:
SELECT * FROM Employee e, Position p
WHERE (e.position & p.posValue) > 0 AND e.empID = ‘S102’
ORDER BY p.posValue;

Thursday, July 23, 2009

Circular Queue

Definition
An implementation of a limited collection with specified a fixed numbers of items in which only the earliest added item may be accessed using an array.
Basic operations are add (to the tail; or the last item of list) or enqueue; and throw (from the head; or the first item of a list) or dequeue. Also known as “first-in, first-out” or FIFO. And list is a collection of items accessible one after another beginning at the head and ending at the tail.

clip_image002
Above visually shows that the queue has no real end and it can loop around the queue. However, since memory is never physically created as a ring, a linear representation is generally used as is done below.

Illustrations
1. A circular queue first starts empty and of some predefined length. For example, circular queue with length 7.

clip_image002[4]

2. Enqueue the defined circular queue until 7 times, then it is completely full.

clip_image004

3. The head is first element; tail is last element.
4. When dequeue was execute, the first element “6” will throw; and head index will increment.
5. Head index will increment and back to first when equals with defined length of circular queue.

Implementations (with C# .NET 3.5)
Defined length of circular queue directly when instantiate the object.

  1: /// <summary>Default constructor.</summary>
  2: /// <exception cref="System.ArgumentOutOfRangeException"></exception>
  3: /// <param name="capacity">The number of elements that the new list can initially store.</param>
  4: public clsCircularQueue(int capacity)
  5: {
  6:      if (capacity < 0)
  7:          throw new ArgumentOutOfRangeException("capacity", "Cannot create an array with a negative size.");
  8:
  9:      //...other code.
 10: }

Enqueue implementation or add member/ element.

  1: /// <summary>Adds the integer value to the tail of Circular Queue.</summary>
  2: /// <exception cref="System.IndexOutOfRangeException"></exception>
  3: /// <param name="value"></param>
  4: public void Enqueue(int value)
  5: {
  6:     if (this.Length == 0)
  7:         throw new IndexOutOfRangeException();
  8:
  9:     if (this.tail >= this.items.Length)
 10:         this.tail = 0;
 11:            
 12:     this.items[this.tail] = value;
 13:     this.tail++;
 14: }

Dequeue implementation or throw member/ element.

  1: /// <summary>Returns the integer value at the head positon of Circular Queue.</summary>
  2: /// <exception cref="System.MissingMemberException"></exception>
  3: /// <returns></returns>
  4: public int Dequeue()
  5: {
  6:     int? result = this.items[this.head];
  7:
  8:     if (!result.HasValue)
  9:         throw new MissingMemberException();
 10:
 11:     this.head++;
 12:     if (this.head >= this.Length)
 13:         this.head = 0;
 14:
 15:     return result.Value;
 16: }

Because that class was inherit from IEnumerable<T>, so we must implemented the IEnumerator<T>. In this case, we implemented own IEnumerator<T>, called clsEnumeratorCircularQueue.


The MoveNext implementation of that custom IEnumerator.

  1: /// <summary>Advances the enumerator to the next element of the collection.</summary>
  2: /// <returns></returns>
  3: public bool MoveNext()
  4: {
  5:     try
  6:     {
  7:         this.counter++;
  8:         if (counter > this.circularQueue.Length)
  9:         {
 10:             this.current = null;
 11:             this.counter = 0;
 12:             return false;
 13:         }
 14:
 15:         this.current = this.circularQueue.Dequeue();
 16:         return true;
 17:     }
 18:     catch
 19:     {
 20:         return false;
 21:     }
 22: }

Lets test!

  1: ...
  2: int capacity = 8;
  3: Console.WriteLine("Capacity: " + capacity);
  4: clsCircularQueue circularQueue = new clsCircularQueue(capacity);
  5: for (int i = 1; i <= 10; i++)
  6:     circularQueue.Enqueue(i);
  7:
  8: Console.WriteLine("Iteration with foreach.");
  9: foreach (int value in circularQueue)
 10:     Console.Write(value + " | ");
 11: ...

The result:


image


The complete source-code:

  1: using System;
  2: using System.Collections.Generic;
  3:
  4: namespace Ronald.CircularQueue
  5: {
  6:     /// <summary>Represent circular queue.</summary>
  7:     public sealed class clsCircularQueue : IEnumerable<Int32>
  8:     {
  9:         #region Declarations
 10:
 11:         private readonly int?[] items = null;
 12:         private int head = 0;
 13:         private int tail = 0;
 14:
 15:         #endregion
 16:
 17:         #region Constructors
 18:
 19:         /// <summary>Private constructor.</summary>
 20:         /// <exception cref="System.NotSupportedException"></exception>
 21:         private clsCircularQueue()
 22:         {
 23:             throw new NotSupportedException("Use constructor with parameter that pass the capacity.");
 24:         }
 25:
 26:         /// <summary>Default constructor.</summary>
 27:         /// <exception cref="System.ArgumentOutOfRangeException"></exception>
 28:         /// <param name="capacity">The number of elements that the new list can initially store.</param>
 29:         public clsCircularQueue(int capacity)
 30:         {
 31:             if (capacity < 0)
 32:                 throw new ArgumentOutOfRangeException("capacity", "Cannot create an array with a negative size.");
 33:
 34:             this.items = new int?[capacity];
 35:         }
 36:
 37:         #endregion
 38:
 39:         /// <summary>Gets a 32-bit integer that represents 
 40:         /// the total number of elements in all the dimensions of the System.Array.</summary>
 41:         public int Length { get { return this.items.Length; } }
 42:
 43:         /// <summary>Adds the integer value to the tail of Circular Queue.</summary>
 44:         /// <exception cref="System.IndexOutOfRangeException"></exception>
 45:         /// <param name="value"></param>
 46:         public void Enqueue(int value)
 47:         {
 48:             if (this.Length == 0)
 49:                 throw new IndexOutOfRangeException();
 50:
 51:             if (this.tail >= this.items.Length)
 52:                 this.tail = 0;
 53:            
 54:             this.items[this.tail] = value;
 55:             this.tail++;
 56:         }
 57:
 58:         /// <summary>Returns the integer value at the head positon of Circular Queue.</summary>
 59:         /// <exception cref="System.MissingMemberException"></exception>
 60:         /// <returns></returns>
 61:         public int Dequeue()
 62:         {
 63:             int? result = this.items[this.head];
 64:
 65:             if (!result.HasValue)
 66:                 throw new MissingMemberException();
 67:
 68:             this.head++;
 69:             if (this.head >= this.Length)
 70:                 this.head = 0;
 71:
 72:             return result.Value;
 73:         }
 74:
 75:         #region IEnumerable<Int32> Members
 76:
 77:         /// <summary>Returns an enumerator that iterates through the Circular Queue.</summary>
 78:         /// <returns></returns>
 79:         public IEnumerator<Int32> GetEnumerator()
 80:         {
 81:             return new clsEnumeratorCircularQueue(this);
 82:         }
 83:
 84:         #endregion
 85:
 86:         #region IEnumerable Members
 87:
 88:         /// <summary>
 89:         /// 
 90:         /// </summary>
 91:         /// <returns></returns>
 92:         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 93:         {
 94:             throw new Exception("The method or operation is not implemented.");
 95:         }
 96:
 97:         #endregion
 98:
 99:         /// <summary>Supports a circular iteration over a collection.</summary>
100:         public sealed class clsEnumeratorCircularQueue : IEnumerator<Int32>
101:         {
102:             #region Declarations
103:
104:             private readonly clsCircularQueue circularQueue = null;
105:             private int? current = null;
106:             private int counter = 0;
107:
108:             #endregion
109:
110:             #region Constructors
111:
112:             /// <summary>Private constructor.</summary>
113:             /// <exception cref="System.NotSupportedException"></exception>
114:             private clsEnumeratorCircularQueue()
115:             {
116:                 throw new NotSupportedException("Use constructor with parameter that pass the capacity.");
117:             }
118:
119:             /// <summary>Default constructor.</summary>
120:             /// <exception cref="System.ArgumentNullException"></exception>
121:             /// <param name="circularQueue"></param>
122:             public clsEnumeratorCircularQueue(clsCircularQueue circularQueue)
123:             {
124:                 if (circularQueue == null)
125:                     throw new ArgumentNullException("circularQueue");
126:
127:                 this.circularQueue = circularQueue;
128:                 this.current = this.circularQueue.items[this.circularQueue.head];
129:             }
130:
131:             #endregion
132:
133:             #region IEnumerator<Int32> Members
134:
135:             /// <summary>Gets the element in the collection at 
136:             /// the current position of the enumerator.</summary>
137:             /// <exception cref="System.MissingMemberException"></exception>
138:             public int Current
139:             {
140:                 get
141:                 {
142:                     if (!this.current.HasValue)
143:                     {
144:                         this.counter = 0;
145:                         throw new MissingMemberException();
146:                     }
147:
148:                     return this.current.Value;
149:                 }
150:             }
151:
152:             #endregion
153:
154:             #region IDisposable Members
155:
156:             /// <summary>Performs application-defined tasks associated with 
157:             /// freeing, releasing, or resetting unmanaged resources.</summary>
158:             public void Dispose()
159:             { }
160:
161:             #endregion
162:
163:             #region IEnumerator Members
164:
165:             /// <summary>
166:             /// 
167:             /// </summary>
168:             object System.Collections.IEnumerator.Current
169:             {
170:                 get { throw new Exception("The method or operation is not implemented."); }
171:             }
172:
173:             /// <summary>Advances the enumerator to the next element of the collection.</summary>
174:             /// <returns></returns>
175:             public bool MoveNext()
176:             {
177:                 try
178:                 {
179:                     this.counter++;
180:                     if (counter > this.circularQueue.Length)
181:                     {
182:                         this.current = null;
183:                         this.counter = 0;
184:                         return false;
185:                     }
186:
187:                     this.current = this.circularQueue.Dequeue();
188:                     return true;
189:                 }
190:                 catch
191:                 {
192:                     return false;
193:                 }
194:             }
195:
196:             /// <summary>Sets the enumerator to its initial position, 
197:             /// which is before the first element in the collection.</summary>
198:             public void Reset()
199:             {
200:                 this.circularQueue.head = 0;
201:                 this.circularQueue.tail = 0;
202:             }
203:
204:             #endregion
205:         }
206:        
207:     }
208: }
209: 

References:
http://en.wikipedia.org/wiki/Circular_buffer
http://www.itl.nist.gov/div897/sqg/dads/