Написание Расширения Для Adobe Air С Использованием Purebasic

На волне растущей популярности здесь PureBasic , предлагаю вам ознакомиться еще с одной областью применения этого языка.

Начиная с третьей версии Air, появилось возможность компенсировать ограничения SDK за счет расширений (Flash Runtime Extensions).

Расширения можно писать на C/C++/Java(Android) и на любом другом языке, позволяющем создавать нативные библиотеки для соответствующих платформ.

Скорость вычислений, многопоточность, взаимодействие с операционной системой — все это доступно расширениям.

Некоторое время назад я написал расширение C для Mac и Windows — обертку для кроссплатформенной библиотеки.

ХИДАПИ .

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

PureBasic, в свою очередь, предоставляет возможность создания приложений (dll, т. водители ) для нескольких систем.

Есть возможность оптимизации, поддержка Unicode, готовые объекты (Список, Карта), набор кроссплатформенных функций (алгоритмы сжатия, обработка изображений, шифрование), большая часть WINAPI, макросы и даже ООП импортированы для Windows. Все это значительно ускоряет разработку (для тех, у кого нет опыта работы с C/C++) по сравнению с голым C.



Пример – системный модальный диалог
Исходники и демо-приложение для Windows доступны по адресу code.google.com .

Первым шагом было Импортировать функции из библиотеки FlashRuntimeExtensions.lib (Windows, {AIR SDK}\lib\win\), это делается относительно просто с помощью FlashRuntimeExtensions.h ({AIR SDK}\include\):

  
  
  
  
   

ImportC ".

/lib/FlashRuntimeExtensions.lib" ;returns FRE_OK ; FRE_WRONG_THREAD ; FRE_INVALID_ARGUMENT If nativeData is null. ;FREResult FREGetContextNativeData( FREContext ctx, void** nativeData ); ;-FREGetContextNativeData FREGetContextNativeData.l (ctx.l, nativeData.l) .

EndImport

В настройках компилятора необходимо указать формат Shared Dll, при необходимости включить поддержку Unicode (+ в меню Файл/Формат выбрать кодировку UTF8 исходных файлов), потокобезопасность или поддержку ассемблера.

Единственное неудобство — отсутствие в PureBasic беззнакового типа long; для этого пришлось написать дополнительные функции.

В дескрипторе расширения указываем платформу, библиотеку расширений и единственные 2 экспортируемые функции (инициализатор и финализатор):

<Эxml version="1.0" encoding="UTF-8"?> <extension xmlns=" http://ns.adobe.com/air/extension/3.1 "> <id>com.pure.Extension</id> <name>pureair</name> <copyright>compile4fun 2012</copyright> <description>Extenion for Adobe AIR</description> <versionNumber>1.0.0</versionNumber> <platforms> <platform name="Windows-x86"> <applicationDeployment> <nativeLibrary>pureair.dll</nativeLibrary> <initializer>initializer</initializer> <finalizer>finalizer</finalizer> </applicationDeployment> </platform> </platforms> </extension>

Идентификатор должен совпадать с указанным в манифесте приложения и пакете расширения (ActionScript), профиль приложения — ExtendedDesktop. Обязательная часть расширения — это функции AttachProcess(Instance), DetachProcess(Instance), AttachThread(Instance) и DetachThread(Instance), а также:

.

;CDecl ProcedureC contextInitializer(extData.l, ctxType.s, ctx.l, *numFunctions.Long, *functions.Long) *log\info("create context: " + Str(ctx) + "=" + Utf8ToUnicode(ctxType)) Define result.l ;exported extension functions count: Define size.l = 1 ;Array of FRENamedFunction: Dim f.FRENamedFunction(size - 1) ;there is no unsigned long type in PB setULong(*numFunctions, size) ;If you want to return a string out of a DLL, the string has to be declared as Global before using it. ;method name f(0)\name = asGlobal("showDialog") ;function data example f(0)\functionData = asGlobal("showDialog") ;function pointer f(0)\function = @showDialog() *functions\l = @f() ;some additional data can be stored extData = #Null ;native data example result = FRESetContextNativeData(ctx, asGlobal("FRESetContextNativeData")) *log\Debug(ResultDescription(result, "FRESetContextNativeData")) *log\info("create context complete"); EndProcedure ;CDecl ProcedureC contextFinalizer(ctx.l) *log\info("dispose context: " + Str(ctx)) EndProcedure ;CDecl ProcedureCDLL initializer(extData.l, *ctxInitializer.Long, *ctxFinalizer.Long) *ctxInitializer\l = @contextInitializer() *ctxFinalizer\l = @contextFinalizer() EndProcedure ;CDecl ;this method is never called on Windows. ProcedureCDLL finalizer(extData.l) ;do nothing EndProcedure

Массив FRENamedFunction содержит методы нашего расширения (в данном случае только один — showDialog), также есть возможность привязки любых данных к экземпляру функции или расширения.

Следует обратить внимание на тип вызова CDecl и функцию asGlobal, которая специально выделяет память для строк, передаваемых из расширения в основную программу, об этом упоминается в справке PureBasic по работе с dll. Наше расширение покажет модальный диалог с настраиваемым набором кнопок и текста и передаст событие закрытия диалога:

