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.
Tuesday, Jul 29, 2003, 10:11 AM in Tools
*Free* (as in beer) Access to VSIP!
Here. "Register to become a Visual Studio Industry partner and get FREE access to the VSIP SDK. As a VSIP you will have access to the technology, private newsgroups and secured technical content to assist you in seamlessly integrating your tools, components, and languages into the Visual Studio .NET IDE." Everything you always wanted to do to integrate seamlessly into VS.NET used to cost money. As of todays, it's free! Wahoo!
Monday, Jul 7, 2003, 8:21 PM in Tools
Regular Expression Workbench 2.0
Here. While I don't think it's as pretty, Eric Gunnerson's RegexW has some cool features I wish RegexD [1] had, like regex->English translation and menus for regex parts. Anyone want to merge the best of both into some uber regex tool? [1] http://sellsbrothers.com/tools/#regexd
Monday, Jul 7, 2003, 11:44 AM in Tools
Genghis Moved to GotDotNet Workspaces 1.0
Here. Now that GotDotNet Workspaces 1.0 has shipped (congrats!) Mike has moved Genghis there. Feel free to apply for membership if you've already contributed source to Genghis or if there's something you'd like to add.
Tuesday, Jul 1, 2003, 1:04 PM in Tools
Spout: Learning to Learn
Here. The one where I talk about the most important thing I ever learned.
Wednesday, Jun 18, 2003, 5:09 PM in Tools
XmlSerializerPreCompiler
Here. Tired of the XmlSerializer's generic "File or assembly name ctewkx4b.dll, or one of its dependencies, was not found" exception? The XmlSerializerPreCompiler tools checks to see if a type can be serialized by the XmlSerializer class and if it can't, shows the compiler errors happening behind the scenes so that the type can be modified. Enjoy.
Wednesday, Jun 18, 2003, 12:00 AM in Tools
XmlSerializerPreCompiler
Tired of the XmlSerializer's generic "File or assembly name ctewkx4b.dll, or one of its dependencies, was not found" exception? The XmlSerializerPreCompiler tools checks to see if a type can be serialized by the XmlSerializer class and if it can't, shows the compiler errors happening behind the scenes so that the type can be modified. Enjoy.
This just in: Mathew Nolton has posted a GUI front-end to my XmlSerializerPreCompiler that you might find useful. Thanks, Matt!
Friday, Jun 13, 2003, 12:00 AM in Tools
Filter Files With Unknown Extensions For XP
The fact that Windows XP Find in Files only searches files with known file extensions drives me crazy because it skips, among other things, .cs files. Every time I set up a new system, I have to re-figure out how to fix this. To facilitate that, I put together a .reg file that will search all unknown file extensions as text files. Enjoy.
Tuesday, Jun 3, 2003, 10:10 AM in Tools
SQL Buddy
Here. I got turned onto this tool internally and I love it. It installs simply, works intuitively and allows for easy ad hoc queries and commands. Recommended.
Wednesday, May 7, 2003, 9:30 PM in Tools
Genghis v0.4
Sorry for the long wait. There's been two whole books written between the last release of Genghis and this one. Here's the list of new features:
- Everything works under .NET 1.0 and 1.1 (except HandleCollector)
- MSN Messanger-style popup window
- UI Type Editor for use with ImageList index selection
- AppBar implementation
- Text file search
- Themed controls
- Single instance application support, including passing command line args from other instances
- Multiple top-level window support
- Many enhancements and fixes to the command line parser
- Splash screen class
- Improved WebCommandLineHelper class which now works with .NET 1.0 and 1.1, includes URL decoding and requires no additional permissions (although it does require a server-side HTTP handler, included as the ConfigFileHandler class that maps requests for foo.exe?bar=quux.config to the foo.exe.config file)
- WizardSheet updates
- Status Bar Extender for mapping status messages to Windows Forms controls
Thanks to my new build master, Mike Marshal, who got all of this together. He's the greatest!
Thursday, Apr 24, 2003, 2:38 PM in Tools
NAnt 0.8.2 released
Here. From Matthew Mastracci: NAnt 0.8.2 was recently released. Note that this release has support for both the Microsoft.NET framework v1.1 and Mono. See the download link above or the NAnt homepage: http://nant.sourceforge.net/
Thursday, Apr 17, 2003, 4:40 AM in Tools
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.
Thursday, Apr 17, 2003, 4:40 AM in Tools
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
Wednesday, Jan 22, 2003, 1:07 PM in Tools
NUnitASP
Here. From Colin: NUnitAsp is a tool for automatically testing ASP.NET web pages. It's an extension to NUnit, a tool for test-driven development in .NET.
Friday, Jan 10, 2003, 12:00 AM in Tools
.NET ResourceExplorer
In writing the resources chapter of my WinForms book, I found that there was no single utility for displaying the resource contents of assemblies, .resx files and .resource files (even embedded .resources files) in a way that made sense to me. So, I built one and called it ResourceExplorer.
ResourceExplorer is very simple and could stand some extension to show specific resources at images, text, data, etc, but it certainly served my needs, especially when it came to understanding the usage and limitations of the ResourceReader (for reading .resources files) and the ResXResourceReader (for reading .resx files). Enjoy.
Sunday, Dec 15, 2002, 4:35 PM in Tools
New Genghis Release Coming
Here. There has been all kinds of activity on Genghis since the v0.3 release. I've been gathering these bits together to make a coherent v0.4 release in the new year (right after I finish my darn book!). Those of you with final bits and pieces that need to get in before that release, please get them to me ASAP. Thanks.