Run tensorflow model in CPP - c++

I trained my model using tf.keras. I convert this model to '.pb' by,
import os
import tensorflow as tf
from tensorflow.keras import backend as K
K.set_learning_phase(0)
from tensorflow.keras.models import load_model
model = load_model('model_checkpoint.h5')
model.save('model_tf2', save_format='tf')
This creates a folder 'model_tf2' with 'assets', varaibles, and saved_model.pb
I'm trying to load this model in cpp. Referring to many other posts (mainly, Using Tensorflow checkpoint to restore model in C++), I am now able to load the model.
RunOptions run_options;
run_options.set_timeout_in_ms(60000);
SavedModelBundle model;
auto status = LoadSavedModel(SessionOptions(), run_options, model_dir_path, tags, &model);
if (!status.ok()) {
std::cerr << "Failed: " << status1;
return -1;
}
The above screenshot shows that the model was loaded.
I have the following questions
How do I do a forward pass through the model?
I understand 'tag' can be gpu, serve, train.. What is the difference between serve and gpu?
I don't understand the first 2 arguments to LoadSavedModel i.e. session options and run options. What purpose do they serve? Also, could you help me understand with a syntactical example? I have set run_options by looking at another stackoverflow post, however I don't understand its purpose.
Thank you!! :)

Code to perform forward pass through the model, as mentioned by Patwie in the comments, is given below:
#include <tensorflow/core/protobuf/meta_graph.pb.h>
#include <tensorflow/core/public/session.h>
#include <tensorflow/core/public/session_options.h>
#include <iostream>
#include <string>
typedef std::vector<std::pair<std::string, tensorflow::Tensor>> tensor_dict;
/**
* #brief load a previous store model
* #details [long description]
*
* in Python run:
*
* saver = tf.train.Saver(tf.global_variables())
* saver.save(sess, './exported/my_model')
* tf.train.write_graph(sess.graph, '.', './exported/graph.pb, as_text=False)
*
* this relies on a graph which has an operation called `init` responsible to
* initialize all variables, eg.
*
* sess.run(tf.global_variables_initializer()) # somewhere in the python
* file
*
* #param sess active tensorflow session
* #param graph_fn path to graph file (eg. "./exported/graph.pb")
* #param checkpoint_fn path to checkpoint file (eg. "./exported/my_model",
* optional)
* #return status of reloading
*/
tensorflow::Status LoadModel(tensorflow::Session *sess, std::string graph_fn,
std::string checkpoint_fn = "") {
tensorflow::Status status;
// Read in the protobuf graph we exported
tensorflow::MetaGraphDef graph_def;
status = ReadBinaryProto(tensorflow::Env::Default(), graph_fn, &graph_def);
if (status != tensorflow::Status::OK()) return status;
// create the graph
status = sess->Create(graph_def.graph_def());
if (status != tensorflow::Status::OK()) return status;
// restore model from checkpoint, iff checkpoint is given
if (checkpoint_fn != "") {
tensorflow::Tensor checkpointPathTensor(tensorflow::DT_STRING,
tensorflow::TensorShape());
checkpointPathTensor.scalar<std::string>()() = checkpoint_fn;
tensor_dict feed_dict = {
{graph_def.saver_def().filename_tensor_name(), checkpointPathTensor}};
status = sess->Run(feed_dict, {}, {graph_def.saver_def().restore_op_name()},
nullptr);
if (status != tensorflow::Status::OK()) return status;
} else {
// virtual Status Run(const std::vector<std::pair<string, Tensor> >& inputs,
// const std::vector<string>& output_tensor_names,
// const std::vector<string>& target_node_names,
// std::vector<Tensor>* outputs) = 0;
status = sess->Run({}, {}, {"init"}, nullptr);
if (status != tensorflow::Status::OK()) return status;
}
return tensorflow::Status::OK();
}
int main(int argc, char const *argv[]) {
const std::string graph_fn = "./exported/my_model.meta";
const std::string checkpoint_fn = "./exported/my_model";
// prepare session
tensorflow::Session *sess;
tensorflow::SessionOptions options;
TF_CHECK_OK(tensorflow::NewSession(options, &sess));
TF_CHECK_OK(LoadModel(sess, graph_fn, checkpoint_fn));
// prepare inputs
tensorflow::TensorShape data_shape({1, 2});
tensorflow::Tensor data(tensorflow::DT_FLOAT, data_shape);
// same as in python file
auto data_ = data.flat<float>().data();
data_[0] = 42;
data_[1] = 43;
tensor_dict feed_dict = {
{"input_plhdr", data},
};
std::vector<tensorflow::Tensor> outputs;
TF_CHECK_OK(
sess->Run(feed_dict, {"sequential/Output_1/Softmax:0"}, {}, &outputs));
std::cout << "input " << data.DebugString() << std::endl;
std::cout << "output " << outputs[0].DebugString() << std::endl;
return 0;
}
The tags Serve and GPU can be used together if we want to perform inference on a Model using GPU.
The argument session_options in C++ is equivalent to
tf.ConfigProto(allow_soft_placement=True, log_device_placement=True),
which means that, If allow_soft_placement is true, an op will be placed on CPU if
(i) there's no GPU implementation for the OP (or)
(ii) no GPU devices are known or registered (or)
(iii) need to co-locate with reftype input(s) which are from CPU.
The argument run_options is used if we want to use the Profiler, i.e., to extract runtime statistics of the graph execution. It adds information about the time of execution and memory consumption to your event files and allow you to see this information in tensorboard.
Syntax to use session_options and run_options is given in the code mentioned above.

