Thursday, June 25, 2009

Threading Needles and the BackgroundWorker

I'm not going to lie, sometimes, I'm just so irked at myself for doing something just... stupid, or worse, implementing something that I've not gotten a complete and thorough understanding of.

And yes, I've implemented code structures which I've not understood in their entirety many, many times. I'm a consultant, it's par for the course. Half of my job is just getting a firm enough grasp of various coding scenarios that I can interpret and fix whatever error is cropping up.

Regardless, I've been so engrossed in WPF and just ensuring that things work as I expect (and hope) that I've not paid too much attention to the exact specifics of what was happening. A fact which slapped me in the face, quite hard, when I realized my current application was taking upwards of 8 seconds to save a page and advance to the next one.

8 seconds for a windows-based application, that runs entirely on the client-side of things, is a heck of a long time. It's on the far side of truly usable, IMO.

So of course, I dive into the code and restructure and reorganize and refactor and all these other nifty terms. Whole structures are modified and I shave the time which the page takes to process from 8 seconds down to 2. At least that's the time that is being reported to me. The application was still noticeably showing lag bewteen shifting views. So I watched the second hand on my watch, and despite the 2 seconds being reported, it was closer to 8 on switching.

So, I closed my eyes, exhaled, and then worked until 1 in the morning hunting for the reason.

And I found it.

The page upon advancing, reported that fact to the parent form, which then passed a command to a secondary form to refresh the overview of the structure being operated upon. It was this refresh which was taking nearly 8 seconds.

But that confused me, as I thought that I had made that multi-threaded.

And while doing various readings is when I realized the cold hard truth about a bit of technology that I had utilized.

Basically, I had mis-read the role that Dispatcher has in multi-threaded systems. My thought, and I'm still not sure how I came to this one, was that by Invoking a function against the Dispatcher, it performed its tasks in a separate thread.

Boy, was I wrong.

And in case anyone is wondering, the 60,000 foot view is that the Dispatcher.Invoke, attaches the given function to the UI thread to allow safe access of the form's controls.

I felt the need to bang my head against the closest hard surface. Luckily for me, my desktop is somewhat covered in papers and books which aren't quite as hard as the wood surface itself.

So, with the cold, hard realization that Dispatcher.Invoke wasn't doing what I thought (which was spawn off a method to a new thread), I went forth to find something that would do it effectively.

And thus, I found the BackgroundWorker class.

This is my new best friend.

Here's how the BackgroundWorker works. You instantiate it as a new object, and then you set up a couple of event handlers for it. There's three possibilities:

  1. DoWork
  2. ProgressChanged
  3. RunWorkerCompleted
The way those work are simple, in the DoWork handler, you perform the actual functionality that you're trying to do. If you wish to report progress back to the user, then you tell the object that you want to report some progress (which takes an integer representing the percentage down, and an object that can be basically anyother data you desire).

The BackgroundWorker reports being done whenever it reaches the end of the DoWork handler. And again, there's an object that gets passed around from one portion to the other for you to play with.

Then of course, I ran up against the need to be able to spawn up multiple instances of these things. Well, an array of BackgroundWorkers sounds like a horrible, horrible idea, so I once again turned to my research skills.

And thus found the Thread Pool.

Again, it's an object which you can create, and then utilize to spawn off a function (in this case passed as a delegate) in a separate thread.

The thing about it is, that it's a smart object and watches how much the CPU is being utilized and determines whether to create a new thread, or wait for one of its current ones to finish as you add things to it for processing.

The bad news is that there's nothing that tells you when an item is finished. Therefore, you must use Dispatcher.Invoke in order to pass information back to the user interface.

Now, watch that I've learned all that hassle, that Microsoft will go and change everything in .NET 5 or something silly like that.

Thursday, April 30, 2009

Sometimes I Wonder...

You know, I sometimes wonder about the intellectual capabilities of some of my peers. Specifically, my peers in the programming/software industry. Or at least their Marketing Masters.

The reason I wonder is that on occasion, you'll get things just named stupidly. Utterly, and hopelessly stupidly.

A while back, one of my friends passed a contact to me that needed some help fixing his website. He had gotten it built, and then the development company he had used more or less disappeared on him. So, I got his issues straightened out, and his site was back in business, and he was happy.

Fast forward just under two years to this evening. I got a call from him, and it appears that his site was experiencing some major issues. For whatever reason, ASP.NET was not loading the DLLs it needed to run, and that was throwing configuration errors, and stopping processing on that yellow-screen of annoyance that anyone who has developed in ASP.NET has seen at least once.

