Web Design-time Control SDK

October 28, 1996
Beta 1 Release

Looking for a way to let users author for your cool Java™ applet or server component? Want to create an embedded, editable wizard that generates anything you can imagine into an HTML page? If so, Web design-time controls are the solution you've been looking for. The Web Design-time Control SDK will show you how easy it is to create Web design-time controls that can make the most complex HTML authoring tasks simple. The SDK includes:

  • Technical documentation and step-by-step tutorials
    (also included in HTML format on this Web site; see below)
  • Sample code projects for Visual C++ and
    Visual Basic Control Creation Edition 5.0
  • Sample Web design-time controls

Microsoft has also created a special edition of the Microsoft ActiveX Control Pad that supports Web design-time controls. (This is a beta release for developers.) You can download this version of the ActiveX Control Pad below to try out the sample controls in the SDK and the controls you build yourself.

Make sure to visit http://www.microsoft.com/vinterdev/ to find out about Visual Interdev™ (previously code-named "Internet Studio"), a new tool that leverages ActiveX design-time controls.

For more information on the Web Design-time Control SDK, select a link below:

* Overview
* Building and Hosting Web Design-time Controls
* Web Design-time Control Tutorial
* Step-by-Step: Building a Web Design-time Control with MFC
* Step-by-Step: Building a Web Design-time Control with Visual Basic

When you're ready to download the SDK and the special edition of the ActiveX Control Pad, select:

Download Download the Web Design-time Control SDK, Beta 1 (self-extracting, 1.83 MB)
Download Download the special edition of ActiveX Control Pad, Beta 1 release for developers (self-extracting, 3.32 MB)

The files are self-installing, so after saving the files to your hard drive, simply double-click the filename to install.

Accessing the Web Design-time Control SDK newsgroup
We have established an online user-to-user support newsgroup for the Web Design-time Control SDK. This newsgroup is for user-to-user support, although it is monitored by Microsoft for product feedback purposes. To access this newsgroup, use your preferred newsgroup reader and enter the news server address as msnews.microsoft.com. The name of the newsgroup is "activex.programming.control.webdc". Or, you can use the following URL to access the newsgroup directly from a Web browser: news:microsoft.public.activex.programming.control.webdc. The newsreader that is included with Internet Explorer version 3.0 supports multiple news servers; if you're running Internet Explorer 3.0 Beta 1 or later, you can download the newsreader from the Internet Mail and News page.

© 1997 Microsoft Corporation. All rights reserved. Legal Notices.


Microsoft Design-time Controls Overview

October 28, 1996

This overview is provided in question-and-answer format.

Questions:

What is a Web design-time control?
What can be authored with a Web design-time control?
Which editors will support Web design-time controls?
Can third parties build Web design-time controls?
How can I build a Web design-time control?
How do Web design-time controls relate to FrontPage WebBots?
Do Web design-time controls replace standard ActiveX controls?

What is a Web design-time control?

Microsoft is defining a new class of ActiveX controls called Web design-time ActiveX controls. These controls are used exclusively at design time (authoring time) to author Web content. They function just like an embedded wizard that can be continuously edited to modify the text it generates into the Web page. Web design-time controls take advantage of all the rich OLE design-time capabilities -- in-place editing, property sheets, and persistence -- to capture user inputs, thereby extending the capabilities of editors that support Web design-time ActiveX controls.

Unlike an ActiveX control, a Web design-time control is never "alive" at run time. Instead, Web design-time controls persist run-time text that is acted on by downstream text consumers such as browsers and components running on Web servers. A host container can use them extensively to provide easy authoring of Active Server Pages that contain scripting and ActiveX server components.

Web design-time ActiveX controls are standard ActiveX controls that have a special interface that lets them generate text. The text is saved into the file by the editor and processed at run time. The structure and content of the text that is generated is entirely up to the control. The control can generate any text that could have been inserted into the file by a knowledgeable user with a standard text editor. Once the file has been saved, the Web design-time controls are inactive, and only the run-time text is "alive". Web design-time controls participate at design-time as all ActiveX controls do; they can handle their own drawing, accept mouse and keyboard input, and provide context menus and property pages. They provide a rich mechanism for extending the graphical editing environment of an HTML editor.

Lifetime of a Web design-time control

When the file is saved, the Web design-time control is wrapped inside an HTML comment, making it invisible to any downstream processor that respects HTML comments. If the file is an Active Server Page, the Web design-time control is stripped from the file before the file is processed and any HTML is delivered to the browser. In the example below, the Web design-time control is shown as green text, and the run-time text is in black. All content in green will be logically hidden from any HTML browser.

Sample of a Web design-time control in a text file after it has been saved:

<!--METADATA
TYPE="DesignerControl" startspan
<OBJECT ID="Billboard1" WIDTH=460 HEIGHT=60
CLASSID="CLSID:CF40D084-10E4-11D0-8712-00A024A7C573">
<PARAM NAME="RuntimeCodeBase"
VALUE="code">
<PARAM NAME="ImageCount" VALUE="2">
<PARAM NAME="ImageUrl0"
VALUE="media/ad_1.gif">
<PARAM NAME="ImageTrans0" VALUE="1">
<PARAM NAME="ImageUrl1"
VALUE="media/ad_2.gif">
<PARAM NAME="ImageTrans1" VALUE="6">
</OBJECT>
-->
<APPLET
NAME="Billboard" CODE="DynamicBillBoard"
CODEBASE="code"
HEIGHT="60" WIDTH="460"
ALT="Billboard">
<PARAM NAME="Delay" VALUE="2000">
<PARAM NAME="billboards" VALUE="2">
<PARAM NAME="Bill0"
VALUE="media/ad_1.gif,""">
<PARAM NAME="Bill1"
VALUE="media/ad_2.gif,""">
<PARAM
NAME="Transitions"VALUE="2,ColumnTransition,FadeTransition">
</APPLET>
<!--METADATA
TYPE="DesignerControl" endspan-->

Up Back to list of questions

What can be authored with a Web design-time control?

Web design-time controls can author any text-based solution. Because text (the ultimate standard) is what Web design-time controls generate into the Web page, they can target any downstream processor that consumes text. Examples of downstream text processors are HTML browsers, Active Server Pages in Microsoft Internet Information Server version 3.0, and template processors like Cold Fusion. The range of uses for Web design-time controls is wide. A Web design-time control can be used to author:

  • HTML tags and content
  • Server-side scripting in any language
  • Client-side scripting in any language
  • Java applets
  • ActiveX controls
  • ActiveX server components

Up Back to list of questions

Which editors will support Web design-time controls?

Web design-time controls will be supported by all Microsoft HTML editors. These controls were introduced first in Visual InterDev™ (previously code-named "Internet Studio"), but they will be hosted by Microsoft FrontPage in 1997 and "Trident". We expect Web design-time support from a significant number of third-party HTML editors. This technology allows third-party developers to dynamically extend their editors and leverage their investment in ActiveX technology, thereby creating a market for developers of Web design-time controls.

Up Back to list of questions

Can third parties build Web design-time controls?

Yes. Microsoft has held design previews with independent software vendors, and published the Web Design-time Control SDK for building these controls. The SDK also includes the information necessary to build tools that host Web design-time controls. We expect hundreds of Web design-time controls to be available from third-party software vendors over time, providing a rich set of components that can be used to seamlessly extend the capabilities of any editor that supports these controls.

Up Back to list of questions

How can I build a Web design-time control?

The specs in the Web Design-time Control SDK (and also included on this Web site) provide background information on Web design-time controls and some preliminary information on writing controls. Web design-time controls can be written using any development tool that can create ActiveX controls, including:

  • Microsoft Visual C++ 4.1 or later
  • Visual Basic Control Creation Edition version 5.0
  • Borland Delphi or C++
  • Symantec C++

We are working with third-party development tools to provide integrated support for building Web design-time ActiveX controls. Building Web design-time controls consists first of creating a standard ActiveX control and then implementing the IActiveDesigner interface.

Up Back to list of questions

How do Web design-time controls relate to FrontPage WebBots?

Web design-time control technology is a COM-based component solution that will be supported by a wide number of HTML editors, including the Microsoft FrontPage, Visual InterDev (previously code-named "Internet Studio"), and "Trident" editors. The FrontPage WebBots technology, on the other hand, is specifically designed to work with FrontPage.

