Помогите, плиз, советом или примером исходника... Я хочу через COM вызывать методы MS Word. Программа типа: invoke CoInitialize,NULL invoke CoCreateInstance,.... возвращает ошибку E_NOINTERFACE, если я среди параметров последнего вызова передаю CLSID объекта "Приложение Microsoft Word" и IID любого из его интерфейсов (Н: _Application). Когда среди параметров передаю CLSID объекта "Документ Microsoft Word" и IID любого из его интерфейсов (Н: IPersist, IPersistStorage, IDataObject), возвращается корректный указатель. Рылся кучу времени в Инете и нигде не нашел ни одного примера по взаимодействию работе Асма с MS Word. Мне необходимо из Асма открыть документ Ворда, править его и сохранять, (т.е. выполнять любой из методов его интерфейсов) Я думаю, может это потому что перед получением указателя на интерфейсы Ворда нужно как то его активировать? Благодарю заранее за любую подсказку или пример
offset А какая разница, на асме пример, или на си? А примеров с последним валом, тема давно изжевана.
Сколько ни искал, примеры только на С++. А там используются высокоуровневые фишки с объектами и классами, которые генерируют десятки Кб кода на Асме. Если не трудно, скинь хоть одну ссылку, где изложен подобный пример на С или С++, но только с использованием одних Win32API. Я, естественно, без проблем применю их на Асме.
offset см. аттач, компилировал vct2k3 + psdk feb-2k3. Сердце программы - функция wrap. в подпрограмме foo в комментариях указаны некие подобия команд VBA. В качестве параметра необходимо указывать _полное_ имя док.файла.
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). Вот тест.
На самом деле, действительно, чтоб работало с разными версиями ворда (9,10,11, например) вызовы интерфейсных методов ворда по смещениям, т.е. через вирт. таблицу не пойдут, надо использовать полноценное позднее связывание и, со-но, все вызывать через IDispatch::Invoke, как в примере q_q. К слову, есть очень удобный враппер над IDispatch, упрощает синтаксис до нельзя, правда на с++ (http://www.morearty.com/code/dispatch/).
IceStudent может и должен, я занимался этим вопросом не очень глубоко. И кстати, на каком уровне обеспечивается обратная совместимость? И все-таки между какими именно версиями? Для макронаписания / скриптования, и сами понимаете, достаточно оставить имена базовых методов с их сигнатурами и внутреннюю объектную модель. А в нативных интерфейсах могли и накрутить, порядок там поменять или еще что. Это я чисто опытным путем получил, не факт что наверняка верно это. Мне нужно было заавтоматизировать ворд на с++, у меня стоял 2003-ий, я использовал #import, все работало. Запустил на соседней машине с ворд 2к и получил exception. Валился Word:ocuments::Open по-моему. Я дебажить не стал, соседняя машина совсем не девелоперская была, сети там не было и все такое, да и времени тоже было в обрез. Я переписал код один в один с использованием чистого IDispatch и все мило заработало. Черт его знает, почему оно валилось на интерфейсных вызовах на той машине Когда я начинал это дело писать, я немного искал по нету и там в каких-то примерах были имена файлов, в которых хранятся type-либы для каждой из версий ворда. Файлы с совсем разными именами и путями. Я понимаю, что они могли одни и те же описания интерфейсов куда угодно всовывать от версии к версии, но еще тогда у меня это вызвало подозрение. Позднее оно оправдалось. Но может быть дело в другом, повторюсь, не выкапывал причину, торопился я.
Спасибо q_q и другим ! Все ок! И с Вордом, и с Экселем прога работает нормально! Получается, через IDispatch мы можем у др. программ вызывать различные методы и устанавливать / читать свойства (посмотреть которые можно через редактор Visual Basic например). У меня возникает еще один вопрос. А как с событиями? Можно ли через IDispatch организовать так, чтобы вызывался наш код при наступлении какого-либо события, например DocumentChange в Word.Application?
Я так понимаю, что речь идет о случае, когда мы не знаем гуид ивент-интерфейса заранее. Можно, но только в том случае, если объект, от которого мы хотим получать ивенты соответствует ряду условий (MS Word соответствует). Безусловно объект должен реализовывать дефолтный dispinterface ивент (можно дуальный, но для ивентов не рекомендуется). Но главная проблема тут в том, что нам все равно нужно как-то получить connection point у объекта без знания гуида в момент компиляции. И выход тут есть. Объекты, которые хотят позволить с собой такое обращение (т.е. хотят быть макимально автоматизируемыми) реализуют интерфейс IProvideClassInfo2. У этого интерфейса есть замечательны метод GetGUID, возвращающий гуид дефолтного сорс ивент-интерфейса (того, который нам и нужен). Взяв этот гуид, мы передаем его в FindConnectionPoint и делаем Advise, передавая себя как параметр (ну это как обычно). Пару слов "о себе". Сами мы должны просто реализовать IDispatch и в нем обрабатывать вызовы, исходя из их dispid'ов. Тут возникает трудность - нам нужно знать dispid интересующих ивентов, либо полностью реализовать ивент интерфейс. И в первом и во втором случае это подразумевает доступ к тому или иному описанию ивент интерфейса. Я не знаю, почему в своих имплементациях fire для disp-ивентов MS не вызывают GetIbsByName на sink объекте, ведь в таком случае последние можно было бы писать вообще зная одни лишь сигнатуры методов, не беспокоясь о dispid'ах, а получая их "на лету". Ведь этим, на мой взгляд, они нивелируют идею полноценного "позднего связывания" для ивентов. Ну а если же гуид и описание ивент интерфейса на момент компиляции известены, то просто используйте IDispEvent(Simple)Impl наряду с DispEventAdvise / DispEventUnadvise да и все.
Вы меня прямо озадачили этим вопросом Дело в том, что бессмыссленно наверное советовать изъезженные труды по COMу, Вы наверняка знаете об этих книгах, Дон Бокс, Троельсон и т.д. А что касается этих вопросов чисто по позднему связыванию, давайте, может быть, процитируйте те места в моем ответе, где Вам что-то непонятно, и я попробую ответить. А тем временем, возможно, попадется что-нибуть для прочтения или кто еще с форума подскажет.
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. Ранее я никогда не писал код под Ивенты с использованием этих функций. Буду очень признателен, если у Вас есть возможность показать пример на С или Асме. А я б уже разобрался... Ясно, что надо изучать матчасть, но это на будущие месяцы. Проблему сейчас решить надо.
offset Может сорцы ATL помогут? Не обязательно их у себя использовать, можно просто смотреть, как реализовано там то или иное.
offset Надо сказать, что то, что ворд не выставляет вторую версию интерфейса достаточно неожиданно для меня Я посмотрел, действительно IProvideClassInfo2 ворд не выставляет. К сожалению сейчас у меня нет времени, чтобы раскопать этот вопрос до конца, возможно информацию касательно дефолтного ивент диспинтерфейса скрипты узнают просто через type library (ITypeLib) или какими-то иными способами (ITypeInfo, еще может что). Не суть важно на самом деле сейчас. Особенно это все интересно после прочтения описания AtlGetObjectSourceInterface А пока давайте остановимся на таком варианте: просто откройте Microsoft Word Type Library (в oleview) и посмотрите его сорс ивент интерфейсы. У меня это выглядит так: Код (Text): [ uuid(000209FF-0000-0000-C000-000000000046), helpcontext(0x00000970) ] coclass Application { [default] interface _Application; [source] dispinterface ApplicationEvents; [source] dispinterface ApplicationEvents2; [source] dispinterface ApplicationEvents3; [default, source] dispinterface ApplicationEvents4; }; Выберите тот (ту версию), который вас интересует и запомните его гуид. Чем меньше версию выберете, тем больше вероятность того, что заработает со старыми версиями ворда. Если это критично, попробуйте узнать, в какой версии какой ивент интерфейс был добавлен, чтоб наверняка определиться. Или делайте это динамически. Безусловно этот интерфейс нужно реализовать на вашем объекте, чтоб в него приходили эти самые вызовы. Верно. Вот описание: 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ит - пишите, разберемся.
Достаточно реализовать IDispatch и просматривать нужные методы по DISPID. То есть, динамическое связывание и в событиях работает.
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делать