Friday, July 24, 2009

Build Your Own CAB: Introduction

Jeremy Miller was working on a "Build Your Own CAB" series a while back. However, he does not have an intimate familiarity with CAB. I have spent 2.5 years working on a CAB-based application, with some side projects that use Castle Windsor.

Currently, I'm starting on a GUI project using Mono on Linux. It's against the CAB license agreement to use it on Linux, so I won't be using it. Instead, I'll be taking a look at the components of CAB, how to reimplement them or what replacements are readily available, the merits and deficiencies of CAB's implementation, and ways to avoid potential problems.

CAB Components


CAB consists of several components which could in theory be used separately. In practice, CAB's internal design is too scary to contemplate swapping out components, so the best you could hope for is to rip out part to use elsewhere.

Service locator


Technically, it's dependency injection, but I generally expect a dependency injection system to figure out the correct order in which to build things.

CAB's dependency injection system encourages you to have god classes that add a few hundred services to the workitem. (A WorkItem is like a Windsor container; it holds a collection of services and objects built, and handles events.) This is very procedural and does not involve actual inversion of control, as such; but it looks enough like using a full-fledged IoC container that it's reasonable to switch.

Events


CAB events are wonderful. Somewhat. They closely match Ayende's EventBroker, except for being based around EventHandler rather than Action (that is, Ayende decided his events shouldn't ever have parameters, whereas CAB decided on [object, EventArgs]).

There are some advantages to the CAB system, but there are a couple of maintenance issues as well, and a few handy features that it entirely lacks support for. On the whole, CAB events are great.

Commands


Commands are events that can be turned on or off. Not much to say about them other than that.

Items


Items are, well, any state you want to keep around. It's a place to build transient objects or store items temporarily.

I suggest only using it for building transient objects. You don't want arbitrary classes pulling stuff directly out of the workitem, since that's unnecessary coupling, so you may as well use a service with local variables instead.

Modules


A Module is just a DLL with some bootstrapping code. A CAB application will contain an XML catalog of modules that will be loaded and run on startup.

CAB Organization


In CAB, everything is part of a WorkItem. The intent is that you have a root WorkItem that represents the essential, system-wide state of your application, and you create child WorkItems on a per-task basis.

In essence, CAB uses WorkItems as a lifecycle mechanism for components. In CAB, configuration is tied to initialization. This means that creating a child WorkItem requires you to call the necessary configuration code when creating the WorkItem. This code can be relatively verbose, and this leads to the impression that CAB is slow. (Additionally, since CAB uses reflection extensively and, due to the marriage of configuration and initialization, unable to cache anything, CAB is slow. However, your IoC container will almost never be a significant bottleneck.)

In practice, child WorkItems will be used rarely. Only if you know you'll have a whole slew of items that you need to create now and destroy later -- for instance, creating a new and complex window that shares little functionality with the rest of your application-- will you resort to child workitems.

Conclusion


I've talked a bit about what CAB is. Next time, I'll discuss how a CAB application is structured, with special reference to the common pain points.

Monday, July 13, 2009

On window managers and Windows 7

I hate Windows.

There are two major reasons for this. First is the lack of a reasonable text shell like bash. Second is the window manager.

The State of the Art


There are a lot of great features that every window manager on Linux has: edge detection (provide resistance to positioning windows partially off-screen or overlapping other windows), virtual desktops (swap out all your windows for another set of windows), vertical maximize/horizontal maximize, alt-drag, always on top...

Windows XP was early enough that these features were not necessarily compulsory. Windows doesn't need to be a technology leader. Vista should have included them. Windows 7 still doesn't.

Vista/W7 tried to include a couple new features for the window manager. Edge detection? No. Virtual desktops? No. Always on top? No. Alt drag? No.

There's vertical maximize...sort of. If you drag a window to the left or right side of the screen, it's resized to take up that half of the screen.

There are two other notable features in W7's window manager.

Mouse Gestures


Make that mouse gesture. There's only one. If you "shake" a window, all other windows are minimized.

Why? When would you want this? If you only want the one window, why not maximize it? If you had virtual desktops, you could send the window to an empty desktop, or even make a new desktop and send it to that.

Perhaps someone heard about virtual desktops and misunderstood the feature entirely. They ended up with something absolutely useless.

Alt-Tab Outlining


When you're waiting in the alt-tab menu, the window you're currently waiting on will be outlined. For example, if you have Firefox maximized, and a terminal in the background in the upper left and another in the lower right, you can cycle through the alt-tab menu, and it'll show the outline of the upper left terminal when you've selected it, and the same with the lower right.

Windows 7 takes this to the logical extreme. When alt-tabbing, all windows are rendered as outlines. (Well, shadows, actually. Which is effectively the same, just harder to see.) If you have ten windows open, you have no hope of distinguishing between them, except the 1/16 scale pictures of the window that the menu provides.

It's The Team, Stupid


If anyone working on these features had sat back and thought about them even briefly, they would have realized that their implementations were worse than useless. This is a case of keeping up with the Joneses without knowing what the Joneses are doing.

Microsoft stole these features, but when you're stealing a feature, you can at least get it vaguely right. With Microsoft's resources, they should be able to surpass the features they stole. This can only be a result of gross incompetence.