Как сделать контрол активным

Добавил пользователь Дмитрий К.
Обновлено: 03.10.2024

В интернете существует множество учебников по использованию ATL, и в частности, по созданию COM-компонентов с его помощью, в том числе и ActiveX/OLE контролов, но большинство из них почему-то описывают процесс тыкания мышкой в разные интересные места Visual Studio, использование графических инструментов последней, и мало какой из них затрагивает нутро сгенерированного средой разработки кода в достаточно глубоком объеме. В русскоязычном сегменте интернета ситуация и того хуже — материалов по ATL крайне мало, практически нет(да и не только по ATL, а и по созданию COM-компонентов вообще), поэтому я решил этот недостаток компенсировать.

Ну, начать следует, наверное, с краткого описания того, что из себя представляют ActiveX-компоненты и что такое ATL.

Интерфейс ActiveX-компонента поставляют с собой многие известные и популярные программы для Windows, как от самой Microsoft(Windows Media Player, или, например, программы из Microsoft Office, в частности Word, Excel и т.п.), так и от сторонних компаний(уже вышеупомянутый флеш, Adobe Reader, плюс многие другие программы того же Adobe — например Photoshop, если я правильно помню).

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

Теперь про то, что такое ATL. ATL, Active Template Library — известная библиотека от Microsoft, которая упрощает работу с Winapi на C++. В ATL входят классы/шаблоны не только для работы с COM/OLE/ActiveX, но и классы для, к примеру, построения и управления GUI и т.п.

ATL обычно поставляется с полноценными версиями Microsoft Visual Studio, но если у вас ее нет, то можете взять эту библиотеку из Windows DDK.


Так вот, в данной статье я опишу процесс создания простенького компонента, который будет у нас средствами DirectX 11 рисовать крутящуюся растеризованную в wireframe сферу, и у которого будет два метода — Run — запустить вращение сферы, и Stop — остановить вращение.

Для начала нам нужно придумать интерфейс нашего модуля, придумать GUID для библиотеки, интерфейсов и класса компонента, и записать все это, как водится, в MIDL, Microsoft Interface Definition Language.

Сохраним это дело в файл, и назовем MyActiveX.idl

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

Интерфейс, реализуемый нашим классом, относится к так называемым dual-интерфейсам. Это значит, что его можно использовать не только из языков, способных на общение с native-кодом, а соответственно, и способных общаться с COM-компонентами через таблицы виртуальных методов, но и из скриптовых языков, посредством интерфейса IDispatch.

Далее нам нужно записать определения IMyControl и _IMyControlEvents в заголовочном файле для C++ — MyControl.hpp

Макрос DEFINE_GUID же, в свою очередь, раскрывается в зависимости от определения макроса INITGUID — в случае отсутствия оного, он декларирует extern-переменную типа GUID с определенным названием. В случае INITGUID, он ее еще и инициализирует.

Теперь следует определить переменную _Module, которая принадлежит классу CComModule из ATL.

Запишем декларацию переменной в отдельный заголовочный файл, скажем MyActiveX.hpp


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

Для регистрации COM-серверов в реестре(а COM работает именно через реестр) мы могли бы написать reg-файл, или вручную создать в реестре соответствующие записи, но также мы можем использовать программу regsvr32.exe, входящую в состав Windows, и позволяющую проводить автоматическую регистрацию, средствами самого компонента. Для этого необходимо, чтобы наша библиотека экспортировала некоторые функции, и в частности DllRegisterServer и DllUnregisterServer.

CComModule упрощает весь процесс автоматической регистрации, и позволяет сводить ее к вызову соответствующих методов в вышеупомянутых экспортируемых функциях библиотеки, но ему требуется, чтобы в секции ресурсов dll, реализующего компонент, находился скрипт регистрации. Назовем файл, который мы позже добавим в ресурсы, MyControl.rgs, и добавим туда такой текст:

Наиболее значимые части скрипта регистрации — CLSID, то есть GUID класса нашего компонента, ProgID, т.е. человекочитаемое представление CLSID, и ThreadingModel — модель многопоточности нашего компонента, которая в данном случае устанавливается в Apartment, что значит что напрямую к нашему контролу можно обращаться только из того потока, в котором он был создан, а все внешние вызовы, в том числе извне процесса(или даже с другого компьютера — через DCOM) будут сериализоваться и синхронизироваться средствами рантайма COM.

