怎样用代码安装控件

super_kevin 2006-09-05 05:37:39
如题, 自己往工具箱里加控件太麻烦,devExpress的安装程序就可以自动安装控件到工具箱,哪位知道是怎么作的 ?
...全文
417 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
super_kevin 2006-09-11
  • 打赏
  • 举报
回复
自己顶
super_kevin 2006-09-05
  • 打赏
  • 举报
回复
谢谢dugupiaoyun(独孤飘云) 的关注,不过我这里说的是.net控件的安装,不是ActiveX控件
dugupiaoyun 2006-09-05
  • 打赏
  • 举报
回复
使用SFActiveX

SFActiveX 是易于使用的:简单的拖动它到表单中(或其它容器中,如ActiveX 控件所在的页框中的页中),填写一些属性,并在表单的 Init 中放置一些代码来按需设置ActiveX 控件的实例。为了使它更易于使用,我为我最常用的 ActiveX 控件创建了一些SFActiveX 的子类:SFCommonDialog,SFImageList 和SFTreeView。这些子类只是在类 ID 中设置了适当的cClassID 属性,因此你不必再查找它的值。拖动它们中的一个到表单中就可以创建所需的ActiveX 控件。
啊,有一个新出现的问题:假设你想在ActiveX 控件的一个方法中放入代码时怎么办? 在可视化控件中,这是常见的情况—例如,对于TreeView 和 ListView 控件,当用户单击节点或列表中的项时你可能会采取一些行动。对于对于TreeView 控件,你应该放入代码到NodeClick 方法中。当然,该问题是你不能放入代码到一个对象。要达到这一目的的唯一办法是创建一个 ActiveX 控件的子类,放入所需的代码到子类中,并告诉SFActiveX (通过cClass 和 cLibrary 属性) 实例化该类。一个轻微的"gotcha":你不能在 VCX 中创建子类,因为就象直接拖动一个ActiveX 控件到表单中一样,VFP 放入关于控件的二进制信息到VCX 中,而这正是SFActiveX 所想解决的问题。作为代替,你需要在一个PRG 中创建子类。以下是ACLASSES.PRG 中的示例代码,用于定义一个TreeView 控件的子类; 它的NodeClick 方法中有控制代码 (虽然是示例代码) ,该代码在用户单击节点时激活。注意OLEClass 必须指定,否则用户将会得到一个询问要插入哪一控件的对话框。
define class MyTreeView as OLEControl
OLEClass = COMCTL.TreeCtrl
Name = MyTreeView
procedure NodeClick
lparameters toNode
wait window You clicked on node + toNode.Text
endproc
enddefine
不管你信不信,对于所有问题我们都用该方案解决了,我们还没有完全脱离危险! 还有最后一个"gotcha":虽然你可能会想你可以在子类定义中设置控件的一些属性 (如TreeView 控件的Style 和 LineStyle 属性),不要这样做。理由是,在子类中设置属性似乎会使这些属性发生问题。在表单中修改这些属性会失败。这就意味着你必须在控件实例化后设置这些属性,通常是在表单的Init 中 (因为在表单的Init 执行时,ActiveX 控件已经实例化了)。关于这一点在以后的示例中演示。
一个最后的关于子类的问题:如果你在子类在创建了一个Init 方法,它将需要接受一个参数。OLE 类是传递到Init 方法(注意AddObject 调用LoadActiveX 时的第三个参数); 虽然你不需要它,如果没有LPARAMETERS 参数,你将会得到一条错误。
示例表单TREEVIEW.SCX 演示了如何使用SFActiveX。该表单包括三个对象:一个SFTreeView 对象(为TreeViews指定的SFActiveX) 调用oTreeViewLoader,一个SFImageList 对象(为ImageLists 指定的SFActiveX) 调用oImageListLoader 和一个叫做shpTreeView 的Shape作为TreeView 控件的占位对象。oTreeViewLoader 的cNewObjectName 属性和oImageListLoader 指定要创建的控件的名字 (分别为oTree 和oImageList)。oTreeViewLoader 在它的 cObjectName 属性中,也有占位对象(shpTreeView)名字(shpTreeView)和我们要使用的 TreeView 子类名字(MyTreeView) 和在 cClass 中和 cLibrary 属性中的的它的位置(ACLASSES.PRG) 。表单的Init 方法设置一些 ActiveX 控件的属性,载入 ImageList 图象和 TreeView 的节点。
local loPicture
* 载入ImageList 图形。
with This.oImageList
loPicture = loadpicture(AUDIO.ICO)
.ListImages.Add(, Audio, loPicture)
loPicture = loadpicture(DESKTOP.ICO)
.ListImages.Add(, Desktop, loPicture)
endwith
* 设置一些TreeView 属性。
with This.oTree
.ImageList = This.oImageList.Object
.Style = 7
.LineStyle = 1
.LabelEdit = 1
.HideSelection = .F.
.Indentation = 25
* 载入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

