Trouble in ActiveX multi-thread invoke javascript callback routine - javascript

everyone.
I'm get some trouble in ActiveX programming with ATL. I try to make a activex which can async-download files from http server to local folder and after download it will invoke javascript callback function.
My solution: run a thread M to monitor download thread D, when D is finish the job, M is going to terminal themself and invoke IDispatch inferface to call javascript function.
**************** THERE IS MY CODE: ****************
/* javascript code */
funciton download() {
var xfm = new ActiveXObject("XFileMngr.FileManager.1");
xfm.download(
'http://somedomain/somefile','localdev:\\folder\localfile',function(msg){alert(msg);});
}
/* C++ code */
// main routine
STDMETHODIMP CFileManager::download(BSTR url, BSTR local, VARIANT scriptCallback)
{
CString csURL(url);
CString csLocal(local);
CAsyncDownload download;
download.Download(this, csURL, csLocal, scriptCallback);
return S_OK;
}
// parts of CAsyncDownload.h
typedef struct tagThreadData {
CAsyncDownload* pThis;
} THREAD_DATA, *LPTHREAD_DATA;
class CAsyncDownload :
public IBindStatusCallback
{
private:
LPUNKNOWN pcaller;
CString csRemoteFile;
CString csLocalFile;
CComPtr<IDispatch> spCallback;
public:
void onDone(HRESULT hr);
HRESULT Download(LPUNKNOWN caller, CString& csRemote, CString& csLocal, VARIANT callback);
static DWORD __stdcall ThreadProc(void* param);
};
// parts of CAsyncDownload.cpp
void CAsyncDownload::onDone(HRESULT hr) {
if(spCallback) {
TRACE(TEXT("invoke callback function\n"));
CComVariant vParams[1];
vParams[0] = "callback is working!";
DISPPARAMS params = { vParams, NULL, 1, 0 };
HRESULT hr = spCallback->Invoke(0,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&params, NULL, NULL, NULL);
if(FAILED(hr)) {
CString csBuffer;
csBuffer.Format(TEXT("invoke failed, result value: %d \n"),hr);
TRACE(csBuffer);
}else {
TRACE(TEXT("invoke was successful\n"));
}
}
}
HRESULT CAsyncDownload::Download(LPUNKNOWN caller, CString& csRemote, CString& csLocal, VARIANT callback) {
CoInitializeEx(NULL, COINIT_MULTITHREADED);
csRemoteFile = csRemote;
csLocalFile = csLocal;
pcaller = caller;
switch(callback.vt){
case VT_DISPATCH:
case VT_VARIANT:{
spCallback = callback.pdispVal;
}
break;
default:{
spCallback = NULL;
}
}
LPTHREAD_DATA pData = new THREAD_DATA;
pData->pThis = this;
// create monitor thread M
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, (void*)(pData), 0, NULL);
if(!hThread) {
delete pData;
return HRESULT_FROM_WIN32(GetLastError());
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CoUninitialize();
return S_OK;
}
DWORD __stdcall CAsyncDownload::ThreadProc(void* param) {
LPTHREAD_DATA pData = (LPTHREAD_DATA)param;
// here, we will create http download thread D
// when download job is finish, call onDone method;
pData->pThis->onDone(S_OK);
delete pData;
return 0;
}
**************** CODE FINISH ****************
OK, above is parts of my source code, if I call onDone method in sub-thread,
I will get OLE ERROR(-2147418113 (8000FFFF) Catastrophic failure.).
Did I miss something? please help me to figure it out.

IE's JavaScript engine is single-threaded and so is ATL's event raising code. Have the subthread post a message to the thread in which the ActiveX is created (e.g. to the handle of the ActiceX window if there is one), then raise the event.

Related

Emitting a Node.js Event from a C++ callback function

