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.




XML For Humans

Here. It took me a bit of writing to figure out why the XMLigential at Microsoft seem so fired up about blogs. Ironically, the writing itself pointed it out for me. I do so love writing. : )

0 comments




My Interview

Fri, Apr 18, 2003

After never thinking that I would, I had two days of interviews at Microsoft in the middle of March, 2003. The first day was with the MSDN content group. They're the folks responsible for all of the technical articles at http://msdn.microsoft.com and the ones at Microsoft with the job most closely related to what I already do. It's their job to tell developers how Microsoft technologies really work through articles, talks, samples, online chats and whatever other means they think will be effective.

My first interview of day #1 sticks freshly in my mind even two weeks later. It was a guy that I'd most closely describe as a human molecule. I've been told that he's big into coffee and it's not hard to see the effects the guy never stops moving! Likewise, his brain was constantly moving. He had all kinds of interesting questions about how I would turn my product ideas (created on the spot) into compelling educational materials of Microsoft technologies. Very fun.

I had several other fun interviews that day, all with smart people asking really great questions. However, after a few interviews, I was disappointed that nobody was asking me to write any code on the board. Apparently I had enough street cred that this wasn't necessary for me, but one interviewer laughed and said I could write whatever code I wanted on his board if it would make me happy. Another had a strongly negative reaction to the idea that anyone would be asked to write code on a whiteboard, which he considered a supremely unnatural act. In fact, all day long, I had questions that dove into my motivations and my ideas, but none of them tested my technical knowledge at all until the very last interview w/ my potential boss's boss (or my boss's boss's boss I lost track). She asked me to solve one thought question, one visual riddle and to write some code on the board. The thought question we talked through, but she threw out leading questions faster than I could come up with my own conclusions (although it was a very interesting discussion). The visual riddle seemed impossible at first, but I solved it in a few minutes. When I showed it to Don Box, he also declared it impossible, then solved it faster than I did (bastard). The coding question showed me just how unnatural it is to write code on a write board (even my simple linked list code had three bugs in it doh!). I am *totally* addicted to incremental code-compile-test and at one point even suggested using a goto to avoid erasing half of my code to insert a loop (truly the low point of the day).

Even so, at the end of day #1, I was energized. I'm one of those sick people that loves to be tested, especially if I'm confident that I know the answers. My last interviewer told me that I had done well and we talked about what would happen if I was offered both jobs from the two teams that I was interviewing with. She needn't have worried day #2 did not go nearly as well.

The day #1 position was a highly technical position, which I've trained for since I was 12. The day #2 position was a <gasp> marketing position. But is was *so* cool. The job is to take whatever technology MS comes up with that day and make a business out of it. It was so diverse and so different than things I'd done before that I was drawn to it like a moth to a flame. Unfortunately, my answers made it clear that I didn't have anywhere near the background needed for the job. I felt like a new college graduate, trying desperately to match something from my background to what they were asking.

As a measure of the kinds of questions I got on day #2, at one point, I was asked to develop a marketing plan for packaged ice to native Alaskans. "Did you just ask me to sell ice cubes to Eskimos?" I asked. "Uh, yeah," he agreed. This was during lunch. And it wasn't the hardest question I got! By my last interview, I was so scared that they might actually offer me the job and that my brain would explode trying to do it that I confessed that I was definitely not the man for the job. However, that didn't stop my potential boss from drilling me on how to make a business on one of my sons' hobbies (about which I know almost nothing).

Of course, I never clicked with anyone on the team from day #2 (you can't click with someone that thinks you're ignorant). However, the experience was amazing. One of the interviewers had a fabulous technique that I just had to appreciate -- he had me role-playing in various real-world Microsoft-related situations over and over again til I swear my ears were bleeding. I'm a big fan of behavioral interviewing instead of role-playing as an indicator of someone's real skills, but watching him put me through the ringer was a thing of beauty.

