26

Re: VBS: Функции COM-библиотеки в VBScript

Xameleon

штурмуя форумы либо это один человек, которому эта задача не даёт покоя. Но ответ до сих пор не найден.

Все верно! Это один человек) Я!
В августе стояла задача написать на PHP. Также мучился, искал ходы и варианты.
Сейчас на VBS....

Какие функции и методы должна реализовать обёртка на VBS ?

Все методы, которые присутствуют в библиотеке... Они все COM-овские.

Получается так:


Set FSO = CreateObject(HybridCOMLib.HybridCOM") 			' Подключаю библиотеку
FSO.Init() 													'Вызываю первую функцию, которая  как бы проверяет подключение библиотеки
er = FSO.LoadKeyStore(1, "Pasword", "C:\1.p12") 			'Затем вызываю функцию, в которой подключаю сертификат и ввожу его пароль. Сертификат записывается в ОП
er = FSO.X509ExportCertificateFromStore("", 0, (outCert)) 	' Эта функция должна вывести сертификат из ОП. Т.е. просто передать его в переменную outCert

И еще таких функций больше 30 штук различного рода...
Но у всех одинаковый интерфейс в COM-e.
(STDMETHODIMP HybridCOM::Имя_функции(INT variable1, BSTR* variable2 или BSTR variable2...)  и так далее и тому подобное)
Сейчас у меня именно не получается возвращать(перезаписывать значения переменных)

27

Re: VBS: Функции COM-библиотеки в VBScript

Xameleon

Function X509ExportCertificateFromStore(ByVal alias As String, ByVal Flags As Long, ByRef outCert As Variant) As Long
    Dim outCert_BSTR As String
    'Положили из outCert значение в подготовленную переменную нужного типа
    outCert_BSTR = outCert
    'Вызвали функцию
    X509ExportCertificateFromStore = Hybrid.X509ExportCertificateFromStore(alias, Flags, outCert_BSTR)
    'Вернули в параметр, полученное значение
    outCert = outCert_BSTR
End Function

Здорово! Спасибо за "пинок") Как запущу, так сразу отпишусь)

28

Re: VBS: Функции COM-библиотеки в VBScript

don_777_11,

Все методы, которые присутствуют в библиотеке... Они все COM-овские.

К сожалению это ни коим образом не облегчает задачу, так как COM лишь соглашение об интерфейсах и правилах их работы, но не о типе передаваемых данных.

В связи с этим я пока вижу несколько путей решения задачи.

1) Создавать ActiveX обёртку на библиотеку. Это значит, что Вам нужно на любом доступном Вам языке программирования скомпилировать компонент реализующий тот принцип оборачивания, который я привёл примером в сообщении выше. При этом исходный COM объект может создаваться внутри обёртки, может передаваться извне. Тут как Вам удобно.

2) Можно попробовать написать универсальный Wrapper, позволяющий на вход принимать ссылку на объект, а дальше через специальный интерфейс подготавливать для него параметры и методом типа CallFunction(...) вызывать у объекта его функции и методы. Звучит не совсем понятно. Проще - посмотрите в интернете DynamicWrapperX от уважаемого коллеги YMP.  К сожалению, на сколько мне известно, его Wrapper не умеет работать с внешними COM объектами для вызова их методов через прослойку, а только с stdcall и cdecl функциями в библиотеках.

(*) Но надо понимать, что при выборе первого и второго варианта каждый пользователь обречён на регистрацию этого компонента-обёртки у себя в реестре. Т.е это уже не portable решение (не так, что скопировал, запустил и работает.). Либо придётся заморачиваться с манифестами... А это уже совсем другая история.

3) В принципе, возможно реализовать EXE wrapper на COM библиотеку и общаться с ним через StdIn и StdOut....А уже обмен обернуть в VBS вызовы. Но это такой изврат, который Маркизу де Саду даже не снился. Зато такой вариант не требует регистрации чего-либо в реестре.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

29

Re: VBS: Функции COM-библиотеки в VBScript

Xameleon пишет:

Но надо понимать, что при выборе первого и второго варианта каждый пользователь обречён на регистрацию этого компонента-обёртки у себя в реестре.

Может быть, dll промежуточного объекта подсовывать вместо dll основного, соответственно она будет загружаться по CreateObject, а потом уже будет загружать основную dll и создавать основной объект. Это уже, конечно, тоже махинации. Можно, наверно, основную dll засунуть в ресурсы обёртки и загружать оттуда. Вроде бы есть какой-то способ.

30

Re: VBS: Функции COM-библиотеки в VBScript

YMP,