Comparison points | Web design-time control | FrontPage WebBot
Requires FrontPage server extensions | No | Yes
Requires FrontPage editor | No | Yes
Based on COM - easy to build, share, host | Yes | No

Up Back to list of questions

Do Web design-time controls replace standard ActiveX controls?

No. Web design-time controls differ from standard ActiveX controls in that they do not contain a binary run-time component, so Microsoft has decided to name Web design-time controls as a separate category of ActiveX controls. Unlike ActiveX controls, a Web design-time control is never "alive" at run time. Instead, Web design-time controls persist run-time text that is acted on by downstream text consumers like browsers and components running on Web servers. Web design-time controls will co-exist successfully with standard ActiveX controls.

Up Back to list of questions © 1997 Microsoft Corporation. All rights reserved. Legal Notices.


 Building and Hosting Web Design-time Controls

Jay Massena and Douglas Hodges

October 6, 1996

Contents

Overview
Design requirements
Part of the ActiveX authoring extensions family
Building a Web design-time control
ActiveX control plus a little bit more
The lifetime of a Web design-time control
Creating a Web design-time control
How a Web design-time control identifies itself to a host at creation time
Web design-time control persistence using IPersistPropertyBag
Changes to a Web design-time control in the text file
ActiveX Server treatment of Web design-time controls
Registering a Web design-time control
Test container for Web design-time controls
Hosting Web design-time controls
Benefits of hosting Web design-time controls
Assumed host functionality
Persisting Web design-time controls
Inserting a Web design-time control into a host
Persistence of a Web design-time control
Persistence of a Web design-time control's run-time text
Deleting a Web design-time control
Changes to a Web design-time control in the text file
Managing the name of a Web design-time control
Test controls for hosts of Web design-time controls
IActiveDesigner interface
IActiveDesigner interface definition
ActiveX controls
What is an "ActiveX control"?
How to start writing an ActiveX control

Summary

Controls are moving towards a model that lets a control called a design-time ActiveX control provide rich user interface (UI) at design time and target a different control for use at run time. We see a related example of this today with Microsoft® Word. You use the full blown Word product when creating your document but can get by with the much more lightweight Word Viewer when it comes to browsing the document. This technical document covers a special type of design-time control called a Web design-time control. All further references to design-time controls in this document are specifically about Web design-time controls.

Web design-time controls are used by an HTML editor to author custom content into an HTML file. Web design-time controls are standard ActiveX controls with a special interface that lets them generate a separate run-time implementation. They participate at design time as all ActiveX controls do: they can handle their own drawing, accept mouse and keyboard input, provide context menus and property pages. They are a rich mechanism for extending the graphical editing environment of an HTML editor.

The key difference for Web design-time controls is that they are never used at run time; instead Web design-time controls persist run-time text that is acted on by downstream text consumers like browsers and ActiveX Server. A host container can use them extensively to provide easy authoring of Active Server Pages that contain Visual Basic® Scripting Edition (VBScript) and ActiveX server components.

Hosting Web design-time controls consists first of being a standard ActiveX control container and then, in addition, supporting the IActiveDesigner interface so the Web design-time control can persist its run-time text. Building a Web design-time control requires implementing the IActiveDesigner interface.

Note: This document is an early release of the final specification. It is meant to specify and accompany software that is still in development. Some of the information in this documentation may be inaccurate or may not be an accurate representation of the funcntionality of the final specification or software. Microsoft assumes no responsibility for any damages that might occur either directly or indirectly from these inaccuracies. Microsoft may have trademarks, copyrights, patents or pending patent applications, or other intellectual property rights covering subject matter in this document. The furnishing of this document does not give you a license to these trademarks, copyrights, patents, or other intellectual property rights.

Overview

Web design-time controls allow separation of design implementation and run-time implementation. In particular, Web design-time controls can persist one representation for design-time and an alternate representation for run-time. The Web design-time control persists its design-time representation within a METADATA comment using a standard HTML OBJECT tag. The run-time text persists below the comment and is bracketed with another comment. The run-time text targets any run-time implementation, including HTML tags, server components, java applets, Netscape plug-ins, or script.

Web design-time controls leverage most of the support that an ActiveX-enabled HTML editor will already have for the standard OBJECT tag. Web design-time controls need some additional special handling by the host. Web design-time controls implement an interface called IActiveDesigner that allows the container to retrieve the run-time text of the control. This document describes the special handling a host provides for this interface. It is assumed that the host already has the support for standard ActiveX controls.

Design requirements

  • Flexible mechanism for extending editors
  • Generate text for any downstream run-time text consumer
  • Easy for an editor to host by leveraging their investment in ActiveX controls
  • Build them using any tools that create ActiveX controls
  • Give the Web design-time control developer complete control over UI at presentation

Part of the ActiveX authoring extensions family

ActiveX Authoring Extensions are a family of components that share the same technology (interfaces), letting developers build a single component that can adapt to a wide variety of host environments. The following are part of the ActiveX Authoring Extensions family:

  • ActiveX Plug-ins: components that supplement the design environment with Designer UI enabled by ActiveX documents technology (DocObjects)
  • Design-time ActiveX controls: design-time components that generate a run-time object or run-time content

This is Microsoft's strategic direction for extending authoring environments. To get the ball rolling, we have implemented Web design-time controls targeted at Web authoring.

Up Back to contents

Building a Web design-time control

ActiveX control plus a little bit more

Build an ActiveX control, implement the IActiveDesigner interface, and you have a Web design-time control that can persist any text you can imagine. This paper does not cover any of the details about building an ActiveX control. Please see the ActiveX Control section for some hints on what to do if you want to write an ActiveX control.

The lifetime of a Web design-time control

The following description assumes a host that supports the OBJECT tag and the IActiveDesigner interface (described later in this paper).

Creation

A Web design-time control is created by any container that can create ActiveX controls and supports the IActiveDesigner interface.

Persistence

The container persists the Web design-time control using IPersistPropertyBag. The run-time text of the Web design-time control is persisted by the container calling IActiveDesigner::SaveRuntimeState. The result is that the saved file always contains both the Web design-time control and the run-time text it generated at the same time.

Run-time

Because the Web design-time control is wrapped inside an HTML comment and thus logically invisible to browsers, the file can be delivered as-is to the client browser with both the Web design-time control and its run-time text. If the file is processed by ActiveX Server, the Web design-time control is stripped from the file before processing.

Creating a Web design-time control

A Web design-time control is inserted into an HTML page in the same manner as standard ActiveX controls. The control is selected either from a toolbox or an Insert Object/Control dialog. The control itself is then instantiated via CoCreateInstance and initialized in the standard manner.

How a Web design-time control identifies itself to a host at creation time

A Web design-time control has two ways to identify itself at creation time. It can always be identified by a host that QueryInterfaces for IActiveDesigner and then calls IActiveDesigner::QueryPersistenceInterface to see if the control supports IID_IPersistTextStream.

Alternately, the host can recognize that a given control's CLSID is a Web design-time control by checking to see it the control has registered the fact that it implements the special component category CATID_WebDesigntimeControl in the registration database under the control's CLSID section.

// {73CEF3DD-AE85-11cf-A406-00AA00C00940}
DEFINE_GUID(CATID_WebDesigntimeControl,
0x73cef3dd, 0xae85, 0x11cf, 0xa4, 0x6, 0x0, 0xaa, 0x0, 0xc0, 0x9, 0x40);

Web design-time control persistence using IPersistPropertyBag

Each Web design-time control persists its design-time state into the text file using IPersistPropertyBag when the text file is saved. Each property is persisted as a PARAM tag within the OBJECT tag block. There is a 1024 byte limit to the string that can be assigned to the VALUE attribute of the PARAM tag. Web design-time control developers can choose to spread a chunk of text across multiple PARAM tags for later reconstitution when the Web design-time control is reloaded.

Persistence form of a Web design-time control

<!--METADATA TYPE="DesignerControl" startspan
   <OBJECT ID=MyPageCounter
      ClassID="clsid:2FA70250-9333-11CF-8F68-00AA006D27C2">
      <PARAM Name="ImageStyle" Value="Odometer">
      <PARAM Name="TextColor" Value="Yellow">
   </OBJECT>
-->
<% Set MyPageCounter = Server.CreateObject("XYZ.PageCounter")
MyPageCounter.ImageStyle = 2
MyPageCounter.TextColor = Yellow
MyPageCounter.PageHits = Application("PageHits")%>
<IMG SRC=<%=MyPageCounter.ImageURL%> >
<!--METADATA TYPE="DesignerControl" endspan -->

