Tame ActiveX Controls

Source: Internet
Author: User

ActiveX controls are forward rather than backward compatible. This means that if you install a newer version of ocx in your system, any application that you publish to others that has an earlier version of OCX will have problems. This document provides a reusable tool and uses an example to demonstrate an easy-to-use solution to this problem.

How have you been between Christmas and New Year? I hope you will have a good time. But for me, it is terrible. Because: I posted an updated Stonefield database Toolkit (SDT) on my website before Christmas ). When a person who downloads and installs it tries to run it, he gets an error "Ole class not registered. During the week after Christmas, I received a large number of requests for support calls and e-mails, and had to propose a quick solution to solve these problems to prevent more calls in the future.

What caused this problem? The SDT main form uses a Treeview control. I have installed some software in my system, and a newer version of comctl32.ocx is installed. The ocx file contains the Treeview control. Although I didn't change the Treeview control on the form, if I simply open the form and save it, I will be prompted that a newer version of The Treeview control has been written into the form. Of course, few others have the newer control on their machines. Therefore, although forms require a Treeview control, they also have a Treeview control, however, this control is not exactly the one required by the form. In this case, the preceding "Ole class not registered" error occurs.

Is it just me, or do you think this is untrustworthy and untenable? I did not notice the Treeview controls of different versions. I admit that this situation is possible if I use a new property or method that is not available in the old version of the control in the new version. But as I have mentioned, I have not even moved my form.

I guess this crisis may have been caused by my mistakes before Christmas. After all, I upgraded VFP 5.0 to 5.0a prior to this scenario; it carries an upgraded COMCTL32.OCX, in this way, every user who has not upgraded to 5.0a will get an error message "OLE class not registered" when trying to use SDT. In this case, I kept a computer equipped with VFP 5.0 (not 5.0a) in my office and did any development on that computer about SDT, include the form with the TreeView control. If I forget and modify the form on my machine...
However, the experience I have gained from this issue does not make me feel a little better. I have a dozen email Problem Reports in my mailbox. That was the final blow to the failure; I had to consider a permanent solution to the problem. The tool described in this article is the result.

Background

First, let's take a look at the cause of the problem. If you open a form containing ActiveX Controls (in other words, use myform. SCX) and browse it. You will find the binary information stored in the OLE field in the ActiveX control record. The binary information seems to contain some version-specific information about the control. As a result-at least ActiveX controls released by Microsoft are forward rather than backward compatible. This means that if I put a 2.01 ActiveX control into the form, people with new versions of ActiveX Controls (such as 2.02) can open the form, but those with older versions (such as 2.00) cannot open the form.

Let's further look at how the system handles ActiveX controls. You may have realized that simply copying an OCX file to someone's hard disk does not allow them to use ActiveX controls in the file. You also need to use REGSVR32.EXE (a copy of the program released with VFP) or the VFP Installation Wizard to register the control in your system. Using REGEDIT, we can see that the ActiveX control is registered with the class ID in HKEY_CLASSES_ROOT/CLSID. For example, the TreeView control is registered as HKEY_CLASSES _ ROOT/CLSID/0713E8A2-850A-101B-AFC0-4210102A8DA7. There are also some subkeys under this key, the most important of which are ProgID, VersionIndependentProgID and InProcServer32. ProgID includes the program ID, which may vary with versions. For example, in my system, the TreeView sub-key value is COMCTL. TreeCtrl.1. VersionIndependentProgID only contains the implied name (in my system, the TreeView value is COMCTL. TreeCtrl). InProcServer32 contains the OCX name and path containing the control. Direct suggestions for several possible problems are:
· ActiveX Control has never been installed on the machine-the control has no entry in the Registry and OCX does not exist on the drive. The solution is to install it as appropriate.
· The OCX file is copied to the system but not registered-the file exists, but there is no entry in the registry. The solution is to use regsvr32.EXE to register ActiveX controls in OCX. Tip: the file extension must be included during registration:
· "Regsvr32 myfile. ocx" instead of "regsvr32 myfile"
· The ActiveX control is registered, but cannot be found-it may have been renamed or deleted, or the user may have recovered the registry from the backup file but not all files. The best way is to reinstall and register.
· The version installed in the system does not match the version required by your application. An obvious solution to this problem is to install the version required by the control, which may not always be feasible. For example, if I publish my comctl32.ocx with my program (SDT), this can solve my problem, but it may cause problems for you and your customers who use Treeview.
Another unexpected problem may also occur. Some ActiveX controls look for a license that can be used in the development environment (a keyword stored in the registry or a file on your drive ). This makes sense; it allows you to use a vendor's controls to develop and publish applications and use them in the runtime environment to protect the vendor's interests. This is the root cause of the problem. However, if the keyword is lost from your system, when you drag the control to the form, you will get a "license not found" error. Reinstallation does not always solve this problem. Because of the information I see on CompuServe and universal thread, this problem is a common problem. For controls released with VFP, the solution is simple: Download a control named Fox from the universal thread. reg file and run it; it will register the appropriate license in your system. Reinstall other third-party controls or contact your vendor.
As you can see, many questions about ActiveX controls need to be re-installed. However, as I mentioned, this method may not solve the version issue. The following sections discuss how to solve this annoying problem easily.