Может быть, dll промежуточного объекта подсовывать вместо dll основного, соответственно она будет загружаться по CreateObject, а потом уже будет загружать основную dll и создавать основной объект.

Тогда возникает вопрос как создавать инстанс исходного компонента в оболочке ). Ведь обычный вызов CoCreateInstance обратится в реестр, там найдёт путь до DLL и попробует создать её от туда, а там лежит наша DLL. )

Помню был вариант на форуме VBStreets. Приходилось делать хук - перехват API, подменять данные... Но это черевато большими косяками в работе.

Можно, наверно, основную dll засунуть в ресурсы обёртки и загружать оттуда. Вроде бы есть какой-то способ.

+ открыть спойлер

https://cs.pikabu.ru/images/big_size_comm/2013-09_1/13783048769859.jpeg

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

31

Re: VBS: Функции COM-библиотеки в VBScript

Xameleon

Получилось вывести сертификат. Даже не изменяя предложенной вами функции) Огромное спасибо!
Значит буду писать обёртку.

Пока вопросов больше нет.
Очень благодарен за оказанное внимание и советы!

32

Re: VBS: Функции COM-библиотеки в VBScript

don_777_11,

Получилось вывести сертификат. Даже не изменяя предложенной вами функции) Огромное спасибо!
Значит буду писать обёртку.

Чудеса ). Неожиданно. А на чём собираетесь обёртку писать, если не секрет ?

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

33

Re: VBS: Функции COM-библиотеки в VBScript

Xameleon пишет:

Тогда возникает вопрос как создавать инстанс исходного компонента в оболочке ). Ведь обычный вызов CoCreateInstance обратится в реестр, там найдёт путь до DLL и попробует создать её от туда, а там лежит наша DLL. )

Так для этого же dll и экспортирует функцию DllGetClassObject. Из неё получаем объект ClassFactory и создаём объект нужного класса методом CreateInstance. То же самое делает CoCreateInstance.

Xameleon пишет:

Помню был вариант на форуме VBStreets. Приходилось делать хук - перехват API, подменять данные...

+ открыть спойлер

https://cs.pikabu.ru/images/big_size_comm/2013-09_1/13783048769859.jpeg


Хотя, может, там всё же что-то другое было.

34

Re: VBS: Функции COM-библиотеки в VBScript

YMP,

Так для этого же dll и экспортирует функцию DllGetClassObject. Из неё получаем объект ClassFactory и создаём объект нужного класса методом CreateInstance. То же самое делает CoCreateInstance.

Кривить душой не буду. Сам не пробовал. )) Возможно и так как Вы говорите можно реализовать.

Порылся на форуме, нашёл - http://bbs.vbstreets.ru/viewtopic.php?f=28&t=45527. Вроде бы оно

Если открыть архив, то в коде модуля modTrickUnregControl.bas, автор пишет:

' Функция перехвата CLSIDFromProgID
' При передаче нашего ProgID мы обманываем вызывающую сторону, делая вид что в реестре есть запись HKCR\CLSID\ProgID\CLSID

' Функция перехвата CoGetClassObject
' Здесь мы создаем объект (обычно фабрики классов), используя не запись в реестра, а переданные параметры для имени библиотеки

' Функция перехвата OleRegGetMiscStatus
' Здесь мы обманываем вызывающую строну, делая вид что в реестре есть запись HKCR\CLSID\MiscStatus и подсовываем ей свой

' Функция перехвата RegQueryValue
' Здесь мы обманываем вызывающую строну, делая вид что в реестре есть записи HKCR\CLSID\(TypeLib, InprocServer32, Version) и подсовываем ей свой

Вот об этом я и вспомнил. )

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

35

Re: VBS: Функции COM-библиотеки в VBScript

Ну, там задача явно более масштабная.

создание условий при которых VB "думает" что библиотека зарегистрирована.

А здесь-то она зарегистрирована, так что ситуация другая. Имитировать наличие записей в реестре не нужно. VBS всего лишь запросит IDispatch, который обёртка ему и выдаст. А основной объект создаст сама. Механизм, описанный мной, работает, мне ведь пришлось его реализовывать в DWX. Никаких шорткатов от системы я не использовал. IClassFactory, IUnknown, IDispatch — всё ручной работы.

36

Re: VBS: Функции COM-библиотеки в VBScript

YMP, Хм... Вот как. А зачем такая возможность понадобилась в DWX, если не секрет ?

А здесь-то она зарегистрирована, так что ситуация другая. Имитировать наличие записей в реестре не нужно.

Да, библиотека то зарегистрирована, но пути то тогда будут смотреть на подменённую DLL ? Но если создавать, как Вы говорите, то получается при этом можно как-то напрямую указывать путь до нужного DLL ?

