February 2007 - Posts

Last week, I introduced C# 2.0 to a few academic people who had prior exposure to C, C++ and Java. Does language matter is an often heard question. Well, in the world of .NET, the importance of language choice has somewhat blurred but richness of languages still is a great decision factor, certainly for general-purpose languages.

Typical samples of developer convenience in the world of C# include the foreach loop, nullable types, generics, etc. One more dark feature is the principle of iterators and lazyness however; the typical example of an even number generator and the explanation of a finate state automaton isn't the best approach to introduce this feature...

So, I came up with another (more academic) sample: the Pascal triangle. For those who're not familiar with this concept, it looks as follows:

1 1 1 1 2 1 1 3 3 1 1 4 6 4 1

Basically, each number inside the triangle (i.e. without the borders that always contain 1) is the sum of the two numbers above it. And yes, it has its use (beside of a geeky wallpaper layout for math freaks): every row indicates the coefficients of the mixed products of a and b when calculating (a+b)^n (Binomium of Newton).

So, here's the code:

1 using System; 2 using System.Collections.Generic; 3 4 class Pascal 5 { 6 static void Main() 7 { 8 foreach (uint[] row in GetPascalTriangle()) 9 { 10 Print(row); 11 Console.ReadKey(); 12 } 13 } 14 15 static void Print(uint[] row) 16 { 17 foreach (uint e in row) 18 Console.Write("{0}\t", e); 19 Console.WriteLine(); 20 } 21 22 static IEnumerable<uint[]> GetPascalTriangle() 23 { 24 uint[] row = new uint[1] { 1 }; 25 26 for(int n = 2; ; n++) 27 { 28 yield return row; 29 30 uint[] nrow = new uint[n]; 31 Array.Copy(row, nrow, n-1); 32 for (int i = 1; i < n; i++) 33 nrow[i] += row[i-1]; 34 row = nrow; 35 } 36 } 37 }

The code above produces something like this:

If you want to know how iterators work, check out my former post on C# 2.0 Iterators.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

This quiz was derived from an old piece of code I reviewed for a student's project somewhere in the past. Actually, I was doing some perf-related work today, so I thought it might be useful to have a little quiz around this. Below is the code (reduced in size) of a matrix implementation in C#. Using operator overloads, it supports various operations of which I only kept the multiplication operation (implemented in a very naive way):

using System; using System.Diagnostics; class Matrix { private double[,] m; public Matrix(int dim0, int dim1) { m = new double[dim0,dim1]; } public int Height { get { return m.GetLength(0); } } public int Width { get { return m.GetLength(1); } } public double this[int x, int y] { get { return m[x,y]; } set { m[x,y] = value; } } public static Matrix operator*(Matrix m1, Matrix m2) { if (m1.Width != m2.Height) throw new InvalidOperationException("Matrices should have compatible dimensions for multiplication."); Matrix m = new Matrix(m1.Height, m2.Width); for(int i = 0; i < m.Height; i++) { for(int j = 0; j < m.Width; j++) { m[i,j] = 0; for (int k = 0; k < m1.Width; k++) m[i,j] += m1[i,k] * m2[k,j]; } } return m; } } class Program { static void Main() { Random rand = new Random(); Matrix m1 = new Matrix(20,30); for (int i = 0; i < m1.Height; i++) for (int j = 0; j < m1.Width; j++) m1[i,j] = rand.Next(-100,100); Matrix m2 = new Matrix(30,40); for (int i = 0; i < m2.Height; i++) for (int j = 0; j < m2.Width; j++) m2[i,j] = rand.Next(-100,100); Matrix r = null; Stopwatch sw = new Stopwatch(); sw.Start(); for (int k = 0; k < 10000; k++) r = m1 * m2; sw.Stop(); Console.WriteLine(sw.Elapsed); } }

Question:

  1. Measure the execution time of the code fragment above on your machine (compile with debugging disabled and optimization enabled, i.e. using csc /o).
  2. Perform optimizations to the code (warning: the code should - duh - still produce the same multiplication result r). You can touch everything except for the Program::Main method.
  3. Go back to 1.

Feel free to break the loop above at any time if you're satisfied with the result. What's the speed-up factor you can realize? (Tip: You even might want to change something in the process steps 1-3 outlined above to realize a performance boost...)  A follow-up post to this quiz question will follow later. Enjoy!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

