When implementing DVP_Init/DVP_InitEx/DVP_Uninit in a viewer plugin, and VFS_Init/VFS_Uninit in a virtual filesystem plugin, you need to keep a reference count of how many times Init has been called vs Uninit.
- Be prepared for nested pairs of calls to the Init/Uninit functions. That is, Init may be called when you are already initialised.
- Be prepared for more than one thread to use those functions.
- Be prepared to be fully uninitialised and then re-initialised again later.
The only guarantee about the number of Init/Uninit calls is that they will, eventually, cancel each other out.
The example below uses critical sections to make itself thread-safe since the Init/Uninit calls don't always come from the same thread.
Also note that if your Init function fails then it is your responsibility to clean-up whatever it created. Opus won't call Uninit in that situation (which makes sense as it has no way of knowing that your Uninit function can properly clean-up an Init that aborted half-way through). You can call your own Uninit function (as in the example below) if it's suitably written. Either way, remember that a failed Init should not leave the reference counter increased.
- Code: Select all
static int s_refCount = 0;
static CRITICAL_SECTION s_cs; // Initialised/Deleted by DllMain
BOOL DVP_Init()
{
CriticalSectionScoper css(&s_cs);
BOOL bInitSuccess = FALSE;
if (0 < s_refCount++)
{
bInitSuccess = TRUE;
}
else
{
// ...Your init code here...
if (!bInitSuccess)
{
// If our Uninit function can work out what was allocated
// by itself, even if Init failed half-way through, then we
// can simply call it here to clean up our mess.
DVP_Uninit();
}
}
return bInitSuccess;
}
void DVP_Uninit(void)
{
CriticalSectionScoper css(&s_cs);
if (0 < s_refCount)
{
if (0 == --s_refCount)
{
// ...Your clean-up code here...
}
}
}
CriticalSectionScoper is not really important, but here it is anyway. It's a tiny class that enters a critical section and automatically releases it when the object goes out of scope:
- Code: Select all
class CriticalSectionScoper
{
public:
CriticalSectionScoper(CRITICAL_SECTION *pCS) : m_pCS(pCS) { EnterCriticalSection(m_pCS); }
/*not virtual*/ ~CriticalSectionScoper() { LeaveCriticalSection(m_pCS); }
void Lock() const { EnterCriticalSection(m_pCS); }
void Unlock() const { LeaveCriticalSection(m_pCS); }
private: // Disallow copying
CriticalSectionScoper(const CriticalSectionScoper &rhs);
CriticalSectionScoper &operator=(const CriticalSectionScoper &rhs);
private:
CRITICAL_SECTION *m_pCS;
};
Here is the DllMain, again for completeness:
- Code: Select all
static HMODULE s_hDllModule = NULL;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// If DLL_PROCESS_ATTACH fails it should clean up everything it did
// as DllMain won't be called again after such a failure.
InitializeCriticalSection(&s_cs);
s_hDllModule = reinterpret_cast<HMODULE>(hModule);
// DisableThreadLibraryCalls(s_hDllModule); <-- No, do not do this in code using the CRT.
break;
case DLL_PROCESS_DETACH:
DeleteCriticalSection(&s_cs);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
default:
break;
}
return TRUE;
}