Changes to a Web design-time control in the text file

Changes to the run-time text of a Web design-time control using an editor on the text file will be blown away the next time the Web design-time control is persisted. Changes to the VALUE attribute of a PARAM tag will stick. If the user strips the METADATA comments and leaves the run-time text, the Web design-time control will not be instantiated by the ActiveX control host when the document is reloaded. If the user deletes the run-time text, it will be regenerated the next time the control is saved. If the user deletes the endspan METADATA comment, one will be implied at the end of the file.

ActiveX Server treatment of Web design-time controls

Authors who want to prevent the Web design-time control from being delivered to the client browser must use Active Server Pages (.ASP) that will be processed by the ActiveX Server. When ActiveX Server sees a METADATA comment, they will strip it from the file before processing. This leaves behind just the run-time text of the Web design-time control. This step occurs before any other logic so the run-time text is fully active.

Before stripping

<!--METADATA TYPE="DesignerControl" startspan
   <OBJECT ID=MyPageCounter
      ClassID="clsid:2FA70250-9333-11CF-8F68-00AA006D27C2">
      <PARAM Name="ImageStyle" Value="Odometer">
      <PARAM Name="TextColor" Value="Yellow">
   </OBJECT>
-->
<% Set MyPageCounter = Server.CreateObject("XYZ.PageCounter")
MyPageCounter.ImageStyle = 2
MyPageCounter.TextColor = Yellow
MyPageCounter.PageHits = Application("PageHits")%>
<IMG SRC=<%=MyPageCounter.ImageURL%> >
<!--METADATA TYPE="DesignerControl" endspan -->
After stripping

<% Set MyPageCounter = Server.CreateObject("XYZ.PageCounter")
MyPageCounter.ImageStyle = 2
MyPageCounter.TextColor = Yellow
MyPageCounter.PageHits = Application("PageHits")%>
<IMG SRC=<%=MyPageCounter.ImageURL%> >

Registering a Web design-time control

Web design-time controls are registered the same way as any other ActiveX control. Additionally, Web design-time controls can declare their identity by registering that they implement the special component category CATID_WebDesigntimeControl in the registration database under the control's CLSID section. For more information on component categories, see the Component Categories specification in the ActiveX Development Kit.

// {73CEF3DD-AE85-11cf-A406-00AA00C00940}
DEFINE_GUID(CATID_WebDesigntimeControl,
0x73cef3dd, 0xae85, 0x11cf, 0xa4, 0x6, 0x0, 0xaa, 0x0, 0xc0, 0x9, 0x40);

Test container for Web design-time controls

The Web design-time controls SDK includes a test container for Web design-time controls. The test container is a special version of the Microsoft ActiveX Control Pad 1.0 that supports the IActiveDesigner interface.

Up Back to contents

Hosting Web design-time controls

Benefits of hosting Web design-time controls

Web design-time controls leverage the host's investment in ActiveX controls, making them relatively inexpensive to support. Hosting Web design-time controls lets third party control developers extend the host's functionality beyond its intrinsic feature set. For example, a control developer could write a Web design-time control that creates HTML 3.2 tables even though the host does not specifically support them.

Assumed host functionality

  • Support ActiveX controls
  • Support for OBJECT tag
  • Support persisting objects using IPersistPropertyBag interface

Persisting Web design-time controls

Introduction

This proposal documents a persistence format for Web design-time controls. This format permits a Web design-time control to store in the text file the information for instantiation at design time and the run-time text that can be directly read at browse time. This format is entirely compatible with FrontPage webbots.

Detailed description

The Web design-time control is represented by an OBJECT Tag and is stored in the HTML file within a special METADATA HTML comment:

<!--METADATA TYPE="DesignerControl" startspan [attrib1, attrib2…attribN]
 <OBJECT>
 .....
 </OBJECT>
-->
<H1>Hello World</H1>
<!--METADATA TYPE="DesignerControl" endspan  [attrib1, attrib2…attribN] -->

The run-time text is stored in the same HTML file just after the startspan METADATA comment and is followed by a comment that contains the attribute endspan.

There is no space between <!-- and METADATA.

Upper/lower case are not meaningful.

Additional attributes in the METADATA comment are generated by the container and are optional. They consist of name/value pairs, and for the Web design-time control METADATA comment, are all on the first line (no line-feed/carriage return).

Design time
Loading

At load-time, METADATA comments with TYPE="DesignerControl" are recognized as a special comment. If the startspan attribute is present, the first line of the comment is skipped and the following element is the OBJECT tag. The OBJECT Tag is recognized and parsed and the object element is instantiated in Trident. Everything that follows </OBJECT> is read by the parser and skipped until a METADATA comment with TYPE="DesignerControl" and the endspan attribute is found. This means that the run-time text is not parsed by the editor at load time.

Any optional attributes following the startspan attribute are not preserved by the editor.

Saving

The save command QueryInterfaces for the IActiveDesigner interface to determine if the object is a Web design-time control. If present…

  • The object is wrapped with the Web design-time control METADATA comment (<!--METADATA TYPE="DesignerControl" startspan … -->) all on the first line. The OBJECT tag begins on line 2
  • The run-time text is returned from the control by calling IActiveDesigner:: SaveRuntimeState(IID_IPersitTextStream, IID_IStream,...) and saved after the startspan comment
  • The Web design-time control is ended by a METADATA comment that includes both the TYPE="DesignerControl" and the endspan attribute: (<!--METADATA TYPE="DesignerControl" endspan -->
Browse time

When an HTML file is sent to a browser, it will skip the METADATA comments and parse the run-time text. The OBJECT tag is not instantiated because it is masked inside a comment.

Inserting a Web design-time control into a Host

A Web design-time control is inserted into a page in the same manner as standard controls. The control is selected either from a toolbox or an Insert Object/Control dialog. The control itself is then instantiated via CoCreateInstance and initialized in the standard manner. A container can recognize that a given control CLSID is a Web design-time control by checking to see if the control has registered the fact that it implements the special component category CATID_WebDesigntimeControl in the registration database under the control's CLSID section.

// {73CEF3DD-AE85-11cf-A406-00AA00C00940}
DEFINE_GUID(CATID_WebDesigntimeControl,
0x73cef3dd, 0xae85, 0x11cf, 0xa4, 0x6, 0x0, 0xaa, 0x0, 0xc0, 0x9, 0x40);

Persistence of a Web design-time control

The HTML container should persist each Web design-time control into the HTML file using IPersistPropertyBag.

Example of persisted Web design-time control

<!--METADATA TYPE="DesignerControl" startspan
   <OBJECT ID=MyPageCounter
      ClassID="clsid:2FA70250-9333-11CF-8F68-00AA006D27C2">
      <PARAM Name="ImageStyle" Value="Odometer">
      <PARAM Name="TextColor" Value="Yellow">
   </OBJECT>
-->

Persistence of a Web design-time control's run-time text

The run-time text of a Web design-time control is retrieved by the container using the IActiveDesigner interface implemented by the control. A Web design-time control persists its run-time text via the IActiveDesigner::SaveRuntimeState method. This method is general; it allows for many types of design-time ActiveX controls that persist their state via any OLE persistence medium. Web design-time controls respond to IActiveDesigner::SaveRuntimeState using the IID_IPersistTextStream persistence format. The run-time text is written into an IStream medium. The text written to the IStream must be UNICODE.

The following pseudo code demonstrates how the container saves the run-time text of a Web design-time control:

HRESULT SaveRuntimeText(IActiveDesigner *pActiveDesigner, ...)
{
   HRESULT      hr;
   BOOL      fSupported;
   HGLOBAL      hGlobal = NULL;
   IStream      *pStream = NULL;
   OLECHAR      *pstr;

   pActiveDesigner->QueryPersistenceInterface(
                             IID_IPersistTextStream, &fSupported );
   if ( !fSupported )
      return S_OK;    // Control has no run-time text to be saved

   hGlobal = GlobalAlloc( GMEM_MOVEABLE | GMEM_NODISCARD, 0 );
   if ( !hGlobal )
      goto ErrRtn;

   hr = CreateStreamOnHGlobal( hGlobal, FALSE, &pStream );
   if ( FAILED(hr) )
      goto ErrRtn;

   hr = pActiveDesigner->SaveRuntimeState(
                      IID_IPersistHTMLStream, IID_IStream, pStream );
   if ( FAILED(hr) )
      goto ErrRtn;

   pstr = (OLECHAR *)GlobalLock( hGlobal );
   if ( !pstr )
      goto ErrRtn;

   // write run-time text out here...

   GlobalUnlock( hGlobal );

ErrRtn:
   if ( pStream )
      pStream->Release();
   if ( hGlobal )
      GlobalFree( hGlobal );

   return S_OK;
}

The run-time text is the final deliverable for a Web design-time control. Any arbitrary text is valid. This is not used in anyway by the Web design-time control container.

The container should always refresh the run-time text whenever a Web design-time control is saved.

Example of Web design-time control's run-time text

<!--METADATA TYPE="DesignerControl" startspan
   <OBJECT ID=MyPageCounter
      ClassID="clsid:2FA70250-9333-11CF-8F68-00AA006D27C2">
      <PARAM Name="ImageStyle" Value="Odometer">
      <PARAM Name="TextColor" Value="Yellow">
   </OBJECT>
-->
<% Set MyPageCounter = Server.CreateObject("XYZ.PageCounter")
MyPageCounter.ImageStyle = 2
MyPageCounter.TextColor = Yellow
MyPageCounter.PageHits = Application("PageHits")%>
<IMG SRC=<%=MyPageCounter.ImageURL%> >
<!--METADATA TYPE="DesignerControl" endspan -->

Deleting a Web design-time control

Deleting a Web design-time control will remove only the selected control from the text stream.

Changes to a Web design-time control in the text file

Changes to the run-time text of a Web design-time control using an editor on the text file will be blown away the next time the Web design-time control is persisted. Changes to the VALUE attribute of a PARAM tag will stick. If the user strips the METADATA comments and leaves the run-time text, the Web design-time control will not be instantiated by the ActiveX control host when the document is reloaded. If the user deletes the run-time text, it will be regenerated the next time the control is saved. If the user deletes the endspan METADATA comment, one will be implied at the end of the file.

Managing the name of a Web design-time control

Many Web design-time controls will incorporate their programming name (as given by the control container) in their run-time text, as part of their display presentation, or in other ways. For an HTML file, the interesting name for an object is the name that is persisted as the ID attribute of the OBJECT tag. Users need to be able to modify the name of the Web design-time control to make it something meaningful to the user. This will normally be done via a property sheet (either a Property Page or the All Page). The Web design-time control needs to be notified when the x-object property used to name a control is modified (typically called ID or Name). This then gives the control an opportunity to update things it manages as necessary. The container needs to enforce that the name is unique per normal HTML rules. The container needs to notify the Web design-time control when its programming name changes. This is done by supporting the DISPID_AMBIENT_DISPLAYNAME ambient property and keeping its value in sync with the ID attribute of the OBJECT tag. When the name is changed, the container should call IOleControl::OnAmbientChange.

The default name for a Web design-time control should be meaningful to the user based on the control type. As a root for the name, the container can use the co-class name specified in the control's TypeInfo; else if the control has a ShortUserType name, then it can be used.

Test controls for hosts of Web design-time controls

The Web design-time controls SDK contains several Web design-time controls that can be used for testing. The controls support the IActiveDesigner interface.

Up Back to contents

IActiveDesigner interface

ActiveX controls that want to function as Web design-time controls must implement the IActiveDesigner interface, which includes the following methods:

GetRuntimeClassID method

Returns the CLSID of the run-time object corresponding to the design-time ActiveX control. Web design-time controls do not have a run-time object, instead they give text as their run-time representation.

HRESULT IActiveDesigner::GetRuntimeClassID(CLSID *pclsid)
{
    *pclsid = CLSID_NULL;
    return S_FALSE;
}
GetRuntimeMiscStatusFlags method

Return MiscStatus flags for the run-time object. These should be the same flags that the run-time object would return from IOleObject::GetMiscStatus. Web design-time controls do not have a run-time object, so NULL is returned for the flags.

HRESULT IActiveDesigner::GetRuntimeMiscStatusFlags(DWORD *pdwMiscFlags)
{
    if (!pdwMiscFlags)
        return E_INVALIDARG

    *pdwMiscFlags = NULL;

    return E_UNEXPECTED;
}
QueryPersistenceInterface method

Return TRUE or FALSE if a particular run-time persistence interface is supported. Web design-time controls return text as their run-time persistence. As such, they support IPersistTextStream persistence into an IStream medium.

HRESULT IActiveDesigner::QueryPersistenceInterface(REFIID riid)
{
   if (riid == IID_IPersistTextStream)
      return S_OK;
   return S_FALSE;
}
SaveRuntimeState property

Save the run-time persistence interface for the design-time ActiveX control via a supported persistence interface. Web design-time controls return text as their run-time persistence. As such, they support writing text via IPersistTextStream persistence into an IStream medium.

HRESULT IActiveDesigner::SaveRuntimeState
(
    REFIID riidPersist,
    REFIID riidObjStgMed,
    void  *pObjStgMed
)
{
    HRESULT  hr;
    BSTR     bstrHtml;

    if ((riidPersist   != IID_IPersistTextStream) ||
        (riidObjStgMed != IID_IStream))
        return E_NOINTERFACE;

  // Call your internal routine to generate runtime text
    hr = this->GetRuntimeText(&bstrText);
    if (SUCCEEDED(hr))
    {
        hr = ((IStream *)pObjStgMed)->Write(bstrText, SysStringByteLen(bstrText) + sizeof(OLECHAR), NULL);
        SysFreeString(bstrText);
    }

    return hr;
}
GetExtensibilityObject property

Return the OLE Automation interface for the design-time ActiveX control that should be exposed via the design-time tools OLE Automation object model.

HRESULT IActiveDesigner::GetExtensibilityObject(IDispatch **ppvObjOut)
{
    if (!ppvObjOut)
        return E_INVALIDARG

    return this->QueryInterface(IID_IDispatch, ppvObjOut);
}

IActiveDesigner interface definition

///////////////////////////////////////////////////////////////////////////
// IActiveDesigner
//
// {51AAE3E0-7486-11cf-A0C2-00AA0062BE57}
DEFINE_GUID(IID_IActiveDesigner, 0x51aae3e0, 0x7486, 0x11cf, 0xa0, 0xc2, 0x0, 0xaa, 0x0, 0x62, 0xbe, 0x57);

// {56223fe3-d397-11cf-a42e-00aa00C00940}
DEFINE_GUID(IID_IPersistTextStream, 0x56223fe3, 0xd397, 0x11cf, 0xa4, 0x2e, 0x0, 0xaa, 0x0, 0xc0, 0x9, 0x40);

#undef  INTERFACE
#define INTERFACE IActiveDesigner

DECLARE_INTERFACE_(IActiveDesigner, IUnknown)
{
   // IUnknown methods
   //
   STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
   STDMETHOD_(ULONG,AddRef)(THIS) PURE;
   STDMETHOD_(ULONG,Release)(THIS) PURE;

   // IActiveDesigner methods
   //
   STDMETHOD(GetRuntimeClassID)(THIS_ CLSID *pclsid) PURE;
   STDMETHOD(GetRuntimeMiscStatusFlags)(THIS_ DWORD *dwMiscFlags) PURE;
   STDMETHOD(QueryPersistenceInterface)(THIS_ REFIID riidPersist) PURE;
   STDMETHOD(SaveRuntimeState)(THIS_ REFIID riidPersist, REFIID riidObjStgMed,              void *pObjStgMed) PURE;
   STDMETHOD(GetExtensibilityObject)(THIS_ IDispatch **ppvObjOut) PURE;
};

Up Back to contents

ActiveX controls

What is an "ActiveX control"?

ActiveX™ controls, formerly known as OLE controls or OCX controls, are components (or objects) you can insert into a Web page or other application to reuse packaged functionality someone else programmed. For example, the ActiveX controls that are included with Microsoft® Internet Explorer version 3.0 allow you to enhance your Web pages with sophisticated formatting features and animation.

A key advantage of ActiveX controls over Java applets and Netscape™ plug-ins is that ActiveX controls can also be used in applications written in many programming languages, including all of the Microsoft programming and database languages.

There are hundreds of ActiveX controls today with functionality ranging from a timer control (which simply notifies its container at a particular time) to full-featured spreadsheets and word processors. If you can imagine it, you can do it with an ActiveX control.

You can add ActiveX controls to your Web pages by using the standard HTML <OBJECT> tag. The object tag includes a set of parameters that you use to specify which data the control should use and to control the appearance and behavior of the control.

Microsoft Internet Explorer version 3.0 Alpha comes with a small set of ActiveX controls that it uses. With these controls, you can:

Simplify and automate your authoring tasks (for example, display "NEW!" images for new items on your site with a date specification; after that date, the images will automatically become invisible)

Display data in ways other than text and tables (for example, charts or text displayed at different angles)

Add functionality to your pages (for example, timers, animation, and background downloading)

The ActiveX controls that are provided with Internet Explorer 3.0 are installed automatically when the user installs Internet Explorer 3.0. Once installed, ActiveX controls run automatically when a Web page containing them is displayed--users do not need to download any additional files.

If you are a Web author, you can take advantage of the ready-to-use ActiveX controls listed below. If you are a programmer, you can write your own controls using Visual C++® and one of the ActiveX control frameworks: the Microsoft Foundation Class Library (MFC), the ActiveX Template Library (ATL), or the BaseCtl framework.

How to start writing an ActiveX control

ActiveX controls are OLE controls that have been extended for the Internet environment. The specs listed below provide background information on ActiveX controls and some preliminary information on writing controls. For copies of these specs, download the Microsoft ActiveX Development Kit and install it on your system. Most of these docs are also available in full-text searchable format on the Microsoft ActiveX SDK, available for downloading from the Site Builder Workshop Web site and from the Microsoft Developer Network.

OLE Controls/COM Objects for the Internet

Extensions for OLE controls that will enable them to work well in the Internet environment (that is, when inserted into Web pages).

OLE Controls 96

Enhancements to OLE controls, including information on windowless, transparent, and irregularly shaped controls. Also provides information on performance enhancements.

OLE Control and Control Container Guidelines, Version 2.0

General guidelines for creating OLE controls and control containers.

Palette Behavior for OLE Controls

Palette behavior and management guidelines for implementing OLE controls and control containers. This spec also describes the behavior of Internet Explorer 3.0 as an OLE control container.

Component Categories

Categorization of COM objects based on the interfaces they support.

Internet Component Download

Mechanism Internet Explorer 3.0 uses to automatically download control code.

There are three basic ways to write ActiveX controls today:
  1. The easiest is to use the OLE control development facilities built into Visual C++ and MFC. This technology allows you to create control executables that are pretty small (so they download faster); however, it requires the correct MFC dynamic link library (DLL) to be installed on the user's system. This DLL is quite large--nearly a megabyte. But even so, it needs to be installed only once on each user's system and shouldn't take more than 10 minutes or so to download, even at 14.4 Kbps. Microsoft is working to make these controls and their associated DLLs even smaller and more efficient, so controls you write using MFC today will be even faster in the future.
  2. The Microsoft Internet Explorer developers included a sample called BaseCtl in the ActiveX Development Kit. This sample comprises the FrameWrk, ToDoSvr, and WebImage examples. Writing your controls in this style gives you the smallest possible controls; however, it also requires intimate knowledge of OLE COM and ActiveX control architecture--so it's not for the faint-of-heart.
  3. Finally, the Visual C++ team has created the ActiveX Template Library (ATL), which includes the library plus the Beeper and Labrador samples. This is a library of C++ templates that allows you to create very fast and small controls--in fact, you can even avoid linking in the C run-time library. The templates also do some of the grunge work of OLE for you, but they still require a pretty intimate knowledge of COM and ActiveX control architecture--so they're for those who aren't timid, but who'd like to have some of the tedious OLE work done for them.

Up Back to contents

© 1997 Microsoft Corporation. All rights reserved. Legal Notices .


 Web Design-time Control Tutorial
Jay Massena and Douglas Hodges

BGSound code fragments
IActiveDesigner interface declaration
BGSound class definition
IActiveDesigner interface implementation
GetRuntimeText helper function
Tagging a Web design-time control with CATID
Hosting code fragments
Persisting a Web design-time control

Summary

This document is a tutorial that covers how to take an ActiveX control and make it a Web design-time control. It also provides some assistance for HTML editors who want to host Web design-time controls. For our Beta 1 release, we will only give a brief description along with the code fragments for the BGSound Web design-time control that is included in the SDK.

BGSound code fragments

These code fragments illustrate what it takes to turn an MFC control wizard generated ActiveX control into a Web design-time control.

IActiveDesigner interface declaration

/////////////////////////////////////////////////////////////////////////////
// IActiveDesigner Interface Declaration
//
// {51AAE3E0-7486-11cf-A0C2-00AA0062BE57}
/////////////////////////////////////////////////////////////////////////////
DEFINE_GUID(IID_IActiveDesigner, 0x51aae3e0, 0x7486, 0x11cf, 0xa0, 0xc2, 0x0, 0xaa, 0x0, 0x62, 0xbe, 0x57);

#undef  INTERFACE
#define INTERFACE IActiveDesigner

DECLARE_INTERFACE_(IActiveDesigner, IUnknown)
{
   // IUnknown methods
   //
   STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
   STDMETHOD_(ULONG,AddRef)(THIS) PURE;
   STDMETHOD_(ULONG,Release)(THIS) PURE;

   // IActiveDesigner methods
   //
   STDMETHOD(GetRuntimeClassID)(THIS_ CLSID *pclsid) PURE;
   STDMETHOD(GetRuntimeMiscStatusFlags)(THIS_ DWORD *dwMiscFlags) PURE;
   STDMETHOD(QueryPersistenceInterface)(THIS_ REFIID riidPersist) PURE;
   STDMETHOD(SaveRuntimeState)(THIS_ REFIID riidPersist, REFIID riidObjStgMed,          void *pObjStgMed) PURE;
   STDMETHOD(GetExtensibilityObject)(THIS_ IDispatch **ppvObjOut) PURE;
};

const GUID IID_IActiveDesigner =
      { 0x51aae3e0, 0x7486, 0x11cf, { 0xa0, 0xc2, 0x0, 0xaa, 0x0, 0x62, 0xbe,             0x57 } };
const GUID IID_IPersistTextStream =
   {0x56223fe3, 0xd397, 0x11cf, { 0xa4, 0x2e, 0x0, 0xaa, 0x0, 0xc0, 0x9, 0x40 } };

Up Back to contents

BGSound class definition

/////////////////////////////////////////////////////////////////////////////
// CBGSndCtrl class
//    this class implements the BackgroundSound Designer control
//    this class implements the standard OLE control interfaces by
//    deriving from MFC's COleControl class.  this class adds the
//    implementation of the IActiveDesigner interface to act as an
//    HTML Designer control.
/////////////////////////////////////////////////////////////////////////////
class CBGSndCtrl : public COleControl
{
   DECLARE_DYNCREATE(CBGSndCtrl)

// Constructor
public:
   CBGSndCtrl();

// Overrides
   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CBGSndCtrl)
   public:
   virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid);
   virtual void DoPropExchange(CPropExchange* pPX);
   virtual void OnResetState();
   virtual DWORD GetControlFlags();
   //}}AFX_VIRTUAL

// Implementation
protected:
   HBITMAP m_hBitmap;

    // control properties
    short m_nLoop;
    CString m_strSource;

    // destruction
    ~CBGSndCtrl();

    virtual HRESULT GetRuntimeText(CString& szHTML);

   DECLARE_OLECREATE_EX(CBGSndCtrl)    // Class factory and guid
   DECLARE_OLETYPELIB(CBGSndCtrl)      // GetTypeInfo
   DECLARE_OLECTLTYPE(CBGSndCtrl)      // Type name and misc status

// Message maps
   //{{AFX_MSG(CBGSndCtrl)
      // NOTE - ClassWizard will add and remove member functions here.
      //    DO NOT EDIT what you see in these blocks of generated code !
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()

// Dispatch maps
   //{{AFX_DISPATCH(CBGSndCtrl)
   afx_msg short GetLoop();
   afx_msg void SetLoop(short nNewValue);
   afx_msg BSTR GetSource();
   afx_msg void SetSource(LPCTSTR lpszNewValue);
    virtual BOOL OnSetExtent(LPSIZEL lpSizeL);
   //}}AFX_DISPATCH
   DECLARE_DISPATCH_MAP()

   afx_msg void AboutBox();

// Event maps
   //{{AFX_EVENT(CBGSndCtrl)
   //}}AFX_EVENT
   DECLARE_EVENT_MAP()

// Interface maps
   DECLARE_INTERFACE_MAP();

public:
//$$$$$$$$$$$$$$$$$$$$$$$DESIGNER SPECIFIC$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    BEGIN_INTERFACE_PART(ActiveDesigner, IActiveDesigner)
      STDMETHOD(GetRuntimeClassID)(CLSID *pclsid);
      STDMETHOD(GetRuntimeMiscStatusFlags)(DWORD *dwMiscFlags);
      STDMETHOD(QueryPersistenceInterface)(REFIID riidPersist);
      STDMETHOD(SaveRuntimeState)(REFIID riidPersist, REFIID riidObjStgMed, void          *pObjStgMed);
      STDMETHOD(GetExtensibilityObject)(IDispatch **ppvObjOut);
   END_INTERFACE_PART(ActiveDesigner)
//$$$$$$$$$$$$$$$$$$$$$END DESIGNER SPECIFIC$$$$$$$$$$$$$$$$$$$$$$$$$$

// Dispatch and event Ids
    enum {
   //{{AFX_DISP_ID(CBGSndCtrl)
   dispidLoop = 1L,
   dispidSource = 2L,
   //}}AFX_DISP_ID
   };
};

Up Back to contents

IActiveDesigner interface implementation

STDMETHODIMP_(ULONG) CBGSndCtrl::XActiveDesigner::AddRef(void)
{
   METHOD_MANAGE_STATE(CBGSndCtrl, ActiveDesigner)
   return (ULONG)pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) CBGSndCtrl::XActiveDesigner::Release(void)
{
   METHOD_MANAGE_STATE(CBGSndCtrl, ActiveDesigner)
   return (ULONG)pThis->ExternalRelease();
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::QueryInterface(REFIID iid,
                                                                                LPVOID far* ppvObj)
{
   METHOD_MANAGE_STATE(CBGSndCtrl, ActiveDesigner)
   return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::GetRuntimeClassID(CLSID *pclsid)
{
    *pclsid = CLSID_NULL;
    return S_FALSE;
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::GetRuntimeMiscStatusFlags(DWORD    *pdwMiscFlags)
{
   if (!pdwMiscFlags)
      return E_INVALIDARG;
   *pdwMiscFlags = NULL;
   return E_UNEXPECTED;
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::QueryPersistenceInterface(REFIID riid)
{
   if (riid == IID_IPersistHTMLStream)
      return S_OK;
   return S_FALSE;
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::SaveRuntimeState
(
   REFIID riidPersist,
   REFIID riidObjStgMed,
   void  *pObjStgMed
)
{
   HRESULT  hr;
   CString  cstrText;
   BSTR     bstrText;

   METHOD_MANAGE_STATE(CBGSndCtrl, ActiveDesigner)

   if (riidPersist != IID_IPersistTextStream)
      return E_NOINTERFACE;

   if (riidObjStgMed != IID_IStream)
      return E_NOINTERFACE;

   hr = pThis->GetRuntimeText(cstrText);
   if (SUCCEEDED(hr))
   {
      bstrHtml = cstrHtml.AllocSysString();
      if (bstrText)
      {
         hr = ((IStream *)pObjStgMed)->Write(bstrText,                         SysStringByteLen(bstrText) + sizeof(OLECHAR), NULL);
         SysFreeString(bstrText);
      }
      else
         hr = E_OUTOFMEMORY;
   }

   return hr;
}

STDMETHODIMP CBGSndCtrl::XActiveDesigner::GetExtensibilityObject(IDispatch    **ppvObjOut)
{
   if (!ppvObjOut)
      return E_INVALIDARG;
   return this->QueryInterface(IID_IDispatch, (void **)ppvObjOut);
}

Up Back to contents

GetRuntimeText helper function

/////////////////////////////////////////////////////////////////////////////
// CBGSndCtrl::GetRuntimeText - Generates the run-time text for the control
//
// GetRuntimeText() is called whenever the container needs to update the
// run-time text after property changes or other state changes are made.

HRESULT CBGSndCtrl::GetRuntimeText(CString& strText)
{
    // our run-time Text template
   CString strTemplate = _T("<BGSOUND SRC=\"%s\" LOOP=%d>");

    // format our string with our properties
   strText.Format(strTemplate, m_strSource, m_nLoop);

   return S_OK;
}

Up Back to contents

Tagging a Web design-time control with CATID

DEFINE_GUID(CATID_WebDesigntimeControl, 0x73cef3dd, 0xae85, 0x11cf, 0xa4, 0x6, 0x0,  0xaa, 0x0, 0xc0, 0x9, 0x40);

////////////////////////////////////////////////////////////////////////////
// HandleComponentRegisration - registers the controls in this libary as
//   web design-time controls.
HRESULT HandleComponentRegistration(BOOL fRegister)
{
   if (fRegister)
      return RegisterControlCategory(CATID_WebDesigntimeControl,
                                      L"Web Design-time Control",
                                      (REFCLSID)CBGSndCtrl::guid);
   else
      return UnRegisterControlCategory(CATID_WebDesigntimeControl,
                                                  (REFCLSID)CBGSndCtrl::guid);
}

/////////////////////////////////////////////////////////////////////////////
// RegisterControlCategory : Register controls with catid

HRESULT RegisterControlCategory(CATID catid, WCHAR* wszDesc, REFCLSID rClsid)
{
    ICatRegister* pcr = NULL ;
    HRESULT hr = S_OK ;

    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
                          NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister,                                     (void**)&pcr);
    if (FAILED(hr))
        return hr;

    hr = CreateComponentCategory(pcr, catid, wszDesc);
    if (SUCCEEDED(hr))
   {
      CATID rgcatid[1] ;
      rgcatid[0] = catid;

      hr = pcr->RegisterClassImplCategories(rClsid, 1, rgcatid);
   }

    pcr->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////////////////
// UnRegisterControlCategory : Unregister controls with catid

HRESULT UnRegisterControlCategory(CATID catid, REFCLSID rClsid)
{
    ICatRegister* pcr = NULL ;
    HRESULT hr = S_OK ;

    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
                          NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister,                            (void**)&pcr);
    if (FAILED(hr))
        return hr;

   CATID rgcatid[1] ;
   rgcatid[0] = catid;

   hr = pcr->UnRegisterClassImplCategories(rClsid, 1, rgcatid);

    pcr->Release();
   return hr;
}

/////////////////////////////////////////////////////////////////////////////
// CreateComponentCategory : Create a COM Server Category

HRESULT CreateComponentCategory(ICatRegister* pcr, CATID catid, WCHAR* wszCatDescription)
{
    HRESULT hr = S_OK ;

    // Make sure the HKCR\Component Categories\{..catid...} key is registered
    CATEGORYINFO catinfo;
    catinfo.catid = catid;
    catinfo.lcid = 0x0409 ; // english

    // Make sure the provided description is not too long.
    // Only copy the first 127 characters if it is
    int len = wcslen(wszCatDescription);
    if (len>127)
        len = 127;
    wcsncpy(catinfo.szDescription, wszCatDescription, len);
    // Make sure the description is null terminated
    catinfo.szDescription[len] = '\0';

    hr = pcr->RegisterCategories(1, &catinfo);

    return hr;
}

Up Back to contents

Hosting code fragments

These code fragements show how a hosting container can perform some common operations associated with Web design-time controls. We assume that the container already has containership support for ActiveX controls.

Up Back to contents

Persisting a Web design-time control

/////////////////////////////////////////////////////////////////////////////
// The following sample code fragment demonstrates how the container saves the
// run-time text of a web design-time control:

HRESULT SaveRuntimeText( ... IUnknown* pUnk ... )
{
   IActiveDesigner *pActiveDesigner;
   HGLOBAL      hGlobal = NULL;
   Istream      *pStream = NULL;
   OLECHAR      *pstr;
   HRESULT      hr;

   hr = pUnk->QueryInterface( IID_IActiveDesigner, (void**)&pActiveDesigner );
   if ( SUCCEEDED(hr) && pActiveDesigner )
   {
      hr = pActiveDesigner->QueryPersistenceInterface( IID_IPersistTextStream );
      if ( SUCCEEDED(hr) )
      {
      hGlobal = GlobalAlloc( GMEM_MOVEABLE | GMEM_NODISCARD, 0 );
         if ( hGlobal )
         {
            hr = CreateStreamOnHGlobal( hGlobal, FALSE, &pStream );
            if ( SUCCEEDED(hr) )
            {
               hr = pActiveDesigner->SaveRuntimeState( IID_IPersistTextStream,                   IID_IStream, pStream );
                 if ( SUCCEEDED(hr) )
                 {
                    OLECHAR chZero = 0;
                    ULONG   cbWritten;

                    // Make sure the text has a null terminator
                    pStream->Write(&chZero, sizeof(chZero), &cbWritten);

                    pstr = (OLECHAR *)GlobalLock( hGlobal );
                    if ( pstr )
                    {
                       TCHAR    *pch;

                          pch = MyOLE2T( pstr );

                       //... save out run-time text  ...

                       GlobalUnlock( hGlobal );
                    }
                }
                pStream->Release();
            }
            GlobalFree( hGlobal );
         }
      }
      pActiveDesigner->Release();
   }
   return NOERROR;
}

Up Back to contents © 1997 Microsoft Corporation. All rights reserved. Legal Notices.


 Step-by-Step: Building a Web Design-time Control with MFC

Create an ActiveX control
Step 1: Use MFC OLE ControlWizard to create your control
Add Web design-time control support
Step 2: Add IActiveDesigner interface to class definition
Step 3: Implement IActiveDesigner interface

Summary

This document is a step-by-step tutorial for creating the BGSound Web design-time ActiveX control using MFC.

Create an ActiveX control

Step 1: Use MFC OLE ControlWizard to create your control

  1. Create new project workspace and run OleControlWizard, creating a project named "BGSound." Set to Windowless activation in advanced options
  2. Add Design-time Control SDK include directory to INCLUDE Path

c:\progra~1\dcsdk\Samples\Mfc\include
  3. Use View.ClassWizard to add Property Get/Set methods

BSTR Source
short Loop
  4. Add Member Variables (in bgsoundctl.h)

// control properties
short m_nLoop;
CString m_strSource;
  5. Add Get/Set Methods (in bgsoundctl.cpp)

short CBGSoundCtrl::GetLoop()
{
      return m_nLoop;
}

void CBGSoundCtrl::SetLoop(short nNewValue)
{
      // verify loop property is valid
      if (nNewValue >= -1)
      {
             // set our internal loop value
             m_nLoop = nNewValue;

             // notify container that a property was modified
             SetModifiedFlag();

             // notify property browsers that this property changed
             BoundPropertyChanged(dispidLoop);
      }
      else
      {
             ThrowError(CTL_E_INVALIDPROPERTYVALUE,
                            _T("Loop must be an integer greater than or equal to -1"));
      }
}

BSTR CBGSoundCtrl::GetSource()
{
      return m_strSource.AllocSysString();
}

void CBGSoundCtrl::SetSource(LPCTSTR lpszNewValue)
{
    // set our internal source string
    m_strSource = lpszNewValue;

    // notify container that a property was modified
    SetModifiedFlag();

    // notify property browsers that this property changed
    BoundPropertyChanged(dispidSource);

    // make sure we get repainted since 'Source' is our caption
    InvalidateControl();
}
  6. Add Support to persist properties (in CBGSoundCtrl::DoPropExchange)

// persist our properties
PX_String(pPX, _T("Source"), m_strSource, _T(""));
PX_Short(pPX, _T("Loop"), m_nLoop, -1);
  7. Add Drawing Code (in CBGSoundCtrl::OnDraw)

void CBGSoundCtrl::OnDraw(
                    CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
    CFont fnt;
    CRect rcText;

    // get text rect;
    rcText = rcBounds;
    rcText.left += 2;
    rcText.right -= 2;
    rcText.top += 2;
    rcText.bottom -= 2;

    // create font
    fnt.CreatePointFont(160, _T("MS Sans Serif"), pdc);
    CFont* pOldFont = pdc->SelectObject(&fnt);
    pdc->SetBkMode(TRANSPARENT);

    // fill our rect with gray
    pdc->FillRect(rcBounds,                                                                               CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH)));

    // draw an edge
    pdc->DrawEdge(&(RECT)rcBounds, EDGE_RAISED, BF_RECT);

    // draw the Source text
    pdc->DrawText(_T("\"") + m_strSource + _T("\""), &(RECT)rcText, DT_WORDBREAK | DT_CENTER);

    // return to old font
    pdc->SelectObject(pOldFont);
}

Add Web design-time control support

Step 2: Add IActiveDesigner interface to class definition

  1. Add IActiveDesigner interface definition (in bgsoundctl.h)

#include "designer.h"
#include "webdc.h"
  2. Declare Interface_Map and IActiveDesigner interface (in bgsoundctl.h - protected)

// Interface maps
DECLARE_INTERFACE_MAP();

// IActiveDesigner Interface
BEGIN_INTERFACE_PART(ActiveDesigner, IActiveDesigner)
   STDMETHOD(GetRuntimeClassID)(CLSID *pclsid);
   STDMETHOD(GetRuntimeMiscStatusFlags)(DWORD *dwMiscFlags);
   STDMETHOD(QueryPersistenceInterface)(REFIID riidPersist);
   STDMETHOD(SaveRuntimeState)(REFIID riidPersist, REFIID riidObjStgMed, void *pObjStgMed);
   STDMETHOD(GetExtensibilityObject)(IDispatch **ppvObjOut);
END_INTERFACE_PART(ActiveDesigner)

virtual HRESULT GetRuntimeText(CString& strText);

Step 3: Implement IActiveDesigner interface

  1. Add intialization of IActiveDesigner GUIDs (in bgsoundctl.cpp after stdafx.h)

// Define and initialize required GUIDs
#include <initguid.h>
#include "designer.h"
#include "webdc.h"
  2. Add Interface Map (in bgsoundctl.cpp)

////////////////////////////////////////////////////////////////////////
// Interface Map

BEGIN_INTERFACE_MAP(CBGSoundCtrl, COleControl)
      INTERFACE_PART(CBGSoundCtrl, IID_IActiveDesigner, ActiveDesigner)
END_INTERFACE_MAP()
  3. Add IActiveDesigner interface implementation (in bgsoundctl.cpp)

////////////////////////////////////////////////////////////////////////
// CBGSoundCtrl::XActiveDesigner - IActiveDesigner interface
//
// The following functions represent the implementation of the IActiveDesigner
// interface. For the most part, these functions can be left as is. The
// SaveRuntimeState() function will call the CBGSoundCtrl::GetRuntimeText()
// function when it needs to generate run-time text.

STDMETHODIMP_(ULONG) CBGSoundCtrl::XActiveDesigner::AddRef(void)
{
      METHOD_MANAGE_STATE(CBGSoundCtrl, ActiveDesigner)
      return (ULONG)pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) CBGSoundCtrl::XActiveDesigner::Release(void)
{
      METHOD_MANAGE_STATE(CBGSoundCtrl, ActiveDesigner)
      return (ULONG)pThis->ExternalRelease();
}

STDMETHODIMP CBGSoundCtrl::XActiveDesigner::QueryInterface(REFIID iid,
                                                                                LPVOID far* ppvObj)
{
      METHOD_MANAGE_STATE(CBGSoundCtrl, ActiveDesigner)
      return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP CBGSoundCtrl::XActiveDesigner::GetRuntimeClassID(CLSID *pclsid)
{
    *pclsid = CLSID_NULL;
    return S_FALSE;
}

STDMETHODIMP CBGSoundCtrl::XActiveDesigner::GetRuntimeMiscStatusFlags(DWORD *pdwMiscFlags)
{
      if (!pdwMiscFlags)
             return E_INVALIDARG;
      *pdwMiscFlags = NULL;
      return E_UNEXPECTED;
}

STDMETHODIMP CBGSoundCtrl::XActiveDesigner::QueryPersistenceInterface(REFIID riid)
{
      if (riid == IID_IPersistTextStream)
             return S_OK;
      return S_FALSE;
}

STDMETHODIMP CBGSoundCtrl::XActiveDesigner::SaveRuntimeState
(
      REFIID riidPersist,
      REFIID riidObjStgMed,
      void  *pObjStgMed
)
{
      HRESULT  hr;
      CString  cstrText;
      BSTR     bstrText;

      METHOD_MANAGE_STATE(CBGSoundCtrl, ActiveDesigner)

      if (riidPersist != IID_IPersistTextStream)
             return E_NOINTERFACE;

      if (riidObjStgMed != IID_IStream)
             return E_NOINTERFACE;

      hr = pThis->GetRuntimeText(cstrText);
      if (SUCCEEDED(hr))
      {
             bstrText = cstrText.AllocSysString();
             if (bstrText)
             {
                    hr = ((IStream *)pObjStgMed)->Write(bstrText,                                     SysStringByteLen(bstrText) + sizeof(OLECHAR), NULL);
                 SysFreeString(bstrText);
             }
             else
                 hr = E_OUTOFMEMORY;
      }

      return hr;
}

STDMETHODIMP CBGSoundCtrl::XActiveDesigner::GetExtensibilityObject(IDispatch **ppvObjOut)
{
      if (!ppvObjOut)
             return E_INVALIDARG;
      return this->QueryInterface(IID_IDispatch, (void **)ppvObjOut);
}

////////////////////////////////////////////////////////////////////////
// CBGSoundCtrl::GetRuntimeText - Generates run-time text for the control
//
// GetRuntimeText() is called whenever the container needs to update the
// run-time text after property changes or other state changes are made.

HRESULT CBGSoundCtrl::GetRuntimeText(CString& strText)
{
    // our run-time Text template
    CString strTemplate = _T("<BGSOUND SRC=\"%s\" LOOP=%d>");

    // format our string with our properties
      strText.Format(strTemplate, m_strSource, m_nLoop);

      return S_OK;
}

© 1997 Microsoft Corporation. All rights reserved. Legal Notices.


 Step-by-Step: Building a Web Design-time Control with Visual Basic

Create an ActiveX control
&NBSP;&NBSP;&NBSP;Step 1: Create ActiveX control project
&NBSP;&NBSP;&NBSP;Step 2: Use Visual Basic ActiveX Control Interface Wizard to configure your control
Add Web design-time control support
&NBSP;&NBSP;&NBSP;Step 3: Add IActiveDesigner interface reference to project
&NBSP;&NBSP;&NBSP;Step 4: Implement IActiveDesigner interface

Summary

This document is a step-by-step tutorial for creating the BGSound Web design-time control using Visual Basic® Control Creation Edition version 5.0.

Create an ActiveX control

Step 1: Create ActiveX control project

  1. Create new ActiveX control project using File.New Project and choose ActiveX Control at the type
  2. Add label control to the UserControl (ActiveX control) you have just created
  3. Set properties on the label control

    Set label control Name property to "lblSource"
    Set label control Caption property to "[No Sound Source]"

  4. Name the project and control
    Project name = web
Control name = bgsound
  5. Save project and name project file as web.vbp; control file as bgsound.ctl

Step 2: Use Visual Basic ActiveX Control Interface Wizard to configure your control

  1. Use Add-Ins.ActiveX Control Interface Wizard to launch wizard
  2. Remove default events, props, and methods that have been added
  3. Add the following properties
    (Note: Repeat is used instead of Loop because Loop is a Visual Basic keyword)
    Source
Repeat
  4. Point Source property to lblSource.Caption property
  5. Set datatype of Repeat to integer and default to -1
  6. Finish wizard

Add Web design-time control support

Step 3: Add IActiveDesigner interface reference to project

  1. Add reference to "Microsoft Web Design-time Control Type Library"
    Use Project.References and scan the list of available references to ensure that it is checked if present. If not, then browse to the c:\progra~1\dcsdk\samples\vb\common directory (if you used the default install location) and add webdc.tlb as a project type reference. This will automatically check it and include it in your project.

Step 4: Implement IActiveDesigner interface

  1. Add IProvideRuntimeText interface definition
    Implements IProvideRuntimeText
  2. Implement IProvideRuntimeText_GetRuntimeText
    Dim strText As String
Dim strQuote As String
strQuote = Chr$(34)
strText = "<BGSOUND SRC=" & strQuote & lblSource.Caption & strQuote
strText = strText & " LOOP=" & strQuote & Repeat & strQuote &  ">"
IProvideRuntimeText_GetRuntimeText = strText
  3. Compile web.ocx
© 1997 Microsoft Corporation. All rights reserved. Legal Notices.


Legal Info

 Legal Notices
NOTICE SPECIFIC TO SOFTWARE AVAILABLE ON THIS CD OR WEBSITE Any software that is made available to download from this server or CD ("Software") is the copyrighted work of Microsoft Corporation and/or its suppliers. Use of the Software is governed by the terms of the end user license agreement, if any, which accompanies or is included with the Software ("License Agreement"). An end user will be unable to install any Software that is accompanied by or includes a License Agreement, unless he or she first agrees to the License Agreement terms. The Software is made available for downloading solely for use by end users according to the License Agreement. Any reproduction or redistribution of the Software not in accordance with the License Agreement is expressly prohibited by law, and may result in severe civil and criminal penalties. Violators will be prosecuted to the maximum extent possible. WITHOUT LIMITING THE FOREGOING, COPYING OR REPRODUCTION OF THE SOFTWARE TO ANY OTHER SERVER OR LOCATION FOR FURTHER REPRODUCTION OR REDISTRIBUTION IS EXPRESSLY PROHIBITED. THE SOFTWARE IS WARRANTED, IF AT ALL, ONLY ACCORDING TO THE TERMS OF THE LICENSE AGREEMENT. EXCEPT AS WARRANTED IN THE LICENSE AGREEMENT, MICROSOFT CORPORATION HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS WITH REGARD TO THE SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. RESTRICTED RIGHTS LEGEND. Any Software which is downloaded from this Server for or on behalf of the United States of America, its agencies and/or instrumentalities ("U.S. Government"), is provided with Restricted Rights. Use, duplication, or disclosure by the U.S. Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer Software - Restricted Rights at 48 CFR 52.227-19, as applicable. Manufacturer is Microsoft Corporation, One Microsoft Way, Redmond, WA 98052-6399. NOTICE SPECIFIC TO DOCUMENTS AVAILABLE ON THIS WEBSITE Permission to use documents and related graphics available from this server ("Server") is granted, provided that (1) the above copyright notice appears in all copies and that both the copyright notice and this permission notice appear, (2) use of documents and related graphics available from this Server is for informational and non-commercial or personal use only, (3) no documents or related graphics available from this Server are modified in any way, and (4) no graphics available from this Server are used separate from accompanying text. MSP, MICROSOFT AND/OR THEIR RESPECTIVE SUPPLIERS MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE INFORMATION CONTAINED IN THE DOCUMENTS AND RELATED GRAPHICS PUBLISHED ON THIS SERVER FOR ANY PURPOSE. ALL SUCH DOCUMENTS AND RELATED GRAPHICS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MSP, MICROSOFT AND/OR THEIR RESPECTIVE SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES AND CONDITIONS WITH REGARD TO THIS INFORMATION, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL MSP, MICROSOFT AND/OR THEIR RESPECTIVE SUPPLIERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF INFORMATION AVAILABLE FROM THIS SERVER. THE DOCUMENTS AND RELATED GRAPHICS PUBLISHED ON THIS SERVER COULD INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS. CHANGES ARE PERIODICALLY ADDED TO THE INFORMATION HEREIN. MSP, MICROSOFT AND/OR THEIR RESPECTIVE SUPPLIERS MAY MAKE IMPROVEMENTS AND/OR CHANGES IN THE PRODUCT(S) AND/OR THE PROGRAM(S) DESCRIBED HEREIN AT ANY TIME. NOTICES REGARDING SOFTWARE, DOCUMENTS AND SERVICES AVAILABLE ON THIS WEBSITE. IN NO EVENT SHALL MSP, MICROSOFT AND/OR THEIR RESPECTIVE SUPPLIERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF SOFTWARE, DOCUMENTS, PROVISION OF OR FAILURE TO PROVIDE SERVICES, OR INFORMATION AVAILABLE FROM THIS SERVER. COPYRIGHT NOTICE. Copyright 1997 Microsoft and/or its suppliers, One Microsoft Way, Redmond, Washington 98052-6399 U.S.A. All rights reserved. TRADEMARKS. Microsoft, Windows, Windows NT, MSN, The Microsoft Network and other names of MSP and/or Microsoft products referenced herein are trademarks or registered trademarks of MSP and/or Microsoft. Other product and company names mentioned herein may be the trademarks of their respective owners. The names of companies, products, people, characters and/or data mentioned herein are fictitious and are in no way intended to represent any real individual, company, product or event, unless otherwise noted. Any rights not expressly granted herein are reserved. Contact www@microsoft.com or webmaster@msn.com or slate@msn.com with questions or problems with this service. © 1997 Microsoft Corporation. All rights reserved.