缺少方法的奇怪情况:SXS 和 Controls.Add 结果为“对象不支持此属性或方法”?
我有一个用 VB6 编写的项目,它使用 UserControl,注册 OCX 时该项目运行良好,但如果我使用并排清单运行同一项目,则会导致错误。
只要静态加载(之前在表单上添加),我就可以毫无问题地使用该控件,但是如果我在使用新控件(属性或方法)时向表单添加动态控件,则会收到此错误:
对象不支持此属性或方法
此错误可以通过以下方式重现:
- 在 VB6 中创建 OCX 项目
- 添加用户控件
- 向控件添加方法,例如
DoSomething
- 创建 exe 项目
- 添加控件形成,例如
UserControl1
- 在事件调用中
DoSomething
动态加载如下:
Dim y 作为控件 UserControl1.DoSomething '<-------- 案例(1) 这没关系!' 设置 y = Controls.Add("Project1.UserControl1", "y") y.DoSomething '<----(情况 2)使用 SXS 将失败'
我将 WinDbg 中的错误跟踪回 IDispatch::GetIDsOfNames
,在第二种情况下调用时会失败。
有什么想法吗?
编辑:我的错,这是清单。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="client.exe" version="1.0.0.0" type="win32" processorArchitecture="x86"/>
<file name="Project1.ocx">
<comClass
clsid="{C8CF7991-A8F2-4360-9404-03C9A052C245}"
description="Project1.UserControl1"
tlbid="{47853CCC-7BE6-4377-9C82-38A0B7755F65}"
threadingModel="apartment"
miscStatusContent="recomposeonresize,cantlinkinside,insideout,activatewhenvisible,setclientsitefirst"
progid="Project1.UserControl1"/>
<typelib
tlbid="{47853CCC-7BE6-4377-9C82-38A0B7755F65}"
version="1.0"
helpdir=""
flags="control,hasdiskimage"/>
</file>
<comInterfaceExternalProxyStub
iid="{0E4F313E-7EF3-4FE6-9591-9F7D2D819AEE}"
name="UserControl1"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"/>
<comInterfaceExternalProxyStub
iid="{53307849-4F14-4A59-B0CA-DE4950CE499D}"
name="UserControl1"
proxyStubClsid32="{00020420-0000-0000-C000-000000000046}"/>
</assembly>
I have a project written in VB6 that uses a UserControl, The project runs fine when the OCX is registered, but if I run the same project with a side by side manifest it results in an error.
I can use the Control with no problem as long as it's loaded statically (added before on the form) but if I add a dynamic control to form on any use of the new control (property or method) I get this error:
Object doesn't support this property or method
This error can be reproduced this way:
- Create an OCX project in VB6
- Add a user control
- Add a method, e.g.
DoSomething
to the control - Create an exe project
- Add the control to form, e.g.
UserControl1
- In an event call
DoSomething
Load dynamically Like:
Dim y As Control UserControl1.DoSomething '<-------- CASE(1) THIS IS ALLRIGHT!' Set y = Controls.Add("Project1.UserControl1", "y") y.DoSomething '<---- (CASE 2) THIS WILL FAIL USING SXS'
I tracked the error in WinDbg back to IDispatch::GetIDsOfNames
that when called in second case will fail.
Any Idea?!
Edit: My bad, Here is the manifest.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="client.exe" version="1.0.0.0" type="win32" processorArchitecture="x86"/>
<file name="Project1.ocx">
<comClass
clsid="{C8CF7991-A8F2-4360-9404-03C9A052C245}"
description="Project1.UserControl1"
tlbid="{47853CCC-7BE6-4377-9C82-38A0B7755F65}"
threadingModel="apartment"
miscStatusContent="recomposeonresize,cantlinkinside,insideout,activatewhenvisible,setclientsitefirst"
progid="Project1.UserControl1"/>
<typelib
tlbid="{47853CCC-7BE6-4377-9C82-38A0B7755F65}"
version="1.0"
helpdir=""
flags="control,hasdiskimage"/>
</file>
<comInterfaceExternalProxyStub
iid="{0E4F313E-7EF3-4FE6-9591-9F7D2D819AEE}"
name="UserControl1"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"/>
<comInterfaceExternalProxyStub
iid="{53307849-4F14-4A59-B0CA-DE4950CE499D}"
name="UserControl1"
proxyStubClsid32="{00020420-0000-0000-C000-000000000046}"/>
</assembly>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
简短的回答:这是 VB6 的一个已知问题。 “Controls.Add”语句不能并排使用。尝试使用“Load”语句代替。
长答案:即使您使用 ProgID 创建控件,VB6 也会将控件的 CLSID 编译为可执行文件。执行“Control.Add”可验证 CLSID 是否相同。别问为什么,这是一个谜,最好不要碰。同时,win32 并排(与 .Net 并排相反——另一个主题)必须准备好处理多个清单使用的相同 ProgID(当您在激活上下文之间翻转时) ,例如),因此它在内部为每个 ProgID 生成一个新的临时 CLSID。最后,当您调用 CLSIDFromProgID 时,您将获得临时 CLSID。如果您随后调用 CoCreateInstance,它会正常工作 - sxs 遵循 CLSID。但是,如果您在任何地方(注册表、内部表)查找 CLSID,您将找不到它。 VB6 程序调用 CLSIDFromProgID,然后检查它接收到的 CLSID 是否在内表中。失败。
The short answer: This is a known problem with VB6. The "Controls.Add" statement does not work with side-by-side. Try using a "Load" statement instead.
The long answer: VB6 compiles the CLSID of the control into the executable even though you use a ProgID to create the control. Execution of "Control.Add" verifies that the CLSID is the same. Don't ask why, this is a mystery better not touched. At the same time win32 side-by-side (as opposed by .Net side-by-side -- another topic) must be prepared to handle the same ProgID being used by more than one manifest (when you flip-flop between activation contexts, for example) so it internally generates a new, temporary, CLSID for each ProgID. In the end when you call CLSIDFromProgID you will get the temporary CLSID. If you then call CoCreateInstance it works fine - sxs honors the CLSID. But if you go looking for the CLSID anywhere (registry, your internal table) you will not find it. And here comes the VB6 program calling CLSIDFromProgID then checking if the CLSID it receives is in the internal table. Fail.
这是 VB 魔法与清单不兼容的情况。该问题源于 VB 对用户控件引用的行为。即使您强烈调暗变量
As UserControl
,访问此引用上的方法/属性也是后期绑定的! VB 在每个引用的用户控件上创建一个扩展类以公开常用方法(如 SetFocus、Move 等),因此当您将某些内容变暗As UserControl
时,它不会被编译为对UserControl< 的引用/code> 位于控件类型库中,但属于
VBControlExtender
继承类,无论如何都是UserControl
上自动生成的包装器。自从在 Curland 的 Advanced Visual Basic 6 书中发现有关用户控件的章节后,我正在做的事情是创建一个强制 VB 不使用包装器的自定义
直接用户控件
类型库。基本上它看起来像这样:在我的项目中,保留“直接”引用,调用早期绑定的方法。我像这样使用
Controls.Add
,其中铸造助手是这样的
。此代码片段在三种情况下按预期工作:在 VBIDE 中,使用注册控件和使用免注册控件。
This is a case of VB magic that is incompatible with manifests. The problem stems from VB's behavior with user control references. Even if you strongly Dim a variable
As UserControl
, accessing methods/properties on this reference is late-bound! VB creates an extension class on each referenced user control to expose common methods (like SetFocus, Move, etc) so when you Dim somethingAs UserControl
this is compiled not as a reference to theUserControl
in the controls typelib, but to aVBControlExtender
inherited class, in any case an auto-generated wrapper onUserControl
.What I'm doing since discovering the chapter on user controls in Curland's Advanced Visual Basic 6 book is to create a custom
Direct User Controls
typelib that forces VB not to use wrappers. Basically it looks like this:In my projects a keep "direct" references, calling methods early-bound. I use
Controls.Add
like thiswhere casting helper is something like this
This snippet works as expected in the three cases: in VBIDE, with registered controls and with registration free controls.