This worked well with TF1.5
load graph function
Status LoadGraph(const tensorflow::string& graph_file_name,
std::unique_ptr<tensorflow::Session>* session, tensorflow::SessionOptions options) {
tensorflow::GraphDef graph_def;
Status load_graph_status =
ReadBinaryProto(tensorflow::Env::Default(), graph_file_name, &graph_def);
if (!load_graph_status.ok()) {
return tensorflow::errors::NotFound("Failed to load compute graph at '",
graph_file_name, "'");
}
//session->reset(tensorflow::NewSession(tensorflow::SessionOptions()));
session->reset(tensorflow::NewSession(options));
Status session_create_status = (*session)->Create(graph_def);
if (!session_create_status.ok()) {
return session_create_status;
}
return Status::OK();
}
Call the load graph function with path to .pb model and other session configuration. Once the model is loaded you can do forward pass by calling Run
Status load_graph_status = LoadGraph(graph_path, &session_fpass, options);
if (!load_graph_status.ok()) {
LOG(ERROR) << load_graph_status;
return -1;
}
std::vector<tensorflow::Tensor> outputs;
Status run_status = session_fpass->Run({ {input_layer, image_in} },
{ output_layer1}, { output_layer1}, &outputs);
if (!run_status.ok()) {
LOG(ERROR) << "Running model failed: " << run_status;
return -1;
}

Related

How to get the state of a service with sd-bus?