Speaking now from experience, I'd have to say that the Microsoft interview process is all about finding the right fit. That's the case with all interviews, of course, but Microsoft seems to be very good at it. The fit includes both technical savvy (which they seemed to assume in me) and personality relative to the team. I fit very well into one group and not at all well into another. Working with the day #1 group would make me a successful part of the Microsoft machine, whereas if I had mistakenly gotten the job associated with day #2, I would have been set up to fail. So, if you are turned away from Microsoft, it means that you wouldn't do well there. That's a good thing to know; you certainly don't want to take a job at which can't possibly be successful. I know I don't.

0 comments




WinForms Design-Time Integration, Part 2

Here. "This is the second of two articles discussing the extremely rich design-time features of the .NET Framework. Part 2 continues the journey by concentrating on design-time functionality that you can implement beyond your components and controls, including TypeConverters, UITypeEditors, and Designers."

0 comments




Printer Margins, Part 2

Here. In this 2nd part of my discussion on printer margins, I show how to get real margin bounds taking the physical limitations of the printer into account.

0 comments




Major Retrofit for ASP.NET 1.0

This site, the logo, business cards and t-shirts were designed by guonan. She's got an amazing style and she's very easy to work with.

The initial version of this site was implemented by Jon Flanders. The way he was able to make conflicting requirements work for this site was inspirational. Most of what I know about ASP.NET, I learned from this site and Jon.

Continuous grammar checking is provided by Craig Andera (whether I want it or not : ).

The site itself is hosted at SecureWebs.

The implementation is a mixture of ASP.NET as the navigation and layout framework and HTML for the content. My goals for the site were simple: look nice, easy to maintain. I didn't want to give up FrontPage for my HTML editor ('cuz it has the Word key bindings that are hard-coded into my brain : ), but I wanted to be able to get the functionality of ASP.NET. So, the main content comes for an ascx file that reads in the index.htm and strips off the head section. Except for the home page, which is laid out specially, each default.aspx on my site just pulls in a single pageLayout.ascx file that does all of the layout using content from the target of the URL. By separating content from layout, I get the best of both FrontPage and ASP.NET, making both content and site maintenance easy.

0 comments




Hello IDispatch Lovers

I'm probably jumping into this topic a bit late but it doesn't seem to end. I admit I haven't read all article in this thread but I think I have an implementation solution for multiple dual interfaces on one class. And I would like opinions on it from others as well, and as to whether or not it displays proper COM implementation.

The implementation is very much like one that has been posted earlier, which I've seen different variations of many times before. However, I feel the one I am presenting allows all dual interfaces to be on one object and accessible via scripting without explicitly asking for a piece. No new interface definitions will be necessary with this technique. However, there may be one flaw. It will work ideally when none of the dispid's of any of the dual interfaces collide, and when none of the named functions collide.

I consider a collision when the dispids,or names are equal, but the parameter list/type are not. Even still, when they do collide, the implementers using this technique can choose which should take priority.

Using ATL, I defined a templated class which should be derived by the class that exposes multiple dual interfaces.

template<UINT t_uiNum, class tihclass = CComTypeInfoHolder>
class XMultiDualImpl : public IDispatch
{
public :
typedef tihclass _tihclass;
    void Add( UINT uiIndex, LPDISPATCH pDisp, _tihclass & tih)
    {
        ASSERT( t_uiNum > uiIndex-1 && uiIndex>0 );
        m_arrDispImpls[uiIndex-1] = pDisp; //no need to addref, pDisp==this
        m_arrpTih[uiIndex-1] = &tih;
    };

    STDMETHOD(GetTypeInfoCount)(UINT* pctinfo) {*pctinfo = 1; return S_OK;}
    STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
    {return m_arrpTih[0]->GetTypeInfo(itinfo, lcid, pptinfo);}
    STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
        LCID lcid, DISPID* rgdispid)
    {
        /*perhaps the loop should check for a succeeded hresult instead*/
        HRESULT hr = DISP_E_UNKNOWNNAME;
        for ( UINT i=0; i < t_uiNum && hr == DISP_E_UNKNOWNNAME; i++ )
        {
            hr = m_arrpTih[i]->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
        }
        return hr;
    };

    STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
    {
        HRESULT hr = DISP_E_MEMBERNOTFOUND;
        for ( int i=0; i < t_uiNum && hr == DISP_E_MEMBERNOTFOUND ; i++ )
        {
            hr = m_arrpTih[i]->Invoke( m_arrDispImpls[i], dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
        }
        return hr;
    } ;

    LPDISPATCH m_arrDispImpls[t_uiNum];
    _tihclass * m_arrpTih[t_uiNum];

};

