Rainer's profileCyrons BlogPhotosBlogListsMore Tools Help

Cyrons Blog

///<Summary>.net rocks!</Summary>

Rainer Hilmer

Occupation
Location
Interests

Searchbox

Loading...
Vielen Dank für den Besuch!
Please wait...
Sorry, the comment you entered is too long. Please shorten it.
You didn't enter anything. Please try again.
Sorry, we can't add your comment right now. Please try again later.
To add a comment, you need permission from your parent. Ask for permission
Your parent has turned off comments.
Sorry, we can't delete your comment right now. Please try again later.
You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
Complete the security check below to finish leaving your comment.
The characters you type in the security check must match the characters in the picture or audio.
Das ist alles sicher super interessant und hoch wissenschaftlich, aber ehrlich gesagt verstehe ich kein Wort Animoticon .
Trotzdem ganz liebe Grüße von PC zu PC, von Blog zu Blog...
Karina
May 14
Hallo ich bin der Torsten und ich wollte nur  mal hallo sagen und  finde deinen Space super gut mach weiter so
May 2

SkyDrive

October 10

Updates für Musikstücke?

Heute Vormittag hörte ich auf NDR2 eines meiner Lieblingsstücke, “Here I go again” von White Snake. Eines hat mich dabei stutzig gemacht: Es kam mir ausgefeilter vor als die “alte” Version die ich kenne. Toller Surround Sound und perfekte Gitarren-Riffs ergaben ein homogenes, harmonisches Ganzes und nicht, wie sonst üblich bei Rock-Musik, einen Lärmbrei aus konkurrierenden Instrumenten und einem Sänger der in dem Getöse fast untergeht.
Ob White Snake den Song wirklich neu aufgelegt hat, oder was sonst der Grund für den Hörgenuss war, lasse ich mal dahin gestellt. Jedenfalls kam mir dabei die Idee von <SongXYZ> V2.0. Jaja, ich kann mein Entwicklerherz eben nicht leugnen. :-D
Wie wäre es denn wenn Bands bzw. Produzenten für den einen oder anderen Song wirklich ein Update machen?
Es ist doch viel besser, es kommt dabei so etwas tolles heraus wie ich heute Vormittag hörte, als daß irgend ein Plagiator das Stück als “verschlimmbesserte” Version auf den Markt bringt und damit auch noch dicke Kohle macht, nur weil er/sie einen großen Namen trägt und naive Fans eben alles kaufen.
Mit einem Update dagegen könnten die wahren Urheber noch einmal abkassieren – und das sogar ohne ein schlechtes Gewissen haben zu müssen.

DotNetKicks-DE Image
October 05

DotNet Expansions framework

For several years of my developer career I gathered useful code into my own framework. Formerly known as “Cyrons Framework” the new name is “.Net Expansions”.

The actual build 1.0.3554.36533 consists of the following:

  • DotNetExpansions Namespace
    • ExceptionEventArgs
    • Extension methods
      • Limit<T>
      • LimitRange<T>
      • TimeStamp
      • ExactTimeStamp
    • LimitEventArgs
    • StateManager // (Multiple Undo/Redo for everything!)
  • DotNetExpansion.Collections Namespace
    • CollectionContainer<T>
    • SharedCollectionContainer<T>
  • DotNetExpansions.CollectiveEvent Namespace
    • CollectiveEventContract // (many events, one or many listener(s))
    • CollectiveEventArgs
  • DotNetExpansions.Forms Namespace
    • ControlCommunicationsManager // (cross thread handling of Windows forms controls)
    • ControlExtensions (at the time being it consists of a cross thread control invoker developed by Marcell Spies)
    • Fading Message Box // (well known)
    • ProgressionStateInfo // (notifies a listener about the progression of a function)
  • DotNetExpansions.IO Namespace
    • CompressedBinaryFileIO // (easy saving and loading of any data in compressed binary format)
    • XmlIo // (easy saving and loading of any business objects to/from XML files)
  • DotNetExpansions.Logger Namespace
    • Logger // (base class for specific logging classes)
    • TextLogger
    • SqlLogger (not fully finished yet but usable)
    • more loggers to come…
  • DotNetExpansions.MathLib Namespace // (growing)
    • Mathematical and physics constants
    • mathematical conversions
    • Statistics
  • DotNetExpansions.MathLib.Geometry Namespace
    • // Too many to list here!
  • DotNetExpansions.MathLib.Physics Namespace
    • Electronics
    • Kinetics
  • DotNetExpansions.ParallelSearch Namespace
    • ParallelFileSearch
  • DotNetExpansions.Picture Namespace
    • PictureHandler // (easy handling of image IO in Windows forms)
  • DotNetExpansions.Time
    • Time // (easy handling for many aspects of time).

The download package (Zip file or Visual Studio 2008 solution) contains the following items:

  • DotNetExpansions.dll
  • DotNetExpansions.xml (intellisense information)
  • A complete help file in chm format with samples for everything in the framework
  • A special guide in PDF format for the XMLIo class. This is a first issue of a new series I am working on. Once it’s ready it will be a complete book about the .Net Expansions Framework. Please give me feedback about the content and the design. 

You can download the latest release of DotNet Expansions at
http://cyrons.svn.beanstalkapp.com/general/DotNetExpansions/tags/

or check out a copy of the project at
http://cyrons.svn.beanstalkapp.com/general/DotNetExpansions/trunk 

This framework has grown over years and maybe I would do some of the code in a different way today but nonetheless everything works fine – does it?

Your feedback is appreciated.

LEGAL NOTICE

This software is GPL. Do with it whatever you want.

Documentation copyright by Rainer Hilmer. You may not change or redistribute it without my permission.

DotNetKicks-DE Image
September 05

Going to move my blog? (ziehe ich ziehe um?)

MAYBE this will be my last entry on this blog. I’m going to move. The new blog URL:

http://dotnet-forum.de/blogs/rainerhilmer/default.aspx

Vielleicht wird das der letzte Beitrag in diesem Blog sein. Ich ziehe um. Die neue Blog-Adresse:

http://dotnet-forum.de/blogs/rainerhilmer/default.aspx

DotNetKicks-DE Image
September 04

Brain degeneration process unstoppable?

Watch this video of a nuclear explosion and read the comments.

http://www.youtube.com/watch?v=H1sS1TmXF38&feature=related

It's just 20 years ago when everybody was still shocked by the view of a nuclear explosion - but here and now all I can see is stupid comments! 20 years ago we thought the end of mankind would be world war III where everything is nuked. Now I fear the end of mankind will be due to the loss of  brain power.

August 16

AntiFor patterns

