Странный случай отсутствующего метода: SXS и Controls.Add приводят к «объект не поддерживает это свойство или метод»? - PullRequest
4 голосов
/ 01 апреля 2011

У меня есть проект, написанный на VB6, который использует UserControl. Проект работает нормально, когда OCX зарегистрирован, но если я запускаю тот же проект с параллельным манифестом, это приводит к ошибке.

Я могу без проблем использовать элемент управления, если он статически загружен (добавлен ранее в форму), но если я добавляю в форму динамический элемент управления при любом использовании нового элемента управления (свойство или метод), я получаю эту ошибку:

Объект не поддерживает это свойство или метод

Эту ошибку можно воспроизвести следующим образом:

  1. Создать проект OCX в VB6
  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>

Ответы [ 2 ]

4 голосов
/ 02 апреля 2011

Краткий ответ: это известная проблема с VB6. Оператор Controls.Add не работает бок о бок. Попробуйте вместо этого использовать оператор «Load».

Длинный ответ: VB6 компилирует CLSID элемента управления в исполняемый файл, даже если вы используете ProgID для создания элемента управления. Выполнение «Control.Add» проверяет, что CLSID одинаков. Не спрашивай почему, это загадка лучше не трогать. В то же время Win32 бок о бок (в отличие от .Net бок о бок - другая тема) должны быть готовы обрабатывать один и тот же ProgID, который используется несколькими манифестами (когда вы переключаетесь между контекстами активации например), поэтому он генерирует новый временный CLSID для каждого ProgID. В конце концов, когда вы вызовете CLSIDFromProgID, вы получите временный CLSID. Если вы затем вызываете CoCreateInstance, он работает нормально - sxs учитывает CLSID. Но если вы будете искать CLSID в любом месте (реестр, ваша внутренняя таблица), вы не найдете его. И вот идет программа VB6, вызывающая CLSIDFromProgID, затем проверяющая, находится ли полученный CLSID во внутренней таблице. Сбой.

2 голосов
/ 02 апреля 2011

Это случай магии VB, который несовместим с манифестами. Проблема связана с поведением VB со ссылками на элементы управления пользователя. Даже если вы сильно Dim переменной As UserControl, доступ к методам / свойствам по этой ссылке имеет позднюю привязку! VB создает класс расширения в каждом элементе управления, на который ссылаются, для предоставления общих методов (таких как SetFocus, Move и т. Д.), Поэтому, когда вы что-то делаете Dim As UserControl, это компилируется не как ссылка на UserControl в библиотеке элементов управления, а VBControlExtender унаследованный класс, в любом случае автоматически сгенерированная оболочка на UserControl.

То, что я делаю с тех пор, как обнаружил главу о пользовательских элементах управления в Книга 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, с зарегистрированными элементами управления и с элементами управления без регистрации.

...