Не совсем понимаю как, если

STDAPI DllGetClassObject

REFCLSID rclsid,  // Уникальный идентификатор объекта
REFIID riid,      // Ссылка на интерфейс связанный с объектом
LPVOID * ppv      // адрес переменной которая получит интерфейс
);

Функция на вход же принимает rclsid ?

Попробую более подробно пояснить, что мне пока непонятно.

Представим:
У нас имеется некая COM.dll, сделанная сторонним разработчиком (будем называть её сторонняя).
Мы делаем свою COM.dll обёртку (будем называть её наша)

Изначально регистрируем стороннюю COM.dll через regsvr32.
Получаем в реестре некие записи:
HKEY_CLASSES_ROOT\COM.Object\CLSID c параметром REG_SZ и некоторым классидом (Например:{0E57F1D5-1FСE-11D0-8FF2-00A0В10018BC})
и
HKEY_CLASSES_ROOT\CLSID\{0E57F1D5-1FСE-11D0-8FF2-00A0В10018BC}
в которой несколько подразделов отвечающих за информацию о компоненте в числе которых подраздел InProcServer32, который содержит параметр по умолчанию REG_SZ с путём до зарегистрированной DLL - (например:C:\MyActiveX\COM.dll)

Теперь мы берём и подменяем зарегистрированную DLL на нашу, а исходную называем COM1.dll
Получаем:
C:\MyActiveX\COM.dll - наша DLL
C:\MyActiveX\COM1.dll - переименованная сторонняя DLL

Теперь, к примеру, в скрипте выполняем CreateObject("COM.Object")

По идее должен создаться наш компонент оболочки, если описание в ветках реестра не противоречит описанию объекта внутри DLL.
Далее, как Вы говорите, мы должны вызвать DllGetClassObject ? Я пока не имел опыта работы с ней и сейчас поиск в гугл не дал мне понятного ответа.
Но я так понимаю, что она должна обратиться в реестр для получения информации по переданному classid-у, а там путь, указывающий на "C:\MyActiveX\COM.dll". Т.е на нашу DLL ?
Или всё работает иначе и я ошибаюсь ?

(*) Ааа.... Кажется понял...

Первым делом вызывается CoLoadLibrary или LoadLibrary для подгрузки библиотеки в память, далее получаем указатель на экспортируемую DllGetClassObject и передаём ей классид создаваемого объекта ? И по идее ей тогда в реестр обращаться нет нужды ?

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

37

Re: VBS: Функции COM-библиотеки в VBScript

Xameleon
Всё верно. Обёртка же по идее уже знает, где лежит основная dll и знает CLSID нужного объекта и ID нужного интерфейса. Реестр ей не нужен. А создавая основной объект, она взаимодействует только с кодом внутри основной dll и получает от неё объект. Основной dll реестр для этого тоже ни к чему, она и так про себя всё знает.

Насчёт

REFIID riid,      // Ссылка на интерфейс связанный с объектом

уточню, что тут передаётся ID нужного интерфейса ClassFactory (есть просто IClassFactory и есть более продвинутый IClassFactory2), а не интерфейса того объекта, чей ID в rclsid. Интерфейс объекта запрашивается уже потом, в методе CreateInstance.

38 (изменено: YMP, 2019-01-18 20:32:38)

Re: VBS: Функции COM-библиотеки в VBScript

Кстати, при создании объекта нужно ещё его ThreadingModel учитывать. Не должно быть противоречия с той, что была указана в CoInitializeEx потока, где работает создающий объект код. Иначе, я так понимаю, для объекта нужно создавать отдельный поток с соответствующей ему инициализацией COM.

39

Re: VBS: Функции COM-библиотеки в VBScript

YMP, да, именно так. Вот я как раз об этом и думал, когда написал

По идее должен создаться наш компонент оболочки, если описание в ветках реестра не противоречит описанию объекта внутри DLL.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

40

Re: VBS: Функции COM-библиотеки в VBScript

Хотя, если обёртка именно подсовывается вместо, то она будет создана системой с учётом записей в реестре для основного объекта, так что ThreadingModel потока будет правильной. Я скорее подумал о создании объекта, в реестре не записанного, где уже мы должны помнить о его особенностях. Но это же не наш случай.

41

Re: VBS: Функции COM-библиотеки в VBScript

Могу ошибаться, подтверждения в инете не нашёл, но мне всегда казалось, что ThreadingModel задаётся для DLL при компиляции и по идее если в реестре будет указана иная ThreadingModel, то может не работать ?

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

42

Re: VBS: Функции COM-библиотеки в VBScript