In a Node.js application, I need to be notified when the default audio device has been changed. This program will be used on Windows 7.
Currently I am trying to do this by making a C++ add-on for node that, through the IMMNotificationClient::OnDefaultDeviceChanged method from the Windows Core Audio API, emits an event that can be received by a Node event emitter.
Here's an example of how the Windows Core Audio API callback method would look:
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceID)
{
//User written code that emits a node Event.
}
Here is some ideal c++ style pseudo code of what I would love to have in the above callback function:
EventEmitter.emit(v8::String::NewFromUtf8("defaultDeviceChanged"), deviceName);
I'm not sure how to make this happen though, so here is my question: How do I emit an event to a Node.js application through a C++ callback function?
I'm open to other solutions as long as they can be run natively on Node (IE through an add-on), and will work on Windows 7, this means external calls to applications like nircmd are off the table.
EDIT
This is some experimental code to try and help describe what I am doing:
AudioDeviceEmitter.h:
#pragma once
#include <stdio.h>
#include <wchar.h>
#include <tchar.h>
#include <time.h>
#include "windows.h"
#include "Mmdeviceapi.h"
#include "Propidl.h"
#include "Functiondiscoverykeys_devpkey.h"
#include <vector>
#include <string>
#include <comdef.h>
#define NAPI_DISABLE_CPP_EXCEPTIONS
#include <napi.h>
#include <vector>
class AudioDeviceEmitter : public Napi::ObjectWrap<AudioDeviceEmitter>, public IMMNotificationClient {
public:
static Napi::Object Init(Napi::Env env, Napi::Object exports);
AudioDeviceEmitter(const Napi::CallbackInfo& info);
Napi::Value AudioDeviceEmitter::enrollInNotifications(const Napi::CallbackInfo& info);
Napi::Value AudioDeviceEmitter::unenrollInNotifications(const Napi::CallbackInfo& info);
//WIN API
~AudioDeviceEmitter()
{
if (_pEnumerator != NULL) {
_pEnumerator->UnregisterEndpointNotificationCallback(this);
_pEnumerator->Release();
}
}
// IUnknown methods -- AddRef, Release, and QueryInterface
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef)
{
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid, VOID **ppvInterface)
{
if (IID_IUnknown == riid)
{
AddRef();
*ppvInterface = (IUnknown*)this;
}
else if (__uuidof(IMMNotificationClient) == riid)
{
AddRef();
*ppvInterface = (IMMNotificationClient*)this;
}
else
{
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
// Callback methods for device-event notifications.
//------------THIS IS A FUNCTION I AM TRYING TO GET TO WORK----------
//------------It doesnt currently work, but hopefully shows----------
//------------ what I'm trying to accomplish ----------
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
EDataFlow flow, ERole role,
LPCWSTR pwstrDeviceId)
{
if (flow == eRender) {
_locked = true;
std::string name = "";
IMMDevice * pDevice = NULL;
HRESULT hr = _pEnumerator->GetDevice(pwstrDeviceId, &pDevice);
if(SUCCEEDED(hr)){
name = getFriendlyNameString(pDevice);
pDevice->Release();
}
for(int i = 0; i < _stillEnrolled.size(); i++) {
if(_stillEnrolled.at(i)) {
Napi::CallbackInfo & info = _enrolledSessions.at(i);
Napi::Env env = info.Env();
Napi::Function emit = info.This().As<Napi::Object>()
.Get("emit").As<Napi::Function>();
emit.Call(info.This(), { Napi::String::New(env, "defaultDeviceChanged"),
Napi::String::New(env, name.c_str())});
}
}
_locked = false;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
{
return S_OK;
};
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
LPCWSTR pwstrDeviceId,
DWORD dwNewState)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
LPCWSTR pwstrDeviceId,
const PROPERTYKEY key)
{
return S_OK;
}
private:
std::string getFriendlyNameString(IMMDevice *pDevice) {
HRESULT hr;
IPropertyStore *pStore;
hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
PROPVARIANT variant;
PropVariantInit(&variant);
hr = pStore->GetValue(PKEY_Device_FriendlyName, &variant);
size_t strlen = wcslen((wchar_t *)variant.pwszVal);
int throwAwaylen;
char *pOutBuffer = (char *)malloc(strlen);
wcstombs_s((size_t *)&throwAwaylen, pOutBuffer, size, wideCharArray, size);
std::string toReturn = pOutBuffer;
free(pOutBuffer);
PropVariantClear(&variant);
pStore->Release();
return toReturn;
}
LONG _cRef;
IMMDeviceEnumerator *_pEnumerator;
static Napi::FunctionReference constructor;
std::vector<Napi::CallbackInfo> _enrolledSessions;
std::vector<bool> _stillEnrolled;
bool _locked;
};
AudioDeviceEmitter.cpp
#include "AudioDeviceEmitter.h"
Napi::FunctionReference AudioDeviceEmitter::constructor;
Napi::Object AudioDeviceEmitter::Init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
Napi::Function func = DefineClass(env, "AudioDeviceEmitter", {
InstanceMethod("enrollInNotifications", &AudioDeviceEmitter::enrollInNotifications),
InstanceMethod("unenrollInNotifications", &AudioDeviceEmitter::unenrollInNotifications)
});
constructor = Napi::Persistent(func);
constructor.SuppressDestruct();
exports.Set("AudioDeviceEmitter", func);
return exports;
}
AudioDeviceEmitter::AudioDeviceEmitter(const Napi::CallbackInfo& info)
: Napi::ObjectWrap<AudioDeviceEmitter>(info), _locked(false), _cRef(1),
_pEnumerator(NULL)
{
HRESULT hr = CoInitialize(NULL);
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&_pEnumerator);
_pEnumerator->RegisterEndpointNotificationCallback(this);
}
//------------THIS IS A FUNCTION I AM TRYING TO GET TO WORK----------
//------------It doesnt currently work, but hopefully shows----------
//------------ what I'm trying to accomplish ----------
Napi::Value AudioDeviceEmitter::enrollInNotifications(const Napi::CallbackInfo& info) {
while(_locked){}
_locked = true;
int currentPos = _enrolledSessions.size();
_enrolledSessions.push_back(info);
_stillEnrolled.push_back(true);
_locked = false;
while(_stillEnrolled.at(currentPos)){}
return Napi::String::New(info.Env(), "OK");
}
//------------THIS IS A FUNCTION I AM TRYING TO GET TO WORK----------
//------------It doesnt currently work, but hopefully shows----------
//------------ what I'm trying to accomplish ----------
Napi::Value AudioDeviceEmitter::unenrollInNotifications(const Napi::CallbackInfo& info) {
while(_locked){}
for(int i = 0; i < _enrolledSessions.size(); i++) {
if (info.This() == _enrolledSessions.at(i).This()) {
_stillEnrolled.at(i) = false;
}
}
return Napi::String::New(info.Env(), "OK");
}
binding.cpp:
#include <napi.h>
#include "AudioDeviceEmitter.h"
Napi::Object InitNAPI(Napi::Env env, Napi::Object exports) {
AudioDeviceEmitter::Init(env, exports);
return exports;
}
NODE_API_MODULE(WinDefaultAudioDevice, InitNAPI)
This code throws errors and doesn't compile, it is here to hopefully explain my problem more fully.
Implement one of Nan's AsyncWorker classes. For some example code on how to do so, look at this answer. after implementing the worker, assign a callback function to the async worker.

Make methods execute synchronously

Good day, I am new to JavaScript/TypeScript and looking for generic solution to indicate that ajax callback has completed so that other code that depends on the outcome of callback can execute.
For example class Fee Calculator, it gets instantiated with a service type and method calculate fee is called in order to display a fee of the service for the customer to agree.
class FeeCalculator {
private cost: number = 0;
private serviceType: number = 0;
private jsonAjax: JsonAjax = null;
constructor(serviceType: number) {
this.serviceType = serviceType;
this.jsonAjax = new JsonAjax("/tmaMaster/JSONServlet");
}
public calculateFee(): void {
// First, get the cost of the service from DB
this.cost = this.getServiceCost();
// calculate the fee and display it
// Now, this code would still be executed even if the callback
// didn't complete and therefore this.cost = 0,
// fee will be calculated wrongly
var fee: number = this.calculateTheFee(cost);
alert(fee);
}
getServiceCost = () => {
// IntegerWrapper is not relevant to this problem - just a primitive wrapper
var cost: IntegerWrapper = new IntegerWrapper();
this.execute("getServiceCost", this.serviceType, cost, this.setServiceCost);
}
// This is the callback
setServiceCost = (cost: IntegerWrapper) => {
this.cost = IntegerWrapper.primitiveValue;
}
private getFeeForTheCost(cost: number): number {
return cost / 4;
}
execute(command: string, requestDto: any, responseDto: any, callback: (responseDto: any) => void) {
var commandObject: JsonCommand = new JsonCommand(command, requestDto);
this.jsonAjax.call(commandObject, responseDto, callback);
}
}
Will also include JsonAjax class:
import JsonExternalizable = require("./JsonExternalizable");
class JsonAjax {
private READY_STATUS_CODE = 4;
private url: string;
constructor(url: string) {
this.url = url;
}
private isCompleted(request: XMLHttpRequest) {
return request.readyState === this.READY_STATUS_CODE;
}
call(requestDto: any, responseDto: any, callback: (responseDto: JsonExternalizable) => any) {
// Create a request
var request = new XMLHttpRequest();
// Attach an event listener
request.onreadystatechange = () => {
var completed = this.isCompleted(request);
if (completed && callback != null) {
/*
* N.B. PLease note that readFromJsonString returns void, therefore below line must be called sperately from line below.
*/
responseDto.readFromJsonString(request.responseText);
callback(responseDto);
}
};
// Specify the HTTP verb and URL
request.open('POST', this.url, true);
// Send the request
request.send(JSON.stringify(requestDto));
}
toString() {
return "JsonAjax";
}
}
export = JsonAjax;
The question is how do I make sure that calculateTheFee strictly called after getServiceCost is complete in other words make functions execute synchronously?
Ways I know of:
Do all other steps that require the cost value in a callback
itself. Not keen on that solution (what if there are more values
that need to be extracted from DB and all of them needed for
calculation) readability of that code will decrease.
Use timeout (Not generic enough, as we don't know how big is what we querying and how long it needs to "sleep")
P.S. Preferably without using extra libraries and plugins (company requirement)

Error Calling method on NPObject - Firefox Plugin

I am having a piece of visual C++ code (firebreath) that retrieves data from a visual C# application. The communication works fine for negative cases(returns value without out parameter) but in positive case(returns value with out parameter) the following error is shown.
Error: Error calling method on NPObject!
I guess the problem is in the out parameter of Visual C# application. Can anyone help me in this?? Kindly use the below code for reference.
ngContent.js: (js to call firebreath function)
function GetDetails(param1, param2) {
try {
return document.getElementById("nGCall").ReturnDetails(param1, param2);
}
catch (e) {
alert("Exception Occured " + e.message);
}
};
nGAPI.cpp: (Firebreath function to call C# application)
FB::VariantList nGAPI::ReturnDetails(std::wstring& param1, std::wstring& param2)
{
try {
InetGuardNPAPI *CSharpInterface = NULL;
//Open interface to C#
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(CLSID_netGuardIEBHO, NULL, CLSCTX_INPROC_SERVER, IID_InetGuardNPAPI, reinterpret_cast<void**>(&CSharpInterface));
BSTR out1= NULL;
BSTR out2= NULL;
BSTR out3= NULL;
BSTR out4= NULL;
int returns = CSharpInterface->NPGetDetails(param1.c_str(), param2.c_str(), &out1, &out2, &out3, &out4);
if (out1 != NULL && out2 != NULL) {
return FB::variant_list_of(out1)(out2)(out3)(out4);
} else {
return FB::variant_list_of();
}
} catch (...) {
MessageBoxW(NULL, L"Exception occured.", L"NG", NULL);
return FB::variant_list_of();
}
nGHost.cs: (Visual C# application)
public int NPGetDetails(string param1, string param2, out string out1, out string out2, out string out3, out string out4)
{
int retValue = 0;
out1 = null;
out2 = null;
out3 = null;
out4 = null;
bool userIdentified = IdentifyUser(ngDB, out ngdata);
if (!userIdentified)
return retValue;
try {
out1 = ngdata.abc;
out2 = ngdata.def;
out3 = ngdata.ghi;
out4 = ngdata.jkl;
retValue = 1;
} catch (Exception ex) {
MessageBox.Show(ex.Message);
}
return retValue;
}
Thanks in advance.
The error you're getting indicates that an exception was thrown. Unfortunately the browsers stopped exposing exceptions when they switched to out of process plugins. I'd recommend attaching a debugger and stepping through to see what is actually happening; alternately, add some logging.

Creating custom Cordova plugin for NfcV Tags

I need to read the Data off NfcV (ISO 15693) Tags, I already tried the Phonegap-Nfc Plugin from Chariotsolution, but it seems this plugin can only read the TagId and Type off NfcV tags, so i decided i could create my own custom plugin, I first foud the echo Hello World Plugin tutorial which worked fine and without problems, so I looked for Java NfcV reader and found this Java NfcV Reader.
From what I understood so far is, that I need to call the cordova.exec function and extend my Java class from CordovaPlugin, which is working.
I don't know Java since I am a Webdeveloper, which makes it a little hard for me.
This actually looks pretty good but i tried to implement it in the way of the Hello World echo example which didnt work out as planned.
I have now a plugin folder with
Hello.java
package org.apache.cordova.plugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
/**
* This class echoes a string called from JavaScript.
*/
public class Hello extends CordovaPlugin
{
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("hello")) {
String message = args.getString(0);
this.hello(message, callbackContext);
return true;
}
return false;
}
private void hello(String message, CallbackContext callbackContext) {
if (message != null && message.length() > 0) {
callbackContext.success(message);
} else {
callbackContext.error("Expected one non-empty string argument.");
}
}
}
ReadNfcV.java from the link above
package org.apache.cordova.plugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import org.apache.http.util.ByteArrayBuffer;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.nfc.tech.NfcV;
import android.nfc.tech.TagTechnology;
//import android.os.Parcelable;
import android.util.Log;
/**
* #author uhahn
*
*/
public class ReadNfcV extends CordovaPlugin implements TagTechnology {
protected NfcV mynfcv;
// protected Tag mytag; // can be retrieved through mynfcv
private final String TAG=this.getClass().getName();
protected final int maxtries=3;
protected boolean isTainted=true; // Tag info already read?
protected byte[] mysysinfo=null; // NfcV SystemInformation - or generated
protected byte[] myuserdata=null; // buffer user content
protected boolean[] blocktainted; // true when block is to be uploaded to tag
protected byte[] blocklocked; // 0 means writable
protected byte afi=0;
public byte nBlocks=0;
public byte blocksize=0;
public byte[] Id;
public byte[] UID; // becomes valid when a real tag is contacted
public byte DSFID = -1;
public int maxtrans=0; // tag dependent max transceive length
public byte lastErrorFlags=-1; // re-set by each transceive
public byte lastErrorCode=-1; // re-set by each transceive
public byte manuByte=0;
public static final byte BYTE_IDSTART=(byte)0xe0;
public static final byte MANU_TAGSYS=0x04;
public static final HashMap<Byte,String> manuMap = new HashMap<Byte, String>();
static{
manuMap.put(MANU_TAGSYS, "TagSys");
}
/**
* read new NfcV Tag from NFC device
*/
public ReadNfcV(Tag t) {
UID = t.getId(); // sysinfo holds the UID in lsb order - Id will be filled lateron from sysinfo!
// Log.d(TAG,"getId: "+toHex(t.getId()));
mynfcv=NfcV.get(t);
try {
mynfcv.connect();
mysysinfo=getSystemInformation();
// explore Nfcv properties..
//initfields(); // done by getSys..
maxtrans=mynfcv.getMaxTransceiveLength();
DSFID=mynfcv.getDsfId();
Log.d(TAG,nBlocks + " x " + blocksize + " bytes");
blocklocked=new byte[nBlocks]; // init the lock shadow
getMultiSecStatus(0, nBlocks); // and fill from tag
blocktainted=new boolean[nBlocks];
taintblock(0,nBlocks);
// Log.d(TAG,"maxtrans "+maxtrans);
// init space for userdata ?
myuserdata= new byte[nBlocks*blocksize];
} catch (IOException e) {
// TODO Auto-generated catch block
lastErrorFlags=-1;
Log.d(TAG, "MyNfcV failed: "+e.getMessage());
e.printStackTrace();
}
}
/**
* recreate NfcV Tag from log
* #param sysinfo: the logged system info only
*/
public ReadNfcV(String sysinfo){
int startat=0;
sysinfo.toLowerCase(); // ignore case
if(sysinfo.startsWith("0x")){ // lets believe in HEX
startat=2;
}
mysysinfo=hexStringToByteArray(sysinfo.substring(startat));
initfields();
// init space for userdata TODO limit size?
//myuserdata= new byte[nBlocks*blocksize];
isTainted=false;
// TODO fake Tag? mytag = Tag.CREATOR.createFromParcel(???);
}
/**
* recreate NfcV Tag from log
* #param sysinfo: the logged system info
* #param userdata: the logged userdata
*/
public ReadNfcV(String sysinfo, String userdata){
this(sysinfo);
// TODO fake userdata
int startat=0;
userdata.toLowerCase(); // ignore case
if(userdata.startsWith("0x")){ // lets believe in HEX
startat=2;
}
myuserdata=hexStringToByteArray(userdata.substring(startat));
}
/**
* parse system information byte array into attributes
* with respect to the flags found
* DSFID
* AFI
* memsize values (block count and length)
*/
private void initfields(){
byte[] read=mysysinfo;
if((null!=read)&&(12<read.length)&&(0==read[0])){// no error
char flags=(char)read[1]; //s.charAt(1);
// String s=new String(read);
//s.substring(2, 9).compareTo(Id.toString()) // the same?
//set the Id from mysysinfo
int pos=2;
boolean forwardId=false; // the Id field is in lsb order
if(BYTE_IDSTART==read[pos]){
forwardId=true;
manuByte=read[pos+1];
}else if(BYTE_IDSTART==read[pos+7]){
manuByte=read[pos+6];
forwardId=false;
}else
Log.e(TAG,"Id start byte not found where expected");
if(null==Id){ // dont overwrite, if given
Id=new byte[8];
for(int i=0;i<8;i++)
// TODO decide if Id to be reversed (Zebra needs msb order, that is Id[7] changes between tags)
Id[i]=(forwardId? read[pos+i] : read[pos + 7 - i]); //reverse?!
Log.d(TAG,"Id from sysinfo (reversed): "+toHex(Id));
}
pos=10; // start after flags, Infoflags and Id TODO: change if transceive should eat up the error byte
if(0<(flags&0x1)){ // DSFID valid
pos++; // already implemented
}
if(0<(flags&0x2)){ // AFI valid
afi=(byte)read[pos++];//s.charAt(pos++);
}
if(0<(flags&0x4)){ // memsize valid
nBlocks=(byte)(read[pos++]+1);//(s.charAt(pos++)+1);
blocksize=(byte)(read[pos++]+1); //((s.charAt(pos++)&0x1f)+1);
}
}
}
/**
* #return the stored afi byte
*/
public byte getAFI(){
if(isTainted){ // system info not read yet
getSystemInformation(); // fill in the fields
}
return afi;
}
public byte getDsfId(){
// return mynfcv.getDsfId(); // avoid re-reading
return DSFID;
}
public int getblocksize(){
return (int)blocksize;
}
public int getnBlocks(){
return (int)nBlocks;
}
public byte[] getSystemInformation(){
if(isTainted){ // dont reread
mysysinfo=transceive((byte)0x2b);
isTainted=false; // remember: we have read it and found it valid
if(0==lastErrorFlags){// no error
isTainted=false; // remember: we have read it and found it valid
initfields(); // analyze
}}
return mysysinfo;
}
/**
* overload method transceive
* #return resulting array (or error?)
*/
protected byte[] transceive(byte cmd){
return transceive(cmd, -1, -1, null);
}
protected byte[] transceive(byte cmd, int m){
return transceive(cmd, m, -1, null);
}
protected byte[] transceive(byte cmd, int m ,int n){
return transceive(cmd, m, n, null);
}
/**
* prepare and run the command according to NfcV specification
* #param cmd command byte
* #param m command length
* #param n
* #param in input data
* #return
*/
protected byte[] transceive(byte cmd,int m, int n, byte[] in){
byte[] command;
byte[] res="transceive failed message".getBytes();
ByteArrayBuffer bab = new ByteArrayBuffer(128);
// flags: bit x=adressed,
bab.append(0x00);
bab.append(cmd); // cmd byte
// 8 byte UID - or unaddressed
// bab.append(mytag.getId(), 0, 8);
// block Nr
if(-1!=m)bab.append(m);
if(-1!=n)bab.append(n);
if(null!=in)bab.append(in, 0, in.length);
command=bab.toByteArray();
Log.d(TAG,"transceive cmd: "+toHex(command));
// Log.d(TAG,"transceive cmd length: "+command.length);
// TODO background!
try {
if(!mynfcv.isConnected()) return res;
for(int t=maxtries;t>0;t++){ // retry reading
res=mynfcv.transceive(command);
if(0==res[0]) break;
}
}
catch (TagLostException e){ //TODO roll back user action
Log.e(TAG, "Tag lost "+e.getMessage());
try {
mynfcv.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return e.getMessage().getBytes();
}
catch (IOException e) {
Log.d(TAG, "transceive IOEx: "+e.getMessage()+toHex(res));
// e.printStackTrace();
return e.getMessage().getBytes();
}
finally{
Log.d(TAG,"getResponseFlags: "+mynfcv.getResponseFlags());
lastErrorFlags=res[0];
Log.d(TAG,"Flagbyte: "+String.format("%2x", lastErrorFlags));
if(0!=lastErrorFlags){
lastErrorCode=res[1];
Log.d(TAG,"ErrorCodebyte: "+String.format("%2x", lastErrorCode));
}
}
if(0==mynfcv.getResponseFlags())
return (res);
else
// return new String("response Flags not 0").getBytes();
return res;
}
public void taintblock(int i, int n){
for(int j=0;j<n;j++)
setblocktaint(j,true);
}
public void taintblock(int i){
setblocktaint(i,true);
}
protected void setblocktaint(int i, boolean b){
blocktainted[i]=b;
}
/* (non-Javadoc)
* #see android.nfc.tech.TagTechnology#getTag()
*
*/
#Override
public Tag getTag() {
// TODO Auto-generated method stub
//return mytag;
return mynfcv.getTag();
}
/* (non-Javadoc)
* #see android.nfc.tech.TagTechnology#close()
*/
#Override
public void close() throws IOException {
try {
mynfcv.close();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "close failed: "+e.getMessage());
e.printStackTrace();
}
}
/* (non-Javadoc)
* #see android.nfc.tech.TagTechnology#connect()
*/
#Override
public void connect() throws IOException {
try {
mynfcv.connect();
} catch (IOException e) {
lastErrorFlags=-1; // TODO discriminate error states
Log.d(TAG,"connect failed: "+e.getMessage());
e.printStackTrace();
}
}
/* (non-Javadoc)
* #see android.nfc.tech.TagTechnology#isConnected()
*/
#Override
public boolean isConnected() {
// TODO Auto-generated method stub
// mynfcv.getDsfId();
return mynfcv.isConnected(); // better?
}
public byte[] readSingleBlock(int i){
byte[] read=transceive((byte)0x20,i);
setblocktaint(i,false); // remember we read this block
if(0!=lastErrorFlags)return read; // TODO not so ignorant..
byte[] res=new byte[read.length-1]; // drop the (0) flag byte TODO: in transceive?
for (int l = 0; l < read.length-1; l++) {
res[l]=read[l+1];
myuserdata[i*blocksize+l]=res[l]; // sort block into our buffer
}
return res;
}
/**
*
* #param i starting block number
* #param j block count
* #return block content concatenated
*/
public byte[] readMultipleBlocks(int i,int j){
if(0==blocksize){
Log.e(TAG,"readMult w/o initfields?");
getSystemInformation(); // system info was not read yet
}
byte[] read = transceive((byte)0x23,i,j);
if(0!=read[0])return read; // error flag set: TODO left as exercise..
byte[] res=new byte[read.length-1]; // drop the (0) flag byte
for (int l = 0; l < read.length-1; l++) {
res[l]=read[l+1];
myuserdata[i*blocksize+l]=res[l]; // sort block into our buffer
}
if(res.length<j*blocksize) return read; // da fehlt was
for (int k = i; k < j; k++) { // all blocks we read
setblocktaint(k, false); // untaint blocks we read
// #TODO reverting block order should be done on demand - or under user control (done again in DDMData)
// reverse(res,k*blocksize,blocksize); // swap string positions
}
return res;
}
public byte[] getMultiSecStatus(int i,int n){
byte[] read = transceive((byte)0x2c,i,n-1);
Log.d(TAG,"secstatus "+toHex(read));
if(0!=read[0])return read;
int startat=1; // TODO transceive will skip the error field soon
for(int j=0;j<nBlocks;j++)
blocklocked[j]=read[startat+i+j];
return read;
}
/**
* move anywhere to utils
* #param s
* #return
*/
public static String toHex(byte[] in){
String text=String.format("0x");
for (byte element : in) {
text=text.concat(String.format("%02x", element));
}
return text;
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
my hello.js file
var hello = {
world: function(str, callback) {
cordova.exec(callback, function(err) {
callback('Nothing to hello.');
}, "Hello", "hello", [str]);
}
}
var ReadNfcV = {
read: function (str, callback) {
cordova.exec(callback, function (err) {
callback('Nothing to hello.');
}, "Hello", "hello", [str]);
}
}
module.exports = hello;
module.exports = ReadNfcV;
and my plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
id="org.apache.cordova.plugin"
version="0.1.0">
<js-module src="hello.js" name="hello">
<clobbers target="hello" />
</js-module>
<!-- Android -->
<platform name="android">
<source-file src="Hello.java" target-dir="src/org/apache/cordova/plugin" />
<source-file src="ReadNfcV.java" target-dir="src/org/apache/cordova/plugin" />
<config-file target="res/xml/config.xml" parent="/*">
<feature name="Hello" >
<param name="android-package" value="org.apache.cordova.plugin.Hello"/>
</feature>
</config-file>
</platform>
</plugin>
I was able to deploy the app so I can test a bit, My problem is that I dont really understand how i can call the ReadNfc class from the ReadNfcV.java file from within my app via javascript. I just did the same as in the Tutorial but now the hello.World function is not a function anymore so i guess i did smth wrong in my hello.js file. I would really appreciate it if someone could help and explain me how i can call my java class via javascript and then return the result from the java class back to my javascript. I looked 2 Days for an already existing plugin but didnt find anything on that subject but the phonegap-nfc plugin.
Kind regards Christopher
Update Day1
I added tech.NfcV to the Import List
import android.nfc.tech.NfcV;
Changed the execute function as suggested
#Override
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
Log.d(TAG, "execute " + action);
if (!getNfcStatus().equals(STATUS_NFC_OK)) {
callbackContext.error(getNfcStatus());
return true; // short circuit
}
createPendingIntent();
if (action.equals(REGISTER_DEFAULT_TAG)) {
addTechList(new String[]{NfcV.class.getName()}); //changed this form Mifare to NfcV
registerDefaultTag(callbackContext);
} else if (action.equalsIgnoreCase(INIT)) {
init(callbackContext);
} else {
// invalid action
return false;
}
return true;
}
Problem seems to be that I get Invalid action returned at the moment so something is wrong here
I changed the registerDefault function to
private void registerDefaultTag(CallbackContext callbackContext) {
addTechFilter();
callbackContext.success();
}
And i changed the Parse Message function to
void parseMessage() {
cordova.getThreadPool().execute(new Runnable() {
#Override
public void run() {
Log.d(TAG, "parseMessage " + getIntent());
Intent intent = getIntent();
String action = intent.getAction();
Log.d(TAG, "action " + action);
if (action == null) {
return;
}
if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED) || action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)){
Tag tagFromIntent = (Tag)intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
NfcV mfc = NfcV.get(tagFromIntent);
fireTagEvent(tag);
}
/*if (action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) {
Ndef ndef = Ndef.get(tag);
fireNdefEvent(NDEF_MIME, ndef, messages);
} else if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED)) {
for (String tagTech : tag.getTechList()) {
Log.d(TAG, tagTech);
if (tagTech.equals(NdefFormatable.class.getName())) {
fireNdefFormatableEvent(tag);
} else if (tagTech.equals(Ndef.class.getName())) { //
Ndef ndef = Ndef.get(tag);
fireNdefEvent(NDEF, ndef, messages);
}
}
}
if (action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) {
fireTagEvent(tag);
}*/
setIntent(new Intent());
}
});
}
So currently i get the error invalid action as soon as i click on start Listening and calling the Taglisteners in Javascript atm I use all 4 different Listeners to see if any work. It seems that I need to write a new fireEvent function specific for NfcV since the existing ones arent working
Update 2
I have managed to compile the plugin and deploy the app but nothing is happening i am not getting a Tag object back
The parse Message Function
void parseMessage() {
cordova.getThreadPool().execute(new Runnable() {
#Override
public void run() {
Log.d(TAG, "parseMessage " + getIntent());
Intent intent = getIntent();
String action = intent.getAction();
Log.d(TAG, "action " + action);
if (action == null) {
return;
}
if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED) || action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)){
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if(tag != null){
byte[] id = tag.getId();
// set up read command buffer
byte blockNo = 0; // block address
byte[] readCmd = new byte[3 + id.length];
readCmd[0] = 0x20; // set "address" flag (only send command to this tag)
readCmd[1] = 0x20; // ISO 15693 Single Block Read command byte
System.arraycopy(id, 0, readCmd, 2, id.length); // copy ID
readCmd[2 + id.length] = blockNo; // 1 byte payload: block address
NfcV tech = NfcV.get(tag);
if (tech != null) {
// send read command
try {
tech.connect();
byte[] data = tech.transceive(readCmd);
fireTagEvent(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
tech.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
setIntent(new Intent());
}
}
});
}
My fireTagEvent
private void fireTagEvent(byte[] data) {
String s2 = new String(data);
String command = MessageFormat.format(javaScriptEventTemplate, TAG_DEFAULT, s2);
Log.v(TAG, s2);
this.webView.sendJavascript(s2);
}
The execute function
#Override
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
Log.d(TAG, "execute " + action);
if (!getNfcStatus().equals(STATUS_NFC_OK)) {
callbackContext.error(getNfcStatus());
return true; // short circuit
}
createPendingIntent();
if (action.equals(REGISTER_DEFAULT_TAG)) {
addTechList(new String[]{NfcV.class.getName()});
registerDefaultTag(callbackContext);
} else if (action.equalsIgnoreCase(INIT)) {
init(callbackContext);
} else {
// invalid action
return false;
}
return true;
}
Thats pretty much it the app starts the "addTagDiscoveredListener" is registered but im not getting any Object back so either the nfcv tag is not read or i just dont get anything back not really sure...
I used the Chariotsolution plugin as a start to build my own nfc reading plugin. I think it would save you much time if you don't start from scratch.
There are many things you can remove because the original plugin only deals with NDEF tags, but removing lines is faster than re-inventing the wheel.
It's a bit old in my head, so I'm not sure I can explain everything right...
My nead was to read info in Mifare classic tags, but maybe you can adapt for your needs...
So, if you look at NfcPlugin.java, in the execute function, all I kept was the code for the actions REGISTER_DEFAULT_TAG and INIT.
Updated the code in REGISTER_DEFAULT_TAG to register the listening for mifare classic tag. And modified registerDefaultTag function to call addTechFilter instead of addTagFilter.
So this leaves us with
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
Log.d(TAG, "execute " + action);
if (!getNfcStatus().equals(STATUS_NFC_OK)) {
callbackContext.error(getNfcStatus());
return true; // short circuit
}
createPendingIntent();
if (action.equals(REGISTER_DEFAULT_TAG)) {
addTechList(new String[]{MifareClassic.class.getName()});
registerDefaultTag(callbackContext);
} else if (action.equalsIgnoreCase(INIT)) {
init(callbackContext);
} else {
// invalid action
return false;
}
return true;
}
private void registerDefaultTag(CallbackContext callbackContext) {
addTechFilter();
callbackContext.success();
}
Now what you need to understand is that once you called from the js the init function, the parseMessage function of the plugin will be called each time the device sees a nfc tag.
So in the parseMessage function I have a test
if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED) || action.equals(NfcAdapter.ACTION_TAG_DISCOVERED))
in wich I have all the code to deal with my tag.
In that code can get info from the tag in the intent using something like this :
Tag tagFromIntent = (Tag)intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
MifareClassic mfc = MifareClassic.get(tagFromIntent);
and then depending on your treatment you can call either fireErrorEvent or a fire...Event of your own that would return the data to the javascript using the webView.sendJavascript function.
I'm running out of time to detail the js part.
Not sure sure if it will help you or if it's the way you want to go (don't know how the tag you're using is working). Let me know if it helps you and if you need more details.
Okay so I finally managed to get it working
In the Phonegap-nfc.js I added an eventlistener for the NFCV tag
addNfcVListener: function (callback, win, fail) {
document.addEventListener("nfcv", callback, false);
cordova.exec(win, fail, "NfcPlugin", "registerNfcV", []);
},
The matching Execute function looks like this
#Override
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
Log.d(TAG, "execute " + action);
if (!getNfcStatus().equals(STATUS_NFC_OK)) {
callbackContext.error(getNfcStatus());
return true; // short circuit
}
createPendingIntent();
if (action.equalsIgnoreCase(REGISTER_NFCV)) {
registerNfcV(callbackContext);
}else {
// invalid action
return false;
}
return true;
}
Here is where the Tag gets added to the techlist
private void registerNfcV(CallbackContext callbackContext) {
addTechList(new String[]{NfcV.class.getName()});
callbackContext.success();
}
Here the Tag gets parsed and fires an event
void parseMessage() {
cordova.getThreadPool().execute(new Runnable() {
#Override
public void run() {
Log.d(TAG, "parseMessage " + getIntent());
Intent intent = getIntent();
String action = intent.getAction();
Log.d(TAG, "action " + action);
if (action == null) {
return;
}
if (action.equals(NfcAdapter.ACTION_TECH_DISCOVERED) || action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)){
NfcvData ma;
Tag tagFromIntent = (Tag)intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Parcelable[] messages = intent.getParcelableArrayExtra((NfcAdapter.EXTRA_NDEF_MESSAGES));
NfcV mfc = NfcV.get(tagFromIntent);
Tag tag = mfc.getTag();
fireNfcVReadEvent(NFCV, mfc, messages);
}
setIntent(new Intent());
}
});
}
Then this event is called
private void fireNfcVReadEvent(String type, NfcV nfcv, Parcelable[] messages) {
JSONObject jsonObject = buildNfcVReadJSON(nfcv, messages);
String tag = jsonObject.toString();
String command = MessageFormat.format(javaScriptEventTemplate, type, tag);
Log.v(TAG, command);
this.webView.sendJavascript(command);
}
Which sends the Taginformation back to my Javascript
It appears that you are not sending any data back in you callback, just the cordova SUCCESS. callbackContext.success(returndatahere); See the cordova plugin spec for supported data types.

