PureBasicでのAdobe Airの拡張機能の作成

ここでPureBasicの人気が高まっていることを受けて、この言語の別の応用分野に精通することを提案します。



Airの3番目のバージョンから、拡張機能(Flash Runtime Extensions)による制限されたSDK 補うことが可能になりました。 拡張機能は、C / C ++ / Java(Android)および関連プラットフォーム用のネイティブライブラリを作成できる他の言語で作成できます。



計算速度、マルチスレッド、オペレーティングシステムとの相互作用-これらはすべて拡張機能で利用できます。 しばらく前に、MacおよびWindows用のCで拡張機能-HIDAPIクロスプラットフォームライブラリのラッパーを作成しました 。 いくつかの困難にもかかわらず、利便性はライブラリのクロスプラットフォームの性質にあり、拡張コードを一度書くだけで、最小限の労力でプラットフォームごとにアセンブルできるようになりました。



次に、PureBasicは、複数のシステム用のアプリケーション(dll、so、 ドライバー )を作成する機能を提供します。 最適化、Unicodeサポート、既製のオブジェクト(リスト、マップ)、一連のクロスプラットフォーム機能(圧縮、画像処理、暗号化アルゴリズム)、WINAPI、マクロ、さらにはOOPのほとんどが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
      
      





コンパイラの設定では、共有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>
      
      





Idは、アプリケーションマニフェストおよび拡張パッケージ(ActionScript)で指定されたものと一致する必要があり、アプリケーションプロファイルはextendedDesktopである必要があります。



拡張機能の必須部分は、AttachProcess(インスタンス)、DetachProcess(インスタンス)、AttachThread(インスタンス)、およびDetachThread(インスタンス)関数、および次の機能です。



 ... ;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配列には拡張機能のメソッド(この場合は1つだけ-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
      
      





Air側では、次のようになります。

 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は、拡張機能が送信できるイベントの唯一のタイプです。



作業の結果はスクリーンショットで見ることができます:

画像



ご清聴ありがとうございました。



All Articles