NPAPI Plugin for Chrome doesn't get past NP_Initialize - c++

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

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.

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.

mm try to change in NP_GetEntryPoints()
pFuncs->newp = &NPP_New;
pFuncs->destroy = &NPP_Destroy;
(...)
to
pFuncs->newp = NPP_New;
pFuncs->destroy = NPP_Destroy;

Related

Dynamic loading Leadtools DLLs

I am using Leadtools 17.5. If I statically link the Leadtools Dlls into my 64 bit C++ Application and then call L_SetLicenseBuffer everything works fine and the return value is zero. But for security reasons, the final product is not allowed to add those DLLs into the System32 folder and is also not allowed to change the system path, and since multiple applications are using the tools I want to install them in a common folder (C:\Program Files\Common Files\LeadTools\17.5 for example) and use AddDllDirectory to add the path to the DLL search path. So I decided to load the DLLs dynamically at the run-time. So I created a definition for the function like this:
typedef L_INT (EXT_FUNCTION* TL_SetLicenseBuffer)(L_UCHAR* pLicenseBuffer, L_SSIZE_T nSize, L_TCHAR* pszDeveloperKey);
typedef L_BOOL (EXT_FUNCTION* TL_IsSupportLocked)(L_UINT uType);
then created a function pointer like this:
TL_SetLicenseBuffer pfSetLicenseBuffer = NULL;
TL_IsSupportLocked pfIsSupportLocked = NULL;
then add the paths to where the DLLs are to the DLL search path:
AddDllDirectory(LEAD_DLL_PATH);
AddDllDirectory(LEAD_FILTER_PATH);
and set the default directory search path for DLLs to be the user defined:
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_USER_DIRS);
then load the DLL and get the address of the functions I need:
HINSTANCE hKrn = LoadLibrary(L"ltkrnx.dll");
pfSetLicenseBuffer = (TL_SetLicenseBuffer)GetProcAddress(hKrn, "L_SetLicenseBuffer");
pfIsSupportLocked = (TL_IsSupportLocked)GetProcAddress(hKrn, "L_IsSupportLocked");
now if I use the function pointer with the same parameters as before, the function fails and returns -13 and any subsequent call to for example to pfIsSupportLocked shows the nag dialog:
retCode = pfSetLicenseBuffer(pLicenseData, LicSize, pKeyStr); // retCode is -13
pfIsSupportLocked(L_SUPPORT_DOCUMENT); // Shows nag dialog
Does anyone know how I can fix this?
Thank you
Sam
The first thing you need to do is check the debugger output and make sure that the DLL you are expecting to get loaded is the one getting loaded by verifying the path. It is possible that you have multiple versions of LTKRNX.DLL in your search path. I have tested your code here and it returned SUCCESS:
typedef L_INT (EXT_FUNCTION* TL_SetLicenseBuffer)(L_UCHAR* pLicenseBuffer, L_SSIZE_T nSize, L_TCHAR* pszDeveloperKey);
typedef L_BOOL (EXT_FUNCTION* TL_IsSupportLocked)(L_UINT uType);
HINSTANCE hKrn = LoadLibrary(L"ltkrnx.dll");
TL_SetLicenseBuffer pfSetLicenseBuffer = NULL;
TL_IsSupportLocked pfIsSupportLocked = NULL;
pfSetLicenseBuffer = (TL_SetLicenseBuffer)GetProcAddress(hKrn, "L_SetLicenseBuffer");
pfIsSupportLocked = (TL_IsSupportLocked)GetProcAddress(hKrn, "L_IsSupportLocked");
L_INT retCode = pfSetLicenseBuffer(szLICAnsi, _countof(szLICAnsi), pKeyStr);
if(retCode == SUCCESS)
bRet = pfIsSupportLocked(L_SUPPORT_DOCUMENT);
else
printf("Problem!");
Also what PaulMcKenzie suggested is another way to verify that your calls to LoadLibrary are working correctly. If you still cannot figure it out, you can contact our Technical Support to assist you with this issue at support#leadtools.com
I was not able to make the dynamic loading to work at all, but I was able to use Delay loading to work.What I had to do was to go back to linking the extracted .Lib files to my application and then tell compiler to load the associated DLLs with delay, which gave me a chance to create Notification Hooks to __pfnDliNotifyHook2 and __pfnDliFailureHook2 and that way I could use LoadLibrary to load the delayed loaded Dlls from correct location.But that only fixed half the problem because some of these Dlls are dependent on other DLLs and when I used the full path to load the DLL that I wanted, it could not find the secondary DLLs (which were located in the same directory as the one I was loading) and that would cause LoadLibrary to fail. The solution was to keep track of those dependencies and pre-load them. I am including some of the code to fix the issue for anyone whom might run into similar situation later on.P. S. I am using Embarcadero's C++ Builder, so Some of the objects like the Strings, TStringList and Exception may not be exactly what everyone is familiar with, but the concept should work in VC++ as well.
#include <map>
struct TDllDependency
{
TStringList* Dependency;
HMODULE hDll;
__fastcall TDllDependency(void)
{
hDll = NULL;
Dependency = new TStringList();
}
virtual __fastcall ~TDllDependency(void)
{
delete Dependency;
}
};
class TDllModList : public std::map<System::String, TDllDependency>
{
public:
void __fastcall CheckDependency(const System::String& aName);
};
//---------------------------------------------------------------------------
System::String __fastcall GetLtDllPath(void)
{
wchar_t* pfPath = NULL;
System::String dllPath;
SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, KF_FLAG_DEFAULT, NULL, &pfPath);
if (NULL != pfPath)
{
dllPath = IncludeTrailingBackslash(pfPath) + L"LeadTools\\17.5\\";
::CoTaskMemFree(pfPath);
}
return dllPath;
}
System::String mDllPath(GetLtDllPath());
TDllModList DllModList;
void __fastcall InitDllDepends()
{
DllModList.clear();
#if defined(_WIN64)
DllModList[L"ltimgefxx.dll"].Dependency->CommaText = L"ltdisx.dll,ltimgutlx.dll";
DllModList[L"ltefxx.dll"].Dependency->CommaText = L"ltdisx.dll,ltimgutlx.dll";
DllModList[L"ltimgcorx.dll"].Dependency->CommaText = L"ltdisx.dll,ltimgutlx.dll";
DllModList[L"ltdlgimgefxx.dll"].Dependency->CommaText = L"ltdisx.dll,ltdlgkrnx.dll,ltdlgcomx.dll,ltdlgctrlx.dll,ltdlgutlx.dll,ltimgefxx.dll,ltimgsfxx.dll,ltimgcorx.dll,ltimgclrx.dll";
DllModList[L"ltdlgutlx.dll"].Dependency->CommaText = L"ltdisx.dll,ltfilx.dll,ltdlgkrnx.dll,ltimgclrx.dll,ltimgcorx.dll,ltimgefxx.dll,ltimgsfxx.dll";
DllModList[L"ltdlgctrlx.dll"].Dependency->CommaText = L"ltdlgutlx.dll,ltdlgkrnx.dll,ltdisx.dll,ltfilx.dll,ltimgefxx.dll";
DllModList[L"ltdlgcomx.dll"].Dependency->CommaText = L"ltdlgkrnx.dll,ltdlgctrlx.dll,ltdlgutlx.dll";
#elif defined(__WIN32__)
DllModList[L"ltimgefxu.dll"].Dependency->CommaText = L"ltdisu.dll,ltimgutlu.dll";
DllModList[L"ltefxu.dll"].Dependency->CommaText = L"ltdisu.dll,ltimgutlu.dll";
DllModList[L"ltimgcoru.dll"].Dependency->CommaText = L"ltdisu.dll,ltimgutlu.dll";
DllModList[L"ltdlgimgefxu.dll"].Dependency->CommaText = L"ltdisu.dll,ltdlgkrnu.dll,ltdlgcomu.dll,ltdlgctrlu.dll,ltdlgutlu.dll,ltimgefxu.dll,ltimgsfxu.dll,ltimgcoru.dll,ltimgclru.dll";
DllModList[L"ltdlgutlu.dll"].Dependency->CommaText = L"ltdisu.dll,ltfilu.dll,ltdlgkrnu.dll,ltimgclru.dll,ltimgcoru.dll,ltimgefxu.dll,ltimgsfxu.dll";
DllModList[L"ltdlgctrlu.dll"].Dependency->CommaText = L"ltdlgutlu.dll,ltdlgkrnu.dll,ltdisu.dll,ltfilu.dll,ltimgefxu.dll";
DllModList[L"ltdlgcomu.dll"].Dependency->CommaText = L"ltdlgkrnu.dll,ltdlgctrlu.dll,ltdlgutlu.dll";
#endif
};
HMODULE SafeLoadLeadDll(const System::String tName)
{
System::String tPath;
HMODULE retVal = NULL;
DllModList.CheckDependency(tName);
tPath = mDllPath + tName;
if(FileExists(tPath))
retVal = ::LoadLibrary(tPath.c_str());
return retVal;
}
FARPROC WINAPI MyDliNotifyHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
FARPROC retVal = NULL;
System::String tStr(pdli->szDll);
tStr = tStr.LowerCase();
if(dliNotePreLoadLibrary == dliNotify)
{
TDllModList::iterator i = DllModList.find(tStr);
if(DllModList.end() == i)
{
retVal = (FARPROC)SafeLoadLeadDll(tStr);
DllModList[tStr].hDll = (HMODULE)retVal;
}
else if(NULL == i->second.hDll)
{
i->second.hDll = SafeLoadLeadDll(tStr);
retVal = (FARPROC)i->second.hDll;
}
else
retVal = (FARPROC)i->second.hDll;
}
else if(dliFailLoadLib == dliNotify)
{
tStr = L"Compleatly falied to load " + tStr;
::OutputDebugString(tStr.c_str());
}
return retVal;
}
FARPROC WINAPI MyDliFailureHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
FARPROC retVal = NULL;
if(dliNotePreLoadLibrary == dliNotify)
{
System::String tMsg = pdli->szDll;
tMsg = L"Failed to load \"" + tMsg + L"\".\n" + SysErrorMessage(::GetLastError());
throw Exception(tMsg);
}
return retVal;
}
extern "C" PfnDliHook __pfnDliNotifyHook2 = MyDliNotifyHook;
extern "C" PfnDliHook __pfnDliFailureHook2 = MyDliFailureHook;
void __fastcall TDllModList::CheckDependency(const System::String& aName)
{
TDllModList::iterator i = find(aName);
if(end() != i)
{
int len = i->second.Dependency->Count;
int j;
System::String tPath;
for(j = 0; j < len; j++)
{
if(end() == find(i->second.Dependency->Strings[j]))
{
CheckDependency(i->second.Dependency->Strings[j]);
tPath = mDllPath + i->second.Dependency->Strings[j];
(*this)[i->second.Dependency->Strings[j]].hDll = ::LoadLibrary(tPath.c_str());
}
}
}
}
//---------------------------------------------------------------------------
And of course InitDllDepends(); should be called at the beginning of WinMain to set things up correctly.

c++ find command no longer works

This is some old code not written by me. It compiles with GCC 3.4.6, but now we are checking the build with GCC 4.4.7 and the build fails.
I hope this code is enough to go on:
list<Chapter*> * tocP; //Chapter is a class
tocP = NULL;
if (_searchChapter)
{
_chapter = _manager->GetCurrentChapter(); // _chapter is a Chapter*
}
else
{
tocP = _manager->GetTableOfContents();
if (tocP != NULL && tocP->size() > 0)
_chapter = tocP->front();
}
...
list<Chapter*>::iterator chp;
if (tocP != NULL && tocP->size() > 0)
for (chp=find(tocP->begin(),tocP->end(),_chapter); chp != tocP->end(); ++chp) // this code fails
{
//code to process chapter
}
error message is:
../src/HelpSearchC.C: In member function 'int HelpSearchC_i::DoSearch()':
../src/HelpSearchC.C:685: error: no matching function for call to 'find(std::_List_iterator<Chapter*>,
std::_List_iterator<Chapter*>, Chapter*&)'
You have to add #include <algorithm> on top of the file. The function find is defined within this header.

MinGW: Modify internet proxy options

I am trying to modify the proxy settings in C++ without using visual C++. I found this:
const wchar_t* proxyName = pnt.wc_str(); // pnt is a wxString declared earlier in the code.
INTERNET_PER_CONN_OPTION_LIST OptionList;
INTERNET_PER_CONN_OPTION Option[3];
unsigned long listSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
Option[0].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
Option[1].dwOption = INTERNET_PER_CONN_FLAGS;
Option[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
OptionList.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
OptionList.pszConnection = NULL;
OptionList.dwOptionCount = 3;
OptionList.dwOptionError = 0;
DWORD proxyType = PROXY_TYPE_DIRECT;
if (proxyName)
{
if (proxyName[0])
{
proxyType = PROXY_TYPE_PROXY;
}
}
Option[0].Value.pszValue = (LPWSTR)proxyName;
Option[1].Value.dwValue = proxyType;
if (on)
{
Option[2].Value.pszValue = (LPWSTR)L"";
}
else
{
Option[2].Value.pszValue = (LPWSTR)L"";
}
OptionList.pOptions = Option;
if (!InternetSetOption(0, INTERNET_OPTION_PER_CONNECTION_OPTION, &OptionList, listSize))
{
// handle error
}
InternetSetOption(0, INTERNET_OPTION_PROXY_SETTINGS_CHANGED, NULL, NULL);
However because I have to use MinGW and MinGW's wininet.h is very different, I get "'INTERNET_PER_CONN_OPTION_LIST' was not declared in this scope" among other "not declared" messages. In all my searching I have not found anything on it, thanks to visual c++ being so prominent. I found this but it has almost no views, doesn't have any answers, and has not been touched almost a month and it's not even quite what I am looking for. I can't seem to find any documentation either. Any ideas? Thanks in advanced!

How to mix two wav files using LibSoX

I am trying to mix two wav files using LibSoX on Windows. I can do this by using sox from the command line using the following command:
sox -m f1.wav f2.wav out.wav
However I want to do this programmatically using a C/C++ function and linking with LibSoX. I have built LibSoX successfully and have tried out the sample programs which do not cover a "mix" of two audio files.
Has someone done this earlier? It would be great if you can give a code snippet or at least some pointers to do this using LibSoX calls
Want to share the solution (workaround) which I made for the above issue. Basically, mixing is not exposed as an exported function call. Mixing can be done via the command line of course, so there could be two solutions: 1) spawning a process (sox.exe) via the program and 2) via libsox. I needed a libsox based solution as the function which was going to call the mixing function, could not spawn a process (constraint). So I moved the main function functionality from sox to a new method in libsox which I exported :) . So now I can pass the same command line switches to my function, and get the job done using libsox! This would be a "workaround" ideally, till Chris Bagwell exposes the mixing (and other missing) functionality from libsox.
Though this is an old question, I came across this same problem recently. In order to use the sox.c main method from your code, you'll need to reset all the globals after a call is made. sox.c was intended to run once then exit. To do that you can add a function like below. Then you can rename main, sox_main_private and things will basically work to use the command line calls directly building your argv array manually by calling sox_main()
// reset the globals so that multiple calls can be made to sox_main
static void cleanup_globals(void)
{
file_count = 0;
input_count = 0;
output_count = 0;
current_input = 0;
input_wide_samples = 0;
read_wide_samples = 0;
output_samples = 0;
input_eof = sox_false;
output_eof = sox_false;
user_abort = sox_false;
user_skip = sox_false;
user_restart_eff = sox_false;
success = 0;
files = NULL;
user_efftab = NULL;
user_efftab_size = 0;
effects_chain = NULL;
save_output_eff = NULL;
user_effargs_size = NULL; /* array: size of user_effargs for each chain */
nuser_effects = NULL; /* array: number of effects in each chain */
current_eff_chain = 0;
eff_chain_count = 0;
very_first_effchain = sox_true;
effects_filename = NULL;
play_rate_arg = NULL;
norm_level = NULL;
}
int sox_main(int argc, char *argv[])
{
#if DEBUG
printf("sox_main:\n");
int i = 0;
while (i < argc) {
printf("%s ", argv[i]);
i++;
}
printf("\n");
#endif
sox_main_private(argc, argv);
cleanup_globals();
return 0;
}

