Rainer's profileCyrons BlogPhotosBlogListsMore ![]() | Help |
|
|
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. October 05 DotNet Expansions frameworkFor 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:
The download package (Zip file or Visual Studio 2008 solution) contains the following items:
You can download the latest release of DotNet Expansions at or check out a copy of the project at 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. 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: 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 patternsI 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. 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;} //======================================================================
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;} } } ConclusionWell, 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ächtigkeitenNicht 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!!! 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. Assert.That(documentManager, Is.InstanceOfType(typeof(DocumentManager)), "Should create an instance of DocumentManager");
// ****************************************************************// 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 syntaxAssert.IsTrue(2+2==4); // Helper syntaxAssert.That(2+2==4, Is.True); Assert.That(2+2==4); // Inherited syntaxExpect(2+2==4, True); Expect(2+2==4); } [Test] public void IsFalse() { // Classic syntaxAssert.IsFalse(2+2==5); // Helper syntaxAssert.That(2+2== 5, Is.False); // Inherited syntaxExpect(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 syntaxAssert.Contains(3, iarray); Assert.Contains("b", sarray); //... // Helper syntaxAssert.That(iarray, Has.Member(3)); Assert.That(sarray, Has.Member("b")); Assert.That(sarray, Has.No.Member("x")); //... // Inherited syntaxExpect(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 syntaxCollectionAssert.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 syntaxAssert.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 syntaxExpect(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: and the Sandcastle help file builder from codeplex. You can also find a “Getting started” section in the Sandcastle help file builder documentation. Now let's go: Writing the sample code 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 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:
Namespace comments 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.
How to get angle brackets in a code 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) { //...}
Note: You could also use HTML-tags for the angle brackets but
July 31 Parallel file search, Part 4Hier 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 3Zum 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 2ParallelFileSearcher-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 1Parallele 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. 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 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 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 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 KriegSchreibt 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 GrenzenMal nichts über Software-Entwicklung, dafür aber mindestens genau so wichtig! April 10 irbi.de Test bestandenZitat: 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
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: <Beschriftung> kann außerdem als einen Tag <Beschriftung> gerendert werdenLesen 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. |
|
|