Using COM in Applications

What is FlexCOM?

FlexCOM is a framework that makes Component Object Model (COM) technology from Microsoft available for Visual DataFlex developers. By using FlexCOM, a Visual DataFlex developer can implement solutions with COM technology directly within a Visual DataFlex application.

Visual DataFlex provides wizards and classes that make it easy to implement applications based on COM. With these wizards, you can create COM-based classes and controls to use within applications.

This documentation will not describe COM technology in depth. Instead, it is intended to describe how to use COM objects in your Visual DataFlex applications.

See Also

FlexCOM 2 Overview

COM Introduction

The Component Object Model (COM) forms the basis on which OLE and ActiveX technology is built. COM enables communication between components, applications, and between clients and servers through clearly defined interfaces.

Interfaces are essentially tables of functions associated with a COM Object. You can call the methods of an interface just like you call the methods of any other Visual DataFlex object.

There is an ever growing and extending set of capabilities based in COM technology. Today, these capabilities can be divided into three major categories: ActiveX Controls, Automation and Embedded Objects.

ActiveX Controls

An ActiveX Control (OCX) is a custom control, which can be a simple control such as a button, or a complicated control containing several other controls such as the Microsoft Calendar Control or a complete Web browser.

An ActiveX Control can be inserted in an application’s window, for a user to interact with. And the application can call several functions/procedures and get and set properties exposed by the Control. An ActiveX Control can also send events, informing the application of different changes in its state, such as the user clicked on the control or the control has completed an asynchronous function and so on.

An ActiveX Control does not have to be a visualized custom control, though it usually is, it can be a non-visualized control simply exposing functionality and sending events.

Automation

Automation is a technique where one program exposes functionality to other programs. A program exposes functionality by exposing one or more OLE classes, including functions/procedures and properties. Other programs can then instantiate objects from those exposed classes, and make use of them in their program.

For example Word exposes several Automation classes that can be used to manipulate or create Word documents directly from other programs.

The purpose of Automation is in fact very similar to DDE, the big difference is that Automation is object oriented and fully supports encapsulation, inheritance and polymorphism, and of course that using Automation is a lot easier and more straight forward.

The program that exposes Automation classes is called an Automation server, and the program accessing exposed functionality is called an Automation Controller.

Embedded Objects

OLE Compound Documents gives one application the possibility to either link or embed an OLE object from another application into its own document, without having any knowledge of that object’s behavior.

For instance using OLE Compound Documents you can insert a picture from Paint into a Word document, without Word having any idea of how to display the picture. The picture is inserted as an OLE object instantiated from an OLE class in Paint, providing that the object itself is responsible for all drawing and rendering of its picture. Word simply tells the object to draw it self, without having any knowledge of what the object contains.

The embeddable or linked object’s application is called an OLE Compound Document server application, and the application embedding or linking the object is called an OLE container application.

You can import ActiveX Controls, Automation Libraries and Embedded (OLE Document) Objects in the Studio very easily, at which time the necessary "wrapper" classes are automatically created for you. See Importing COM Components for further information.


FlexCOM Overview

FlexCOM 2 Overview

Introduction

FlexCOM 2 is the second generation of COM support for Visual DataFlex. FlexCOM 2 offers a brand new COM implementation and better integration with Visual DataFlex core technology.

If you already have experience with prior revisions of FlexCOM and VDF, you will discover that this second generation of COM support will make your life much easier as a COM user and developer. You’ll also quickly see that even though the internals have been completely rewritten, most of your code can be reused and migrated without much effort. Most of the changes are concentrated to the underlying Visual DataFlex COM core support and technology rather than interface changes. You will be able to immediately apply your prior knowledge to using FlexCOM 2. You will very quickly come to appreciate the technology changes, and the improved and wider support for integrating ActiveX components with your Visual DataFlex applications.

To make your transition to FlexCOM 2 even smoother, Visual DataFlex supports the simultaneous use of both FlexCOM 1 and FlexCOM 2 technology in the same application. This means that you can start using FlexCOM 2 immediately without having to convert any existing code that uses the older FlexCOM technology.

To use FlexCOM 2 in an application you will need to use the new COM class generator to generate new classes for each COM object you wish to use. The new class generator is built into the Studio and only supports generating FlexCOM 2 compliant classes. For more information refer to Importing COM Components.

New Variant Data Type

FlexCOM 2 uses a new native Variant data type introduced in Visual DataFlex 8.3. Variant is a native Visual Data Flex data type, meaning that you can declare a Variant variable anywhere you can declare any other type of variable. Variant variables can be used in assignments, expressions, properties, function return values, just like any other variable, i.e. Variant is a new native Visual DataFlex data type.

In prior revisions there was a OLEVariant structure and a some complicated commands like PutOleVariantValue and GetOleVariantValue. Those days are now gone. Simply declare a Variant variable and use it directly in regular assignments and other statements and expressions. There are no commands and conversions just to use a COM object method that requires Variants.

For more information refer to:

Variant Command in the Language Reference,

Variants in the Language Guide,

Variant Data Type in "Programming with COM - General Topics".

Passing Arguments "By Reference"

Previously, you could only pass arguments "by value", meaning that a copy of a variable was sent to a function and the function was incapable of modifying the original variable. You can now pass variables "by reference". When a parameter is passed"by reference, the called procedure or function has direct read/write access to the passed variable. Changes made to the reference parameter inside the method are reflected in the passed variable outside the method.

This technique is often applied when there’s a need for multiple return values, or for in-out parameters which pass one value to the called method and are allowed to be modified and contain a different value when the execution returns to the caller. The latter is commonly used in COM event methods, as a means of allowing the client code to cancel or modify a certain action or default behavior. In which case the COM object might pass on a Boolean parameter bCancel, which the program may set to True in order to cancel the pending action.

Better Focus and Keyboard Handling

ActiveX controls now actively participate in the Visual DataFlex focus tree just like any other Visual DataFlex control. This means that all of the focus and tab order problems in prior revisions are completely resolved.

In prior revisions there was a property called DFTakesFocus_State which you had to set depending on whether you wanted the VDF object or the ActiveX control to take the focus. This property has now been removed, since ActiveX controls are now working together with VDF, and actively participating in the Visual DataFlex focus tree. And the kind of problems seen in prior revisions related to focus and keyboard handling no longer exists.

Improved Binding to COM Objects

Binding COM Interfaces to Visual DataFlex wrapper classes has been simplified and improved. You no longer need to use ReleaseComPtr. Maintaining references are now taken care of automatically by the use of Variants instead of LPDispatch/LPUnknown. You now use Variants in all places where you previously used LPDispatch and LPUnknown.

In prior revisions, using ActiveX controls or Automation objects containing other COM objects (aggregate objects) required you to use CreateDispatch or AttachDispatch and ReleaseDispatch to manually create and maintain references to COM objects. This required careful coding avoid any associated problems such as memory leaks or referencing a COM object that has been released.

The FlexCOM 2 classes now support the following interface for managing references to COM objects:

pvComObject – This is a variant property which returns an IDispatch pointer to the external COM object. Setting this property to the correct IDispatch* value connects a Visual DataFlex wrapper object to its corresponding external COM object and increments its reference count. Changing this property or setting it to NULL (using the NullComObject() function) automatically releases that reference. Destroying the Visual DataFlex wrapper object also automatically releases the reference to the COM object.

CreateComObject - This method replaces the old CreateDispatch method.

ReleaseComObject – This method replaces the old ReleaseDispatch method.

IsComObjectCreated – This Boolean function returns True if pvComObject is set to some valid IDispatch* value. It returns false if pvComObject is NULL or unassigned.

