Thursday, July 06, 2006

Well, my quiz from about 3 weeks ago didn't really elicit the response I was hoping.  However, for the benefit of those that have been wondering, I've decided to post my results.  As you may recall, the quiz was a seemingly simple one: to determine if a directory is empty (e.g. that it has no contents: files or folders).

Almost alarmingly, there is nothing built into the .NET Framework for such an operation.  One solution (which was proposed in the preceding post) is to evaluate the results of the Directory.GetDirectories() and Directory.GetFiles() methods.  Doing this, however, forces the runtime to enumerate all of the contents of the folder only to be discarded once we see determine that there were indeed files and folders.  There's no way (that I've seen) to call a Directory.IsEmpty() method or even a Directory.GetDirectoryCount() or Directory.GetFileCount() (though those might also force enumeration internally if they existed).

I have a solution that I propose as being better than the contrived example from the original post, though it is a little lengthier.  I haven't run any stringent analysis on its performance though I have to believe that it's faster than the method that relies on enumeration.  I also imagine that its performance will be substantially better over a network.

This solution relies on some built-into-the-Windows-kernel API calls to walk the contents of a directory (for there isn't any functionality there either to see if a directory is empty).  As soon as a file or folder is found it quits and indicates that the folder is non-empty.

private static bool isDirectoryEmpty(string directory) {
   IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
   NativeMethods.WIN32_FIND_DATA findData;
   IntPtr handle = IntPtr.Zero;
  
   try {
      handle = NativeMethods.FindFirstFile(@"
\\?\" + Path.Combine(directory, "*"), out findData);
      if ( INVALID_HANDLE_VALUE == handle ) {
         // call GetLastError() here to be explicit on the reason of the failure
         // for now, simply assume that the directory is not empty.

         return false;
      }
  
      // spin on the directory until a file/directory other than the current (.) or parent (..) is found
      // if something is found, quit immediately - we've determined that the folder is not empty
      do {
         if ( "." != findData.cFileName && ".." != findData.cFileName ) return false;
      } while ( NativeMethods.FindNextFile(handle, out findData) );
     
      // no file or directory was found, so return that it is indeed empty.
      return true;
   }
   finally {
      if ( IntPtr.Zero != handle )
         NativeMethods.FindClose(handle);
   }
}

This code makes use of a NativeMethods class which I define below.  It contains the system API calls that are used to walk the directory contents.

internal static class NativeMethods {
   public const int MAX_PATH = 260;
   public const int MAX_ALTERNATE = 14;

   [StructLayout(LayoutKind.Sequential)]
   public struct FILETIME {
      public uint dwLowDateTime;
      public uint dwHighDateTime;
   };

   [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
   public struct WIN32_FIND_DATA {
      public FileAttributes dwFileAttributes;
      public FILETIME ftCreationTime;
      public FILETIME ftLastAccessTime;
      public FILETIME ftLastWriteTime;
      public int nFileSizeHigh;
      public int nFileSizeLow;
      public int dwReserved0;
      public int dwReserved1;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)]
      public string cFileName;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_ALTERNATE)]
      public string cAlternate;
   }

   [DllImport("kernel32", CharSet=CharSet.Unicode)]
   public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

   [DllImport("kernel32", CharSet=CharSet.Unicode)]
   public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

   [DllImport("kernel32")]
   public static extern bool FindClose(IntPtr hFindFile);
}

NOTE:  Please let me know if I overlooked something obvious.  I've not done stringent analysis with this code but wanted to put it out there as a proposed solution for determining that a directory is empty in an efficient manner.

Thoughts?

Thursday, July 06, 2006 7:31:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Sunday, July 02, 2006

