/* ======================================================================== * Copyright (c) 2005-2016 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ #include "StdAfx.h" #include "COpcUaProxyUtils.h" #include "COpcEnumString.h" #include ".\Hda\COpcHdaTime.h" using namespace System; using namespace System::Text; using namespace System::Runtime::InteropServices; using namespace System::Security::Cryptography::X509Certificates; using namespace System::Reflection; using namespace System::Collections::Generic; //============================================================================== // Global Data COpcCriticalSection g_cLock; UINT g_uRefs = 0; void* g_hConfiguration = 0; //============================================================================== // Member Functions // COpcUaProxyUtils COpcUaProxyUtils::COpcUaProxyUtils(void) { } // ~COpcUaProxyUtils COpcUaProxyUtils::~COpcUaProxyUtils(void) { } // TraceState void COpcUaProxyUtils::TraceState(String^ source, String^ context, ... array^ args) { if ((Utils::TraceMask & Utils::TraceMasks::Information) == 0) { //return; } StringBuilder^ buffer = gcnew StringBuilder(); buffer->AppendFormat("{0}::{1}", source, context); if (args != nullptr && args->Length > 0) { buffer->Append(", { "); for (int ii = 0; ii < args->Length; ii++) { if (ii > 0) { buffer->Append(", "); } buffer->AppendFormat("{0}", Variant(args[ii])); } buffer->Append(" }"); } Utils::Trace(Utils::TraceMasks::Error, "{0}", buffer->ToString()); } // CheckApplicationInstanceCertificate void COpcUaProxyUtils::CheckApplicationInstanceCertificate(ApplicationConfiguration^ configuration) { // call the synchronous version of this function in C# Opc::Ua::Com::ProxyUtils::CheckApplicationInstanceCertificate(configuration); } static void OnCertificateValidationFailed(CertificateValidator^ sender, CertificateValidationEventArgs^ e) { // automatically accept untrusted certificates since an administrator had to create the COM server proxy. e->Accept = true; } // Initialize bool COpcUaProxyUtils::Initialize(ApplicationConfiguration^% configuration) { configuration = nullptr; COpcLock cLock(g_cLock); try { g_uRefs++; if (g_hConfiguration == 0) { configuration = Opc::Ua::Com::ProxyUtils::ApplicationConfigurationLoad("Opc.Ua.ComProxyServer", Opc::Ua::ApplicationType::Client); CheckApplicationInstanceCertificate(configuration); configuration->CertificateValidator->CertificateValidation += gcnew Opc::Ua::CertificateValidationEventHandler(&OnCertificateValidationFailed); GCHandle hConfiguration = GCHandle::Alloc(configuration); g_hConfiguration = ((IntPtr)hConfiguration).ToPointer(); } else { GCHandle hConfiguration = (GCHandle)IntPtr(g_hConfiguration); configuration = (ApplicationConfiguration^)hConfiguration.Target; } return true; } catch (Exception^ e) { Utils::Trace(e, "Could not load configuration for the COM UA Proxy Server."); return false; } } // Uninitialize void COpcUaProxyUtils::Uninitialize() { COpcLock cLock(g_cLock); g_uRefs--; if (g_uRefs > 0) { return; } GCHandle hConfiguration = (GCHandle)IntPtr(g_hConfiguration); hConfiguration.Free(); g_hConfiguration = 0; cLock.Unlock(); COpcComModule::ExitProcess(S_OK); } // MarshalProperties void COpcUaProxyUtils::FreeOPCITEMPROPERTIES(OPCITEMPROPERTIES& tItem) { for (DWORD ii = 0; ii < tItem.dwNumProperties; ii++) { CoTaskMemFree(tItem.pItemProperties[ii].szDescription); CoTaskMemFree(tItem.pItemProperties[ii].szItemID); VariantClear(&(tItem.pItemProperties[ii].vValue)); } CoTaskMemFree(tItem.pItemProperties); } // MarshalProperties void COpcUaProxyUtils::MarshalProperties( OPCITEMPROPERTIES& tItem, array^ propertyIds, bool returnPropertyValues, IList^ descriptions, array^ values) { int propertyCount = descriptions->Count; bool returnAllProperties = propertyIds == nullptr || propertyIds->Length == 0; if (returnAllProperties) { propertyCount = 0; // count the number of valid properties. for (int ii = 0; ii < values->Length; ii++) { if (values[ii]->Error != OPC_E_INVALID_PID && values[ii]->Value != nullptr) { propertyCount++; continue; } } } // allocate memory. OpcProxy_AllocArrayToReturn(tItem.pItemProperties, propertyCount, OPCITEMPROPERTY); tItem.dwNumProperties = propertyCount; propertyCount = 0; for (int ii = 0; ii < values->Length; ii++) { // skip invalid properties. if (returnAllProperties && (values[ii]->Error == OPC_E_INVALID_PID || values[ii]->Value == nullptr)) { continue; } OPCITEMPROPERTY* pProperty = &(tItem.pItemProperties[propertyCount++]); // check for other errors. pProperty->hrErrorID = values[ii]->Error; if (FAILED(pProperty->hrErrorID)) { tItem.hrErrorID = S_FALSE; continue; } // copy property id. pProperty->dwPropertyID = descriptions[ii]->PropertyId; // copy property info. pProperty->szDescription = (LPWSTR)Marshal::StringToCoTaskMemUni(descriptions[ii]->Name).ToPointer(); pProperty->szItemID = NULL; pProperty->vtDataType = descriptions[ii]->DataType; if (returnPropertyValues) { // need to watch for conversion errors for some values. if (!COpcUaProxyUtils::MarshalVARIANT(pProperty->vValue, values[ii]->Value, pProperty->hrErrorID)) { tItem.hrErrorID = S_FALSE; continue; } } } // add done. } // MarshalVARIANT bool COpcUaProxyUtils::MarshalVARIANT(VARIANT& tDst, Object^ src, HRESULT& hResult) { try { Marshal::GetNativeVariantForObject(src, (IntPtr)&tDst); return true; } catch (Exception^ e) { hResult = Marshal::GetHRForException(e); return false; } } // GetEnumerator HRESULT COpcUaProxyUtils::GetEnumerator( IList^ strings, REFIID riid, void** ppUnknown) { *ppUnknown = NULL; COpcEnumString* ipEnum = NULL; DWORD dwCount = 0; if (strings != nullptr && strings->Count > 0) { dwCount = strings->Count; LPWSTR* ppStrings = (LPWSTR*)CoTaskMemAlloc(dwCount*sizeof(LPWSTR)); if (ppStrings == NULL) { return E_OUTOFMEMORY; } memset(ppStrings, 0, dwCount*sizeof(LPWSTR)); try { for (int ii = 0; ii < strings->Count; ii++) { if (strings[ii] == nullptr) { ppStrings[ii] = NULL; continue; } ppStrings[ii] = (LPWSTR)Marshal::StringToCoTaskMemUni(strings[ii]).ToPointer(); } } catch (Exception^ e) { if (ppStrings != NULL) { for (DWORD ii = 0; ii < dwCount; ii++) { CoTaskMemFree(ppStrings[ii]); } CoTaskMemFree(ppStrings); } return Marshal::GetHRForException(e); } ipEnum = new COpcEnumString(dwCount, ppStrings); } // create an empty enumerator if nothing found. if (ipEnum == NULL) { ipEnum = new COpcEnumString(); } // return requested interface. HRESULT hResult = ipEnum->QueryInterface(riid, (void**)ppUnknown); // release local reference. ipEnum->Release(); if (FAILED(hResult)) { return hResult; } // return correct error code. return (dwCount > 0)?S_OK:S_FALSE; } // GetFILETIME ::FILETIME COpcUaProxyUtils::GetFILETIME(DateTime dateTime) { System::Runtime::InteropServices::ComTypes::FILETIME ft1 = ComUtils::GetFILETIME(dateTime); ::FILETIME ft2; ft2.dwHighDateTime = ft1.dwHighDateTime; ft2.dwLowDateTime = ft1.dwLowDateTime; return ft2; } // FixupOutputVariants HRESULT COpcUaProxyUtils::FixupOutputVariants(DWORD dwCount, OPCITEMPROPERTIES* pItemProperties) { HRESULT hResult = S_OK; if (pItemProperties != NULL) { for (DWORD ii = 0; ii < dwCount; ii++) { for (DWORD jj = 0; jj < pItemProperties[ii].dwNumProperties; jj++) { FixupOutputVariant(pItemProperties[ii].pItemProperties[jj].vValue); } // check if individual item has an issue if (pItemProperties[ii].hrErrorID != S_OK) hResult = S_FALSE; } } return hResult; } // FixupOutputVariants HRESULT COpcUaProxyUtils::FixupOutputVariants(DWORD dwCount, OPCBROWSEELEMENT* ppBrowseElements) { HRESULT hResult = S_OK; if (ppBrowseElements != NULL) { for (DWORD ii = 0; ii < dwCount; ii++) { if (FixupOutputVariants(1, &(ppBrowseElements[ii].ItemProperties)) != S_OK) { hResult = S_FALSE; } } } return hResult; } // FixupDecimalArray void COpcUaProxyUtils::FixupDecimalArray(VARIANT& vValue) { VARIANT vDst; VariantInit(&vDst); COpcSafeArray cSrc(vValue); UINT uLength = cSrc.GetLength(); COpcSafeArray cDst(vDst); cDst.Alloc(VT_CY, cSrc.GetLength()); for (UINT jj = 0; jj < uLength; jj++) { DECIMAL decVal; if (SUCCEEDED(SafeArrayGetElement(vValue.parray, (LONG*)&jj, (void*)&decVal))) { CY cyVal; if (FAILED(VarCyFromDec(&decVal, &cyVal))) { cyVal.int64 = 0; } SafeArrayPutElement(vDst.parray, (LONG*)&jj, (void*)&cyVal); } } VariantClear(&vValue); vValue = vDst; } // FixupOutputVariant void COpcUaProxyUtils::FixupOutputVariant(VARIANT& vValue) { switch (vValue.vt) { case VT_ARRAY | VT_DECIMAL: { FixupDecimalArray(vValue); break; } case VT_DECIMAL: { VARIANT cyVal; VariantInit(&cyVal); if (SUCCEEDED(VariantChangeType(&cyVal, &vValue, NULL, VT_CY))) { vValue.vt = VT_CY; vValue.cyVal = cyVal.cyVal; } break; } } } // FixupOutputVariants void COpcUaProxyUtils::FixupOutputVariants(DWORD dwCount, OPCITEMSTATE* pItemValues) { if (pItemValues != NULL) { for (DWORD ii = 0; ii < dwCount; ii++) { FixupOutputVariant(pItemValues[ii].vDataValue); } } } // FixupOutputVariants void COpcUaProxyUtils::FixupOutputVariants(DWORD dwCount, VARIANT* pItemValues) { if (pItemValues != NULL) { for (DWORD ii = 0; ii < dwCount; ii++) { FixupOutputVariant(pItemValues[ii]); } } } // FixupInputVariants void COpcUaProxyUtils::FixupInputVariants(DWORD dwCount, VARIANT* pValues) { for (DWORD ii = 0; ii < dwCount; ii++) { if (pValues[ii].vt == VT_DATE) { if (pValues[ii].dblVal > 2e6) { pValues[ii].vt = VT_R8; } } } } // FixupInputVariants void COpcUaProxyUtils::FixupInputVariants(DWORD dwCount, OPCITEMVQT* pValues) { for (DWORD ii = 0; ii < dwCount; ii++) { if (pValues[ii].vDataValue.vt == VT_DATE) { if (pValues[ii].vDataValue.dblVal > 2e6) { pValues[ii].vDataValue.vt = VT_R8; } } } } // ResolveTime System::DateTime COpcUaProxyUtils::ResolveTime(OPCHDA_TIME* pTime) { // check for unspecified values. if (pTime == NULL) { return DateTime::MinValue; } if (pTime->bString) { if (pTime->szTime == NULL || wcslen(pTime->szTime) == 0) { return DateTime::MinValue; } } else { if (pTime->ftTime.dwHighDateTime == 0 && pTime->ftTime.dwLowDateTime == 0) { return System::DateTime(1601,1,1,0,0,1); } } // convert to WIN32 ticks. LONGLONG llComTime = OpcHdaResolveTime(*pTime); LONGLONG llMaxTime = Int64::MaxValue - llComTime; // convert to a .NET time. System::DateTime basetime = System::DateTime(1601,1,1); if (llMaxTime < basetime.Ticks) { return DateTime::MaxValue; } return basetime.AddTicks(llComTime); } // ResolveTime System::DateTime COpcUaProxyUtils::ResolveTime(::FILETIME* pTime) { // check for unspecified values. if (pTime == NULL) { return DateTime::MinValue; } if (pTime->dwHighDateTime == 0 && pTime->dwLowDateTime == 0) { return DateTime::MinValue; } // convert to WIN32 ticks. LONGLONG llComTime = ::OpcToInt64(*pTime); LONGLONG llMaxTime = Int64::MaxValue - llComTime; // convert to a .NET time. System::DateTime basetime = System::DateTime(1601,1,1); if (llMaxTime < basetime.Ticks) { return DateTime::MaxValue; } return basetime.AddTicks(llComTime); }