Monday, November 16, 2009

MFC and the disappearing crashes

We've had a strange problem at work with our biggest MFC app. For some reason, crash-worthy bugs (like a write to a NULL pointer) would not show up in the debugger. Instead, whenever something bad would occur, we'd get booted back to the event loop. Even when stepping in the debugger, you'd be going along and suddenly--wham--your stack would be popped and that would be that. The only evidence would be a "First chance exception" message in the Output pane. Quite frustrating when trying to catch certain types of error.

I found that this is because the MFC headers include structured exception handling which magically consumes and hides your errors. Which is the opposite of what we want, but that's how it is. Fortunately, there's a way to make the debugger cooperate. In the Debug menu, choose "Exceptions" and enable every item in "Win32 Exceptions". Voila--the debugger will stop as soon as an error occurs. Why this isn't the default is beyond me.

Sunday, October 25, 2009

Speaker Hacks

For simple setups, a receiver is totally overrated. They're big and bulky, and draw a lot of power, and complicate a simple wiring setup. But, when you want to drive a set of unpowered speakers, what choice do you have? You've gotta drive those speakers somehow.

Today, I was thinking about this a bit. For the playroom speaker setup, I had given up on my amp because it didn't fit in the space I had, so I was just using computer speakers. The computer speakers are based off of a subwoofer that plugs into the wall, which takes the input via minijack, amplifies the signal, and sends it to two little satellite speakers via another minijack. Finally, I had the a-ha moment! The amp is in the subwoofer! It's itty-bitty compared to a stand-alone amp, and it sits on the floor unobtrusively. So hypothetically I could replace the chintzy satellite speakers with real SPEAKER speakers. The only* catch is that real speakers hook up via speaker wire, and the chintzy satellites hook up via minijack. How hard could it be to fix that?

* Well, and the quality/power probably isn't as good as a real amp, either, but I can compromise.

As it turns out, not hard at all. My first idea was to wire up a new minijack to speaker wire; that'd require a trip to Radio Shack for something like this:



That's a little complicated, though, since you need semi-precise soldering ability. I figured I could probably come up with a simpler plan, and after a little research, my revised idea was this:



Radio Shack asks a mere $9 for this beautiful RCA-to-speaker-wire cable. Not bad, and eliminates the need for soldering. But, I thought, if it's that simple, I have plenty of RCA cables sitting around in a box. PLENTY of them, and not much use for them. So, I figured, what's the worst that could happen? I cut the ends off of a perfectly good six-foot Monster Cable stereo RCA cable, stripped it down, twisted the ends, and voila. I had a high-quality equivalent to Radio Shack's cable.

So at the end of the day, the setup is as follows:

Wall socket → Inexpensive Computer-Speaker Subwoofer → Minijack-to-RCA adapter → Barrel adapter → RCA-to-bare speaker wire → Speakers

I'm having fun with the images in this post, so visually we have:





















Friday, September 11, 2009

Playstation 3 Quirks

I've had to work around a few PS3 quirks in the last few weeks, so I figured I'd write them down.

The first quirk happens if you are hosting your wi-fi network via Mac OS X's built-in Internet Sharing. The PS3 can detect the network, but it fails to acquire an IP and so the connection fails. It turns out that Apple's DHCP implementation and the PS3 aren't compatible; the workaround is to manually assign an IP to the PS3. If you aren't sure how to do this, the subnet mask is 255.255.255.0, the gateway is 10.0.2.1, and the IP is up to you but I'd recommend something like 10.0.2.234.

(Big unknown: who is at fault? Is Apple's DHCP server flawed, or is the PS3 unable to handle Apple's valid-but-different-from-the-norm DHCP server?)

Second quirk: AVC movies ripped via Handbrake's "AppleTV" setting don't play on the PS3. It generates an error about unsupported data. The fix is to run the following command line:

perl -pi.bak -e "s|(avcC.{3}).(.{7}).|\1\x29\2\x29|" YourAVCFileHere.m4v

Apparently this tweaks some "magic number" in the AVC headers which corresponds to a complexity level. Anyway, this worked like a charm for me when I tried it. I discovered this here: http://forum.handbrake.fr/viewtopic.php?f=7&t=5907&start=0

Tuesday, July 28, 2009

MessageBox

I've always assumed that passing a NULL HWND to MessageBox would prevent my windows from getting clicked on. Turns out, that isn't always true. In some cases--e.g. when non-modal dialogs or their children are visible--you can make sure that your message box is really, truly task-modal by passing MB_TASKMODAL in the flags.

Friday, July 3, 2009

The Vista Mystery Control

Recently I was working on a dialog that contains vital information that is unnecessary 99% of the time. But 1% of the time, you really want it. I looked through the Windows UI and found that Microsoft has made a control for just this purpose. It's called an Expander control:





Now, you wouldn't know much about the Expander control if you're a typical C++ programmer, because as far as I can see, it's not supported unless you're writing WPF/XAML, which is only officially supported with C# or VB. (Not that I actually believe that the UAC dialog or the Shell Copy dialog are implemented in C#, but they appear to use XAML somehow.) In fact, I had to Google quite a bit to even come up with its name. None of my coworkers knew what it was called either.

Anyway, if you're observant, you may notice that the final dialog in the list--a file save dialog--is a little different than the first two. You see, file save dialogs definitely don't use XAML, but someone higher-up must have decided that they wanted Expander functionality there as well. I suspect the marketing guys saw this in OS X and thought "hey, that makes the file dialog look simpler:"



Anyway, pity the poor guy who had to implement this control in the file dialog--it's a totally alien object in Win32/MFC. Image buttons with round borders? Clickable text with no border? Not a chance, unless you do owner-draw. But our clever engineer had a trick up his sleeve! If you mouse over the pseudo-Expander control in the file dialog, you'll notice it gets a temporary button-like outline, which is actually a great clue for the real implementation. Turns out this is a one-item toolbar wedged into the middle of the window!! (I used Spy++ to check.) Talk about a hack.

Leave it to Microsoft to introduce a new control and then, in the intersection where marketing and tech collide, implement it with the most esoteric kludge imaginable. They didn't even bother to use the same art. Pathetic.

As for me? I decided that, given the choice between wedging a toolbar at the bottom of my window or just using a regular button, I'd take the easy road and use a plain old button. Maybe one day I will break down and write some code to emulate the one-item toolbar hack. I will need to take a shower afterwards, I suspect.

Monday, April 27, 2009

Stupid SQL, Second Edition - handling the empty string

So it turns out that Oracle has a unique flaw among databases. It treats the empty string exactly as if it were a NULL field. So, for instance, the relation '' = '' is actually false much in the same way that NULL is not equal to NULL.

I've seen some question the utility of empty strings at all, but I ran into this today entirely by accident. I had two tables, both with identical "Path" and "Language" columns, and I wanted to join them. In its simplest form, the query looked like this:

select * from TexturesA
left join TexturesB on TexturesA.Path = TexturesB.Path and TexturesA.Language = TexturesB.Language

Now, as luck would have it, we used an empty string to denote "all languages." This caused the join to fail for any all-languages texture asset, because the equality check on language would fail.

Solution? There are a couple, but I ended up with this gem:

select * from TexturesA
left join TexturesB on TexturesA.Path = TexturesB.Path and TexturesA.Language || '~' = TexturesB.Language || '~'

Not my proudest moment. Works great in practice, though. I suspect that this would keep Oracle from leveraging any indices on the column; if indices are critical, you could probably do the more long-winded:

select * from TexturesA
left join TexturesB on TexturesA.Path = TexturesB.Path and ((TexturesA.Language is null and TexturesB.language is null) or (TexturesA.Language = TexturesB.Language))

Or you could add a function-based index of Language || '~' to the table. Either way, feels like a kludge to me.

Stupid SQL, First Edition - now with support for negative numbers

This one handles negative numbers too, up to negative 2^64:

select
lpad(rawtohex(chr(floor(mod(MyNumber + 18446744073709551616, 18446744073709551616) / 4294967296))), 8, '0') ||
lpad(rawtohex(chr(mod(MyNumber + 18446744073709551616, 4294967296))), 8, '0')
from dual

Friday, April 24, 2009

Stupid SQL, First Edition - turning numbers into hex strings

The following Oracle SQL command will take a 64-bit positive number and emit a 16-character hexadecimal string.

select
lpad(rawtohex(chr(floor(MyNumber / 4294967296))), 8, '0') ||
lpad(rawtohex(chr(mod(MyNumber, 4294967296))), 8, '0')
from dual

I'll post an updated solution for negative numbers when I get a chance. (Basic idea: add 2^64, then mod by 2^64.)

Friday, April 17, 2009

_wtoi64 is broken for large values

I recently found a bug in _wtoi64 on Dev Studio 2005. When passed some values larger than 9,223,372,036,854,775,807 (i.e. the high bit of the 64-bit word is set), the returned value will have some bits flipped. This manifested itself in one of our internal tools.

Workaround? Use sscanf (the wide-char version, however you spell it) with %I64d or %I64u. For some strange reason, sscanf and _wtoi64 don't share any logic for translating strings to values. Go figure.

Good job, Microsoft. *golf clap*

Mission Statement

This blog is here so I can record things I've discovered, typically messed up technical stuff. I don't expect anyone to follow it, since there isn't really going to be a common theme, but I wouldn't be surprised if people find it via Google.