Не могу получить указатели на интерфейсы MS Word

Тема в разделе "WASM.WIN32", создана пользователем offset, 30 окт 2006.

  1. offset

    offset New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2006
    Сообщения:
    9
    Помогите, плиз, советом или примером исходника...

    Я хочу через COM вызывать методы MS Word.
    Программа типа:
    invoke CoInitialize,NULL
    invoke CoCreateInstance,....
    возвращает ошибку E_NOINTERFACE, если я среди параметров последнего вызова передаю CLSID объекта "Приложение Microsoft Word" и IID любого из его интерфейсов (Н: _Application).

    Когда среди параметров передаю CLSID объекта "Документ Microsoft Word" и IID любого из его интерфейсов (Н: IPersist, IPersistStorage, IDataObject), возвращается корректный указатель.

    Рылся кучу времени в Инете и нигде не нашел ни одного примера по взаимодействию работе Асма с MS Word.
    Мне необходимо из Асма открыть документ Ворда, править его и сохранять, (т.е. выполнять любой из методов его интерфейсов)

    Я думаю, может это потому что перед получением указателя на интерфейсы Ворда нужно как то его активировать?

    Благодарю заранее за любую подсказку или пример
     
  2. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    offset
    А какая разница, на асме пример, или на си? А примеров с последним валом, тема давно изжевана.
     
  3. offset

    offset New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2006
    Сообщения:
    9
    Сколько ни искал, примеры только на С++. А там используются высокоуровневые фишки с объектами и классами, которые генерируют десятки Кб кода на Асме.

    Если не трудно, скинь хоть одну ссылку, где изложен подобный пример на С или С++, но только с использованием одних Win32API. Я, естественно, без проблем применю их на Асме.
     
  4. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    offset
    см. аттач, компилировал vct2k3 + psdk feb-2k3. Сердце программы - функция wrap. в подпрограмме foo в комментариях указаны некие подобия команд VBA. В качестве параметра необходимо указывать _полное_ имя док.файла.
     
  5. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    offset
    Ну, си или с++ - разница небольшая, если знаешь предметную часть. Если нет - изучи сам СОМ, возьми пару примеров, пусть и с обёртками, и посмотри, что внутри их происходит. Конкретно по офису читай справку по VBA и смотри TypeLibrary (ObjectLibrary).

    По твоему вопросу - есть такая проблема. По идее, CLSID_WordApplication должен быть равен {00020905-0000-0000-C000-000000000046}, а IID_IWordApplication — {00020970-0000-0000-C000-000000000046}. Но при попытке создать такой интерфейс получаю ошибку "Класс не зарегистрирован". Но можно достать из ProgID (Word.Application) такой guid: {000209FF-0000-0000-C000-000000000046}.
    Ну, а дальше - по OLB и VBA, передавая вместо ненужных параметров (коих бывает немало) указатель на VARIANT (vt=VT_ERROR, SCODE=0x80020004).

    Вот тест.
     
  6. offset

    offset New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2006
    Сообщения:
    9
    q_q
    IceStudent

    Спасибо за подсказку!
    А то неизвестно, сколько б еще тупил...
     
  7. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    На самом деле, действительно, чтоб работало с разными версиями ворда (9,10,11, например) вызовы интерфейсных методов ворда по смещениям, т.е. через вирт. таблицу не пойдут, надо использовать полноценное позднее связывание и, со-но, все вызывать через IDispatch::Invoke, как в примере q_q.

    К слову, есть очень удобный враппер над IDispatch, упрощает синтаксис до нельзя, правда на с++ (http://www.morearty.com/code/dispatch/).
     
  8. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    TheRawGod
    А как же совместимость с прежними версиями? Разве ворд её не поддерживает?
     
  9. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    IceStudent
    может и должен, я занимался этим вопросом не очень глубоко.
    И кстати, на каком уровне обеспечивается обратная совместимость? И все-таки между какими именно версиями? Для макронаписания / скриптования, и сами понимаете, достаточно оставить имена базовых методов с их сигнатурами и внутреннюю объектную модель. А в нативных интерфейсах могли и накрутить, порядок там поменять или еще что.

    Это я чисто опытным путем получил, не факт что наверняка верно это.

    Мне нужно было заавтоматизировать ворд на с++, у меня стоял 2003-ий, я использовал #import, все работало.
    Запустил на соседней машине с ворд 2к и получил exception. Валился Word::lol: ocuments::Open по-моему. Я дебажить не стал, соседняя машина совсем не девелоперская была, сети там не было и все такое, да и времени тоже было в обрез.
    Я переписал код один в один с использованием чистого IDispatch и все мило заработало.
    Черт его знает, почему оно валилось на интерфейсных вызовах на той машине:dntknw:

    Когда я начинал это дело писать, я немного искал по нету и там в каких-то примерах были имена файлов, в которых хранятся type-либы для каждой из версий ворда. Файлы с совсем разными именами и путями. Я понимаю, что они могли одни и те же описания интерфейсов куда угодно всовывать от версии к версии, но еще тогда у меня это вызвало подозрение. Позднее оно оправдалось.
    Но может быть дело в другом, повторюсь, не выкапывал причину, торопился я.
     
  10. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    В моем примере первую строчку функции main надо заменить на SetConsoleOutputCP(1251).
     
  11. offset

    offset New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2006
    Сообщения:
    9
    Спасибо q_q и другим !
    Все ок! И с Вордом, и с Экселем прога работает нормально!

    Получается, через IDispatch мы можем у др. программ вызывать различные методы и устанавливать / читать свойства (посмотреть которые можно через редактор Visual Basic например).

    У меня возникает еще один вопрос. А как с событиями? Можно ли через IDispatch организовать так, чтобы вызывался наш код при наступлении какого-либо события, например DocumentChange в Word.Application?
     
  12. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    Я так понимаю, что речь идет о случае, когда мы не знаем гуид ивент-интерфейса заранее.

    Можно, но только в том случае, если объект, от которого мы хотим получать ивенты соответствует ряду условий (MS Word соответствует:)).
    Безусловно объект должен реализовывать дефолтный dispinterface ивент (можно дуальный, но для ивентов не рекомендуется).
    Но главная проблема тут в том, что нам все равно нужно как-то получить connection point у объекта без знания гуида в момент компиляции. И выход тут есть. Объекты, которые хотят позволить с собой такое обращение (т.е. хотят быть макимально автоматизируемыми) реализуют интерфейс IProvideClassInfo2.
    У этого интерфейса есть замечательны метод GetGUID, возвращающий гуид дефолтного сорс ивент-интерфейса (того, который нам и нужен). Взяв этот гуид, мы передаем его в FindConnectionPoint и делаем Advise, передавая себя как параметр (ну это как обычно).

    Пару слов "о себе".

    Сами мы должны просто реализовать IDispatch и в нем обрабатывать вызовы, исходя из их dispid'ов. Тут возникает трудность - нам нужно знать dispid интересующих ивентов, либо полностью реализовать ивент интерфейс. И в первом и во втором случае это подразумевает доступ к тому или иному описанию ивент интерфейса.
    Я не знаю, почему в своих имплементациях fire для disp-ивентов MS не вызывают GetIbsByName на sink объекте, ведь в таком случае последние можно было бы писать вообще зная одни лишь сигнатуры методов, не беспокоясь о dispid'ах, а получая их "на лету". Ведь этим, на мой взгляд, они нивелируют идею полноценного "позднего связывания" для ивентов.

    Ну а если же гуид и описание ивент интерфейса на момент компиляции известены, то просто используйте IDispEvent(Simple)Impl наряду с DispEventAdvise / DispEventUnadvise да и все.
     
  13. offset

    offset New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2006
    Сообщения:
    9
    TheRawGod

    Уп-с-с, нициво не понимаю...
    Подскажи, что почитать. Может, дойдет...
     
  14. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    Вы меня прямо озадачили этим вопросом:)

    Дело в том, что бессмыссленно наверное советовать изъезженные труды по COMу, Вы наверняка знаете об этих книгах, Дон Бокс, Троельсон и т.д.

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

    offset New Member

    Публикаций:
    0
    Регистрация:
    24 окт 2006
    Сообщения:
    9
    TheRawGod

    смотрю через OLE/COM Object Viewer, у меня объект Приложение Microsoft Word поддерживает следующие интерфейсы:
    _Application
    IClientSecurity
    IConnectionPointContain
    IDispatch
    IMarshal
    IMultiQI
    IProvideClassInfo
    IProxyManager
    ISupportErrorInfo
    IUnknown
    Говоря о IProvideClassInfo2, Вы имели в виду также IProvideClassInfo? Или исключительно IProvideClassInfo2?

    К сожалению, я не могу посмотреть, из каких методов состоит IProvideClassInfo (видно только их кол-во - 4).

    Это из интерфейса IConnectionPointContainer? Перерыл все Хелпы, у меня нет описания этой команды...

    Я нашел эту функцию в интерфейсах IOleObject, IConnectionPoint IOleAdviseHolder, IDataAdviseHolder. Описания ни одной из них у меня нет. Какой интерфейс Вы имели в виду?

    P.S.
    Ранее я никогда не писал код под Ивенты с использованием этих функций. Буду очень признателен, если у Вас есть возможность показать пример на С или Асме. А я б уже разобрался...
    Ясно, что надо изучать матчасть, но это на будущие месяцы. Проблему сейчас решить надо.
     
  16. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    offset
    Может сорцы ATL помогут? Не обязательно их у себя использовать, можно просто смотреть, как реализовано там то или иное.
     
  17. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    offset

    Надо сказать, что то, что ворд не выставляет вторую версию интерфейса достаточно неожиданно для меня:)
    Я посмотрел, действительно IProvideClassInfo2 ворд не выставляет. К сожалению сейчас у меня нет времени, чтобы раскопать этот вопрос до конца, возможно информацию касательно дефолтного ивент диспинтерфейса скрипты узнают просто через type library (ITypeLib) или какими-то иными способами (ITypeInfo, еще может что). Не суть важно на самом деле сейчас.

    Особенно это все интересно после прочтения описания
    AtlGetObjectSourceInterface


    А пока давайте остановимся на таком варианте: просто откройте Microsoft Word Type Library (в oleview) и посмотрите его сорс ивент интерфейсы. У меня это выглядит так:


    Код (Text):
    1. [
    2.   uuid(000209FF-0000-0000-C000-000000000046),
    3.   helpcontext(0x00000970)
    4. ]
    5. coclass Application {
    6.     [default] interface _Application;
    7.     [source] dispinterface ApplicationEvents;
    8.     [source] dispinterface ApplicationEvents2;
    9.     [source] dispinterface ApplicationEvents3;
    10.     [default, source] dispinterface ApplicationEvents4;
    11. };
    Выберите тот (ту версию), который вас интересует и запомните его гуид. Чем меньше версию выберете, тем больше вероятность того, что заработает со старыми версиями ворда. Если это критично, попробуйте узнать, в какой версии какой ивент интерфейс был добавлен, чтоб наверняка определиться. Или делайте это динамически.
    Безусловно этот интерфейс нужно реализовать на вашем объекте, чтоб в него приходили эти самые вызовы.


    Верно. Вот описание:
    http://msdn.microsoft.com/library/d...html/bbe55013-13ca-43e8-8d5e-ef89076df039.asp

    Я имел ввиду IConnectionPoint.
    Вот сссылка: http://msdn.microsoft.com/library/d...html/11257f24-096c-4240-8fac-4e42a6161d66.asp

    В итоге:
    Через IConnectionPointerContainer::FindConnectionPoint мы находим connection point для интересующего вас ивент диспинтерфейса (напрмер, ApplicationEvents4).
    Результатом IConnectionPointerContainer::FindConnectionPoint будет указатель на IConnectionPoint. Вот у него-то как раз и есть метод Advise.

    Далее ваш объект начнет получать ивенты.
    Не забудьте сделать в конце Unadvise:)

    Если что опять не ясно или не выхоlит - пишите, разберемся.
     
  18. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Достаточно реализовать IDispatch и просматривать нужные методы по DISPID. То есть, динамическое связывание и в событиях работает.
     
  19. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    IceStudent

    да, правильно, сам себе противоречу, 2 поста назад еще можно было IDispatch, а теперь весь интерфейс требую:)

    Но тут, кстати, мне пришла в голову мысль, от которой я никак не могу отделаться.
    Как я писал выше, когда ATL генерирует код для fire'инья диспивентов, он вызывает последние сразу по диспидам, не делает GetIdsOfNames.

    Таким образом, положим, что у нас есть маленький объект, схематически мы хотим, чтобы было так:
    ComObject : IInterface, IDispEvenInterface.

    В данном случае IDispEventInterface - дисп ивент интерфейс, который мы хотим слушать.
    А IInterface - интерфейс, через который нами управляют, мы хотим чтобы он был либо диспинтерфейсом, либо дуальным.

    Должны ли мы объединять эти 2 интерфейса в один (ведь скрипты видят только дефолтный диспатч / дуальный интерфейс)?
    А также, если мы хотим различать ивенты IDispEvenInterface'а только по их dispid'ам, т.е от него наследоваться не будем (как вы и написали, это не обязятельно), но нам в таком случае ведь нужно позаботится о том, чтобы в нашем IInterface не было методов с dispid'ами, равными тем, что есть в IDispEvenInterface, иначе как мы сможем различать вызовы ивентов и наших собственным методов (имеется ввиду, различать внутри Invoke)?

    У меня чувство, что я упускаю из внимания что-то элементарное и потому выходит такая путаница:)
    Но вопрос покоя не дает, пролейте свет, пожалуйста:)

    На самом деле, достаточно разобраться с IDispEventImpl и примером http://msdn2.microsoft.com/en-us/library/k6c9b35s(VS.80).aspx
    Что и собираюсь cделать:)