Larry Osterman's WebLog

Confessions of an Old Fogey
  • Larry Osterman's WebLog

    More proof that crypto should be left to the experts

    • 41 Comments

    Apparently two years ago, someone ran a static analysis tool named "Valgrind" against the source code to OpenSSL in the Debian Linux distribution.  The Valgrind tool reported an issue with the OpenSSL package distributed by Debian, so the Debian team decided that they needed to fix this "security bug".

     

    Unfortunately, the solution they chose to implement apparently removed all entropy from the OpenSSL random number generator.  As the OpenSSL team comments "Had Debian [contributed the patches to the package maintainers], we (the OpenSSL Team) would have fallen about laughing, and once we had got our breath back, told them what a terrible idea this was."

     

    And it IS a terrible idea.  It means that for the past two years, all crypto done on Debian Linux distributions (and Debian derivatives like Ubuntu) has been done with a weak random number generator.  While this might seem to be geeky and esoteric, it's not.  It means that every cryptographic key that has been generated on a Debian or Ubuntu distribution needs to be recycled (after you pick up the fix).  If you don't, any data that was encrypted with the weak RNG can be easily decrypted.

     

    Bruce Schneier has long said that cryptography is too important to be left to amateurs (I'm not sure of the exact quote, so I'm using a paraphrase).  That applies to all aspects of cryptography (including random number generators) - even tiny changes to algorithms can have profound effects on the security of the algorithm.   He's right - it's just too easy to get this stuff wrong.

     

    The good news is that there IS a fix for the problem, users of Debian or Ubuntu should read the advisory and take whatever actions are necessary to protect their data.

  • Larry Osterman's WebLog

    What is AUDIODG.EXE?

    • 41 Comments

    One of the new audio components in Vista is a new process named audiodg.exe.

    If you look at it in taskmgr, the description shows "Windows Audio Device Graph Isolation", but that's not really particularly helpful when it comes to figuring out what it does.

    The short answer is that audiodg.exe hosts the audio engine for Vista.  All the DSP and other audio processing is done in audiodg.exe.  There are two reason it runs outside of the windows audio service.

    The first is that there's 3rd party code that gets loaded into audiodg.exe.  Audio hardware vendors have the ability to install custom DSPs (called Audio Processing Objects or APOs) into the audio pipeline.  For a number of reasons (reliability, serviceability, others) we're not allowed to load 3rd party code into svchost processes (svchost.exe is a generic host process for services that's used inside Windows). So we need to move all the code that interacts with these 3rd party APOs outside the audio service (that way if an APO crashes, it won't take out some other critical part of the system with it).

    The second reason for using a separate process for the audio engine is DRM.  The DRM system in Vista requires that the audio samples be processed in a protected process, and (for a number of technical reasons that are too obscure to go into) it's not possible for a svchost hosted service to run in a protected process.

     

    So why audiodg?

    As I mentioned in my post "Audio in Vista, The Big Picture", the route that audio samples take through the audio engine can be considered a directed graph.  Internally, we refer to this graph as the "Audio Device Graph" (ok, strictly speaking we call the part to the left of the mixer as the local graph, and the part to the right of the mixer the device graph, but when we consider the big picture, we just call it the audio device graph).

    So why AudioDG?

    Originally we called the process DeviceGraph.Exe.  For a number of reasons that are no longer relevant (they're related to the INF based installer technology that was used before Vista), we thought that we needed to limit our binary names to 8.3 (it's a long story - in reality we didn't, but we thought we did).  So the nice long names we had chosen (AudioEngine.Dll, AudioKSEndpoint.Dll, and DeviceGraph.Exe) had to be truncated to 8.3.

    I felt it was critically important that all the audio components had to have the word "Audio" in the beginning to make it clear that they had to do with audio functionality.  Since we thought we were limited to 8.3 names, that meant we had 3 letters to play with in the name.  For AudioEngine.Dll, it was relatively simple - it shortened to AUDIOENG.DLL.  Similarly for AudioKSEndpoint.Dll, it shortened to AUDIOKSE.DLL.

    But DeviceGraph was somewhat more complicated.  I originally went with AudioADG.EXE (audio+ADG for Audio Device Graph), but people thought it was redundant (It expanded to audio audio device graph). 

    Eventually we settled on "AUDIODG.EXE".

    So why the funky description?  Well because it accurately reflects what audiodg is - it's a part of Windows, so you get "Windows", it hosts the "Audio Device Graph", and isolates it from the Windows Audio Service.

  • Larry Osterman's WebLog

    My New Monitor's Here! My New Monitor's Here!

    • 41 Comments

    With apologies to Steve Martin.

    I just got my new monitor (after the whole Office Move thingy I decided I didn't want to move the big monitors again).  It's a Del 2001FP which does 1600x1200 natively.

    Oh man, I don't know WHAT I was thinking of in waiting to get this puppy.  I have a 2001FP at home on my home machine, but I hadn't realized just how nice it was as a work monitor.

    This is one SWEET monitor.  It's SO crisp.

     

    Now all I need to do is to figure out how I can justify a second :)

     

  • Larry Osterman's WebLog

    The last consumer operating system written in my lifetime...

    • 40 Comments
    Way back in the day, I was chatting with Tom Miller, one of the NT filesystem developers - he and Gary Kimura were the guys who designed and built NTFS.

    At some point, the conversation drifted around to our motivations for working on NT, and Tom made a comment that's stuck with me for the 15 years since he made it.

    "I work on NT because it's an opportunity to work on the last new PC operating system to be written in my lifetime."

     

    It's true that many of the low level concepts embodied in NT originated in the operating systems that Dave Cutler and the core NT team developed when they were at DEC, but the entire NT operating system was written from the ground up - with the exception of the command interpreter, much of the code in USER, the TCP/IP stack (licensed from Spider systems) and a couple of the networking utilities, every bit of code in NT 3.1 was brand spanking new.  And there were plans in place to replace the TCP/IP stack with an in-house TCP/IP stack (delivered with NT 3.5), so even that was written from the bottom up.

    When I relate Toms comment to others, they invariably respond "But what about [Linux|OSX|Be]".  Taken in turn:

    Linux was started in 1991, after the fact, but it's not a complete OS - Linux is the kernel, but the other parts of the OS date from other projects started long before Linux was started.  The kernel is new, but the entire OS isn't - all the user mode components come from previous operating systems, as does the networking stack.

    OSX is based on FreeBSD, which was in turn based on 386BSD - large parts of the OS have been reengineered, but again, it wasn't a bottom-up attempt to write an entire OS.

    Be's OS might have qualified as a new OS, since it was written from the bottom up, but unfortunately it didn't succeed in the marketplace.

    There have been a number of original OS's built for various small devices since then (Palm, Symbian, WinCE, etc), but none for PC's. 

    In reality, this isn't really surprising, it takes a HUGE amount of effort to launch a complete operating system - hundreds of millions of dollars just in direct development costs.  I still remember when we had the first big scheduling meeting when we laid out the list of things that we had to have for NT - many of the developers in the room were aghast at the number of components that simply HAD to be present to be considered a complete operating system: 16 bit application compatibility (which was a real issue for our non x86 platforms), printer drivers, video drivers, printing UI, multimedia hardware support, networking support, filesystem utilities, the list went on and on.

    Nowadays, the list is even longer - for instance, back in 1989 when that scheduling meeting happened, you didn't need a web browser, because the web hadn't been invented yet (the first web page was written in 1990), but today, clearly you'd have had to have a web browser on the list.  Similarly, today you need to have camera support, digital media player support, instant messenger support, etc.

    My suspicion is that Tom's statement will continue to hold true - the amount of effort required to build an entire operating system from the bottom up is daunting, and it's unlikely that anyone is likely to embark on such an effort in the foreseeable future.

  • Larry Osterman's WebLog

    Another of my favorite tricks - reducing the number of bits in a number by 1

    • 39 Comments

    One of the classic (and thus no longer asked) Microsoft interview questions is "How quickly can you count the bits in a 16 (or 32) bit integer?".

    You get a varied number of responses to this one, from brute force to lookup tables.

    One of my favorite tricks for this is:

       x = x & (x - 1)

    This reduces the number of bits in a number by one.  I'm not sure exactly why it works, and it only works for 2s complement numbers, but it DOES work.

    So using this trick, the bitcount question is:

    {
       bitcount = 0;
       while (value != 0)
       {
         bitcount += 1;
         value = value & (value - 1);
       }
       return bitcount;
    }

    It's a silly trick, but clever.

     

    Btw, being geeks, a number of people over here came up with the fastest known version (in software).  It involves adding the bits in parallel - you can count the bits in 32bit integer in a constant 34 cycles on a 386 machine.

    To do that, you first split the number into odd and even bits thus creating 32 1 bit numbers.

    Next, you shift the even bits right one bit and add the values together to get a collection of 16 2 bit numbers.

    Now split the number into groups of 2 bits by again masking by 0b0011001100110011 and 0b1100110011001100, shift the second value right by two bits and add the values to get a collection of 8 4 bit numbers.

    Now split the number into groups of 4 bits by again masking by 0b0000111100001111 and 0b1111000011110000, shift the second value right by four bits and add the values to get a collection of 4 8 bit numbers.

    Now split the number into groups of 8 bits by again masking by 0b0000000011111111 and 0b1111111100000000, shift the second value right by eight bits and add the values to get a collection of 2 16 bit numbers.

    Finally, split the number into groups of 16 bits by taking the low sixteen bits and adding them to the high 16 bits, you now have one 32 bit integer that contains the number of bits that were on in the original value.

     

  • Larry Osterman's WebLog

    Viruses - I feel your pain

    • 39 Comments

    Well, it finally happened.  For the first time in my 20 year history at Microsoft, I had to reformat a computer because it got hit by a virus.

    I’m not sure how the virus got inside the firewall, my guess is someone brought it inside on a laptop or something, but it happened.

    You see, I was running an interim build of XP SP2, and wanted to update to the RC build.  So I uninstalled the interim build (we only support upgrading from public releases).

    And my machine puked.  This happens; there was probably a bug in the interim build’s uninstaller, no big deal, it’s not like I’ve not done this dozens of times before.

    So I figured I’d reinstall XP and re-install the patches.  Again, nothing new here.  I’ve done this dozens of times, its part of the cost of running interim builds.

    But this time, things went horribly wrong.  Seconds after I installed the RTM bits, I got the dreaded “Access violation in LSASS.EXE at 0x00000023” that indicates I was infected with Sasser.

    I tried about 6 different ways of removing this from my machine – reinstalling again, reinstalling clean, reinstalling into another partition.  Nothing worked, and I was left with wiping the machine.

    Now I’m reinstalling windows again, after the reformat.  I guess I know what I’m going to be doing for the rest of the day L

    The reality is that once I got infected I had no choice but to reformat my machine, I was just holding off on the inevitable.  Why would I have to reformat the machine?  Well, because there’s no way of knowing what the payload of the infection is.  It could have been an innocuous payload that popped up a “Hey, you got infected!” popup every 10 minutes – Annoying but harmless.  It could have been a rootkit that would use my machine as a doorway for hackers to gain access to the Microsoft corporate network.  And once you’re rooted, there is NO way of knowing that you’re rooted – A good root kit covers its tracks so that it is essentially undetectable. 

    This is important:  IMHO, once you’ve confirmed that you’re infected with a virus, you really have no choice but to wipe the machine since you have no way of knowing what’s been compromised.  Hopefully you have a recent backup, or you have a way of saving your critical files before the reformat.  I recently saw a report (I’m not sure where now) that someone discovered a worm that was infecting the system restore partitions on some machines – these are backup partitions that are installed by OEM’s on machines with a copy of the image that they use to create the system – it’s a replacement for the OEM install CD that used to come with computers.  The worm was modifying the files on the master copy, so if you used the OEM’s “recover my system” procedure, you just re-infected your machine.  The only recourse from this one was to find a copy of a Windows CD and reinstall from that.

    I’ve always been a staunch advocate of safe computing.  At my home network (with only 7 computers), before I installed broadband, I bought a hardware firewall (first a Netgear RO318, now a DLINK DI604 (a truly sweet piece of hardware btw)).  I made sure that all 7 machines were kept up-to-date on patches.  Every machine has antivirus installed on it and the signatures are kept up-to-date.  I was smug in my self-assured knowledge that I was safe because I was doing the right thing.  I berated my parents for not having a firewall on their broadband connections. 

    So I’ve just had my first taste of what it feels like to be on the other side of the firewall.  And it leaves a very bitter taste in my mouth.

    So as President Clinton once said: “I feel your pain”.

     

     

  • Larry Osterman's WebLog

    Not in my backyard, you don't!

    • 39 Comments

    A bit of context for those of you who aren’t in the Seattle area.

    Recently, a local church in Bothell decided to host a homeless shelter known as “Tent City 4”.  This decision caused a great deal of consternation on the part of the people living around the church, and the city of Bothell sued to force the church to close the tent city.  There’s been a bunch of going around about it, and eventually another church, the Northshore United Church of Christ agreed to host the homeless shelter.

    It turns out that the Northshore United Church of Christ is located across the street from a local Junior High School, and down the street from the elementary school my daughter Sharron attends.

    Last night, they had a public meeting about the church, which was attended by about 200 people.  One of the attendees was John Gronquist, who later on wrote the following email message in response to an “Oooh, they’re going to stick homeless people in OUR neighborhood, we’ve got to stop them!” email message.

    Hi all!

     

    Normally I can't stand people who "reply all", but since you've taken the liberty of emailing everyone with your views, it's only fair that at least one counter view is allowed a chance to speak. This email was forwarded to me by my wife's account so there's a chance I've missed any discussion which was spurred from TinyWeeHobbit's mail. Sorry in advance for all those who don't want to be involved..

     

    I live off of 168th, and have two young children. I've also visited Tent City in Bothell and seen it with my own eyes. I've also looked up the stats of the abuses in Bothell, as well as the 2003 crime statistics for the city of Woodinville as a whole.. Rather surprising those were. Did you know that there were 83 counts of domestic violence in Woodinville in 2003? That there were 6 forcible rapes? 21 residential Burglaries? 67 Vehicle thefts? 6 arsons?? MY GOD! Someone should do something! Someone should keep all of Woodinville away from Woodinville!!

     

    Compared to the average small town Tent City's crime stats are really low. It's more actively patrolled than our neighborhood currently is, has more active social programs visiting it to check up on problems, and unlike most neighbors they have a REASON to try and behave themselves, because believe it or not the vast majority of them really do want to show that they can make something of themselves and not be simply 'drains on society'.

     

    As a landowning member of the neighborhood and a father I support the Tent City. I'm not going to say that there's no risk at all, that'd be foolish. But I will say that the risk is low, actively managed, and ultimately worth it.

     

    Every night I drive down 168th and see the signs, as do my children.. In fact, another neighbor's children were holding signs last night and cursed me out when I failed to honk in support of their protest. Signs in support of the tent city have strangely been removed and/or defaced. Odd that. Interesting that one of the key arguments against the Tent City is that it'd cause 'litter, vandalism, and profanity' in the neighborhood. So far the only litter, vandalism, and profanity I've encountered, has been from the protesters against the tent city. Clearly there's some key difference I'm missing..

     

    In any case, one sign in particular seems to be the 'big deal'. That there was a "Level 2 Sex Offender found in Bothell Tent City!!" Well, there's a level 2 sex offender living on 165th right now. RIGHT NOW! You can find her name doing a search in the King Country Sheriff's Sex Offender search by zip code. Not sure how long she's been there, but then, it is a free nation supposedly, and people who've served their punishment normally are considered a chance to start their lives over, and not suffer the rest of their lives for their crimes.. That's normally the case, unless, it appears, you've had the misfortune of not being able to get a job after leaving jail, due, I'm sure, in no part to prejudice about hiring previous offenders.

     

    By the way, you can also find the names of every homeless or transient sex offender registered in the state, which is the same list that the guards working at the tent city will be looking up whenever a new person arrives. Can you say the same of the road on which you live? Do you demand to see the names of each new neighbor and look them up in the Sex Offender database when they move in? Great way to be a popular neighbor, I'd wager.. "Oh, sorry, we can't talk to you until we've looked up every bad thing you've ever done, even if you've served your time for it, and parade it before everyone in the neighborhood so they can shun you as well. Give you a chance? Sorry, can't chance it."

     

    By the way, did you know that one of our neighbor's illegally burns his garbage right across the street from Leota Jr. high, sending toxic clouds of smoke from burning plastics and god knows what into the air every single week we've all lived there? He does it at night, and on weekends, so at least his only is poisoning the after hours programs.. I won't name names, but the police have been repeatedly called, and still there's no sign of a garbage can in front of the house on Fridays, and the lovely smell of toxic fumes wafting through my yard and house and into my children's lungs on a continual basis. This neighbor owns a home though, so I guess illegally burning garbage on regular basis is okay. Have to say though, the night he burned lightbulbs and they went off sounding like 6 gunshots during a dinner party we were having was especially disturbing.

     

    Sorry.. ..I just figured that since we're involved in a little game of shunning people for the no legitimate reason, I'd get my digs in as well..

     

    As far as the 'worth it' argument, I've only this to add..

     

    I won't live in fear. Not of neighbors with 'histories', not of neighbors with irrational fears of the unknown, nor of terrorists, nor of polluting whackjobs who won't spend $16 a month to put the garbage out on the street instead of into my kid's lungs..

     

    We almost lost our youngest child to E. Coli when he was 1 year old from a hamburger at a state fair. THAT'S fear. That's horror the likes of which most of you will never know. Did anyone do anything to clean up our meat industry? NO. It was up to us to protect ourselves, as it always is for any family from any threat from anywhere they live.

     

    I won't live in fear, and I won't teach it to my children. Life is to short and precious and full of wonders to live like that, and I've nothing but honest concern for those who do.

     

    We need to be a part of solving the problems of this nation, instead of pushing them off for future generations or other neighborhoods to deal with. If you're really worried about the security of the Tent City, volunteer for night guard duty. Maybe I'll see you there?

     

    In any case, I apologize for those to whom this mail is mere spam.

    Sincerely,

    John Gronquist

    John is absolutely right.  The decision to object to the hosting of the tent city in Woodinville is backed by nothing but FUD.  The real dangers associated with the tent city are nowhere NEAR as bad as the dangers associated with just living in Woodinville (not exactly a high-crime area).  Having homeless people in your neighborhood makes people uncomfortable.  It reminds them that there are people in our society that AREN’T middle class with nice homes and nice cars.  The danger represented by the homeless is an excuse to justify the classist (not racist, but classist) discrimination.

    I find it SO frustrating that people can’t bring themselves to actually understand that just because someone is homeless doesn’t mean that their morals and values are any different from anyone else. Just because someone is homeless doesn’t mean that they are evil and must be avoided.  We should be trying to HELP these people get back on their feet and not hurting them.  Places like Tent City 4 give people dignity.  It gives them an address and a phone number that they can use as a reference.  It gives them a place they can shower before they go to their interview.  If you’re homeless you can’t even get a job at McDonalds because you can’t meet the cleanliness requirements of the restaurant.  We don’t have public toilets and showers here on the eastside.  We don’t have enough shelter space for the homeless right now, and shutting down things like tent city only increase this problem.

    At a minimum, the shelter is only going to be open for six weeks.  Is the presence of the shelter SO intolerable that you can’t even abide its presence for 6 weeks?

    I’m ecstatic that the United Church of Christ stepped up to the plate to host Tent City 4; I only wish others would understand.

    Addendum:  The City of Woodinville graciously stepped up and donated the use of several acres of unused city property for the shelter, thus rendering the issue of locating the tent city in a residential area moot.  

     

    Edit: Removed comments because the post was becoming a forum for the anti-tent city people.  Once this post exceeds the comment time limit (sometime next month), I'll re-enable comments so people can see the other comments that have been made on the article.

     

     

  • Larry Osterman's WebLog

    What IS audio on a PC anyway?

    • 39 Comments

    This may be well known, but maybe not (I didn’t understand it until I joined the Windows Audio team).

    Just what is digital audio, anyway?  Well, at its core, all of digital audio is a “pop” sound made on the speaker.  When you get right down to it, that’s all it is.  A “sound” in digital audio is a voltage spike applied to a speaker jack, with a specific amplitude.  The amplitude determines how much the speaker diaphragm moves when the signal is received by the speaker.

    That’s it, that’s all that digital audio is – it’s a “pop” noise.  The trick that makes it sound like Sondheim is that you make a LOT of pops every second – thousands and thousands of pops per second.  When you make the pops quickly enough, your ear puts the pops together to turn them into a discrete sound.  You can hear a simple example of this effect when you walk near a high voltage power transformer.  AC power in the US runs at 60 cycles per second, and as the transformer works, it emits a noise on each cycle.  The brain smears that 60 Hz sound together and turns it into the “hum” that you hear near power equipment.

    Another way of thinking about this (thanks Frank) is to consider the speaker on your home stereo.  As you’re listening to music, if you pull the cover off the speaker, you can see the cone move in and out with the music.  Well, if you were to take a ruler and measure the displacement of the cone from 0, the distance that it moves from the origin is the volume of the pop.  Now start measuring really fast – thousands of times a second.  Your collected measurements make up an encoded representation of the sound you just heard.

    To play back the audio, take your measurements, and move the cone the same amount, and it will reproduce the original sound.

    Since a picture is worth a thousand words, Simon Cooke was gracious enough to draw the following...

    Take an audio signal, say a sine wave:

    Then, you sample the sine wave (in this case, 16 samples per frequency):

    Each of the bars under the sine wave is the sample.  When you play back the samples, the speaker will reproduce the original sound.  One thing to keep in mind (as Simon commented) is that the output waveform doesn't look quite like the stepped function that the samples would generate.  Instead, after the Digital-to-Audio-Converter (DAC) in the sound card, there's a low pass filter that smooths the output of the signal.

    When you take an analog audio signal, and encode it in this format, it’s also known as “Pulse Coded Modulation”, or “PCM”.  Ultimately, all PC audio comes out in PCM, that’s typically what’s sent to the sound card when you’re playing back audio.

    When an analog signal is captured (in a recording studio, for example), the volume of the signal is sampled at some frequency (typically 44.1 kHz for CD audio).  Each of the samples is captured with a particular range of amplitudes (or quantization).  For CD audio, the quantization is 16 bits, in two samples.  Obviously, this means that each sample has one of at most 65,536 values, which is typically enough for most audio applications.  Since the CD audio is stereo, there are two 16 bit values for each sample. 

    Other devices, like telephones, on the other hand, typically uses 8 bit samples, and acquires their samples at 8kHz – that’s why the sound quality on telephone communications is so poor (btw, telephones don’t actually use direct 8 bit samples, instead their data stream is compressed using a format called mu-law (or a-law in Europe), or G.711).  On the other hand, the bandwidth used by typical telephone communication is significantly lower than CD audio – CD audio’s bandwidth is 44,100*16*2=1.35Mb/second, or 176KB/second.  The bandwidth of a telephone conversation is 64Kb/second, or 8KB/second (reduced to from 3.2Kb/s to 11Kb/s with compression), an order of magnitude lower.  When you’re dealing with low bandwidth networks like the analog phone network or wireless networks, this reduction in bandwidth is critical.

    It’s also possible to sample at higher frequencies and higher sample sizes.  Some common sample sizes are 20bits/sample and 24bits/sample.  I’ve also seen 96.2 kHz sample frequencies and sometimes even higher.

    When you’re ripping your CDs, on the other hand, it’s pointless to rip them at anything other than 44.1 kHz, 16 bit stereo, there’s nothing you can do to improve the resolution.  There ARE other forms of audio that have a higher bit rate, for example, DVD-Audio allows samples at 44.1, 48, 88.2, 96, 176.4 or 192 kHz, and sample sizes of 16, 20, or 24 bits/sample, with up to 6 96 kHz audio channels or 2 192 kHz samples.

    One thing to realize about PCM audio is that it’s extraordinarily sparse – there is a huge amount of compression that can be done to the data to reduce the size of the audio data.  But in most cases, when the data finally hits your sound card, it’s represented as PCM data (this isn’t always the case, for example, if you’re using the SPDIF connector on your sound card, then the data sent to the card isn’t PCM).

    Edit: Corrected math slightly.

    Edit: Added a couple of pictures (Thanks Simon!)

    Edit3: Not high pass, low pass filter, thanks Stefan.

  • Larry Osterman's WebLog

    Why do people write applets?

    • 39 Comments

    Since I spend so much time railing about applets, I also tend to look at applets to see what they do (after all, the first step in knowing how to defeat the enemy is to understand the enemy).

    In general, applets seem to fall into several rough categories:

    • Updaters
    • Notification Area Handlers
    • Helper applications
    • Services (I did say that I lump services into the same category as applets).

    Let me take them in turn...

    Updaters:  I LIKE updaters.  Updaters are awesome.  IMHO, I trust applications that include updates more than those that don't (because an updater implies a commitment to further development and bug fixes).  However way too many vendors build programs that run all the time and do absolutely nothing other than wait to check for updates every week (or every month).  One other problem with updaters is that sometimes the authors of the updater use the updater to push unrelated software (at the moment, I'm particularly annoyed at the iTunes updater - if you install just Quicktime, the updater tries to get you to install Quicktime+iTunes, and there seems to be no way of shutting it up).

    Notification Area Handlers:  Every application seems to want to put its own icon in the notification area.  To me, the functionality that is offered by many of these is of limited value. For example, my display driver includes an applet that allows the user to quickly switch between screen resolutions, but I almost never change my screen resolution - so why provide a easy shortcut for that functionality?  I'm not sure why, but personally I believe it's because of branding (since you get to put an icon with your notification area handler, it makes it obvious to the user that you've installed the software).  Some pieces of notification area functionality are quite useful (the "big 4" (Sound, Network, Battery, Clock) in Windows are good examples, as are things like RSSBandits' status indicator), but many of them make me wonder (which is why I suspect that branding is the real reason behind many of the notification area icons).

    Helper applications: These are things like "FlashUtil9d.exe" (running on my machine right now) and other support processes.  Users often don't see these (since they don't bring up UI), but they live there nonetheless.  I have an HP 7400 printer at home, and the printer driver for that runs 2 separate processes for each user (one of which hangs during shutdown every time a user logs off).

    Services: A special class of helper application, services have some significant advantages over helper applications (and some drawbacks).  Services can be centrally managed, and expose a common startup/shutdown interface.  They also can be automatically started at system boot, have strict dependencies, and can run in arbitrary user contexts (including elevated contexts).  On the other hand, it's difficult (and in many ways effectively impossible) to have services run in the context of the currently logged on user.  I'm a huge fan of services, but it's possible to totally overdo it.  In Windows Vista, there were a slew of new services introduced, and more and more applications are creating services, since the currently logged in user is no longer an administrator.  An example of a helper service is the WHSConnector service that comes with Windows Home Server (another of my current favorite products), and there are a bazillion others.

     

    I'm sure that there are other categories of applets, but these 4 appear to be the biggies.

     

    Tomorrow: So why are applets bad?

  • Larry Osterman's WebLog

    What's wrong with this code, part 20(!): Yet another reason that named synchronization objects are dangerous...

    • 38 Comments

    When you're doing inter-process communication, it's often necessary to use named synchronization objects to communicate state between the processes.  For instance, if you have a memory section that's shared between two processes, it's often convenient to use a named mutex on both processes to ensure that only one process is accessing the memory at a time.

    I recently had to fix a bug that was ultimately caused by a really common coding error when using named events.  I've taken the bug and stripped it down to just about the simplest form that still reproduced the error.

     

    const LPCWSTR EventName = L"MyEventName"; 
    DWORD WINAPI WorkerThread(LPVOID context) 
    { 
        HANDLE eventHandle = CreateEvent(NULL, TRUE, FALSE, EventName); 
        if (eventHandle == NULL) 
        { 
            return GetLastError(); 
        } 
    
        WaitForSingleObject(eventHandle, INFINITE); 
        // Do Some Work. 
        return 0; 
    } 
    
    int _tmain(int argc, _TCHAR* argv[]) 
    { 
        HANDLE threadHandle = CreateThread(NULL, 0, WorkerThread, NULL, 0, NULL); 
        HANDLE eventHandle = CreateEvent(NULL, TRUE, FALSE, EventName); 
    
        SetEvent(eventHandle); 
        WaitForSingleObject(threadHandle, INFINITE); 
        return 0; 
    }

    There are actually TWO things wrong with this code, and they're both really bad.  The second one won't become apparent until after the first one's found.

     

    Things that are not wrong:

    • Using CreateEvent in the worker thread instead of OpenEvent - remember, in the original incarnation of this code, the two components that deal with the event run in different processes - using CreateEvent allows you to protect against race conditions creating the event (if you call OpenEvent and follow it with a call to CreateEvent if the OpenEvent fails, there's a window where the other side could also call CreateEvent).
    • Calling CreateThread BEFORE calling CreateEvent - this was quite intentional to show off the potential for the race condition above.
    • There's limited error checking in this code.  While this code is not production quality, error handling could obfuscate the problem.
    • The _tmain/_TCHAR - this function's Unicode, VS stuck in the _T stuff in it's wizard.

     As always, kudos and explanations on Monday.

  • Larry Osterman's WebLog

    Innovation in the computer industry

    • 38 Comments

    The germ of this post was a comment I made on Robert Scoble's weblog, but it deserves broader distribution (that also explains why it's sort-of two ideas mashed together) :)

    One of the memes that I see going on in the boards (especially on /.) is that Microsoft never innovates; it only ever copies what others have done before.  I find this meme rather fascinating because it is SO wrong.  The thing is that the people who cite this meme set the bar impossibly high - by their definition, if an idea isn't the very first example of an idea, then it was copied, and thus wasn't innovative.

    The problem is that if the bar for "innovative" is that nobody has ever done it before, than the list of "innovations" in the PC industry is very, very, very small - the web is a repackaging of hypertext (HTML is a stripped down SGML), the MacIntosh was done before on the Lisa (which was done before on the Star (which was done before on the Perq (which was done before on the Alto))).

    In the past 25 years, I can only think of two or three truly innovative ideas that weren't rehashes of previous ideas:
          Spreadsheets
          DNS
          Clippy/Bob. (Don't laugh. Innovative doesn't necessarily mean "a good idea" - it means a NOVEL idea. I believe that Bob was the first shipping social UI (I may be wrong on this one)).

    Actually, the more I think about it, the number of things that are truly original is even smaller than I thought.

    For instance GUI operating systems like the Mac or Windows don't count, since there were GUI operating systems more than 25 years ago (the Perq as mentioned above).

    P2P filesharing like Kazaa or Gnutella don't count as innovative, because they are simply the Windows for Workgroups network browser extended to content and scaled up to the Internet, and WfW was just an extension of the Lan Manager browser made peer-to-peer.  Btw, to answer someone who posted in the Scoble comment thread: pure file sharing services like FTP have been around forever, but they have a discoverability issue.  The thing that makes a distributed data store (like file sharing services or the WWW) successful is the directory – consider what finding something on the web would be like without Google or its cousins.

    CD's and DVD's don't count, they are just another storage medium based on older technologies (and CDs are 22 years old anyway).

    Gnu/Linux isn't innovative by this definition; they're just rehashes of UNIX, which is almost 40 years old.

    Neither are Flight Simulators or other games, most of the classes of computer gaming available today were available in 1980 (they weren't as pretty, but the concepts were there). MMORPG's have their origins in the MUDs of the late 1970's and early 1980's.

    The bottom line is that defining innovation as strictly "Doing something that nobody has ever done before" is so restrictive that it's pointless. Instead, focus on what's new and different. That's where the TRUE innovation lies.

    As a simple example, as Robert mentioned in one of his comments, Microsoft Office was innovative. Each of the applications bundled in office wasn't new, but nobody had ever thought of creating a suite by packaging other products together.  And Office wasn't just innovative marketing. Up until the debut of Office, developers created suites like Lotus Symphony, Claris Works, etc...You bundled a spreadsheet, word processor, and database into a single application, which did none of the features as well as the stand-alone applications.

    The thing that made Office successful (and unbelievably innovative) was the incredible amount of synergy you got from the different applications - you could take a spreadsheet, drop a chart created from the spreadsheet into a word document, and have the word document be updated in real-time as the spreadsheet changed. For the PC industry, THAT was innovative, and 100% technical. And EACH of the applications was as full featured as the others - so you didn't sacrifice functionality for coolness.

    The thing is that innovation isn't always being the first with an idea.  Sometimes it's the little things that make a huge difference.

    The innovations in Windows NT are extraordinary (they really are).  Even the much maligned registry is innovative - before the registry existed, every app and its cousin had its own set of configuration files, each of which had its own syntax and standards.  This meant that it was extraordinarily difficult to write management tools to operate on disparate applications, which limited the ability of IT departments to manage diverse configurations.  In addition, there was no real story for per-user configuration in Windows; each application had to solve it in their own way (which usually meant that they didn't bother).  The registry provided a one-stop-shopping solution for a huge set of configuration issues.  This isn't to say that the registry couldn't be abused, it could.  But it was far better than the other solutions (when was the last time you lost a configuration change due to the filesystem not transacting writes to your config data - this was a common problem in pre-registry times).

    NT's ACL infrastructure was just another ACL infrastructure.  But it allowed for a staggering amount of flexibility in implementation - you can do things with the NT ACL infrastructure that are way beyond the concepts that the original creators of the ACL infrastructure intended.

    Robert's original post was a "letter to Bill" about upcoming innovations in content creation.  IMHO, Robert's 100% right-on - with the creation of blogging (and other similar forms of EASY content authoring like moblogging and audioblogging, and podcasting, etc), there is another wave of web content coming, and it's important for MS to be ahead of the curve and not catching up.

    When the web first debuted, everyone talked about how the web provided a level playing field for content creation.  And in many ways that was the case,  But the reality is that barrier to entry for publishing content on the web was way too high - you had to find hosting space, then author pages individually.

    This has affected me personally I've had hundreds of stories and dozens of articles pent up inside me for years now that I couldn't publish because the barrier to entry was too high.  Publishing by hand had too much overhead for me, I didn't want to take the time to design and implement the content management system to handle all the publishing aspects.  And then there was the issue of driving traffic to the site - there's no point in putting up content if people aren't going to read it.  Until services like Blogger.com etc came along, there wasn't an easy mechanism for updating content and informing consumers that the content had changed. Blogging/RSS/whatever gives all of these, which means that the complexity level required to produce content has been lowered again. 

    But blogging and its friends have lowered the bar to the point where Abby could easily start producing content.  The barriers to entry are low enough that ANYONE can start a blog, and the creation of content has been democratized.  Creation of content is now trivial, and that's a good thing.

     

  • Larry Osterman's WebLog

    Running Non Admin

    • 38 Comments

    There’s been a fascinating process going on over here behind the curtains.  With the advent of XP SP2, more and more people are running as non administrative users.  Well, it’s my turn to practice what I preach, I’ve taken the plunge on my laptop and my home machine, I’m now running as a non admin user (I can’t do it on my development machine at work for the next few weeks for a variety of reasons).

    The process so far has been remarkably pain free, but there have been some “interesting” idiosyncrasies.  First off, I’ve been quite surprised at the number of games that have worked flawlessly.  I was expecting to have major issues, but none so far, with the exception of Asheron’s Call.  Annoyingly, the problem with AC isn’t the game itself, it’s with Microsoft’s Gaming Zone software, which insists on modifying files in the C:\Program Files directory. 

    Aaron Margosis’ blog posts about running as a limited user have been extremely helpful as well.

    Having said that, there are some oddities I’ve noticed.  First off: There seem to be a lot of applications that “assume” that they know what the user’s going to do.  For instance, if you double click on the time in the system tray, it pops up with “You don’t have the proper privilege level to change the System Time”.  This is a completely accurate statement, since modifying the time requires the SeSystemTime privilege, which isn’t granted to limited users.  But it assumes that the reason that I was clicking on the time was to change the time.  But maybe I wanted to use the date&time control panel as a shortcut to the calendar?  I know of a bunch of users that call action of double clicking on the time in the taskbar as invoking the “cool windows calendar”, they don’t realize that they’re just bringing up the standard date&time applet.  If I don’t have the SeSystemTime privilege, then why not just grey out the “OK” button?  Let me navigate the control but just prevent me from changing things.

    Similarly, the users control panel applet prompts you with a request to enter your credentials.  Why?  There are lots of things a limited user can do with the users control panel applet (enumerating groups, enumerating users, enumerating group membership, setting user information).  But the control panel applet ASSUMES that the user wants to manipulate the state of the other users.  It’s certainly true that most of the useful functionality of that control panel applet requires admin access.  But it should have waited until the user attempted to perform an action that was denied before it prompted the user for admin credentials.

    From my standpoint, these examples violate two of the principals of designing interfaces that involve security: 

    1)      Don’t tell the user they can’t do something until you’ve proven that they can’t do it.

    2)      Don’t assume what access rights the user needs to perform an operation. 

    The date&time control panel violates the first principal.  The user might be interacting with the control panel for reasons other than changing the time.  It turns out that the reason for this is that the date&time applet violates the principle of least privilege by enabling the SeDateTime privilege, running the control panel applet, and then disabling the privilege.  If the control panel applet had waited until the user clicked on the “Apply” button before it enabled the privilege (and then failed when the enable privilege failed), it would have better served the user IMHO.

    The users control panel applet violates the second principal.  In the case of the users control panel, it assumed that I was going to do something that required admin access.   This may in fact be a reasonable assumption given the work that the users control panel applet does (its primary task is to manage local group membership).  But the applet assumes up front that the user has to be an administrator to perform the action.  There may in fact be other classes of users that can access the information in the users control panel – as an example, members of the domains’ “account operators” group may very well be able to perform some or all the actions that the users control panel applet performs.  But the control panel applet doesn’t check for that – it assumes that the user has to be a member of the local administrators group to use the control panel applet.  Interestingly enough, this behavior only happens on XP PRO when joined to a domain.  If you’re not joined to a domain, the users control panel applet allows you to change your user information without prompting you – even as a limited user.   Peter Torr also pointed out that the computer management MCC snapin (compmgmt.msc) does the “right” thing – you can interact with the UI, perform actions (adding users to groups, etc), and it’s only when you click the “Apply” button that it fails.  The snap-in doesn’t know what’s allowed or not, it just tries the operation, and reports the failure to the user.

    This is a really tough problem to solve from a UI perspective – you want to allow the user to do their work, but it’s also highly desirable that you not elevate the privilege of the user beyond the minimum that’s required for them to do their job.  The good news is that with more and more developers (both at Microsoft and outside Microsoft) running as non administrative users, more and more of these quirks in the system will be fixed.

     

    Edit: Thanks Mike :)
  • Larry Osterman's WebLog

    Laptops and Kittens....

    • 38 Comments
    I mentioned the other day that we have four cats currently.  Three of them are 18 month old kittens (ok, at 18 months, they're not kittens anymore, but we still refer to them as "the kittens").

    A while ago, one of them (Aphus, we believe) discovered that if they batted at Valorie's laptop, they could remove the keys from the laptop, and the laptop keys made great "chase" toys.  Valorie has taken to locking her laptop up in a tiny computer nook upstairs as a result, but even with that, they somehow made off with her "L" key.  We've not been able to find it even after six months of looking.  To get her computer up and running, we replaced the "L" key with the "windows" key. Fortunately she's a touch typist, and thus never looks at her keyboard - when she does, she freaks out.

    Last night, I left a build running on my laptop when I went to bed.  Valorie mentioned that it would probably be a bad idea to do this, since the kittens were on the loose.

    Since I couldn't close the laptop without shutting down the build, I hit on what I thought was a great solution.  I put the laptop in two plastic bags, one on each side of the laptop (sorry about the mess on the table :)):

    I went to bed confident that I'd outsmarted the kittens.  My laptop would remain safe.

    Well, this morning, I got up, and went downstairs (you can see Sharron's breakfast cereal on the table to the top right).  I asked the kids if there had been any problems, and Daniel, with his almost-teenager attitude said "Yeah, the kittens scattered the keys on your laptop all over the kitchen".

    I  figured he was just twitting me, until I went to check on the computer...

    Oh crud...

    There were the keys, sitting in a pile where Sharron had collected them...

    I love my cats, I really do...

    The good news is that I managed to find all the keys, although I was worried about the F8 key for a while.

  • Larry Osterman's WebLog

    What does style look like, part 7

    • 37 Comments
    Over the course of this series on style, I've touched on a lot of different aspects, today I want to discuss aspects C and C++ style specifically.

    One of the things about computer languages in general is that there are often a huge number of options available to programmers to perform a particular task.

    And whenever there's a choice to be made while writing programs, style enters into the picture.  As does religion - whenever the language allows for ambiguity, people tend to get pretty religious about their personal preferences.

    For a trivial example, consider the act of incrementing a variable.  C provides three different forms that can be used to increment a variable. 

    There's:

    • i++,
    • ++i,
    • i+=1, and
    • i=i+1.

    These are all semantically identical, the code generated by the compiler should be the same, regardless of which you chose as a developer (this wasn't always the case, btw - the reason that i++ exists as a language construct in the first place is that the original C compiler wasn't smart enough to take advantage of the PDP-8's built-in increment instruction, and i++ allowed a programmer to write code that used it).

    The very first time I posted a code sample, I used my personal style, of i+=1 and got howls of agony from my readers.  They wanted to know why on EARTH I would use such a stupid construct when i++ would suffice.  Well, it's a style issue :)

    There are literally hundreds of these language specific style issues.  For instance, the syntax of an if statement (or a for statement) is:

    if (conditional) statement

    where statement could be either a single line statement or a compound statement.  This means that it's totally legal to write:

    if (i < 10)
        i = 0;

    And it's totally legal to write

    if (i < 10)
    {
        i = 0;
    }

    The statements are utterly identical from a semantic point of view.  Which of the two forms you choose is a style issue.  Now, in this case, there IS a fairly strong technical reason to choose the second form over the first - by putting the braces in always, you reduce the likelihood that a future maintainer of the code will screw up and add a second line to the statement.  It also spaces out the code (which is a good thing IMHO :) (there's that personal style coming back in again)).

    Other aspects of coding that ultimately devolve to style choices are:

    if (i == 10)

    vs

    if (10 == i)

    In this case, the second form is often used to prevent the assignment within an if statement problem - it's very easy to write:

    if (i = 10)

    which is unlikely to be what the developer intended.  Again, this is a style issue - by putting the constant on the left of the expression, you cause the compiler to generate an error when you make this programming error.  Of course, the compiler has a warning, C4706, to catch exactly this situation, so...

    Another common stylistic convention that's often found is:

    do {
        < some stuff >
    } while (false);

    This one exists to allow the programmer to avoid using the dreaded "goto" statement.  By putting "some stuff" inside the while loop, it enables the use of the break statement to exit the "loop". Personally, I find this rather unpleasant, a loop should be a control construct, not syntactic sugar to avoid language constructs.

    Speaking of goto...

    This is another language construct that people either love or hate.  In many ways, Edsger was totally right about goto - it is entirely possible to utterly misuse goto. On the other hand, goto can be a boon for improving code clarity.  

    Consider the following code:

    HRESULT MyFunction()
    {
        HRESULT hr;

        hr = myCOMObject->Method1();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method2();
            if (hr == S_OK)
            {
                hr = myCOMObject->Method3();
                if (hr == S_OK)
                {
                    hr = myCOMObject->Method4();
                }
                else
                {
                    hr = myCOMObject->Method5();
                }
            }
        }
        return hr;
    }

    In this really trivial example, it's vaguely clear what's going on, but it suffices.  One common change is to move the check for hr outside and repeatedly check it for each of the statements, something like:

        hr = myCOMObject->Method1();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method2();
        }
        if (hr == S_OK)
     

    What happens when you try that alternative implementation?

    HRESULT MyFunction()
    {
        HRESULT hr;

        hr = myCOMObject->Method1();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method2();
        }
        if (hr == S_OK)
        {
            hr = myCOMObject->Method3();
            if (hr == S_OK)
            {
                hr = myCOMObject->Method4();
            }
            else
            {
                hr = myCOMObject->Method5();
            }
        }
        return hr;
    }

    Hmm.  That's not as nice - some of it's been cleaned up, but the Method4/Method5 check still requires that you indent an extra level.

    Now consider what happens if you can use gotos:

    HRESULT MyFunction()
    {
        HRESULT hr;

        hr = myCOMObject->Method1();
        if (hr != S_OK)
        {
            goto Error;
        }
        hr = myCOMObject->Method2();
        if (hr != S_OK)
        {
            goto Error;
        }
        hr = myCOMObject->Method3();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method4();
        }
        else
        {
            hr = myCOMObject->Method5();
        }
        if (hr != S_OK)
        {
            goto Error;
        }
    Cleanup:
        return hr;
    Error:
        goto Cleanup;
    }

    If you don't like seeing all those gotos, you can use a macro to hide them:

    #define IF_FAILED_JUMP(hr, tag) if ((hr) != S_OK) goto tag
    HRESULT MyFunction()
    {
        HRESULT hr;

        hr = myCOMObject->Method1();
        IF_FAILED_JUMP(hr, Error);

        hr = myCOMObject->Method2();
        IF_FAILED_JUMP(hr, Error);

        hr = myCOMObject->Method3();
        if (hr == S_OK)
        {
            hr = myCOMObject->Method4();
            IF_FAILED_JUMP(hr, Error);
        }
        else
        {
            hr = myCOMObject->Method5();
            IF_FAILED_JUMP(hr, Error);
        }

    Cleanup:
        return hr;
    Error:
        goto Cleanup;
    }

    Again, there are no right answers or wrong answers, just choices.

    Tomorrow, wrapping it all up.

  • Larry Osterman's WebLog

    What's wrong with this code, part 15

    • 37 Comments
    Work's been pretty hectic lately, that's why so few posts this month, but I ran into a real bug in my code recently that I realized would make a GREAT "What's wrong with this code" post.

     

    HRESULT CNotification::GenerateEvent 
       (     
          PNOTIFICATION_BLOCK NotificationBlock
       ) 
    { 
        HRESULT hr = S_OK; 
        BYTE *buffer = NULL; 
        DWORD bufferSize = sizeof(NOTIFICATION_HEADER) + 
                                NotificationBlock->BlockSize; 
        if (NotificationBlock->BlockSize < sizeof(NOTIFICATION_BLOCK)) 
        { 
            hr = E_INVALIDARG; 
            goto Error; 
        } 
        else 
        { 
            buffer = new BYTE[bufferSize]; 
            if (buffer == NULL) 
            { 
                hr = E_OUTOFMEMORY; 
                goto Error; 
            } 
            PNOTIFICATION_HEADER notificationHeader = (PNOTIFICATION_HEADER)buffer; 
            PNOTIFICATION_BLOCK notificationBuffer; 
    
            ZeroMemory(buffer, bufferSize); 
            notificationBuffer = (PNOTIFICATION_BLOCK)(notificationHeader + 1); 
    
            <Fill in the EVENT_TRACE_HEADER within the NOTIFICATION_HEADER structure>
    
            CopyMemory(notificationBuffer, NotificationBlock, NotificationBlock->BlockSize); 
    
            hr = HRESULT_FROM_WIN32(TraceEvent(_TraceHandle, (PEVENT_TRACE_HEADER)&notificationHeader._TraceHeader));
            if (hr != S_OK) 
            { 
                goto Error; 
            } 
        } 
    Cleanup: 
        delete []buffer; 
        return hr; 
    
    Error: 
        goto Cleanup; 
    }

    Pretty straightforward, but it's got a REALLY nasty bug in it (I was SO embarrassed when I found it).

     

    As always, kudos and mea culpas next post.

    Edit1: Fixed typo (mediaBuffer->buffer).  Also fixed NOTIFICATIONBLOCK that should be PNOTIFICATIONBLOCK

  • Larry Osterman's WebLog

    Critical Driver or Cargo Cult Programming?

    • 37 Comments

    I've been self hosting Vista on my laptop since sometime in January.  Every Monday morning, without fail, I installed the latest build available from the "main" windows branch, and tried it.

    There have been good builds and bad builds - the first few were pretty painful, everything since sometime in March has been wonderfully smooth.

    But sometime late in May, things changed for the worse.  Weekly builds installed just fine on my main development machine, but my laptop would get about 3/4ths of the way through the install and stop after a reboot complaining about a problem with the critical system driver <driver>.sys.

    Of course, I filed a bug on the problem and moved on - every week I'd update my laptop and it'd fail.  While I was away on vacation, the guys looking into the bug finally figured out what was happening. 

    The first part of the problem was easy - something was causing <driver>.sys to fail to load (we don't know what).  But that didn't explain  the unbootable system.

    Well, the <driver>.sys driver is the modem driver for my laptop.  Eventually one of the setup devs figured the root cause.  For some totally unknown reason, their inf has the following lines:

    [DDInstall.Services]
    AddService=<driver>_Service_Inst

    [<driver>_Service_Inst]
    StartType=0

    If you go to msdn and look up DDInstall.Services, you get this page.

    If you follow the documentation a bit you find the documentation for the service install section which describes the StartType key - it's the same as the start type for Windows services.

    In particular, you find:

    StartType=start-code
    Specifies when to start the driver as one of the following numerical values, expressed either in decimal or, as shown here, in hexadecimal notation.
    0x0 (SERVICE_BOOT_START)
    Indicates a driver started by the operating system loader.

    This value must be used for drivers of devices required for loading the operating system.

    0x1 (SERVICE_SYSTEM_START)
    Indicates a driver started during operating system initialization.

    This value should be used by PnP drivers that do device detection during initialization but are not required to load the system.

    For example, a PnP driver that also can detect a legacy device should specify this value in its INF so that its DriverEntry routine will be called to find the legacy device, even if that device cannot be enumerated by the PnP manager.

    0x2 (SERVICE_AUTO_START)
    Indicates a driver started by the service control manager during system startup.

    This value should never be used in the INF files for WDM or PnP device drivers.
    0x3 (SERVICE_DEMAND_START)
    Indicates a driver started on demand, either by the PnP manager when the corresponding device is enumerated or possibly by the service control manager in response to an explicit user demand for a non-PnP device.

    This value should be used in the INF files for all WDM drivers of devices that are not required to load the system and for all PnP device drivers that are neither required to load the system nor engaged in device detection.

    0x4 (SERVICE_DISABLED)
    Indicates a driver that cannot be started.

    This value can be used to temporarily disable the driver services for a device, but a device/driver cannot be installed if this value is specified in the service-install section of its INF file.

    So in this case, the authors of the modem driver decided that their driver was a boot time critical driver - which, as the documentation clearly states is only intended for drivers required to load the operating system.

    So I'll leave it up to you to decide - is this an example of cargo cult programming, or did the authors of this modem driver REALLY think that the driver is a critical system driver?

    What makes things worse is that this is a 3rd party driver - we believe that their INF is in error, but we can't fix it because it's owned by the 3rd party.  Our only choice is to baddriver it and prevent Vista from loading that particular driver.  The modem chip in question hasn't been made for many, many years, the vendor for that chip has absolutely no interest in supporting it on Vista, so we can't get it fixed (the laptop is old enough that it's out of OEM support, so there's no joy from that corner either - nobody wants to support this hardware anymore).

    Please note: This is NOT an invitation for a "If only the drivers were open source, then you could just fix it" discussion in the comments thread.  The vendor for the modem driver owns the rights to their driver, they get to choose whether or not they want to support it, not Microsoft.

     

  • Larry Osterman's WebLog

    Where did the second parties go?

    • 37 Comments

    We were chatting at lunch the other day about 3rd parties building solutions on the audio engine.

    One of the people in my group asked "Why do we call them 3rd parties?"

     

    It's one of those "things that make you go hmm".

    There's general consensus in the business world that the people/companies who build a platform are first party developers (it doesn't matter if the platform is Windows, Photoshop, or Quake III).

    There's also general consensus that the people who build solutions ON those platforms (so applications on Windows, Photoshop Plugins on Photoshop, <pick your favorite game> on the Quake III engine) are called 3rd party developers.

    So we've covered 1st and 3rd party developers, what about the 2nd party developers?

    Wikipedia's definition for 3rd party developers is consistent with mine, but they describe 2nd party developers as developers operating under contract to 1st party developers -but they also say it's not a part of standard business practices.

     

    So my question is: "Whatever happened to the second party developers?  Where did they go?"

    Bonus question: From Microsoft's perspective, Adobe is a 3rd party developer, even though they build a platform.  If I'm a developer working on a Photoshop plugin, am I 4th party developer from the eyes of Microsoft?

  • Larry Osterman's WebLog

    Remember the blibbet

    • 36 Comments

    There was a thread on Channel9 that got me to remember the blibbet.

    "The blibbet?"  What on earth is a blibbet?

    The blibbet is the "O" in the 2nd Microsoft logo:

    Those of us who've been at Microsoft for a long time all have fond memories of the blibbet, which was cruely killed off in a fit of corporate ire in 1987.  Not only was it our corporate logo, but if you look at Microsoft binders from the time (1982-1987), you'll see that the blibbet is used as a watermark on it.

    At one point, I had a "save the blibbet" button, but unfortunately, I can't seem to find it (otherwise I'd post a photo of it).

    Periodically you find reminders of the blibbet around campus.  For example, the cafeterias used to offer a "Blibbet Burger" - a double cheeseburger with bacon, IIRC.

    I miss the blibbet :)  Somehow, it reminds me of Herbie (or any other VW bug) - cute and friendly :)

    Edit: Added more after a little bird sent me the image..

    When Microsoft announced that the would be retiring the blibbet, a number of employees mounted a fruitless "Save the Blibbet" campaign to retain the corporate icon.

    Unfortunately, the suits won :(

     

  • Larry Osterman's WebLog

    Hugarian notation - it's my turn now :)

    • 36 Comments

    Following on the heals of Eric Lippert’s posts on Hungarian and of course Rory Blyth’s classic “Die, Hungarian notation… Just *die*”, I figured I’d toss my hat into the fray (what the heck, I haven’t had a good controversial post in a while).

    One thing to keep in mind about Hungarian is that there are two totally different Hungarian implementations out there.

    The first one, which is the one that most people know about, is “Systems Hungarian”.  System’s Hungarian is also “Hungarian-as-interpreted-by-Scott-Ludwig” (Edit: For Scott's side of this comment, see here - the truth is better than my original post).  In many ways, it’s a bastardization of “real” (or Apps) Hungarian as proposed by Charles Simonyi. 

    Both variants of Hungarian have two things in common.  The first is the concept of a type-related prefix, and the second is a suffix (although the Systems Hungarian doesn’t use the suffix much (if at all)).  But that’s where the big difference lies.

    In Systems Hungarian, the prefix for a type is almost always related to the underlying data type.  So a parameter to a Systems Hungarian function might be “dwNumberOfBytes” – the “dw” prefix indicates that the type of the parameter is a DWORD, and the “name” of the parameter is “NumberOfBytes”.  In Apps Hungarian, the prefix is related to the USE of the data.  The same parameter in Apps Hungarian is “cb” – the “c” prefix indicates that the parameter is a type, the “b” suffix indicates that it’s a byte parameter.

    Now consider what happens if the parameter is the number of characters in a string.  In Systems Hungarian, the parameter might be “iMaxLength”.  It might be “cchWideChar”.  There’s no consistency between different APIs that use Systems Hungarian.  But in Apps Hungarian, there is only one way of representing the parameter; the parameter would be “cch” – the “c” prefix again indicates a count, the “ch” type indicates that it’s a character.

    Now please note that most developers won’t use “cch” or “cb” as parameters to their routines in Apps Hungarian.  Let’s consider the Win32 lstrcpyn function:

     LPTSTR lstrcpyn(     
    LPTSTR lpString1,
    LPCTSTR lpString2,
    int iMaxLength
    );

    This is the version in Systems Hungarian.  Now, the same function in Apps Hungarian:

     LPTSTR Szstrcpyn(     
    LPTSTR szDest,
    LPCTSTR szSrc,
    int cbLen
    );

    Let’s consider the differences.  First off, the name of the function changed to reflect the type returned by the function – since it returns an LPTSTR, which is a variant of a string, the function name changed to “SzXxx”.  Second, the first two parameters name changed.  Instead of “lpString1” and “lpString2”, they changed to the more descriptive “szSrc” and “szDest”.  The “sz” prefix indicates that the variable is a null terminated string.  The “Src” and “Dest” are standard suffixes, which indicate the “source” and “destination” of the operation.  The iMaxLength parameter which indicates the number of bytes to copy is changed to cbLen – the “cb” prefix indicates that it’s a count of bytes, the standard “Len” suffix indicates that it’s a length to be copied.

    The interesting thing that happens when you convert from Systems Hungarian to Apps Hungarian is that now the usage of all the parameters of the function becomes immediately clear to the user.  Instead of the parameter name indicating the type (which is almost always uninteresting), the parameter name now contains indications of the usage of the parameter.

    The bottom line is that when you’re criticizing Hungarian, you need to understand which Hungarian you’re really complaining about.  Hungarian as defined by Simonyi isn’t nearly as bad as some have made it out to be.

    This is not to say that Apps Hungarian was without issue.  The original Hungarian specification was written by Doug Klunder in 1988.  One of the things that was missing from that document was a discussion about the difference between “type” and “intent” when defining prefixes.  This can be a source of a great confusion when defining parameters in Hungarian.  For example, if you have a routine that takes a pointer to a “foo” parameter to the routine, and internally the routine treats the parameter as single pointer to a foo, it’s clear that the parameter name should be “pfoo”.  However, if the routine treats the parameter as an array of foo’s, the original document was not clear about what should happen – should the parameter be “pfoo” or “rgfoo”.  Which wins, intent or type?  To me, there’s no argument, it should be intent, but there have been some heated debates about this over the years.  The current Apps Hungarian document is quite clear about this, intent wins.

    One other issue with the original document was that it predated C++.  So concepts like classes weren’t really covered and everyone had to come up with their own standard.  At this point those issues have been resolved.  Classes don’t have a “C” prefix, since a class is really just a type.  Members have “m_” prefixes before their actual name.  There are a bunch of other standard conventions but they’re relatively unimportant.

    I used Hungarian exclusively when I was in the Exchange team; my boss was rather a Hungarian zealot and he insisted that we code in strict Apps Hungarian.  Originally I chafed at it, having always assumed that Hungarian was stupid, but after using it for a couple of months, I started to see how it worked.  It certainly made more sense than the Hungarian I saw in the Systems division.  I even got to the point where I could understand what an irgch would without even flinching.

    Now, having said all that, I don’t use Hungarian these days.  I’m back in the systems division, and I’m using a home-brewed coding convention that’s based on the CLR standards, with some modifications I came up with myself (local variables are camel cased, parameters are Pascal cased (to allow easy differentiation between parameters and local variables), class members start with _ as a prefix, globals are g_Xxx).  So far, it’s working for me.

    I’ve drunk the kool-aid from both sides of the Hungarian debate though, and I’m perfectly happy working in either camp.

     

  • Larry Osterman's WebLog

    What's wrong with this code, part lucky 13

    • 35 Comments
    Today's example is a smidge long, I've stripped out everything I can possibly imagine stripping out to reduce size.

    This is a very real world example that we recently hit - only the names have been changed to protect the innocent.

    I've used the built-in C++ decorations for interfaces, but that was just to get this stuff to compile in a single source file, it's not related to the bug.

    extern CLSID CLSID_FooDerived;
    [
        object,
        uuid("0A0DDEDC-C422-4BB3-9869-4FED020B66C5"),
    ]
    __interface IFooBase : IUnknown
    {
        HRESULT FooBase();
    };

    class CFooBase: public IFooBase
    {
        LONG _refCount;
        virtual ~CFooBase()
        {
            ASSERT(_refCount == 0);
        };
    public:
        CFooBase() : _refCount(1) {};
        virtual HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** ppUnk)
        {
            HRESULT hr=S_OK;
            *ppUnk = NULL;
            if (iid == IID_FooBase)
            {
                AddRef();
                *ppUnk = reinterpret_cast<void *>(this);
            }
            else if (iid == IID_IUnknown)
            {
                AddRef();
                *ppUnk = reinterpret_cast<void *>(this);
            }
            else
            {
                hr = E_NOINTERFACE;
            }
            return hr;
        }
        virtual ULONG STDMETHODCALLTYPE AddRef(void)
        {
            return InterlockedIncrement(&_refCount);
        }
        virtual ULONG STDMETHODCALLTYPE Release(void)
        {
            LONG refCount;
            refCount = InterlockedDecrement(&_refCount);
            if (refCount == 0)
            {
                delete this;
            }
            return refCount;

        }
        STDMETHOD(FooBase)(void);
    };
    class ATL_NO_VTABLE CFooDerived :
        public CComObjectRootEx<CComMultiThreadModel>,
        public CComCoClass<CFooDerived, &CLSID_FooDerived>,
        public CFooBase
    {
        virtual ~CFooDerived();
        public:
        CFooDerived();
        DECLARE_NO_REGISTRY()
        BEGIN_COM_MAP(CFooDerived)
            COM_INTERFACE_ENTRY(IFooBase)
        END_COM_MAP()
        DECLARE_PROTECT_FINAL_CONSTRUCT()

    };

    OBJECT_ENTRY_AUTO(CLSID_FooDerived, CFooDerived)

     

    As always, tomorrow I'll post the answers along with kudos and mea culpas.

    Edit: Fixed missing return value in Release() - without it it doesn't compile.  Also added the addrefs - my stupid mistake.  mirobin gets major props for those ones.

  • Larry Osterman's WebLog

    Converting between bases...

    • 35 Comments
    The other day there was a thread on Channel9 where "orangie" was complaining about the fact that his programming class required him to learn how to convert between different bases (base 10->2->whatever).

    In reality, it's actually quite useful to know how to do the conversions, because knowing how the computer represents values can be remarkably important, especially in a CS course.  In my case, we covered base conversions in the "intro to digital circuits" class, one of our projects was to write a circuit that would take a binary input (from a set of switches) and convert it to hex.

    It turns out that base conversion is a core part of "New Math", Tom Lehrer's "New Math" song is all about how you do arithmetic in different bases.  They're still teaching "New Math" in classes, converting between bases is covered in the 5th or 6th grade at least at Wellington (the school where Valorie used to work).  This makes sense, because it's important that students have a fundamental understanding about how numeric operations work, and what makes up a number - understanding different base systems helps to figure it out.

    Most of the time, I do my base conversions using this:

    I've had it for over 20 years now, and it's still going strong (gotta love HP's engineering).  Until I looked it up on eBay, I hadn't realized it was worth about $100, go figure.

    But, for those times that I need to convert by hand, it's actually pretty simple, especially for some very common conversions (hex to binary, and hex to octal).

    For hex to binary, all you need to do is to memorize the bit pattern for the 16 possible hex values (5 is 0101, 6 is 0110, f is 1111, etc).

    Then it's just a matter of string concatenation - you convert each digit to binary, string them together and convert back to whatever your destination base is.

    Octal to binary is similar, you just have to remember values from 0-8 (which is easy if you've memorized the hex values :)).

    My personal favorites are converting from hex to octal (and vice versa) because you don't have to do any math.  You do it by converting to a common base, binary in this case.

    So 0x123456789abcdef in hex converts to:

    0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

    or:

    000100100011010001010110011110001001101010111100110111101111

    Regrouping:

    000 100 100 011 010 001 010 110 011 110 001 001 101 010 111 100 110 111 101 111

    And converting from binary to octal:

    04432126361152746757

    Tada!  Hex to octal conversion without having to do ugly math :)

    Obviously this works for any power of two, it's just that octal and hex are the two most common.

    Now once you want to include converting to decimal, it's gets a bit harder - then whipping out the calculator's usually the best solution.  If you still MUST do the conversion by hand, it's often easier to do it with the octal representation than the hex representation.  In this case, it's:

        4*180124248109481984
    +  4*22515531013685248
    +  3*2814441376710656
    +  2*351805172088832
    +  1*43975646511104
    +  2*5496955813888
    +  6*68719476736
    +  3*85889934592
    +  6*1073741824
    +  1*134217728
    +  1*16777216
    +  5*2097152
    +  2*262144
    +  7*32768
    +  4*4096
    +  6*512
    +  7*64
    +  5*8
    +  7
    -------------------------------------
    81985529216486895

    Easy, right?

    Oh, and before someone asks, no, they don't expect the 5th graders to convert numbers that are that large :)

     

    Edit: Toned down the language about orangie, and added the comment about Tom Lehrer's "New Math" song, it wasn't my intention to embarass orangie.

     

  • Larry Osterman's WebLog

    Why no Easter Eggs?

    • 35 Comments

    Yesterday's post caused a bit of a furor in the comments thread.  A large number of people leaving comments (and others) didn't understand why the OS division has a "no Easter Eggs" policy.

    If you think about this, it's not really that surprising.  One of the aspects of Trustworthy Computing is that you can trust what's on your computer.  Part of that means that there's absolutely NOTHING on your computer that isn't planned.  If the manufacturer of the software that's on every desktop in your company can't stop their developers from sneaking undocumented features into the product (even features as relatively benign as an Easter Egg), how can you be sure that they've not snuck some other undocumented feature into the code.

    Even mandating that you have access to the entire source code to the product doesn't guarantee that - first off, nobody in their right mind would audit all 10 million+ lines of code in the product before deployment, and even if you DID have the source code, that doesn't mean anything - Ken Thompson made that quite clear in his Turing Award lecture.  Once you've lost the trust of your customers, they're gone - they're going to find somewhere else to take their business.

    And there are LOTS of businesses and governments that have the sources to Microsoft products.  Imagine how they'd react if (and when) they discovered the code?  Especially when they were told that it was a "Special Surprise" for our users.  Their only reaction would be to wonder what other "Special Surprises" were in the code.

    It's even more than that.  What happens when the Easter Egg has a security bug in it?  It's not that unplausable - the NT 3.1 Easter Egg had a bug in it - the easter egg was designed to be triggered when someone typed in I LOVE NT, but apparently it could also be triggered by any anagram of "I LOVE NT" - as a result, "NOT EVIL" was also a trigger.

    Going still further, Easter Eggs are percieved as a symptom of bloat, and lots of people get upset when they find them.  From Adequacy.org:

    Now if you followed the link above and read the article you may be thinking to yourself...
  • Is this what MS developers do when they should be concentrating on security?
  • How often do they audit their code?
  • What's to stop someone from inserting malicious code?
  • Is this why I pay so much for Windows and MS Office?
  • I know other non-MS software contains EEs but this is rediculous.
  • One more reason why peer review is better as EEs and malicious code can be removed quickly.
  • Is this why security patches takes so long to be released?
  • This is TrustWorthy Computing!?!
  • From technofile:

    Even more disturbing is the vision of Microsoft as the purveyor of foolishness. Already, the cloying "Easter eggs" that Microsoft hides away in its software -- surprise messages, sounds or images that show off the skill of the programmers but have no usefulness otherwise -- are forcing many users to question the seriousness of Microsoft's management.
       A company whose engineers can spend dozens or even hundreds of hours placing nonsensical "Easter eggs" in various programs would seem to have no excuse for releasing Windows with any bugs at all. Microsoft's priorities are upside down if "Easter egg" frills and other non-essential features are more important than getting the basic software to work right.

    From Agathering.net:

    "and some users might like to know exactly why the company allows such huge eggs to bloat already big applications even further"

    I've been involved in Easter Eggs in the past - the Exchange 5.0 POP3 and NNTP servers had easter eggs in them.  In our case, we actually followed the rules - we filed a bug in the database ("Exchange POP3 server doesn't have an Easter Egg"), we had the PM write up a spec for it, the test lead developed test cases for it.  We even contacted the legal department to determine how we should reference the contingent staff that were included in the Easter Egg. 

    But it didn't matter - we still shouldn't have done it.  Why?  Because it was utterly irresponsible.  We didn't tell the customers about it, and that was unforgivable, ESPECIALLY in a network server.  What would have happened if there had been a buffer overflow or other security bug in the Easter Egg code?  How could we POSSIBLY explain to our customers that the reason we allowed a worm to propagate on the internet was because of the vanity of our deveopers?  Why on EARTH would they trust us in the future? 

    Not to mention that we messed up.  Just like the NT 3.1 Easter Egg, we had a bug in our Easter Egg, and we would send the Easter Egg out in response to protocol elements other than the intended ones.  When I was discussing this topic with Raymond Chen, he pointed out that his real-world IMAP client hit this bug - and he was more than slightly upset at us for it.

     

    It's about trust.  It's about being professional.  Yeah, it's cool seeing your name up there in lights.  It's cool when developers get a chance to let loose and show their creativity.  But it's not cool when doing it costs us the trust of our customers.

     

    Thanks to Raymond, KC and Betsy for their spirited email discussion that inspired this post, and especially to Raymond for the awesome links (and the dirt on my broken Easter Egg).

     

    Edit: Fixed some html wierdness.

    Edit2: s/anacronym/anagram/

  • Larry Osterman's WebLog

    Why does Windows still place so much importance on filenames?

    • 35 Comments

    Earlier today, Adrian Kingsley-Hughes posted a rant (his word, not mine) about the fact that Windows still relies on text filenames.

    The title says it all really. Why is it that Windows still place so much importance on filenames.

    Take the following example - sorting out digital snaps. These are usually automatically given daft filenames such as IMG00032.JPG at the time they are stored by the camera. In an ideal world you’d only ever have one IMG00032.JPG on your entire system, but the world is far from perfect. Your camera might decide to restart its numbering system, or you might have two cameras using the same naming format. What happens then?

    I guess I’m confused.  I could see a *very* strong argument against Windows dependency on file extensions, but I’m totally mystified about why having filenames is such a problem.

    At some level, Adrian’s absolutely right – it IS possible to have multiple files on the hard disk named “recipe.txt”.  And that’s bad.  But is it the fault of Windows for allowing multiple files to have colliding names? Or is it the fault of the user for choosing poor names?  Maybe it’s a bit of both.

    What would a better system look like?  Well Adrian gives an example of what he’s like to see:

    Why? Why is the filename the deciding factor? Why not something more unique? Something like a checksum? This way the operating system could decide is two files really are identical or not, and replace the file if it’s a copy, or create a copy if they are different. This would save time, and dramatically reduce the likelihood of data loss through overwriting.

    But how would that system work?  What if we did just that.  Then you wouldn’t have two files named recipe.txt (which is good).

    Unfortunately that solution introduces a new problem: You still have two files.  One named “2B1015DB-30CA-409E-9B07-234A209622B6” and the other named “5F5431E8-FF7C-45D4-9A2B-B30A9D9A791B”. It’s certainly true that those two files are uniquely named and you can always tell them apart.  But you’ve also lost a critical piece of information: the fact that they both contain recipes.

    That’s the information that the filename conveys.  It’s human specific data that describes the contents of the file.  If we were to go with unique monikers, we’d lose that critical information.

    But I don’t actually think that the dependency on filenames is really what’s annoying him.  It’s just a symptom of a different problem. 

    Adrian’s rant is a perfect example of jumping to a solution without first understanding the problem.  And why it’s so hard for Windows UI designers to figure out how to solve customer problems – this example is a customer complaint that we remove filenames from Windows.  Obviously something happened to annoy Adrian that was related to filenames, but the question is: What?  He doesn’t describe the problem, but we can hazard a guess about what happened from his text:

    Here’s an example. I might have two files in separate folders called recipe.txt, but one is a recipe for a pumpkin pie, and the other for apple pie. OK, it was dumb of me to give the files the same name, but it’s in situations like this that the OS should be helping me, not hindering me and making me pay for my stupidity. After all, Windows knows, without asking me, that the files, even if they are the same size and created at exactly the same time, are different. Why does Windows need to ask me what to do? Sure, it doesn’t solve all problems, but it’s a far better solution than clinging to the notion of filenames as being the best metric by which to judge whether files are identical or not.

    The key information here is the question: “Why does Windows need to ask me what to do?”  My guess is that he had two “recipe.txt” files in different directories and copied a recipe.txt from one directory to the other.  When you do that, Windows presents you with the following dialog:

    Windows Copy Dialog

    My suspicion is that he’s annoyed because Windows is forcing him to make a choice about what to do when there’s a conflict.  The problem is that there’s no one answer that works for all users and all scenarios.    Even in my day-to-day work I’ve had reason to chose all three options, depending on what’s going on.  From the rant, it appears that Adrian would like it to chose “Copy, but keep both files” by default.  But what happens if you really *do* want to replace the old recipe.txt with a new version?  Maybe you edited the file offline on your laptop and you’re bringing the new copy back to your desktop machine.  Or maybe you’re copying a bunch of files from one drive to another (I do this regularly when I sync my music collection from home and work).  In that case, you want to ignore the existing copy of the file (or maybe you want to copy the file over to ensure that the metadata is in sync).

    Windows can’t figure out what the right answer is here – so it prompts the user for advice about what to do.

    Btw, Adrian’s answer to his rhetorical question is “the reason is legacy”.  Actually that’s not quite it.  The reason is that it’s filenames provide valuable information for the user that would be lost if we went away from them.

    Next time I want to spend a bit of time brainstorming about ways to solve his problem (assuming that the problem I identified is the real problem – it might not be). 

     

     

    PS: I’m also not sure why he picked on Windows here.  Every operating system I know of has similar dependencies on filenames.  I think that’s an another indication that he’s jumping on a solution without first describing the problem.

  • Larry Osterman's WebLog

    I'm So Excited!

    • 34 Comments

    image

    'nuf said.

     

     

    I know it's bragging, but I'm sorry - I really couldn't resist.  It's been a very long time.

  • Larry Osterman's WebLog

    Casting from one interface to another...

    • 34 Comments

    A co-worker came by to ask what he thought was a coding "style" question that turned into a correctness issue, and I thought I'd share it.

     

    Someone had defined two COM interfaces:

    interface IFoo : IUnknown
    {
        HRESULT FooMethod1();
        HRESULT FooMethod2();
    }

    They also had a factory interface IBar which had a method HRESULT GetFoo(IFoo **ppFoo).

     

    As a result of new work, the team that owned IFoo wanted to extend IFoo.  To do this, they defined a new interface, IFoo2 that inherited from IFoo:

    interface IFoo2 : IFoo
    {
        HRESULT Foo2Method1();
        HRESULT Foo2Method2();
    }

    The team that owned IFoo (and IBar) decided that they didn't want to change the IBar interface to add a GetFoo2 method, feeling that the GetFoo method was "good enough".

    My co-worker wanted to call GetFoo and cast the resulting IFoo object into an IFoo2 object (he knew that the IFoo he got was always going to be an IFoo2).  He was worried about the stylistic implications of doing the cast.

     

    The problem with doing this turns out not to be a style issue, but instead to be correctness issue.  Here's the problem.

    Somewhere under the covers, there's a class CFoo that implements IFoo and IFoo2.  This is the object that will be returned by the IBar::GetFoo method.

    When the compiler lays out this object, the compiler will lay out the data for the class as follows:

    1 CFoo vtable
    2 IFoo vtable with 1*sizeof(void *) adjustor thunk
    3 IFoo2 vtable with 2*sizeof(void *) adjustor thunk
    4 CFoo member variable 1 storage
    5 CFoo member variable 2 storage
    6 :
    : :

    When IBar::GetFoo returns, it returns a pointer to the 2nd element of the class (the adjustor thunk will ensure that the right thing happens when you call into the member functions).

    The IFoo vtable is laid out in memory like this:

    1 QueryInterface()
    2 AddRef()
    3 Release()
    4 FooMethod1()
    5 FooMethod2()

    The IFoo2 vtable on the other hand is laid out in memory like this:

    1 QueryInterface()
    2 AddRef()
    3 Release()
    4 FooMethod1()
    5 FooMethod2()
    6 Foo2Method1()
    7 Foo2Method2()

    When the caller calls into a method on IFoo, the compiler will index into the vtable to find the pointer to the code that implements the specified method.  By casting from an IFoo to an IFoo2, my co-worker was telling the compiler "I know that this thing is also an IFoo2, so you should act like the vtable is really an IFoo2 vtable.

     

    The first time that he called into Foo2Method1 or Foo2Method2 using this mechanism, if he was lucky, his code would crash.  If he was unlucky (and the random chunk of memory sitting at the end of the vtable for IFoo happened to be code that matched the function signature of Foo2Method2, he would simply corrupt memory.

    All-in-all, a bad thing to do.

    In addition, if the particular class implementation of IFoo chose to implement IFoo2 via COM aggregation (in other words, using the delegator pattern), his cast would defeat that.

     

    The right thing to do in this case is to call QueryInterface on the IFoo object looking for IFoo2, then release the IFoo object since it's no longer needed.

     

    Edit: Fixed typos.

Page 3 of 33 (815 items) 12345»