Using an OCX control within UNIFACE Seven

1. What is an OCX?   

2. REGISTER an OCX custom control  

3. How to use OCX   

3.1. ini        

3.2. Paint an OCX           

3.3. Set design-time properties  

3.4. Run-time properties  

3.4.1. Getting a property 

3.4.2. Multiple properties  

3.4.3. Getting an indexed property    

3.4.4. Setting a property 

3.4.5. Setting multiple properties  

3.4.6. Setting an indexed property    

3.5. Code event handling triggers    

3.6. Invoking methods for a control  

3.7. Examples of adding data to a GRID


1. WHAT IS AN OCX?

OLE Custom Controls (OCXs) are reusable software components designed to work in containers that support OLE 2.0. The OCXs are more powerful and more flexible than the VBX custom controls they will replace. OLE controls are also easier to develop than VBXs. These tools can include support for standard control types, such as font, color, or picture, as well as support for control properties, events and methods, property pages, persistence, self-registration, multiple controls in a single OLE custom control file, data binding, licensing, and so on. Additional tools will assist OLE custom control developers who are working from an existing VBX custom control in creating a prototype from the VBX source-code model information.

An OLE custom control is an embeddable object with in-place activation capabilities. Every OLE custom control has three sets of attributes:

·         Properties. Named characteristics or values of the control such as color, text, number, font, and soon.

·         Events. Actions triggered by the control in response to some other external actions on the control, such as clicking a mouse button or pressing a key. Events provide a standard way for the control to invoke a call back in its container.

·         Methods. A function implemented in the control that allows external code to manipulate its appearance, behavior, or properties.

OLE Automation objects can be categorized into three types:

·         Applications. These are programs such as standalone spreadsheets or word processors. The programs may actually be collections of different objects, including OLE Automation objects, or OLE custom controls, such as a grid or graph.

·         Restricted objects. These are OLE objects that cannot be freely copied and distributed by a developer, such as the graphing object in Microsoft Excel. An end user must have a licensed copy of a restricted object or an application that contains that object already on their machine. Restricted objects also might be sold with a distribution license that allows the developer to redistribute a certain number of copies.

·         Distributable custom controls. These are like present-day OCXs. They are added into a development tool environment, and run-time versions of the object can be freely distributed.

In addition to their ease of use, the licensing structure for OCX custom controls contributes greatly to their popularity with the developer community. Once a developer purchases a copy of a third-party OCX, the developer uses that custom control to design an application. The developer is then free to redistribute an executable, run-time version of that custom control as part of the application without having to pay a run-time royalty for each copy of the application that is sold.

A licensing mechanism prevents customers from using this run-time custom control version to develop new applications. This mechanism prevents unauthorized use of OCX by end users unless they have legally purchased the control. The OLE custom control architecture will also include additional licensing capabilities in case custom control developers want to license their OLE custom controls at different levels to different customers or create a site license.

Recently, it was decided to name certain OLE controls under the label ActiveX controls. These ActiveX controls are smaller because they are intended for use on the Internet and are self registering which makes them more user friendly. In this document, the term OLE controls (OCXs) is used for now to avoid confusion, since at this time the term ActiveX is still very new and could be misleading. The OCX controls are developed using the Control Development Kit (CDK) from Visual C++, Visual Basic, Delphi and so on.

2. Register an OCX custom control

Before you can use an OLE Control, you must register it with the system (every client machine). Registering an OLE Control puts information about the control in the system registry. Once the control has been registered, applications and development environments can search the registry to determine which controls have been installed. Unlike a Visual Basic custom control (VBX), an OLE Control does not need to be registered by each application that uses it. Once the control has been registered with the system, any application can find it.

The OLE Control Developer's Kit (CDK) includes a tool called REGSVR.EXE (or REGSVR32.EXE for 32-bit systems) that can register and unregister controls. When you install the CDK, it adds two commands--Register Control and Unregister Control--to the Visual Workbench Tools menu . These commands call REGSVR.EXE to register or unregister the control built by the current project. If the current project does not build a control, the attempt to register or unregister it fails.