结论

ActiveX 控件既是极好的又是很糟的。它们是极好的因为它们可以给你的应用程序以用户所期待的专业化的,优美的标准的32位应用程序外观 (VFP 的TreeView 控件和dbi technologies inc 公司的ctListBar 控件就是极好的例证),或者它们可以提供高能的能力及用VFP 代码编写是耗时的功能 (DynamiCube 控件就是一个示例)。它们是很糟的是因为当"OLE 类未注册" 或其它OLE 错误发生时,你不能仅仅卷起你的袖子并用 VFP 调试器来跟踪问题。SFActiveX 已经是我的救生器; 它处理我所遇到的ActiveX 控件的主要问题。我相信你也会象我一样觉得它是有用的。
dugupiaoyun 2006-09-05
  • 打赏
  • 举报
回复
SFActiveX

我们的方法是在运行时实例化 ActiveX 控件而不是在开发环境中使用它们。这种方法,控件是从用户系统上的已安装的版本中实例化,而不是搜索安装在你的系统中的某一版本的控件。为了使这样做更容易,我创建了一个叫做 SFActiveX 的 ActiveX 载入类。
SFActiveX 是基于 SFCustom 类的类,我们的 Custom 基类定义在基类类库 SFCTRLS.VCX中。它具有如下表所示的自定义属性。
SFActiveX 的息定义属性。
属性 描述
CClass ActiveX 控件的类名或子类名
CClassID 对象的类ID
CLibrary 包含ActiveX 控件子类的类库名
CNewObjectName 给将要创建的ActiveX 控件的该对象的名字
cObjectName ActiveX 占位对象名
cOLEClass 对象的OLE 类
cClass默认为"OLEControl",因为VFP 的ActiveX控件类包含在其中。但是,如果你想使用ActiveX 控件的子类(我将在本文的稍后,展示至少一个为什么这样做的理由),在该属性中输入类的名字和在cLibrary 中输入包含该类的类库的名字(指定一个扩展名来区别 VCX 和PRG 类库)。
你可以用一种或两种方法指定要使用的ActiveX 控件:cClassID 或 cOLEClass。如果你指定了一个,保持另一个为空。cClassID 是ActiveX 控件的类ID,cOLEClass 是它的ProgID。对于指定的控件,怎样知道在该属性中输入什么内容呢? 对于cOLEClass,最简单的方法是拖动控件到表单中,然后查看它的 OLEClass 属性。例如,TreeView 控件的OLEClass是 "COMCTL.TreeCtrl" (你可能在你的实例中看到"COMCTL.TreeCtrl.1"; 但是,不要担心这个cOLEClass 中的 ".1")。cClassID 稍难于确定。你需要激活REGEDIT 并搜索控件的OLEClass,然后查看它下面的类 ID。例如,TreeView 控件出现在"{0713E8A2-850A-101B-AFC0-4210102A8DA7}"。当你在 cClassID 属性中输入该值时,不必输入花括号。最好指定cClassID 而不是cOLEClass,因为控件的名字可能因所安装的版本不同而不同 (例如,VFP 5.0a 的 TreeView 控件的 ProgID 是 "COMCTL.TreeCtrl.1",在某些情况下,你要是指定了 "COMCTL.TreeCtrl" 可能会造成问题)。
一些ActiveX 控件具有可视的外观,而有些没有。对于可视的ActiveX 控件,需要指定控件的位置和大小。可以在表单的Init 代码中设置控件的Top,Left,Width 和Height 属性,但我认为在表单上的ActiveX 所在的位置放置一个与运行时的ActiveX 大小一致的Shape 控件更为直观。该对象是ActiveX 控件的占位对象,有了占位符,你可以方便地调整它的大小,位置和其它相关控件。要告诉SFActiveX 该对象是一个占位对象,在cObjectName 属性中输入它的名字。SFActiveX 将移去该对象并在该位置放置ActiveX 控件。
SFActiveX 没有许多代码。它的Init 方法调用自定义方法LoadActiveX (执行所有的操作) 除非传递一个.T. 参数告诉它不这样做(提供这个功能使你能够用代码实例化SFActiveX,适当的设置参数,然后手动地调用LoadActiveX 来载入控件)。我不想在这里解释LoadActiveX 方法中的所有代码(你可以自己查看它们),而只是说一下值得注意的东西。
SFActiveX 很大程序上依赖对Windows 注册表内容的搜索,因为那是用户机器上安装了的ActiveX 控件注册的地方。为了处理注册表,LoadActiveX 另一个在SFREGISTRY.VCX中定义的类 SFRegistry。在这里我们不考虑该类; 可以说它是VFP 自已的注册表处理类 REGISTRY.PRG 的替代物,但使编程接口更简单。
为了使用SFRegistry,你必须实例化它; 调用一个叫做NEWOBJECT 的过程来执行这一点。NEWOBJECT.PRG 是一个更新版本的NEWOBJ.PRG。LoadActiveX 的第一个任务是指出用户的系统中是否安装了所需的ActiveX 控件,而且如果指定了类 ID 而不是使用的OLE 类。以下代码执行该功能。注意如果你指定了OLE 类且不能找到它,LoadActiveX 添加一个".1" 后缀到类并在放弃前再试。(方法以WITH THIS 开始,因此所有未指明的对象引用是类自已。)
loRegistry = newobject(SFRegistry, ;
SFRegistry.vcx, cnHKEY_CLASSES_ROOT)
if empty(.cOLEClass)
lcCLSID = iif(left(.cClassID, 1) = {, ;
.cClassID, { + .cClassID + })
lcProgID = loRegistry.GetKey(CLSID\ + lcCLSID + ;
\ProgID)
llOK = 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)
llOK = not empty(lcCLSID)
endif empty(.cOLEClass)
if llOK
lcOCX = loRegistry.GetKey(CLSID\ + lcCLSID + ;
\InProcServer32)
llOK = not empty(lcOCX) and file(lcOCX)
endif llOK
在代码结束时,lcProgID 包含OLE 类(ProgID),lcCLSID 包含了类 ID,而且lcOCX 包含了用户系统中的OCX 文件的名字和路径 (在注册表中的InProcServer32 键值中)。如果我们在注册表中找到了要找的东西且OCX 文件存在,llOK 的值为.T.。如果没有,用户将会看到一条错误信息,且该方法将返回.F.
如果一切就绪,LoadActiveX 检查是否指定了占位对象,如果指定了,保存它的位置和大小并删除它。
llObject = not empty(.cObjectName)
if llObject
lcObject = .cObjectName
with .Parent.&lcObject
lnTop = .Top
lnLeft = .Left
lnWidth = .Width
lnHeight = .Height
endwith
.Parent.RemoveObject(lcObject)
endif
如果指定了类和类库,LoadActiveX 打开类库(用SET PROCEDURE 或 SET CLASSLIB,取决于类库的扩展名)。然后添加ActiveX 控件或ActiveX 子类到容器中,并命名它为cNewObjectName 属性中指定的值。如果指定了占位对象,控件以保存的值重调大小和位置。注意ProgID (保存在lcProgID 中) 必须在调用AddObject 中指定; 如果该属性没有填充值,用户会看到一个对话框要求选择要插入的控件。这就是为什么我们必须在注册表中搜索并决定ActiveX 控件的ProgID。
lcObject = .cNewObjectName
.Parent.AddObject(lcObject, .cClass, lcProgID)
with .Parent.&lcObject
if llObject
.Top = lnTop
.Left = lnLeft
.Width = lnWidth
.Height = lnHeight
endif llObject
.Visible = .T.
endwith
dugupiaoyun 2006-09-05
  • 打赏
  • 举报