New Helper Functions

FlexCOM 2 introduces the following global helper functions for operating on Variant properties or variables that are used to store IDispatch* values:

NullComObject() – This function returns a NULL value of variant type IDispatch*. You can use this function to release a reference to a COM object that is stored in a variant property or variable, or to initialize a variant property or variable.

IsNullComObject(vSomeVariant) – This Boolean function returns True if the passed variable or property is of type variant IDispatch* and is set to NULL. It will return False if the passed value is a non-NULL IDispatch* value.

IsSameComObject(vSomeVariant1, vSomeVariant2) – This Boolean function returns True if the two parameters are of type Variant and point to the same COM object, otherwise it returns False.

Better Studio Integration

The process of importing COM objects into the Studio and generating wrapper classes is now completely integrated with the Studio. The Studio also has improved support for visual modeling ActiveX controls and modifying ActiveX default properties.

For more information refer to:

Importing COM Components

Using the Built-in ActiveX Properties Dialog

Support for Events with Pure Automation Components

Many COM objects support event handling, which is a type of callback method called by the COM object and executed within the Visual DataFlex program. Event methods are typically triggered by the control as part of, or to implement, a particular action, e.g. OnDocumentChange, OnClick.

In prior revisions these event methods were only supported with ActiveX controls, and not using pure Automation components. Because of this lack of support many COM objects could not be fully utilized. FlexCOM 2 now fully supports event methods with all types of COM objects, including pure Automation components.

Support for Passing Arguments By Reference

FlexCOM 2 now supports calling methods and events that pass arguments by reference. This technique is supported via the new unary ‘&’ (by reference) operator introduced in Visual DataFlex 8.3.

Many COM objects utilize a technique referred to as call by reference, which allows arguments to be modified by the callee and affect the actual arguments passed by the caller. This technique is often applied when there’s a need for multiple return values, or for in-out parameters which pass one value to the callee and are allowed to be modified and contain a different value when the execution returns to the caller. The latt7er is commonly used in COM event methods, as a means of allowing the client code to cancel or modify a certain action or default behavior. In which case the COM object might pass on a Boolean parameter bCancel, which the program may set to True in order to cancel the pending action.

For more information refer to: Passing Arguments by Reference.

Support for Embedded Document Objects

FlexCOM 2 now supports Document Embedding / ActiveX Documents, within your Visual DataFlex application. You can now embed Word documents and Excel spreadsheets, for example, directly in a Visual DataFlex View.

Embedding Document Objects using FlexCOM 2 is supported directly from within the Studio, via the familiar drag 'n' drop interface of the Controls Palette.

ActiveX Control Persistence Embedded in .exe File

ActiveX Control persistent state, as configured at design time in the Studio, is now compiled and embedded into the resulting Visual DataFlex program .exe file. This means that you no longer need to deploy and maintain those dreaded .ocp files. It’s all done automatically and maintained by the Studio and the compiler instead.

For more information refer to: Editing Properties.

Support for Error Handling via Visual DataFlex Standard Error Handlers

When an error occurs a standard Visual DataFlex error is triggered, which means that you can trap the error using a standard Visual DataFlex error handler, and optionally suppress the error at will.

In prior revisions, errors popped up a message box, which often could not be suppressed. And error conditions often could not be dealt with properly in your application. Because errors are now triggered and handled via standard Visual DataFlex error handlers, all error conditions can now be easily dealt with, and easily suppressed at will, using familiar Visual DataFlex error handling techniques.

For more information refer to Error Handling.

Support for Trapping Keystrokes via Visual DataFlex Standard On_Key

All keyboard input for ActiveX Controls and Embedded Document Objects are routed through the standard Visual DataFlex On_Key handler. This means that you can easily trap any keystroke and execute custom code for any COM object, just like any other Visual DataFlex control.

In prior revisions you could not easily trap keystrokes and at the same time let the control handle keyboard input that you were not interested in. But this is no longer a problem. You can now easily trap the keystrokes you’re interested in and let the control handle the rest. This removes any need for the old DFTakesFocus_State property.

For more information refer to Trapping Keystrokes.

Improvements in Class Generator Output

The class generator has been improved in several areas, both in speed and quality. It no longer generates unnecessary classes that have been marked by the COM library as obsolete/hidden and only available for compatibility. Thus resulting in sometimes-smaller packages.

It automatically generates code for referenced external definitions, which had to be generated separately in prior revisions. Thus eliminating the need to manually generate code for dependent libraries just to be able to compile the generated code for another library.

In the rare case when the class generator detects unconventional methods or data types, it also automatically disables the related methods, and inserts a comment describing the reason the method was disabled. Thus removing sources of known compile problems.

Support for Bindable Properties

FlexCOM 2 now supports COM Bindable Properties, a.k.a. Data Binding, which significantly improves and simplifies the use of data-aware ActiveX Controls.

The class generator automatically detects bindable properties and marks them as such. You can then easily request a binding to such properties, and receive notifications of any internal or external modifications to that property.

Data binding in ActiveX controls can be disabled by the use of the pbBindValue property.

For more information refer to: Data Binding.

Other Changes

The special keyword Nothing replaces OLE_VT_OPTIONAL when calling a method that supports optional parameters.

For information about migrating applications using the old FlexCOM to use the new FlexCOM 2 technology refer to: Migrating FlexCOM1 Applications To FlexCOM2.

See Also

Using COM in Applications


Importing COM Components

Before you can use a COM object in your application you must first import it into the Visual DataFlex development environment. Importing a COM object instructs the Studio to build all of the appropriate wrapper classes and definitions you need in order to use it. Importing COM Components is divided into three categories, and is accessed from the Studio menu File -> New -> Class.

  • Import ActiveX Control

  • Import Automation Library

  • Import Embedded Object

See Also

Using COM in Applications


COM Object Wrapper Classes

In Visual DataFlex you use a COM object by instantiating the appropriate wrapper class that was generated by the COM Import wizards. After you have imported a COM component, the Studio automatically opens the package containing all of the class code and other definitions it has generated.

You should be aware of the definitions inside this package as you will need to reference them as you begin programming. Use the following guideline to determine the naming used to expose the COM object's interface:

Class Name

As discussed in the section on Importing COM Components the wrapper classes have the same name as the COM objects that they represent but prefixed with "Com" (or "ComDb" for data aware ActiveX controls).

The wrapper class declaration can be found at the end of the generated package.

Wrapper Class Structure

Each wrapper class that is generated is constructed from three superclasses using multiple inheritance. The diagram below shows this structure….

cCom{wrapper}

This is the wrapper class that you will directly use in your application to access the COM component that you imported. It is generated when you import a COM component. For example the Calendar ActiveX control's wrapper class is generated as cComCalendar.

cComI{wrapper}

This superclass is also generated when you import a COM component. It provides wrappers for all of the component's interface elements (properties, procedures and functions). You can review this class definition to inspect the names, data types, etc. that appear in this interface. For example the Calendar ActiveX control's interface is generated in a class named cComICalendar.

cCom{wrapper}Events

This superclass is also generated when you import a COM component. It implements the COM component's event interface. For example the Calendar ActiveX control's event interface is generated in a class named cComCalendarEvents.

cCom{type}

The third superclass is one of the built-in Visual DataFlex COM classes. The type of COM component that you import determines which class is used. For example the Calendar ActiveX control's Visual DataFlex superclass would be either cComActiveXControl or cComDbActiveXControl, depending on whether you imported the control as data-aware or not.

ActiveX Controls