Many control containers or installation programs prefer to handle control registration themselves instead of spawning REGSVR.EXE to do the job. These applications can adopt the method that dictates that OLE controls must be self-registering, that is, an OLE Control must be able to register and unregister itself. REGSVR.EXE takes advantage of this requirement and gets the control to do the registration work.

OLE controls export two functions to implement the registration process: DllRegisterServer registers a control and DllUnregisterServer unregisters it. Both functions return zero on success and an OLE error code if an error occurs. Every control must be registered before it can be used, and a number of common methods exist for performing this task:

·         Use the REGSVR.EXE (16-bit) or REGSVR32.EXE (32-bit) program to perform the registration. They are redistributable applications that take the name of an OLE control file as a command line parameter and register it by calling the internal DllRegisterServer function. If you provide the /U option, the specified control is unregistered by calling the DllUnregisterServer function.

·         Take advantage of installation programs' built-in capability for registering OLE controls.

·         Write your own setup program in VB, creating a declare statement that calls the DllRegisterServer or DllUnregisterServer function directly.

·         Take advantage of the fact that many controls contain a version information string in their version resource called OLESelfRegister indicating that the control can register itself. This can be used as a signal to the system to automatically verify that a control is registered when it is loaded.

The system registry contains a lot of information about each control on the system, and the registry information is used in a number of different ways. First, it allows Windows to find the control, eliminating the need for controls to be stored in the path or project directory and changing the nature of the problem of resolving version conflicts between components. The registry also contains information used by control containers (like UNIFACE) to load control property pages, access control properties and events, and bind to control events.

Understanding the nature of how OLE controls are registered can help you understand what is going on behind the scenes with these controls and help you perform clean-up operations, should it become necessary.

To distribute applications, use an installation program that performs a proper version check before overwriting any shared components. You can also perform your own installations using the version API functions included in Windows, but never do a simple file copy. Since you can never be sure how long your software will sit on somebody's shelf, you can never be certain that your distribution disk will always contain the latest version of a component. Overwriting a later version of a control or DLL with an older version may let your program run, but it can be devastating to other applications that use the same component.

Some of the files you may need to ship with your application.

MFC40.DLL                          The Microsoft Foundation Class runtime library. This library is shared by many OLE controls and contains a framework on which these controls are based. It is possible to write OLE controls without this file, but it is much more difficult. OC25.DLL is the 16-bit equivalent for this file. Some controls may be using other versions of MFC such as:

MSVCRT40.DLL                A run-time library containing the C library functions used by many OLE controls. Some controls may be using other versions of C or C++, which would use other run-time DLLs, for example MSVCRT20.DLL.

OLEPRO32.DLL                    Standard OLE types, such as picture and font objects.

Other OLE files                     OLE2PROX.DLL, STORAGE.DLL, TYPELIB.DLL, COMPOBJ.DLL.

These are files that implement OLE itself. Your control may expect that the latest versions of these files be present on the user's system.

Your control vendor should be able to tell you which files are required by any given control. In some cases you can assume that these files are already available on the user's system. For example, every Windows NT and Windows 95 system includes the OLE support files. But how can you be certain that the version of these files is recent enough to work with your controls? The only safe way is to redistribute the latest version that you have. Your installation program can then update the target file if it is out of date.

3.       How to use OCX

3.1.          ini

The logical name of the widget is defined in the .ini file and refers to a physical name and, optionally, a collection of default properties. It is recommended that you use a separate logical name for each OCX control you want to use. Do not start your name with the letter ‘U’ as this is reserved for UNIFACE standard widgets.

The following section from an example .ini file shows the use of the OCX grid control:

[WIDGETS]

Grid=uocxcontainer(ocx=MSGrid.Grid)

