Приведение типов (вопрос)

Nikz

всем приветсобственно столкнулся с проблемой об которую почти поломал мозгесть некая библиотека содержащая в себе интерфейс IFTP и классы Explorer и Connection. Я так понимаю оба класса имплементят этот интерфейс. Класс Connection имплементит также функцию Open(), класс Explorer имплементит функцию Dir(). Библиотека добавлена в референсы и имеем такой код:
Sub test(ServerName As String, DestFolder As String)
 Dim expl As IFTP
 Dim con As Connection
 
 Set con = new Connection
 Set expl = con
 
 If con.Open(ServerName) Then
 debug.print expl.Dir(DestFolder)
 con.Close
 End If
End Sub
т.е. обьявляем expl как IFTP. Затем создаем обьект Connection и кастим его на expl.В результате мы можем открыть соединение методом класса Connection.Open() и листануть файлы методом класса Explorer.Dir()как они там друг другу передают состояние подключения я не знаю, это что то зашитое внутри.задача - отвязаться от раннего связыванияпока сделал такой код:
Sub test(ServerName As String, DestFolder As String)
 Dim expl As IFTP
 Dim con As Object
 
 Set con = CreateObject("Connection")
 Set expl = con
 
 If con.Open(ServerName) Then
 debug.print expl.Dir(DestFolder)
 con.Close
 End If
End Sub
все работает, остается отвязаться от Dim expl As IFTPделаю так
Sub test(ServerName As String, DestFolder As String)
 Dim expl As Object
 Dim con As Object
 
 Set con = CreateObject("Connection")
 Set expl = con
 
 If con.Open(ServerName) Then
 debug.print expl.Dir(DestFolder)
 con.Close
 End If
End Sub
валится на expl.Dir(DestFolder) - Object does not support property or method. Это в принципе логично - так как тип expl теперь явно не указыватся, строка Set expl = con присваивает объекту expl объект типа Connection. А надо чтобы вместо присваивания происходил кастинг как было раньше.Собственно вопрос - как это сделать и вообще в какую сторону копать?исходники библиотеки и ее авторы недоступны.спасибо.
16 ответов

Nikz

Лучше забей на это дело, иначе придется аццки хачить механизмы COM. По умолчанию отдается ссылка на базовый класс — Connection. Если ты хочешь получить другой интерфейс, ты должен сказать какой. В целях образования скорми гуглю слово VTABLE (virtual table).


Nikz

забить не получится, придется аццки хачитьвот я собственно и ищу способ как указать какой именно интерфейс мне нужен, только сделать это надо путем использования стринговой константы


Nikz

Nikz,надо отвязаться от раннего связывания во всём проекте, или же от раннего связывания с этой библиотекой только?


Nikz

есть некая библиотека содержащая в себе интерфейс IFTP и классы Explorer и Connection. Я так понимаю оба класса имплементят этот интерфейс. Класс Connection имплементит также функцию Open(), класс Explorer имплементит функцию Dir(). Библиотека добавлена в референсы и имеем такой код: ...т.е. обьявляем expl как IFTP. Затем создаем обьект Connection и кастим его на expl.В результате мы можем открыть соединение методом класса Connection.Open() и листануть файлы методом класса Explorer.Dir()
Что-то не понятная ситуация, если класс Connection не имеет метода Dir(), а интерфейс IFTP его имеет, то все-таки класс Connection нифига не импелементит этот интерфейс?Интересно было бы глянуть на сами интерфейсы и их иерархию, по-моему подобной проблемы не должно возникать.


Nikz

Что-то не понятная ситуация, если класс Connection не имеет метода Dir(), а интерфейс IFTP его имеет, то все-таки класс Connection нифига не импелементит этот интерфейс?
Нет, всё нормально. Кастуем Connection к IFTP и имеем Dir. Не путай поддержку интерфейса COM-объекта с наследованием реализации в классе языка, который её поддерживает.


Nikz

Нет, всё нормально. Кастуем Connection к IFTP и имеем Dir. Не путай поддержку интерфейса COM-объекта с наследованием реализации в классе языка, который её поддерживает.
Да не путаю, вроде как. "Поддержка интерфейса" разве не означает имплементацию всех объявленных в интерфейсе [и не имплементированных, хотя в "интерфейсе" только такие и есть] методов? Наследование реализации тут ни при чем, в общем-то.p.s.
Class1.cls:
Public Function Dir()
End Function
Public Function Opens()
End Function
Class2.cls:
Implements Class1
Public Function Opens()
End Function
vb исправно ругается на неимплементированный Dir() в Class2


