ActiveX Shell technology

easy way to enable ActiveX scripting on Delphi and C++ Builder application


Table of contents

What is this

This is a technology for Delphi and C++ Builder.

It allows to create ActiveX object from any Delphi object at design time and at RUN-TIME by one function call. That allows OLE automation to access properties and methods of Delphi object.

There is nothing to code with this technology. Just call one function and use ALL your objects as true ActiveX object now.

The technology can be used, for example, to embed scripting language such as VBScript, JScript into the application. With scripting based on MS ActiveX Scripting or VBA.

In this document you can find all you need for that.


You known that Delphi and C++ Builder have their own object model which is distinct from object model of MS Windows named COM / DCOM / ActiveX / etc. Therefore to apply Delphi objects as ActiveX objects it is necessary each time to create separated ActiveX Library and separated Automation Object object as wrapper for your code. But will be very problematic to make it for all classes used by application. Moreover that will not be Delphi application.

The ActiveX Shell technology overcomes this misunderstanding and makes properties and methods of Delphi objects accessible for OLE automation that has a huge range of use.

The technology allows to create special ActiveX Shell wrappers around Delphi objects. ActiveX Shell wrapper is true ActiveX object.

Delphi object + ActiveX Shell = ActiveX object

Typical example of use is an embedding a scripting language into the application by using Microsoft ActiveX Scripting or VBA. Where in role of  the scripting language can be used VBScript, JScript, ActivePerl or any other language maintained MS ActiveX Scripting.
External scripting:
1.Windows applications supporting OLE
2.Windows Scripting Host
3.Office VB
4.Internet Explorer
Delphi application internal scripting on:

by using MS Script Control

| |
OLE automation <- MS ActiveX Scripting Engine
| | |
ActiveX Shell wrappers
[ threadsafe ]
Delphi objects

But MS ActiveX scripting is only one of ways to use application's object hierarchy created with the ActiveX Shell technology. Now you can use anyone supporting ActiveX model.

For example, Windows Scripting Host is new MS technology that allows to use JScript and VBScript programs as Windows batch file. It uses an ActiveX object model too. Other examples - IIS ASP, Office VB code, VBA.

From side of OLE automation it is enough to create one ActiveX Shell wrapper to receive access to all other linked Delphi objects in application which is accessible from first as properties, child-components, or methods results. That means we receive a not only one ActiveX object but access to all objects in the hierarchy - an OBJECT MODEL OF APPLICATION.

What does it mean ?! For example, if script engine has a reference to Form1 created with ActiveX Shell then all other components on the form accessible in script as properties, child-components or as methods results like this:


This is same object hierarchy as you use in Delphi code.
  |____ Panel1
  |       |-- Caption
  |       |-- Font
  |            |-- Size
  |            |-- Color
  |            |-- Name
  |____ Panel2
  |       |-- ...
  |____ Button1
  |       |-- ...
  |____ DBGrid1
  |       |-- Visible
  |       |-- Columns
  |              |-- Item(i)
  |                    |-- FieldName
  |                    |-- Title
  |                    |-- Font
  |                         |- ...
  |____ Query1
  |       |-- SQL
  |       |    |- Text
  |       |
  |       |-- Active
  |       |-- ...

How to obtain a first reference to ActiveX Shell object in your script ?! There are some ways.

For internal scripting you need just to add first name to the script engine's name space.

And you can use your Delphi objects for external ActiveX scripting. For example, if application is designed as OCX library's object then it is possible to execute your application by calling CreateOLEObject() (or CreateObject() in Visual Basic) and then to use application objects hierarchy in Visual Basic program, VBScript, JScript, VBA code of any Windows applications supporting OLE such as Word, Excel, Outlook, Internet Explorer, IIS ASP etc. This is a second way to obtain a reference to first ActiveX Shell object.



Technology allows to create ActiveX object from any Delphi object at design time and at RUN-TIME by one function call. That allows OLE automation to access properties and methods of corresponding Delphi object. See below how to create ActiveX wrapper for your Delphi object.

ActiveX Shell wrapper is TRUE ActiveX object. From the point of view of OLE, the ActiveX Shell wrapper have same properties and methods as defined in corresponding native Delphi object. OLE works with native Delphi object by its ActiveX wrapper.

