Home page Home page Home page Home page
Pixel
Pixel Header R1 C1 Pixel
Pixel Header R2 C1 Pixel
Pixel Header R3 C1 Pixel
Pixel
By Sprezz | Wednesday 3 July 2024 10:09 | 1 Comments

Recent queries on the Works forum led us to realise that there isn’t one single document that describes the various ways (well two) that you can interact with OLE controls within OI10. So we thought that we’d put it all together in one place using as an example a third party control called CSxGraph which you can download here . This is actually an ActiveX control, rather than an OLE control but that’s primarily a Microsoft branding exercise from nearly 30 years ago now, 1996! So, if you see something described as an ActiveX control, just assume it’s an OLE control.

Put simply, OLE components can be added to forms and manipulated via the standard Presentation Server functions, GET and SET_PROPERTY, EXEC_METHOD etc, or they can be created programmatically and be manipulated using intrinsic R/Basic routines such as OLEGETPROPERTY. If you want the user to see the control and possibly interact with it, you’ll need to add it to a form. If that isn’t a requirement then you’d instantiate the control programmatically.

OLE on Forms

To add an OLE control to a form, simply select the OLE Control menu item, and position the control where you want it on the form.


As this is a new OLE control, the system needs to know which OLE control to use, so it presents a dialog from which you can choose the control to insert 

If you know the CLSID or the ProgId of the control you can enter it here, or you can use the options button to select from a dialog, cleverly only showing the OLE objects that can be added as controls. In this case we know that we’re working with csXGraph to we can select that from the dialog


OK this, then OK the initial dialog, the system inserts the OLE control as requested


 There is an options button next to the CLSID which enumerates all the properties, methods and events that the control exposes, along with additional general information about the control type
 

 


 

So, it’s possible to work with the control without checking the documentation for a lot of things. Whilst this dialog provides a handy summary of what’s available, it’s more like a table of contents, pointing to additional items in the property panel. Further down the property panel we find OLE Properties.

Interacting with the UI

All of the OLE Properties that are exposed by the control can be found in the OLE Properties section of the property panel. We can set these properties manually in the property panel at design time or we can alter these properties at run time programmatically.

The property list is comprehensive as you can see


But,  if we know that we want this to be a Pie Chart we could select the graph type and choose that option


Similarly, we could set the title to a known value 


And if we run the form, having set these properties,


Nothing happens…

This is to be expected, we’ve told the control what title we want and that we want it to draw a pie chart, but we haven’t given it any data to display (properties), and more importantly, we haven’t told it that we actually want it to display anything (methods). 

So let’s add an edittable to capture our pie slice values and add a button to actually grab that data and display it.


To achieve this, we need to firstly get the data which we wish to display, then grab the title and amend it to include the updated date and time, clear the existing data (if any) from the OLE Control and then for each line of  data to display, update the data in the pie chart. Finally, we need to tell the control to plot and display the results. 

In the interests of transparency, the device this is being written on has a High DPI screen and this control is not High DPI aware so some manual playing with properties in the panel was required to make it visually – I won’t say appealing – appropriate.

Getting the data

This is a normal OI GET_PROPERTY call

 


Changing the title

This still uses normal OI GET and SET properties, but to ensure there is no clash between a PS property and an OLE property, we explicitly tell the PS that we’re wanting an OLE property by prepending the property name with “OLE.”

 


Clearing the data

For this we use EXEC_METHOD function, but as with the GET and SET we qualify our method name

 

Updating the data

Again we’re using EXEC_METHOD and providing random colours for the slices


Plot and display

And finally again an EXEC_METHOD

Interacting with Events

That’s the UI element dealt with but of course we might want to respond to an event on the control. For example, clicking the mouse down button on the graph, or maybe doubleclicking on an area of the graph.

For this we have to turn to the property panel again, but this time the OLE Integration property

 


As you will see, by default there are no events qualified, so when the events ARE triggered, they will not raise an OLEEVENT on the control.

If we were interested in trapping the mouse down event and the doubleclick event, we would simply launch the QualifiedOLEEvents dialog and check those events.


Now when the control is double clicked, or the mouse is clicked down on the control, an OLEEVENT will be raised and the parameters documented will be passed to your handling routine, be it a script or a commuter.

So if we put a debug in the OLEEVENT script for a control and mouse down over the control we will see this

 


With the EventName being onMouseDown, and with the parameters being (according to the control documentation)

