// dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include "ANSMOT.h" #include "ANSByteTrackNCNN.h" #include "ANSByteTrack.h" #include "ANSByteTrackEigen.h" #include "ANSOCSortTrack.h" #include "ANSUCMC.h" #include #include #include #include // DebugView: filter on "[ANSMOT]" — gated by ANSCORE_DEBUGVIEW in ANSLicense.h. // Handle registry with refcount — prevents use-after-free when // ReleaseANSMOTHandle is called while an operation is still running. // destructionStarted: set by the first Unregister caller; blocks new Acquires // and makes subsequent Unregister calls return false without deleting. // Prevents double-free when Release is raced on the same handle. struct MOTEntry { int refcount; bool destructionStarted; }; static std::unordered_map& MOTHandleRegistry() { static std::unordered_map s; return s; } static std::mutex& MOTHandleRegistryMutex() { static std::mutex m; return m; } static std::condition_variable& MOTHandleRegistryCV() { static std::condition_variable cv; return cv; } static void RegisterMOTHandle(ANSCENTER::ANSMOT* h) { std::lock_guard lk(MOTHandleRegistryMutex()); MOTHandleRegistry()[h] = { 1, false }; ANS_DBG("ANSMOT","Register: handle=%p (uint=%llu) registrySize=%zu", (void*)h, (unsigned long long)(uintptr_t)h, MOTHandleRegistry().size()); } static ANSCENTER::ANSMOT* AcquireMOTHandle(ANSCENTER::ANSMOT* h) { std::lock_guard lk(MOTHandleRegistryMutex()); auto it = MOTHandleRegistry().find(h); if (it == MOTHandleRegistry().end()) { ANS_DBG("ANSMOT","Acquire FAIL: handle=%p (uint=%llu) NOT in registry. registrySize=%zu", (void*)h, (unsigned long long)(uintptr_t)h, MOTHandleRegistry().size()); size_t i = 0; for (auto& kv : MOTHandleRegistry()) { ANS_DBG("ANSMOT"," registry[%zu] = %p (uint=%llu) refcount=%d destructionStarted=%d", i++, (void*)kv.first, (unsigned long long)(uintptr_t)kv.first, kv.second.refcount, kv.second.destructionStarted ? 1 : 0); } return nullptr; } if (it->second.destructionStarted) { ANS_DBG("ANSMOT","Acquire FAIL: handle=%p is being destroyed (destructionStarted=true)", (void*)h); return nullptr; } it->second.refcount++; ANS_DBG("ANSMOT","Acquire OK: handle=%p refcount=%d", (void*)h, it->second.refcount); return h; } static bool ReleaseMOTHandleRef(ANSCENTER::ANSMOT* h) { std::lock_guard lk(MOTHandleRegistryMutex()); auto it = MOTHandleRegistry().find(h); if (it == MOTHandleRegistry().end()) return false; it->second.refcount--; if (it->second.refcount <= 0) { MOTHandleRegistryCV().notify_all(); } return false; // Only Unregister deletes. } static bool UnregisterMOTHandle(ANSCENTER::ANSMOT* h) { std::unique_lock lk(MOTHandleRegistryMutex()); auto it = MOTHandleRegistry().find(h); if (it == MOTHandleRegistry().end()) { ANS_DBG("ANSMOT","Unregister: handle=%p NOT in registry (already gone)", (void*)h); return false; } if (it->second.destructionStarted) { ANS_DBG("ANSMOT","Unregister: handle=%p already being destroyed by another thread, returning false", (void*)h); return false; // Another thread already owns the delete. } ANS_DBG("ANSMOT","Unregister: handle=%p starting (refcount before=%d)", (void*)h, it->second.refcount); it->second.destructionStarted = true; it->second.refcount--; bool ok = MOTHandleRegistryCV().wait_for(lk, std::chrono::seconds(30), [&]() { auto it2 = MOTHandleRegistry().find(h); return it2 == MOTHandleRegistry().end() || it2->second.refcount <= 0; }); if (!ok) { ANS_DBG("ANSMOT","WARNING: Unregister timed out waiting for in-flight operations on handle=%p", (void*)h); OutputDebugStringA("WARNING: UnregisterMOTHandle timed out waiting for in-flight operations\n"); } MOTHandleRegistry().erase(h); return true; } // RAII guard — ensures ReleaseMOTHandleRef is always called class MOTHandleGuard { ANSCENTER::ANSMOT* engine; public: explicit MOTHandleGuard(ANSCENTER::ANSMOT* e) : engine(e) {} ~MOTHandleGuard() { if (engine) ReleaseMOTHandleRef(engine); } ANSCENTER::ANSMOT* get() const { return engine; } explicit operator bool() const { return engine != nullptr; } MOTHandleGuard(const MOTHandleGuard&) = delete; MOTHandleGuard& operator=(const MOTHandleGuard&) = delete; }; BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } /* enum MOTEType { BYTETRACKNCNN = 0, BYTETRACK = 1, BYTETRACKEIGEN=2, OCSORT=3 }; */ extern "C" __declspec(dllexport) int __cdecl CreateANSMOTHandle(ANSCENTER::ANSMOT **Handle, int motType) { ANS_DBG("ANSMOT","CreateANSMOTHandle: HandlePtr=%p, *Handle(in)=%p, motType=%d", (void*)Handle, (void*)(Handle ? *Handle : nullptr), motType); if (Handle == nullptr) { ANS_DBG("ANSMOT","Create FAIL: Handle is null"); return 0; } try { // Pure constructor: ignore *Handle(in). LabVIEW's CLF Node marshalling // reuses the same temp buffer per call site, so *Handle(in) often holds // leftover bytes from the previous Create's output even when the actual // LabVIEW wire is a different, freshly-allocated instance. Inspecting // *Handle(in) and destroying what we "see" tears down legitimate // parallel instances. (Same reasoning as CreateANSAWSHandle.) // Trade-off: a true double-Create on the same wire leaks the prior // handle -- caller's bug; the alternative is far worse. *Handle = nullptr; switch (motType) { case 0: //BYTETRACKNCNN =0 (*Handle) = new ANSCENTER::ANSByteTrack();// ANSByteTrackNCNN(); break; case 1: //BYTETRACK = 1 (*Handle) = new ANSCENTER::ANSByteTrack(); break; case 2://BYTETRACKEIGEN = 2 (*Handle) = new ANSCENTER::ANSByteTrack();// ANSByteTrackEigen(); break; case 3://UCMC = 3 (*Handle) = new ANSCENTER::ANSUCMCTrack(); break; case 4://OCSORT = 4 (*Handle) = new ANSCENTER::ANSOCSortTrack(); break; default: (*Handle) = new ANSCENTER::ANSByteTrack();// ANSByteTrackNCNN(); break; } if (*Handle == nullptr) { ANS_DBG("ANSMOT","Create FAIL: new returned null"); return 0; } else { ANS_DBG("ANSMOT","Create: allocated handle=%p (uint=%llu), calling Register...", (void*)*Handle, (unsigned long long)(uintptr_t)*Handle); RegisterMOTHandle(*Handle); ANS_DBG("ANSMOT","Create OK: handle=%p (uint=%llu)", (void*)*Handle, (unsigned long long)(uintptr_t)*Handle); return 1; } } catch (std::exception& e) { ANS_DBG("ANSMOT","Create EXCEPTION (std::exception): %s", e.what()); return 0; } catch (...) { ANS_DBG("ANSMOT","Create EXCEPTION (unknown)"); return 0; } } static int ReleaseANSMOTHandle_Impl(ANSCENTER::ANSMOT** Handle) { try { if (!Handle || !*Handle) { ANS_DBG("ANSMOT","Release: HandlePtr or *Handle is null, no-op"); return 1; } ANSCENTER::ANSMOT* h = *Handle; ANS_DBG("ANSMOT","Release called: handle=%p (uint=%llu)", (void*)h, (unsigned long long)(uintptr_t)h); if (!UnregisterMOTHandle(h)) { ANS_DBG("ANSMOT","Release: Unregister returned false (already gone or being destroyed by another thread), handle=%p", (void*)h); *Handle = nullptr; return 1; } h->Destroy(); delete h; *Handle = nullptr; ANS_DBG("ANSMOT","Release OK: handle=%p deleted, registry now has %zu entries", (void*)h, MOTHandleRegistry().size()); return 1; } catch (...) { ANS_DBG("ANSMOT","Release EXCEPTION (unknown)"); if (Handle) *Handle = nullptr; return 0; } } extern "C" __declspec(dllexport) int __cdecl ReleaseANSMOTHandle(ANSCENTER::ANSMOT **Handle) { ANS_DBG("ANSMOT","ReleaseANSMOTHandle: HandlePtr=%p, *Handle=%p", (void*)Handle, (void*)(Handle ? *Handle : nullptr)); __try { return ReleaseANSMOTHandle_Impl(Handle); } __except (EXCEPTION_EXECUTE_HANDLER) { ANS_DBG("ANSMOT","ReleaseANSMOTHandle: SEH exception caught"); if (Handle) *Handle = nullptr; return 0; } } extern "C" __declspec(dllexport) int __cdecl UpdateANSMOTParameters(ANSCENTER::ANSMOT * *Handle, const char* trackerParameter) { ANS_DBG("ANSMOT","UpdateANSMOTParameters: HandlePtr=%p, *Handle=%p, trackerParameter=%s", (void*)Handle, (void*)(Handle ? *Handle : nullptr), trackerParameter ? trackerParameter : "(null)"); if (Handle == nullptr || *Handle == nullptr) return -1; MOTHandleGuard guard(AcquireMOTHandle(*Handle)); if (!guard) return -1; try { bool result = guard.get()->UpdateParameters(trackerParameter); if (result) return 1; else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" __declspec(dllexport) int __cdecl UpdateANSMOT(ANSCENTER::ANSMOT * *Handle, int modelId, const char* detectionData, std::string & result) { ANS_DBG("ANSMOT","UpdateANSMOT: HandlePtr=%p, *Handle=%p, modelId=%d", (void*)Handle, (void*)(Handle ? *Handle : nullptr), modelId); if (Handle == nullptr || *Handle == nullptr) return -1; MOTHandleGuard guard(AcquireMOTHandle(*Handle)); if (!guard) return -1; try { result = guard.get()->Update(modelId, detectionData); if (result.empty())return 0; else return 1; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" __declspec(dllexport) int __cdecl UpdateANSMOT_LV(ANSCENTER::ANSMOT * *Handle, int modelId, const char* detectionData, LStrHandle trackerResult) { ANS_DBG("ANSMOT","UpdateANSMOT_LV: HandlePtr=%p, *Handle=%p, modelId=%d", (void*)Handle, (void*)(Handle ? *Handle : nullptr), modelId); if (Handle == nullptr || *Handle == nullptr) return -1; MOTHandleGuard guard(AcquireMOTHandle(*Handle)); if (!guard) return -1; try { std::string st = guard.get()->Update(modelId, detectionData); int size = static_cast(st.length()); MgErr error; error = DSSetHandleSize(trackerResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*trackerResult)->cnt = size; memcpy((*trackerResult)->str, st.c_str(), size); return 1; } else return 0; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } extern "C" __declspec(dllexport) int __cdecl UpdateANSMOTTracker(ANSCENTER::ANSMOT** Handle, int modelId, const std::vector& detectionObjects, std::vector& trackedObjects) { ANS_DBG("ANSMOT","UpdateANSMOTTracker: HandlePtr=%p, *Handle=%p, modelId=%d, detectionObjects.size=%zu", (void*)Handle, (void*)(Handle ? *Handle : nullptr), modelId, detectionObjects.size()); if (Handle == nullptr || *Handle == nullptr) return -1; MOTHandleGuard guard(AcquireMOTHandle(*Handle)); if (!guard) return -1; try { trackedObjects = guard.get()->UpdateTracker(modelId, detectionObjects); if (trackedObjects.empty()) return 0; else return 1; } catch (std::exception& e) { return 0; } catch (...) { return 0; } } // ============================================================================ // V2 LabVIEW API — Accept handle as uint64_t by value. // Eliminates Handle** pointer-to-pointer instability when LabVIEW calls // concurrently from multiple tasks. // LabVIEW CLFN: set Handle parameter to Numeric / Unsigned Pointer-sized Integer / Pass: Value // ============================================================================ extern "C" __declspec(dllexport) int __cdecl UpdateANSMOT_LV_V2(uint64_t handleVal, int modelId, const char* detectionData, LStrHandle trackerResult) { ANSCENTER::ANSMOT* directHandle = reinterpret_cast(handleVal); ANS_DBG("ANSMOT","UpdateANSMOT_LV_V2: handleVal=%llu handle=%p, modelId=%d", (unsigned long long)handleVal, (void*)directHandle, modelId); if (directHandle == nullptr) return -1; MOTHandleGuard guard(AcquireMOTHandle(directHandle)); if (!guard) return -1; try { std::string st = guard.get()->Update(modelId, detectionData); int size = static_cast(st.length()); MgErr error = DSSetHandleSize(trackerResult, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*trackerResult)->cnt = size; memcpy((*trackerResult)->str, st.c_str(), size); return 1; } else return 0; } catch (...) { return 0; } }