2

I've been trying to write an extension for Chrome that uses an NPAPI plugin. I'm using mingw to compile it. I struggled originally to get Chrome to load the plugin, but now I have a different problem.

I've managed to get Chrome to call NP_GetEntryPoints and NP_Initialize, but it crashes right after that. Here's my code so far...

main.cpp :

#include <iostream>
#include <cstdlib>
#include <Windows.h>
#include <npapi.h>
#include <npfunctions.h>
#define Exported extern "C" __declspec(dllexport)

NPNetscapeFuncs NPNFuncs;

Exported NPError NP_Initialize(NPNetscapeFuncs* pFuncs) {

    if (pFuncs == NULL)
        return NPERR_INVALID_FUNCTABLE_ERROR;

    if (HIBYTE(pFuncs->version) > NP_VERSION_MAJOR)
        return NPERR_INCOMPATIBLE_VERSION_ERROR;

    if (pFuncs->size < sizeof(NPNetscapeFuncs))
        return NPERR_INVALID_FUNCTABLE_ERROR;

    // Save functions
    NPNFuncs.size             = pFuncs->size;
    NPNFuncs.version          = pFuncs->version;
    NPNFuncs.geturlnotify     = pFuncs->geturlnotify;
    NPNFuncs.geturl           = pFuncs->geturl;
    NPNFuncs.posturlnotify    = pFuncs->posturlnotify;
    NPNFuncs.posturl          = pFuncs->posturl;
    NPNFuncs.requestread      = pFuncs->requestread;
    NPNFuncs.newstream        = pFuncs->newstream;
    NPNFuncs.write            = pFuncs->write;
    NPNFuncs.destroystream    = pFuncs->destroystream;
    NPNFuncs.status           = pFuncs->status;
    NPNFuncs.uagent           = pFuncs->uagent;
    NPNFuncs.memalloc         = pFuncs->memalloc;
    NPNFuncs.memfree          = pFuncs->memfree;
    NPNFuncs.memflush         = pFuncs->memflush;
    NPNFuncs.reloadplugins    = pFuncs->reloadplugins;
    NPNFuncs.getJavaEnv       = pFuncs->getJavaEnv;
    NPNFuncs.getJavaPeer      = pFuncs->getJavaPeer;
    NPNFuncs.getvalue         = pFuncs->getvalue;
    NPNFuncs.setvalue         = pFuncs->setvalue;
    NPNFuncs.invalidaterect   = pFuncs->invalidaterect;
    NPNFuncs.invalidateregion = pFuncs->invalidateregion;
    NPNFuncs.forceredraw      = pFuncs->forceredraw;*/

    // Success
    MessageBoxA(0, "NP_Initialize", "Log", 0);
    return NPERR_NO_ERROR;

}

Exported void NP_Shutdown() {

    MessageBoxA(0, "NP_Shutdown", "Log", 0);

}

    /* Entry points */
    NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* savedData) {

        MessageBoxA(0, "NPP_New", "Log", 0);
        return NPERR_NO_ERROR;

    }

    NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) {

        MessageBoxA(0, "NPP_GetValue", "Log", 0);
        return NPERR_NO_ERROR;

    }

    /*** Omitted... All the other functions are here, with just a MessageBox call in them ***/

Exported NPError NP_GetEntryPoints(NPPluginFuncs* pFuncs) {

    if (pFuncs == NULL)
        return NPERR_INVALID_FUNCTABLE_ERROR;

    if (pFuncs->size < sizeof(NPPluginFuncs))
        return NPERR_INVALID_FUNCTABLE_ERROR;

    pFuncs->size                = sizeof(NPPluginFuncs);
    pFuncs->version             = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
    pFuncs->newp                = &NPP_New;
    pFuncs->destroy             = &NPP_Destroy;
    pFuncs->setwindow           = &NPP_SetWindow;
    pFuncs->newstream           = &NPP_NewStream;
    pFuncs->destroystream       = &NPP_DestroyStream;
    pFuncs->asfile              = &NPP_StreamAsFile;
    pFuncs->writeready          = &NPP_WriteReady;
    pFuncs->write               = &NPP_Write;
    pFuncs->print               = &NPP_Print;
    pFuncs->event               = &NPP_HandleEvent;
    pFuncs->urlnotify           = &NPP_URLNotify;
    pFuncs->getvalue            = &NPP_GetValue;
    pFuncs->setvalue            = &NPP_SetValue;
    pFuncs->javaClass           = NULL;
    pFuncs->gotfocus            = &NPP_GotFocus;
    pFuncs->lostfocus           = &NPP_LostFocus;
    pFuncs->urlredirectnotify   = &NPP_URLRedirectNotify;
    pFuncs->clearsitedata       = &NPP_ClearSiteData;
    pFuncs->getsiteswithdata    = &NPP_GetSitesWithData;

    MessageBoxA(0, "NP_GetEntryPoints", "Log", 0);
    return NPERR_NO_ERROR;

}

I added a MessageBox call to every function, so I can see if the right functions are being called. When I run my test page, which is a blank page with an <embed> tag in it, I get a message saying NP_GetEntryPoints, then one saying NP_Initialize, then Chrome pops up a bar saying that my plugin has crashed. I think the problem is in my NP_GetEntryPoints, but I just can't see it... Is there anything I'm doing wrong, or forgetting to do?

I compile with :

g++.exe -DWIN32 -D_WIN32 -D_WINDOWS -D_WIN32_WINNT=0x0600 -D_WIN32_IE=0x0600 -D_UNICODE -DUNICODE -static-libgcc -static-libstdc++ -c "main.cpp" -o main.o

and

windres.exe "resource.rc" "resource.o"