Кстати, о сериализации, то есть, вернее сказать, о маршалинге. В теории, для того чтобы сериализовать вызовы методов нашего компонента и указатели на его интерфейсы, мы должны поставить параллельно с ним отдельную библиотеку, так называемую прокси-dll, которая будет подгружаться как в клиентское приложение(если оно на другом компьютере), так и к процессу, куда загружен наш COM-компонент.

Компилятор MIDL от Microsoft мог бы генерировать код для прокси-библиотеки, или как она в данном случае называлась бы, proxy/stub, но в нашем случае можно поступить проще — так как типы данных у нас более-менее стандартные, мы может использовать встроенный маршалинг рантайма OLE. Для данного дела нам нужно из нашего IDL-файла, опять же средствами компилятора MIDL, midl.exe(входит в состав как Windows SDK, так и VS), скомпилировать так называемую библиотеку типов(type library, tlb), и поставить с нашим компонентом ее. Более того, мы можем еще дальше упростить все это дело, и включить скомпилированную библиотеку типов в секцию ресурсов DLL, что мы и сделаем.

Заголовочный файл для ресурсов нашего модуля:


IDR_MYCONTROL — id ресурса скрипта регистрации.
IDB_MAIN_ICON — иконка 16x16 для нашего компонента, в формате BMP. Я лично для этого файла взял иконку DirectX из MS DirectX SDK.
IDS_SHADER и SHADER_RESOURCE — id ресурса и типа ресурса, содержащего код шейдеров для отрисовки сферы.

Сам файл ресурсов, MyActiveX.rc, такой:


Теперь перейдем непосредственно к реализации нашего контрола.
Назовем класс CMyControl, и создадим заголовочный файл CMyControl.hpp

Три наиболее важных базовых класса нашего CMyControl — CComObjectRootEx, управляющий, в частности, подсчетом ссылок объектов нашего класса, CComCoClass, реализующий фабрику класса(IClassFactory), и CComControl, в свою очередь наследующийся от CWindowImpl(класс, обертывающий HWND, то есть дескриптор окна), и реализующий большую часть необходимой для встроенных ActiveX-контролов фунциональности.

Наиболее значимые макросы в теле класса:

DECLARE_REGISTRY_RESOURCEID — указывает id ресурса, в котором находится скрипт регистрации компонента.

BEGIN_COM_MAP + END_COM_MAP — реализуют метод QueryInterface интерфейса IUnknown(который является вершиной иерархии интерфейсов COM), который позволяет получать ссылки на разные интерфейсы объекта(и каждая из COM_INTERFACE_ENTRY указывает один из вариантов).

BEGIN_CONNECTION_POINT_MAP и соответствующий END — необходимы для реализации интерфейсов, связанных с оповещениями о событиях контрола.

Вся функциональность нашего компонента, и, в частности, работа с Direct3D, реализуется приватным классом CMyControlImpl, согласно паттерну pimpl, в файле CMyControl.cpp. Я не буду в деталях его описывать, отмечу только то, что в конструкторе самого CMyControl необходимо выставить внутреннее свойство m_bWindowOnly в TRUE — это будет означать, что наш компонент поддерживает встраивание исключительно в графические приложение.

Также, стоит отметить что в реализации компонента для управления подсчетом ссылок на COM-интерфейсы активно используется шаблонный класс умного указателя CComPtr из ATL, очень похожий на intrusive_ptr из boost.

Теперь создадим файл MyActiveX.cpp, в котором у нас будет определены GUID класса и интерфейсов, переменная _Module, а также реализована точка входа DLL и необходимые для ActiveX-модуля экспортируемые функции:


Прежде чем скомпилировать dll, определим, какие функции наш модуль экспортирует, в def-файле:


Весь исходный код проекта, включая Makefile для сборки с помощью Windows SDK, приведен на github, по ссылке в конце статьи. Но сначала несколько примеров встраивания компонента:

но почему то с каким-бы контролом я не работал, все время активным получается именно SplitContainer1.
Поделитесь мыслями на этот счет.

Не работает ActiveControl
:wall:Ничего не пойму. В VB6 не работает ActiveControl.Name - пишет ошибку "91, Переменная объекта.

ActiveControl и возврат названия объекта
ActiveControl.Name - возвращает имя активного контроля. А как сделать чтобы возращял название.