The first thing I did was FTP into the server to ensure that the DLLs still existed, which they did. Then I checked on ensuring that the web.config file was well formed (though if it wasn't it wouldn't have worked ~20 months ago when I was working on it).

Finally, deciding that maybe the DLLs in question had just gotten corrupted or expired or something, I found the first one, named FreeTextBox.dll and went hunting. It didn't take long for Google to turn up the results, at which time, I found myself kind of staring at the screen in abject... flabergastion.

The reason why is captured there in that image perfectly.

This is a product named FreeTextBox with the tagline "The no. 1 free ASP.NET HTML Editor."

And then you have two different licenses that you can purchase to legally use this control, one costing roughly $50 and the other $200.

Now sure, the thing is a free download, and it's only "extra" features that you're getting when you get a license, but you've got to really read the website to discover that particular salient factoid.

Just the sheer... gumption one has to charge for a product who uses FREE in its very name is just... astounding.

Ah well, I'm off to see if I can track down the cause of those missing DLLs...

Tuesday, April 28, 2009

Finally Released the new version of eComic

I’ve pushed out the latest release of eComic, and it is available for download here: http://ecomic.codeplex.com/

This is a fairly drastic overhaul of the entire system, as I made some changes on how I want things to work.

ecomicscreen001The primary change is that I’ve decided against allowing people to open multiple comic archives within a single instance of the application (though multiple instances can run at one time). This is coupled with a much more efficient memory management system for the images which only loads them as they’re needed for the thumbnail display.

Those that download the source code will notice that there’s a new project in the system, one which will produce a second application whose sole purpose will be the creation of the various archives.

The final bit of interest in this release is that I’ve finally got the Graphic Novel Archive (gn*) format up and working. What this is, is an archive of the CBR archives. The files can be either read sequentially based upon their names, or a manifest file can be included (XML) which lists the order in which they should be read.

I even generated an XSD for it:

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="chapter">
<xs:complexType>
<xs:attribute name="sequence" type="xs:NMTOKEN" use="required" />
<xs:attribute name="fileName" type="xs:NMTOKEN" use="required" />
</xs:complexType>
</xs:element>

<xs:element name="graphicNovel">
<xs:complexType>
<xs:sequence>
<xs:element ref="chapter" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>



I’m excited about the GNR format as these reading lists concepts are the primary reason that I decided to create eComic. As an aside, the editor portion of the software will also generate the GNR files.


Thos looking for content for eComic can find free (and sometimes in the public domain) copies of comics. Here’s two sites:

Thursday, April 9, 2009

My New PC!

Well it finally arrived on Tuesday evening. I've gotten my new pc, and all I have to say is:

POWAH! UNLIMTED POWAH!!!
I know, I know, I'm an utter geek. But hey, this thing is awesome. It has 8GB of RAM (DDR3 SDRAM @ 1066MHz), and an i7-920 Intel processor (8MB L2 Cache, 2.66GHz) featuring 8 cores of sweet, sweet bit-crunching goodness. Couple this with an ATI Radeon HD 4670 video card with 512MB of onboard memory dedicated just to the GPU, and you can imagine the utter...snappiness with which this thing rocks at.


Just look at that Process tab. Good times.

Sunday, February 15, 2009

Sometimes, I'm Just Soooo Smart

The CD-ROM on my PC is dead. It's been dead for a couple months, and I've not really thought about it, as I've been planning on getting a new one soon. Well, it became an issue, as I purchased some tax software to do my taxes--and when I went to install it I came up hard against that issue.

Not a problem, I'm a geek--more importantly, I'm a software geek.

As such, I have all sorts of things at my disposal in order to bypass this--namely, a bit of software called "Virtual CloneDrive." What this guy does is it allows me to mount a ISO image as virtual drive.

So, being the geek that I am, I went down to the office, and used the CD-ROM on my PC there to make an image of the disc and shoved it onto my thumb drive so that I could install it at home.

But, that's not the good part of my story for today; that's not the part of the story which warrants the title for this part.

No, the good part is that AFTER all that hassle, of making a special trip, across town to my office (a nearly 30 minute drive mind you) on the way home, me an the wife stopped at Circuit City to check out if there were any good deals left as there were just a few days before they closed for good (two Saturdays ago, I got a 1TB Network HDD for half price).

