<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>Chris Taylor</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/default.aspx</link><description>A .NET WebLog</description><dc:language>en-ZA</dc:language><generator>CommunityServer 1.0 (Build: 1.0.1.50214)</generator><item><title>Building Web Applications using a Windows Forms paradigm</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2007/07/07/259366.aspx</link><pubDate>Sat, 07 Jul 2007 19:43:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:259366</guid><dc:creator>taylorza</dc:creator><slash:comments>0</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/259366.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=259366</wfw:commentRss><description>I don't typically post on a non-technical topic, but recently I started looking at &lt;a href="http://www.visualwebgui.com/"&gt;Visual WebGUI&lt;/a&gt; and I was really impressed.&lt;br&gt;&lt;br&gt;

In essence it provides a Windows Forms design time interface for building web applications. The user experience and developer experience very very closely matches that of a traditional Windows Forms application, but the content is delivered as web code which seems from the little testing that I have done to at least support IE and FireFox.&lt;br&gt;&lt;br&gt;

The realy cool thing about the framework is that the client updates and events are all driven using ajax to lighten the network traffic. Once the server side code has executed only the required updates are transmitted back to the client and automagically integrated into the client presentation without any assitance or know how from the developer.&lt;br&gt;&lt;br&gt;

The framework seems to have a very friendly licencing model, essentially free unless you want guaranteed support (that's my simplified interpretation). The controls are all open-sourced so you can develop new controls and extend and modify the appeance and functionality of the intial control library.&lt;br&gt;&lt;br&gt;

I would do the framework an injustice to try an explain all the things it achieves, so I would strongly recommend you take a look at it. I would be very interested to hear your opinions on it. &lt;br&gt;&lt;br&gt;

Now I wonder if Microsoft would pick-up on something like this.&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=259366" width="1" height="1"&gt;</description></item><item><title>Using XBOX 360 Controller on Windows 2003</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2006/12/31/182433.aspx</link><pubDate>Sun, 31 Dec 2006 16:23:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:182433</guid><dc:creator>taylorza</dc:creator><slash:comments>0</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/182433.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=182433</wfw:commentRss><description>&lt;p&gt;&lt;b&gt;Updated to include step by step instructions&lt;/b&gt;&lt;/p&gt;
Now that the Game Studio Express has released I intend to be playing around quite a bit with it. Since I am not a game programmer and the software I typically work on is server based my preferred development platform is Windows 2003, this poses a problem because the XBOX 360 Controller for windows driver refuses to install if the target platform is not Windows XP with the following message.
&lt;br&gt;&lt;br&gt;
&lt;img src="/WebLog/photos/chris.taylor/images/185185/original.aspx"&gt;
&lt;br&gt;&lt;br&gt;
However, I found that all the required driver files have been extracted at this point and can be installed on Windows 2003 and so far (touch wood) with no ill-effect. 
&lt;br&gt;&lt;br&gt;
The driver files are extracted to a folder on the root of the drive you ran the installation from, the folder has a randomly generated name which looks something like the following
&lt;br&gt;&lt;br&gt;
D:\f37260c804717b0288154486
&lt;br&gt;&lt;br&gt;
The associated driver files can be located at 
&lt;br&gt;&lt;br&gt;
D:\f37260c804717b0288154486\xbox360\setup\files.
&lt;br&gt;&lt;br&gt;
Now all you need to do is plug in the XBOX 360 Controller into a free USB port and point the hardware installation wizard to the folder that contains the driver files. Once the installation is complete you should be able to use the controller on Windows 2003.
&lt;p&gt;&lt;b&gt;Example Installation&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;
1. Run &lt;a href="http://download.microsoft.com/download/0/b/7/0b71e9ca-0706-4d39-8221-0c9cb04e43a1/Xbox360_32Eng.exe"&gt;Xbox360_32Eng.exe&lt;/a&gt; installation program. If you get the Open File - Security Warning dialog, click the Run button. Take note which drive you run the installation on.
&lt;/p&gt;
&lt;p&gt;
2. On Windows 2003 you will be prompted with the message 'Setup cannot install this software because your computer does not meet the system requirements. To install this software, you must have a computer running Microsoft Windows XP SP1 or greater.'. &lt;br&gt;&lt;B&gt;DO NOT DISMISS/CLOSE THIS MESSAGE BOX&lt;/B&gt;&lt;/p&gt;
&lt;p&gt;
3. While the message box is still on the screen, use explorer to browser the root directory of the drive you ran the installation on. In my case it was the 'D' drive. On the root of this drive you will find a folder with a randomly generated name, in my case the folder is named 'f7267e57e15594f2f5a62054b463fb0e', note your folder name will differ.
&lt;/p&gt;
&lt;p&gt;
4. Once you have noted the folder name, you can proceed to plug in the XBOX 360 controller. Windows should detect the device and prompt you with the 'Found New Hardware Wizard'.
&lt;/p&gt;
&lt;p&gt;
5. Select the 'Install from a list or specific location(Advanced)' radio button and click 'Next'.
&lt;/p&gt;
&lt;p&gt;
6. Ensure that the 'Searc for the best driver in these locations.' radio button is selected and check the 'Include this location in the search' checkbox, nothing else needs to be checked.
&lt;/p&gt;
&lt;p&gt;
7. Enter the path to the driver, this path is relative to the temporary folder created by the installation program, in my case the path is.
D:\f7267e57e15594f2f5a62054b463fb0e\xbox360\setup\files. Click 'Next'. Windows should proceed to install the driver, do not be concerned, this can take a few moments.
&lt;/p&gt;
&lt;p&gt;
8. Now all that is left to be done is to click on 'Finish' and you can use your controller for your XNA development. Oh and before I forget, you can dismiss that message box now :) You know the one that did not want you to install the driver on Windows 2003.
&lt;/p&gt;
Enjoy!!&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=182433" width="1" height="1"&gt;</description></item><item><title>My foray into XNA : Basic Pixel Perfect Collision Detection</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2006/09/30/148798.aspx</link><pubDate>Sat, 30 Sep 2006 09:55:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:148798</guid><dc:creator>taylorza</dc:creator><slash:comments>0</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/148798.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=148798</wfw:commentRss><description>&lt;P&gt;So I decided to give XNA a quick spin. Now I am not a game developer, not even an aspiring game developer, but I generally take anything new for a drive just to familiarize myself with it. &lt;/P&gt;
&lt;P&gt;I was pleasantly surprised, in about 10 minutes I have objects flying around the screen in all directions and that was written entirely from scratch using only the initially wizard. The 2D stuff is really easy to get started with, I have not ventured into the 3D side yet.&lt;/P&gt;
&lt;P&gt;Anyway, once I had all these object flying around I decided to do some basic collision detection to test when they collide. I wanted to go further than just bounding box checks; I wanted pixel perfect collision detection, the kind that reports collisions on when the pixels of one image collide with the pixels of another. &lt;/P&gt;
&lt;P&gt;To optimize the collision detection the detection is performed in two stages.&lt;/P&gt;
&lt;P&gt;1. Check for potential collisions using the bounding box&lt;/P&gt;
&lt;P&gt;2. If test 1 revealed a potential collision perform a pixel level comparison of the overlapping areas of the two textures.&lt;/P&gt;
&lt;P&gt;Before I get started I first want to define some simplified terminology that I will be using.&lt;/P&gt;&lt;B&gt;
&lt;P&gt;Texture&lt;/B&gt; – Represents the graphical information required to render a Sprite.&lt;/P&gt;&lt;B&gt;
&lt;P&gt;Texture&lt;/B&gt; &lt;B&gt;co-ordinate space&lt;/B&gt; – Rectangle defining the size of the texture. A texture that is 32x32 will have texture co-ordinate space (0, 0, 32, 32).&lt;/P&gt;&lt;B&gt;
&lt;P&gt;Sprite&lt;/B&gt; – A graphical object painted with a texture at a location defined by the sprite co-ordinates.&lt;/P&gt;&lt;B&gt;
&lt;P&gt;Sprite co-ordinates&lt;/B&gt; – Rectangle representing the space occupied by the sprite in the game.&lt;/P&gt;&lt;FONT&gt;
&lt;P&gt;Note that the routines defined here are very simplistic and do not cater to scaling and rotation transformations of the sprite. Therefore the width and height of the sprite must correspond to the width and height of the texture painted on the sprite! &lt;/P&gt;&lt;B&gt;
&lt;P&gt;See the notes below for more information.&lt;/P&gt;&lt;/B&gt;&lt;/FONT&gt;
&lt;P&gt;Now back to the tests, test 1 is to check for the potential collision by checking for a overlap between the is easily accomplished using the following function which compares to rectangles and returns true if they overlap.&lt;/P&gt;&lt;FONT face="Courier New"&gt;
&lt;P&gt;public&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;bool&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Intersect(&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; rectangle1, &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; rectangle2)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;if&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; (((rectangle1.X &amp;lt; (rectangle2.X + rectangle2.Width)) &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;&amp;amp; (rectangle2.X &amp;lt; (rectangle1.X + rectangle1.Width))) &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;&amp;amp; (rectangle1.Y &amp;lt; (rectangle2.Y + rectangle2.Height)))&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;return&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; (rectangle2.Y &amp;lt; (rectangle1.Y + rectangle1.Height));&lt;BR&gt;&amp;nbsp; }&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;return&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;false&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;;&lt;BR&gt;}&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;To determine if two sprites have overlapped the &lt;B&gt;Intersect&lt;/B&gt; function is called with sprite co-ordinates of the two sprites you are interested in. If this function returns true we need to move on to Test 2.&lt;/P&gt;
&lt;P&gt;Test 2 requires that we determine if any of the corresponding pixels in the overlap between the two sprites are visible i.e. not transparent. If both sprites contain corresponding visible pixels at the same location this represents a collision, if on the other hand none of the visible pixels overlap with visible pixels of the other texture then there is no collision. The following images depict the two situations graphically.&lt;/P&gt;
&lt;TABLE&gt;

&lt;TR&gt;
&lt;TD&gt;&lt;IMG src="/WebLog/photos/chris.taylor/images/148795/original.aspx"&gt;&lt;/TD&gt;
&lt;TD&gt;&lt;IMG src="/WebLog/photos/chris.taylor/images/148796/original.aspx"&gt;&lt;/TD&gt;&lt;/TR&gt;

&lt;/TABLE&gt;; 
&lt;P&gt;The first image shows two sprites which there bounding rectangles overlapping however none of the visible pixels overlap and therefore this is not a true collision. The second image shows that there is an overlap of visible pixels between the two sprites in the intersection and therefore represents a collision.&lt;/P&gt;
&lt;P&gt;To do the pixel level tests we first need to determine the intersection rectangle, the rectangle highlighted in black in the above images. The following function can be used to achieve this.&lt;/P&gt;&lt;FONT face="Courier New"&gt;
&lt;P&gt;public&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Intersection(&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; rectangle1, &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; rectangle2)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; x1 = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Math&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;.Max(rectangle1.Left, rectangle2.Left);&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; y1 = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Math&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;.Max(rectangle1.Top, rectangle2.Top);&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; x2 = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Math&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;.Min(rectangle1.Right, rectangle2.Right);&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; y2 = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Math&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;.Min(rectangle1.Bottom, rectangle2.Bottom);&lt;BR&gt;&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;if&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; ((x2 &amp;gt;= x1) &amp;amp;&amp;amp; (y2 &amp;gt;= y1))&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;return&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;(x1, y1, x2 - x1, y2 - y1);&lt;BR&gt;&amp;nbsp; }&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;return&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;.Empty;&lt;BR&gt;}&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;The two rectangles passed to the function will be the sprite rectangles; the result is a new rectangle which represents the intersection of the two rectangles.&lt;/P&gt;
&lt;P&gt;Armed with the intersection rectangle we need to convert the co-ordinate of the intersection rectangle to the texture co-ordinates of the two textures. In other words, we need to determine the rectangles within the textures that overlapped. Viewing each texture individually this would look like the following.&lt;/P&gt;&lt;IMG src="/WebLog/photos/chris.taylor/images/148797/original.aspx"&gt; 
&lt;P&gt;The following function will translate the co-ordinates one rectangle to the co-ordinate space of the reference rectangle.&lt;/P&gt;&lt;FONT face="Courier New"&gt;
&lt;P&gt;public&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Normalize(&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; reference, &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; rectangle)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;return&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;(&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rectangle.X - reference.X,&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rectangle.Y - reference.Y,&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rectangle.Width,&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; rectangle.Height);&lt;BR&gt;}&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;The function is called passing the sprite co-ordinate rectangle as the reference and the intersection rectangle as the second rectangle argument. Call this function once for each of the sprites, resulting in two rectangles, representing the rectangle within each of the textures that we are interested in. &lt;/P&gt;
&lt;P&gt;Now we need to extract the pixel data for the two rectangles and compare them pixel for pixel. The following function will perform this task in a reasonably efficient manner, but my focus was readability.&lt;/P&gt;&lt;FONT face="Courier New"&gt;
&lt;P&gt;public&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;bool&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; TestCollision(&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Texture2D&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; texture1, &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; texture1Intersection,&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Texture2D&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; texture2, &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;Rectangle&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; texture2Intersection)&lt;BR&gt;{&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT&gt;&amp;nbsp; &lt;/FONT&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; pixelCount = texture1Intersection.Width * texture1Intersection.Height;&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;uint&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;[] texture1Pixels = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;uint&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;[pixelCount];&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;uint&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;[] texture2Pixels = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;uint&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;[pixelCount];&lt;BR&gt;&lt;BR&gt;&amp;nbsp; texture1.GetData(0, texture1Intersection, texture1Pixels, 0, pixelCount);&lt;BR&gt;&amp;nbsp; texture2.GetData(0, texture2Intersection, texture2Pixels, 0, pixelCount);&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;for&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; (&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; i = 0; i &amp;lt; pixelCount; ++i)&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;if&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; (((texture1Pixels[i] &amp;amp; 0xff000000) &amp;gt; 0)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;&amp;amp; ((texture2Pixels[i] &amp;amp; 0xff000000) &amp;gt; 0))&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;return&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;true&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp; }&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;return&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;false&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;;&lt;BR&gt;}&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;The key to performance in this function is to extract only the pixel data we are interested in, because the area of intersection within both texture is the same size we can extract the data and run through it one element at a time. Each element index represents the corresponding pixel data in each of the textures. In the loop we mask out the Alpha channel bits by performing a bitwise and, if the alpha channel of both pixels (texture1 and texture2) is not 0 we have a visible pixel overlap which is considered a collision and we can break out of the loop.&lt;/P&gt;
&lt;P&gt;Some notes:&lt;/P&gt;
&lt;P&gt;1. As has already been mentioned the above does not address scaled or rotated sprites. &lt;/P&gt;
&lt;P&gt;2. Additional performance can be achieved by compiling with the unsafe switch and using pointer arithmetic to iterate the pixel data. From my tests I get roughly 4 frames per second extra when using pointers, which is with 100 sprites bouncing around.&lt;/P&gt;
&lt;P&gt;3. If the original textures are not powers of 2 in size XNA automatically scales the textures, this is for performance reasons. However the problem with that for this version of the algorithm is that the scaling introduces noise (anti-aliasing) into the image which improves the visual quality of the scaled image. While the artifacts from this process are not individually visible to the eye the algorithm does detect them as visible pixels and potentially results in a false positive, at least as far as the eye can see.&lt;/P&gt;&lt;FONT&gt;&lt;/FONT&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=148798" width="1" height="1"&gt;</description></item><item><title>Basic Application Partitioning</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2006/07/21/142387.aspx</link><pubDate>Fri, 21 Jul 2006 18:06:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:142387</guid><dc:creator>taylorza</dc:creator><slash:comments>1</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/142387.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=142387</wfw:commentRss><description>&lt;P&gt;I often need to present a more practical view of the classical application partitioning &lt;BR&gt;Presentation Layer -&amp;gt; Business Logic Layer -&amp;gt; Data Access Layer&lt;BR&gt;I believe the diagram below depicts a clear if simplified representation of the interaction between layers.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="/WebLog/photos/chris.taylor/images/142467/original.aspx"&gt;&lt;/P&gt;
&lt;P&gt;The key tenant is that there is no SQL or ADO.NET data access code beyond the Data Access Layer (DAL). The DAL provides all data services to the higher level layers. In the cases where the where the business logic is hosted within the physical database the DAL provides a pass-through interface to expose the business logic to the business layer.&lt;/P&gt;
&lt;P&gt;With in the database a similar logical partitioning is maintained restricting the DAL from directly accessing the underlying tables. Within the database it self this is relaxed and both the database access code and business code have direct access to the tables.&lt;/P&gt;
&lt;P&gt;The two bars cutting across the layers Business Entities and Data Entities represent the data container classes that transport data and user service request arguments between the layers. &lt;/P&gt;
&lt;P&gt;While the data entities represent basic entities within the system, the business entities are represent more complex aggregates of the data entities required to complete an autonomous business function. For example to save a customers application for an account could be broken down into the following. For simplicity this explanation forgoes the actual business logic involved in processing an application and focuses on storing the application.&lt;/P&gt;
&lt;P&gt;The class definitions are simplified pseudo-code of entity classes &lt;/P&gt;&lt;B&gt;
&lt;P&gt;Data Entities&lt;/P&gt;
&lt;P&gt;&lt;/B&gt;&lt;FONT face="Courier New"&gt;class ApplicationEntity&lt;BR&gt;class CustomerEntity&lt;BR&gt;class AccountEntity&lt;/FONT&gt;&lt;/P&gt;&lt;B&gt;
&lt;P&gt;Business Entity&lt;/P&gt;
&lt;P&gt;&lt;/B&gt;&lt;FONT face="Courier New"&gt;class AccountApplicationEntity&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; ApplicationEntity ApplicationDetails;&lt;BR&gt;&amp;nbsp; CustomerEntity CustomerDetails;&lt;BR&gt;&amp;nbsp; AccountEntity AccountDetails;&lt;BR&gt;}&lt;/FONT&gt;&lt;/P&gt;&lt;B&gt;
&lt;P&gt;Data Service Interface&lt;/P&gt;
&lt;P&gt;&lt;/B&gt;&lt;FONT face="Courier New"&gt;SaveApplication(ApplicationEntity entity); // Saves the data in the Applications table&lt;BR&gt;SaveCustomer (CustomerEntity entity); // Saves the data in the Customers table&lt;BR&gt;SaveAccount(AccountEntity entity); // Saves the data in the Accounts table&lt;/FONT&gt;&lt;/P&gt;&lt;B&gt;
&lt;P&gt;Business Service Interface&lt;/P&gt;&lt;/B&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;SaveAccountApplication(AccountApplicationEntity entity)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; DataService ds = new DataServiceInterface();&lt;BR&gt;&amp;nbsp; ds.SaveApplication(entity.ApplicationDetails);&lt;BR&gt;&amp;nbsp; ds.SaveCustomer(entity.CustomerDetails);&lt;BR&gt;&amp;nbsp; ds.SaveAccount(entity.AccountDetails);&lt;BR&gt;}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;You can assume that the data access is in a transaction.&lt;/P&gt;
&lt;P&gt;From the consumer’s point of view creating an application is a matter of instantiating an instance of the AccountApplicationEntity and populating the ApplicationDetails, CustomerDetails and the AccountDetails, then invoking the business service for further processing.&lt;/P&gt;&lt;FONT&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/FONT&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=142387" width="1" height="1"&gt;</description></item><item><title>Debugging WindowsIdentity and IsInRole 2.0</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2006/06/29/141324.aspx</link><pubDate>Thu, 29 Jun 2006 18:12:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:141324</guid><dc:creator>taylorza</dc:creator><slash:comments>2</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/141324.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=141324</wfw:commentRss><description>&lt;P&gt;&lt;FONT&gt;Update: Accidentally posted an older version of the function, this is a slightly cleaner version.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;In a previous &lt;a href="http://dotnetjunkies.com/WebLog/chris.taylor/archive/2004/02/15/7268.aspx"&gt;post&lt;/A&gt;&amp;nbsp;I showed how to get get a list of groups from a WindowsIdentity, this method used all kinds of under handed trick to get to the information and as warned the solution no longer works for the new version of the framework. Here is an updated version which takes advantage of officially supported techniques available in the 2.0 Framework, given a WindowsIdentity it returns a list of role names from the WindowsIdentity.&lt;/P&gt;&lt;FONT&gt;&lt;FONT&gt;
&lt;P&gt;public&lt;/FONT&gt;&lt;FONT&gt; &lt;/FONT&gt;&lt;FONT&gt;string&lt;/FONT&gt;&lt;FONT&gt;[] GetWindowsIdentityRoles(&lt;/FONT&gt;&lt;FONT&gt;WindowsIdentity&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT&gt; identity)&lt;BR&gt;&lt;/FONT&gt;{&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT&gt;if&lt;/FONT&gt;&lt;FONT&gt; (identity == &lt;/FONT&gt;&lt;FONT&gt;null&lt;/FONT&gt;&lt;FONT&gt;) &lt;/FONT&gt;&lt;FONT&gt;throw&lt;/FONT&gt;&lt;FONT&gt; &lt;/FONT&gt;&lt;FONT&gt;new&lt;/FONT&gt;&lt;FONT&gt; &lt;/FONT&gt;&lt;FONT&gt;ArgumentNullException&lt;/FONT&gt;&lt;FONT&gt;(&lt;/FONT&gt;&lt;FONT&gt;"identity"&lt;/FONT&gt;&lt;FONT&gt;);&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT&gt;&amp;nbsp; IdentityReferenceCollection&lt;/FONT&gt;&lt;FONT&gt; groups = &lt;FONT&gt;identity.Groups.Translate(&lt;/FONT&gt;&lt;FONT&gt;typeof&lt;/FONT&gt;&lt;FONT&gt;(&lt;/FONT&gt;&lt;FONT&gt;NTAccount&lt;/FONT&gt;&lt;FONT&gt;));&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT&gt;string&lt;/FONT&gt;&lt;FONT&gt;[] roles = &lt;/FONT&gt;&lt;FONT&gt;new&lt;/FONT&gt;&lt;FONT&gt; &lt;/FONT&gt;&lt;FONT&gt;string&lt;/FONT&gt;&lt;FONT&gt;[groups.Count]; &lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT&gt;for&lt;/FONT&gt;&lt;FONT&gt; (&lt;/FONT&gt;&lt;FONT&gt;int&lt;/FONT&gt;&lt;FONT&gt; i = 0; i &amp;lt; groups.Count; ++i)&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; roles[i] = groups[i].Value; &lt;BR&gt;&amp;nbsp; }&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &lt;FONT&gt;return&lt;/FONT&gt;&lt;FONT&gt; roles;&lt;BR&gt;}&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;
&lt;P&gt;This function comes in handy when you are not certain of the string representation of a role you need to pass to the IsInRole function.&lt;FONT&gt;&lt;/P&gt;&lt;/FONT&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=141324" width="1" height="1"&gt;</description></item><item><title>Gradient Shades</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2006/06/25/141152.aspx</link><pubDate>Sun, 25 Jun 2006 17:53:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:141152</guid><dc:creator>taylorza</dc:creator><slash:comments>0</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/141152.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=141152</wfw:commentRss><description>&lt;P&gt;Recently I was googling and came a routine to calculate the transision from one color to another color in 255 discreet steps.&lt;BR&gt;Looking at the code it did not quite look like it was going to achieve what I understood goal of the routine to be. So I quickly&lt;BR&gt;put some code together and then found that there was no way to post a comment on the blog. The original post can be found&lt;BR&gt;here &lt;A href="http://dalldorf.us/home/?m=200603"&gt;http://dalldorf.us/home/?m=200603&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;So rather than waste the code here it is.&lt;/P&gt;&lt;FONT&gt;
&lt;P&gt;public&lt;/FONT&gt;&lt;FONT&gt; &lt;/FONT&gt;&lt;FONT&gt;Color&lt;/FONT&gt;&lt;FONT&gt; Gradient(&lt;/FONT&gt;&lt;FONT&gt;Color&lt;/FONT&gt;&lt;FONT&gt; from, &lt;/FONT&gt;&lt;FONT&gt;Color&lt;/FONT&gt;&lt;FONT&gt; to, &lt;/FONT&gt;&lt;FONT&gt;byte&lt;/FONT&gt;&lt;FONT&gt; step)&lt;BR&gt;&lt;/FONT&gt;&lt;FONT&gt;{&lt;BR&gt;&lt;/FONT&gt;&lt;FONT&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;double&lt;/FONT&gt;&lt;FONT&gt; stepFactor = (&lt;/FONT&gt;&lt;FONT&gt;double&lt;/FONT&gt;&lt;FONT&gt;)step / 255;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/FONT&gt;&lt;FONT&gt;return&lt;/FONT&gt;&lt;FONT&gt; &lt;/FONT&gt;&lt;FONT&gt;Color&lt;/FONT&gt;&lt;FONT&gt;.FromArgb(&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;/FONT&gt;&lt;FONT&gt;int&lt;/FONT&gt;&lt;FONT&gt;)(from.R + ((to.R - from.R) * stepFactor)),&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;/FONT&gt;&lt;FONT&gt;int&lt;/FONT&gt;&lt;FONT&gt;)(from.G + ((to.G - from.G) * stepFactor)),&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(&lt;/FONT&gt;&lt;FONT&gt;int&lt;/FONT&gt;&lt;FONT&gt;)(from.B + ((to.B - from.B) * stepFactor)));&lt;BR&gt;}&lt;/FONT&gt;&lt;FONT&gt;&lt;/P&gt;&lt;/FONT&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=141152" width="1" height="1"&gt;</description></item><item><title>Thread Local Storage the .NET way</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2005/08/18/132026.aspx</link><pubDate>Thu, 18 Aug 2005 20:58:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:132026</guid><dc:creator>taylorza</dc:creator><slash:comments>7</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/132026.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=132026</wfw:commentRss><description>&lt;P&gt;Those of you familiar with the intricacies of Win32 multi-threaded programming will be intimately familiar with thread local storage (TLS). And you might have wondered how .NET exposes this often useful construct.&lt;/P&gt;
&lt;P&gt;Before we begin I would like to offer a brief description of what TLS is, for those of you that might not have had the fortune of learning Win32 multi-threaded programming. TLS is a means of storing data on a per-thread basis. Any methods accessing the data will access the data associated with the thread in which the method is running. And this is achieved without the method having any special knowledge of the thread in which it is executing. &lt;/P&gt;
&lt;P&gt;Now the real question, how can we achieve the same results in the .NET managed environment? Well depending on your situation there are a number of options available. I will describe only&amp;nbsp;3 options here. First I will describe the more commonly known CallContext and then what in my experience is less widely known Thread Static variables and finally there is the thread&amp;nbsp;data slots.&lt;/P&gt;&lt;B&gt;
&lt;P&gt;The CallContext&lt;BR&gt;&lt;/B&gt;Most of you are probably familiar with the CallContext class and how it relates to .NET remoting. A CallContext is used to flow data across multiple calls with out having to pass the data along as some part of the argument list of each called method. And if the stored data implements the ILogicalThreadAffinative interface the data will flow across machine boundaries.&lt;/P&gt;
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;A call context is maintained by the .NET framework for each logical thread of execution. By storing data in the call context you are essentially storing data specific to the thread currently executing the method, and any method called in that thread of execution has access to the threads call context. As a simple example, if you wanted to maintain a count of the number of times a function was called per thread you could do some thing like the following.&lt;BR&gt;&lt;FONT face="Courier New"&gt;&lt;BR&gt;class&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Class1&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;void&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Main(&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;string&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;[] args)&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Threading.Thread t1 = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; System.Threading.Thread(&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; System.Threading.ThreadStart(ThreadFunction));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Threading.Thread t2 = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; System.Threading.Thread(&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; System.Threading.ThreadStart(ThreadFunction));&lt;BR&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;// Start the threads&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;t1.Start();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; t2.Start();&lt;BR&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;// Wait for the threads to complete&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.Join();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; t2.Join();&lt;BR&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Console.Read();&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;} &lt;BR&gt;&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;private&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;const&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;string&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; PerThreadCallCounterKey = "CallCounter";&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;private&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Random Rnd = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Random();&lt;BR&gt;&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT&gt;private&lt;/FONT&gt; &lt;FONT&gt;static&lt;/FONT&gt; &lt;FONT&gt;void&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; ThreadFunction()&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;{&lt;BR&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Initialize the per-thread CallCounter&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Runtime.Remoting.Messaging.CallContext.SetData(PerThreadCallCounterKey, 0);&lt;BR&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Pick a randon number of times to call the target method&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; iterations = Rnd.Next(255); &lt;BR&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Call the method iteration number of times&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; for&lt;/FONT&gt; (&lt;FONT&gt;int&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; i = 0; i &amp;lt; iterations; ++i)&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DoTheWork();&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DisplayCounterValue();&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;}&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;private&lt;/FONT&gt; &lt;FONT&gt;static&lt;/FONT&gt; &lt;FONT&gt;void&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; DoTheWork()&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; {&lt;BR&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; &amp;nbsp; // Get the current threads CallCounter from the CallContext&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&lt;/FONT&gt; counter = (&lt;FONT&gt;int&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;)System.Runtime.Remoting.Messaging.CallContext.GetData(PerThreadCallCounterKey);&lt;BR&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Increment the counter and store the new value in the CallContext&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Notice the pre-increment (++counter).&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Runtime.Remoting.Messaging.CallContext.SetData(PerThreadCallCounterKey, ++counter);&lt;BR&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //OK, so we updated the counter now do the real work of this method&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //...&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; }&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;private&lt;/FONT&gt; &lt;FONT&gt;static&lt;/FONT&gt; &lt;FONT&gt;void&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; DisplayCounterValue()&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; {&lt;BR&gt;&lt;/FONT&gt;&lt;FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Get the current threads CallCounter from the CallContext&lt;BR&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&lt;/FONT&gt; counter = (&lt;FONT&gt;int&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;)System.Runtime.Remoting.Messaging.CallContext.GetData(PerThreadCallCounterKey);&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Console.WriteLine("The method was called {0} times from this thread", counter);&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; }&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;Notice how neither the DoTheWork or DisplayCounterValue methods have any knowledge of the calling thread. They are only aware of context information which is thread specific.&lt;/P&gt;&lt;B&gt;
&lt;P&gt;Thread Static Members&lt;/P&gt;&lt;/B&gt;
&lt;P&gt;As an alternative to using the CallContext class, .NET provides support for what I call Thread Static Members. Thread static members for the most part act like normal static members except that they have per-thread storage rather than per-AppDomain. Declaring a thread static member is very similar to declaring a normal static member, except that the ThreadStaticAttribute attribute is applied to the declaration as follows.&lt;BR&gt;&lt;FONT face="Courier New"&gt;&lt;BR&gt;[ThreadStatic]&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;private&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; PerThreadCallCounter;&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;From this point on any thing that is done to the PerThreadCallCounter member affects the instance of this member associate with the current thread of execution. The following achieves the same results as the earlier example, only this time rather than using the CallContext class a thread static member is used.&lt;/P&gt;&lt;FONT face="Courier New"&gt;
&lt;P&gt;class&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Class1&lt;BR&gt;{&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;void&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Main(&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;string&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;[] args)&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Threading.Thread t1 = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; System.Threading.Thread(&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; System.Threading.ThreadStart(ThreadFunction));&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Threading.Thread t2 = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; System.Threading.Thread(&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; System.Threading.ThreadStart(ThreadFunction));&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Start the threads&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.Start();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; t2.Start();&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Wait for the threads to complete&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.Join();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; t2.Join();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Console.Read();&lt;BR&gt;&amp;nbsp; } &lt;/P&gt;
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp; [ThreadStatic]&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; private&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; PerThreadCallCounter;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; &lt;BR&gt;&amp;nbsp; private&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Random Rnd = &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; Random();&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;BR&gt;&amp;nbsp; private&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;void&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; ThreadFunction()&lt;BR&gt;&amp;nbsp; { &lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Pick a randon number of times to call the target method &lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; iterations = Rnd.Next(255);&amp;nbsp;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Call the method iteration number of times&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; for&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; (&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; i = 0; i &amp;lt; iterations; ++i)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DoTheWork();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DisplayCounterValue();&lt;BR&gt;&amp;nbsp; }&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; private&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;void&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; DoTheWork()&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Increment the static member associated with the current thread&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PerThreadCallCounter++;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //OK, so we updated the counter now do the real work of this method&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //...&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; }&lt;/P&gt;
&lt;P&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp; private&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;void&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; DisplayCounterValue()&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Display the value of the thread static member to the user&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Console.WriteLine("The method was called {0} times from this thread", PerThreadCallCounter);&lt;BR&gt;&amp;nbsp;&amp;nbsp;}&lt;BR&gt;}&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;As you can see and I am sure you will agree this is a much simpler solution. However a word of caution, with this example the naming of the instance becomes critical, sooner or later you will need to come back to a piece of code and if you loose site of the fact that a particular static member is bound to a thread rather than the entire AppDomain you could be in for some interesting debugging in the early hours of the morning.&lt;/P&gt;
&lt;P&gt;While thread static members appear to work just like there normal static member counter parts, there are a few caveats. Firstly you might be tempted to initialize your thread static members in a static constructor. Unfortunately the results might surprise you. At first inspection it might appear that your initialization was ignored, however this is not the case, it is just that the static constructor is not run per-thread, but per-AppDomain. That is it is only executed once which means that the Thread Static member is only initialized for one thread, the and the rest are left untouched. And no, you can not apply the ThreadStaticAttribute to the constructor, this attribute can only be applied to member variables (fields).&lt;/P&gt;
&lt;P&gt;Of course as apposed to the CallContext solution the ThreadStatic member is limited to the AppDomain boundary, which for most requirements is sufficient.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Thread Data Slots&lt;BR&gt;&lt;/STRONG&gt;Thread data slots are allocated once for every thread by using the Thread.AllocateDataSlot(). Once a data slot has been allocated the slot can be used from any thread to access the data stored in the data slot for the executing thread. This most closely matches the workings of the TLS APIs in the Win32 api. Rather than bore you with another rendition of the demonstration application I will leave it to the reader to investigate this solution which is adequately documented in the MSDN documentation.&lt;/P&gt;&lt;B&gt;
&lt;P&gt;Conclusion&lt;BR&gt;&lt;/B&gt;This brief overview of TLS the .NET way probably leaves more questions than what have been answered. One thing I would like to mention is why in the world you would require TLS in an Object Oriented environment like .NET.&lt;/P&gt;
&lt;P&gt;In my case I often end up writing code to analyze/profile existing code or applications which I either do not want to tamper with or do not have access to tamper with. Using these techniques I have been able to attach tracking information to ASP.NET requests without impacting existing code. Prepare information in one place and access that information from a event handler that was not designed to carry this kind of ad-hoc tracking information etc.&lt;BR&gt;&lt;BR&gt;While this is rife for abuse this does have its merits when used wisely.&lt;/P&gt;&lt;B&gt;
&lt;P&gt;See Also:&lt;BR&gt;&lt;/B&gt;Along similar lines as the ThreadStaticAttribute, you might also want to take a look at the ContextStaticAttribute. Especially for those of you interested in the potential of AOP offered by context bound objects.&lt;/P&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=132026" width="1" height="1"&gt;</description></item><item><title>Writing a Windows Service using ATL 7.1</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2005/05/03/72925.aspx</link><pubDate>Wed, 04 May 2005 03:36:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:72925</guid><dc:creator>taylorza</dc:creator><slash:comments>1</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/72925.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=72925</wfw:commentRss><description>&lt;P&gt;After more than 12 years of C++ Windows development I have written my share of Windows Services. These days however I spend most my income generating time in the throws of .NET development and consulting. But every now and then I get to do some really cool task using C++, mostly real-time hardware interfaces.&lt;/P&gt;
&lt;P&gt;Recently I had to develop a Windows Service and since the service is interacting with a hardware device I thought I would go with C++ rather than .NET. This would be the first non .NET Windows service I have developed in about 3 years. Of course I would have to dig out all my backups to find my tried and tested service framework. Then I thought since the service was really nothing special let me use the ATL framework to develop the service. Now ATL is a technology that I really had a handle on, but that was in the days of version 3 so what did version 7 have to offer?&lt;/P&gt;
&lt;P&gt;I fired up Visual Studio and selected the Visual C++ ATL Project. From the ensuing wizard I selected the Application Settings tab and checked the Attributed checkbox and the Service (EXE) radio button. In the generated code I quickly came across the class that implemented the heart of the service, this is essentially a template class that derives from a specialization of &lt;B&gt;CAtlServiceModuleT&amp;lt;&amp;gt;&lt;/B&gt;. &lt;/P&gt;
&lt;P&gt;Looking through the code for the template class (this is in the file atlbase.h) I decided that the Run method was a good place to put the code that started the service, so to test my theory I made a call the the LogEvent utility function to log an event when the Run method was invoked. I compiled the service and registered it by executing the service with the &lt;B&gt;/service&lt;/B&gt; command line argument. When I started the service the service started and immediately stop, some thing was up so I stepped through the code and found that the call to &lt;B&gt;PreMessageLoop&lt;/B&gt; was not returning S_OK, in short the function was trying to register COM object, but I had not written and COM objects and nor was I intending to. Fortunately I noticed that Microsoft had place the code in a define &lt;B&gt;_ATL_NO_COM_SUPPORT &lt;/B&gt;so I defined this in the &lt;B&gt;stdafx.h &lt;/B&gt;file, recompiled and started the service, this time my services started and stayed that way. I could even stop my service without any problem. To indicated to the Service Control Manager (SCM) that the service can handle notifications other than &lt;B&gt;Start/Stop&lt;/B&gt; I modified the &lt;B&gt;m_status.dwControlsAccepted&lt;/B&gt; flag to include &lt;B&gt;SERVICE_ACCEPT_PAUSE_CONTINUE &lt;/B&gt;and overrode the &lt;B&gt;OnPause &lt;/B&gt;and &lt;B&gt;OnContinue &lt;/B&gt;functions.&lt;/P&gt;
&lt;P&gt;As it turns out building a standard Windows Service using ATL is very simple. In summary here are the steps I followed:&lt;/P&gt;
&lt;DIR&gt;
&lt;DIR&gt;
&lt;P&gt;1) Add the following code to the &lt;B&gt;stdafx.h&lt;/B&gt;, before any of the #include statements&lt;BR&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;#define&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; _ATL_NO_COM_SUPPORT&lt;BR&gt;&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;2) Add a constructor to the Module class, this class will be called CxxxModule where xxx is the name used when creating the project. In the constructor modify the m_status.dwControlsAccepted as required, here is an example that enables support for Pause and Continue.&lt;BR&gt;&lt;FONT face="Courier New" size=2&gt;m_status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;&lt;BR&gt;&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;3) Override the Run method to start you service activities, ultimately this is called whenever the service is started. It is important that the base class implementation is called so that the message loop is started. A simple implementation would look like this.&lt;BR&gt;&lt;/P&gt;&lt;/DIR&gt;&lt;/DIR&gt;&lt;FONT face="Courier New" size=2&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HRESULT Run(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;int&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; nShowCmd = SW_HIDE)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#008000 size=2&gt;// Start worker thread here &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;return&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;__super&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;::Run(nShowCmd);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/FONT&gt;&lt;/P&gt;
&lt;DIR&gt;
&lt;DIR&gt;
&lt;P&gt;4) Override OnStop to stop the service. Here you should stop your thread from running and call the base class OnStop which takes care of terminating the message loop and notifying the SCM that the service has stopped. If the process of stopping the service thread is time consuming then you need to take further steps to manage this, if there is enough interest I will cover this in a later post.&lt;BR&gt;&lt;/P&gt;&lt;/DIR&gt;&lt;/DIR&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; void&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; OnStop()&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" color=#008000 size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Stop worker-thread&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; __super&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;::OnStop();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/P&gt;&lt;/FONT&gt;
&lt;DIR&gt;
&lt;DIR&gt;
&lt;P&gt;5) Override whatever other handlers you have choosen to support eg. OnPause and OnContinue. In these overrides you should call the SetServiceStatus to update the SCM on the status of the service. For example a simple implementation of OnPause and OnContinue would be something like the following.&lt;BR&gt;&lt;/P&gt;&lt;/DIR&gt;&lt;/DIR&gt;&lt;FONT face="Courier New" size=2&gt;
&lt;P&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; void&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; OnPause()&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; SetServiceStatus(SERVICE_PAUSE_PENDING);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New"&gt;&lt;FONT size=2&gt;&lt;FONT color=#008000&gt;// Pause worker-thread activity&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &lt;/FONT&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;SetServiceStatus(SERVICE_PAUSED);&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New" color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; void&lt;/FONT&gt;&lt;FONT face="Courier New"&gt; OnContinue()&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;SetServiceStatus(SERVICE_CONTINUE_PENDING);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#008000 size=2&gt;// Resume worker-thread activity&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;SetServiceStatus(SERVICE_RUNNING);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;}&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;This is not intended as an introduction to implementing windows services, just a documentation of the steps required to implement a standard Windows service using ATL 7.1. I have not even mentioned message string resources etc. I only hope this is useful enough to get some started without having to dig around like I did.&lt;/P&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=72925" width="1" height="1"&gt;</description></item><item><title>Propagating Distributed Transactions – A .NET Remoting Solution </title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2005/02/15/54504.aspx</link><pubDate>Wed, 16 Feb 2005 04:26:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:54504</guid><dc:creator>taylorza</dc:creator><slash:comments>0</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/54504.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=54504</wfw:commentRss><description>&lt;P&gt;I have published an early draft of an article that I am working on at &lt;A href="http://dotnetjunkies.com/WebLog/chris.taylor/articles/54503.aspx"&gt;http://dotnetjunkies.com/WebLog/chris.taylor/articles/54503.aspx&lt;/A&gt;. The article describes a simple strategy to flowing distributed transactions across remoting boundaries. It also includes information on enabling TIP (Transaction Internet Protocol) on Windows 2003 server as well as Windows XP SP2. The formating still needs work, especially the code.&lt;/P&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=54504" width="1" height="1"&gt;</description></item><item><title>Distributed Transactions without COM+ Components</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2005/02/12/53985.aspx</link><pubDate>Sat, 12 Feb 2005 20:40:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:53985</guid><dc:creator>taylorza</dc:creator><slash:comments>0</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/53985.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=53985</wfw:commentRss><description>&lt;P&gt;&lt;FONT color=#ff0000&gt;Update: I have posted a draft of an article that takes this topic further and addresses flowing distributed transactions. See the post at &lt;A href="http://dotnetjunkies.com/WebLog/chris.taylor/articles/54503.aspx"&gt;http://dotnetjunkies.com/WebLog/chris.taylor/articles/54503.aspx&lt;/A&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;Not being in a position to wait for all the wonderful support for transactions in .NET Framework 2.0 namespace System.Transactions I have had to make use of existing services to enlist in Distributed transactions and further more to propagate those transactions across process and machine boundaries. &lt;/P&gt;
&lt;P&gt;Initial solutions required the development of numerous COM+ components. Being a C++ developer with extensive COM experience this did not bother me but fortunately with COM+ 1.5&amp;#8217;s Services Without Components I no longer needed jump these hoops to satisfy the transactional requirements of the software. Even better is .NET Framework 1.1 support for these services in the System.EnterpriseServices namespace. Through the next few posts I will be passing on some of the techniques I have used to propagate distributed transaction across WebServices and .NET Remoting. To kick start here is a watered down version of the TransactionContext class.&lt;BR&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;/P&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   1:  &lt;/SPAN&gt;  &lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   2:  &lt;/SPAN&gt;  &lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System.EnterpriseServices;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   3:  &lt;/SPAN&gt;  &lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System.Runtime.InteropServices;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   4:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   5:  &lt;/SPAN&gt;  &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;class&lt;/SPAN&gt; TransactionContext : IDisposable&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   6:  &lt;/SPAN&gt;  {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   7:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;private&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;bool&lt;/SPAN&gt; _consistent = &lt;SPAN class=kwrd&gt;false&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   8:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   9:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; TransactionContext() : &lt;SPAN class=kwrd&gt;this&lt;/SPAN&gt;(TransactionOption.Required)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  10:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  11:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  12:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  13:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; TransactionContext(TransactionOption option)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  14:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  15:  &lt;/SPAN&gt;      ServiceConfig config = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; ServiceConfig();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  16:  &lt;/SPAN&gt;      config.Transaction = option;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  17:  &lt;/SPAN&gt;      config.TrackingEnabled = &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  18:  &lt;/SPAN&gt;      ServiceDomain.Enter(config);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  19:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  20:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  21:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Complete()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  22:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  23:  &lt;/SPAN&gt;      _consistent = &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  24:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  25:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  26:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Dispose()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  27:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  28:  &lt;/SPAN&gt;      &lt;SPAN class=kwrd&gt;try&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  29:  &lt;/SPAN&gt;      {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  30:  &lt;/SPAN&gt;        GC.SuppressFinalize(&lt;SPAN class=kwrd&gt;this&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  31:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (_consistent)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  32:  &lt;/SPAN&gt;          ContextUtil.SetComplete();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  33:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;else&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  34:  &lt;/SPAN&gt;          ContextUtil.SetAbort();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  35:  &lt;/SPAN&gt;      }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  36:  &lt;/SPAN&gt;      &lt;SPAN class=kwrd&gt;finally&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  37:  &lt;/SPAN&gt;      {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  38:  &lt;/SPAN&gt;        ServiceDomain.Leave();  &lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  39:  &lt;/SPAN&gt;      }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  40:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  41:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  42:  &lt;/SPAN&gt;    ~TransactionContext()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  43:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  44:  &lt;/SPAN&gt;      System.Diagnostics.Debug.Fail(&lt;SPAN class=str&gt;"TransactionContext must de explicitly Disposed"&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  45:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  46:  &lt;/SPAN&gt;  }&lt;/PRE&gt;&lt;PRE&gt;&amp;nbsp;&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;To use this class is relatively simple, all you do is create an instance of the TransactionContext and if everything completes without causing a problem you put the context into a consistent state by calling Complete() when the Dispose method is called the vote for the success of the transaction is cast based on the consistency flag, you must ensure that Dispose is called as soon as your work is complete. The following example demonstrates the use of the TransactionContext class. &lt;/P&gt;
&lt;P&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;/P&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   1:  &lt;/SPAN&gt;      SqlConnection oCon;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   2:  &lt;/SPAN&gt;      SqlCommand oCmd;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   3:  &lt;/SPAN&gt;      &lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; (TransactionContext tx = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; TransactionContext())&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   4:  &lt;/SPAN&gt;      {  &lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   5:  &lt;/SPAN&gt;        oCon = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; SqlConnection(&lt;SPAN class=str&gt;"Your Connection String here"&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   6:  &lt;/SPAN&gt;        oCmd = oCon.CreateCommand();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   7:  &lt;/SPAN&gt;        oCmd.CommandText = &lt;SPAN class=str&gt;"insert into ..."&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   8:  &lt;/SPAN&gt;        &lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   9:  &lt;/SPAN&gt;        oCon.Open(); &lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  10:  &lt;/SPAN&gt;        oCmd.ExecuteNonQuery();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  11:  &lt;/SPAN&gt;        oCon.Close();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  12:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  13:  &lt;/SPAN&gt;        tx.Complete();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  14:  &lt;/SPAN&gt;      }&lt;/PRE&gt;&lt;PRE&gt;&amp;nbsp;&lt;/PRE&gt;&lt;/DIV&gt;In a future post I will address some of the more na&amp;#239;ve solutions to propagate these transactions across process and machine boundaries.&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=53985" width="1" height="1"&gt;</description></item><item><title>A simple LineNumberReader</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2005/02/06/51790.aspx</link><pubDate>Sun, 06 Feb 2005 16:46:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:51790</guid><dc:creator>taylorza</dc:creator><slash:comments>1</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/51790.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=51790</wfw:commentRss><description>&lt;P&gt;For a small personal project I have been developing a parser, as part of the parser I developed the LineNumberReader modeled after the Java(TM) class of the same name. Since the class has been useful, I thought I would share it. For the purposes of the post I have removed comments and a number of the constructors.&lt;/P&gt;
&lt;P&gt;One of the nice features is the ability to set a marker in the stream using the Mark() method and returning to that point in the stream with the Reset() method. Enjoy!&lt;/P&gt;
&lt;p&gt;
&lt;div class="csharpcode"&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;class&lt;/span&gt; LineNumberReader : StreamReader&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;    {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;      &lt;span class="kwrd"&gt;public&lt;/span&gt; LineNumberReader(Stream stream) : &lt;span class="kwrd"&gt;base&lt;/span&gt;(stream){}&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;      &lt;span class="kwrd"&gt;public&lt;/span&gt; LineNumberReader(&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;        Stream stream, &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;        System.Text.Encoding encoding) : &lt;span class="kwrd"&gt;base&lt;/span&gt;(stream, encoding){}&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;      &lt;span class="rem"&gt;// More constructors matching those from StreamReader ...&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;      &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; LineNumber&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;      {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;        get { &lt;span class="kwrd"&gt;return&lt;/span&gt; _lineNumber; }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;        set &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;        { &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;          &lt;span class="kwrd"&gt;if&lt;/span&gt; (value &amp;lt; 0 ) &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ArgumentOutOfRangeException( &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;            &lt;span class="str"&gt;"LineNumber"&lt;/span&gt;, &lt;span class="str"&gt;"Must be greater or equal to 0."&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;          _lineNumber = value; &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;      }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;      &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Mark()&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;      {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (!BaseStream.CanSeek) &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotSupportedException(&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;          &lt;span class="str"&gt;"Mark is not supported, underlying stream is not seekable."&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;        _mark = &lt;span class="kwrd"&gt;new&lt;/span&gt; MarkData(_lineNumber, BaseStream.Position);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;        _markset = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;      }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;      &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Reset()&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;      {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (!_markset) &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; IOException(&lt;span class="str"&gt;"Reader not marked"&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;        _markset = &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;        DiscardBufferedData();&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;        BaseStream.Seek(_mark.Position, SeekOrigin.Begin);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;        _lineNumber = _mark.LineNumber;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;      }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;      &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; Read()&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;      {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt; retval = &lt;span class="kwrd"&gt;base&lt;/span&gt;.Read();  &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (retval == (&lt;span class="kwrd"&gt;int&lt;/span&gt;)&lt;span class="str"&gt;'\r'&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;        {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;          &lt;span class="kwrd"&gt;if&lt;/span&gt; (Peek() == (&lt;span class="kwrd"&gt;int&lt;/span&gt;)&lt;span class="str"&gt;'\n'&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  44:  &lt;/span&gt;          {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  45:  &lt;/span&gt;            &lt;span class="kwrd"&gt;base&lt;/span&gt;.Read();    &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  46:  &lt;/span&gt;          }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;          retval = (&lt;span class="kwrd"&gt;int&lt;/span&gt;)&lt;span class="str"&gt;'\n'&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (retval == (&lt;span class="kwrd"&gt;int&lt;/span&gt;)&lt;span class="str"&gt;'\n'&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;          _lineNumber++;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; retval;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt;      }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt;    &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;      &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; Read(&lt;span class="kwrd"&gt;char&lt;/span&gt;[] buffer, &lt;span class="kwrd"&gt;int&lt;/span&gt; index, &lt;span class="kwrd"&gt;int&lt;/span&gt; count)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  57:  &lt;/span&gt;      {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  58:  &lt;/span&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt; bytesRead = &lt;span class="kwrd"&gt;base&lt;/span&gt;.Read(buffer, index, count);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  59:  &lt;/span&gt;        _lineNumber += CountLinesInBuffer(buffer, index, bytesRead);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  60:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; bytesRead;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  61:  &lt;/span&gt;      }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  62:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  63:  &lt;/span&gt;      &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; ReadLine()&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  64:  &lt;/span&gt;      {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  65:  &lt;/span&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; retval = &lt;span class="kwrd"&gt;base&lt;/span&gt;.ReadLine(); &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  66:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (retval != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  67:  &lt;/span&gt;          _lineNumber++;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  68:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; retval; &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  69:  &lt;/span&gt;      }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  70:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  71:  &lt;/span&gt;      &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; CountLinesInBuffer(&lt;span class="kwrd"&gt;char&lt;/span&gt;[] buffer, &lt;span class="kwrd"&gt;int&lt;/span&gt; index, &lt;span class="kwrd"&gt;int&lt;/span&gt; count)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  72:  &lt;/span&gt;      {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  73:  &lt;/span&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt; lineCount = 0;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  74:  &lt;/span&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt; i = index;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  75:  &lt;/span&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt; lastIndex = index + count;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  76:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  77:  &lt;/span&gt;        &lt;span class="kwrd"&gt;do&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  78:  &lt;/span&gt;        {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  79:  &lt;/span&gt;          &lt;span class="kwrd"&gt;char&lt;/span&gt; ch = buffer[i];&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  80:  &lt;/span&gt;          &lt;span class="kwrd"&gt;if&lt;/span&gt; (ch == &lt;span class="str"&gt;'\r'&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  81:  &lt;/span&gt;          {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  82:  &lt;/span&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; ((i + 1 &amp;lt; lastIndex) &amp;amp;&amp;amp; (buffer[i + 1] == &lt;span class="str"&gt;'\n'&lt;/span&gt;))&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  83:  &lt;/span&gt;              i++;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  84:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  85:  &lt;/span&gt;            ch = &lt;span class="str"&gt;'\n'&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  86:  &lt;/span&gt;          }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  87:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  88:  &lt;/span&gt;          &lt;span class="kwrd"&gt;if&lt;/span&gt; (ch == &lt;span class="str"&gt;'\n'&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  89:  &lt;/span&gt;            lineCount++;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  90:  &lt;/span&gt;        } &lt;span class="kwrd"&gt;while&lt;/span&gt; (++i &amp;lt; lastIndex);&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  91:  &lt;/span&gt;        &lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  92:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; lineCount;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  93:  &lt;/span&gt;      }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  94:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  95:  &lt;/span&gt;      &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;struct&lt;/span&gt; MarkData&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  96:  &lt;/span&gt;      {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  97:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; MarkData(&lt;span class="kwrd"&gt;int&lt;/span&gt; lineNumber, &lt;span class="kwrd"&gt;long&lt;/span&gt; position)&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  98:  &lt;/span&gt;        {&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt;  99:  &lt;/span&gt;          LineNumber = lineNumber;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 100:  &lt;/span&gt;          Position = position;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 101:  &lt;/span&gt;        }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 102:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; LineNumber;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 103:  &lt;/span&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;long&lt;/span&gt; Position;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 104:  &lt;/span&gt;      }&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 105:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 106:  &lt;/span&gt;      &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; _lineNumber = 0;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 107:  &lt;/span&gt;      &lt;span class="kwrd"&gt;private&lt;/span&gt; MarkData _mark;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 108:  &lt;/span&gt;      &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; _markset = &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;&lt;span class="lnum"&gt; 109:  &lt;/span&gt;    }&lt;/pre&gt;
&lt;/div&gt;&lt;/p&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=51790" width="1" height="1"&gt;</description></item><item><title>Nostalgia</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2005/01/18/45766.aspx</link><pubDate>Wed, 19 Jan 2005 01:42:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:45766</guid><dc:creator>taylorza</dc:creator><slash:comments>0</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/45766.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=45766</wfw:commentRss><description>&lt;P class=MsoNormal style="MARGIN: 0cm 0cm 0pt"&gt;Sitting this evening talking to my wife, I realized that she had never seen any of the Microsoft Operating Systems prior to Windows 95. This got me all nostalgic and I am in the process of installing a virtual machine with DOS 6.22 and Window for Workgroups 3.11. I might even have an old Visual Studio 1.5 lying around some where. Ah the good old days&amp;#8230;&amp;#8230;&lt;/P&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=45766" width="1" height="1"&gt;</description></item><item><title>Oracle Team Development – Locking Oracle Objects</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2004/11/22/33103.aspx</link><pubDate>Tue, 23 Nov 2004 00:17:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:33103</guid><dc:creator>taylorza</dc:creator><slash:comments>4</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/33103.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=33103</wfw:commentRss><description>&lt;P&gt;Current source control systems such as Visual Source Safe, CVS, and Subversion is that they all rely on having a working copy of the source code under development on the developers local machine. Unfortunately this does not map quite so easily to the database environment. This is particularly problematic when multiple developers are working on database objects on the same server.&lt;/P&gt;&lt;B&gt;
&lt;P&gt;The Problem&lt;/P&gt;&lt;/B&gt;
&lt;P&gt;The in our environment we are using Oracle as the backend database. One of the problems the Oracle developers are facing is when more than one of them is working on the same Package at the same time. Most tools allow you to extract the source for the Package edit the package and then compile it. It is at the compile stage that the problem occurs. If two developers edit the same package at the same time, the one that compiles last will overwrite the changes made by the other developer. Obviously there is another option, each developer could have his/her own database instance to work against and then use version control software to maintain the sources, but for this team this is how they work and since I have already thrown a spoke in the wheels by introducing Subversion I did not want to push things. &lt;/P&gt;&lt;B&gt;
&lt;P&gt;Finding a solution&lt;/P&gt;&lt;/B&gt;
&lt;P&gt;With my focus on Microsoft Technologies, I am not particularly knowledgeable about Oracle, but I decided that I would take up the challenge to find a solution to this problem and learn a thing or two about Oracle along the way. Obviously the problem with piecing together a solution for a technology that you are not particularly knowledgeable about is that you run the risk of re-inventing the wheel. But assuming that the database developers are familiar with there tools there did not seem to be a satisfactory solution to the problem. &lt;/P&gt;
&lt;P&gt;On of the few things I do know about Oracle is that they allow you to create triggers on the schema. These triggers are fired when changes are made to the existing schema. With this I decided I would attempt to piece together an object locking scheme where the developer could lock a database object before working on it and then once he has compiled and tested the object he can unlock it. To compile an object the object must be locked by the developer attempting the compile, if not a message should be displayed indicating that the object is either not locked or is locked by another developer. The solution should also be independent of the tools that are being used by the developer.&lt;/P&gt;&lt;B&gt;
&lt;P&gt;The solution&lt;/P&gt;&lt;/B&gt;
&lt;P&gt;As it turns out the solution to this problem was rather easily implemented even with my limited knowledge of Oracle PL/SQL. I created a table that maintains a list of locked objects.&lt;/P&gt;&lt;B&gt;&lt;FONT face="Courier New" size=1&gt;
&lt;P&gt;create&lt;/B&gt; &lt;B&gt;table&lt;/B&gt; DBSCC_LOCKS&lt;BR&gt;(&lt;BR&gt;&amp;nbsp; OBJTYPE &lt;B&gt;varchar2&lt;/B&gt;(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;19&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;) &lt;B&gt;not&lt;/B&gt; &lt;B&gt;null&lt;/B&gt;,&lt;BR&gt;&amp;nbsp; OBJNAME &lt;B&gt;varchar2&lt;/B&gt;(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;30&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;) &lt;B&gt;not&lt;/B&gt; &lt;B&gt;null&lt;/B&gt;,&lt;BR&gt;&amp;nbsp; LOCKEDBY &lt;B&gt;varchar2&lt;/B&gt;(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;64&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;) &lt;B&gt;not&lt;/B&gt; &lt;B&gt;null&lt;/B&gt;,&lt;BR&gt;&lt;B&gt;&amp;nbsp; constraint&lt;/B&gt; PK_DBSCC_LOCKS &lt;B&gt;primary&lt;/B&gt; &lt;B&gt;key&lt;/B&gt; (OBJTYPE, OBJNAME)&lt;BR&gt;)&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;The table has the object type for instance PROCEDURE or PACKAGE etc. and the name of the object and who has the object locked. For my purposed I decided to use the machine name the complete solution actually uses the OS user for this purpose.&lt;/P&gt;
&lt;P&gt;The next step was to provide a trigger that would check against the DBSCC_LOCKS table before allowing a object creation to proceed.&lt;/P&gt;&lt;B&gt;&lt;FONT face="Courier New" size=1&gt;
&lt;P&gt;create&lt;/B&gt; &lt;B&gt;or&lt;/B&gt; &lt;B&gt;replace&lt;/B&gt; &lt;B&gt;trigger&lt;/B&gt; TR_DBSCC_CHANGE&lt;BR&gt;&lt;B&gt;before&lt;/B&gt; &lt;B&gt;create&lt;/B&gt; &lt;B&gt;on&lt;/B&gt; &lt;B&gt;schema&lt;/B&gt;&lt;BR&gt;&lt;B&gt;declare&lt;/B&gt;&lt;BR&gt;&amp;nbsp; vMachine v$session.MACHINE%&lt;B&gt;TYPE&lt;/B&gt;;&lt;BR&gt;&amp;nbsp; vLockedBy DBSCC_LOCKS.LOCKEDBY%&lt;B&gt;TYPE&lt;/B&gt;;&lt;BR&gt;&amp;nbsp; vObjExists &lt;B&gt;integer&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;begin&lt;/B&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- Only locking PROCEDURES, PACKAGE and PACKAGE BODY&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp; if&lt;/B&gt; ora_dict_obj_type &lt;B&gt;in&lt;/B&gt; (&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PROCEDURE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'FUNCTION'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE BODY'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;) &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- Get the machine name that is executing the current session&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/B&gt; s.machine &lt;B&gt;into&lt;/B&gt; vMachine &lt;B&gt;from&lt;/B&gt; v$session s&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; s.audsid = userenv(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'sessionid'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;);&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- Check if the object already exists &lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/B&gt; &lt;B&gt;count&lt;/B&gt;(*) &lt;B&gt;into&lt;/B&gt; vObjExists &lt;B&gt;from&lt;/B&gt; ALL_OBJECTS o&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; o.object_type = ora_dict_obj_type&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/B&gt; o.object_name = ora_dict_obj_name;&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- If the object does not exist then we allow the&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if&lt;/B&gt; vObjExists = &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;0&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; &lt;B&gt;then&lt;/B&gt; &lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;;&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- Query the DBSCC_LOCKS table to see if and who&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- has the object locked&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; begin&lt;/B&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/B&gt; l.LOCKEDBY &lt;B&gt;into&lt;/B&gt; vLockedBy &lt;B&gt;from&lt;/B&gt; DBSCC_LOCKS l&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; l.OBJTYPE = ora_dict_obj_type&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/B&gt; l.OBJNAME = ora_dict_obj_name;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; exception&lt;/B&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; when&lt;/B&gt; NO_DATA_FOUND &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; vLockedBy := &lt;B&gt;null&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/B&gt;;&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- Test the lock state of the object&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if&lt;/B&gt; vLockedBy &lt;B&gt;is&lt;/B&gt; &lt;B&gt;null&lt;/B&gt; &lt;B&gt;then&lt;/B&gt; &lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;-- Not locked&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; raise_application_error(-&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;20001&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ora_dict_obj_type || &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'.'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; || ora_dict_obj_name || &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;' is not locked, please use DBSCC_LOCK before updating'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;); &lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; elsif&lt;/B&gt; vLockedBy &amp;lt;&amp;gt; vMachine &lt;B&gt;then&lt;/B&gt; &lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;-- Locked from another workstation&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; raise_application_error(-&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;20001&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;,&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ora_dict_obj_type || &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'.'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; || ora_dict_obj_name || &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;' cannot be created/updated because it is currently locked by '&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; || vLockedBy); &lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- If we get here then the workstation owning the session&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- has the lock and the creation can proceed.&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;end&lt;/B&gt;;&lt;BR&gt;&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;Now this is where my lack of PL/SQL experience really shows itself, I feel that there are a number of things that can be improved but as it stands it works. &lt;/P&gt;
&lt;P&gt;The trigger checks if the object being created is one of the types that we are expecting to be locked before creation. Then the machine name is extracted from the current session (the OS user could be used instead). The trigger checks that the object is an existing object; if it is further tests are done to check that the machine creating the object has a lock on the existing object. In the case that the object is locked by the machine issuing the create or this is the first time the object is being created the trigger allows the create to proceed. Otherwise an exception is raised indicating either that the object must be locked or that it is already locked from elsewhere. Now all that is left is to enable the developer to easily lock and unlock database objects.&lt;/P&gt;
&lt;P&gt;The following procedure can be called by the developer to lock the intended database object.&lt;/P&gt;&lt;B&gt;&lt;FONT face="Courier New" size=1&gt;
&lt;P&gt;create&lt;/B&gt; &lt;B&gt;or&lt;/B&gt; &lt;B&gt;replace&lt;/B&gt; &lt;B&gt;procedure&lt;/B&gt; DBSCC_LOCK(&lt;BR&gt;&amp;nbsp; p_objtype &lt;B&gt;in&lt;/B&gt; DBSCC_LOCKS.OBJTYPE%&lt;B&gt;TYPE&lt;/B&gt;, &lt;BR&gt;&amp;nbsp; p_objname &lt;B&gt;in&lt;/B&gt; DBSCC_LOCKS.OBJNAME%&lt;B&gt;TYPE&lt;/B&gt;) &lt;BR&gt;&lt;B&gt;is&lt;/B&gt;&lt;BR&gt;&amp;nbsp; vLocks &lt;B&gt;integer&lt;/B&gt;;&lt;BR&gt;&amp;nbsp; vCanLock &lt;B&gt;integer&lt;/B&gt;;&lt;BR&gt;&amp;nbsp; vLockedBy DBSCC_LOCKS.LOCKEDBY%&lt;B&gt;TYPE&lt;/B&gt;;&lt;BR&gt;&amp;nbsp; vMachine v$session.MACHINE%&lt;B&gt;TYPE&lt;/B&gt;;&lt;BR&gt;&amp;nbsp; vObjType DBSCC_LOCKS.OBJTYPE%&lt;B&gt;TYPE&lt;/B&gt;;&lt;BR&gt;&amp;nbsp; vObjName DBSCC_LOCKS.OBJNAME%&lt;B&gt;TYPE&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;begin&lt;/B&gt;&lt;BR&gt;&amp;nbsp; vObjType := upper(&lt;B&gt;trim&lt;/B&gt;(p_objtype));&lt;BR&gt;&amp;nbsp; vObjName := upper(&lt;B&gt;trim&lt;/B&gt;(p_objname));&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- Validate object type&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp; if&lt;/B&gt; &lt;B&gt;not&lt;/B&gt; vObjType &lt;B&gt;in&lt;/B&gt; (&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PROCEDURE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'FUNCTION'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;) &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; raise_application_error(-&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;20001&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'Only objects of type PROCEDURE, FUNCTION and PACKAGE can be locked'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;);&lt;BR&gt;&lt;B&gt;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;;&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- Validate object that the object exists&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&lt;/B&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;B&gt;select&lt;/B&gt; &lt;B&gt;count&lt;/B&gt;(*) &lt;B&gt;into&lt;/B&gt; vCanLock&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/B&gt; ALL_OBJECTS o &lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; o.object_type = vObjType&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/B&gt; o.object_name = vObjName;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;B&gt;&lt;BR&gt;&amp;nbsp; if&lt;/B&gt; vCanLock = &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;0&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; raise_application_error(-&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;20001&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, vObjType || &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'.'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; || vObjName || &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;' does not exist'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;);&lt;BR&gt;&lt;B&gt;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;;&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- Assume the object is already locked&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&amp;nbsp; vLocks := &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;1&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;;&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- Query the locks table to determine if and who&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- has the object locked.&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp; begin&lt;/B&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/B&gt; scc.LOCKEDBY &lt;B&gt;into&lt;/B&gt; vlockedBy&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/B&gt; DBSCC_LOCKS scc&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; scc.OBJTYPE = vObjType&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/B&gt; scc.OBJNAME = vObjName;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; exception&lt;/B&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- NO_DATA_FOUND implies that the object is not locked&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; when&lt;/B&gt; NO_DATA_FOUND &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; vLocks := &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;0&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;end&lt;/B&gt;;&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- If the object is locked inform the caller that the object is&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- already locked &lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp; if&lt;/B&gt; (vLocks &amp;gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;0&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;) &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; raise_application_error(-&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;20001&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, vObjType || &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'.'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; || vObjName || &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;' is locked by '&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; || vLockedBy);&lt;BR&gt;&lt;B&gt;&amp;nbsp; else&lt;/B&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- Get the name of the workstation requesting the lock&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/B&gt; s.MACHINE &lt;B&gt;into&lt;/B&gt; vMachine &lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/B&gt; v$session s&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; audsid = userenv(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'sessionid'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;);&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- Special case PACKAGE objects so that both the &lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; -- PACKAGE and PACKAGE BODY are locked&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if&lt;/B&gt; vObjType = &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/B&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;B&gt;insert&lt;/B&gt; &lt;B&gt;into&lt;/B&gt; DBSCC_LOCKS&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (OBJTYPE, OBJNAME, LOCKEDBY)&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; values&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, vObjName, vMachine);&lt;BR&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; insert&lt;/B&gt; &lt;B&gt;into&lt;/B&gt; DBSCC_LOCKS &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (OBJTYPE, OBJNAME, LOCKEDBY)&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; values&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE BODY'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, vObjName, vMachine);&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;/B&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; insert&lt;/B&gt; &lt;B&gt;into&lt;/B&gt; DBSCC_LOCKS&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (OBJTYPE, OBJNAME, LOCKEDBY)&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; values&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (vObjType, vObjName, vMachine);&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;; &lt;BR&gt;&lt;B&gt;&amp;nbsp; &amp;nbsp; commit&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;end&lt;/B&gt; DBSCC_LOCK;&lt;BR&gt;&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;The procedure mostly just checks that the object is not already locked and if not makes an entry into the DBSCC_LOCKS table. One special case is for packages, if a package is locked an entry is made for both the package and the package body. The reciprocal of this procedure is the DBSCC_UNLOCK, this procedure is used to unlock a database object.&lt;/P&gt;&lt;B&gt;&lt;FONT face="Courier New" size=1&gt;
&lt;P&gt;create&lt;/B&gt; &lt;B&gt;or&lt;/B&gt; &lt;B&gt;replace&lt;/B&gt; &lt;B&gt;procedure&lt;/B&gt; DBSCC_UNLOCK(&lt;BR&gt;&amp;nbsp; p_objtype &lt;B&gt;in&lt;/B&gt; DBSCC_LOCKS.OBJTYPE%&lt;B&gt;TYPE&lt;/B&gt;,&lt;BR&gt;&amp;nbsp; p_objname &lt;B&gt;in&lt;/B&gt; DBSCC_LOCKS.OBJNAME%&lt;B&gt;TYPE&lt;/B&gt;,&lt;BR&gt;&amp;nbsp; p_force &lt;B&gt;in&lt;/B&gt; &lt;B&gt;integer&lt;/B&gt; := &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;0&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;) &lt;BR&gt;&lt;B&gt;is&lt;/B&gt;&lt;BR&gt;&amp;nbsp; vMachine v$session.MACHINE%&lt;B&gt;TYPE&lt;/B&gt;; &lt;BR&gt;&amp;nbsp; vObjType DBSCC_LOCKS.OBJTYPE%&lt;B&gt;TYPE&lt;/B&gt;;&lt;BR&gt;&amp;nbsp; vObjName DBSCC_LOCKS.OBJNAME%&lt;B&gt;TYPE&lt;/B&gt;;&lt;BR&gt;&amp;nbsp; vLocks &lt;B&gt;integer&lt;/B&gt;;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;B&gt;begin&lt;/B&gt;&lt;BR&gt;&amp;nbsp; vObjType := upper(&lt;B&gt;trim&lt;/B&gt;(p_objtype));&lt;BR&gt;&amp;nbsp; vObjName := upper(&lt;B&gt;trim&lt;/B&gt;(p_objname));&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- Validate object type&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp; if&lt;/B&gt; &lt;B&gt;not&lt;/B&gt; vObjType &lt;B&gt;in&lt;/B&gt; (&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PROCEDURE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'FUNCTION'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;) &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; raise_application_error(-&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;20001&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'Only objects of type PROCEDURE, FUNCTION and PACKAGE are supported'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;);&lt;BR&gt;&lt;B&gt;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;;&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- Get the name of the workstation executing the request&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp; select&lt;/B&gt; s.machine &lt;B&gt;into&lt;/B&gt; vMachine &lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/B&gt; v$session s&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; audsid = userenv(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'sessionid'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;);&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- Determine if the object is locked and if it is locked&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- by the caller, if p_force is non-zero then the machine&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- is ignored &lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp; select&lt;/B&gt; &lt;B&gt;count&lt;/B&gt;(*) &lt;B&gt;into&lt;/B&gt; vLocks&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/B&gt; DBSCC_LOCKS l&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; l.OBJTYPE = vObjType&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/B&gt; l.OBJNAME = vObjName&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/B&gt; (l.LOCKEDBY = vMachine &lt;B&gt;or&lt;/B&gt; p_force &amp;lt;&amp;gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;0&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;);&lt;BR&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- If locked then remove the entry from the DBSCC_LOCKS&lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;I&gt;&lt;FONT face="Courier New" color=#ff0000 size=1&gt;&amp;nbsp; -- table. PACKAGE and PACKAGE BODY are special cased &lt;/I&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp; if&lt;/B&gt; vLocks &amp;gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;0&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if&lt;/B&gt; vObjType = &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; &lt;B&gt;then&lt;/B&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; delete&lt;/B&gt; &lt;B&gt;from&lt;/B&gt; DBSCC_LOCKS&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; OBJTYPE &lt;B&gt;in&lt;/B&gt; (&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'PACKAGE BODY'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;)&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/B&gt; OBJNAME = vObjName; &lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;/B&gt;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; delete&lt;/B&gt; &lt;B&gt;from&lt;/B&gt; DBSCC_LOCKS&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/B&gt; OBJTYPE = vObjType&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/B&gt; OBJNAME = vObjName;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; commit&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;&amp;nbsp; else&lt;/B&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; raise_application_error(-&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;20001&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;, &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'You do not have '&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; || vObjType || &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;'.'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt; || vObjName ||&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000f0 size=1&gt;' locked'&lt;/FONT&gt;&lt;FONT face="Courier New" size=1&gt;);&lt;BR&gt;&lt;B&gt;&amp;nbsp; end&lt;/B&gt; &lt;B&gt;if&lt;/B&gt;;&lt;BR&gt;&lt;B&gt;end&lt;/B&gt; DBSCC_UNLOCK;&lt;BR&gt;&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;DBSCC_UNLOCK does pretty much the reverse, it checks that the machine issuing the unlock in fact has the object locked and if so removes the entry from the DBSCC_LOCKS table. Again there is a special case for packages, where unlocking a package removes both the entry for the PACKAGE and PACKAGE BODY. Additionally the DBSCC_UNLOCK procedure includes a force parameter which when set will force an object to be unlocked even if it is not currently locked by the user. &lt;/P&gt;
&lt;P&gt;So a few questions, if I keep saying the OS User can be used rather than the machine name why did I opt to use the machine name. Well, this might just be ignorance, but I recall reading somewhere that OS User might not be reliable from all environments especially when connecting via SQL*NET. Since this solution has been released to the team they have provided some nice enhancements. One of those was integrating this with PL/SQL Developer, the environment predominantly used by the team members, now they can just right click on an object in the object browser and lock/unlock, in the case of the object being locked the context menu even shows who has it locked, they have also changed the procedures to use the OS User rather than the machine name, maybe I should make that change some time soon.&lt;/P&gt;
&lt;P&gt;Hope fully those of you out there that have more Oracle expertise than I do will point out the short comings in my implementation as well as maybe a better or alternative solution to the problem. &lt;/P&gt;&lt;FONT face=Arial size=2&gt;&lt;/FONT&gt;&lt;img src="http://dotnetjunkies.com/WebLog/aggbug.aspx?PostID=33103" width="1" height="1"&gt;</description></item><item><title>Of Delegates and Events</title><link>http://dotnetjunkies.com/WebLog/chris.taylor/archive/2004/11/07/31283.aspx</link><pubDate>Sun, 07 Nov 2004 23:46:00 GMT</pubDate><guid isPermaLink="false">58df7014-fd75-437c-9641-150997716d1c:31283</guid><dc:creator>taylorza</dc:creator><slash:comments>0</slash:comments><comments>http://dotnetjunkies.com/WebLog/chris.taylor/comments/31283.aspx</comments><wfw:commentRss>http://dotnetjunkies.com/WebLog/chris.taylor/commentrss.aspx?PostID=31283</wfw:commentRss><description>&lt;P&gt;I have often read and heard explanations of the differences between a Delegate and Events in the .NET framework; I thought I would try my hand at explaining the &lt;B&gt;relationship&lt;/B&gt; between delegates and events. I will not be addressing all the finer details; I will purposely ignore most of the metadata related to events. I will only describe as much as I require conveying the relationship between delegates and events. Please do not expect this to be an exhaustive review of how to work with delegates and events or for that matter how they work.&lt;/P&gt;
&lt;P&gt;For the purposes of this explanation I will use C# and IL for the examples, but most aspects should be relevant for VB.NET or any other language targeting the .NET Framework that exposes the .NET event mechanism. &lt;/P&gt;
&lt;P&gt;The delegate declaration defines a new data type; instances of this data type can maintain a list of zero or more methods; each method in the list must have a signature that matches the delegate data type declaration. Take the following declaration for example&lt;/P&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;
&lt;P&gt;public&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;delegate&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;void&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; ValueChangedDelegate(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;object&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; sender, EventArgs e);&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;This declares a new delegate type called &lt;B&gt;ValueChangedDelegate&lt;/B&gt;. The type definition also defines the signature of the method that can be added to instances of this type. In this example the method must return void (&lt;strike&gt;function&lt;/strike&gt; Sub in VB.NET) and accept &lt;B&gt;object &lt;/B&gt;and &lt;B&gt;EventArgs&lt;/B&gt; parameters.&lt;/P&gt;&lt;B&gt;&lt;/B&gt;
&lt;P&gt;The next code snippet declares a class called &lt;B&gt;Foo&lt;/B&gt; that exposes a delegate as an instance variable called &lt;B&gt;ValueHasChanged&lt;/B&gt;.&lt;/P&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;
&lt;P&gt;public&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;class&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; Foo&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;public&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; ValueChangedDelegate ValueHasChanged;&lt;/P&gt;
&lt;P&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;&amp;nbsp; public&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;void&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; ChangeTheValue()&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;if&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; (ValueHasChanged != &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;null&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ValueHasChanged(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;null&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;, EventArgs.Empty);&lt;BR&gt;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;The above class pretty much addresses everything we would need to put together an event mechanism. We can add handlers to the delegate instance and at the appropriate time we can invoke the delegate to fire the handlers. In the sample code the event is fired when ever the method &lt;B&gt;ChangeTheValue &lt;/B&gt;is called, of course in a real application it would do more that just invoke the delegate and in we would need to take extra precautions to prevent race conditions. &lt;/P&gt;
&lt;P&gt;The following snippet demonstrates subscribing to our new "event" (note the quotes).&lt;/P&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;
&lt;P&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;void&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; Main() &lt;BR&gt;{&lt;BR&gt;&amp;nbsp; Foo myFoo = &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; Foo();&amp;nbsp;&lt;BR&gt;&amp;nbsp; myFoo.ValueHasChanged = &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;new&amp;nbsp;&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;ValueChangedDelegate(OnValueChanged);&lt;BR&gt;} &lt;/P&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;
&lt;P&gt;static&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;void&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; OnValueChanged(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;object&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; sender, EventArgs e)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; System.Diagnostics.Debug.WriteLine("It fired!!!!");&lt;BR&gt;}&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;Our &lt;B&gt;Foo&lt;/B&gt; class is instantiated and the reference stored in the variable &lt;B&gt;myFoo&lt;/B&gt;, following that we create an instance of our delegate passing a method to the constructor; I will refer to this method as the handler method. The method &lt;B&gt;OnValueChanged&lt;/B&gt;&amp;#8217;s signature matches that required by our delegate. The newly created delegate instance is then assigned to the &lt;B&gt;ValueHasChanged&lt;/B&gt; instance variable of the &lt;B&gt;myFoo&lt;/B&gt; instance. Now when ever the delegate &lt;B&gt;myFoo.ValueHasChanged&lt;/B&gt; is invoked the &lt;B&gt;static&lt;/B&gt; method &lt;B&gt;OnValueChanged&lt;/B&gt; will be called. Note that even though I have used static methods here neither events or delegates are limited to using static methods. At this point potential subscribers are free to add there handler functions to the &lt;B&gt;myFoo.ValueChangedDelegate&lt;/B&gt; and these will be called when ever the delegate is invoked.&lt;/P&gt;
&lt;P&gt;What has been described thus far addresses what we need to create and handle events. And in terms of pure functionality delegates do fit the bill here. The problem with we have so far is with the accessibility of the delegate, as it stands anybody using and instance of the &lt;B&gt;Foo&lt;/B&gt; class can subscribe to the "event", but also just as easily invoke the delegate directly artificially firing the all handlers into action. The following code shows this.&lt;/P&gt;&lt;FONT face="Courier New" size=2&gt;
&lt;P&gt;Foo myFoo = &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; Foo(); &lt;BR&gt;myFoo.ValueHasChanged = &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; ValueChangedDelegate(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;new &lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;ValueChangedDelegate(OnValueChanged));&lt;/P&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" color=#008000 size=2&gt;
&lt;P&gt;// Even though the value has not change I am going to trick everyone&lt;BR&gt;// into thinking it has by firing the event&lt;BR&gt;&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;if&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; (myFoo.ValueHasChanged != &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;null&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;)&lt;BR&gt;&amp;nbsp; myFoo.ValueHasChanged(&lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;null&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt;, EventArgs.Empty);&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;Clearly we would not want our "event" fired at any arbitrary time, so we go to the drawing board and think through the problem and come up with the following solution.&lt;/P&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;
&lt;P&gt;public&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;class&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; Foo&lt;BR&gt;{&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT face="Courier New" color=#0000ff size=2&gt;private&lt;/FONT&gt;&lt;FONT face="Courier New" size=2&gt; ValueChangedDelegate _valueHasChanged;&lt;