4

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

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

对象不支持此属性或方法

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

  1. 在 VB6 中创建 OCX 项目
  2. 添加用户控件
  3. 添加一个方法,例如添加DoSomething到控件中
  4. 创建一个exe项目
  5. 将控件添加到窗体,例如UserControl1
  6. 在事件调用中DoSomething
  7. 动态加载喜欢:

    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在第二种情况下调用时将失败。

任何想法?!

编辑:我的错,这是清单。

<?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>
4

2 回答 2

4

简短的回答:这是 VB6 的一个已知问题。“Controls.Add”语句不能并排使用。尝试改用“加载”语句。

长答案:即使您使用 ProgID 创建控件,VB6 也会将控件的 CLSID 编译为可执行文件。执行“Control.Add”验证 CLSID 是否相同。不要问为什么,这是一个谜,最好不要碰。同时,win32 并排(与 .Net 并排——另一个主题相反)必须准备好处理多个清单使用的相同 ProgID(当您在激活上下文之间切换时) ,例如),因此它在内部为每个 ProgID 生成一个新的临时 CLSID。最后,当您调用 CLSIDFromProgID 时,您将获得临时 CLSID。如果您然后调用 CoCreateInstance 它工作正常 - sxs 尊重 CLSID。但是,如果您在任何地方(注册表、您的内部表)查找 CLSID,您将找不到它。VB6 程序调用 CLSIDFromProgID 然后检查它收到的 CLSID 是否在内部表中。失败。

于 2011-04-01T23:41:15.950 回答
2

这是一个与清单不兼容的 VB 魔术案例。问题源于 VB 对用户控件引用的行为。即使您强烈 Dim a variable As UserControl,访问此引用上的方法/属性也是后期绑定的!VB 在每个引用的用户控件上创建一个扩展类以公开常用方法(如 SetFocus、Move 等),因此当您 Dim 某些东西时,它不会编译为控件类型库中As UserControl的引用,而是编译为继承的类,在任何情况下上自动生成的包装器。UserControlVBControlExtenderUserControl

自从发现Curland 的 Advanced Visual Basic 6书中关于用户控件的章节以来,我正在做的是创建一个自定义Direct User Controls类型库,强制 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 中,带有注册控件和无注册控件。

于 2011-04-02T08:57:20.320 回答