Emitting an Event in Node.js C++ Addon

I have an application that reads an array of process data from an industrial controller. I want to push that data to a web page as it changes. To that end, I wrote a node.js addon in c++ that scans the process data and attempts to fire an event when the data value changes. Everything works fine with the Addon until it tries to fire an event, at which point node.js terminates with the error:
undefined:0
TypeError: undefined is not a function
The CPP, javascript shim and test javascript are below. Any insights are greatly appreciated.
Thanks in advance.
node_corelink.cpp
typedef struct CoreLinkValue
{
// pointer to a CTS variant value
CtsVariant* value;
// copy of the last value that was broadcast
CtsVariant lastValue;
} CoreLinkValue;
//
// An event structure for pushing events to node.js
// Requires the javascript shim code in node_corelink.js
//
struct Emitter: ObjectWrap
{
static Handle<Value> New(const Arguments& args);
static Handle<Value> DataChange(const char* topic, CtsVariant* value);
};
//
// Create a message payload based on the variant type and
// initiate sending out on the topic
//
static Handle<Value>
createVariantHandle(CtsVariant* value)
{
Handle<Value> ret;
switch (value->type)
{
case CTSTYPE_BIT:
case CTSTYPE_BYTE:
ret = Integer::New(value->value.byte[0]);
break;
case CTSTYPE_WORD:
ret = Integer::New(value->value.word[0]);
break;
case CTSTYPE_DWORD:
ret = Integer::New(value->value.dword[0]);
break;
case CTSTYPE_WORD64:
ret = Number::New(value->value.word64);
break;
case CTSTYPE_REAL64:
ret = Number::New(value->value.real64);
break;
default:
ret = Undefined();
break;
}
return ret;
}
Handle<Value> Emitter::New(const Arguments& args)
{
HandleScope scope;
assert(args.IsConstructCall());
Emitter* self = new Emitter();
self->Wrap(args.This());
return scope.Close(args.This());
}
// emits DataChange Event
Handle<Value> Emitter::DataChange( const char* topic, CtsVariant* value )
{
HandleScope scope;
Handle<Value> argv[3] = {
String::New("DataChange"), // event name
String::New(topic), // topic argument
createVariantHandle(value) // value argument
};
printf ("C++ Emitting event!\n" );
MakeCallback(context_obj_, "emit", 2, argv);
return True();
}
//
// Triggered by the event loop on a regular interval.
// Scans the registered data to see if the latest value has been
// broadcast and does so if needed.
//
void
scan_task( uv_timer_t* timer, int status )
{
std::map<std::string, CoreLinkValue>::iterator it;
bool doUpdate;
for( it = pdos_.begin();
it != pdos_.end();
++it )
{
if (forceRefreshPdos_ == true)
{
//
// An update of this value was requested.
//
doUpdate = true;
}
else if ( it->second.value->type != it->second.lastValue.type )
{
//
// If the types don't match, then this variant was obviously
// updated.
//
doUpdate = true;
}
else if ( it->second.value->value.word64 != it->second.lastValue.value.word64 )
{
//
// Word64 contains all bits of the value. If this value has
// changed, then they've all changed.
//
doUpdate = true;
}
else
{
doUpdate = false;
}
if (doUpdate)
{
it->second.lastValue.value = it->second.value->value;
Emitter::DataChange( it->first.c_str(), it->second.value );
}
}
if (forceRefreshPdos_)
{
forceRefreshPdos_ = false;
printf("Completed refresh all.\n");
}
}
//
// Start the execution of the scan loop
//
int
startScanLoop( void )
{
uv_timer_init( uv_default_loop(), &scanTimer_ );
uv_timer_start(
&scanTimer_, // timer instance
&scan_task, // callback function
0, // startup delay (ms)
100 ); // repeat interval (ms)
return 1;
}
//
// Stop the execution of the scan loop
//
void
stopScanLoop( void )
{
uv_timer_stop( &scanTimer_ );
}
//
// Connects to the kernel IPC
//
Handle<Value>
connect(const Arguments& args)
{
HandleScope scope;
...
startScanLoop();
return scope.Close( True() );
}
//
// Shuts down the kernel IPC
//
Handle<Value>
close(const Arguments& args)
{
HandleScope scope;
stopScanLoop();
...
return scope.Close( True() );
}
//
// Called by node.js to initialize the library.
//
void
init(Handle<Object> target)
{
target->Set(String::NewSymbol("connect"),
FunctionTemplate::New(connect)->GetFunction());
target->Set(String::NewSymbol("close"),
FunctionTemplate::New(close)->GetFunction());
//
// Events interface
//
Local<FunctionTemplate> t = FunctionTemplate::New(Emitter::New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::New("Emitter"));
target->Set(String::NewSymbol("Emitter"), t->GetFunction());
}
NODE_MODULE(node_corelink, init)
node_corelink.js
module.exports = require(__dirname + '/build/Release/node_corelink.node');
var Emitter = require(__dirname + '/build/Release/node_corelink.node').Emitter;
var events = require('events');
inherits(Emitter, events.EventEmitter);
exports.Emitter = Emitter;
// extend prototype
function inherits(target, source) {
for (var k in source.prototype)
target.prototype[k] = source.prototype[k];
}
test.js
process.stdin.resume(); //so the program will not close instantly
process.on('exit', function () {
corelink.close();
console.log('Goodbye!');
});
process.on('SIGINT', function () {
console.log('Got SIGINT.');
process.exit();
});
var corelink = require('./node_corelink');
var Emitter = require('./node_corelink').Emitter;
var e = new Emitter();
e.on('DataChange', function(s) {
console.log('DataChange');
});
corelink.connect();
I was able to trigger callback in a less-graceful method.
node_corelink.js
module.exports = require(__dirname + '/build/Release/node_corelink.node');
test.js
var corelink = require('./node_corelink');
function onDataChange( topic, value )
{
if ( value !== undefined )
console.log ( topic + " ::: " + value.toString() );
}
function onMessage( msg )
{
console.log ( "Message from kernel: " + msg.toString() );
}
corelink.connect(onDataChange, onMessage);
node_corelink.cpp
static void
dataChange( const char* topic, CtsVariant* value )
{
HandleScope scope;
Handle<Value> argv[2] =
{
String::New(topic), // topic argument
createVariantHandle(value) // value argument
};
MakeCallback(Context::GetCurrent()->Global(), pfOnDataChange_, 2, argv);
}
static void
onMessage( const char* message )
{
HandleScope scope;
Handle<Value> argv[1] =
{
String::New(message) // message argument
};
MakeCallback(Context::GetCurrent()->Global(), pfOnMessage_, 1, argv);
}
//
// Connects to the kernel IPC
//
Handle<Value>
connect(const Arguments& args)
{
HandleScope scope;
if ( args.Length() < 2
|| !args[0]->IsFunction()
|| !args[1]->IsFunction() )
{
return scope.Close( False() );
}
pfOnDataChange_ = Persistent<Function>::New(args[0].As<Function>());
pfOnMessage_ = Persistent<Function>::New(args[1].As<Function>());
...
return scope.Close( True() );
}

Categories