How to use Win32 ThreadPool API? - c++

This version (based on this article) works:
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
VOID
CALLBACK
MyWorkCallback(
PTP_CALLBACK_INSTANCE Instance,
PVOID Parameter,
PTP_WORK Work
)
{
// Instance, Parameter, and Work not used in this example.
UNREFERENCED_PARAMETER(Instance);
UNREFERENCED_PARAMETER(Parameter);
UNREFERENCED_PARAMETER(Work);
DWORD threadId = GetCurrentThreadId();
BOOL bRet = FALSE;
//
// Do something when the work callback is invoked.
//
{
_tprintf(_T("MyWorkCallback: ThreadId = %d Task performed.\n"), threadId);
}
return;
}
int main()
{
TP_CALLBACK_ENVIRON CallBackEnviron;
PTP_POOL pool = NULL;
PTP_CLEANUP_GROUP cleanupgroup = NULL;
PTP_WORK_CALLBACK workcallback = MyWorkCallback;
PTP_TIMER timer = NULL;
PTP_WORK work = NULL;
InitializeThreadpoolEnvironment(&CallBackEnviron);
pool = CreateThreadpool(NULL);
SetThreadpoolThreadMaximum(pool, 1);
SetThreadpoolThreadMinimum(pool, 3);
cleanupgroup = CreateThreadpoolCleanupGroup();
SetThreadpoolCallbackPool(&CallBackEnviron, pool);
SetThreadpoolCallbackCleanupGroup(&CallBackEnviron, cleanupgroup, NULL);
work = CreateThreadpoolWork(workcallback, NULL, &CallBackEnviron);
for (int i = 0; i < 10; ++i)
{
SubmitThreadpoolWork(work);
}
}
However, this version also works (with the same work function from above):
int main()
{
PTP_WORK = CreateThreadpoolWork(workcallback, NULLPTR, NULLPTR);
for (int i = 0; i < 10; ++i)
{
SubmitThreadpoolWork(work);
}
}
What are the differences between the two versions (except for the minimum and maximum thread count)?
Why would I use one version over another?

This is covered in the documentation for InitializeThreadpoolEnvironment:
Create a callback environment if you plan to call one of the following functions to modify the environment:
SetThreadpoolCallbackCleanupGroup
SetThreadpoolCallbackLibrary
SetThreadpoolCallbackPool
SetThreadpoolCallbackPriority
SetThreadpoolCallbackRunsLong
If you need the functionality provided by one or more of the listed functions, then you need to create a callback environment. If you don't, you don't.
If in doubt when starting a new project, use the simple approach to begin with, and see whether it meets your needs. You can always go back and switch to the more complex variant if and when it becomes necessary.

Related

How to properly use WriteConsoleInput function for KEY_EVENT?

To terminate a blocking Input from another thread, I tried to simulate a input event or more precisely a kayboard input using WriteConsoleInput function.
#include<Windows.h>
#include<conio.h>
#include<thread>
void KillBlockingIO() {
DWORD entityWritten;
INPUT_RECORD inputs;
inputs.EventType = KEY_EVENT;
inputs.Event.KeyEvent.bKeyDown = true;
inputs.Event.KeyEvent.uChar.AsciiChar = VK_RETURN;
inputs.Event.KeyEvent.wRepeatCount = 0;
inputs.Event.KeyEvent.dwControlKeyState = 0;
inputs.Event.KeyEvent.wVirtualKeyCode = 0;
inputs.Event.KeyEvent.wVirtualScanCode = 0;
// inputs.Event = { true, 0, 0, 0, VK_RETURN, 0 }; // same as above
Sleep(2000);
WriteConsoleInputA(GetStdHandle(STD_INPUT_HANDLE), &inputs, 1, &entityWritten);
}
int main()
{
std::thread t(KillBlockingIO);
char c = _getch();
printf("character recieved without typing: %c\n", c);
t.join();
}
It is working but I'm not sure, I've used WriteConsoleInput function property because members like wVirtualKeyCode, wVirtualScanCode and dwControlKeyState is set zero. No matter what value I pass, It is always going to have same result. It is also not very well Documented. I tried finding code examples but there is no such example.
what is purpose of these parameters and How to use WriteConsoleInput function properly?

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.

Setting a hardwarebreakpoint in multithreaded application doesn't fire

I wrote a little debugger for analysing and looging certain problems. Now I implemented a hardwarebreakpoint for detecting the access of a memory address being overwritten. When I run my debugger with a test process, then everything works fine. When I access the address, the breakpoint fires and the callstack is logged. The problem is, when I run the same against an application running multiple threads. I'm replicating the breakpoint into every thread that gets created and also the main thread. None of the functions report an error and everything looks fine, but when the address is accessed, the breakpoint never fires.
So I wonder if there is some documentation where this is described or if there are additionaly things that I have to do in case of a multithreaded application.
The function to set the breakpoint is this:
#ifndef _HARDWARE_BREAKPOINT_H
#define _HARDWARE_BREAKPOINT_H
#include "breakpoint.h"
#define MAX_HARDWARE_BREAKPOINT 4
#define REG_DR0_BIT 1
#define REG_DR1_BIT 4
#define REG_DR2_BIT 16
#define REG_DR3_BIT 64
class HardwareBreakpoint : public Breakpoint
{
public:
typedef enum
{
REG_INVALID = -1,
REG_DR0 = 0,
REG_DR1 = 1,
REG_DR2 = 2,
REG_DR3 = 3
} Register;
typedef enum
{
CODE,
READWRITE,
WRITE,
} Type;
typedef enum
{
SIZE_1,
SIZE_2,
SIZE_4,
SIZE_8,
} Size;
typedef struct
{
void *pAddress;
bool bBusy;
Type nType;
Size nSize;
Register nRegister;
} Info;
public:
HardwareBreakpoint(HANDLE hThread);
virtual ~HardwareBreakpoint(void);
/**
* Sets a hardware breakpoint. If no register is free or an error occured
* REG_INVALID is returned, otherwise the hardware register for the given breakpoint.
*/
HardwareBreakpoint::Register set(void *pAddress, Type nType, Size nSize);
void remove(void *pAddress);
void remove(Register nRegister);
inline Info const *getInfo(Register nRegister) const { return &mBreakpoint[nRegister]; }
private:
typedef Breakpoint super;
private:
Info mBreakpoint[MAX_HARDWARE_BREAKPOINT];
size_t mRegBit[MAX_HARDWARE_BREAKPOINT];
size_t mRegOffset[MAX_HARDWARE_BREAKPOINT];
};
#endif // _HARDWARE_BREAKPOINT_H
void SetBits(DWORD_PTR &dw, size_t lowBit, size_t bits, size_t newValue)
{
DWORD_PTR mask = (1 << bits) - 1;
dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
}
HardwareBreakpoint::HardwareBreakpoint(HANDLE hThread)
: super(hThread)
{
mRegBit[REG_DR0] = REG_DR0_BIT;
mRegBit[REG_DR1] = REG_DR1_BIT;
mRegBit[REG_DR2] = REG_DR2_BIT;
mRegBit[REG_DR3] = REG_DR3_BIT;
CONTEXT ct;
mRegOffset[REG_DR0] = reinterpret_cast<size_t>(&ct.Dr0) - reinterpret_cast<size_t>(&ct);
mRegOffset[REG_DR1] = reinterpret_cast<size_t>(&ct.Dr1) - reinterpret_cast<size_t>(&ct);
mRegOffset[REG_DR2] = reinterpret_cast<size_t>(&ct.Dr2) - reinterpret_cast<size_t>(&ct);
mRegOffset[REG_DR3] = reinterpret_cast<size_t>(&ct.Dr3) - reinterpret_cast<size_t>(&ct);
memset(&mBreakpoint[0], 0, sizeof(mBreakpoint));
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
mBreakpoint[i].nRegister = (Register)i;
}
HardwareBreakpoint::Register HardwareBreakpoint::set(void *pAddress, Type nType, Size nSize)
{
CONTEXT ct = {0};
super::setAddress(pAddress);
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if(!GetThreadContext(getThread(), &ct))
return HardwareBreakpoint::REG_INVALID;
size_t iReg = 0;
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
{
if (ct.Dr7 & mRegBit[i])
mBreakpoint[i].bBusy = true;
else
mBreakpoint[i].bBusy = false;
}
Info *reg = NULL;
// Address already used?
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
{
if(mBreakpoint[i].pAddress == pAddress)
{
iReg = i;
reg = &mBreakpoint[i];
break;
}
}
if(reg == NULL)
{
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
{
if(!mBreakpoint[i].bBusy)
{
iReg = i;
reg = &mBreakpoint[i];
break;
}
}
}
// No free register available
if(!reg)
return HardwareBreakpoint::REG_INVALID;
*(void **)(((char *)&ct)+mRegOffset[iReg]) = pAddress;
reg->bBusy = true;
ct.Dr6 = 0;
int st = 0;
if (nType == CODE)
st = 0;
if (nType == READWRITE)
st = 3;
if (nType == WRITE)
st = 1;
int le = 0;
if (nSize == SIZE_1)
le = 0;
else if (nSize == SIZE_2)
le = 1;
else if (nSize == SIZE_4)
le = 3;
else if (nSize == SIZE_8)
le = 2;
SetBits(ct.Dr7, 16 + iReg*4, 2, st);
SetBits(ct.Dr7, 18 + iReg*4, 2, le);
SetBits(ct.Dr7, iReg*2, 1, 1);
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if(!SetThreadContext(getThread(), &ct))
return REG_INVALID;
return reg->nRegister;
}
I'm setting the breakpoint in the main debugger loop whenever a new thread is created CREATE_THREAD_DEBUG_EVENT but looking at the sourcecode of GDB it seems not to be done there, so maybe that is to early?
So I finally found the answer to this problem.
In the debug event loop, I'm monitoring the events that windows sends me. One of those events is CREATE_THREAD_DEBUG_EVENT which I used to set the hardware breakpoint whenever a new thread was created.
The problem is, that the notification of this event comes before the thread got actually started. So Windows is setting the context for the first time AFTER this event is sent, which of course overwrites any context data that I have set before.
The solution I implemented now is, when a CREATE_THREAD_DEBUG_EVENT comes I put a software breakpoint at the start adress of the thread, so that the first instruction is my breakpoint. When I receive the breakpoint event, I restore the original code and install the hardware breakpoint, which now fires fine.
If there is a better solution, I'm all ears. :)