AnyButton=uocxcontainer(ocx=AniBtn.AniPushButton)

The name used between brackets is the PROGID of the OCX; for the Grid Control this is  MsGrid.Grid.

Following the addition of a new custom control to the USYS71.INI file, the control will be available for use from the Tool Palette in the Component Editor. Paint the control onto the form.

Error handling

Errors are reported in the Transcript Window. If an OCX cannot be loaded, the OCX container widget shows an error bitmap on the field.

To direct the OCX’s messages to the Transcript Window, use the following setting in the usys71.ini file:

[OCX]

                OCXTRACE=ON

3.2. Paint an OCX

Before painting an OCX, it is necessary to see what properties,  methods and events are available. There are several tools for this,  for example the OLE/COM OBJECT VIEWER that Microsoft distributes via the Internet. The GUID indicates the type library; a type library contains information on all of the properties, methods, and events for the control.

Another tool to get more information on an OCX ,which is however not downloadable for free, is the Test Container from VISUAL C++. The CDK includes a Test Container (tstcon32.exe), a sample application that can examine and change the properties of an OLE Control, invoke its methods, and watch it generate events. This is a valuable tool, not only for developing and debugging new controls but also for learning about existing controls.

However, the Test Container is limited to being an interactive tool that cannot be programmed. You can manipulate a control only with the mouse. You cannot write functions to see how the control works, nor can you develop code to direct and respond to the control, so you cannot tell whether it is easy or difficult to write a handler for the control you are developing.

If you look at the OLE/COM OBJECT VIEWER, from the Control tree, select the Grid Control, then  the related Typelib and look in the tree at ‘dispinterfaces’, where you can see  the supported properties, methods and events. Some information, such as Helptext, datatype (interface) and property/method/event name from the MSGrid.Grid controls :

PROPERTIES

Specifies the name of the font that appears in each row for the given level.

BSTR FontName; Return or set bold font styles

VARIANT_BOOL FontBold; Return or set the total number of rows in a Grid control.

short Rows; Return or set the active cell in a Grid control.

short Col; Determines whether lines between cells are visible.

VARIANT_BOOL GridLines; Return or set the background color used to display text and graphics in an object.

OLE_COLOR BackColor;

METHODS

propget, Return or set the height of the specified row (record) in twips.

long RowHeight(short Index);

propput, Return or set the height of the specified row (record) in twips.

void RowHeight(short Index, long rhs);

propget, Return or set the width of the specified column in twips.

long ColWidth(short Index);

propput, Return or set the width of the specified column in twips.

void ColWidth(short Index, long rhs);

EVENTS

Click

Occurs when the user presses and then releases a mouse button over an object.

void Click(); RowColChange

Occurs when the current cell changes to a different cell.

void RowColChange(); MouseMove

Occurs when the mouse is moved over the control.

void MouseMove(); short Button

short Shift

OLE_XPOS_PIXELS X

OLE_YPOS_PIXELS Y

The last event shows four different parameters that will be available if the event is invoked.

3.3.          Set design-time properties

Examine the OCX properties for the control. These can be set as follows:

OCX Name:                           Determines which OCX control the system should interface with, the name appearing is the AppID code of the OCX. (for example, Grid Control, PROGID is MsGrid.Grid).

Field  Value Linked To                This property determines which of the OCX properties are used to hold the value of the field. The selected property will be updated whenever the value of the UNIFACE field changes. The name is not case-sensitive. The default for this property is either VALUE, TEXT or CAPTION, respectively, depending on what is supported in the OCX.

Furthermore the data type of the field and the OCX have to match. If there is no match (for example,. string field to an integer property), the OCX container reports an error during data transfer of the field.               

Fire Value Changed On                Events that fire the <Value Changed> event for the control, separated by commas.

Fire <Detail> On                   Events that fire the <Detail> trigger for the control. This property allows

