How can I implement Marshal-By-Value components?
How can I create complex structures that the MS scripting engines can take advantage of?
How can I use the ATL wizard to help me generate code?
Then this article and its accompanying tools should help you out a great deal. If you already know the answers to these questions, I still think that the MBV Wizard is a great time saver. Additionally, the COM Container Library(CCL) is a simple general purpose set of containers that can be used to create complex structures which can be used in scripted environments.
Marshal By Value
I will actually not provide an in-depth discussion of Marshal-By-Value here. There are some great resources, which do a much better job than I would. Try taking a look at:
Essential COM or Richard Grimes articles on www.vcdj.com, or past issues of MSJ in Don Boxs QA column.
The ATL wizard is an extremely powerful tool for capturing code idioms and patterns. These patterns can be captured in a template form and new components can be generated by prompting the user to fill in certain attributes. By using the wizard, you can automate the generation of boilerplate code. Until recently(VStudio 6.0) the ATL Wizard was an undocumented feature. The only resource available is from Alex Stockton at www.worldofatl.com. Alex has done a great job at putting some good samples and references on how to customize your code generation with the ATL Wizard. I would definitely have had a very hard time figuring out how to use the wizard without this valuable resource.
If you are doing large scale COM development, I highly recommend trying to leverage the ATL Wizard to generate as much of your code as possible. Once you have identified certain patterns in your system, it helps a great deal to capture them in this template form. It will do wonders for your code standards as well as knowledge transfer to others. I surely hope to see additional extensions to the wizard such as script handlers that get called when a new method is added.
The Marshal By Value wizard allows the user to generate a COM component the implements IMarshal, and IPersistStream and is suitable for marshalling. Skeletons are generated for all the necessary functions and TODOs are littered throughout the code where some work still needs to be done. The wizard also has the flexibility to generate the necessary functionality for a container that can be used in scripting environments. This is done by generating implementations of _NewEnum,Value and Count. To make your component a container, you must select
the Is Container checkbox on the Container Attributes of the wizard. All the generated container code assumes that you are using STL for your container implementation. You also need to supply the index type for the container. In the case of a vector, it will generally be a long, but in the case of a map, it could be a BSTR.
You will notice comments in the StreamHelpers.h and the MBVClass_impl.h files which reflect other authors contributions. In particular, I must thank Axel Heitland and Don Box(via MSJ) for their help with code samples when I was getting started in the COM world a while back.
Installing the Wizard
Note that this wizard has only been tested in VC++ 5.0. I doubt it will work in 6.0.
There are a few simple steps that need to be done to install the MBV Wizard:
- Compile .\MBV\MBVWizard\MBVContainerPage. This will produce MBVContainerPage.dll in .\MBV\MBVWizard\
- copy all the files from the .\MBV\MBVWizard directory to your .\Devstudio\SharedIde\template\atl directory. This will include the MBVContainerPage.dll you just created.
- Run regsvr32 on .\Devstudio\SharedIde\template\atl\MBVContainerPage.dll
- Double click .\Devstudio\SharedIde\template\atl\MBVClass.reg to put the settings for the wizard into the registry
You should now be able to create an ATL project and generate a MBV component.
COM Container Library and Samples
To test the wizard, I created a container library consisting of CCLSTLVector and CCLSTLMap. You can use these containers to implement simple data-types for use in scripted environments or to return multiple values in one trip to the server. The containers are easy to use and have much less overhead than ADO Recordsets. We use similar containers where an ADO Recordset is overkill or too bulky to be used. Note that you can place Recordsets into a map or a vector if you need to. The disadvantage of using these containers as opposed to a Recordset is lack of support for binding in the presentation layer. Since ADO 1.5 lacked the ability to fabricate a Recordset, these containers were extremely important. Now, with this support in ADO 2.0, it is not as imperative to have these containers.
People who are familiar with the STL containers should have no problem using these since I mimicked the STL interfaces.
To use the library, you must first build .\MBV\bin\COMContLib.dll and run regsvr32 on it. To run the tests, you must build .\MBV\CCLTests\CCLTestServer.dll and .\MBVCCLTests\CCLTestClient.exe. After building and registering the library, you can run the tests by putting CCLTestServer.dll in a package in Microsoft Transaction Server. You can then run CCLTestClient.exe to test that it is working correctly. There is also an ASP page in .\MBV\CCLTests\ccltest.asp that demonstrates how to use the containers in a scripted environment.
You will also find that since these containers implement IPerisistStream, that they can easily be saved away to a structured storage file. This holds true for any MBV Class generated with the wizard.
For containers that hold VARIANT types, you will need to take advantage of getVariantMarshalSize() in StreamHelpers.h in your implementation of GetSizeMax(). This function will recursively traverse a VARAINT to determine its actual marshal size. I couldnt determine another way to do this, so if anyone has a better idea I would like to hear it. There is an example of this in CCL.
All the source code for the Wizard and CCL are provided as-is. Please feel free to use them for your own purposes. I make no guarantees to their stability, so if they break anything, you cannot blame me. If you find a bug, or would like a feature added, drop me a line at firstname.lastname@example.org and I will see what I can do. Enjoy.