Create a function with unique function pointer in runtime

When calling WinAPI functions that take callbacks as arguments, there's usually a special parameter to pass some arbitrary data to the callback. In case there's no such thing (e.g. SetWinEventHook) the only way we can understand which of the API calls resulted in the call of the given callback is to have distinct callbacks. When we know all the cases in which the given API is called at compile-time, we can always create a class template with static method and instantiate it with different template arguments in different call sides. That's a hell of a work, and I don't like doing so.
How do I create callback functions at runtime so that they have different function pointers?
I saw a solution (sorry, in Russian) with runtime assembly generation, but it wasn't portable across x86/x64 archtectures.
You can use the closure API of libffi. It allows you to create trampolines each with a different address. I implemented a wrapping class here, though that's not finished yet (only supports int arguments and return type, you can specialize detail::type to support more than just int). A more heavyweight alternative is LLVM, though if you're dealing only with C types, libffi will do the job fine.
I've come up with this solution which should be portable (but I haven't tested it):
#define ID_PATTERN 0x11223344
#define SIZE_OF_BLUEPRINT 128 // needs to be adopted if uniqueCallbackBlueprint is complex...
typedef int (__cdecl * UNIQUE_CALLBACK)(int arg);
/* blueprint for unique callback function */
int uniqueCallbackBlueprint(int arg)
{
int id = ID_PATTERN;
printf("%x: Hello unique callback (arg=%d)...\n", id, arg);
return (id);
}
/* create a new unique callback */
UNIQUE_CALLBACK createUniqueCallback(int id)
{
UNIQUE_CALLBACK result = NULL;
char *pUniqueCallback;
char *pFunction;
int pattern = ID_PATTERN;
char *pPattern;
char *startOfId;
int i;
int patterns = 0;
pUniqueCallback = malloc(SIZE_OF_BLUEPRINT);
if (pUniqueCallback != NULL)
{
pFunction = (char *)uniqueCallbackBlueprint;
#if defined(_DEBUG)
pFunction += 0x256; // variable offset depending on debug information????
#endif /* _DEBUG */
memcpy(pUniqueCallback, pFunction, SIZE_OF_BLUEPRINT);
result = (UNIQUE_CALLBACK)pUniqueCallback;
/* replace ID_PATTERN with requested id */
pPattern = (char *)&pattern;
startOfId = NULL;
for (i = 0; i < SIZE_OF_BLUEPRINT; i++)
{
if (pUniqueCallback[i] == *pPattern)
{
if (pPattern == (char *)&pattern)
startOfId = &(pUniqueCallback[i]);
if (pPattern == ((char *)&pattern) + sizeof(int) - 1)
{
pPattern = (char *)&id;
for (i = 0; i < sizeof(int); i++)
{
*startOfId++ = *pPattern++;
}
patterns++;
break;
}
pPattern++;
}
else
{
pPattern = (char *)&pattern;
startOfId = NULL;
}
}
printf("%d pattern(s) replaced\n", patterns);
if (patterns == 0)
{
free(pUniqueCallback);
result = NULL;
}
}
return (result);
}
Usage is as follows:
int main(void)
{
UNIQUE_CALLBACK callback;
int id;
int i;
id = uniqueCallbackBlueprint(5);
printf(" -> id = %x\n", id);
callback = createUniqueCallback(0x4711);
if (callback != NULL)
{
id = callback(25);
printf(" -> id = %x\n", id);
}
id = uniqueCallbackBlueprint(15);
printf(" -> id = %x\n", id);
getch();
return (0);
}
I've noted an interresting behavior if compiling with debug information (Visual Studio). The address obtained by pFunction = (char *)uniqueCallbackBlueprint; is off by a variable number of bytes. The difference can be obtained using the debugger which displays the correct address. This offset changes from build to build and I assume it has something to do with the debug information? This is no problem for the release build. So maybe this should be put into a library which is build as "release".
Another thing to consider whould be byte alignment of pUniqueCallback which may be an issue. But an alignment of the beginning of the function to 64bit boundaries is not hard to add to this code.
Within pUniqueCallback you can implement anything you want (note to update SIZE_OF_BLUEPRINT so you don't miss the tail of your function). The function is compiled and the generated code is re-used during runtime. The initial value of id is replaced when creating the unique function so the blueprint function can process it.

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;
}