the user to select the OCX controls’ events that should  fire the <Detail> trigger. The function uOCXEvent will allow the 4 GL to provide a list of events from which the events to be ignored can be selected.

Initial value                           The value is used to initialize the property identified by the VALUE property

On creation, the OCX container widget passes the value on to the property.

Native Properties                 Allows additional properties supported by the control to be set. This property holds a semicolon-separated string of the native properties of the OCX. Whenever the uOCXPropSheet is invoked, it returns a list of OCX specific properties (for example, ‘backcolor=black;font=courier;forecolor=white’). These properties are stored as the value of the OCXNativeProps. When an OCX widget is re-created, it retrieves the value of this property and uses it to initialize the OCX.

3.4.          Run-time properties

To manipulate an OCX, it is necessary to change its properties in a well-defined order. Such control is not possible using $properties or $fieldproperties. Only properties supported by the OCX can be used.

The control’s properties can be manipulated with the following commands :-

                    uSetOcxProp                        Set a the value of a property

                    uGetOcxProp                        Get the value of a property

                    uSetOcxPropI                      Set a property requiring an index

                    uGetOcxPropI                      Get a property which has an index

                    uSetOcxPropM                    Set multiple properties requiring an index

                    uGetOcxPropM                    Get multiple properties requiring an index

3.4.1. Getting a property

Returns the value of the OCX property for a field. The field must be an OCX widget. The value is returned in $result. The global variables must be set, before calling uGetOCXProp, as follows:

¨               $<base> specifies the field name as a string.

¨                $<base+1> specifies the property name as a string.

$<base> is the base register. By default this is $50 and can be set by the uOCXSetBaseReg function.

Example Grid Control

$50="field1.dummy"                            ; specifies the field name

$51="Rows"                                          ; specifies the property name

perform "UGetOCXProp"

3.4.2. Multiple properties

Returns multiple properties of an OCX field. The field must be an OCX widget. The global variables must be set, before calling uGetOCXPropM, as follows:

¨               $<base> specifies the field name as a string.

¨                $<base+1> specifies the properties to be returned as a comma-separated list.

$<base> is the base register. By default this is $50 and can be set by the uOCXSetBaseReg function.

¨               $status returns 0 if OK, -1 if an error occurs.

¨               $result returns a comma-separated list of “property” items..

Example Grid Control

$50="field1.dummy"                                            ; specifies the field name

$51="Rows;Col"                ; properties to be returned

perform "uGetOCXPropM"

3.4.3. Getting an indexed property

Returns the value of the index of the OCX property for a field. The field must be an OCX widget. The value is returned in $result. The global variables must be set, before calling uGetOCXPropI, as follows:

¨               $<base> specifies the field name as a string.

¨                $<base+1> specifies the property name as a string.

¨                $<base+2> specifies the index (base 1) of the property.

$<base> is the base register. By default this is $50 and can be set by the uOCXSetBaseReg function.

Example

$50="field1.dummy"            ; specifies the field name

$51="FullPath"                     ; specifies the property name

$52=$index_selected$        ; specifies the index (base 1)

perform "uGetOCXPropI"

3.4.4.Setting a property

Assigns a new value to the OCX property for a field. The field must be an OCX widget. The global variables must be set, before calling uSetOCXProp, as follows:

¨               $<base> specifies the field name as a string.

¨                $<base+1> specifies the property name as a string.

¨                $<base+3> specifies the new value as a string, number or Boolean.

$<base> is the base register. By default this is $50 and can be set by the uOCXSetBaseReg function.

Example

$50="field1.dummy"                            ; specifies the field name

$51="Col"                                              ; specifies the property name

$53="8"                                                  ; specify new value as a string

perform "uSetOCXProp"

3.4.5. Setting multiple properties

Assigns multiple properties to an OCX field. The field must be an OCX widget. The global variables must be set, before calling uSetOCXPropM, as follows:

¨               $<base> specifies the field name as a string

¨                $<base+1> specifies the “property=value” assignments as a comma-separated list

