diff --git a/modules/ANSFR/dllmain.cpp b/modules/ANSFR/dllmain.cpp index 9689b8d..cc2c4ec 100644 --- a/modules/ANSFR/dllmain.cpp +++ b/modules/ANSFR/dllmain.cpp @@ -250,14 +250,15 @@ extern "C" ANSFR_API int CreateANSRFHandle(ANSCENTER::ANSFacialRecognition** std::cout << buf; } - // Release existing handle if called twice (prevents leak from LabVIEW) - if (*Handle) { - if (UnregisterFRHandle(*Handle)) { - (*Handle)->Destroy(); - delete *Handle; - } - *Handle = nullptr; - } + // 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; // std::unique_ptr ensures automatic cleanup if Initialize() throws auto ptr = std::make_unique(); diff --git a/modules/ANSLLM/dllmain.cpp b/modules/ANSLLM/dllmain.cpp index 4665038..ff391b7 100644 --- a/modules/ANSLLM/dllmain.cpp +++ b/modules/ANSLLM/dllmain.cpp @@ -107,13 +107,15 @@ static int CopyToLStrHandle(LStrHandle handle, const std::string& str) noexcept extern "C" ANSLLM_API int CreateANSLLMHandle(ANSCENTER::ANSLLM** Handle, const char* licenseKey, int localLLM) { if (Handle == nullptr || licenseKey == nullptr) return 0; try { - // Release existing handle if called twice (prevents leak from LabVIEW) - if (*Handle) { - if (UnregisterLLMHandle(*Handle)) { - delete *Handle; - } - *Handle = nullptr; - } + // 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; // std::unique_ptr ensures automatic cleanup on any failure path auto ptr = std::make_unique(); diff --git a/modules/ANSLPR/dllmain.cpp b/modules/ANSLPR/dllmain.cpp index 101a7c7..01c4b88 100644 --- a/modules/ANSLPR/dllmain.cpp +++ b/modules/ANSLPR/dllmain.cpp @@ -140,14 +140,15 @@ static int CreateANSALPRHandle_Impl(ANSCENTER::ANSALPR** Handle, const char* lic // Ensure all shared DLLs (OpenCV, OpenVINO, TRT, ORT) are pre-loaded ANSCENTER::ANSLibsLoader::Initialize(); - // Release existing handle if called twice (prevents leak from LabVIEW) - if (*Handle) { - if (UnregisterALPRHandle(*Handle)) { - (*Handle)->Destroy(); - delete *Handle; - } - *Handle = nullptr; - } + // 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; if (engineType == 0) { (*Handle) = new ANSCENTER::ANSALPR_CPU();// built-in paddle OCR diff --git a/modules/ANSOCR/dllmain.cpp b/modules/ANSOCR/dllmain.cpp index 9f210aa..6726a59 100644 --- a/modules/ANSOCR/dllmain.cpp +++ b/modules/ANSOCR/dllmain.cpp @@ -163,14 +163,15 @@ extern "C" ANSOCR_API int CreateANSOCRHandleEx(ANSCENTER::ANSOCRBase** Handle, OutputDebugStringA(buf); } - // Release existing handle if called twice (prevents leak from LabVIEW) - if (*Handle) { - if (UnregisterOCRHandle(*Handle)) { - (*Handle)->Destroy(); - delete *Handle; - } - *Handle = nullptr; - } + // 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; // Validate limitSideLen: must be in (0, 20000], default to 960 if (limitSideLen <= 0 || limitSideLen > 20000) diff --git a/modules/ANSTrainingEngine/ANSTrainingEngine.cpp b/modules/ANSTrainingEngine/ANSTrainingEngine.cpp index c43f672..54b9121 100644 --- a/modules/ANSTrainingEngine/ANSTrainingEngine.cpp +++ b/modules/ANSTrainingEngine/ANSTrainingEngine.cpp @@ -322,13 +322,15 @@ namespace ANSCENTER extern "C" ANSTRE_API int CreateANSTREHandle(ANSCENTER::ANSTRE** Handle, const char* licenseKey, const char* projectDirectory, const char* engineDirectory, const char* modelTemplateDirectory, const char* modelZipPassword, int trainingEngineType, int latestEngine) { if (Handle == nullptr) return 0; if (licenseKey == nullptr || projectDirectory == nullptr || engineDirectory == nullptr || modelTemplateDirectory == nullptr || modelZipPassword == nullptr) return 0; - // Release existing handle if called twice (prevents leak from LabVIEW) - if (*Handle) { - if (UnregisterTREHandle(*Handle)) { - delete *Handle; - } - *Handle = nullptr; - } + // 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; try { switch (trainingEngineType) { diff --git a/modules/ANSUtilities/dllmain.cpp b/modules/ANSUtilities/dllmain.cpp index 77850d2..7789b99 100644 --- a/modules/ANSUtilities/dllmain.cpp +++ b/modules/ANSUtilities/dllmain.cpp @@ -191,14 +191,21 @@ BOOL APIENTRY DllMain( HMODULE hModule, return TRUE; } +// Pure constructor: every call allocates a brand-new ANSUtilities and writes it +// to *Handle. *Handle(in) is ignored completely -- LabVIEW's CLF Node marshalling +// reuses the same temp buffer per call site, so *Handle(in) often contains +// leftover bytes from the previous Create's output even when the actual LabVIEW +// wire is a different, freshly-allocated instance. Inspecting *Handle(in) for +// "is this live?" detection cannot distinguish that case from a genuine +// double-Create-on-same-wire, so we don't try -- we trust the caller. +// +// Trade-off: if a caller really does call Create twice on the same wire without +// Release, the first handle leaks. That is the caller's bug. The alternative +// (releasing live objects we "see" in the input) destroys legitimate parallel +// instances and is far worse. (Same reasoning as CreateANSAWSHandle.) extern "C" ANSULT_API int CreateANSUtilityHandle(ANSCENTER::ANSUtilities** Handle, const char* licenseKey) { if (Handle == nullptr || licenseKey == nullptr) return 0; - if (*Handle) { - if (UnregisterUtilHandle(*Handle)) { - delete *Handle; - } - *Handle = nullptr; - } + *Handle = nullptr; try { *Handle = new ANSCENTER::ANSUtilities(); if (*Handle == nullptr) return 0;