Re: QueueUserWorkItem with functions in a DLL, how to unload safely?



Hello Skywing,

Skywing wrote:
>
> Well, that's a tricky thing to solve. The easiest way to do it is to wrap
> the call to the DLL-provided work item routine in a function that will
> always remain loaded (such as a function present in the main .exe for the
> process).
>
> For example, you might have the .exe export a QueueUserWorkItem-type
> function to the DLL. This function would call QueueUserWorkItem internally,
> but instead of passing the ThreadProc/Context directly to QueueUserWorkItem,
> it would instead pass the address of a wrapper function and a structure that
> contains the original function to call and the original context.
>
> For example... (assume this code resides in the main .exe image and plugin
> DLLs call into MyQueueUserWorkItem to queue a work item - this code would in
> reality need to track reference counts on a per dll basis and not globally,
> but that's specific to your implementation so I just used a global reference
> count to get the idea across):

You indeed got the idea across, thanks for your answer. I had the almost
same idea in case I cannot find a more elegant solution. Meanwhile I
found a method that looks even a bit more elegant to me. Im case you are
interested, read on:

As part of the initialization, after loading the plugin, the exe passes
to the plugin a function pointer of type

typedef BOOL (__stdcall *MA_QUEUE_USER_WORK_ITEM)(LPTHREAD_START_ROUTINE
fnThreadPoolFunc, LPVOID pContext, ULONG dwFlags, LPLONG plRefCount);

and this points to the function MAQueueUserWorkItem which is implemented
in the exe (and which therefore stays alive during the whole process):

typedef struct tagQUWI_PACKET
{
LPTHREAD_START_ROUTINE m_pfnThreadPoolFunc;
LPVOID m_lpContext;
LPLONG m_plRefCount;
}
QUWI_PACKET, *PQUWI_PACKET;

BOOL __stdcall MAQueueUserWorkItem(LPTHREAD_START_ROUTINE
fnThreadPoolFunc, LPVOID pContext, ULONG dwFlags, LPLONG plRefCount)
{
#ifdef _DEBUG
MA_QUEUE_USER_WORK_ITEM pfn = MAQueueUserWorkItem;
pfn = NULL;
#endif

if(!fnThreadPoolFunc || !plRefCount)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

PQUWI_PACKET pqwip = (PQUWI_PACKET)LocalAlloc(LPTR,
sizeof(QUWI_PACKET));
if(!pqwip)
return FALSE; // use the LocalAlloc last error

pqwip->m_lpContext = pContext;
pqwip->m_plRefCount = plRefCount;
pqwip->m_pfnThreadPoolFunc = fnThreadPoolFunc;

if (QueueUserWorkItem(SafeThreadProc, pqwip, dwFlags))
return TRUE;
DWORD dwLastError = GetLastError();
VERIFY(!LocalFree(pqwip));
SetLastError(dwLastError);
return FALSE;
}

As you can see, it is a wrapper around QueueUserWorkItem with an
additional refcount parameter, which should be implemented in each
plugin as a global zero-initialized volatile variable. The thread pool
function SafeThreadProc that it passes to the real QUWI is again
implemented in the exe and looks like so:

static DWORD WINAPI SafeThreadProc(LPVOID lpParam)
{
ASSERT(lpParam);
QUWI_PACKET qwip = *(PQUWI_PACKET)lpParam;
VERIFY(!LocalFree((PQUWI_PACKET)lpParam));

ASSERT((*qwip.m_plRefCount)>=0);
VERIFY(0 < InterlockedIncrement(qwip.m_plRefCount));

DWORD dwRetval = qwip.m_pfnThreadPoolFunc(qwip.m_lpContext);

ASSERT((*qwip.m_plRefCount)>0);
VERIFY(0 <= InterlockedDecrement(qwip.m_plRefCount));

return dwRetval;
}

All that a plugin now has to do is call this function pointer that
points to MAQueueUserWorkItem just like it would call QueueUserWorkItem
and additionally pass it the address of its per-plugin global refcounter
variable. Within the plugin's uninitialization function, it can then do
a semi-busy wait loop until its global refcount has dropped to zero,
like so:

while (g_lRefCount)
{
TRACE(_T("Spinning in a semi-busy loop until all thread pool
functions have finished executing\n"));
Sleep(100);
}

Any comments?

--
Stefan
.



Relevant Pages

  • Re: QueueUserWorkItem with functions in a DLL, how to unload safely?
    ... This opaque handle would identify a plugin "instance" to the host ... and provide among other things the reference count for the plugin ... you might have the .exe export a QueueUserWorkItem-type ... This function would call QueueUserWorkItem ...
    (microsoft.public.win32.programmer.kernel)
  • Re: Downloading programs comes up like images!
    ... If it is only .exe files, then read on and see if this is relevant: ... Remove the plugin with Regedit.exe ... >Download certain EXEs that are initiated by scripts (i.e. ... just how do you reinstall IE6 on ...
    (microsoft.public.windows.inetexplorer.ie6.browser)
  • Re: Plugin Architecture Question - Best Practice For Forms Update
    ... DLL's used by an EXE don't run on a separate thread. ... class are held in a DLL that's used by your EXE. ... In cases where you use the more reliable timers you must ... > I have an application that follows a simple plugin architecture. ...
    (microsoft.public.dotnet.framework.windowsforms)
  • Re: Downloading programs comes up like images!
    ... They just delete the invalid plugin and forget it. ... >Indeed- the Flashget click catcher plugin existed. ... >>If it is only .exe files, then read on and see if this ... >>>Download certain EXEs that are initiated by scripts ...
    (microsoft.public.windows.inetexplorer.ie6.browser)