#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved //----------------------------------------------------------------------------- // Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved // Web: https://www.technosoftware.com // // The source code in this file is covered under a dual-license scenario: // - Owner of a purchased license: SCLA 1.0 // - GPL V3: everybody else // // SCLA license terms accompanied with this source code. // See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf // // GNU General Public License as published by the Free Software Foundation; // version 3 of the License are accompanied with this source code. // See https://technosoftware.com/license/GPLv3License.txt // // This source code is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. //----------------------------------------------------------------------------- #endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved #region Using Directives using System; using System.Net; using System.Globalization; using System.Runtime.InteropServices; using Technosoftware.DaAeHdaClient.Da; #pragma warning disable 618 #endregion namespace Technosoftware.DaAeHdaClient.Com.Utilities { /// /// Exposes WIN32 and COM API functions. /// public class Interop { #region NetApi Function Declarations [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct SERVER_INFO_100 { public readonly uint sv100_platform_id; [MarshalAs(UnmanagedType.LPWStr)] public readonly string sv100_name; } private const uint LEVEL_SERVER_INFO_100 = 100; private const uint LEVEL_SERVER_INFO_101 = 101; private const int MAX_PREFERRED_LENGTH = -1; private const uint SV_TYPE_WORKSTATION = 0x00000001; private const uint SV_TYPE_SERVER = 0x00000002; [DllImport("Netapi32.dll")] private static extern int NetServerEnum( IntPtr servername, uint level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, uint servertype, IntPtr domain, IntPtr resume_handle); [DllImport("Netapi32.dll")] private static extern int NetApiBufferFree(IntPtr buffer); /// /// Enumerates computers on the local network. /// public static string[] EnumComputers() { IntPtr pInfo; int entriesRead; int totalEntries; var result = NetServerEnum( IntPtr.Zero, LEVEL_SERVER_INFO_100, out pInfo, MAX_PREFERRED_LENGTH, out entriesRead, out totalEntries, SV_TYPE_WORKSTATION | SV_TYPE_SERVER, IntPtr.Zero, IntPtr.Zero); if (result != 0) { throw new ApplicationException("NetApi Error = " + string.Format("0x{0:X8}", result)); } var computers = new string[entriesRead]; var pos = pInfo; for (var ii = 0; ii < entriesRead; ii++) { var info = (SERVER_INFO_100)Marshal.PtrToStructure(pos, typeof(SERVER_INFO_100)); computers[ii] = info.sv100_name; pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(SERVER_INFO_100))); } NetApiBufferFree(pInfo); return computers; } #endregion #region OLE32 Function/Interface Declarations private const int MAX_MESSAGE_LENGTH = 1024; private const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; private const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; /// /// The WIN32 system default locale. /// public const int LOCALE_SYSTEM_DEFAULT = 0x800; /// /// The WIN32 user default locale. /// public const int LOCALE_USER_DEFAULT = 0x400; /// /// The size, in bytes, of a VARIANT structure. /// private const int VARIANT_SIZE = 0x10; private const int DISP_E_TYPEMISMATCH = -0x7FFDFFFB; // 0x80020005 private const int DISP_E_OVERFLOW = -0x7FFDFFF6; // 0x8002000A private const int VARIANT_NOVALUEPROP = 0x01; private const int VARIANT_ALPHABOOL = 0x02; // For VT_BOOL to VT_BSTR conversions convert to "True"/"False" instead of private const uint RPC_C_AUTHN_NONE = 0; private const uint RPC_C_AUTHN_DCE_PRIVATE = 1; private const uint RPC_C_AUTHN_DCE_PUBLIC = 2; private const uint RPC_C_AUTHN_DEC_PUBLIC = 4; private const uint RPC_C_AUTHN_GSS_NEGOTIATE = 9; private const uint RPC_C_AUTHN_WINNT = 10; private const uint RPC_C_AUTHN_GSS_SCHANNEL = 14; private const uint RPC_C_AUTHN_GSS_KERBEROS = 16; private const uint RPC_C_AUTHN_DPA = 17; private const uint RPC_C_AUTHN_MSN = 18; private const uint RPC_C_AUTHN_DIGEST = 21; private const uint RPC_C_AUTHN_MQ = 100; private const uint RPC_C_AUTHN_DEFAULT = 0xFFFFFFFF; private const uint RPC_C_AUTHZ_NONE = 0; private const uint RPC_C_AUTHZ_NAME = 1; private const uint RPC_C_AUTHZ_DCE = 2; private const uint RPC_C_AUTHZ_DEFAULT = 0xffffffff; private const uint RPC_C_AUTHN_LEVEL_DEFAULT = 0; private const uint RPC_C_AUTHN_LEVEL_NONE = 1; private const uint RPC_C_AUTHN_LEVEL_CONNECT = 2; private const uint RPC_C_AUTHN_LEVEL_CALL = 3; private const uint RPC_C_AUTHN_LEVEL_PKT = 4; private const uint RPC_C_AUTHN_LEVEL_PKT_INTEGRITY = 5; private const uint RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6; private const uint RPC_C_IMP_LEVEL_ANONYMOUS = 1; private const uint RPC_C_IMP_LEVEL_IDENTIFY = 2; private const uint RPC_C_IMP_LEVEL_IMPERSONATE = 3; private const uint RPC_C_IMP_LEVEL_DELEGATE = 4; private const uint EOAC_NONE = 0x00; private const uint EOAC_MUTUAL_AUTH = 0x01; private const uint EOAC_CLOAKING = 0x10; private const uint EOAC_STATIC_CLOAKING = 0x20; private const uint EOAC_DYNAMIC_CLOAKING = 0x40; private const uint EOAC_SECURE_REFS = 0x02; private const uint EOAC_ACCESS_CONTROL = 0x04; private const uint EOAC_APPID = 0x08; private const uint CLSCTX_INPROC_SERVER = 0x1; private const uint CLSCTX_INPROC_HANDLER = 0x2; private const uint CLSCTX_LOCAL_SERVER = 0x4; private const uint CLSCTX_REMOTE_SERVER = 0x10; private const uint CLSCTX_DISABLE_AAA = 0x8000; private const uint SEC_WINNT_AUTH_IDENTITY_ANSI = 0x1; private const uint SEC_WINNT_AUTH_IDENTITY_UNICODE = 0x2; private const int LOGON32_PROVIDER_DEFAULT = 0; private const int LOGON32_LOGON_INTERACTIVE = 2; private const int LOGON32_LOGON_NETWORK = 3; private const int SECURITY_ANONYMOUS = 0; private const int SECURITY_IDENTIFICATION = 1; private const int SECURITY_IMPERSONATION = 2; private const int SECURITY_DELEGATION = 3; /// /// The base for the WIN32 FILETIME structure. /// private static readonly DateTime FILETIME_BaseTime = new DateTime(1601, 1, 1); private static readonly IntPtr COLE_DEFAULT_PRINCIPAL = new IntPtr(-1); private static readonly IntPtr COLE_DEFAULT_AUTHINFO = new IntPtr(-1); private static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046"); [DllImport("Kernel32.dll")] private static extern int FormatMessageW( int dwFlags, IntPtr lpSource, int dwMessageId, int dwLanguageId, IntPtr lpBuffer, int nSize, IntPtr Arguments); [DllImport("Kernel32.dll")] private static extern int GetSystemDefaultLangID(); [DllImport("Kernel32.dll")] private static extern int GetUserDefaultLangID(); [DllImport("OleAut32.dll")] private static extern int VariantChangeTypeEx( IntPtr pvargDest, IntPtr pvarSrc, int lcid, ushort wFlags, short vt); /// /// Intializes a pointer to a VARIANT. /// [DllImport("oleaut32.dll")] private static extern void VariantInit(IntPtr pVariant); /// /// Frees all memory referenced by a VARIANT stored in unmanaged memory. /// [DllImport("oleaut32.dll")] public static extern void VariantClear(IntPtr pVariant); [DllImport("ole32.dll")] private static extern int CoInitializeSecurity( IntPtr pSecDesc, int cAuthSvc, SOLE_AUTHENTICATION_SERVICE[] asAuthSvc, IntPtr pReserved1, uint dwAuthnLevel, uint dwImpLevel, IntPtr pAuthList, uint dwCapabilities, IntPtr pReserved3); [DllImport("ole32.dll")] private static extern int CoQueryProxyBlanket( [MarshalAs(UnmanagedType.IUnknown)] object pProxy, ref uint pAuthnSvc, ref uint pAuthzSvc, [MarshalAs(UnmanagedType.LPWStr)] ref string pServerPrincName, ref uint pAuthnLevel, ref uint pImpLevel, ref IntPtr pAuthInfo, ref uint pCapabilities); [DllImport("ole32.dll")] private static extern int CoSetProxyBlanket( [MarshalAs(UnmanagedType.IUnknown)] object pProxy, uint pAuthnSvc, uint pAuthzSvc, IntPtr pServerPrincName, uint pAuthnLevel, uint pImpLevel, IntPtr pAuthInfo, uint pCapabilities); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct COSERVERINFO { public uint dwReserved1; [MarshalAs(UnmanagedType.LPWStr)] public string pwszName; public IntPtr pAuthInfo; public uint dwReserved2; }; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct COAUTHINFO { public uint dwAuthnSvc; public uint dwAuthzSvc; public IntPtr pwszServerPrincName; public uint dwAuthnLevel; public uint dwImpersonationLevel; public IntPtr pAuthIdentityData; public uint dwCapabilities; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct COAUTHIDENTITY { public IntPtr User; public uint UserLength; public IntPtr Domain; public uint DomainLength; public IntPtr Password; public uint PasswordLength; public uint Flags; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct MULTI_QI { public IntPtr iid; [MarshalAs(UnmanagedType.IUnknown)] public object pItf; public uint hr; } [DllImport("ole32.dll")] private static extern void CoCreateInstanceEx( ref Guid clsid, [MarshalAs(UnmanagedType.IUnknown)] object punkOuter, uint dwClsCtx, [In] ref COSERVERINFO pServerInfo, uint dwCount, [In, Out] MULTI_QI[] pResults); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct LICINFO { public int cbLicInfo; [MarshalAs(UnmanagedType.Bool)] public bool fRuntimeKeyAvail; [MarshalAs(UnmanagedType.Bool)] public bool fLicVerified; } [ComImport] [GuidAttribute("00000001-0000-0000-C000-000000000046")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IClassFactory { void CreateInstance( [MarshalAs(UnmanagedType.IUnknown)] object punkOuter, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.Interface)] [Out] out object ppvObject); void LockServer( [MarshalAs(UnmanagedType.Bool)] bool fLock); } [ComImport] [GuidAttribute("B196B28F-BAB4-101A-B69C-00AA00341D07")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] private interface IClassFactory2 { void CreateInstance( [MarshalAs(UnmanagedType.IUnknown)] object punkOuter, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.Interface)] [Out] out object ppvObject); void LockServer( [MarshalAs(UnmanagedType.Bool)] bool fLock); void GetLicInfo( [In, Out] ref LICINFO pLicInfo); void RequestLicKey( int dwReserved, [MarshalAs(UnmanagedType.BStr)] string pbstrKey); void CreateInstanceLic( [MarshalAs(UnmanagedType.IUnknown)] object punkOuter, [MarshalAs(UnmanagedType.IUnknown)] object punkReserved, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.BStr)] string bstrKey, [MarshalAs(UnmanagedType.IUnknown)] [Out] out object ppvObject); } [DllImport("ole32.dll")] private static extern void CoGetClassObject( [MarshalAs(UnmanagedType.LPStruct)] Guid clsid, uint dwClsContext, [In] ref COSERVERINFO pServerInfo, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)][Out] out object ppv); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool LogonUser( string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private extern static bool CloseHandle(IntPtr handle); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] private extern static bool DuplicateToken( IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle); #region Nested type: GUID /// /// WIN32 GUID struct declaration. /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct GUID { public readonly int Data1; public readonly short Data2; public readonly short Data3; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly byte[] Data4; } #endregion #region Nested type: SOLE_AUTHENTICATION_SERVICE [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct SOLE_AUTHENTICATION_SERVICE { public readonly uint dwAuthnSvc; public readonly uint dwAuthzSvc; [MarshalAs(UnmanagedType.LPWStr)] public readonly string pPrincipalName; public readonly int hr; } [DllImport("ole32.dll")] internal static extern int CoCancelCall(uint threadId, uint timeout); [DllImport("ole32.dll")] internal static extern int CoEnableCallCancellation(IntPtr reserved); [DllImport("ole32.dll")] internal static extern int CoDisableCallCancellation(IntPtr reserved); [DllImport("Kernel32.dll")] internal static extern uint GetCurrentThreadId(); #endregion #endregion private static bool preserveUtc_ = true; /// /// This flag suppresses the conversion to local time done during marshalling. /// public static bool PreserveUtc { get { lock (typeof(Interop)) { return preserveUtc_; } } set { lock (typeof(Interop)) { preserveUtc_ = value; } } } #region ServerInfo Class /// /// A class used to allocate and deallocate the elements of a COSERVERINFO structure. /// class ServerInfo { #region Public Interface /// /// Allocates a COSERVERINFO structure. /// public COSERVERINFO Allocate(string hostName, OpcUserIdentity identity) { // initialize server info structure. var serverInfo = new COSERVERINFO(); serverInfo.pwszName = hostName; serverInfo.pAuthInfo = IntPtr.Zero; serverInfo.dwReserved1 = 0; serverInfo.dwReserved2 = 0; // no authentication for default identity if (OpcUserIdentity.IsDefault(identity)) { return serverInfo; } m_hUserName = GCHandle.Alloc(identity.Username, GCHandleType.Pinned); m_hPassword = GCHandle.Alloc(identity.Password, GCHandleType.Pinned); m_hDomain = GCHandle.Alloc(identity.Domain, GCHandleType.Pinned); m_hIdentity = new GCHandle(); // create identity structure. var authIdentity = new COAUTHIDENTITY(); authIdentity.User = m_hUserName.AddrOfPinnedObject(); authIdentity.UserLength = (uint)((identity.Username != null) ? identity.Username.Length : 0); authIdentity.Password = m_hPassword.AddrOfPinnedObject(); authIdentity.PasswordLength = (uint)((identity.Password != null) ? identity.Password.Length : 0); authIdentity.Domain = m_hDomain.AddrOfPinnedObject(); authIdentity.DomainLength = (uint)((identity.Domain != null) ? identity.Domain.Length : 0); authIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; m_hIdentity = GCHandle.Alloc(authIdentity, GCHandleType.Pinned); // create authorization info structure. var authInfo = new COAUTHINFO(); authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT; authInfo.dwAuthzSvc = RPC_C_AUTHZ_NONE; authInfo.pwszServerPrincName = IntPtr.Zero; authInfo.dwAuthnLevel = RPC_C_AUTHN_LEVEL_CONNECT; authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE; authInfo.pAuthIdentityData = m_hIdentity.AddrOfPinnedObject(); authInfo.dwCapabilities = EOAC_NONE; // EOAC_DYNAMIC_CLOAKING; m_hAuthInfo = GCHandle.Alloc(authInfo, GCHandleType.Pinned); // update server info structure. serverInfo.pAuthInfo = m_hAuthInfo.AddrOfPinnedObject(); return serverInfo; } /// /// Deallocated memory allocated when the COSERVERINFO structure was created. /// public void Deallocate() { if (m_hUserName.IsAllocated) m_hUserName.Free(); if (m_hPassword.IsAllocated) m_hPassword.Free(); if (m_hDomain.IsAllocated) m_hDomain.Free(); if (m_hIdentity.IsAllocated) m_hIdentity.Free(); if (m_hAuthInfo.IsAllocated) m_hAuthInfo.Free(); } #endregion #region Private Members private GCHandle m_hUserName; private GCHandle m_hPassword; private GCHandle m_hDomain; private GCHandle m_hIdentity; private GCHandle m_hAuthInfo; #endregion } #endregion #region Initialization Functions /// /// Initializes COM security. /// public static void InitializeSecurity() { var error = CoInitializeSecurity( IntPtr.Zero, -1, null, IntPtr.Zero, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, IntPtr.Zero, EOAC_DYNAMIC_CLOAKING, IntPtr.Zero); // this call will fail in the debugger if the // 'Debug | Enable Visual Studio Hosting Process' // option is checked in the project properties. if (error != 0) { // throw new ExternalException("CoInitializeSecurity: " + GetSystemMessage(error), error); } } /// /// Determines if the host is the local host. /// private static bool IsLocalHost(string hostName) { // lookup requested host. var requestedHost = Dns.GetHostEntry(hostName); if (requestedHost == null || requestedHost.AddressList == null) { return true; } // check for loopback. for (var ii = 0; ii < requestedHost.AddressList.Length; ii++) { var requestedIP = requestedHost.AddressList[ii]; if (requestedIP == null || requestedIP.Equals(IPAddress.Loopback)) { return true; } } // lookup local host. var localHost = Dns.GetHostEntry(Dns.GetHostName()); if (localHost == null || localHost.AddressList == null) { return false; } // check for localhost. for (var ii = 0; ii < requestedHost.AddressList.Length; ii++) { var requestedIP = requestedHost.AddressList[ii]; for (var jj = 0; jj < localHost.AddressList.Length; jj++) { if (requestedIP.Equals(localHost.AddressList[jj])) { return true; } } } // must be remote. return false; } /// /// Creates an instance of a COM server using the specified license key. /// public static object CreateInstance(Guid clsid, string hostName, OpcUserIdentity identity) { return CreateInstance1(clsid, hostName, identity); } /// /// Creates an instance of a COM server. /// public static object CreateInstance1(Guid clsid, string hostName, OpcUserIdentity identity) { var serverInfo = new ServerInfo(); var coserverInfo = serverInfo.Allocate(hostName, identity); var hIID = GCHandle.Alloc(IID_IUnknown, GCHandleType.Pinned); var results = new MULTI_QI[1]; results[0].iid = hIID.AddrOfPinnedObject(); results[0].pItf = null; results[0].hr = 0; try { // check whether connecting locally or remotely. var clsctx = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER; if (!string.IsNullOrEmpty(hostName) && hostName != "localhost") { clsctx = CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER; } // create an instance. CoCreateInstanceEx( ref clsid, null, clsctx, ref coserverInfo, 1, results); } finally { if (hIID.IsAllocated) hIID.Free(); serverInfo.Deallocate(); } if (results[0].hr != 0) { return null; } return results[0].pItf; } // COM impersonation is a nice feature but variations between behavoirs on different // windows platforms make it virtually impossible to support. This code is left here // in case it becomes a critical requirement in the future. #if COM_IMPERSONATION_SUPPORT /// /// Returns the WindowsIdentity associated with a UserIdentity. /// public static WindowsPrincipal GetPrincipalFromUserIdentity(UserIdentity user) { if (UserIdentity.IsDefault(user)) { return null; } // validate the credentials. IntPtr token = IntPtr.Zero; bool result = LogonUser( user.Username, user.Domain, user.Password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, ref token); if (!result) { throw ServiceResultException.Create( StatusCodes.BadIdentityTokenRejected, "Could not logon as user '{0}'. Reason: {1}.", user.Username, GetSystemMessage(Marshal.GetLastWin32Error(), LOCALE_SYSTEM_DEFAULT)); } try { // create the windows identity. WindowsIdentity identity = new WindowsIdentity(token); // validate the identity. identity.Impersonate(); // return a principal. return new WindowsPrincipal(identity); } finally { CloseHandle(token); } } /// /// Sets the security settings for the proxy. /// public static void SetProxySecurity(object server, UserIdentity user) { // allocate the GCHandle hUserName = GCHandle.Alloc(user.Username, GCHandleType.Pinned); GCHandle hPassword = GCHandle.Alloc(user.Password, GCHandleType.Pinned); GCHandle hDomain = GCHandle.Alloc(user.Domain, GCHandleType.Pinned); GCHandle hIdentity = new GCHandle(); // create identity structure. COAUTHIDENTITY authIdentity = new COAUTHIDENTITY(); authIdentity.User = hUserName.AddrOfPinnedObject(); authIdentity.UserLength = (uint)((user.Username != null) ? user.Username.Length : 0); authIdentity.Password = hPassword.AddrOfPinnedObject(); authIdentity.PasswordLength = (uint)((user.Password != null) ? user.Password.Length : 0); authIdentity.Domain = hDomain.AddrOfPinnedObject(); authIdentity.DomainLength = (uint)((user.Domain != null) ? user.Domain.Length : 0); authIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; hIdentity = GCHandle.Alloc(authIdentity, GCHandleType.Pinned); try { SetProxySecurity(server, hIdentity.AddrOfPinnedObject()); } finally { hUserName.Free(); hPassword.Free(); hDomain.Free(); hIdentity.Free(); } } /// /// Sets the security settings for the proxy. /// public static void SetProxySecurity(object server, IntPtr pAuthInfo) { // get the existing proxy settings. uint pAuthnSvc = 0; uint pAuthzSvc = 0; string pServerPrincName = ""; uint pAuthnLevel = 0; uint pImpLevel = 0; IntPtr pAuthInfo2 = IntPtr.Zero; uint pCapabilities = 0; CoQueryProxyBlanket( server, ref pAuthnSvc, ref pAuthzSvc, ref pServerPrincName, ref pAuthnLevel, ref pImpLevel, ref pAuthInfo2, ref pCapabilities); pAuthnSvc = RPC_C_AUTHN_WINNT; pAuthzSvc = RPC_C_AUTHZ_NONE; pAuthnLevel = RPC_C_AUTHN_LEVEL_CONNECT; pImpLevel = RPC_C_IMP_LEVEL_IMPERSONATE; pCapabilities = EOAC_DYNAMIC_CLOAKING; // update proxy security settings. CoSetProxyBlanket( server, pAuthnSvc, pAuthzSvc, COLE_DEFAULT_PRINCIPAL, pAuthnLevel, pImpLevel, pAuthInfo, pCapabilities); } /// /// Creates an instance of a COM server using the specified license key. /// public static object CreateInstance2(Guid clsid, string hostName, UserIdentity identity) { // validate the host name before proceeding (exception thrown if host is not valid). bool isLocalHost = IsLocalHost(hostName); // allocate the connection info. ServerInfo serverInfo = new ServerInfo(); COSERVERINFO coserverInfo = serverInfo.Allocate(hostName, identity); object instance = null; IClassFactory factory = null; try { // create the factory. object server = null; CoGetClassObject( clsid, (isLocalHost)?CLSCTX_LOCAL_SERVER:CLSCTX_REMOTE_SERVER, ref coserverInfo, IID_IUnknown, out server); // SetProxySecurity(server, coserverInfo.pAuthInfo); factory = (IClassFactory)server; // check for valid factory. if (factory == null) { throw ServiceResultException.Create(StatusCodes.BadCommunicationError, "Could not load IClassFactory for COM server '{0}' on host '{1}'.", clsid, hostName); } // SetProxySecurity(factory, coserverInfo.pAuthInfo); factory.CreateInstance(null, IID_IUnknown, out instance); // SetProxySecurity(instance, coserverInfo.pAuthInfo); } finally { serverInfo.Deallocate(); } return instance; } /// /// Creates an instance of a COM server using the specified license key. /// public static object CreateInstanceWithLicenseKey(Guid clsid, string hostName, UserIdentity identity, string licenseKey) { ServerInfo serverInfo = new ServerInfo(); COSERVERINFO coserverInfo = serverInfo.Allocate(hostName, identity); object instance = null; IClassFactory2 factory = null; try { // check whether connecting locally or remotely. uint clsctx = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER; if (hostName != null && hostName.Length > 0) { clsctx = CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER; } // get the class factory. object server = null; CoGetClassObject( clsid, clsctx, ref coserverInfo, typeof(IClassFactory2).GUID, out server); // SetProxySecurity(server, coserverInfo.pAuthInfo); factory = (IClassFactory2)server; // check for valid factory. if (factory == null) { throw ServiceResultException.Create(StatusCodes.BadCommunicationError, "Could not load IClassFactory2 for COM server '{0}' on host '{1}'.", clsid, hostName); } // SetProxySecurity(factory, coserverInfo.pAuthInfo); // create instance. factory.CreateInstanceLic( null, null, IID_IUnknown, licenseKey, out instance); // SetProxySecurity(instance, coserverInfo.pAuthInfo); } finally { serverInfo.Deallocate(); ComUtils.ReleaseServer(factory); } return instance; } #endif #endregion /// /// Tests if the specified string matches the specified pattern. /// public static bool Match(string target, string pattern, bool caseSensitive) { // an empty pattern always matches. if (pattern == null || pattern.Length == 0) { return true; } // an empty string never matches. if (target == null || target.Length == 0) { return false; } // check for exact match if (caseSensitive) { if (target == pattern) { return true; } } else { if (target.ToLower() == pattern.ToLower()) { return true; } } char c; char p; char l; var pIndex = 0; var tIndex = 0; while (tIndex < target.Length && pIndex < pattern.Length) { p = ConvertCase(pattern[pIndex++], caseSensitive); if (pIndex > pattern.Length) { return (tIndex >= target.Length); // if end of string true } switch (p) { // match zero or more char. case '*': { while (pIndex < pattern.Length && pattern[pIndex] == '*') { pIndex++; } while (tIndex < target.Length) { if (Match(target.Substring(tIndex++), pattern.Substring(pIndex), caseSensitive)) { return true; } } return Match(target, pattern.Substring(pIndex), caseSensitive); } // match any one char. case '?': { // check if end of string when looking for a single character. if (tIndex >= target.Length) { return false; } // check if end of pattern and still string data left. if (pIndex >= pattern.Length && tIndex < target.Length - 1) { return false; } tIndex++; break; } // match char set case '[': { c = ConvertCase(target[tIndex++], caseSensitive); if (tIndex > target.Length) { return false; // syntax } l = '\0'; // match a char if NOT in set [] if (pattern[pIndex] == '!') { ++pIndex; p = ConvertCase(pattern[pIndex++], caseSensitive); while (pIndex < pattern.Length) { if (p == ']') // if end of char set, then { break; // no match found } if (p == '-') { // check a range of chars? p = ConvertCase(pattern[pIndex], caseSensitive); // get high limit of range if (pIndex > pattern.Length || p == ']') { return false; // syntax } if (c >= l && c <= p) { return false; // if in range, return false } } l = p; if (c == p) // if char matches this element { return false; // return false } p = ConvertCase(pattern[pIndex++], caseSensitive); } } // match if char is in set [] else { p = ConvertCase(pattern[pIndex++], caseSensitive); while (pIndex < pattern.Length) { if (p == ']') // if end of char set, then no match found { return false; } if (p == '-') { // check a range of chars? p = ConvertCase(pattern[pIndex], caseSensitive); // get high limit of range if (pIndex > pattern.Length || p == ']') { return false; // syntax } if (c >= l && c <= p) { break; // if in range, move on } } l = p; if (c == p) // if char matches this element move on { break; } p = ConvertCase(pattern[pIndex++], caseSensitive); } while (pIndex < pattern.Length && p != ']') // got a match in char set skip to end of set { p = pattern[pIndex++]; } } break; } // match digit. case '#': { c = target[tIndex++]; if (!char.IsDigit(c)) { return false; // not a digit } break; } // match exact char. default: { c = ConvertCase(target[tIndex++], caseSensitive); if (c != p) // check for exact char { return false; // not a match } // check if end of pattern and still string data left. if (pIndex >= pattern.Length && tIndex < target.Length - 1) { return false; } break; } } } return true; } // ConvertCase private static char ConvertCase(char c, bool caseSensitive) { return (caseSensitive) ? c : char.ToUpper(c); } /// /// Unmarshals and frees an array of HRESULTs. /// public static int[] GetStatusCodes(ref IntPtr pArray, int size, bool deallocate) { if (pArray == IntPtr.Zero || size <= 0) { return null; } // unmarshal HRESULT array. var output = new int[size]; Marshal.Copy(pArray, output, 0, size); if (deallocate) { Marshal.FreeCoTaskMem(pArray); pArray = IntPtr.Zero; } return output; } /// /// Unmarshals and frees an array of 32 bit integers. /// public static int[] GetInt32s(ref IntPtr pArray, int size, bool deallocate) { if (pArray == IntPtr.Zero || size <= 0) { return null; } var array = new int[size]; Marshal.Copy(pArray, array, 0, size); if (deallocate) { Marshal.FreeCoTaskMem(pArray); pArray = IntPtr.Zero; } return array; } /// /// Unmarshals and frees an array of 32 bit integers. /// public static int[] GetUInt32s(ref IntPtr pArray, int size, bool deallocate) { if (pArray == IntPtr.Zero || size <= 0) { return null; } var array = new int[size]; Marshal.Copy(pArray, array, 0, size); if (deallocate) { Marshal.FreeCoTaskMem(pArray); pArray = IntPtr.Zero; } return array; } /// /// Allocates and marshals an array of 32 bit integers. /// public static IntPtr GetInt32s(int[] input) { var output = IntPtr.Zero; if (input != null) { output = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * input.Length); Marshal.Copy(input, 0, output, input.Length); } return output; } /// /// Unmarshals and frees a array of 16 bit integers. /// public static short[] GetInt16s(ref IntPtr pArray, int size, bool deallocate) { if (pArray == IntPtr.Zero || size <= 0) { return null; } var array = new short[size]; Marshal.Copy(pArray, array, 0, size); if (deallocate) { Marshal.FreeCoTaskMem(pArray); pArray = IntPtr.Zero; } return array; } /// /// Allocates and marshals an array of 16 bit integers. /// public static IntPtr GetInt16s(short[] input) { var output = IntPtr.Zero; if (input != null) { output = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(short)) * input.Length); Marshal.Copy(input, 0, output, input.Length); } return output; } /// /// Marshals an array of strings into a unmanaged memory buffer /// /// The array of strings to marshal /// The pointer to the unmanaged memory buffer public static IntPtr GetUnicodeStrings(string[] values) { var size = (values != null) ? values.Length : 0; if (size <= 0) { return IntPtr.Zero; } var pointers = new int[size]; for (var ii = 0; ii < size; ii++) { pointers[ii] = (int)Marshal.StringToCoTaskMemUni(values[ii]); } var pValues = Marshal.AllocCoTaskMem(values.Length * Marshal.SizeOf(typeof(IntPtr))); Marshal.Copy(pointers, 0, pValues, size); return pValues; } /// /// Unmarshals and frees a array of unicode strings. /// public static string[] GetUnicodeStrings(ref IntPtr pArray, int size, bool deallocate) { if (pArray == IntPtr.Zero || size <= 0) { return null; } var pointers = new IntPtr[size]; Marshal.Copy(pArray, pointers, 0, size); var strings = new string[size]; for (var ii = 0; ii < size; ii++) { var pString = pointers[ii]; strings[ii] = Marshal.PtrToStringUni(pString); if (deallocate) { Marshal.FreeCoTaskMem(pString); } } if (deallocate) { Marshal.FreeCoTaskMem(pArray); pArray = IntPtr.Zero; } return strings; } /// /// Marshals a DateTime as a WIN32 FILETIME. /// /// The DateTime object to marshal /// The WIN32 FILETIME public static OpcRcw.Da.FILETIME GetFILETIME(DateTime datetime) { OpcRcw.Da.FILETIME filetime; if (datetime <= FILETIME_BaseTime) { filetime.dwHighDateTime = 0; filetime.dwLowDateTime = 0; return filetime; } // adjust for WIN32 FILETIME base. long ticks; if (preserveUtc_) { ticks = datetime.Subtract(new TimeSpan(FILETIME_BaseTime.Ticks)).Ticks; } else { ticks = (datetime.ToUniversalTime().Subtract(new TimeSpan(FILETIME_BaseTime.Ticks))).Ticks; } filetime.dwHighDateTime = (int)((ticks >> 32) & 0xFFFFFFFF); filetime.dwLowDateTime = (int)(ticks & 0xFFFFFFFF); return filetime; } /// /// Unmarshals a WIN32 FILETIME from a pointer. /// /// A pointer to a FILETIME structure. /// A DateTime object. public static DateTime GetDateTime(IntPtr pFiletime) { if (pFiletime == IntPtr.Zero) { return DateTime.MinValue; } return GetDateTime((OpcRcw.Da.FILETIME)Marshal.PtrToStructure(pFiletime, typeof(OpcRcw.Da.FILETIME))); } /// /// Unmarshals a WIN32 FILETIME. /// public static DateTime GetDateTime(OpcRcw.Da.FILETIME filetime) { // check for invalid value. if (filetime.dwHighDateTime < 0) { return DateTime.MinValue; } // convert FILETIME structure to a 64 bit integer. long buffer = filetime.dwHighDateTime; if (buffer < 0) { buffer += ((long)uint.MaxValue + 1); } var ticks = (buffer << 32); buffer = filetime.dwLowDateTime; if (buffer < 0) { buffer += ((long)uint.MaxValue + 1); } ticks += buffer; // check for invalid value. if (ticks == 0) { return DateTime.MinValue; } // adjust for WIN32 FILETIME base. if (preserveUtc_) { return FILETIME_BaseTime.Add(new TimeSpan(ticks)); } else { return FILETIME_BaseTime.Add(new TimeSpan(ticks)).ToLocalTime(); } } /// /// Marshals an array of DateTimes into an unmanaged array of FILETIMEs /// /// The array of DateTimes to marshal /// The IntPtr array of FILETIMEs public static IntPtr GetFILETIMEs(DateTime[] datetimes) { var count = (datetimes != null) ? datetimes.Length : 0; if (count <= 0) { return IntPtr.Zero; } var pFiletimes = Marshal.AllocCoTaskMem(count * Marshal.SizeOf(typeof(System.Runtime.InteropServices.ComTypes.FILETIME))); var pos = pFiletimes; for (var ii = 0; ii < count; ii++) { Marshal.StructureToPtr(GetFILETIME(datetimes[ii]), pos, false); pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(System.Runtime.InteropServices.ComTypes.FILETIME))); } return pFiletimes; } /// /// Unmarshals an array of WIN32 FILETIMEs as DateTimes. /// public static DateTime[] GetDateTimes(ref IntPtr pArray, int size, bool deallocate) { if (pArray == IntPtr.Zero || size <= 0) { return null; } var datetimes = new DateTime[size]; var pos = pArray; for (var ii = 0; ii < size; ii++) { datetimes[ii] = GetDateTime(pos); pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(System.Runtime.InteropServices.ComTypes.FILETIME))); } if (deallocate) { Marshal.FreeCoTaskMem(pArray); pArray = IntPtr.Zero; } return datetimes; } /// /// Unmarshals an array of WIN32 GUIDs as Guid. /// public static Guid[] GetGUIDs(ref IntPtr pInput, int size, bool deallocate) { if (pInput == IntPtr.Zero || size <= 0) { return null; } var guids = new Guid[size]; var pos = pInput; for (var ii = 0; ii < size; ii++) { var input = (GUID)Marshal.PtrToStructure(pInput, typeof(GUID)); guids[ii] = new Guid(input.Data1, input.Data2, input.Data3, input.Data4); pos = (IntPtr)(pos.ToInt64() + Marshal.SizeOf(typeof(GUID))); } if (deallocate) { Marshal.FreeCoTaskMem(pInput); pInput = IntPtr.Zero; } return guids; } /// /// Converts an object into a value that can be marshalled to a VARIANT. /// /// The object to convert. /// The converted object. public static object GetVARIANT(object source) { // check for invalid args. if (source == null || source.GetType() == null) { return null; } // convert a decimal array to an object array since decimal arrays can't be converted to a variant. if (source.GetType() == typeof(decimal[])) { var srcArray = (decimal[])source; var dstArray = new object[srcArray.Length]; for (var ii = 0; ii < srcArray.Length; ii++) { try { dstArray[ii] = (object)srcArray[ii]; } catch (Exception) { dstArray[ii] = double.NaN; } } return dstArray; } // no conversion required. return source; } /// /// Converts a LCID to a Locale string. /// public static string GetLocale(int input) { try { if (input == LOCALE_SYSTEM_DEFAULT || input == LOCALE_USER_DEFAULT || input == 0) { return CultureInfo.InvariantCulture.Name; } return new CultureInfo(input).Name; } catch { throw new ExternalException("Invalid LCID", OpcResult.E_INVALIDARG.Code); } } /// /// Converts a Locale string to a LCID. /// public static int GetLocale(string input) { // check for the default culture. if (input == null || input == "") { return 0; } CultureInfo locale; try { locale = new CultureInfo(input); } catch { locale = CultureInfo.CurrentCulture; } return locale.LCID; } /// /// Converts the VARTYPE to a system type. /// public static Type GetType(VarEnum input) { switch (input) { case VarEnum.VT_EMPTY: return null; case VarEnum.VT_I1: return typeof(sbyte); case VarEnum.VT_UI1: return typeof(byte); case VarEnum.VT_I2: return typeof(short); case VarEnum.VT_UI2: return typeof(ushort); case VarEnum.VT_I4: return typeof(int); case VarEnum.VT_UI4: return typeof(uint); case VarEnum.VT_I8: return typeof(long); case VarEnum.VT_UI8: return typeof(ulong); case VarEnum.VT_R4: return typeof(float); case VarEnum.VT_R8: return typeof(double); case VarEnum.VT_CY: return typeof(decimal); case VarEnum.VT_BOOL: return typeof(bool); case VarEnum.VT_DATE: return typeof(DateTime); case VarEnum.VT_BSTR: return typeof(string); case VarEnum.VT_ARRAY | VarEnum.VT_I1: return typeof(sbyte[]); case VarEnum.VT_ARRAY | VarEnum.VT_UI1: return typeof(byte[]); case VarEnum.VT_ARRAY | VarEnum.VT_I2: return typeof(short[]); case VarEnum.VT_ARRAY | VarEnum.VT_UI2: return typeof(ushort[]); case VarEnum.VT_ARRAY | VarEnum.VT_I4: return typeof(int[]); case VarEnum.VT_ARRAY | VarEnum.VT_UI4: return typeof(uint[]); case VarEnum.VT_ARRAY | VarEnum.VT_I8: return typeof(long[]); case VarEnum.VT_ARRAY | VarEnum.VT_UI8: return typeof(ulong[]); case VarEnum.VT_ARRAY | VarEnum.VT_R4: return typeof(float[]); case VarEnum.VT_ARRAY | VarEnum.VT_R8: return typeof(double[]); case VarEnum.VT_ARRAY | VarEnum.VT_CY: return typeof(decimal[]); case VarEnum.VT_ARRAY | VarEnum.VT_BOOL: return typeof(bool[]); case VarEnum.VT_ARRAY | VarEnum.VT_DATE: return typeof(DateTime[]); case VarEnum.VT_ARRAY | VarEnum.VT_BSTR: return typeof(string[]); case VarEnum.VT_ARRAY | VarEnum.VT_VARIANT: return typeof(object[]); default: return OpcType.ILLEGAL_TYPE; } } /// /// Converts the system type to a VARTYPE. /// public static VarEnum GetType(Type input) { if (input == null) return VarEnum.VT_EMPTY; if (input == typeof(sbyte)) return VarEnum.VT_I1; if (input == typeof(byte)) return VarEnum.VT_UI1; if (input == typeof(short)) return VarEnum.VT_I2; if (input == typeof(ushort)) return VarEnum.VT_UI2; if (input == typeof(int)) return VarEnum.VT_I4; if (input == typeof(uint)) return VarEnum.VT_UI4; if (input == typeof(long)) return VarEnum.VT_I8; if (input == typeof(ulong)) return VarEnum.VT_UI8; if (input == typeof(float)) return VarEnum.VT_R4; if (input == typeof(double)) return VarEnum.VT_R8; if (input == typeof(decimal)) return VarEnum.VT_CY; if (input == typeof(bool)) return VarEnum.VT_BOOL; if (input == typeof(DateTime)) return VarEnum.VT_DATE; if (input == typeof(string)) return VarEnum.VT_BSTR; if (input == typeof(object)) return VarEnum.VT_EMPTY; if (input == typeof(sbyte[])) return VarEnum.VT_ARRAY | VarEnum.VT_I1; if (input == typeof(byte[])) return VarEnum.VT_ARRAY | VarEnum.VT_UI1; if (input == typeof(short[])) return VarEnum.VT_ARRAY | VarEnum.VT_I2; if (input == typeof(ushort[])) return VarEnum.VT_ARRAY | VarEnum.VT_UI2; if (input == typeof(int[])) return VarEnum.VT_ARRAY | VarEnum.VT_I4; if (input == typeof(uint[])) return VarEnum.VT_ARRAY | VarEnum.VT_UI4; if (input == typeof(long[])) return VarEnum.VT_ARRAY | VarEnum.VT_I8; if (input == typeof(ulong[])) return VarEnum.VT_ARRAY | VarEnum.VT_UI8; if (input == typeof(float[])) return VarEnum.VT_ARRAY | VarEnum.VT_R4; if (input == typeof(double[])) return VarEnum.VT_ARRAY | VarEnum.VT_R8; if (input == typeof(decimal[])) return VarEnum.VT_ARRAY | VarEnum.VT_CY; if (input == typeof(bool[])) return VarEnum.VT_ARRAY | VarEnum.VT_BOOL; if (input == typeof(DateTime[])) return VarEnum.VT_ARRAY | VarEnum.VT_DATE; if (input == typeof(string[])) return VarEnum.VT_ARRAY | VarEnum.VT_BSTR; if (input == typeof(object[])) return VarEnum.VT_ARRAY | VarEnum.VT_VARIANT; // check for special types. if (input == OpcType.ILLEGAL_TYPE) return (VarEnum)Enum.ToObject(typeof(VarEnum), 0x7FFF); if (input == typeof(Type)) return VarEnum.VT_I2; if (input == typeof(TsCDaQuality)) return VarEnum.VT_I2; if (input == typeof(TsDaAccessRights)) return VarEnum.VT_I4; if (input == typeof(TsDaEuType)) return VarEnum.VT_I4; return VarEnum.VT_EMPTY; } /// /// Converts the HRESULT to a system type. /// public static OpcResult GetResultId(int input) { switch (input) { // data access. case Com.Da.Result.S_OK: return new OpcResult(OpcResult.S_OK, input); case Com.Da.Result.E_FAIL: return new OpcResult(OpcResult.E_FAIL, input); case Com.Da.Result.E_INVALIDARG: return new OpcResult(OpcResult.E_INVALIDARG, input); case Com.Da.Result.DISP_E_TYPEMISMATCH: return new OpcResult(OpcResult.Da.E_BADTYPE, input); case Com.Da.Result.DISP_E_OVERFLOW: return new OpcResult(OpcResult.Da.E_RANGE, input); case Com.Da.Result.E_OUTOFMEMORY: return new OpcResult(OpcResult.E_OUTOFMEMORY, input); case Com.Da.Result.E_NOINTERFACE: return new OpcResult(OpcResult.E_NOTSUPPORTED, input); case Com.Da.Result.E_INVALIDHANDLE: return new OpcResult(OpcResult.Da.E_INVALIDHANDLE, input); case Com.Da.Result.E_BADTYPE: return new OpcResult(OpcResult.Da.E_BADTYPE, input); case Com.Da.Result.E_UNKNOWNITEMID: return new OpcResult(OpcResult.Da.E_UNKNOWN_ITEM_NAME, input); case Com.Da.Result.E_INVALIDITEMID: return new OpcResult(OpcResult.Da.E_INVALID_ITEM_NAME, input); case Com.Da.Result.E_UNKNOWNPATH: return new OpcResult(OpcResult.Da.E_UNKNOWN_ITEM_PATH, input); case Com.Da.Result.E_INVALIDFILTER: return new OpcResult(OpcResult.Da.E_INVALID_FILTER, input); case Com.Da.Result.E_RANGE: return new OpcResult(OpcResult.Da.E_RANGE, input); case Com.Da.Result.E_DUPLICATENAME: return new OpcResult(OpcResult.Da.E_DUPLICATENAME, input); case Com.Da.Result.S_UNSUPPORTEDRATE: return new OpcResult(OpcResult.Da.S_UNSUPPORTEDRATE, input); case Com.Da.Result.S_CLAMP: return new OpcResult(OpcResult.Da.S_CLAMP, input); case Com.Da.Result.E_INVALID_PID: return new OpcResult(OpcResult.Da.E_INVALID_PID, input); case Com.Da.Result.E_DEADBANDNOTSUPPORTED: return new OpcResult(OpcResult.Da.E_NO_ITEM_DEADBAND, input); case Com.Da.Result.E_NOBUFFERING: return new OpcResult(OpcResult.Da.E_NO_ITEM_BUFFERING, input); case Com.Da.Result.E_NOTSUPPORTED: return new OpcResult(OpcResult.Da.E_NO_WRITEQT, input); case Com.Da.Result.E_INVALIDCONTINUATIONPOINT: return new OpcResult(OpcResult.Da.E_INVALIDCONTINUATIONPOINT, input); case Com.Da.Result.S_DATAQUEUEOVERFLOW: return new OpcResult(OpcResult.Da.S_DATAQUEUEOVERFLOW, input); // complex data. case Com.Cpx.Result.E_TYPE_CHANGED: return new OpcResult(OpcResult.Cpx.E_TYPE_CHANGED, input); case Com.Cpx.Result.E_FILTER_DUPLICATE: return new OpcResult(OpcResult.Cpx.E_FILTER_DUPLICATE, input); case Com.Cpx.Result.E_FILTER_INVALID: return new OpcResult(OpcResult.Cpx.E_FILTER_INVALID, input); case Com.Cpx.Result.E_FILTER_ERROR: return new OpcResult(OpcResult.Cpx.E_FILTER_ERROR, input); case Com.Cpx.Result.S_FILTER_NO_DATA: return new OpcResult(OpcResult.Cpx.S_FILTER_NO_DATA, input); // historical data access. case Com.Hda.Result.E_MAXEXCEEDED: return new OpcResult(OpcResult.Hda.E_MAXEXCEEDED, input); case Com.Hda.Result.S_NODATA: return new OpcResult(OpcResult.Hda.S_NODATA, input); case Com.Hda.Result.S_MOREDATA: return new OpcResult(OpcResult.Hda.S_MOREDATA, input); case Com.Hda.Result.E_INVALIDAGGREGATE: return new OpcResult(OpcResult.Hda.E_INVALIDAGGREGATE, input); case Com.Hda.Result.S_CURRENTVALUE: return new OpcResult(OpcResult.Hda.S_CURRENTVALUE, input); case Com.Hda.Result.S_EXTRADATA: return new OpcResult(OpcResult.Hda.S_EXTRADATA, input); case Com.Hda.Result.W_NOFILTER: return new OpcResult(OpcResult.Hda.W_NOFILTER, input); case Com.Hda.Result.E_UNKNOWNATTRID: return new OpcResult(OpcResult.Hda.E_UNKNOWNATTRID, input); case Com.Hda.Result.E_NOT_AVAIL: return new OpcResult(OpcResult.Hda.E_NOT_AVAIL, input); case Com.Hda.Result.E_INVALIDDATATYPE: return new OpcResult(OpcResult.Hda.E_INVALIDDATATYPE, input); case Com.Hda.Result.E_DATAEXISTS: return new OpcResult(OpcResult.Hda.E_DATAEXISTS, input); case Com.Hda.Result.E_INVALIDATTRID: return new OpcResult(OpcResult.Hda.E_INVALIDATTRID, input); case Com.Hda.Result.E_NODATAEXISTS: return new OpcResult(OpcResult.Hda.E_NODATAEXISTS, input); case Com.Hda.Result.S_INSERTED: return new OpcResult(OpcResult.Hda.S_INSERTED, input); case Com.Hda.Result.S_REPLACED: return new OpcResult(OpcResult.Hda.S_REPLACED, input); // Alarms and Events. case Com.Ae.Result.S_ALREADYACKED: return new OpcResult(OpcResult.Ae.S_ALREADYACKED, input); case Com.Ae.Result.S_INVALIDBUFFERTIME: return new OpcResult(OpcResult.Ae.S_INVALIDBUFFERTIME, input); case Com.Ae.Result.S_INVALIDMAXSIZE: return new OpcResult(OpcResult.Ae.S_INVALIDMAXSIZE, input); case Com.Ae.Result.S_INVALIDKEEPALIVETIME: return new OpcResult(OpcResult.Ae.S_INVALIDKEEPALIVETIME, input); // This function returns Da.Result.E_INVALID_PID. AE specific code must map to E_INVALIDBRANCHNAME. // case Technosoftware.DaAeHdaClient.Com.Ae.Result.E_INVALIDBRANCHNAME: return new OpcResult(OpcResult.Ae.E_INVALIDBRANCHNAME, input); case Com.Ae.Result.E_INVALIDTIME: return new OpcResult(OpcResult.Ae.E_INVALIDTIME, input); case Com.Ae.Result.E_BUSY: return new OpcResult(OpcResult.Ae.E_BUSY, input); case Com.Ae.Result.E_NOINFO: return new OpcResult(OpcResult.Ae.E_NOINFO, input); default: { // check for RPC error. if ((input & 0x7FFF0000) == 0x00010000) { return new OpcResult(OpcResult.E_NETWORK_ERROR, input); } // chekc for success code. if (input >= 0) { return new OpcResult(OpcResult.S_FALSE, input); } // return generic error. return new OpcResult(OpcResult.E_FAIL, input); } } } /// /// Converts a result id to an HRESULT. /// public static int GetResultID(OpcResult input) { // data access. if (input.Name == null) { return OpcResult.E_FAIL.Code; } else if (input.Name != null && input.Name.Namespace == OpcNamespace.OPC_DATA_ACCESS) { if (input == OpcResult.S_OK) return Com.Da.Result.S_OK; if (input == OpcResult.E_FAIL) return Com.Da.Result.E_FAIL; if (input == OpcResult.E_INVALIDARG) return Com.Da.Result.E_INVALIDARG; if (input == OpcResult.Da.E_BADTYPE) return Com.Da.Result.E_BADTYPE; if (input == OpcResult.Da.E_READONLY) return Com.Da.Result.E_BADRIGHTS; if (input == OpcResult.Da.E_WRITEONLY) return Com.Da.Result.E_BADRIGHTS; if (input == OpcResult.Da.E_RANGE) return Com.Da.Result.E_RANGE; if (input == OpcResult.E_OUTOFMEMORY) return Com.Da.Result.E_OUTOFMEMORY; if (input == OpcResult.E_NOTSUPPORTED) return Com.Da.Result.E_NOINTERFACE; if (input == OpcResult.Da.E_INVALIDHANDLE) return Com.Da.Result.E_INVALIDHANDLE; if (input == OpcResult.Da.E_UNKNOWN_ITEM_NAME) return Com.Da.Result.E_UNKNOWNITEMID; if (input == OpcResult.Da.E_INVALID_ITEM_NAME) return Com.Da.Result.E_INVALIDITEMID; if (input == OpcResult.Da.E_INVALID_ITEM_PATH) return Com.Da.Result.E_INVALIDITEMID; if (input == OpcResult.Da.E_UNKNOWN_ITEM_PATH) return Com.Da.Result.E_UNKNOWNPATH; if (input == OpcResult.Da.E_INVALID_FILTER) return Com.Da.Result.E_INVALIDFILTER; if (input == OpcResult.Da.S_UNSUPPORTEDRATE) return Com.Da.Result.S_UNSUPPORTEDRATE; if (input == OpcResult.Da.S_CLAMP) return Com.Da.Result.S_CLAMP; if (input == OpcResult.Da.E_INVALID_PID) return Com.Da.Result.E_INVALID_PID; if (input == OpcResult.Da.E_NO_ITEM_DEADBAND) return Com.Da.Result.E_DEADBANDNOTSUPPORTED; if (input == OpcResult.Da.E_NO_ITEM_BUFFERING) return Com.Da.Result.E_NOBUFFERING; if (input == OpcResult.Da.E_NO_WRITEQT) return Com.Da.Result.E_NOTSUPPORTED; if (input == OpcResult.Da.E_INVALIDCONTINUATIONPOINT) return Com.Da.Result.E_INVALIDCONTINUATIONPOINT; if (input == OpcResult.Da.S_DATAQUEUEOVERFLOW) return Com.Da.Result.S_DATAQUEUEOVERFLOW; } // complex data. else if (input.Name != null && input.Name.Namespace == OpcNamespace.OPC_COMPLEX_DATA) { if (input == OpcResult.Cpx.E_TYPE_CHANGED) return Com.Cpx.Result.E_TYPE_CHANGED; if (input == OpcResult.Cpx.E_FILTER_DUPLICATE) return Com.Cpx.Result.E_FILTER_DUPLICATE; if (input == OpcResult.Cpx.E_FILTER_INVALID) return Com.Cpx.Result.E_FILTER_INVALID; if (input == OpcResult.Cpx.E_FILTER_ERROR) return Com.Cpx.Result.E_FILTER_ERROR; if (input == OpcResult.Cpx.S_FILTER_NO_DATA) return Com.Cpx.Result.S_FILTER_NO_DATA; } // historical data access. else if (input.Name != null && input.Name.Namespace == OpcNamespace.OPC_HISTORICAL_DATA_ACCESS) { if (input == OpcResult.Hda.E_MAXEXCEEDED) return Com.Hda.Result.E_MAXEXCEEDED; if (input == OpcResult.Hda.S_NODATA) return Com.Hda.Result.S_NODATA; if (input == OpcResult.Hda.S_MOREDATA) return Com.Hda.Result.S_MOREDATA; if (input == OpcResult.Hda.E_INVALIDAGGREGATE) return Com.Hda.Result.E_INVALIDAGGREGATE; if (input == OpcResult.Hda.S_CURRENTVALUE) return Com.Hda.Result.S_CURRENTVALUE; if (input == OpcResult.Hda.S_EXTRADATA) return Com.Hda.Result.S_EXTRADATA; if (input == OpcResult.Hda.E_UNKNOWNATTRID) return Com.Hda.Result.E_UNKNOWNATTRID; if (input == OpcResult.Hda.E_NOT_AVAIL) return Com.Hda.Result.E_NOT_AVAIL; if (input == OpcResult.Hda.E_INVALIDDATATYPE) return Com.Hda.Result.E_INVALIDDATATYPE; if (input == OpcResult.Hda.E_DATAEXISTS) return Com.Hda.Result.E_DATAEXISTS; if (input == OpcResult.Hda.E_INVALIDATTRID) return Com.Hda.Result.E_INVALIDATTRID; if (input == OpcResult.Hda.E_NODATAEXISTS) return Com.Hda.Result.E_NODATAEXISTS; if (input == OpcResult.Hda.S_INSERTED) return Com.Hda.Result.S_INSERTED; if (input == OpcResult.Hda.S_REPLACED) return Com.Hda.Result.S_REPLACED; } // check for custom code. else if (input.Code == -1) { // default success code. if (input.Succeeded()) { return OpcResult.S_FALSE.Code; } // default error code. return OpcResult.E_FAIL.Code; } // return custom code. return input.Code; } /// /// Returns an exception after extracting HRESULT from the exception. /// public static Exception CreateException(string message, Exception e) { return CreateException(message, Marshal.GetHRForException(e)); } /// /// Returns an exception after extracting HRESULT from the exception. /// public static Exception CreateException(string message, int code) { return new OpcResultException(GetResultId(code), message); } /// /// Releases the server if it is a true COM server. /// public static void ReleaseServer(object server) { if (server != null && server.GetType().IsCOMObject) { Marshal.ReleaseComObject(server); } } /// /// Retrieves the system message text for the specified error. /// public static string GetSystemMessage(int error, int localeId) { int langId; switch (localeId) { case LOCALE_SYSTEM_DEFAULT: { langId = GetSystemDefaultLangID(); break; } case LOCALE_USER_DEFAULT: { langId = GetUserDefaultLangID(); break; } default: { langId = (0xFFFF & localeId); break; } } var buffer = Marshal.AllocCoTaskMem(MAX_MESSAGE_LENGTH); var result = FormatMessageW( (int)FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, error, langId, buffer, MAX_MESSAGE_LENGTH - 1, IntPtr.Zero); if (result > 0) { var msg = Marshal.PtrToStringUni(buffer); Marshal.FreeCoTaskMem(buffer); if (!string.IsNullOrEmpty(msg)) { return msg.Trim(); } } return $"0x{error:X8}"; } } }