diff --git a/modules/ANSUtilities/dllmain.cpp b/modules/ANSUtilities/dllmain.cpp index 775205f..77850d2 100644 --- a/modules/ANSUtilities/dllmain.cpp +++ b/modules/ANSUtilities/dllmain.cpp @@ -2,10 +2,23 @@ #include "pch.h" #include "ANSUtilities.h" #include +#include +#include #include #include #include +// ── DebugView logger (filter on "[ANSAWS]") ── +static void AWSDbg(const char* fmt, ...) { + char buf[512]; + va_list ap; + va_start(ap, fmt); + int n = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (n < 0) return; + OutputDebugStringA(buf); +} + // ── ANSUtilities handle registry ── static std::unordered_map& UtilHandleRegistry() { static std::unordered_map s; @@ -90,13 +103,31 @@ static std::condition_variable& AWSHandleRegistryCV() { static void RegisterAWSHandle(ANSCENTER::ANSAWSS3* h) { std::lock_guard lk(AWSHandleRegistryMutex()); AWSHandleRegistry()[h] = 1; + AWSDbg("[ANSAWS] Register: handle=%p (uint=%llu) registrySize=%zu\n", + (void*)h, (unsigned long long)(uintptr_t)h, AWSHandleRegistry().size()); +} + +static bool IsAWSHandleLive(ANSCENTER::ANSAWSS3* h) { + std::lock_guard lk(AWSHandleRegistryMutex()); + return AWSHandleRegistry().find(h) != AWSHandleRegistry().end(); } static ANSCENTER::ANSAWSS3* AcquireAWSHandle(ANSCENTER::ANSAWSS3* h) { std::lock_guard lk(AWSHandleRegistryMutex()); auto it = AWSHandleRegistry().find(h); - if (it == AWSHandleRegistry().end()) return nullptr; + if (it == AWSHandleRegistry().end()) { + AWSDbg("[ANSAWS] Acquire FAIL: handle=%p (uint=%llu) NOT in registry. registrySize=%zu\n", + (void*)h, (unsigned long long)(uintptr_t)h, AWSHandleRegistry().size()); + // dump current registry contents so you can compare what IS there + size_t i = 0; + for (auto& kv : AWSHandleRegistry()) { + AWSDbg("[ANSAWS] registry[%zu] = %p (uint=%llu) refcount=%d\n", + i++, (void*)kv.first, (unsigned long long)(uintptr_t)kv.first, kv.second); + } + return nullptr; + } it->second++; + AWSDbg("[ANSAWS] Acquire OK: handle=%p refcount=%d\n", (void*)h, it->second); return h; } @@ -116,7 +147,11 @@ static bool ReleaseAWSHandleRef(ANSCENTER::ANSAWSS3* h) { static bool UnregisterAWSHandle(ANSCENTER::ANSAWSS3* h) { std::unique_lock lk(AWSHandleRegistryMutex()); auto it = AWSHandleRegistry().find(h); - if (it == AWSHandleRegistry().end()) return false; + if (it == AWSHandleRegistry().end()) { + AWSDbg("[ANSAWS] Unregister: handle=%p NOT in registry (already gone)\n", (void*)h); + return false; + } + AWSDbg("[ANSAWS] Unregister: handle=%p starting (refcount before=%d)\n", (void*)h, it->second); it->second--; bool ok = AWSHandleRegistryCV().wait_for(lk, std::chrono::seconds(120), [&]() { auto it2 = AWSHandleRegistry().find(h); @@ -1185,19 +1220,36 @@ extern "C" ANSULT_API int ANSUnescapeDoubleEscapedUnicode(const char* str, LStrH } // AWSS3 +// +// Pure constructor: every call allocates a brand-new ANSAWSS3 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. +// +// Return codes: +// 1 : success, *Handle now points to a new registered instance +// 0 : invalid arg, alloc failure, or Initialize() failed extern "C" ANSULT_API int CreateANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* licenseKey) { + AWSDbg("[ANSAWS] Create called: HandlePtr=%p, *Handle(in)=%p (input ignored, always allocates new)\n", + (void*)Handle, Handle ? (void*)*Handle : nullptr); if (Handle == nullptr || licenseKey == nullptr) return 0; - if (*Handle) { - if (UnregisterAWSHandle(*Handle)) { - delete *Handle; - } - *Handle = nullptr; - } + *Handle = nullptr; + try { *Handle = new ANSCENTER::ANSAWSS3(); if (*Handle == nullptr) return 0; if ((*Handle)->Initialize(licenseKey)) { RegisterAWSHandle(*Handle); + AWSDbg("[ANSAWS] Create OK: *Handle(out)=%p (uint=%llu) -- LabVIEW will see this number\n", + (void*)*Handle, (unsigned long long)(uintptr_t)*Handle); return 1; } // Initialize failed - clean up to prevent memory leak @@ -1223,16 +1275,26 @@ extern "C" ANSULT_API int CreateANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, cons } static int ReleaseANSAWSHandle_Impl(ANSCENTER::ANSAWSS3** Handle) { try { - if (!Handle || !*Handle) return 1; - if (!UnregisterAWSHandle(*Handle)) { + if (!Handle || !*Handle) { + AWSDbg("[ANSAWS] Release: noop (Handle or *Handle was null)\n"); + return 1; + } + ANSCENTER::ANSAWSS3* h = *Handle; + AWSDbg("[ANSAWS] Release called: handle=%p (uint=%llu)\n", + (void*)h, (unsigned long long)(uintptr_t)h); + if (!UnregisterAWSHandle(h)) { + AWSDbg("[ANSAWS] Release: handle %p not in registry, clearing wire only\n", (void*)h); *Handle = nullptr; return 1; } - delete *Handle; + delete h; *Handle = nullptr; + AWSDbg("[ANSAWS] Release OK: handle %p deleted, registry now has %zu entries\n", + (void*)h, AWSHandleRegistry().size()); return 1; } catch (...) { + AWSDbg("[ANSAWS] Release EXCEPTION: clearing wire and returning 0\n"); if (Handle) *Handle = nullptr; return 0; } @@ -1243,57 +1305,102 @@ extern "C" ANSULT_API int ReleaseANSAWSHandle(ANSCENTER::ANSAWSS3** Handle) { return ReleaseANSAWSHandle_Impl(Handle); } __except (EXCEPTION_EXECUTE_HANDLER) { + AWSDbg("[ANSAWS] Release SEH EXCEPTION: clearing wire and returning 0\n"); if (Handle) *Handle = nullptr; return 0; } } extern "C" ANSULT_API int ConnectANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* baseDomain, const char* bucketRegion, const char* serviceName, int port, int bTls, int autoReconnect, int* awsPath) { + AWSDbg("[ANSAWS] Connect called: *Handle=%p, baseDomain=%s, region=%s, service=%s, port=%d, tls=%d, autoReconnect=%d\n", + Handle ? (void*)*Handle : nullptr, + baseDomain ? baseDomain : "(null)", + bucketRegion ? bucketRegion : "(null)", + serviceName ? serviceName : "(null)", + port, bTls, autoReconnect); if (Handle == nullptr || *Handle == nullptr || awsPath == nullptr) { + AWSDbg("[ANSAWS] Connect: returning 0 (null arg)\n"); return 0; } AWSHandleGuard guard(AcquireAWSHandle(*Handle)); - if (!guard) return 0; + if (!guard) { + AWSDbg("[ANSAWS] Connect: returning 0 (handle %p not in registry)\n", (void*)*Handle); + return 0; + } try { bool bTlsBool = (bTls == 1); bool autoReconnectBool = (autoReconnect == 1); bool awsPathBool = true; int result = guard.get()->Connect(baseDomain, bucketRegion, serviceName, port, bTlsBool, autoReconnectBool, awsPathBool); *awsPath = awsPathBool ? 1 : 0; + AWSDbg("[ANSAWS] Connect result: %d (1=connected, 0=failed, 2=no internet) awsPath=%d\n", result, *awsPath); return result; // 1 = connected, 0 = failed, 2 = no internet (retrying) } catch (std::exception& e) { + AWSDbg("[ANSAWS] Connect EXCEPTION: %s\n", e.what()); return 0; } catch (...) { + AWSDbg("[ANSAWS] Connect EXCEPTION: unknown\n"); return 0; } } extern "C" ANSULT_API int SetProxyANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* proxyHost, int proxyPort, const char* proxyUsername, const char* proxyPassword) { + AWSDbg("[ANSAWS] SetProxy called: *Handle=%p, host=%s, port=%d, user=%s\n", + Handle ? (void*)*Handle : nullptr, + proxyHost ? proxyHost : "(null)", + proxyPort, + proxyUsername ? proxyUsername : "(null)"); if (Handle == nullptr || *Handle == nullptr) { + AWSDbg("[ANSAWS] SetProxy: returning 0 (null arg)\n"); return 0; } AWSHandleGuard guard(AcquireAWSHandle(*Handle)); - if (!guard) return 0; + if (!guard) { + AWSDbg("[ANSAWS] SetProxy: returning 0 (handle %p not in registry)\n", (void*)*Handle); + return 0; + } try { - if (guard.get()->SetServerProxy(proxyHost, proxyPort, proxyUsername, proxyPassword)) return 1; - else return 0; + if (guard.get()->SetServerProxy(proxyHost, proxyPort, proxyUsername, proxyPassword)) { + AWSDbg("[ANSAWS] SetProxy OK\n"); + return 1; + } + else { + AWSDbg("[ANSAWS] SetProxy FAILED (SetServerProxy returned false)\n"); + return 0; + } } catch (std::exception& e) { + AWSDbg("[ANSAWS] SetProxy EXCEPTION: %s\n", e.what()); return 0; } catch (...) { + AWSDbg("[ANSAWS] SetProxy EXCEPTION: unknown\n"); return 0; } } extern "C" ANSULT_API int SetAuthenticationANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* accessKey, const char* secretKey) { + AWSDbg("[ANSAWS] SetAuth called: *Handle=%p, accessKey=%.6s... (length-only secretKey=%zu)\n", + Handle ? (void*)*Handle : nullptr, + accessKey ? accessKey : "(null)", + secretKey ? strlen(secretKey) : 0); if (Handle == nullptr || *Handle == nullptr ) { + AWSDbg("[ANSAWS] SetAuth: returning 0 (null arg)\n"); return 0; } AWSHandleGuard guard(AcquireAWSHandle(*Handle)); - if (!guard) return 0; + if (!guard) { + AWSDbg("[ANSAWS] SetAuth: returning 0 (handle %p not in registry)\n", (void*)*Handle); + return 0; + } try { - if (guard.get()->SetAuthentication(accessKey, secretKey)) return 1; - else return 0; + if (guard.get()->SetAuthentication(accessKey, secretKey)) { + AWSDbg("[ANSAWS] SetAuth OK\n"); + return 1; + } + else { + AWSDbg("[ANSAWS] SetAuth FAILED (SetAuthentication returned false)\n"); + return 0; + } } catch (std::exception& e) { return 0; @@ -1775,11 +1882,11 @@ extern "C" ANSULT_API int DownloadFileStreamANSAWSHandle(ANSCENTER::ANSAWSS3** } } extern "C" ANSULT_API int UploadJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, LStrHandle uploadedFilePath) { - if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || jpeg_string == nullptr || bufferLength <= 0 || fileName == nullptr || uploadedFilePath == nullptr) { - return 0; + if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || jpeg_string == nullptr || bufferLength <= 0 || fileName == nullptr) { + return 0; // Handle error } AWSHandleGuard guard(AcquireAWSHandle(*Handle)); - if (!guard) return 0; + if (!guard) return -1; // guard error try { std::string outPath; if (guard.get()->UploadJpegImage(bucketName, jpeg_string, bufferLength, fileName, outPath)) { @@ -1791,25 +1898,37 @@ extern "C" ANSULT_API int UploadJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3** Han } return 1; } - else return 0; + else return -2;// failed upload } catch (std::exception& e) { - return 0; + return -3;//excception } catch (...) { - return 0; + return -4;//exception } } extern "C" ANSULT_API int UploadPrefixJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, LStrHandle uploadedFilePath) { + AWSDbg("[ANSAWS] Upload called: HandlePtr=%p, *Handle=%p (uint=%llu), bucket=%s, prefix=%s, file=%s, len=%d\n", + (void*)Handle, + Handle ? (void*)*Handle : nullptr, + Handle && *Handle ? (unsigned long long)(uintptr_t)*Handle : 0ULL, + bucketName ? bucketName : "(null)", + prefix ? prefix : "(null)", + fileName ? fileName : "(null)", + bufferLength); if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || jpeg_string == nullptr || bufferLength <= 0 || - fileName == nullptr || uploadedFilePath == nullptr) + fileName == nullptr) { + AWSDbg("[ANSAWS] Upload: returning 0 (null/invalid arg)\n"); return 0; } AWSHandleGuard guard(AcquireAWSHandle(*Handle)); - if (!guard) return 0; + if (!guard) { + AWSDbg("[ANSAWS] Upload: returning -1 (handle %p not in registry)\n", (void*)*Handle); + return -1; + } try { std::string outPath; if (guard.get()->UploadPrefixJpegImage(bucketName, prefix, jpeg_string, bufferLength, fileName, outPath)) { @@ -1819,19 +1938,29 @@ extern "C" ANSULT_API int UploadPrefixJpegImageANSAWSHandle(ANSCENTER::ANSAWSS3 (*uploadedFilePath)->cnt = size; memcpy((*uploadedFilePath)->str, outPath.c_str(), size); } + AWSDbg("[ANSAWS] Upload OK (1): file=%s -> %s\n", + fileName ? fileName : "(null)", outPath.c_str()); return 1; } - else return 0; + else { + AWSDbg("[ANSAWS] Upload FAILED (-2): file=%s, UploadPrefixJpegImage returned false\n", + fileName ? fileName : "(null)"); + return -2; + } } catch (std::exception& e) { - return 0; + AWSDbg("[ANSAWS] Upload EXCEPTION (-3): file=%s, what=%s\n", + fileName ? fileName : "(null)", e.what()); + return -3; } catch (...) { - return 0; + AWSDbg("[ANSAWS] Upload EXCEPTION (-4): file=%s, unknown exception\n", + fileName ? fileName : "(null)"); + return -4; } } extern "C" ANSULT_API int UploadPrefixMultiPartDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* fileDataPath, const char* objectName, int fileSize, LStrHandle uploadedFilePath) { - if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || fileDataPath == nullptr || fileSize <= 0 || uploadedFilePath == nullptr) { + if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || fileDataPath == nullptr || fileSize <= 0 ) { return 0; } AWSHandleGuard guard(AcquireAWSHandle(*Handle)); @@ -1857,7 +1986,7 @@ extern "C" ANSULT_API int UploadPrefixMultiPartDataANSAWSHandle(ANSCENTER::ANSA } } extern "C" ANSULT_API int UploadPrefixBinaryDataANSAWSHandle(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* binaryDataPath, const char* objectName, LStrHandle uploadedFilePath) { - if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || binaryDataPath == nullptr || objectName == nullptr || uploadedFilePath == nullptr) { + if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || binaryDataPath == nullptr || objectName == nullptr) { return 0; } AWSHandleGuard guard(AcquireAWSHandle(*Handle)); @@ -1882,9 +2011,7 @@ extern "C" ANSULT_API int UploadPrefixBinaryDataANSAWSHandle(ANSCENTER::ANSAWSS return 0; } } - // ── C++ test APIs (std::string& instead of LStrHandle) ── - extern "C" ANSULT_API int UploadTextDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* textDataPath, std::string& uploadedFilePath) { if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || textDataPath == nullptr) return 0; AWSHandleGuard guard(AcquireAWSHandle(*Handle)); @@ -1893,7 +2020,6 @@ extern "C" ANSULT_API int UploadTextDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** H return guard.get()->UploadTextData(bucketName, textDataPath, uploadedFilePath) ? 1 : 0; } catch (...) { return 0; } } - extern "C" ANSULT_API int UploadBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* binaryDataPath, std::string& uploadedFilePath) { if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || binaryDataPath == nullptr) return 0; AWSHandleGuard guard(AcquireAWSHandle(*Handle)); @@ -1902,7 +2028,6 @@ extern "C" ANSULT_API int UploadBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** return guard.get()->UploadBinaryData(bucketName, binaryDataPath, uploadedFilePath) ? 1 : 0; } catch (...) { return 0; } } - extern "C" ANSULT_API int UploadFileStreamDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* fileDataPath, std::string& uploadedFilePath) { if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || fileDataPath == nullptr) return 0; AWSHandleGuard guard(AcquireAWSHandle(*Handle)); @@ -1911,7 +2036,6 @@ extern "C" ANSULT_API int UploadFileStreamDataANSAWSHandle_CPP(ANSCENTER::ANSAWS return guard.get()->UploadFileStream(bucketName, fileDataPath, uploadedFilePath) ? 1 : 0; } catch (...) { return 0; } } - extern "C" ANSULT_API int UploadMultiPartDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* fileDataPath, int fileSize, std::string& uploadedFilePath) { if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || fileDataPath == nullptr) return 0; AWSHandleGuard guard(AcquireAWSHandle(*Handle)); @@ -1920,7 +2044,6 @@ extern "C" ANSULT_API int UploadMultiPartDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS return guard.get()->UploadMultipartData(bucketName, fileDataPath, uploadedFilePath, fileSize) ? 1 : 0; } catch (...) { return 0; } } - extern "C" ANSULT_API int UploadJpegImageANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, std::string& uploadedFilePath) { if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || jpeg_string == nullptr || bufferLength <= 0 || fileName == nullptr) return 0; AWSHandleGuard guard(AcquireAWSHandle(*Handle)); @@ -1929,7 +2052,6 @@ extern "C" ANSULT_API int UploadJpegImageANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** return guard.get()->UploadJpegImage(bucketName, jpeg_string, bufferLength, fileName, uploadedFilePath) ? 1 : 0; } catch (...) { return 0; } } - extern "C" ANSULT_API int UploadPrefixBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* binaryDataPath, const char* objectName, std::string& uploadedFilePath) { if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || binaryDataPath == nullptr || objectName == nullptr) return 0; AWSHandleGuard guard(AcquireAWSHandle(*Handle)); @@ -1938,7 +2060,6 @@ extern "C" ANSULT_API int UploadPrefixBinaryDataANSAWSHandle_CPP(ANSCENTER::ANSA return guard.get()->UploadPrefixBinaryData(bucketName, prefix, binaryDataPath, objectName, uploadedFilePath) ? 1 : 0; } catch (...) { return 0; } } - extern "C" ANSULT_API int UploadPrefixMultiPartDataANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, const char* fileDataPath, const char* objectName, int fileSize, std::string& uploadedFilePath) { if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || fileDataPath == nullptr || fileSize <= 0) return 0; AWSHandleGuard guard(AcquireAWSHandle(*Handle)); @@ -1947,7 +2068,6 @@ extern "C" ANSULT_API int UploadPrefixMultiPartDataANSAWSHandle_CPP(ANSCENTER::A return guard.get()->UploadPrefixMultipartData(bucketName, prefix, fileDataPath, objectName, uploadedFilePath, fileSize) ? 1 : 0; } catch (...) { return 0; } } - extern "C" ANSULT_API int UploadPrefixJpegImageANSAWSHandle_CPP(ANSCENTER::ANSAWSS3** Handle, const char* bucketName, const char* prefix, unsigned char* jpeg_string, int32 bufferLength, const char* fileName, std::string& uploadedFilePath) { if (Handle == nullptr || *Handle == nullptr || bucketName == nullptr || prefix == nullptr || jpeg_string == nullptr || bufferLength <= 0 || fileName == nullptr) return 0; AWSHandleGuard guard(AcquireAWSHandle(*Handle)); diff --git a/tests/ANSUtilities-UnitTest/ANSUtilities-UnitTest.cpp b/tests/ANSUtilities-UnitTest/ANSUtilities-UnitTest.cpp index 2f90635..b630a42 100644 --- a/tests/ANSUtilities-UnitTest/ANSUtilities-UnitTest.cpp +++ b/tests/ANSUtilities-UnitTest/ANSUtilities-UnitTest.cpp @@ -170,7 +170,7 @@ int ANSAWSTest() { return 1; } // Upload a jpeg image to bucket - std::string jpegFilePath = "E:\\Programs\\DemoAssets\\NV\\Test.jpg"; + std::string jpegFilePath = "C:\\ProgramData\\ANSCENTER\\Shared\\Test.jpg"; std::string objectName = "test/test.jpg"; std::string uploadedUrl;