ActiveX control wrapper classes are derived from either cComActiveXControlcontrol or cComDbActiveXControl.Which class is used depends on whether you imported the control using the "data-aware" option or not (see Importing ActiveX Controls for more information).

Automation Libraries

Automation object wrapper classes are derived from cComAutomationObject.

Embedded Objects

Embedded object wrapper classes are derived from cComDocumentObject

Method Name

Each exposed interface member (property, procedure or function) will have the same name in the Visual DataFlex wrapper class as the documented name in the COM object, except that the prefix "Com" is added. e.g. In the Calendar ActiveX control, the Month property is exposed as ComMonth, and ShowDays is exposed as ComShowDays.

Events

Event methods are similarly exposed except that the prefix becomes "OnCom". E.g. in the Calendar ActiveX control the OnClick event is exposed as OnComClick.

Enumerated Constants

Many COM Objects define integer types based on a discrete set of integer values. These are exposed in the generated package with the same names as used in the COM object's interface except that they are prefixed with "OLE". For example in the Automation controller sample, the COM Automation Server object exposes an integer type called txShapeType, this is exposed in the wrapper class as OLETxShapeType.

These definitions are generated at the beginning of the package containing the wrapper class definitions.

See Also

Using COM in Applications


Using COM Components in the Studio's Visual Designer

Using COM Components in the Studio

  • Instantiating Controls and Embedded Documents

  • Editing Properties

  • ActiveX Properties Dialog

See Also

Using COM in Applications


Instantiating Controls and Embedded Documents

Importing COM Components in the Studio creates Visual DataFlex wrapper classes for each COM object class. The next step is to create object instances of these classes in your application.

For ActiveX controls and embedded document objects, this is done by dragging and dropping the control from the Studio's Class Palette onto your view in the visual designer. In other words, it is just like using any other Windows control like a Form or Grid.

Since Automation components are not visual objects, they not added to the Studio's Class Palette. This means you would not use them in the Studio's visual designer. For information about instantiating Automation objects refer to Programming with Automation Objects.

By default, imported ActiveX controls and Embedded Documents are placed on the Class Palette's "ActiveX"group. You can specify which group a COM component appears on when the component is imported.

The bitmaps used to represent each imported component in the Class Palette were extracted from the component's library when it was imported. If you created a data-aware control, a small "data-aware" image is superimposed.

See Also

Importing COM Components

Using COM Components in the Studio


Editing Properties

Each COM wrapper object that you instantiate in the Studio supports interfaces to two different sets of properties.

  1. Built-in Properties

  2. COM Properties

Built-in Properties

The built-in properties are internal properties defined by the Visual DataFlex COM classes. The data for these properties are stored in the Visual DataFlex wrapper object. These properties exist when the Visual DataFlex object exists and cease to exist when the Visual DataFlex object is destroyed. You can manipulate these properties in the visual designer's Object Properties dialog. In other words, these are regular Visual DataFlex properties.

As with all Visual DataFlex classes, you can find information on the wrapper object's built-in properties in the class reference manual.

COM Properties

COM properties do not belong to the Visual DataFlex COM wrapper object. The COM wrapper object only defines access methods to read or write each COM property. The data is stored externally in the actual COM object.

COM properties are not documented in the Class Reference manual because they will be different for each COM component that you import. Usually you would refer to specific documentation provided by the COM component's authors to learn how to use that object in your application.

The names of the access methods for the COM component's properties (and methods) are defined in the package file where the Visual DataFlex wrapper class is defined.

You would initialize a COM Object's properties in the wrapper object's OnCreate event handler.

See Also

COM Object Wrapper Classes

COM Object Life Span

Setting Properties with the OnCreate Event Handler

Using COM Components in the Studio


ActiveX Properties Dialog

Many ActiveX controls define a built-in dialog for making property settings at design-time. This dialog is available in the visual designer by selecting "ActiveX Properties…" from the control's context menu.

The dialog that is displayed belongs to the ActiveX control and will vary for each different type of control. The example below shows the Calendar control's design-time properties dialog.

ActiveX Property Persistence

When you set ActiveX properties in the Studio the states of each property you set is serialized into a base64 format and embedded in the source code within a Embed_ActiveX_Resource / End_Embed_ActiveX_Resource section in the object declaration. When you compile an application, the ActiveX controls' property values, are compiled into the application .exe.

See Also

Using COM Components in the Studio


Programming with COM – General Topics

  • COM Object Life Span

  • Using IsComObjectCreated

  • Setting Properties with the OnCreate Event Handler

  • Variant Data Types

  • Using pvComObject

  • Passing Arguments By Reference

  • Using Optional Parameters

  • Error Handling

  • Trapping Keystrokes

See Also

Using COM in Applications


COM Object Life Span

An important point about programming with COM objects is that the existence of a COM object does not directly coincide with the scope of its Visual DataFlex wrapper object. The COM object may get created when the Visual DataFlex wrapper object is created but this should not be relied upon. For example, ActiveX controls are usually created when the control is paged (i.e. when it is activated and added to the Visual DataFlex focus tree). In such a case the ActiveX is destroyed again when the control is deactivated. This will happen automatically and as often as the Visual DataFlex wrapper object is activated and deactivated. All the while, the Visual DataFlex wrapper object that you use to communicate with the COM object is still in scope.

Another example is with Automation objects. Most Automation objects must be manually created and released by sending CreateComObject or AttachActiveObject and ReleaseComObject to the Visual DataFlex wrapper object. Once again this cycle could occur multiple times, while the Visual DataFlex wrapper object still remains in scope (look to the Simple Automation COM sample to see this behavior in action).

Attempting to access a COM object's interface while it is not active will produce runtime errors. It is important to protect your code against this.

The Visual DataFlex COM classes provide several ways to ensure that you access the COM Object's interface only when you should:

  • IsComObjectCreated : Use this function to test whether the COM object is currently active.

  • OnCreate: Write a handler for this event to set properties each time the COM object is created.

  • peAutoCreate: Use this property to manipulate the create / release behavior of a COM object with respect to its Visual DataFlex wrapper object.

See Also

Using IsComObjectCreated,

Setting Properties with the OnCreate Event Handler,

Manipulating COM Object Life Span with peAutoCreate.

Programming with COM – General Topics


Using IsComObjectCreated

You can test to see if a COM object exists by getting the value of IsComObjectCreated from its Visual DataFlex wrapper object. This function returns True, if the COM object has been created indicating that it is safe to access its interface.

The following example from the Simple Automation Controller sample demonstrates the use of IsComObjectCreated…

Object oText_Btn is a Button

Set Location to 10 10

Procedure OnClick

// Get the text from the COM Automation Server and display it.

String sValue

Boolean bIsComObjectCreated

// Test to see If we are already connected. If we are connected,

// then the IsComObjectCreated Returns True.

Get IsComObjectCreated of oAutomationTest to bIsComObjectCreated

If (bIsComObjectCreated) Begin

Get ComEditText of oAutomationTest to sValue

Set Value of oText_frm to sValue

End

End_Procedure // OnClick

End_Object

See Also

Programming with COM – General Topics


Setting Properties with the OnCreate Event Handler

The OnCreate event is always sent right after a COM object is created. You should write an OnCreate event handler to initialize the properties of a COM object when it is created.

It is important to understand the difference between when a COM object is created and when its Visual DataFlex wrapper object is created. This is discussed in detail in the topic COM Object Life Span.

It is always safe to set the properties of a COM object inside of the OnCreate event handler. It is also good practice to always Forward Send OnCreate when augmenting this event handler.

The following example demonstrates using OnCreate to initialize the default URL of the cComWebBrowser ActiveX control:

Procedure OnCreate

Forward Send OnCreate

Send ComNavigate "www.dataaccess.com" 0 0 0 0
End_Procedure

See Also

Manipulating COM Object Life Span with peAutoCreate.

Programming with COM – General Topics


Variant Data Types

The Variant type is capable of storing values that change type dynamically. Variables of any other type (Integer, String, Date, etc) are statically bound to that type but a variable of the Variant type can assume values of differing types at runtime.

You would use a Variant type in situations where the actual type of your data is unknown at compile-time. This is a common situation when writing applications that use COM objects.

Variant is a native type and therefore you can declare a Variant as follows:

Variant vMyData

The above will declare a variable of type Variant called vMyData.

The Variant type can be used to declare global or local variables, properties, function return types, and procedure or function parameters.

Variants have the following characteristics:

  • Variants can contain Integer, Number, Real, Boolean, Date, Time, TimeSpan, DateTime, String, Handle and IDispatch pointer values.

  • The special Variant value VT_EMPTY is used to indicate that a Variant has not yet been assigned any value.

  • Variants can be combined with other Variants and Integer, String, Boolean etc. values in an expression. The Visual DataFlex expression evaluator will automatically perform the necessary type conversions.

  • When a Variant contains a reference to a COM Object (IDispatch*), then it automatically performs reference counting for the referenced object.

The use of Variants is very straightforward. The following example demonstrates moving a Variant value into a String variable:

Variant vMyData

String sMyString

Move vMyData to sMyString

The variable sMyString will now hold the same value as vMyData converted to the String type.

Similarly, assigning the contents of another variable into a Variant variable is also very straightforward:

Variant vMyData

String sMyString

Move sMyString to vMyData

The following is an example of invoking a COM method expecting a Variant parameter:

Variant vMyData

Move 01/01/2000 to vMyData

Set ComValue of oCalendar1 to vMyData

Since Variant is a native data type, implicit conversion will automatically be performed if necessary. Therefore you can also pass any other type and it will automatically be converted to the expected type:

Set ComValue of oCalendar1 to 01/01/2000

In the above code the date literal will be implicitly converted to Variant. This is a lot simpler than the code needed in prior revisions of FlexCOM to accomplish the same task.

Using Variants to Store IDispatch* Values

It is important that COM object references (IDispatch* values) are controlled only by Variant types in order to maintain automatic reference counting of COM objects. Changing a Variant variable that contains a COM object reference or setting it to NULL (using the NullComObject function) automatically releases any reference to the COM object pointed to by the old value.

When Variant variables go out of scope they delete their contents automatically. In the case of a Variant containing an IDispatch*, the referenced COM object is released. Thus the reference count of the COM object is automatically decremented when a variant goes out of scope.

Variant variables containing IDispatch* values can not be converted to any other data type. The Visual DataFlex runtime protects against this and will raise an error ("Illegal type conversion"). Such a conversion is meaningless and, if allowed, could subvert Visual DataFlex's reference counting mechanism for COM objects.

The Visual DataFlex runtime also protects against pointer arithmetic and comparison expressions for variant variables containing an IDispatch*. Doing so would also raise a runtime error. The helper functions IsNullComObject and IsSameComObject can be used to perform any operations that you would normally require on variants containing IDispatch* values.

See Also

Using pvComObject.

Programming with COM – General Topics


Using pvComObject

pvComObject is a variant property which returns a reference to the external COM object in the form of an IDispatch pointer.

Setting this property to the correct IDispatch* value connects a Visual DataFlex wrapper object to its corresponding external COM object and increments the COM object's reference count.

Below is an example of creating a cComStdFont object and attaching it to an existing OLE font object using the pvComObject property. The code sample assumes the existence of the Calendar ActiveX control (oCalendar1) from the COM sample application.

// create an instance of OLE Font wrapper class...

Object oFont is a cComStdFont

Procedure SetFontUnderline Variant vFont Boolean bMode

// bind our wrapper Object to the passed IDispatch*

Set pvComObject of oFont to vFont

// now we can change the title font...

Set ComUnderline of oFont To bMode

End_Procedure

End_Object

// checkbox to underline the calendar's title

Object oUnderlineTitle_chkbx is a Checkbox

Set Location to 10 10

Procedure OnChange

Variant vFont

Boolean bIsComObjectCreated

// test If the calendar COM Object is active.

Get IsComObjectCreated of oCalendar1 to bIsComObjectCreated

If (bIsComObjectCreated) Begin

// Get an IDispatch* to the calendar's Title font

Get ComTitleFont of oCalendar1 to vFont

// change the font by attaching it to our generic COM

// font Object..

Send SetFontUnderline vFont (Checked_State(Self))

End

End_Procedure

End_Object

// checkbox to underline the calendar's day text

Object oUnderlineDays_chkbx is a Checkbox

Set Location to 10 20

Procedure OnChange

Variant vFont

Boolean bIsComObjectCreated

Get IsComObjectCreated of oCalendar1 to bIsComObjectCreated

If (bIsComObjectCreated) Begin

// Get an IDispatch* to the calendar's Day font

Get ComDayFont of oCalendar1 to vFont

// change the font by attaching it to our generic COM

// font Object..

Send SetFontUnderline vFont (Checked_State(Self))

End

End_Procedure

End_Object

Notice that in the above example, the object oFont is dynamically connected to font Automation objects by setting the pvComObject property. This is done by passing the font's IDispatch* value to the SetFontUnderline method where it is used to set pvComObject.

Changing the pvComObject property or setting it to NULL (using the NullComObject function) automatically releases any reference to the IDispatch pointed to by the old property value. Destroying the Visual DataFlex wrapper object also automatically releases the reference to the COM object.

This property is automatically assigned and released when sending CreateComObject and ReleaseComObject.

Runtime Protection of COM Object References

The Visual DataFlex runtime protects against potential corruption of any IDispatch* variant value. This protection is required for Visual DataFlex's COM object reference counting.

Once any Variant property or variable is assigned and IDispatch* value, it cannot be used to store a value of any other type. Doing so would raise an "Illegal type conversion" error.

Performing pointer arithmetic or comparison expressions for variant variables containing an IDispatch* also raises a runtime error.

The helper functions IsNullComObject and IsSameComObject can be used to perform any operations that you would normally require on variants containing IDispatch* values.

See Also

Variant Data Types

Programming with COM – General Topics


Passing Arguments By Reference

The parameters of called procedure or function methods can be passed by reference, as opposed to being passed by value.

By Value Parameters

When a parameter is passed by value it acts like a local variable in the called procedure or function. Changes made to the value parameter inside method the do not affect the value of the passed variable outside the method. I.e. the called method is only passed the value of the passed variable or expression, not the variable itself. This is the default behavior of procedure or method parameters in Visual DataFlex.

By Reference Parameters

When a parameter is passed by reference, the called procedure or function has direct read/write access to the passed variable. Changes made to the reference parameter inside the method are reflected in the passed variable outside the method.

This technique is often applied when there’s a need for multiple return values, or for in-out parameters which pass one value to the called method and are allowed to be modified and contain a different value when the execution returns to the caller. The latter is commonly used in COM event methods, as a means of allowing the client code to cancel or modify a certain action or default behavior. In which case the COM object might pass on a Boolean parameter bCancel, which the program may set to True in order to cancel the pending action.

'&' (by reference) Operator

