October 2006 - Posts

Spyware protection for free. For use on Windows XP SP2 and Windows Server 2003 SP1. Requires genuine Windows.

Download it here

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

The picture tells it all...

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

Introduction

A couple of days ago I received the following mail: 

Hi Bart,

I’m trying to add an install functionally that will just basically copy the whole CD content to a local C drive folder. Executing the launcher.exe in that local folder works fine but if I create a desktop shortcut that targets the launcher.exe produced a file not found error for cassinilight.dll. I was wondering if you have an idea in what location was the application looking for cassinilight.dll.  Any help is highly appreciated.

The short answer is really short: probing. Probing is the technique employed by the CLR's assembly loader to find a dependent assembly based on searching for it in various folders. Strongly named assemblies (those you signed using an sn.exe generated public/private key pair) are being searched for in the GAC (and paths specified in codeBase configuration elements, see further; and the "standard locations"). Weakly named assemblies are also probed, by looking in the same folder as the application and in subfolders named after the dependent assemblies themselves. However, sometimes it's not that easy and you really want to see what's going on (a common problem is an assembly being loaded from the GAC while you have recompiled it to your bin\Debug folder in Visual Studio which leads to unexpected debugging results).

In this post, I'm showing you how to make a jumpstart with Fusion, assembly probing and the "Fusion log viewer" aka fuslogvw.exe. For the record, Fusion is the codename of the assembly loader component of the CLR which (you can still see this in the SSCLI source tree under sscli20\clr\src\fusion).

A faulty application

Right, let's create a plain simple demo to illustrate the principle. It's so simple it fits in one console window using "copy con" file generation:

The code is:

foo.cs (compile using csc /t:library foo.cs)
public class Foo
{
}

bar.cs (compile using csc /t:library /r:foo.dll bar.cs)
class Bar
{
   public static void Main()
   {
      new Foo();
   }
}

Now you should have two assemblies: foo.dll and bar.exe. Run the application, it should just run (although it doesn't do anything useful, it doesn't produce any errors either).

Time has come to make the app faulty. Create a subfolder called "oops" and move the foo.dll file to it. Now bar.exe will fail:

And guess what, you shouldn't even read my blog to find out what's wrong. The runtime is so kind to tell you to enable Fusion:

To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.

Setting up Fusion

Redundant info maybe, but here are two ways to enable Fusion.

For modern developers - PowerShell

PS C:\Users\Bart> new-itemproperty -path HKLM:\SOFTWARE\Microsoft\Fusion -n EnableLog -t Dword -va 1

PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\               Microsoft\Fusion
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\               Microsoft
PSChildName  : Fusion
PSDrive      : HKLM
PSProvider   : Microsoft.PowerShell.Core\Registry
EnableLog    : 1

For command-line freaks - reg.exe

C:\temp>reg add HKLM\Software\Microsoft\Fusion /v EnableLog /t REG_DWORD /d 1
The operation completed successfully.

For UI lovers - regedit.exe

You should be able to find out yourself :-).

Analyzing the problem

Run bar.exe again after you've enabled Fusion. This time you get a realm of information:

The most interesting portion is the last part:

LOG: Attempting download of new URL file:///C:/temp/foo.DLL.
LOG: Attempting download of new URL
file:///C:/temp/foo/foo.DLL.
LOG: Attempting download of new URL
file:///C:/temp/foo.EXE.
LOG: Attempting download of new URL
file:///C:/temp/foo/foo.EXE.

These are the locations where the system attempted to find the referenced assembly "foo". The "oops" folder isn't there obviously, so the probing operation fails.

Now run fuslogvw.exe and you should see the following log information:

If you double-click on the last line, you'll see the following in a browser window:

*** Assembly Binder Log Entry  (12/10/2006 @ 12:16:56) ***

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
Running under executable  C:\temp\bar.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: User = Bart-PC\Bart
LOG: DisplayName = foo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
(Fully-specified)
LOG: Appbase = file:///C:/temp/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = bar.exe
Calling assembly : bar, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/temp/foo.DLL.
LOG: Attempting download of new URL file:///C:/temp/foo/foo.DLL.
LOG: Attempting download of new URL file:///C:/temp/foo.EXE.
LOG: Attempting download of new URL file:///C:/temp/foo/foo.EXE.
LOG: All probing URLs attempted and failed.