As an example, if we were to implement an object with 3 dual interfaces we would do the following

typedef XMultiDualImpl<3> XTriDualImpl ;

class DispTest3 : public XTriDualImpl,
    public IDispatchImpl<IDispTest1, &IID_IDispTest1, &LIBID_FirstLib>,
    public IDispatchImpl<IDispTest2, &IID_IDispTest2, &LIBID_SecondLib>,
    public IDispatchImpl<IThirdIntfTest, &IID_IThirdIntfTest, &LIBID_ThridLib>,
    public CComObjectRoot,
    public CComCoClass<DispTest3,&CLSID_DispTest3>
{
public:
    DispTest3()
    {
        Add( 1,(IDispTest1*)this, IDispatchImpl<IDispTest1, &IID_IDispTest1, &LIBID_FirstLib>::_tih );
        Add( 2,(IDispTest2*)this, IDispatchImpl<IDispTest2, &IID_IDispTest2, &LIBID_SecondLib>::_tih );
        Add( 3,(IThirdIntfTest*)this, IDispatchImpl<IThirdIntfTest, &IID_IThirdIntfTest, &LIBID_ThridLib>::_tih );
}

BEGIN_COM_MAP(DispTest3)
    COM_INTERFACE_ENTRY2(IDispatch, XTriDualImpl)
    COM_INTERFACE_ENTRY(IDispTest1)
    COM_INTERFACE_ENTRY(IDispTest2)
    COM_INTERFACE_ENTRY(IThirdIntfTest)
END_COM_MAP()

//IDispTest1 methods [code omitted for brevity]
//IDispTest2 methods [code omitted for brevity]
//IThirdIntfTest methods [code omitted for brevity]
};

I admit the constructor of the derived class leaves room for elegance.

Basically, all QueryInterfaces for IDispatch will result in the XMultiDualImpl vtable. The IDispatch implementation for that vtable basically asks each IDispatch vtable (of the dual interface) in it's array one by one if it can carry out the call. If it fails, it moves on to the next dual interface vtable.

One might point out the following however :

pUnk->QueryInterface( IID_IDispatch, (void**)&pIDispatch );
pUnk->QueryInteface( IID_IDispTest1, (void**)&pIDispTest1 );

pIDispatch->GetIDsOfNames does not have the same implementation as pIDispTest1->GetIDsOfNames. as well as the implementation differs for ->Invoke. I question whether or not that's a problem. Since IDispTest1 derives from IDispatch, I could see why it may be.

-herb

0 comments




Exposing Multiple Interfaces to Scripting Clients

This page represents an attempt to capture the collective consciousness of the COM community. Of course, thoughtful feedback is welcome and encouraged (who the hell am I to voice the collective opinions of the entire COM community?). Once this is all sorted out, I anticipate never implementing IDispatch again ("Get COM+ now, I'll show you how!").

This page attempts to answer the question:

"How do I expose multiple interfaces to scriping clients?"

Why is this an issue? Because IDispatch is limited? No. It's a problem because current scripting clients don't support QI. Based on my observations of this list and my extensive conversations with members of the COM community, I see several solutions to this problem:

1. Don't try to expose the functionality of multiple interfaces to a scripting client.

This is my favorite technique and was originally suggested to me by Keith Brown. He recommends using IDispatch to model the entire functionality of the object at a higher level than the individual interfaces, e.g.

interface IRect : IUnknown
{
    HRESULT GetCoords([out] long* pleft, [out] long* ptop,
                      [out] long* pright, [out] long* pbottom);

    HRESULT SetCoords([in] long left, [in] long top,
                      [in] long right, [in] long bottom);
}

interface I2DObject : IUnknown
{
    HRESULT Inflate([in] long cx, [in] long cy);
    HRESULT Translate([in] long cy, [in] long cy);
}

