【问题标题】:Can't get NaCl C++ module to load file from within packaged app无法让 NaCl C++ 模块从打包的应用程序中加载文件
【发布时间】:2014-05-12 22:07:07
【问题描述】:

我有一个 chrome 打包的应用程序,其中还包括一个 PNaCl/NaCl C++ 模块,以及 NaCl 模块需要读入的一些数据文件。但是,我无法让它读入文件。

我根据我能找到的所有文档和官方示例进行设置,以及回复:How to include a data file in a chrome app for a native client module to read

SDK 附带的 nacl_io 演示可以做到这一点,但它是用 C 语言编写的,而不是 C++。

我想出了一个简单的例子,我将在下面发布。当您按下页面上的按钮时,NaCl 模块应该加载 test.txt 的第一个字符并显示它。到目前为止,它总是只响应“-100”(我输入的错误值),这意味着它无法打开文件,而不是文件的第一个字符。

任何人都可以提出一些更改以使其正常工作并加载文件吗?

为了运行它,至少在 Mac 上,我使用这个命令,所有文件都在 ./file-test 目录中: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --load-and-launch-app=./file-test

请注意,如果您尝试使用它,您很可能需要更改 makefile 中的 NACL_SDK_ROOT 路径。

file_test.cc

#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"

#include "nacl_io/nacl_io.h"
#include "sys/mount.h"

class FileTestInstance : public pp::Instance {
 public:
  explicit FileTestInstance(PP_Instance instance) : pp::Instance(instance)
  {
    // initialize nacl file system
    nacl_io_init_ppapi(instance, pp::Module::Get()->get_browser_interface());

    // mount the http root at /http
    mount("", "/http", "httpfs", 0, "");
  }
  virtual ~FileTestInstance() {}

  // Receive message from javascript
  virtual void HandleMessage(const pp::Var& var_message) {
    // Open and load from the file  
    int c;
    FILE *file;
    file = fopen("/http/test.txt", "r");
    if (file) {
        c = getc(file);
        fclose(file);
    } else {
        c = -100;
    }

    // Send message to JavaScript
    pp::Var var_reply(c);
    PostMessage(var_reply);
  }
};

class FileTestModule : public pp::Module {
 public:
  FileTestModule() : pp::Module() {}
  virtual ~FileTestModule() {}

  virtual pp::Instance* CreateInstance(PP_Instance instance) {
    return new FileTestInstance(instance);
  }
};

namespace pp {
Module* CreateModule() {
  return new FileTestModule();
}
}  // namespace pp

index.html

<!DOCTYPE html>
<html>
<head>
  <title>File Test</title>
 <script type="text/javascript" src="script.js"></script>
</head>
<body>

  <h1>File Test</h1>

  <input type="button" id="test" name="test" value="Test" />

  <p><b>Output:</b><p>
  <div id="output">
  </div>

  <p>
    <div id="listener">
      <embed id="file_test" width=0 height=0 src="file_test.nmf" type="application/x-pnacl" />
    </div>
  </p>
</body>
</html>

script.js

// outgoing messages
function postMessage(message) {
 var nacl_module = document.getElementById('file_test')
 nacl_module.postMessage(message);
}

// incoming messages
function handleMessage(message_event) {
  var outputDiv = document.getElementById('output');
  outputDiv.textContent = message_event.data;
}

// button action
function buttonClicked() {
    postMessage("file");
}

// set up
function init() {
    // add listener to nacl module
    var listener = document.getElementById('listener');
    listener.addEventListener('message', handleMessage, true);

    // add action to button
    document.getElementById("test").onclick = buttonClicked;
}

window.onload = init;

main.js

/**
 * Listens for the app launching then creates the window
 */
chrome.app.runtime.onLaunched.addListener(function() {
  // Center window on screen.
  var screenWidth = screen.availWidth;
  var screenHeight = screen.availHeight;
  var width = 600;
  var height = 600;

  chrome.app.window.create('index.html', {
    id: "File-TestID",
    bounds: {
      width: width,
      height: height,
      left: Math.round((screenWidth-width)/2),
      top: Math.round((screenHeight-height)/2)
    }
  });
});

file_test.nmf

{
  "program": {
    "portable": {
      "pnacl-translate": {
        "url": "file_test.pexe"
      }
    }
  }
}

生成文件

#
# Get pepper directory for toolchain and includes.
#
# If NACL_SDK_ROOT is not set, then assume where it can be found.
#
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
NACL_SDK_ROOT ?= $(abspath $(dir $(THIS_MAKEFILE))../../nacl_sdk/pepper_33)

# Project Build flags
WARNINGS := -Wno-long-long -Wall -Wswitch-enum -pedantic -Werror
CXXFLAGS := -pthread -std=gnu++98 $(WARNINGS)

#
# Compute tool paths
#
GETOS := python $(NACL_SDK_ROOT)/tools/getos.py
OSHELPERS = python $(NACL_SDK_ROOT)/tools/oshelpers.py
OSNAME := $(shell $(GETOS))
RM := $(OSHELPERS) rm

PNACL_TC_PATH := $(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_pnacl)
PNACL_CXX := $(PNACL_TC_PATH)/bin/pnacl-clang++
PNACL_FINALIZE := $(PNACL_TC_PATH)/bin/pnacl-finalize
CXXFLAGS := -I$(NACL_SDK_ROOT)/include -I$(NACL_SDK_ROOT)/include/pnacl
LDFLAGS := -L$(NACL_SDK_ROOT)/lib/pnacl/Release -lppapi_cpp -lppapi -lnacl_io