Since there's no application configuration file to specify probing locations, the default probing process is used, effectively looking in the same folder as the application (see Appbase) and in a subfolder with the assembly name (without the extension, i.e. Appbase\AssemblyName, in our example c:\temp\foo). All logs end up in the IE temporary files cache, but you can override this in fuslogvw (or by setting registry entries):

where "c:\temp" is set in the LogPath REG_SZ value in the Fusion registry key. The logging info will end up in a subfolder called "NativeImage":

Setting a custom probing path

You can drive the probing mechanism by specifying probing paths in a configuration file. So create the following bar.exe.config file in the bar.exe folder (c:\temp on my system):

<?xml version="1.0"?>
<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <probing privatePath="oops" />
      </assemblyBinding>
   </runtime>
</configuration>

Now bar.exe works fine again:

If you configure Fusion to log all binds to disk, like this:

you'll see log entries appear after re-running the bar.exe applicatin again:

This time with the following logging info:

*** Assembly Binder Log Entry  (12/10/2006 @ 13:01:05) ***

The operation was successful.
Bind result: hr = 0x0. The operation completed successfully.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
Running under executable  C:\temp\bar.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: User = Bart-PC\Bart
LOG: DisplayName = foo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
(Fully-specified)
LOG: Appbase = file:///C:/temp/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = bar.exe
Calling assembly : bar, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: Private path hint found in configuration file: oops.
LOG: Using application configuration file: C:\temp\bar.exe.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/temp/foo.DLL.
LOG: Attempting download of new URL file:///C:/temp/foo/foo.DLL.
LOG: Attempting download of new URL file:///C:/temp/oops/foo.DLL.

LOG: Assembly download was successful. Attempting setup of file: C:\temp\oops\foo.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: foo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
LOG: Binding succeeds. Returns assembly from C:\temp\oops\foo.dll.
LOG: Assembly is loaded in default load context.

As you can see, the oops path is being probed and the assembly is found thanks to our "private path hint" in the configuration file.

Conclusion

Once the FileNotFoundExceptin might have been your worst nightmare (actually MissingMethodException should deserve that spot) but when things get bad, Fusion comes to the rescue. Happy probing!

kick it on DotNetKicks.com

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

Did you know the third version of the Visual Studio 2005 SDK was shipped about one month ago? I missed this one out for a while, so it's time to explore it right now. Check out Somasegar's WebLog too for information on it.

One great thing in there is the MPPG/MPLex parser/lexer twin (just like yacc/lex or bison/flex) sample that comes with the SDK. I saw this project (called GPPG) from Queensland University quite a while ago (after attending the Good For Nothing Compiler session at the PDC last year) and have been skimming over it because of my interest in compilers. The big difference with yacc/lex is the output language, which is C# instead of C.

As you might already know by now, I spend a lot of my free time on the "dark side", inside the runtime (cf. SSCLI), messing around with specs (cf. ECMA-335) and having fun with compiler technology (cf. some-personal-project-without-a-name). I'll try to blog about MPPG/MPLex experiences in the near future.

If this isn't something to spend your Sunday afternoon on ...

Bart De Smet - Ghent - 10/12/06 1:55 (deferred post)

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

Introduction

Lately I've done some code review focusing on security and performance. One of my findings is presented in this blog post, concerning network performance when working with streams. This sample illustrates how to optimize network throughput when working with NetworkStream objects.

Basically, a NetworkStream is non-buffered by default. When dealing with small pieces of data at a time this is highly inefficient. It's much better to have some buffer that collects data for network submission. This can be accomplished using the BufferedStream class in the System.IO API.

A simple demo server: the byte sucker

For demo purposes I'm presenting you with a simple "byte sucker" server that listens on some port for an incoming TCP connection and sucks data from it till the socket is closed by the client. No multi-threading, just plain simple code:

using System;
using
System.IO;
using
System.Net;
using
System.Net.Sockets;

class
Srv
{
   public static void Main(string
[] args)
   {
      Console.WriteLine("Byte sucker network server"
);
      Console.WriteLine("--------------------------\n"
);

      int
port = -1;
      if (args.Length != 1 || !int.TryParse(args[0], out
port))
      {
         Console.WriteLine("Usage: srv.exe <port>"
);
         return
;
      }

      TcpListener srv =
new
TcpListener(IPAddress.Loopback, port);
      srv.Start();

      while (true
)
      {
         Console.Write("Listening... "
);

         TcpClient clnt = srv.AcceptTcpClient();
         NetworkStream ns = clnt.GetStream();

         Console
.WriteLine("Client connected.");
         Console.Write("Receiving data... "
);

         while
(ns.ReadByte() != -1) ;

         Console
.WriteLine("Finished.");
         Console
.WriteLine();
      }
   }
}

