Frank 的个人资料Frank de Groot日志列表 工具 帮助
5月14日

EntLib 3.0 Hand-on labs

The EntLib 3.0 Hand-on labs haven't been updated for the april 2007 release. You may have to delete and add the references and update the App.config by replacing "2.0.0.0" with "3.0.0.0" and "PublicKeyToken=null" with "PublicKeyToken=b03f5f7f11d50a3a". Other than that, they're a handy intro. I finally have the chance to try these things out since the separate application block era.
4月29日

UI Smell

Just like code smell I recently thought of the term UI smell. Apparently the term is already sort of used but not very much.
 
The system I'm currently participating on has a few:
  • System message "Please press button ...": If the system already knows what button should be pressed, why ask me to do it?
  • System message "Would you like to save the changes?" - "Yes" - "Can't save changes because of ...": Why don't you check if you can save before you ask me?
  • System message "Message sent." - "Can't send message because of ...": Why tell me you performed the action while in fact you haven't already?
  • And the best: a form that can open in 9 different modes which make the form act subtly different while there are no visual clues telling the user what mode the form is in.

I really feel sorry for the future users.

.NET 2.0 Stream Compression & Encryption

A while ago I created a little application to store my passwords compressed and encrypted using XML serialization. I created it in .NET 1.1 so I could use it a my customer's site (poor me). It's compressed and encrypted XML in an XML container so I can use XmlSerializer all the way.
I handcrafted the streaming of the encryption and compression which was fun but took some time.
In .NET 2.0 this should be easier with CryptoStream and DeflateStream. The only tricky bit is how to get the compressed & encrypted data into and out of a MemoryStream properly. If you don't close the Crypto & Deflate stream they don't finalize their output properly and if you do close them they close the underlying MemoryStream. There's a way around this but it differs between the CryptoStream and DeflateStream. The sample code below shows how to do this (and these aren't my real passwords  of course):
 
The Save method required some tweaking, in short it does the following:
  • create a MemoryStream and a CryptoStream,
  • create a DeflateStream with leaveOpen set to true in the constructor, serialize the secret information into it and close it,
  • call CryptoStream.FlushFinalBlock(),
  • reset the MemoryStream to the beginning,
  • create the containing object (Vault) and put in the contents of the MemoryStream,
  • serialize the containing object to a file.

So, the CryptoStream has an extra method FlushFinalBlock() to tell the stream to finish its work. On the other hand DeflateStream is instructed to leave the underlying stream open. If the DeflateStream is then closed it will also finish its work.

namespace Spikes
{
    public class Credential
    {
        [XmlAttribute]
        public string Site;
        [XmlAttribute]
        public string User;
        [XmlAttribute]
        public string Password;
    }

   
public class Secrets
    {
        public Credential[] Credentials;
    }

   
public class Vault
    {
        [XmlAttribute]
        public int Hashes;
        [XmlAttribute]
        public byte[] IV;
        [XmlText]
        public byte[] Data;
    }

   
class Program
    {
        static void Main(string[] args)
        {
            Credential credential1 = new Credential();
            credential1.Site = "Info Support";
            credential1.User = "frankg";
            credential1.Password = "bl@h'1";
            Credential credential2 = new Credential();
            credential2.Site = "Live Mail";
            credential2.User = "fjtdg(at)hotmail.com";
            credential2.Password = "Y0!";
            Secrets secrets = new Secrets();
            secrets.Credentials = new Credential[] { credential1, credential2, };
            RandomNumberGenerator rng = RNGCryptoServiceProvider.Create();
            byte[] iv = new byte[16];
            rng.GetBytes(iv);
            const string path = @"C:\Users\Frank\Documents\vault.xml";
            const string password = @"P@$$\/\/w0rd";
            const int hashes = 16; //1048576;
            Save(secrets, path, password, hashes, iv);
            secrets = Open(path, password);
            Console.WriteLine(secrets.Credentials.Length);
        }

