2009-02-22

My Answering Machine has a Name -- and a Debt

The random debt collection continues. Recently, I've been getting calls from a machine that go something like this [the names have been changed to protect the nonexistent]:

normal female voice This is an important call for…
deep, robotic voice JAMES WARNER
normal female voice If you are…
deep, robotic voice JAMES WARNER
normal female voice Please press 1 now. If you are not…
deep, robotic voice JAMES WARNER
normal female voice Please press 2 now.

I press 2.

normal female voice If you need some time to get…
deep, robotic voice JAMES WARNER
normal female voice Please press 1 now. If…
deep, robotic voice JAMES WARNER
normal female voice Is not available, please press 2 now.

I press 2, and the normal, female voice tells me how important this call is for (deep, robotic voice) JAMES WARNER, and the call ends. Unfortunately (and, in retrospect, I guess I should've expected such), there was no way to specify that there is no "James Warner" at this number, despite it being a "Warner" residence.

I've been getting a lot of these calls lately, but my answering machine picked up an interesting variant recently. It went like this:

normal male voice This is an important call for…
deep, robotic voice JAMES WARNER
normal male voice If you are not…
deep, robotic voice JAMES WARNER
normal male voice Please hang up now.

My answering machine fails to hang up.

normal male voice By staying on the line, you confirm that you are…
deep, robotic voice JAMES WARNER

The message goes on to offer "James Warner" (the new name for my answering machine) time to get some privacy for this "very important call", and then to tell "James Warner" that this call is to obtain information to collect a debt (surprise), and please call some 1-800 number.

So apparently, my answering machine has now accepted a new name and, by extension, responsibility for a debt. Not that I believe for a second it would hold up in any legal venue.

It'd be almost amusing if my phone wasn't ringing twice a day with these automated calls.

2009-02-12

I don't like all those decimals anymore

So I'm working on a project, and I'm using the SQL Server CE engine as my database. The database engine recently had an upgrade to 3.5.1, so when my client got a new computer and needed to install my program on it, the upgraded database engine was what was downloaded from Microsoft's web site. I didn't think much of it, as I had downloaded it as well, and I hadn't had any issues. However, when he started using the program with real-world data, he got an error:

SqlCeException: 'A parameter is missing. [ Parameter ordinal = 1 ]'

Ok, maybe I goofed. Perhaps in one of my updates, which added a column to one of the tables, I left off a parameter. So I tested the code on my machine, and it of course performed flawlessly.

I went to the client's site and tested it there. Sure enough, there was the error. I copied his database and ran my code against it, in debug mode, and… yes, I finally duplicated the error. Well, at least that told me the error was real.

Then commenced the tedious task of analyzing the SQL command being constructed; ensuring that all parameters were accounted for, all columns were spelled correctly, and all parameter names in the statement matched the names of the parameter objects added to the command; comparing his database to mine to make sure all table columns were accounted for and of the correct types. Everything matched. There were no parameters missing, misspelled, or misplaced.

After I fully convinced myself that everything was in order, I finally went to Google with the full text of the error, and I was fortunate to stumble across a bug reported on Microsoft's Connect site that indicated a problem with decimal values in SQL CE 3.5.1 under "certain conditions". Fortunately, the workaround listed helped me identify those conditions and what to do about them: it has to do with the number of decimal places one attempts to pass to SQL CE.

Fortunately, when building parameters, I pass every value through a function that checks for null and converts to DBNull (I have yet to hear a convincing reason why these must be different), and checks for bool types and converts true/false to 1/0. All I needed to do to fix this was to add code that checks for the decimal type and return Math.Round(value, 4) (although it took a little experimentation to determine that "4" was the correct number of decimals).

All because Microsoft decided they didn't like seeing all those decimal places anymore; and instead of rounding or ignoring them like they did in a previous revision, suddenly they were going to throw a completely incorrect error message (no parameter was missing, and the one it suddenly found "questionable" was not in position 1).

2009-02-11

What are you, Picasso(.Net)?

There is a form in our application that draws very, very slowly. I'm trying to fix that.

In .Net, you have very little control over a border. In many cases (in particular, in this instance, with a Panel), you only get to set whether or not the control has a border, and whether that border is solid or rendered with a 3D effect. If you want more control over the border (such as setting its thickness or color), you have to draw one yourself. Since WinForms.Net has no "Line" control, this means you have to draw the line on the form at runtime by subscribing to the form's Paint event and inserting your line-drawing code there.

You have slightly more control over a background, being able to set the color in most cases; but if you want anything fancy like a gradient, it again requires custom paint code.

It is, therefore, not surprising that we have quite a few Paint event handlers on this particular form.

The layout of our form is as follows:

  • There is data in a couple panels at the top of the form.
  • There are submit and cancel buttons in a panel at the bottom of the form, along with an information panel.
  • In the center of the form is a large area. This area (which is itself a panel) contains one to many user controls.
  • Each user control is designed first as a panel.
  • The panel contains five "header-style" panels and three user controls.
  • The top-most and bottom-most header panels contain summary information — the top being the name of this whole grouping of data, and the bottom being a subtotal.
  • The middle three header panels contain a summary of the data on each of the "sub-user controls", plus a button that hides or shows that user control.
  • On load, the sub-user controls are hidden, so a "collapsed" view of all header panels is shown.
  • The header panels are filled with a gradient fill. This gradient creates a much more visually-appealing division between header rows than solid colors with lines between them would do. (There are lines between them as well, but even with 3pt black dividing lines, solid colors just blend together across those lines in a way gradients don't.)
  • There are drawn borders around each header panel, and around the entire user control itself. There are also separator lines between most of the panels on the form elsewhere.

I thought maybe the gradient fill was just causing too much overhead, but when I replaced it with a solid fill, it had no effect on the overall speed of the form. So, I put debug code in all the Paint event handlers, to see how often the paint events were firing, as I figured this might be the source of my problem. The paint events for each of the panels on the main form fired 2-10 times each (the one panel containing the user controls firing the most often). The paint events for the innermost user controls (the "sub-user controls") fired three times each, which made sense given there were three sets of them loading — except for the first of the group, which fired six times (twice per control).

The containing user control is where things go insane. There are three of them, remember, containing five header panels and three (hidden, at startup) user controls inside. The header panels' paint events fired 5 (for the header itself) to 25 (for each of the sub-controls' headers) times each (which, I suppose, you can divide by 3 to get approximate firings per control). The user control's large panel containing all child controls (header panels and sub-user controls), its paint event fired 651 times, and the paint event for the user control itself fired 747 times!

And that's just when the form is created. I added a button that resizes the form by increasing the width by about 10 pixels, just to see the effect, and I got 153 panel paint events and 168 control paint events.

The maddening thing is, the user control's paint event handler is empty. It does nothing (except for the debug code that notes the firing). Oh, it did do something — it did draw a gradient pattern under the entire control — but since the drawing it was doing was completely covered by all the controls that were placed on top of it, I removed it.

The panel's paint method actually does something — it draws a heavy border around all the subordinate controls. And, aesthetically-speaking, it's really needed. What I can't understand is why exactly it's needed over 200 times per control, especially when the containing and contained controls combined aren't getting painted as often (by a factor of about 7).

So, what's my solution? Well, aside from making sure the paint events did as little work as possible (why is e.Graphics.SetClip(e.ClipRectangle) not automatic in a Paint event?), drawing gradients in Rectangle rect only if (e.ClipRectangle.Contains(rect) || e.ClipRectangle.IntersectsWith(rect)), and then only in the part rect.Intersect(e.ClipRectangle); but I also completely unbound the Paint event handlers in the user control, and instead of drawing a thick border on the panel, just used the default BorderStyle.FixedSingle.

It's still slow, although not as slow as it was (I'm sure those events are still getting fired, just with no custom code to process); and it doesn't look the same with the change in border, but there's enough padding between user control instances that I think I can get away with it.