Param1 Mouse Button
Param2 Shift state
Param3 Pixel X
Param4 Pixel Y
Param5 Graph X
Param6 Graph Y
Param7 Pixel Colour

Moving on to other elements of the event setup - the three Sync Types are Synchronous, Asynchronous and Callback. 

By default, the sync type is set to Asynchronous. An asynchronous event is queued and is thus guaranteed to be executed once everything before it in the queue has been dealt with. This can lead to a slight lag if the application is busy.

Setting the event to Synchronous will attempt to execute it immediately, but if the engine is tied up doing anything else it will just fail.

Setting the event to Callback creates a sort of hybrid situation where the system will attempt to execute the event immediately, but if it fails it will establish if the engine is waiting on a response from the Presentation Server rather than the engine being tied up running a program.  If it is waiting, it will branch off and run the event there and then.

So synchronous can be used when failure is immaterial, asynchronous can be used when failure is not an option and callback can be used when failure is optional but speed is of the essence.

For a discussion of the other check boxes see OLE event [Revelation Wiki], but essentially –


SuppressName ShowDispID Result
False False Returns the event name and any arguments
True True Returns the DispID (the numeric constant representing the event type) and any arguments. Using a numeric constant makes branching faster, as it removes the requirement for string comparisons.
False True Returns the event name, the DispId and the arguments. Permits mapping between the DispId and the name.
True False Of limited use as it returns nothing.


OLE in Programs

Programmatic OLE control allows us to automate our applications, driving external programs to produce results that would be hard to achieve in pure OI. Sending Emails, performing mail merges, generating images etc. 

Using OLE in programs is normally just a case of “instantiating” the OLE control – creating and initialising it – setting relevant properties and telling it to run the required methods. It is less likely that we would need to respond to OLE events in this context.

To illustrate programmatic usage, we will again return to our csXGraph control, but this time we will programmatically generate the graph and save the image.

To manipulate OLE controls programmatically we make use of the following self-evidently named routines –

OLECREATEINSTANCE     - to instantiate the OLE control
OLEPUTPROPERTY         - to set a property of the control
OLEGETPROPERTY         - to get a property of the control
OLECALLMETHOD         - to call a method of the control
OLESTATUS                 - to query the status of an OLE operation.

Note that these routines are intrinsic to the compiler and so do not need to be declared. Note further that these routines must be called individually for each property or method. It is not possible to stack the parameters in an array using @Rms and make a single call as is possible with the relevant PS routines.

OLESTATUS

This function takes no parameters and returns the status of the last OLE operation. If the value if FALSE$ then the operation succeeded otherwise a negative integer representing the error code is returned.

This error code is not particularly helpful, so it helps to use the system function RTI_ErrorText to retrieve an English language description. This function takes three parameters. The kind of error being retrieved, the error code in question, and whether Carriage Return/LineFeed should be swapped out for spaces.

Status = oleStatus()
    If status then errorText = RTI_ErrorText(“WIN”, status, FALSE$)

It should be used after every OLE operation.

OLECREATEINSTANCE

This function takes a single parameter – being the name of the class of the OLE control that we wish to instantiate.

So for example

oleObj = oleCreateInstance( “csXGraphTrial.Draw” )

If there was no error then we now have an OLE control instantiated in memory to play with. The oleObj variable will contain the handle for the OLE control.

OLEPUTPROPERTY

This routine takes a polymorphic set of parameters, depending upon whether the OLE object is indexed. If it isn’t, then it takes the standard three SET_PROPERTY parameters, namely the OLE object handle, the property and the value to set.

If the property is indexed (and it’s important to realise that this might not just be X and Y indexes – the object could be n dimensional), then the index values follow the property name, so

olePutProperty( oleObj, property[, index1, index2,…], value)

OLEGETPROPERTY

This routine takes a polymorphic set of parameters, depending upon whether the OLE object is indexed. If it isn’t, then it takes the standard two GET_PROPERTY parameters, namely the OLE object handle, and the property to get.

If the property is indexed (and it’s important to realise that this might not just be X and Y indexes – the object could be n dimensional), then the index values follow the property name, so

Return = oleGetProperty( oleObj, property[, index1, index2,…])

OLECALLMETHOD

This routine takes two required parameters – the OLE object handle and the name of the method to call. If the method requires parameters then these are passed as optional parameters to the OLECALLMETHOD call.

OleCallMethod( oleObj, methodName[, param1, param2…])

Bringing it all together

