Tuesday, March 29, 2005
The more I write “real” software - not just demos, but things you actually have to make work for a user you'll never meet - the more I realize that you can never write just one application. Instead, you always have to write at least four:
- There's the app itself. This is the thing that the user runs.
- There's the tests. These will generally be about the same size as the app. Yes, a 20,000 line application will usually have 20,000 lines of test code.
- There's the build. Truly an application in itself (albeit usually not quite as big as the test code) it needs to deal with all the weird third party crap, odd test scenarios, and specialized hacks.
- There's the installer. If you're very lucky, this is a zipfile. Usually, you're not lucky. If not, you're looking at a significant effort to make sure it works right - uninstall, unattended install, rollback on failure partway through, blah blah blah.
Oh, and there's the documentation, too, although I didn't list that since I haven't decided yet whether it qualifies as an application or not. Probably it does.
Each of these things is a major effort in and of itself for any nontrivial project. Each needs to be written, tested, patched, documented, and supported. This also means that the app itself represents well south of 50% of the code, and probably less than 30%.
All of this just goes to justify the practice I've used for years: when someone asks me how long something will take, I estimate it in my head, and then multiply by four before telling them a number. It has worked surprisingly well.
Friday, March 25, 2005
For a long time now, the FlexWiki development team (a formal-sounding term for a bunch of guys who hack FlexWiki in their spare time) has been working towards FlexWiki 2.0. We have a fairly extensive list of features that we want to implement to get there. Some of them are done, but many of them are not. Although we build on every checkin, and make every build available to the brave, we were hoping that we would be able to jam through the remaining 2.0 features via a coherent push to get a new version of FlexWiki “blessed” as the official release sometime soon.
Last week, we came to the realization that it's still going to take some time to get to 2.0. And in the meantime, real users are having problems with build 1613 (the last “official” release), to which the answer is most often, “Oh yeah, we fixed that since November. Upgrade to the latest interim build.” At some point, we realized that it made more sense to simply make the latest build the official release and keep driving towards 2.0. “Release early, release often” is a good motto for open source projects to have. So that's what I spent part of today doing: you can now download FlexWiki 18.104.22.1687 from SourceForge. Here are the release notes I posted, in case you were wondering what goodies have been added:
This release contains many, many new features and bugfixes. Here is a
partial listing. See the change notes for a complete history. Thanks
to the many contributors who wrote all these goodies in their spare
* Added support for colored text via the %color% tag.
* A completely rearchitected storage engine that allows for pluggable
* An implementation of the pluggable storage engine that allows a
namespace to be stored in SQL Server.
* RSS feed format improvements.
* A new URL scheme. Topics can now be accessed using URLs like
* Fixed newsletter functionality.
* Improved anti-spam technology, like nofollow support and URL
* Improved testing - the automated build installs FlexWiki and
verifies correct behavior via a browser.
* Support for page and page fragment templates.
* Many WikiTalk improvements, including improvements to forms-building
* Improved support for Windows 2000 installation.
* Support for setting page title using page's Title property.
* Better support for Firefox.
* Support for "breadcrumb" functionality to display user's
most-recently-visited pages in a sidebar.
Saturday, March 19, 2005
I've been writing my own text editor control. Although only a moron would actually do this, I'm learning a lot along the way. One of the toughest bits of it is to correctly implement word wrapping, particularly because I want to get it right not just for ASCII characters, but for others as well, like East Asian languages. (While I may be a moron, I'm not insane: I'm using the Uniscribe API to do all the hard parts.)
One of my challenges is that I don't always know what the “right” way to do something is. For example, should there be an invisible character representing the newline that shows up when drag selecting? Should the width of this character count against word wrapping?
When faced with a challenge like this, I often fire up another program and see what its authors decided to do. One of these that I frequently consult is Word. Another is Notepad. I figure I ought to at least be able to provide what Notepad does, and at best I'd match the capabilities of Word (although I'm not quite so mad as to actually target that). Well, tonight as I was trying to work out the kinks in my word breaking algorithm, I saw an interesting quirk in Notepad's line breaking capabilities. It surprised me because I assume Notepad to be one of the most mature pieces of software in Windows. Anyway, try this:
- Fire up Notepad.
- Make sure that Word Wrap is enabled.
- Type some gibberish that has some spaces in it.
- As soon as you type something that wraps to the next line, start hitting backspace.
- Note that backspacing to the point where the word that wrapped becomes short enough that it could fit on the previous line does not actually “unwrap“.
- Marvel that the amount of utility in this information is so small as to be undetectable by modern scientific instruments. :)
Wednesday, March 16, 2005
Monday, March 14, 2005
Sorry for the awkward title on this post, but I've been having a hard time solving a Managed Direct3D mystery, and if I can find or figure out the solution, I want the entry to be obvious in Google, because I've been unsuccessful in locating the answer that way myself.
So it seems that people are still reading my managed Direct3D tutorial, because last week I got three questions about it via email. Two of them, surprisingly, were exactly the same. Apparently, if you compile and run the device recovery tutorial, you'll find that everything works great the first time you come back from hitting ctrl-alt-delete. But if you do it again, your application crashes with a Microsoft.DirectX.Direct3D.InvalidCallException during a call to Device.Present. Turning on the DirectX debug spew shows more details about the error:
2300: Direct3D9: (ERROR) :The following D3DPOOL_DEFAULT surfaces/buffers/textures still exist
2300: Direct3D9: (ERROR) : D3DRTYPE_VERTEXBUFFER
2300: Direct3D9: (ERROR) :All user created D3DPOOL_DEFAULT surfaces must be freed before Reset can succeed. Reset Fails.
2300: Direct3D9: (ERROR) :Reset failed and Reset/TestCooperativeLevel/Release are the only legal APIs to be called subsequently
There are several weird things about this:
- It only happens the second time you lose the device.
- It only happens if you lose the device via ctrl-alt-delete; you can alt-tab as many times as you like.
- I'm pretty sure I'm freeing the VertexBuffer correctly.
So far, I've discovered that it only happens if you create the VertexBuffer in the default pool. If you change to using Pool.Managed, the problem goes away. This makes sense, and is a reasonable workaround, but sometimes Pool.Managed isn't an option, so I'd like to figure out how to use Pool.Default.
So far here's what I've tried with no luck:
- Calling Marshal.Release on the VertexBuffer's underlying raw UnmanagedComPointer.
- Calling Device.SetStreamSource(0, null, 0) after every render.
- Calling GC.Collect and GC.WaitForPendingFinalizers after Disposing of the VertexBuffer.
I'm rapidly running out of ideas. But I'll keep searching, and when I figure something out (or someone tells me what I'm doing wrong), I'll post a comment here and update the tutorial.
Saturday, March 12, 2005
This is too cool not to point out: a C# compiler written in C#. It looks like it's roughly beta quality, but I can already think of a few uses for it even so. The parser/lexer alone could be quite useful.
The author has put the source code in the public domain, so it's available to use however you like. Looks like he might also create a SourceForge project so the code can be collectively improved upon in a more organized manner.
Friday, March 11, 2005
Don't build strongly named assemblies unless you have to.
I've thought for a long time now that strongly named assemblies cause more trouble than they're worth (in most scenarios). This was brought home to me late last night at work as I struggled to recover from an ill-planned upgrade of NAnt. Basically, I got screwed by the fact that the version of NDoc that ships with NAnt is identical to the one I could download, but because they were built on different days, they had different build numbers, and that lead to some assembly load failures. OK, so that's more an argument for “don't base your build number on the date”, but that's another post. The experience still served to reinforce my low opinion of strongly-named assemblies.
See, the problem is that the whole strong-naming thing is supposed to give us two features:
- Tamper detection: If someone mucks with your assembly, the runtime will detect it because it's “signed“.
- Version policy: A strong name means that only exactly the version you request will satisfy a load attempt.
But of course, to do the first one right, you really need to have a whole PKI infrastructure in place, not to mention keeping private keys under tight control. And what do you do if it's an open source project? Even if you solve that problem, I still look at it and say, “If someone has the ability to mess with the bits in some DLL on my hard drive, I probably have bigger problems.”
I think point number two suffers from a severe case of over-engineering. Or, if you like, the issue is that the problem is just inherently very, very hard. Because the system has no way of inferring anything about whether two different versions are compatible or not, it has to assume that they never are. Which leads to problems like the one I had where NDoc.Core 1.3.1856.0 and NDoc.Core 1.3.1811.0 don't talk to each other. Of course, there are hooks like the <assemblyBinding> element in the config file (which I wound up using), but the rules are complex, and the file is hard to get right. Oh, and it fails silently if you get it wrong. The mere fact that the assembly loading and versioning versioning talk I used to give was three hours long should tell you something.
Having spent more and more of my time over the last few years writing real systems, I'm convinced that the simple approach to assembly loading is usually best: don't use strong names. Just put all your DLLs and EXEs in one directory, and the right thing will happen. In the rare event that you do need to make a breaking change to an assembly and you aren't recompiling the whole application, just change the name, like foo2.dll. Some may object that it's less aesthetically pleasing, but I think developers do a lot of stupid stuff in the name of making things prettier.
Will this approach always work? Of course not. BizTalk is one place where you're pretty well constrained to deploy to the GAC, and that means using strong names. Fine - not your choice, and there may well be a good reason for them making you do it that way. But I'd be willing to bet that 99% of the code out there isn't being deployed somewhere where strong names are a requirement. So make your life simple(r) and ditch strong names.
There - I feel better now. :)
Monday, March 7, 2005
One of the things that always works to calm down my four-month-old daughter Ellen is to walk her around. If you're moving, she's generally happy. Since today it was about 65 degrees here in Washington, D.C., I figured I'd bring her along with me when I went out for a walk tonight. She's been out with me before in her baby carrier, and has invariably been quiet the whole time, usually falling asleep after just a few minutes. She seems to love it.
It was pretty dark tonight, but I take a series of smooth, paved trails through a park, and only have to walk down one road for about a block to get there. There's usually not much traffic, but tonight as I was crossing that road, I was scared out of my wits when a car flicked its lights at me as I was crossing. Scared, because it had been driving without any lights, and I hadn't noticed it until I was already in the street. It wasn't even a close call, and clearly the driver saw me, but when you're carrying your baby, that's the sort of thing that takes the starch right out of you.
By itself, that would have been no big deal. Like I said, there was no real danger, I was just startled. So I continued my walk into the park. But Ellen started to complain. First it was just a bit of whimpering, then some intermittent crying. I kept going, figuring she was tired, and that the walking would be just the thing to soothe her to sleep. Not so.
For whatever reason, the crying continued to increase in intensity until she was into the “truly upset” range. “Oh well,” I figured, “I guess I don't get to take my full walk tonight.” I turned around, and when Ellen continued to freak out, I called my wife so she could come meet us in the car so we could get Ellen home the sooner to figure out what was up with her. As I was walking back to meet up with her at the parking lot, I saw some cars go speeding up the hill not far away. “Probably the same idiot teenager that was driving around with his lights off - out having fun tonight.”
Again, not so. No, as I walked up to my wife's car, I found out exactly what those lights were, as no less than three police cars came zooming up to box us into the end of the parking lot. It seems that the police had “had a report of some guy wandering around in the woods with a baby.” My response? “Yes, that's exactly what I was doing.”
Fortunately the officers were very reasonable and polite about it. They believed my account of events immediately, and after simply noting my name and address, bid us a polite good night. Frankly, it's nice to know that the response in our neighborhood is so sure and swift. My wife took the baby and walked the three blocks home (she'd forgotten to bring the car seat), and I drove the car back. An episode of Whose Line Is It Anyway? went a long way toward calming me down a bit, but the surreality of the whole event is still with me. I'm sure I'll have some interesting dreams tonight.
And at the end of it all, I have to wonder if the same goon that was driving without his/her lights was the person that phoned me in. If so, it says something fairly interesting about their priorities. Whomever it was, I definitely won't be taking Ellen for walks after dark any more. Not because I don't think it's safe (I do), but because I don't need that sort of hassle when there's a possibility that Ellen doesn't even like it for some reason.
Tuesday, March 1, 2005
I've been digging out from under a giant pile of tasks that have been accumulating since my daughter was born four months ago. Recently, I got to the point where I felt like I had the mound of jobs under control, so I started working on my TextEditor control again. Not only is it part of my (seemingly) never-ending quest to complete FlexWikiPad, but I've discovered that I really need an interesting problem to cook in the back of my head. I run and take walks to keep fit, and over the last few months I found myself a bit bored while doing so, because I wasn't hacking in my head. Weird but true.
Anyway, it's been nice to tackle the problem again. Although the control is still completely unusable, I find myself thinking forward to features I'd like to add some day. One of them is support for images, so you can use the control to change :) into a picture of a smiley face for example. The problem is that my model for the control is of a series of styled runs of text. How do you model images in that? Should they be a special Unicode character that means “image”? If so, when someone reads the text, how will they know what to do with it?
Today I came up with what I think is a pretty good idea: images are simply a style property of text, like being bold or being underlined. That is, I can assign “picture of smiley face” to the text “:)”, or assign “picture of my daughter” to the text “http://wangdera.com/baby/XMas2004AtHome/P1100115.JPG.htm”. So when it's shown on screen, you see an image. But when you read the text, you get the original text. It's a lot like the alternate text you can apply to an image in that sense - an analogy that indicates I'm not the first guy to think of this, but that's a good thing.