I need to query, monitor and possibly change the state of a few systemd services from a C++ application. It looks like sd-bus is the right way to do this, but I'm having a terrible time finding an example.
So, how do I:
1) Query the current status of a service via sd-bus, similar to systemctl status foo.service?
2) Monitor the status of a service such that I get a callback whenever it changes?
3) Change the status of a service, similar to systemctl start/stop/restart?
Thanks!
Using the sd-bus API is absolutely correct (header #include <systemd/sd-bus.h>)
First you need get access to a bus object:
I do this:
Systemctl::Systemctl() :
m_bus(nullptr)
{
int r = sd_bus_default_system(&m_bus);
if (r < 0)
throw exception("Could not open systemd bus");
}
If you're having problems opening the bus:
Run as root/sudo
Make some polkit policies to grant your user/group access to this command
Run the _user bus instead of the _system bus
Don't forget to release the bus when you are done:
Systemctl::~Systemctl()
{
sd_bus_unref(m_bus);
}
Now you had 3 questions:
Query the status
For each unit, I have a class which holds the escaped name (foo_2eservice) as m_name, and a reference to the bus in m_bus. Call this method with any property. You seem to be most interested in "ActiveState" or "SubState".
std::string Unit::GetPropertyString(const std::string& property) const
{
sd_bus_error err = SD_BUS_ERROR_NULL;
char* msg = nullptr;
int r;
r = sd_bus_get_property_string(m_bus,
"org.freedesktop.systemd1",
("/org/freedesktop/systemd1/unit/" + m_unit).c_str(),
"org.freedesktop.systemd1.Unit",
property.c_str(),
&err,
&msg);
if (r < 0)
{
std::string err_msg(err.message);
sd_bus_error_free(&err);
std::string err_str("Failed to get " + property + " for service "
+ m_name + ". Error: " + err_msg);
throw exception(err_str);
}
sd_bus_error_free(&err);
// Free memory (avoid leaking)
std::string ret(msg);
free (msg);
return ret;
}
Monitor the status of a service:
The first step is to set up a file-descriptor to subscribe to changes. In this case you are interested in subscribing to the "PropertiesChanged" signal. Note that you'll get a signal for any property changing, not just the state. In the sd_bus_add_match() call, there is room for a callback, though I haven't experimented with it.
void Systemctl::SubscribeToUnitChanges(const std::string& escaped_name)
{
/* This function is an easier helper, but it as only introduced in systemd 237
* Stretch is on 232 while buster is on 241 . Need re replace this as long as
* we still support stretch
sd_bus_match_signal(
m_bus,
nullptr, // slot
nullptr, // sender
std::string("/org/freedesktop/systemd1/unit/" + escaped_name).c_str(), // path
"org.freedesktop.DBus.Properties", // interface
"PropertiesChanged", // member
nullptr, // callback
nullptr // userdata
);
*/
std::string match = "type='signal'";
match += ",path='/org/freedesktop/systemd1/unit/" + escaped_name + "'" ;
match += ",interface='org.freedesktop.DBus.Properties'";
match += ",member='PropertiesChanged'";
sd_bus_add_match(
m_bus,
nullptr, // slot
match.c_str(),
nullptr, // callback
nullptr // userdata
);
}
Instead what I do is periodically poll the bus for the subscribed changes and update each unit:
bool Systemctl::ProcessBusChanges()
{
bool changed = false;
sd_bus_message* msg = nullptr;
// for each new message
std::list<std::string> escaped_names;
while( sd_bus_process(m_bus, &msg) )
{
// Note: Once sd_bus_process returns 0, We are supposed to call
// sd_bus_wait, or check for changes on sd_bus_get_fd before calling
// this function again. We're breaking that rule. I don't really know
// the consequences.
if (msg)
{
std::string path = strna( sd_bus_message_get_path(msg) );
sd_bus_message_unref(msg);
std::string escaped_name = path.erase(0, path.find_last_of('/')+1 );
escaped_names.push_back(escaped_name);
changed = true;
}
}
escaped_names.sort();
escaped_names.unique();
for (auto unit : escaped_names)
{
auto it = m_units.find(unit);
if (it != m_units.end())
it->second.RefreshDynamicProperties();
}
return changed;
}
If it tells us that the bus has changed, then I go ahead and read all of my monitored units on that bus.
Change the status
This one is easy. I use the following, where method is one of "StartUnit", "StopUnit", or "RestartUnit".
static void CallMethodSS(sd_bus* bus,
const std::string& name,
const std::string& method)
{
sd_bus_error err = SD_BUS_ERROR_NULL;
sd_bus_message* msg = nullptr;
int r;
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1", /* <service> */
"/org/freedesktop/systemd1", /* <path> */
"org.freedesktop.systemd1.Manager", /* <interface> */
method.c_str(), /* <method> */
&err, /* object to return error in */
&msg, /* return message on success */
"ss", /* <input_signature (string-string)> */
name.c_str(), "replace" ); /* <arguments...> */
if (r < 0)
{
std::string err_str("Could not send " + method +
" command to systemd for service: " + name +
". Error: " + err.message );
sd_bus_error_free(&err);
sd_bus_message_unref(msg);
throw exception(err_str);
}
// Extra stuff that might be useful: display the response...
char* response;
r = sd_bus_message_read(msg, "o", &response);
if (r < 0)
{
LogError("Failed to parse response message: %s\n", strerror(-r) );
}
sd_bus_error_free(&err);
sd_bus_message_unref(msg);
}

Tensorflow C++ YOU_MADE_A_PROGRAMMING_MISTAKE

I train my NN-model in python and load it in VS2015 C++. The piece of code:
// The session will initialize the outputs
vector<Tensor> outputs;
// Run the session, evaluating our "c" operation from the graph
status = session->Run(inputs, { "y_pred" }, {}, &outputs);
// Convert the node to a scalar representation.
auto output_c = outputs[0].flat<float>();
The y_pred is a 2-element tensor, so I use flat to get it. However, I got an error, "YOU_MADE_A_PROGRAMMING_MISTAKE", from EIGEN_STATIC_ASSERT.
Anyone has this issue before? How should I solve it? Thanks!
Finally, I found a post in stackoverflow but I cannot make sure who is the original author. Indeed we need flat function.
session->Run(inputs, { "pred" }, {}, &outputs);
TTypes<float>::Flat indices_flat = outputs[0].flat<float>();
float coutput[6];
for (int i = 0; i<dataSize; i++) {
coutput[i] = indices_flat(i);
cout << "outptut[i]: " << indices_flat(i) << endl;
}