System;
using
System.IO;
using
System.Net;
using
System.Net.Sockets;

class
Srv
{
   public static void Main(string
[] args)
   {
      Console.WriteLine("Byte sucker network server"
);
      Console.WriteLine("--------------------------\n"
);

      int
port = -1;
      if (args.Length != 1 || !int.TryParse(args[0], out
port))
      {
         Console.WriteLine("Usage: srv.exe <port>"
);
         return
;
      }

      TcpListener srv =
new
TcpListener(IPAddress.Loopback, port);
      srv.Start();

      while (true
)
      {
         Console.Write("Listening... "
);

         TcpClient clnt = srv.AcceptTcpClient();
         NetworkStream ns = clnt.GetStream();

         Console
.WriteLine("Client connected.");
         Console.Write("Receiving data... "
);

         while
(ns.ReadByte() != -1) ;

         Console
.WriteLine("Finished.");
         Console
.WriteLine();
      }
   }
}

Optimizing a client

On to the real stuff: the client that submits data to the server. We'll assume a client that sends data on a byte-per-byte basis to the server. The straightforward way to do this is the following:

TcpClient clnt = new TcpClient("localhost", 1234);
NetworkStream ns = clnt.GetStream();
for (...; ...; ...)
   ns.WriteByte(...);

new TcpClient("localhost", 1234);
NetworkStream ns = clnt.GetStream();
for (...; ...; ...)
   ns.WriteByte(...);

A better way to do this relies on a buffer:

TcpClient clnt = new TcpClient("localhost", 1234);
NetworkStream ns = clnt.GetStream();
BufferedStream bs = new BufferedStream(ns);
for (...; ...; ...)
   bs.WriteByte(...);

The goal is to buffer data before passing it on to the underlying stream, in casu the NetworkStream.

Here's the full code:

using System;
using
System.Diagnostics;
using
System.IO;
using
System.Net.Sockets;

class
Buffer
{
   public static void Main(string
[] args)
   {
      Console.WriteLine("Buffered network traffic demo"
);
      Console.WriteLine("-----------------------------\n"
);

      int
port = -1;
      if (args.Length != 1 || !int.TryParse(args[0], out
port))
      {
         Console.WriteLine("Usage: buffer.exe <port>"
);
         return
;
      }

      Console.Write("Connecting on port {0}... "
, port);

      Stopwatch watch =
new
Stopwatch();

      TcpClient clnt =
new TcpClient("localhost"
, 1234);
      NetworkStream ns = clnt.GetStream();

      Console.WriteLine("Connected."
);
      Console
.WriteLine();
      Console.Write("Sending non-buffered data... "
);

      Random rand = new Random
();

      watch.Start();
      for (int
i = 0; i < 1000000; i++)
         ns.WriteByte((
byte
)rand.Next(255));
      watch.Stop();

      Console
.WriteLine("Done.");
      Console.WriteLine("Non-buffered: {0}"
, watch.Elapsed);

      Console
.WriteLine();
      Console.Write("Sending buffered data... "
);

      BufferedStream bs = new BufferedStream
(ns);

      watch.Reset();
      watch.Start();
      for (int
i = 0; i < 1000000; i++)
         bs.WriteByte((
byte
)rand.Next(255));
      watch.Stop();

      Console.WriteLine("Done."
);
      Console.WriteLine("Buffered: {0}"
, watch.Elapsed);

      ns.Close();
      clnt.Close();
   }
}

System;
using
System.Diagnostics;
using
System.IO;
using
System.Net.Sockets;

