by Michael S. Kaplan, published on 2008/11/04 10:01 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2008/11/04/9037027.aspx
Via the Contact link, Rich asks:
Hi Michael, I've been reading your (very detailed and useful) series on keyboard layouts. There's one thing that's puzzling me with respect to the post about the Caps Lock state (Getting all you can out of a keyboard layout, Part #8).
When you are processing a standard key that's not part of a dead key sequence, you have some special-case processing for control chars that tests whether the VK value minus 0x40 is the same as the control character.
if((rc == 1) &&
(ss == ShiftState.Ctrl || ss == ShiftState.ShftCtrl) && )rgKey[iKey].VK == ((uint)sbBuffer[0] + 0x40))) { ... }
However, there is a subtle difference in the equivalent code that processes a dead key combining character:if((((ss == ShiftState.Ctrl) || (ss == ShiftState.ShftCtrl)) &&
(char.IsControl(basechar))) ||
(basechar.Equals(combchar)))
{ ... }
In this case, you use char.IsControl(basechar). What is the difference between this and the test for VK - 0x40?
Many thanks
Rich
A very reasonable question, one that I don't really cover in the blog proper.
It is actually to avoid a situation that kind of happens in keyboards.
The very first (pre-alpha) version of the tool that eventually became known as MSKLC but at the time was just something we knew as "the keyboard tool", and a feature that I have talked about previously in Accessibility, Internationalization, and Keyboards (#3: MSKLC's UI) and Do your utmost to be conventional (and then pimp, q.d. or p.r.n.).
The ability to load an existing keyboard layout.
The very first version of that code did not include the CTRL and CTRL+SHFT shift states, but the second version I demonstrated a week later did. And while I was working on that code I ran into a problem - the fact that the various keyboarding API calls I was using would treat almost every single keystroke in the particular shift states as if it had a control character in it -- unless a letter or something else was assigned.
We realized that the code was just always saying these were here unless something else was added.
There is a certain symmetry in CTRL putting in "<Control>" characters, and you can see why if you ever spend time in the DOS prompt and you actually hit some of these keystrokes -- because this is how the are sent.
After a conversation about this where Simon pointed out that just because Windows is going to simulate them being there is no reason to add them explicitly in the keyboard tool. So we decided to strip these automatically added characters, since they really were not part of the layout.
For MSKLC 1.3, we decided to just always strip anything that made char.IsControl return true.
Just 'cause, you know?
Anyway, it turned out that broke some stuff, namely some applications that work at lower levels and don't get the benefit of the automatic additions of these special characters. This is a bug I have mentioned previously in blogs like Usage (customer intent) vs. Design (developer intent) and when we broke TELNET as I mentioned in Michael's Keyboard Laws for Developers, Part 3. With a special blessing from the test team (the original triage team said the bug did not meet the bar) the fix for this bug was snuck into MSKLC 1.4.
To limit the impact while maximizing the chance of fixing all of the known problems, the original char.IsControl code was removed and replaced with the test limiting the "ignoring the character" when it was the exact control character under the letter in question or a small list of specific control characters that applications depended on.
But neither the tester nor I could see any reasonable purpose to doing the sane with dead keys -- who would involve a control character with a dead key? That would make no sense!
So the dead key check did not include the extra filtering.
And for this series, I essentially went through the same process as i had the first time the code was written, except I remembered what had been done -- so I just put the same checks in, to get comparable results.
It seemed obscure enough that the explanation would never be needed; a comment would only call attention to it.
Well, that was my thinking at the time. Clearly Rich proved to me that there are people who can be as detail-oriented as I can be. :-)
Anyway, that is why the two different checks exist. Because every reasonable keyboard should be able to be loaded properly with it, including a bunch of unusual cases....
This blog brought to you by U+001d, aka <CONTROL>, aka INFORMATION SEPARATOR THREE)