#
# Disable DOS PATH warning when using Cygwin based tools Windows
#
CYGWIN ?= nodosfilewarning
export CYGWIN


# Declare the ALL target first, to make the 'all' target the default build
all: file_test.pexe

clean:
    $(RM) file_test.pexe file_test.bc

file_test.bc: file_test.cc
    $(PNACL_CXX) -o $@ $< -O2 $(CXXFLAGS) $(LDFLAGS)

file_test.pexe: file_test.bc
    $(PNACL_FINALIZE) -o $@ $<

test.txt

AAAA

【问题讨论】:

  • 为了使您的代码更像其他答案中的示例,您是否尝试 mount ("/"...) ?
  • 我也尝试了 mount ("/"...),但也没有用,但是在 SDK 附带的 nacl_io_demo.c 文件中,他们使用 mount("",. ..),所以我认为这样做是有意义的。
  • 好。你说它返回“0”。你确定吗?还是“\0”(空)?您是否通过发送成功案例的硬编码值来确认消息传递管道正常工作?
  • 我将错误值更改为 -100,现在它返回该值,以便确认错误状态和消息传递功能。我编辑了上面的代码和问题以反映这些变化。

标签: google-chrome-app google-nativeclient


【解决方案1】:

来自本地客户讨论列表上的 Sam Clegg:

“我认为你遇到的主要问题是你试图在主线程上使用 nacl_io。nacl_io 就像它主要基于的阻塞 PPAPI 接口一样,只能在允许阻塞调用的后台线程上工作。看: https://developer.chrome.com/native-client/devguide/coding/nacl_io。”

“尝试在单独的线程上运行您的代码。一种简单的方法是使用 ppapi_simple 库。”

使用这个建议,并查看 SDK 中包含的 using_ppapi_simple、flock 和 earth 示例,我能够制作一个工作版本:

file_test.cc

#include <stdio.h>
#include "sys/mount.h"

#include <ppapi/cpp/var.h>
#include "ppapi_simple/ps_main.h"
#include "ppapi_simple/ps_event.h"
#include "ppapi_simple/ps_interface.h"


int file_test_main(int argc, char* argv[]) {
    PSEventSetFilter(PSE_ALL);

    // mount the http root at /http
    mount("", "/http", "httpfs", 0, "");

    while (true) {
        PSEvent* ps_event;
        // Consume all available events
        while ((ps_event = PSEventWaitAcquire()) != NULL) {
            // handle messages from javascript
            if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) {
                // Convert Pepper Simple message to PPAPI C++ vars
                pp::Var var_message(ps_event->as_var);
                // process the message if it is a string
                if (var_message.is_string()) {
                    // get the string message
                    std::string message = var_message.AsString();

                    // handle message
                    if (message == "file") {
                        // Open and load from the file  
                        int c;
                        FILE *file;
                        file = fopen("/http/test.txt", "r");
                        if (file) {
                            c = getc(file);
                            fclose(file);
                        } else {
                            c = -100;
                        }

                        // Send response back to JavaScript
                        pp::Var var_reply(c);
                        PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), var_reply.pp_var());
                    }
                }
            }

            PSEventRelease(ps_event);
        }
    }

    return 0;
}

/*
 * Register the function to call once the Instance Object is initialized.
 * see: pappi_simple/ps_main.h
 */
PPAPI_SIMPLE_REGISTER_MAIN(file_test_main)

另外,需要在Makefile的LDFLAGS中加入-lppapi_simple。

也可以自己处理线程,而不是使用 ppapi_simple,这可以在 SDK 中包含的 nacl_io_demo 中看到。

【讨论】:

  • 在该 URL 的“使用 nacl_io”下,第 5 点:“确保文件和套接字 API 调用均来自后台线程。这是因为主 Pepper 线程不支持POSIX I/O 操作所需的阻塞行为。”
  • 这似乎是一个容易检测并报告给开发人员的案例。嘿,stackoverflow.com/users/1420383/sbc,如果在主线程上调用它们,你能把这些方法改成壮观的死吗?
  • 进一步来自 native-client-discuss 列表:问题:“如果我要使用 ppapi_simple 库,我将如何获得 nacl_io_init_ppapi 的适当参数?” Ben Smith 的回答:“ppapi_simple 为你设置了 nacl_io。当你的 main 函数被调用时,它已经被初始化了。......你可以挂载文件系统并直接调用 fopen/fread/etc。它会起作用。”
  • Ben Smith 的另一条注释:“您可能希望将 ... PSEventTryAcquire()) ... 更改为 ... PSEventWaitAcquire()) ... 否则 NaCl 模块将自旋循环等待用于事件。当您想要在等待事件(例如渲染)时执行其他操作时,PSEventTryAcquire 非常有用。 (我相应地编辑了上面的代码。)
猜你喜欢
  • 1970-01-01
  • 2012-12-03
  • 1970-01-01
  • 2022-01-22
  • 2021-05-06
  • 2016-10-23
  • 1970-01-01
  • 1970-01-01
  • 2019-11-24
相关资源
最近更新 更多