Передача интерфейса
От: LelicDsp Россия  
Дата: 09.03.05 16:22
Оценка:
Вот вопрос от не большого знатока ATL/COM.
Есть интерфейсы скажем
interface IReader : IUnknown
{
  void Read(BSTR* pStr);
};
//

и классы
class CNetReader: IDispatchImpl<INetReader>, public IReader;
class CPortReader: IDispatchImpl<IPortReader>, public IReader;

class CMyTest DispImpl<IMyTest>
{
  IReader* m_pReader
public:
  void Bind(IReader* pReader){m_pReader = pReader;};
  void Test(){m_pReader->Read(...);};
};

Идея понятна, есть класс CMyTest, который может работать через разный транспорт. С реализацией не получается. Необходимо, что бы клиенское приложение можно было написать на VBA, VB, VBScript, Perl ну и C++ естественно.

Если пишу так как указано выше — ругается перл. Он не понимает не-dispatch инетрфейсов (в данном случае iReader). Если передаю как IUnknown IDispatch или VARIANT не работает VBA по совершенно непонятной причине. Замучался уже, время жалко тратить на изучение всего этого добра, поможите кто может.

Спасибо.
Re: Передача интерфейса
От: Кодёнок  
Дата: 10.03.05 07:20
Оценка:
Соглашения OLE Automation требуют, чтобы все методы возвращали HRESULT. Может поэтому VBA не работает?
Re: Передача интерфейса
От: SaloS http://salos.narod.ru/
Дата: 10.03.05 08:30
Оценка:
Здравствуйте, LelicDsp, Вы писали:

LD>Вот вопрос от не большого знатока ATL/COM.

LD>Есть интерфейсы скажем
LD>[midl]
LD>interface IReader : IUnknown
LD>{
LD> void Read(BSTR* pStr);
LD>};
а почему бы тебе не наследовать от IDispatch?
WTL Helper и WTL Wizards помощники для WTL, скачать отсюда http://salos.narod.ru
Re[2]: Передача интерфейса
От: LelicDsp Россия  
Дата: 10.03.05 09:06
Оценка:
Здравствуйте, SaloS, Вы писали:

SS>Здравствуйте, LelicDsp, Вы писали:


LD>>Вот вопрос от не большого знатока ATL/COM.

LD>>Есть интерфейсы скажем
LD>>[midl]
LD>>interface IReader : IUnknown
LD>>{
LD>> void Read(BSTR* pStr);
LD>>};
SS>а почему бы тебе не наследовать от IDispatch?

Если я правильно понимаю, то только один из реализуемых интерфейсов может быть Dispatch. Соответственно, если уменя есть класс CNetReader то он реализует дуальный интерфейс INetReader, а IReader должен быть обычным. Хотя я пробовал и такой вариант.
Re[2]: Передача интерфейса
От: LelicDsp Россия  
Дата: 10.03.05 09:12
Оценка:
Здравствуйте, Кодёнок, Вы писали:

Кё>Соглашения OLE Automation требуют, чтобы все методы возвращали HRESULT. Может поэтому VBA не работает?

Да нет, это конечно не причем. И метод, HRESULT, я текст по памяти просто воспроизводил. Вот полный IDL:

import "oaidl.idl";
import "ocidl.idl";


    [
        object,
        uuid(3A84A871-05FB-4194-BFAD-459C02F6030E)
    ]
    interface IReader : IUnknown
    {
        [helpstring("method Read")] HRESULT Read([out, retval]BSTR* pRes);
    };



    [
        object,
        uuid(F3864754-1039-4123-BEAE-DD2045704005),
        dual,
        helpstring("IComPort Interface"),
        pointer_default(unique)
    ]
    interface IComPort : IDispatch
    {
    };

    [
        object,
        uuid(9A59DCA7-9B3A-4588-A94D-3C001A2311C0),
        dual,
        helpstring("ITcpClient Interface"),
        pointer_default(unique)
    ]
    interface ITcpClient : IDispatch
    {
    };


