缺少方法的奇怪情况:SXS 和 Controls.Add 结果为“对象不支持此属性或方法”?

发布于 2024-10-29 01:55:43 字数 2004 浏览 4 评论 0原文

我有一个用 VB6 编写的项目,它使用 UserControl,注册 OCX 时该项目运行良好,但如果我使用并排清单运行同一项目,则会导致错误。

只要静态加载(之前在表单上添加),我就可以毫无问题地使用该控件,但是如果我在使用新控件(属性或方法)时向表单添加动态控件,则会收到此错误:

对象不支持此属性或方法

此错误可以通过以下方式重现:

  1. 在 VB6 中创建 OCX 项目
  2. 添加用户控件
  3. 向控件添加方法,例如 DoSomething
  4. 创建 exe 项目
  5. 添加控件形成,例如 UserControl1
  6. 在事件调用中 DoSomething
  7. 动态加载如下:

    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:

  1. Create an OCX project in VB6
  2. Add a user control
  3. Add a method, e.g. DoSomething to the control
  4. Create an exe project
  5. Add the control to form, e.g. UserControl1
  6. In an event call DoSomething
  7. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

情丝乱 2024-11-05 01:55:43

简短的回答:这是 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.

画▽骨i 2024-11-05 01:55:43

这是 VB 魔法与清单不兼容的情况。该问题源于 VB 对用户控件引用的行为。即使您强烈调暗变量As UserControl,访问此引用上的方法/属性也是后期绑定的! VB 在每个引用的用户控件上创建一个扩展类以公开常用方法(如 SetFocus、Move 等),因此当您将某些内容变暗As UserControl 时,它不会被编译为对 UserControl< 的引用/code> 位于控件类型库中,但属于 VBControlExtender 继承类,无论如何都是 UserControl 上自动生成的包装器。

自从在 Curland 的 Advanced Visual Basic 6 书中发现有关用户控件的章节后,我正在做的事情是创建一个强制 VB 不使用包装器的自定义直接用户控件类型库。基本上它看起来像这样:

[
  uuid(GUIDHERE-0000-1111-2222-2B5E1A72D6BF),
  version(1.0),
  helpstring("Direct User Controls Typelib 1.0")
]
library <<mytypelib>>
{
    importlib("stdole2.tlb");
    importlib("C:\\WINDOWS\\system32\\COMCTL32.ocx");
    importlib("C:\\WINDOWS\\system32\\COMCT232.ocx");
    importlib("C:\\WINDOWS\\system32\\shdocvw.dll");
    ...

    typedef [public] ComctlLib.ImageList                DirectImageList;
    typedef [public] ComctlLib.ListView                 DirectListView;
    typedef [public] ComctlLib.ProgressBar              DirectProgressBar;
    typedef [public] ComctlLib.Slider                   DirectSlider;
    typedef [public] ComctlLib.StatusBar                DirectStatusBar;
    typedef [public] ComctlLib.TabStrip                 DirectTabStrip;
    typedef [public] ComctlLib.Toolbar                  DirectToolbar;
    typedef [public] ComctlLib.TreeView                 DirectTreeView;

    typedef [public] ComCtl2.Animation                  DirectAnimation;
    typedef [public] ComCtl2.UpDown                     DirectUpDown;

    typedef [public] SHDocVw.WebBrowser                 DirectWebBrowser;
    ...
}

在我的项目中,保留“直接”引用,调用早期绑定的方法。我像这样使用 Controls.Add

Dim oCtl As DirectXxx
Set oCtl = pvCastVBControlExtender(Controls.Add(PROGID_Xxx, sName)).Object

其中铸造助手是这样的

Private Function pvCastVBControlExtender(oCtl As VBControlExtender) As VBControlExtender
    Set pvCastVBControlExtender = oCtl
End Function

。此代码片段在三种情况下按预期工作:在 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 something As UserControl this is compiled not as a reference to the UserControl in the controls typelib, but to a VBControlExtender inherited class, in any case an auto-generated wrapper on UserControl.

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:

[
  uuid(GUIDHERE-0000-1111-2222-2B5E1A72D6BF),
  version(1.0),
  helpstring("Direct User Controls Typelib 1.0")
]
library <<mytypelib>>
{
    importlib("stdole2.tlb");
    importlib("C:\\WINDOWS\\system32\\COMCTL32.ocx");
    importlib("C:\\WINDOWS\\system32\\COMCT232.ocx");
    importlib("C:\\WINDOWS\\system32\\shdocvw.dll");
    ...

    typedef [public] ComctlLib.ImageList                DirectImageList;
    typedef [public] ComctlLib.ListView                 DirectListView;
    typedef [public] ComctlLib.ProgressBar              DirectProgressBar;
    typedef [public] ComctlLib.Slider                   DirectSlider;
    typedef [public] ComctlLib.StatusBar                DirectStatusBar;
    typedef [public] ComctlLib.TabStrip                 DirectTabStrip;
    typedef [public] ComctlLib.Toolbar                  DirectToolbar;
    typedef [public] ComctlLib.TreeView                 DirectTreeView;

    typedef [public] ComCtl2.Animation                  DirectAnimation;
    typedef [public] ComCtl2.UpDown                     DirectUpDown;

    typedef [public] SHDocVw.WebBrowser                 DirectWebBrowser;
    ...
}

In my projects a keep "direct" references, calling methods early-bound. I use Controls.Add like this

Dim oCtl As DirectXxx
Set oCtl = pvCastVBControlExtender(Controls.Add(PROGID_Xxx, sName)).Object

where casting helper is something like this

Private Function pvCastVBControlExtender(oCtl As VBControlExtender) As VBControlExtender
    Set pvCastVBControlExtender = oCtl
End Function

This snippet works as expected in the three cases: in VBIDE, with registered controls and with registration free controls.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文