Things have been a little insane around here lately.  I've made it a point to work on around-the-house projects.  Over the past several years we've done many: replaced the driveway, replaced the garage door, etched/painted/sealed the garage floor, installed vinyl fencing, poured approx 3800 sq feet of colored/stamped cement (including a basketball court), built an ironwood deck, built a shed, landscaped and then re-landscaped the yard, built a garden box, remodeled the master bedroom (I'm still working on the shelving and cabinetry), poured a courtyard and constructed a brick wall w/lights, and replaced the front door.

In addition to the aforementioned projects I've had this 5.5-6 year-running project of the basement.  I started it about six years ago and, amid all of the other goings-on, haven't really found the time to finish it.  Sure, I've worked on it here and there (mostly dabbling and stuff), but over the course of the past few weeks I've made it a point to actually finish that project.  Well 99.9% of my part is finally done (I just have to replace two can lights that for some reason aren't working and move a few ethernet cables).  I'll be moving my office to downstairs :-).  Primarily due to this project I've not really taken the time to blog like I have wanted to.

The drywall is getting installed this week (that's the one part of the whole project I don't like to do so I'm contracting it out).  A finished basement will add about 1300 sq feet to the house so I'm very much looking forward to that!  Plus I'll be moving my office out of an over-cramped 11'x10' bedroom to a dedicated office that is about 25'x14' + bath.  I'm very excited - this is long overdue.  I just pray that I did everything correctly and that it will all work once it's up.

Anyhow, I very much enjoy house projects, but sometimes I'm a bit overwelmed by trying to do so many at once.

Sunday, July 02, 2006 5:42:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, June 29, 2006

We had a great time tonight at the DevUtah Geek Dinner.  To set it apart from the other Geek Dinners in the past, this one was purely a dinner at Tucanos (for networking - though I'm not really sure how much networking actually went on) and a movie.  As a group we headed over to the Wynnsong Movies 12 in Provo (which is across the street from the restaurant) to watch Superman Returns.  The theatre was great in that they had reserved a whole theatre room for us.

Nate Jones and I carpooled to the event together.  I'm not sure, but I think we may have been the only ones not from Utah County at the event.  C'mon, you Salt Lake County people - let's show our support for the community and get going to these events!  Anyway, the move was loads of fun and dinner was great!

I can't wait for the next one.

Thursday, June 29, 2006 4:48:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, June 23, 2006

I had the opportunity today to give conduct a follow-up “Brownbag Session” today at SelectHealth.  Last time I gave a presention on C# whereas this time we talked about SOA (Service-Oriented Architecture) and specifically the Microsoft slant on it (e.g. tools, practices, etc).  This time, however I was a bit blind sided as I was asked late last night if I'd give the presentation.  I agreed (obviously), but I didn't have a chance to prepare any materials until this morning before work.

The presentation was pretty high level and there were no code examples - purely a discussion and “theoretical“.  Despite that, it was great fun! :)

As it turned out, the presentation went over very well was well received; the conversations were great!

Friday, June 23, 2006 7:12:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, June 21, 2006

I was performing some routine debugging today and suddenly received a very strange error; one that I had not heretofore received:

Error while trying to run project: Could not load file or assembly 'TestApplication' or one of its dependencies. The module was expected to contain an assembly manifest.

When I attempted to run the application from Explorer I was greeted with the message:

C:\SomeBuildDirectory\TESTAP~2.EXE
The NTVDM CPU has encountered an illegal instruction.
CS:0e1b IP:0173 OP:65 63 74 69 6f Choose 'Close' to terminate the application.

Needless to day, I was a bit disconcerted.  I started to dig into to see what the problem was.

The very first thing I did was Google it.  The search yielded a page that seemed to fit the bill exactly.  A user by the name of PCarrier had experienced a very similar problem.  He had developed an application in C# and was getting the same result.  As it turns out, he had a virus.  Well, that was a possibility here, but I doubted it at first.  However, I decided to see for myself what was going on.

The next thing I did was investigate my Post-Build actions - this solution has several projects each of which has its own custom Post-Build action.  Nothing seemed out of place.

I then investigated the directory system.  I noticed was that in my \obj directory, the output .exe file was the proper 13K but in the \bin directory it was 6K.  The file was being truncated!

Therefore, I opened up FileMon and started monitoring for changes to that file and sure enough I saw some very erratic behavior.  It looked strange.  Several accesses to the file in the \obj folder were correct and they allocated the file properly but when it came time to write it to the \bin folder it looked like there was some ADS (Alternate Data Stream) stuff going on which was truncating the file to it's 6K size.

I started to suspect virus so I installed the AV software (yes, I didn't have any AV software running at this juncture) and ran a scan.  Nothing turned up.  I then ran the RootkitRevealer to see if anything cropped up there - nothing out of the ordinary.  Well, that was putting my mind at ease, but I was still facing the same problem.

Strangely, after doing the AV scan and Rootkit detection the ADS activity ceased.

I decided to retrace my steps to see if anything was amiss and started again to investigate the Post-Build actions and then (finally!) something stood out at me.  I had edited the macro to copy a shared configuration file and rather than copy the file to the target directory, I had mistakenly copied the configuration file to the target path (which includes the output .exe name)

The command I had was

copy $(SolutionDir)SharedConfiguration.config $(TargetPath)

And it should have been

copy $(SolutionDir)SharedConfiguration.config $(TargetDir)

Sometimes the little things get you.  I'm thankful that it was such a simple thing and that viruses didn't enter the picture, but I kick myself that I didn't suspect that the .exe file wasn't a valid one to begin with.  I should have opened the .exe in Notepad2 and the problem would have been clear from the start.

...but I can now rest at ease.

Wednesday, June 21, 2006 6:31:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [6]  |  Trackback
 Tuesday, June 20, 2006

[Updated 06/21/2006 to include date - somehow I left that off...Thanks Nate!]

After a few month of hiatus we're back and ready to have yet another fantastic Geek Dinner.  Started back in November 2005 the DevUtah Geek Dinner has been semi-sporatic, but the next one is coming up pretty quick.

Essentially, the dinner will be held next Thursday, June 29th in Tucanos in Provo (one of my all-time favorite restaurant - better than Rodizio's Grill IMO).  This time around dinner will begin at 5:30 and will be $14.95 (down from the $17.95) which is a good deal for the fabulous food you'll receive.

Following dinner, we'll head over to the Wynnsong Theatre across the street from the restaurant to see Superman Returns (tickets are $5.50).

Full details are available here (please check them out as I'm not repeating them all here)

I'm very excited about this and am looking forward to hanging out with my fellow Utah Geeks.  PS: I'd also like to have more of a Microsoft-kool-aid-drinking presence there.  At the previous dinners (with but one exception) I have been the sole MS-devotee, everyone else is Linux/PHP/Java/Open Source/etc and while there's nothing wrong with that, I'd love to have some more diversity and have some showing from the considerable MS/.NET following there.

C'mon guys, support the cause!

Tuesday, June 20, 2006 6:29:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Monday, June 19, 2006

I got a Father's Day gift today (shipped UPS and didn't arrive ealier) that is WAY COOL!  It's still charging (it charges its battery through the USB cable), but that hasn't prevented me from playing with it a bit.

ASIDE

It ships with Windows Media Player 11 which I like a ton.  I didn't really hesitate installing it because there's something odd about the way Media Player 10 was installed on my box by Dell (no, I didn't pave my machine this time around) and it wouldn't rip music.  After installing MP11 it worked without a glitch.

It plays crystal clear music and FM radio and the synchronization between MP11 and the Clix was fantastic - and fast!

The coolest thing about it (the feature from which it name is probably derived) is its navigation.  The top panel (e.g. faceplate) pivots and clicks slightly in each direction (NSEW) which guides you through the various menus.  It's remarkably easy to navigate.

With 2GB of capacity (which is more than enough for me) and 25 hrs of battery life, I'm set!

The only complaint I have is that it is effectively useless while connected via the USB port.  In otherwords, you can't play music or navigate the menus while connected to the PC.  All interaction (synch-ing, file management, etc) is to be done from the OS via Windows Explorer or Media Player.  Even so, I'm very happy with it and can't wait to really get into using it.

 

Monday, June 19, 2006 7:29:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, June 17, 2006

I would love to have one of these.  I just stumbled upon this website today and found the OQO (pronunciation anyone?), purportedly the world's smallest Windows XP computer.  If nothing else but for the sheer “fun-ness” of having one, I'd like to try it out.  I seriously doubt I could make it a development machine what for the whole form factor, but it'd be a blast to use and test applications on.

It seems quite capable with 512 MB RAM, USB 2.0, Firewire, wireless (even though it's only 802.11b and not g), full keyboard, mouse, tablet PC support, 30 GB drive...it would simply be a cool little device.

Personally, I think the $1000 price difference between the XP Pro and Tablet versions is a bit exorbitant (maybe $300-500 would be more palatable), but I'm sure that's the niche.  I wouldn't want one without Tablet support.

I don't see me getting one (at least until I've saved my clams for a few years), but it sure would be fun to play with.

Saturday, June 17, 2006 2:45:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Friday, June 16, 2006

A buddy of mine had an interesting experience with bad customer service, after which he decided to take matters into his own hands.  After repeated attempts to contact them to no avail, he decided to write his own automated system to bombard their email server with email - at least this time it worked (I don't know if I'd recommend it as a customary practice but rather as a last resort).

When, as individuals, we get unsolicited, undesired email we refer to it as spam.  We have spam-blockers, junk mail filters, etc all aimed at eliminating spam.  However he coined the term (albeit accidentally in IM) smap.  I like it - hence the post.

smap ('smap)
n. smap·
per
v. smapped; smapping

1: n. A virtual 'slap', usually in the form of an email, intended to arouse a response.  Frequently sent en masse.
2: v. To incite via email correspondence
example: I sent some smap to customer service, let's see if they respond for once.
example: I smapped my brother today - he's been ignoring me for weeks.

Friday, June 16, 2006 4:00:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, June 15, 2006

We had the treat tonight of having Aaron Skonnard present at our Utah .NET User Group meeting.  The meeting was delayed a week so we invited the Utah County .NET User Group to attend.  I was really impressed with the turnout.  Getting a speaker like Aaron out sure pulls people out of the woodwork that haven't been there for a while. :-)

Aaron talked about BizTalk 2006 and it was great!  His presentation focused on use-cases and scenarios for BizTalk and then he delved into creating schemas, maps, and pipelines.  We then took the published assembly and set up the ports and subscriptions within the BizTalk environment.  It sure is easier now in the 2006 timeframe than when I first did this stuff in BizTalk 2000.  I kind of followed the progress of the product through the 2002, 2004, and 2006 timeframes and it sure has made great strides.

It was a great presentation and everyone had a great time.

Thursday, June 15, 2006 5:24:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback