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 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
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.
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.
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.
Thursday, Feb 22, 2001, 5:53 PM in Tools
TZ Data to XML Project
These are the outputs of my attempts to translate the native tz data into XML for easier parsing for applications other than implementations of the standard C routines related to time.
This is the first step in a project to merge time zone and map data by the Time Zone Map Group, lead by Chuck Ellis.
Done
- tz2xml.zip: A VC++ program to translate native tz data files into XML. Warning: This requires a certain directory structure and a few modified files from the tz code to export shared functions. I'm working to fix that.
- /tools/tz/tzxml.zip: Native tz data files translated to XML, including comments. Suitable for replacement as the native format. Generated by running tz2xml on the tz data files.
Yet To Do
- Namespace support.
- XSD support.
- An XSLT to translate back to native tz data format.
- An XSLT to output popular data needs, e.g. all the zones, all the rules, etc.
- An updated zic to use the new XML format instead of the existing format.
- Unix port of tz2data (I'll need help on that one).
Help!
Unfortunately, I'm but one man. If you'd like to help on any of these projects, let me know.
License
Copyright © 2001 by Chris Sells. All rights reserved. No warrantees extended. Use at your own risk. You may not distribute any portion of the tz2xml source code without express written consent. You may, however, use the source with no fee or redistribute the sample XML data at will.Friday, Feb 16, 2001, 12:00 AM in Tools
Genghis
Genghis is a set of extensions built on top of .NET and integrated with WinForms to provide application-level services in the same flavor as the Microsoft Foundation Classes. Genghis gets its name as the functional heir to Attila, a similar set of functionality built on top of ATL.
Monday, Jun 26, 2000, 12:00 AM in Tools
UrlRun
Mon, 26 Jun 2000
To deal w/ the number of wrapped URLs I get in my email box, I built UrlRun. It checks the clipboard for an URL, no matter how broken, strips spaces, newlines and greater thans out of it and runs IE. To handle an URL as show above, select it, copy it to the clipboard and launch UrlRun.exe. I keep it on my QuickStart toolbar.
If you happen to be using Outlook 2000 or Outlook XP, Tim Tabor has built an UrlRun add-in that does the magic by simply right-clicking on a mangled URL that's you've selected. It's pure sex.
Monday, Nov 1, 1999, 3:38 PM in Tools
Welcome to Attila!
Attila GDI sample
(Jim Murphy likes Attila and GDI a little too much...)
Welcome to Attila
Attila stands for "ATL for Applications." Attila is a set of extensions built on top of ATL to provide application-level services in the same flavor as MFC. Towards that end, Attila uses a lot of the same notions as ATL, e.g. heavy use of templates, static binding and reliance on the compiler and the linker doing their job. Also, in the flavor of ATL, Attila is under-documented and requires a lot of user investment to make use of it. However, once you do, we think you'll find the flexibility and efficiency worth it. If you don't, you haven't lost much, 'cuz Attila is free. Enjoy.
Disclaimer
Attila isn't a class library. Some of the headers depend on some of the others. Some are stand-alone. You are free to pull in the pieces of Attila that you need and leave the rest alone. Most of the headers were built by different folks and while every effort was made to keep things consistent by choosing ATL design techniques, everyone does things there own way.
This work is the work of the individuals involved and does not represent any contribution of their respective employers. You are under no obligation to use any of Attila at any time, but if you do, please abide by the License.
License
Attila is copyright (c) 1999-2000 by its contributors.
All rights reserved.
No warrantees extended.
Use at your own risk.
You may use Attila in your product in compiled form without royalty.
You may not distribute Attila in source code form without the express written
consent of the contributors.
The official home of Attila is http://www.sellsbrothers.com.
Installation
The current Attila bits are located at attila.zip. Download and unzip them into the folder of your choice (making sure to preserve path names). Make sure to add the Attila include directory to your compilers include path. For example, if you unzipped Attila into the c:\attila directory, add c:\attila\include to your compiler's include path. You can check to make sure you did this right by building any of the samples in the samples folder.
Support
Please do not send Chris Sells or any of the other contributors personal email complaining about Attila. Also please do not post Attila bug reports to any other mailing list, e.g. DCOM or ATL. If you like, you can submit bugs to the Attila Hit Squad mailing list (but only if you're a member). Nobody there is under any responsibility to fix anything in Attila, but you might shame someone into it. A much better way to contribute to the life of Attila is to submit the fix along with the bug report. Or even better, join the Attila Hit Squad mailing list and fix bugs submitted by others.
Official Attila Contributors
- Chris Sells csells@sellsbrothers.com
- Justin Rudd justin.rudd@home.com
- Curt Hagenlocher curth@motek.com
- Ed Ball ed@libronix.com
- Matt Wilson Matt.Wilson@wdr.com
- Simon Fell SFell@streetfusion.com
- Shawn MacFarland macfars@towers.com
- Joe O'Leary JOLeary@artisoft.com
- Anthony Toivonen anthony.toivonen@intel.com
- Rodrigo B. de Oliveira rodrigob@vesta.com.br
- Jim Murphy JMurphy@Transactionworks.com
- Jorge Lopez cprog@pacbell.net
- Brad Wilson bradw@pobox.com
- Shawn A. VanNess xonix@MINDSPRING.COM
Who Did What
- Chris did a lot of the initial framework.
- Justin did CMsgTranslator.
- Ed did CRegPreferences and CRegWindowSerializer.
- Curt did the common dialogs and restructured and added AX support to property pages.
- Matt and Shawn did work on the coolmenus and coolbars.
- Simon did the multi-SDI stuff.
- Joe did the command UI routing support.
- Anthony did the command routing.
- Rodrigo and Jim did the GDI wrappers.
- Jorge did the Doc/View stuff.
- Brad did the DDX/DDV support.
- Shawn did CSimpleList.
Documentation
You might find some implementation notes for Attila in the docs folder, but you probably won't. If you have any documentation you'd like to submit, we'd love to have it.
Samples
Check the samples.htm file in the samples folder of the Attila distribution. You can build all of the samples from the command line using the buildall.cmd batch file in the samples folder.
CComSingleInstanceModule
- Uses Single_Instance_App as the DDE application name, preventing more than one single instance ATL application from running at a time. DDE application name should be based on something unique to the application, e.g. the module name.
SdiDocView
- No MRU.
MdiDocView
- None of the MDI accelerators work, e.g. ctrl-tab.
- There seems to be a lot of glue code in CMainWindow that could be hoisted to a base class.
- No MRU.
LookOut
- Left pane needs work.
History
- 8/14/00, Various property sheet enhancements.
- 1/28/00, Simulated PSN_APPLY for Wizards and PSN_APPLY for modeless propsheets bug fix.
- 12/8/99, CSimpleList and bug fixes.
- 11/29/99, Bug fixes.
- 11/25/99, Bug fixes.
- 11/10/99, DDX/DDV and bug fixes.
- 11/1/99, Bug fixes.
- 11/1/99, Beta release.
Friday, Jun 27, 1997, 12:00 AM in Tools
Welcome to MeowMoniker!
Overview
MeowMoniker and Feline are COM objects designed and built by Chris Sells, with a great deal of input from Don Box and Tim Ewald. The MeowMoniker is a custom moniker derived from the CComMoniker class implemented by Don Box, Tim Ewald and Chris Sells.
To contact the authors with suggestions or comments, use csells@sellsbrothers.com, tewald@obelisk-llc.com and dbox@microsoft.com.
MeowMoniker Usage
Microsoft tags every marshalled interface reference packet with the signature MEOW (which they claim stands for Microsoft Extended Object Wire-representation). This binary packet is a serialized interface reference used for inter-apartment activation. Turning this binary packet into a string (via some encoding technique, i.e. Base64) allows a serialized interface pointer to be passed more easily than binary data, e.g. as a command-line argument or in an HTML page.
Monikers, on the other hand, provide a general-purpose way for a client to separate itself from the object binding policy. These object binding polices can be composed and stored in strings. These strings contain a string description of the binding policy and policy parameters. By building a custom moniker that takes a string-ized interface reference as a parameter, a client can use the standard moniker-based binding algorithm and bind to a running object via an interface reference. The MeowMoniker is an implementation of a moniker that can compose display names out of interface pointers and bind to objects via its display names.
To create a MeowMoniker from an interface pointer, MeowMoniker.dll exposes the CreateMeowMoniker() function:
STDAPI CreateMeowMoniker(
IUnknown* punk, // Pointer
to the interface to be marshaled
REFIID riid, // Reference to the
identifier of the interface
DWORD dwDestContext, // Destination context
DWORD mshlflags, // Reason for
marshaling
IMoniker** ppmk); // Indirect
pointer to the moniker
e.g.
IMoniker* pmk;
hr = CreateMeowMoniker(punkObject,
IID_IUnknown,
MSHCTX_DIFFERENTMACHINE,
MSHLFLAGS_NORMAL,
&pmk);
To obtain the MeowMoniker's display name, use the GetDisplayName() member function of the IMoniker interface:
HRESULT GetDisplayName(
IBindCtx *pbc, // Pointer to bind context to
be used
IMoniker *pmkToLeft, // Pointer to moniker
to the left in the composite
LPOLESTR *ppszDisplayName); //Indirect
pointer to the display name
e.g.
IBindCtx* pbc;
hr = CreateBindCtx(0, &pbc);
wchar_t* pwszDisplayName;
hr = pmk->GetDisplayName(pbc, 0, &pwszDisplayName);
To parse a display name composed by the MeowMoniker, OLE32.dll exposes the MkParseDisplayName() function:
WINOLEAPI MkParseDisplayName(
LPBC pbc, // Pointer to the bind context
object
LPCOLESTR szUserName, // Pointer to display
name
ULONG FAR *pchEaten, // Pointer to the
number of characters consumed
LPMONIKER FAR *ppmk); // Indirect
pointer to the moniker
e.g.
IBindCtx* pbc;
hr = CreateBindCtx(0, &pbc);
IMoniker* pmk;
ULONG cchEaten;
hr = MkParseDisplayName(pbc,
pwszDisplayName,
&pmk);
To bind to a object, use the BindToObject() member function of the IMoniker interface:
HRESULT BindToObject(
IBindCtx *pbc, // Pointer to bind context
object to be used
IMoniker *pmkToLeft, // Pointer to moniker
that precedes this one in the composite
REFIID riidResult, // IID of interface
pointer requested
void **ppvResult); // Indirect
pointer to the specified interface on the object
e.g.
IUnknown* punk;
hr = pmk->BindToObject(pbc, 0, IID_IUnknown, (void**)&punk);
Note: The MeowMoniker does not current support composition or the IMoniker
interface member function BindToStorage().
Feline Usage
Since neither VB nor VBScript supports calling CreateMeowMoniker() and since VBScript has no equivalent of the VB function GetObject(), the Feline object provides a dual-interface for making display names out of interface pointers and parsing moniker display names:
[
object,
uuid(CB18CB8E-C7CC-11D0-9A44-00008600A105),
dual,
helpstring("DIFeline Interface"),
pointer_default(unique)
]
interface DIFeline : IDispatch
{
[id(1), helpstring("Returns the Meow Moniker name of an
interface pointer")]
HRESULT GetDisplayName([in] IDispatch* pdisp, [out, retval]
BSTR* pbstrName);
[id(2), helpstring("Returns an interface pointer given
any moniker display name")]
HRESULT ParseDisplayName([in] BSTR bstrName, [out, retval]
IDispatch** ppdisp);
};
The following is an example Active Server Page that creates an object on the
server, creates a display name using the object's interface pointer and uses
the display name to create a client-side script. The client-side script uses
its own instance of a feline object to parse the display name and bind to the
object created on the server-side.
<HEAD>
<TITLE>feline.asp</TITLE>
</HEAD>
<BODY>
<object classid="clsid:CB18CB8F-C7CC-11D0-9A44-00008600A105"
runat=server
id=feline>
</object>
<object classid="clsid:7CF322E0-29A9-11D0-B367-0080C7BC7884"
runat=server
id=pt>
</object>
<object classid="clsid:CB18CB8F-C7CC-11D0-9A44-00008600A105"
id=feline>
</object>
<script language=vbscript>
dim pt
set pt = feline.ParseDisplayName("<%=
feline.GetDisplayName(pt) %>")
pt.x = 100
pt.y = 200
document.write pt.x & ", " & pt.y
</script>
</BODY>
</HTML>
CComMoniker Implementation
The MeowMoniker derives most of its functionality from CComMoniker, provided in mkbase.h and mkbase.cpp. This base class implements IMoniker, IParseDisplayName, IROTData and IMarshal (for marshal-by-value). This implementation uses the moniker's display name as the persistent state. The custom moniker implementor deriving from CComMoniker must provide the CSLID of the moniker as the single template parameter (used in the implementation of the GetClassID() member function of IPersist and the GetUnmarshalClass() member function of IMarshal). The implementor may override any of the base class's member functions but must implement these three IMoniker members:
STDMETHODIMP BindToObject(
IBindCtx* pbc,
IMoniker* pmkToLeft,
REFIID riidResult,
void** ppvResult);
STDMETHODIMP GetDisplayName(
IBindCtx* pbc,
IMoniker* pmkToLeft,
LPOLESTR* ppszDisplayName);
STDMETHODIMP ParseDisplayName(
IBindCtx* pbc,
IMoniker* pmkToLeft,
LPOLESTR pszDisplayName,
ULONG* pchEaten,
IMoniker** ppmkOut);
As a aid in parsing display names, the CComMoniker class provides the following helper function:
bool MatchesProgID(
const wchar_t* pwszDisplayName, // Display name
being parsed
const wchar_t** ppwszDisplayParam); // Pointer to
character past :
This function checks the display name for a leading ProgID or
VersionIndependentProgID followed by a colon and returns a pointer to the first
character of the display name parameter(s), i.e. one character past the colon.
The implementation of this function requires the derived class to implement the
following member functions:
virtual const wchar_t* ProgID() =0;
virtual const wchar_t* VersionIndependentProgID() =0;
e.g.
const wchar_t* ProgID() { return L"dm.meow.1"; }
const wchar_t* VersionIndependentProgID() { return L:dm.meow"; }
CoMeowMoniker Implementation
The MeowMoniker is an ATL object named CoMeowMoniker. It derives from CComMoniker and provides an implementation of the required members only. The bulk of the implementation is contained in the two member functions, MarshalInterface() and UnmarshalInterface(). These two member functions manage the Base64 encoding and decoding of the interface pointer. The actual Base64 implementation is kept in Base64.cpp.
CoFeline Implementation
The Feline is an ATL object named CoFeline. Its implementation of GetDisplayName() uses a MeowMoniker. Its implementation of ParseDisplayName() uses MkParseDisplayName() so that any moniker's display name can be used.
Download
Here.
YACL
The implementation of the MeowMoniker is based on utilities provided in the
YACL (Yet Another COM Library), developed and maintained by Don Box and
available at Don's YACL web site.
The pieces of YACL
that I have used, I've included in the appropriate directories.
Revisions
6/15/98
- Updated MeowMoniker to perform GetSizeMax properly.
- Updated MeowMoniker and CComMonikerBase to use CComPtr and CComQIPtr instead of SmartInterface.
- Removed use of HRESULTEX and therefore the use of C++ exception handling.
- Moved some includes around.
- Reduced RelMinDepend build from 89KB to 80KB. Could probably reduce it more if I could find Matt Pietrek's tinycrt and stop using the real CRT.
Copyright
MeowMoniker, copyright (c) 1997, Chris Sells.
Feline, copyright (c) 1997, Chris Sells.
CComMoniker, copyright (c) 1997, Don Box, Tim Ewald and Chris Sells.
YACL, copyright (c) 1997, Don Box.
Extensions to YACL, copyright (c) 1997, Tim Ewald and Chris Sells.
All rights reserved. NO WARRANTIES ARE EXTENDED. USE AT YOUR OWN RISK.
No older posts 225 newer posts