回复
网上看到一篇文章,有点介绍:(以下引用)

ActiveX 控件是向前而不是向后兼容的。这就意味着如果你在你的系统中安装了一个较新版本的 OCX,任何你发布给别人的拥有老版本OCX 的应用程序将会发生问题。本文提供一个可重用工具,并用一个示例来简单的演示了一个易于使用的解决该问题的方案。

你在圣诞节到新年期间过得怎样?我希望你过得轻松愉快。但对于我来说,它是可怕的。因为:在圣诞节前我在我的网站上发布了一个更新了的 Stonefield Database Toolkit (SDT)。每一个下载并安装了它的人在试着运行它时,得到一条 "OLE 类未注册" 错误。圣诞节后的一个星期中,我收到大量的要求支持电话和电子邮件,并不得不提出一个快速方案来解决这些人的问题以防止今后更多的电话。

是什么造成了这个问题? SDT 主表单使用了一个 TreeView 控件,我在我的系统中安装了一些软件,这也就安装了较新版本的 COMCTL32.OCX,该OCX 文件包含了 TreeView 控件。虽然我没有改变表单上的 TreeView 控件,但如果简单的打开表单并保存它,就会被提示较新版本的 TreeView 控件被写入了表单。当然,很少有其它人在他们的机器上拥有该较新的控件,因此 虽然表单需要一个 TreeView 控件,而且他们也有一个 TreeView 控件,但该控件并不是表单所确切需要的那个控件。这样就发生了上述"OLE 类未注册" 错误。

难道仅仅是我,或你是否认为这是不能相信的和站不住脚的? 我没有注意到不同版本的 TreeView 控件。我承认如果我在新版本的控件中使用了该控件的旧版本中没有的新的属性或方法,发生这种情况是可能的。但正如我曾提及的那样,我甚至没有动过我的表单。

我猜想可能是由于我自己在圣诞节前的过失造成了这个危机。毕竟,我在遭遇这种情景前将 VFP 5.0 升级到 5.0a;它带有一个升级了的 COMCTL32.OCX,这样每一个没有升级到 5.0a 的用户在试图使用 SDT时会得到一条 "OLE 类未注册" 错误信息。我在这时的处理是在我的办公室中保持一台电脑中装有 VFP 5.0 (不是 5.0a),并在那台电脑上进行任何关于 SDT 的开发,包括那一个有 TreeView 控件的表单。如果我忘记了并在我的机器上修改了那个表单 . . .
但是,要知道我从该问题中获得的经验,并没有使我感到好受一点,我的信箱中有成打的电子邮件问题报告。那是导致失败之最后一击; 我不得不考虑一个解决该问题的永久的方案。本文中描述的工具便是结果。

背景

首先,让我们看一下问题的原因。如果你以打开表的方式打开一个含有 ActiveX 控件的表单(换句话说就是 USE MYFORM.SCX)并浏览它,你会发现保存在 ActiveX 控件记录中的 OLE 字段中的二进制信息。这些二进制信息看起来包含了一些关于控件的版本特定信息。作为结果 -- 至少 Microsoft 发布的 ActiveX 控件是向前而不是向后兼容的。这就意味着如果我放入一个 2.01 版的 ActiveX 控件到表单中,拥有新版本的 ActiveX 控件(如 2.02 版) 的人们可以打开表单,但那些拥有老版本的(如 2.00 版)人们则不能打开表单。

