Clickteam Fusion 2.5 Windows Extension Developement Kit Печать
Добавил(а) microsin   

Этот Extension Developement Kit (далее для краткости EDK) позволит Вам создавать свои собственные индивидуальные объекты (расширения), которые можно будет использовать вместе в CF 2.5. Каждый объект может иметь свое собственное поведение и специализированные условия событий (conditions) и действия (actions). Для того, чтобы начать создавать свои расширения, Вам понадобится Visual C++ 2005 (или более свежая версия) и хорошие знания языка C.

Примечание: здесь приведен перевод документации из Clickteam Fusion 2.5 Extension Developement Kit, файл Extensions\Help\ExtSdk.chm из архива Fusion25SDK.zip [1]. Этот EDK предназначен для создания расширений на платформе Windows, и является базовым. В принципе система CF предназначена для написания не только WEB-приложений (на основе HTML5), но также и приложений для других платформ: Windows, iOS, Android, Flash, Mac OS. В документации часто одно и то же называют то SDK, то EDK, вроде бы как это одно и то же.

Команда CF 2.5 разработала файл шаблона, чтобы ускорить и упростить для Вас создание первого объекта расширения. Все, что Вам нужно - заполнить пустые места в шаблоне для настройки своего объекта. Этот файл шаблона разработан так, что он быстро скомпилируется на Вашей рабочей станции.

Предлагаемый EDK очень мощный инструмент, поставляемый с обширной документацией. Рекомендуется уделить время для обстоятельного изучения содержимого EDK, шаг за шагом, чтобы полностью понять его. Хороший кандидат на первую попытку создания объекта - сделать невидимый объект, который просто генерирует звуковой сигнал BEEP или печатает слово "Hello!" на экране. После того, как освоите базовые техники по созданию простых объектов, добавьте к ним больше действий, и создайте более сложные объекты. Попробуйте добавить подпрограмму, которая нарисует во фреймах объект, который Вы создали.

Команда Clickteam определенно хотела бы видеть Ваши расширения доступными в магазине Clickstore [2], что в перспективе может Вам предоставить шанс дополнительного заработка на разработанных объектах.

[Папка Template]

В этой папке содержится готовый для сборки шаблон нового объекта с примерами условий событий (example conditions), действий (actions) и выражений (expressions). Шаблон состоит из нескольких файлов:

Файл Описание
Common.h Общий заголовочный файл, который нужно подключить ко всем Вашим файлам исходного кода. Содержит несколько определений, которые позволят ограничить Ваше расширение для определенных версий и определенных сборок CF.
Edittime.cpp Содержит функции, вызываемые редакторами фрейма (Frame editor) и событий (Event editor).
Ext.def Файл определения экспорта для линкера (export definition file). Этот файл очень важен: здесь перечислены все функции, к которым нужно получить доступ из CF. Если Вы включите функцию, вызываемую из CF, то убедитесь, что она также включена и в файле .def (удалите точку с запятой перед именем функции), поскольку CF знает только те имена, которые находятся в файле .def, и не осуществляет доступ к функциям через их декорированные имена.
Ext.rc Файл ресурсов для Вашего проекта. Содержит все строки, рисунки, окна диалога объекта.
General.cpp Содержит подпрограммы, которые вызываются во время редактирования (edit time) и во время выполнения (run time).
Main.cpp Содержит действия (actions), условия (conditions) и выражения (expressions) для Вашего объекта.
Main.h Файл определения для Вашего расширения, содержит структуры EDITDATA и RUNDATA, необходимые для редактирования и запуска объекта.
Runtime.cpp Содержит функции, необходимые для запуска объекта, которые работают только во время выполнения (run time). Включает подпрограммы для отладчика, подпрограммы для отображения Вашего объекта во время выполнения приложения, и т. д.
Resource.h Файл определения для ресурсов. Содержит идентификаторы разных элементов в файле ресурсов.
Template.sln Файл решения (Solution file) для использования в среде разработки MS Visual C++ 2005 (или более новой версии).
Template.vcproj Файл проекта для использования в среде разработки MS Visual C++ 2005 (или более новой версии).
Res\ExtIcon.bmp Иконка для объекта: эта картинка используется для отображения объекта в окне объекта редактора фрейма и редактора событий. Удалите этот файл, если Вы рисуете свою иконку вручную с помощью функции MakeIconEx.
Res\ExtImg.bmp Картинка объекта: будет использоваться для отображения объекта в редакторе фрейма. Удалите этот файл, если Вы рисуете объект вручную с помощью функции EditorDisplay.

[Папка Inc]

Папка Inc содержит все подключаемый файлы, необходимые для компиляции Вашего расширения.

Файл Описание
Ccx.h Этот файл содержит определения, используемые расширением и компилятором ресурсов. Вы должны подключить его в начало своего кода.
Ccxhdr.h Этот файл сразу подключает все необходимые файлы. Вы должны подключить его в начало своего кода.
CfcError.h Содержит некоторые коды ошибки, возвращаемые функциями из mmfs2.dll и в фильтрах.
CfcFile.h Содержит декларации файловых функций в mmfs2.dll.
Cncf.h Этот файл содержит основное определение для выполняемого кода (runtime). Содержит все структуры, объекты, действия и условия, используемые во время работы объекта. Он автоматически подключается из файла Ccxhdr.h.
Cncy.h Содержит другие структуры для работы приложения (runtime) и среды CF (edittime). Он автоматически подключается из файла Ccxhdr.h.
Cncr.h Маленький файл, который подключает все другие файлы. Он автоматически подключается из файла Ccxhdr.h.
Cnpdll.h Содержит основную часть определений для mmfs2.dll. Он автоматически подключается из файла Ccxhdr.h.
Colors.h Маленький файл, который содержит определения базовых цветов, используемых в программах (таких как BLACK, к примеру). Он автоматически подключается из файла Ccxhdr.h.
EvtCcx.h Этот файл определяет структуры информации события, используемые в главном исходном коде расширения, чтобы определить действия (actions) и условия (conditions) событий. Он автоматически подключается из файла Ccxhdr.h.
Fill.h Содержит структуры, используемые для функций заливки (Rectangle, Ellipse, и т. д.) в классе cSurface.
FilterMgr.h Содержит базовый класс для менеджеров фильтров изображения и звука.
ImageFlt.h Содержит менеджер фильтра изображения (image filter manager) и классы фильтра изображений (image filter classes).
ImageDefs.h Игнорируйте этот файл, он присутствует только для поддержки совместимости с внутренними подключаемыми файлами Clickteam.
Palet.h Содержит функции палитры в mmfs2.dll.
PictEdDefs.h Содержит определения для редактора изображений (picture editor). Для использования в функциях mvEditImage и mvEditAnimation.
Props.h Этот файл содержит определения новой системы свойств CF. Он автоматически подключается из файла Ccxhdr.h.
PType.h Этот файл содержит определения некоторых типов переменных, используемых программой. Он автоматически подключается из файла Ccxhdr.h.
SndDefs.h Игнорируйте этот файл, он присутствует только для поддержки совместимости с внутренними подключаемыми файлами Clickteam.
SoundFilter.h Содержит класс фильтра звука (sound filter class).
SoundFilterMgr.h Содержит класс менеджера фильтра звука (sound filter manager class).
StdDefs.h Игнорируйте этот файл, он присутствует только для поддержки совместимости с внутренними подключаемыми файлами Clickteam.
Surface.h Содержит класс cSurface class. Подключается файлом Common.h в шаблоне расширения (Extension template).
SurfaceDefs.h Игнорируйте этот файл, он присутствует только для поддержки совместимости с внутренними подключаемыми файлами Clickteam.
TigsExt.hpp Содержит некоторые полезные макросы, созданные для старого SDK стороннего производителя.
WinMacro.h Набор макросов, определенных для Win32.

[Папка Lib]

Папка Lib содержит только один файл: Mmfs2.Lib. Он используется для линковки CF DLL к расширению, если это необходимо.

[Примеры объектов (расширений)]

Разработчики добавили в EDK полнофункциональные объекты в качестве примеров, чтобы Вы могли научиться делать объекты, исследуя готовый код.

Simple Data Buffer. Это пример объекта, у которого нет вывода во фрейм. Он используется только для сохранения данных, и может также загружать или сохранять файлы.

Simple Text. Это пример объекта, который отображает простой текст. Оп показывает, как отображать что-то во фрейме с использованием функции DisplayRunObject.

Simple Picture. Это пример объекта, который загружает и отображает файлы картинок. Он демонстрирует, как загружать файлы, используя фильтры графики (CF graphic filters), как отображать их с использованием функции GetRunObjectSurface, и как точно обрабатывать коллизии объектов с использованием функции GetRunObjectCollisionMask.

Simple Control. Этот пример объекта управления Windows (Windows control object), например объект, отображаемый в своем собственном окне (в действительности этот объект работает как окно редактирования, edit box). Он показывает, как работать с подклассами окон управления и как перехватывать основные оконные сообщения. Дополнительно показывается разница между обычными условиями и "событийными" условиями ("event" conditions) в системе CF.

Замечание: если Вы используете один из этих примеров как базу для своих собственных объектов, например если Вы повторно используете исходный код и файл проекта для своих собственных расширений, то не забудьте поменять этот идентификатор в файле Main.h.

[Поддержка Unicode]

CF 2.5 работает как Unicode-приложение, обрабатывает текст в формате Unicode (UTF16).

Формат Unicode позволяет сохранять в строке любые символы, например греческие (Greek) или китайские (Chinese). Строка UTF16 состоит из 2-байтных символов, вместо 1-байтных символов для обычной строки, не Unicode (например, в кодировке ASCII или ANSI).

CF 2.5 поддерживает оба вида расширений, расширения не-Unicode (наподобие старых расширений, сделанных для MMF2) и расширения Unicode. В этом SDK предоставляется шаблон (Template), который сконфигурирован по умолчанию для поддержки Unicode. Он использует следующие макросы:

TCHAR заменяется на WCHAR в режиме Unicode (и на char в режиме не-Unicode).
LPTSTR заменяется на LPWSTR в режиме Unicode (и на LPSTR в режиме не-Unicode).
LPCTSTR заменяется на LPCWSTR в режиме Unicode (и на LPCSTR в режиме не-Unicode).
_tcslen заменяется на wcslen в режиме Unicode (и на strlen в режиме не-Unicode).

и так далее ...

Замечание: эти макросы позволяют Вам собрать не-Unicode расширение, если Вы действительно не можете собрать расширение Unicode (например, если Ваше расширение использует какую-то не-Unicode библиотеку). Если Вы хотите собрать не-Unicode расширение, удалите определения для препроцессора UNICODE и _UNICODE, установите набор символов на MBCS в настройках проекта и установите Ваше расширение в папки Extensions и Data\Runtime каталога установки CF (вместо папок Extensions\Unicode и Data\Runtime\Unicode). Когда CF передает текст в не-Unicode расширение (или принимает от него текст), то текст преобразуется в кодировку ANSI (или из кодировки ANSI) с использованием языка текущего пользователя.

CF 2.5 может загрузить расширения Unicode или расширения не-Unicode, однако конечно же символы Unicode поддерживаются только расширениями Unicode, поэтому если Ваши расширения используют текст, то Вы должны портировать их для поддержки Unicode.

[Как преобразовать расширение, чтобы оно поддерживало Unicode]

Чтобы скомпилировать расширение Unicode, добавьте определения препроцессора _UNICODE и UNICODE в настройки своего проекта расширения. Если Вы не определите эти символы, то расширение будет скомпилировано в режиме не-Unicode.

Первое, что нужно сделать, это папку расширения MMF2 (extension folder) в папку Fusion25SDK/Extensions, и затем нужно:

• Отредактировать этот проект.
• Добавить в проект новые конфигурации Debug Unicode, Release Unicode и Runtime Unicode на основе конфигураций Debug, Release и Runtime.
• Для этих новых добавленных конфигураций добавить _UNICODE и UNICODE в определения для препроцессора.

Замечание 1: расширения Unicode устанавливаются в папки и Extensions\Unicode и Data\Runtime\Unicode, находящиеся в каталоге установки CF.

Замечание 2: если Вы используете MSVC++ 2005 или более новую версию этой системы разработки, то установите опцию "Threat wchar_t as Built-in Type" в значение "No" раздела "C++ / Language" настроек проекта, иначе будут появляться ошибки линковки, если Вы используете функции в DLL с текстовыми параметрами.

[Что делают символы _UNICODE и UNICODE]

Все структуры и функции Windows, которые используют текст, как и все структуры CF, существуют в 2 версиях: без поддержки Unicode и с поддержкой Unicode. Когда определены макросы _UNICODE и UNICODE, используется Unicode-версия структур, а когда эти макросы не определены, то используется non-Unicode версия.

Например, на самом деле Windows-функция DrawText не существует, и Windows содержит 2 функции DrawTextA и DrawTextW, и DrawText на самом деле макрос, который устанавливается на функцию DrawTextW, если определены _UNICODE/UNICODE, и устанавливается на DrawTextA, если _UNICODE/UNICODE не определены.

Структуры наподобие LOGFONT также макросы, и реальные структуры для него это LOGFONTA и LOGFONTW.

Такие функции и структуры не используют типы char или char*, они также используют вместо них макросы, поэтому для определения строк нужно соблюдать определенные правила, если хотите, чтобы расширение могло компилироваться и для поддержки Unicode, и без поддержки Unicode, если это необходимо. Поэтому замените определения символов и строк следующим образом:

char замените на TCHAR
LPSTR замените на LPTSTR
LPCSTR замените на LPCTSTR

Замечание: Вы все еще можете использовать char для символов, которые всегда не Unicode, или WCHAR для символов, которые всегда Unicode.

Функции из библиотеки C++ runtime также заменяются на макросы (это определено в tchar.h):

strlen заменяется на _tcslen
strcpy заменяется на _tcscpy
splitpath заменяется на _tsplitpath

... и так далее.

Для строк констант используйте макрос _T: "это строка" замените на _T("это строка"):

//char str1[] = "это строка";
TCHAR str1[] = _T("это строка");

Если Ваше расширение компилируется в режиме Unicode, то _T("это строка") будет заменено на L"это строка", иначе это будет заменено на "это строка".

[Изменения в Main.h]

Структура EDITDATA. Эта наиболее проблематичная часть в преобразовании расширений. Если расширение не содержит и не использует никакого текста, то ничего делать не нужно. Если же текст используется, то нужно обеспечить поддержку 2 версий этой структуры:

• Структура EDITDATAA, non-Unicode, со значениями типа char (например, char array[2], LOGFONTA lfnt, и т. д.).
• Структура EDITDATAW, Unicode, со значениями типа WCHAR (например, WCHAR array[2], LOGFONTW lfnt, и т. д.).

И еще сделайте следующее определение:

#ifdef _UNICODE
  #define EDITDATA EDITDATAW
  #define LPEDATA LPEDATAW
#else
  #define EDITDATA EDITDATAA
  #define LPEDATA LPEDATAA
#endif

Внимание: не используйте в EDITDATA тип TCHAR.

Вы также также должны конвертировать Вашу структуру EDITDATA в Unicode и из Unicode в функции UpdateEditStructure, находящейся в модуле General.cpp: если Вы уже не используете функцию UpdateEditStructure в своем приложении, то реализуйте её так, чтобы она могла конвертировать не-Unicode EDITDATA из не-Unicode приложений в Unicode.

Вы можете проверить, загружен ли файл приложения как Unicode, вызовом функции mvIsUnicodeApp(pMV, pMV->mvEditApp). Если Вам не нужно преобразовывать текст из Unicode и в Unicode, то используйте функции MultiByteToWideChar и WideCharToMultiByte, и используйте в качестве кодовой страницы mvGetAppCodePage(pMV, pMV->mvEditApp).

Как все это делается, можно подсмотреть в примере Simple Control. И не забудьте разрешить эту функцию в файле .DEF!

Если уже используете UpdateEditStructure для обновления EDITDATA старых версий Вашего объекта, то это немного более сложнее: сначала Вам нужно обновить старую структуру EDITDATAA, и затем преобразовать обновленную структуру EDITDATAA в Unicode.

Структура RUNDATA. С ней ничего делать не нужно, просто используйте типы TCHAR, LPTSTR, и т. д.

[Изменения в других модулях исходного кода]

EditTime.cpp. Замените LPSTR (или LPCSTR) на LPTSTR (или LPCTSTR) в заголовочных файлах следующих функций: 

GetObjInfos
GetHelpFileName
MakeIconEx
UsesFile
CreateFromFile
GetTextFont
GetCodeTitle
GetConditionTitle
GetActionTitle
GetExpressionTitle
GetConditionString
GetActionString
GetExpressionString
GetExpressionParam
GetParameterString

И замените функцию menucpy на следующий код:

void menucpy(HMENU hTargetMenu, HMENU hSourceMenu)
{
#ifndef RUN_ONLY
   int n, id, nMn;
   LPTSTR strBuf;
   HMENU hSubMenu;
 
   nMn = GetMenuItemCount(hSourceMenu);
   strBuf = (LPTSTR)calloc(80, sizeof(TCHAR));
   for (n = 0; n < nMn; n++)
   {
      if (0 == (id = GetMenuItemID(hSourceMenu, n)))
         AppendMenu(hTargetMenu, MF_SEPARATOR, 0, 0L);
      else
      {
         GetMenuString(hSourceMenu, n, strBuf, 80, MF_BYPOSITION);
         if (id != -1)
            AppendMenu(hTargetMenu, GetMenuState(hSourceMenu, n, MF_BYPOSITION), id, strBuf);
         else
         {
            hSubMenu = CreatePopupMenu();
            AppendMenu(hTargetMenu, MF_POPUP | MF_STRING, (uint)hSubMenu, strBuf);
            menucpy(hSubMenu, GetSubMenu(hSourceMenu, n));
         }
      }
   }
   free(strBuf);
#endif
}

Свойства (Properties). Не нужно делать изменений в свойствах, кроме строковых свойств. Вы использовали ранее CPropDataValue, чтобы передать/получить строки в/из менеджера свойств (CF property manager). Теперь здесь новый класс CPropStringValue. В функции GetPropValue:

   // Было раньше так:
  case PROPID_MYSTRING:
      return new CPropDataValue((LPCSTR)edPtr->myString);
 
   // Теперь стало так:
   case PROPID_MYSTRING:
      return new CPropStringValue(edPtr->myString);

В функции SetPropValue:

   // Было раньше так:
   case PROPID_MYSTRING:
      LPSTR ps = (LPSTR)((CPropDataValue*)pValue)->m_pData;
 
   // Теперь стало так:
   case PROPID_MYSTRING:
      LPCTSTR ps = ((CPropStringValue*)pValue)->GetString();

Runtime.cpp. Замените заголовочный файл функции GetDebugItem:

void WINAPI DLLExport GetDebugItem(LPTSTR pBuffer, LPRDATA rdPtr, int id)

General.cpp. Добавьте в функции GetInfos:

   case KGI_UNICODE:
#ifdef _UNICODE
      return TRUE;
#else
      return FALSE;
#endif

Это позволит системе CF узнать, каким является Ваше расширение: Unicode или non-Unicode. Тогда CF преобразует эти строки, если это необходимо, перед передачей из в функции Вашего расширения, или после получения строк из Вашего расширения.

Замените LPCSTR на LPCTSTR в функции GetDependencies() и в таблице, которую она возвращает, если это нужно (и добавьте _T() в имена dll в этой таблице). Замените LPSTR на LPTSTR в заголовочном файле функции UpdateFileNames.

Реализуйте структуру UpdateEditStructure, если это необходимо, как это было описано выше.

Main.cpp. Действия/условия/выражения (Actions/Conditions/Expressions): если Ваше расширение Unicode, то оно будет принимать строковые параметры в формате Unicode (и будет возвращать строковые параметры в формате Unicode). Если Ваше расширение не Unicode, то оно будет получать/возвращать строки non-Unicode.