Now it is possible through this mechanism:

For object received as result of first two listed actions a new ActiveX Shell object will be created automatically. To return an object as result of  Iactivex_shell_executable interface methods you have to create new ActiveX Shell object manually in the method implementation. You need just to place a call of  obj_to_variant  or  obj_to_activex  function from our unit.

Now it is possible too:

ActiveX Shell object is true ActiveX object. Therefore to reference it in Delphi we should use variable of Variant type. Function var_is_object described below  is used to check that Variant contains exactly ActiveX Shell object.

When having ActiveX Shell wrapper it is possible anytime to access corresponding Delphi object by calling function var_to_object described below.

For some Delphi classes there are extended ActiveX Shell wrappers which are created by the engine when you call obj_to_variant and obj_to_activex.  We need extended wrappers to receive access to basic features of some well known Delphi objects. Especially if the class is frequently used in Delphi but have not sufficient published properties to manipulate from OLE through our mechanism. The technology allows to define such extended ActiveX Shell wrappers for any Delphi classes. See below in detail.

Multithread support

You know that Delphi objects have not multithread support. Usually you have to use Synchronize or other methods to synchronize access to the objects.

All requests to Delphi object via ActiveX Shell wrappers are always automatically synchronized. That allow to create multithread application or multithread accessible application / component without care about threads synchronization in your code. The technology guarantees that only one thread in multithread framework will access the Delphi object at the time through OLE. Even if you create more than one ActiveX Shell wrappers for one Delphi object. But, of course, any two different Delphi objects are synchronized independently.
Thread 1 (main)
ActiveX Shell wrapper
for the same Object1
Thread 2
ActiveX Shell wrapper
for the same Object1
Thread 3
ActiveX Shell wrapper
for the same Object1


Limitations of the technology are based on the features of Run Time Type Information. The request on TPersistent is explained by availability the Run Time Type Information (RTTI) for the object. Though most of Delphi classes are inherited from TPersistent. And practically absolutely all published properties of Delphi objects have simple data types or type inherited from TPersistent.
Available versions, downloading

Available compiled versions for:

Source code is compatible with Delphi 3.02, 4, 5, 6 and C++ Builder 3, 4, 5.

Latest version of the component is always available on the components download page.

How to install
1. Unzip archive with subdirectories.
2. Directory CB3\ is for CBuilder 3 users. File activex_shell.obj.
Directory CB4\ is for CBuilder 4 users. File activex_shell.obj.
Directory CB5\ is for CBuilder 5 users. File activex_shell.obj.
Directory D3\ is for Delphi 3 users. File activex_shell.dcu.
Directory D4\ is for Delphi 4 users. File activex_shell.dcu.
Directory D5\ is for Delphi 5 users. File activex_shell.dcu.
Directory D6\ is for Delphi 6 users. File activex_shell.dcu.

Copy file activex_shell.dcu (.obj) from one of this to any Delphi library path or to your project path.
Or install activex_shell.dcu (.obj) from Menu > Component > Install Component.

3. Just add activex_shell to uses statement when need it.

Examples are located in the corresponding directories too. Also see examples below.

Hint: For CB3 and CB4 version you can get "Linker error: file not found dclusr35.lib" (or dclusr40.lib in CB4 case). Just open the corresponding dclusr??.bpk file in $(BCB)/lib and make it.


It is very easy to use. Just call one function to create ActiveX Shell wrapper for your Delphi object

function obj_to_variant(obj : TObject) : Variant;

FYI: The type of returned Variant will be varDispatch because it will be real ActiveX object.

For example,
unit Unit1; 



  TForm1 = class (TForm) 
      Button1: TButton; 
      procedure Button1Click (Sender: TObject);


{$R *.DFM} 

procedure TForm1. Button1Click (Sender: TObject); 
  var wrapper: Variant; 

// creating an ActiveX Shell wrapper for this form
  wrapper := obj_to_variant(Self); 

// using published properties
  wrapper.Caption := 'New caption'; 
  wrapper.Font.Size := 22; 

// using child-components by name -
// Button1 is not a published property
  wrapper.Button1.Caption := 'After click caption'; 

