Skip to content

Commit

Permalink
fixed IsVBEHosted properties and renamed to HasVBEExtensions,
Browse files Browse the repository at this point in the history
fixed AggregateInterfacesWrapper and renamed to RestrictComInterfaceByAggregation
  • Loading branch information
WaynePhillipsEA committed Jan 22, 2018
1 parent 20c5b74 commit 5ded21b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 26 deletions.
61 changes: 39 additions & 22 deletions Rubberduck.VBEEditor/ComManagement/TypeLibs/TypeLibs.cs
Expand Up @@ -70,19 +70,35 @@ public static T ReadStructureSafe<T>(IntPtr memAddress)
}
}

// AggregateSingleInterface is used to ensure that a wrapped COM object only responds to a specific interface
public class MarshalHelper
{
public static bool DoesComObjectSupportInterface(IntPtr rawObjectPtr, Guid IID)
{
IntPtr ppv = IntPtr.Zero;
if (Marshal.QueryInterface(rawObjectPtr, ref IID, out ppv) >= 0)
{
if (ppv != IntPtr.Zero)
{
Marshal.Release(ppv);
return true;
}
}

return false;
}
}

// RestrictComInterfaceByAggregation is used to ensure that a wrapped COM object only responds to a specific interface
// In particular, we don't want them to respond to IProvideClassInfo, which is broken in the VBE for some ITypeInfo implementations
public class AggregateInterfacesWrapper : ICustomQueryInterface, IDisposable
public class RestrictComInterfaceByAggregation<T> : ICustomQueryInterface, IDisposable
{
private IntPtr _outerObject;
private Type[] _supportedInterfaces;
private object _wrappedObject;

public AggregateInterfacesWrapper(IntPtr outerObject, Type[] supportedInterfaces)
public RestrictComInterfaceByAggregation(IntPtr outerObject)
{
_outerObject = outerObject;
Marshal.AddRef(_outerObject);
_supportedInterfaces = supportedInterfaces;

var aggObjPtr = Marshal.CreateAggregatedObject(_outerObject, this);
_wrappedObject = (ComTypes.ITypeInfo)Marshal.GetObjectForIUnknown(aggObjPtr); // when this CCW object gets released, it will free the aggObjInner (well, after GC)
Expand All @@ -107,14 +123,11 @@ public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)

if (!_isDisposed)
{
foreach (var intf in _supportedInterfaces)
if (iid == typeof(T).GUID)
{
if (intf.GUID == iid)
{
ppv = _outerObject;
Marshal.AddRef(_outerObject);
return CustomQueryInterfaceResult.Handled;
}
ppv = _outerObject;
Marshal.AddRef(_outerObject);
return CustomQueryInterfaceResult.Handled;
}
}

Expand Down Expand Up @@ -168,7 +181,7 @@ public class TypeInfoWrapper : ComTypes.ITypeInfo, IDisposable
private DisposableList<TypeInfoWrapper> _typeInfosWrapped;
private TypeLibWrapper _containerTypeLib;
private int _containerTypeLibIndex;
private AggregateInterfacesWrapper _typeInfoAggregatorObj;
private RestrictComInterfaceByAggregation<ComTypes.ITypeInfo> _typeInfoAggregatorObj;
private bool _isUserFormBaseClass = false;
private ComTypes.TYPEATTR _cachedAttributes;

Expand All @@ -190,7 +203,8 @@ private ITypeInfo_Ptrs _ITypeInfoAlt
private ITypeInfo_VBE _ITypeInfoVBE
{ get => ((ITypeInfo_VBE)_wrappedObject); }

public bool IsVBEHosted() => (_wrappedObject as ITypeInfo_VBE) != null;
private readonly bool _hasVBEExtensions;
public bool HasVBEExtensions() => _hasVBEExtensions;

private void CacheCommonProperties()
{
Expand Down Expand Up @@ -224,7 +238,11 @@ public TypeInfoWrapper(ComTypes.ITypeInfo rawTypeInfo)

public TypeInfoWrapper(IntPtr rawObjectPtr, int? parentUserFormUniqueId = null)
{
_typeInfoAggregatorObj = new AggregateInterfacesWrapper(rawObjectPtr, new Type[] { typeof(ComTypes.ITypeInfo), typeof(ITypeInfo_VBE) });
// We have to use DoesComObjectSupportInterface to work with the raw pointer rather than the RCW here
// due to the IProvideClassInfo VBE bug
_hasVBEExtensions = MarshalHelper.DoesComObjectSupportInterface(rawObjectPtr, typeof(ITypeInfo_VBECheck).GUID);

_typeInfoAggregatorObj = new RestrictComInterfaceByAggregation<ComTypes.ITypeInfo>(rawObjectPtr);
_wrappedObject = (ComTypes.ITypeInfo)_typeInfoAggregatorObj.WrappedObject;

// base classes of VBE UserForms cause an access violation on calling GetDocumentation(MEMBERID_NIL)
Expand All @@ -247,7 +265,6 @@ private void DetectUserFormClass()
// Determine if this is a UserForm base class, that requires special handling to workaround a VBE bug in its implemented classes
// the guids are dynamic, so we can't use them for detection.
if ((_cachedAttributes.typekind == ComTypes.TYPEKIND.TKIND_COCLASS) &&
IsVBEHosted() &&
HasNoContainer() &&
(_cachedAttributes.cImplTypes == 2) &&
(Name == "Form"))
Expand Down Expand Up @@ -338,7 +355,7 @@ public void Dispose()

public IDispatch GetStdModInstance()
{
if (IsVBEHosted())
if (HasVBEExtensions())
{
return _ITypeInfoVBE.GetStdModInstance();
}
Expand All @@ -350,7 +367,7 @@ public IDispatch GetStdModInstance()

public object StdModExecute(string name, Reflection.BindingFlags invokeAttr, object[] args = null)
{
if (IsVBEHosted())
if (HasVBEExtensions())
{
var StaticModule = GetStdModInstance();
var retVal = StaticModule.GetType().InvokeMember(name, invokeAttr, null, StaticModule, args);
Expand Down Expand Up @@ -491,13 +508,13 @@ public class TypeLibWrapper : ComTypes.ITypeLib, IDisposable
private ITypeLib_Ptrs _ITypeLibAlt
{ get => ((ITypeLib_Ptrs)_wrappedObject); }

public bool IsVBEHosted() => (_wrappedObject as IVBProjectEx_VBE) != null;
public bool HasVBEExtensions() => (_wrappedObject as IVBProjectEx_VBE) != null;

private IVBProjectEx_VBE _IVBProjectEx
{
get
{
if (IsVBEHosted())
if (HasVBEExtensions())
{
return (IVBProjectEx_VBE)_wrappedObject;
}
Expand Down Expand Up @@ -587,7 +604,7 @@ public string ConditionalCompilationArguments
{
get
{
if (IsVBEHosted())
if (HasVBEExtensions())
{
return _IVBProjectEx.get_ConditionalCompilationArgs();
}
Expand All @@ -599,7 +616,7 @@ public string ConditionalCompilationArguments

set
{
if (IsVBEHosted())
if (HasVBEExtensions())
{
_IVBProjectEx.set_ConditionalCompilationArgs(value);
}
Expand Down
Expand Up @@ -36,9 +36,15 @@ public interface ITypeInfo_Ptrs
void ReleaseVarDesc(IntPtr pVarDesc);
}

// An extended version of ITypeInfo_Ptrs that includes a particularly helpful member, GetStdModInstance
// An interface known to be supported by VBE hosted ITypeInfos
[ComImport(), Guid("CACC1E82-622B-11D2-AA78-00C04F9901D2")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITypeInfo_VBECheck
{
}

// An extended version of ITypeInfo_Ptrs that includes a particularly helpful member, GetStdModInstance
[ComImport(), Guid("00020401-0000-0000-C000-000000000046")]
public interface ITypeInfo_VBE
{
void GetTypeAttr(out IntPtr ppTypeAttr);
Expand Down
6 changes: 3 additions & 3 deletions Rubberduck.VBEEditor/ComManagement/TypeLibs/TypeLibsDebug.cs
Expand Up @@ -55,8 +55,8 @@ public void AddTypeLib(ComTypes.ITypeLib typeLib)
var typeLibVBE = typeLib as TypeLibWrapper;
if (typeLibVBE != null)
{
AppendLine("- IsVBEHosted: " + typeLibVBE.IsVBEHosted());
if (typeLibVBE.IsVBEHosted())
AppendLine("- HasVBEExtensions: " + typeLibVBE.HasVBEExtensions());
if (typeLibVBE.HasVBEExtensions())
{
AppendLine("- VBE Conditional Compilation Arguments: " + typeLibVBE.ConditionalCompilationArguments);
}
Expand Down Expand Up @@ -100,7 +100,7 @@ void AddTypeInfo(ComTypes.ITypeInfo typeInfo, string qualifiedName, int implemen
if (typeString != null) AppendLineButRemoveEmbeddedNullChars("- Documentation: " + typeString.Replace("\0", string.Empty));
if (typeHelp != 0) AppendLineButRemoveEmbeddedNullChars("- HelpContext: " + typeHelp);
if (TypeHelpFile != null) AppendLineButRemoveEmbeddedNullChars("- HelpFile: " + TypeHelpFile.Replace("\0", string.Empty));
AppendLine("- IsVBEHosted: " + (((TypeInfoWrapper)typeInfo).IsVBEHosted() ? "true" : "false")); // FIXME not safe for ITypeInfos that aren't from our wrappers
AppendLine("- HasVBEExtensions: " + (((TypeInfoWrapper)typeInfo).HasVBEExtensions() ? "true" : "false")); // FIXME not safe for ITypeInfos that aren't from our wrappers

AppendLine("- Type: " + (TYPEKIND_VBE)typeInfoAttributes.typekind);
AppendLine("- Guid: {" + typeInfoAttributes.guid + "}");
Expand Down

0 comments on commit 5ded21b

Please sign in to comment.