Замечание: параметр size в функциях RFUNCTION_GETSTRINGSPACE и RFUNCTION_GETSTRINGSPACE_EX указывается в байтах. Умножьте длину строки на sizeof(TCHAR), чтобы получить size байтах.

Изменения в runtime-структурах. Есть несколько изменений в некоторых внутренних структурах, особенно в структуре objInfoList. Теоретически Вам нужно заботиться о них только если Ваше расширение не Unicode, и Вы хотите получить прямой доступ к этим структурам.

1. Сделайте копию директории исходного кода Вашего расширения в папку MMF2SDK\Extensions.

2. Откройте Ваш файл проекта в Visual Studio, отредактируйте настройки проекта, выберите All Configurations.

2a. В combo-боксе Settings For выберите закладку C/C++, Preprocessor и введите ..\..\inc как дополнительную директорию для подключаемых файлов (Additional Include directory).
2b. Сделайте то же самое для закладки Resources.
2c. На закладке Link замените ..\lib\cncs32.lib на ..\..\lib\mmfs2.lib (обратите внимание: это mmfs2, не mmfs32).
2d. Рекомендуется: MMF2 runtime линкуется с MSVCRT.DLL, так что не нужно для Вашего расширения подключить код из библиотеки C runtime. Чтобы линковать расширение с MSVCRT.DLL, выберите закладку C/C++, Code Generation и выберите Multithreaded DLL. Имейте в виду, что некоторые расширения используют расширения C++, которые являются частью другой MS VC DLL, msvcp60.dll (например, если Вы используете объект "string"). Поскольку эта DLL может быть не установлена в старых системах, Вы должны сохранить старые установки (или поменять их на Multithreaded или Single-threaded, если Ваше расширение было линкован с этой DLL).

Если Вы используете Visual Studio 2005 или Visual Studio 2005 Express, то не должны использовать "Multithreaded DLL", так как файлы VS2005 runtime пока не присутствуют на некоторых машинах, и от пользователя может потребоваться загрузить и установить эти файлы runtime библиотек (2.5 мегабайта) с сайта microsoft.com.

Чтобы узнать, какие DLL требуются для Вашего расширения, запустите утилиту MS "Dependency Viewer", и загрузите Ваш файл .MFX.

3. Отредактируйте Ваш файл Common.h, сохранив свои определения (если они есть) и перезапишите остальную часть Common.h новым шаблоном. Также, если есть файл TigsExt.hpp в Вашей директории, то удалите его. Обновленная версия этого файла подключается из директории Inc.

4. Важно: функции UsesFile и CreateFromFile (модуль EditTime.cpp) теперь в качестве первого параметра имеют LPMV mV. В должны добавить его, иначе MMF упадет, если Вы бросите файл в редактор фрейма.

BOOL WINAPI DLLExport UsesFile (LPMV mV, LPCTSTR fileName)
void WINAPI DLLExport CreateFromFile (LPMV mV, LPCTSTR fileName, LPEDATA edPtr)

5. Важно: если используете RFUNCTION_SUBCLASSWINDOW, то количество окон, которое Вы передаете в эту функцию, теперь int вместо short int. Вы должны модифицировать свою структуру RUNDATA, чтобы она получила соответствующее изменение.

6. Менее важно:

• Некоторые функции MMFireSDK не находились в правильных файлах исходного кода. EnumElts, LoadObject, UnloadObject, Initialize, Free, UpdateEditStructure, UpdateFileNames должны быть перемещены в модуль General.cpp, так как они тоже вызываются из runtime, и их пустые клоны должны быть удалены из RunOnly.cpp.
• Заголовок параметров выражения теперь отображается в редакторе выражения (expression editor). Проверьте определение в своих расширениях, чтобы увидеть, имеют ли их параметры корректные имена, и реализуйте функцию GetExpressionParam (скопируйте её из шаблона в Edittime.cpp).
• mvGetExtUserData и mvSetExtUserData, используемые для сохранения глобальных данных в приложении, получили незначительные изменения. Вы должны передать параметр CRunApp*. Используйте либо CRunApp*, передаваемый как параметр функций StartApp / EndApp / StartFrame / EndFrame, или используйте rhPtr->rhApp, если Вы вызвали если Вы вызываете их из другой функции.

7. Отредактируйте Ваши строки ресурсов, поменяйте значение строки KPX_MARK на "MF2", и удалите строку KPX_CREATETITLE. Вы также можете удалить M_DESC, если не используете это, так как это не используется в MMF2.

8. Скомпилируйте Ваше расширение. Ниже см. описание и способы устранения возможных ошибок компиляции и линковки.

[Возможные ошибки линковки]

"Cannot find unique match for symbol xxx".

("Не получается найти уникальное совпадение для символа xxx"). Обычно эта проблема происходит с функциями Initialize и Free. Чтобы исправить их, либо добавьте extern "C" перед заголовком функции, например так:

extern "C" int WINAPI DLLExport Initialize(mv _far *knpV, int quiet)

Или замените определения Initialize и Free в своем файле .def:

Initialize = ?Initialize@@YGHPAVmv@@H@Z @25
Free = ?Free@@YGHPAVmv@@@Z @26

[Возможные ошибки компиляции]

"error C2664: 'DialogBoxParamA': cannot convert parameter 4 from etc."

("ошибка C2664: 'DialogBoxParamA': не могу преобразовать параметр 4 из ..."). Функция DialOpen теперь простой макрос (переопределение для DialogBoxParam). Удалите приведение типа FARPROC перед параметром оконной процедуры (window proc), чтобы избежать этой ошибки компиляции (также Вы можете напрямую использовать DialogBoxParam).

Ошибка в RunOnly.cpp, "loOiNum not a member of 'LO'" ("loOiNum не член 'LO'"). Удалите строку "loPtr->loOiNum = 0".

Структура rhy больше не существует. Как теперь получить доступ к переменным, которые были раньше членами этой структуры:

ryPtr->ryHmainWin замените на rhPtr->rhHMainWin
ryPtr->ryHeditWin замените на rhPtr->rhHEditWin

DibToImage и AddImage теперь не возвращают DWORD, но старшее слово всегда равно 0 в MMF2, так что Вы можете безопасно использовать приведение типа результата к WORD.

oiHFran больше не является членом ObjInfo. Вам не нужен этот параметр в MMF2, можно просто удалить все ссылки на него.

Члены oiEffect и oiEffectParam структуры ObjInfo теперь oiHdr.oiInkEffect и oiHdr.oiInkEffectParam. Если Вы их используете, то убедитесь, что наложили маску EFFECT_MASK на oiInkEffect.

GetKnpPalette был переименован в GetPaletteAppli.

Изменен параметр AntiA функций cSurface::Blit. Если используете этот параметр, то просто замените TRUE на BLTF_ANTIA для blit с анти-алиасингом.

Изменены параметры по умолчанию Blit и Stretch, так что это иногда может привести к ошибкам компиляции, или даже с проблемами компилятора, который ошибется с 2 функциями и не выдаст какое-либо предупреждение. Если встретитесь с любой ошибкой компиляции, просто добавьте параметры по умолчанию к функции (BMODE_OPAQUE, BOP_COPY, и т. д.).

Изменены параметры функции cSurface::DrawText. Вставьте счетчик символов (или -1) перед вторым параметром.

cSurface::BeginRaster и EndRaster заменены на LPBYTE cSurface::LockBuffer() и void cSurface::UnlockBuffer(LPBYTE spBuffer).

mvKncHelp была заменена на mvHelp.

Изменена функция mvEditImage. Редактор изображений (Picture Editor) теперь работает как диалоговое окно (dialog box), наподобие TGF, так что больше не нужно закрывать Ваше окно настройки (setup box) для редактирования изображения. Вы можете редактировать свою картинку примерно так:

EditImageParams eip;
eip.m_dwSize = sizeof(EditImageParams);
eip.m_pWindowTitle = NULL;
eip.m_dwImage = edPtr->nImage;
eip.m_dwOptions = PICTEDOPT_NOALPHACHANNEL | PICTEDOPT_CANBETRANSPARENT;
eip.m_dwFixedWidth = 32;
eip.m_dwFixedHeight = 32;
 
if ( spa->kv->mvEditImage(edPtr, &eip, hDlg) )
{
   edPtr->nImage = (WORD)eip.m_dwImage;
}

Символы PICTEDOPT_ определены в PicEdDefs.h.

Применение функции mvEditAnimation. Вы можете редактировать анимацию примерно так:

EditAnimationParams eap;
eap.m_dwSize = sizeof(EditAnimationParams);
eap.m_pWindowTitle = NULL;
eap.m_nImages = number_of_images;
eap.m_nMaxImages = max_number_of_images_in_image_list;
eap.m_nStartIndex = 0;
eap.m_pImages = image_list;   // LPWORD
eap.m_pImageTitles = NULL;
eap.m_dwOptions = PICTEDOPT_etc.;
eap.m_dwFixedWidth = 32;
eap.m_dwFixedHeight = 32;
 
if ( spa->kv->mvEditAnimation(edPtr, &eap, hDlg) )
{
   number_of_images = eap.m_nImages;
}

[Как реализовать новые функции MMF2 в Вашем расширении]

Функция MakeIcon все еще вызывается, если в Вашем расширении она есть, но это устарело. Если эта функции только загружает EXO_ICON bitmap из своих ресурсов, то Вы можете удалить её, MMF2 теперь загружает эту растровую картинку по умолчанию, и берет цвет первой точки как цвет прозрачности (transparent color). Иначе Вы можете заменить это функцией MakeIconEx, которая использует параметр cSurface и она проще для применения (и может поддерживать большее количество цветов).

Функция EditorDisplay теперь не обязательна. Если эта функция отсутствует, то MMF загружает и отображает EXO_IMAGE bitmap из ресурсов расширения. Цвет первой точки этой картинки считается цветом прозрачности (transparent).

Устарели функции AppendPopup, SelectPopup, ModifyObject, RebuildExt и EndModifyObject:

- Функции AppendPopup, RebuiltExt и EndModifyObject больше не вызываются.
- Функция SelectPopup все еще существует по соображениям совместимости, но Вы должны заменить её функцией EditObject, которая вызывается, когда пользователь делает двойной клик на объекте, или выбирает Edit в контекстном меню редактора фрейма (frame editor).
- ModifyObject все еще существует по соображениям совместимости (для расширений, размеры которых могут быть изменены), но Вы должны заменить её функцией SetEditSize, если Ваше расширение может менять размер, или удалить её, если расширение не может менять размер.

Если Вы хотите использовать новую систему свойств, то реализуйте следующие функции:

GetProperties
GetPropValue / SetPropValue
GetPropCheck / SetPropCheck
IsPropEnabled
EditProp

Также определите идентификаторы свойств и определения в файле Edittime.cpp.

Если Вы хотите отобразить значения в отладчике (Debugger) для своего объекта, то реализуйте следующие функции:

GetDebugTree
GetDebugItem
EditDebugItem

Также задайте идентификаторы элемента и определения в Runtime.cpp.

Реализуйте функцию GetHelpFileName в Edittime.cpp, с ней MMF может отобразить Ваш файл подсказки (help file), когда пользователь кликает на кнопку Help в меню свойств About объекта.

Замечание: окне диалога настройки (setup dialog box), если Вы все еще его используете, вызовите mV->mhHelp to display your help file. E.g. mV->mvHelp(GetHelpFileName(), HH_DISPLAY_TOPIC, (LPARAM)"index.html");

Если Вашему расширению для работы нужны внешние DLL (bass.dll и т. п.), то реализуйте функцию GetDependencies, и установите эти DLL в папку Data\Runtime системы MMF, чтобы они были включены в автономные приложения.

Если Вы хотите, чтобы Ваш объект поддерживал переходы (transitions), то можно это реализовать. Добавьте OEPREFS_TRANSITIONS к флагам OEPREFS в своем файле .h, и затем реализуйте GetRunObjectSurface. Если эта функция имеется, то она будет вызвана вместо DisplayRunObject, и возвратит поверхность (surface), которая отображается на экране.

Замечание: если Вы хотите, чтобы расширение использовало эффекты чернил (ink effects), то это можно реализовать вручную в DisplayRunObject, или Вы можете позволить системе MMF делать это автоматически, если реализуете GetRunObjectSurface, описанную ранее. Примечание: Вы также должны добавить опцию OEPREFS_INKEFFECT к своим опциям OEPREFS.

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

Если Ваш объект отображает текст, и Вы хотите поддерживать автоматические текстовые функции MMF:

- edittime (во время редактирования приложения): реализуйте функции GetTextCaps, GetTextFont, и т. д. (обратите внимание: они такие же в MMF 1.5).
- runtime (во время работы приложения): добавьте OEFLAG_TEXT в свои OEFLAGS, и реализуйте функцию GetRunObjectFont и другие функции.

Если Вы хотите ограничить использование расширения для некоторых версий или сборок MMF2, то добавьте следующий код к GetInfos:

case KGI_PRODUCT:
   return PRODUCT_VERSION_STANDARD;    // Стандартная система MMF, или более новаяcase KGI_BUILD:
   return 219;                         // Сборка 219 или более новая

Если Ваше расширение загружает файлы, и Вы хотите реализовать поддержку новых двоичных встроенных файлов MMF2, то рассмотрите callback-функции mvOpenHFile, mvCloseHFile, mvGetFile, mvReleaseFile в Help-файле SDK [1].

[Пошаговый процесс создания расширения]

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

• Компиляция
• Изменение имени и иконок
• Выбор флагов объекта
• Создание диалога настройки (setup dialog) и/или свойств объекта (properties)
• Создание подпрограмм, которые действуют во время работы приложения (run-time routines).
• Создание условий событий (conditions), действий (actions), выражений (expressions)
• Отладка Вашего объекта расширения

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

Первая задача состоит в правильной компиляции этого шаблона.

• Распакуйте архив EDK (Fusion25SDK.zip) в произвольную папку на диске. Предположим, что это будет папка Fusion25Sdk. В ней появятся 3 папки: Extensions, Inc, Lib.
• Сделайте копию папки "Template", которая находится в каталоге Fusion25Sdk\Extensions, под новым именем. Хорошее решение - дать папке имя, чтобы оно соответствовало Вашему новому расширению. Предположим, это будет папка Template-copy, которая находится в том же каталоге Fusion25Sdk\Extensions.
• Запустите среду Visual C++, и загрузите в ней решение Template.sln (solution) из этой скопированной папки.

Примечание: если у Вас IDE более новой версии, чем Microsoft Visual C++ 2005, то запустится мастер преобразования решения и проекта. Выполните все шаги, необходимые для этого преобразования (Далее -> Резервную копию не создавать, далее -> Готово -> Закрыть).

• Выберите конфигурацию отладки (Debug Unicode), скомпилируйте, компиляция должна пройти успешно. Если все прошло хорошо, то в результате получится файл "Template.mfx" в папке Obj\Unicode\Debug.

Примечание: у меня при компиляции появлялись предупреждения MSB8012, сообщающие о том, что цель компиляции TargetPath (c:\..\Debug\Template.dll) не соответствует значению свойства выходного файла (c:\..\Template.mfx) для Linker. Это может привести к неправильному построению проекта. Чтобы исправить это, убедитесь, что значения свойств $(OutDir), $(TargetName) и $(TargetExt) соответствуют значению, указанному в %(Link.OutputFile). Насколько я понял, это предупреждение не критично, и связано с тем, что в командной строке линкера выходной файл указан с расширением .mfx вместо традиционного расширения .dll (опция линкера /OUT:).

• Теперь мы поменяем имя расширения. Откройте диалог настроек проекта (Project Settings, открывается выбором свойств в контекстном меню, если сделать правый клик на имени проекта), убедитесь, что выбрана конфигурация Debug Unicode. Перейдите в раздел Link (Компоновщик -> Общие), и поменяйте настройку "Output file name" (Выходной файл). Цель изменения - чтобы выходной файл после компиляции оказался в каталоге Extensions\Unicode текущей копии Вашей системы CF 2.5, тогда сразу после компиляции расширение будет доступно в среде CF. И конечно, переименуйте "Template.mfx" в то имя, которое хотите. Как вариант, для этой цели можно использовать событие построения (Post-build event), чтобы сделать копию Template.mfx в то имя и тот каталог, которые Вам нужны.

Важное замечание: объект MFX это стандартный DLL-файл. Если CF работает, и приложение использует Ваше загруженное расширение, то линкер не сможет поместить выходной файл в директорию Extensions\Unicode. Вы должны закрыть приложение, загруженное в CF, которое использует Ваше расширение, перед тем, как выполнить компиляцию расширения. Примечание: не нужно закрывать саму среду CF, просто обновите список объектов и убедитесь сначала в наличие Вашего расширения в списке, если среда CF уже открыта. Должно быть закрыто приложение, использующее это расширение, и должны быть закрыты окна редакторов, где вставлено это расширение.

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

Каждое расширение должно иметь свой собственный идентификатор. Этот идентификатор используется, например, когда Вы копируете (copy) / вставляете (paste) действия или условия (actions, conditions), связанные с объектами расширения. Если 2 расширения используют одинаковый идентификатор, то CF не будет знать, с каким расширением связать вставленные события. Таким образом первое, что Вам нужно сделать при создании своего расширения, это дать ему уникальный идентификатор.

Идентификатор определяется в файле Main.h. Идентификатор по умолчанию следующий:

#define IDENTIFIER MAKEID(S,A,M,2)

Просто поменяйте буквы в макросе MAKEID. Когда это будет сделано, удалите перед ним директиву #pragma message, чтобы удалить предупреждающее сообщение.

 

Иконки определены как растровые картинки (bitmaps), находящиеся во внешних файлах в формате BMP. 

EXO_ICON (файл Exticon.bmp): иконка объекта, как он выглядит в окне "Object" редактора фрейма (Frame editor) и редактора событий (Event editor). Цвет первой точки картинки считается цветом прозрачности (transparent color). Если Вы хотите поменять цвет прозрачности, то реализуйте функцию MakeIconEx, загрузите свою иконку в параметр поверхности (surface) и поменяйте её цвет прозрачности функцией cSurface::SetTransparentColor.

EXO_IMAGE (файл Extimg.bmp): картинка объекта, отображаемая в игровом поле редактора фрейма. Вы должны сделать трехмерный вид этой иконки, чтобы её было проще идентифицировать на игровом поле. Цвет первой точки дает цвет прозрачности. Если Вы не хотите использовать цвет прозрачности, или хотите нарисовать свою иконку вручную (программно), то реализуйте функцию EditorDisplay. 

 

В файле Ext.rc Вы найдете разные строки, описывающие объект, отображаемые в свойствах закладки About объекта (окно Property системы CF). 

KPX_NAME и IDST_OBJNAME: содержат имя объекта.
IDST_AUTHOR должна содержать Ваше имя.
IDST_COPYRIGHT должна содержать Ваш копирайт.
IDST_COMMENT короткое описание объекта, отображаемое в окне диалога Insert New Object в редактора фрейма.
IDST_HTTP адрес Вашего веб-сайта.

Также Вы должны поменять информацию, содержащуюся в части "Version" ресурсов, просто отредактируйте эти поля и введите корректную информацию. Замечание: Вы должны увеличить номер версии каждый раз, когда выпускаете новый релиз своего расширения.

Выбор флагов объекта (object flags). Когда определяется объект, важно решить, должен ли Ваш объект двигаться, быть анимированным, открывать окно поверх фрейма, открывать элемент управления, или просто быть невидимым. Сейчас нужно принять все эти решения.

Как только Вы определили свойства своего объекта, прочитайте содержимое раздела "Определение флагов объекта", введите правильные описания OEFLAGS и OEPREFS в своем файле Main.h.

Создание диалога настройки и/или свойства. Как управлять расширением - через настройку (setup) или бразузер свойств (property explorer)? Следует определить, в какой информации нуждается объект - путь до файла, числовые значения, флаги, и т. д. Вся эта информация сохраняется в структуре EDITDATA. Сначала просмотрите главу "Важные структуры", и затем определите свою структуру EDITDATA в файле Main.h.

Если Вы решили сохранить файловый путь в структуре EDITDATA, то используйте переменную _MAX_PATH как максимальную длину для полей пути (pathname fields). Вы также должны реализовать подпрограмму UpdateFileNames, чтобы она поддерживала корректные файловые пути, когда приложение собирается или перемещается в другой каталог.

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

Как только структура EDITDATA создана, у Вас есть выбор, что делать дальше. Вы можете либо определить окно диалога настройки (setup dialog box, наподобие расширений до версии MMF1.5) или использовать систему свойств (property system) CF. Разработчики рекомендуют последнее, однако для некоторых объектов подойдет только диалог настройки. Замечание: Вы также можете использовать оба способа, как в объекте CF Picture: у него есть свойства, но двойной клик на объект (или выбор пункта Edit в контекстном меню) отобразит окно диалога (выбор файла).

Для реализации окна диалога, см. функцию EditObject в главе "Список необходимых функций / время редактирования".

Для реализации свойств читайте главу "Реализация свойств".

Создание подпрограмм, работающих во время выполнения приложения (run-time routines). Если Вы скомпилировали шаблон, поменяли имя и иконки, выбрали флаги объекта, создали диалог настройки или свойства, то Вы теперь готовы бросить Ваш объект во фрейм и вызвать диалог настройки (или свойства в редакторе свойств CF). Следующий шаг состоит в том, чтобы объект работал во время выполнения приложения (run-time). Для разработки объекта расширения абсолютно критично наличие двух подпрограмм:

CreateRunObject
DestroyRunObject

Подпрограмма CreateRunObject вызывается, когда создается объект. В этой подпрограмме Вы должны инициализировать структуру RUNDATA из информации, находящейся в структуре EDITDATA. Обычно структура RUNDATA отражает структуру EDITDATA, и имеет больше полей, чтобы хранить данные времени выполнения (run-time data). CreateRunObject только мостик между EDITDATA и RUNDATA, и поэтому Вам здесь нужно сделать копию всей необходимой информации. DestroyRunObject вызывается, когда объект уничтожается. Эта подпрограмма должна освобождать любую выделенную память, открытые окна, или ресурсы, которые были загружены за время жизни объекта.

Другая важная подпрограмма это HandleRunObject. Она вызывается в каждой прокрутке цикла приложения. Пожалуйста сделайте время работы этой подпрограммы как можно меньше: медленная подпрограмма будет замедлять работу всего приложения. Например, объект поиска просматривает только 100 строк текста на каждой прокрутке цикла приложения. Если подпрограмма HandleRunObject быстрая, то пользователь все еще может использовать анимации во время работы объекта, в то время как объект осуществляет поиск запрошенного текста.

Если Ваш объект невидимый, то перейдите к главе "Определение условий, действий и выражений". Если видимый, то Вам нужно заполнить подпрограмму DisplayRunObject своим кодом. Запомните 2 вещи:

• Только в подпрограмме DisplayRunObject можно записывать данные в экран, чтобы работал процесс сохранения и восстановления фона.
• Вы не должны использовать GetDC для получения контекста устройства (device context) в окне фрейма. Вместо этого используйте функции из класса cSurface.

Определение условий, действий и выражений. Условия (conditions), действия (actions) и выражения (expressions) являются основными частями Вашего объекта расширения. Следующий пример покажет, как создать новое условие. Используйте ту же самую технику для создания для создания действий и выражений для своего объекта.

1. Подумайте, какие условия должны быть для объекта, подберите хорошие имена для них в всплывающем меню и отображении в редакторе событий (Event Editor). Нужны ли для них параметры, какие они должны быть, и т. п.? Здесь мы создадим условие, которое будет запрашивать числовой параметр.

2. Определите в файле MAIN.H код для условия, и найдите значение CND_LAST. Замените имя (name) на имя в коде для своего расширения, например CND_MINE, и определите CND_LAST для своего последнего условия плюс 1 (CND_LAST должно быть всегда последним определенным, плюс единица).

3. Отредактируйте файл EXT.RC в редакторе ресурсов Visual Studio. Определите новую запись в MN_CONDITIONS для своего условия. Убедитесь, что Вы дали новой опции меню понятный идентификатор, например IDMN_CND_MINE, так как он понадобится Вам позже.

4. В файле EXT.RC определите новую строку для отображения своего условия в редакторе событий. Например: "%o: какое хорошее условие %0". Убедитесь, что идентификатор этой строки также понятен, например IDS_CND_MINE, Вам он позже понадобится.

5. Закройте редактор ресурсов, и загрузите файл "Resource.h" в редакторе кода Visual Studio. Найдите идентификатор опции меню, и поменяйте его значение так, чтобы оно лежало в диапазоне 26000 .. 26999. Это важно сделать, если хотите, чтобы система CF распознавала опцию меню, когда она выбрана. Просто имейте в виду: для действий (actions) идентификатор должен лежать в диапазоне 25000 .. 25999, и для выражений (expressions) между 27000 .. 27999.

6. Откройте файл MAIN.CPP, и найдите массив conditionsInfos. Введите новые определения, которые будут содержать информацию для CF. Для нашего примера строка должна быть такой: [IDMN_CND_MINE, IDS_CND_MINE, CND_MINE, EVFLAGS_ALWAYS, 1, PARAM_EXPRESSION, IDS_PARAM]. Порядок следующий: идентификатор опции меню, идентификатор отображаемой строки, код условия, флаги условия, количество параметров, идентификатор параметра и строка для отображения, когда вводится параметр.

7. В файле EXT.CPP определите новую подпрограмму для обработки Вашего нового условия. Например, short WINAPI DLLExport ConditionOfMine(LPRDATA rdPtr, long param1, long param2). Подробнее см. главу "List of run-time routines" для получения информации о подпрограммах обработки условий (condition routines).

8. Теперь найдите массив ConditionJumps, и в точно определенном месте, как было задано кодом CND_MINE, вставьте имя для своей новой подпрограммы. В этом примере вставленное имя будет "ConditionOfMine".

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

1. С среде Visual C++ откройте настройки проекта.

2. Убедитесь, что выбрана конфигурация отладки (Debug Unicode), и выбрана секция отладки (Debugging) настроек.

3. Введите путь до копии установки CF в поле ввода Command.

4. Введите директорию копии установки CF в поле ввода Working directory.

5. В разделе настроек линкера (Linker) введите в качестве пути до выходного файла (Output File) путь до файла расширения в каталоге Extensions\Unicode Вашей копии каталога установки CF. Измените права на запись в этот файл, если на Вашем компьютере разрешена работа системы UAC, чтобы Вы могли осуществить сборку этого файла.

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

Отладка расширения во время выполнения приложения, где используется расширение, несколько сложнее, так как CF запускает процесс runtime как внешнюю программу, но это важный шаг для отладки условий и действий. Выполните следующие шаги:

1. Откройте папку Extensions \ Help \ Misc Documentation в SDK, и сделайте копию файла CopyRun.Bat в корневую папку CF, где находится файл MMF2U.EXE.

2. Если папка C:\Temp еще не существует, то создайте её в корне диска C:. В этой директории создайте папку Rt. Примечание: также можно отредактировать файл CopyRun.bat, и поменять имя директории, см. далее.

3. Загрузите файл проекта Вашего расширения в Visual C++, откройте настройки проекта (Project Settings), перейдите в раздел отладки (Debugging). В опции командной строки (Command line), введите путь до файла EdRt.exe, который находится в папке Data\Runtime\Unicode каталога установки системы CF.

4. В строке опции аргумента приложения (Program argument) введите "/fC:\Temp\Rt\App0.ccn".

Как отлаживать?

• Загрузите CF и создайте приложение с Вашим объектом, который хотите отладить.
• Удерживайте клавишу SHIFT, когда кликаете на иконку Run Frame или Run Application: это укажет системе CF запустить скрипт CopyRun.bat, чтобы временные файлы, сгенерированные для runtime, копировались в папку C:\Temp\Rt.
• Перейдите в Visual C++ и нажмите F5 для запуска отладки: это действие запустит исполняемый файл EdRt.exe с командной строкой, показывающей, где найти проигрываемые файлы. В этом случае будет проигран App0.ccn, находящийся во временной папке на Вашем диске C:, где этот файл мы просто сохранили. Конечно, Вы можете поместить точки останова в своем коде.

Через некоторое время эти операции станут для Вас привычными. Замечание: если Вы не хотите создавать папку C:\Temp\Rt folder, то можете поменять её на любую, какую пожелаете: для этого нужно поменять CopyRun.Bat и поменять опции командной строки (command line options) в настройках отладки проекта (Debugging settings).

Добавление файла помощи (Help) для Вашего расширения. Файл подсказки (help file) рекомендуется создавать, в формате Microsoft HTML Help (*.CHM). Шаблон для этого предоставлен в подкаталоге Help каталога Template шаблона. Просто поменяйте имя расширения в файле .HHP, отредактируйте файлы HTML и затем скомпилируйте файл CHM с помощью утилиты Microsoft HTML Help Workshop. Файл CHM будет собран в подкаталоге ToInstall\Files\Help каталога Template. Вы можете загрузить Microsoft HTML Help Workshop по ссылке [] (установщик htmlhelp.exe).

Пожалуйста установите свой файл CHM в папку Help системы CF, и убедитесь, что у файла уникальное имя.

Примечание: не меняйте позицию и имя индекса, он должен быть в файле htm\index.htm.

Как реализовать Help в Вашем расширении. Свойства объекта (Properties) имеют кнопку Help на закладке About. Если кликнуть на эту кнопку, то будет вызвана функция GetHelpFileName. Убедитесь, что эта функция определена в Вашем исходном коде, и разрешена в файле .DEF.

Если Ваш объект имеет диалог настройки (setup dialog box), то Вы должны вызвать функцию mvHelp. Подробнее см. описание функции mvHelp в разделе "Callback-функции".

Создание релиза Вашего объекта. Чтобы можно было распространять Ваше расширение, сначала скомпилируйте "редактируемую" (editable) версию релиза и версию релиза "только для запуска" (run only). Editable-версия должна быть скопирована в директорию Extensions\Unicode системы CF и run-only-версия должна быть скопирована в директорию Data\Runtime\Unicode системы CF.

Почему надо создавать 2 версии своего расширения? Есть 2 причины для этого: во-первых, подпрограммы и ресурсы, используемые в редакторе, не нужны для исполняемых приложений, где используется расширение. Во-вторых, когда CF создает автономное исполняемое приложение (stand-alone application), то расширение из каталога Data\Runtime\Unicode встраивается в файл EXE. Когда исполняемое приложение запускается, эти расширения распаковываются во временную папку, таким образом, из может получить любой. Путем предоставления "non editable" версии Вашего расширения можно защититься от того, что кто угодно может получить расширение из исполняемого приложения и будет использовать его в среде CF.

Конфигурации релиза (Release). Когда Вы просмотрите конфигурации в настройках проекта, то увидите там следующие готовые конфигурации:

Release Unicode: компилируется editable-версия Вашего расширения, для копирования / установки в папку Extensions\Unicode системы CF. EDITOR определяется в опциях препроцессора в разделе настроек ресурсов (Resource).

Run_Only Unicode: компилируется run-only-версия Вашего расширения, для копирования / установки в папку Data\Runtime\Unicode системы CF. RUN_ONLY определяется в опциях препроцессора C/C++ и в разделе настроек ресурсов.

Вы наверное заметили, что некоторые части исходного кода подключаются в секции условной компиляции между директивами #ifndef RUN_ONLY и #endif, чтобы выкинуть это из версии "run only". Также некоторые ресурсы имеют условие EDITOR, чтобы предотвратить их подключение в версию "run only".

Установка Вашего расширения. Для установки расширения самый простой путь это использование бесплатной версии Clickteam Install Creator (или любую другую версию инсталлятора). Шаблон для Install Creator предоставлен в подкаталоге ToInstall директории Template. Сделайте копию своей "run only" версии в папку ToInstall\Files\Data\Runtime\Unicode, "editable" версию в папку ToInstall\Files\Extensions\Unicode, файл подсказки (help file, если он есть) в папку ToInstall\Files\Help, любые примеры в специальный подкаталог папки ToInstall\Files\Examples, и затем запустите Install Creator, загрузите файл .iit, поменяйте заголовок продукта и выполните сборку своего инсталлятора.

Если Вы не используете предоставленный шаблон для Install Creator, и хотите автоматически детектировать директорию инсталляции CF, то эту информацию можно получить из следующего ключа реестра:

Key = HKEY_CLASSES_ROOT\MMF2.Document\DefaultIcon

Этот ключ используется вышеупомянутым шаблоном Install Creator. Он содержит путь последней версии Clickteam Fusion, работающей на машине. Замечание: Вы должны удалить ",1" в конце строки - это делается автоматически утилитой Install Creator.

Key = HKEY_LOCAL_MACHINE\Software\Clickteam\Fusion 2.5\Settings, Value = InstallPath

Этот ключ содержит путь установки стандартной версии CF (Standard version).

Key = HKEY_LOCAL_MACHINE\Software\Clickteam\Fusion Developer 2.5\Settings, Value = InstallPath

Этот ключ содержит путь установки версии разработчика CF (Developer version).

[Определение флагов объекта]

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

В подпрограмме GetRunObjectInfos расширение должно заполнить 2 поля флагов:

infoPtr->editFlags=OEFLAGS;
infoPtr->editPrefs=OEPREFS;

Первое поле флагов содержит текущие флаги для объекта (объект создается с использованием этих флагов, когда Вы бросаете его в игровое поле). Эти флаги могут быть изменены позже с использованием настройки расширения (extension setup).

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

OEFLAGS и OEPREFS определены в конце файла MAIN.H.

OEFLAGS очень важны для Вашего объекта расширения. CF использует эти флаги, чтобы определить, как обрабатывать объект - как спрайт или фон (backdrop object), должны ли быть добавлены свойства движения и т. д.

Главные свойства OEFLAGS

OEFLAG_MOVEMENTS. Флаг OEFLAG_MOVEMENTS добавляет объекту свойство передвижения (movement property). Если Вы используете этот флаг, то должны подключить структуры rCom и rMov к структуре RUNDATA объекта.

OEFLAG_ANIMATIONS. Флаг OEFLAG_ANIMATIONS добавляет объекту свойство анимации (animation property), основанное на спрайтах (sprite-based). Если используете этот флаг, то должны подключить структуры rCom и rAni к структуре RUNDATA объекта.

Если у объекта есть оба флага OEFLAG_ANIMATIONS и OEFLAG_SPRITE, то отображение будет автоматически выполняться системой CF. Таким образом, Вам не нужно определять подпрограмму рисования в своем расширении.

OEFLAG_VALUES. Флаг OEFLAG_VALUES добавляет объекту свойство значения (value property), которое содержит 26 изменяемых значений и 10 изменяемых строк. Если используете этот флаг, то должны добавить структуру rVal в структуру RUNDATA объекта.

Свойства отображения OEFLAGS

OEFLAG_BACKGROUND. OEFLAG_BACKGROUND говорит CF отображать этот объект посередине объектов фона (background objects).

Замечание: этот флаг должен использоваться с осторожностью. Используйте его для объектов, которые не обновляются очень часто, и которые не перемещаются. Всякий раз, когда объект перемещается, перерисовывается весь экран. Объект Picture является примером объекта, который использует подпрограмму OEFLAG_BACKGROUND. Этот флаг не должен использоваться вместе с OEFLAG_SPRITES и OEFLAG_QUICKDISPLAY, и т. п.

OEFLAG_SPRITES. OEFLAG_SPRITES говорит CF обрабатывать этот объект расширения как спрайт. Если также установлен флаг OEFLAG_ANIMATIONS, то CF будет использовать реальные спрайты для отображения этого объекта, и Вам не нужно для этого писать ни одной строки кода. Будет иметь эффект только OEFLAG_BACKSAVE.

OEFLAG_BACKSAVE. CF будет сохранять фон объекта, если этот флаг установлен, и не будет сохранять этот фон, если флаг не установлен. Сохраняемая зона вычисляется по координатам объекта и полям hoImgXHot, hoImgYHot, hoImgWidth, hoImgHeight, возвращаемым подпрограммой HandleRunObject.

OEFLAG_INTERNALBACKSAVE. Если установлен флаг OEFLAG_INTERNALBACKSAVE, и также установлен флаг OEFLAG_BACKSAVE, то CF будет вызывать подпрограммы сохранения фона расширения (extension background saving routines, которые должны быть определены). SaveBackground должна сохранять background в буфер, и RestoreBackground должна восстанавливать сохраненный буфер по тем же самым координатам. KillBackground должна очищать буфер из памяти.

Если Вы используете функции SaveRect / RestoreRect / KillRect из mmfs2.dll, то можете использовать поле hoBackSave структуры headerObject. Или Вы можете использовать поверхность (surface) для сохранения фона.

OEFLAG_QUICKDISPLAY. Отображение объекта как реальных спрайтов может значительно замедлить Ваше приложение. Если Ваш объект отображается медленно (например, Вы создали большой текстовый объект с использованием многих текстовых функций Windows), то каждый раз, когда другой спрайт отображается спереди этого объекта, то объект должен быть очищен и перерисован. Это приведет к тому, что Ваше приложение будет работать очень медленно.

Чтобы не перерисовывать объект все время, используйте флаг QUICKDISPLAY. Этот флаг можно использовать только совместно с флагом OEFLAG_SPRITE. Такой спрайт удаляется из главного списка спрайтов, но рисуется только поверх фона. Когда спрайт поступает на верх, он не перерисовывается. Таким образом, Вы можете отобразить большой текст при движущихся спрайтах на полной скорости.

Объекты QUICKDISPLAY могут или не могут сохранить фон в зависимости от флагов OEFLAG_BACKSAVE и OEFLAG_INTERNALBACKSAVE. Одна из вещей, которые эти флаги не делают правильно, это обработка сохранения фона, при пересечении друг друга. Некоторые части второго объекта могут или не могут быть сохранены как часть фоновой сохраняемой области, и наоборот. В результате не помещайте объекты quickdisplay один поверх другого.

Когда OEFLAG_SPRITE или OEFLAG_BACKGROUND не используются. Если не используете флаг OEFLAG_SPRITE или OEFLAG_BACKGROUND, то Вы не можете получить прямой доступ к экрану CF. Это не означает, что Вы не можете ничего отображать, Ваша подпрограмма DisplayRunObject может использоваться для открытия (open), перемещения (move) или изменения размера окна поверх экрана CF. Разработчики реально используют эту технику для объектов AVI, QuickTime, Button, List, Edit, Combo, и всех объектов с окном. 

Другие флаги 

OEFLAG_TEXT. Флаг OEFLAG_TEXT добавляет меню "Text" в действия (actions), условия (conditions) и выражения (expressions). Этот пункт меню содержит условия наподобие "IsBold", "IsItalic" и т. д., действия наподобие "Set font name" (установить имя шрифта), "Set italic" (установить наклонный вид шрифта), "Set bold" (установить толстый вид шрифта) и т. д. Если хотите реализовать этот флаг (если Ваш объект текстовый), то нужно определить 4 функции в исходном коде Вашего расширения: "GetRunObjectFont", "SetRunObjectFont", "GetRunObjectTextColor" и "SetRunObjectTextColor". Эти функции вызываются основным кодом работающего приложения (main runtime) для изменения/запроса данных шрифта и цвета шрифта, используемого в Вашем объекте. Этот флаг сейчас используется в объектах Button, Clock, Edit, List, ListView и других.

OEFLAG_DISPLAYINFRONT. Если Вы установили флаг OEFLAG_DISPLAYINFRONT, то объект будет отображен спереди всех других объектов в редакторе. Вы должны использовать этот флаг, когда Ваш объект отображается как окно, так чтобы редактор учитывал отображение, которое Вы получаете в приложении.

OEFLAG_WINDOWPROC. Если Ваш объект нуждается в перехвате оконных сообщений CF, то Вы должны установить флаг OEFLAG_WINDOWPROC. CF будет вызывать подпрограмму "WindowProc" в объекте расширения. См. раздел "How to control a window from an extension".