// using an object type properties
  wrapper.ActiveControl := wrapper.Button1;

// String representation is used here to read and write data of enumeration type
  wrapper.WindowState := 'wsMinimized';

  wrapper.WindowState := 'wsNormal';



That is all you need for using published properties and for accessing child-components by name trowgh ActiveX Shell wrapper.

Note that we are using a Variant reference in this example to show that we work with the ActiveX objects. Variant in Delphi is used for OLE / ActiveX objects. All this code can be executed by script engine by VBA, JScript, VBScript, ActivePerl, etc. See below how to embed scripting into the application.


Important declarations in unit activex_shell

Useful functions declared in activex_shell unit:
// checking that Variant is ActiveX Shell wrapper
function var_is_object(value: Variant): Boolean; 

// getting back Delphi object from Variant containing ActiveX Shell wrapper
function var_to_object(value: Variant) : TObject; 

// Getting textual representation of Variant (if exists) or zero length string 
function var_to_string(value: Variant): AnsiString; 

Example: Receiving back Delphi object from Variant containing ActiveX Shell wrapper. Let variable param is VarArray.
  var obj : TObject;

  if var_is_object( param[i] ) then obj := var_to_object( param[i] );

// making something with the object here 
// ...



How to use methods

Simple way to access methods without arguments is to use published property reader.
TMyComponent = class(TComponent)
  function runMethod1 : String;
  property Method1 : String read runMethod1;

As you see, runMethod1 is called every time when accessing published property Method1.

More flexible way to access the methods is to use special interface declared in the activex_shell unit as:
Iactivex_shell_executable = interface(IUnknown) 

  // to return a comma delimited list of methods supported by your object
  function  activex_shell_methods : AnsiString;

  // to implement methods of your object
  function  activex_shell_exec(method_name: AnsiString; var param: Variant): Variant;


By implementing Iactivex_shell_executable interface it is possible to define methods with variable number of arguments. The interface allows to define what methods of your class will be accessible from OLE automation. 

Method activex_shell_methods returns a comma separated list of methods which you want to publish. 
Method activex_shell_exec is a implementation of those methods.

Methods arguments:

Developer should use standard Delphi functions VarIsArray and VarArrayHighBound to check that Variant is an array and for determining size of the array.

Example of work with methods via Iactivex_shell_executable

You know, Delphi (read OLE engine) needs IUnknown interface to be implemented to check that the object implements some other  interface (see Delphi manual in detail). Therefore at least two interfaces are required: Iactivex_shell_executable and IUnknown.

Some Delphi classes are already implements IUnknown. For example, TComponent class and all its descendants in Delphi 4 and later, C++ Builder 3 and later. 

In other cases you have to implement IUnknown manually in each class that uses Iactivex_shell_executable interface.

We provide TAXObject class which can be used as base class instead of TObject in your code to perform automatic IUnknown implementation.

For Delphi 3 we also provide TAXComponent class which can be used instead of TComponent in your code to perform automatic IUnknown implementation.

Both classes are defined in the activex_shell unit as:
// Useful as base class instead of TObject 
  TAXObject = class(TPersistent, IUnknown)
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;

// Useful as base class instead of TComponent
  TAXComponent = class(TComponent, IUnknown)
{$ifdef VER100} // only for Delphi 3
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;

Examples, how to provide IUnknown in your class:
Examples Description
// Declaration you have

TMyObject = class

// To provide IUnknown write

TMyObject = class(TAXObject)
// ...

TAXObject is used here. Real base class now is not TObject but TPersistent. That means Delphi will also create RTTI for the instance.
// Declaration you have

TMyComponent = class(TComponent)

// To provide IUnknown write

TMyComponent = class(TAXComponent)

TAXComponent is used here. It is not needed in Delphi 4 and above, C++ Builder 3 and above. Do it just for compatibility with Delphi 3.
// Declaration you have

TMyClass = class(TSomeBaseClass)
// ...

// To provide IUnknown write

TMyClass = class(TSomeBaseClass, IUnknown)
 function QueryInterface(const IID: TGUID; 
 out Obj): HResult; stdcall;
 function _AddRef: Integer; stdcall;
 function _Release: Integer; stdcall;