A little different quiz approach today; starting from IL :-). The question is pretty simple: when does the C# compiler emit a call instruction instead of a callvirt instruction?

A little sample to illustrate the non-trivial character of this question:

1 class Call 2 { 3 static void Main() 4 { 5 C c = new C(); 6 c.Do(); 7 c.DoIt(); 8 } 9 } 10 11 class C 12 { 13 public void Do() {} 14 public virtual void DoIt() {} 15 }

This piece of code will contain two callvirt instructions: one for c.Do() (line 6) and one for c.DoIt() (line 7). A side-question you might ask yourself is why the compiler doesn't emit a call instruction for the non-virtual method call on line 6. Stay tuned for the answer (or find it out yourself :-)).

Have fun!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Last week, I was doing an introduction session on C# for students with a Java background. There were quite some interesting discussions around the why and how of properties, indexers, operator overloads, events, delegates, attributes, etc compared to "equivalents" in Java. I won't repeat this discussion over here again but would like to pay a bit attention to another difference: virtual versus non-virtual methods. Today, I read the following question on a discussion list for .NET:

Anybody know why methods aren't virtual by default in .NET?
It seems like a really bad default to have all methods non-virtual.

Basically, in Java methods are virtual by default, unless you declare them as "final". In C#, it's the other way around: methods are non-virtual by default, unless you declare them as "virtual". The reasons why are pointed out by Anders Hejlsberg on Artima.

In this post, I just wanted to put the performance aspect in perspective by means of a little demo app:

1 using System; 2 using System.Diagnostics; 3 4 class Virt 5 { 6 public virtual void Do() {} 7 public void DoIt() {} 8 9 static void Main() 10 { 11 Virt v = new Virt(); 12 13 Stopwatch sw = new Stopwatch(); 14 sw.Start(); 15 for (int i = 0; i < 1000000000; i++) 16 v.DoIt(); 17 sw.Stop(); 18 Console.WriteLine(sw.Elapsed); 19 20 sw.Reset(); 21 sw.Start(); 22 for (int i = 0; i < 1000000000; i++) 23 v.Do(); 24 sw.Stop(); 25 Console.WriteLine(sw.Elapsed); 26 } 27 }

Make a little estimate of the figures (relative to each other) printed out on lines 18 and 25. You might be a little surprised... (Tip: do compile the code fragments once with the /o csc.exe flag too and observe the difference)

For ILDASM freaks, here are the IL equivalents of both methods:

.method public hidebysig newslot virtual instance void Do() cil managed { // Code size 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method Virt::Do .method public hidebysig instance void DoIt() cil managed { // Code size 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method Virt::DoIt

The words public, instance, void, virtual, cil and managed should be self-explanatory.

The hidebysig thing means "hide by signature" (in contrast to "hide by name" semantics which you find in languages like C++). Hidebysig isn't used by the runtime itself, but is used by language compilers and tools to see what has to be hidden (in this case, Do and DoIt will hide methods lower in the class hierarchy which have the same signature, i.d. void <name>() - in this specific case there's nothing to hide because the base class is just System.Object).

On the virtual method Do there's another keyword that might be a bit non-intuitive at first sight, newslot. This keyword is used to indicate that a new virtual method is introduced, which adds a new entry to the vtable. Assume you're overriding the method in some subclass, then there won't be a newslot declaration.

Question: Predict the IL method signatures of all Do methods below:

class Virt2 : Virt { public override void Do() {} } class Virt3 : Virt { public new void Do() {} } class Virt4 : Virt { public new virtual void Do() {} } class Virt5 : Virt { public sealed override void Do() {} }

Another question: On the caller's side (i.e. Virt::Main), which types of calls will be emitted (call or callvirt) for C# code lines 16 and 23? And why? (Note: you might be amazed/confused if you see it ;-)).

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

A question of one of my blog readers: what about multiple workflows calling into the same Local Communication Service concerning possible threading and synchronization issues.

Consider the following scenario. Assume you have one WorkflowRuntime in your host application, together with one registered Local Communication Service defined by the following interface contract:

[ExternalDataExchange] interface IBar { void Bar(int i); void Foo(int i); }

Assume Workflow1 relies on the Bar method while Workflow2 relies on the Foo method, with both workflow definitions like the following:

The whileActivity1 has a condition that evaluatues true at all times (read: endless loop). The sequenceActivity1 acts as the container for a set of activities in the body of the loop, and wraps the callExternalMethodActivity1 (that calls the Bar and Foo method for Workflow1 and Workflow2 respectively) as well as the delayActivity1 (that has a 1 second and a 3 second delay for Workflow1 and Workflow2 respectively).

Next, assume the following host application code (note: there's a little issue in the code below since I'm running more than one workflow and the waitHandle would be set upon completion of one of both, causing the app to terminate - since both workflows won't ever terminate due to the endless loop, this doesn't cause further problems in this particular demo):

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 using(WorkflowRuntime workflowRuntime = new WorkflowRuntime()) 6 { 7 AutoResetEvent waitHandle = new AutoResetEvent(false); 8 workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) {waitHandle.Set();}; 9 workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e) 10 { 11 Console.WriteLine(e.Exception.Message); 12 waitHandle.Set(); 13 }; 14 15 ExternalDataExchangeService edx = new ExternalDataExchangeService(); 16 workflowRuntime.AddService(edx); 17 edx.AddService(new MyService()); 18 19 WorkflowInstance instance1 = workflowRuntime.CreateWorkflow(typeof(TestConcurrency.Workflow1)); 20 instance1.Start(); 21 22 WorkflowInstance instance2 = workflowRuntime.CreateWorkflow(typeof(TestConcurrency.Workflow2)); 23 instance2.Start(); 24 25 waitHandle.WaitOne(); 26 } 27 } 28 }

where MyService implements the IBar interface:

class MyService : IBar { public void Bar(int n) { for (int i = 0; i < 100; i++) { Thread.Sleep(10); Console.Write('#'); } } public void Foo(int n) { for (int i = 0; i < 100; i++) { Thread.Sleep(10); Console.Write('@'); } } }

Question: Predict the console output of the workflow execution above. Tip: what about adding [MethodImpl(MethodImplOptions.Synchronized)] to the method declarations? What about inspecting the ManagedThreadId property of the Thread.CurrentThread in both methods?

Happy threading!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

One of the new features in .NET Framework 3.5 is the new namespace System.Numeric that lives in System.Core.dll. It's a pretty lonesome place for a class to be defined in: System.Numeric's only citizen at this very moment is BigInteger. But how happy it is to be there :-) A little tale...

Everyone knows the mathematical ! operator for factorials. Recursively it is defined as a unary operator on natural numbers:

0! = 1
n! = n * (n-1)! for n > 0

which can be implemented using a simple loop:

int res = 1; for (int i = 2; i <= n; i++) res *= i; return res;

The bottleneck? Take a look at the type of res, which is int (aka System.Int32). Further than 12! you won't get without overflowing. Replacing the type by long instead of int won't help us much further: after calculating 20! we're out of luck (question: would uint or ulong bring us any further?). Luckily BigInteger is here to help us out. A sample:

1 using System; 2 using System.Numeric; 3 4 namespace ConsoleApplication1 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 FactorialWithInt32(); 11 FactorialWithInt64(); 12 FactorialWithBigInteger(); 13 } 14 15 static void FactorialWithInt32() 16 { 17 int res = 1; 18 19 try 20 { 21 checked 22 { 23 for (int n = 2; ; n++) 24 Console.WriteLine("{0}! = {1}", n, res *= n); 25 } 26 } 27 catch { } 28 } 29 30 static void FactorialWithInt64() 31 { 32 long res = 1; 33 34 try 35 { 36 checked 37 { 38 for (int n = 2; ; n++) 39 Console.WriteLine("{0}! = {1}", n, res *= n); 40 } 41 } 42 catch { } 43 } 44 45 static void FactorialWithBigInteger() 46 { 47 BigInteger res = 1; 48 49 try 50 { 51 checked 52 { 53 for (int n = 2; ; n++) 54 Console.WriteLine("{0}! = {1}", n, res *= n); 55 } 56 } 57 catch { } 58 } 59 } 60 }

The checked keyword performs overflowing checks and is needless in the FactorialWithBigInteger sample; BigInteger won't run out of juice pretty soon (basically, a BigInteger is an abstraction of an array of integer values together with a sign, with overloads for typical integer operators). So, if you ever need to deal with large (I mean really large!) integers, BigInteger might be something for you. However, keep in mind that BigIntegers are slower in use (after all, it has to deal with much more data than a simple add operation) so unless you really need to keep a large value, stay away from it.

