/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom * * For more information see: http://getgreenshot.org/ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 1 of the License, or * (at your option) any later version. * * This program 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. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using System; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Remoting; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Proxies; namespace Greenshot.Interop { /// /// Wraps a late-bound COM server. /// public sealed class COMWrapper : RealProxy, IDisposable { private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(COMWrapper)); private const int MK_E_UNAVAILABLE = -2147221021; #region Private Data /// /// Holds reference to the actual COM object which is wrapped by this proxy /// private object _COMObject; /// /// Type of the COM object, set on constructor after getting the COM reference /// private Type _COMType; /// /// The type of which method calls are intercepted and executed on the COM object. /// private Type _InterceptType; #endregion #region Construction /// /// Gets a COM object and returns the transparent proxy which intercepts all calls to the object /// /// Interface which defines the method and properties to intercept /// Transparent proxy to the real proxy for the object /// The must be an interface decorated with the attribute. public static object GetInstance(Type type) { if (null == type) { throw new ArgumentNullException("type"); } if (!type.IsInterface) { throw new ArgumentException("The specified type must be an interface.", "type"); } ComProgIdAttribute progID = ComProgIdAttribute.GetAttribute(type); if (null == progID || null == progID.Value || 0 == progID.Value.Length) { throw new ArgumentException("The specified type must define a ComProgId attribute.", "type"); } object comObject = null; Type comType = Type.GetTypeFromProgID(progID.Value, true); try { comObject = Marshal.GetActiveObject(progID.Value); } catch (COMException comE) { if (comE.ErrorCode == MK_E_UNAVAILABLE) { LOG.DebugFormat("No current instance of {0} object available.", progID.Value); } else { LOG.Warn("Error getting active object for " + progID.Value, comE); } } catch (Exception e) { LOG.Warn("Error getting active object for " + progID.Value, e); } if (comObject != null) { COMWrapper wrapper = new COMWrapper(comObject, type); return wrapper.GetTransparentProxy(); } return null; } /// /// Gets or creates a COM object and returns the transparent proxy /// which intercepts all calls to the object /// /// Interface which defines the method and properties to intercept /// Transparent proxy to the real proxy for the object /// The must be an interface decorated with the attribute. public static object GetOrCreateInstance(Type type) { if (null == type) { throw new ArgumentNullException("type"); } if (!type.IsInterface) { throw new ArgumentException("The specified type must be an interface.", "type"); } ComProgIdAttribute progID = ComProgIdAttribute.GetAttribute(type); if (null == progID || null == progID.Value || 0 == progID.Value.Length) { throw new ArgumentException("The specified type must define a ComProgId attribute.", "type"); } object comObject = null; Type comType = Type.GetTypeFromProgID(progID.Value, true); try { comObject = Marshal.GetActiveObject(progID.Value); } catch (COMException comE) { if (comE.ErrorCode == MK_E_UNAVAILABLE) { LOG.DebugFormat("No current instance of {0} object available.", progID.Value); } else { LOG.Warn("Error getting active object for " + progID.Value, comE); } } catch (Exception e) { LOG.Warn("Error getting active object for " + progID.Value, e); } // Did we get the current instance? If not, try to create a new if (comObject == null) { try { comObject = Activator.CreateInstance(comType); if (comObject != null) { LOG.DebugFormat("Created new instance of {0} object.", progID.Value); } } catch (Exception e) { LOG.Warn("Error creating object for " + progID.Value, e); } } if (comObject != null) { COMWrapper wrapper = new COMWrapper(comObject, type); return wrapper.GetTransparentProxy(); } throw new TypeLoadException(string.Format("Unable to get or create an instance of the specified COM server \"{0}\".", progID.Value)); } /// /// Wrap an object and return the transparent proxy which intercepts all calls /// to the object /// /// An object to intercept /// Interface which defines the method and properties to intercept /// Transparent proxy to the real proxy for the object public static object Wrap(object comObject, Type type) { if (null == comObject) { throw new ArgumentNullException("comObject"); } if (null == type) { throw new ArgumentNullException("type"); } COMWrapper wrapper = new COMWrapper(comObject, type); return wrapper.GetTransparentProxy(); } /// /// Constructor /// /// /// The COM object to wrap. /// /// /// The interface type to impersonate. /// private COMWrapper(object comObject, Type type) : base( type ) { this._COMObject = comObject; this._COMType = comObject.GetType(); this._InterceptType = type; } #endregion #region Clean up /// /// If is not called, we need to make /// sure that the COM object is still cleaned up. /// ~COMWrapper() { LOG.DebugFormat("Finalize {0}", this._InterceptType.ToString()); this.Dispose(false); } /// /// Cleans up the COM object. /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } /// /// Release the COM reference /// /// /// if this was called from the /// interface. /// private void Dispose(bool disposing) { if (null != this._COMObject) { if(Marshal.IsComObject(this._COMObject)) { try { while (Marshal.ReleaseComObject(this._COMObject) > 0) ; } catch (Exception ex) { LOG.WarnFormat("Problem releasing {0}", _COMType); LOG.Warn("Error: ", ex); } } this._COMObject = null; } } #endregion #region Object methods /// /// Returns a string representing the wrapped object. /// /// /// The full name of the intercepted type. /// public override string ToString() { return this._InterceptType.FullName; } /// /// Returns the hash code of the wrapped object. /// /// /// The hash code of the wrapped object. /// public override int GetHashCode() { return this._COMObject.GetHashCode(); } /// /// Compares this object to another. /// /// /// The value to compare to. /// /// /// if the objects are equal. /// public override bool Equals(object value) { if (null != value && RemotingServices.IsTransparentProxy(value)) { COMWrapper wrapper = RemotingServices.GetRealProxy(value) as COMWrapper; if (null != wrapper) { return this._COMObject == wrapper._COMObject; } } return base.Equals(value); } /// /// Returns the base type for a reference type. /// /// /// The reference type. /// /// /// The base value type. /// /// /// is . /// private static Type GetByValType(Type byRefType) { if (null == byRefType) { throw new ArgumentNullException("byRefType"); } if (byRefType.IsByRef) { string name = byRefType.FullName; name = name.Substring(0, name.Length - 1); byRefType = byRefType.Assembly.GetType(name, true); } return byRefType; } #endregion /// /// Intercept method calls /// /// /// Contains information about the method being called /// /// /// A . /// public override IMessage Invoke(IMessage myMessage) { IMethodCallMessage callMessage = myMessage as IMethodCallMessage; if (null == callMessage) { LOG.DebugFormat("Message type not implemented: {0}", myMessage.GetType().ToString()); return null; } MethodInfo method = callMessage.MethodBase as MethodInfo; if (null == method) { LOG.DebugFormat("Unrecognized Invoke call: {0}", callMessage.MethodBase.ToString()); return null; } object returnValue = null; object[] outArgs = null; int outArgsCount = 0; string methodName = method.Name; Type returnType = method.ReturnType; BindingFlags flags = BindingFlags.InvokeMethod; int argCount = callMessage.ArgCount; object invokeObject; Type invokeType; Type byValType; object[] args; object arg; COMWrapper[] originalArgs; COMWrapper wrapper; ParameterModifier[] argModifiers = null; ParameterInfo[] parameters = null; ParameterInfo parameter; if ("Dispose" == methodName && 0 == argCount && typeof(void) == returnType) { this.Dispose(); } else if ("ToString" == methodName && 0 == argCount && typeof(string) == returnType) { returnValue = this.ToString(); } else if ("GetType" == methodName && 0 == argCount && typeof(System.Type) == returnType) { returnValue = this._InterceptType; } else if ("GetHashCode" == methodName && 0 == argCount && typeof(int) == returnType) { returnValue = this.GetHashCode(); } else if ("Equals" == methodName && 1 == argCount && typeof(bool) == returnType) { returnValue = this.Equals(callMessage.Args[0]); } else if (1 == argCount && typeof(void) == returnType && (methodName.StartsWith("add_") || methodName.StartsWith("remove_"))) { bool removeHandler = methodName.StartsWith("remove_"); methodName = methodName.Substring(removeHandler ? 7 : 4); Delegate handler = callMessage.InArgs[0] as Delegate; if (null == handler) { return new ReturnMessage(new ArgumentNullException("handler"), callMessage); } } else { invokeObject = this._COMObject; invokeType = this._COMType; if (methodName.StartsWith("get_")) { // Property Get methodName = methodName.Substring(4); flags = BindingFlags.GetProperty; args = callMessage.InArgs; } else if (methodName.StartsWith("set_")) { // Property Set methodName = methodName.Substring(4); flags = BindingFlags.SetProperty; args = callMessage.InArgs; } else { args = callMessage.Args; if (null != args && 0 != args.Length) { // Modifiers for ref / out parameters argModifiers = new ParameterModifier[1]; argModifiers[0] = new ParameterModifier(args.Length); parameters = method.GetParameters(); for(int i=0; i