/*
* 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.Diagnostics;
using System.Runtime.Remoting.Proxies;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting;
using System.Reflection;
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 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)) {
while( Marshal.ReleaseComObject(this._COMObject) > 0 );
}
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