Нужен интерфейс с методом write(vStr as Variant)

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Нужен интерфейс с методом write(vStr as Variant)

Сообщение Antonariy » 29.06.2007 (Пт) 10:08

Позарез нужно создать класс, имеющий метод write, что есть зарезервированное слово. Мне почему-то кажется, что этого можно добиться унаследовав интерфейс с таким методом из какой-нибудь tlb. Может кто-нибудь знающий напишет его исходник, а я попробую компильнуть midl'ом?
Лучший способ понять что-то самому — объяснить это другому.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 29.06.2007 (Пт) 10:34

Код: Выделить всё
[
  uuid(55BEB11C-FCD7-4E05-A06D-10772E368783),
  version(1.0),
  helpstring("Project1 Library")
]
library Project1
{
  [
    uuid(9CF0484D-9454-477D-BC46-D59DBB1A0444),
    version(1.0),
    dual,
    oleautomation
  ]
   interface IClass1: IDispatch
  {
    [id(0x00000001)]
    HRESULT _stdcall Write([in] VARIANT vStr );
  };
};
Изображение

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.06.2007 (Пт) 10:55

midl писал(а):C:\Distr\Visual Studio 6\VC98\BIN>"C:\Distr\Visual Studio 6\VC98\BIN\MIDL.EXE" C:\resv.txt /newtlb /tlb c:\Resv.tlb
Microsoft (R) MIDL Compiler Version 5.01.0164
Copyright (c) Microsoft Corp 1991-1997. All rights reserved.
Processing C:\resv.txt
resv.txt
Processing .\oaidl.idl
oaidl.idl
Processing .\objidl.idl
objidl.idl
Processing .\unknwn.idl
unknwn.idl
Processing .\wtypes.idl
wtypes.idl
midl\oleaut32.dll : error MIDL2020 : error generating type library : Could not add UUID, STDOLE2.TLB probably needs to be imported : IDispatch

C:\Distr\Visual Studio 6\VC98\BIN>
Лучший способ понять что-то самому — объяснить это другому.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 29.06.2007 (Пт) 11:05

Ну добавь туда строку importlib("stdole2.tlb"); -- я забыл :-(
Изображение

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.06.2007 (Пт) 11:14

А куда конкретно? Из синтаксиса не совсем ясно.
Лучший способ понять что-то самому — объяснить это другому.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 29.06.2007 (Пт) 11:24

После "library Project1 {"
Изображение

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.06.2007 (Пт) 12:09

Спасибо, все почти получилось.
Write работает если класс на этом интерфейсе юзается из VB, но я хочу его подсунуть скрипт контролу, а он при вызове говорит "Объект не поддерживает это свойство или метод". Это какое-то ограничение или я что-то упустил?

Кстати когда-то давно я интересовался способом, которым можно было бы заюзать зарезервированные слова, решения найдено не было.
Лучший способ понять что-то самому — объяснить это другому.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 29.06.2007 (Пт) 12:17

Что-то упустил.
VB6 поддерживает классы с несколькими независимыми интерфейсами IDispatch, в нарушение COM :-(
Тебе нужно сделать функцию, которая бы кастовала твой объект к нужному (унаследованному) интерфейсу, и из скрипта вызывать её.
Изображение

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.06.2007 (Пт) 13:06

Что сделано:
Из исходника - resv.tlb "VB6RW.VB6Reserved".
LibResvTest.dll:
Код: Выделить всё
'интерфейс ITest
Public Property Get Result() As String
End Property

'класс ResvTest
Implements VB6Reserved
Implements ITest
Private LenTxt As Long
Private TxtBuf As String 
Private LenBuf As Long

Private Sub Class_Initialize()
    LenBuf = 255
    LenTxt = 0
    TxtBuf = Space(LenBuf)
End Sub

Private Property Get ITest_Result() As String
    LenBuf = LenTxt
    Result = Left$(TxtBuf, LenTxt)
End Property

Private Sub VB6Reserved_write(ByVal vStr As Variant)
Dim LenStr As Long
Dim nStr As String
    nStr = CStr(vStr)
    LenStr = Len(nStr)
    If LenStr = 0 Then Exit Sub
    If (LenTxt + LenStr) > LenBuf Then
        LenBuf = (LenTxt + LenStr) * 2&
        TxtBuf = TxtBuf & Space$(LenBuf)
    End If
    Mid$(TxtBuf, LenTxt + 1&) = nStr
    LenTxt = LenTxt + LenStr
End Sub
Вызов:
Код: Выделить всё
Private Sub Command1_Click()
Dim scr As New ScriptControl
Dim d As ResvTest
    On Error GoTo errh
    scr.Language = IIf(Option1.Value, "javascript", "vbscript")
    Set d = New ResvTest
    scr.AddObject "document", d
    scr.Eval Text1.Text 'document.write('test');
    Exit Sub
errh:
    MsgBox Err.Description, vbExclamation
End Sub
Еще пробовал объявлять d1 As VB6Reserved и передавать ее в качестве "document", результат тот же.
Лучший способ понять что-то самому — объяснить это другому.

NashRus
Постоялец
Постоялец
 
Сообщения: 386
Зарегистрирован: 18.03.2006 (Сб) 1:16

Сообщение NashRus » 29.06.2007 (Пт) 14:54

tyomitch писал(а):Тебе нужно сделать функцию, которая бы кастовала твой объект к нужному (унаследованному) интерфейсу, и из скрипта вызывать её.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.06.2007 (Пт) 15:09

NashRus писал(а):
tyomitch писал(а):Тебе нужно сделать функцию, которая бы кастовала твой объект к нужному (унаследованному) интерфейсу, и из скрипта вызывать её.
Еще пробовал объявлять d1 As VB6Reserved и передавать ее в качестве "document", результат тот же.
Разве это не тоже самое? VB6Reserved и есть унаследованный интерфейс.
Код: Выделить всё
Dim d1 As VB6Reserved
Set d = New ResvTest
Set d1 = d
scr.AddObject "document", d1
Лучший способ понять что-то самому — объяснить это другому.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 29.06.2007 (Пт) 15:14

Если AddObject свой аргумент кастует к IDispatch, то он получит интерфейс ResvTest, как ни старайся :-(
Остаётся только вызов IDispatch::GetIDsOfNames перехватывать, чтобы вызовы метода "Write" перенаправлять на собственный соседний интерфейс.
Изображение

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.06.2007 (Пт) 15:31

... но на vb это не делается?..
Лучший способ понять что-то самому — объяснить это другому.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 29.06.2007 (Пт) 15:40

Делается. У edanmo раньше была масса примеров, но он старые примеры с сайта постепенно убирает.

Вкратце: первый DWORD по адресу ObjPtr(d) -- это указатель на VTbl. В ней надо заменить шестой элемент.
Изображение

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.06.2007 (Пт) 15:57

К сожалению в этом "кратце" я не особо шарю :( так что было бы неплохо "в деталях", начиная с первого dword.

Однако, если я все правильно понял, это закроет доступ к методам самого ResvTest и, тем более, прочим интерфейсам?
Лучший способ понять что-то самому — объяснить это другому.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 29.06.2007 (Пт) 15:59

Если культурно сделать, то не закроет.

Сорри, но у меня пятничным вечером просто нет сил на эксперименты. Если в выходные будут, что-нибудь напишу.
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 29.06.2007 (Пт) 18:05

Гы, не удержался-таки :-))

Код: Выделить всё
Option Explicit

Sub Main()
Dim o As Object
Set o = New Class1
o.write o.Anything
End Sub


Код: Выделить всё
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
Private Declare Function lstrcmpiW Lib "kernel32" (ByVal lpString1 As Long, ByVal lpString2 As Long) As Long
Private Const PAGE_EXECUTE_READWRITE = &H40

Public Function Anything() As String
    Anything = "Mary had a little lamb"
End Function

Public Sub Write_(ByVal vStr As Variant)
    MsgBox vStr
End Sub

Private Sub GetIDsOfNamesReplacement(ByVal riid As Long, rgszNames As Long, ByVal cNames As Long, ByVal lcid As Long, ByVal rgDispId As Long)
If 0 = lstrcmpiW(rgszNames, StrPtr("Write")) Then
    ReDim Names(cNames) As Long
    CopyMemory Names(0), rgszNames, cNames * 4
    Names(0) = StrPtr("Write_")
    Call GetIDsOfNamesOriginal(riid, Names(0), cNames, lcid, rgDispId)
Else
    Call GetIDsOfNamesOriginal(riid, rgszNames, cNames, lcid, rgDispId)
End If
End Sub

Private Sub GetIDsOfNamesOriginal(ByVal riid As Long, rgszNames As Long, ByVal cNames As Long, ByVal lcid As Long, ByVal rgDispId As Long)
'Never called
End Sub

Private Sub Class_Initialize()
Dim pVTbl As Long, OldProtect As Long
    CopyMemory pVTbl, ByVal ObjPtr(Me), 4
    VirtualProtect ByVal pVTbl, 44, PAGE_EXECUTE_READWRITE, OldProtect
    If OldProtect = PAGE_EXECUTE_READWRITE Then Exit Sub
    CopyMemory ByVal pVTbl + 40, ByVal pVTbl + 20, 4
    CopyMemory ByVal pVTbl + 20, ByVal pVTbl + 36, 4
End Sub
Изображение

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16475
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 29.06.2007 (Пт) 18:26

Эх :( А я уж сам начал писать. Обломали...

Только я использовал GetMem4 / PutMem4 для получения адреса vtable / перезаписи.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 29.06.2007 (Пт) 21:33

Тоже не удержался. У меня более универсальное решение, но реализовано в виде DLL(юзать через tlb, но регить у клиента не надо).
Суть в создании объекта - враппера для IDispatch.
То есть в IDispatchWrapper::AddName передаётся ссылка на объект куда перенаправлять вызов, имя метода в объекте, имя метода во враппере(видимое извне). После чего присваиваем враппер к переменной типа Object и она начнёт дегелировать вызовы в нужный объект.
Достоинства метода:
1) Можно зацепить несколько объектов на один IDispatch
2) Легко настраивается - 1 вызов на функцию
3) Вроде как не падает под IDE
4) Не надо имплементить никаких доп. интерфейсов
Недостатки:
1) Каждую процедуру(проперти) надо отдельно регистрировать во враппере
2) Надо таскать с собой DLL