// For scripting clients only
[ hidden, dual ]
interface _IRectangle : IDispatch
{
    [propput] HRESULT Left([in] long left);
    [propget] HRESULT Left([out, retval] long* pleft);
    ...
    HRESULT Inflate([in] long cx, [in] long cy);
    HRESULT Translate([in] long cy, [in] long cy);
}

An implementation would use the dual strictly for IDispatch purposes, i.e. vtbl-binding clients wouldn't use _IRectangle:

class CRectangle : ...,
    public IRect,
    public I2DObject,
    public IDispatchImpl<_IRectangle, &IID__IRectangle,
                         &LIBID_SHAPESLib>
{
public:
BEGIN_COM_MAP(CRectangle)
    COM_INTERFACE_ENTRY(IRect)
    COM_INTERFACE_ENTRY(I2DObject)
    COM_INTERFACE_ENTRY(IDispatch)
    // No entry for _IRectangle
END_COM_MAP()

    // IRect methods
    ...

    // I2DObject methods
    ...

    // _IRectangle methods
    ...

};

Since we're no longer using interface-based programming, this technique allows full control when implementing the single interface the scripting client will see w/o having to worry about mapping every method of all of the interfaces. By using the dual strictly as a means of implementing IDispatch and not exposing it, your IDispatch implementation can evolve as your object functionality evolves, i.e. using another dual w/ another IID. While it's still possible for a savvy developer to get a hold of the dual and try to implement it or derive from it, a slap on the forehead is generally enough to discourage this behavior.

Unfortunately, nobody ever likes this technique because it forces them to provide client-specific implementation (which, btw, you'll likely to have to do anyway...).

2. Use the C++ MI trick.

If you have n interfaces that are already [oleautomation] compatible, you can define a dual interface in IDL that is a union of all of the methods of all of the interfaces you'd like to expose methods from and let the C++ compiler match up the vtbls for you.

[ oleautomation ]
interface IRect : IUnknown
{
    HRESULT GetCoords([out] long* pleft, [out] long* ptop,
                      [out] long* pright, [out] long* pbottom);

    HRESULT SetCoords([in] long left, [in] long top,
                      [in] long right, [in] long bottom);
}

[ oleautomation ]
interface I2DObject : IUnknown
{
    HRESULT Inflate([in] long cx, [in] long cy);
    HRESULT Translate([in] long cy, [in] long cy);
}

[ hidden, dual ]
interface _IRectangle
{

    // Copied from IRect
    HRESULT GetCoords([out] long* pleft, [out] long* ptop,
                      [out] long* pright, [out] long* pbottom);

    HRESULT SetCoords([in] long left, [in] long top,
                      [in] long right, [in] long bottom);

    // Copied from I2DObject
    HRESULT Inflate([in] long cx, [in] long cy);
    HRESULT Translate([in] long cy, [in] long cy);

}

The implementation derives from all of the interfaces and implements the union of the methods. The C++ compiler will fill in the vtbl entries for the dual interface using the methods you are already implementing for your non-dual interfaces, e.g.

class CRectangle :
    ...,
    public IRect,
    public I2DObject,
    public IDispatchImpl<_IRectangle, &IID__IRectangle,
                         &LIBID_SHAPESLib>

{
public:

BEGIN_COM_MAP(CRectangle)
    COM_INTERFACE_ENTRY(IRect)
    COM_INTERFACE_ENTRY(I2DObject)
    COM_INTERFACE_ENTRY(IDispatch)
    // No entry for _IRectangle
END_COM_MAP()

    // IRect methods
    ...

    // I2DObject methods
    ...

    // _IRectangle methods already implemented!

};

This method allows your clients to have access to the union of the methods of all of the interfaces that you have copied and pasted into your dual interface. However, now you're left with the copy 'n' paste dance in IDL and there's no way to resolve name conflicts, e.g. IRect and I2DObject both have a method Foo() that is each meant to do different things. This method also requires you to define your non-dual interfaces with scripting clients in mind. Finally, this method also exposes the dual in the TypeLib to savvy developers (see the aforementioned forehead-slap solution).

3. Use a typeinfo-driven implementation of IDispatch that provides a union of the methods of the scriptable interfaces.

Daniel Sinclair provides another implementation of this technique called MultiDual, described in this online ReadMe.
Herbert Carroll provides a similar implementation, but not based on typeinfo.
Kjell Tangen provides another solution for by implementing a extended version of CComTypeInfoHolder.

Imagine the following IDL:

[ oleautomation ]
interface ICowboy : IUnknown
{
    HRESULT Draw([in] long nSpeed); // Conflicts with IArtist::Draw
    HRESULT Shoot();                 // Conflicts with ICurse::Shoot
}

[ oleautomation ]
interface IArtist : IUnknown
{
    HRESULT Draw([in] long nStrokes); // Conflicts with ICowboy::Draw
    HRESULT Paint();
}

[ oleautomation ]
interface ICurse : IUnknown
{
    HRESULT Shoot(); // Conflicts with ICowboy::Shoot
    HRESULT Darn();
}

library TURNOFTHECENTURYLib
{
    coclass AcePowell
    {
        interface ICowboy;
        [default] interface IArtist;
        interface ICurse;
    }
}

The client would like to write code like this:

<script language=vbscript>

    ace.paint    ' unambiguously IArtist::Paint
    ace.draw 100 ' resolved to IArtist::Draw because
                 'IArtist is [default]

    ace.darn    ' unambiguously ICurse::Darn
    ace.shoot   ' resolved to ICowboy::Shoot because ICowboy
                ' comes before ICurse in the coclass statement

    ace.icurse_shoot    ' resolved to ICurse::Shoot because of prefix
    ace.icurse_darn     ' prefix unnecessary but still works

</script>

The typelib-based table-driven implementation would use the coclass statement to map calls to GetIDsOfNames and Invoke to the appropriate interface definition, doing a little pre-processing along the way to provide non-colliding DISPIDs for non-default interface methods and handling the prefixes to perform fully-scoped name resolution.

Because the implementation is completely table-deriven based on the coclass statement, an aggregatable implementation of IDispatch that provided this amalgam behaviour could be implemented that only depended on the object to expose IProvideClassInfo. In fact, some members of this list have provided implementations of similiar techniques w/o, unless I'm mistaken, the name resolution scheme I've proposed above. Assuming such an implementation of IDispatch, the object implementation could look like this:

class CAcePowell :
    ...,
    public IProvideClassInfo
{

BEGIN_COM_MAP(CAcePowell)
    // Appropriate entries for nested composition of
    // ICowboy, IArtist and ICurse

    COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_IDispatch, m_spunkDisp.p,
                                      CLSID_StdAmalgamDispatch)

    COM_INTERACE_ENTRY(IProvideClassInfo)
END_COM_MAP()

    ...

private:
    CComPtr<IUnknown> m_spunkDisp;
};

