用C++开发应用于OSX和Windows上的AIR 本机拓展

40 篇文章 0 订阅

在AIR 3.0里面,Adobe新增了一个叫做本机拓展的功能。这个功能允许你在任何AIR支持的平台上(iOS,Android,Windows,OSX等等)直接拓展AIR运行时的能力。在我给NitroLM的许可和安全项目担任顾问的时候,我获得了一个机会去深入钻研到底怎么用C++来开发出一个跨平台的本机拓展。



我在Windows上使用Microsoft   Visual   C++  Express 创建一个.DLL程序,并且也在OSX上利用XCode 4来创建了一个框架库。在这个例子中,我们将会从NitroLM里面一个名为getMessages()的函数开始分析。这个函数检索来自NitroLM的服务器上的由管理而生成的消息。例如,开发人员想在发布他们的软件产品的一段时间之后提示用户已有该软件的新版本。



AS3方面,你需要一个类来处理本机库的集合。对于NitroLM而言,这个类叫做LicenseClient。你可以在GitHub那里看到这个文件的所有内容,因为NitroLM库的AS3部分是开源的。



https://github.com/westbam/NitroLM/blob/master/nitrolm-air/src/com/nitrolm/LicenseClient.as

这个类的构造函数里面,我们创建了ExtensionContext的变量并且任意地调用了一个函数来开启本机代码的调试模式。

  1. public static var context:ExtensionContext;

  2. /**
  3. * Create a new LicenseClient instance

  4. * @param debug If enabled, the native library will write a file called nitrolm_debug.out
  5. */
  6. public function LicenseClient(debug:Boolean = false)
  7. {
  8.   if(!context)
  9.   {
  10.     context = ExtensionContext.createExtensionContext("com.nitrolm.NitroLM", "");
  11.   }
  12.   
  13.   if(context && debug)
  14.   {
  15.     context.call("enableDebug_air");
  16.   }
  17. }
复制代码

接下来,脚本方面,我们调用getMessages()函数来检索我们的消息列表。因为我们不想因为一个IO的服务请求而阻断我们的GUI线程,我们必须处理检索数据事件。我们对LicenseClientEvent进行侦听,因为在LicenseClientEvent返回时它的data属性里面含有大量的消息。

  1. public function getMessages(email:String, version:String, days:int):void
  2. {
  3.   context.addEventListener(StatusEvent.STATUS, handleStatusEvent);
  4.   context.call("getMessages_air", email, version, days);
  5. }
复制代码

你会注意到我们现在正在处理StatusEvent的另一种类型。这是我们在内部侦听的一类消息,这类消息可以让我们知道数据正准备被本机代码检索。当我们在handleStatusEvent()中处理这个消息的时候,我们可以到我们的本机库中再进行一次调用,目的是重新获取消息的数据。进行两次调用避免了我们的AIR GUI因等待本地代码的完成而遭到锁定的情况的发生。

  1. private function handleStatusEvent(event:StatusEvent):void
  2. {
  3.   context.removeEventListener(StatusEvent.STATUS, handleStatusEvent);
  4.   var req_type:int = new int(event.code);
  5.   var response:int = new int(event.level);
  6.   var lce:LicenseClientEvent = new LicenseClientEvent(LicenseClientEvent.LICENSE_RESPONSE, req_type, response);
  7.   
  8.   switch(req_type)
  9.   {
  10.     case NLMConstants.REQUEST_MESSAGES:
  11.       if(response == NLMConstants.RESPONSE_OK)
  12.       {
  13.         lce.data = context.call("getMessages_data");
  14.       }
  15.     break;
  16.   }
  17.   
  18.   dispatchEvent(lce);
  19. }
复制代码

进行两次调用的原因是我们同时使用了IO设备,如果我们不在本机为IO请求启动一个新的线程,那么AIR GUI将会在每次我们进行服务器请求的时候停滞。为了解决这个问题,进行第一次调用是为了将我们的AS3参数转换成本机数据类型并且启动一个新的线程来处理IO请求。我们的线程在收到NitroLM服务器的响应之后会立即发出一个StatusEvent事件。第二次调用是为了获取消息的数据,进而将本地响应的数据转换成我们可以使用的脚本类型。现在让我们看看本机代码吧。