OEFLAG_TABSTOP. Элементы управления интерфейса пользователя (Controls), такие как объект Edit или объект List, нуждаются в активизации клавишей TAB. Используйте флаг OEFLAG_TABSTOP, если хотите, чтобы Ваш объект перехватывал команды табуляции.

OEFLAG_SCROLLINGINDEPENDANT. Если установлен флаг OEFLAG_SCROLLINGINDEPENDANT, то объект не будет следовать игровому полю, когда оно прокручивается. Таким образом, объект останется статическим на экране, наподобие объекта Score (очки игрока) или объекта Lives (жизни игрока).

OEFLAG_NEVERKILL. Установите флаг OEFLAG_NEVERKILL, чтобы предотвратить удаление объекта, если он уйдет далеко от границ игрового поля.

OEFLAG_RUNBEFOREFADEIN. Этот флаг позволяет объекту расширения быть созданным перед переходом постепенного появления изображения (fade-in transition). Этот флаг устанавливается пользователем через свойство Create Before Fade-In Transition.

OEFLAG_MANUALSLEEP. Обычно объект деактивируется, если он уходит слишком далеко из окна отображения - он все еще существует, но удаляется из окна отображения. Принятие решения о том, должен ли быть деактивирован этот объект, обычно осуществляет CF. Если объект не управляется игроком, и не является частью детектирования столкновения (collision detection), то он может быть деактивирован. Если нет, то он будет оставаться активным, даже если уйдет за границы области отображения. OEFLAG_MANUALSLEEP меняет этот процесс на ручной режим. В этом случае флаг OEFLAG_NEVERSLEEP используется как триггер, и если этот флаг установлен, то объект никогда не деактивируется.

Изменения флагов. Какой флаг можно изменить в диалоге настройки (setup dialog)?

Окно диалога настройки (или подпрограммы свойства SetPropValue/SetPropCheck) Вашего объекта расширения можно использовать для изменения любого из флагов, кроме следующих:

OEFLAG_MOVEMENTS
OEFLAG_ANIMATIONS
OEFLAG_VALUES
OEFLAG_BACKGROUND
OEFLAG_SPRITE
OEFLAG_TEXT

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

В соответствии с флагами, которые используете, Вы должны задать определенные структуры в своей структуре RUNDATA. Для дополнительной информации см. следующую главу "".

Важные структуры. Эти структуры Вы должны использовать в своей структуре RUNDATA. Базовая структура RUNDATA должна содержать структуру headerObject в качестве первого элемента:

typedef struc tagRDATA
{
   headerObject rHo;
} RUNDATA;

Если установлены флаги OEFLAG_MOVEMENTS, OEFLAG_ANIMATIONS или OEFLAG_SPRITES, то Вы должны добавить структуру rCom после структуры rHo:

typedef struc tagRDATA
{
   headerObject rHo;
   rCom rc;
} RUNDATA;

Если также установлен флаг OEFLAG_MOVEMENTS, то Вы должны добавить структуру rMvt сразу после структуры rCom.

typedef struc tagRDATA
{
   headerObject rHo;
   rCom rc;
   rMvt rm;
} RUNDATA;

Вот различные случаи:

//OEFLAG_MOVEMENTS
headerObject rHo;
rCom rc;
rMvt rm;
 
//OEFLAG_ANIMATIONS
headerObject rHo;
rCom rc;
rAni ra;
 
//OEFLAG_SPRITE
headerObject rHo;
rCom rc;
rSpr rs;
 
//OEFLAG_VALUE
headerObject rHo;
rVal rv;
 
//OEFLAG_MOVEMENTS | OEFLAG_ANIMATIONS
headerObject rHo;
rCom rc;
rMvt rm;
rAni ra;
 
//OEFLAG_MOVEMENTS | OEFLAG_ANIMATIONS|OEFLAG_SPRITE
headerObject rHo;
rCom rc;
rMvt rm;
rAni ra;
rSpr rs;

Если вышеуказанные комбинации включают OEFLAG_VALUE, то Вы должны поместить rVal как последний элемент.

Помните:

• Должен быть представлен элемент rCom, если используются флаги OEFLAG_MOVEMENTS, OEFLAG_ANIMATIONS или OEFLAG_SPRITES.
• Должен быть представлен элемент rMvt, если используется OEFLAG_MOVEMENTS. Элемент rMvt должен идти непосредственно после rCom.
• Элементы rAni, rSpr и rVal всегда используются в этом порядке.

Флаги OEPREFS управляют некоторыми свойствами, отображаемыми CF в окне редактора свойств (Property window). Каждая опция управляется соответствующей опцией OEFLAG_ в поле hoOEFlag.

OEPREFS_BACKSAVE. OEPREFS_BACKSAVE разрешит опцию "Save background", сохранить задний фон (OEFLAG_BACKSAVE).

OEPREFS_BACKEFFECTS. OEPREFS_BACKEFFECTS разрешит опцию эффектов очистки фона, если установлен флагOEPREFS_BACKSAVE.

OEPREFS_QUICKDISPLAY. OEPREFS_QUICKDISPLAY разрешит опцию быстрого отображения (OEFLAG_QUICKDISPLAY).

OEPREFS_SCROLLINGINDEPENDANT. OEPREFS_SCROLLINGINDEPENDANT разрешит опцию следования за игровым полем (Follow the playfield, OEFLAG_SCROLLINGINDEPENDANT).

OEPREFS_LOADONCALL. OEPREFS_LOADONCALL разрешит опцию "load on call" (загрузка при вызове). Имейте в виду, что эта опция не имеет эффекта на объектах расширений.

OEPREFS_GLOBAL. OEPREFS_GLOBAL разрешит опцию "global object" (глобальный объект). Имейте в виду, что эта опция не имеет эффекта на объектах расширений.

OEPREFS_SLEEP. OEPREFS_SLEEP разрешит опции ручного входа в сон (manual sleep, OEFLAG_MANUALSLEEP).

OEPREFS_KILL. OEPREFS_KILL разрешит уничтожение (Destroy), если объект далеко от игрового поля (OEFLAG_NEVERKILL).

OEPREFS_INKEFFECTS. OEPREFS_INKEFFECTS разрешит свойства эффекта чернил (Ink Effect).

OEPREFS_TRANSITIONS. OEPREFS_TRANSITIONS разрешит свойства переходов (Transitions). Вы должны реализовать в своем объекте функцию GetRunObjectSurface.

OEPREFS_FINECOLLISIONS. OEPREFS_FINECOLLISIONS разрешит опцию точного детектирования столкновений (Collisions / Use Fine Detection). Вы должны реализовать в своем объекте функцию GetRunObjectCollisionMask.

Без вывода на экран: 

Ini object 

• OEFLAG_DISPLAYINFRONT
• 0

MCI object

• OEFLAG_DISPLAYINFRONT
• 0

Window control object

• OEFLAG_DISPLAYINFRONT
• 0

CD Audio object

• OEFLAG_DISPLAYINFRONT
• 0

Print object

• OEFLAG_DISPLAYINFRONT
• 0

Mixer object

• OEFLAG_DISPLAYINFRONT
• 0

ODBC object

• OEFLAG_DISPLAYINFRONT
• 0

SEARCH object

• OEFLAG_DISPLAYINFRONT
• 0

Открытие элемента управления. Объекты, открывающие элемент управления интерфейса пользователя (control) в окне фрейма:

Button object

• OEFLAG_WINDOWPROC | OEFLAG_DISPLAYINFRONT | OEFLAG_TABSTOP | OEFLAG_MOVEMENT | OEFLAG_TEXT
• OEPREFS_GLOBAL | OEPREFS_SCROLLINGINDEPENDANT 

List object

• OEFLAG_WINDOWPROC | OEFLAG_DISPLAYINFRONT | OEFLAG_TABSTOP | OEFLAG_MOVEMENT | OEFLAG_TEXT
• OEPREFS_GLOBAL | OEPREFS_SCROLLINGINDEPENDANT

Combo object

• OEFLAG_WINDOWPROC | OEFLAG_DISPLAYINFRONT | OEFLAG_TABSTOP | OEFLAG_MOVEMENT | OEFLAG_TEXT
• OEPREFS_GLOBAL | OEPREFS_SCROLLINGINDEPENDANT

Открытие окна. Объекты, открывающие окно в окне фрейма:

AVI object

• OEFLAG_MOVEMENTS | OEFLAG_WINDOWPROC | OEFLAG_DISPLAYINFRONT
• OEPREFS_GLOBAL

QT3 object

• OEFLAG_MOVEMENTS | OEFLAG_DISPLAYINFRONT | OEFLAG_WINDOWPROC
• OEPREFS_GLOBAL

Рисование на экране. Объекты, непосредственно рисующие в окне фрейма:

Anim object

• OEFLAG_SPRITES | OEFLAG_BACKSAVE | OEFLAG_MOVEMENTS | OEFLAG_VALUES
• OEPREFS_BACKSAVE | OEPREFS_BACKEFFECTS | OEPREFS_KILL | OEPREFS_SCROLLINGINDEPENDANT | OEPREFS_INKEFFECTS | OEPREFS_TRANSITIONS | OEPREFS_FINECOLLISIONS

Hiscore object

• OEFLAG_SPRITES | OEFLAG_QUICKDISPLAY | OEFLAG_BACKSAVE | OEFLAG_TEXT
• OEPREFS_GLOBAL | OEPREFS_BACKSAVE | OEPREFS_BACKEFFECTS

Date & Time object

• OEFLAG_SPRITES | OEFLAG_MOVEMENTS | OEFLAG_QUICKDISPLAY | OEFLAG_BACKSAVE | OEFLAG_SCROLLINGINDEPENDANT | OEFLAG_TEXT
• OEPREFS_GLOBAL | OEPREFS_QUICKDISPLAY | OEPREFS_BACKSAVE | OEPREFS_BACKEFFECTS | OEPREFS_SCROLLINGINDEPENDANT

[Действия, условия и выражения]

Действия (actions), условия (conditions) и расширения (expressions) ключевые составляющие для качественной работы Вашего объекта расширения. Действия, условия и выражения дают пользователю полное управление над объектом, который Вы создали. Тщательно выбирайте условия и действия, и тогда Вы умножите возможности использования Вашего объекта.

long WINAPI DLLExport Condition(LPRDATA rdPtr, long param1, long param2)

Каждое условие, которое Вы определяете, должно иметь свою подпрограмму. Эта подпрограмма вызывается системой CF, когда условие требует анализа.

Параметры:

LPRDATA rdPtr указатель на run-time структуру объекта
long param1 первый параметр, если он необходим
long param2 второй параметр, если он необходим

Возвращаемое значение:

FALSE, если условие не удовлетворяется, TRUE если удовлетворяется. Если один из параметров PARAM_COMPARAISON или PARAM_CMPSTRING, то возвращаемое значение должно быть результатом сравнения.

Замечания:

- Эта подпрограмма должна быть как можно более быстрой, потому что она может вызываться довольно часто при работе приложения.
- Параметры param1 и param2 определяются только если Вы укажете параметр в массиве conditionsInfos, в начале модуля MAIN.CPP.
- Когда используется больше 2 параметров, Вы должны использовать макросы CNC_GetIntParameter, CNC_GetStringParameter или CNC_GetFloatParameter для захвата параметров. Для дополнительной информации см. секцию "CallBack Functions / Runtime / Others".
- Каждая подпрограмма должна иметь указатель на саму себя в массиве ConditionJumps.

//Пример: условие без параметров
long WINAPI DLLExport Condition(LPRDATA rdPtr, long param1, long param2)
{
   return TRUE;         // это условие всегда верное
}
 
//Пример: условие с одним параметром
long WINAPI DLLExport Condition(LPRDATA rdPtr, long param1, long param2)
{
   return (param != 0); // это условие верно, если параметр не равен 0
}
 
//Пример: условие с двумя параметрами
long WINAPI DLLExport Condition(LPRDATA rdPtr, long param1, long param2)
{
   return (param1==param2);   // это условие верно, если параметры идентичны
}
 
//Пример: условие с тремя параметрами
long WINAPI DLLExport Condition(LPRDATA rdPtr, long param1, long param2)
{
   int p1 = CNC_GetIntParameter(rdPtr);
   int p2 = CNC_GetIntParameter(rdPtr);
   int p3 = CNC_GetIntParameter(rdPtr);
 
   return (p1==p2 && p1==p3); // это условие верно, если параметры идентичны
}

short WINAPI DLLExport Action(LPRDATA rdPtr, long param1, long param2)

Каждое действие, которое Вы определяете, должно иметь свою подпрограмму. Эта подпрограмма вызывается системой CF, когда действие запускается приложением.

Параметры

LPRDATA rdPtr указатель на run-time структуру объекта
long param1 первый параметр, если он необходим
long param2 второй параметр, если он необходим

Возвращаемое значение: должен быть возвращен 0.

Замечания:

- Делайте Вашу подпрограмму обработки действия максимально быстрой, насколько это возможно, потому что она может вызываться довольно часто при работе приложения.
- Параметры param1 и param2 определяются только если Вы укажете параметр в массиве actionsInfos, в начале модуля MAIN.CPP.
- Когда используется больше 2 параметров, Вы должны использовать макросы CNC_GetIntParameter, CNC_GetStringParameter или CNC_GetFloatParameter для захвата параметров. Для дополнительной информации см. секцию "CallBack Functions / Runtime / Others".
- Каждая подпрограмма должна иметь указатель на саму себя в массиве ActionJumps.
- Подпрограмме действия не разрешено рисовать что-либо на экране. Это должно осуществляться путем вызова процесса перерисовки объекта, что делается при переходе приложения к следующему экрану и обновлению с вызовом подпрограммы DisplayRunObject. Эту работу делает для Вас вызов callRuntimeFunction(rdPtr, RFUNCTION_REDRAW, 0, 0).

//Пример: действие без параметров
short WINAPI DLLExport Action(LPRDATA rdPtr, long param1, long param2)
{
   Beep();
   return 0;
}
 
//Пример: действие с одним параметром
short WINAPI DLLExport Action(LPRDATA rdPtr, long param1, long param2)
{
   if ( param1 == 0 )
      Beep(0);
   else
      Beep(1);
   return 0;
}
 
//Пример: действие с двумя параметрами
short WINAPI DLLExport Action(LPRDATA rdPtr, long param1, long param2)
{
   Beep(param1, param2);
   return 0;
}
 
//Пример: действие с тремя параметрами
short WINAPI DLLExport Action(LPRDATA rdPtr, long param1, long param2)
{
   int p1 = CNC_GetIntParameter(rdPtr);
   int p2 = CNC_GetIntParameter(rdPtr);
   int p3 = CNC_GetIntParameter(rdPtr);
   Beep(p1, p2, p3);
   return 0;
}

long WINAPI DLLExport Expression(LPRDATA rdPtr,long param1)

Каждое выражение (expression), которое Вы задаете для своего расширения, должно иметь собственную подпрограмму обработки выражения. Эта подпрограмма вызвается системой CF, когда это выражение нуждается в вычислении.

Параметры:

LPRDATA rdPtr указатель на run-time структуру объекта
long param1 первый параметр, если он необходим

Возвращаемое значение:

Если выражение числовое, то подпрограмма выражения должна возвратить вычисленное значение типа long. Если выражение строковое, то подпрограмма должна вернуть указатель на вычисленную строку.

Важный момент: если выражение возвращает строку, то оно должно также установить флаг HOF_STRING в поле rdPtr->rHo.hoFlags, вот так:

rdPtr->rHo.hoFlags |= HOF_STRING;

Вы должны сделать это перед возвратом из функции выражения (это должно быть после захвата параметров). Установите флаг HOF_FLOAT, если выражение возвращает float (предупреждение: не делайте приведение типа float к long, см. пример ниже).

Замечания:

- Делайте Вашу подпрограмму обработки выражения максимально быстрой, насколько это возможно, потому что она может вызываться довольно часто при работе приложения.
- Для получения параметров используют макросы CNC_GetFirstExpressionParameter и CNC_GetNextExpressionParameter. Для дополнительной информации см. секцию "CallBack Functions / Runtime / Others".

//Пример: выражение с 1 параметром
long WINAPI DLLExport Expression(LPRDATA rdPtr,long param1)
{
   long p1 = CNC_GetFirstExpressionParameter(rdPtr, param1, TYPE_INT);
 
   // В этом месте выполняется вычисление выражения:
   return p1 * 2;
}
 
//Пример: выражение с 2 параметрами
long WINAPI DLLExport Expression(LPRDATA rdPtr,long param1)
{
   long p1 = CNC_GetFirstExpressionParameter(rdPtr, param1, TYPE_INT);
   long p2 = CNC_GetNextExpressionParameter(rdPtr, param1, TYPE_INT);
 
   // В этом месте выполняется вычисление выражения:
   return p1+p2;
}
 
//Пример: выражение с 3 параметрами
long WINAPI DLLExport Expression(LPRDATA rdPtr,long param1)
{
   long p1 = CNC_GetFirstExpressionParameter(rdPtr, param1, TYPE_INT);
   long p2 = CNC_GetNextExpressionParameter(rdPtr, param1, TYPE_INT);
   long p3 = CNC_GetNextExpressionParameter(rdPtr, param1, TYPE_INT);
 
   // В этом месте выполняется вычисление выражения:
   return p1+p2+p3;
}
 
//Пример: выражение, которое возвращает строку
long WINAPI DLLExport Expression(LPRDATA rdPtr,long param1)
{
   long p1 = CNC_GetFirstExpressionParameter(rdPtr, param1, TYPE_INT);
   // тут может быть получение других параметров
 
   // Установка флага строки:
   rdPtr->rHo.hoFlags |= HOF_STRING;
   // Возврат строки:
   return (long)_T("Hello world");
}
 
//Пример: выражение, которое возвращает float
long WINAPI DLLExport Expression(LPRDATA rdPtr,long param1)
{
   long p1 = CNC_GetFirstExpressionParameter(rdPtr, param1, TYPE_INT);
   // тут может быть получение других параметров
 
   // Установка флага "Float":
   rdPtr->rHo.hoFlags |= HOF_FLOAT;
   // Возврат float:
   float fResult = 1.0f;
   // Не возвращайте fResult напрямую, так как это значение должно
   // быть преобразовано в long:
   return *((long*)&fResult);
}

Для каждого действия или условия, которые Вы назначаете своему объекту расширения, Вы должны определить следующее:

• Пункт меню для всплывающего меню действий условия.
• Отображаемая строка, которую CF может показать для действий или условий в списке редактора событий.

Довольно просто добавить пункт меню во всплывающее меню  действий условия. Используйте обычную опцию меню для нормального всплывающего меню Windows.

Важное замечание: определение строки это одна из вещей, однако Вы должны редактировать файл ресурса для изменения идентификатора для новой опции меню: CF сравнивает этот идентификатор выбранной опции с граничными значениями и детектирует, чем является последняя выбранная опция с этим методом - действием или условием. Обратите внимание: Вы также можете делать это напрямую в редакторе ресурсов Visual C, когда Вы вводите новый идентификатор, например вводите IDMN_YOURACTION = 25011 для присваивания значения идентификатора.

Идентификаторы меню действий должны лежать между 25000 и 25999.

Идентификаторы меню условий должны лежать между 26000 и 26999.

Если Вы не меняете идентификаторы, то Ваша опция меню не будет детектироваться системой CF.

Строка отображения. Это строка, которую CF может показать для действий или событий в списке редактора событий. Отображение несколько более сложное, потому что CF должна вставить параметры и иконку объекта, когда отображает строку в редакторе событий. Система CF была разработана, чтобы справиться со всеми комбинациями одной строки (строка условия/действия), имени объекта и параметров.

Эти строки кодируются с символами %, каждый из этих символов заменяется строкой, либо именем объекта, либо параметрами:

%o заменяется именем объекта
%0 заменяется первым параметром
%1 заменяется вторым параметром
%2 заменяется третьим параметром

и т. д.

Пример 1:

"Number of objects in %0=%1"

С первым параметром как параметром zone и вторым параметром сравнения, будет отображено:

"Number of objects in zone(0,0) to (10,10)=15"

Пример 2:

"%o is facing a direction %0"

Оригинальная строка в программе выдает:

Romeo is facing a direction south

Пример 3:

"Number of %o in %0=%1"

Number of Romeo in zone () to () = 10

Выражения, как и условия и действия, нуждаются в 2 строках, находящихся в файле ресурсов:

• Пункт меню во всплывающем меню выражения.
• Отображаемая строка, с которой CF может составить список в редакторе событий.

Добавить пункт меню во всплывающем меню выражений довольно просто. Используйте обычную опцию меню в нормальном всплывающем меню Windows, и в меню максимально кратко опишите, что возвращает Ваше выражение.

Важное замечание: определение строки это одна из вещей, однако Вы должны отредактировать файл resource.h для смены идентификатора в новой опции меню: CF сравнивает этот идентификатор выбранной опции с границей значений, и определяет этим методом, является ли последняя выбранная опция выражением. Обратите внимание: Вы также можете делать это напрямую в редакторе ресурсов Visual C, когда вводите новый идентификатор, например введите IDMN_YOUREXPRESSION = 27005 чтобы определить значение для этого идентификатора.

Идентификаторы меню выражения должны лежать между 27000 и 27999.

Если Вы не меняете идентификаторы, то Ваша опция меню не будет детектироваться системой CF.

Строка отображения. Для отображения строки, которой CF может показать выражение, имеется токен выражения. Он должен быть уникальным для этого объекта, потому что вычислитель выражения сравнивает строку, введенную пользователем, и строки, найденные в различных ресурсах объектов, пока не найдет корректную. Строка не должна содержать больше 20 символов, и должна завершаться открывающей скобкой.

Примеры из объекта Clock:

"seconds of("
"minutes of("
"hours of("
"day of week of("

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

Пример строки выражения из объекта system:

"str$("

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

Если Вы просматривали исходный код объектов примеров, то наверное заметили, что некоторые флаги в массивах conditionsInfos и actionsInfos в начале файла EVFLAGS_ALWAYS или EVFLAGS_NOTABLE. Эти флаги очень важны, потому что они определяют поведение каждого условия (condition).

Условия событий, управляемые true и false. CF может обработать 2 разных типа условий: событийные и не событийные условия.

Действительно событийные условия полагаются на реальное событие, такое как клик мышью, столкновение (collision), нажатие кнопки клавиатуры или выбранная опция меню. Возьмем например клик мышью: пользователь кликает где-нибудь во фрейме, CF получает сообщение клика, и затем просматривает список событий, чтобы увидеть, существует ли условие, работающее с кликом, как первое условие в группе событий. Если да, то условие оценивается, и если результат оценки TRUE, то вычисляются другие (событийные) условия этой группы событий. Условие, которое реально событийное, и первое условие в группе условий, вычисляется по-настоящему быстро, потому что вычисление происходит только кода событие реально происходит.

Не событийные условия обычно вовлекают сравнение, такое как Global Value 1 < 10, и оно не срабатывает по реальному событию. CF вычисляет эти условия на каждом цикле приложения. Условие также может быть не событийным, когда реально событийное условие находится в позиции 2 (или с большим номером по списку) группы списка условий. В этом случае CF вызывает подпрограмму оценки, когда это необходимо.

Что Вам следует делать? Если Ваше условие отвечает за клик мышью или другое очень специфичное событие, то Вы должны сделать его реально событийным. Если нет, то оно должно вычисляться на каждой итерации цикла приложения. Флаги, которые можно использовать, следующие:

EVFLAGS_ALWAYS: говорит CF, что условие должно вычисляться на каждой прокрутке цикла. Если Вы просмотрите исходный код, то увидите, что это случай для большинства условий в примерах объектов.

Нет флага EVFLAGS_ALWAYS: говорит CF, что Ваше условие будет вычисляться только если генерируется событие (для Вас это может быть реализовано функциями RFUNCTION_GENERATEEVENT, RFUNCTION_PUSHEVENT), если оно первое в группе событий, или когда необходимо, если оно не первое в группе событий.

В случае не событийных условий Вы также можете добавить флаг:

EVFLAGS_NOTABLE: показывает, что Ваше условие может быть инвертировано. В этом случае будет доступна опция Negate, когда Вы кликаете на условии правой кнопкой мыши.

Параметры действия и условия. Действия (actions) и условия (conditions) могут запрашивать у пользователя некоторое количество параметров. эти параметры могут быть числовыми выражениями, строками или указателями на другие объекты или зоны, и т. д.

Вы должны указать, что у одного действия или одного условия есть параметр в массивах actionsinfos и conditionsInfos Вашего расширения. Просто укажите код параметра для использования в поле param структуры.

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

Значение, вводимое в подпрограмму действия/условия: младший байт представляет тип клика (left 0, middle 1, right 2), и старший байт флагом PARAMCLICK_DOUBLE (0x0100) показывает двойной клик.

PARAM_COLOUR. Что запрашивается у пользователя: он должен выбрать цвет из палитры.

Значение, вводимое в подпрограмму действия/условия: значение long представляет значение RGB выбранного цвета.

PARAM_EXPRESSION. Что запрашивается у пользователя: он должен ввести числовое выражение.

Значение, вводимое в подпрограмму действия/условия: значение long представляет введенное значение.

PARAM_EXPSTRING. Что запрашивается у пользователя: он должен ввести строку или строковое выражение.

Значение, вводимое в подпрограмму действия/условия: указатель на строку, оканчивающуюся нулем.

PARAM_FILENAME.  Что запрашивается у пользователя: он выбирает файл, используя диалог селектора файла.

Примечание: этот параметр сейчас устарел. В новых действиях Вы должны вместо него использовать PARAM_FILENAME2.

Значение, вводимое в подпрограмму действия/условия: указатель на имя файла (pathname) со свойством перемещения (поддержка относительного пути), если приложение было перенесено в другой каталог и если файл был в директории приложения или в её подкаталоге.

Замечания: если Вы хотите, то можете определить фильтр для использования в селекторе файлов. Чтобы сделать это, определите параметр null после Вашего параметра PARAM_FILENAME, и введите строку фильтра в качестве заголовка этого параметра. Имейте в виду, что новый параметр PARAM_FILENAME2 позволяет Вам вставить и заголовок, и фильтр в одной строке, и Вы должны использовать этот новый параметр для новых действий.

Пример: определение действия "Загрузка текста":

{IDMN_ACTLOADTEXT, IDST_ACTLOADTEXT, ACT_LOADTEXT, 0, 2,
PARAM_FILENAME,0,M_LOADTEXTTITLE,M_LOADTEXTFILTER}

со строками:

M_LOADTEXTTITLE,"Please select a text file"
M_LOADTEXTFILTER,"Text files|*.txt|All files|*.*|"

Примечание: сепаратор, используемый в строке фильтра, должен быть символом |, вместо нуля.

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

Значение, вводимое в подпрограмму действия/условия: указатель на имя файла (pathname) со свойством произвольного перемещения приложения, если путь был указан относительно каталога приложения.

Замечания: если Вы хотите, то можете определить заголовок и фильтр для использования в селекторе файлов. Чтобы сделать это, Вам нужно определить Ваш заголовок как часть строки с префиксом #Title" перед заголовком, и фильтр как часть строки, перед которым находится тег #Filter#.

Пример: здесь задан селектор файла с заголовком "Выберите картинку" и фильтром "*.bmp":

{IDMN_ ACTLOADIMAGE, IDST_ACTLOADIMAGE, ACT_LOADIMAGE, 0, 1,
PARAM_FILENAME2, M_IMAGETITLE}

со строкой:

M_IMAGETITLE,"#Title#Выберите картинку...#Filter#Windows Bitmap Files|*.bmp|All files|*.*|"

Примечание: сепаратор, используемый в строке фильтра, должен быть символом |, вместо нуля.

PARAM_JOYDIRECTION. Что запрашивается у пользователя: направление и/или кнопка огня при использовании джойстика.

Значение, вводимое в подпрограмму действия/условия: значение long, отражающее состояние джойстика:

JOYSTICK_UP: север
JOYSTICK_DOWN: юг
JOYSTICK_LEFT: запад
JOYSTICK_RIGHT: восток
JOYSTICK_FIRE1: кнопка огня 1
JOYSTICK_FIRE2: кнопка огня 2
JOYSTICK_FIRE3: кнопка огня 3
JOYSTICK_FIRE4: кнопка огня 4

PARAM_KEY. Что запрашивается у пользователя: нажатая клавиша.

Значение, вводимое в подпрограмму действия/условия: значение long, содержащее виртуальный код клавиши.

PARAM_NEWDIRECTION. Что запрашивается у пользователя: направление из 32 возможных.

Значение, вводимое в подпрограмму действия/условия: значение long, которое состоит из 32 бит, отражающих выбранные в селекторе направления (селектор выглядит как набор стрелочек). Бит 0 означает запад, бит 8 север, бит 16 восток, и бит 24 юг.

PARAM_PLAYER. Что запрашивается у пользователя: выбор игрока от 0 до 3.

Значение, вводимое в подпрограмму действия/условия: значение long от 0 до 3, где 0 представляет player 1, и 3 представляет player 4.

PARAM_POSITION. Что запрашивается у пользователя: позиция на фрейме, либо абсолютная, либо относительно другого объекта, с использованием селектора позиции.

Значение, вводимое в подпрограмму действия/условия: значение long, где старшее слово содержит позицию X на фрейме, и младшее слово содержит позицию Y на фрейме.

PARAM_SPEED. Что запрашивается у пользователя: значение скорости.

Значение, вводимое в подпрограмму действия/условия: значение long содержащее скорость, от 0 до 100.

PARAM_TIME. Что запрашивается у пользователя: ввод времени с использованием селектора секунд/минут.

Значение, вводимое в подпрограмму действия/условия: значение long, представляющее время в единицах 1/1000 секунды.

PARAM_ZONE. Что запрашивается у пользователя: определение зоны фрейма.

Значение, вводимое в подпрограмму действия/условия: указатель на короткую структуру прямоугольника, содержащую зону в координатах фрейма:

typedef
{
   short left;
   short top;
   short right;
   short bottom;
} SRECT;

CF содержит функции, позволяющие Вам определить свои собственные параметры действия. Это новые параметры находятся в зоне памяти, в которой Вы можете сохранить то, что хотите (строки, цвета, и т. д.). Вам нужно только определить 3 функции для создания своего нового параметра.

Что нужно для этого сделать: сначала в файле main.h определяется новое значение параметра: первый должен быть PARAM_EXTBASE, следующий PARAM_EXTBASE+1 и так далее. Пример:

#define PARAM_MYPARAM (PARAM_EXTBASE)

В файле EditTime.cpp Вы должны определить 3 функции: InitParameter, EditParameter и GetParameterString. Пожалуйста обратитесь к описанию функции в секции "List of necessary functions / Edit time".

В файле Main.cpp, в записи Вашего действия (таблица actionsInfos), используйте свой пользовательский параметр в качестве типа параметра (PARAM_MYPARAM в примере выше).

Когда будет вызвана Ваша подпрограмма действия, Вы получите в качестве параметра адрес начала Вашей новой структуры параметра: указатель на структуру paramExt. Интересующее Вас поле это paramData, где должны лежать Ваши пользовательские данные.

Параметры только для условия. Следующие параметры допустимы только для условий.

PARAM_COMPARAISON. Что запрашивается у пользователя: ввод сравнения, состоящего из выражения и оператора сравнения.

Значение, вводимое в подпрограмму условия: Ваше условие не должно делать сравнение само, CF обрабатывает сравнение автоматически. Эта подпрограмма должна вернуть значение true или false, вместо этого должно быть возвращено значение long для сравнения со значением, введенным пользователем. Таким образом, у значения параметра, который Вы получаете, нет назначения.

PARAM_CMPSTRING. Что запрашивается у пользователя: ввод строки или строки расширения и процесса сравнения.

Значение, вводимое в подпрограмму условия: Ваше условие не должно делать сравнение само, CF обрабатывает сравнение автоматически. Эта подпрограмма не должна возвращать значение true или false, но вместо этого значение long для сравнения со значением, введенным пользователем. Эта функция должна вернуть указатель на строку. Таким образом, у значения параметра, который Вы получаете, нет назначения.

PARAM_CMPTIME. Что запрашивается у пользователя: ввод значения времени и выбор процесса сравнения.

Значение, вводимое в подпрограмму условия: Ваше условие не должно делать сравнение само, CF обрабатывает сравнение автоматически. Эта подпрограмма не должна возвращать значение true или false, но вместо этого значение long для сравнения со значением, введенным пользователем. Это значение должно быть временем, указанным в миллисекундах. Таким образом, у значения параметра, который Вы получаете, нет назначения.

Параметры выражения. CF предлагает только 2 типа параметров для расширений.

EXPPARAM_LONG. Что запрашивается у пользователя: числовое выражение в вычислителе выражения. Это выражение может состоять из простого числа или из сложного выражения со скобками и функциями.

Значение, вводимое в подпрограмму условия: значение long, содержащее результат выражения.

EXPPARAM_STRING. Что запрашивается у пользователя: строковое выражение в вычислителе выражения. Это выражение может состоять из простой строки или сложного выражения со скобками и функциями.

Значение, вводимое в подпрограмму условия: указатель на начало строки, заканчивающейся нулем.

[Реализация свойств]

CF поставляется с новой системой свойств, чтобы заменить диалог настройки (setup box) объекта. Вы определенно заходите подключить эту систему к своему объекту, потому что у ней много достоинств:

• Стандартизованный интерфейс во всем приложении.
• Простой доступ к каждому элементу Вашего объекта.
• Возможность модификации нескольких объектов сразу.
• Современный интерфейс.

Реализация поддержки новой системы свойств проста, просто выполните шаги, рассмотренные в этой главе.

Шаг 1. Определите значения для редактирования.

На этом первом шаге нужно подготовить список всех редактируемых значений в Вашей структуре EDITDATA. Для каждого из значений найдите, какой интерфейс нужен для его редактирования: будет ли это простая галочка (checkbox), строка для редактирования, значение типа float и т. д. Определитесь, должны ли быть некоторые значения сгруппированы, или вставлены одно в другое, и т. п.

Другими словами, спланируйте интерфейс редактора свойств объекта.

Шаг 2. Таблица PropData.

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

Сначала нужно определить идентификатор для каждого из свойств, которые Вы намереваетесь реализовать. Это делается через простое перечисление (enum). Поместите её в начало файла EDITTIME.CPP. Пример:

// Это перечисление определяет идентификаторы свойств:
enum
{
   PROPID_SETTINGS = PROPID_EXTITEM_CUSTOM_FIRST,
   PROPID_TEXTTITLE, 
   PROPID_TEXT, 
   PROPID_CHECK,
   PROPID_COMBO,
   PROPID_COLOR,
};

Обратите внимание, что Ваши идентификаторы свойств должны быть в диапазоне от PROPID_EXTITEM_CUSTOM_FIRST до PROPID_EXTITEM_CUSTOM_LAST. Другие значения используются CF для других свойств объекта.

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

Таблица PropData это список макросов, которые определяют для каждого свойства его тип и связанные отображаемые строки. Эти макросы определены в файле Props.h находящемся в каталоге Inc EDK.

Определено множество макросов, по одному для каждой возможной конфигурации свойства. Например, макрос PropData_CheckBox будет отображать простую галочку для двоичного выбора (разрешено или запрещено, check box). Макрос PropData_ComboBox_Check отобразит checkbox (галочка), связанный с combobox (выпадающий список для выбора), и т. д. Для дополнительной информации см. раздел "Макросы PropData" в этой главе.

Каждому макросу нужно указать параметры:

- Первый параметр это идентификатор свойства, который Вы определили на шаге 1.
- Второй параметр это идентификатор строки ресурсов для свойства текста, отображаемого в окне редактора свойств.
- Третий параметр это идентификатор строки ресурсов для быстрого описания, отображаемого в Information box окна редактора свойств.
- У некоторых макросов есть четвертый параметр: этот параметр передается в функцию инициализации свойства, например элемент из списка свойства ComboBox.
- Некоторые макросы могут также иметь другие параметры перед четвертым параметром, подробнее см. раздел "Макросы PropData".

Эта таблица должна заканчиваться макросом PropData_End.

Пример массива PropData:

// Определение дерева свойств.
PropData Properties[] =
{
   PropData_Group (PROPID_TEXTTITLE, IDS_PROP_TEXTTITLE, IDS_PROP_TEXTTITLE),
   PropData_EditString (PROPID_TEXT, IDS_PROP_TEXT, IDS_PROP_TEXT_INFO),
   PropData_CheckBox (PROPID_CHECK, IDS_PROP_CHECK, IDS_PROP_CHECK_INFO),
   PropData_ComboBox (PROPID_COMBO, IDS_PROP_COMBO, IDS_PROP_COMBO, ComboList),
   PropData_Color (PROPID_COLOR, IDS_PROP_COLOR, IDS_PROP_COLOR_INFO),
   PropData_End()
};

Первый элемент массива показывает группу заголовка, простой разделитель в списке. Его параметрами являются идентификатор свойства, затем идентификатор строки в ресурсах, где содержится текстовый заголовок свойства, и снова та же самая строка (для этого случая строка информации не должна нести дополнительной информации).

Второй элемент показывает редактируемую строку. Здесь также 2 параметра: заголовок и информационная строка.

Третий элемент показывает простой check box, со строкой заголовка и строкой информации.

Четвертый элемент представляет combo box, с заголовком и строкой информации, плюс дополнительный параметр, который указывает на таблицу, содержащую строки для этого combo box:

// Список вариантов выбора для combo box:
LPCSTR ComboList[] =
{
   0,    // зарезервировано
   MAKEINTRESOURCE(IDS_FIRSTOPTION), 
   MAKEINTRESOURCE(IDS_SECONDOPTION), 
   MAKEINTRESOURCE(IDS_THIRDOPTION), 
   NULL
};

Как Вы можете видеть, это список содержит простой макрос MAKEINTRESOURCE для каждого идентификатора строки. Эта таблица завершается NULL (и начинается с 0).

Пятый элемент определяет свойство цвета, здесь также есть текст заголовка и строка информации.

Таблица заканчивается макросом PropData_End().

Шаг 3. Функция GetProperties.

Функция GetProperties вызывается системой CF, когда ей нужно вставить свойства объекта в список отображаемых свойств. Это очень простая функция, она должна вызывать callback-функцию, передавая ей в параметрах адрес таблицы PropData.

BOOL WINAPI DLLExport GetProperties(LPMV mV, LPEDATA edPtr, BOOL bMasterItem)
{
   mvInsertProps(mV, edPtr, Properties, PROPID_TAB_GENERAL, TRUE);
   return TRUE;
}

В зависимости от Вашего объекта можно иметь разные таблицы свойств для вставки: просто поменяйте адрес соответствующим образом адрес таблицы PropData.

Также Вы можете добавить свойства к другой закладки свойств, кроме закладки General Options, или даже удалить стандартные параметры с помощью callback-функции mvRemoveProps.

Для дополнительной информации по функции GetProperties обратитесь к секции "List of necessary functions / Edit time".

Шаг 4. Возврат значений в CF.

Когда CF отображает каждое свойство, она должна знать отображаемое значение. Тогда она вызывает функцию объекта, которая возвращает значение для отображения, в соответствии с идентификатором свойства. Вот эта функция:

LPVOID WINAPI DLLExport GetPropValue(LPMV mV, LPEDATA edPtr, UINT nPropID)

Параметры, передаваемые в GetPropValue:

• Указатель на структуру mv, содержащую информацию о системе CF.
• Указатель на структуру EDITDATA объекта.
• Идентификатор свойства.

Сама функция должна быть очень простой. Она должны быть основана на простом операторе switch / case на базе идентификатора свойства, и для каждого случая эта функция должна выбирать из структуры EDITDATA нужное значение и возвращать его.

Возвращаемое значение это е сами данные, а класс C++, содержащий данные. Определено несколько классов для разных видов возвращаемых значений. Например:

CPropDWordValue: когда нужно вернуть значение типа DWORD, наподобие цвета, числа.
CPropFloatValue: когда нужно вернуть значение с плавающей точкой (float).
CPropDataValue: когда нужно вернуть двоичные данные.
CPropStringValue: когда нужно вернуть строку текста.

В функции нужно вернуть значение следующим образом: для каждого варианта создается объект нужного типа:

return new CPropDWordValue( myValue );

   или

return new CPropStringValue( myString );

   или

return new CPropFloatValue( myFloat );

   и так далее.

LPVOID WINAPI DLLExport GetPropValue(LPMV mV, LPEDATA edPtr, UINT nPropID)
{
   switch (nPropID)
   {
   case PROPID_COLOR:
      // Возврат цвета:
      return new CPropDWordValue(edPtr->dwColor);
   case PROPID_TEXT:
      // Возврат текста:
      return new CPropStringValue(edPtr->szText);
   case PROPID_COMBO:
      // Возврат значения из combo box:
      return new CPropDWordValue(edPtr->nComboIndex);
   }
   return NULL;
}

Объект CPropXXXValue является автоудаляемым, т. е. он будет удален системой CF, когда больше не нужен.

Обратите внимание, что функция GetPropValue вызывается только для свойств, которые имеют свое значение. Свойства простого чекбокса не имеют других значений, кроме как свое установленное значение (checked/unchecked state), которое возвращается функцией GetPropCheck, что будет рассмотрено на следующем шаге 5.

Для дополнительной информации по функции GetPropValue см. раздел "List of necessary functions / Edit time".

Шаг 5. Возврат состояния чекбокса.

Если в Вашем списке свойств есть чекбоксы, то нужно реализовать функцию GetPropCheck: она специально вызывается из CF, чтобы узнать состояние чекбоксов в списке свойств. Замечание: любое свойство может иметь чекбокс, не только простые свойства с чекбоксом. Вот прототип этой функции:

BOOL WINAPI DLLExport GetPropCheck(LPMV mV, LPEDATA edPtr, UINT nPropID)

Параметры функции GetPropCheck:

• Указатель на структуру mv, содержащую информацию о системе CF.
• Указатель на структуру EDITDATA объекта.
• Идентификатор свойства.

Возвращаемое значение: состояние галочки чекбокса (0 если галочка не стоит или 1 если галочка стоит), в зависимости от состояния свойства на момент вызова функции.

Эта подпрограмма, как и GetPropValue, должна быть основана на операторе switch / case на базе идентификатора свойства. Для каждого идентификатора Вы опрашиваете структуру EDITDATA, выбирая оттуда состояние нужного чекбокса, и возвращаете его простым оператором return.

Пример:

BOOL WINAPI DLLExport GetPropCheck(LPMV mV, LPEDATA edPtr, UINT nPropID)
{
   switch (nPropID)
   {
   case PROPID_CHECK:
      // Возврат 0 (unchecked) или 1 (checked):
      return edPtr->nCheck;
   }
   return 0;        // по умолчанию возвращается 0 (unchecked)
}

Для дополнительной информации по функции GetPropCheck см. раздел "List of necessary functions / Edit time".

Шаг 6. Изменение значения свойства.

Когда пользователь меняет значение свойства в списке, отображаемом на экране, система CF вызывает функцию объекта, чтобы изменить соответствующее значение свойства во внутренней структуре EDITDATA объекта. Вот эта функция:

void WINAPI DLLExport SetPropValue(LPMV mV, LPEDATA edPtr, UINT nPropID, LPVOID lParam)

Параметры функции SetPropValue:

• Указатель на структуру mv, содержащую информацию о системе CF.
• Указатель на структуру EDITDATA объекта.
• Идентификатор свойства.
• Указатель на класс CPropValue, содержащий новое значение.

Эта подпрограмма должна быть также основана на операторе switch / case на базе идентификатора свойства. Для каждого идентификатора свойства функция должна восстановить данные и записать их в структуру EDITDATA объекта.

Чтобы получить данные, Вам нужно сделать приведение типа (cast) параметра lParam в один из классов CPropValue. Например:

DWORD myDWordValue = ((CPropDWordValue*)lParam)->m_dwValue;float myFloatValue = ((CPropFloatValue*)lParam)->m_fValue;
LPCTSTR myStringPointer = ((CPropStringValue*)pValue)->GetString();

Обратите внимание, что для свойств чекбокса CF вызывает специальную функцию, а не SetPropValue, что будет рассмотрено отдельно на шаге 7.

Для дополнительной информации о функции SetPropValue см. раздел "List of necessary functions / Edit time".

Пример:

void WINAPI DLLExport SetPropValue(LPMV mV, LPEDATA edPtr, UINT nPropID, LPVOID lParam)
{
   // Получение указателя на структуру CPropValue:
   CPropValue* pValue = (CPropValue*)lParam;
 
   switch (nPropID)
   {
   case PROPID_COMBO:
      // Простая выборка значения:
      edPtr->nComboIndex = ((CPropDWordValue*)pValue)->m_dwValue;
      break;
   case PROPID_COLOR:
      // Здесь то же самое, выборка значения:
      edPtr->dwColor = ((CPropDWordValue*)pValue)->m_dwValue;
      break;
   case PROPID_TEXT:
      // Получение строки:
      LPCTSTR pStr = ((CPropStringValue*)pValue)->GetString();
      // Вы можете просто сделать копию строки, если структура
      // EDITDATA имеет фиксированный размер. Либо она может
      // иметь адаптивный размер, как показано ниже.
      // Если длина отличается:
      if  (_tcslen(pStr)!=_tcslen(edPtr->text))
      {
         // Запрос к CF для изменения размера структуры в памяти:
         LPEDATA pNewPtr=(LPEDATA)mvReAllocEditData(mV,
                                                    edPtr,
                                                    sizeof(EDITDATA)+_tcslen(pStr)*sizeof(TCHAR));
         if (pNewPtr!=NULL)
         {
            // Если повторное выделение структуры сработало,
            // делаем копию строки:
            edPtr=pNewPtr;
            _tcscpy(edPtr->text, pStr);
         }
      }
      else
      {    
         // Тот же размер: простое копирование:
         _tcscpy(edPtr->text, pStr);
      }
      break;
   }
   // Вы можете захотеть перерисовать свой объект в редакторе фрейма
   // после изменений, в этом случае просто вызовите эту функцию:
   mvInvalidateObject(mV, edPtr);
}

Шаг 7. Изменение свойства чекбокса.

Когда пользователь меняет состояние чекбокса в списке свойств, система CF вызывает подпрограмму объекта, чтобы отразить соответствующие изменения в структуре EDITDATA объекта. Вот эта функция:

void WINAPI DLLExport SetPropCheck(LPMV mV, LPEDATA edPtr, UINT nPropID, BOOL nCheck)

Параметры SetPropCheck:

• Указатель на информационную структуру mv.
• Указатель на структуру EDITDATA объекта.
• Идентификатор свойства.
• Состояние чекбокса, 0 или 1.

Эта функция также должна быть основана на операторе switch / case по базе идентификатора свойства. Для каждого идентификатора функция должна записать текущее состояние в нужную ячейку структуры EDITDATA.

Пример:

void WINAPI DLLExport SetPropCheck(LPMV mV, LPEDATA edPtr, UINT nPropID, BOOL nCheck)
{
   switch (nPropID)
   {
   case PROPID_CHECK:
      edPtr->nCheck = nCheck;
 
      // Перерисовать объект в редакторе фрейма:
      mvInvalidateObject(mV, edPtr);
 
      // Указать свойство PROPID_COMBO для перерисовки:
      mvRefreshProp(mV, edPtr, PROPID_COMBO);
      break;
   }
}

Для дополнительной информации о функции SetPropCheck см. раздел "List of necessary functions / Edit time".

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

Обратите внимание, что каждый макрос обычно бывает 3 версий:

PropData_xxxx (id, name, info[, param])
PropData_xxxx_Check (id, name, info[, param])
PropData_xxxx_Opt (id, name, info, options[, param])

- Первый макрос задает определение по умолчанию.
- Второй добавляет чекбокс в левую часть свойства.
- Третий позволяет Вам указать некоторые опции, что используется редко. Опции, которые Вы можете использовать, следующие:

PROPOPT_CHECKBOX к свойству добавляется чекбокс.
PROPOPT_BOLD заголовок свойства должен быть показан жирным шрифтом.
PROPOPT_PARAMREQUIRED требуется параметр инициализации, и он не может быть NULL (полезно, если Вы определяете свои собственные свойства).
PROPOPT_SINGLESEL это свойство не отображается, когда в редакторе фрейма выделено несколько объектов.

Замечание: просто для информации, PropData_xxxx_Check ( id, title, info, param ) является эквивалентом PropData_xxxx_Opt ( id, title, info, PROPOPT_CHECKBOX, param).

Параметры макросов:

id идентификатор свойства.
name идентификатор строки или указатель на строку, содержащую имя свойства.
info идентификатор или указатель строки информации.
options один или большее количество флагов, описанных ранее.
param опциональный параметр инициализации, зависящий от свойства.

Замечание по поводу param: если свойство требует параметра инициализации, и param == NULL в макросе PropData, то система CF вызовет функцию GetPropCreateParam, когда создается свойство (или когда оно обновляется функцией mvRefreshProp с параметром ReInit, установленным в TRUE). Функция GetPropCreateParam должна возвратить параметр инициализации. Этот механизм позволяет Вам динамически создавать содержимое свойства (например, список элементов в combo box), соответствующее данным объекта. Когда свойство инициализировано, система CF вызывает функцию ReleasePropCreateParam, так что Вы можете освободить память, если Вы выделяете параметр динамически. Для дополнительной информации см. описание функций GetPropCreateParam и ReleasePropCreateParam в разделе "List of necessary functions / Edit time".

PropData_Button( id, name, info, text )
PropData_Button_Check( id, name, info, text )
PropData_Button_Opt( id, name, info, options, text )

Отображает кликабельную кнопку. Когда пользователь кликает на ней, CF вызывает функцию EditProp, реализованную в Вашем расширении. Для получения подробностей обратитесь к документации по этой функции в в разделе "List of necessary functions / Edit time".

Даже если это свойство не отображает реальное свое содержимое, в зависимости от содержимого этого свойства Вы должны реализовать функции GetPropValue и SetPropValue, так что когда пользователь выбирает несколько объектов, и кликает на кнопку Edit, это свойство копировалось во все другие выделенные объекты. CPropValue, возвращенное GetPropValue, и переданное в SetPropValue не определено, Вы можете использовать то, что хотите.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
text идентификатор или указатель на текст, отображаемый на кнопке.
options см. раздел "Макросы PropData".

PropData_CheckBox( id, name, info )
PropData_CheckBox_Opt( id, name, info, options )

Отобразит чекбокс. Система CF вызовет функцию GetPropCheck в Вашем расширении, чтобы получить состояние чекбокса, и вызовет функцию SetpropCheck, когда пользователь кликает на чекбоксе. Вызов GetPropValue или SetPropValue не осуществляется.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".

Примечание: здесь нет макроса PropData_CheckBox_Check, так как свойство CheckBox в основном "пустое" свойство для чекбокса.

PropData_Color( id, name, info )
PropData_Color_Check( id, name, info )
PropData_Color_Opt( id, name, info, options )

Отображает свойство цвета: цвет и селектор цвета.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое из GetPropValue, и передаваемое в SetPropValue, это CPropDWordValue, которое содержит значение COLORREF.

PropData_ComboBox( id, name, info, list )
PropData_ComboBox_Check( id, name, info, list )
PropData_ComboBox_Opt( id, name, info, options, list )

Отображает список выбора, содержащий строки.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
list указатель на массив строк, или массив макросов MAKEINTRESOURCE с идентификаторами строк. Первая запись в этом массиве должна быть 0 (зарезервировано для использования в будущем), и массив должен заканчиваться значением NULL.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое из GetPropValue, и передаваемое в SetPropValue, является значением CPropDWordValue, которое содержит индекс выбранного элемента в combo box (-1 = ничего не выбрано, 0 = выбран первый элемент, и т. д.).

Замечания:

Параметр list может быть равен NULL. В этом случае система CF вызовет функцию GetPropCreateParam, когда создается свойство combo box (или когда оно обновляется функцией mvRefreshProp с параметром ReInit, установленным в TRUE). Функция GetPropCreateParam должна возвратить ненулевой указатель (не равный null) на массив строк (наподобие статического параметра). Когда свойство combo box инициализировано, CF вызовет функцию ReleasePropCreateParam, так что Вы можете освободить память, если Вы её динамически выделили для указателя на массив строк. Для дополнительной информации см. описание функций GetPropCreateParam и ReleasePropCreateParam в разделе "List of necessary functions / Edit time".

Пример:

LPCTSTR ComboList[] =
{
   0,    // зарезервировано
   MAKEINTRESOURCE(IDS_FIRSTOPTION),    
   MAKEINTRESOURCE(IDS_SECONDOPTION),    
   MAKEINTRESOURCE(IDS_THIRDOPTION),    
   NULL
};
 
PropData_ComboBox (PROPID_COMBO,  IDS_PROP_COMBO,  IDS_PROP_COMBO, ComboList);

PropData_ComboBoxBtn( id, name, info, list )
PropData_ComboBoxBtn_Check( id, name, info, list )
PropData_ComboBoxBtn_Opt( id, name, info, options, list )

Отображает кнопку "+/-" и список выбора, содержащий строки.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
list указатель на массив строк, или массив макросов MAKEINTRESOURCE с идентификаторами строк. Первая запись в этом массиве должна быть 0 (зарезервировано для использования в будущем), и массив должен заканчиваться значением NULL.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое из GetPropValue, и передаваемое в SetPropValue, является значением CPropDWordValue, которое содержит индекс выбранного элемента в combo box (-1 = ничего не выбрано, 0 = выбран первый элемент, и т. д.).

Замечания:

Параметр list может быть равен NULL. В этом случае система CF вызовет функцию GetPropCreateParam, когда создается свойство combo box (или когда оно обновляется функцией mvRefreshProp с параметром ReInit, установленным в TRUE). Функция GetPropCreateParam должна возвратить ненулевой указатель (не равный null) на массив строк (наподобие статического параметра). Когда свойство combo box инициализировано, CF вызовет функцию ReleasePropCreateParam, так что Вы можете освободить память, если Вы её динамически выделили для указателя на массив строк. Для дополнительной информации см. описание функций GetPropCreateParam и ReleasePropCreateParam в разделе "List of necessary functions / Edit time".

Пример:

LPCTSTR ComboList[] =
{
   0,    // зарезервировано
   MAKEINTRESOURCE(IDS_FIRSTOPTION),    
   MAKEINTRESOURCE(IDS_SECONDOPTION),    
   MAKEINTRESOURCE(IDS_THIRDOPTION),    
   NULL
};
 
PropData_ComboBoxBtn (PROPID_COMBO,  IDS_PROP_COMBO,  IDS_PROP_COMBO, ComboList);

PropData_Custom( id, name, info, param )
PropData_Custom_Check( id, name, info, param )
PropData_Custom_Opt( id, name, info, options, param )

Это свойство позволяет Вам создавать свои собственные свойства.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
param указатель на структуру CustomPropCreateStruct structure, см. ниже.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое из GetPropValue, и передаваемое в SetPropValue, должно быть таким же, как и значение, используемое в функциях GetPropValue и SetPropValue Вашего объекта CCustomProp (см. описание ниже).

Пример:

CustomPropCreateStruct initStruct =
{
   CreateMyCustomPropProc,
   0
};
 
PropData_Custom( PROPID_MYPROP, IDS_MYPROP, IDS_MYPROP_INFO, &initStruct );

Структура CustomPropCreateStruct. Эта структура содержит значения для создания элемента свойства в новом окне, и инициализирует его.

typedef struct CustomPropCreateStruct
{
   CP_CREATEINSTANCE    m_pCreateCustomPropFnc;
   LPARAM               m_lCreateParam;
} CustomPropCreateStruct;

m_lCreateParam это параметр инициализации, который соответствует lParam, обычно передаваемые в свойства наподобие ComboBox. Поместите в этот член структуры значение, специфичное для Вашего идентификатора свойства.

m_pCreateCustomPropFnc это callback-функция, которую CF вызовет для создания свойства, она имеет следующие формат:

CCustomProp* CALLBACK CreateMyCustomPropProc()
{
    // Эта функция должна возвратить объектCCustomProp:
    return new CMyCustomProp;
}

Класс CCustomProp. Базовый класс CCustomProp, определенный в Props.h, представляет Ваш элемент свойства в окне редактора свойств (property window). Вы должны создать на его основе свой новый класс, например CMyCustomProp, производный от CCustomProp. Базовый класс CCustomProp имеет следующие функции:

void Initialize(HINSTANCE hInst, LPARAM lCreateParam, BOOL bReInit);

Вызывается системой CF для инициализации элемента свойства. hInst это указатель HINSTANCE на программу, lCreateParam это член m_lCreateParam структуры CustomPropCreateStruct (в PropData) и bReInit будет TRUE, когда эта функция вызывается для повторной инициализации свойства, если оно уже было создано (когда запрашивается обновление с параметром reinit, равным TRUE).

void Delete();

Вы не должны изменять эту функцию.

void Draw(HDC hDC, LPRECT pRc, HFONT hFont, COLORREF textColor);

Позволяет Вам нарисовать элемент свойства в окне редактора свойств. hDC это контекст устройства окна свойств (windows device context), pRc это прямоугольник, в котором Вам нужно нарисовать элемент свойства, hFont и textColor должны использоваться для отображения некоторого текста.

CPropValue* GetPropValue();

Вы должны вернуть значение свойства, которое отображается в настоящий момент окном редактораYou must return the property value currently displayed in the property window.

void SetPropValue(CPropValue* pValue);

Эта функция вызывается CF для установки значения элемента свойства.

void Activate(HWND hParent, LPRECT pRc, HFONT hFont);

Эта функция вызывается, когда пользователь делает клик на элементе свойства в редакторе свойств. Например, в этой функции Вы можете создать или показать окно combo box, кнопку, и т. п. hParent это хендл окна редактора свойств, pRc это координаты прямоугольника элемента свойства относительно родительского окна.

void Deactivate();

Эта функция вызывается для деактивации элемента свойств. Например, в этой функции Вы можете скрыть элемент управления (control), который был создан функцией Activate.

void Refresh();

Эта функция вызывается системой CF после изменения значения.

void Move(LPRECT pRc); 

Эта функция вызывается, если элемент свойства был перемещен или у него был изменен размер. Например, Вы можете обновить позицию элементов управления пользовательского интерфейса, созданные при активации окна (Activate). pRc это новый прямоугольник области элемента свойств.

BOOL UpdateData();

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

PropData_DirCtrl( id, name, info, param )
PropData_DirCtrl_Check( id, name, info, param )
PropData_DirCtrl_Opt( id, name, info, options, param )

Отобразит селектор для 32 возможных направлений, наподобие используемого в свойствах перемещения (movement properties).

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".
param указатель на структуру DirCtrlCreateParam:

typedef struct
{
   BOOL  bMultiSel;  // TRUE для множественного выбора, FALSE для одиночного выбора
   int   numDirs;    // количество направлений 4, 8, 16 или 32
   DWORD style;      // стиль
} DirCtrlCreateParam;

Поле style может содержать один или большее количество флагов:

DCS_FLAT плоское отображение (Flat).
DCS_3D трехмерное отображение (3D).
DCS_SLIDER слайдер для изменения количества направлений.
DCS_EMPTY непонятно, что это должно означать... вроде должно как-то влиять на точки направлений.
DCS_SETALL_BTNS добавляет кнопки "Select All" (выбрать все) и "Reset" (сброс).

Пример:

DirCtrlCreateParam initDirParam32 = { TRUE, 32, DST_3D | DST_SETALL_BTNS };
PropData_DirCtrl(PROPID_INITDIR, IDS_INITDIR, IDS_INITDIR_INFO, &initDirParam32);

CPropValue: значение, возвращаемое GetPropValue, и передаваемое в SetPropValue, должно быть CPropDataValue, которое содержит структуру PropDirValue.

typedef struct
{
   int   selDir;     // выбранное направление (индекс одиночного выбора направления)
   DWORD selDir32;   // выбранные направления (32-битная маска направлений, для множественного выбора)
   int   numDirs;    // количество направлений
   int   reserved;   // не используется
} PropDirValue;

Пример:

PropDirValue pv;
memset(&pv, 0, sizeof(PropDirValue));
pv.selDir32 = m_pMvtData->mvDirAtStart;
pv.numDirs = 32;return new CPropDataValue(sizeof(PropDirValue), (LPBYTE)&pv);

PropData_EditButton( id, name, info )
PropData_EditButton_Check( id, name, info )
PropData_EditButton_Opt( id, name, info, options )

Отображает кнопку, на которой имеется текст "Edit". Это тот же PropData_Button, к которого нельзя поменять текст на кнопке.

Когда пользователь кликает на этой кнопке, CF вызовет функцию EditProp, реализованную в Вашем расширении. Для получения подробностей обратитесь к документации по этой функции в в разделе "List of necessary functions / Edit time".

Даже если это свойство не отображает реальное свое содержимое, в зависимости от содержимого этого свойства Вы должны реализовать функции GetPropValue и SetPropValue, так что когда пользователь выбирает несколько объектов, и кликает на кнопку Edit, это свойство копировалось во все другие выделенные объекты. CPropValue, возвращенное GetPropValue, и переданное в SetPropValue не определено, Вы можете использовать то, что хотите.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".

PropData_EditFloat ( id, name, info )
PropData_EditFloat_Check ( id, name, info )
PropData_EditFloat_Opt ( id, name, info, options )

Отображает окошко редактирования для ввода числа с плавающей запятой.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое GetPropValue, и передаваемое в SetPropValue, то тип CPropFloatValue.

PropData_EditMultiLine ( id, name, info )
PropData_EditMultiLine_Check ( id, name, info )
PropData_EditMultiLine_Opt ( id, name, info, options )

Отображает окошко редактирования с кнопкой Edit, которая открывает окно диалога с многострочным вводом (multi-line edit box).

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropStringValue, оно содержит многострочный текст: каждая строка заканчивается на последовательность символов CR/LF (0x0D, 0x0A), и весь многострочный текст заканчивается нулевым символом (символ с кодом 0).

PropData_EditNumber ( id, name, info )
PropData_EditNumber_Check ( id, name, info )
PropData_EditNumber_Opt ( id, name, info, options )

Отображает бокс ввода для редактирования числа.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropDWordValue.

PropData_EditString ( id, name, info )
PropData_EditString_Check ( id, name, info )
PropData_EditString_Opt ( id, name, info, options )

Отображает бокс для ввода и редактирования строки.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropStringValue, это значение содержит строку.

Пример:

return new CPropDataValue(pStr);
LPCTSTR pStr = ((CPropStringValue*)lParam)->GetString();

PropData_End( )

Показывает конец списка PropData.

PropData_Filename ( id, name, info, param )
PropData_Filename_Check ( id, name, info, param )
PropData_Filename_Opt ( id, name, info, options, param )

Отображает свойство имени файла: имя файла в строке редактирования пути, и кнопка browse, которая открывает браузер/селектор файла.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".
param указатель на структуру FilenameCreateParam:

typedef struct
{
   // Строка фильтра для диалога GetOpenFilename
   // (например "All Files (*.*)|*.*|"):
   LPCTSTR extFilter;
   // Опции для диалога GetOpenFilename (OFN_FILEMUSTEXIST,
   // OFN_PATHMUSTEXIST, OFN_HIDEREADONLY, и т. д.):
   DWORD   options;
} FilenameCreateParam;

Пример:

FilenameCreateParam fcp = { MAKEINTRESOURCE(IDS_TXTFILTER),
                            OFN_FILEMUSTEXIST | OFN_HIDEREADONLY };
PropData_Filename(PROPID_FILENAME,
                  IDS_PROP_FILENAME,
                  IDS_PROP_FILENAME_INFO,
                  &fcp);

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropStringValue, это значение содержит имя файла.

PropData_Folder( id, name, info )

Инициализирует папку, содержащую другие свойства.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.

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

PropData_Folder_End()

Показывает окончание папки для свойств.

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

PropData_Font ( id, name, info, param )
PropData_Font_Check ( id, name, info, param )
PropData_Font_Opt ( id, name, info, options, param )

Отображает свойство шрифта: имя шрифта и кнопку его выбора (Choose Font), которая открывает селектор шрифта.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".
param опции для Windows-диалога (CF_EFFECTS, CF_SCREENFONTS, и т. п.).

Пример:

PropData_Font(PROPID_TEXT_FONT, IDS_TEXTPROP_FONT,
              IDS_TEXTPROP_FONT_INFO, (CF_EFFECTS | CF_SCREENFONTS));

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropDataValue, это значение содержит структуру LOGFONT. Пример:

return new CPropDataValue(sizeof(LOGFONT), (LPBYTE)&lf);
LOGFONT* plf = (LOGFONT*)((CPropDataValue*)lParam)->m_pData;

PropData_Group( id, name, info)

Отображает заголовок группы.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.

Замечания: группу используются в подавляющем большинстве объектов: они упрощают список и позволяют составить его в организованном порядке.

PropData_IconComboBox( id, name, info, list )
PropData_IconComboBox_Check( id, name, info, list )
PropData_IconComboBox_Opt( id, name, info, options, list )

Отображает окно списка (list box), содержащее строки, в начале которых содержаться иконки.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".
list указатель на массив LPCTSTR, который содержит:

- Значение NULL, зарезервированное для будущего использования.
- Значение, которое содержит размер иконки, например (LPCTSTR)24.
- Каждая строка массива либо указывает на строку LPCTSTR, либо в строке будет MAKEINTRESOURCE(string identifier) и ресурс HICON.
- Значение NULL, показывающее конец таблицы.

Пример:

LPCTSTR iconComboList[] =
{
   (LPCTSTR)0,    // зарезервировано
   (LPCTSTR)24,   // размер иконки
   MAKEINTRESOURCE(IDS_LINE1), hIcon1,
   MAKEINTRESOURCE(IDS_LINE2), hIcon2,
   MAKEINTRESOURCE(IDS_LINE3), hIcon3
   MAKEINTRESOURCE(IDS_LINE4), hIcon3,
   NULL           // показывает конец списка
}; 
 
PropData_IconComboBox (PROPID_COMBO, IDS_PROP_COMBO,
                       IDS_PROP_COMBO, iconComboList);

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropDWordValue, это значение содержит индекс выбранного элемента в combo box (-1 = ничего не выбрано, 0 = первый элемент).

Замечания: Вы можете установить параметр list в значение NULL, когда используете макрос в PropData, создайте его в функции GetPropCreateParam function и освободите в функции ReleasePropCreateParam. Это самый простой способ загрузить иконки. Внимание: не уничтожайте иконки созданного списка, они будут уничтожены, когда завершится программа (CF).

PropData_ImageList ( id, name, info )
PropData_ImageList_Check ( id, name, info )
PropData_ImageList_Opt ( id, name, info, options )

Отображает список картинок с кнопкой редактирования (Edit).

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropDataValue, это значение содержит количество изображений (WORD), за которым идет список значений WORD, содержащий идентификаторы изображений.

Замечания: когда пользователь делает клик на кнопке Edit, CF вызывает функцию EditProp, реализованную в Вашем расширении. Например, в функции EditProp Вы можете открыть редактор списка картинок CF (image list editor) вызовом mvEditAnimation (или редактор картинки, picture editor, с помощью вызова mvEditImage, если это только одна картинка). Пример:

GetPropValue:

   CPropDataValue* pv = new CPropDataValue((nImages + 1) * sizeof(WORD), NULL);
   if ( pv != NULL )
   {
      if ( pv->m_pData != NULL )
      {
         LPWORD pw = (LPWORD)pv->m_pData;
         *pw++ = nImages;
         for (WORD w=0; w < nImages; w++)
             *pw++ = edPtr->images[w];
         return pv;
      }
      pv->Delete();
   }

SetPropValue:

   if ( ((CPropDataValue*)pValue)->m_pData != NULL )
   {
      LPWORD pw = (LPWORD)((CPropDataValue*)pValue)->m_pData;
      nImages = *pw++;
      // и т. д.
   }

EditProp:

BOOL WINAPI DLLExport EditProp(LPMV mV, LPEDATA edPtr, UINT nPropID)
{
   if ( nPropID == PROPID_IMAGES )
   {
      EditAnimationParams    eap;
      eap.m_dwSize = sizeof(EditAnimationParams);
      eap.m_pWindowTitle = NULL;
      eap.m_nImages = edPtr->nImages;
      eap.m_nMaxImages = edPtr->nImages;
      eap.m_nStartIndex = 0;
      eap.m_pImages = edPtr->images;
      eap.m_pImageTitles = NULL;
      eap.m_dwOptions = PICTEDOPT_FIXEDNFRAMES;
      eap.m_dwFixedWidth = 100;
      eap.m_dwFixedHeight = 100;
 
      if ( mV->mvEditAnimation(edPtr, &eap, NULL) )
      {
         mvRefreshProp(mV, edPtr, PROPID_IMAGES, FALSE);
         mvInvalidateObject(mV, edPtr);
         return TRUE;
      }
   }
 
   return FALSE;
}

PropData_PictureFilename ( id, name, info, param )
PropData_PictureFilename_Check ( id, name, info, param )
PropData_PictureFilename_Opt ( id, name, info, options, param )

Отображает свойство имени файла картинки: имя файла к строке редактирования (edit box), и кнопка browse, которая открывает селектор файла картинки.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".
param TRUE, если Вы хотите также использовать и файлы анимации (FLI, AVI, и т. п.).

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropStringValue, это значение содержит имя файла.

Пример:

PropData_PictureFilename(PROPID_FILENAME,
                         IDS_PROP_FILENAME,
                         IDS_PROP_FILENAME_INFO);

PropData_Size ( id, name, info, tab )
PropData_Size_Check ( id, name, info, tab )
PropData_Size_Options ( id, name, info, options, tab )

Отображает свойство размера (size): две поля ввода (edit box), одна для горизонтального размера, другая для вертикального.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".
tab указатель на таблицу размеров.

Пример:

int tabPredefinedSizes[] =
{
   320, 200,
   320, 240,
   640, 480,
   800, 600,
   1024, 768,
   1280, 1024,
   1400, 1050,
   1600, 1200,
   0, 0
};
 
PropData_Size(PROPID_WINDOWSIZE, IDS_WINDOWSIZE,
              IDS_WINDOWSIZE_INFO, (LPARAM)tabPredefinedSizes);

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropSizeValue.

PropData_SpinEdit ( id, name, info, minmax)
PropData_SpinEdit_Check ( id, name, info, minmax)
PropData_SpinEdit_Opt ( id, name, info, options, minmax)

Отображает spinbox (регулятор), связанный с полем редактирования (edit box), чтобы проще редактировать целые числа.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".
minmax указатель на таблицу, которая содержит минимальное и максимальное значения. Внимание: минимальное значение не может быть меньше -32768, максимальное значение не может быть больше 32767, и разница между обоими этими значениями не может быть больше 32767.

Пример:

int MinMaxAngle[] = { 0, 359 };
PropData_SpinEdit(PROPID_FII_ANGLE, IDS_FIIPROP_ANGLE,
                  IDS_FIIPROP_ANGLE_INFO, &MinMaxAngle);

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropDWordValue.

PropData_SliderEdit ( id, name, info, minmax)
PropData_SliderEdit_Check ( id, name, info, minmax)
PropData_SliderEdit_Opt ( id, name, info, options, minmax)

Отображает слайдер, связанный с полем редактирования (edit box), чтобы проще редактировать целые числа.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".
minmax указатель на таблицу, которая содержит минимальное и максимальное значения.

Пример:

int MinMaxAngle[] = { 0, 359 };
PropData_SliderEdit(PROPID_FII_ANGLE, IDS_FIIPROP_ANGLE,
                    IDS_FIIPROP_ANGLE_INFO, &MinMaxAngle);

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropDWordValue.

PropData_StaticString ( id, name, info )
PropData_StaticString_Check ( id, name, info )
PropData_StaticString_Options ( id, name, info, options )

Отображает простую статическую строку текста.

Параметры:

id идентификатор свойства.
name идентификатор строки или указатель на строку, где содержится имя свойства.
info идентификатор или указатель на строку информации.
options см. раздел "Макросы PropData".

CPropValue: значение, возвращаемое GetPropValue, и передаваемое SetPropValue, имеет тип CPropStringValue, здесь содержится строка.

[Реализация поддержки отладчика]

CF содержит инструментарий, чтобы помочь отлаживать Ваше приложение: debugger.

Этот инструмент поставляется в форме окна, в котором Вы можете, когда отлаживаемая программа работает (runtime), отобразить важную информацию об объектах, которые можно найти в приложении. Эта часть документации будет покажет Вам, как по шагам реализовать обмен отладочной информацией, касающейся Вашего объекта, чтобы она отображалась в окне отладчика.

Шаг 1. Определение значений для отображения.

Первое, что Вам нужно сделать для реализации отладчика в объекте - определить, что Вы хотите показывать в окне отладки. Составьте список из всех этих значений. Обратите внимание, что по умолчанию в окне отладки будут показаны параметры X и Y позиции объекта, а также его ширина (width) и высота (height).

Шаг 2. Создание таблицы DebugTree.

Для каждого значения, которое хотите отобразить, Вам нужно определить идентификатор. Сделайте это в начале файла EXT.CPP, с помощью простой структуры перечисления:

// Определение отлаживаемых элементов:
enum
{
   DB_CURRENTSTRING,
   DB_CURRENTVALUE,
   DB_CURRENTCHECK,
   DB_CURRENTCOMBO
};

В этом примере мы определили 4 идентификатора, поскольку хотим показывать 4 значения.

Как только идентификатора определены, Вы должны составить таблицу DebugTree. Эта таблицу представляет простой список значений WORD, с идентификаторами:

// Эта таблица определяет, что будет показано в отладчике:
WORD DebugTree[] =
{
   DB_CURRENTSTRING|DB_EDITABLE,
   DB_CURRENTVALUE|DB_EDITABLE,
   DB_CURRENTCHECK,
   DB_CURRENTCOMBO,
   DB_END
};

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

Шаг 3. Определение функции GetDebugTree.

Функция GetDebugTree вызывается системой CF, чтобы получить адрес DebugTree. Она просто возвратит адрес этой таблицы.

LPWORD WINAPI DLLExport GetDebugTree(LPRDATA rdPtr)
{
   return DebugTree;
}

Шаг 4. Определение функции GetDebugItem.

Функция GetDebugItem вызывается системой CF, чтобы получить текст элемента для отображения.

void WINAPI DLLExport GetDebugItem(LPTSTR pBuffer, LPRDATA rdPtr, int id)

На входе в функцию CF передает 3 параметра:

• Адрес буфера с текстом, куда Вы будете копировать строку для отображения. У этого буфера длина равна DB_BUFFERSIZE.
• Адрес структуры RUNDATA.
• Идентификатор отлаживаемого элемента.

Эта подпрограмма должна просто состоять из оператора switch / case на базе идентификатора элемента. Тогда Вы загружаете строку из ресурса, описывающего элемент, используете wsprintf для обработки значения в строке и копируете результат по адресу pBuffer, уделяя внимание тому, чтобы не переполнить буфер.

Как это делается, см. проект Template, поставляемый вместе с EDK.

Шаг 5. Если необходимо определяется функция EditDebugItem.

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

void WINAPI DLLExport EditDebugItem(LPRDATA rdPtr, int id)

В эту функцию передается 2 параметра:

• Адрес структуры RUNDATA.
• Идентификатор отлаживаемого элемента.

Здесь также подпрограмма должна состоять из простого оператора simple switch / case на базе идентификатора. Для каждого передаваемого значения подпрограмма должна открыть для пользователя поле редактирования (edit box), чтобы он мог изменить значение элемента. Обратите внимание, что приложение будет приостановлено (как будто поставлено на паузу) перед вызовом этой функции: Вы можете тратить столько времени, сколько нужно, чтобы отредактировать значение. Для реализации редактирования нужно предоставить 2 вызова callback-функции, один для целого значения, другой для строкового:

callRunTimeFunction(rdPtr, RFUNCTION_EDITINT, 0, (LPARAM)&dbi);

   и

callRunTimeFunction(rdPtr, RFUNCTION_EDITTEXT, 0, (LPARAM)&dbi);

Подробнее об этих callback-функциях см. раздел "CallBacks" документации, секция "runtime / RFUNCTION".

[Управление окном]

Некоторые объекты нуждаются в открытии окна поверх фрейма CF. Это могут быть, например, органы управления, просмотрщики видео, интерфейс MCI (Media Control Interface) и т. д. Такого рода окна будут либо дочерним окном основного окна приложения, либо дочерним окно окна фрейма (последнее является дочерним для основного окна приложения). Вам нужно уметь принимать Windows-сообщения от Вашего окна, и перехватывать сообщения, которые отправляются в окно фрейма, такие как окна оповещения.

CF предоставляет Вам все инструменты, которые нужны для тонкого управления Вашим окном.

Пожалуйста, просмотрите исходный код объекта StaticText для получения информации по следующим функциям: CreateRunObject, WindowProc, DestroyRunObject и в файле ext.h определение для OEFLAGS. Далее идет описание, как реализовать управление окном из расширения.

Перехват оповещающих сообщений окна фрейма. Можно очень просто реализовать перехват каждого сообщения Windows, которое посылается в окно фрейма. Такие сообщения могут быть сообщениями палитры, кодом оповещения, и т. д.

Нужно реализовать 2 вещи:

• Установка OEFLAG_WINDOWPROC в поле OEFLAGS расширения.
• Определить функцию с именем WindowProc (см. список Run-time функций) для обработки сообщений.

Вы также можете определить приоритет для Вашей подпрограммы, если хотите, чтобы она была вызвана первой: #define WINDOWPROC_PRIORITY 100. 100 соответствует среднему уровню приоритета, 0 самому низкому приоритету и 255 самому высокому приоритету.

Открытие нового окна. Обычно окна открываются подпрограммой CreateRunObject, и удаляется в подпрограмме DestroyRunObject. Используйте обычную подпрограмму Windows, такую как CreateWindow, чтобы открыть окно, и сохраните указатель на него (хендл, handle) в структуре RUNDATA. Рекомендуется сделать Ваше окно дочерним для окна фрейма.

Преобразования координат. Координаты объекта находятся в подструктуре headerObject, находящейся в начале Вашей структуры RUNDATA. В этой подструктуре содержатся координаты фрейма для Вашего объекта. Вы должны преобразовать эти координаты в координаты клиентского окна фрейма. Это осуществляется простым вычитанием полей rhWindowX и rhWindowY, содержащихся в структуре RunHeader. Пример открытия окна управления:

// Для получения rhPtr:
rhPtr = rdPtr->rHo.hoAdRunHeader;
 
// Создание окна:
rdPtr->hWnd = CreateWindow(_T("EDIT"), NULL, dwStyle, 
                           rdPtr->rHo.hoX - rhPtr->rhWindowX, 
                           rdPtr->rHo.hoY - rhPtr->rhWindowY, 
                           rdPtr->rHo.hoImgWidth, 
                           rdPtr->rHo.hoImgHeight, 
                           rhPtr->rhHEditWin, 
                           NULL, 
                           rhPtr->rh4.rh4Instance, 
                           NULL);

Пояснения параметров, передаваемых в функцию CreateWindow:

dwStyle стиль окна, содержащий WS_CHILD, если Ваше окно будет дочерним по отношению к окну фрейма.
rdPtr->rHo.hoX - rhPtr->rhWindowX клиентская координата X объекта.
rdPtr->rHo.hoY - rhPtr->rhWindowY клиентская координата Y объекта.
rdPtr->rHo.hoImgWidth ширина объекта.
rdPtr->rHo.hoImgHeight высота объекта.
ryPtr->ryHeditWin handle окна фрейма.
rhPtr->rh4.rh4Instance экземпляр приложения.

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

Подкласс окна. Если Ваше окно это объект управления пользовательского интерфейса (объект control, такой как кнопка, list box, и т. д.), то вы должны быть в состоянии обрабатывать сообщения оповещения, относящиеся к этому окну. Для этой цели Вы определите флаг OEFLAG_WINDOWPROC и определите процедуру WindowProc. Однако этого недостаточно. Оповещающее сообщение содержит только хендл окна (window handle, HWND), но в нем нет указателя на структуру RUNDATA, связанную с окном. Multimedia Fusion (MMF) предоставляет простую систему подкласса, чтобы было проще получить указатель на RUNDATA.

Чтобы сделать хендл окна доступным для CF, Вы должны вставить следующие поля данных в свою структуру RUNDATA (это 2 поля должны следовать одно за другим):

#define MAX_WINDOWS xxxint   rWindowNumber;
HWND  hwnd[MAX_WINDOWS];

MAX_WINDOWS содержит количество окон, которое планируется открывать из объекта. Перед вызовом hook-функции Вы должны установить указатель в поле rWindowNumber подструктуры headerObject, которая находится в Вашей структуре RUNDATA:

rdPtr->hwnd[0]=my_window_handle;
rdPtr->rWindowNumber = MAX_WINDOWS;
rdPtr->rHo.hoOffsetToWindows = (int)((LPBYTE)&rdPtr->rWindowNumber - (LPBYTE)rdPtr);
callRunTimeFunction(rdPtr, RFUNCTION_SUBCLASSWINDOW, 0, 0);

Вот пример подпрограммы WindowProc, которая восстанавливает адрес структуры RUNDATA:

LRESULT CALLBACK DLLExport WindowProc(fprh rhPtr, HWND hWnd, UINT msgType, WPARAM wParam, LPARAM lParam)
{
   LPRDATA    rdPtr;
   // Если пауза, то выход:
   if (rhPtr->rh2.rh2PauseCompteur != 0)
       return 0;
   
   // Обработка типа сообщения:
   switch (msgType)
   {
      case WM_COMMAND:
         // Обработка сообщений, которые мне нужно перехватить...
         switch (wmCommandNotif)
         {
         case BN_CLICKED:
            // Это для меня?
            if ((rdPtr = GetRdPtr((HWND)lParam, rhPtr, &n)) != NULL
             && (IDENTIFIER == rdPtr->rHo.hoIdentifier))
            {
               // ... тут делается требуемая обработка сообщения ...
           
               // Сигнал от том, что сообщение было обработано:
               return REFLAG_MSGHANDLED;
            }    
            break;
         default:
            break;
         }
         return 0;
 
   default:
      // Не для меня ...
      return 0;
   }
}

Первое, что нужно сделать - определить, что мы не находимся в режиме паузы приложения (pause mode). Если сейчас pause mode, то объект расширения не должен реагировать на действия пользователя.

Затем Вы должны получить адрес структуры RUNDATA, соответствующей этому окну. Когда окно было "подклассом" с callback-функцией RFUNCTION_SUBCLASSWINDOW, система CF сохраняет адрес структуры RUNDATA в структуру окна. Следующая подпрограмма GetRdPtr позволяет Вам получить этот адрес из Вашего окна управления:

LPRDATA GetRdPtr(HWND hWnd, LPRH rhPtr)
{
   return (LPRDATA)GetProp(hWnd, (LPCTSTR)rhPtr->rh4.rh4AtomRd);
}

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

      if ((rdPtr = GetRdPtr((HWND)lParam, rhPtr, &n)) != NULL
       && rdPtr->rHo.hoIdentifier == IDENTIFIER)
      {
         // OK, это для меня:
      }

Ваша главная оконная процедура. Для окон, не являющихся окнами управления интерфейса, Вам нужно определить реальную процедуру окна. Вы должны определить hook в своем окне, чтобы привязаться к обработке окон системы CF.

Здесь показано, как реализована оконная процедура в объекте QTVR: сначала мы регистрируем класс окна в подпрограмме Initialize:

// Регистрация класса окна QTVR:
wcMov.style = 0;
wcMov.lpfnWndProc = (WNDPROC)WndProc;
wcMov.cbClsExtra = 0;
wcMov.cbWndExtra = 0;
wcMov.hInstance = hInstLib;
wcMov.hIcon = NULL;
wcMov.hCursor = LoadCursor(NULL, IDC_ARROW);
wcMov.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcMov.lpszMenuName = NULL;
wcMov.lpszClassName = szMovClass;
if (RegisterClass(&wcMov) != 0)
   QuickTimeInit++;

В подпрограмме CreateRunObject мы создаем окно для этого класса:

rdPtr->hwndMov = CreateWindow(szMovClass,
                              NULL,
                              WS_CHILDWINDOW,
                              rdPtr->rHo.hoX - rhPtr->rhWindowX,
                              rdPtr->rHo.hoY - rhPtr->rhWindowY,
                              rdPtr->rHo.hoImgWidth,
                              rdPtr->rHo.hoImgHeight,
                              rhPtr->rhHEditWin,
                              NULL,
                              hInstLib,
                              NULL);

Теперь установим hook:

// Ветка hook:
rdPtr->raviWindowNumber = 1;
rdPtr->rHo.hoOffsetToWindows = (int)((LPBYTE)&rdPtr->raviWindowNumber - (LPBYTE)rdPtr);
callRunTimeFunction(rdPtr, RFUNCTION_SUBCLASSWINDOW, 0, (DWORD)WndHookProc);

Обратите внимание, что мы передаем адрес hook-подпрограммы как последний параметр подпрограммы callRunTimeFunction.

Теперь реальная подпрограмма окна, которая ничего не делает:

LRESULT CALLBACK DLLExport WndProc(HWND hWnd, uint msgType, WPARAM wParam, LPARAM lParam)
{
   return DefWindowProc(hWnd, msgType, wParam, lParam);
}

Hook-процедура, выполняющая нужную работу:

LRESULT CALLBACK DLLExport WndHookProc(fprh rhPtr, HWND hWnd, uint msgType, WPARAM wParam, LPARAM lParam)
{
   LPRDATA rdPtr;
   
   // Обработка сообщений:
   rdPtr=GetRdPtr(hWnd, rhPtr);
   if (rdPtr!=0)
   {
      // Сюда вставляется необходимый код ...
      ...
      // Отправка сообщения в контроллер изображения (movie controller) с возвратом:
      return MCIsPlayerMessage(rdPtr->mcController, hWnd, msgType, wParam, lParam);
   }
   return 0;
}

Hook-процедура вызывается перед реальной процедурой окна. Она должна вернуть значение, отличающееся от 0, чтобы показать, что сообщение было обработано. В этом случае WndProc не будет вызвана.

Как обработать сообщения палитры. Хотя 256 цветов приложения теперь устарели, Вы можете захотеть обработать сообщения палитры для своих окон. Когда приложение работает в 256 цветах, дочернее окно должно правильно реагировать на сообщения палитры (palette messages). Как это можно реализовать:

LRESULT CALLBACK DLLExport WindowProc(LPRH rhPtr, HWND hWnd, UINT msgType, WPARAM wParam, LPARAM lParam)
{
   LPRDATA  rdPtr;
   HWND     hwndT;
   HWND     hwndNewPal;
 
   // Это для меня?
   switch (msgType)
   {
   // Сообщения палитры:
   case WM_PALETTECHANGED:
   case WM_QUERYNEWPALETTE:
      // Поиск окон AVI со своей собственной палитрой:
      if ( NULL == rhPtr->rhHEditWin || rhPtr->rhHMainWin != hWnd )
         break;
      rdPtr = NULL;
      hwndNewPal = NULL;
      hwndT = GetWindow(rhPtr->rhHEditWin, GW_CHILD);
      
      while (hwndT)
      {
         // Получение указателя на структуру avi:
         rdPtr = GetRdPtr(hwndT, rhPtr);
         if ((rdPtr != 0) && (IDENTIFIER == rdPtr->rHo.hoIdentifier))
         {
            if (msgType == WM_PALETTECHANGED)
               SendMessage (hwndT, WM_PALETTECHANGED, wParam, lParam);
            else
            {
               // Отправка сообщения WM_QUERYNEWPALETTE к AVI
               // и WM_PALETTECHANGED в окно фрейма:
               if (NULL == hwndNewPal)
                  SendMessage(hwndNewPal = hwndT, WM_QUERYNEWPALETTE, 0, 0);
               else
                  SendMessage(hwndT, WM_PALETTECHANGED, (WPARAM)hwndNewPal, 0);
            }
            break;
         }
         hwndT = GetWindow(hwndT, GW_HWNDNEXT);
      }
      if ( hwndNewPal != NULL )
      {
         SendMessage(ryPtr->ryHeditWin, WM_PALETTECHANGED, (WPARAM)hwndNewPal, 0);
         // Показывает, что сообщение было обработано:
         return REFLAG_MSGHANDLED;
      }
      break;
   }
   return 0L;
}

Как Вы можете видеть, эта подпрограмма детектирует сообщения палитры. Когда появляется сообщение палитры, оно проходит через список дочерних окон AVI. Когда одно из них детектировано, то окну отправляется сообщение QUERYNEWPALETTE, и сообщение PALETTECHANGED в окно фрейма. Даже если такой объект реально не рисуется во фрейме, Вы должны только модифицировать позицию и размер окна в подпрограмме DisplayRunObject. Это гарантирует, что все модификации, осуществляемые по действиям, произойдут одновременно, синхронно с главным процессом перерисовки окна приложения.

[Используемые структуры]

В этой главе перечислены самые важные структуры, которые Вы должны знать и использовать, чтобы запрограммировать Ваш объект расширения. Некоторые из этих структур являются частью исходного кода расширения, и некоторые частью исходного кода CF (определены в cncf.h, cncr.h и cncy.h).

Определена в CNCR.H, часть основных структур CF. Используется только во время работы приложения runtime.

Назначение: содержит всю нужную информацию о текущем приложении.

Замечание: как и все runtime-структуры, возможно разработчики будут вынуждены поменять её содержимое в будущих обновлениях CF. Если так, то придется обновить SDK. Вы должны избегать прямого использования этих структур, конечно за исключением случаев, когда нет другого способа получить доступ к нужной информации.

Полезные поля:

m_hdr
   - gaNewFlags: содержит флаги GANF_xxx, в которых находятся настройки.
   - gaFlags: содержит флаги GA_xxx с другими настройками.
   - gaNbFrames: количество фреймов в приложении.
   - gaFrameRate: текущая скорость обновления кадров в приложении (frame rate, влияет на плавность анимации).

m_name: имя приложения.

m_appFileName: имя файла приложения (оно может находиться во временной директории, см. структуру mV, если Вам требуется имя исполняемого файла EXE).

m_copyright: сообщение копирайта.

m_aboutText: текст для отображения в окошке About.

m_doc: файл подсказки (help).

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

m_kpxTab и m_kpxDataTable: информация о расширениях (HINSTANCE и функции).

m_nbMvx: количество расширений перемещения (movement extensions).

m_mvxTable: информация о расширениях перемещения (HINSTANCE и функции).

m_oiMaxIndex: количество объектов.

m_ois: таблица объектов.

m_pParentApp: родительское приложение (если текущее приложение работает как дочернее, sub-application).

m_Frame: текущий фрейм (см. структуру CRunFrame для дополнительной информации).

m_nCurrentFrame: индекс текущего фрейма.

m_pGlobalValues: глобальные значения (предупреждение: если m_bShareGlobalValues == TRUE, используйте m_pGlobalValues из родительского приложения, или выше).

m_pGlobalStrings: глобальные строки (предупреждение: если m_bShareGlobalStrings == TRUE, используйте m_pGlobalStrings из родительского приложения, или выше).

Определена в CNCR.H, часть основных структур CF.

Назначение: содержит всю необходимую информацию по текущему фрейму во время выполнения приложения (runtime).

Замечание: как и все runtime-структуры, возможно разработчики будут вынуждены поменять её содержимое в будущих обновлениях CF. Если так, то придется обновить SDK. Вы должны избегать прямого использования этих структур, конечно за исключением случаев, когда нет другого способа получить доступ к нужной информации.

Полезные поля:

m_hdr
   - leWidth : ширина игрового поля.
   - leHeight: высота игрового поля.
   - leBackground: цвет фона.
   - leFlags: некоторые опции.

m_name: имя фрейма.

m_loMaxIndex: количество экземпляров объекта.

m_los: экземпляры объекта во фрейме.

m_rhPtr: указатель на структуру RunHeader.

Определена в MAIN.H, часть кода расширения.

Назначение: хранит настройки времени редактирования (edit-time) объекта, определенные в диалоге настройки и свойствах объекта. Эта структура должна быть полностью определена Вами! EDITDATA должен начинаться с дочерней структуры extheader, которая содержит информацию для CF.

Замечания: здесь показан минимум для структуры EDITDATA:

typedef struct
{
   extHeader eHeader;
} EDITDATA;

Структура extheader:

typedef struct tagEditExtension
{
   DWORD extSize;          // размер структуры EDITDATA
   DWORD extMaxSize;       // максимальный размер
   DWORD extVersion;       // номер версии
   LPVOID extID;           // идентификатор объекта
   LPVOID extPrivateData;  // приватные данные
} extHeader;

Замечание: Вы не должны менять эту структуру. Если Вам нужно повторно выделить структуру EDITDATA, используйте callback-функцию mvReAllocEditData в структуре mv.

Определена в CNCF.H, часть основных структур CF.

Назначение: хранит run-time информацию, не зависящую от объекта. Структура HeaderObject должна всегда быть в начале структуры RUNDATA. Предупреждение: обращайтесь только к "полезным полям", изменения в любых других областях могут полностью нарушить работу приложения!

Полезные поля:

short hoNumber;      // Номер объекта в списке объектов CF
LPRH hoAdRunHeader;  // Адрес структуры RunHeader
short hoX;           // Координата X объекта
short hoY;           // Координата Y объекта
int hoImgXSpot;      // Позиция X активной области (hot spot) текущей картинки
int hoImgYSpot;      // Позиция Y активной области (hot spot) текущей картинки
int hoImgWidth;      // Текущая ширина объекта
int hoImgHeight;     // Текущая высота объекта
short hoOEFlags;     // Флаги OEFLAGS объекта

Определена в CNPY.H, часть основного кода CF.

Назначение: хранит информацию о каждом отдельном объекте во фрейме. Имеется один экземпляр структуры LO на каждый объект во фрейме. Дублированные объекты имеют одну и ту же структуру OI.

Замечание: как и все runtime-структуры, возможно разработчики будут вынуждены поменять её содержимое в будущих обновлениях CF. Если так, то придется обновить SDK. Вы должны избегать прямого использования этих структур, конечно за исключением случаев, когда нет другого способа получить доступ к нужной информации.

Полезные поля:

loX: координата X объекта на игровом поле

loY: координата Y объекта на игровом поле

loLayer: индекс слоя объекта

loType: тип объекта

Обратите внимание, что поля этой структуры действительны только во время работы приложения (run time).

Структура mv служит для коммуникации между CF и Вашим расширением. Она содержит глобальные данные наподобие хендлов окна, callback-функции, которые Ваше расширение может использовать. Для получения списка callback-функций см. раздел "Callback-функции" этой документации.

Важные поля в структуре mv:

mvHInst: HINSTANCE программы (CF или runtime-приложения).

mvIdAppli: хендл приложения в mmfs2.dll.

mvIdMainWin: хендл главного окна в mmfs2.dll.

mvIdEditWin: хендл дочернего окна в mmfs2.dll.

mvHMainWin: хендл Windows основного окна.

mvHEditWin: хендл Windows дочернего окна.

mvHPal256: 256-цветная палитра.

mvScrMode: текущий режим экрана SM_8=256, SM_15=32768, SM_16=65536, SM_32=16 миллионов цветов.

mvEditDXDocToClient: координата X окна редактора фрейма относительно игрового поля (только для времени редактирования, edit time).

mvEditDYDocToClient: координата Y окна редактора фрейма относительно игрового поля (только для времени редактирования, edit time).

mvRunApp: только для времени выполнения приложения (run time), указатель на структуру CRunApp.

mvRunFrame: только для времени выполнения приложения (run time), указатель на структуру CRunFrame.

mvPrefs: свойства (звук включить/выключить) - только для времени выполнения приложения (run time).

mvImgFilterMgr: менеджер фильтра изображения (image filter manager).

mvSndFilterMgr: менеджер звукового фильтра (sound filter manager).

mvSndMgr: менеджер звука (sound manager).

mvMainAppFileName: файловый путь приложения (pathname). Оригинальный путь может отличаться от имени, сохраненном в CRunApp, которое может содержать путь во временной директории.

Другие поля структуры это callback-функции, подробнее о них см. раздел "Callback-функции" этой документации.

Примечание: во время работы программы (runtime) Вы можете обращаться к структуре mv из структуры RunHeader: rhPtr->rh4.rh4Mv.

Определена в CNCY.H, часть основного кода CF.

Назначение: хранит информацию об объекте. Структура OI сохраняется в приложении. Экземпляры объекта имеют структуры LO, сохраненные в каждом фрейме.

Замечание: как и все runtime-структуры, возможно разработчики будут вынуждены поменять её содержимое в будущих обновлениях CF. Если так, то придется обновить SDK. Вы должны избегать прямого использования этих структур, конечно за исключением случаев, когда нет другого способа получить доступ к нужной информации.

Полезные поля:

oiHdr
   - oiType: тип объекта.
   - oiInkEffect: эффект чернил + прозрачность (transparency) и флаг антиалиасинга.
   - oiInkEffectParam: параметр эффекта чернил.

oiName: имя объекта.

oiOC: указатель на структуру, которая содержит информацию времени редактирования (анимации, перемещения, данные расширения, и т. д.).

Примечание: только поля эффекта чернил в этой структуры достоверны во время редактирования (edit time).

Определена в MAIN.H, часть кода расширения.

Назначение: хранит информацию об объекте во время выполнения приложения (run-time). Структура RUNDATA должна всегда начинаться с headerObject, за которым идут структуры movement, animation, common и value, в зависимости от флагов OEFLAG. Для получения дополнительной информации обратитесь к секции "Определение флагов объекта / важные структуры" этой документации.

Как устроена структура RUNDATA. Вот простейшая структура RUNDATA:

typedef struct
{
   headerObject rHo;
} RUNDATA;

Определена в CNCF.H, часть основного исходного кода CF.

Назначение: хранит информацию приложения во время его выполнения (run-time). Обычно Вам нужно обращаться к этой структуре для получения хендла окон.

Предупреждение: обращайтесь только к "полезным полям", изменения в любых других полях могут полностью нарушить работу приложения!

Полезные поля:

npWin rhIdEditWin;   // идентификатор MMFS окна приложения
npWin rhIdMainWin;   // идентификатор MMFS окна фрейма приложения
npAppli rhIdAppli;   // идентификатор MMFS приложения
HWND rhHEditWin;     // хендл Windows для окна приложения
HWND rhHMainWin;     // хендл Windows для окна фрейма
CRunApp* rhApp;      // указатель на информационную структуру приложения
CRunFrame* rhFrame;  // указатель на информационную структуру фрейма
int rhLoopCount;     // количество циклов от момента запуска фрейма
UINT rhTimer;        // таймер в единицах 1/1000 секунды от начала фрейма

[Ссылки]

1. Download: Fusion 2.5 Windows SDK site:community.clickteam.com.
2. Clickstore site:clickteam.com.
3. Microsoft HTML Help Workshop site:msdn.microsoft.com.