Обращение к специальным свойствам через ActiveControl
Никак нельзя обратиться к Edit.Text и тому подобным специальным свойствам компонентов через.

Как узнать, что ActiveControl имеет свойство Text?
Вылетает ошибка во время выполнения программы '438: Объект не поддерживает это свойство или метод.

Нужно понимать что каждый элемент управления это класс, а значит это набор методов и свойств. Если мы говорим о таком элементе управления как ListBox (список элементов), то его базовый набор необходимых элементов можно описать так:
1) Непосредственно список элементов;
2) Выбранный элемент;
Маловато, не правда ли? Однако это база. Имея список и возможность помнить выбранный элемент из списка мы получаем, то что нам нужно. Будем отталкиваться что возможностей стандартного элемента нам не достаточно (например там нельзя назначать картинку элементу списка), поэтому дополним следующий набор свойств (забегая вперед отметим, что если мы создаем новый элемент с нуля, то он будет является наследником класса UserControl, что значит заведомую реализацию всех стандартных свойств (Enabled, ForeColor и т.д.)):
3) Цвет текста обычного и выбранного элемента;
4) Цвет фона обычного и выбранного элемента;

По поводу изображения нужно сразу определиться с тем что мы хотим видеть в конечном итоге. Хотим ли мы видеть одну картинку рядом с каждым элементом, хотим ли мы что бы эта картинка менялась на другую при выборе элемента, или же мы хотим видеть разные картинки рядом с каждым элементом списка. В зависимости от этого стоит продумать где эти картинки хранить. В первом и втором случае, изображения логичней хранить как свойства нашего контрола, в третем же случая, логичней хранить для каждого объекта в списке. Остановимся на варианте №3.

Сам процесс создания контрола на форме — это чистой воды рисование с помощью класса Graphics, поэтому умение рисовать объекты программно очень пригодится. Нужно четко представлять что, как и где должно располагаться. В нашем случае особых сложностей нет, нам придется рисовать лишь прямоугольники, текст и готовые изображения.

Примечание: В очередной раз сделаю оговорку, что в данном примере мы рассматриваем создание элемента с нуля. Класс ListBox всего лишь ориентир на то, что мы хотим получить. Это означает что будет код, который в принципе можно было не писать, использовав наследование от нужного класса.

Опишем скелет нашего класса контрола:

Шаблон класса есть. Шаблон интуитивно понятен, кроме класса ObjectCollection, которого у нас нет, и который должен отвечать непосредсвено за элементы списка.
Данный класс в себе должен хранить список объектов с изображениями к ним, а так как это public элемент нашего класса GUI, то он должен быть наследником интерфейсов ICollection и IEnumerator. Далее приведу полный листинг данного класса.

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

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


У контрола могут быть два состояния: видимый (активный) и невидимый (неактивный). Активные контролы показываются на захваченном изображении в виде нумерованных выносок, а также перечисляются в самом разделе.

Каждому контролу назначается прямоугольная область на аннотируемом экране. Dr.Explain использует эти области для создания отдельных изображений контролов. Вы можете задавать область контрола при помощи диалога редактирования прямоугольника контрола .

Вы можете изменять взаимное положение контролов, обновлять их свойства и настраивать их внешний вид двумя способами: в основном редакторе или в режиме дизайнера .


Control — шутер от 3-го лица от создателей знаменитой серии Max Payne. Не запутаться в игре поможет наш гайд.

Основы

Оружие

Внешний вид вооружения не блещет разнообразием, но с боевой точки зрения здесь есть где развернуться. Менять оружие можно на ходу.

  • Хватка. Ваш самый первый пистолет, который дают в начале игры. Он не очень силен, поэтому его лучше заменить, когда появится доступ к более продвинутому арсеналу.
  • Осколки. Является местным аналогом дробовика. Большая обойма, мощный урон на близком расстоянии — с этим оружием уже можно воевать серьезно.
  • Вращение. Полуавтоматический пистолет, особенно эффективный при стрельбе короткими очередями. Обладает приличной поражающей способностью и дальностью, но патронов в обойме вечно будет не хватать.
  • Кумулятив. Энергетическое оружие, вмещает два патрона. Выстрелы наносят очень достойный урон, но их сперва придется зарядить. Особенно эффективно использовать это оружие против мощных неповоротливых врагов.
  • Заряд. Местный гранатомет. Особенно хорошо показывает себя против скопления врагов и боссов.

Способности