Если однопоточный объект заявит себя многопоточным, то могут быть проблемы, конечно, т.к. он же не умеет разруливать одновременные вызовы из разных потоков. При обратной ситуации проблем не должно быть, по идее.

Хотя я тут, похоже, не знаю каких-то тонкостей, т.к. не понимаю, зачем нужна поточная модель "both". Казалось бы, если объект многопоточный, то и шабаш. Чего ещё надо-то? Зачем специально указывать ещё и возможность работы в однопоточном апартменте? Что-то тут нечисто. Казалось бы это само собой разумеется, но нет, зачем-то указывают. Какую способность объекта это предполагает? Непонятно.

43

Re: VBS: Функции COM-библиотеки в VBScript

https://rsdn.org/article/com/apartmnt.xml.

Вот тут хорошо расписано. Я даже вроде бы понял. ) Хотел коротко пояснить, но чую, что буду дублировать статью.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !
+ YMP

44 (изменено: YMP, 2019-01-20 20:21:14)

Re: VBS: Функции COM-библиотеки в VBScript

Спасибо, хорошая статья.

ПРИМЕЧАНИЕ
COM старается помещать внутрипроцессные объекты в то же подразделение, что и у создающих их потоков.

Т.е. те, кто помечает свои многопоточные объекты как "free", почему-то не желают, чтобы они создавались в STA. Наверно, просто хотят избежать затрат на системный маршалинг вызовов, который их объектам не нужен.

А вот это мне как-то не приходило в голову:

Даже ThreadingModel=Apartment объекты должны быть "немного потокобезопасными", поскольку ThreadingModel=Apartment не мешает нескольким объектам созданным в одной DLL ссориться из-за разделяемых данных (в случае, если они попадут в разные STA).

Вот ещё важная вещь:

Некоторые программисты используют локальную память потока (TLS) как временное хранилище данных. Предположим, вы реализуете COM метод и вам нужно сохранить некоторую информацию о текущем вызове и которая понадобится при следующем вызове. Вы можете воспользоваться TLS. И в STA это будет работать. Но если объект, который вы пишете находится в MTA, вы должны избегать TLS как чумы.

Почему? Потому что вызов, попадающий в MTA перемещается в RPC потоки. Каждый вызов запросто может попасть в различные RPC потоки, даже если все они были из одного потока и одного и того же подразделения. Поток B не имеет доступа к локальной памяти потока A, так что если вызов номер 1 попадает в поток A и объект помещает данные в TLS, то если вызов номер 2 попадает в поток B и объект пытается получить данные записанные в TLS при первом вызове, он их не получит. Никак.

Избегайте использовать TLS для сохранения данных между вызовами MTA-шных объектов. Это будет работать, только если все вызовы придут из одного и того же объекта и из того же самого MTA, в котором находится объект.

У меня в TLS может храниться код последней ошибки, возвращаемый вызовом GetLastError после вызова API-функции. Его потом можно запросить, вызвав метод LastError.

Конечно, в VBS и JS, выполняемых Windows Script Host, DWX в MTA не попадёт. Но теоретически, т.к. он вообще-то потокобезопасный (2-я версия), где-то и когда-то может попасть. При этом, правда, ещё надо, чтобы его вызывали из STA. Довольно сложный сценарий.

45

Re: VBS: Функции COM-библиотеки в VBScript

Xameleon
Добрый день.  Я просто решил писать на VBA (Оказалось, что никому это не важно...). Просто объявляю переменную и обозначаю ее тип.
Т.е. я просто прописал так:

Dim outCert As String
err = HCL.X509ExportCertificateFromStore("", 0, outCert)

Ну вот сейчас принимаюсь за создание всего кода. Думаю теперь особых проблем не должно быть)
Спасибо большое за советы) Обычно у меня так всегда, нужен небольшой пинок)

46

Re: VBS: Функции COM-библиотеки в VBScript

Xameleon
Кстати, еще появился такой вопрос.
Типа LongLong в VB нет?

В СИ++ время в секундах с 1970 года передается как LongLong (Это время может быть разным - и 2,365e+9(2045 год), и 1,104e+9 (2005 год))
Получается для хранения таких больших переменных что можно использовать в VB?

47

Re: VBS: Функции COM-библиотеки в VBScript

Хм. Тут надо экспериментировать.

Вот справочник по типам переменных в VB
http://www.codenet.ru/progr/vbasic/bit/Variables.php

Для того, чтобы VBScript адекватно принял дату, ему на выход нужен либо сразу Date, либо Date возвращённый через Variant, как в примере Выше, если отдавать через параметры. Я бы попробовал поэкспериментировать с ним (Date), а потом уж если не сложится, то попробовать универсальный вариант - буфер Byte().

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !