Странный случай отсутствия метода: SXS и Controls.Add results in object не поддерживает это свойство или метод?

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

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

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

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

  1. Создайте проект OCX на VB6
  2. Добавить пользовательский элемент управления
  3. Добавьте метод, например DoSomething к контролю
  4. Создать exe-проект
  5. Добавьте элемент управления в форму, например UserControl1
  6. В случае звонка DoSomething
  7. Загружать динамически 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, которая при вызове во втором случае не сработает.

Любая идея?!

Изменить: Моя проблема, вот манифест.

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

person Mehran    schedule 01.04.2011    source источник
comment
Что ж, ваш манифест неверен. Отсюда не видно.   -  person Hans Passant    schedule 01.04.2011
comment
На самом деле это не бок о бок (что подразумевает наличие нескольких сборок), а COM без регистрации. Я согласен, что в вашем манифесте, вероятно, отсутствует важная информация, в данном случае ProgID.   -  person Bob77    schedule 01.04.2011
comment
Я согласен, но это не меняет проблемы, вот манифест. Я почти уверен, что ProgID в порядке.   -  person Mehran    schedule 01.04.2011


Ответы (2)


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

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

person JurekM    schedule 01.04.2011

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

Что я делаю с тех пор, как открыл для себя главу о пользовательских элементах управления в книге Advanced Visual Basic 6 Curland, является создание пользовательский Direct User Controls typelib, который заставляет 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 с зарегистрированными элементами управления и с элементами управления без регистрации.

person wqw    schedule 02.04.2011
comment
приятно знать, что есть еще хардкорные vb-разработчики, ваше решение отличное, но на данный момент я нашел более простой способ решить свою проблему. как описано в support.microsoft.com/kb/190670 Интерфейс VBControlExtender имеет свойство Object, которое позволяет вы получаете доступ к членам реального типа, поэтому в моем случае вызов UserControl.Object.DoSomething устранил проблему. - person Mehran; 02.04.2011
comment
Вызов DoSomething свойства Object - это более очевидный вызов с поздним связыванием. Проблемы начинаются, когда такой UserControl является частью проекта, использующего свойство Object для доступа к неупакованной ссылке. - person wqw; 02.04.2011
comment
Я предполагаю, что свойство объекта не имеет поздней привязки, в любом случае вызов работает отлично, даже если это так. Я не разработчик vb, как можно проверить, запаздывает ли он или нет? - person Mehran; 02.04.2011
comment
Любой вызов метода / свойства в IDispatch (как объект в VB) имеет позднюю привязку. Обычно методы вызова строго типизированных варов (As MyClass) связаны с ранним связыванием. Исключение составляют переменные с размерами как UserControl, которые за сценой получают прокси-объект от компилятора (объявленный в файлах OCA кеша), и все вызовы методов получают позднюю привязку. - person wqw; 04.04.2011