Marquee de Sells: Chris's insight outlet via ATOM 1.0 csells on twitter

You've reached the internet home of Chris Sells, who has a long history as a contributing member of the Windows developer community. He enjoys long walks on the beach and various computer technologies.




Can I still be evil?

0 comments




Basic Monikers

Wish there was a moniker that did CoCreateInstance just like the Class Moniker calls CoGetClassObject? Wish you were able to compose the Class Moniker with a host name? Then you'll want the BasicMonikers project, which bundles together the New moniker and the Host moniker. Sample syntax follows:

dm.newmk.1:Excel.Application
dm.newmk.1:00024500-0000-0000-C000-000000000046:
dm.hostmk.1:frodo:!dm.newmk.1:00024500-0000-0000-C000-000000000046:
dm.hostmk.1:frodo:!clsid:00024500-0000-0000-C000-000000000046:

0 comments




regsvr.reg

July 14, 2001

regsvr.reg is a regedit script file that adds Register COM Server and Unregister COM Server to the context menu for DLLs, OCXs and EXEs under Win95+ and NT4+. In addition, it's also been updated to add Register TypeLib and Unregister TypeLib commands to .tlb, .odl, .dll, .ocx and .exe files, using VC6's new regtlib tool.

0 comments




ATL Composition

July 13, 2001

I've developed a set of macros to support implementing interfaces using nested composition in ATL (the one common way of implementing interfaces they neglected). The benefit of composition is that it is easy to implement multiple interfaces with methods with the same name but that require different behavior, e.g.

interface IArtist : IUnknown {
     HRESULT Draw();
}

interface ICowboy : IUnknown {
     HRESULT Draw();
}

The benefit of this particular implementation is that it has no object size overhead. Interfaces implemented using nested composition have no greater overhead than using multiple-inheritance. Feel free to download atlcompose.h for your own use.

0 comments




STL Enumerator Iterator

Have you ever been jealous of the VB programmer who could write this:

sub EnumVariants(col as Collection)
    dim v as variant for each v in col
        ' Do something with v
    next v
end sub

when we poor C++ programmers have to write this:

void EnumVariants(IEnumVARIANT* pevar)
{
    HRESULT hr;
    enum { CHUNKSIZE = 100 };
    VARIANT rgvar[CHUNKSIZE] = { 0 };
    do {
        ULONG cFetched;

        hr = pevar->Next(CHUNKSIZE, rgvar, &cFetched)
        if( SUCCEEDED(hr) ) {
            if( hr == S_OK ) cFetched = CHUNKSIZE;
            for( ULONG i = 0; i < cFetched; i++ )
            {
                // Do something with rgvar[i]
                VariantClear(&rgvar[i]);
            }
        }
    }
    while (hr == S_OK);
}

Well no more! I've built an enumeration iterator class that holds IEnumXxx and exposes an STL-compatible iterator:

template <typename EnumItf, const IID* pIIDEnumItf,
          typename EnumType, typename CopyClass = _Copy<EnumType> >
class enum_iterator;

It uses the same copy policy classes as ATL for convenience. Now you can write:

void EnumVariants(IEnumVARIANT* pevar)
{
    typedef enum_iterator<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT> EVI;
    for( EVI i = EVI(pevar); i != EVI(); ++i )
    {
        VARIANT&    v = *i;
        // Do something with v
    }
}

or you can use the typedefs for the standard enumerators:

void EnumVariants(IEnumVARIANT* pevar)
{
    for( variant_iterator i = variant_iterator(pevar);
         i != variant_iterator();
         ++i )
    {
        VARIANT&    v = *i;
        // Do something with v
    }
}

or you can use STL algorithms (this is my personal favorite):

struct DoSomethingWithVariant
{
    void operator()(const VARIANT& v)
    {
        // Do something with v
    }
};

void EnumVariants(IEnumVARIANT* pevar)
{
    for_each(enum_variant(pevar),
             enum_variant(),
             DoSomethingWithVariant());    
}