$<base> is the base register. By default this is $50 and can be set by the uOCXSetBaseReg function.

¨                $status returns 0 if OK, -1 if an error occurred.

Example

$50="field1.dummy"                                                            ; specifies field name

$51="Rows=4;Col=8"                ;specifies properties

perform "uSetOCXPropM"

3.4.6. Setting an indexed property

Assigns a new value to a specified index of the OCX property for a field. The field must be an OCX widget. The global variables must be set, before calling uSetOCXPropI, as follows:

¨               $<base> specifies the field name as a string.

¨                $<base+1> specifies the property name as a string.

¨                $<base+2> specifies the index (base 1) of the property.

¨                $<base+3> specifies the new value as a string, number or Boolean.

$<base> is the base register. By default this is $50 and can be set by the uOCXSetBaseReg function.

Example

$50="field2.dummy"                            ; specify the field name

$51="PictureType"                              ; specify the property name

$52=$index_selected$                        ; specify the index (base 1)

$53=1                                                      ; specify the new value

perform "uSetOCXPropI"


3.5.          Code event handling triggers

OCX events can be mapped to the <Detail> trigger. Within the <Detail> trigger, the user can call the following functions to obtain the true name of the event and event parameters:

1.        uGetOCXEvent

2.        uGetOCXParam

The <detail> and <value changed> triggers need to be coded to handle any events that the control may generate. This also serves to flush out unwanted events. Events can be captured using the “uGetOcxEvent” command.

uGetOCXEvent

Returns the name of the event that caused the <Detail> trigger to be fired. The value is returned in $result. The global variable must be set before calling uGetOcxEvent, as follows:

¨               $<base> specifies the field name as a string.

-               $<base> is the base register. By default this is $50 and can be set by the uOCXSetBaseReg

function.

¨                $status returns 0 if OK, -1 if an error occurred.

An example for the GRID control is listed below :-

<Detail> trigger

$50 = "DAY.DIARY"                     ; Name of the field

perform "uGetOcxEvent"                   ; Get next event waiting to be processed

while ($status >= 0)                             ; Event found

   if ($result = "DblClick")                            ; Process if its a double-click

        ;

        ; Code to handle a double-click event

        ;

   endif

   perform "UgetOcxEvent"                ; Find next event

endwhile

return(0)                                                 ; Ensure $status is re-set to okay

uGetOCXParam

This function has been made to retrieve a parameter of the event causing the <Detail> trigger.

It returns the value of the named event parameter. For more information on the names of the parameters associated with each event, refer to your OCX control documentation. The value is returned in $result.

This can be called directly after calling uGetOcxEvent, in order to obtain the values of any parameters associated with the event. The global variables must be set before calling uGetOcxParam, as follows:

¨               $<base> specifies the field name as a string.

-                $<base+1\> specifies parameter name as string

¨               $status returns 0 if OK, -1 if an error occurs

-               $result for the returned value

Example

$50="field3.dummy"            ; specifies field name

perform "uGetOcxEvent"

while ($status>=0)

                if ($result = "MouseMove")

                                $51 = "Button"                ; specifies parameter

                                perform "uGetOcxParam"                ; handle event

                endif

endwhile

$result will give you the value of the button (0 or 1; mousebutton pressed or not)

3.6.          Invoking methods for a control

The OCX container widget queries the OCX for the right number of arguments expected. The registers are read and the method is invoked.

The UNIFACE “uOcxMethod” can be used to invoke method for a control. An example follows:

When setting an OCX property through a method, the method for setting the property must be prefixed with string “set”. For example, if you want use the “put” variation of the RowHeight method in the Grid Control, code as follows :

$50=container_name

$51="SetRowHeight"

$52=1

$53=1000

This sets the RowHeight of row 1 to 1000 units.

This prefix is not required when using the “Get” variation of a method. For example:

$50=container_name

$51="RowHeight"