class
Buffer
{
   public static void Main(string
[] args)
   {
      Console.WriteLine("Buffered network traffic demo"
);
      Console.WriteLine("-----------------------------\n"
);

      int
port = -1;
      if (args.Length != 1 || !int.TryParse(args[0], out
port))
      {
         Console.WriteLine("Usage: buffer.exe <port>"
);
         return
;
      }

      Console.Write("Connecting on port {0}... "
, port);

      Stopwatch watch =
new
Stopwatch();

      TcpClient clnt =
new TcpClient("localhost"
, 1234);
      NetworkStream ns = clnt.GetStream();

      Console.WriteLine("Connected."
);
      Console
.WriteLine();
      Console.Write("Sending non-buffered data... "
);

      Random rand = new Random
();

      watch.Start();
      for (int
i = 0; i < 1000000; i++)
         ns.WriteByte((
byte
)rand.Next(255));
      watch.Stop();

      Console
.WriteLine("Done.");
      Console.WriteLine("Non-buffered: {0}"
, watch.Elapsed);

      Console
.WriteLine();
      Console.Write("Sending buffered data... "
);

      BufferedStream bs = new BufferedStream
(ns);

      watch.Reset();
      watch.Start();
      for (int
i = 0; i < 1000000; i++)
         bs.WriteByte((
byte
)rand.Next(255));
      watch.Stop();

      Console.WriteLine("Done."
);
      Console.WriteLine("Buffered: {0}"
, watch.Elapsed);

      ns.Close();
      clnt.Close();
   }
}

Test time

Compile both apps and run in two command prompts (start srv.exe first and then buffer.exe), or use "start" to launch the apps in a separate window. Needless to say the port parameter should be the same (e.g. 1234). The server output isn't of any particular interest, but the client output is:

Buffered network traffic demo
-----------------------------

Connecting on port 1234... Connected.
Sending non-buffered data... Done.
Non-buffered: 00:00:06.1217484

Sending buffered data... Done.
Buffered:     00:00:01.2173279

A factor 5 faster with buffering. Great isn't it? One little remark: you can specify an additional parameter to the BufferedStream's constructor, to indicate the buffer size in bytes. Play around with this setting to find out about the right balance for your app.

Keep it fast!

kick it on DotNetKicks.com

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

Simple question today: "How to change the password of a SQL Server account programmatically using .NET?". The answer: Microsoft.SqlServer.Management.Common.

Create a simple Console Application project and add a reference to the Microsoft.SqlServer.ConnectionInfo.dll assembly (should be listed on your machine if you've installed any flavor of SQL Server 2005).

Here's the code of our password change tool:

 

using System; using System.Security; using System.Runtime.InteropServices; using Microsoft.SqlServer.Management.Common; namespace SqlResetPwd { class Program { static void Main(string[] args) { Console.WriteLine("Reset SQL Server password"); Console.WriteLine("-------------------------\n"); Console.Write("Server name: "); string instance = Console.ReadLine(); Console.Write("User name: "); string user = Console.ReadLine(); Console.Write("Password: "); SecureString pwd = AskPassword(); Console.WriteLine(); Console.WriteLine(); Console.Write("Trying to connect... "); ServerConnection conn = new ServerConnection(instance, user, pwd); try { conn.Connect(); Console.WriteLine("Connected."); Console.WriteLine(); conn.Disconnect(); conn = new ServerConnection(instance, user, pwd); SecureString newPwd, conPwd; while (true) { Console.Write("New password: "); newPwd = AskPassword(); Console.WriteLine(); Console.Write("Confirm: "); conPwd = AskPassword(); Console.WriteLine(); if (!Match(newPwd, conPwd)) { Console.WriteLine("The specified passwords do not match. Please try again."); } else { try { conn.ChangePassword(newPwd); break; } catch (Exception ex) { Console.WriteLine("Failed to change password. " + ex.Message); } } } Console.WriteLine(); Console.WriteLine("Password changed successfully."); } catch (ConnectionFailureException ex) { Console.WriteLine((ex.InnerException != null ? ex.InnerException.Message : "Failed.")); } finally { conn.SqlConnectionObject.Close(); } } static SecureString AskPassword() { SecureString pwd = new SecureString(); while (true) { ConsoleKeyInfo i = Console.ReadKey(true); if (i.Key == ConsoleKey.Enter) { break; } else if (i.Key == ConsoleKey.Backspace) { pwd.RemoveAt(pwd.Length - 1); Console.Write("\b \b"); } else { pwd.AppendChar(i.KeyChar); Console.Write("*"); } } return pwd; } unsafe static bool Match(SecureString s1, SecureString s2) { if (s1.Length != s2.Length) return false; IntPtr bs1 = Marshal.SecureStringToBSTR(s1); IntPtr bs2 = Marshal.SecureStringToBSTR(s2); char* ps1 = (char*) bs1.ToPointer(); char* ps2 = (char*) bs2.ToPointer(); try { for (int i = 0; i < s1.Length; i++) if (ps1[i] != ps2[i]) return false; return true; } finally { if (IntPtr.Zero != bs1) Marshal.ZeroFreeBSTR(bs1); if (IntPtr.Zero != bs2) Marshal.ZeroFreeBSTR(bs2); } } } }

 

A few interesting things to mention:

  • The AskPassword method uses Console.ReadKey(true) to get one key stroke at a time, without printing the character to the console (the "true" parameter takes care of that). If the key is <ENTER>, the loop stops. If it is <BACKSPACE>, the last character is removed from the SecureString password and a backspace-space-backspace sequence is printed to the console to remove the last * character. Otherwise, the key character is appended to the SecureString password and an * character is printed on the screen.
  • Match compares two SecureStrings with one another. To do so, it uses unsafe code to iterate over the SecureString. By doing so, no System.String ends up in the managed heap, which keeps things more secure than they would be in such a case. That's the end goal of SecureString after all.
  • The ServerConnection class can be used to reset a password and knows about SecureString to deal with passwords, which is just great.

For more information on SecureString, see my "Talking about System.Security.SecureString" blog post too.

Have fun!

kick it on DotNetKicks.com

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

Interesting site - http://www.ieaddons.com. Just like other browsers support add-ons, IE7 does (older versions of the browser did too, through Windows Marketplace). I hope people will find it as hot as the ones of competing browsers. Just check it out!

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

Need to say more? Download here: http://www.microsoft.com/windows/ie/default.mspx or via the redir website http://install7.com. (Or you can wait for Microsoft Update to install it for you within a few weeks from now.) For information on language support, see this.

Install 7 now :-)

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