Note: Assuming the implementation exposed the same DISPIDs for the methods and properties of the default interface as the default interface, early bound clients would be just as happy as late bound ones. And, because no dual interface is defined to implement IDispatch, there's no worry of a developer getting a hold of the interface definition for the dual interface directly (although a slap is still warranted for those that try).

4. Use a typeinfo-driven implementation of IDispatch that provides a collection of nested objects that implement the scriptable interfaces.

Don and I implemented this technique at Tim's house. It's available here. Also, this has been updated by Serge Sandler for VC6 and for Win9x compatibility.

Assuming the AcePowell IDL shown above, the client would like to write the following code (which performs the same as the previous client code):

<script language=vbscript>

    ace.paint    ' top level object has all methods of
                 ' [default] interface

    dim artist
    set artist = ace.iartist ' Can "QI" for specific interface
    artist.draw 100

    dim curse
    set curse = artist.icurse ' Simulated QI
    curse.darn

    dim cowboy
    set cowboy = curse.icowboy ' Simulated QI
    cowboy.shoot

    curse.shoot
    curse.darn

</script>

As you can see, we're exposing a collection of objects where the top level object has all of the methods and properties of the [default] interface as well a property to obtain an interface pointer on a sub-object for each of the oleautomation interfaces. The sub-objects are separate COM identities whose implementation of IDispatch simply forwards to the main identity via the specific interface in question. Each of the sub-objects also have one property per interface to get to the other interfaces, thus simulated the rules of COM identity as exposed via QI. This allows us to build an invocation model very similar to VB's where QI is an assignment and references of type class mean references to the [default] interface.