我们首先必须在头文件中定义初始化模块,收尾模块以及数据函数。

  1. /*
  2. * AIR_COMMANDS.H
  3. * Native entry point for Adobe AIR
  4. *
  5. * Copyright 2011 Simplified Logic, Inc.  All Rights Reserved.
  6. * Author: Andrew Westberg
  7. */

  8. #ifndef __AIR_COMMANDS_H_
  9. #define __AIR_COMMANDS_H_

  10. #include "nlm_constants.h"
  11. #include "nlm_commands.h"
  12. #include "FlashRuntimeExtensions.h"

  13. #ifdef __cplusplus
  14. extern "C" {
  15. #endif

  16. ...
  17. FREObject getMessages_air(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
  18. FREObject getMessages_data(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);

  19. void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctions, const FRENamedFunction** functions);
  20. void ContextFinalizer(FREContext ctx);
  21. void ExtInitializer(void** extData, FREContextInitializer* ctxInitializer, FREContextFinalizer* ctxFinalizer);
  22. void ExtFinalizer(void* extData);

  23. #ifdef __cplusplus
  24. } //extern "C"
  25. #endif

  26. #endif //__AIR_COMMANDS_H_
复制代码

我们第一次创建拓展环境时调用初始化函数。我们需要告诉它哪些函数是已经定义了的。

  1. /*
  2. * AIR_COMMANDS.C
  3. * Exported functions for Adobe AIR (and related non-exported functions)
  4. *
  5. * Copyright 2011 Simplified Logic, Inc.  All Rights Reserved.
  6. *
  7. * Written by Andrew Westberg
  8. */

  9. #include <stdlib.h>
  10. #include "air_commands.h"
  11. #include "CAsyncTask.h"
  12. #include "storage.h"
  13. #include "Debug.h"

  14. #ifdef __cplusplus
  15. extern "C" {
  16. #endif

  17. static CThread asyncThread;
  18. static CAsyncTask asyncTask;

  19. void ExtInitializer(void** extData, FREContextInitializer* ctxInitializer, FREContextFinalizer* ctxFinalizer)
  20. {
  21.   *ctxInitializer = &ContextInitializer;
  22.   *ctxFinalizer = &ContextFinalizer;
  23. }

  24. void ExtFinalizer(void* extData)
  25. {
  26. }

  27. void ContextFinalizer(FREContext ctx)
  28. {
  29.   gracefulShutdown();
  30. }

  31. void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctions, const FRENamedFunction** functions)
  32. {
  33.   asyncThread.m_gracefulShutdown = FALSE; //terminate thread hard at shutdown because we're inside a .DLL

  34.   FRENamedFunction *func;

  35.   *numFunctions = 50;
  36.   
  37.   func = (FRENamedFunction*) malloc(sizeof(FRENamedFunction) * (*numFunctions));

  38.   ...
  39.   
  40.   func[24].name = (const uint8_t*) "getMessages_air";
  41.   func[24].functionData = NULL;
  42.   func[24].function = &getMessages_air;

  43.   func[46].name = (const uint8_t*) "getMessages_data";
  44.   func[46].functionData = NULL;
  45.   func[46].function = &getMessages_data;

  46.   ...
  47.   
  48.   *functions = func;
  49. }

  50. #ifdef __cplusplus
  51. } //extern "C"
  52. #endif
复制代码

