mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-25 03:43:05 +08:00
574 lines
13 KiB
C++
574 lines
13 KiB
C++
/* ========================================================================
|
|
* 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<Object^>^ 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<int>^ propertyIds,
|
|
bool returnPropertyValues,
|
|
IList<DaProperty^>^ descriptions,
|
|
array<DaValue^>^ 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<String^>^ 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);
|
|
}
|