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.
Friday, Jul 27, 2001, 12:00 AM in Tools
Simple Object II ATL ObjectWizard Extension
July 27, 2001
The Simple Object II ATL ObjectWizard Extension (whew -- I've got to come up with a shorter name...) provides the following features:
- Support for [oleautomation] interfaces.
- Support for the Neutral-threaded apartment.
- Support for IDispatch-based interfaces other than duals.
- Better support for disabling features that do not make sense together, e.g. FTM + ThreadingModel=Apartment.
- Updated pop-up help.
Originally, SimpleObject2 was built to do away with the one benefit of IDispatchImpl over IDelegatingDispImpl -- wizard support. As do most things developed under the greedy eye of my colleagues, it grew to support a few more features than that. It's available for download here. Enjoy.
Thursday, Jul 26, 2001, 12:00 AM in Tools
CComVector: A SAFEARRAY Wrapper
July 26, 2001
I got tired of not having a smart type for SAFEARRAYs, so I built one. It's limited to one dimension (does anyone use multi-dimentional SAFEARRAYs?) and provides one class for the SAFEARRAY and another for the data itself (the lock is a resource, too), but the usage is pretty nifty. See comvector.h for the what and the how.
Also, Ron Jacobs sent me an ATL port of COleSafeArray called CComSafeArray that I've made available here.
Wednesday, Jul 25, 2001, 12:00 AM in Tools
CComDate
July 25, 2001
This zip provides two classes for use with dates and times under Windows:
- CComDATE: Wraps the COM DATE type, providing conversions from VARIANT, DATE, ANSI string, UNICODE string, time_t, SYSTEMTIME, FILETIME, DOS time and DBTIMESTAMP data types.
- CComSpan: Represents the result of subtracting one CComDATE from another. Useful for time/date math.
Tuesday, Jul 24, 2001, 12:00 AM in Tools
Exposing Multiple IDispatch Interfaces
If you'd like to know about how to expose multiple IDispatch implementations from a single COM object, check out this extensive treatment of that very topic.
Monday, Jul 23, 2001, 12:00 AM in Tools
MDI Applications in ATL
July 23, 2001
At atlmdi.zip, please find Charles Petzold's famous raw Win32 MDI application, MDIDemo, ported to ATL. See atlmdi.h for a set of base classes for building MDI applications in ATL and mdidemo.cpp for an example of their use.
Sunday, Jul 22, 2001, 12:00 AM in Tools
Fixing the External Dispatch in ATL + IE5
July 22, 2001
Are you getting this: "Error: object doesn't support this property or method" when you try to call a method on your ATL HTML Control exposed external dispatch (accessed via window.external in the HTML of the control)? Did it used to work with IE4 and now it doesn't with IE5? That's because the ATL boys played fast and loose with the identity laws of COM and, while it took the COM police a while to catch up with them, catch up with them they did.
Download WrapperDispatch.h for an explanation of the problem and my solution to it.
Saturday, Jul 21, 2001, 12:00 AM in Tools
TraceHook
July 21, 2001
TraceHook is a member function call tracing delegator hook based on Keith Brown's most excellent universal delegator. It allows you to write:
// Get a COM object to wrap CComPtr<ICalc> spCalc; spCalc.CoCreateInstance(CLSID_Calc); // Wrap it (when _DEBUG is defined only) TRACEHOOK(OLESTR("MyCalc"), &spCalc.p); // Use it spCalc->put_Sum(0); spCalc->Add(2); spCalc->Add(2); long nSum; spCalc->get_Sum(&nSum);
and see the following in the debug output (with no changes to the object's source) :
MyCalc, ICalc::put_Sum(0) MyCalc, ICalc::Add(2) MyCalc, ICalc::Add(2) MyCalc, ICalc::get_Sum()
Notice that the call stack is now dumped (for duals, either directly or via IDispatch, or any method with automation-compliant data types, thanks to features added by the most excellent Simon Fell! He also added the ability to hook objects created in scripting environments (or VB or Java, too), e.g.
' WSH Test script for CalcSvr / TraceHook ' Create the implementation of ITraceHookFactory set th = CreateObject("UDTraceHookSvr.TraceHook") ' Create an object (an <object> tag in HTML would be fine, too) set calc = CreateObject("CalcSvr.Calc") ' Trace all further calls on calc th.ComTrace "MyCalc", calc calc.sum = 0 calc.add 2 calc.add 2 msgbox "2+2= " & calc.sum
would yield the following debug output:
MyCalc, IDispatch::(Invoke) sum(0) MyCalc, IDispatch::(Invoke) add(2) MyCalc, IDispatch::(Invoke) add(2) MyCalc, IDispatch::(Invoke) sum() returned 4
The source, pre-built binaries and client-side headers are available here. The pre-built binaries include Keith's delegator as well.
Friday, Jul 20, 2001, 12:00 AM in Tools
CAspPtr
If you've ever had to write the code to pull the ASP intrinsic objects from the MTS context, you might appreciate CAspPtr, which allows the following:
STDMETHODIMP CJon::DoIt() { CAspPtr<IResponse> spResponse; if( spResponse ) { spResponse->Write(CComVariant(OLESTR("Baby got <b><i>Response</i></b>."))); } return S_OK; }
CAspPtr is available here and was inspired by Jon Flanders. Thanks, Jon!
Thursday, Jul 19, 2001, 12:00 AM in Tools
Smart IEnumVARIANT
Visual Basic and its variants (sic) use IEnumVARIANT to implement the For-Each statement. Unfortunately, they only ask for 1 element at a time, leading to terrible performance across apartments. I propose two solutions. One allows you to wrap any collection in a CollectionBuffer object and set the buffering yourself, e.g.
Dim collBuffer As Object Set collBuffer = CreateObject("SmartEnumSvr.CollectionBuffer") collBuffer.Collection = coll ' coll is an interface on any collection collBuffer.BufferSize = 1024 ' How many items would you like buffered? c = 0 For Each v In collBuffer ' Uses SmartEnumVARIANT to buffer items c = c + 1 Next v MsgBox "Counted " & c
The second solution allows you to leverage SmartEnumVARIANT on the server-side, seamlessly to the client. A detailed explanation and code is available here.
Thursday, Jul 19, 2001, 12:00 AM in Tools
SmartEnumVARIANT
The Problem
VB/VBScript provides the implementation of For-Each using an implementation of IEnumVARIANT exposed from a collection via the _NewEnum method. Unfortunately, VB uses 1 for the argument to Next, making round-trip time terrible.
A Solution:
If you don't own the collection implementation code...
If you don't own the collection implementation code, you can still wrap the
collection in a custom object called the CollectionBuffer, which implements the
following interface:
[ object, uuid(B6175081-83D4-11D2-987D-00600823CFFB), dual, helpstring("ICollectionBuffer Interface"), pointer_default(unique) ] interface ICollectionBuffer : IDispatch { [propget, helpstring("property Collection")] HRESULT Collection([out, retval] IDispatch** pVal); [propput, helpstring("property Collection")] HRESULT Collection([in] IDispatch* newVal); [propget, helpstring("property BufferSize")] HRESULT BufferSize([out, retval] long *pVal); [propput, helpstring("property BufferSize")] HRESULT BufferSize([in] long newVal); [propget, id(DISPID_NEWENUM)] HRESULT _NewEnum([out, retval] IUnknown** ppunkEnum); };The Collection property is used to pass in the collection object to which you'd like buffered access. The BufferSize property is used to control how many VARIANTs you'd like to have returned on each call to Next. The implementation of _NewEnum gets the IEnumVARIANT interface from the collection and wraps it in a buffered implementation of IEnumVARIANT called SmartEnumVARIANT.
The SmartEnumVARIANT object manages buffered access to the underlying IEnumVARIANT. VB can still ask for one item at a time, but the items will come out of the buffer. Depending on the buffer size, this can improve performance by many order of magnitude.
Usage
The following VB code uses an unbuffered collection and is slow:Dim coll As Object Set coll = CreateObject("DumbEnumSvr.CollectionOfNumbers") coll.CountOfNumbers = 10000 Dim v As Variant Dim c As Long For Each v In coll c = c + 1 Next v MsgBox "Counted " & cThis VB code wraps the unbuffered collection in the CollectionBuffer object:
Dim collBuffer As Object Set collBuffer = CreateObject("SmartEnumSvr.CollectionBuffer") collBuffer.Collection = coll collBuffer.BufferSize = 1024 c = 0 For Each v In collBuffer c = c + 1 Next v MsgBox "Counted " & c
Another Solution:
If you do own the collection implementation code...
If you do have the source to the collection, there's no reason that VB clients
have to do anything. Instead, your implementation of IEnumVARIANT can use a
manual form of handler marshaling (invented, as far as I know, by Don Box --
the manual form, MS invented handler marshaling...).
The handler marshaling works like this:
- The server-side implementation of IEnumVARIANT implements IMarshal.
- Server-side enumerator returns CLSID_SmartEnumVARIANT when GetUnmarshalClass is called.
- Server-side enumerator marshals the interface of it's own stub (obtained by calling CoGetStandardMarshal) into the stream provided during the call to GetMarshalSizeMax and MarshalInterface.
- On the client-side, the Proxy Manager used the CLSID provided by the server-side enumerator to create an instance of SmartEnumVARIANT.
- The SmartEnumVARIANT object also implements IMarshal and it will pull the marshaled stub to the real implementation of IEnumVARIANT out of the marshal packet.
- The SmartEnumVARIANT becomes a "smart proxy," forwarding calls to the server-side implementation of IEnumVARIANT. However, instead of forwarding _Next calls directly, the calls are buffered for efficiency.
For the handler marshaling to work, the server-side enumerator must implement IMarshal appropriately. In UseSmartEnum.h, I've provided an implementation of IMarshal for this purpose:
template <typename Deriving, ULONG celtBuffer = 1024> class IMarshalForSmartEnumImpl : public IMarshal {...};For example, the CollectionOfNumbers2 example class uses IMarshalForSmartEnumImpl to add IMarshal to CComEnum like so:
typedef CComEnum< IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT> > CComEnumVariant; class CSmartProxiedEnumVARIANT : public CComEnumVariant, public IMarshalForSmartEnumImpl<CSmartProxiedEnumVARIANT, 1024> { public: BEGIN_COM_MAP(CSmartProxiedEnumVARIANT) COM_INTERFACE_ENTRY(IMarshal) COM_INTERFACE_ENTRY_CHAIN(CComEnumVariant) END_COM_MAP() };
The Source
CollectionBuffer, SmartEnumVARIANT, IMarshalForSmartEnumVARIANT and two samples, CollectionOfNumbers (which doesn't use handler marshaling) and CollectionOfNumbers2 (which does use handler marshaling) are available here.Copyright
This source is copyright (c) 1998, Chris Sells.All rights reserved. No warrenties extended. Use at your own risk.
Comments to csells@sellsbrothers.com
Wednesday, Jul 18, 2001, 12:00 AM in Tools
Implementing Marshal-by-Value using ATL
July 18, 2001
Not too long ago, Jonathan Borden, jborden@MEDIAONE.NET, posted a nifty class called IMarshalByValueImpl that implemented IMarshal for objects interested in being marshaled by value . It was built in the ATL style, i.e. it used fun template tricks, and depended on the COM class also implementing either IPersistStream or IPersistStreamImpl. And, in fact, ATL provides an implementation of IPersistStreamInit called IPersistStreamInitImpl. However, IPersistStreamInitImpl has one fatal flaw: it implements GetSizeMax by returning E_NOTIMPL. This breaks Jonathan's IMarshalByValueImpl, which depends on a sane implementation of GetSizeMax from the class.
IPersistStreamInitImpl2 leverages the property map of ATL's IPersistStreamInitImpl to build GetSizeMax. The code is not pretty. It's just pieced together from ATL's implementation of IPersistStreamInit::Save and CComVariant::WriteToStream, but it seems to work (thanks to Dharma Shukla, v-dharsh@microsoft.com, for testing it!).
Here's the usage for giving a class MBV:
class PassByValue : public CComObjectRootEx<CComMultiThreadModel>, public CComCoClass<PassByValue, &CLSID_PassByValue>, public IDispatchImpl<IPassByValue, &IID_IPassByValue>, public IMarshalByValueImpl<PassByValue>, public IPersistStreamInitImpl2<PassByValue> { public: BEGIN_COM_MAP(PassByValue) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IPassByValue) COM_INTERFACE_ENTRY(IMarshal) // Don't need to expose these to the world //COM_INTERFACE_ENTRY(IPersist) //COM_INTERFACE_ENTRY (IPersistStreamInit) END_COM_MAP() BEGIN_PROP_MAP(PassByValue) // List of properties to marshal-by-value ... END_PROP_MAP() // IPassByValue methods ... private: // Whatever data members you wish to marshal ... public: BOOL m_bRequiresSave; // Required by IPersistStreamInitImpl2 };
Tuesday, Jul 17, 2001, 12:00 AM in Tools
Implementing dispatch-based interfaces in ATL
July 17, 2001
There are three kinds of ways to declare a dispatch-based interface in IDL, but ATL (as of version 3.0), only supports the implementation of 1.5 of them (duals and a raw dispinterfaces for handling events). The ATL Dispatch Sampledemonstrations the use of three ATL-based classes, one for implementing each type of dispatch-based interface. The header file that defines these classes, dispimpl2.h, is provided for use in your own projects.
BTW, thanks to Ehab Kashkash for making this code work under VS.NET as well as VC6.
Monday, Jul 16, 2001, 12:00 AM in Tools
MeowMoniker
If you're into COM Monikers, here's one that I like.
- A brief description of the purpose and the implementation of the MeowMoniker.
- MeowMoniker.zip
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.