在getMessages_air()函数中,我们分列出我们输入的参数并且将它们存入到我们的线程任务中,然后在线程中启动这个任务来处理这些数据。我正在使用的超线程库是由Walter Capers开发的,可以在CodeProject上的创建一个C++线程类这篇文章下面找到。

  1. FREObject getMessages_air(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
  2. {
  3.     while(asyncTask.Status() == TaskStatusWaitingOnQueue || asyncTask.Status() == TaskStatusBeingProcessed)
  4.     {
  5.         //wait until our task is ready to process something new
  6.         Sleep(10);
  7.     }

  8.     asyncTask.initialize(ctx, REQUEST_MESSAGES, argc, argv);

  9.     if(asyncThread.Event(&asyncTask))
  10.         Debug::logDebug("submitted getMessagesTask for background processing");
  11.     else
  12.         Debug::logDebug("ERROR submitting getMessagesTask for background processing");

  13.     return NULL;
  14. }
复制代码

asyncTask.initialize()方法从脚本的角度分列出我们输入的参数并且将它们存入本机对象类型,所以我们的线程在运行的时候可以用到它们。在启动线程之前执行这些代码很重要,因为你的线程没有权限从Adobe哪里访问到FRE 函数。你的线程唯一能从Adobe那里获得的功能是当线程结束的时候使用FREDispatchStatusEventAsync()函数发出一个StatusEvent的事件。你也将会发现你需要在每次FRE函数调用的时候进行很多的错误处理。

  1. void CAsyncTask::initialize(FREContext ctx, const int requestType, int argc, FREObject *argv)
  2. {
  3.   //reset the task
  4.   ThreadId_t id = 0;
  5.   SetTaskStatus(TaskStatusNotSubmitted);
  6.   Thread(&id);

  7.   CAsyncTask::ctx = ctx;
  8.   CAsyncTask::requestType = requestType;

  9.   uint32_t len = -1;
  10.   const uint8_t *email = 0;
  11.   const uint8_t *version = 0;
  12.   int32_t days = 0;
  13.   
  14.   switch(requestType) 
  15.   {
  16.     case REQUEST_MESSAGES:
  17.       if((res=FREGetObjectAsUTF8(argv[0], &len, &email)) != FRE_OK)
  18.       {
  19.         Debug::logDebug("ERROR: FREGetObjectAsUTF8(argv[0], &len, &email) = %d", res);
  20.         dispatchEvent(REQUEST_MESSAGES, RESPONSE_INTERNAL_CLIENT_ERROR);
  21.         return;
  22.       }
  23.       strcpy(CAsyncTask::email, (const char*)email);

  24.       if(argv[1] != NULL)
  25.       {
  26.         if((res=FREGetObjectAsUTF8(argv[1], &len, &version)) != FRE_OK)
  27.         {
  28.           Debug::logDebug("ERROR: FREGetObjectAsUTF8(argv[1], &len, &version) = %d", res);
  29.           dispatchEvent(REQUEST_MESSAGES, RESPONSE_INTERNAL_CLIENT_ERROR);
  30.           return;
  31.         }
  32.         strcpy(CAsyncTask::version, (const char*)version);
  33.       }
  34.       else
  35.         strcpy(CAsyncTask::version, "");

  36.       if((res=FREGetObjectAsInt32(argv[2], &days)) != FRE_OK)
  37.       {
  38.         Debug::logDebug("ERROR: FREGetObjectAsUTF8(argv[2], &days) = %d", res);
  39.         dispatchEvent(REQUEST_MESSAGES, RESPONSE_INTERNAL_CLIENT_ERROR);
  40.         return;
  41.       }
  42.       CAsyncTask::days = days;
  43.     break;
  44.   }
  45. }
复制代码

一旦CThread开始工作,则它会调用CAsyncTask的Task()方法。在这个函数里面,我们通过调用内部的getMessages()方法来获得发往NitroLM服务器上的同步IO请求,一旦这个过程结束,就会发出一个事件。

  1. void CAsyncTask::dispatchEvent(const int req_type, const int response)
  2. {
  3.   FREResult res;
  4.   char code[5];
  5.   char level[5];

  6.   sprintf(code, "%d", req_type);
  7.   sprintf(level, "%d", response);
  8.   if((res=FREDispatchStatusEventAsync(ctx, (const uint8_t*)code, (const uint8_t*)level)) != FRE_OK)
  9.   {
  10.     Debug::logDebug("ERROR: FREDispatchStatusEventAsync(ctx, (const uint8_t*)code, (const uint8_t*)level) = %d", res);
  11.     return;
  12.   }
  13. }

  14. BOOL CAsyncTask::Task()
  15. {
  16.   ThreadId_t id = 0;
  17.   Thread(&id);

  18.   int ret;

  19.   switch(CAsyncTask::requestType)
  20.   {
  21.     case REQUEST_MESSAGES:
  22.       outMsgs = NULL;
  23.       ret = getMessages(email, version, days, &outMsgs);
  24.       dispatchEvent(REQUEST_MESSAGES, ret);
  25.     break;
  26.   }

  27.   return TRUE;
  28. }
复制代码

最后,当AIR调用了getMessages_data()函数的时候我们把所有的数据封装成很多的对象。

  1. FREObject getMessages_data(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
  2. {
  3.   int i;
  4.   FREResult res;
  5.   FREObject result;
  6.   FREObject subject;
  7.   FREObject body;
  8.   FREObject message;
  9.   FREObject thrownException;
  10.   FREObject stackTrace;
  11.   const uint8_t *stack;
  12.   uint32_t len;

  13.   if(asyncTask.outMsgs != NULL)
  14.   {
  15.     if((res=FRENewObject((const uint8_t*)"Array", 0, NULL, &result, &thrownException)) != FRE_OK)
  16.     {
  17.       Debug::logDebug("ERROR: FRENewObject((const uint8_t*)\"Array\", 0, NULL, &result, &thrownException) = %d", res);
  18.       freeMessages(asyncTask.outMsgs);
  19.       FRECallObjectMethod(thrownException, (const uint8_t*)"getStackTrace", 0, NULL, &stackTrace, NULL);
  20.       FREGetObjectAsUTF8(stackTrace, &len, &stack);
  21.       Debug::logDebug((const char*)stack);
  22.       return NULL;
  23.     }

  24.     FRESetArrayLength(result, asyncTask.outMsgs->size);

  25.     for(i=0;i<asyncTask.outMsgs->size;i++)
  26.     {
  27.       subject = 0;
  28.       if(asyncTask.outMsgs->list[i].subject != NULL)
  29.       {
  30.         if((res=FRENewObjectFromUTF8(strlen(asyncTask.outMsgs->list[i].subject), (const uint8_t*)asyncTask.outMsgs->list[i].subject, &subject)) != FRE_OK)
  31.         {
  32.           Debug::logDebug("ERROR: FRENewObjectFromUTF8(strlen(asyncTask.outMsgs->list[i].subject), (const uint8_t*)asyncTask.outMsgs->list[i].subject, &subject) = %d", res);
  33.           freeMessages(asyncTask.outMsgs);
  34.           return NULL;
  35.         }
  36.       }
  37.       if((res=FRENewObjectFromUTF8(strlen(asyncTask.outMsgs->list[i].msg), (const uint8_t*)asyncTask.outMsgs->list[i].msg, &body)) != FRE_OK)
  38.       {
  39.         Debug::logDebug("ERROR: FRENewObjectFromUTF8(strlen(asyncTask.outMsgs->list[i].msg), (const uint8_t*)asyncTask.outMsgs->list[i].msg, &body) = %d", res);
  40.         freeMessages(asyncTask.outMsgs);
  41.         return NULL;
  42.       }

  43.       if((res=FRENewObject((const uint8_t*)"Object", 0, NULL, &message, &thrownException)) != FRE_OK)
  44.       {
  45.         Debug::logDebug("ERROR: FRENewObject((const uint8_t*)\"Object\", 0, NULL, &message, &thrownException) = %d", res);
  46.         freeMessages(asyncTask.outMsgs);
  47.         FRECallObjectMethod(thrownException, (const uint8_t*)"getStackTrace", 0, NULL, &stackTrace, NULL);
  48.         FREGetObjectAsUTF8(stackTrace, &len, &stack);
  49.         Debug::logDebug((const char*)stack);
  50.         return NULL;
  51.       }

  52.       if((res=FRESetObjectProperty(message, (const uint8_t*)"subject", subject, &thrownException)) != FRE_OK)
  53.       {
  54.         Debug::logDebug("ERROR: FRESetObjectProperty(message, (const uint8_t*)\"subject\", subject, &thrownException) = %d", res);
  55.         freeMessages(asyncTask.outMsgs);
  56.         FRECallObjectMethod(thrownException, (const uint8_t*)"getStackTrace", 0, NULL, &stackTrace, NULL);
  57.         FREGetObjectAsUTF8(stackTrace, &len, &stack);
  58.         Debug::logDebug((const char*)stack);
  59.         return NULL;
  60.       }
  61.       if((res=FRESetObjectProperty(message, (const uint8_t*)"body", body, &thrownException)) != FRE_OK)
  62.       {
  63.         Debug::logDebug("ERROR: FRESetObjectProperty(message, (const uint8_t*)\"body\", body, &thrownException) = %d", res);
  64.         freeMessages(asyncTask.outMsgs);
  65.         FRECallObjectMethod(thrownException, (const uint8_t*)"getStackTrace", 0, NULL, &stackTrace, NULL);
  66.         FREGetObjectAsUTF8(stackTrace, &len, &stack);
  67.         Debug::logDebug((const char*)stack);
  68.         return NULL;
  69.       }

  70.       if((res=FRESetArrayElementAt(result, i, message)) != FRE_OK)
  71.       {
  72.         Debug::logDebug("ERROR: FRESetArrayElementAt(result, i, message) = %d", res);
  73.         freeMessages(asyncTask.outMsgs);
  74.         return NULL;
  75.       }
  76.     }

  77.     freeMessages(asyncTask.outMsgs);
  78.     return result;
  79.   }

  80.   return NULL;
  81. }
复制代码

我希望,这些代码对你开始为Adobe AIR开发你自己的本机拓展会有帮助。你过你想知道更多有关NitroLM项目的信息,你可以浏览GitHub projects NitroLM documentation.

原文链接:http://bbs.9ria.com/viewthread.php?tid=107283

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要在 macOS 上开发 Go 用程序,您需要先安装 Go 编程语言和一些必要的工具。以下是一些步骤: 1. 安装 Go 编程语言:可以从官方网站 https://golang.org/dl/ 下载并安装最新版本的 Go。 2. 安装 Go 的包管理器 - Go Module:Go Modules 是一个包管理工具,可用于管理项目的依赖项。在终端中执行以下命令:`go env -w GO111MODULE=on`,然后在任何 Go 项目中执行`go mod init`以初始化模块。 3. 安装 Visual Studio Code 编辑器:建议使用 VS Code 作为 IDE 来编写和运行 Go 用程序。 4. 在 VS Code 中安装 Go 插件:打开 VS Code 编辑器,点击左侧的扩展图标,搜索 Go 并安装它。 5. 创建一个新项目:在终端中创建一个新目录,并在该目录中创建一个名为 `main.go` 的文件。 6. 在 main.go 文件中编写代码:可以使用以下代码作为示例: ```go package main import "fmt" func main() { fmt.Println("Hello, world!") } ``` 7. 在终端中运行用程序:在终端中切换到包含 main.go 文件的目录,并使用以下命令运行用程序: ```bash go run main.go ``` 这将编译并运行 main.go 文件中的代码,并输出 "Hello, world!"。 这只是入门指南,您可以继续深入了解 Go 语言和 macOS 用程序开发。 ### 回答2: 要在OSX开发用,可以使用Go语言和相关工具来进行开发。 首先,确保已经安装了Go语言的开发环境。可以从官方网站(https://golang.org/)下载并安装Go语言的二进制文件。安装完成后,可以通过运行“go version”命令来验证是否安装成功。 接下来,可以选择一个合适的集成开发环境(IDE)来进行开发。一些流行的Go语言IDE包括Visual Studio Code和GoLand等。这些IDE都有丰富的功能,能够提供代码自动完成、调试、版本控制等开发所需的功能。 然后,可以开始编写用的代码。使用Go语言的强大功能和标准库,可以轻松地构建出高效、可靠的用程序。可以使用Go语言的GUI库,如fyne或go-gtk等,来创建OSX用程序的图形用户界面。 在编写代码时,可以利用Go语言的包管理工具来管理项目的依赖关系。Go语言的包管理工具有go mod和dep等,可以帮助解决包依赖和版本管理的问题。 在开发完成后,可以使用Go语言的交叉编译功能,将用程序编译为OSX平台上的可执行文件。通过交叉编译,可以在不同的操作系统上构建用程序,以便在OSX上进行测试和部署。 最后,可以使用OSX上的图形用户界面工具(如Finder)或命令行工具,在OSX系统中安装和运行已编译的用程序。 总之,使用Go语言进行OSX用程序的开发是一种高效、可靠的方式。通过选择合适的工具和库,结合Go语言的强大功能,可以轻松地创建出优秀的用程序。 ### 回答3: 如果您想要开发用于OSX操作系统的用程序,可以使用Go语言作为开发工具。Go是一种简洁高效的编程语言,由谷歌公司开发,广泛用于构建可靠且具有高性能的用程序。 首先,您需要安装Go语言的开发环境。您可以从官方网站上下载并安装适用于OSX的Go编译器和工具链。安装完成后,您可以在终端中执行"go version"命令,以验证Go是否正确安装。 接下来,您可以选择使用一些流行的集成开发环境(IDE)来编写Go代码,例如GoLand、Visual Studio Code或者LiteIDE。这些IDE可以提供便捷的代码编辑和调试功能,以加快开发速度。 对于OSX开发,您可以使用Go的标准库和第三方库来构建功能丰富的用。标准库中提供了许多实用的模块,例如操作系统接口、网络通信和文件处理等。此外,Go语言拥有丰富的第三方库和框架,您可以借助这些库来简化开发过程,例如GUI库(如fyne或go-gtk)和数据库库(如gorm或xorm)等。 在编写代码时,您可以使用Go的并发和并行特性来提高用程序的性能。Go通过goroutine和channel机制,使并发编程变得简单而高效。您可以将耗时的操作(如网络请求或文件读写)放入单独的goroutine中,并通过channel进行通信和同步。 最后,当您完成用的开发后,可以使用Go提供的交叉编译功能,将用程序编译为适用于OSX平台的可执行文件。此外,您还可以使用一些打包工具来创建用程序的安装包,以方便用户进行安装和使用。 总之,使用Go语言开发OSX用程序可以使您的开发过程更简单和高效。Go的简洁性、高性能和并发特性使其成为构建现代化用程序的理想选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值