and I link into a .DLL with :

g++.exe -Wl,--subsystem,windows -o "npplugin.dll" -s -shared main.o resource.o -lcomctl32 -lws2_32 -luxtheme -lgdi32 -lshell32 -lshlwapi
jjv360
  • 4,120
  • 3
  • 23
  • 37
  • One point, though I doubt this is the issue, is that those should all be &NPP_New, etc; the & operator in that case is optional in some compilers but to be correct you should have it. – taxilian Oct 22 '11 at 19:06
  • @Taxilian Thanks, I'd forgotten about that. Unfortunately, it didn't help with the crash :(. – jjv360 Oct 23 '11 at 01:11
  • Your exported functions probably need to be __stdcall. – Luke Oct 23 '11 at 19:50
  • @Luke They are, it's set in their declarations in `npfunctions.h`. I had some trouble getting them exported properly, but I've since fixed that... – jjv360 Oct 23 '11 at 23:16
  • Are they? I'm pretty sure you need to include the (same) calling convention in both the function declaration and definition. Since your definition does not include it and (presumably) the compiler doesn't complain about it, I'm not sure. From what I can tell npfunctions.h seems to be a standard header file that just defines function signatures without actually declaring any of the functions (unless yours is modified) so any calling convention specified there would not apply to your functions. – Luke Oct 24 '11 at 12:12
  • @Luke I tried adding `__stdcall` to the exported functions, but the exported functions all get a `@n` at the end, and the browser couldn't even load the plugin. When I changed them all to `__cdecl` the `@n` disappeared and `NP_Initialize` and `NP_GetEntryPoints` was called again, but again that's as far as it got. – jjv360 Oct 24 '11 at 17:35
  • Yep, that's normal. You need to use a [.def file](http://msdn.microsoft.com/en-us/library/d91k01sh%28v=vs.80%29.aspx) to export the undecorated name. – Luke Oct 24 '11 at 19:47

3 Answers3

5

The size between function tables differs between browsers and version of browsers.

You check the size against your hard compiled library headers:

if (pFuncs->size < sizeof(NPNetscapeFuncs))
    return NPERR_INVALID_FUNCTABLE_ERROR;

This can be the problem. For example the latest (at the moment of writing gecko xulrunner SDK contains a 84 byte size struct:

typedef struct _NPPluginFuncs {
  uint16_t size;
  uint16_t version;
  NPP_NewProcPtr newp;
  NPP_DestroyProcPtr destroy;
  NPP_SetWindowProcPtr setwindow;
  NPP_NewStreamProcPtr newstream;
  NPP_DestroyStreamProcPtr destroystream;
  NPP_StreamAsFileProcPtr asfile;
  NPP_WriteReadyProcPtr writeready;
  NPP_WriteProcPtr write;
  NPP_PrintProcPtr print;
  NPP_HandleEventProcPtr event;
  NPP_URLNotifyProcPtr urlnotify;
  void* javaClass;
  NPP_GetValueProcPtr getvalue;
  NPP_SetValueProcPtr setvalue;
  NPP_GotFocusPtr gotfocus;
  NPP_LostFocusPtr lostfocus;
  NPP_URLRedirectNotifyPtr urlredirectnotify;
  NPP_ClearSiteDataPtr clearsitedata;
  NPP_GetSitesWithDataPtr getsiteswithdata;
  NPP_DidCompositePtr didComposite;
} NPPluginFuncs;

Chrome sends in a 80 byte struct.

So I guess the last function pointer is not in the struct used by chrome.

Air2
  • 369
  • 3
  • 13
  • 1
    You pasted NPPluginFuncs while you probably meant to paste NPNetscapeFuncs, just a heads up. For anyone interested, they're in npfunctions.h of the npapi-sdk – Aktau Apr 09 '13 at 11:41
1

I would recommend using some form of logging rather than messageboxes; have you tried removing the messageboxes that are getting hit and see if you make it further? Chrome starts the plugin out of process, so it's going to expect those entrypoints to finish in a timely manner or else it'll likely kill the plugin.

Also, the --plugin-startup-dialog option may be useful to in in some cases just so you can see for sure when the plugin is starting; also, it may be useful if you wanted to attach a debugger and see if you can find more info that way. Just an FYI. I'd also try loading it in firefox; sometimes loading a plugin in another browser gives you different information. If it works in one and not the other, that can tell you something as well.

taxilian
  • 14,229
  • 4
  • 34
  • 73
  • I removed all the MessageBoxes, but the exact same thing happens. I also tried the --plugin-startup-dialog, and it popped up a window with the process ID. I'm kinda new to mingw though, so how would I attach a debugger? Also, where do I put my npplugin.dll in order for Firefox to find it? I'm using the manifest.json to get Chrome to load it... – jjv360 Oct 23 '11 at 01:20
  • I don't know; I build plugins on windows using visual studio, because it's a lot easier to use. You could also look at FireBreath (firebreath.org), since it makes all of this a heck of a lot easier. Disclaimer: I wrote FireBreath. – taxilian Oct 23 '11 at 07:30
  • Later I'm going to try `gdb` to try and see what the problem is. I wanted to understand NPAPI and NPRuntime, which is why I decided not to use FireBreath, but if I can't get it going, I'll start using it. – jjv360 Oct 23 '11 at 09:18
0

mm try to change in NP_GetEntryPoints()

  pFuncs->newp                = &NPP_New;
  pFuncs->destroy             = &NPP_Destroy;

(...)

to

  pFuncs->newp                = NPP_New;
  pFuncs->destroy             = NPP_Destroy;
Aurelio De Rosa
  • 21,856
  • 8
  • 48
  • 71
neutrino38
  • 111
  • 1
  • 3