Пример приложен.
Вложения
DWrap.rar
(27.41 Кб) Скачиваний: 178
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 29.06.2007 (Пт) 21:54

Так ведь твоя DLL не на VB...
А вопрос изначально ставился именно про возможность реализации этой фичи средствами VB.

У меня тоже, кстати, 3) Вроде как не падает под IDE и
4) Не надо имплементить никаких доп. интерфейсов.
Изображение

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 29.06.2007 (Пт) 22:06

Враппер можно и на VB переписать, тока работать помедленнее будет+ за работоспособность не ручаюсь.

Но я не настаиваю на использовании моего варианта, просто предложил, может, кому понадобиться...
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 30.06.2007 (Сб) 9:13

У твоего подхода есть ещё третий недостаток: сквозь обёртку будут недоступны дополнительные интерфейсы исходного класса.
Например, не будет работать TypeName, потому что ей нужен IProvideClassInfo.
Изображение

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 30.06.2007 (Сб) 11:24

tyomitch
В принципе это можно устранить, добавив функцию MapInterface и поправив реализацию QueryInterface(она у меня своя), чтобы она перенаправляла вызов. Только это вроде как нарушение COM, потому как IUnknown вернётся не тот потом.

Можно конечно создать отдельную vtable с адресами переходников, но это как-то очень извращённо.
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 30.06.2007 (Сб) 13:47

keks-n писал(а):Только это вроде как нарушение COM, потому как IUnknown вернётся не тот потом.

Угу, и вместо обёртки по освобождению ссылки разрушится обёрнутый объект :-)

keks-n писал(а):Можно конечно создать отдельную vtable с адресами переходников, но это как-то очень извращённо.

Про этот способ давай подробнее, а то я не въезжаю сходу...
Изображение

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 30.06.2007 (Сб) 14:14

Передаётся объект As Any сразу нужного типа. Враппер получает указатель на неизвестный ему интерфейс, также надо указывать IID и размер vtable. Враппер создаёт в памяти переходники с подменой this, т. е.
pop eax
push оригинальный_this
push огигинальный_адрес
ret
и выделяет в памяти новую vtable куда пишет адреса переходников. При этом реализация IUnknown остаётся своей, т. е. не перенаправляется.
Таким образом при вызове QueryInterface с запросом нужного интерфейса клиент получает указатель на сгенерированную vtable и инкрементится счётчик ссылок враппера. После чего все вызовы дегелируются переходниками на исходный объект(this которого был в них прописан при создании). При освобождении ссылки декрементится счётчик враппера.
При данном подходе важно знать размер vtable интерфейса.


Но это на VB уже очень сложно реализовывать, так что враппер в итоге остаётся на C++.
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 30.06.2007 (Сб) 14:40

keks-n писал(а):также надо указывать IID и размер vtable

Ну ага, размечтался. Размер vtable я ещё смогу угадать, а как угадать IID в Standard EXE?
Изображение

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 30.06.2007 (Сб) 14:50

Легко. Перенаправляем QueryInterface, делаем Set. После чего руками обнуляем переменную(PutMem4), куда шло присвоение и восстанавливаем QueryInterface. В момент вызова перехватчика получили IID.
Можно попытаться через ITypeInfo(полученный через IDispatch), но я не уверен что он после компиляции не выдаст E_NOTIMPL.
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 30.06.2007 (Сб) 15:15

Ну а если всё равно придётся патчить VTbl, тогда почему бы не пойти по более короткому пути и не пропатчить сразу GetIDsOfNames? ;-)
Изображение

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 30.06.2007 (Сб) 20:17

Но я не настаиваю на использовании моего варианта, просто предложил, может, кому понадобиться...

+ Если реализовать предложенный мною метод маппинга интерфейсов, то можно получить соборную солянку из нескольких объектов, да и IDispatch будет динамическим. Если кому то это надо - обращайтесь - допишу по уму и выложу.
З. Ы.
Могу ещё сделать маппинг неизвестного имени на функцию вида Sub OnCall(ByVal FuncName As String, Arguments() As Variant, RetVal As Variant) прикрутить кастомный тайп и т.п., но это если кому надо.
Изображение

NashRus
Постоялец
Постоялец
 
Сообщения: 386
Зарегистрирован: 18.03.2006 (Сб) 1:16

Сообщение NashRus » 02.07.2007 (Пн) 12:20

keks-n писал(а):но это если кому надо.


было бы круто, чтобы в ИДЕ интелисенс работал из XML файла, например. т.е. подключаем либу, и она там отдает, что прочла в файле. здорово так всякие разные протоколы прикладного уровня описывать. сам хотел такое сделать, но там надо педалировать тему, а временной ресурс очень ограничен.

такое есть например в ADDIN - дизайнере подключения к БД, где имена рекордсетов вылезают как свойства у коннекшена или что-то в этом духе.

След.

Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4

    TopList