In short, I didn't solve the problem; I still can't find the cause, and I've only been marginally successful in treating the symptom.

2009-02-08

A Whole New Url

I've had the domain yakkowarner.com for a couple years now. I originally used it when I decided to try out Microsoft's Office Live service. Well, I didn't really have any use for it, so I thought the URL could better serve me elsewhere.

Microsoft doesn't provide an easy-to-find link to instructions for how to grab your domain name, so I tried the back-door approach: I contacted the registrar to request a domain transfer. I got to send my very first international fax, to Melbourne, Australia. Excitement. (I actually had to do it four times. The first one didn't go through, the second one I realized I put the paper in upside down [oops], the third time didn't go through again.) Doing so prompted a tech rep from Microsoft's Office Live team to send me a very helpful and detailed message, with links, with exactly what I needed to do to release my domain so I could transfer it to my control.

So, welcome to www.YakkoWarner.com! It's the same blog with an easier-to-type address. :)

2009-02-06

Live CNN video turns your PC into a P2P server

Here's something I think should get a little more attention. According to Windows Secrets, CNN's live video streaming application turns your PC into a peer-to-peer video server, using your PC to stream the video to other PCs as well.

Personally, I don't find anything wrong with the concept. World of Warcraft uses similar technology to improve the speed of distribution of their software updates. It's what P2P tech is good for. Windows Secrets takes exception to the fact that CNN isn't exactly forthcoming about what is happening, though, and rightly so. If they came out and said what was happening when you were asked to install the "Octoshape add-in for Adobe Flash Player", then it would be less of an issue — people tend to be much more forgiving if you're up front about what you're doing. (Granted, there are other valid concerns about CNN's use of Octoshape posted there, but I won't address those here.)

What makes this such a large concern is that they are using other people's bandwidth, without their notification or explicit consent, at a time where many people's ISPs are capping their usage. The ISPs have declared that we must be conscious of how much data we consume and produce. Having an application that is uploading up to three times as much data as it's downloading, especially without our knowledge, is an issue, when every byte is being counted.

On the one hand, I fully support the concept of new technologies that help to distribute content efficiently. I don't think Blizzard is wrong to use it in distributing WoW patches, and I don't think CNN is wrong to use it in distributing video. (Again, the other issues that Windows Secrets brings up — specifically, "deceptive marketing", "ludicrous license terms", "security vulnerabilities", etc., are worth criticism, but are out of scope here.) However, they're trying to shift the cost to the end users, and we may not have the cost to spare, now that we're being limited.

I know it probably seems odd to hear me complain about bandwidth caps when mine is so large compared to what I use, but as I've said before, knowing I have a limit makes me much more conscious of the throughput I generate, and it makes me much more sensitive to anything that increases that value. Besides, just because I'm fortunate to have a wide use-to-limit margin doesn't mean everyone else is — or that I'll always be, for that matter.

2009-02-01

Bandwidth for January

It's a new month, and a new set of data for my ongoing bandwidth analysis.

There's a big spike around the 9th and 10th. Tech-savvy users may recognize this date as the day the Windows 7 beta was released. I downloaded both the 32-bit and 64-bit releases for testing. (My laptop has a 64-bit CPU, but it came pre-installed with a 32-bit version of Vista. I wanted to test how it would do upgrading to the next 32-bit OS, and how it would compare running in its native 64-bit mode.) If the downloads could've completed within the one day, it would've been the single largest day since I started tracking. As it stands, however, the 9th is only my second-largest day of consumption; the record still maintained by one day in October watching streaming video of my church's general conference.

The total for the month comes to 24.64GB down, 5.44GB up, 30.08GB total. This puts it second behind October, at 37.77GB total. My total data consumption for five months, up and down, is 148.71GB, still under the 250GB cap for a single month.

What's kind of interesting, though, is that there is news that Comcast is about to launch an online backup service. They'll have a tiered pricing plan, where you can purchase up to 200GB of online storage at a monthly rate. But, according to a Comcast representative in the linked article, transfer to and from this service is not exempt from the bandwidth cap. So, they'll be offering a service whereby you pay to use it (a lot more than you could find from other providers, too), but if you use what you pay for, you'll come close to having them cut you off of the internet. <sarcasm>Oh yeah, sign me up for that…</sarcasm>