$52=1

This returns the current RowHeight of row 1.


3.7.                Examples Add Data to a GRID     

The following section gives an example of generic global procedures designed to simplify the use of custom controls. Specific routines have also been listed for the GRID custom control.

Set GRID Data (for currently selected cell)

; Set $$FIELDNAME, $$ENTITY, $$THISROW, $$THISCOL, $$DATA

entry ADD_GRID_DATA

   $50 = "%%$$FIELDNAME%%%.%%$$ENTITY"

   $51 = "Row"

   $53 = $$THISROW

   perform "usetocxprop"

   if ($status < 0)

      message "Failed to set Row property for %%$$FIELDNAME"

      done

   endif

   $51 = "Col"

   $53 = $$THISCOL

   perform "usetocxprop"

   if ($status < 0)

      message "Failed to set Col property for %%$$FIELDNAME"

      done

   endif

   $51 = "Text"

   $53 = $$DATA

   perform "usetocxprop"

   if ($status < 0)

      message "Failed to set Text property for %%$$FIELDNAME"

      done

   endif

end

Get GRID Data (for currently selected cell)

 ; Set $$FIELDNAME, $$ENTITY

; Result will be returned in $$DATA, $$THISROW and $$THISCOL will be set

entry GET_GRID_DATA

   $50 = "%%$$FIELDNAME%%%.%%$$ENTITY"

   $51 = "Row"

   perform "ugetocxprop"

   if ($status < 0)

      message "Failed to get Row property for Grid widget."

      done

   endif

   $$THISROW = $result

   $50 = "%%$$FIELDNAME%%%.%%$$ENTITY"

   $51 = "Col"

   perform "ugetocxprop"

   if ($status < 0)

      message "Failed to get Col property for Grid widget."

      done

   endif

   $$THISCOL = $result

   $50 = "%%$$FIELDNAME%%%.%%$$ENTITY"

   $51 = "Text"

   perform "ugetocxprop"

   if ($status < 0)

      message "Failed to get Text property for Grid Widget"

      done

   endif

   $$DATA = $result

Available Methods (taken from As Built Specification)

end

  • OcxMethod Invoke a method
  • uOcxPropSheet Show the property sheet of the control
  • uOcxEnumProps Enumerate the properties of the control
  • uOcxEnumEvents Enumerate the events of the control
  • uOcxEnumMethods Enumerate the methods of the control
  • uOcxIUnknown 3GL only; return the Iunknown pointer of the control
  • uOcxNameDialog Show the control selection dialog and return the selected progid
  • uGetOcxProp Get the value of an Ocx property
  • uSetOcxProp Set the value of an Ocx property
  • uSetOcxPropM Set Multiple properties
  • uGetOcxPropM Get multiple properties
  • uGetOcxEvent Get name of the last event which caused this detail trigger
  • uGetOcxAsyncEvent Get name of the last event which caused this Async trigger
  • uGetOcxParam Get a parameter form this Detail event
  • uOcxAsyncParam Get a parameter form this Async event
  • uOcxEvent Enumerate the events of the control
  • uOcxSetLocalId Set a new locale ID
  • uOcxSetBaseReg Set the base register
  • uOcxPrototype Get the prototype of an event property or method
  • uGetOcxPropI Get indexed property (Obsolete)
  • uSetOcxPropI Set Indexed property (Obsolete)
  • uVbxMethod Vbx compatibility: Invoke a method
  • uVbxGetProp Vbx compatibility: Get a property
  • uVbxSetProp Vbx compatibility: Set a property
  • uVbxGetPropI Vbx compatibility: Obsolete
  • uVbxSetPropI Vbx compatibility: Obsolete
  • uVbxGetPropM Vbx compatibility: Get a multiple properties
  • uVbxSetPropM Vbx compatibility: Set a multiple properties
  • uGetVbxEvent Get the name of the last event
  • uGetVbxParam Get the value of a event parameter