I have read Matthew Podwysocki’s AntiFor campaign article recently and I wondered wether it is possible to substitute all kinds of for-loops with LINQ expressions.
This came back to my mind when I wrote a little application yesterday which consists of some for-loops.
Here are the three methods with for-loops and their counterparts which use LINQ, so you can compare them directly.

using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace AntiForVersuche
{
   public class AntiFor
   {
      //======================================================================
 
      // Spaltet einen multiline-Text in einzelne Zeilen und setzt diese in ein String-Array.
      public string[] SplitText2LinesWithFor(string text)
      {
         char[] splitCharacters = { '\n' };
         var lines = text.Split(splitCharacters);
         for(var i = 0; i < lines.Length; i++)
         {
            lines[i] = lines[i].TrimEnd('\r');
         }
         return lines;
      }
 
      public IEnumerable<string> SplitText2LinesWithLinq(string text)
      {
         char[] splitCharacters = { '\n' };
         if(text == null)
            return null;
         string[] lines = text.Split(splitCharacters);
         var query = lines.Select(value => value.TrimEnd('\r'));
         return query;
      }
 
      //======================================================================
 
      // Setzt drei Slashes vor jede Zeile.
      public string[] PrependXmlCommentSignsWithFor(string[] lines)
      {
         var result = new string[lines.Length];
         for(int i = 0; i < lines.Length; i++)
         {
            result[i] = "///" + lines[i];
         }
         return result;
      }
      
      public IEnumerable<string> PrependXmlCommentSignsWithLinq(IEnumerable<string> lines)
      {
         if(lines == null)
            return null;
         if(lines.Count() == 0)
            return null;
         var query = lines.Select(value => "///" + value);
         return query;
      }
 
      //======================================================================

 

// Fügt die einzelnen Zeilen wieder zu einem Multiline-Text zusammen.

      public string ConcatenateLinesWithFor(string[] lines)
      {
         var builder = new StringBuilder();
         for(int i = 0; i < lines.Length; i++)
         {
            builder.AppendLine(lines[i]);
         }
         return builder.ToString();
      }
 
      public string ConcatenateLinesWithLinq(IEnumerable<string> lines)
      {
         if(lines == null)
            return null;
         if(lines.Count() == 0)
            return null;
         string query = lines.Aggregate((build, line) => build + "\r\n" + line);
         query += "\r\n";
         return query;
      }
   }
}

Conclusion


Well, these three samples sure don’t answer my initial question to my utter satisfaction but they showed me some of the possibilities and I’m confident. It has been a pretty informative piece of work for me because I never used the Select and Aggregate Extension methods before. Maybe this blog post is as informative for some of you out there as it has been for me.
August 11

Software-Eigenmächtigkeiten

Nicht nur daß die Installation von Windows Live Essentials meine IE-Startseite geändert hat, ohne mich zu fragen GRRRRRRR!!!!!!! >:-(

nein, dieses Miststück von Messenger startet sich jedesmal bei dem Besuch einer Webseite, die irgendwas mit Windows live zu tun hat (wie z.B. mein Blog) von selber – obwohl ich in den Optionen eingestellt habe daß er das nicht soll!!!
Ich schmeiß diesen Dreck in die Tonne!!!!!!!!

Hat jemand ‘ne Gif-Animation die das Messenger-Icon erschießt?

August 10

NUnit, a competitor for MS-Test, which you shouldn’t let pass by without a closer look!

I hesitated a long time. I use MS-Test, PEX and CHESS. Shouldn’t that be enough? Why the heck another unit test framework? Reading “The Curse of the Rewrite” from Tim Ross got me curious today (and most of all after I saw the code, wich uses NUnit). That blog post deals with testing of legacy code – but that’s just a side note.
So what’s special about NUnit? Well, just look at this line of code:

Assert.That(documentManager, Is.InstanceOfType(typeof(DocumentManager)), "Should create an instance of DocumentManager");


It’s code which fulfills the BDD paradigm at last! So I downloaded and installed NUnit, looked at the syntax sample and I’m convinced ever since.
If you have never heard of NUnit or still hesitate as I did before, than just take a look at this syntax sample. It’s right from the installation. I hope they don’t sue me for this. It’s pure advertising (but an honest one ;-) ).

// ****************************************************************
// Copyright 2007, Charlie Poole
// This is free software licensed under the NUnit license. You may
// obtain a copy of the license at http://nunit.org/?p=license&r=2.4
// ****************************************************************
 
 
// Cropped due to text length constraints in this blog. :-/
// ...
// The original code is much longer!
 
      [Test]
      public void IsTrue()
      {
         // Classic syntax
         Assert.IsTrue(2+2==4);
 
         // Helper syntax
         Assert.That(2+2==4, Is.True);
         Assert.That(2+2==4);
 
         // Inherited syntax
         Expect(2+2==4, True);
         Expect(2+2==4);
      }
 
      [Test]
      public void IsFalse()
      {
         // Classic syntax
         Assert.IsFalse(2+2==5);
 
         // Helper syntax
         Assert.That(2+2== 5, Is.False);
         
         // Inherited syntax
         Expect(2+2==5, False);
      }
 