        static void Save(Secrets secrets, string path, string password, int hashes, byte[] iv)
        {
            using (MemoryStream dataStream = new MemoryStream())
            {
                Rijndael rijndael = Rijndael.Create();
                byte[] key = Key(password, hashes);
                using (CryptoStream cryptoStream = new CryptoStream(dataStream, rijndael.CreateEncryptor(key, iv),
CryptoStreamMode
.Write))
                {
                    using (Stream zipStream = new DeflateStream(cryptoStream, CompressionMode.Compress, true))
                    {
                        XmlSerializer secretsSerializer = new XmlSerializer(typeof(Secrets));
                        secretsSerializer.Serialize(zipStream, secrets);
                    }
                    cryptoStream.FlushFinalBlock();
                    dataStream.Position = 0;
                    Vault vault = new Vault();
                    vault.Hashes = hashes;
                    vault.IV = iv;
                    byte[] data = new byte[dataStream.Length];
                    dataStream.Read(data, 0, data.Length);
                    vault.Data = data;
                    using (FileStream vaultStream = File.Open(path, FileMode.Create))
                    {
                        XmlSerializer vaultSerializer = new XmlSerializer(typeof(Vault));
                        vaultSerializer.Serialize(vaultStream, vault);
                    }
                }
            }
        }

       
static Secrets Open(string path, string password)
        {
            using (FileStream vaultStream = File.Open(path, FileMode.Open))
            {
                XmlSerializer vaultSerializer = new XmlSerializer(typeof(Vault));
                Vault vault = (Vault)vaultSerializer.Deserialize(vaultStream);
                byte[] key = Key(password, vault.Hashes);
                using (MemoryStream dataStream = new MemoryStream())
                {
                    dataStream.Write(vault.Data, 0, vault.Data.Length);
                    dataStream.Flush();
                    dataStream.Position = 0;
                    Rijndael rijndael = Rijndael.Create();
                    using (CryptoStream cryptoStream = new CryptoStream(dataStream, rijndael.CreateDecryptor(key,
vault.IV),
CryptoStreamMode.Read))
                    {
                        using (Stream zipStream = new DeflateStream(cryptoStream, CompressionMode.Decompress))
                        {
                            XmlSerializer secretsSerializer = new XmlSerializer(typeof(Secrets));
                            return (Secrets)secretsSerializer.Deserialize(zipStream);
                        }
                    }
                }
            }
        }

       
static byte[] Key(string password, int hashes)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(password);
            HashAlgorithm hasher = new SHA256Managed();
            for (int i = 0; i < hashes; i++)
            {
                bytes = hasher.ComputeHash(bytes);
            }
            return bytes;
        }
    }
}

1月10日

Funky Little Laptop

The organization OLPC (One Laptop Per Child) plans to give special laptops to children. Later on they plan to sell them in developed countries where you'd have to buy two and one will go to a child, as explained in this news article. The idea is you get a chance to stay in touch with the kid for whom your buying the laptop. I'd be almost jealous, seeing what a little funky laptops these kids get.
 
Well, connect me to a kid in Latin America! A good chance to practise my spanish...
12月29日

My smart girlfriend

With the advent of Vista and the urge to install Ubuntu I decided to buy another hard disk. I couldn't resist buying another 150 Gb Raptor, of course totally overkill but that's not the point I wanted to make...
 
As I was trying figure out how to get the hard disk into the bay, my girlfriend walked in, stared at it blankly for a few seconds and then said "Can't you take out the whole bay?". When I looked more closely, it turned you she was entirely right!
 
Looks like she saved me some serious time trying to get the motherboard out of the way. Go girl!
12月26日

Finding your tools

I finally found a tool to download big files from my employer's intranet through the VPN. The VPN connection gets killed after some time, so it must be restartable. Today I found Robocopy included in the Windows Server 2003 Resource Kit Tools.
 
Then I used 7-Zip to scoop out robocopy.exe from the self-extracting .exe and the contained .msi. So no need to install anything.
 
Thank you Microsoft and thank you 7-Zip!
12月24日

NBear

Browsing CodePlex I found NBear, an ORM framework for .NET 2.0. Interesting? Well, I'll be damned if I know: apparently it's a chinese product; all the documentation and web sites are in chinese! Needless to say that diminishes my interest in the product somewhat...
12月22日

Human Methodologies 2

In another not-so-recent ARCast Lewis Curtis and Brian Derda explain their new methodology Perspective Based Architecture. Now, I'm not an architect and I don't have any experience with this methodology but I found it an appealing methodology because:
  • The basic idea is indeed very basic (what can be more basic than asking questions?),
  • The architects they intend to force people out of their comfort zone (especially useful to technical specialists),
  • and the questions also encourage people to collaborate (instead of just throwing documents over the wall).
  • They also emphasized that a strict timebox encourages people to prioritize the questions,
  • and be honest when they just don't know the answer yet.

Again a methodology with a strong social component. In my opinion methods like these have a promising future.

12月21日

Human Methodologies 1

Just recently I read the following paragraph in the book "Agile Project Management with Scrum" by Ken Schwaber.
 