Well, while there, I found a game that I had thought interesting, but not worth the $40 price tags that games usually come with. Well, it was only a bit over $6, and it well was worth that particular purchase price, so I got it.

So, then me and the wife left, and she started teasing me about purchasing the game. Mainly, taking the stance that it's been a few months since I've bothered playing a game, and here I was getting a new one.

At which time I was about to tell her that I hadn't played a game recently because my drive was dead.

And the realization of the entire reason for the trip to the office that morning once more slammed into me.

It was enough to make me literally stumble in a step.

All, in all, a sequence of events which my beloved wife found infinitely amusing.

Monday, February 2, 2009

Redesigning the Road System

This little design of mine scares me; but the sad thing is that I can see it fully realized at this moment, with the current levels of technology. What am I talking about? Why self-driving cars of course.

Consider these pertinent facts:

  • Update-Ready GPS devices consistently tell us where we are, and where we need to go
  • WIFI Network bandwidth is in massive supply thanks to the recent influx of bands due to the broadcast television turnover to digital
  • Cars come with the following technology today:
    • onboard computers
    • radar (that fun little beep when you back up)
    • video cameras
This design will need to make a few modifications to the cars rolling off the presses as new vehicles (but the components needed could just as easily be added in as aftermarket devices) as well as changes to the infrastructure. The good part of this change is that the devices would not affect or otherwise impair existing vehicles--though the system would run more efficiently if every vehicle was tied into the network as a whole.

Changes To Cars
The main changes to the car, outside of the computer brain needed to interpret the incoming data and to do the actual driving (which isn't that far fetched if one takes a look at how well airplane auto-pilots perform these days) would include additional radar devices, RFID readers (4 distinct ones) and a wireless network adapter.

The Radar devices are there to tell the car if something is in front of it, or behind it and how close. Quite simple; after all, no matter how many vehicles you get onto the network, a tree falling across the road can still kill someone if their car hits it at 55MPH.

Now, the RFID chips, these would need to be positioned at the four corners of the car, that way the computer can tell the direction the car is traveling in, and basically ensure that it remains within the lane. We want all four corners to have them so that the car will know when it has completed moves such as merging into a new lane, or turning a corner. More about the RFID chips later though.

The final addition to the cars would be a wireless network adapter. Basically, this would be used to allow the car to talk to all the other nearby cars on the road. If the car intends to move over a lane, it sends out the signal across the network, and pings the other vehicles on their precise location so that it knows whether it needs to speed up or slow down, or just to go ahead and merge into the next lane.

Changes to the Road
The primary change to the roadbed is that ever one of those little reflectors would need to be a RFID chip with the exact lat/long coordinates embedded within it. Basically, it would be a little chip that constantly screamed "Here I am!"

Another change, that would not be needed, but would be useful, would be to have network cabling running down the entire branch of the road. Then you could routine put up a wireless repeater, which would allow the vehicles to ping the internet as they drove. This would be useful for such things as real-time weather updates for the next X miles of road, or to allow the driver to send a command to their online stove to start cooking, or even send an email or an SMS message to the driver's spouse or kids or parents when the car gets X miles from the house.

Drawbacks
As in anything, there are drawbacks. The little things like people will still do stupid things in their cars, and older vehicles that have not been upgarded would not be a part of the vehicle's network mesh (though every smart-car would be able to locate and broadcast the location of the dumb-car via it's radar systems).

Beyond that, the issues I have with this design are privacy. First and foremost, is that it's a small change in those RFID chips to go from Broadcast to Receive/Broadcast. Imagine, every day, a thousand cars fly past those little devices. Now, if each car is network aware, then that means each car has a MAC address. A MAC address that would probably be tied into the VIN number and license plates of the vehicle. Now, if that chip is reading that MAC address as the cars go by, then someone with a reader can come by and snatch up all of the traffic metrics for a road. While such data can be useful (for example, accurate counts of high-traffic areas, and points of time which drivers experience excessive congestion) it can also be put to nefarious uses (tracking where people go).

The next issue is the fact that each of these cars is a small, mobile network, attached to a greater, wide-area network which is the entire set of cars in a given area. What's stopping the government from monitoring everything you do in your vehicle, from a remote point.

I know, and hope, that this was nothing more than an exercise in intelligent design and capabilities, but I actually do kind of fear this system getting installed and put into use. After all, as I said, we have the technology as is to do everything I listed in this article.