A few notes

What do I need to run the sample? Answer: download the January 07 CTP of Orcas

Where is System.Core.dll? Answer: take a look at %windir%\Microsoft.NET\Framework\v3.5.11209

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Probably you did know already that Windows XP SP2, Windows Server 2003 and Windows Vista have a component called http.sys to handle HTTP requests.

Basically, http.sys is a kernel-mode listener that has intrinsic knowledge of HTTP. Different parties can register with this listener to have requests forwarded to them. Typical examples nowadays include IIS 6 and SQL Server 2005 (for Web Service functionality). The result? An increase throughput (kernel-mode caching), a more reliable architecture and a decreased footprint on the system (no need to install IIS to do - for example - SQLXML kind of stuff since SQL Server can hook directly into http.sys).

Enough theory. Did you know your managed code application can take advantage of all this goodness right away? Discover System.Net.HttpListener right away. This sample shows the basic concepts:

1 using System; 2 using System.Collections; 3 using System.Collections.Specialized; 4 using System.IO; 5 using System.Net; 6 using System.Reflection; 7 using System.Text; 8 using System.Threading; 9 10 class Program 11 { 12 static void Main() 13 { 14 HttpListener listener = new HttpListener(); 15 listener.Prefixes.Add("http://*:8080/"); 16 listener.Start(); 17 Console.WriteLine("Listening..."); 18 for(;;) 19 { 20 HttpListenerContext ctx = listener.GetContext(); 21 new Thread(new Worker(ctx).ProcessRequest).Start(); 22 } 23 } 24 25 class Worker 26 { 27 private HttpListenerContext context; 28 29 public Worker(HttpListenerContext context) 30 { 31 this.context = context; 32 } 33 34 public void ProcessRequest() 35 { 36 string msg = context.Request.HttpMethod + " " + context.Request.Url; 37 Console.WriteLine(msg); 38 39 StringBuilder sb = new StringBuilder(); 40 sb.Append("<html><body><h1>" + msg + "</h1>"); 41 DumpRequest(context.Request, sb); 42 sb.Append("</body></html>"); 43 44 byte[] b = Encoding.UTF8.GetBytes(sb.ToString()); 45 context.Response.ContentLength64 = b.Length; 46 context.Response.OutputStream.Write(b, 0, b.Length); 47 context.Response.OutputStream.Close(); 48 } 49 50 private void DumpRequest(HttpListenerRequest request, StringBuilder sb) 51 { 52 DumpObject(request, sb); 53 } 54 55 private void DumpObject(object o, StringBuilder sb) 56 { 57 DumpObject(o, sb, true); 58 } 59 60 private void DumpObject(object o, StringBuilder sb, bool ulli) 61 { 62 if (ulli) 63 sb.Append("<ul>"); 64 65 if (o is string || o is int || o is long || o is double) 66 { 67 if(ulli) 68 sb.Append("<li>"); 69 70 sb.Append(o.ToString()); 71 72 if(ulli) 73 sb.Append("</li>"); 74 } 75 else 76 { 77 Type t = o.GetType(); 78 foreach (PropertyInfo p in t.GetProperties(BindingFlags.Public | BindingFlags.Instance)) 79 { 80 sb.Append("<li><b>" + p.Name + ":</b> "); 81 object val = null; 82 83 try 84 { 85 val = p.GetValue(o, null); 86 } 87 catch {} 88 89 if (val is string || val is int || val is long || val is double) 90 sb.Append(val); 91 else 92 93 if (val != null) 94 { 95 Array arr = val as Array; 96 if (arr == null) 97 { 98 NameValueCollection nv = val as NameValueCollection; 99 if (nv == null) 100 { 101 IEnumerable ie = val as IEnumerable; 102 if (ie == null) 103 sb.Append(val.ToString()); 104 else 105 foreach (object oo in ie) 106 DumpObject(oo, sb); 107 } 108 else 109 { 110 sb.Append("<ul>"); 111 foreach (string key in nv.AllKeys) 112 { 113 sb.AppendFormat("<li>{0} = ", key); 114 DumpObject(nv[key],sb,false); 115 sb.Append("</li>"); 116 } 117 sb.Append("</ul>"); 118 } 119 } 120 else 121 foreach (object oo in arr) 122 DumpObject(oo, sb); 123 } 124 else 125 { 126 sb.Append("<i>null</i>"); 127 } 128 sb.Append("</li>"); 129 } 130 } 131 if (ulli) 132 sb.Append("</ul>"); 133 } 134 } 135 }

Ignore the naive approach to threading in Main and the quick-n-dirty dumb DumpObject thing at the end of the sample and just focus on the real stuff in ProcessRequest. HttpListenerContext has not more than three properties that allow you to deal with the request, the response and the user. The first two are illustrated. I could have created a simple "Hello World" kind of thing, but decided to make it a little more complex by means of a "HTTP Request Reflector". Although there are better approaches to reflect against an object tree (e.g. the object dumper in the LINQ CTPs - which does a similar thing compared to my DumpObject method), the result looks pretty good (click to enlarge):

Line 15 in the code snippet above is a pretty interesting one since it's crucial to the whole sample. What it does is the registration of a so-called prefix with the http.sys infrastructure. Essentially, this tells the kernel mode listener to forward all requests that match the specified pattern (http://*:8080/) to this listener. Other patterns that you could use include a hostname (http://bart-pc:8080/), the https protocol instead of http, other port numbers of course, virtual directories (try to substitute http://*:8080/ in the sample above with http://*:8080/reflector/ and make a request to the original url and the new url including the "vdir"), FQDNs, etc. Notice the prefixes should end with /.

Some nice exercises:

  • Combine this with the goodness of Cassini (http://blogs.msdn.com/dmitryr/default.aspx search for Cassini).
  • Make a proxy server (and avoid endless request loops to the same proxy over and over again - the IE proxy setting is used by some BCL classes such as WebRequest)

A few notes

If you receive an "Access is denied" HttpListenerException message, check that your command prompt (or whatever shell) is elevated as Administrator (for Vista users):

On Windows Vista, use netsh to inspect the registered http.sys listeners. An approach is shown below:

Windows Server 2003 users can use the httpcfg.exe tool.

Enjoy (Cache-Control = public)!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

In November last year, I blogged about TxF:

A few days ago I received a mail from a reader of my blog who had an issue with a similar CreateFileTransacted code snippet which allows to manipulate file content in the scope of a transaction. In today's post, I show a little demo on how to do this.

1 using System; 2 using System.IO; 3 using Microsoft.Win32.SafeHandles; 4 using System.Runtime.InteropServices; 5 6 class TxfDemo 7 { 8 const uint GENERIC_READ = 0x80000000; 9 const uint GENERIC_WRITE = 0x40000000; 10 const uint CREATE_NEW = 1; 11 const uint CREATE_ALWAYS = 2; 12 const uint OPEN_EXISTING = 3; 13 14 [DllImport("kernel32.dll", SetLastError = true)] 15 static extern SafeFileHandle CreateFile( 16 string lpFileName, 17 uint dwDesiredAccess, 18 uint dwShareMode, 19 IntPtr lpSecurityAttributes, 20 uint dwCreationDisposition, 21 uint dwFlagsAndAttributes, 22 IntPtr hTemplateFile); 23 24 [DllImport("kernel32.dll", SetLastError = true)] 25 static extern SafeFileHandle CreateFileTransacted( 26 string lpFileName, 27 uint dwDesiredAccess, 28 uint dwShareMode, 29 IntPtr lpSecurityAttributes, 30 uint dwCreationDisposition, 31 uint dwFlagsAndAttributes, 32 IntPtr hTemplateFile, 33 IntPtr hTransaction, 34 IntPtr pusMiniVersion, 35 IntPtr pExtendedParameter); 36 37 [DllImport("ktmw32.dll", SetLastError = true)] 38 static extern IntPtr CreateTransaction( 39 IntPtr lpTransactionAttributes, 40 IntPtr uow, 41 uint createOptions, 42 uint isolationLevel, 43 uint isolationFlags, 44 uint timeout, 45 string description); 46 47 [DllImport("ktmw32.dll", SetLastError = true)] 48 static extern bool CommitTransaction( 49 IntPtr transaction); 50 51 [DllImport("ktmw32.dll", SetLastError = true)] 52 static extern bool RollbackTransaction( 53 IntPtr transaction); 54 55 [DllImport("Kernel32.dll")] 56 static extern bool CloseHandle(IntPtr handle); 57 58 static void Main() 59 { 60 string file = "txfdemo.txt"; 61 62 // 63 // For demo purposes, delete the file first (if it already exists). 64 // 65 if (File.Exists(file)) 66 File.Delete(file); 67 68 // 69 // Create KTM transaction. 70 // 71 IntPtr transaction = CreateTransaction(IntPtr.Zero, IntPtr.Zero, 0, 0, 0, 0, null); 72 73 // 74 // Check transaction handle. 75 // 76 if (transaction == IntPtr.Zero) 77 { 78 Console.WriteLine("Oops! KTM has taken a day off."); 79 return; 80 } 81 82 try 83 { 84 // 85 // Non-transacted. 86 // 87 //SafeFileHandle handle = CreateFile(file, GENERIC_READ | GENERIC_WRITE, 0, IntPtr.Zero, CREATE_NEW, 0, IntPtr.Zero); 88 89 // 90 // Transacted file creation (CREATE_NEW). 91 // 92 SafeFileHandle handle = CreateFileTransacted(file, GENERIC_READ | GENERIC_WRITE, 0, IntPtr.Zero, CREATE_NEW, 0, IntPtr.Zero, transaction, IntPtr.Zero, IntPtr.Zero); 93 94 // 95 // Check handle. 96 // 97 if (handle.IsInvalid) 98 Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); 99 100 // 101 // Using pattern for stream operations. 102 // 103 using (FileStream fs = new FileStream(handle,FileAccess.ReadWrite)) 104 { 105 using (StreamWriter sw = new StreamWriter(fs)) 106 { 107 sw.WriteLine("Hello"); 108 } 109 } 110 111 // 112 // To commit or not to commit? 113 // 114 Console.WriteLine("Press Y to commit"); 115 116 char c = Console.ReadKey().KeyChar; 117 if (c == 'y' || c == 'Y') 118 CommitTransaction(transaction); 119 else 120 RollbackTransaction(transaction); 121 } 122 catch (Exception ex) 123 { 124 Console.WriteLine("Oops! {0}", ex.Message); 125 126 // 127 // Rollback. 128 // 129 RollbackTransaction(transaction); 130 } 131 finally 132 { 133 // 134 // Close kernel mode transaction handle. 135 // 136 CloseHandle(tx); 137 } 138 } 139 }

The sample shows how to create a file (change CREATE_NEW on line 89 if you want to open an existing file for instance) and how to hand over the SafeFileHandle to the FileStream class for further usage. Notice that the code above has some fundamental security problems concerning path sanitation of the variable "file". You might want to take a look at the SSCLI code (\clr\src\bcl\system\io\filestream.cs - FileStream::Init(...) method) for details on how to implement a much safer library that calls CreateFile(Transacted). Especially, you have to pay attention to path normalization (do you really want ".." sequences in your path?), the infamous \\.\ sequence that allows to talk to ports (e.g. COM1), the \\?\ prefix for long path names (cf. MAX_PATH), code access security (cf. FileIOPermission demand), etc.

Ignoring the warnings above (which - one day - I might resolve using a TxF aware set of classes that incorporates the required security checks), the sample will create a file called "txfdemo.txt" in the current folder, write "Hello" to it (line 104) and wait for the end-user to press Y to commit. Before you press a key (line 113), open up an instance of Windows Explorer or another command prompt and observe that (due to the isolation property of ACID transactions) the file isn't present yet. If you press Y, the file will appear out of the blue because of the transaction commit. Any other key (other than 'y' and 'Y') will rollback the transaction, which also happens in case of an exception.

Exercise: (re)read Windows Vista - Introducing TxF in C# (part 2) - Using System.Transactions and the DTC and make the code above System.Transactions aware (also adding more flexibility concerning various parameters - line 89 - see Windows SDK CreateFileTransacted documentation for more info, as well as the .h header file that come with the SDK for symbolic constant values).

Brain.Commit();

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Recently, I was playing a bit with generics in CLR 2.0, focusing on some low-level aspects. Consider the following fragment (don't worry about the bigger context from which this sample is derived - maybe some posts about the real intent of my "private investigation" will follow later):

1 using System; 2 3 class Program 4 { 5 static void Main() 6 { 7 new Foo<Bar1>().Do(123); 8 new Foo<Bar2>().Do(123); 9 } 10 } 11 12 interface IBar 13 { 14 void Bar(int n); 15 } 16 17 class Foo<T> where T : IBar, new() 18 { 19 public void Do(int n) 20 { 21 new T().Bar(n); 22 } 23 } 24 25 class Bar1 : IBar 26 { 27 public void Bar(int n) { Console.WriteLine("Bar 1 says {0}", n); } 28 } 29 30 struct Bar2 : IBar 31 { 32 public void Bar(int n) { Console.WriteLine("Bar 2 says {0}", n); } 33 }

In the definition of the generic type Foo on line 17, two constraints are specified. One is that the type parameter has to implement IBar. The other tells the compiler a default constructor should be present, which is what I need on line 21 (if I did not specify the "new()" constraint, a CS0304 error message would appear at compile time). However, take a closer look at line 21 and predict the IL that corresponds to it. Simple you say? Just a newobj instruction? I'm afraid it's a little more complex than that :-). Here's what really happens:

.method public hidebysig instance void Do(int32 n) cil managed { // Code size 50 (0x32) .maxstack 2 .locals init (!T V_0) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: initobj !T IL_0009: ldloc.0 IL_000a: box !T IL_000f: brfalse.s IL_001c IL_0011: ldloca.s V_0 IL_0013: initobj !T IL_0019: ldloc.0 IL_001a: br.s IL_0021 IL_001c: call !!0 [mscorlib]System.Activator::CreateInstance<!T>() IL_0021: stloc.0 IL_0022: ldloca.s V_0 IL_0024: ldarg.1 IL_0025: constrained. !T IL_002b: callvirt instance void IBar::Bar(int32) IL_0030: nop IL_0031: ret } // end of method Foo`1::Do

Let's take a look. The subtlety has to do with the fact that our type parameter can be either a value type (struct) or a reference type (class). Line IL_0003 contains the intuitive instruction "initobj" (no, not "newobj") - what it does is initialization of a value type, given a managed pointer to the value type instance. Hmm, weird ... since we don't know it is or it isn't a value type. Instead, we just assume it is a value type and check whether our guess was correct on line IL_000f after boxing the maybe-be-value type (boxing takes a value type instance from the stack, creates a new instance and pushes the object reference on the stack). In case this fails, the guess was wrong and we're dealing with a reference type, which is handled by the code on line IL_001c (and further). Now, System.Activator lurks around the corner to create the instance. Why no newobj? Because we don't have the necessary token in the IL stream to refer to the type of which an instance has to be created (newobj instance void [???]???::.ctor() won't do the trick :-)).

Tip: Observe what happens if you change the definition of Foo as follows:

class Foo<T> where T : class, IBar, new() { public void Do(int n) { new T().Bar(n); } }

or as follows:

class Foo<T> where T : struct, IBar { public void Do(int n) { new T().Bar(n); } }

In the end, when we arrive on line IL_0024 of our original piece of IL, the stack has the address of the object on its top position, allowing for a call to the Bar method. The big unknown on line IL_0025 might be the constrained. prefix to the callvirt instruction on the line below. Basically, the problem with the code on line IL_002b is that a virtual call can't be done on a value type. Since we can't know at compile time whether the type parameter will be a value type or a reference type (unless you specify a "class" or "struct" constraint), the compiler has no clue whether to use a callvirt or a call instruction. The constrained prefix gets around this issue and defers the decision about the type of call till code runtime. More info can be found on http://msdn2.microsoft.com/en-us/library/system.reflection.emit.opcodes.constrained.aspx and in Partition III of the CLI specification.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

For the moment, I'm responsible to set up a TFS machine for a team project that's performed over the internet. No big deal you might think? Well, there are some issues that you have to beware of.

The most 'difficult' one is the "fully qualified domain name" issue. When you install TFS, quite a bunch of references to the machine are kept as the NETBIOS name (e.g. TFSLAB01). These references are used by lots of services (e.g. a page called tfsredirect.aspx will use a registry setting to gather reports in the SharePoint environment) as well as the Team Explorer clients (for which settings are obtained via and exported through a web service call to the TFS machine).

As you can imagine, the server naming above does work out for intranet scenarios but on the internet a name like TFSLAB01 is worthless. There are a few solutions to this:

  • Have every team member connect to the company over VPN.
  • Modify the hosts file on each team member's dev box by adding an entry that points TFSLAB01 (the TFS server name) to the right IP.
  • Change some settings on the TFS machine to use the FQDN. Read Mike Glaser's blog entry for more information.

Have fun!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

More Posts Next page »