For our example, it wasn’t apparent from the screen shots, but several default properties had been changed to make the graph more attractive. We need to replicate this in our code. The properties that were changed were primarily to do with the size of the image, the title and obviously the graph type. It doesn’t really matter what order we set the properties in as long as all required properties are set before we ask the control to execute any methods.

So alphabetical is as good as any.   

CenterX
CenterY
GraphType
*Height 
LegendX
LegendY
MaxX
MaxY
Offset
OriginX
OriginY
PieDia
ShowLabel
ShowLegend
Square
Title
TitleX
TitleY
Width 

The “*” is used to draw attention to the fact that although this property is named identically to the form designer property, there will be no confusion as we are setting and getting OLE properties directly without going through the PS.

So now we need to modify the program to set the required properties.



Note that there’s a debug in the checkOLEStatus. Check the code above to see what line you think it’ll break at when the program is run. Well, the line with the debug in checkOLEStatus obviously, but who was the culprit?

So we run the code, there is an error, and obviously  it’s  -2147352570 error….

 


Which using the aforementioned RTI_ErrorText tells us is an “Unknown name”… note that the final version of this program uses OLE in place of WIN as the error type – the two are interchangeable but using OLE makes more sense in an OLE context.


And sure enough “PiaDia” is an error – it should be PieDia.

We can rectify this then start to add the data.

To add the data, we first tell the control to use randomised colours – so we can see that the graph is a different screen shot than the Form Designer one. We then use oleCallMEthod to add our four data values.


Then we tell the OLE control to draw the graph


 And finally, we tell it to save the graph to a BMP file

 


The end result?

 

Hopefully armed with the foregoing you'll now be adequately equipped to tackle your own OLE projects, but if you want to play the code for this article follows.

The Demo Program

Subroutine zz_OLE_Demo( Void ) 

   Declare Function RTI_ErrorText
   $Insert logical

   oleObj = Olecreateinstance( "cSxGraphTrial.Draw") |
                  ; Gosub checkOLEStatus

   If ok Then olePutProperty( oleObj, "CenterX", 350 ) |                           
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "CenterY", 350 ) |                           
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "GraphType", 0 ) |                           
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "Height", 700 ) |                            
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "LegendX", 100 ) |                           
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "LegendY", 50 ) |                            
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "MaxX", 500 ) |                               
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "MaxY", 500 ) |                              
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "Offset", 20 ) |                             
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "OriginX", 200 ) |                           
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "OriginY", 200 ) |                           
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "PieDia", 400 ) |                            
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "ShowLabel", False$ ) |                      
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "ShowLegend", True$ ) |                      
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "Square", 16) |                              
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "Title", "OI 10 OLE Demo" ) |                
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "TitleX", 20 ) |                             
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "TitleY", 20 ) |                             
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "Width", 700 ) |                             
                  ; Gosub checkOLEStatus

   // now add the data 

   If ok Then olePutProperty( oleObj, "UseRNDColor", TRUE$ ) |                     
                  ; Gosub checkOLEStatus
   If ok Then olePutProperty( oleObj, "RNDColor", 25 ) |                           
                  ; Gosub checkOLEStatus

   If ok Then retVal = oleCallMethod( oleObj, 'AddData', "Cherries", 48, 0) |      
                  ; Gosub checkOLEStatus
   If ok Then retVal = oleCallMethod( oleObj, 'AddData', "Apples", 16, 0) |        
                  ; Gosub checkOLEStatus
   If ok Then retVal = oleCallMethod( oleObj, 'AddData', "Peppers", 4, 0) |        
                  ; Gosub checkOLEStatus
   If ok Then retVal = oleCallMethod( oleObj, 'AddData', "Aubergines", 2, 0) |     
                  ; Gosub checkOLEStatus

   // and tell it to draw the graph

   If ok Then retVal = oleCallMethod( oleObj, 'DrawGraph' ) |                      
                  ; Gosub checkOLEStatus
  

   // and save to file

   If ok Then retVal = oleCallMethod( oleObj, 'SaveToFile', "ZZ_OLE_DEMO.BMP" ) |  
                  ; Gosub checkOLEStatus

Return

checkOLEStatus:

   OK = TRUE$
   Status = Olestatus() 
   If Status Then
      debug
      errorText = RTI_ErrorText("OLE", Status, FALSE$ ) 
      OK = FALSE$
   End

return

      
















Pixel
Pixel Footer R1 C1 Pixel
Pixel
Pixel
Pixel