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.
Sunday, Jul 15, 2001, 3:20 AM in Fun
Can I still be evil?
Sunday, Jul 15, 2001, 12:00 AM in Tools
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:
Saturday, Jul 14, 2001, 12:00 AM in Tools
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.
Friday, Jul 13, 2001, 12:00 AM in Tools
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.
Thursday, Jul 12, 2001, 12:00 AM in Tools
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.
Thursday, Jul 12, 2001, 12:00 AM in Tools
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.
Wednesday, Jul 11, 2001, 12:00 AM in Tools
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.
Saturday, Jun 30, 2001, 9:40 PM in Tools
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:
- text blocks
- script blocks (<% script %>)
- output blocks (<%= output %>)
-
An optional language block as the first line in the file only (<%@
language= language %>)
(TextBox defaults to vbscript and currently only works with vbscript and jscript).
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.
Monday, Jun 18, 2001, 3:50 PM in Fun
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
Thursday, Jun 14, 2001, 9:06 PM in Fun
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
Saturday, Jun 9, 2001, 12:00 AM in The Spout
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:
- 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?
- 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.
- 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.
Friday, Jun 1, 2001, 8:27 PM in Fun
Why I Will Never Offer Don Box A Ride Again...
Rohit Khare
Posted There: Fri, Aug 2, 1996 20:27:42 -0400
Friday, May 18, 2001, 12:24 AM in .NET
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.
Wednesday, May 16, 2001, 12:00 AM in The Spout
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:
- SlashDot. This is the first blog I ever saw and it's still really cool. Lots of Linux stuff, of course, but also lots of fun general-purpose technology stuff.
- BetaNews. This has no commentary. It's just a list of software that's gone into beta recently. I often find interesting stuff here.
- Joel on Software. Joel Spolsky is an ex-Microsofty who runs Fog Creek Software and has a lot of really good ideas when it comes to how to build software. He often says what I think, so I think he's a genius. : )
- Dot Net Dan's. Dan Green aka "Dot Net Dan," is a guy who really likes .NET and once mentioned me right between Joel Spolsky and Stephen Hawking, so I think he's cool. : )
- The CodeProject. This is my favorite code posting site by far. There's something new there daily and I almost always start there when I'm looking for a hunk of code.
- ActiveWin. This one is mostly an excuse to pay homage to Microsoft, but there's interesting Windows news up often enough that I still visit pretty regularly.
- Jason Whittington: Managed Space blog. Jason's another DevelopMentor guy heavy into .NET and particularly garbage collection (at least for the moment -- most of us have fairly short attention spans... : ).
Tuesday, May 15, 2001, 12:00 AM in The Spout
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:
- I want to make people's lives better. This drives pretty much everything I do. Since my education is in computer science and my sphere of influent is developers, most of what I do is to help that sub-species of human known as "developers." Recently I've been experimenting with ways to help normal humans, but what these people want is foreign to me, so it's slow going.
- I'm interested in putting food on my table. For the same reason, I'm interested in what's good for me and my family. This drive is the most potentially dangerous to you if you're going to put any stock into my spoutings. Be warned.
- I'm interested in what I'm interested in. Once I like something, I often like it for a long, long time. However, only one thing at a time can really consume me and you're likely to hear about it here.
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.