function TMyClass.QueryInterface(const IID: TGUID; 
  out Obj): Integer; stdcall;
  if GetInterface(IID, Obj) 
    then Result := 0
    else Result := E_NOINTERFACE;

function TMyClass._AddRef: Integer; stdcall;
  Result := 0;

function TMyClass._Release: Integer; stdcall;
  Result := 0;

if TSomeBaseClass doesn't implement IUnknown then you should do it manually in TMyClass. 

Example implementation is presented here too.

This is example of a component which publishes three its methods as ActiveX methods by using Iactivex_shell_executable interface:
TMyComponent = class(TAXComponent,Iactivex_shell_executable)
 function activex_shell_methods : AnsiString;
 function activex_shell_exec(method_name : AnsiString; var param: Variant): Variant;
 procedure Method1(); 
 function  Method2(obj : TObject) : String;
 function  Method3(text : String) : Object;


// declaration of methods
function TMyComponent.activex_shell_methods : AnsiString;
 result := 'method1,method2,method3';

// implementation of methods
function TMyComponent.activex_shell_exec(method_name : AnsiString; 
                               var param: Variant): Variant;
  result := Unassigned;

  if method_name = 'method1' then begin
  else if method_name = 'method2' then begin
    if VarIsArray(param)
      then result := Method2(var_to_object(param[0]));
  else if method_name = 'method3' then begin
    if VarIsArray(param) 
      then result := obj_to_variant(Method3(var_to_string(param[0])));


Let now MyRef is a ActiveX Shell reference to an instance of TMyComponent in script engine. Now you can write the following VBScript code:
rem =====================
rem This is VBScript code
rem =====================

rem Receiving a published property value

res = MyObj.Name

rem Assigning a published property value
MyObj.Name = "new_name"

rem Calling a procedure

rem Receiving a string result
res = MyObj.Method2( MyObj )

rem Receiving a object result
Set obj_res = MyObj.Method3( "test" ) 

This is a working example of  Iactivex_shell_executable interface use for TForm class. You have this as the example application in distribution kit. Additional IUnknown interface implementation here needs only for Delphi 3.

unit Unit1; 


  Windows, Messages, SysUtils, Classes, Graphics, 
  Controls, Forms, Dialogs, StdCtrls, ExtCtrls,
// ^^^^^^^^^^^^^

  TForm1 = class (TForm, Iactivex_shell_executable)
//                       ^^^^^^^^^^^^^^^^^^^^^^^^^
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 

// Iactivex_shell_executable
    function activex_shell_methods : AnsiString;
    function activex_shell_exec(method_name : AnsiString; var param: Variant): Variant;


{$R *.DFM} 

procedure TForm1. Button1Click(Sender: TObject); 
  var wrapper: Variant; 

// creating an ActiveX Shell wrapper
  wrapper := obj_to_variant(Self); 

// using an ActiveX Shell methods
  wrapper.show_arguments('only one arg');
  wrapper.show_arguments('two', 'args');
  wrapper.show_arguments('now', 3 ,'args');

  ShowMessage( wrapper.object_classname( wrapper.Button1 ) );


// Iactivex_shell_executable interface implementation

// declaration of ActiveX Shell methods
function TForm1.activex_shell_methods : AnsiString;
  result := 'show_arguments,object_classname';

// implementation of ActiveX Shell methods
function TForm1.activex_shell_exec(method_name : AnsiString; 
                                   var param: Variant): Variant;
 var  i : Integer;
        temp_str : String;
  result := Unassigned;

  if method_name = 'show_arguments' then begin

    if VarIsArray(param) then begin
      for i := 0 to VarArrayHighBound(param, 1) do begin
        temp_str := temp_str + 'arg' + IntToStr(i) + '=' 
                    + var_to_string( param[i] ) + #13;



  // object_classname
  else if method_name = 'object_classname' then begin

// How to raise an exception:
    if VarIsArray( param ) and (var_to_object( param[0] ) = nil) then begin
      raise Exception.Create( 'Method parameter is nil !' );

    if VarIsArray( param ) and (var_to_object( param[0] ) <> nil) then begin
      result  := var_to_object( param[0] ).ClassName;





Using TStrings, TCollection, TDataSet

