Есть необходимость написать EXE-сервер автоматизации с GUI.
При этом необходимо реализовать следующие особенности:
1. Сам сервер реализовать на ATL
2. GUI сервера реализовать в отдельном компоненте (для которого сам сервер является клиентом) на ATL, VB, MFC или чем-то еще и может, в принципе, выбираться сервером из нескольких компонентов, например категории SomeGUICat
3. Сервер должен иметь возможность обновлять ("перерисовывать" GUI), а GUI может вызывать методы сервера
4. Сервер может просто запускаться и функционировать как обычная программа (без COM-клиента сервера)
5. Сервер, по желанию клиента, может вообще не отображать свой GUI
Общая идея создания GUI-компонента такова:
Server::Run(BOOL fGUI)
{
...
if(fGUI)
{
GUI pGUI = NULL;
hresult = CreateInstance(..., &pGUI);
pGUI->Show(this); // to get GUI possibility to call the server
}
...
}
При этом возникают некоторые вопросы:
1. Как организоватьвзаимосвязь между сервером и GUI, когда они одновременно являются серверами и клиентами друг друга?
2. Как, например, реализовать GUI на VB? Простая тестовая программа показывает, что при такой конфигурации VB не может создать немодальное окно приложения, а модальное окно приведет к deadlock'у сервера.
3. Как инициализировать сервер, если он запущен как обычное приложени? Иными словами, если сервер имеет какой-то головной объект (скажем Application), то как и где его при этом создавать и удалять?
Если у кого-либо есть соображения по этим вопросом, буду очень признателен, если вы выскажетесь!
Если есть возможность отдать человеку все, не прося ничего взамен — отдай. Если тебе один раз из десяти долг отдадут — будешь богатым.
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: EXE-сервер с GUI
От:
Аноним
Дата:
20.01.02 09:20
Оценка:
Здравствуйте VladD2, Вы писали:
VD>Здравствуйте chum, Вы писали:
VD>Ничего не понятно! Ты объясни что конкретно хочешь сделать и в чем проблемы?
Проблемы две. Скорее всего, они вызваны моим недостаточным знанием COM и ATL, и их решение для меня неочевидно. Итак:
1. Как реализовать локальный сервер (на ATL), GUI которого реализован другим компонентом (например, на MFC или VB)? Мне непонятно, как реализовать двухстороннюю связь между сервером и его GUI? Мне кажется, что connection point'ами тут не обойдешься (хотя, возможно я неправ). Кроме того, я написал простой тестовый сервер, котрый вызывал GUI-сервер, реализованный на VB в виде ActiveX DLL, и VB начал ругаться, что он не может создать при таком раскладе немодальное окно, а модальное окно "вгоняет в ступор" сервер.
2. Как написать на ATL локальный сервер, который можно запускать как обычное приложение, а не только "дергать" через COM? Код, сгенерированный визардом, вообще представляет собой программу, которая, будучи запущенной как приложение, висит в памяти и не дает никакой возможности себя закрыть, иначе как через task manager. Это, конечно, не проблема: можно изменить код так, что при пустой командной строке (без -embedding), программа будет завершать работу. Но кто мне скажет: в каком месте программы и как я должен инициализировать объекты сервера? Т. е., если, например, пользователь сервера автоматизации начинает работать с объектом Application, а потом получает через него все остальное, то где и как создавать объект Application, если EXE-шник запущен как обычное приложение? И где этот объект уничтожать? Причем, так, чтобы сервер завершил при этом работу (при условии отсутствия COM-клиентов).
Надеюсь, что теперь объяснил более толково. Прошу разъяснить мне "политику партии" по этим вопросам.
Re[3]: EXE-сервер с GUI
От:
Аноним
Дата:
20.01.02 09:39
Оценка:
Здравствуйте IT, Вы писали:
IT>Здравствуйте VladD2, Вы писали:
VD>>Ничего не понятно! Ты объясни что конкретно хочешь сделать и в чем проблемы?
IT>Скорее всего вот это — http://www.rsdn.ru/article/?com/autoatl.xml
Большое спасибо. Очень интересная статья. Но к сожалению, это не совсем то, что мне надо. Тут просто программа, написанная на MFC и имеющая некий GUI, предоставляет, с помощью ATL, интерфейсы автоматизации.
А мне надо, чтобы к серверу, написанному на ATL и реализующему всю прграммную логику, можно было подключать разные GUI (естественно: один GUI в любой момент времени), реализованные другими компонентами и имеющими, естественно, заранее известные ATL-серверу интерфейсы. Т.е., мне нужна двухсторонняя связь между ядром и GUI. Возможно ли реализвать ее только с помощью connection points? И сможет ли, например, кто-нибудь написать упрощенный GUI на VB, реализующий такую двухстороннюю связь?
Ты описываешь реализацию! Свое видение тык сызыть, а нужно было бы описать конкретную проблему! Т.е. задачу в челавеческих терминах. Что есть сервер (его предназначение)? Зачем сменный интерфейс? Чем интерфейс отличается от клиента? А то похоже, что ты путаешь понятие GUI и клиента.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте Аноним, Вы писали:
А>А мне надо, чтобы к серверу, написанному на ATL и реализующему всю прграммную логику, можно было подключать разные GUI (естественно: один GUI в любой момент времени), реализованные другими компонентами и имеющими, естественно, заранее известные ATL-серверу интерфейсы. Т.е., мне нужна двухсторонняя связь между ядром и GUI. Возможно ли реализвать ее только с помощью connection points? И сможет ли, например, кто-нибудь написать упрощенный GUI на VB, реализующий такую двухстороннюю связь?
Всё же я не совсем понял задачу.
Connection points — это лишь стандартизованный вариант подключения к событиям объекта, использующийся, грубо говоря, в Automation. Никто не мешает придумать свой, более подходящий к конкретной задаче механизм. Например, в OLE DB используется похожий, но свой механизм подключения к событиям объекта. В принципе, тебе просто нужно решить кто будет инициатором этого действа, затем нужно обменяться интерфейсами и вперёд.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте VladD2, Вы писали:
VD>Здравствуйте Аноним.
VD>Ты описываешь реализацию! Свое видение тык сызыть, а нужно было бы описать конкретную проблему! Т.е. задачу в челавеческих терминах. Что есть сервер (его предназначение)? Зачем сменный интерфейс? Чем интерфейс отличается от клиента? А то похоже, что ты путаешь понятие GUI и клиента. :(
Тысяча извинений за подпись Аноним — почему-то забыл залогиниться, а заметил уже когда послал сообщения.
К баранам:
Задача, так задача. Очень упрощенно.
Представьте себе программу. Например, электронный переводчик. Он имеет свой GUI и имеет ядро (локальный сервер автоматизации), которое "общается" со словарями и, вообще говоря, может переводить без всякого GUI, например через интерфейс IInterpreter. Итак, мы имеем программу-переводчик (типа Контекста или Лингво), которая может запускаться как обычное приложение, в котором можно набирать слова (или "бросать" в окно программы с помощью dran'n'drop) и переводить их. А можно, например, использовать как COM-сервер-переводчик из программы SuperInterpreter.
Теперь о смене GUI.
Представьте себе, что автор программы договорился с неким издательством о продаже вместе с их бумажным словарем его электронной версии, использующей вышеуказанное ядро. Этому варианту словаря ненужен очень сложный суперпользовательский интерфейс исходной программы. А просто это быстро написанное на VB некое "ведро" (типа Recycle Bin), которое висит поверх всего в углу экрана, и в которое можно "кидать" слова для перевода.
Вот такая вот диспозиция в двух словах. При этом мне кажутся вполне очевидными следующие вещи:
— ядро программы является сервером автоматизации
— ядро программы является клиентом сервера, реализующего GUI программы
— сервер GUI должен иметь возможность обращаться к ядру
— ядро должно иметь возможность обращаться к GUI (например, если требуется перерисовать GUI при добавлении нового словаря через интерфейсы ядра)
Теперь о проблемах реализации. Я не знаю как:
— организовать двухстороннее взаимодействие между ядром и GUI
— написать на ATL локальный сервер, который может запускаться как обычное приложение
Заранее благодарен.
Если есть возможность отдать человеку все, не прося ничего взамен — отдай. Если тебе один раз из десяти долг отдадут — будешь богатым.
Здравствуйте chum, Вы писали:
C>Вот такая вот диспозиция в двух словах. При этом мне кажутся вполне очевидными следующие вещи:
Теперь понятно.
C>- ядро программы является сервером автоматизации
правильно.
C>- ядро программы является клиентом сервера, реализующего GUI программы
:no:
C>- сервер GUI должен иметь возможность обращаться к ядру
точно
C>- ядро должно иметь возможность обращаться к GUI (например, если требуется перерисовать GUI при добавлении нового словаря через интерфейсы ядра)
не совсем так
C>Теперь о проблемах реализации. Я не знаю как:
Если ты сделаешь GUI сервером, то я тебе искренне сочувствую.
GUI вообще можно не делать COM-объектом, только ядро. Ядро не должно знать никаких подробностей о клиентах, то ли это GUI, то ли что-то ещё. Для сообщения об изменении состояния внутри ядра следует посльзоваться событиями. Вот и всё. Т.е. тебе нужно твоё ядро завернуть в сервер автоматизации, exe или dll зависит от задачи.
Если нам не помогут, то мы тоже никого не пощадим.
GUI — это плохой термин когда речь идет о COM.
В твоих рассуждениях перепутаны понятия сервера и клиента (последнего понятия вообще нет, оно заменено словом GUI).
Привыкай приложение предоставляющее услуги – это сервер.
Потребляющее – клиент.
Причем если они меняются местами, то и роли (названия) меняются тоже.
Теперь о том как бы я подошел решению данной задачи:
Нужно разработать ядро которое должно заниматься непосредственно переводом. Данное ядро и должно быть сервером. Оно вообще не должно иметь GUI.
Создать отладочного клиента. На базе этого клиента должен отлаживаться интерфейс (программный) сервера. Ну, и это будет первый клиент.
Применение слова клиент позволит избежать путаницы (ну, и все обитатели этого форума будут понимать о чем идет речь.
По серверу: Сервер лучше создать в виде in-процессного (DLL), но все интерфейсы сервера должны отвечать требованиям oleautomation. Это с одной стороны даст возможность использовать данный сервер внутри процесса клиента (что даст значительное ускорение и экономию ресурсов). С другой стороны поддержка oleautomation позволит запускать такой сервер на удаленной машине (через COM+, присутствующий на W2k, или через суррогатный процесс).
Клиент должен общаться с сервером только через COM-интерфейс и предоставлять GUI для конечного пользователя. Все операции по поиску и т.п. должны производиться на сервере. Общение между сервером и клиентом должно осуществляться блоками небольшого размера. При этом блоки должны быть более-менее полноценными (не следует для каждого чиха лезть на сервер, так как это приведет к замедлению работы при использовании удаленного режима).
Приблизительный список методов (чисто для примера):
// Перевод слова.
HRESULT Translate([in] int Dictionary, [in] BSTR Word, [out] BSTR * TranslateCard);
HRESULT GetDictionaryList([out, retval] SAFEARRAY(BSTR)) * psa // Массив содержащий список словарей
// Список строк для заполнение списка в GUI-клиенте.
HRESULT GetList(
[in] int AlfabetWordPos, // Позиция слова в списке
[in] int MaxWordCount, // Количество строк в возвращаемом массива
[out, retval] SAFEARRAY(BSTR) * psa // Массив содержащий списак строк
Теперь на счет обновления словаря и т.п. ...
Такие операции осуществляются очень редко и обычно для пользователя проходят незаметно. Ничего страшного если пользователю придется нажать на кнопку "рефрешь" или перезагрузить программу.
Зато постоянная обратная связь с сервером, в случае если сервер загружается в отдельном процессе, и тем более на отдельной машине, приведет к неустойчивости работы сервера (ведь сервер станет клиентом для каждого клиента!), проблемам безопасностью (их конечно можно обойти, но времени на это уйдет море).
Если сервер предполагается использовать исключительно как in-процессный, то данный проблемы нивелируются или исчезают вовсе. Однако дизайн приложения лучше делать в расчете на распределенную работу.
В любом случае лучше идти по пути наименьшего сопротивления. Т.е. лучше создать сервер без обратной связи и уж если будет невмоготу, добавить такую возможность.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Приношу свои извенения. В строках приведенных ниже массивы нужно называть не psa, а ppsa, т.е. указатель на указатель на Safe Array, отражаю тем самым, что это [out]-параметр.
VD>
Большое спасибо всем за участие! Special thanks to IT & VladD2.
Помощь была крайне полезной.
Жалко, что нельзя реализовать пользовательский интерфейс программы в отдельноподключаемом компоненте. Это была одна из главных идей: дать пользователю сервера возможность "прилеплять" к нему свои собственные пользовательские интерфейсы.
Можно, конечно, писать для каждого нового GUI новый сервер, его реализующий, который, в свою очередь, является клиентом ядра. Но тогда у каждой такой программы будет свой CLSID (или ProgID). А тут, собственно, идея была в том, чтобы этот сервер все время вызывался клиентами совершенно единообразно, вне зависимости от того, какой GUI к нему в данный момент "прилеплен".
Далее:
Я понимаю, что если, например, ядро реализовать inproc сервером, а основная программа с пользовательским интерфейсом будет для него клиентом, то мой второй вопрос отпадает сам собой. Но все-таки (в принципе), интересно узнать:
Как на ATL написать локальный сервер, который может запускаться и закрываться как обычное приложение? Где и как создавать и удалять объекты сервера?
Если есть возможность отдать человеку все, не прося ничего взамен — отдай. Если тебе один раз из десяти долг отдадут — будешь богатым.
Здравствуйте chum, Вы писали:
C>Жалко, что нельзя реализовать пользовательский интерфейс программы в отдельноподключаемом компоненте. Это была одна из главных идей: дать пользователю сервера возможность "прилеплять" к нему свои собственные пользовательские интерфейсы. C>Можно, конечно, писать для каждого нового GUI новый сервер, его реализующий, который, в свою очередь, является клиентом ядра. Но тогда у каждой такой программы будет свой CLSID (или ProgID). А тут, собственно, идея была в том, чтобы этот сервер все время вызывался клиентами совершенно единообразно, вне зависимости от того, какой GUI к нему в данный момент "прилеплен".
Мда...
To IT & Vlad2:
Может нарисуете человеку картинку, шоб понятней было, вот мол сервер, вот его интерфейсы, а вот клиенты с GUI.
To chum:
Только не обижайся, но похоже тебе для начала нужно немного почитать о технологии COM, может тогда ты и сам все поймешь. Просто мне стало жалко IT и Vlada2, они так доходчиво все расписали и похоже впустую.
Здравствуйте chum, Вы писали:
C>Жалко, что нельзя реализовать пользовательский интерфейс программы в отдельноподключаемом компоненте. Это была одна из главных идей: дать пользователю сервера возможность "прилеплять" к нему свои собственные пользовательские интерфейсы.
Не совсем понятно, о чем страдания. Если у тебя будет сервер (ядро) реализующий некоторые интерфесы, кто угодно может лепить какие-угодно и на чем угодно свои GUI и использовать для собственно перевода твой сервер, дергая его интерфейсы.
C>Можно, конечно, писать для каждого нового GUI новый сервер, его реализующий, который, в свою очередь, является клиентом ядра. Но тогда у каждой такой программы будет свой CLSID (или ProgID).
А зачем программе — GUI быть сервером и иметь CLSID (или ProgID)?
C>А тут, собственно, идея была в том, чтобы этот сервер все время вызывался клиентами совершенно единообразно, вне зависимости от того, какой GUI к нему в данный момент "прилеплен".
Интерфейсы твоего сервера и будут стандартом для всех программ-GUI, которые его будут пользовать, вызывать его не единообразно они просто не будут иметь никакой физической возможности.
Здравствуйте Аноним, Вы писали:
А>2. Как написать на ATL локальный сервер, который можно запускать как обычное приложение, а не только "дергать" через COM? Код, сгенерированный визардом, вообще представляет собой программу, которая, будучи запущенной как приложение, висит в памяти и не дает никакой возможности себя закрыть, иначе как через task manager.
Вместе с WTL распостраняется мастер, который может создавать MDI, SDI или Dialog проги (exe), являющиеся серверами автоматизации. Можно создать такую прогу, в ней реализовать сервер и некий (тестовый) GUI работающий со своим же сервером. Те, кого не устраивает этот стандартный GUI напишут своего клиента, при этом используя твой сервер они будут запускать этот exe в "безинтерфейсном" варианте, весь код для этого мастер создает сам.
Здравствуйте Willi, Вы писали:
W>To IT & Vlad2: W>Может нарисуете человеку картинку, шоб понятней было, вот мол сервер, вот его интерфейсы, а вот клиенты с GUI.
Видимо, у меня не очень хорошо получается объяснить проблему. Очень трудно это сделать в рамках форума. Но я все-таки постараюсь. Прошу только принять во внимание, что программу-переводчик я привел только для примера! Реальное приложение иммет совсем другую специфику, на объяснение которой уйдет на порядок больше времени. Прошу всех, кто хочет помочь советом (я десйствительно очень ценю помощь), не зацикливаться на вопросе: зачем реализовывать GUI в отдельном компоненте. Лучше ответьте: как?
Попробую зайти с другой стороны. Хотите картинок, их есть у меня:
КЛИЕНТ_ПЕРЕВОДЧИКА /*1..n клиенты работают с переводчиком через CLSID_TRANSLATOR*/
|
| CLSID_TRANSLATOR (ITranslate)
v
СЕРВЕР_ПЕРЕВОДЧИК(ЯДРО) /*реализуется разработчиком ядра*/
|
| CLSID_GUIXXXXX (IGUI)
v
СЕРВЕР_GUI /*может быть реализован сторонним разработчиком*/
Специфика программы такова, что пользователю КЛИЕНТ_ПЕРЕВОДЧИКА может понадобиться видеть как окно КЛИЕНТ_ПЕРЕВОДЧИКА, так и окно СЕРВЕР_ПЕРЕВОДЧИКА (т.е. окно СЕРВЕРА_GUI). При этом, пользователь КЛИЕНТ_ПЕРЕВОДЧИКА (а вместе с ним и СЕРВЕР_ПЕРЕВОДЧИКА) воспртнимает окно СЕРВЕР_ПЕРЕВОДЧИКА как данность и может совершенно не догадывается, что оно где-то как-то отдельно.
СЕРВЕР_GUI может быть разработано кем угодно. Представьте себе, что кто-то (скажем фирма Translators Ltd.) разработал СЕРВЕР_ПЕРЕВОДЧИК(ЯДРО) и опубликовал интерфейс IGUI для сторонних разработчиков. Кто-то разработал КЛИЕНТ_ПЕРЕВОДЧИКА. А пользователь КЛИЕНТ_ПЕРЕВОДЧИКА время от времени лазает на сайт www.supertranslator.com, чтобы скачать и установить новую Контекст-оболочку, Лингво-оболочку или Мультилекс-оболочку (которые день и ночь пишут сторонние разработчики) для СЕРВЕР_ПЕРЕВОДЧИКА.
Теперь еще раз о двухсторонней связи СЕРВЕР_ПЕРЕВОДЧИК(ЯДРО)<--->СЕРВЕР_GUI:
Представьте, что пользователь КЛИЕНТ_ПЕРЕВОДЧИКА может захотеть перевести какое-то СЛОВО в документе КЛИЕНТ_ПЕРЕВОДЧИКА. Он жмет там какую-то кнопочку и СЛОВО летит из КЛИЕНТ_ПЕРЕВОДЧИКА в СЕРВЕР_ПЕРЕВОДЧИК(ЯДРО) (через ITranslate), а затем перевод отображается в окне СЕРВЕРА_GUI (через IGUI). А может набрать это СЛОВО прямо в окне СЕРВЕРА_GUI и оно полетит сначала в СЕРВЕР_ПЕРЕВОДЧИК(ЯДРО) (непонятно через какой интерфейс), а затем обратно полетит перевод (через IGUI).
Вот, собственно, в дух словах и все.
Так в каком месте технологии COM это все почитать?
Если есть возможность отдать человеку все, не прося ничего взамен — отдай. Если тебе один раз из десяти долг отдадут — будешь богатым.
Здравствуйте chum, Вы писали:
C>Попробую зайти с другой стороны. Хотите картинок, их есть у меня: C> C>КЛИЕНТ_ПЕРЕВОДЧИКА /*1..n клиенты работают с переводчиком через CLSID_TRANSLATOR*/ C> | C> | CLSID_TRANSLATOR (ITranslate) C> v C>СЕРВЕР_ПЕРЕВОДЧИК(ЯДРО) /*реализуется разработчиком ядра*/ C> | C> | CLSID_GUIXXXXX (IGUI) C> v C>СЕРВЕР_GUI /*может быть реализован сторонним разработчиком*/
Бррррр
Не понимаю. Зачем нужен СЕРВЕР_GUI?
Ты пишешь ядро для перевода — некий COM server который предоставляет набор COM интерфейсов для перевода, получения синонимов, выбора словаря, внесения изменений в словари и т.п. а также, возможно, поддерживает механизм уведомления клиентов об изменениях в словарях, добавлении новых словарей и т.п.
Всё.
На этом можно останавливаться. Теперь дав подробное описание своих интерфейсов ты предлагаешь всем желающим писать клиентов твоего сервера — GUI оболочки для перевода. Ну и наверное сам пишешь приложение-оболочку которое пользуется этими же интерфейсами, дабы твой продукт мог существовать самостоятельно.
Здравствуйте Willi, Вы писали:
W>Бррррр W>Не понимаю. Зачем нужен СЕРВЕР_GUI?
W>Ты пишешь ядро для перевода — некий COM server который предоставляет набор COM интерфейсов для перевода, получения синонимов, выбора словаря, внесения изменений в словари и т.п. а также, возможно, поддерживает механизм уведомления клиентов об изменениях в словарях, добавлении новых словарей и т.п.
W>Всё.
W>На этом можно останавливаться. Теперь дав подробное описание своих интерфейсов ты предлагаешь всем желающим писать клиентов твоего сервера — GUI оболочки для перевода. Ну и наверное сам пишешь приложение-оболочку которое пользуется этими же интерфейсами, дабы твой продукт мог существовать самостоятельно.
W>Или я не догоняю?
W>С уважением, Willi.
Ну, грубо говоря, "не догоняете". Перечитайте еще раз предыдущее сообщение (22.01 19:08). И еще раз говорю: не зацикливайтесь на том, что это программа-переводчик (это упрощение, просто пример) и на вопросе "зачем это надо?". Исходный вопрос был "как?".
СЕРВЕР_GUI нужен, чтобы можно было менять пользовательский интерфейс программы без смены ядра и без изменения КЛИЕНТА_ПЕРЕВОДЧИКА (всех уже написанных КЛИЕНТОВ_ПЕРЕВОДЧИКОВ), которые обращаются к СЕРВЕРУ_ПЕРЕВОДЧИКУ через CLSID ядра! Ему нельзя подсовывать каждый раз новую реализацию GUI с агрегированным ядром и новым CLSID (или ProgID)!
Если есть возможность отдать человеку все, не прося ничего взамен — отдай. Если тебе один раз из десяти долг отдадут — будешь богатым.
Здравствуйте chum, Вы писали:
C>Ну, грубо говоря, "не догоняете". Перечитайте еще раз предыдущее сообщение (22.01 19:08). И еще раз говорю: не зацикливайтесь на том, что это программа-переводчик (это упрощение, просто пример) и на вопросе "зачем это надо?". Исходный вопрос был "как?".
C>СЕРВЕР_GUI нужен, чтобы можно было менять пользовательский интерфейс программы без смены ядра и без изменения КЛИЕНТА_ПЕРЕВОДЧИКА (всех уже написанных КЛИЕНТОВ_ПЕРЕВОДЧИКОВ), которые обращаются к СЕРВЕРУ_ПЕРЕВОДЧИКУ через CLSID ядра! Ему нельзя подсовывать каждый раз новую реализацию GUI с агрегированным ядром и новым CLSID (или ProgID)!
У тебы в голове каша. Ты все переставляешь с ног на голову...
Если речь идет о скинах (я правильно понимаю) как в WinAmp-е, но зачем вообще о сервере говорить. Просто сказал бы нужен универсальный способ менять скины клиентов.
Если о чем другом, то... ну, короче ОЧЕНЬ_ПЛОХО_ОБЪЯСНЯЕШЬ!
Ты уж лучше попробуй довести до нас идею НАСТОЯЩЕГО проекта, а то этот пример явно не гадится.
Ну, зачем серверу, задача которого заниматься переводом, менять клиенту внешний вид?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.