Parts of the implementation for this scheme have also been posted w/o, unless I'm mistaken, the full simulated QI as I propose above. The nested implementation of IDispatch could also be fully typelib-drived based on an object's implementation of IProvideClassInfo and aggregated, e.g. using a clsid like CLSID_StdNestedDispatch.

One alternative syntax that might be a bit more flexible is the following:

<script language=vbscript>

    ace.paint    ' top level object has all methods of
                 ' [default] interface

    dim artist
    set artist = ace.interface("iartist")

    if not artist is nothing then artist.draw 100

    dim curse
    set curse = artist.interface("icurse")
    if not curse is nothing then curse.darn

    dim cowboy
    set cowboy = curse.interface("icowboy")
    if not cowboy is nothing then cowboy.shoot

   if not curse is nothing then
        curse.shoot
        curse.darn

    end if

</script>

This syntax makes it even more clear that we're doing COM, it only requires one additional property (interface) and it's easier to handle the QI failure case.

5. Use a separate object to perform QI.

This method is made real in an implementation by Dave Rogers, available at http://www.combatcom.com/adminstore/component-warehouse/thedispadapter.htm and Valery Pryamikov, available at http://home.sol.no/~valery Instead of building the pseudo-QI into the object itself, Valery uses another another object to perform the QI. This allows the client code to look like this:

<script language=vbscript>

    ' Requires object to implement IProvideClassInfo or IPersist
    set dispenum.ObjectClass = ace

    ' Doesn't require IProvideClassInfo or IPersist
    ' Note: Could use CLSID instead of ProgID
    dispenum.SetCLSIDObjectClass(ace, "Ace.AcePowell.1")

    Dim artist
    Set artist = dispenum("iartist") ' Use disenum to perform QI
    If not artist is nothing then artist.draw 100

    Dim curse
    Set curse = dispenum("icurse")
    If not curse is nothing then curse.darn

    Dim cowboy
    Set cowboy = dispenum("icowboy")
    If not cowboy is nothing then cowboy.shoot

   If not curse is nothing then
        Curse.shoot
        Curse.darn
    End if

</script>

This technique requires another object to perform the QI, so it seems a little weird, but the good news is that it works with existing objects. No recompile necessary!

Now What?

Given those techniques of implementing IDispatch to expose the functionality of multiple interfaces, the first two require no additional tool support. You can use them today. The last two, amalgam dispatch and nested dispatch, represent two models of exposing the functionality of multiple interfaces to a scripting client. Either model may be appropriate, but each is conceptually separate, i.e. the object implementor would pick one or the other.

After writing the client code, I find I really like technique 4 best. It looks like COM to me. However, I can see that some folks would prefer technique 3 or 5.

So, what do we do now? Do we wait for MS to ship COM+? We won't see meta-information driven scripting until the turn of the millennia. Do we wait for MS to implement CLSID_StdAmalgamDispatch and CLSID_StdNestedDispatch? Those guys are pretty busy with COM+, so I won't hold my breath. Are we sure of the ramifications and usage of amalgam dispatch and nested dispatch? I'm pretty sure, but not completely so. If you think I'm all wrong (or even just a litte), let me know.

On the other hand, if there are enough folks that are excited about a typelib-driven implementation of IDispatch, we can get it done. I've dedicated this web page to collecting and distributing the pieces. If folks have some source to contribute, I'll use it to build the two implementations. If folks have full implementations, I'm happy to post those as well. Please, let's stop implementing IDispatch so I can stop thinking about the worthlessness of duals. Thanks.

BTW, want to know the real reason we have duals? It saves a vptr. Doh!

This article is translated to Serbo-Croatian by Anja Skrba from Webhostinggeeks.com.

0 comments




XML For Humans

When I sat down to write this piece, I didn't get why the work that Don, Gudge, KeithBa, Yasser, Tim and ChrisAn is doing is so amazing. I mean, I know that they're smart and I know that they're building the hell out of their stuff, but why? It seems like everyone that gets into blogging starts by writing their own blogging engine. It's like C programmers writing editors, C++ programmers writing string classes and XML programmers writing XSD<->Class codegen. Sure, it's a useful learning tool, but earth-shattering? Worth that much energy from that many guys? When they're done, what great problems will be solved?