Calling Windows API with libffi on MinGW

I'm trying to have FFI support for my new programming language, which is written in C++ with QT Creator using the MinGW toolchain.
To do this I used a custom-built version of libffi found here: http://ftp.gnome.org/pub/GNOME/binaries/win32/dependencies/libffi-dev_3.0.6-1_win32.zip
I also tried it with another build: http://pkgs.org/fedora-14/fedora-updates-i386/mingw32-libffi-3.0.9-1.fc14.noarch.rpm.html by downloading the SRPM file on Linux, extracting it, and copying the needed files to a Windows partition.
Anyway, I included the required header file, added the import library to the project and put the .dll beside the application's .exe, it compiles and runs, calling MessageBeep() successfully. I tried it next with MessageBoxA(), but it keeps crashing. The debugger doesn't seem to provide much useful information (edit: beside the fact that a call to MessageBoxA did happen) so I keep fiddling with stuff and re-running to no avail.
To isolate the problem from the details of my language, I tried to manually call MessageBoxA by filling myself all the parameters, resulting in the code below, still crashing.
So my question distills to: How can I get the code snippet below to run under QT Creator/MinGW and actually show a message box?
#include "libffi/include/ffi.h"
#include <QLibrary>
void testMessageBox()
{
int n = 4;
ffi_cif cif;
ffi_type **ffi_argTypes = new ffi_type*[n];
void **values = new void*[n];
values[0] = new ulong(0);
values[1] = (void *) "hello";
values[2] = (void *) "mommy";
values[3] = new int32_t(0);
ffi_argTypes[0] = &ffi_type_ulong;
ffi_argTypes[1] = &ffi_type_pointer;
ffi_argTypes[2] = &ffi_type_pointer;
ffi_argTypes[3] = &ffi_type_uint32;
ffi_type *c_retType = &ffi_type_sint32;
int32_t rc; // return value
if (ffi_prep_cif(&cif, FFI_STDCALL, n, c_retType, ffi_argTypes) == FFI_OK)
{
QLibrary lib("user32.dll");
lib.load();
void *msgbox = lib.resolve("MessageBoxA");
ffi_call(&cif, (void (*)()) msgbox, &rc, values);
}
}
you should pass the address to the values array instead of the values. the working code under mingw64 is
#include <stdio.h>
#include <ffi.h>
#include <Windows.h>
int main()
{
ffi_cif cif;
HINSTANCE dllHandle = LoadLibrary("user32.dll");
int n = 4;
ffi_type *ffi_argTypes[4];
void *values[4];
UINT64 a=0;
UINT32 b=0;
TCHAR* s1= "hello";
TCHAR* s2= "hello2";
values[0] = &a;
values[1] = &s1;
values[2] = &s2;
values[3] = &b;
ffi_argTypes[0] = &ffi_type_uint64;
ffi_argTypes[1] = &ffi_type_pointer;
ffi_argTypes[2] = &ffi_type_pointer;
ffi_argTypes[3] = &ffi_type_uint;
ffi_type *c_retType = &ffi_type_sint;
ffi_type rc; // return value
if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 4, &ffi_type_sint, ffi_argTypes) == FFI_OK) {
ffi_call(&cif, FFI_FN(GetProcAddress(dllHandle,"MessageBoxA")), &rc, values);
}
return 0;
}