Monday, January 26, 2009

WPF & The Data Context

I love me some WPF, though I have to admit that when I first started, I did things wrong. I held onto my old-fangled way of doing things as much as possible, and only hesitantly took steps out into the deep waters where the big fish play.

Which means it wasn't until I started slapping my head up against the datagrid that I first really looked at the Data Context object.

Afterwards, I thoroughly smacked my forehead repeatedly. Imagine it, thousands of lines of code, and a good bit of it can be culled if I had the time or energy to go retrofit it all with Data Contexts.

Now, for the uninitiated, a Data Context is just as it says: an object which holds the data that a WPF form uses as a display source for its UI elements.

So, looking at the eComic application; every CBR file can potentially have a xml fragment/document within it which contains details about that CBR file. This would contain information such as number of pages, page descriptions, artists, writers, series title, issue title, chapter number, volume number, etc. All the important meta data, that's truly unimportant to reading an actual issue of a comic.

So, what we have is this standard kind of programming structure: UI - Business Logic - Data Store. These three things are supposed to work together, while remaining within their own domains and never quite touching. Now, the way I had always done this in the past is that either the UI was aware of the Logic layer or the other way around.

In my mind, they had to be in order to talk to one another. It just made sense.

Enter the joys of WPF.

What happens here is that I have my UI and Data Store as usual. Then I have my business logic, and within it is this Data Context object which holds the data from the data store that I am currently interested in.

Taking eComic, let's keep it simple, and only deal with Series Title and Issue Title.

What happens is that I'll create a class, which exposes properties for each of those elements. I can either have the class populate itself, or populate it elsewhere, that's fundamentally irrelevant to the discussion. All we need is the class itself.

The next step is that when we're building our UI in XAML, we need to utilize the Binding command.

So, if have a Series Title textbox that I want to display in the UI, I'd have this code:

<Textbox Name="SeriesTitle" Margin="5,5,5,5" Text="{Binding SeriesTitle}" />

Now, here's where it gets real fun. All I have to do to get the SeriesTitle from my Data Context to that UI is just assign it to that SeriesTitle's parent container's Data Context. Any changes to the textbox will instantly be promulgated back to the Data Context so I never have to manually check the textbox or any other UI element. All I have to look at is the Data Context. This is accomplished via the PropertyChanged event, which is brought to us courtesy of the INotifyPropertyChanged interface found in the System.ComponentModel Namespace.

So, I'm using the Data Context, and now I have to determine if I need to save changes to the data store based on user input or not.

It is important here that I not waste CPU cycles in saving data that hasn't changed. So, this is where the PropertyChanged event really comes into play.

First off, since we were smart, we implemented access to the PropertyChanged event via a OnPropertyChanged method which takes in the name of the property being changed.

Once the event has been risen, then we would set an IsDirty flag to true. Then, we can check that flag to determine if we need to attempt to save the data or not.

This Data Context example can be viewed here:


   1:  Public Class eComicDataContext

   2:         Implements INotifyPropertyChanged

   3:   

   4:  Private _isDirty As Boolean

   5:  Private _series As String

   6:  Private _issue As String

   7:   

   8:  Public Sub New()

   9:         SeriesTitle = ""

  10:         IssueTitle = ""

  11:         IsDirty = False

  12:  End Sub

  13:   

  14:  Public Property SeriesTitle() As String

  15:         Get

  16:                Return _series

  17:         End Get

  18:         Set(ByVal value As String)

  19:                _series = value

  20:                OnPropertyChanged("SeriesTitle")

  21:         End Set

  22:  End Property

  23:   

  24:  Public Property IssueTitle() As String

  25:         Get

  26:                Return _series

  27:         End Get

  28:         Set(ByVal value As String)

  29:                _series = value

  30:                OnPropertyChanged("IssueTitle")

  31:         End Set

  32:  End Property

  33:   

  34:  Public Property IsDirty() As Boolean

  35:         Get

  36:                Return _isDirty

  37:         End Get

  38:         Private Set(ByVal value As Boolean)

  39:                _isDirty = value

  40:         End Set

  41:  End Property

  42:   

  43:  Public Sub ResetDirtyBit()

  44:         Me.IsDirty = False

  45:  End Sub

  46:   

  47:  Public Sub OnPropertyChanged(ByVal propertyName As String)

  48:         RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))

  49:         IsDirty = True

  50:  End Sub

  51:   

  52:  Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

  53:  End Class