Understanding the Synergy ActiveX API
ActiveX is a Microsoft technology that stems from OLE and COM (Component Object Model). ActiveX Automation is the process of controlling the functionality of an ActiveX component’s methods from another application or development tool. By adding ActiveX components to your application, you can extend its capabilities with a wide variety of interactive and multimedia effects.
The ActiveX API is a set of subroutines with standard arguments that enables ActiveX controls to be used directly from within a Synergy DBL program. With this technology, you can load an ActiveX control, bind Synergy DBL methods to events signaled by the control, call ActiveX control methods from Synergy routines, set and get properties, and release control to the ActiveX control.
This topic includes the following:
- Requirements
- Registering an ActiveX control
- Writing a routine that calls an ActiveX control
- Callback routines
- Workbench support
- UI Toolkit support
- Microsoft Windows support
- Properties for ActiveX objects
- ActiveX data types
- ActiveX Diagnostic utility
- Licensing considerations
- Distributing applications that use ActiveX controls
- Debugging
- Sample program
Requirements
The file DBLDIR:activex.def defines the macros and constants required by the ActiveX API. You must include this file in your program using the .INCLUDE compiler directive.
All programs that use the ActiveX API must be built using the compiler options -T (which trims trailing null arguments from XCALLs and function references) and -X (which automatically declares all undefined function references as external ^VAL functions).
You must also link with DBLDIR:axlib.elb.
32-bit ActiveX controls can be used only with the 32-bit Runtime, and 64-bit ActiveX controls can be used only with the 64-bit Runtime.
Before an application can use an ActiveX control, the control must be registered with the Windows operating system (see Registering an ActiveX control below).
Registering an ActiveX control
Before an application can use an ActiveX control, the control must be registered with the Windows operating system. Once the file is registered, it should not be moved or renamed. Use one of the following methods:
- Use the regsvr32.exe utility that is distributed with Windows. On 64-bit Windows, use the version of regsvr32 that corresponds to the bitness of the control. That is, to register a 64-bit control, use the regsvr32 in %windir%\system32, and to register a 32-bit control, use the regsvr32 in %windir%\syswow64. (This program must run in administrator mode to register or deregister anything.)
regsvr32 filename
- Use the Add Control button in the ActiveX diagnostic utility (axutl) that is included in your distribution.
You can also use the Synergy DLL API to register ActiveX controls programmatically if your program is running in administrator mode. Use the DLL API to open the .ocx file as a DLL, and then call the DllRegisterServer routine within it. For example, hndl = %dll_open("OCXDIR:mycontrol.ocx") xcall dll_call(hndl,,"DllRegisterServer") xcall dll_close(hndl) To avoid doing this every time the control is loaded, trap an error on the load and register the control only if the load fails. This method is particularly useful for shared server installations where controls may be added to the existing application without performing an installation script on each workstation. For more information, see Synergy DLL API. |
Writing a routine that calls an ActiveX control
These steps give you an overview of calling an ActiveX control. Note that some of these steps may be performed by other routines in your program.
1. | Include the file DBLDIR:activex.def in your routine. |
2. | Create an ActiveX container by calling %AX_CREATE. |
3. | Load your ActiveX control by calling %AX_LOAD. |
4. | Write one or more Synergy Language callback subroutines (see Callback routines) that will be called whenever specific events occur within the control. For example, you might write a callback subroutine to be executed when the user clicks on something or when a close is signaled from ActiveX. |
5. | To call a Synergy Language routine, first define an event within the source code for your ActiveX control. Raise (or fire) the event. |
6. | Bind the callback subroutine you wrote in step 4 by calling %AX_BIND. When you bind a callback routine, you establish that routine as the supporting method for your ActiveX event. (You can omit this step if you specify the autobind_prefix argument to %AX_CREATE or %AX_LOAD). |
7. | Repeat steps 5 and 6 for every subroutine and event you want to bind. |
8. | Set properties by calling AX_SET, and get properties by calling AX_GET. |
9. | Invoke methods by calling %AX_CALL. |
10. | Show the ActiveX container by calling %AX_SHOW. |
11. | Process your control by calling %WIN_PROC. |
12. | Compile your routine using the compiler switches -T and -X. |
13. | Link your routine with DBLDIR:axlib.elb. |
Callback routines
A callback routine is a Synergy DBL routine that is called whenever a specific event occurs within an ActiveX control. A callback routine must be bound to the event using the AX_BIND routine. Alternatively, you can automatically bind the routine by specifying the autobind_prefix argument to %AX_CREATE or %AX_LOAD. If this argument is present, each event is automatically bound to any existing routine whose name is the specified prefix followed by the event’s name.
The first argument of every callback routine is the ID of the ActiveX control that calls it (that is, the id passed to AX_BIND). Passing this control ID enables a routine to interact with an ActiveX control without requiring global data. In fact, if there are no global data references, the same routine can be used as a callback for more than one loaded ActiveX control or event.
ActiveX controls can also have parameters associated with their events. The parameters are passed as arguments to the bound callback routine, beginning with the callback routine’s second argument. In other words, the first parameter of the ActiveX event is passed as the second argument to the callback routine, the second parameter is passed as the callback routine’s third argument, and so on.
Every parameter passed is automatically promoted or converted to a data type that is consistent within Synergy DBL. For example, if two parameters are defined as an integer and a string, they are converted to an i4 and an ann field, respectively. From the standpoint of the callback routine, it is the same as processing a standard external subroutine from a Synergy DBL routine. Passing an array argument converts the array to a BSTR (a variable-length string). (See ActiveX data types.)
All parameters passed to a callback routine are read-only. If you attempt to modify one directly, a runtime “Writing to a literal” error is generated. If you need to modify a callback routine parameter, use AX_NEWVAL.
A callback routine cannot perform a STOP chain or throw an exception that can be caught back in the Synergy caller. The runtime will generate an $ERR_NOCHAIN error if a callback routine attempts to perform a STOP chain. Unpredictable results can occur if a callback routine throws an exception. |
Be aware of synchronicity with your callback routines. An ActiveX control may fire many simultaneous events as the user moves the mouse, for instance. While your program is in a callback routine, it can be interrupted and the same callback called recursively. Ensure all callback routines are re-entrant, and don’t use writable global data. See %M_SIGNAL concerning the use of %M_SIGNAL within an ActiveX event.
Workbench support
Professional Series Workbench facilitates the use of ActiveX controls in your applications. First, it enables you to add ActiveX controls to your Workbench projects in the same way you would add any other file to a project. As soon as you add an ActiveX control, Workbench loads the classes specified in the control, along with their methods and fields, into the project’s tag database.
Workbench also enables you to browse an ActiveX control in its class browser so you can see what functionality and objects the control provides.
UI Toolkit support
To integrate ActiveX controls into UI Toolkit, you must use a special Toolkit container window. A Toolkit container window has the basic characteristics of a Toolkit window (character-based increments, placement order, maintained environment, and title manipulation, for example), yet it has the display and operational characteristics of an ActiveX container (load controls, get/set property values, call methods, bind callback routines, and so forth).
For more information, see ActiveX Routines.
Microsoft Windows support
The ActiveX API includes two functions that provide the Synergy DBL developer with an interface to the Windows system. Refer to the specified pages for syntax and additional information:
Properties for ActiveX objects
A property is a named attribute associated with an ActiveX object. Property names can be up to 63 characters long and are not case sensitive. All properties are manipulated using the AX_GET (or %AX_GETINT) and AX_SET routines. The following basic kinds of properties are supported:
- Control
- Extended
- Ambient
- Local
Control properties are defined by each ActiveX object, both in name and in data type. There are no requirements or conventions regarding which control properties should or should not be present; it is entirely up to the ActiveX control’s designer. The ActiveX API supports the following control properties for containers:
- Caption - Container’s caption
- ClipChildren - WS_CLIPCHILDREN window style
- Icon - Container’s icon
The value of Caption is a string that specifies the caption, or title, of the window.
ClipChildren can be set to true (the default) or false. True makes the ActiveX container have the WS_CLIPCHILDREN window style, which reduces background repainting (flicker) and some repaint anomalies. You can turn this setting off if the control expects the background to be erased.
Icon is set using an icon filename string and an optional integer value. If present, the value is the one-based index in the icon file for the icon to reference. If not specified, a value of 1 is assumed.
Some controls support “indexed” properties, which are stored as arrays in a control and have one or more indices. When an indexed property is accessed, the index values of the array element to reference must be added as arguments to the end of the AX_GET, %AX_GETINT, or AX_SET call. The user documentation for a given control should specify when a property is indexed.
An ActiveX container can “extend” a set of properties to each control that is loaded in that container. The following extended properties are supported:
- Ext_hWnd - Object’s window handle (read-only)
- Ext_Rect - Object’s position and size (read/write)
- Ext_Visible - Object’s visible state (read/write)
- Ext_Focus - Whether or not the object has focus (read/write)
For the read/write properties, changing the property using AX_SET changes the object’s visual representation if necessary.
The value of Ext_hWnd is a standard numeric window handle for the window associated with the object.
The value of Ext_Rect is a single-dimension numeric array whose first four elements are the pixel values for the left, top, right, and bottom of the object’s window. (These values are screen coordinates for an ActiveX container but container coordinates for an ActiveX control.)
The value of Ext_Visible is the numeric value 1 (true) if the object’s window is visible or 0 (false) if the window is hidden. Although the initial state of Ext_Visible is true for loaded controls, it is false for created containers. Thus, you must use AX_SET to set a created container’s Ext_Visible value to true before the container can be viewed on the screen. The routines AX_SHOW(id) and AX_HIDE(id) manipulate the visible state.
The value of Ext_Focus can only be true. When Ext_Focus is set for a control, the overall system focus is set to that control. You don’t have to clear the focus from the previous control; it is cleared automatically. An AX_GET is true (-1) if the related object currently has the system focus and false (0) if not. An AX_SET of a nonzero value sets the system focus to the related object, while an AX_SET of zero is ignored. Set Ext_Focus for the control on which you want to focus rather than on the control’s container.
Each ActiveX container has a set of properties that provides default values for control properties. These properties are known as ambient properties, and one set is associated with each container. Setting an ambient property for a container sets it for all controls within that container.
The ambient properties provided by the Synergy ActiveX API are
- Amb_BackColor - Background color
- Amb_ForeColor - Foreground (text) color
- Amb_LCID - The language used (read only)
- Amb_UserMode - The user or design mode state (read/write)
The value of Amb_BackColor and Amb_ForeColor is a numeric RGB (red, green, blue) value that has the hexadecimal form
^X(00bbggrr)
where bb, gg, and rr are intensity values in the decimal range 0–255 for the blue, green, and red color components, respectively. (For convenience, the macro RGB_VALUE(rr,gg,bb), which constructs a numeric RGB value, is defined in DBLDIR:activex.def. The RED_PART(rgb_value), GREEN_PART(rgb_value), and BLUE_PART(rgb_value) macros are also defined.)
The value of Amb_LCID is a numeric “Locale ID” as defined within the Windows system.
The returned value of Amb_UserMode is either true (-1) if the container is currently running in user mode or false (0) if it is running in design mode. When setting Amb_UserMode, a zero value sets the container to design mode, while a nonzero value sets it to user mode. Note that a container should be set to user mode before any processing of loaded controls occurs.
You may sometimes want to associate user-definable data with an ActiveX object. Local properties enable you to define, access, and modify named values that are associated with any given ActiveX object (container or control). Local properties are manipulated using the standard AX_GET, AX_SET, and %AX_GETINT mechanisms and have the following characteristics:
- All local property names begin with a tilde (~).
- The data associated with a local property is a signed, 32-bit value.
- Local properties are created with a value of 0 on their first reference.
If a single 32-bit value is not enough information for a given local property, you can always use the dynamic memory support (the %MEM_PROC routine and the STRUCTURE statement) and store the memory handle as the property’s value. |
ActiveX data types
The following ActiveX data types are supported for AX_GET and arguments passed to a routine bound to a control’s event.
- VT_BOOL. Represented in Synergy DBL as an integer field whose valid values are 0 (false) and -1 (true).
- VT_R4. A 4-byte (32-bit) “real” value that is represented in Synergy DBL as a d28.15, with seven decimal digits of significance.
- VT_R8. An 8-byte (64-bit) “real” value that is represented in Synergy DBL as a d28.15, with fifteen decimal digits of significance.
- VT_CY. A currency type, which is a 64-bit integer whose low-order four decimal digits are to the right of the decimal point. The representation in Synergy DBL is d19.4.
- VT_DATE. A date type that is represented in Synergy DBL as an a14 in the same form as %DATETIME (YYYYMMDDHHMMSS).
- VT_BSTR. A variable-length string represented in Synergy DBL as an alpha field.
- VT_I1, VT_I2, VT_I4, VT_I8, VT_INT, VT_ERROR, VT_HRESULT, VT_UI1, VT_UI2, VT_UI4, VT_UI8, VT_UINT. Represented in Synergy DBL as an integer field.
- VT_VARIANT. Allows any of the listed data types. This is similar to using “,n” in a Synergy DBL subroutine, and in fact, using “,n” is correct for numeric VT_VARIANTs in a bound routine.
- VT_USERDEFINED. Represented in Synergy DBL as an integer. You (the developer) define what it means.
- Any other data types return an a4 containing the string “????”.
For AX_SET, AX_NEWVAL, and %AX_CALL, in addition to the above data types, VT_NULL and VT_EMPTY are also supported. If the ActiveX data type is not supported for conversion from a Synergy data type, an error is generated. When dealing with VT_VARIANT, the data type of the passed Synergy DBL argument determines the data type passed to the ActiveX control. We translate the Synergy data to the nearest VT_ data type. It is up to the ActiveX control to be able to support the data type passed.
An ActiveX method that contains optional arguments generates an “Argument missing” error if the argument is specified as data type VT_BSTR. This error does not occur if the data type for the optional argument is specified as VT_VARIANT. |
ActiveX Diagnostic utility
The ActiveX diagnostic utility (axutl) provides basic analysis of an ActiveX control. It tests to make sure a given control can be accessed on your system, and it can also be used to register controls. If you cannot get an ActiveX control to load, this utility should be your first diagnostic tool.
Licensing considerations
Many ActiveX controls require a license. If your site is licensed to use a particular ActiveX control, you will usually have an .lic file for that control on your system. An ActiveX control can also be run at an unlicensed site (in other words, one that does not have an .lic file) as long as you pass a license string to the control using %AX_LOAD. This means that if you want to redistribute an ActiveX control to your end users, you must either redistribute the .lic file (if it is legally permitted by the control’s owner) or pass a license string to %AX_LOAD in order for the control to load on an end user’s machine. If you are not permitted to redistribute the .lic file, passing a license string to %AX_LOAD is your only legal option.
The ActiveX Diagnostic utility can return licensing information for a control. See Testing an ActiveX control for details.
Distributing applications that use ActiveX controls
When you distribute your application, make sure you distribute the following files:
- The file that contains your ActiveX controls (usually .ocx)
- Your main program (.dbr)
- Any files on which your control depends. For example, for controls created with Visual Basic 6, you should redistribute MSVBVM60.DLL (the VB6 runtime) to ensure the target system has it.
If you redistribute shared DLLs (such as MSVBVM60.DLL), you must ensure that your installation either puts the file only in your software’s private directory or only replaces older versions of the same file. |
The .ocx file (or whatever you named it) must be registered before it can be used. If you’ve written an installation script, make sure it registers the file. If not, you can do it manually as described in Registering an ActiveX control.
Debugging
The ActiveX API supports a debugging system to enable you to analyze problems in your code. Set the AXDEBUG environment variable as follows to use the debugging system:
To |
Set AXDEBUG to |
---|---|
Enable the debug display system |
YES |
Enable the debugging system and log events to a file |
FILE=filename |
Enable debugging system and send debug information to an attached Windows debugger |
EXTERNAL |
On 32-bit Windows, when the debugging system is enabled by setting AXDEBUG to YES, the creation of the first ActiveX container creates a debug display, which consists of a tree-view representation of the Synergy DBL/ActiveX system and a log of the related operations as shown in figure 1. (On 64-bit Windows, the built-in debugger is not supported, but you can set AXDEBUG to EXTERNAL and use a third-party debugger. See AXDEBUG for more information.)
|
The tree view is in a standard layout:
- The outer nodes of the tree are the containers.
- Nodes beneath a container are the ActiveX controls loaded into that container.
- Each control has method, property, and event “folder” nodes beneath it.
- The individual methods have nodes within the method folder, and argument nodes are beneath each method.
A log window is displayed in conjunction with the tree view. As Synergy DBL/ActiveX operations occur, a description of each operation is written to the log. You can scroll through the log, as well as specify the maximum number of log entries. See AXDEBUG for more information.
You can also turn on debug mode programmatically if no containers have been created yet. To do so, make an empty call to AX_CREATE and specify -1 as the parent window, as shown below:
xcall ax_create(,,,,-1)
This call does not create a container; instead, it serves the same purpose as setting AXDEBUG to YES before the program is run. When you call %AX_CREATE a second time to actually generate the container, it starts up in debug mode.
Sample program
Other samples are included in the dbl\examples directory of your Synergy DBL distribution.
;====================================================================== ; Module: axmusic.dbl ; ; Facility: Synergy DBL ActiveX demo ; ; Description: Synergy program demonstrating ActiveX support ; ; $Revision: 1.0 $ ; ; $Date: Jun 04 1997 13:45:42 $ ;====================================================================== main axmusic .include "DBLDIR:ActiveX.def" .define AX_XPOS ,200 .define AX_YPOS ,200 .define AX_HEIGHT ,500 .define AX_WIDTH ,740 record axwid ,i4 ;ID of ActiveX container object axid ,i4 ;ID of ActiveX control object proc xcall flags(7000000) ;Disable stop msg axwid = %ax_create(AX_XPOS, AX_YPOS, AX_WIDTH, AX_HEIGHT) ;Create the container axid = %ax_load(axwid, "AXDemoMusic.AXDemo2", 0, 0, AX_WIDTH, AX_HEIGHT) ;Load the control xcall ax_bind(axid, "OnLoading", "OnLoading") ;Bind events xcall ax_bind(axid, "OnGetDetail", "OnGetDetail") xcall ax_bind(axid, "OnClose", "OnClose") xcall ax_show(axwid) ;Show the container xcall win_proc(1) ;Process Activex controls stop endmain subroutine OnLoading ; ; Description: Load from file event ; axid ,n ;ID of ActiveX control FileName ,a ;Filename to open record music_rec ,a127 proc open(10, u:i, FileName)[err=openerr] ;Open the demo file repeat begin reads(10, music_rec, eof) xcall ax_call(axid, "AddRecord", music_rec) ; and pass to control end eof, xcall ax_set(axid, "FindBtnEnabled", 1) ;Enable the find button close 10 openerr, xreturn endsubroutine subroutine OnGetDetail ; ; Description: ActiveX requests a detail record ; axid ,n ;ID of ActiveX control KeyString ,a ;Key of detail record record filchn ,i4 detail_rec ,a333 proc open(11, u:i, "detail")[err=notfound] ;Open the demo file read(11, detail_rec, KeyString)[eof=notfound,key=notfound] xcall ax_call(axid, "AddDetail", detail_rec) ; and pass to control close 11 notfound, xreturn endsubroutine subroutine OnClose ; ; Description: Closing down ; proc xcall win_stop(1) ;Terminate ActiveX processing xreturn endsubroutine