When you are using  obj_to_variant and obj_to_activex, extended ActiveX Shell wrapper for some Delphi classes is created automatically by the engine. It helps to receive access to basic features of those Delphi objects via OLE automation. The technology allows you to define such extended ActiveX Shell wrappers for any Delphi classes. 

For TStrings class and all inherited classes, the following properties and methods are presented in the extended Active X Shell wrapper:

TStrings properties (see Delphi documentation)

TStrings methods (see Delphi documentation) Additional methods for getting value of an element Additional method for setting value of an element
For TCollection class and all inherited classes, the following properties and methods are presented in the extended Active X Shell wrapper:

TCollection properties (see Delphi documentation)

TCollection methods (see Delphi documentation) Additional method for getting value of element
For TDataSet class and all inherited classes, the following properties and methods are presented in the extended Active X Shell wrapper:

TDataSet properties (see Delphi documentation)

TDataSet methods (see Delphi documentation) Additional methods for getting field and value of field

Of course those properties and methods always return values as Variant. Even if result type is TObject here it means result Variant contains ActiveX Shell wrapper for the instance of that TObject.


Use from C++ Builder

The use from C++ Builder is same as from Delphi. With only a small difference. In C++ Builder Variant is class, so we have to use the class syntax. For example, in Delphi we had the following code:

  var myRef : Variant; 
  // creates OLE object from the form given as self
  myRef := obj_to_variant( Self ); 

  // sets new caption to the form 
  myRef.Caption := 'Welcome !'; 

Translated to C++ Builder looks like:
// C++ include directives
#include "activex_shell.hpp" 
#pragma link "activex_shell" 


  // creates OLE object from the form given as this 
  // class Variant constructor is used
  Variant myRef( obj_to_variant(this) ); 

  // sets new caption to the form 
  // class Variant method is used
  myRef.OlePropertySet("Caption", "Welcome !"); 


Using with MS ActiveX Scripting, embedding scripts into the application

MS ActiveX Scripting
is just one of ways to use application's ActiveX object model created with ActiveX Shell technology. You can use any other scripting engines with your application. If they supports ActiveX / OLE object model. See above about it.

For embedding scripts on VBScript, JScript into an application the following components are required:

MS Script Engine (Scripting Host) is already on your computer if MS IE 4.0 or above is installed. It is possible to install separately with the distribution kit loaded from Microsoft site 

License: Freeware
GUID: {EE09B103-97E0-11CF-978F-00A02463E06F}

MS Script Control - is necessary for use the MS Script Engine by your application. It is possible to install with the distribution kit loaded from Microsoft site

License: Freeware, see site
GUID: {0E59F1D5-1FBE-11D0-8FF2-00A0D10038BC}


Hint: This function allows to do dynamically check by GUID that the necessary OLE component is installed.
function check_by_guid (component_guid: String): Boolean
 var v : Variant;
    v := CreateCOMObject( StringToGUID (component_guid) ); 
    result := True; 
    result := False; 
    ShowMessage('Component is not installed: ' + component_guid); 



This is a example of use of MS Script Control in Run Time where two VBScript commands are interpreted:
    var this_form, scripting, module: Variant; 

// creating an instance of MS Script Control
  scripting := CreateOLEObject('ScriptControl'); 
  scripting.AllowUI  := True; 
  scripting.Language := 'VBScript'; 

// creating ActiveX Shell for the object, 
// here for the form given as 'Self'
  this_form := obj_to_variant(Self); 

// The fist way is to add name to ScriptControl's namespace
// example of creation of 'this' reference which is global name for code of all scripts
  scripting.AddObject('this', this_form, True); 

// example of use of the reference 'this'
   'this.Caption = "New Caption 1"' + #13#10 + 


// The second way is to use a special "module" for the object
// where this. prefix is not need.
  module := scripting.Modules.Add('my_form_module', this_form ); 

    'Caption = "New Caption 2"' + #13#10 + 
    'MsgBox (Caption)'


In detail about using MS Script Control, its properties, methods and objects you can read in msscript.hlp from the MS ScriptControl distribution kit.

Creating OCX library

Read this only if you need to execute your application as OLE object.