[
    uuid(2C752472-2A78-4F27-AF97-1D64F27A9277),
    version(1.0),
    helpstring("MT 1.0 Type Library")
]
library MTLib
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    interface IReader;

    [
        uuid(610BEAB6-393B-46F7-A8C2-58A2DF19ADE9),
        helpstring("_IReaderEvents Interface")
    ]
    dispinterface _IReaderEvents
    {
        properties:
        methods:
        [id(1), helpstring("method DataReady")] HRESULT DataReady();
    };
    [
        object,
        uuid(0541A5A1-ED88-4903-81E5-6DC65ACA8452),
        dual,
        helpstring("ISimpleTester Interface"),
        pointer_default(unique)
    ]
    interface ISimpleTester : IDispatch
    {
        [id(1), helpstring("method Bind")] HRESULT Bind([in]IUnknown* pUnk);
        [id(2), helpstring("method Exec")] HRESULT Exec();
        [propget, id(3), helpstring("property Result")] HRESULT Result([out, retval] BSTR *pVal);
    };

    [
        uuid(8CCFBFA0-94FE-46F1-A4ED-3639B18ED6A6),
        helpstring("ComPort Class")
    ]
    coclass ComPort
    {
        [default] interface IComPort;
        [default, source] dispinterface _IReaderEvents;
    };

    [
        uuid(F23FC867-2431-4294-8CD0-97AF10750B5D),
        helpstring("TcpClient Class")
    ]
    coclass TcpClient
    {
        [default] interface ITcpClient;
        [default, source] dispinterface _IReaderEvents;
    };
    [
        uuid(D0518F05-0BF7-4A4C-BAAC-1376D273EF22),
        helpstring("SimpleTester Class")
    ]
    coclass SimpleTester
    {
        [default] interface ISimpleTester;
    };
};

А классы явно наследуют IReader:

class ATL_NO_VTABLE CComPort : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CComPort, &CLSID_ComPort>,
    public ISupportErrorInfo,
    public IConnectionPointContainerImpl<CComPort>,
    public IDispatchImpl<IComPort, &IID_IComPort, &LIBID_MTLib>,
    public IReader
{
///
}
Re: Передача интерфейса
От: Left2 Украина  
Дата: 10.03.05 10:04
Оценка:
Насколько я полнял твою проблему (не уверен что до конца, в первом сообщении у тебя одни классы, в IDL — другие, и не совсем понятно кто из них как должен друг с другом взаимодействовать) — тебе нужно наследовать IReader от IDispatch, INetReader и IPortReader — от IReader.
Re[2]: Передача интерфейса
От: LelicDsp Россия  
Дата: 10.03.05 10:11
Оценка:
Здравствуйте, Left2, Вы писали:

L>Насколько я полнял твою проблему (не уверен что до конца, в первом сообщении у тебя одни классы, в IDL — другие, и не совсем понятно кто из них как должен друг с другом взаимодействовать) — тебе нужно наследовать IReader от IDispatch, INetReader и IPortReader — от IReader.


Классы разные, т.к. вчера писал по памяти и очень быстро, просто сильно достало сидеть и разбираться в мелкософтских ограничениях.

Предлагаемое тобой решение это как раз то, что было сделано с самого начала. Не работает. роблема в том, что нельзя имплементировать болеее одного Dispatch интерфейса. Точнее имплементировать можно, но виден будет только [default]. Спасибо MS, испоганили хорошую идею. Полный маразм, какой смысл вообще в абстрактных интерфейсах, если реально один класс = один интерфейс.
Re[3]: Передача интерфейса
От: LelicDsp Россия  
Дата: 10.03.05 10:21
Оценка:
Здравствуйте, LelicDsp, Вы писали:

LD>Здравствуйте, Left2, Вы писали:


L>>Насколько я полнял твою проблему (не уверен что до конца, в первом сообщении у тебя одни классы, в IDL — другие, и не совсем понятно кто из них как должен друг с другом взаимодействовать) — тебе нужно наследовать IReader от IDispatch, INetReader и IPortReader — от IReader.


LD>Классы разные, т.к. вчера писал по памяти и очень быстро, просто сильно достало сидеть и разбираться в мелкософтских ограничениях.


LD>Предлагаемое тобой решение это как раз то, что было сделано с самого начала. Не работает.

Поясню что не работает. При работе из perl (VBS вероятно, тоже) вываливаемся с ошибкой про несовместимыен типы. Как Я понимаю ее происхождение: скажем CComPort реализует IComPort & IReader, оба IDispatch. Когда вызывается CSimpleTester.Bind с указателем на инстанцию CComPort код маршалинга решает в рантайме проверить что за класс ему передали, желая убедиться, что это IReader, а делает он это через IDispatch. А IDispatch он получает от [default] интерфейса сиречь IComPort. Соответственно, при проверке типов он вылетает c ошибкой, т.к. не находит ожидаемого интерфейса IReader.

Вообще жто мои догадки, т.к. я не знаю как работает маршалинг при передаче Dispatch интерфейсов, но из получается, что именно так он и работает.
Re[3]: Передача интерфейса
От: Left2 Украина  
Дата: 10.03.05 10:21
Оценка:
Погоди на MS грешить
У тебя и будет 1 Idispatch-унаследованый интерфейс в классе — IPortReader или INetReader
Имплементацию Ireader выносишь в шаблонный класс IreaderImpl

class ATL_NO_VTABLE CComPortReader :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CComPort, &CLSID_ComPort>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CComPort>,
public IDispatchImpl<IreaderImpl<IPortReader >, &IID_IPortReader, &LIBID_MTLib>,
{
///
}

В карте интерфейсов выставляешь 3 интерфейса — IPortReader, Ireader и IDispatch

Это вообщем-то стандартное решение для имплементации интерфейсов, унаследованных от наследников Idispatch — порывшись по форуму найдёшь немало таких примеров
Re[4]: Передача интерфейса
От: LelicDsp Россия  
Дата: 10.03.05 10:24
Оценка:
Здравствуйте, Left2, Вы писали:

L>В карте интерфейсов выставляешь 3 интерфейса — IPortReader, Ireader и IDispatch


L>Это вообщем-то стандартное решение для имплементации интерфейсов, унаследованных от наследников Idispatch — порывшись по форуму найдёшь немало таких примеров


Спасибо большое, вот это уже что-то реальное, буду пробовать!
Хотя пока и не въехал
Re: Передача интерфейса
От: Vi2 Удмуртия http://www.adem.ru
Дата: 10.03.05 10:42
Оценка:
Здравствуйте, LelicDsp, Вы писали:

LD>Идея понятна, есть класс CMyTest, который может работать через разный транспорт. С реализацией не получается. Необходимо, что бы клиенское приложение можно было написать на VBA, VB, VBScript, Perl ну и C++ естественно.


Я так понимаю, что у тебя в клиенте есть экземпляры-коклассов CMyTest, CNetReader и/или CPortReader, один из которых ты передаешь экземпляру CMyTest для какой-то работы. Так что все твои умеющие работать с dispatch интерфейсами клиенты имеют правильные и понятные им указатели. И передаче экземпляра CXxxReader-а в экземпляр CMyTest они попытаются передать то, что имеют.

Поэтому правильно специфицировать метод Bind так, как ты сделал в IDL файле
      [id(1), helpstring("method Bind")] HRESULT Bind([in]IUnknown* pUnk);

Это универсальный формат, в котором любой клиент передаст имеющийся указатель. Но тогда нужно корректно отработать внутри метода:
      void Bind(IUnknown* pReader) { return AtlComQIPtrAssign( (IUnknown**) &m_pReader, pReader, __uuidof(IReader) ); }

PS
Единственное, что можно еще заметить, — IReader не является OLE-совместимым и требует прокси DLL для маршаллинга. Поставь ключевое слово oleautomation в определение IReader:
[
      object, oleautomation,
      uuid(3A84A871-05FB-4194-BFAD-459C02F6030E)
]
interface IReader : IUnknown
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[2]: Передача интерфейса
От: LelicDsp Россия  
Дата: 10.03.05 10:47
Оценка:
Vi2>Поэтому правильно специфицировать метод Bind так, как ты сделал в IDL файле
Vi2>
Vi2>      [id(1), helpstring("method Bind")] HRESULT Bind([in]IUnknown* pUnk);
Vi2>

Vi2>Это универсальный формат, в котором любой клиент передаст имеющийся указатель. Но тогда нужно корректно отработать внутри метода:
Vi2>
Vi2>      void Bind(IUnknown* pReader) { return AtlComQIPtrAssign( (IUnknown**) &m_pReader, pReader, __uuidof(IReader) ); }
Vi2>

PS


Проверялось, в таком виде работает из perl, не работает из VBA.


Vi2>Единственное, что можно еще заметить, — IReader не является OLE-совместимым и требует прокси DLL для маршаллинга. Поставь ключевое слово oleautomation в определение IReader:


попробую, это может быть причиной неработы в VBA?
Re[2]: Передача интерфейса
От: LelicDsp Россия  
Дата: 10.03.05 11:50
Оценка:
Здравствуйте, Vi2, Вы писали:

Vi2>Единственное, что можно еще заметить, — IReader не является OLE-совместимым и требует прокси DLL для маршаллинга. Поставь ключевое слово oleautomation в определение IReader:


Спасибо, помогло вроде. По крайней мере тестовые проекты на VB, VBA и perl работают. Единственное что при сборке компилятор начал ругаться на InlineIsEqualGUID — пришлось заменить на IsEqualGUID. В чем тут могут быть грабли?
Re[3]: Передача интерфейса
От: Кодёнок  
Дата: 10.03.05 12:19
Оценка:
LD>Предлагаемое тобой решение это как раз то, что было сделано с самого начала. Не работает. роблема в том, что нельзя имплементировать болеее одного Dispatch интерфейса. Точнее имплементировать можно, но виден будет только [default]. Спасибо MS, испоганили хорошую идею. Полный маразм, какой смысл вообще в абстрактных интерфейсах, если реально один класс = один интерфейс.

Можно. Покажи, как ты делаешь.
Re: Передача интерфейса
От: LelicDsp Россия  
Дата: 10.03.05 12:47
Оценка:
Всем спасибо за помощь! Но остались непонятные грабли в VB (&VBA). Так работает:

Set Test = New SimpleTester
Set Port = New ComPort
Test.Bind (Port)

Так тоже
Dim ComPort as Variant
Set Test = New SimpleTester
Set Port = New ComPort
Test.Bind (Port)

А вот так нет:
Dim Port as ComPort
Set Test = New SimpleTester
Set Port = New ComPort
Test.Bind (Port)

Re[2]: Передача интерфейса
От: Vi2 Удмуртия http://www.adem.ru
Дата: 10.03.05 13:19
Оценка:
Здравствуйте, LelicDsp, Вы писали:

LD>Но остались непонятные грабли в VB (&VBA). Так работает:

LD>Test.Bind (Port)


А что непонятного? Вычисление выражения, однако! Нужно:
' или так
Test.Bind Port
' или так
Call Test.Bind(Port)
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[3]: Передача интерфейса
От: LelicDsp Россия  
Дата: 10.03.05 13:27
Оценка:
Здравствуйте, Vi2, Вы писали:

Vi2>А что непонятного? Вычисление выражения, однако! Нужно:

Vi2>
Vi2>' или так
Vi2>Test.Bind Port
Vi2>' или так
Vi2>Call Test.Bind(Port)
Vi2>


Спаситель! thnx! Век живи — век учись
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.