Wednesday, March 30, 2005
Tuesday, March 29, 2005
Meet Clint
There's No Such Thing As Just One Application
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
FlexWiki 1.8.0.1677 Released
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 1.8.0.1677 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
time.
* Added support for colored text via the %color% tag.
* A completely rearchitected storage engine that allows for pluggable
stores.
* 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
http://www.flexwiki.com/default.aspx/Namespace/TopicName
* Fixed newsletter functionality.
* Improved anti-spam technology, like nofollow support and URL
blacklisting.
* 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
support.
* 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
"Interesting" Notepad Bug
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
All Complex Ecosystems Have Parasites
This is a great essay on trying to “solve” problems via restrictions on the market. It has the feel of one of those things I'll find myself citing over and over again, like Joel's Law of Leaky Abstractions.
[Via Sam Ruby.]
Monday, March 14, 2005
Managed Direct3D InvalidCallException on Second Device Reset
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.