Feel free to download the enum_iterator class for your own use. You'll also need a supporting file, atlcopies.h.

0 comments




Client-Side Enumeration Iterator

July 12, 2001

Have you ever been jealous of the VB programmer who could write this:

sub EnumVariants(col as Collection)
    dim v as variant for each v in col
        ' Do something with v
    next v
end sub

If so, you may be interested in the STL-style IEnumXxx iterator I've built. You'll also need a supporting file, atlcopies.h.

0 comments




GitHelp

July 11, 2001

githelp.hdefines a set of wrappers for implementing inter-thread marshaling using the GIT instead of streams. githelp.cpp provides the non-inline implementation. For another spin on GIT usage, check out Don Box's GitLip.

0 comments




Codename TextBox

The Need for Code Generation

Have you ever wanted to generate code like the wizards do, i.e. start with a template, mix in some symbols and boom, out comes the code? If you're building a custom AppWizard, you define code like so:

    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int nShow) {
    $$IF(coinit)
        // Initialize COM
        CoInitialize(0);

    $$ENDIF
        // Initialize the ATL module
        _Module.Init(0, hinst);
    $$IF(axhost)

        // Initialize support for control containment
        AtlAxWinInit();
    $$ENDIF

        // Create and show the main window
        HMENU   hMenu = LoadMenu(_Module.GetResourceInstance(),
                       MAKEINTRESOURCE(IDR_$$ROOT$$));
    ...
    

This is fine if you've got the MFC-based interpreter building your code and you're willing to live within the boundaries of a very small set of features. The ATL Object Wizard-style of generation is similar, i.e.

    class [!ClassName] : 
     public CAxDialogImpl<[!ClassName]>
    {
    public:
     [!ClassName]()
     {
     }
    [!crlf]
    ...
    

Again, only good if you're running under the ObjectWizard and again, somewhat limited. What you really want is to be able to do things ASP-style, e.g.

    <%@ language=vbscript %>
    <% ' test.cpp.asp %>
    <%
        greeting = Request.QueryString("greeting")
        if len(greeting) = 0 then greeting = "Hello, World."
    %>
    // test.cpp

    <% if Request.QueryString("iostream") <> "" then %>
    #include <iostream>
    using namespace std;
    <% else %>
    #include <stdio.h>
    <% end if %>

    int main()
    {
    <% if Request.QueryString("iostream") <> "" then %>
        cout << "<%= greeting %>" << endl;
    <% else %>
        printf("<%= greeting %>\n");
    <% end if %>
        return 0;
    }

In this case, you get the same effect as the other two, but you've got the full power of a scripting language. However, for this to work, you had to run under ASP... until now...

TextBox

TextBox is a ASP-like script host that will process any file you give it looking for:

TextBox pre-processes the text to turn the whole thing into into script and hands it to the scripting engine for execution, outputting the result to standard out. Whatever features of the scripting language you want to use, feel free.

Usage

To provide for I/O, TextBox emulates ASP somewhat. It provides two intrinsics, the request object and the response object. The Request object has a single property, QueryString, that works just like ASP. The Response object has a single property, Write, just like ASP. In fact, if you only use Request.QueryString and Response.Write, you should be able to test your script files using ASP.

To set name/value pairs for use by the script via the Request object, the usage of TextBox is like so:

    usage: textbox <file> [name=value]

For example, to interpret the file above, any of the following command lines would work:

    textbox test.cpp.asp
    textbox test.cpp.asp greeting="Double Wahoo!"
    textbox test.cpp.asp iostream=true greeting="Double Wahoo!"
    

The first would yield the following output:

    // test.cpp

    #include <stdio.h>

    int main()
    {
        printf("Hello, World.\n");
        return 0;
    }
    

while the last would yield the following:

    // test.cpp

    #include <iostream>
    using namespace std;

    int main()
    {
        cout << "Double Wahoo!" << endl;
        return 0;
    }
    

Errors and Debugging

If the scripting engine finds an error, it will notify TextBox, who will notify you. However, if you've got script debugging enabled on your machine, the scripting engine will ask you if you'd like to fire up the debugger, showing you exactly the offending code.

Not Just Code

Of course, TextBox is good for the generation of any text, not just code.

Download

TextBox is available for download. It's just a prototype, so please adjust your expectations accordingly. If you have any comments, please send them to csells@sellsbrothers.com.

Copyright

Copyright (c) 1998-2001, Chris Sells All rights reserved. NO WARRANTIES EXTENDED. Use at your own risk.

0 comments




Limerick

A programmer who coded in C
decided he hated GC.
"It's bogus" he said,
"I keep track in my head -
and always remember to free!"

Jason Whittington [jasonw@develop.com]
Private Email

0 comments




The .NET

A parody of Eddy Poe's "The Raven"

Once upon a platform tired, while my code was stranded, mired in a pool of leaky pointers running up the mem'ry load, while I started to debug it, suddenly I screamed "Oh f*** it!", and decided to just chuck it in the hallway guest commode.
Then discovered: managed code.

Ah, distinctly I remember, bugs caused by a private member, and later having to call AddRefs and Releases by the busload, These things, they fill me with regret, time wasted on pointer management, Now, simply Fire And Forget! Oh the freedom newly bestowed!
Thank you, thank you, managed code.

Of course the bloat is sometimes scary, and IL can be kind of hairy, And there's this Tower of Babel thing that is threat'ning to explode. I mean, Perl and Python and Eiffel and all the scripting langs are just a ball but please, for God's sake, NOT COBOL! Java, even, but not THAT toad,
running in my managed code.

I traded in my GIT and SCM, my registry (which was kind of dumb) and in return I got C# and the CLR, my new abode. And VS7, though often crashing, as a tool is really smashing. I shan't bore you by rehashing the gifts .NET has bestowed.
Just be grateful: managed code.

Justin Gehtland [ justin@DEVELOP.COM]
brains@develop.com

0 comments




Choose Your Defaults Carefully

Saturday, 6/9/01

In the last week, I've been bitten twice by developers that have chosen defaults poorly and, therefore, have adversely affected my life. The first was on my Nokia phone. I was in a "meeting" with some of my key engineers (read: we were goofing off at a movie on a Thursday afternoon), and I had set my phone to Silent mode. Now, I have a Nokia 8260 (blue) that I dearly love. It kicks butt on the silly Ericsson that I used to have. However, I noticed when I came out of the meeting that their had been several calls from my blushing bride. The Nokia is cool not only because it's small enough to fit into my pocket (where I keep it during our "meetings"), but also because it has a vibrate mode. So, I figured that if the phone was in Silent mode, but the phone was still on, if someone called, I'd get the vibrate in my pocket (separate from my change and my keys to honor the spirit of the Nokia Silent profile). Unfortunately, this was not the case. Calls came in, but no vibration was forthcoming. This happened a couple more times in the next few days until I had a free moment to check the settings for Silent mode (ironically this free moment came while waiting for another movie to start today -- "Operation: Swordfish," which I enjoyed, btw). The default for the vibrate setting in the Silent profile was *off*. How could this possibly be right? It's a phone! It's on! Sure, I want it silent so it doesn't bug people around me, but I still want to know when someone calls! Hence the vibrate mode! Needless to say, my blushing bride was less than pleased.

The second time that incorrect defaults interrupted my life happened to be a bug recently found in Gen<X>. Because Gen<X> installs some COM components, we require it to be installed by an account that's part of the Administrator group. Unfortunately, the bug is that once Gen<X> is installed, another user can't use it on that machine unless they are also part of the Administrator group. Of course, since 98% of developers run with Admin privileges on their own machines, this bug was never reported by the secular community. So how did we find it? It was reported by none other than Keith "Mr. Security" Brown, who was experimenting with running at a lower level of security for some perverse purposes that I can only imagine... Anyway, when Keith calls you with a security bug, you listen (at least it was the good kind of security bug, i.e. offering less access then we should instead of more. I can only imagine what the phone call would be like if Keith discovers that we've opened a hole...). The problem with security? Defaults.

The author of CRegKey, a class in ATL we were using to read the serial number out of the Registry, built in a default access level setting. We use the Registry key when our tools are started to make sure that user has a valid serial number on their machine -- standard stuff. You need to be an Admin to write the key, but you shouldn't have to be just to read it. However, the default setting in the CRegKey::Open method is set to allow read-write permissions, as show here:

LONG Open(HKEY hKeyParent, LPCTSTR lpszKeyName, REGSAM samDesired = KEY_ALL_ACCESS);

The developer who wrote the code (let's call him "Shawn"), being a human with many tasks interested in doing them all efficiently, left off the default value, assuming that the default would be read-only. This was the correct assumption to make if you assume that the CRegKey author was specifying his defaults properly. Unfortunately, he wasn't, so when we tried to merely read the SN out of the Registry, we were asking for read-write permissions, and the read failed, causing the SN validation to fail, keeping Keith from using Gen<X> and motivating him to call me and complain about it. All due to bad defaults.

Rules for Defaults

Defaults are often set to save typing. That's a bad idea. If the Nokia guy and the CRegKey guy had but followed the these simple rules for determining defaults, my life would have been better:

  1. Apply "the principle of least surprise." I'm sure he didn't invent this rule, but it's one I learned from Don Box and it sticks with me as one of "the seven good ideas" that exist in our field (a story all its own). The principle of least surprise says that you make interface design decisions based on what people expect. Shawn expected the default access level to be read-only. I expected my phone to vibrate in Silent mode. Pretend that the default isn't even an option. What would people expect?
  2. Minimize the harm of inadvertently choosing the default. Assuming the defaults are not changed (a very common occurrence), what bad things could happen? If the default had been read-only on CRegKey, unit testing a code path that required read-write would have immediately uncovered that greater access was needed, a fix could have been immediately supplied and no harm done (except some extra typing). However, because the default was read-write, we introduce an invisible bug that Keith has to find. Likewise, if the default had been vibrate in Silent mode, when a call came in, it would still have been silent, but I would have been notified of the call. Again, no harm (assuming no holes in your pockets and that you're wearing underwear). However, because of the default, I missed calls and angered my wife. That didn't happen with the Ericsson.
  3. Change defaults sparingly. Once you've chosen your defaults and released your thing, people are going to learn those defaults and depend on them. For example, we can't go back into our ATL source and update the default to CRegKey::Open because we've got all kinds of code paths through our code depending on the default. If you change a default, even if the default was set incorrectly to begin with, you're doing more harm than good. Unless you can change the defaults without inducing harm, don't.

Unfortunately, not all developers follow these rules, so when you make use of defaults, please double-check them first.

0 comments




Why I Will Never Offer Don Box A Ride Again...

Read all about it...

Rohit Khare
Posted There: Fri, Aug 2, 1996 20:27:42 -0400

0 comments




Generics for C# and .NET CLR

This is a small site dedicated to the .NET generics research, including a version of the whitepaper describing their research updated as of 5/2/02.

0 comments




Other Blogs

5/16/2001

My spout page is just one of literally thousands of web logs ("blogs") available on the net. These are a few of the ones that I like:

0 comments




Rules of Engagement

Tuesday, 5/15/01

This is a page where I can just spout off about whatever. I keep it separate from my Tools page so that you don't mix up any of my spoutings with facts.

Before I  get started, I'd like to lay some ground rules. First and foremost, I'd like to warn you that every human on the planet has an agenda, often more than one. These are some of mine:

The second ground rule is that while I try to be as accurate as possible, I'm human and I make mistakes. If you find a mistake or you think my opinion is just so wrong that it requires a response or you'd just like to respond in general, feel free to send me email at csells@sellsbrothers.com.

0 comments




25 older posts       2610 newer posts