I used to teach people the theory, practices, and rules of Scrum. Now I teach them what Scrum feels like as it is implemented. I teach them how to recognize when things are going right and when they are going wrong. I provide exercises and discussions that let them experience the epiphanies so that they know what Scrum should feel like. Just as you don’t really know what it’s like to be someone else until you’ve walked however many miles in his or her shoes, you might not fully understand Scrum until you implement it yourself. But as you read this book, you will begin to understand what Scrum feels like and how you might feel using Scrum in your organization.
 
How a project management methodology feels? That's what I like about the recent progress in software development, things like XP and Scrum I find more straightforward, pragmatic and most of all, more human than previous methodologies.
 
This is why I'm enjoying software development more than ever, power to the people!

Open XML in Office 2007 produces smaller files!

In a not-so-recent ARCast I heard that the new Open XML format for Office documents actually produces smaller files. After downloading the update for Office 2003 to create Open XML files I was surprised to see that this is actually true! A few tries produced files 5x smaller!
 
Amazing, an open format, much more accessible (for developers that is) format and smaller files too!
12月20日

Find & replace special characters in Word

I just pasted syntac coloured C# code from a Word document into the editor for this blog. The result was an empty line between each line of code, caused by a Paragraph tag. This in turn was caused by the Paragraph Mark which should be replaced by a Manual End of Line Mark (Shift-Enter).

 

To replace the Paragraph Mark with a Manual End of Line Mark in Word I tried to use find-and-replace ^v to ^| but Word couldn't find the Paragraph Marks, even though the ^v is inserted by a menu option on the same dialog (More / Special / Paragraph Character).

 

After googling I found this page explaining I should use ^13 instead of ^v. Odd, but it works!

Easier .NET Service installation 2

Also, removing a .NET Windows Service that is running under an account other than Local System/Local Service lingers in the list of services until a restart. Adding the snippet below in the project installer will reset the account to local system just before the actual uninstall. This neatly removes the service from the list.

/// <summary>
///
Overriden to set the user account to local service.
/// If set to custom user on uninstall the service entry lingers until a reboot,
/// when set to local service the entry is removed immediately.
/// </summary>
///
<param name="savedState">Only passed to base class.</param>
protected override void OnBeforeUninstall(IDictionary savedState)
{
      
this.serviceProcessInstaller.Account = ServiceAccount.LocalService;
      
base.OnBeforeUninstall(savedState);
}

Easier .NET Service installation 1

Below are two code snippets useful when creating a Windows Service in .NET.
 
Windows Services created as a .NET assembly are installed using InstallUtil. The code below allows installation and removal using the executable .NET assembly with the command-line options /i and /u respectively.
 

/// <summary>
///
The main entry point for the process.
/// Allows install and uninstall with command line option.
/// </summary>
///
<remarks>
///
Relies on <see cref="ProjectInstaller.OnBeforeUninstall"/> to set the user to local service
/// just before uninstall to prevent the service entry to linger until a reboot.
/// </remarks>
///
<param name="args">Command line arguments.</param>
private static void Main(string[] args)
{
      
if (args.Length > 0)
       {
             
string systemPath = Environment.GetFolderPath(Environment.SpecialFolder.System);
             
string command = Path.Combine(systemPath, @"..\Microsoft.NET\Framework\v1.1.4322\InstallUtil.exe");
             
// Remove the 'file:///' prefix.
              string service = "\"" + Assembly.GetExecutingAssembly().CodeBase.Substring(8) + "\"";
             
switch (args[0].ToLower().Substring(1, 1))
              {
                    
case "i":
                           Run(command, service);
                           Run("net", "start Berichtensimulator");
                          
break;
                    
case "u":
                           Run("net", "stop Berichtensimulator");
                           Run(command, "/u " + service);
                          
break;
                    
default:
                           Console.WriteLine("Please specify '/i' to install service and '/u' to uninstall service.");
                          
break;
              }
       }
      
else
       {
              System.ServiceProcess.ServiceBase[] ServicesToRun;
              ServicesToRun =
new ServiceBase[] { new BerichtenSimulatorSvc() };
              ServiceBase.Run(ServicesToRun);
       }
}

private static void Run(string command, string args)
{
       ProcessStartInfo processStart =
new ProcessStartInfo(command, args);
       processStart.UseShellExecute =
false;
       processStart.RedirectStandardOutput =
true;
       Process process = Process.Start(processStart);
       process.WaitForExit();
       Console.WriteLine(process.StandardOutput.ReadToEnd());
}

2月16日

Previous blog

My previous blog is at http://yadnd.blogger.com (yadnd = yet another dot net developer).