В ходе игры вы сможете открыть несколько особых умений, рассмотрим их.

  • Метание. Позволяет кидать предметы во врагов. Эффективная способность в бою, которая также поможет сбить вражеский энергетический щит. Необходима, чтобы пройти некоторые сюжетные миссии.
  • Уклонение. С помощью этой способности можно уклоняться от взрывов и вражеских снарядов, в том числе ракет. Очень полезное умение, не лишним будет научиться им пользовать.
  • Левитация. Позволяет летать. Используется в основном в ходе сюжета для преодоления платформ. В бою делает вас уязвимым для вражеских стрелков.

Блиц-советы

  • В бою фокусируйтесь в первую очередь на камикадзе и лекарях.
  • Не игнорируйте иссов, которые висят в воздухе, иначе позже они атакуют вас исподтишка.
  • Хоть патроны и бесконечны, не стоит полностью тратить обойму, иначе начнется перезарядка, которая требует времени. Если вы оставите в обойме хоть 1 патрон, боезапас будет восстанавливаться быстрее.
  • Ближний бой не расходует энергию или патроны, поэтому станет отличным вспомогательным средством в бою. Он также помогает противостоять щитам.
  • При прокачке не забывайте про здоровье и метание, эти параметры будут особенно полезные ближе к концу игры.
  • Откройте все оружие и познакомьтесь с ним поближе. Каждое из них может быть эффективнее других в той или иной ситуации — экспериментируйте.
  • Чтобы восстановить здоровье, подбирайте зеленые сферы. Их можно выбить за убийство обычных врагов или за нанесение урона сильным противникам.

Общие советы по игре

Двигайтесь и маневрируйте

Старайтесь играть как можно активнее, чем более сложные и непредсказуемые маневры вы проводите, тем сложнее по вам попасть.

Отдавайте приоритет здоровью и его восстановлению

В игре нет регенерации здоровья, поэтому нужно стараться максимизировать эффекты его восстановления. Это особенно поможет в тяжелых боях, где за здоровьем нужно следить очень внимательно. Также старайтесь посильнее прокачать сам показатель здоровья, это позволит увереннее чувствовать себя в сражениях и сэкономит вам кучу нервов в финальной части игры.

Чередуйте оружие и способности

Вам будет нелегко фокусироваться на чем-то одном. Скажем, решив быть только стрелком, вас регулярно будет тянуть вниз частая перезарядка. Поэтому старайтесь чередовать оба этих аспекта игры. Стало мало патронов — дайте им восстановиться и воспользуйтесь способностями.

Научитесь читать карту

Карта в игре довольно замысловата, поэтому потеряться здесь проще простого. Но есть одна важная деталь, которая поможет вам лучше ориентироваться в пространстве — оттенок на карте показывает высоту. На карте, как правило, вы увидите два цвета: серый и черный. Так вот каждый цвет показывает разные этажи, как правило, верхний и нижний.

Не пренебрегайте дополнительными заданиями

За них вы будете получать ценный лут, а главное — дополнительные очки для прокачки способностей.

Как улучшить оружие

Это можно сделать в отбитой у врагов контрольной точке. Для этого нужно зайти во вкладку, которая называется астральные конструкции. Там вы сможете создавать новое оружие и делать апгрейд уже имеющихся экземпляров.

Советы по боссам

Альберто Томасси

Сальваторе

Старайтесь использовать колонну как укрытие и метайте в спутников босса куски цемента. Гранаты, которые он кидает в вас, можно перенаправлять на группы врагов. Когда у босса не останется соратников, сбивайте ему защиту с помощью телекинеза и перенаправляйте гранаты в него самого.

Холодильник

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

Светофор

Тут все как в жизни. Если побежите на красный свет, то поплатитесь. Передвигайтесь только на зеленый, ваша задача подойти к боссу и нажать нужную кнопку.

Костюмы: где их найти

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

Асинхронный костюм

Загадка с зеркалами

Чтобы решить загадку с зеркалами, стоя лицом к указанном стороне, нужно установить объекты в положение:

Кандидат П17

Можно найти в локации крыло сдерживания в ходе сюжета.

Офисный помощник

Можно получить в ходе сюжета, ближе к концу игры.

Костюм директора

Станет доступен после прохождения.

Помощник дворника

Можно получить за выполнение всех шести побочных квестов уборщика, который находится в локации старейший дом.

Читайте также: