缺少方法的奇怪案例:SXS 和 Controls.Add 导致“对象不支持此属性或方法"?
我有一个用 VB6 编写的使用 UserControl 的项目,该项目在 OCX 注册后运行良好,但如果我使用并排清单运行同一个项目,则会导致错误.
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:
- 在 VB6 中创建 OCX 项目
- 添加用户控件
- 添加一个方法,例如
DoSomething
到控件 - 创建一个 exe 项目
- 将控件添加到表单中,例如
UserControl1
- 在事件调用中
DoSomething
动态加载喜欢:
- 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'
我将 WinDbg 中的错误追踪回 IDispatch::GetIDsOfNames
,在第二种情况下调用该错误会失败.
I tracked the error in WinDbg back to IDispatch::GetIDsOfNames
that when called in second case will fail.
有什么想法吗?!
我的错,这是清单.
<?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>
推荐答案
简短的回答:这是 VB6 的一个已知问题.Controls.Add"语句不能并排使用.尝试改用加载"语句.
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.
长答案:即使您使用 ProgID 创建控件,VB6 也会将控件的 CLSID 编译到可执行文件中.执行Control.Add"验证 CLSID 是否相同.不要问为什么,这是一个谜,最好不要碰.同时,win32 并排(与 .Net 并排――另一个主题相反)必须准备好处理多个清单使用的相同 ProgID(当您在激活上下文之间切换时),例如),因此它在内部为每个 ProgID 生成一个新的临时 CLSID.最后,当您调用 CLSIDFromProgID 时,您将获得临时 CLSID.如果您然后调用 CoCreateInstance 它工作正常 - sxs 尊重 CLSID.但是,如果您在任何地方(注册表、您的内部表)查找 CLSID,您将找不到它.VB6 程序调用 CLSIDFromProgID 然后检查它收到的 CLSID 是否在内部表中.失败.
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.
相关文章