There is standard way to create an OCX library with "YourAppName.Application" OLE object by using Delphi ActiveX framework:

  1. use Delphi menu > New  then choose ActiveX > ActiveX Library
  2. save project as "YourAppName"
  3. use Delphi menu > New  then choose ActiveX > Automation object
  4. write "Application" as Class Name
  5. add properties and methods in the Type Library editor and register library
Now you have OCX library with "YourAppName.Application" object inside.

Now it is possible to execute project and use its objects by call

my_object := CreateOLEObject('YourAppName.Application');
in Delphi or similar function from VBA, VBScript, JScript and all other languages supporting OLE.

In detail see Delphi documentation.

Now you can use "YourAppName.Application" OLE object as an entry point to your application object space.

Let the OLE object has property or method returning OleVariant. This OleVariant can be ActiveX Shell reference to any Delphi object. And all OLE world can access your Delphi classes by this entry point.

For example, if the Automation object you created has property DataSet of type OleVariant which is added by Delphi Type library editor.

An implementation unit will contain empty method which gives the property value. Use ActiveX Shell technology there:
function TYourAutomationObject.Get_DataSet : OleVariant;

  // simply return an object
  result := obj_to_variant(FDataSet);



Now you can access properties and methods of DataSet and all other objects hierarchically from VBScript. The example:
rem =====================
rem This is VBScript code
rem =====================

my_object = CreateObject('YourAppName.YourAutomationObject')


AmountPaid_sum = 0

do while not my_object.DataSet.EOF 
  AmountPaid_sum = AmountPaid_sum + my_object.dataset.FieldValues("AmountPaid")



It is a beautiful decision for one more problem.

It is known that only descendants of TWinControl can be automatically transformed by Delphi to ActiveX. What to do when need to provide functionality of other classes that are not controls ?

Simply create one Automation object with property that returns the ActiveX Shell wrapper for Delphi class you need. That allows to receive functionality of any Delphi classes in the scripts. It was described above for TDataSet.

Advantage of the Professional version

  Multithread support is in professional version only.

II. In professional version it is possible to create ActiveX Shell object with reference counting feature and without caring about destruction of corresponding Delphi object after using it. The Delphi object will be auto destroyed when reference count is 0.

This feature is very powerful, for example, when returning object value from a function:
function some_function ... : Variant;
  var strings : TStrings;

// creating some object 
  strings := TStringList.Create();

  strings.Add('line 1');
  strings.Add('line 2');
  strings.Add('line 3');

// creating and returning as result an ActiveX Shell object
// with feature of reference counting
  result := obj_to_activex( strings );

// You have not to care about destruction of TStringList object.
// It will be auto destroyed when will not be needed.


Note that 

function obj_to_activex( obj : TObject ) : Variant;

is same as considered above obj_to_variant .

But function obj_to_activex create ActiveX Shell wrapper with feature of reference counting and autodestruction.

This feature is very flexible not only in ActiveX Shell methods implementation, it can be used freely in Delphi application to return or store any objects without caring about destruction of them. The object exists while exist one or more Variant reference to it.

Corresponding Delphi object can be received back from the Variant reference by using  var_to_object  function.

Using with HTML Template component to create HTML, XML, SGML reports

If you need to create in application HTML, XML, SGML or text reports then use HTML Template component. It is available for downloading. In detail see the manual page of the component.

It works like well known ASP,  JSP,  PHP templates inside your application and allows to produce HTML (XML, SGML, text) page from the page template stored in the file, database etc. HTML Template works at application side without and Web server engines and can access application's objects and data to create report.

To design report view it is possible to use your favorite editor. For example, MS FrontPage, Word, Notepad and so on.

Template page is formatted page with embedded script. The ActiveX Shell technology can be used as engine for the scripts execution.

How to buy and download the source code

For commercial purposes or if you are interested in the sources you have to buy the professional version.
Software will be available immediately after your registration from the secure web site.

ActiveX Shell technology
(professional version, source code, compatible with D3 and above, CB3 and above)
Personal license,
for one developer
40 $ Buy it now
Company license,
for any number of developers within one company
90 $ Buy it now

Contact information

Send your questions and comments to

Apelseen software website is


Useful links


Version 2.2
Copyright (c) 1999, Apelseen software. All Rights Reserved