让我们进一步查看系统是如何处理 ActiveX 控件的。可能你已经意识到了,简单地复制一个 OCX 文件到某人的硬盘上并不能使他们能使用该文件中的 ActiveX 控件。控件还需要用REGSVR32.EXE (随 VFP 发布的一个该程序的拷贝) 或用VFP 的安装向导注册到你的系统中。使用REGEDIT,我们可以看到ActiveX 控件是在HKEY_CLASSES_ROOT\CLSID 中以类 ID 注册。例如,TreeView 控件注册为HKEY_CLASSES_ ROOT\CLSID\0713E8A2-850A-101B-AFC0-4210102A8DA7。在该键下还有一些子键,最重要的是 ProgID,VersionIndependentProgID 和 InProcServer32。ProgID 包括程序 ID,它可能因版本的不同而不同。例如,在我的系统中,TreeView 子键值是COMCTL.TreeCtrl.1。VersionIndependentProgID 只包含暗示名 (在我的系统中,TreeView 的值是 COMCTL.TreeCtrl),InProcServer32 包含了含有控件的 OCX 的名字和路径。对于几个可能的问题的直接建议是:
· ActiveX 控件从没有安装到机器上—该控件在注册表中没有入口,且OCX 不存在于驱动器上。解决办法是适当的安装它。
· OCX 文件复制到了系统中但未注册—文件存在,但注册表中没有入口。解决方法是用 regsvr32.EXE to 注册OCX 中的ActiveX 控件。提示:注册时必须包括文件的扩展名:
· "regsvr32 myfile.ocx" 而不是"regsvr32 myfile"
· ActiveX 控件注册了,但不能找到OCX – 它可能已被更名或删除,或用户可能从备份的文件中恢复了注册表而未恢复所有文件。最好的办法是重新安装和注册。
· 系统中安装的版本与用户应用程序要求的版本不符。解决该问题的一个显而易见的方法是安装控件所要求的版本,这可能并不总是可行的。例如,如果我与我的程序( SDT)一起发布我的 COMCTL32.OCX,这可以解决我的问题,但可能会对你和你的使用 TreeView 的客户带来问题。
另一个无法预料的问题也可能出现。一些 ActiveX 控件寻找一个可以在开发环境中使用它们的许可(一个保存在注册表中的关键字或你的驱动器上的一个文件)。这是有意义的;它允许你用一个厂商的控件开发和发布应用程序,并在运行时环境中使用它们,以保护厂商利益。这是问题的根原,可是,如果关键字从你的系统中丢失,当你拖动控件到表单时,你会得到一个 "许可未找到" 错误。重新安装并不总能解决该问题。因为我在 CompuServe 和 Universal Thread 上看到的信息,该问题是一个普遍问题。对于与 VFP 一起发布的控件,解决方法是简单的:从 Universal Thread 下载一个叫 FOX.REG 的文件并运行它; 它将在你的系统中注册适当的许可。对于其它第三方控件,重新安装或与你的销售商联系。
正如你可以看到的一样,许多关于 ActiveX 控件的问题需要重新安装它们。但是,正如我所提及过的,这种方法可能不能解决版本问题。本文后面的部分讨论一种易于实现的方法解决这一讨厌的问题。
wangwei155 2006-09-05
  • 打赏
  • 举报
回复
不清楚,帮顶

110,539

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