// Cropped due to text length constraints in this blog. :-/
// ...
// The original code is much longer!
      
      [Test]
      public void CollectionContainsTests()
      {
         int[] iarray = new int[] { 1, 2, 3 };
         string[] sarray = new string[] { "a", "b", "c" };
 
         // Classic syntax
         Assert.Contains(3, iarray);
         Assert.Contains("b", sarray);
         //...
 
         // Helper syntax
         Assert.That(iarray, Has.Member(3));
         Assert.That(sarray, Has.Member("b"));
         Assert.That(sarray, Has.No.Member("x"));
         //...
      
         // Inherited syntax
         Expect(iarray, Contains(3));
         Expect(sarray, Contains("b"));
         Expect(sarray, Not.Contains("x"));
 
         //...
      }
 
      // ...
 
      [Test]
      public void SubsetTests()
      {
         int[] ints1to5 = new int[] { 1, 2, 3, 4, 5 };
 
         // Classic syntax
         CollectionAssert.IsSubsetOf(new int[] { 1, 3, 5 }, ints1to5);
         CollectionAssert.IsSubsetOf(new int[] { 1, 2, 3, 4, 5 }, ints1to5);
         CollectionAssert.IsNotSubsetOf(new int[] { 2, 4, 6 }, ints1to5);
         CollectionAssert.IsNotSubsetOf(new int[] { 1, 2, 2, 2, 5 }, ints1to5);
 
         // Helper syntax
         Assert.That(new int[] { 1, 3, 5 }, Is.SubsetOf(ints1to5));
         Assert.That(new int[] { 1, 2, 3, 4, 5 }, Is.SubsetOf(ints1to5));
         Assert.That(new int[] { 2, 4, 6 }, Is.Not.SubsetOf(ints1to5));
      
         // Inherited syntax
         Expect(new int[] { 1, 3, 5 }, SubsetOf(ints1to5));
         Expect(new int[] { 1, 2, 3, 4, 5 }, SubsetOf(ints1to5));
         Expect(new int[] { 2, 4, 6 }, Not.SubsetOf(ints1to5));
      }
      #endregion
 
      #region Property Tests
      [Test]
      public void PropertyTests()
      {
         string[] array = { "abc", "bca", "xyz", "qrs" };
         string[] array2 = { "a", "ab", "abc" };
         ArrayList list = new ArrayList( array );
 
         // Not available using the classic syntax
 
         // Helper syntax
         Assert.That( list, Has.Property( "Count" ) );
         Assert.That( list, Has.No.Property( "Length" ) );
 
         // ...

Do you know any other unit test framework which can beat this? Let me know!

August 03

Sample code in help files, namespace descriptions and angle brackets in XML comments. How to get around these hurdles.

This tutorial will show you how to add sample code into your custom help files. It will not show you how to make help files in general because you can find several tutorials for this in the internet. Just one tip:
Get Sandcastle from Microsoft at
http://www.microsoft.com/downloads/details.aspx?familyid=E82EA71D-DA89-42EE-A715-696E3A4873B2&displaylang=en

and the Sandcastle help file builder from codeplex.
http://www.codeplex.com/SHFB

You can also find a “Getting started” section in the Sandcastle help file builder documentation. 

Now let's go:
At first you must enable XML documentation file in the build section of the project properties.

image

Writing the sample code
To add sample code, you have to write (and test!) it first, of course.

using System;
 
namespace XmlCommentSampleCodeDemo
{
   class DemoCode
   {
      // This is the demo code which will be inserted into the XML-comment of the AddWorldTo-method.
      public void Demo()
      {
         MyClass instance = new MyClass();
         string myString = "Hello";
         string result = instance.AddWorldTo(myString);
         Console.WriteLine(result);
      }
   }
}

A demo class with inserted sample code
Just copy and paste the demo code into the XML-comment block as shown below. Notice that the code is surrounded by a <code>-Section which is contained in an <example>-section.

namespace XmlCommentSampleCodeDemo
{
   /// <summary>
   /// This class is just a sample for the XML documentaion.
   /// You will see this line at the class level in the help file.
   /// </summary>
   public class MyClass
   {
      /// <summary>
      /// Adds " world." to a string.
      /// </summary>
      /// <param name="someString">Any string.</param>
      /// <returns>A modificated string.</returns>
      /// <example>
      /// <code>
      /// public void Demo()
      /// {
      ///    MyClass instance = new MyClass();
      ///    string myString = "Hello";
      ///    string result = instance.AddWorldTo(myString);
      ///    Console.WriteLine(result);
      /// }
      /// </code>
      /// </example>
      public string AddWorldTo(string someString)
      {
         return someString + " world.";
      }
   }
}

 

The help entry
After generating the helpfile with Sandcastle help file builder, the entry for the AddWorldTo method will look like this:

image

Namespace comments
Namespace comments are special because you cannot comment a namespace by default. Hopefully this will change with a future release of Visual Studio. Meanwhile you need the following trick to achieve this goal.

namespace XmlCommentSampleCodeDemo
{
   // Add this empty class in conjunction with this attribute to your project.
 
   /// <summary>
   /// This is just an entry to demonstrate a namespace comment.
   /// </summary>
   [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
   class NamespaceDoc
   {
   }
}

Here you can see the comment for the namespace in the help.

image 

How to get angle brackets in a code example
When you have angle brackets the compiler will warn you about inappropriate tags. This is simply because either the XML file with the comments as the code inside the comments use them together. To overcome this problem you have to surround the code with a <![CDATA[ ]]> tag.
Here is an example:

/// <summary>
      /// My method.
      /// </summary>
      /// <param name="stringList">The string list.</param>
      /// <example>
      /// <code>
      /// <![CDATA[
      /// // This is no usage example.
      /// public void MyMethod(List<string> stringList)
      /// {
      ///   //...
      /// }
      /// ]]>
      /// </code>
      /// </example>
      public void MyMethod(List<string> stringList)
      {
         //...
      }


This is what it looks like in the help.
image

Note: You could also use HTML-tags for the angle brackets but

  1. it looks distracting and
  2. it’s likeley to forget or overlook them here and there. With the compilation time of the help file in mind, it’s very annoying when that happens.

July 31

Parallel file search, Part 4

Hier noch ein paar Unit Tests. Darunter auch ein Concurrency-Test mittels CHESS.

/**********************************************************************************************
 * In order to make these tests working correctly, they must run in MTA and not STA as usual. *
 * This modification is done in the LocalTestRun.testrunconfig with the                       * 
 * <ExecutionThread apartmentState="MTA" /> -tag.                                             *
 **********************************************************************************************/

using System;
using System.IO;
using Cyrons.ParallelSearch;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;

namespace ParallelSearchTests
{
   /// <summary>
   ///This is a test class for ParallelFileSearcher and is intended
   ///to contain all ParallelFileSearcher Unit Tests.
   ///</summary>
   [TestClass]
   public class ParallelFileSearcherTest
   {
      private static int eventCounter;

      /// <summary>
      ///Gets or sets the test context which provides
      ///information about and functionality for the current test run.
      ///</summary>
      public TestContext TestContext { get; set; }
      
      /// <summary>
      ///A test for GroupDriveItems
      ///</summary>
      [TestMethod]
      [DeploymentItem("ParallelSearch.dll")]
      public void ShouldCreate3FolderGroups()
      {
         var folders = new List<SearchSet>();
         var searchSet = new SearchSet(
            new DirectoryInfo(@"A:\"), SearchOption.TopDirectoryOnly);
         folders.Add(searchSet);
         searchSet = new SearchSet(
            new DirectoryInfo(@"B:\"), SearchOption.TopDirectoryOnly);
         folders.Add(searchSet);
         searchSet = new SearchSet(
            new DirectoryInfo(@"C:\"), SearchOption.TopDirectoryOnly);
         folders.Add(searchSet);
         /* A fourth entry is for a logical drive that's already in the list.
          * So even we have 4 entries, we should get 3 folder groups only. */
         searchSet = new SearchSet(
            new DirectoryInfo(@"C:\Program files"), SearchOption.TopDirectoryOnly);
         folders.Add(searchSet);

         IEnumerable<IGrouping<string, SearchSet>> actual
            = ParallelFileSearcher_Accessor.GroupDriveItems(folders);
         Assert.AreEqual(3, actual.Count());
      }

      [TestMethod]
      [HostType("Chess")]
      [TestProperty("ChessBreak", "BeforePreemption")]
      [TestProperty("ChessDebug", "true")]
      [Ignore] // Ran 2000 Threads without problems. Test has been aborted after that to save time.
      public void ChessParallelTest()
      {
         // See the remark at the top of the code.
         Console.WriteLine("Current thread state (should be MTA!): "
            + System.Threading.Thread.CurrentThread.GetApartmentState().ToString());
         const bool ignoreExceptions = true;
         var fileContentSearcher = new ParallelFileSearcher(ignoreExceptions);
         fileContentSearcher.FoundFile += NewFileFoundHandler;
         var searchSets = new List<SearchSet>();
         
         searchSets.Add(new SearchSet(
            new DirectoryInfo(@"C:\Program Files"),
            SearchOption.TopDirectoryOnly));
        
         searchSets.Add(
            new SearchSet(
            new DirectoryInfo(@"H:\VS2008\Lab\CyronsFramework"),
            SearchOption.AllDirectories));
         
         searchSets.Add(
            new SearchSet(
            new DirectoryInfo(@"I:\Developer\VS2008\Projects\Demos\Eigene\CallBackDelegateEssential"),
            SearchOption.AllDirectories));

         Console.WriteLine("Bitte warten. Dateien werden gesucht...");

         // Define search-parameters.
         const string filenamePattern = "*.cs";
         const string contentToSearchFor = ""; // Could also be null.

         // Start the search with those parameters.
         List<FileInfo> fileInfos =
            fileContentSearcher.FindFiles(
            searchSets, filenamePattern, contentToSearchFor, FindMode.FindAll);
         // Do something with fileInfos like, for instance, delete those files.
         Console.WriteLine(fileInfos.Count);
      }

      [TestMethod]
      public void ShouldGetCallOutEvent()
      {
         // Siehe Anmerkung.
         Console.WriteLine("Current thread state (should be MTA!): "
            + System.Threading.Thread.CurrentThread.GetApartmentState().ToString());
         // Create a search-list.
         var searchList = new List<SearchSet>();

         // Generate Search sets...
         var searchSet = new SearchSet(
            new DirectoryInfo(@"H:\VS2008\Lab\ActiveRecordPattern"),
            SearchOption.AllDirectories);
         // ...and add them to the search-list.
         searchList.Add(searchSet);

         searchSet = new SearchSet(
            new DirectoryInfo(@"I:\Developer\VS2008\Projects\Demos\Eigene\CallBackDelegateEssential"),
            SearchOption.AllDirectories);
         searchList.Add(searchSet);

         searchSet = new SearchSet(
            new DirectoryInfo(@"C:\Program Files"),
            SearchOption.TopDirectoryOnly);
         searchList.Add(searchSet);

         const bool ignoreExceptions = true;
         const string filenamePattern = "*.cs";
         const string contentToSearchFor = null;
         var parallelFileSearcher = new ParallelFileSearcher(ignoreExceptions);
         parallelFileSearcher.CallOutUnauthorizedAccess += ShowCurrentlyDeniedAccess;
         parallelFileSearcher.FoundFile += NewFileFoundHandler;
         List<FileInfo> files = parallelFileSearcher.FindFiles(
            searchList,
            filenamePattern,
            contentToSearchFor,
            FindMode.FindAll);
         Assert.IsTrue(eventCounter > 0);
      }

      [TestMethod]
      public void ShouldNotFailOnDeniedAccess()
      {
         var searchList = new List<SearchSet>();
         // The bad guy.
         var searchSet = new SearchSet(
            new DirectoryInfo(@"C:\System Volume Information"), SearchOption.TopDirectoryOnly);
         searchList.Add(searchSet);
         const bool ignoreExceptions = true;
         const string filenamePattern = "*.*";
         const string contentToSearchFor = null;
         var parallelFileSearcher = new ParallelFileSearcher(ignoreExceptions);
         parallelFileSearcher.CallOutUnauthorizedAccess += ShowCurrentlyDeniedAccess;
         parallelFileSearcher.FoundFile += NewFileFoundHandler;
         List<FileInfo> files = parallelFileSearcher.FindFiles(
            searchList,
            filenamePattern,
            contentToSearchFor,
            FindMode.FindAll);
         Assert.IsTrue(eventCounter > 0);
      }

      [TestMethod]
      public void ShouldNotFailOnInvalidPath()
      {
         var searchList = new List<SearchSet>();
         // The bad guy.
         var searchSet = new SearchSet(new DirectoryInfo(@"X:\"), SearchOption.AllDirectories);
         searchList.Add(searchSet);
         const bool ignoreExceptions = true;
         const string filenamePattern = "*.cs";
         const string contentToSearchFor = null;
         var parallelFileSearcher = new ParallelFileSearcher(ignoreExceptions);
         parallelFileSearcher.CallOutUnauthorizedAccess += ShowCurrentlyDeniedAccess;
         parallelFileSearcher.FoundFile += NewFileFoundHandler;
         List<FileInfo> files = parallelFileSearcher.FindFiles(
            searchList,
            filenamePattern,
            contentToSearchFor,
            FindMode.FindAll);
         Assert.IsTrue(files.Count == 0);
      }

      #region Helpers

      private static void ShowCurrentlyDeniedAccess(string fullName)
      {
         Console.WriteLine("Access denied for " + fullName);
         eventCounter++;
      }

      private static void NewFileFoundHandler(string fullFileName)
      {
         Console.WriteLine("found " + fullFileName);
      }

      #endregion
   }
}

Parallel file search, Part 3

Zum Schluß noch ein kleines Demo, das zeigt wie einfach und intuitiv der ParallelFileSearcher zu verwenden ist:

using System;
using System.Collections.Generic;
using System.IO;
using Cyrons.ParallelSearch;

namespace ParallelSearchDemo
{
   class Program
   {
      static void Main()
      {
         ShowFindFileContents();

         // Verhindert das selbsttätige Schließen des Konsolenfensters.
         Console.WriteLine("\nPress any key to terminate the program.");
         Console.ReadKey();
      }

      private static void ShowFindFileContents()
      {
         const bool ignoreExceptions = true;
         var parallelFileSearcher = new ParallelFileSearcher(ignoreExceptions);
         parallelFileSearcher.FoundFile += FileFoundHandler;
         parallelFileSearcher.CallOutUnauthorizedAccess += ShowCurrentlyDeniedAccess;
         
         // Make a container for the search sets.
         List<SearchSet> searchSets = new List<SearchSet>();

         // Generate a search set...
         var searchSet = new SearchSet(
            new DirectoryInfo(@"H:\VS2008\Lab\CyronsFramework"),
            SearchOption.AllDirectories);
         // ...and add it to the container.
         searchSets.Add(searchSet);

         // Generate another search set...
         searchSet = new SearchSet(
            new DirectoryInfo(@"I:\Developer\VS2008\Projects\Demos\Eigene"),
            SearchOption.AllDirectories);
         // ...and add it to the container.
         searchSets.Add(searchSet);
         
         // Define search-parameters.
         string filenamePattern = "*.cs";
         string contentToSearchFor = ""; // Could also be null.

         // Start the search with those parameters.
         Console.WriteLine("Search in progress. Please wait...");
         List<FileInfo> fileInfos =
            parallelFileSearcher.FindFiles(
            searchSets, filenamePattern, contentToSearchFor, FindMode.FindAll);
         // Do something with fileInfos like, for instance, delete those files.
      }

      static void ShowCurrentlyDeniedAccess(string fullName)
      {
         Console.WriteLine("Access denied on " + fullName);
      }

      static void FileFoundHandler(string fullFileName)
      {
         Console.WriteLine(fullFileName);
      }
   }
}

/* Sample Output:
g:\projektinterkosmos\interkosmos\codecontractstrials\codecontractstrials\Program.cs
g:\projektinterkosmos\interkosmos\codecontractstrials\codecontractstrials\SomeClass.cs

h:\vs2008\lab\decoratorversuch\decoratorversuch\Program.cs

i:\developer\vs2008\projects\demos\eigene\collectiveeventcontract\eineventvieleklassen\CollectiveEventArgs.cs
i:\developer\vs2008\projects\demos\eigene\collectiveeventcontract\eineventvieleklassen\CollectiveEventContract.cs
i:\developer\vs2008\projects\demos\eigene\collectiveeventcontract\eineventvieleklassen\Program.cs

Press any key to terminate the program.
*/

Parallel file search, Part 2

ParallelFileSearcher-Klasse
Das Herzstück des Ganzen.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Permissions;
using System.Threading;

namespace Cyrons.ParallelSearch
{
   /// <summary>
   /// Sucht Dateien und/oder nach bestimmtem Inhalt in Dateien.
   /// Es können beliebig viele Suchstrings übergeben werden.
   /// Diese werden nach Laufwerksbuchstabe gruppiert.
   /// Alle Gruppen werden parallel durchsucht.
   /// Somit bietet diese Klasse eine optimale Performance.
   /// </summary>
   public class ParallelFileSearcher
   {
      #region Delegates

      /// <summary>
      /// Kapselt eine Methode, die aufgerufen wird, wenn die gesuchte Datei gefunden wurde.
      /// </summary>
      public delegate void FoundFileHandler(string fullFileName);

      /// <summary>
      /// Kapselt eine Methode, die aufgerufen wird,
      /// wenn versucht wurde, auf ein Verzeichnis zuzugreifen,
      /// für das keine Zugriffsberechtigung besteht.
      /// </summary>
      public delegate void CurrentAccessDeniedHandler(string fullName);

      #endregion
      #region Events

      /// <summary>
      /// Tritt ein wenn die gesuchte Datei gefunden wurde.
      /// </summary>
      public event FoundFileHandler FoundFile;

      /// <summary>
      /// Tritt ein wenn für den Zugriff auf ein Verzeichnis oder eine Datei keine Berechtigung besteht.
      /// </summary>
      public event CurrentAccessDeniedHandler CallOutUnauthorizedAccess;

      #endregion
      #region Fields

      private List<string> fileList = new List<string>();

      private volatile string content;
      private volatile bool found;
      private FindMode mode;
      private volatile string namePattern;

      #endregion Fields
      #region Constructors

      /// <summary>
      /// Initialisiert eine neue Instanz der <see cref="ParallelFileSearcher"/> Klasse.
      /// </summary>
      /// <param name="suppressExceptions">Wenn auf <c>true</c> gesetzt,
      /// werden IO-Exceptions ignoriert.</param>
      public ParallelFileSearcher(bool suppressExceptions)
      {
         SuppressExceptions = suppressExceptions;
      }

      #endregion Constructors
      #region Properties

      /// <summary>
      /// Ruft einen Wert ab, der angibt ob Exceptions ignoriert werden.
      /// </summary>
      /// <value>
      ///     <c>true</c> wenn Exceptions ignoriert werden; anderenfalls <c>false</c>.
      /// </value>
      public bool SuppressExceptions { get; private set; }

      #endregion Properties
      #region Methods
      #region Public Methods
      
      /// <summary>
      /// Findet Dateien und/oder bestimmten Inhalt von Dateien.
      /// </summary>
      /// <param name="searchSets">Eine Liste mit Suchsets (Instanzen der 
      /// <see cref="SearchSet"/>-Klasse.</param>
      /// <param name="filenamePattern">Ein Dateinamensmuster (z.B. *.cs).</param>
      /// <param name="contentToSearchFor">Der textuelle Inhalt, nach dem gesucht werden soll.</param>
      /// <param name="findMode">Der durch die 
      /// <see cref="FindMode"/>-Enumeration angegebene Suchmodus.</param>
      /// <returns>Eine Liste mit voll qualifizierten Dateinamen
      ///  (Instanzen der <see cref="FileInfo"/>-Klasse,
      ///  die den Suchkriterien entspricht.</returns>
      public List<FileInfo> FindFiles(
         List<SearchSet> searchSets, string filenamePattern,
         string contentToSearchFor, FindMode findMode)
      {
         var permission = new FileIOPermission(PermissionState.Unrestricted);
         permission.AllFiles = FileIOPermissionAccess.AllAccess;
         SetInstanceParameters(filenamePattern, contentToSearchFor, findMode);
         List<SearchSet> drivesAndFolders = GetDrivesAndFolders(searchSets);
         IEnumerable<IGrouping<string, SearchSet>> fileGroups = GroupDriveItems(drivesAndFolders);
         List<string> internalList = InvokeSearchBase(fileGroups);
         var fileInfos = new List<FileInfo>();
         foreach(var item in internalList)
         {
            fileInfos.Add(new FileInfo(item));
         }
         return fileInfos;
      }

      #endregion
      #region Private methods

      private static List<SearchSet> GetDrivesAndFolders(
         IEnumerable<SearchSet> searchSets)
      {
         var drivesAndFolders = new List<SearchSet>();
         foreach(var item in searchSets)
         {
            drivesAndFolders.Add(new SearchSet(item.DirectoryInformation, item.Recursive));
         }
         return drivesAndFolders;
      }

      private void GetFiles(IEnumerable<FileInfo> files)
      {
         // Wenn keine Content-Angabe vorhanden ist, soll der Inhalt der Files nicht durchsucht werden.
         if(!string.IsNullOrEmpty(content))
         {
            SearchFileContents(files);
         }
         else
         {
            found = true;
            foreach(var file in files)
            {
               fileList.Add(file.FullName);
               if(FoundFile != null)
                  FoundFile(file.FullName);
            }
         }
      }

      private void SearchFileContents(IEnumerable<FileInfo> files)
      {
         foreach(var file in files)
         {
            StreamReader stream = null;
            try
            {
               stream = file.OpenText();
               string fileContent = stream.ReadToEnd();
               if(fileContent.Contains(content))
               {
                  found = true;
                  fileList.Add(file.FullName);
                  if(FoundFile != null)
                     FoundFile(file.FullName);
                  if(mode == FindMode.FindOne)
                  {
                     stream.Close();
                     stream.Dispose();
                     break;
                  }
               }
            }
            catch(IOException)
            {
               if(!SuppressExceptions)
                  throw;
            }
            catch(UnauthorizedAccessException)
            {
               if(!SuppressExceptions)
                  throw;
               if(CallOutUnauthorizedAccess != null)
                  CallOutUnauthorizedAccess(file.FullName);
            }
            finally
            {
               if(stream != null)
               {
                  stream.Close();
                  stream.Dispose();
               }
            }
         }
      }

      private void GetFileInfos(SearchSet container, IEnumerable<DirectoryInfo> directories)
      {
         foreach(var directory in directories)
         {
            try
            {
               FileInfo[] files = directory.GetFiles(namePattern, container.Recursive);
               if(files.Length > 0)
                  GetFiles(files);
            }
            catch(UnauthorizedAccessException)
            {
               if(!SuppressExceptions)
                  throw;
               if(CallOutUnauthorizedAccess != null)
                  CallOutUnauthorizedAccess(directory.FullName);
            }
            catch(IOException)
            {
               if(!SuppressExceptions)
                  throw;
            }
         }
      }

      private static IEnumerable<IGrouping<string, SearchSet>> GroupDriveItems(
         IEnumerable<SearchSet> folders)
      {
         IEnumerable<IGrouping<string, SearchSet>> query =
            folders.GroupBy(drive => drive.DirectoryInformation.Root.ToString(), container => container);
         return query;
      }

      private void InvokeSearchByFileGroups(
         IEnumerable<IGrouping<string, SearchSet>> fileGroups,
         AutoResetEvent[] threadReadyEvents)
      {
         int counter = 0;
         foreach(IGrouping<string, SearchSet> fileGroup in fileGroups)
         {
            threadReadyEvents[counter] = new AutoResetEvent(false);
            IEnumerable<SearchSet> containers =
               from items in fileGroup
               select items;
            object parameters =
               new SearchTaskParameters(containers, threadReadyEvents[counter]);
            if(!found)
               ThreadPool.QueueUserWorkItem(Search, parameters);
            counter++;
         }
      }

      private List<string> InvokeSearchBase(
         IEnumerable<IGrouping<string, SearchSet>> fileGroups)
      {
         var threadReadyEvents = new AutoResetEvent[fileGroups.Count()];
         InvokeSearchByFileGroups(fileGroups, threadReadyEvents);
         WaitForThreads(threadReadyEvents);
         return fileList;
      }

      private void Search(object parameters)
      {
         if(found && mode == FindMode.FindOne)
            return;
         // Container auspacken -->>
         IEnumerable<SearchSet> folderGroup;
         AutoResetEvent doneEvent;
         UnpackParameterContainer(parameters, out folderGroup, out doneEvent);
         // <<--
         // Schleife für Folder innerhalb eines Laufwerks.
         foreach(SearchSet container in folderGroup)
         {
            var dir = new DirectoryInfo(container.DirectoryInformation.FullName);
            if(!dir.Exists)
               break;
            dir.GetAccessControl();
            var directories = new DirectoryInfo[] { };
            try
            {
               directories = dir.GetDirectories();
            }
            catch(UnauthorizedAccessException)
            {
               if(!SuppressExceptions)
                  throw;
               if(CallOutUnauthorizedAccess != null)
                  CallOutUnauthorizedAccess(container.DirectoryInformation.FullName);
            }
            GetFileInfos(container, directories);
         }
         doneEvent.Set();
      }

      private void SetInstanceParameters(
         string filenamePattern, string contentToSearchFor, FindMode findMode)
      {
         if(string.IsNullOrEmpty(filenamePattern))
            throw new ArgumentException("filenamePattern fehlt.");
         namePattern = filenamePattern;
         content = contentToSearchFor;
         mode = findMode;
      }

      private static void UnpackParameterContainer(
         object parameters, out IEnumerable<SearchSet> folderGroup,
         out AutoResetEvent doneEvent)
      {
         folderGroup = ((SearchTaskParameters)parameters).Containers;
         doneEvent = ((SearchTaskParameters)parameters).DoneEvent;
      }

      private void WaitForThreads(AutoResetEvent[] threadReadyEvents)
      {
         if(mode == FindMode.FindAll)
            WaitHandle.WaitAll(threadReadyEvents);
         else
            WaitHandle.WaitAny(threadReadyEvents);
      }

      #endregion Private methods
      #endregion Methods
   }
}

Parallel file search, Part 1

Parallele Dateisuche? Natürlich macht es keinen Sinn, auf einem Laufwerk verschiedene Ordner gleichzeitig zu durchsuchen. Die Parallelität geht durch die Hardware-Struktur flöten – viel schlimmer noch, die Suche würde verlangsamt, weil der Laufwerks-Controller den Schreib-/Lesekopf unnötig viel hin und her bewegen würde. Diesen Umstand habe ich berücksichtigt. Nur Suchanfragen auf unterschiedlichen logischen Laufwerken werden wirklich parallel durchgeführt (wobei aber darauf zu achten ist, daß eine parallele Suche auf verschiedenen Partitionen einer physikalischen Festplatte ebenfalls keinen Geschwindigkeitsvorteil bringt). Suchstrings für gleiche Laufwerke werden dagegen sequentiell abgearbeitet. Bei der Angabe der Suchkriterien muß der Anwender darauf aber keine Rücksicht nehmen. Diese Gruppierungen werden automatisch unter der Haube vorgenommen.
Wie funktioniert nun das Ganze?
Es wird, wie gewohnt nach einer Datei gefragt (die üblichen Wildcards sind natürlich erlaubt) und es wird angegeben wo gesucht werden soll. Es muß aber, anders als bei Standard-Suchprogrammen, möglich sein, mehrere Orte für die Suche anzugeben. Das wird mittels sogenannter SearchSets organisiert. Dazu später mehr.
Des weiteren kann ich mir zwei verschiedene Szenarien für eine Suche vorstellen.

1. Man sucht nach einer bestimmten Datei und weiß daß sie nur an einem Ort sein kann, nur an welchem ist nicht klar. Oder man weiß eine Datei an verschiedenen Orten, wobei die Datei aber gleich ist. Nun wäre es in beiden Fällen doch sinnvoll wenn die Suche abgebrochen würde, sobald die Datei gefunden wurde.

2. Man sucht alle Vorkommen einer bestimmten Datei, um z.B. redundante Dateien zu eliminieren. Hier macht es Sinn daß die Suche bis zum Ende durchläuft und alle Vorkommen zurück gibt.

Für diese Szenarien gibt es die FindMode-Enumeration. Darüber kann angegeben werden, ob alle Suchtasks abgebrochen werden sollen, sobald einer die gewünschte Datei gefunden hat, oder ob alle Tasks ihre Aufgabe bis zum Ende durchziehen sollen.

Hier nun die Komponenten von ParallelFileSearch im Einzelnen:

Die FindMode-Enumeration
Beschreibung siehe oben.

namespace Cyrons.ParallelSearch
{
   /// <summary>
   /// Gibt an ob die Suche nach dem ersten Fund abgebrochen werden soll,
   /// oder ob die Suche fortgeführt werden soll bis alle Pfade durchsucht sind.
   /// </summary>
   public enum FindMode
   {
      /// <summary>
      /// Stoppt die Suche wenn die erste Datei in einem der angegebenen Ordner gefunden wurde,
      /// die dem Suchkriterium entspricht.
      /// </summary>
      FindOne,
      /// <summary>
      /// Findet alle Dateien in allen angegebenen Ordnern, die dem Suchkriterium entsprechen.
      /// </summary>
      FindAll
   }
}

SearchSet-Klasse
Diese Klasse hält die unterschiedlichen Pfadinformationen der Suchanfragen und die Suchoption für jede Anfrage (Rekursiv oder nicht).

using System.IO;

namespace Cyrons.ParallelSearch
{
   /// <summary>
   /// Stellt einen Suchset, bestehend aus dem zu durchsuchenden Pfad
   /// und der Angabe, ob dieser Pfad rekursiv (inklusive Unterordnern) durchsucht werden soll.
   /// </summary>
   public class SearchSet
   {
      #region Constructors

      /// <summary>
      /// Initialisiert eine neue Instanz der <see cref="SearchSet"/> Klasse.
      /// </summary>
      /// <param name="directoryInfo">Eine Pfadangabe in Form einer 
      /// <see cref="DirectoryInfo"/>-Instanz.</param>
      /// <param name="recursive">Eine <see cref="SearchOption"/>-Enumeration,
      /// die angibt ob der Pfad rekursiv durchsucht werden soll.</param>
      public SearchSet(DirectoryInfo directoryInfo, SearchOption recursive)
      {
         DirectoryInformation = directoryInfo;
         Recursive = recursive;
      }

      #endregion Constructors
      #region Properties

      /// <summary>
      /// Ruft den aktuell zu durchsuchenden Pfad des Suchsets ab.
      /// </summary>
      /// <value>Eine Instanz der <see cref="DirectoryInfo"/>-Klasse.</value>
      public DirectoryInfo DirectoryInformation { get; private set; }
      
      /// <summary>
      /// Ruft die Suchoption des aktuellen Suchsets ab.
      /// </summary>
      /// <value>Eine der Enumerationen von <see cref="SearchOption"/>.</value>
      public SearchOption Recursive { get; private set; }

      #endregion Properties
   }
}
 

SearchTaskParameters-Klasse
ParallelFileSearch arbeitet mit dem ThreadPool. QueueUserWorkItem erwartet neben dem WaitCallback-Delegaten einen Parameter vom Typ Objekt. Da ein Suchtask aber mit mehreren Parametern gefüttert werden muß, werden diese über die SearchTaskParameters-Klasse definiert und zusammengefasst. Innerhalb eines Threads werden diese dann wieder ausgepackt. Die SearchTaskParameters-Klasse ist internal deklariert, weil sie ausserhalb des APIs nicht sichtbar sein muß.

using System.Collections.Generic;
using System.Threading;

namespace Cyrons.ParallelSearch
{
   /// <summary>
   /// Beinhaltet Parameter, die an die Suchthreads übegeben werden.
   /// <remarks>
   /// Eine Methode die über den Threadpool gestartet wird,
   /// darf nur einen Parameter vom Typ Object haben.
   /// Mittels der ParameterContainer-Klasse wird diese Klippe elegant umschifft.
   /// </remarks>
   /// </summary>
   internal class SearchTaskParameters
   {
      #region Constructors

      /// <summary>
      /// Initialisiert eine neue Instanz der <see cref="SearchTaskParameters"/>-Klasse.
      /// </summary>
      /// <param name="containers">SearchSets</param>
      /// <param name="doneEvent">Jeder Thread bekommt ein 
      /// <see cref="AutoResetEvent"/> in's Gepäck.</param>
      internal SearchTaskParameters(
         IEnumerable<SearchSet> containers, AutoResetEvent doneEvent)
      {
         Containers = containers;
         DoneEvent = doneEvent;
      }

      #endregion Constructors
      #region Properties

      internal AutoResetEvent DoneEvent { get; private set; }
      internal IEnumerable<SearchSet> Containers { get; private set; }

      #endregion Properties
   }
}
July 24

Directory.GetFiles und System Volume Information, der ewige Krieg

Schreibt man eine Suchfunktion, die rekursiv ein NTFS-Laufwerk durchsuchen soll, kommt es zu einer UnauthorizedAccessException wenn der Algorithmus bei System Volume Information ankommt. So kann man das Problem lösen:

using System;
using System.Collections.Generic;
using System.IO;

namespace UnauthorizedAccessExceptionWorkAround
{
   class Program
   {
      static void Main()
      {
         var someList = new List<string>();
         string filenamePattern = "*.txt";         
         string[] directories = Directory.GetDirectories("c:\\");
         Console.WriteLine("Searching, please wait..." + Environment.NewLine);
         foreach(string directory in directories)
         {
            try
            {               
               someList.AddRange(Directory.GetFiles(
                                    directory, filenamePattern, SearchOption.AllDirectories));
            }
            catch(UnauthorizedAccessException)
            {
               Console.WriteLine("Skipped directory {0}", directory);
            }
         }

         Console.WriteLine("=======================================================");

         if(someList.Count > 0)
         {
            Console.WriteLine(filenamePattern + " found at:");
            foreach(string item in someList)
            {
               Console.WriteLine(item);
            }
         }

         // Verhindert das selbsttätige Schließen des Konsolenfensters.
         Console.WriteLine("\nPress any key to terminate the program.");
         Console.ReadKey();
      }
   }
}

Im Catch-Block könnte z.B. auch ein Event gefeuert, daß eine darüber liegende Applikation über den Vorfall informiert.
July 14

Musik kennt keine Grenzen

Mal nichts über Software-Entwicklung, dafür aber mindestens genau so wichtig!

http://vimeo.com/moogaloop.swf?clip_id=2539741

April 10

irbi.de Test bestanden

Zitat:
"IRBI steht für Internet-Risk-Behaviour-Index. Mit IRBI lernen Sie auf einfache und spielerische Weise, im Internet Gefahren zu vermeiden. Sie erproben, wie Sie sich online angemessen verhalten können."

Um den IRBI-Test zu machen, muß man sich erst einmal mit seiner Email-Adresse registrieren. Nun, wie jeder weiß, sollte man seine persönlichen Daten nicht einfach so in's Internet streuen - dazu gehört auch die Email-Adresse! Darum habe ich mich nicht registriert und den Link zu irbi.de in die Tonne getreten. Test bestanden! ;-P

March 20

IE8 schlägt Crack für Office 2007 vor!

Habt ihr schon den neuen Internet Explorer 8 installiert? Er bietet ein paar neue Funktionen. Dazu gehört "Vorgeschlagene Sites". Besucht man eine beliebige Webseite, schlägt diese Funktion weitere Seiten mit ähnlichem Inhalt vor, die für den User evtl. interessant sein könnten. Eben war ich auf InfoQ und klicke auf die Funktion. Seht selbst was dabei herausgekommen ist. :-D
 
 
March 12

Microsoft Certified Professional

Seit heute bin ich einer. :-)

MCP(rgb)

January 08

Korrektur mit derben Stilblüten!

Ich bin gerade auf eine Korrekturseite des Trainingsbuches zu 70-528 gestoßen. Das haut dem Fass die Krone in's Gesicht!
Nur ein Beispiel:
 

Page 98: &lt;Beschriftung&gt; kann außerdem als einen Tag &lt;Beschriftung&gt; gerendert werden

Lesen Absatz die Sekunde zu dem letzten Satz der Sekunde auf Seite 98:
 
Alles klar???
Mehr solche Klopper gibt es hier:
Lesen und Kopf schütteln oder lachen - das ist Geschmacksache.

Na sowas! Is ja 'n Ding!

 
"VSTO 2005 verwendet dasselbe Sprachpaket wie VSTO 2005."
 
Etwas herunterscrollen. Es steht in der ersten Hinweisbox.
Die englische Variante der Seite enthüllt dann aber daß:
 

"VSTO 2005 SE uses the same language pack as VSTO 2005."

Ich sag's ja: MSDN ja bitte, aber nur die englische Version!

November 13

Camtasia Studio gratis!

Camtasia Studio ist, kurz gesagt, ein Screen-Rekorder. Alles was man auf dem Desktop sieht, kann aufgenommen werden. Alle Microsoft-Webcasts wurden mit Camtasia Studio gemacht. Wer Interesse an dieser Software hat, wurde aber meistens vom Preis abgeschreckt. Das ist jetzt vorbei!

Auf der aktuellen Ausgabe der c't (Ausgabe 24 vom 10.11.2008) befindet sich eine Jubiläums-DVD (25 Jahre c't). Hier findet man neben jeder Menge anderer Software-Vollversionen eben auch Camtasia Studio. OK, mit Version 3.02 ist es nicht die allerneuste Version (aktuell ist 5.1), aber sie erfüllt voll ihren Zweck.

October 18

ToolBox Items fehlen und es gibt eine ToolBox-Kategorie "#13119"

Ich mußte das jetzt zum zweiten mal erleben und darum schreibe ich hier jetzt mal die Lösung für das Problem!
Die Ursache für den Fehler ist ein zerstörter Toolbox-Cache. Diesen findet man hier (gilt für Windows XP!)

ToolBox-Cache

Falls das Bild nicht erscheint, hier noch einmal der Pfad:

C:\Dokumente und Einstellungen\BENUTZERKONTONAME\Lokale Einstellungen\Anwendungsdaten\Microsoft\VisualStudio\9.0

Das gilt für Visual Studio 2008. Wer VS2005 benutzt, ersetzt 9.0 durch 8.0.

Man sieht vier versteckte Dateien, die alle mit "toolbox" anfangen. Falls Visual Studio noch läuft, dieses jetzt beenden und diese vier Dateien löschen.
Das ist schon alles. Das nächste mal wenn man in VS eine ToolBox öffnet, wird der Cache neu generiert und alles ist wieder OK.

October 13

Unbekannte Patterns (für mich)

Wow, was hab ich denn da gerade entdeckt?

http://en.wikipedia.org/wiki/Concurrency_pattern

Etliche der dort aufgeführten Patterns sind mir vollkommen unbekannt!

  • Active Object (schon mal den Begriff gelesen, aber das war's auch schon)
  • Balking pattern
  • Double checked locking pattern
  • Guarded suspension
  • Leaders/followers pattern
  • Scheduler pattern
  • Thread-specific storage
  • Reactor pattern

/me schaltet seine Helmlampe an und macht sich bereit für die Erforschung. (*Musik aus Indiana Jones*)

September 11

Auf dem Speicher gefunden

Erinnert ihr euch noch, wie es war, wenn man als Kind auf dem Dachboden gespielt und dort alte Schätze gefunden hat?
Ungefähr so fühle ich mich immer, wenn ich beim Stöbern im Internet mal wieder die eine oder andere Software-Perle für .net-Entwickler finde.
Was ich dort finde, ist bei weitem nicht alt - ganz im Gegenteil! Ob meine Funde sich am Ende vielleicht doch als Nieten herausstellen, müssen erst einige Experimente zu Tage fördern. Also ganz wertfrei möchte ich jetzt meine neuesten Fundstücke präsentieren.

Bezeichnung Hersteller
EasyAsync CodePlex (ajaishankar)
Guidance Automation Extensions
Microsoft
Guidance Automation Toolkit Microsoft
JSON.net CodePlex (James Newton-King)
Managed Extensibility Framework Microsoft/CodePlex
Web Service Software Factory Microsoft
Unity Application Block Microsoft
Rhino Mocks Ayende Rahien
 
Blogs anderer, die mich interessieren
Photo 1 of 1