Sfactivex

Our method is to instantiate ActiveX controls at runtime instead of using them in the development environment. In this way, the control is instantiated from the installed version on the user system, rather than searching for a certain version of the Control installed in your system. To make it easier, I created an ActiveX loading class called sfactivex.
Sfactivex is a Class Based on the sfcustom class. Our M base class is defined in the base class library sfctrls. VCX. It has the Custom Attributes shown in the following table.
Defines the attributes of sfactivex.
Attribute description
Class Name or subclass name of the cClass ActiveX Control
Class ID of the CClassID object
CLibrary class library name that contains ActiveX Control subclass
CNewObjectName: name of the ActiveX control to be created
CObjectName ActiveX placeholder Object Name
OLE class of the cOLEClass object
CClass is "OLEControl" by default, because the ActiveX control class of VFP is included in it. However, if you want to use a subclass of ActiveX Controls (I will show at least one reason for doing so later in this article ), enter the class name in this attribute and the class library name that contains the class in cLibrary (specify an extension to distinguish the VCX and PRG class libraries ).
You can use either of the following methods to specify the ActiveX control: cClassID or cOLEClass. If you specify one, keep the other empty. The cClassID is the class ID of the ActiveX control, and the cOLEClass is its ProgID. For a specified control, how do I know what to input in this attribute? For a cOLEClass, the simplest way is to drag the control to the form and view its OLEClass attributes. For example, the OLEClass of the TreeView control is "COMCTL. TreeCtrl" (You may see "COMCTL. TreeCtrl.1" in your instance; but don't worry about ". 1" in this cOLEClass "). It is slightly difficult to determine the value of cClassID. You need to activate REGEDIT, search for the OLEClass of the control, and view the class ID under it. For example, the TreeView control appears in "{0713E8A2-850A-101B-AFC0-4210102A8DA7 }". When you enter this value in the cClassID attribute, you do not need to enter curly brackets. It is best to specify the cClassID instead of the cOLEClass, because the control name may vary depending on the installed version (for example, the ProgID of the TreeView control of VFP 5.0a is "COMCTL. treeCtrl.1 ", in some cases, if you specify" COMCTL. treeCtrl "may cause problems ).
Some ActiveX controls have a visual appearance, while some do not. For visual ActiveX controls, you must specify the position and size of the controls. You can set the top, left, width, and height attributes of the control in the form's init code, however, I think it is more intuitive to place a shape control with the same size as the running ActiveX control in the position of ActiveX on the form. This is the placeholder object of ActiveX controls. With placeholders, you can easily adjust the size, position, and other related controls. To notify sfactivex that this object is a placeholder object, enter its name in the cobjectname attribute. Sfactivex will remove this object and place ActiveX controls at this position.
Sfactivex does not have much code. Its init method calls the custom method loadactivex (executes all operations) unless one. t. the parameter tells it not to do so (this function enables you to instantiate sfactivex with code, set parameters appropriately, and then manually call loadactivex to load the control ). I don't want to explain all the code in the loadactivex method here (you can view them by yourself), but I just want to explain something worth noting.
A large program of sfactivex relies on searching for Windows Registry content, because it is the place where ActiveX controls installed on your machine are registered. To process the registry, loadactivex is another class defined in sfregistry. VCX. This class is not considered here. It can be said that it is a substitute for registry. PRG, which is the Registry processing class of VFP, but makes the programming interface simpler.
To use sfregistry, you must instantiate it; call a process called newobject to execute this. Newobject. PRG is an updated version of newobj. PRG. The first task of loadactivex is to identify whether the required ActiveX control is installed in the user's system, and if the class ID is specified, it is not the OLE class used. Run the following code to perform this function. Note: If you specify an ole class and cannot find it, loadactivex adds a ". 1" suffix to the class and tries again before giving up. (The method starts with this, so all unspecified object references are the class itself .)
Loregistry = newobject (sfregistry ,;
Sfregistry. VCX, cnhkey_classes_root)
If empty (. coleclass)
Lccsid = IIF (left (. ccassid, 1) = {,;
. Ccassid, {+. ccassid + })
Lcprogid = loregistry. getkey (CLSID/+ lcclsid +;
/Progid)
Loop = not empty (lcprogid)
Else
Lcprogid =. coleclass
Lcclsid = loregistry. getkey (lcprogid +/CLSID)
If empty (lcclsid)
Lcprogid =. coleclass +. 1
Lcclsid = loregistry. getkey (lcprogid +/CLSID)
Endif empty (lcclsid)
Loop = not empty (lcclsid)
Endif empty (. coleclass)
If llok
Lcocx = loregistry. getkey (CLSID/+ lcclsid +;
/Inprocserver32)
Loop = not empty (lcocx) and file (lcocx)
Endif llok
At the end of the Code, lcProgID contains the OLE class (ProgID), lcCLSID contains the class ID, in addition, lcOCX contains the name and path of the OCX file in the user's system (in the InProcServer32 key value in the registry ). If we find something in the Registry and the OCX file exists, the value of the loop is. T .. If no, the user will see an error message and the method will return. F.
If everything is ready, LoadActiveX checks whether a placeholder object is specified. If so, save its location and size and delete it.
Lobject = not empty (. cObjectName)
If lobject
LcObject =. cObjectName
With. Parent. & lcObject
LnTop =. Top
LnLeft =. Left
LnWidth =. Width
LnHeight =. Height
Endwith
. Parent. RemoveObject (lcObject)
Endif
If the Class and Class Library are specified, LoadActiveX opens the class library (using set procedure or set classlib depends on the extension of the class library ). Add the ActiveX control or ActiveX subclass to the container and name it the value specified in the cNewObjectName attribute. If a placeholder object is specified, the control resizes and positions the stored values. Note that the ProgID (saved in lcProgID) must be specified in the call to AddObject. If this property is not filled, you will see a dialog box asking you to select the control to be inserted. This is why we have to search in the registry and decide the ProgID of the ActiveX control.
LcObject =. cNewObjectName
. Parent. addobject (lcobject,. cClass, lcprogid)
With. Parent. & lcobject
If lobject
. Top = lntop
. Left = lnleft
. Width = lnwidth
. Height = lnheight
Endif lobject
. Visible =. T.
Endwith

Use sfactivex

Sfactivex is easy to use: simply drag it to a form (or other containers, such as pages in the page box where ActiveX control is located), fill in some properties, put some code in the form init to set ActiveX Control instances as needed. To make it easier to use, I have created some sfactivex subclasses for my most commonly used ActiveX Controls: sfcommondialog, sfimagelist and sftreeview. These subclasses only set the appropriate cclassid attribute in the class ID, so you do not have to look for its value. Drag one of them to the form to create the required ActiveX control.
Ah, there is a new problem: Suppose you want to add code in a method of ActiveX Control? In visual controls, this is a common situation-for example, for TreeView and ListView controls, you may take some action when you click an item in a node or list. For the TreeView control, you should put the code into the NodeClick method. Of course, you cannot put code into an object. The only way to achieve this is to create a subclass of the ActiveX control, put the required code into the subclass, And tell SFActiveX (through the cClass and cLibrary attributes) to instantiate the class. A slight "gotcha": you cannot create a subclass in VCX, because, just like dragging an ActiveX control directly to a form, VFP puts binary information about the control into VCX, this is exactly what SFActiveX wants to solve. Instead, you need to create a subclass in a PRG. The following is the sample code in ACLASSES. PRG, used to define a subclass of A TreeView control. Its NodeClick method contains control code (although the sample code is used) that is activated when you click a node. Note that OLEClass must be specified. Otherwise, you will get a dialog box asking which control to insert.
Define class MyTreeView as OLEControl
OLEClass = COMCTL. TreeCtrl
Name = MyTreeView
Procedure NodeClick
Lparameters toNode
Wait window You clicked on node + toNode. Text
Endproc
Enddefine
Believe it or not, we have used this solution to solve all problems, and we are not completely out of danger! The last "gotcha": although you may want to set some attributes of the control (such as the Style and LineStyle attributes of the TreeView control) in the subclass definition, do not do this. The reason is that setting attributes in the subclass seems to cause problems for these attributes. Modifying these attributes in the form fails. This means that you must set these attributes after the control is instantiated, usually in the form Init (because the ActiveX control has already been instantiated when the form Init is executed ). This will be demonstrated in future examples.
A final question about subclass: If you create an Init method in the subclass, it will need to accept a parameter. The OLE class is passed to the Init method (note that AddObject is the third parameter when LoadActiveX is called). Although you do not need it, if you do not have the LPARAMETERS parameter, you will get an error.
The example form TREEVIEW. SCX demonstrates how to use SFActiveX. This form contains three objects: An SFTreeView object (SFActiveX specified for TreeViews) calls oTreeViewLoader and an SFImageList object (SFActiveX specified by ImageLists) call oImageListLoader and a Shape called shpTreeView as the placeholder object of the TreeView control. The cNewObjectName attribute of oTreeViewLoader and oImageListLoader specify the name of the control to be created (oTree and oImageList respectively ). In its cObjectName attribute, oTreeViewLoader also has a placeholder object (shpTreeView) name (shpTreeView) and a TreeView subclass name (MyTreeView) to be used) and its location in the cClass and cLibrary attributes (ACLASSES. PRG ). The form Init method sets the properties of some ActiveX controls and loads the nodes of ImageList images and TreeView.
Local loPicture
* Load the ImageList image.
With This. oImageList
LoPicture = loadpicture (AUDIO. ICO)
. ListImages. Add (, Audio, loPicture)
LoPicture = loadpicture (DESKTOP. ICO)
. ListImages. Add (, Desktop, loPicture)
Endwith
* Set some TreeView attributes.
With This. oTree
. ImageList = This. oImageList. Object
. Style = 7
. LineStyle = 1
. LabelEdit = 1
. HideSelection =. F.
. Indentation = 25
* Load the sample node of the TreeView.
. Nodes. Add (, 1, Top1, First Top Node, Audio)
. Nodes. Add (, 1, Top2, Second Top Node, Audio)
. Nodes. Add (Top1, 4, Child1, First Child Node ,;
Desktop)
. Nodes. Add (Top1, 4, Child2, Second Child Node ,;
Desktop)
. Nodes. Add (Top2, 4, Child3, Third Child Node ,;
Desktop)
. Nodes. Add (Top2, 4, Child4, Fourth Child Node ,;
Desktop)
Endwith

Conclusion

ActiveX controls are both excellent and bad. They are excellent because they can provide your applications with the specialization you expect, elegant Standard 32-bit application appearance (VFP's TreeView control and dbi technologies inc's ctListBar control are an excellent example ), or they can provide high-energy capabilities and time-consuming functions written using VFP code (DynamiCube control is an example ). They are bad because when "OLE class not registered" or other OLE errors occur, you cannot just roll up your sleeves and use the VFP debugger to track problems. SFActiveX is already my lifeguard; it handles the main problems of ActiveX Controls I encountered. I believe that you will also find it useful like me.
 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.