Structure MessageParameters text.s title.s dwFlags.l ctx.l EndStructure Procedure ModalMessage(*params.MessageParameters) Define result.l, code.l code = MessageRequester(*params\title, *params\text, *params\dwFlags) result = FREDispatchStatusEventAsync(*params\ctx, asGlobal("showDialog"), asGlobal(Str(code))) *log\Debug (ResultDescription(result, "FREDispatchStatusEventAsync")) EndProcedure ;CDecl ProcedureC.l showDialog(ctx.l, funcData.l, argc.l, *argv.FREObjectArray) *log\info("Invoked showDialog") Define result.l ;ActionScriptData example Define actionScriptObject.l, actionScriptInt.l, type.l result = FREGetContextActionScriptData(ctx, @actionScriptObject) *log\Debug(ResultDescription(result, "FREGetContextActionScriptData")) result = FREGetObjectType(actionScriptObject, @type) *log\Debug("result=" + ResultDescription(result, "FREGetObjectType")) *log\info("ContextActionScriptData: type=" + TypeDescription(type)) result = FREGetObjectAsInt32(actionScriptObject, @actionScriptInt) *log\Debug("result=" + ResultDescription(result, "FREGetObjectAsInt32")) *log\info("ContextActionScriptData: actionScriptInt=" + Str(actionScriptInt)) ;function data example Define funcDataS.s funcDataS = PeekS(funcData, -1, #PB_Ascii) *log\info("FunctionData: " + funcDataS) *log\info("Method args size: " + Str(fromULong(argc))) Define resultObject.l, length.l, booleanArg.l, dwFlags.l, message.s, *string.Ascii result = FREGetObjectAsBool(*argv\object[0], @booleanArg) *log\Debug("result=" + ResultDescription(result, "FREGetObjectAsBool")) result = FREGetObjectAsInt32(*argv\object[1], @dwFlags) *log\Debug("result=" + ResultDescription(result, "FREGetObjectAsInt32")) result = FREGetObjectAsUTF8(*argv\object[2], @length, @*string) *log\Debug("result=" + ResultDescription(result, "FREGetObjectAsUTF8")) message = PeekS(*string, fromULong(length) + 1) *log\info("Argument: booleanArg=" + Str(fromULong(booleanArg))) *log\info("Argument: dwFlags=" + Str(dwFlags)) *log\info("Argument: message=" + Utf8ToUnicode(message)) ;native data example Define native.l, nativeData.s result = FREGetContextNativeData(ctx, @native) *log\Debug(ResultDescription(result, "FREGetContextNativeData")) nativeData = PeekS(native, -1, #PB_Ascii) *log\info("FREGetContextNativeData: " + nativeData) Define *params.MessageParameters = AllocateMemory(SizeOf(MessageParameters)) *params\ctx = ctx *params\title = "PureBasic" *params\text = Utf8ToUnicode(message) *params\dwFlags = dwFlags CreateThread(@ModalMessage(), *params) ;return Boolean.TRUE result = FRENewObjectFromBool(toULong(1), @resultObject) *log\Debug(ResultDescription(result, "FRENewObjectFromBool")) ProcedureReturn resultObject EndProcedure

Со стороны Воздуха это выглядит так:

package com.pure { import flash.events.StatusEvent; import flash.external.ExtensionContext; import mx.logging.ILogger; import mx.logging.Log; /** * Wrapper for PureBasic extension */ public class Extension { /** * Extension id, must be specified in air-manifest.xml and extension.xml */ public static const CONTEXT:String = "com.pure.Extension"; private static const log:ILogger = Log.getLogger(CONTEXT); /** * @private */ private var _context:ExtensionContext; /** * @private */ private var contextType:String; /** * Creates context * @param contextType default value is "PureAir" * @param actionScriptData any number */ public function Extension(contextType:String = "PureAir", actionScriptData:int = 4) { //random type this.contextType = contextType + Math.round(Math.random() * 100000); try { log.debug("Creating context: {0}, contextType: {1}", CONTEXT, this.contextType); _context = ExtensionContext.createExtensionContext(CONTEXT, this.contextType); if (_context == null) { //creation failed log.error("Failed to create context: {0}, contextType: {1}", CONTEXT, this.contextType); } else { log.debug("Context was created successfully"); //listen for extension events _context.addEventListener(StatusEvent.STATUS, onStatusEvent); //set actionScript data _context.actionScriptData = actionScriptData; } } catch(e:Error) { log.error("Failed to create context: {0}, contextType: {1}, stacktrace: {2}", CONTEXT, this.contextType, e.getStackTrace()); } } private function get contextCreated():Boolean { return _context != null; } /** * Test method, shows YesNoCancel modal dialog * @param booleanArg boolean parameter * @param flags integer parameter, #PB_MessageRequester_YesNoCancel=3, #MB_APPLMODAL = 0 * @param message string parameter * @return */ public function showDialog(booleanArg:Boolean, flags:int, message:String):Boolean { if (!contextCreated) return false; var result:Boolean = false; try { result = _context.call('showDialog', booleanArg, flags, message) as Boolean; if (!result) { log.error("Invocation error: test({0}, {1}, {2})", booleanArg, flags, message); } } catch (e:Error) { log.error("Invocation error: test({0}, {1}, {2}), stacktrace: {3}", booleanArg, flags, message, e.getStackTrace()); } return result; } private function onStatusEvent(event:StatusEvent):void { log.info("Status event received: contextType={0} level={2}, code={1}", this.contextType, event.code, event.level); } /** * Performs clean-up */ public function dispose():void { if (_context) { _context.dispose(); //clean all references _context.removeEventListener(StatusEvent.STATUS, onStatusEvent); _context = null; log.info("Disposed {0}", this.contextType); } else { log.warn("Can not dispose {0}: Context is null", this.contextType); } } } }

StatusEvent — единственный тип событий, который может отправлять расширение.

Результат работы можно увидеть на скриншоте:

Написание расширения для Adobe Air с использованием PureBasic

Спасибо за внимание.

Теги: #purebasic #PureBasic #Adobe AIR #собственное расширение #flex #Flex #программирование #Adobe Flash

Вместе с данным постом часто просматривают: