/* ========================================================================
* Copyright (c) 2005-2019 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/
* ======================================================================*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Security.Principal;
using System.Threading;
using System.Runtime.InteropServices;
using Opc.Ua;
using Opc.Ua.Server;
using Opc.Ua.Com;
using OpcRcw.Da;
namespace Opc.Ua.Com.Client
{
///
/// A base class for COM server wrappers.
///
public class ComObject : IDisposable
{
#region IDisposable Members
///
/// The finializer implementation.
///
~ComObject()
{
Dispose(false);
}
///
/// Frees any unmanaged resources.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// An overrideable version of the Dispose.
///
protected virtual void Dispose(bool disposing)
{
// release references to the server during garbage collection.
if (!disposing)
{
ReleaseServer();
}
// clean up managed objects if
if (disposing)
{
lock (m_lock)
{
m_disposed = true;
// only release server if there are no outstanding calls.
// if it is not released here it will be released when the last call completes.
if (m_outstandingCalls <= 0)
{
ReleaseServer();
}
}
}
}
#endregion
#region Public Properties
///
/// Gets an object which is used to synchronize access to the COM object.
///
/// An object which is used to synchronize access to the COM object.
public object Lock
{
get { return m_lock; }
}
///
/// Gets a value indicating whether this is disposed.
///
/// true if disposed; otherwise, false.
public bool Disposed
{
get { return m_disposed; }
}
///
/// Gets or sets the COM server.
///
/// The COM server.
public object Unknown
{
get { return m_unknown; }
set { m_unknown = value; }
}
#endregion
#region Protected Members
///
/// Releases all references to the server.
///
protected virtual void ReleaseServer()
{
lock (m_lock)
{
ComUtils.ReleaseServer(m_unknown);
m_unknown = null;
}
}
///
/// Checks if the server supports the specified interface.
///
/// The interface to check.
/// True if the server supports the interface.
protected bool SupportsInterface() where T : class
{
lock (m_lock)
{
return m_unknown is T;
}
}
///
/// Must be called before any COM call.
///
/// The interface to used when making the call.
/// Name of the method.
/// if set to true an exception is thrown on error.
///
protected T BeginComCall(string methodName, bool throwOnError) where T : class
{
Utils.Trace(Utils.TraceMasks.ExternalSystem, "{0} called.", methodName);
lock (m_lock)
{
m_outstandingCalls++;
if (m_disposed)
{
if (throwOnError)
{
throw new ObjectDisposedException("The COM server has been disposed.");
}
return null;
}
T server = m_unknown as T;
if (throwOnError && server == null)
{
throw new NotSupportedException(Utils.Format("COM interface '{0}' is not supported by server.", typeof(T).Name));
}
return server;
}
}
///
/// Must called if a COM call returns an unexpected exception.
///
/// Name of the method.
/// The exception.
/// Note that some COM calls are expected to return errors.
protected void ComCallError(string methodName, Exception e)
{
ComUtils.TraceComError(e, methodName);
}
///
/// Must be called in the finally block after making a COM call.
///
/// Name of the method.
protected void EndComCall(string methodName)
{
Utils.Trace(Utils.TraceMasks.ExternalSystem, "{0} completed.", methodName);
lock (m_lock)
{
m_outstandingCalls--;
if (m_disposed && m_outstandingCalls <= 0)
{
ComUtils.ReleaseServer(m_unknown);
}
}
}
#endregion
#region Private Fields
private object m_lock = new object();
private int m_outstandingCalls;
private bool m_disposed;
private object m_unknown;
#endregion
}
}