// dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include "ANSRabbitMQ.h" #include #include #include #include // Handle registry with refcount — prevents use-after-free when // ReleaseANSRabbitMQHandle is called while an operation is still running. static std::unordered_map& RMQHandleRegistry() { static std::unordered_map s; return s; } static std::mutex& RMQHandleRegistryMutex() { static std::mutex m; return m; } static std::condition_variable& RMQHandleRegistryCV() { static std::condition_variable cv; return cv; } static void RegisterRMQHandle(ANSCENTER::ANSRABBITMQ* h) { std::lock_guard lk(RMQHandleRegistryMutex()); RMQHandleRegistry()[h] = 1; } static ANSCENTER::ANSRABBITMQ* AcquireRMQHandle(ANSCENTER::ANSRABBITMQ* h) { std::lock_guard lk(RMQHandleRegistryMutex()); auto it = RMQHandleRegistry().find(h); if (it == RMQHandleRegistry().end()) return nullptr; it->second++; return h; } static bool ReleaseRMQHandleRef(ANSCENTER::ANSRABBITMQ* h) { std::lock_guard lk(RMQHandleRegistryMutex()); auto it = RMQHandleRegistry().find(h); if (it == RMQHandleRegistry().end()) return false; it->second--; if (it->second <= 0) { RMQHandleRegistry().erase(it); RMQHandleRegistryCV().notify_all(); return true; } return false; } static bool UnregisterRMQHandle(ANSCENTER::ANSRABBITMQ* h) { std::unique_lock lk(RMQHandleRegistryMutex()); auto it = RMQHandleRegistry().find(h); if (it == RMQHandleRegistry().end()) return false; it->second--; bool ok = RMQHandleRegistryCV().wait_for(lk, std::chrono::seconds(30), [&]() { auto it2 = RMQHandleRegistry().find(h); return it2 == RMQHandleRegistry().end() || it2->second <= 0; }); if (!ok) { OutputDebugStringA("WARNING: UnregisterRMQHandle timed out waiting for in-flight operations\n"); } RMQHandleRegistry().erase(h); return true; } // RAII guard — ensures ReleaseRMQHandleRef is always called class RMQHandleGuard { ANSCENTER::ANSRABBITMQ* engine; public: explicit RMQHandleGuard(ANSCENTER::ANSRABBITMQ* e) : engine(e) {} ~RMQHandleGuard() { if (engine) ReleaseRMQHandleRef(engine); } ANSCENTER::ANSRABBITMQ* get() const { return engine; } explicit operator bool() const { return engine != nullptr; } RMQHandleGuard(const RMQHandleGuard&) = delete; RMQHandleGuard& operator=(const RMQHandleGuard&) = 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; } extern "C" ANSRabbitMQ_API int CreateANSRabbitMQHandle(ANSCENTER::ANSRABBITMQ** Handle, const char* licenseKey) { if (Handle == nullptr || licenseKey == nullptr) return 0; try { // Release existing handle if called twice (prevents leak from LabVIEW) if (*Handle) { if (UnregisterRMQHandle(*Handle)) { delete *Handle; } *Handle = nullptr; } *Handle = new ANSCENTER::ANSRABBITMQ(); if (*Handle == nullptr) { return 0; // Memory allocation failed } RegisterRMQHandle(*Handle); return 1; } catch (std::exception& e) { if (*Handle != nullptr) { delete *Handle; *Handle = nullptr; } return 0; } catch (...) { if (*Handle != nullptr) { delete *Handle; *Handle = nullptr; } return 0; } } static int ReleaseANSRabbitMQHandle_Impl(ANSCENTER::ANSRABBITMQ** Handle) { try { if (!Handle || !*Handle) return 1; if (!UnregisterRMQHandle(*Handle)) { *Handle = nullptr; return 1; } delete *Handle; *Handle = nullptr; return 1; } catch (...) { if (Handle) *Handle = nullptr; return 0; } } extern "C" ANSRabbitMQ_API int ReleaseANSRabbitMQHandle(ANSCENTER::ANSRABBITMQ** Handle) { __try { return ReleaseANSRabbitMQHandle_Impl(Handle); } __except (EXCEPTION_EXECUTE_HANDLER) { if (Handle) *Handle = nullptr; return 0; } } extern "C" ANSRabbitMQ_API int Connect(ANSCENTER::ANSRABBITMQ** Handle, const char* hostname, int port, const char* vhost,int channel, const char* userName, const char* password, int useSSL) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (hostname == nullptr || vhost == nullptr || userName == nullptr || password == nullptr) { return -1; // Invalid parameters } bool SSL = false; if (useSSL == 1) { SSL = true; } if (guard.get()->Connect(hostname, port, vhost,channel, userName, password, SSL)) { return 1; // Success } else { return 0; // Connection failed } } extern "C" ANSRabbitMQ_API int Disconnect(ANSCENTER::ANSRABBITMQ** Handle) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (guard.get()->Disconnect()) { return 1; // Success } else { return 0; // Disconnection failed } } extern "C" ANSRabbitMQ_API int Publish(ANSCENTER::ANSRABBITMQ** Handle, const char* exchange_name, const char* routing_key, const char* message) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (exchange_name == nullptr || routing_key == nullptr || message == nullptr) { return -1; // Invalid parameters } if (guard.get()->Publish(exchange_name, routing_key,message)) { return 1; // Success } else { return 0; // Publish failed } } extern "C" ANSRabbitMQ_API int CreateChannel(ANSCENTER::ANSRABBITMQ** Handle, const char* exchange_name, const char* exchange_type, const char* queue_name, const char* routing_name, int passvive, int durable, int auto_delete, int internalVal) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (exchange_name == nullptr || exchange_type == nullptr || queue_name == nullptr || routing_name == nullptr) { return -1; // Invalid parameters } if (guard.get()->CreateChannel(exchange_name, exchange_type, queue_name, routing_name, passvive, durable, auto_delete, internalVal)) { return 1; // Success } else { return 0; // Setup failed } } extern "C" ANSRabbitMQ_API int ConfigureSSL(ANSCENTER::ANSRABBITMQ** Handle, const char* caCertPath, const char* clientCertPath, const char* clientKeyPath, int verifyPeer, int verifyHostname) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (caCertPath == nullptr || clientCertPath == nullptr || clientKeyPath == nullptr) { return -1; // Invalid parameters } bool verifyPeerBool = verifyPeer != 0; bool verifyHostnameBool = verifyHostname != 0; if (guard.get()->ConfigureSSL(caCertPath, clientCertPath, clientKeyPath, verifyPeerBool, verifyHostnameBool)) { return 1; // Success } else { return 0; // SSL configuration failed } } extern "C" ANSRabbitMQ_API int UnbindQueue(ANSCENTER::ANSRABBITMQ** Handle, const char* exchange_name) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (exchange_name == nullptr) { return -1; // Invalid parameters } if (guard.get()->UnbindQueue(exchange_name)) { return 1; // Success } else { return 0; // Unbind failed } } extern "C" ANSRabbitMQ_API int DeleteQueue(ANSCENTER::ANSRABBITMQ** Handle, const char* queue_name) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (queue_name == nullptr) { return -1; // Invalid parameters } if (guard.get()->DeleteQueue(queue_name)) { return 1; // Success } else { return 0; // Delete failed } } extern "C" ANSRabbitMQ_API int DeleteExchange(ANSCENTER::ANSRABBITMQ** Handle, const char* exchange_name) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (exchange_name == nullptr) { return -1; // Invalid parameters } if (guard.get()->DeleteExchange(exchange_name)) { return 1; // Success } else { return 0; // Delete failed } } extern "C" ANSRabbitMQ_API int PurgeQueue(ANSCENTER::ANSRABBITMQ** Handle, const char* queue_name) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (queue_name == nullptr) { return -1; // Invalid parameters } if (guard.get()->PurgeQueue(queue_name)) { return 1; // Success } else { return 0; // Purge failed } } extern "C" ANSRabbitMQ_API int GetQueueMessage_CPP(ANSCENTER::ANSRABBITMQ** Handle, const char* queue_name, int ack_mode, std::string& message) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (queue_name == nullptr) { return -1; // Invalid parameters } if (guard.get()->GetQueueMessage(queue_name, message, ack_mode)) { return 1; // Success } else { return 0; // Get message failed } } extern "C" ANSRabbitMQ_API int GetQueueMessage(ANSCENTER::ANSRABBITMQ** Handle, const char* queue_name, int ack_mode, LStrHandle strMessage) { if (Handle == nullptr || *Handle == nullptr) { return -1; // Invalid handle } RMQHandleGuard guard(AcquireRMQHandle(*Handle)); if (!guard) return -1; if (queue_name == nullptr || strMessage == nullptr) { return -1; // Invalid parameters } std::string message; if (guard.get()->GetQueueMessage(queue_name, message, ack_mode)) { if (message.empty()) return 0; int size = static_cast(message.length()); MgErr error; error = DSSetHandleSize(strMessage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*strMessage)->cnt = size; memcpy((*strMessage)->str, message.c_str(), size); return 1; } else return 0; } else { return 0; // Get message failed } } // ============================================================================ // V2 entry points — accept uint64_t handleVal by value instead of Handle** // Eliminates LabVIEW buffer reuse bug when concurrent calls share the same // Handle** buffer address. // ============================================================================ extern "C" ANSRabbitMQ_API int ConfigureSSL_V2(uint64_t handleVal, const char* caCertPath, const char* clientCertPath, const char* clientKeyPath, int verifyPeer, int verifyHostname) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (caCertPath == nullptr || clientCertPath == nullptr || clientKeyPath == nullptr) { return -1; } bool verifyPeerBool = verifyPeer != 0; bool verifyHostnameBool = verifyHostname != 0; if (guard.get()->ConfigureSSL(caCertPath, clientCertPath, clientKeyPath, verifyPeerBool, verifyHostnameBool)) { return 1; } else { return 0; } } extern "C" ANSRabbitMQ_API int CreateChannel_V2(uint64_t handleVal, const char* exchange_name, const char* exchange_type, const char* queue_name, const char* routing_name, int passvive, int durable, int auto_delete, int internalVal) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (exchange_name == nullptr || exchange_type == nullptr || queue_name == nullptr || routing_name == nullptr) { return -1; } if (guard.get()->CreateChannel(exchange_name, exchange_type, queue_name, routing_name, passvive, durable, auto_delete, internalVal)) { return 1; } else { return 0; } } extern "C" ANSRabbitMQ_API int Connect_V2(uint64_t handleVal, const char* hostname, int port, const char* vhost, int channel, const char* userName, const char* password, int useSSL) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (hostname == nullptr || vhost == nullptr || userName == nullptr || password == nullptr) { return -1; } bool SSL = false; if (useSSL == 1) { SSL = true; } if (guard.get()->Connect(hostname, port, vhost, channel, userName, password, SSL)) { return 1; } else { return 0; } } extern "C" ANSRabbitMQ_API int Disconnect_V2(uint64_t handleVal) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (guard.get()->Disconnect()) { return 1; } else { return 0; } } extern "C" ANSRabbitMQ_API int Publish_V2(uint64_t handleVal, const char* exchange_name, const char* routing_key, const char* message) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (exchange_name == nullptr || routing_key == nullptr || message == nullptr) { return -1; } if (guard.get()->Publish(exchange_name, routing_key, message)) { return 1; } else { return 0; } } extern "C" ANSRabbitMQ_API int GetQueueMessage_V2(uint64_t handleVal, const char* queue_name, int ack_mode, LStrHandle strMessage) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (queue_name == nullptr || strMessage == nullptr) { return -1; } std::string message; if (guard.get()->GetQueueMessage(queue_name, message, ack_mode)) { if (message.empty()) return 0; int size = static_cast(message.length()); MgErr error; error = DSSetHandleSize(strMessage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*strMessage)->cnt = size; memcpy((*strMessage)->str, message.c_str(), size); return 1; } else return 0; } else { return 0; } } extern "C" ANSRabbitMQ_API int UnbindQueue_V2(uint64_t handleVal, const char* exchange_name) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (exchange_name == nullptr) { return -1; } if (guard.get()->UnbindQueue(exchange_name)) { return 1; } else { return 0; } } extern "C" ANSRabbitMQ_API int DeleteExchange_V2(uint64_t handleVal, const char* exchange_name) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (exchange_name == nullptr) { return -1; } if (guard.get()->DeleteExchange(exchange_name)) { return 1; } else { return 0; } } extern "C" ANSRabbitMQ_API int DeleteQueue_V2(uint64_t handleVal, const char* queue_name) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (queue_name == nullptr) { return -1; } if (guard.get()->DeleteQueue(queue_name)) { return 1; } else { return 0; } } extern "C" ANSRabbitMQ_API int PurgeQueue_V2(uint64_t handleVal, const char* queue_name) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; RMQHandleGuard guard(AcquireRMQHandle(_v2h)); if (!guard) return -1; if (queue_name == nullptr) { return -1; } if (guard.get()->PurgeQueue(queue_name)) { return 1; } else { return 0; } }