Refactor project structure
This commit is contained in:
106
modules/ANSCV/dllmain.cpp
Normal file
106
modules/ANSCV/dllmain.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// dllmain.cpp : Defines the entry point for the DLL application.
|
||||
#include "pch.h"
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
|
||||
// ── Global handle registry for shutdown cleanup ──────────────────────────
|
||||
// Streaming handle creators register a stop-callback on creation.
|
||||
// DLL_PROCESS_DETACH invokes all callbacks so threads exit before DLL unmaps.
|
||||
|
||||
static std::mutex& shutdownMutex() {
|
||||
static std::mutex m;
|
||||
return m;
|
||||
}
|
||||
|
||||
static std::unordered_map<void*, std::function<void()>>& shutdownRegistry() {
|
||||
static std::unordered_map<void*, std::function<void()>> r;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Called by Create*Handle functions to register a cleanup callback
|
||||
extern "C" void anscv_register_handle(void* handle, void(*stopFn)(void*)) {
|
||||
if (!handle || !stopFn) return;
|
||||
std::lock_guard<std::mutex> lk(shutdownMutex());
|
||||
shutdownRegistry()[handle] = [=]() { stopFn(handle); };
|
||||
}
|
||||
|
||||
// Called by Release*Handle functions to unregister (handle properly cleaned up)
|
||||
extern "C" void anscv_unregister_handle(void* handle) {
|
||||
if (!handle) return;
|
||||
std::lock_guard<std::mutex> lk(shutdownMutex());
|
||||
shutdownRegistry().erase(handle);
|
||||
}
|
||||
|
||||
// Stop all leaked handles
|
||||
static void stopAllHandles() {
|
||||
std::unordered_map<void*, std::function<void()>> copy;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(shutdownMutex());
|
||||
copy.swap(shutdownRegistry());
|
||||
}
|
||||
for (auto& [ptr, fn] : copy) {
|
||||
try { fn(); } catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
) noexcept
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
// Pin the DLL so it is never unmapped from the process address space.
|
||||
// ANSCV creates background threads (RTSP receive, timers) that may
|
||||
// still be running when LabVIEW's CLR/COM shutdown unloads the DLL.
|
||||
// If the DLL is unmapped while threads are executing its code, the
|
||||
// threads crash with an access violation at <Unloaded_ANSCV.dll>.
|
||||
// Pinning keeps the code pages mapped; the OS kills all threads when
|
||||
// the process exits, so this is safe and is Microsoft's recommended
|
||||
// pattern for DLLs that own threads.
|
||||
{
|
||||
HMODULE hSelf = nullptr;
|
||||
GetModuleHandleExW(
|
||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_PIN,
|
||||
reinterpret_cast<LPCWSTR>(&DllMain),
|
||||
&hSelf);
|
||||
}
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
// CRITICAL: Do NOT call stopAllHandles() here.
|
||||
//
|
||||
// DllMain holds the OS loader lock (LdrpLoaderLock). Destroying
|
||||
// streaming handles (CRtspPlayer → CVideoPlayer → CWAudioPlay)
|
||||
// calls dsound!CThread::Terminate which waits for the DirectSound
|
||||
// admin thread to exit. That thread needs the loader lock to shut
|
||||
// down → classic deadlock. (Confirmed by crash dump analysis:
|
||||
// Thread 0 holds LdrpLoaderLock and waits for dsound thread 331,
|
||||
// which is blocked on LdrpLoaderLock.)
|
||||
//
|
||||
// If lpReserved is non-NULL the process is terminating — the OS
|
||||
// will tear down all threads and reclaim resources; cleanup is
|
||||
// unnecessary and dangerous. If lpReserved is NULL (dynamic
|
||||
// FreeLibrary), LabVIEW should have called Release*Handle first.
|
||||
// Log leaked handles for diagnostics but do NOT stop them here.
|
||||
if (lpReserved == nullptr) {
|
||||
// Dynamic unload — warn about leaked handles (non-blocking)
|
||||
try {
|
||||
std::lock_guard<std::mutex> lk(shutdownMutex());
|
||||
if (!shutdownRegistry().empty()) {
|
||||
std::cerr << "[ANSCV] WARNING: " << shutdownRegistry().size()
|
||||
<< " streaming handle(s) leaked at DLL unload."
|
||||
<< " Call Release*Handle before FreeLibrary." << std::endl;
|
||||
}
|
||||
} catch (...) {}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
Reference in New Issue
Block a user