Introduction

This post builds upon the foundation created in yesterday's post about the HandleExternalEventActivity. Please follow the instructions in that post before continuing with this one. As you've learned from this previous post, waiting for an external event to occur is a very powerful mechanism to drive execution of a workflow. There are a lot of scenarios however where you'll want to wait for more than one event at the same time, or you want to timeout such a wait for an event to get raised. To make this kind of dreams reality, WF has the ListenActivity in its magic toolbox.

The demo

Take the solution you built in the previous post on working with events and go to Workflow1.cs's designer view. It should look like this:

Your next job is to transform this into the following:

An easy way to accomplish this is outlined below:

  • Add a ListenActivity below the WhileActivity.
  • Rename the left branch of the ListenActivity to clientArrived and the right branch to timeoutOccurred.
  • Drag and drop the clientArrival and doWork activities from the while loop to the left branch.
  • Delete the SequenceActivity from the WhileActivity's body.
  • Drag and drop the ListenActivity to the body of our WhileActivity.
  • Add a DelayActivity (set to 10 seconds) called timeout to the right branch, as well as a CodeActivity called oops with the following ExecuteCode handler code:

    Console.ForegroundColor = ConsoleColor.Cyan;
    Console.WriteLine("Oops! Timeout occurred"
    );
    Console.ResetColor();

Basically, the ListenActivity blocks till one branch completes. Notice you can add additional branches, e.g. to listen to three, four, ... events at the same time:

When you try to execute this workflow, you'll see timeouts occurring when the client waits to long to enter her name:

I'm sorry for the defaced console output, but hey that's not the goal of the demo is it?

Conclusion

Waiting for one event to occur is a common requirement; waiting for multiple events can be even a more common requirement. WF makes it really easy to do this, without having to mess around with threading and waithandles (even more: in a stateful long-running world relying on additional runtime services like persistence) using the ListenActivity. I bet you'll find yourself leveraging the power of this activity on a regular basis, especially in combination with a DelayActivity to model timeouts. Now you know everything about waiting for events in WF, it's time to wake up yourself and don't wait any longer to explore WF! Cheers!

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

Gert Drapers, development manager for DataDude and SQL guru for life, announces the availability of CTP6 on his blog. Also notice the RTM release is scheduled for Q1 2007.

For the Belgians not attending TechEd IT Forum (most developers don't I guess, but there are exceptions like me), Gunther Beersaerts will deliver an MSDN Evening Session on November 15, 2006: Introducing Visual Studio Team Edition for Database Professionals.

Time to become datadudes, don't we?

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

More Posts « Previous page - Next page »