Don't get me wrong. I love the that the web makes it easy for anyone to not only publish but also get an audience. I also love that there's a protocol (RSS) that lets me subscribe to my favorite freelance authors in any one of dozens of tools (I'm into SharpReader today). I even like the Dear Diary style of writing because it leads itself to thoughts, feelings and insights that give me greater understanding of not only the topic but the author. As an added bonus, blog entries have turned into everything that's good about mailing lists w/o the endless angle brackets that remove the need to write in coherent sentences. Blogs feel like the kind of democracy we had that lead to the American Revolution when the world was re-shaped with big ideas, written by great authors.

But does the world need yet another blog engine? Personally, while it's primitive, I find that FrontPage have served me well as a blogging tool. The mental overhead is small, the development is nil and I get red squiggles. It falls down sometimes, but I've spent far less time maintaining the infrastructure for my content than I would if I were to build my own blogging engine.

On the other hand, while I don't feel the need for another blogging engine in the world, I definitely see value in what those guys are doing. Specifically, they're building apps, which is not something that XML guys are traditionally into. In fact, I'm scratching my head trying to think of another kind of XML-based technology that isn't infrastructure-based and I'm not coming up with one (even InfoPath is a dev-tool). Is RSS really the first mainstream use of XML that hasn't nothing to do with technology for it's own sake? Are blogs the killer app for XML? I know web services were supposed to fill that role, but while they do solve a real problem, they're not for consumers. My aunt in FLA couldn't make use of a web service. However, she could definitely subscribe to an RSS feed and read up on the local news, her job and her hobbies.

Of course, the beauty of having infrastructure guys building apps is that they get to put into practice what they preach. When stuff is a pain (like every single XML API ever invented and most XML vocabularies), they feel the pain. The difference between us and them is that instead of just wrapping a layer of abstraction around the ugly stuff like we have to, they can actually fix it. I started this piece wanting to shake those guys until they could tell me why they cared so much. How ironic that thinking through the issues in the very medium they're digging into helped me figure out what they were up to. The blog engine work is but the vehicle. They're riding the app train to enlightenment. They're trying to understand how XML can be used to solve problems for real humans. How can I argue with that?

0 comments




Essential .NET Nominated for Reader's Choice Award

Here. If you liked Don's Essential .NET (I know I did), then you should vote for it on the .NET Developer's Journal Reader's Choice Awards. You also get the opportunity to vote in all kinds of other categories besides books.

0 comments




Priceless

Here. It's the little things in life...

0 comments




Make XML More Readable

Here. I didn't know that XmlTextWriter could format your code for you. I've written the code to do it myself. Doh! Is there nothing that the .NET Framework Class Library authors haven't already thunk up?!?

0 comments




C# Programmers Out-Earn VB.NET Programmers by 26%

Here. Visual Studio Magazine posts the results of their latest salary survey. Average Visual Studio programmer salary is $76K, up 12% from last year. Unfortunately, layoffs and such like are still the order of the day.

0 comments




Microsoft CRM Software Development Kit (SDK)

Here. From Ramon: This SDK provides the information necessary for developers to integrate their applications into Microsoft CRM or to build a vertical solution on the Microsoft CRM platform. An architectural overview includes descriptions of the object and security models, as well as the integration points. The API references are documented and the database schema is provided. There is a “How To” section with sample code.

0 comments




Priceless

I don't know how my son picked up on the tiny bit of fame I've acquired amongst a small, strange (but loveable!) group of people, but he did and now he thinks I'm cool. I'm sure it won't last, but I plan to enjoy it while it does. : )

0 comments




The SellsBrothers Little League Team

Here's the SellsBrothers Beaverton Area Little League team looking very serious. Notice the SellsBrothers logo on the hats. Having your own company definitely has it's perks. : )

Here's the SellsBrothers team looking more natural.

Beaverton, OR
Saturday, April 12, 2003

0 comments




610 older posts       2025 newer posts