Nikz

Да не путаю, вроде как. "Поддержка интерфейса" разве не означает имплементацию всех объявленных в интерфейсе [и не имплементированных, хотя в "интерфейсе" только такие и есть] методов? Наследование реализации тут ни при чем, в общем-то.
Вот-с, нашел.
Inheritance in COM does not mean code reuse. Because no implementations are associated with interfaces, interface inheritance does not mean code inheritance. It means only that the contract associated with an interface is inherited in a C++ pure-virtual base-class fashion and modified — either by adding new methods or by further qualifying the allowed usage of methods. There is no selective inheritance in COM. If one interface inherits from another, it includes all the methods that the other interface defines.
Чудненько, значит все правильно я понимаю, не совсем еще моск атрофировался ;-)Таки вот и не понятно, как может кастинг к более "старшему" (в иерархии) интерфейсу "добавить" новый метод.


Nikz

"Поддержка интерфейса" разве не означает имплементацию всех объявленных в интерфейсе [и не имплементированных, хотя в "интерфейсе" только такие и есть] методов?
Да, означает. Но, во-первых, эти обязательные к реализации методы будут private, во-вторых, иметь другие имена (добавляется префикс - имя имплементируемого класса и подчёркивание). Пример же просто ошибочный (что, если добавить Public Function Dir(), компилятор перестанет ругаться?)
Наследование реализации тут ни при чем, в общем-то.
Может, и не при чём, и я ошибся, сделав такое предположение, но вопросы, схожие с твоим, как-то больше задают люди с опытом в языках с поддержкой наследования реализации.


Nikz

junior idiot,inherits - наследует (один интерфейс другому)implements - реализует (COM-объект - интерфейс)


Nikz

junior idiot,здесь нет иерархии (вот было бы чудесно в VB поиметь все проблемы, связанные со множественным наследованием :-\), и в VB нет зарезервированного слова Inherits.Nikz,так вот, если цель - отвязаться от конкретной библиотеки, но не превращать код на VB в код на VBScript :), то см. приложенный файл. Требуется библиотека OLELIB.TLB отсюда. cкачать


Nikz

Пример же просто ошибочный (что, если добавить Public Function Dir(), компилятор перестанет ругаться?)
Ну к очепяткам не обязательно придираться)) если Opens() заменить на Class1_Opens(), то ругаться не перестанет, и если при этом же добавить Public Function Class1_Dir() - вот тогда только и перестанет, т.е. все методы имплементируемого интерфейса обязательно надо определить, как ни крути.А вот то что их можно объявить Private (при этом сохранив доступ к имплементации в потомке через каст к предку), это интересно ;-)
inherits - наследует (один интерфейс другому)implements - реализует (COM-объект - интерфейс)
Спасибо, я в курсе; могу перефразировать: нельзя отнаследовать и не реализовать.
junior idiot,здесь нет иерархии (вот было бы чудесно в VB поиметь все проблемы, связанные со множественным наследованием :-\), и в VB нет зарезервированного слова Inherits.
Где "здесь"? Технология СОМ, являясь более или менее кое-как основанной на ООП, имеет модель наследования, и даже множественного, а VB целиком и полностью основан на этом самом СОМ. Implements - как раз и означает, что новый класс должен полностью имплементировать интерфейс предка (что вполне себе можно называть "наследованием"), что позволяет впоследствии объекты потомка кастовать к типу предка (что дает еще больше оснований рассматривать их как родитель-потомок, т.е. как связанные отношением "наследования")."Implements Class1Implements Class2"Добро пожаловать в множественные проблемы наследования СОМ, перекочевавшие в VB ;-) Тут-то, наверное, проектировщиков VB и посетила мысль при имплементации методов родителя дописывать к ним имя базового класса.


Nikz

Class1:
Public Function Dir()
 MsgBox "Class1: Class1_Dir()"
End Function
Public Function Opens()
 MsgBox "Class1: Class1_Opens()"