How to check node name of tensorflow graph (protocol buffers) by c++

I want to check the loading graph is correct.
I save the learned protocol buffers file by python.
And, I load the protocol buffers file by c++.
But I can't get the output tensor when the session run.
I want to output and check the graph information.
Saveing code by python
with tf.Graph().as_default() as graph:
input_data = tf.placeholder(tf.float32, shape=train_data.shape, name="input")
keep_prob = tf.placeholder(tf.float32)
answer = net.inference(input_data, units, io_data_dim, keep_prob, output_net=True)
saver = tf.train.Saver()
with tf.Session() as sess:
# Load model file
sess.run(tf.initialize_all_variables())
ckpt = tf.train.get_checkpoint_state(ckpt_dir_name)
if ckpt: # checkpoint is exist
last_model = ckpt.model_checkpoint_path # last model path
saver.restore(sess, last_model)
else:
print("There is no training network...")
exit()
# check the saveing graph
for v in sess.graph.get_operations():
print(v.name)
graph_def = graph_util.convert_variables_to_constants(sess, graph.as_graph_def(), ['output_lay/output_lay'])
tf.train.write_graph(graph_def, '.', pb_name, as_text=False)
Loading code by C++
Status LoadGraph(string graph_file_name, std::unique_ptr<tensorflow::Session>* session) {
tensorflow::GraphDef graph_def;
Status graph_status = ReadBinaryProto(tensorflow::Env::Default(), graph_file_name, &graph_def);
if (!graph_status.ok())
{
return graph_status;
}
session->reset(tensorflow::NewSession(tensorflow::SessionOptions()));
Status create_status = (*session)->Create(graph_def);
if (!create_status.ok())
{
return create_status;
}
return Status::OK();
}
Runing code by c++
input_node_name = "input"
output_node_name = "output_lay/output_lay"
Status Infer(std::unique_ptr<tensorflow::Session>* session,
tensorflow::Tensor* input,
string* input_node_name,
string* output_node_name,
tensorflow::Tensor* output)
{
tensorflow::Tensor input_object = *input;
// input
// I am not confident here
tensorflow::Tensor keep_prob(tensorflow::DT_FLOAT, tensorflow::TensorShape());
keep_prob.scalar<float>()() = 1.0;
std::vector<std::pair<string, tensorflow::Tensor>> inputs = {
{"Placeholder", keep_prob},
{*input_node_name, *input}
};
// ouput
std::vector<tensorflow::Tensor> outputs;
std::cout << "Runnning network..." << std::endl;
Status result = (*session)->Run(
inputs,
{*output_node_name},
{},
&outputs
);
// output 0 (no reply?)
std::cout << "outputs size " << outputs.size() << std::endl;
if (!result.ok())
{
LOG(ERROR) << "Failure: " << result;
}
(*output) = outputs[0];
//std::cout << "take first output" << std::endl;
return result;
}
result is
Invalid argument: Incompatible shapes: [77,1,513,16] vs. [13780,1,513,16]
[[Node: conv1/dropout/mul = Mul[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/cpu:0"](conv1/dropout/Div, conv1/dropout/Floor)]]
ex) if not use the cnn ( the 3 layer perceptron network)
Invalid argument: Incompatible shapes: [77,600] vs. [10242,600]
[[Node: hidden1/dropout/mul = Mul[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/cpu:0"](hidden1/dropout/Div, hidden1/dropout/Floor)]]
pickup of the output 1st python code
input
Placeholder
Reshape/shape
Reshape
conv1/weights
conv1/biases
conv1/Conv2D
conv1/MaxPool
conv1/Add
conv1/conv1
conv1/dropout/Shape
conv1/dropout/add
conv1/dropout/Floor
conv1/dropout/Div
conv1/dropout/mul
conv2/weights
conv2/biases
conv2/Conv2D
...
Reshape_1/shape
Reshape_1
hidden1/weights
hidden1/biases
hidden1/MatMul
hidden1/Add
hidden1/hidden1
hidden1/dropout/add
hidden1/dropout/Floor
hidden1/dropout/Div
hidden1/dropout/mul
hidden2/weights
...
output_lay/weights
output_lay/biases
output_lay/MatMul
output_lay/Add
output_lay/output_lay

Reading *.mhd/*.raw format 3D images in ITK

How to load and write .mhd/.raw format 3D images in ITK? I have tried to use the following code but it is not getting loaded as the dimension of the loaded image is displayed as 0,0,0.
Can someone please point out the mistake I am making?
typedef float InputPixelType;
const unsigned int DimensionOfRaw = 3;
typedef itk::Image< InputPixelType, DimensionOfRaw > InputImageType;
//typedef itk::RawImageIO<InputPixelType, DimensionOfRaw> ImageIOType;
typedef itk::ImageFileReader<InputImageType > ReaderType;
/*
* --------------------Loader and saver of Raws, as well the function that takes a resulting (from inference matrix/vector) and creates a Raw out of it.-----------------------
*/
InputImageType::Pointer loadRawImageItk( std::string RawFullFilepathname, ReaderType::Pointer & RawImageIO ) {
//http://www.itk.org/Doxygen/html/classitk_1_1Image.html
//http://www.itk.org/Doxygen/html/classitk_1_1ImageFileReader.html
typedef itk::ImageFileReader<InputImageType> ReaderType;
ReaderType::Pointer reader = ReaderType::New();
reader->SetFileName(RawFullFilepathname);
//ImageIOType::Pointer RawImageIO = ImageIOType::New();
reader->SetImageIO( RawImageIO );
try {
reader->Update();
} catch (itk::ExceptionObject& e) {
std::cerr << e.GetDescription() << std::endl;
exit(1); // You can choose to do something else, of course.
}
//InputImageType::Pointer inputImage = reader->GetOutput();
InputImageType::Pointer inputImage = reader->GetOutput();
return inputImage;
}
int saveRawImageItk( std::string RawFullFilepathname, InputImageType::Pointer & outputImageItkType , ImageIOType::Pointer & RawImageIO) {
std::cout << "Saving image to: " << RawFullFilepathname << "\n";
typedef itk::ImageFileWriter< InputImageType > Writer1Type;
Writer1Type::Pointer writer1 = Writer1Type::New();
writer1->SetInput( outputImageItkType );
writer1->SetFileName( RawFullFilepathname );
writer1->SetImageIO( RawImageIO ); //seems like this is useless.
// Execution of the writer is triggered by invoking the \code{Update()} method.
try
{
writer1->Update();
}
catch (itk::ExceptionObject & e)
{
std::cerr << "exception in file writer " << std::endl;
std::cerr << e.GetDescription() << std::endl;
std::cerr << e.GetLocation() << std::endl;
return 1;
}
return 0;
}
I have just read the mhd and raw files in Python successfully using the following SimpleITK code:
import SimpleITK as sitk
import numpy as np
def load_itk_image(filename):
itkimage = sitk.ReadImage(filename)
numpyImage = sitk.GetArrayFromImage(itkimage)
return numpyImage
Maybe you can use it as a reference.
Whether you should use the ReadImage function instead of the ImageFileReader? You can have a try.
A few good examples of file reading depending on a known format are found here.
reader->SetImageIO( RawImageIO );
seems the incorrect thing to do here if you are loading both .mhd and .raw files as they are seperate formats, MetaImage vs Raw format where you do and don't know the image size, origin, spacing etc based on the absense or presense of a header.
How are you determining the size of the image and getting (0,0,0)? image->GetSize()?
Can you provide test data?
https://itk.org/Wiki/ITK/Examples/IO/ReadUnknownImageType
https://itk.org/ITKExamples/src/IO/ImageBase/RegisterIOFactories/Documentation.html

Import a NPAPI DLL in a C++ application

I have to import a NPAPI DLL in a C++ application for my project.
I follow the excellent tutorial : http://colonelpanic.net/2009/03/building-a-firefox-plugin-part-one/
However I have some trouble to access to the methods of the dll. (Note: in a browser the dll is completly functional). After calling the main functions : NP_GetEntryPoints and NP_Initialize and retrieving the ScriptableNPObject, the invocation of the methods return no value with no error. The property or method names are the same used in javascript in the browser (functional case).
For information, the property and method names and the mime-type have been replaced in this sample.
Anyone has an idea to invoke the methods of the dll by simulating what the browser does?
Here is a part of the main program:
if (hDLL == 0)
{
std::cout << "DLL failed to load!" << std::endl;
}
else
{
std::cout << "DLL loaded!" << std::endl;
//WRAP NP_GETENTRYPOINTS FUNCTION:
_GetEntryPointsFunc = (GetEntryPointsFunc)GetProcAddress(hDLL, "NP_GetEntryPoints");
if (_GetEntryPointsFunc)
{
std::cout << "Get NP_GetEntryPoints Function!" << std::endl;
status = _GetEntryPointsFunc(pFuncs);
}
//WRAP NP_INITIALIZE FUNCTION:
_InitializeFunc = (InitializeFunc)GetProcAddress(hDLL, "NP_Initialize");
if (_InitializeFunc)
{
std::cout << "Get NP_Initialize Function!" << std::endl;
status = _InitializeFunc(&sBrowserFuncs);
}
int32_t mode = NP_EMBED;
int32_t argc = 7;
static const char mimetype[] = "application/x-mime_type_of_my_plugin";
char * argn[] = {"param1", "param2", "param3", "param4", "param5", "param6", "param7"};
char * argv[] = { "value1", "value2", "value3", "value4", "value5", "value6", "value7" };
NPObject np_object;
uint16_t size;
char* descritpionString;
char* nameString;
instance = &(plugin_instance.mNPP);
status = pFuncs->newp((char*)mimetype, instance, (uint16_t)mode, argc, argn, argv, &saved);
status = pFuncs->version; //OK
status = pFuncs->getvalue(instance,NPPVpluginDescriptionString,&descritpionString); //OK
status = pFuncs->getvalue(instance,NPPVpluginNameString,&nameString); //OK
status = pFuncs->getvalue(instance,NPPVpluginScriptableNPObject,&np_object); //ISSUE STARTS HERE
std::cin.get();
}
Here is my create_object function called after getting the scriptable NPObject with the getvalue function:
NPObject* _createobject(NPP npp, NPClass* aClass)
{
if (!npp) {
return nullptr;
}
if (!aClass) {
return nullptr;
}
NPObject *npobj;
if (aClass->allocate) {
npobj = aClass->allocate(npp, aClass);
} else {
npobj = (NPObject *)malloc(sizeof(NPObject));
}
if (npobj) {
npobj->_class = aClass;
npobj->referenceCount = 1;
//TEST:
NPError status;
NPString url;
NPVariant result;
NPVariant variant;
NPIdentifier property = "existing_property";
NPIdentifier *arrayId;
uint32_t count = 2;
const char *str = "https://test_url.com";
url.UTF8Characters = str;//;
url.UTF8Length = 20;
variant.type = NPVariantType_String;
variant.value.stringValue = url;
NPVariant args[] = { variant };
status = 1; //GENERIC ERROR VALUE
status = npobj->_class->structVersion; //OK
status = npobj->_class->hasMethod(npobj,L"existing_set_function"); //STATUS OK
status = npobj->_class->enumerate(npobj, &arrayId, &count); //Privileged instruction ERROR
status = npobj->_class->hasProperty(npobj, property); //STATUS OK
status = npobj->_class->getProperty(npobj, property, &result); //STATUS OK BUT NO RESULT
status = npobj->_class->invoke(npobj,L"existing_set_function",args,1,&result); //STATUS OK
status = npobj->_class->invoke(npobj,L"existing_get_function",args,0,&result); //STATUS OK BUT NO RESULT
status = npobj->_class->invokeDefault(npobj,args,0,&result); //STATUS OK BUT NO RESULT
//END TEST
}
return npobj;
}
Finally, here is the plugin_instance methods to declare ndata and pdata:
nsNPAPIPluginInstance::nsNPAPIPluginInstance()
{
mNPP.pdata = NULL;
mNPP.ndata = this;
}
nsNPAPIPluginInstance::~nsNPAPIPluginInstance()
{
}
Thanks in advance.
While it is unclear what "no result" means, a privileged instruction error suggests the function pointer is off.
I'd start with:
Checking that structVersion >= NP_CLASS_STRUCT_VERSION_ENUM (otherwise NPClass::enumerate is not available)
Using the proper npapi-sdk headers - your function pointer type names suggest you are not doing that
Using a simple test plugin to see what happens on the plugin side
Take care that the NPIdentifiers match up between your host application and the plugin, i.e. use a common string->NPIdentifier mapping for the host code and NPN_GetStringIdentifier() - as posted this can't work
Don't test the NPObject right in the creation function - the plugin may set things up to work properly only after NPN_CreateObject() returned