ActiveX Controls for Mac
Volume Number: 13 (1997)
Issue Number: 6
Column Tag: Webtech
ActiveX Controls for Macintosh
by Michael Fanning, Microsoft
Microsoft's next generation of content-rich web page development
Welcome
Macintosh ActiveX is Microsoft's native implementation of ActiveX controls for the Macintosh platform. A "control" can be loosely defined as a next-generation plug-in technology for Web browsers such as Microsoft's Internet Explorer, Netscape Navigator, and (soon) Apple's Cyberdog browser part. Controls can also be used to quickly add specialized functionality to desktop applications and development tools in addition to Web sites. Mac ActiveX provides a powerful environment for developing interactive and content-rich Web pages. ActiveX on the Macintosh retains much of the ActiveX programming model on the Win32 platform and most features; there are, not surprisingly, some important architectural differences.
Mac ActiveX is built on the Component Object Model (COM), a simple object model that provides mechanisms for object instantiation, object querying and object reference counting (a means of controlling object usage & lifetime). COM interfaces allow for flexible object design and are semantically equivalent to Java interfaces (for a definition of COM interfaces, see the beginning of the 'Architectural Overview' section at the end of this article). Macintosh ActiveX is based on a lightweight COM library which has been optimized for in-process controls. These services allow ActiveX controls to work seamlessly within a browser rather than as separate processes.
Other advanced features of Mac ActiveX make the experience of ActiveX controls more seamless than plug-ins. Transparent download, for example, permits a control to be downloaded, installed, and activated in a Web page without requiring the browser to be restarted or that the user go through any installation procedure.
This article presents a general architectural overview of Mac ActiveX and a detailed description of its software development kit. The last half discusses the mechanics of building and running ActiveX controls and details a simple modification of an SDK sample in order to create a new, custom control. Much of the information here appears in two essential SDK documents, the ActiveX User's Guide and ActiveX Technical Guide. There are pointers to additional SDK documents and other references where topics to which I refer are beyond the scope of this article. I've included definitions of some important, basic terms and concepts as they appear for anyone who might be encountering them for the first time.
The final section of the article contains important information on obtaining technical support from Microsoft for developing ActiveX controls on the Macintosh.
ActiveX and Java
ActiveX and Java are complementary, not competing technologies. First and foremost, Java is a programming language. Second, Java is a set of virtual machine bytecodes that can be executed on any platform running a Java Virtual Machine (VM). Third, Java is a set of programming interfaces that define the underlying services available from Java code.
ActiveX, on the other hand, provides a totally different set of benefits focused on integrating objects created in Metrowerks' CodeWarrior environment with Web pages. ActiveX controls are native Mac applications and can be written using any of the rich MacOS services available through C or C++ interfaces.
ActiveX, OLE, and COM
ActiveX and OLE for Macintosh are both built on the powerful foundation of the Component Object Model. The 2.0x version of OLE was a monolithic release which incorporated COM and the OLE libraries in a single shared library (for PowerMac) or 68k Extension. The latest version of OLE has been broken out into constituent libraries which contain related functionality, one of which is the Microsoft Component Library which supports Mac ActiveX controls. This factored version of COM/OLE interacts more efficiently with Apple's Code Fragment Manager, performance is improved and less code is mapped into memory on launching an OLE application.
The latest version of COM, included in the Mac ActiveX SDK, has been optimized for lightweight, high-performance, in-process objects such as ActiveX controls. New API's make it easier for applications or controls to exploit the Alias Manager and work within the Mac-specific runtime world to deliver compelling Macintosh solutions. ActiveX controls require the use of the new Microsoft Component Library and are not compatible with OLE 2.0x.
SDK Contents Overview
The ActiveX SDK for Macintosh consists of three separate,
downloadable archives available at: http://www.microsoft.com/intdev/sdk/mac/mactivex.html
ActiveX SDK (SDK_sea.hqx, ~2.5 MB) -- SDK, including sample controls and containers, projects, source, headers, COM libraries, and ActiveX plug-in adapter.
ActiveX Runtime (Runtime_sea.hqx, ~6000k) -- binary files needed to run ActiveX controls in commercial containers such as Internet Explorer and Netscape Navigator. (Note: this archive contains the 'MS IE ActiveX Lib (PPC)' library required to use ActiveX controls in versions of Internet Explorer later than 3.0b1. If you are using 3.0b1 or earlier, the ActiveX SDK archive listed above contains all the runtime binaries required and you can skip this download.)
ActiveX Documents (Documents_sea.hqx, ~600k) -- documentation describing how to create and run ActiveX controls on Macintosh. It contains the ActiveX User & Technical Guides, as well as documents covering more advanced ActiveX topics such as asynchronous monikers and code download. This archive also includes an introduction to COM and a Component Object Model technical overview/specification.
Note that the Beta3 release of the Mac ActiveX SDK is currently being finalized and will be available by the time this article is printed. Beta3 will be fully compatible with Metrowerks CodeWarrior 11. There will be a document in the SDK correcting any information presented here which needs revision in order to build and run the Beta3 samples. The Beta2 release is fully compatible with Metrowerks CodeWarrior 10.
The ActiveX for Macintosh SDK
General Overview
The ActiveX SDK for Macintosh includes:
- Microsoft Component Library & ActiveX Plugin.
- Sample Controls.
- Sample Containers.
- Source, headers, GUIDs, and other supporting files.
- Utilities.
The Microsoft Component Library includes the most important elements of the Component Object Model, managing object instantiation, the Registry and essential COM structures. This library can be used to build other, custom interfaces. The ActiveX interfaces are built on this library and do not require the OLE 2.0x libraries. The ActiveX Plug-in permits the use of ActiveX controls in browsers that support Netscape Navigator-compatible plug-in technology.
The Microsoft Component Library was named comi2.ppc.ret in previous beta releases of the Mac ActiveX SDK. You should throw away any copies of this file which exist on your system before using the latest version of the SDK.
The SDK Sample Controls include samples demonstrating basic features of Mac ActiveX controls including property retrieval, data streaming, event handling, drawing, popup menus, dialogs, outgoing custom interfaces, and more. These samples have been designed to easily serve as the basis for writing your own controls. Metrowerks CodeWarrior version 10 or later is required for building all Macintosh ActiveX controls.
The Mac ActiveX SDK ships with several Sample Containers which provide a simple environment for testing individual controls, including a Metrowerks PowerPlant-based container that extends PowerPlant document and view classes to host controls.
SDK Utilities include tools for managing the COM Registration Database, registering and unregistering controls, GUID creation, bin-hexing, and more.
More SDK Details
Folders in the SDK include
*QuickStart* Documents and tools for getting started in ActiveX right away
COM Libraries Debug and retail versions of Mac COM
Common Files used to build ActiveX controls and containers
Container Common Files used to build more than one sample container
Containers Sample ActiveX containers (including Netscape plug-in)
Control Common Files used to build more than one sample control
External Libraries Shared libraries used by ActiveX controls
Guids COM globally unique identifier (GUID) definitions
Headers ActiveX system header files
Sample Controls Source code and HTML for samples (see preceding section)
UrlMon Support for COM URL Moniker interface
Utilities ActiveX utilities
SDK Utilities
The Utilities folder contains these utility programs:
AutoBin Automatically generates binhexed versions of controls
Create GUID Generates GUIDs for interfaces and controls
DeScoder Decodes COM error values
RegEdit Edits the COM Registry ('PPC Registration Database')
Register Adds/removes a control from the registration database
When a control is created for the first time, it must be binhexed so that the automatic code download feature will download it, decode it (un-binhex it), and register it in the COM Registry. AutoBin is a drag-drop utility which is used to create binhexed versions of controls.
New controls need a globally unique identifier (GUID) defined in their local register.h. Use the CreateGUID utility to generate a unique ID and copy it into the register.h file. Also, don't forget to copy this string into the HTML file that runs your control.
GUIDs (globally unique identifiers), are 128-bit values used to identify COM/ActiveX interfaces and object classes (something like an application Creator code identifies an application, only in 16 bytes, not 4). IIDs are interface identifiers, a type of GUID which specifically identify interfaces. IIDs are used typically when requesting an instance of an interface implementation. CLSIDs, or class identifiers, identify object classes. CLSIDs associate objects with particular ActiveX controls.
DeScoder is a simple utility which returns human-readable strings from error values returned by COM/ActiveX API and methods. Typing in a value of 0x80030002, for example, returns the more descriptive error label 'STG_E_FILENOTFOUND'.
If you have to edit the COM registry (named 'PPC Registration Database' inside System:Preferences) directly, use the RegEdit utility. This program displays the contents of the current COM registry and allows you to edit, add, and remove keys. Removing keys of outdated controls is the most common use of this utility. However, the Register utility program can do that for you as well.
The Register utility provides a simple means of adding or removing control registry information from the Registration Database. In actual use, a control will be registered as part of its code download and installation process. The Register utility is more useful for removing outdated control information before testing a new version of it. To use Register, shift-drag the shared library of the control you want to unregister onto the Register application. The COM libraries must to be either in a common system location, such as the Extensions folder, or in the same folder as the control being unregistered.
Getting Started
Follow these steps to familiarize yourself with the contents of the Mac ActiveX SDK:
- Install the runtime files required for your browser.,
- Build the ActiveX SDK samples.
- Run the sample controls.
- Study the sample control source code (and the ActiveX Technical Guide).
- Modify a sample control in order to create your own.
- Build and run your own control.
The rest of this article provides more detail on accomplishing each of these steps.
Install the Runtime Files
Mac ActiveX controls can be viewed in several different containers and browsers. Commercial containers include Microsoft Internet Explorer 2.1 or later, Netscape Navigator 2.0 or later. To run controls in Internet Explorer 3.0b1 or earlier, Navigator, or Cyberdog, install the ActiveX Plugin and the Microsoft Component Library. The ActiveX Plugin should be placed in the browser Plug-ins folder; the Microsoft Component Library lives in the System:Extensions folder. An OpenDoc part adapter is currently in development to support ActiveX controls in Cyberdog and should be available soon.
Before using Microsoft Internet Explorer to run Mac ActiveX controls, make sure and do the following:
- Run Internet Explorer to create the 'Explorer' folder inside System:Preferences.
- If you are using Internet Explorer 3.0 (later than b1), drag MS IE ActiveX Lib (PPC) from the ActiveX Runtime portion of the SDK to System:Extensions.
- For Internet Explorer 3.0b1 or earlier, or if using another browser that supports plug-ins, such as CyberDog or Netscape Navigator, drag the ActiveX Plugin to your Plug-ins folder.
- Drag the Microsoft Component Library from the ActiveX SDK portion of the SDK to System:Extensions. The 'Build ActiveX SDK' script located at the root of the 'ActiveX SDK' folder will perform steps 2-4.
- Choose Edit/Preferences within Internet Explorer, then click the 'Web Content' tab. Make sure 'Use Plug-In Objects' is checked. In Internet Explorer version 2.1, choose Edit/Options, click the 'Web Content' tab, and make sure 'Load Plug-In Objects' is checked.
- Restart the computer.
Build the ActiveX SDK Samples
Individual ActiveX sample controls can be built by opening the sample projects with Metrowerks CodeWarrior version 10 or later and compiling. The SDK includes a script labeled 'Build ActiveX SDK' which will configure your system for running ActiveX controls (by copying required shared libraries to the Extensions folder, etc.) and build all samples in the SDK. If there is a problem building any samples during script execution, you can cancel by clicking the process icon in the upper right corner of the Mac monitor, selecting 'Build ActiveX SDK' in order to bring the script to the foreground, and pressing command-period.
Run the sample controls
After configuring your system and building a sample control, running it is simply a matter of dragging the HTML (xxx.htm) file found in the sample control folder onto your particular Web Browser.
Writing Your Own Control
Notes on the SDK Sample Code
CBaseControl.cpp and .h in the Control Common folder contain the source for the ActiveX control base class from which all other controls descend. Each individual control folder contains the files CxxxControl.cpp and .h, which include the source for the subclass. Controls that do data streaming will pull in the CBaseBindStatusCallback class, found in Control Common, in order to receive progress status and data streams.
The ActiveX base classes are provided as a convenience to the developer, providing baseline functionality which speeds control development. Developers are encouraged to use the base classes but are not limited to them -- any semantically equivalent implementation of the interfaces can be used. The Mac ActiveX development team is interested in feedback on the base classes in order to make them better and better. See the end of this article for information on contacting the development team through a Microsoft-provided mailing list.
Register.h in each control's project folder contains the defines for the sample control GUIDs, program IDs, and so on, required for COM registration.
The sample controls override anywhere from 2 to 15 methods of the base control depending on the features desired. For simple controls that have no data or properties, you only have to modify some of the event methods and Draw methods. For properties and data, one or both of the Load methods and possibly the OnStopBinding and OnDataAvailable methods of CBaseBindStatusCallback must be modified. The Clock is the simplest, with no data or properties.
The PictPlayer, MoviePlayer and ListBox all implement data streaming to some degree. PictPlayer is an example of a control that receives data through multiple data streams. Instead of mixing in and subclassing CBaseBindStatusCallback, a separate subclass CPictControlBSC is instantiated for each data stream. The MoviePlayer demonstrates data streaming to a file.
Modify, Build and Run a Sample Control
You can prepare for writing your own control by copying an existing control folder and modifying it. The most basic sample control is the clock, as it supports only event handling and drawing, with no support for properties or data streaming.
Here are the steps:
- Rename files as desired.
- Modify the event and Draw methods for basic functionality.
- Modify the Load, OnStopBinding and/or OnDataAvailable methods as necessary for the level of streaming support you require.
- Define an identifier in the prefix file for your control.
- Add new GUID and constants for your control within the register.h file.
- Build.
- Generate a .hqx file from the shared library file of your new control.
- Modify the HTML file CODEBASE and DATA arguments to point to your .hqx file for code and any data file you may have.
- Modify the HTML file CLASSID argument to the ASCII value of your new CLSID GUID.
- Drop your new HTML file onto Internet Explorer.
What could be simpler? If your control runs successfully, you probably did everything right. If not, check to make sure your URLs for CODEBASE and DATA are correct, as well as the ASCII CLASSID. Make sure an entry for your control appears in the Registry. Check the ActiveX Cache in System:Preferences:Explorer to see if your control is there.
SpyClock
This section is a step-by-step example of modifying an SDK sample in order to create a new, custom control. The sample we'll modify is 'Clock', the simplest control in the Mac ActiveX SDK, with no persistent data or properties. We'll instrument this control in order to write debug information tracking calls to various control information out to LRPCSpy, an OLE for Macintosh debugging utility. LRPCSpy is located in the Beta3 SDK Utilities folder. Also, be sure to check out CodeSampler in the SDK 'QuickStart' folder. This tool takes care of much of the work involved in generating a new control based on one of the samples. The detailed information given below is a good way to become familiar with ActiveX controls and the work done by CodeSampler to create new versions of them.
Clock Modifications
- Duplicate the 'Clock' sample folder and rename it 'SpyClock'.
- Within the SpyClock folder, delete the following files, if they exist: Clock, Clock.hqx, and Clock.xSym.
- Rename all file names from *Clock* to *SpyClock*. (clock.acx should be named spyclock.acx, for example, CClockControl.cpp should be CSpyClockControl.cpp).
- Drag a copy of CBaseCOM.cpp from the SDK 'Common' folder to the 'SpyClock' folder. Drag a copy of CBaseControl.cpp from the 'Control Common' folder to 'SpyClock'. We will modify both these files later on and don't want the changes to be pulled into other controls.
- Open the SpyClock.u project file in Metrowerks CodeWarrior.
- Choose 'Add File...' from the Project Menu. Add SpyClock.rsrc and CSpyClockControl.cpp to the project.
- Remove references to Clock.rsrc and CClockControl.cpp from the project.
- Open CSpyClockControl.cpp and do a Find/Replace All, replacing all references to ClockControl with SpyClockControl.
- Repeat step #7 in file CSpyClockControl.h. Save and close the file.
- Add the following line to the beginning of CSpyClockControl.cpp: #include "spy.h". (This header will be created in a later step). Save and close the file.
- Choose 'Project Settings...' from the CodeWarrior Edit Menu. Select 'PPC Project' in the Project section. Edit the project output file name to SpyClock.
- Still within Project Settings, select 'C/C++ Language' in the Language Settings section. Edit the Prefix File name to read SpyClockPrefix.h. Close the Project Settings dialog.
- Open register.h. Do a Find/Replace All, replacing all references to Clock with SpyClock.
- Switch to the Finder, locate the CreateGuid.ppc utility in the SDK, and launch it. CreateGuid.ppc will automatically generate a new GUID for use in our modified control on startup.
- Click the 'DEFINE_GUID' radio button. This changes the representation of the GUID currently displayed to an ActiveX macro statement which will literally construct the 128-bit GUID value in the project on compiling. Click 'Copy' to put this macro statement on the Clipboard.
- Back in the CodeWarrior IDE, select the first three lines of register.h and then Paste. This will replace Clock's DEFINE_GUID macro with the new version provided by CreateGuid.ppc. Replace the <<name>> placeholder in the macro statement with the label 'CLSID_ocx' (no apostrophes).
- Select the text representation of our new GUID from the commented line at the top of register.h. Select the GUID from the left brace all the way through the right brace, then Edit/Copy or press cmd-C.
- Select the remaining instance of Clock's GUID in register.h from brace to brace. Press cmd-V or Edit/Paste to replace it with the new GUID. Save and close register.h.
- Open spyclock.acx (still within CodeWarrior), select the old GUID in its entirety, and Paste the new version. Save and close the file.
- Open SpyClock.inf, select the old GUID at the end of the file from brace to brace and replace it with the new version. Do a Find/Replace All, replacing all occurrences of Clock with SpyClock. Save and close the file.
- Open SpyClock.htm. Replace all occurrences of Clock's GUID in this file with the newly generated GUID. Both GUID instances in this file appear without braces. Be sure to delete the right and left braces from the new GUID after pasting each. Save and close the file.
New Code for SpyClock
Now it's time to generate some new code (at last!). Choose New from CodeWarrior's File menu and save the file as "spy.h" to the SpyClock folder. Type the following into the spy.h, save, and close:
#ifndef _SPY_H_
#define _SPY_H_
#include <string.h>
#define SZ_MAX_LEN 128
void SpyOutputDebugString (const char *sz);
Boolean SimpleSendHighLevelEvent( OSType sig,
OSType eventClass,
OSType eventID,
unsigned long msgRefCon,
char *eventBuf,
unsigned long bufLen);
#endif // !_SPY_H_
Create another file inside CodeWarrior and save this one to disk as "spy.c". Type the following into the "spy.c" window, save, and close:
#include "spy.h"
void SpyOutputDebugString(const char *sz)
{
long cb;
char buf[SZ_MAX_LEN];
cb = strlen(sz);
if (cb > SZ_MAX_LEN)
cb = SZ_MAX_LEN;
BlockMove(sz,buf,cb);
SimpleSendHighLevelEvent('LSpy','Dbg2','TEXT',0,buf,cb);
}
Boolean SimpleSendHighLevelEvent(OSType sig,
OSType eventClass,
OSType eventID,
unsigned long messageRefCon,
char *eventBuf,
unsigned long bufLen)
{
EventRecord theHLEvt;
OSErr err;
theHLEvt.what = kHighLevelEvent;
theHLEvt.message = eventClass;
theHLEvt.where = * (Point *) &eventID;
err = PostHighLevelEvent(&theHLEvt,
sig,
messageRefCon,
(Ptr) eventBuf,
bufLen,
receiverIDisSignature);
if (-917 == err)
err = PostHighLevelEvent(&theHLEvt,
sig,
messageRefCon,
(Ptr) eventBuf,
bufLen,
receiverIDisSignature);
if (noErr == err)
return true;
else
return false;
}
Add spy.c to the SpyClock project and we're ready to use the SpyOutputDebugString routine to write output messages to the LRPCSpy utility. SpyOutputDebugString checks the length of the debug string passed to it, does a BlockMove to a local buffer, then passes the latter to SimpleSendHighLevelEvent. SpyOutputDebugString specifies LRPCSpy's creator code 'LRPC' as the target for the high-level event. LRPCSpy supports an event class signified by the id 'Dbg2', 'TEXT' indicates the type of message we're passing.
SimpleSendHighLevelEvent does exactly what its name implies: it creates and sends a simple high-level event. If the first try returns a value of -917 ('session was closed'), it makes a second attempt. Note that SimpleSendHighLevelEvent returns a Boolean to indicate whether or not the event was sent successfully. The implementation of SpyOutputDebugString above does not test for a return value.
Also worth noting is that SpyOutputDebugString could easily be replaced with other functionality. The buffer passed in could be converted to a Pascal string and passed to DebugStr() for example. It could open a text file and write the information to disk. It could also be modified to pass the buffer to another ActiveX control (such as the Console control) for output. The SDK Popup sample actually contains an example of the latter.
Now that SpyOutputDebugString is in place, it's time to instrument the SpyClock sample so that we can track code execution when the control is running. Open CBaseControl.cpp and add a #include of "spy.h" to the beginning of the file. Then add SpyOutputDebugString statements to the beginning of each method implementation in the file. Inside CBaseControl::SetSite, for example, add the following statement at the beginning of the member function:
SpyOutputDebugString
("CBaseControl::IObjectWithSite::SetSite\r");
Note that the message passed to our output routine contains a complete reference to the method called, that is, it reflects the specific interface IObjectSite which is wrapped by CBaseControl. These 'complete' function names appear in the comments prefacing each CBaseControl method. Just Copy and Paste to add them to each call to SpyOutputDebugString.
After modifying the SpyClock copy of CBaseControl.cpp, open CBaseCOM.cpp and make similar changes to each method implementation in the file. Don't forget to add a #include of "spy.h" before any calls to SpyOutputDebugString.
Now it's time to build our new control!
Build, BinHex, and Run!
- Build SpyClock within Metrowerks CodeWarrior.
- Drag the resultant control labeled 'SpyClock' onto the AutoBin 1.0 utility. This will generate a binhexed representation of the control inside the SpyClock folder named 'SpyClock.hqx'.
- Launch LRPCSpy. Be sure to modify LRPCSpy's Preferred memory partition size to at least 512K before running.
- Drag SpyClock.htm onto Microsoft Internet Explorer or other supported Web Browser.
After doing so, you should see the SpyClock control appear. A window will appear in LRPCSpy in the background. You should see text messages appear in that window which track various control object methods as they are called.
That's all There is to it!
This detailed control modification example should tell you all you need to know in order to start creating your own ActiveX controls on the Macintosh today. The SDK is filled with interesting, powerful examples to study and serve as a basis for the next generation of content-rich Web development. ActiveX is built on a native Macintosh implementation of COM, which has been broken out of OLE and optimized to support high-performance, lightweight, in-process control objects. Why not get started on your own today, and see what comes next, tomorrow?
For those of you with wind left in your lungs and a desire to dive even deeper into what makes ActiveX tick on the Macintosh platform, read on.
Figure 1. ActiveX for Macintosh Architectural Overview.
The overall ActiveX for Macintosh architecture can be viewed as follows:
An Interface is an array of function pointers known as a virtual table. The functions pointed to by the members of a vtable are called the methods, or member functions, of the interface. In C++ terms, an interface is an abstract base class, composed of pure virtual functions, which specifies a protocol without providing an implementation. Although C++ is extremely convenient for defining interfaces -- because C++ builds the vtable for you -- interfaces can be defined, implemented and used in any language.
An ActiveX/COM interface defines a contract between any object which implements the interface and any client which makes use of it. This contract includes the name of the interface and its methods, the parameter types, and the purpose of each member function. An object must implement all methods in an interface and adhere to the defined protocol (or intent) of each routine. By convention, interface names follow an IXXXXX format -- IControl and Iunknown, for example.
As shown in the figure above, applications implement an IContainer interface to host ActiveX controls. The IContainer interface can be implemented for each container of controls within the application, or potentially for the application itself. Typically, an implementation of this interface would be instantiated for each document of an application that hosts controls. This interface would also be implemented by controls that will host other controls.
A control site is an object in the application that is instantiated for each control. The control site implements interfaces needed by the application and the control hosted by the site. The control site object generally exposes a Control Site API to the client application to enable creation and destruction of the control site. The control site may optionally expose an IControl interface on the site object, used by the client application once the control has been created. Alternatively, the control site may simply return the IControl interface of the hosted control to allow more direct communication.
IUnknown is the base COM interface from which all other ActiveX interfaces are derived. It defines methods for three member functions: QueryInterface, AddRef, and Release. QueryInterface provides the basic COM mechanism for interface negotiation (retrieval of pointers to specific interfaces). AddRef and Release comprise COM reference counting, a mechanism for controlling object lifetime which allows independent objects to obtain and release pointers to the same object without coordination.
The IUnknown interface is the only required interface for all COM objects. IUnknown is fully documented in numerous other sources, including two documents in the Mac ActiveX SDK, COM Introduction and COM Technical Overview.
A control site object exposes at least an IUnknown interface and IContainerSite interface to the hosted control. Other interfaces are acquired by querying the IUnknown interface of the site or through the IServiceProvider interface. A typical control site object design appears as follows.
Figure 2.
Sites connect to controls by passing a pointer to their IUnknown interface to the control. Controls will typically immediately query the site for the IContainerSite interface, which will become the primary callback interface.
Communication to the control can occur through any number of interfaces, the most basic being the IControl interface. The IControl interface allows the control to receive calls such as events, focus notifications, activation notifications, and drawing. If a control has no persistent data or properties, this is all that must be implemented. Other optional interfaces shown in the Control object reference the IPersist*** (for example, IPersistStream or IPersistPropertyBag) and IBindStatusCallback interfaces that must be exposed for persistent properties and data streaming operations, respectively. A typical control object design is shown here.
Figure 3.
Writing ActiveX Controls
The mechanics of building ActiveX controls are documented later in this article as well as the SDK ActiveX User's Guide. With this information, it is possible to use base classes provided in the SDK to build controls by overriding a few methods. This section documents in more detail the interfaces that can be implemented by control writers.
An ActiveX control must implement some or all of the following interfaces:
IUnknown COM query and reference counting
IObjectWithSite Access to Control Site through generic siting
IControl Basic Control Methods
IPersist*** Persistent properties and data
IBindStatusCallback Data Streaming Notification
A control that has no persistent properties and has no need to retrieve data from a URL can choose to implement only the IUnknown, IObjectWithSite, and IControl interfaces. Controls that have persistent properties must implement one or more IPersist interfaces. Controls that must stream data to and from the Internet must implement the Data Streaming notification interface, IBindStatusCallback. For many of these interfaces, the Macintosh ActiveX SDK provides default implementations so that many controls only need to override a few methods to get baseline functionality.
Macintosh ActiveX controls must implement the IObjectWithSite interface to get access to the control site. Since the control site either implements or delegates important functionality from the outside world, this interface is very important. IObjectWithSite is documented in detail in the document, Internet Controls. The interface is defined as follows:
interface IObjectWithSite
{
AXErrorCode SetSite ( [in] IUnknown* Site );
AXErrorCode GetSite ( [in] REFIID RefID, [out] IUnknown** Site );
};
IObjectWithSite::SetSite simply gives the control an IUnknown interface to use for obtaining other interfaces on the site as shown above in the control site diagram. IObjectWithSite::GetSite returns the site that is currently set on the control. As shown in the above interface, Macintosh ActiveX controls return an AXErrorCode which is synonymous with the HRESULT type defined earlier for OLE2.
With these two interfaces it is theoretically possible to write a minimally functional ActiveX control. Of course, the control would have no UI and could not handle events. But it could use the site to get properties and stream data. In order to write a truly useful control that has UI and participates more completely in the life of the container, the IControl interface must be implemented. This important interface is covered in detail in the ActiveX Technical Guide.
Controls that support persistent properties and/or data must implement one or more persistence interfaces. These include IPersistMoniker, IPersistStream, IPersistStorage, IPersistMemory and IPersistPropertyBag. Of these, IPersistPropertyBag is the recommended interface for retrieving properties that are specified either on the command line or in a URL for a run-time control. IPersistStream will become more important as HTML authoring environments that support ActiveX controls become available.
ActiveX sites will call the control's IPersistPropertyBag::Load() method with an IPropertyBag interface so that the control can retrieve persistent properties. When the control receives this call it can use the IPropertyBag interface given to read the persistent properties.
Full documentation of the IPersist*** interfaces is available from many other sources and is beyond the scope of the SDK documentation. ActiveX for Macintosh uses these interfaces as specified in other sources. References include:
- Understanding ActiveX and OLE by David Chappell.
- ActiveX Controls Inside Out by Adam Denning.
- Inside OLE Second Edition by Kraig Brockschmidt.
- Internet Controls (from the ActiveX SDK).
In order to get data across the Internet that is not retrieved as a simple property from a property bag interface, controls must support data streaming interfaces as documented in the URL Moniker and Asynchronous Moniker. The control must implement only the IBindStatusCallback interface for notifications such as progress, data available, and stop binding. In particular, the control must implement the IBindStatusCallback::OnDataAvailable() method for notification whenever new data is available from the net.
The URL Moniker interfaces support both a push and pull model of data streaming as well as file streaming. More information on how ActiveX for Macintosh uses these interfaces can be located in the following SDK documents.
- URL Monikers.
- Asynchronous Moniker Specification.
Microsoft Technical Support
Paid Support for the ActiveX for Macintosh SDK
The Macintosh ActiveX SDK is supported by Microsoft Technical Support. You can ask questions through your Premier Level support contract. You can also ask questions through your Priority Level contract or purchase individual Priority Support incidents (essentially a one-time fee for a single issue or problem). If you would like more information on Microsoft's paid support options, call Microsoft Support Sales at (800) 936-3500 from 6:00 a.m. to 6:00 p.m. Pacific time, Monday through Friday, excluding holidays. Microsoft Technical Support is also available on the World Wide Web at http://www.microsoft.com/support/.
Free support for the ActiveX for Macintosh SDK
Newsgroups are a great place for free peer support. As time and resources allow, Microsoft developers, program managers, support engineers, and test engineers visit the site to collect feedback and answer specific questions or correct misinterpretations. Microsoft's participation largely depends on bandwidth and time, which is greatly affected by shipping schedules. Some interesting news groups for Internet Explorer are on microsoft.internetexplorer.
To access newsgroups, use your preferred newsgroup reader and enter the news server address as news://msnews.microsoft.com. You can use the following URL to access the newsgroup directly from a Web browser: news.microsoft.public.newsgroup-name. The newsreader included with Internet Explorer version 3.0 supports multiple news servers; you can download the newsreader from http://www.microsoft.com/ie/ie3/imn.htm.
The Internet Explorer for Macintosh team maintains a site of known issues affecting the current release at: http://www.microsoft.com/iesupport/content/issues/.
Developers working with the ActiveX for Macintosh SDK can subscribe to a mailing list to receive support and ask questions. To subscribe, send e-mail to
mactivex-dev@ws63.psdbay.xo.com, using a subject line of "subscribe". For detailed information on other mailing lists covering Microsoft Internet technologies, visit the Mailing Lists page on this Web site.