To pass an argument by reference rather than by value, you must prefix the passed parameter with the unary & operator. The prefix and parameter must be enclosed in an expression (that is, the & operator and variable name must be enclosed within parentheses). The passed parameter must also be a variable. E.g. (&MyVariable) will pass on MyVariable by reference rather than by value. This enables the called method to modify the contents of MyVariable directly, so that when the call returns, MyVariable may contain a different value.

The following is an example of passing an argument by reference:

Procedure GetData String ByRef sData

Move "Here is some data" to sData

End_Procedure

String sMyString

Send GetData (&sMyString)

// The value of sMyString is now "Here is some data"

By Reference Parameters

If a parameter is passed by reference, then the called method must specify the parameter with the ByRef keyword.

The example below shows two procedure declarations. One declares a byref parameter and one declares a regular by-value parameter. This is followed by code that attempts to call each method, passing the parameters by reference.

Procedure GetGood String ByRef sVar

Move "Changed" to sVar

End_Procedure

Procedure GetBad String sVar

Move "I didn't Change" to sVar

End_Procedure

String sMyString

Move "Old value" to sMyString

Send GetGood sMyString // passed by value, is ok

Send GetGood (&sMyString) // passed by ref - will change value

Send GetBad sMyString // passed by value, is ok

Send GetBad (&sMyString) // passed by ref, but this will Not work

If you ran the above code you would see that the procedure with the string parameter declared by-value cannot operate on the passed variable by reference.

See Also

Programming with COM – General Topics


Using Optional Parameters

COM supports the notion of optional parameters. Optional parameters, unless specified, are interpreted with some default value according to the COM component. However, Visual DataFlex's FlexCOM technology requires you to specify all parameters when declaring and calling a method.

Visual DataFlex provides the special keyword nothing to use as a place-filler when you wish to call a method but not specify a value for one or more of its optional parameters.

The following example demonstrates how to call a procedure and omit a value for one of its optional parameters:

Procedure MyProcedure Integer param1 Variant anOptionalParam

End_Procedure

Send MyProcedure 1234 Nothing

Notes

The keyword nothing replaces the old OLE_VT_OPTIONAL constant that was used in prior revisions of FlexCOM, which can not be used with FlexCOM 2 components.

See Also

Programming with COM – General Topics


COM Error Handling

Error Handling

When an error occurs in a COM object e.g. a method invocation failed, the control may throw a COM exception and return an error code. When this happens, FlexCOM raises a DataFlex error. This allows you to trap the error using a standard Visual DataFlex error handler, and handle it in an appropriate way or optionally suppress the error.

The following example demonstrates how to write Visual DataFlex code to handle errors in a currency converter COM object (cComEDConverter). The function IsExchangeRateAvailable tests if the passed currency symbol is available by executing ComGetExchangeRate method and trapping any errors raised and returned by the COM method.

Object oEDConverter is a cComEDConverter

Property Boolean pbErrorFree True

Procedure Error_Report Integer iErrNum ;

Integer iErrLine String sErrMsg

// Internal error handler to suppress error messages and Set

// an internal flag.

Set pbErrorFree To False

End_Procedure

Function IsExchangeRateAvailable String sCurrencySymbol ;

Returns Boolean

// Tests If the exchange rate is available. This works by

// trapping the returned error code from ComGetExchangeRate.

Integer hoOldErrorId

Boolean bErrFree

Real rExchangeRate

// Re-rout DataFlex errors to internal Error_Report Procedure.

Move Error_Object_ID To hoOldErrorId

Move Self To Error_Object_ID

// Send ComGetExchangeRate and test for any returned errors.

Set pbErrorFree To True

Get ComGetExchangeRate sCurrencySymbol To rExchangeRate

Get pbErrorFree To bErrFree

// reset the Visual DataFlex error handler.

Move hoOldErrorId To Error_Object_ID

Function_Return bErrFree

End_Function // IsExchangeRateAvailable

End_Object

Note that the techniques used here are identical to the error handling techniques you would use in any DataFlex code.

See Also

Error_Report

Error_Object_ID

Programming with COM – General Topics


Trapping Keystrokes

All keyboard input for ActiveX Controls and Embedded Document Objects are routed through the standard Visual DataFlex On_Key handler. This means that you can easily trap any keystroke and execute custom code for any COM object, just like any other Visual DataFlex control.

The example below demonstrates how to trap keyboard input in the Calendar ActiveX control:

Object oCalendar1 is a cComCalendar

Set Size to 104 223

Set Location to 3 5

On_Key Key_Ctrl+Key_A Send ComAboutBox

End_Object // oCalendar1

This example traps the Ctrl+A key sequence inside the calendar control to display the control's internal About Box dialog.

See Also

Programming with COM – General Topics


Programming with ActiveX Controls

Data Binding

The Visual DataFlex Value Property and the ActiveX Control's Value

The Visual DataFlex class library makes it easy to associate a single piece of data with a control in a concise way. For example, the Form class and the dbForm class. These classes associate the value in the windows control with a Value property. The Form class contains two important properties that allow us to set and get the state of this data - get/set Value and get/set Item_Changed_State.

Using this interface allows you to work with this data even when the windows control is not yet created (not yet activated) and it provides a consistent way to communicate with single value data objects.

Often you will want to associate an ActiveX control with a single data value in the same way. For example the calendar control is a single value data control containing a single date as its data.

Binding using ControlValue

As a Visual DataFlex programmer, you want to be able to synchronize the Visual DataFlex Value property with an ActiveX control's "value" property. To do this you'll need to find the messages in the control that get and set its data value and "bind" these properties to the Visual DataFlex Value property of its wrapper class. This is accomplished by creating the methods Get ControlValue and Set ControlValue. These methods should be written to Get and Set the ActiveX control's real value property.

The name of this real value property is defined by the ActiveX control and could be any name. For example, the Calendar control's real value property happens to be called ComValue so the code that must be created is:

Function ControlValue Returns String
Function_Return (ComValue(Self))
End_Function

Procedure Set ControlValue String sVal
Set ComValue to sVal
End_Procedure

Automatic Binding

Many ActiveX controls publish information about their bindable property. In these cases, the FlexCOM class generator will use this information to create the necessary ControlValue interface automatically. When this occurs, your ActiveX control is automatically bindable allowing you to use the Visual DataFlex get/set Value syntax to work with this object, i.e. you do not have to write your own get/set ControlValue methods.

You can look inside the package generated when you import an ActiveX control for function ControlValue and Procedure Set ControlValue to check if the binding has been done automatically or not.

Using the wrapper class's Value interface also means you do not have to worry about whether the COM control is actually created yet. i.e. you can set the Value property of an ActiveX wrapper object before that object connects with the underlying ActiveX control. The Visual DataFlex wrapper class handles all of this for you. This can be an important feature because many ActiveX controls will automatically release themselves while they are not paged.

Data Aware Binding

If an ActiveX control is bindable, you can import the class and use it as a data aware entry object (a DEO) without any extra coding. This ability let's you automatically bind an ActiveX control with a field from a Data Dictionary Object. This is a powerful feature. As an example you can see how data aware binding is used in the cComDbCalendar class in the COM Samples workspace…

Object oOrderCalendar is a cComDbCalendar

Entry_Item Orderhea.Order_date

Set Size to 140 140

Set Location to 1 1

End_Object // oOrderCalendar

There are two points to note about this example:

  1. The Entry_Item command is all that is required to bind this control to the order_date field of the serving data dictionary object.

  2. All of this code is maintained automatically by the Studio using the Object Properties dialog. This is just like any other data aware Visual DataFlex control like dbForm or dbEdit.

If a class is not automatically bindable, you will either need to create the binding yourself, or, more likely, binding just doesn't make sense for that class (for example, a Web-Browser ActiveX control has no bindable property). You would use these controls in a different manner.