End Function
Class2:
Implements Class1
Private Function Class1_Dir()
 MsgBox "Class2: Class1_Dir()"
End Function
Public Function Class1_Opens()
 MsgBox "Class2: Class1_Opens()"
End Function
Public Function Dir()
 MsgBox "Class2: Class2_Dir()"
End Function
Private Sub Form_Load()
 Dim o1 As Object, o2 As Object
 Dim c1 As Class1, c2 As Class2
 
 Set c2 = New Class2
 c2.Dir
 Set c1 = c2
 c1.Dir
 c1.Opens
 
 Set o2 = New Class2
 o2.Dir ' - результат как c2.Dir
 Set o1 = o2
 o1.Dir ' - результат как c2.Dir (а не c1.Dir)
 o1.Opens ' - та же ошибка, что у Nikz
 o1.Class1_Opens ' - результат как c1.Opens
End Sub
Сомневаюсь, что если СОМ-сервер реализован не на VB, то такая же фишка прокатит.Да и вообще не факт что проблема такая же (private-изация метода Dir в Connection), но попробовать можно ;-)


Nikz

забить не получится, придется аццки хачитьвот я собственно и ищу способ как указать какой именно интерфейс мне нужен, только сделать это надо путем использования стринговой константы
Начинается все с ObjPtr(con) — этим способом ты получишь адрес vtable интерфейса Connection. Как получить адрес IFTP относительно Connection не имею представления, ищи сам. Когда найдешь этот адрес, тебе нужно будет относительно него получить адреса функций этого интерфейса, это делается по формуле pFuncPtr = pVTableHead + (nEntry - 1) * 4. Чтобы узнать порядковый номер функции, воспользуйся утилитой OleView из комплекта VS6, какой она будет по счету в idl-исходнике, такой и номер. Далее тебя подстерегает генеральная засада: единственный способ вызвать функцию по указателю в VB это CallWindowProc, о чем подробно расписано здесь. Начни поиски с прочтения этой статьи, скорее всего она отобьет охоту этим заниматься. Самый простой выход — написать библиотеку-переходник, которую и подключать поздним связыванием.Еще инфа по теме и тут немного.


Nikz

Бенедикт,Спасибо, интересный пример, но в этой ситуации отвязаться от раннего связывания с библиотекой нужно вообще на всем компьютере, а не только в одном этом проекте.Antonariy,мда, это жесть, будем разбиратьсявсем спасибо за советы, есть пища для размышленийа вообще, может быть я не в ту сторону лезу? причина по которой нужно отвязаться от раннего связывания с библиотекой заключается в том что для этой библиотеки было несколько релизов. Соответственно у кучи пользователей (сотни их) стоят разные версии библиотеки. И компилялась она судя по всему не в режиме binary compatible (могу ошибаться с названием, я с библиотеками дела мало имел), то есть GUID у разных релизов библиотеки разный. Поэтому когда когда я ставлю галку в референсах на своей машине (библиотека используется в экселевском спредшите) оно будет работать только у юзеров у которых библиотека того же релиза что и у меня. У пользователей другого релиза библиотеки референс валится, и лечится это удалением Missing Reference из списка и добавлением установленной на том компьютере. Объяснить пользователям как сделать это нереально. Самому поправить у всех тоже, так как много их, и география очень широкая.Как вариант сделать инсталлятор с какой то одной версией длл и обязать всех поставить его, но опять же тетенька бухгалтер вряд ли сможет даже запустить инсталляшку, не говоря о том что exe и msi файлы во многих почтовых программах блокируются поэтому ему надо сначала изменить расширение, а потом объяснить юзеру как поменять расширение обратно чтобы можно было запустить инсталляшку и т.д. Юзеры в массе своей умеют только открыть документ эксель и нажать на нужные кнопки...поэтому идея в том чтобы как то обойти это внутри vba кода


Nikz

мда, во огород то нагородили из-за неправильной разработки dll.делайте TLB и её уже в проект подключайте.


Nikz

TLB не поможет.Nikz, нужно вообще не ставить ссылок на эту либу, добавить ссылку на Microsoft Visual Basic for Applications Extensibility 5.3 (хотя наверное не обязательно), а при открытии файла выполнять ThisWorkbook.VBProject.References.AddFromFile "путь к либе".