Situations may arise where a control is bindable but you do not want to use the control in that manner. In this case you can just ignore the binding mechanism and everything should work perfectly - you just would not use the get/set Value interface. If for some reason, the class binding behavior gets in your way you can disable all binding and synchronization by setting the property pbBindValue to False (by default it is True). Setting pbBindValue to False, severs any connection between the get/set Value message and any of the ActiveX control's interfaces. You will probably never need to change this property.

Aggregate ActiveX Controls

Many ActiveX controls are outer containers for a whole structure of COM objects. This is known in COM as aggregation. The inner objects of an aggregate ActiveX control will be implemented as Automation objects or, in other cases collections of Automation objects.

Refer to Aggregate Objects in the Programming with Automation Objects section for details on accessing aggregate objects and aggregate collections.

See Also

Using COM in Applications


Programming with Automation Objects

  • Instantiating Automation Controller Objects

  • Attaching to the Automation Server

  • Aggregate Objects

See Also

Using COM in Applications


Instantiating Automation Controller Objects

Unlike ActiveX controls or Embedded Document objects, Automation Controller objects are instantiated entirely in code. This is simply a matter of writing an object declaration block for the Automation object's wrapper class.

The example below shows the object declaration for the Simple Automation Controller COM sample:

Object oAutomationTest is a cComAutoTest

End_Object

Additionally, you can write an OnCreate event handler inside the object declaration to initialize the COM object's properties when it is attached with its Automation Server.

The COM object is not ready to use in your Visual DataFlex application until it has been attached to its Automation server.

See Also

Programming with Automation Objects


Attaching to the Automation Server

Before you can use an Automation object's interface it must be attached to its Automation Server. Sending CreateComObject or AttachActiveObject does this.

CreateComObject

This method will launch a new instance of the Automation Server application and attach the Automation controller to it. See also CreateComObject.

AttachActiveObject

This method will search for any current running instances of the Automation Server and attach to it if it finds one. If it does not find one then it does nothing and returns False.

ReleaseComObject

When your Visual DataFlex application has finished with the Automation Server it should be released. This can be done explicitly by sending ReleaseComObject. The Automation Server will be released automatically when the controller object is destroyed.

The example below demonstrates using CreateComObject, AttachActiveObject and ReleaseComObject to connect the simple Automation controller with its server.

Object oAutomationTest is a cComAutoTest

End_Object

Object oConnect_btn is a Button

Set Location to 10 10

Set Label To "Connect"

Procedure OnClick

// Click this button to Attach to the Simple Automation Server

Boolean bIsComObjectCreated

// test If the Visual DataFlex Wrapper is already connected

Get IsComObjectCreated of oAutomationTest To bIsComObjectCreated

If (Not(bIsComObjectCreated)) Begin

// now try to attach to a running instance

Get AttachActiveObject of oAutomationTest To bIsComObjectCreated

If (Not(bIsComObjectCreated)) Begin

// If we got here then we have to create our own instance

Send CreateComObject of oAutomationTest

End

End

End_Procedure // OnClick

End_Object

Object oDisconnect_btn is a Button

Set Location to 10 10

Set Label to "Disconnect"

Procedure OnClick

// Click this button to release the Simple Automation Server

Boolean bIsComObjectCreated

// test If the Visual DataFlex Wrapper is already connected

Get IsComObjectCreated of oAutomationTest To bIsComObjectCreated

If (bIsComObjectCreated) Begin

// now release the automation server

Send ReleaseComObject of oAutomationTest

End

End_Procedure // OnClick

End_Object

Note how the IsComObjectCreated method is used to test whether the Automation controller is connected to an Automation server. This method will return True after AttachActiveObject or CreateComObject have been successfully executed. It will return False after ReleaseComObject has been executed.

See Also

Programming with Automation Objects


Aggregate Objects

Many COM objects are actually composed of a structure of COM objects. In COM this is known as aggregation. Aggregation means that the containing (outer) object creates the contained (inner) object(s) as part of its creation process, and the interfaces of the inner object are exposed by the outer.

A good example of aggregation is the MS Word Automation Library. The diagram below shows the simplified structure of objects for the MS Word 8 Automation library.

In this structure the application object represents the MS Word 8 application; the documents collection represents the collection of Word documents currently open in the application; the document objects correspond to each open document; and the range objects represent the text contained within each document.

When you import the MS Word 8 Automation library into the Studio, the following corresponding classes are generated… cComApplication; cComDocuments; cComDocument; and cComRange.

To access Word Automation in your Visual DataFlex application you must create an instance of cComApplication and attach it to a Word Automation server as in the following code sample:

// Create a Word Automation wrapper Object

Object oWordApplication is a cComApplication

End_Object

// Launch the Word Automation Server application and attach

// the wrapper Object to it.

Send CreateComObject of oWordApplication

Now your program is ready to interact with a MS Word Automation Server. However, in itself, does not give you access to a word document. The interface for working with a document is contained within the cComDocument class, which is, in turn, maintained by a document's collection via the cComDocuments class.

The Word Application object does provide a COM object reference to its documents collection via its ComDocuments property. Thus we can attach to the documents collection with the following code:

// create a Documents collection wrapper for the word application

Object oDocuments is a cComDocuments

End_Object

Function AttachDocumentsCollection Returns Boolean

// Attach the documents collection wrapper to the COM

// documents collection Object inside oWordApplication.

Variant vDocuments

Boolean bSuccess

// we can't access oWordApplication's COM interface until

// it is attached to a Word Automation Server.

Get IsComObjectCreated of oWordApplication To bSuccess

If (bSuccess) Begin

// Get the documents collection COM Object reference from

// oWordApplication.

Get ComDocuments of oWordApplication to vDocuments

// now attach oDocuments to this COM Object.

Set pvComObject of oDocuments to vDocuments

If (IsNullComObject(vDocuments)) Move False To bSuccess

End

Function_Return bSuccess

End_Function

Now that we have access to the Documents collection, we can use its interface to open or create Word documents. We need at least one Document wrapper object in our application to be able to interact with the COM document objects in our collection.

Here is how to open a new Word document and attach its corresponding COM object to our Visual DataFlex document wrapper.

Object oDocument is a cComDocument

End_Object

Function NewDocument Returns Boolean

// Open a new document in the documents collection and attach

// it to our global Word document wrapper.

Variant vDocument

Boolean bSuccess

// we can't access oDocuments COM interface until it is attached

// to the COM documents collection Object inside oWordApplication

Get IsComObjectCreated of oDocuments To bSuccess

If (bSuccess) Begin

// Use the documents collection to create a new Word document

Get ComAdd of oDocuments to vDocument

// now attach oDocuments to this COM Object.

Set pvComObject of oDocument to vDocument

If (IsNullComObject(vDocument)) Move False To bSuccess

End

Function_Return bSuccess

End_Function

Aggregation can also be found in ActiveX controls and Embedded Document objects. In this case the objects contained within the ActiveX are instantiated as Automation objects. You would use the techniques described above to gain programmatic access to these objects and their interfaces.

See Also

Programming with Automation Objects


Programming with Embedded Objects

  • Introduction

  • Creating Embedded Documents

  • Extended Embedded Objects

  • Case Study: Building an MS Word Container Application

See Also

Using COM in Applications


Introductions To Programming with Embedded Objects

Introduction

A chunk of data that can be shared between applications is referred to as an OLE object. The data can be shared by linking or it can be embedded. Visual DataFlex supports embedded objects.

Embedded Objects and Compound Documents

Embedded objects can be considered individual pieces of a compound document. For example; embedded objects provide the mechanism to combine spreadsheets and text documents in a single application. But instead of creating a dedicated application for this purpose, (a hybrid spreadsheet and word processor) you need only build an application that can 'host' compound documents. The applications that host compound documents delegate responsibility for each embedded object to the application which created them.

The applications that are responsible for handling documents embedded within another application's documents are called OLE Servers. Applications that host embedded documents are called OLE Containers. Visual DataFlex provides the capability for creating OLE Container applications.

Object Linking and Embedding

OLE stands for Object Linking and Embedding. Linking is simply embedding a link to another document. Embedding is embedding an actual document within another document. Embedding a spreadsheet within a Word document means that the spreadsheet is physically embedded in the Word document file. The saved document is then referred to as a compound document, since it contains both the Word document itself, and the embedded spreadsheet.

Since a Visual DataFlex OLE container application is not a document-type application (like MS Word or Excel), an embedded document is not really saved within a host document. This is because with Visual DataFlex there really is no host document. Instead each embedded document is stored in a individual in-memory compound document.

OLE Server Responsibilities

With embedded documents we are dealing directly with documents via an object-oriented interface. The OLE server application that created an embedded document is responsible for rendering the object, and any possible editing. The host application does not need to know anything about the embedded document, it just needs to tell it to draw itself, and possibly tell it to enter an in-place editing mode.

So, embedded documents are documents which expose two important features to the host: the ability to render themselves on any particular device, and the ability to enter either in-place or external editing. These are the only two features that are required in order to conform to document embedding.

Since we are dealing directly with a document, rather than, for example, a control containing a document, there is no notion of document loading. You can't load a document because a document object is a document. When instantiating an embedded object in your application you must either create a brand new empty document, or create a new document that is a copy of another document. See Creating Embedded Documents.

There's no standard interface for saving the document back to the file it was based on. Since you're not editing a file, you can't just easily save the modified document. If the document object supports Automation, (e.g. MS Word), it's likely that it will expose a Save function through its Automation interface that you can use for this purpose. See Extended Embedded Objects.

See Also

Programming with Embedded Objects


Creating Embedded Documents

In Visual DataFlex you can create a new embedded document object, using CreateComObject, or you can create a document based on an existing file, using CreateComObjectFromFile.

When you use CreateComObject, a new in-memory compound document is created to store the embedded document. When you use CreateComObjectFromFile, a new in-memory compound document is created and initialized with the contents of the specified file. It is important to remember that with CreateComObjectFromFile you are editing a copy of the document read from file, not the actual file itself.

Any embedded document that your wrapper object may be referencing at the time of creating a new document is discarded without saving any changes.

See Also

Case Study: Building a MS Word Container Application

Programming with Embedded Objects


Extended Embedded Objects

Some OLE server applications expose additional functionality through COM Automation. Word, for example, exposes several Automation classes, e.g. Application and Document. The Document class is the same class that represents an embedded Word document. So whenever you create an embedded Word document, you're instantiating this Document class. This means that if you use an embedded Word document you may also use its Automation interface.

While embedded documents themselves are only required to expose the two features of rendering themselves and possibly in-place editing, they may expose more features through Automation interfaces. This explains why certain embedded documents do not expose any user-defined methods or properties, while others do.

See Also

Aggregate Objects

Case Study: Building a MS Word Container Application

Programming with Embedded Objects


Case Study: Building an MS Word Container Application

This section describes how you can build a simple OLE container application for MS Word 8 documents. The purpose of this case study is to demonstrate basic techniques for embedding document objects in Visual DataFlex applications and controlling and interacting with these documents.

This example assumes that you are already familiar with Visual DataFlex and using the Studio. It also assumes that you have MS Word 8 installed. Different versions of MS Word may operate in different ways and may use a different interface.

Getting Started

  • Start the Visual DataFlex Studio and create a new Workspace "FlexCom Embedded Word Documents"

  • Import the "Microsoft Word Document" embedded object control into your new workspace. Import the component onto the ActiveX page of the Class Palette (this is the default). See Importing Embedded Objects.

  • Create a new Windows Project and a New Data Entry View. Name the View object "oWord_vw". Save each of these components, and make sure the oWord_vw view is added to the application.

Creating the Embedded Document Wrapper Object

  • Open the class palette and drop a cComEmbeddedWordDocument object onto your view. This control was added to the palette when you imported the Word Document component into your workspace.

  • Name the object oWordDocument. Align the object and set the peAnchors property to anAll.

Creating the COM Document Object

Here we will add functionality to either create a new blank Word document, or to create a Word document by copying an existing word file.

  • Add two buttons to the top of your View. Name them oNew_btn and oOpen_btn.

  • Add an Open Dialog control. Name it oOpen_dlg.

  • Set the Filter_String property of oOpen_dlg to "Document Files|*.doc|All Files|*.*".

  • Add the following code to oNew_btn's OnClick event handler…

Procedure OnClick

Send CreateComObject of oWordDocument

End_Procedure

  • Add the following code to oOpen_btn's OnClick event handler…

Procedure OnClick

String sFileName

// Use the open file dialog to Get the filename we

// wish to open.

If (Show_Dialog(oOpen_dlg)) Begin

Get File_Name of oOpen_dlg To sFileName

Send CreateComObjectFromFile of oWordDocument sFileName

End

End_Procedure

Adding More Functionality

Now we will add the ability to save and close the document.

  • Add two more buttons to the top of the oWord_vw view. Name the buttons oClose_btn and oSave_btn.

  • Add a Save As Dialog control. Name it oSave_dlg.

  • Set the Filter_String property of oSave_dlg to "Document Files|*.doc".

  • Add the following code to oClose_btn…

Procedure OnClick

Boolean bIsCreated

// Only close a document If we are attached to one.

Get IsComObjectCreated of oWordDocument To bIsCreated

If (bIsCreated) Begin

// Close the document without saving changes.

Send ReleaseComObject of oWordDocument

End

End_Procedure // OnClick

  • Add the following code to oSave_btn…

Procedure OnClick

String sFileName

// Use the open file dialog to Get the filename we

// wish to open.

If (Show_Dialog(oSave_dlg)) Begin

Get File_Name of oSave_dlg To sFileName

Send ComSaveAs of oWordDocument (sFileName) ;

Nothing Nothing Nothing Nothing Nothing ;

Nothing Nothing Nothing Nothing Nothing

End

End_Procedure // OnClick

Note that we are sending Word's Automation interface method ComSaveAs to save the document to file. The only parameter we are passing to this method is the file name. The remaining 10 parameters are optional so we are passing the "Nothing" keyword as a place filler for each of them.

Conclusion

Compile and run your application. You now have an OLE Container application for embedded MS Word documents. Your application can create new documents, copy a document, edit, close and save.

The screenshot below shows the running application editing an embedded word document:

There are a few things to note about using this application:

  • The embedded word document will not fully draw itself until you click on the object to give it the focus. When the document looses the focus the view of the document reverts to being not fully drawn.

  • Clicking New, Open or Close will destroy any embedded document that the application is currently editing. Remember that an embedded document is entirely contained in the memory space of the container application. Even if you used CreateComObjectFromFile. The COM object therefore will not delete any disk file when destroying an embedded object.

  • You could use the embedded object's Automation Interface to add more functionality, like save prompting and protection against loosing edited changes.

See Also

Programming with Embedded Objects


FlexCOM Helper Functions

FlexCOM 2 introduces the following global helper functions for operating on Variant properties or variables that are used to store IDispatch* values:

  • ComActiveObject

  • NullComObject

  • IsNullComObject

  • IsSameComObject

See Also

Using COM in Applications


Advanced Techniques

Manipulating COM Object Life Span with peAutoCreate

COM Object instantiation can be manipulated via the peAutoCreate property.

For Automation objects the default value for this property is acNoAutoCreate. This means that you must manually instantiate the COM object using CreateComObject. You would normally not change this default.

For ActiveX controls the default peAutoCreate value is acDeferredAutoCreate. This causes the COM object to automatically instantiate when it is paged (during Activate), and to be automatically destroyed when it is unpaged (during Deactivate).

For ActiveX controls you also have the option of setting peAutoCreate to acAutoCreate. This automatically instantiates the COM object when its Visual DataFlex COM wrapper object is created. This means that the COM object will now exist and retain its state even when it’s not paged, just like any other Visual DataFlex control. This might seem like the most desirable setting for all ActiveX controls but you need to be careful when using the acAutoCreate setting. Certain ActiveX controls exhibit problems when invoking methods and setting properties while they're not displayed. For that reason the setting of acDeferredAutoCreate is considered safe in all cases. If you want your ActiveX control to retain its state while it is not visible, then you can use the acAutoCreate setting but it will be up to you to ensure that the control will operate without errors.

Some ActiveX controls are not visible by design. In these cases it is better for the COM object to be created at the same time as its Visual DataFlex wrapper. This is because controls that are invisible, are controls that won't have any problems with setting properties when they're not displayed (they will never be displayed). Also, controls that are invisible are also controls that you are likely to want to use as soon as the Visual DataFlex wrapper object has been created. The FlexCOM class generator has been designed to take this into consideration. It now detects whether the object is invisible at runtime, and then sets peAutoCreate to acAutoCreate in case it's invisible at runtime, otherwise it sets it to acDeferredAutoCreate.

See Also

COM Object Life Span.

Using COM in Applications


Common Problems

  • Modifying Wrapper Class Packages

  • Naming Clashes

  • Class Definition Clashes

See Also

Using COM in Applications


Modifying Wrapper Class Packages

The packages that are generated when you import a COM component in the Studio should ideally be left unedited.

If you need to append or modify a generated COM wrapper class you should try to do this by creating a subclass in a separate package.

Leaving the generated packages untouched means that you can use the Studio to re-generate the package at any time without worrying that your modifications will be overwritten and lost.

The main reasons that you would want to re-generate the packages would be:

  • If the Visual DataFlex COM classes are modified.

  • If the Visual DataFlex COM class generator is modified (this might include enhancements and bug fixes).

  • If the COM control that you are using is modified.

Notes

This is not always possible to use the generated packages without modification. For example, if you have generated a data aware and non-data aware versions of the same ActiveX control that you wish to compile into the same application, then you will need to modify one or both packages (this is covered in the COM Calendar control sample application).

See Also

Common Problems


Naming Clashes

The COM object's type library determines the class names that are assigned by the class generator when you import a COM object. Unfortunately there is always the possibility that two different COM objects might define the same class name.

If you try to import two such COM objects and use them in one application, you will get a class-name clash, i.e. two class definitions with the same class name.

A common example of this problem is when you import MSWord and MSExcel Automation libraries. Both libraries would generate cComApplication, cComDocument, cComWindows, cComRecentFiles, cComFont, etc. classes.

The recommended way to deal with this problem is to prefix each class name with the name of the package it is defined in.

For example, if you import the MSWord 8 Automation library, you would edit the class names so that…

  • cComApplication becomes cComMSWord8_Application

  • cComDocument becomes cComMSWord8_Document

  • cComWindows becomes cComMSWord8_Windows

See Also

Common Problems


Class Definition Clashes

Using data aware and non-data aware versions of the same ActiveX control together in the same application presents a unique problem. The FlexCOM class generator will generate identical superclasses for respective data aware and non-data aware wrapper classes.

This problem is demonstrated in the COM sample application with data aware and non-data aware versions of the Calendar ActiveX control.

The solution is to edit one of the wrapper class packages and remove the class definitions of the wrapper's two superclasses (see COM Object Wrapper Classes). Since these superclasses are identical for both data aware and non-data aware wrapper classes, you can re-use the definitions for both wrappers. You will need to include a Use {package name} command to ensure that the superclass definitions are compiled into the class package where you deleted the identical copies.

One down side to this solution is that you will be modifying a package that was generated by the FlexCOM class generator. This is not as bad as it could be since you are quite unlikely to need to re-generate the modified package. It will be sufficient to only re-generate the unmodified package each time since all of the interface definitions are defined there and used by both wrapper classes.

Study the cComCalendar, and cComDbCalendar class definitions in the COM sample application for more information.

See Also

Common Problems


FlexCOM Class Structure

Overview

The FlexCOM 2 classes that ship with Visual DataFlex since version 8.3 completely replaces the set of classes designed to work with Visual DataFlex's previous COM technology (FlexCOM 1).

FlexCOM 2 classes are divided roughly into three categories: These are classes used to build wrapper classes for:

  • ActiveX controls,

  • OLE Automation objects,

  • Embedded Document Objects.

Normally, you will not need to know how to construct wrapper classes from the FlexCOM 2 classes. The Visual DataFlex Studio has class building tools that build appropriate wrapper classes for you (see Importing COM Components for more information). However, you will find it useful to understand the capabilities of each FlexCOM class in order to gain a full understanding of the COM wrapper classes that you will use in your applications.

Below is an abbreviated class hierarchy of the classes that make up the FlexCOM 2 class set:

When you import a COM object, the properties, events and methods that you will be most interested in will be the COM object's properties, events and methods that are 'binded' into the wrapper. At runtime, these 'binded' properties and methods are only accessible while a COM object is 'attached' to its Visual DataFlex wrapper object.

All of the properties, and methods that belong to the FlexCOM class documented here are available during the entire lifetime of the wrapper object regardless of whether a COM object is attached to it or not. This is an important point to understand and is a powerful feature of Visual DataFlex's FlexCOM technology.

For more information refer to COM Object Life Span.

See Also

Using COM in Applications


Glossary

CLSID

A CLSID (Class ID) the name given to a global unique identifier (GUID) when it is used to refer to a class. A GUID is a 128-bit integer that is guaranteed to be globally unique. See also GUID.

COM

Component Object Model (COM) is Microsoft's framework for developing and supporting program component objects.

DDE

Dynamic Data Exchange (DDE) allows information to be shared or communicated between programs. DDE uses a client/server model in which the application requesting data is considered the client and the application providing data is considered the server.

GUID

A GUID (global unique identifier) is a 128-bit integer used in COM to identify an interface or other entity. The algorithm used to generate a GUID is (almost) guaranteed to produce a number that is globally unique.

In COM, every interface or class has an accompanying GUID that uniquely identifies that interface. This means that two classes or interfaces, which have the same name, will never conflict because their respective GUIDs would be different.

When used to represent an interface, a GUID is often referred to as an interface ID (IID). When used to represent a class, a GUID is referred to as a class ID (CLSID).

OCX

An OCX is an Object Linking and Embedding (OLE) custom control. Microsoft now calls an OCX an ActiveX control.

OLE

OLE (Object Linking and Embedding) is Microsoft's framework for a compound object technology. Part of Microsoft's ActiveX technologies, OLE is part of a larger, more general concept, the Component Object Model (COM).

ROT

Running Object Table. A table of registered running COM servers. A workstation's ROT keeps track of those objects that can be identified by a moniker and that are currently running on the workstation.

See Also

Using COM in Applications