From 3586a84d8a8d0a1966f5afc10bfc470113cf846f Mon Sep 17 00:00:00 2001 From: comintern Date: Sat, 24 Dec 2016 23:19:33 -0600 Subject: [PATCH 01/15] Massive refactor of COM reflection. Much tech debt paid. --- Rubberduck.Parsing/ComReflection/ComBase.cs | 52 ++ .../ComReflection/ComCoClass.cs | 87 +++ .../ComReflection/ComDocumentation.cs | 45 ++ .../ComReflection/ComEnumeration.cs | 36 + .../ComReflection/ComEnumerationMember.cs | 28 + Rubberduck.Parsing/ComReflection/ComField.cs | 40 + .../ComReflection/ComInterface.cs | 82 ++ Rubberduck.Parsing/ComReflection/ComMember.cs | 128 ++++ Rubberduck.Parsing/ComReflection/ComModule.cs | 73 ++ .../ComReflection/ComParameter.cs | 136 ++++ .../ComReflection/ComProject.cs | 142 ++++ Rubberduck.Parsing/ComReflection/ComType.cs | 50 ++ .../ComReflection/ComVariant.cs | 66 ++ Rubberduck.Parsing/Rubberduck.Parsing.csproj | 15 +- .../Symbols/ClassModuleDeclaration.cs | 40 + Rubberduck.Parsing/Symbols/ComInformation.cs | 33 - Rubberduck.Parsing/Symbols/ComParameter.cs | 15 - Rubberduck.Parsing/Symbols/Declaration.cs | 54 +- .../Symbols/FunctionDeclaration.cs | 23 + .../Symbols/ParameterDeclaration.cs | 16 + .../Symbols/ProceduralModuleDeclaration.cs | 34 +- .../Symbols/ProjectDeclaration.cs | 10 +- .../Symbols/PropertyGetDeclaration.cs | 24 + .../Symbols/PropertyLetDeclaration.cs | 21 + .../Symbols/PropertySetDeclaration.cs | 20 + .../ReferencedDeclarationsCollector.cs | 699 +++--------------- .../Symbols/SerializableDeclaration.cs | 39 +- .../Symbols/SubroutineDeclaration.cs | 23 +- Rubberduck.Parsing/VBA/RubberduckParser.cs | 12 +- 29 files changed, 1385 insertions(+), 658 deletions(-) create mode 100644 Rubberduck.Parsing/ComReflection/ComBase.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComCoClass.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComDocumentation.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComEnumeration.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComEnumerationMember.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComField.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComInterface.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComMember.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComModule.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComParameter.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComProject.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComType.cs create mode 100644 Rubberduck.Parsing/ComReflection/ComVariant.cs delete mode 100644 Rubberduck.Parsing/Symbols/ComInformation.cs delete mode 100644 Rubberduck.Parsing/Symbols/ComParameter.cs diff --git a/Rubberduck.Parsing/ComReflection/ComBase.cs b/Rubberduck.Parsing/ComReflection/ComBase.cs new file mode 100644 index 0000000000..22ff07b344 --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComBase.cs @@ -0,0 +1,52 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices.ComTypes; +using Rubberduck.Parsing.Symbols; +using FUNCDESC = System.Runtime.InteropServices.ComTypes.FUNCDESC; + +namespace Rubberduck.Parsing.ComReflection +{ + public interface IComBase + { + Guid Guid { get; } + int Index { get; } + ComDocumentation Documentation { get; } + string Name { get; } + DeclarationType Type { get; } + } + + public abstract class ComBase : IComBase + { + public Guid Guid { get; protected set; } + public int Index { get; protected set; } + public ComDocumentation Documentation { get; protected set; } + public string Name + { + get { return Documentation == null ? string.Empty : Documentation.Name; } + } + + public DeclarationType Type { get; protected set; } + + protected ComBase(ITypeLib typeLib, int index) + { + Index = index; + Documentation = new ComDocumentation(typeLib, index); + } + + protected ComBase(ITypeInfo info) + { + ITypeLib typeLib; + int index; + info.GetContainingTypeLib(out typeLib, out index); + Index = index; + Debug.Assert(typeLib != null); + Documentation = new ComDocumentation(typeLib, index); + } + + protected ComBase(ITypeInfo info, FUNCDESC funcDesc) + { + Index = funcDesc.memid; + Documentation = new ComDocumentation(info, funcDesc.memid); + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComCoClass.cs b/Rubberduck.Parsing/ComReflection/ComCoClass.cs new file mode 100644 index 0000000000..9548227618 --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComCoClass.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using Rubberduck.Parsing.Symbols; +using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; +using IMPLTYPEFLAGS = System.Runtime.InteropServices.ComTypes.IMPLTYPEFLAGS; + +namespace Rubberduck.Parsing.ComReflection +{ + public class ComCoClass : ComType, IComTypeWithMembers + { + private readonly Dictionary _interfaces = new Dictionary(); + private readonly List _events = new List(); + + public ComInterface DefaultInterface { get; private set; } + + public IEnumerable EventInterfaces + { + get { return _events; } + } + public IEnumerable ImplementedInterfaces + { + get { return _interfaces.Keys; } + } + + public IEnumerable VisibleInterfaces + { + get { return _interfaces.Where(i => !i.Value).Select(i => i.Key); } + } + + public IEnumerable Members + { + get { return ImplementedInterfaces.SelectMany(i => i.Members); } + } + + public bool WithEvents + { + get { return _events.Count > 0; } + } + + public ComCoClass(ITypeLib typeLib, ITypeInfo info, TYPEATTR attrib, int index) : base (typeLib, attrib, index) + { + Type = DeclarationType.ClassModule; + GetImplementedInterfaces(info, attrib); + Debug.Assert(attrib.cFuncs == 0); + } + + private void GetImplementedInterfaces(ITypeInfo info, TYPEATTR typeAttr) + { + for (var implIndex = 0; implIndex < typeAttr.cImplTypes; implIndex++) + { + int href; + info.GetRefTypeOfImplType(implIndex, out href); + + ITypeInfo implemented; + info.GetRefTypeInfo(href, out implemented); + + IntPtr attribPtr; + implemented.GetTypeAttr(out attribPtr); + var attribs = (TYPEATTR)Marshal.PtrToStructure(attribPtr, typeof(TYPEATTR)); + + ComType inherited; + ComProject.KnownTypes.TryGetValue(attribs.guid, out inherited); + var intface = inherited as ComInterface ?? new ComInterface(implemented, attribs); + ComProject.KnownTypes.TryAdd(attribs.guid, intface); + + IMPLTYPEFLAGS flags = 0; + try + { + info.GetImplTypeFlags(implIndex, out flags); + } + catch (COMException) { } + + DefaultInterface = flags.HasFlag(IMPLTYPEFLAGS.IMPLTYPEFLAG_FDEFAULT) ? intface : DefaultInterface; + if (flags.HasFlag(IMPLTYPEFLAGS.IMPLTYPEFLAG_FSOURCE)) + { + _events.Add(intface); + } + _interfaces.Add(intface, flags.HasFlag(IMPLTYPEFLAGS.IMPLTYPEFLAG_FRESTRICTED)); + info.ReleaseTypeAttr(attribPtr); + } + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComDocumentation.cs b/Rubberduck.Parsing/ComReflection/ComDocumentation.cs new file mode 100644 index 0000000000..5b3f6b9a1f --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComDocumentation.cs @@ -0,0 +1,45 @@ +using System.Runtime.InteropServices.ComTypes; + +namespace Rubberduck.Parsing.ComReflection +{ + public class ComDocumentation + { + public string Name { get; private set; } + public string DocString { get; private set; } + public string HelpFile { get; private set; } + public int HelpContext { get; private set; } + + public ComDocumentation(ITypeLib typeLib, int index) + { + LoadDocumentation(typeLib, null, index); + } + + public ComDocumentation(ITypeInfo info, int index) + { + LoadDocumentation(null, info, index); + } + + private void LoadDocumentation(ITypeLib typeLib, ITypeInfo info, int index) + { + string name; + string docString; + int helpContext; + string helpFile; + + if (info == null) + { + typeLib.GetDocumentation(index, out name, out docString, out helpContext, out helpFile); + } + else + { + info.GetDocumentation(index, out name, out docString, out helpContext, out helpFile); + } + + //See http://chat.stackexchange.com/transcript/message/30119269#30119269 + Name = name.Equals("LONG_PTR") ? "LongPtr" : name; + DocString = docString; + HelpContext = helpContext; + HelpFile = helpFile; + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComEnumeration.cs b/Rubberduck.Parsing/ComReflection/ComEnumeration.cs new file mode 100644 index 0000000000..92eb7ec88c --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComEnumeration.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using Rubberduck.Parsing.Symbols; +using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; +using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC; + +namespace Rubberduck.Parsing.ComReflection +{ + public class ComEnumeration : ComType + { + public List Members { get; set; } + + public ComEnumeration(ITypeLib typeLib, ITypeInfo info, TYPEATTR attrib, int index) : base(typeLib, attrib, index) + { + Members = new List(); + Type = DeclarationType.Enumeration; + GetEnumerationMembers(info, attrib); + ComProject.KnownEnumerations.TryAdd(Guid, this); + } + + private void GetEnumerationMembers(ITypeInfo info, TYPEATTR attrib) + { + var count = attrib.cVars; + for (var index = 0; index < count; index++) + { + IntPtr varPtr; + info.GetVarDesc(index, out varPtr); + var desc = (VARDESC)Marshal.PtrToStructure(varPtr, typeof(VARDESC)); + Members.Add(new ComEnumerationMember(info, desc)); + info.ReleaseVarDesc(varPtr); + } + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComEnumerationMember.cs b/Rubberduck.Parsing/ComReflection/ComEnumerationMember.cs new file mode 100644 index 0000000000..66de6d403b --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComEnumerationMember.cs @@ -0,0 +1,28 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC; + +namespace Rubberduck.Parsing.ComReflection +{ + [DebuggerDisplay("{Name} = {Value} ({ValueType})")] + public class ComEnumerationMember + { + public string Name { get; private set; } + public int Value { get; private set; } + public VarEnum ValueType { get; private set; } + + public ComEnumerationMember(ITypeInfo info, VARDESC varDesc) + { + var value = new ComVariant(varDesc.desc.lpvarValue); + Value = (int)value.Value; + ValueType = value.VariantType; + + var names = new string[255]; + int count; + info.GetNames(varDesc.memid, names, 1, out count); + Debug.Assert(count == 1); + Name = names[0]; + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComField.cs b/Rubberduck.Parsing/ComReflection/ComField.cs new file mode 100644 index 0000000000..28445884a1 --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComField.cs @@ -0,0 +1,40 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC; +using VARFLAGS = System.Runtime.InteropServices.ComTypes.VARFLAGS; + +namespace Rubberduck.Parsing.ComReflection +{ + [DebuggerDisplay("{Name}")] + public class ComField + { + public string Name { get; set; } + public int Index { get; set; } + public bool IsConstant { get; set; } + public object DefaultValue { get; set; } + public VarEnum DefaultValueType { get; set; } + public VARFLAGS Flags { get; set; } + + public ComField(ITypeInfo info, VARDESC varDesc, int index) + { + Index = index; + + var names = new string[255]; + int length; + info.GetNames(varDesc.memid, names, 255, out length); + Debug.Assert(length >= 1); + Name = names[0]; + + IsConstant = varDesc.varkind.HasFlag(VARKIND.VAR_CONST); + Flags = (VARFLAGS)varDesc.wVarFlags; + + if (IsConstant) + { + var value = new ComVariant(varDesc.desc.lpvarValue); + DefaultValue = value.Value; + DefaultValueType = value.VariantType; + } + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComInterface.cs b/Rubberduck.Parsing/ComReflection/ComInterface.cs new file mode 100644 index 0000000000..46ff32a177 --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComInterface.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using Rubberduck.Parsing.Symbols; +using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; +using FUNCDESC = System.Runtime.InteropServices.ComTypes.FUNCDESC; +using CALLCONV = System.Runtime.InteropServices.ComTypes.CALLCONV; + +namespace Rubberduck.Parsing.ComReflection +{ + [DebuggerDisplay("{Name}")] + public class ComInterface : ComType, IComTypeWithMembers + { + private readonly List _inherited = new List(); + private readonly List _members = new List(); + + public IEnumerable InheritedInterfaces + { + get { return _inherited; } + } + + public IEnumerable Members + { + get { return _members; } + } + + public ComInterface(ITypeInfo info, TYPEATTR attrib) : base(info, attrib) + { + GetImplementedInterfaces(info, attrib); + GetComMembers(info, attrib); + } + + public ComInterface(ITypeLib typeLib, ITypeInfo info, TYPEATTR attrib, int index) : base(typeLib, attrib, index) + { + Type = DeclarationType.ClassModule; + GetImplementedInterfaces(info, attrib); + GetComMembers(info, attrib); + } + + private void GetImplementedInterfaces(ITypeInfo info, TYPEATTR typeAttr) + { + for (var implIndex = 0; implIndex < typeAttr.cImplTypes; implIndex++) + { + int href; + info.GetRefTypeOfImplType(implIndex, out href); + + ITypeInfo implemented; + info.GetRefTypeInfo(href, out implemented); + + IntPtr attribPtr; + implemented.GetTypeAttr(out attribPtr); + var attribs = (TYPEATTR)Marshal.PtrToStructure(attribPtr, typeof(TYPEATTR)); + + ComType inherited; + ComProject.KnownTypes.TryGetValue(attribs.guid, out inherited); + var intface = inherited as ComInterface ?? new ComInterface(implemented, attribs); + _inherited.Add(intface); + ComProject.KnownTypes.TryAdd(attribs.guid, intface); + + info.ReleaseTypeAttr(attribPtr); + } + } + + private void GetComMembers(ITypeInfo info, TYPEATTR attrib) + { + for (var index = 0; index < attrib.cFuncs; index++) + { + IntPtr memberPtr; + info.GetFuncDesc(index, out memberPtr); + var member = (FUNCDESC)Marshal.PtrToStructure(memberPtr, typeof(FUNCDESC)); + if (member.callconv != CALLCONV.CC_STDCALL) + { + continue; + } + _members.Add(new ComMember(info, member)); + info.ReleaseFuncDesc(memberPtr); + } + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComMember.cs b/Rubberduck.Parsing/ComReflection/ComMember.cs new file mode 100644 index 0000000000..66db42b908 --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComMember.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using Rubberduck.Parsing.Symbols; +using ELEMDESC = System.Runtime.InteropServices.ComTypes.ELEMDESC; +using FUNCDESC = System.Runtime.InteropServices.ComTypes.FUNCDESC; +using INVOKEKIND = System.Runtime.InteropServices.ComTypes.INVOKEKIND; +using FUNCFLAGS = System.Runtime.InteropServices.ComTypes.FUNCFLAGS; + +namespace Rubberduck.Parsing.ComReflection +{ + [DebuggerDisplay("{MemberDeclaration}")] + public class ComMember : ComBase + { + public bool IsHidden { get; private set; } + public bool IsRestricted { get; private set; } + public bool IsEventHandler { get; private set; } + public bool IsDefault { get; private set; } + public bool IsEnumerator { get; private set; } + public ComParameter ReturnType { get; private set; } + public List Parameters { get; set; } + + public ComMember(ITypeInfo info, FUNCDESC funcDesc) : base(info, funcDesc) + { + LoadParameters(funcDesc, info); + var flags = (FUNCFLAGS)funcDesc.wFuncFlags; + IsHidden = flags.HasFlag(FUNCFLAGS.FUNCFLAG_FHIDDEN); + IsRestricted = flags.HasFlag(FUNCFLAGS.FUNCFLAG_FRESTRICTED); + IsEventHandler = flags.HasFlag(FUNCFLAGS.FUNCFLAG_FSOURCE); + IsDefault = flags.HasFlag(FUNCFLAGS.FUNCFLAG_FUIDEFAULT); + IsEnumerator = flags.HasFlag(FUNCFLAGS.FUNCFLAG_FNONBROWSABLE) && Name.Equals("_NewEnum"); + SetDeclarationType(funcDesc, info); + } + + private void SetDeclarationType(FUNCDESC funcDesc, ITypeInfo info) + { + if (IsEventHandler) + { + Type = DeclarationType.Event; + } + if (funcDesc.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYGET)) + { + Type = DeclarationType.PropertyGet; + + } + else if (funcDesc.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYPUT)) + { + Type = DeclarationType.PropertyLet; + } + else if (funcDesc.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYPUTREF)) + { + Type = DeclarationType.PropertySet; + } + else if ((VarEnum)funcDesc.elemdescFunc.tdesc.vt == VarEnum.VT_VOID) + { + Type = DeclarationType.Procedure; + } + else + { + Type = DeclarationType.Function; + } + + if (Type == DeclarationType.Function || Type == DeclarationType.PropertyGet) + { + ReturnType = new ComParameter(funcDesc.elemdescFunc, info, string.Empty); + } + } + + private void LoadParameters(FUNCDESC funcDesc, ITypeInfo info) + { + Parameters = new List(); + var names = new string[255]; + int count; + info.GetNames(Index, names, 255, out count); + + for (var index = 0; index < funcDesc.cParams; index++) + { + var paramPtr = new IntPtr(funcDesc.lprgelemdescParam.ToInt64() + Marshal.SizeOf(typeof(ELEMDESC)) * index); + var elemDesc = (ELEMDESC)Marshal.PtrToStructure(paramPtr, typeof(ELEMDESC)); + var param = new ComParameter(elemDesc, info, names[index + 1]); + Parameters.Add(param); + //TODO: + //if (parameters.Any() && memberDescriptor.cParamsOpt == -1) + //{ + // parameters.Last().IsParamArray = true; + //} + } + } + + // ReSharper disable once UnusedMember.Local + private string MemberDeclaration + { + get + { + var type = string.Empty; + switch (Type) + { + case DeclarationType.Function: + type = "Function"; + break; + case DeclarationType.Procedure: + type = "Sub"; + break; + case DeclarationType.PropertyGet: + type = "Property Get"; + break; + case DeclarationType.PropertyLet: + type = "Property Let"; + break; + case DeclarationType.PropertySet: + type = "Property Set"; + break; + case DeclarationType.Event: + type = "Event"; + break; + } + return string.Format("{0} {1} {2}{3}{4}", + IsHidden || IsRestricted ? "Private" : "Public", + type, + Name, + ReturnType == null ? string.Empty : " As ", + ReturnType == null ? string.Empty : ReturnType.TypeName); + } + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComModule.cs b/Rubberduck.Parsing/ComReflection/ComModule.cs new file mode 100644 index 0000000000..4b52fd6d8a --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComModule.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using Rubberduck.Parsing.Symbols; +using FUNCDESC = System.Runtime.InteropServices.ComTypes.FUNCDESC; +using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; +using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC; +using CALLCONV = System.Runtime.InteropServices.ComTypes.CALLCONV; + +namespace Rubberduck.Parsing.ComReflection +{ + public class ComModule : ComType, IComTypeWithMembers + { + private readonly List _members = new List(); + public IEnumerable Members + { + get { return _members; } + } + + private readonly List _fields = new List(); + public IEnumerable Fields + { + get { return _fields; } + } + + public ComModule(ITypeLib typeLib, ITypeInfo info, TYPEATTR attrib, int index) : base(typeLib, attrib, index) + { + if (attrib.cFuncs > 0) + { + Debug.Assert(attrib.cVars == 0); + Type = DeclarationType.ProceduralModule; + GetComMembers(info, attrib); + } + else + { + Debug.Assert(attrib.cVars > 0); + Type = DeclarationType.Module; + GetComFields(info, attrib); + } + } + + private void GetComFields(ITypeInfo info, TYPEATTR attrib) + { + for (var index = 0; index < attrib.cVars; index++) + { + IntPtr ppVarDesc; + info.GetVarDesc(index, out ppVarDesc); + var varDesc = (VARDESC)Marshal.PtrToStructure(ppVarDesc, typeof(VARDESC)); + + _fields.Add(new ComField(info, varDesc, index)); + info.ReleaseVarDesc(ppVarDesc); + } + } + + private void GetComMembers(ITypeInfo info, TYPEATTR attrib) + { + for (var index = 0; index < attrib.cFuncs; index++) + { + IntPtr memberPtr; + info.GetFuncDesc(index, out memberPtr); + var member = (FUNCDESC)Marshal.PtrToStructure(memberPtr, typeof(FUNCDESC)); + if (member.callconv != CALLCONV.CC_STDCALL) + { + continue; + } + _members.Add(new ComMember(info, member)); + info.ReleaseFuncDesc(memberPtr); + } + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComParameter.cs b/Rubberduck.Parsing/ComReflection/ComParameter.cs new file mode 100644 index 0000000000..343e05daaf --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComParameter.cs @@ -0,0 +1,136 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using ELEMDESC = System.Runtime.InteropServices.ComTypes.ELEMDESC; +using PARAMFLAG = System.Runtime.InteropServices.ComTypes.PARAMFLAG; +using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; +using TYPEDESC = System.Runtime.InteropServices.ComTypes.TYPEDESC; +using TYPEKIND = System.Runtime.InteropServices.ComTypes.TYPEKIND; + +namespace Rubberduck.Parsing.ComReflection +{ + [DebuggerDisplay("{DeclarationName}")] + public class ComParameter + { + public string Name { get; private set; } + + public string DeclarationName + { + get + { + return string.Format("{0}{1} {2} As {3}{4}{5}", + IsOptional ? "Optional " : string.Empty, + IsByRef ? "ByRef" : "ByVal", + Name, + TypeName, + IsOptional && DefaultValue != null ? " = " : string.Empty, + IsOptional && DefaultValue != null ? + IsEnumMember ? DefaultAsEnum : DefaultValue + : string.Empty); + } + } + + public bool IsArray { get; private set; } + public bool IsByRef { get; private set; } + public bool IsOptional { get; private set; } + public bool IsParamArray { get; set; } + + + private Guid _enumGuid = Guid.Empty; + public bool IsEnumMember + { + get { return !_enumGuid.Equals(Guid.Empty); } + } + public object DefaultValue { get; private set; } + public string DefaultAsEnum { get; private set; } + + private string _type = "Object"; + public string TypeName + { + get + { + return IsArray ? _type + "()" : _type; + } + } + + public ComParameter(ELEMDESC elemDesc, ITypeInfo info, string name) + { + Name = name; + var paramDesc = elemDesc.desc.paramdesc; + GetParameterType(elemDesc.tdesc, info); + IsOptional = paramDesc.wParamFlags.HasFlag(PARAMFLAG.PARAMFLAG_FOPT); + if (!paramDesc.wParamFlags.HasFlag(PARAMFLAG.PARAMFLAG_FHASDEFAULT) || string.IsNullOrEmpty(name)) + { + DefaultAsEnum = string.Empty; + return; + } + + //lpVarValue points to a PARAMDESCEX structure, but we don't care about the cBytes here at all. + //Offset and dereference the VARIANTARG directly. + var defValue = new ComVariant(paramDesc.lpVarValue + Marshal.SizeOf(typeof(ulong))); + DefaultValue = defValue.Value; + + ComEnumeration enumType; + if (!IsEnumMember || !ComProject.KnownEnumerations.TryGetValue(_enumGuid, out enumType)) + { + return; + } + var member = enumType.Members.FirstOrDefault(m => m.Value == (int)DefaultValue); + DefaultAsEnum = member != null ? member.Name : string.Empty; + } + + private void GetParameterType(TYPEDESC desc, ITypeInfo info) + { + var vt = (VarEnum)desc.vt; + TYPEDESC tdesc; + + switch (vt) + { + case VarEnum.VT_PTR: + tdesc = (TYPEDESC)Marshal.PtrToStructure(desc.lpValue, typeof(TYPEDESC)); + GetParameterType(tdesc, info); + IsByRef = true; + break; + case VarEnum.VT_USERDEFINED: + int href; + unchecked + { + href = (int)(desc.lpValue.ToInt64() & 0xFFFFFFFF); + } + try + { + ITypeInfo refTypeInfo; + info.GetRefTypeInfo(href, out refTypeInfo); + + IntPtr attribPtr; + refTypeInfo.GetTypeAttr(out attribPtr); + var attribs = (TYPEATTR)Marshal.PtrToStructure(attribPtr, typeof(TYPEATTR)); + if (attribs.typekind == TYPEKIND.TKIND_ENUM) + { + _enumGuid = attribs.guid; + } + _type = new ComDocumentation(refTypeInfo, -1).Name; + refTypeInfo.ReleaseTypeAttr(attribPtr); + } + catch (COMException) { } + break; + case VarEnum.VT_SAFEARRAY: + case VarEnum.VT_CARRAY: + case VarEnum.VT_ARRAY: + tdesc = (TYPEDESC)Marshal.PtrToStructure(desc.lpValue, typeof(TYPEDESC)); + GetParameterType(tdesc, info); + IsArray = true; + break; + default: + string result; + if (ComVariant.TypeNames.TryGetValue(vt, out result)) + { + _type = result; + } + break; + } + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComProject.cs b/Rubberduck.Parsing/ComReflection/ComProject.cs new file mode 100644 index 0000000000..b27c9df139 --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComProject.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; +using TYPEKIND = System.Runtime.InteropServices.ComTypes.TYPEKIND; +using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR; + +namespace Rubberduck.Parsing.ComReflection +{ + [DebuggerDisplay("{Name}")] + public class ComProject : ComBase + { + public static ConcurrentDictionary KnownTypes = new ConcurrentDictionary(); + public static ConcurrentDictionary KnownEnumerations = new ConcurrentDictionary(); + + public string Path { get; set; } + public long MajorVersion { get; private set; } + public long MinorVersion { get; private set; } + + // YGNI... + // ReSharper disable once NotAccessedField.Local + private TypeLibTypeFlags _flags; + + private readonly List _interfaces = new List(); + public IEnumerable Interfaces + { + get { return _interfaces; } + } + + private readonly List _enumerations = new List(); + public IEnumerable Enumerations + { + get { return _enumerations; } + } + + private readonly List _classes = new List(); + public IEnumerable CoClasses + { + get { return _classes; } + } + + private readonly List _modules = new List(); + public IEnumerable Modules + { + get { return _modules; } + } + + public IEnumerable Members + { + get + { + return _modules.Cast() + .Union(_interfaces) + .Union(_classes) + .Union(_enumerations); + } + } + + public ComProject(ITypeLib typeLibrary) : base(typeLibrary, -1) + { + ProcessLibraryAttributes(typeLibrary); + LoadModules(typeLibrary); + } + + private void ProcessLibraryAttributes(ITypeLib typeLibrary) + { + try + { + IntPtr attribPtr; + typeLibrary.GetLibAttr(out attribPtr); + var typeAttr = (TYPELIBATTR)Marshal.PtrToStructure(attribPtr, typeof(TYPELIBATTR)); + + MajorVersion = typeAttr.wMajorVerNum; + MinorVersion = typeAttr.wMinorVerNum; + _flags = (TypeLibTypeFlags)typeAttr.wLibFlags; + Guid = typeAttr.guid; + } + catch (COMException) { } + } + + private void LoadModules(ITypeLib typeLibrary) + { + var typeCount = typeLibrary.GetTypeInfoCount(); + for (var index = 0; index < typeCount; index++) + { + try + { + ITypeInfo info; + typeLibrary.GetTypeInfo(index, out info); + IntPtr typeAttributesPointer; + info.GetTypeAttr(out typeAttributesPointer); + var typeAttributes = (TYPEATTR)Marshal.PtrToStructure(typeAttributesPointer, typeof(TYPEATTR)); + + ComType type; + KnownTypes.TryGetValue(typeAttributes.guid, out type); + + switch (typeAttributes.typekind) + { + case TYPEKIND.TKIND_ENUM: + var enumeration = type ?? new ComEnumeration(typeLibrary, info, typeAttributes, index); + _enumerations.Add(enumeration as ComEnumeration); + if (type != null) KnownTypes.TryAdd(typeAttributes.guid, enumeration); + break; + case TYPEKIND.TKIND_COCLASS: + var coclass = type ?? new ComCoClass(typeLibrary, info, typeAttributes, index); + _classes.Add(coclass as ComCoClass); + if (type != null) KnownTypes.TryAdd(typeAttributes.guid, coclass); + break; + case TYPEKIND.TKIND_DISPATCH: + case TYPEKIND.TKIND_INTERFACE: + var intface = type ?? new ComInterface(typeLibrary, info, typeAttributes, index); + _interfaces.Add(intface as ComInterface); + if (type != null) KnownTypes.TryAdd(typeAttributes.guid, intface); + break; + case TYPEKIND.TKIND_RECORD: + //IIR these aren't available to VBA. I could quite possibly be wrong though. + throw new NotImplementedException(string.Format("Didn't expect to find a TKIND_RECORD in {0}.", Path)); + case TYPEKIND.TKIND_MODULE: + var module = type ?? new ComModule(typeLibrary, info, typeAttributes, index); + _modules.Add(module as ComModule); + if (type != null) KnownTypes.TryAdd(typeAttributes.guid, module); + break; + case TYPEKIND.TKIND_ALIAS: + //Haven't seen one of these either. + throw new NotImplementedException(string.Format("Didn't expect to find a TKIND_ALIAS in {0}.", Path)); + default: + throw new NotImplementedException(string.Format("Didn't expect a TYPEATTR with multiple typekind flags set in {0}.", Path)); + } + info.ReleaseTypeAttr(typeAttributesPointer); + } + catch (NullReferenceException) + { + return; + } + } + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComType.cs b/Rubberduck.Parsing/ComReflection/ComType.cs new file mode 100644 index 0000000000..5fd6e35c2e --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComType.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices.ComTypes; + +namespace Rubberduck.Parsing.ComReflection +{ + public interface IComType : IComBase + { + bool IsAppObject { get; } + bool IsPreDeclared { get; } + bool IsHidden { get; } + bool IsRestricted { get; } + } + + public interface IComTypeWithMembers : IComType + { + IEnumerable Members { get; } + } + + [DebuggerDisplay("{Name}")] + public abstract class ComType : ComBase, IComType + { + public bool IsAppObject { get; private set; } + public bool IsPreDeclared { get; private set; } + public bool IsHidden { get; private set; } + public bool IsRestricted { get; private set; } + + protected ComType(ITypeInfo info, TYPEATTR attrib) + : base(info) + { + SetFlagsFromTypeAttr(attrib); + } + + protected ComType(ITypeLib typeLib, TYPEATTR attrib, int index) + : base(typeLib, index) + { + Index = index; + SetFlagsFromTypeAttr(attrib); + } + + private void SetFlagsFromTypeAttr(TYPEATTR attrib) + { + Guid = attrib.guid; + IsAppObject = attrib.wTypeFlags.HasFlag(TYPEFLAGS.TYPEFLAG_FAPPOBJECT); + IsPreDeclared = attrib.wTypeFlags.HasFlag(TYPEFLAGS.TYPEFLAG_FPREDECLID); + IsHidden = attrib.wTypeFlags.HasFlag(TYPEFLAGS.TYPEFLAG_FHIDDEN); + IsRestricted = attrib.wTypeFlags.HasFlag(TYPEFLAGS.TYPEFLAG_FRESTRICTED); + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComVariant.cs b/Rubberduck.Parsing/ComReflection/ComVariant.cs new file mode 100644 index 0000000000..d53e663eed --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComVariant.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Rubberduck.Parsing.ComReflection +{ + //See https://limbioliong.wordpress.com/2011/09/04/using-variants-in-managed-code-part-1/ + public class ComVariant + { + internal static readonly IDictionary TypeNames = new Dictionary + { + {VarEnum.VT_DISPATCH, "Object"}, + {VarEnum.VT_VOID, string.Empty}, + {VarEnum.VT_VARIANT, "Variant"}, + {VarEnum.VT_BLOB_OBJECT, "Object"}, + {VarEnum.VT_STORED_OBJECT, "Object"}, + {VarEnum.VT_STREAMED_OBJECT, "Object"}, + {VarEnum.VT_BOOL, "Boolean"}, + {VarEnum.VT_BSTR, "String"}, + {VarEnum.VT_LPSTR, "String"}, + {VarEnum.VT_LPWSTR, "String"}, + {VarEnum.VT_I1, "Variant"}, // no signed byte type in VBA + {VarEnum.VT_UI1, "Byte"}, + {VarEnum.VT_I2, "Integer"}, + {VarEnum.VT_UI2, "Variant"}, // no unsigned integer type in VBA + {VarEnum.VT_I4, "Long"}, + {VarEnum.VT_UI4, "Variant"}, // no unsigned long integer type in VBA + {VarEnum.VT_I8, "Variant"}, // LongLong on 64-bit VBA + {VarEnum.VT_UI8, "Variant"}, // no unsigned LongLong integer type in VBA + {VarEnum.VT_INT, "Long"}, // same as I4 + {VarEnum.VT_UINT, "Variant"}, // same as UI4 + {VarEnum.VT_DATE, "Date"}, + {VarEnum.VT_CY, "Currency"}, + {VarEnum.VT_DECIMAL, "Currency"}, // best match? + {VarEnum.VT_EMPTY, "Empty"}, + {VarEnum.VT_R4, "Single"}, + {VarEnum.VT_R8, "Double"}, + }; + + + [StructLayout(LayoutKind.Sequential)] + private struct Variant + { + public readonly ushort vt; + private readonly ushort wReserved1; + private readonly ushort wReserved2; + private readonly ushort wReserved3; + private readonly int data01; + private readonly int data02; + } + + public VarEnum VariantType { get; private set; } + public object Value { get; private set; } + + public ComVariant(IntPtr variant) + { + Value = Marshal.GetObjectForNativeVariant(variant); + var members = (Variant)Marshal.PtrToStructure(variant, typeof(Variant)); + VariantType = (VarEnum)members.vt; + if (Value == null && VariantType == VarEnum.VT_BSTR) + { + Value = string.Empty; + } + } + } +} diff --git a/Rubberduck.Parsing/Rubberduck.Parsing.csproj b/Rubberduck.Parsing/Rubberduck.Parsing.csproj index 542f681795..e5e7873f4a 100644 --- a/Rubberduck.Parsing/Rubberduck.Parsing.csproj +++ b/Rubberduck.Parsing/Rubberduck.Parsing.csproj @@ -110,6 +110,14 @@ + + + + + + + + @@ -130,10 +138,13 @@ + + + + - - + diff --git a/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs b/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs index 9d346457dd..bdba10fbc5 100644 --- a/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs @@ -1,4 +1,5 @@ using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; using System.Collections.Generic; @@ -47,6 +48,45 @@ public ClassModuleDeclaration( _subtypes = new HashSet(); } + // skip IDispatch.. just about everything implements it and RD doesn't need to care about it; don't care about IUnknown either + private static readonly HashSet IgnoredInterfaces = new HashSet(new[] { "IDispatch", "IUnknown" }); + + public ClassModuleDeclaration(ComCoClass coClass, Declaration parent, QualifiedModuleName module, + Attributes attributes) + : base( + new QualifiedMemberName(module, coClass.Name), + parent, + parent, + coClass.Name, + null, + false, + coClass.EventInterfaces.Any(), + Accessibility.Public, + DeclarationType.ClassModule, + null, + Selection.Home, + false, + null, + true, + new List(), + attributes) + { + _supertypeNames = + coClass.ImplementedInterfaces.Where(i => !i.IsRestricted && !IgnoredInterfaces.Contains(i.Name)) + .Select(i => i.Name) + .ToList(); + } + + public ClassModuleDeclaration(ComInterface intrface, Declaration parent, QualifiedModuleName module, + Attributes attributes) + : this( + new QualifiedMemberName(module, intrface.Name), + parent, + intrface.Name, + true, + new List(), + attributes) { } + public static IEnumerable GetSupertypes(Declaration type) { if (type.DeclarationType != DeclarationType.ClassModule) diff --git a/Rubberduck.Parsing/Symbols/ComInformation.cs b/Rubberduck.Parsing/Symbols/ComInformation.cs deleted file mode 100644 index d81bf17ea7..0000000000 --- a/Rubberduck.Parsing/Symbols/ComInformation.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Runtime.InteropServices.ComTypes; -using Rubberduck.VBEditor; - -namespace Rubberduck.Parsing.Symbols -{ - public class ComInformation - { - public ComInformation(TYPEATTR typeAttributes, IMPLTYPEFLAGS implTypeFlags, ITypeInfo typeInfo, string typeName, QualifiedModuleName typeModuleName, Declaration moduleDeclaration, DeclarationType typeDeclarationType) - { - TypeAttributes = typeAttributes; - ImplTypeFlags = implTypeFlags; - TypeInfo = typeInfo; - TypeName = typeName; - TypeQualifiedModuleName = typeModuleName; - ModuleDeclaration = moduleDeclaration; - TypeDeclarationType = typeDeclarationType; - } - - public TYPEATTR TypeAttributes { get; internal set; } - public IMPLTYPEFLAGS ImplTypeFlags { get; internal set; } - public ITypeInfo TypeInfo { get; internal set; } - - public string TypeName { get; internal set; } - public QualifiedModuleName TypeQualifiedModuleName { get; internal set; } - public Declaration ModuleDeclaration { get; internal set; } - public DeclarationType TypeDeclarationType { get; internal set; } - - public override string ToString() - { - return ModuleDeclaration.IdentifierName; - } - } -} \ No newline at end of file diff --git a/Rubberduck.Parsing/Symbols/ComParameter.cs b/Rubberduck.Parsing/Symbols/ComParameter.cs deleted file mode 100644 index 4d756ea6ca..0000000000 --- a/Rubberduck.Parsing/Symbols/ComParameter.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Rubberduck.Parsing.Symbols -{ - public class ComParameter - { - public bool IsArray { get; set; } - public bool IsByRef { get; set;} - public string Name { get; set;} - - public ComParameter(string name, bool byRef) - { - Name = name; - IsByRef = byRef; - } - } -} diff --git a/Rubberduck.Parsing/Symbols/Declaration.cs b/Rubberduck.Parsing/Symbols/Declaration.cs index 0291c06474..80748c55da 100644 --- a/Rubberduck.Parsing/Symbols/Declaration.cs +++ b/Rubberduck.Parsing/Symbols/Declaration.cs @@ -1,5 +1,6 @@ using Antlr4.Runtime; using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; @@ -163,7 +164,58 @@ public Declaration( _typeHint = typeHint; } - private string FolderFromAnnotations() + public Declaration(ComEnumeration enumeration, Declaration parent, QualifiedModuleName module) : this( + new QualifiedMemberName(module, enumeration.Name), + parent, + parent, + enumeration.Name, + null, + false, + false, + Accessibility.Global, + DeclarationType.Enumeration, + null, + Selection.Home, + false, + null, + true, + null, + new Attributes()) { } + + public Declaration(ComEnumerationMember member, Declaration parent, QualifiedModuleName module) : this( + new QualifiedMemberName(module, member.Name), + parent, + parent, + member.Name, + null, + false, + false, + Accessibility.Global, + DeclarationType.EnumerationMember, + null, + Selection.Home, + false, + null, + true) { } + + public Declaration(ComField field, Declaration parent, QualifiedModuleName module) + : this( + new QualifiedMemberName(module, field.Name), + parent, + parent, + field.Name, + null, + false, + false, + Accessibility.Global, + field.IsConstant ? DeclarationType.Constant : DeclarationType.UserDefinedType, //TODO: This is wrong. + null, + Selection.Home, + false, + null, + true) { } + + private string FolderFromAnnotations() { var @namespace = Annotations.FirstOrDefault(annotation => annotation.AnnotationType == AnnotationType.Folder); string result; diff --git a/Rubberduck.Parsing/Symbols/FunctionDeclaration.cs b/Rubberduck.Parsing/Symbols/FunctionDeclaration.cs index bebe9ba546..794c77fd4f 100644 --- a/Rubberduck.Parsing/Symbols/FunctionDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/FunctionDeclaration.cs @@ -1,5 +1,6 @@ using Antlr4.Runtime; using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; @@ -47,6 +48,28 @@ public FunctionDeclaration( _parameters = new List(); } + public FunctionDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, + Attributes attributes) : this( + new QualifiedMemberName(module, member.Name), + parent, + parent, + member.ReturnType.TypeName, + null, + null, + Accessibility.Global, + null, + Selection.Home, + member.ReturnType.IsArray, + true, + null, + attributes) + { + _parameters = + member.Parameters.Select(decl => new ParameterDeclaration(decl, this, module)) + .Cast() + .ToList(); + } + public IEnumerable Parameters { get diff --git a/Rubberduck.Parsing/Symbols/ParameterDeclaration.cs b/Rubberduck.Parsing/Symbols/ParameterDeclaration.cs index bf030dc300..153bc8c739 100644 --- a/Rubberduck.Parsing/Symbols/ParameterDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ParameterDeclaration.cs @@ -1,4 +1,5 @@ using Antlr4.Runtime; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.Grammar; using Rubberduck.VBEditor; @@ -76,6 +77,21 @@ public ParameterDeclaration(QualifiedMemberName qualifiedName, IsParamArray = isParamArray; } + public ParameterDeclaration(ComParameter parameter, Declaration parent, QualifiedModuleName module) + : this( + new QualifiedMemberName(module, parameter.Name), + parent, + parameter.TypeName, + null, + null, + parameter.IsOptional, + parameter.IsByRef, + parameter.IsArray) + { + IsParamArray = parameter.IsParamArray; + } + + public bool IsOptional { get { return _isOptional; } } public bool IsByRef { get { return _isByRef; } } public bool IsParamArray { get; set; } diff --git a/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs b/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs index 4bf4b41873..167e8529e4 100644 --- a/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs @@ -1,4 +1,5 @@ using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; using System.Collections.Generic; @@ -30,10 +31,41 @@ public ProceduralModuleDeclaration( null, isBuiltIn, annotations, - attributes) + attributes) { } + + public ProceduralModuleDeclaration(ComModule statics, Declaration parent, QualifiedModuleName module, + Attributes attributes) + : this( + new QualifiedMemberName(module, statics.Name), + parent, + statics.Name, + true, + new List(), + attributes) { + IsPrivateModule = statics.IsRestricted; } + //This is the pseudo-module ctor for COM enumerations. + public ProceduralModuleDeclaration(ComEnumeration enumeration, Declaration parent, QualifiedModuleName module) + : this( + new QualifiedMemberName(module, string.Format("_{0}", enumeration.Name)), + parent, + string.Format("_{0}", enumeration.Name), + true, + new List(), + new Attributes()) { } + + //This is the pseudo-module ctor for anything else from COM with fields instead of members. + public ProceduralModuleDeclaration(string pseudo, Declaration parent, QualifiedModuleName module) + : this( + new QualifiedMemberName(module, pseudo), + parent, + pseudo, + true, + new List(), + new Attributes()) { } + public bool IsPrivateModule { get; internal set; } } } diff --git a/Rubberduck.Parsing/Symbols/ProjectDeclaration.cs b/Rubberduck.Parsing/Symbols/ProjectDeclaration.cs index 3af1c49f2e..fef17fd587 100644 --- a/Rubberduck.Parsing/Symbols/ProjectDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ProjectDeclaration.cs @@ -1,4 +1,5 @@ -using Rubberduck.VBEditor; +using Rubberduck.Parsing.ComReflection; +using Rubberduck.VBEditor; using System.Collections.Generic; using System.Linq; @@ -31,6 +32,13 @@ public ProjectDeclaration( _projectReferences = new List(); } + public ProjectDeclaration(ComProject project, QualifiedModuleName module) + : this(new QualifiedMemberName(module, project.Name), project.Name, true) + { + MajorVersion = project.MajorVersion; + MinorVersion = project.MinorVersion; + } + public long MajorVersion { get; set; } public long MinorVersion { get; set; } diff --git a/Rubberduck.Parsing/Symbols/PropertyGetDeclaration.cs b/Rubberduck.Parsing/Symbols/PropertyGetDeclaration.cs index 70b4ed0361..7d9f6e1c44 100644 --- a/Rubberduck.Parsing/Symbols/PropertyGetDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/PropertyGetDeclaration.cs @@ -1,5 +1,6 @@ using Antlr4.Runtime; using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.Grammar; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; @@ -47,6 +48,29 @@ public PropertyGetDeclaration( _parameters = new List(); } + public PropertyGetDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, + Attributes attributes) + : this( + new QualifiedMemberName(module, member.Name), + parent, + parent, + member.ReturnType.TypeName, + null, + null, + Accessibility.Global, + null, + Selection.Home, + member.ReturnType.IsArray, + true, + null, + attributes) + { + _parameters = + member.Parameters.Select(decl => new ParameterDeclaration(decl, this, module)) + .Cast() + .ToList(); + } + public IEnumerable Parameters { get diff --git a/Rubberduck.Parsing/Symbols/PropertyLetDeclaration.cs b/Rubberduck.Parsing/Symbols/PropertyLetDeclaration.cs index 28ab413fa0..c7ca4eac6a 100644 --- a/Rubberduck.Parsing/Symbols/PropertyLetDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/PropertyLetDeclaration.cs @@ -1,5 +1,6 @@ using Antlr4.Runtime; using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; using System.Collections.Generic; @@ -43,6 +44,26 @@ public PropertyLetDeclaration( _parameters = new List(); } + public PropertyLetDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, + Attributes attributes) + : this( + new QualifiedMemberName(module, member.Name), + parent, + parent, + string.Empty, //TODO: Need to get the types for these. + Accessibility.Global, + null, + Selection.Home, + true, + null, + attributes) + { + _parameters = + member.Parameters.Select(decl => new ParameterDeclaration(decl, this, module)) + .Cast() + .ToList(); + } + public IEnumerable Parameters { get diff --git a/Rubberduck.Parsing/Symbols/PropertySetDeclaration.cs b/Rubberduck.Parsing/Symbols/PropertySetDeclaration.cs index e1fa51a168..98782ea1f1 100644 --- a/Rubberduck.Parsing/Symbols/PropertySetDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/PropertySetDeclaration.cs @@ -1,5 +1,6 @@ using Antlr4.Runtime; using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; using System.Collections.Generic; @@ -43,6 +44,25 @@ public PropertySetDeclaration( _parameters = new List(); } + public PropertySetDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, + Attributes attributes) : this( + new QualifiedMemberName(module, member.Name), + parent, + parent, + string.Empty, //TODO: Need to get the types for these. + Accessibility.Global, + null, + Selection.Home, + true, + null, + attributes) + { + _parameters = + member.Parameters.Select(decl => new ParameterDeclaration(decl, this, module)) + .Cast() + .ToList(); + } + public IEnumerable Parameters { get diff --git a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs index 97c27ed71d..ec14360b6e 100644 --- a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs +++ b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs @@ -1,25 +1,13 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using NLog; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; -using CALLCONV = System.Runtime.InteropServices.ComTypes.CALLCONV; -using FUNCFLAGS = System.Runtime.InteropServices.ComTypes.FUNCFLAGS; -using TYPEDESC = System.Runtime.InteropServices.ComTypes.TYPEDESC; -using TYPEKIND = System.Runtime.InteropServices.ComTypes.TYPEKIND; -using INVOKEKIND = System.Runtime.InteropServices.ComTypes.INVOKEKIND; -using PARAMFLAG = System.Runtime.InteropServices.ComTypes.PARAMFLAG; -using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; -using FUNCDESC = System.Runtime.InteropServices.ComTypes.FUNCDESC; -using ELEMDESC = System.Runtime.InteropServices.ComTypes.ELEMDESC; -using TYPEFLAGS = System.Runtime.InteropServices.ComTypes.TYPEFLAGS; -using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC; -using Rubberduck.Parsing.Annotations; -using IMPLTYPEFLAGS = System.Runtime.InteropServices.ComTypes.IMPLTYPEFLAGS; -using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR; using System.Linq; using Rubberduck.VBEditor.SafeComWrappers.Abstract; @@ -30,6 +18,8 @@ public class ReferencedDeclarationsCollector private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly RubberduckParserState _state; + // ReSharper disable InconsistentNaming + // ReSharper disable UnusedMember.Local /// /// Controls how a type library is registered. /// @@ -38,6 +28,8 @@ private enum REGKIND /// /// Use default register behavior. /// + + REGKIND_DEFAULT = 0, /// /// Register this type library. @@ -48,9 +40,11 @@ private enum REGKIND /// REGKIND_NONE = 2 } + // ReSharper restore UnusedMember.Local [DllImport("oleaut32.dll", CharSet = CharSet.Unicode)] private static extern int LoadTypeLibEx(string strTypeLibName, REGKIND regKind, out ITypeLib TypeLib); + // ReSharper restore InconsistentNaming public ReferencedDeclarationsCollector(RubberduckParserState state) { @@ -59,106 +53,7 @@ public ReferencedDeclarationsCollector(RubberduckParserState state) private static readonly HashSet IgnoredInterfaceMembers = new HashSet { "QueryInterface", "AddRef", "Release", "GetTypeInfoCount", "GetTypeInfo", "GetIDsOfNames", "Invoke" }; - private static readonly IDictionary TypeNames = new Dictionary - { - {VarEnum.VT_DISPATCH, "Object"}, - {VarEnum.VT_VOID, string.Empty}, - {VarEnum.VT_VARIANT, "Variant"}, - {VarEnum.VT_BLOB_OBJECT, "Object"}, - {VarEnum.VT_STORED_OBJECT, "Object"}, - {VarEnum.VT_STREAMED_OBJECT, "Object"}, - {VarEnum.VT_BOOL, "Boolean"}, - {VarEnum.VT_BSTR, "String"}, - {VarEnum.VT_LPSTR, "String"}, - {VarEnum.VT_LPWSTR, "String"}, - {VarEnum.VT_I1, "Variant"}, // no signed byte type in VBA - {VarEnum.VT_UI1, "Byte"}, - {VarEnum.VT_I2, "Integer"}, - {VarEnum.VT_UI2, "Variant"}, // no unsigned integer type in VBA - {VarEnum.VT_I4, "Long"}, - {VarEnum.VT_UI4, "Variant"}, // no unsigned long integer type in VBA - {VarEnum.VT_I8, "Variant"}, // LongLong on 64-bit VBA - {VarEnum.VT_UI8, "Variant"}, // no unsigned LongLong integer type in VBA - {VarEnum.VT_INT, "Long"}, // same as I4 - {VarEnum.VT_UINT, "Variant"}, // same as UI4 - {VarEnum.VT_DATE, "Date"}, - {VarEnum.VT_CY, "Currency"}, - {VarEnum.VT_DECIMAL, "Currency"}, // best match? - {VarEnum.VT_EMPTY, "Empty"}, - {VarEnum.VT_R4, "Single"}, - {VarEnum.VT_R8, "Double"}, - }; - - private readonly Dictionary _comInformation = new Dictionary(); - - private ComParameter GetParameterInfo(TYPEDESC desc, ITypeInfo info) - { - var vt = (VarEnum)desc.vt; - TYPEDESC tdesc; - - switch (vt) - { - case VarEnum.VT_PTR: - tdesc = (TYPEDESC)Marshal.PtrToStructure(desc.lpValue, typeof(TYPEDESC)); - var pointer = GetParameterInfo(tdesc, info); - pointer.IsByRef = true; - return pointer; - case VarEnum.VT_USERDEFINED: - int href; - unchecked - { - href = (int)(desc.lpValue.ToInt64() & 0xFFFFFFFF); - } - try - { - ITypeInfo refTypeInfo; - info.GetRefTypeInfo(href, out refTypeInfo); - return new ComParameter(GetTypeName(refTypeInfo), false); - } - catch (Exception) - { - return new ComParameter("Object", false); - } - case VarEnum.VT_SAFEARRAY: - case VarEnum.VT_CARRAY: - case VarEnum.VT_ARRAY: - tdesc = (TYPEDESC)Marshal.PtrToStructure(desc.lpValue, typeof(TYPEDESC)); - var array = GetParameterInfo(tdesc, info); - array.IsArray = true; - array.Name += "()"; - return array; - default: - string result; - if (TypeNames.TryGetValue(vt, out result)) - { - return new ComParameter(result, false); - } - break; - } - return new ComParameter("Object", false); - } - - private string GetTypeName(ITypeInfo info) - { - string typeName; - string docString; // todo: put the docString to good use? - int helpContext; - string helpFile; - info.GetDocumentation(-1, out typeName, out docString, out helpContext, out helpFile); - - return typeName.Equals("LONG_PTR") ? "LongPtr" : typeName; //Quickfix for http://chat.stackexchange.com/transcript/message/30119269#30119269 - } - - private void LoadVersionFromTypeLib(ITypeLib tlb, ProjectDeclaration project) - { - var attribPtr = IntPtr.Zero; - tlb.GetLibAttr(out attribPtr); - var typeAttr = (TYPELIBATTR)Marshal.PtrToStructure(attribPtr, typeof(TYPELIBATTR)); - project.MajorVersion = typeAttr.wMajorVerNum; - project.MinorVersion = typeAttr.wMinorVerNum; - } - - private bool SerializedVersionExists(ProjectDeclaration project) + private bool SerializedVersionExists(string name, int major, int minor) { var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations"); if (!Directory.Exists(path)) @@ -166,7 +61,7 @@ private bool SerializedVersionExists(ProjectDeclaration project) return false; } //TODO: This is naively based on file name for now - this should attempt to deserialize any SerializableProject.Nodes in the directory and test for equity. - var testFile = Path.Combine(path, string.Format("{0}.{1}.{2}", project.ProjectName, project.MajorVersion, project.MinorVersion) + ".xml"); + var testFile = Path.Combine(path, string.Format("{0}.{1}.{2}", name, major, minor) + ".xml"); if (File.Exists(testFile)) { return true; @@ -174,10 +69,10 @@ private bool SerializedVersionExists(ProjectDeclaration project) return false; } - private List LoadSerializedBuiltInReferences(ProjectDeclaration project) + private List LoadSerializedBuiltInReferences(string name, int major, int minor) { var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations"); - var file = Path.Combine(path, string.Format("{0}.{1}.{2}", project.ProjectName, project.MajorVersion, project.MinorVersion) + ".xml"); + var file = Path.Combine(path, string.Format("{0}.{1}.{2}", name, major, minor) + ".xml"); var reader = new XmlPersistableDeclarations(); var deserialized = reader.Load(file); return deserialized.Unwrap(); @@ -186,8 +81,15 @@ private List LoadSerializedBuiltInReferences(ProjectDeclaration pro public List GetDeclarationsForReference(IReference reference) { var output = new List(); - var projectName = reference.Name; var path = reference.FullPath; + + if (SerializedVersionExists(reference.Name, reference.Major, reference.Minor)) + { + Logger.Trace(string.Format("Deserializing reference '{0}'.", reference.Name)); + return LoadSerializedBuiltInReferences(reference.Name, reference.Major, reference.Minor); + } + Logger.Trace(string.Format("COM reflecting reference '{0}'.", reference.Name)); + ITypeLib typeLibrary; // Failure to load might mean that it's a "normal" VBProject that will get parsed by us anyway. LoadTypeLibEx(path, REGKIND.REGKIND_NONE, out typeLibrary); @@ -195,378 +97,143 @@ public List GetDeclarationsForReference(IReference reference) { return output; } - var projectQualifiedModuleName = new QualifiedModuleName(projectName, path, projectName); - var projectQualifiedMemberName = new QualifiedMemberName(projectQualifiedModuleName, projectName); - var projectDeclaration = new ProjectDeclaration(projectQualifiedMemberName, projectName, isBuiltIn: true); - LoadVersionFromTypeLib(typeLibrary, projectDeclaration); - if (SerializedVersionExists(projectDeclaration)) - { - Logger.Trace(string.Format("Deserializing reference '{0}'.", reference.Name)); - return LoadSerializedBuiltInReferences(projectDeclaration); - } - Logger.Trace(string.Format("COM reflecting reference '{0}'.", reference.Name)); - output.Add(projectDeclaration); - - var typeCount = typeLibrary.GetTypeInfoCount(); - for (var i = 0; i < typeCount; i++) - { - ITypeInfo info; - try - { - typeLibrary.GetTypeInfo(i, out info); - } - catch (NullReferenceException) - { - return output; - } - if (info == null) - { - continue; - } - - var typeName = GetTypeName(info); - var typeDeclarationType = GetDeclarationType(typeLibrary, i); - - QualifiedModuleName typeQualifiedModuleName; - QualifiedMemberName typeQualifiedMemberName; - if (typeDeclarationType == DeclarationType.Enumeration || typeDeclarationType == DeclarationType.UserDefinedType) - { - typeQualifiedModuleName = projectQualifiedModuleName; - typeQualifiedMemberName = new QualifiedMemberName(projectQualifiedModuleName, typeName); - } - else - { - typeQualifiedModuleName = new QualifiedModuleName(projectName, path, typeName); - typeQualifiedMemberName = new QualifiedMemberName(typeQualifiedModuleName, typeName); - } + var type = new ComProject(typeLibrary) { Path = path }; - IntPtr typeAttributesPointer; - info.GetTypeAttr(out typeAttributesPointer); - var typeAttributes = (TYPEATTR)Marshal.PtrToStructure(typeAttributesPointer, typeof(TYPEATTR)); + var projectName = new QualifiedModuleName(type.Name, path, type.Name); + var project = new ProjectDeclaration(type, projectName); + output.Add(project); + foreach (var module in type.Members) + { + var moduleName = new QualifiedModuleName(reference.Name, path, module.Name); + var attributes = new Attributes(); - - if (typeAttributes.wTypeFlags.HasFlag(TYPEFLAGS.TYPEFLAG_FPREDECLID)) + if (module.IsPreDeclared) { attributes.AddPredeclaredIdTypeAttribute(); } - - if (typeAttributes.wTypeFlags.HasFlag(TYPEFLAGS.TYPEFLAG_FAPPOBJECT)) + if (module.IsAppObject) { attributes.AddGlobalClassAttribute(); } - - Declaration moduleDeclaration; - switch (typeDeclarationType) + var declaration = CreateModuleDeclaration(module, moduleName, project, attributes); + output.Add(declaration); + var membered = module as IComTypeWithMembers; + if (membered != null) { - case DeclarationType.ProceduralModule: - moduleDeclaration = new ProceduralModuleDeclaration(typeQualifiedMemberName, projectDeclaration, typeName, true, new List(), attributes); - break; - case DeclarationType.ClassModule: - var module = new ClassModuleDeclaration(typeQualifiedMemberName, projectDeclaration, typeName, true, new List(), attributes); - var implements = GetImplementedInterfaceNames(typeAttributes, info, module); - foreach (var supertypeName in implements) + foreach (var item in membered.Members.Where(m => !m.IsRestricted && !IgnoredInterfaceMembers.Contains(m.Name))) + { + var memberDeclaration = CreateMemberDeclaration(item, moduleName, declaration); + output.Add(memberDeclaration); + var hasParams = memberDeclaration as IDeclarationWithParameter; + if (hasParams != null) { - module.AddSupertype(supertypeName); + output.AddRange(hasParams.Parameters); } - moduleDeclaration = module; - break; - default: - var pseudoModuleName = string.Format("_{0}", typeName); - var pseudoParentModule = new ProceduralModuleDeclaration( - new QualifiedMemberName(projectQualifiedModuleName, pseudoModuleName), - projectDeclaration, - pseudoModuleName, - true, - new List(), - new Attributes()); - - // UDTs and ENUMs don't seem to have a module parent that's why we add a "fake" module - // so that the rest of the application can treat it normally. - moduleDeclaration = new Declaration( - typeQualifiedMemberName, - pseudoParentModule, - pseudoParentModule, - typeName, - null, - false, - false, - Accessibility.Global, - typeDeclarationType, - null, - Selection.Home, - false, - null, - true, - null, - attributes); - - output.Add(pseudoParentModule); - break; + var coClass = memberDeclaration as ClassModuleDeclaration; + if (coClass != null && item.IsDefault) + { + coClass.DefaultMember = memberDeclaration; + } + } } - - ComInformation comInfo; - if (typeAttributes.guid == Guid.Empty) + var enumeration = module as ComEnumeration; + if (enumeration != null) { - comInfo = new ComInformation(typeAttributes, 0, info, typeName, typeQualifiedModuleName, moduleDeclaration, typeDeclarationType); - LoadDeclarationsInModule(output, comInfo); + var enumDeclaration = new Declaration(enumeration, declaration, projectName); + var members = + enumeration.Members.Select( + e => + new Declaration(e, enumDeclaration, + new QualifiedModuleName(reference.Name, path, enumeration.Name))); + output.Add(enumDeclaration); + output.AddRange(members); } - else + var fields = module as ComModule; + if (fields == null || !fields.Fields.Any()) { - if (_comInformation.TryGetValue(typeAttributes.guid, out comInfo)) - { - comInfo.TypeQualifiedModuleName = typeQualifiedModuleName; - comInfo.ModuleDeclaration = moduleDeclaration; - comInfo.TypeDeclarationType = typeDeclarationType; - } - else - { - comInfo = new ComInformation(typeAttributes, 0, info, typeName, typeQualifiedModuleName, moduleDeclaration, typeDeclarationType); - _comInformation.Add(typeAttributes.guid, comInfo); - } + continue; } - - info.ReleaseTypeAttr(typeAttributesPointer); - - output.Add(moduleDeclaration); + var declarations = fields.Fields.Select(f => new Declaration(f, declaration, projectName)); + output.AddRange(declarations); } - - foreach (var member in _comInformation.Values) - { - LoadDeclarationsInModule(output, member); - } - return output; } - private void LoadDeclarationsInModule(List output, ComInformation member) + private Declaration CreateModuleDeclaration(IComType module, QualifiedModuleName project, Declaration parent, Attributes attributes) { - if (member.TypeAttributes.typekind == TYPEKIND.TKIND_COCLASS) + var enumeration = module as ComEnumeration; + if (enumeration != null) { - GetCoClassInformation(member); + return new ProceduralModuleDeclaration(enumeration, parent, project); } - - for (var memberIndex = 0; memberIndex < member.TypeAttributes.cFuncs; memberIndex++) + var coClass = module as ComCoClass; + var intrface = module as ComInterface; + if (coClass != null || intrface != null) { - string[] memberNames; - - IntPtr memberDescriptorPointer; - member.TypeInfo.GetFuncDesc(memberIndex, out memberDescriptorPointer); - var memberDescriptor = (FUNCDESC)Marshal.PtrToStructure(memberDescriptorPointer, typeof(FUNCDESC)); - - var memberDeclaration = CreateMemberDeclaration(memberDescriptor, member.TypeAttributes.typekind, member.TypeInfo, member.ImplTypeFlags, - member.TypeQualifiedModuleName, member.ModuleDeclaration, out memberNames); - if (memberDeclaration == null) - { - member.TypeInfo.ReleaseFuncDesc(memberDescriptorPointer); - continue; - } - - if (member.ModuleDeclaration.DeclarationType == DeclarationType.ClassModule && - memberDeclaration is ICanBeDefaultMember && - ((ICanBeDefaultMember)memberDeclaration).IsDefaultMember) + var output = coClass != null ? + new ClassModuleDeclaration(coClass, parent, project, attributes) : + new ClassModuleDeclaration(intrface, parent, project, attributes); + if (coClass != null) { - ((ClassModuleDeclaration)member.ModuleDeclaration).DefaultMember = memberDeclaration; + var members = + coClass.Members.Where(m => !m.IsRestricted && !IgnoredInterfaceMembers.Contains(m.Name)) + .Select(m => m.Name); + _state.CoClasses.TryAdd(members.ToList(), output); } - output.Add(memberDeclaration); - - var parameterCount = memberDescriptor.cParams - (memberDescriptor.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYGET) ? 0 : 1); - var parameters = new List(); - for (var paramIndex = 0; paramIndex < parameterCount; paramIndex++) - { - var parameter = CreateParameterDeclaration(memberNames, paramIndex, memberDescriptor, - member.TypeQualifiedModuleName, memberDeclaration, member.TypeInfo); - var declaration = memberDeclaration as IDeclarationWithParameter; - if (declaration != null) - { - parameters.Add(parameter); - declaration.AddParameter(parameter); - } - output.Add(parameter); - } - member.TypeInfo.ReleaseFuncDesc(memberDescriptorPointer); - if (parameters.Any() && memberDescriptor.cParamsOpt == -1) - { - parameters.Last().IsParamArray = true; - } - } - - for (var fieldIndex = 0; fieldIndex < member.TypeAttributes.cVars; fieldIndex++) - { - var declaration = CreateFieldDeclaration(member.TypeInfo, fieldIndex, member.TypeDeclarationType, member.TypeQualifiedModuleName, member.ModuleDeclaration); - output.Add(declaration); + return output; } - } - - private void GetCoClassInformation(ComInformation member) - { - var componentMemberNames = new List(); - for (var implIndex = 0; implIndex < member.TypeAttributes.cImplTypes; implIndex++) + var normal = module as ComModule; + if (normal != null && normal.Fields.Any()) { - int href; - member.TypeInfo.GetRefTypeOfImplType(0, out href); - - ITypeInfo implTypeInfo; - member.TypeInfo.GetRefTypeInfo(href, out implTypeInfo); - - IntPtr typeAttributesPointer; - implTypeInfo.GetTypeAttr(out typeAttributesPointer); - - var typeAttributes = (TYPEATTR)Marshal.PtrToStructure(typeAttributesPointer, typeof(TYPEATTR)); - - for (var i = 0; i < typeAttributes.cFuncs; i++) - { - var memberNames = new string[255]; - - IntPtr memberDescriptorPointer; - implTypeInfo.GetFuncDesc(i, out memberDescriptorPointer); - var memberDescriptor = (FUNCDESC)Marshal.PtrToStructure(memberDescriptorPointer, typeof(FUNCDESC)); - - if (!(memberDescriptor.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYGET) || - memberDescriptor.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYPUT) || - memberDescriptor.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYPUTREF))) - { - continue; - } - - int namesArrayLength; - implTypeInfo.GetNames(memberDescriptor.memid, memberNames, 255, out namesArrayLength); - - if (!IgnoredInterfaceMembers.Contains(memberNames[0]) && - !componentMemberNames.Contains(memberNames[0])) - { - componentMemberNames.Add(memberNames[0]); - } - } - - member.TypeInfo.ReleaseTypeAttr(typeAttributesPointer); + //These are going to be UDTs or Consts. Apparently COM modules can declare *either* fields or members. + return new ProceduralModuleDeclaration(string.Format("_{0}", normal.Name), parent, project); } - - _state.CoClasses.TryAdd(componentMemberNames, member.ModuleDeclaration); + return new ProceduralModuleDeclaration(normal, parent, project, attributes); } - private Declaration CreateMemberDeclaration(FUNCDESC memberDescriptor, TYPEKIND typeKind, ITypeInfo info, IMPLTYPEFLAGS parentImplFlags, - QualifiedModuleName typeQualifiedModuleName, Declaration moduleDeclaration, out string[] memberNames) + private Declaration CreateMemberDeclaration(ComMember member, QualifiedModuleName module, Declaration parent) { - if (memberDescriptor.callconv != CALLCONV.CC_STDCALL) - { - memberNames = new string[] { }; - return null; - } - - memberNames = new string[255]; - int namesArrayLength; - info.GetNames(memberDescriptor.memid, memberNames, 255, out namesArrayLength); - - var memberName = memberNames[0]; - var funcValueType = (VarEnum)memberDescriptor.elemdescFunc.tdesc.vt; - var memberDeclarationType = GetDeclarationType(memberName, memberDescriptor, funcValueType, typeKind, parentImplFlags); - - if (((FUNCFLAGS)memberDescriptor.wFuncFlags).HasFlag(FUNCFLAGS.FUNCFLAG_FRESTRICTED) && - IgnoredInterfaceMembers.Contains(memberName)) // Ignore IDispatch and IUnknown members - quick-and-dirty for beta - { - return null; - } - - var asTypeName = new ComParameter(string.Empty, false); - if (memberDeclarationType != DeclarationType.Procedure) - { - asTypeName = GetParameterInfo(memberDescriptor.elemdescFunc.tdesc, info); - } var attributes = new Attributes(); - if (memberName == "_NewEnum" && ((FUNCFLAGS)memberDescriptor.wFuncFlags).HasFlag(FUNCFLAGS.FUNCFLAG_FNONBROWSABLE)) + if (member.IsEnumerator) { - attributes.AddEnumeratorMemberAttribute(memberName); + attributes.AddEnumeratorMemberAttribute(member.Name); } - else if (memberDescriptor.memid == 0) + else if (member.IsDefault) { - attributes.AddDefaultMemberAttribute(memberName); + attributes.AddDefaultMemberAttribute(member.Name); } - else if (((FUNCFLAGS)memberDescriptor.wFuncFlags).HasFlag(FUNCFLAGS.FUNCFLAG_FHIDDEN)) + else if (member.IsHidden) { - attributes.AddHiddenMemberAttribute(memberName); + attributes.AddHiddenMemberAttribute(member.Name); } - switch (memberDeclarationType) + switch (member.Type) { + case DeclarationType.Event: case DeclarationType.Procedure: - return new SubroutineDeclaration( - new QualifiedMemberName(typeQualifiedModuleName, memberName), - moduleDeclaration, - moduleDeclaration, - asTypeName.Name, - Accessibility.Global, - null, - Selection.Home, - true, - null, - attributes); + return new SubroutineDeclaration(member, parent, module, attributes); case DeclarationType.Function: - return new FunctionDeclaration( - new QualifiedMemberName(typeQualifiedModuleName, memberName), - moduleDeclaration, - moduleDeclaration, - asTypeName.Name, - null, - null, - Accessibility.Global, - null, - Selection.Home, - asTypeName.IsArray, - true, - null, - attributes); + return new FunctionDeclaration(member, parent, module, attributes); case DeclarationType.PropertyGet: - return new PropertyGetDeclaration( - new QualifiedMemberName(typeQualifiedModuleName, memberName), - moduleDeclaration, - moduleDeclaration, - asTypeName.Name, - null, - null, - Accessibility.Global, - null, - Selection.Home, - asTypeName.IsArray, - true, - null, - attributes); + return new PropertyGetDeclaration(member, parent, module, attributes); case DeclarationType.PropertySet: - return new PropertySetDeclaration( - new QualifiedMemberName(typeQualifiedModuleName, memberName), - moduleDeclaration, - moduleDeclaration, - asTypeName.Name, - Accessibility.Global, - null, - Selection.Home, - true, - null, - attributes); + return new PropertySetDeclaration(member, parent, module, attributes); case DeclarationType.PropertyLet: - return new PropertyLetDeclaration( - new QualifiedMemberName(typeQualifiedModuleName, memberName), - moduleDeclaration, - moduleDeclaration, - asTypeName.Name, - Accessibility.Global, - null, - Selection.Home, - true, - null, - attributes); + return new PropertyLetDeclaration(member, parent, module, attributes); default: + Debug.Assert(false); return new Declaration( - new QualifiedMemberName(typeQualifiedModuleName, memberName), - moduleDeclaration, - moduleDeclaration, - asTypeName.Name, + new QualifiedMemberName(module, member.Name), + parent, + parent, + string.Empty, // asTypeName.Name, null, false, false, Accessibility.Global, - memberDeclarationType, + member.Type, null, Selection.Home, false, @@ -576,165 +243,5 @@ private Declaration CreateMemberDeclaration(FUNCDESC memberDescriptor, TYPEKIND attributes); } } - - private Declaration CreateFieldDeclaration(ITypeInfo info, int fieldIndex, DeclarationType typeDeclarationType, - QualifiedModuleName typeQualifiedModuleName, Declaration moduleDeclaration) - { - IntPtr ppVarDesc; - info.GetVarDesc(fieldIndex, out ppVarDesc); - - var varDesc = (VARDESC)Marshal.PtrToStructure(ppVarDesc, typeof(VARDESC)); - - var names = new string[255]; - int namesArrayLength; - info.GetNames(varDesc.memid, names, 255, out namesArrayLength); - - var fieldName = names[0]; - var memberType = GetDeclarationType(varDesc, typeDeclarationType); - - var asTypeName = GetParameterInfo(varDesc.elemdescVar.tdesc, info); - info.ReleaseVarDesc(ppVarDesc); - - return new Declaration(new QualifiedMemberName(typeQualifiedModuleName, fieldName), - moduleDeclaration, moduleDeclaration, asTypeName.Name, null, false, false, Accessibility.Global, memberType, null, - Selection.Home, false, null); - } - - private ParameterDeclaration CreateParameterDeclaration(IReadOnlyList memberNames, int paramIndex, - FUNCDESC memberDescriptor, QualifiedModuleName typeQualifiedModuleName, Declaration memberDeclaration, ITypeInfo info) - { - var paramName = memberNames[paramIndex + 1]; - - var paramPointer = new IntPtr(memberDescriptor.lprgelemdescParam.ToInt64() + Marshal.SizeOf(typeof(ELEMDESC)) * paramIndex); - var elementDesc = (ELEMDESC)Marshal.PtrToStructure(paramPointer, typeof(ELEMDESC)); - var isOptional = elementDesc.desc.paramdesc.wParamFlags.HasFlag(PARAMFLAG.PARAMFLAG_FOPT); - var paramDesc = elementDesc.tdesc; - var paramInfo = GetParameterInfo(paramDesc, info); - - return new ParameterDeclaration(new QualifiedMemberName(typeQualifiedModuleName, paramName), memberDeclaration, paramInfo.Name, null, null, isOptional, paramInfo.IsByRef, paramInfo.IsArray); - } - - private IEnumerable GetImplementedInterfaceNames(TYPEATTR typeAttr, ITypeInfo info, Declaration module) - { - var output = new List(); - for (var implIndex = 0; implIndex < typeAttr.cImplTypes; implIndex++) - { - int href; - info.GetRefTypeOfImplType(implIndex, out href); - - ITypeInfo implTypeInfo; - info.GetRefTypeInfo(href, out implTypeInfo); - - IntPtr typeAttributesPointer; - implTypeInfo.GetTypeAttr(out typeAttributesPointer); - - var typeAttributes = (TYPEATTR)Marshal.PtrToStructure(typeAttributesPointer, typeof(TYPEATTR)); - - IMPLTYPEFLAGS flags = 0; - try - { - info.GetImplTypeFlags(implIndex, out flags); - } - catch (COMException) { } - - var implTypeName = GetTypeName(implTypeInfo); - if (implTypeName != "IDispatch" && implTypeName != "IUnknown") - { - // skip IDispatch.. just about everything implements it and RD doesn't need to care about it; don't care about IUnknown either - output.Add(implTypeName); - } - - if (flags != 0) - { - ComInformation comInfo; - if (_comInformation.TryGetValue(typeAttributes.guid, out comInfo)) - { - _comInformation[typeAttributes.guid].ImplTypeFlags = - _comInformation[typeAttributes.guid].ImplTypeFlags | flags; - } - else - { - _comInformation.Add(typeAttributes.guid, - new ComInformation(typeAttributes, flags, implTypeInfo, implTypeName, module.QualifiedName.QualifiedModuleName, module, 0)); - } - } - - info.ReleaseTypeAttr(typeAttributesPointer); - } - return output; - } - - private DeclarationType GetDeclarationType(ITypeLib typeLibrary, int i) - { - TYPEKIND typeKind; - typeLibrary.GetTypeInfoType(i, out typeKind); - - DeclarationType typeDeclarationType = DeclarationType.Control; // todo: a better default - if (typeKind == TYPEKIND.TKIND_ENUM) - { - typeDeclarationType = DeclarationType.Enumeration; - } - else if (typeKind == TYPEKIND.TKIND_COCLASS || typeKind == TYPEKIND.TKIND_INTERFACE || - typeKind == TYPEKIND.TKIND_ALIAS || typeKind == TYPEKIND.TKIND_DISPATCH) - { - typeDeclarationType = DeclarationType.ClassModule; - } - else if (typeKind == TYPEKIND.TKIND_RECORD) - { - typeDeclarationType = DeclarationType.UserDefinedType; - } - else if (typeKind == TYPEKIND.TKIND_MODULE) - { - typeDeclarationType = DeclarationType.ProceduralModule; - } - return typeDeclarationType; - } - - private DeclarationType GetDeclarationType(string memberName, FUNCDESC funcDesc, VarEnum funcValueType, TYPEKIND typekind, IMPLTYPEFLAGS parentImplTypeFlags) - { - DeclarationType memberType; - if (funcDesc.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYGET)) - { - memberType = DeclarationType.PropertyGet; - } - else if (funcDesc.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYPUT)) - { - memberType = DeclarationType.PropertyLet; - } - else if (funcDesc.invkind.HasFlag(INVOKEKIND.INVOKE_PROPERTYPUTREF)) - { - memberType = DeclarationType.PropertySet; - } - else if ((parentImplTypeFlags.HasFlag(IMPLTYPEFLAGS.IMPLTYPEFLAG_FSOURCE) || - ((FUNCFLAGS)funcDesc.wFuncFlags).HasFlag(FUNCFLAGS.FUNCFLAG_FSOURCE))) - { - memberType = DeclarationType.Event; - } - else if (funcValueType == VarEnum.VT_VOID) - { - memberType = DeclarationType.Procedure; - } - else - { - memberType = DeclarationType.Function; - } - return memberType; - } - - private DeclarationType GetDeclarationType(VARDESC varDesc, DeclarationType typeDeclarationType) - { - var memberType = DeclarationType.Variable; - if (varDesc.varkind == VARKIND.VAR_CONST) - { - memberType = typeDeclarationType == DeclarationType.Enumeration - ? DeclarationType.EnumerationMember - : DeclarationType.Constant; - } - else if (typeDeclarationType == DeclarationType.UserDefinedType) - { - memberType = DeclarationType.UserDefinedTypeMember; - } - return memberType; - } } } diff --git a/Rubberduck.Parsing/Symbols/SerializableDeclaration.cs b/Rubberduck.Parsing/Symbols/SerializableDeclaration.cs index e5cbecb777..d30c53a7a7 100644 --- a/Rubberduck.Parsing/Symbols/SerializableDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/SerializableDeclaration.cs @@ -11,7 +11,13 @@ public class SerializableDeclarationTree { public SerializableDeclaration Node; - public IEnumerable Children; + private List _children = new List(); + + public IEnumerable Children + { + get { return _children; } + set { _children = new List(value); } + } public SerializableDeclarationTree() { } @@ -29,6 +35,11 @@ public SerializableDeclarationTree(SerializableDeclaration node, IEnumerable Declarations { get; set; } + + private List _declarations = new List(); + + public IEnumerable Declarations + { + get { return _declarations; } + set { _declarations = new List(value); } + } + [DataMember(IsRequired = true)] public long MajorVersion { get; set; } [DataMember(IsRequired = true)] public long MinorVersion { get; set; } + public void AddDeclaration(SerializableDeclarationTree tree) + { + _declarations.Add(tree); + } + + private readonly Dictionary _pseudoLookup = new Dictionary(); + public SerializableDeclarationTree GetPseudoDeclaration(Declaration declaration) + { + if (!_pseudoLookup.ContainsKey(declaration.IdentifierName)) + { + _declarations.Add(new SerializableDeclarationTree(declaration)); + } + + return _pseudoLookup[declaration.IdentifierName]; + } + public List Unwrap() { var project = (ProjectDeclaration)Node.Unwrap(null); diff --git a/Rubberduck.Parsing/Symbols/SubroutineDeclaration.cs b/Rubberduck.Parsing/Symbols/SubroutineDeclaration.cs index 216c233c29..37b7a4dc6d 100644 --- a/Rubberduck.Parsing/Symbols/SubroutineDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/SubroutineDeclaration.cs @@ -1,5 +1,7 @@ -using Antlr4.Runtime; +using System; +using Antlr4.Runtime; using Rubberduck.Parsing.Annotations; +using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; using System.Collections.Generic; @@ -43,6 +45,25 @@ public SubroutineDeclaration( _parameters = new List(); } + public SubroutineDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, + Attributes attributes) + : this(new QualifiedMemberName(module, member.Name), + parent, + parent, + string.Empty, + Accessibility.Global, + null, + Selection.Home, + true, + null, + attributes) + { + _parameters = + member.Parameters.Select(decl => new ParameterDeclaration(decl, this, module)) + .Cast() + .ToList(); + } + public IEnumerable Parameters { get diff --git a/Rubberduck.Parsing/VBA/RubberduckParser.cs b/Rubberduck.Parsing/VBA/RubberduckParser.cs index dfab250603..571d1eac0f 100644 --- a/Rubberduck.Parsing/VBA/RubberduckParser.cs +++ b/Rubberduck.Parsing/VBA/RubberduckParser.cs @@ -444,12 +444,12 @@ private void SyncComReferences(IReadOnlyList projects) } serialize.Remove(root); - var project = GetSerializableProject(root, serialize); - if (project != null) - { - var added = State.BuiltInDeclarationTrees.TryAdd(project); - //if (!added) { throw new Exception();} - } + //var project = GetSerializableProject(root, serialize); + //if (project != null) + //{ + // var added = State.BuiltInDeclarationTrees.TryAdd(project); + // //if (!added) { throw new Exception();} + //} } catch (Exception exception) { From ca722a75a2b2187b47e03f85078fad2abe7f680d Mon Sep 17 00:00:00 2001 From: comintern Date: Sat, 24 Dec 2016 23:45:16 -0600 Subject: [PATCH 02/15] Apparently we *do* care about TKIND_ALIAS, and everything is procedural (for now)... --- Rubberduck.Parsing/ComReflection/ComModule.cs | 5 ++--- Rubberduck.Parsing/ComReflection/ComProject.cs | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Rubberduck.Parsing/ComReflection/ComModule.cs b/Rubberduck.Parsing/ComReflection/ComModule.cs index 4b52fd6d8a..674a3304c5 100644 --- a/Rubberduck.Parsing/ComReflection/ComModule.cs +++ b/Rubberduck.Parsing/ComReflection/ComModule.cs @@ -26,17 +26,16 @@ public IEnumerable Fields } public ComModule(ITypeLib typeLib, ITypeInfo info, TYPEATTR attrib, int index) : base(typeLib, attrib, index) - { + { + Type = DeclarationType.ProceduralModule; if (attrib.cFuncs > 0) { Debug.Assert(attrib.cVars == 0); - Type = DeclarationType.ProceduralModule; GetComMembers(info, attrib); } else { Debug.Assert(attrib.cVars > 0); - Type = DeclarationType.Module; GetComFields(info, attrib); } } diff --git a/Rubberduck.Parsing/ComReflection/ComProject.cs b/Rubberduck.Parsing/ComReflection/ComProject.cs index b27c9df139..2cd7cc4a43 100644 --- a/Rubberduck.Parsing/ComReflection/ComProject.cs +++ b/Rubberduck.Parsing/ComReflection/ComProject.cs @@ -109,7 +109,8 @@ private void LoadModules(ITypeLib typeLibrary) var coclass = type ?? new ComCoClass(typeLibrary, info, typeAttributes, index); _classes.Add(coclass as ComCoClass); if (type != null) KnownTypes.TryAdd(typeAttributes.guid, coclass); - break; + break; + case TYPEKIND.TKIND_ALIAS: case TYPEKIND.TKIND_DISPATCH: case TYPEKIND.TKIND_INTERFACE: var intface = type ?? new ComInterface(typeLibrary, info, typeAttributes, index); @@ -124,9 +125,6 @@ private void LoadModules(ITypeLib typeLibrary) _modules.Add(module as ComModule); if (type != null) KnownTypes.TryAdd(typeAttributes.guid, module); break; - case TYPEKIND.TKIND_ALIAS: - //Haven't seen one of these either. - throw new NotImplementedException(string.Format("Didn't expect to find a TKIND_ALIAS in {0}.", Path)); default: throw new NotImplementedException(string.Format("Didn't expect a TYPEATTR with multiple typekind flags set in {0}.", Path)); } From db380046e3e6a68b707bca8e456cbbc7c600077f Mon Sep 17 00:00:00 2001 From: comintern Date: Sun, 25 Dec 2016 00:31:59 -0600 Subject: [PATCH 03/15] Forgot about the parameter count thing, paramarrays. --- Rubberduck.Parsing/ComReflection/ComMember.cs | 12 ++--- Rubberduck.Parsing/Symbols/Declaration.cs | 50 ++++--------------- 2 files changed, 16 insertions(+), 46 deletions(-) diff --git a/Rubberduck.Parsing/ComReflection/ComMember.cs b/Rubberduck.Parsing/ComReflection/ComMember.cs index 66db42b908..8e011aaa6b 100644 --- a/Rubberduck.Parsing/ComReflection/ComMember.cs +++ b/Rubberduck.Parsing/ComReflection/ComMember.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using Rubberduck.Parsing.Symbols; @@ -75,17 +76,16 @@ private void LoadParameters(FUNCDESC funcDesc, ITypeInfo info) int count; info.GetNames(Index, names, 255, out count); - for (var index = 0; index < funcDesc.cParams; index++) + for (var index = 0; index < count - 1; index++) { var paramPtr = new IntPtr(funcDesc.lprgelemdescParam.ToInt64() + Marshal.SizeOf(typeof(ELEMDESC)) * index); var elemDesc = (ELEMDESC)Marshal.PtrToStructure(paramPtr, typeof(ELEMDESC)); var param = new ComParameter(elemDesc, info, names[index + 1]); Parameters.Add(param); - //TODO: - //if (parameters.Any() && memberDescriptor.cParamsOpt == -1) - //{ - // parameters.Last().IsParamArray = true; - //} + } + if (Parameters.Any() && funcDesc.cParamsOpt == -1) + { + Parameters.Last().IsParamArray = true; } } diff --git a/Rubberduck.Parsing/Symbols/Declaration.cs b/Rubberduck.Parsing/Symbols/Declaration.cs index 80748c55da..2c4d850dad 100644 --- a/Rubberduck.Parsing/Symbols/Declaration.cs +++ b/Rubberduck.Parsing/Symbols/Declaration.cs @@ -151,7 +151,7 @@ public Declaration( _accessibility = accessibility; _declarationType = declarationType; _selection = selection; - _context = context; + Context = context; _isBuiltIn = isBuiltIn; _annotations = annotations; _attributes = attributes ?? new Attributes(); @@ -162,6 +162,7 @@ public Declaration( _isArray = isArray; _asTypeContext = asTypeContext; _typeHint = typeHint; + Debug.Assert(!string.IsNullOrEmpty(_qualifiedName.MemberName)); } public Declaration(ComEnumeration enumeration, Declaration parent, QualifiedModuleName module) : this( @@ -180,7 +181,7 @@ public Declaration(ComEnumeration enumeration, Declaration parent, QualifiedModu null, true, null, - new Attributes()) { } + new Attributes()) { Debug.Assert(module != null && !string.IsNullOrEmpty(_qualifiedName.MemberName)); } public Declaration(ComEnumerationMember member, Declaration parent, QualifiedModuleName module) : this( new QualifiedMemberName(module, member.Name), @@ -195,15 +196,14 @@ public Declaration(ComEnumerationMember member, Declaration parent, QualifiedMod null, Selection.Home, false, - null, - true) { } + null) { Debug.Assert(module != null && !string.IsNullOrEmpty(_qualifiedName.MemberName)); } public Declaration(ComField field, Declaration parent, QualifiedModuleName module) : this( - new QualifiedMemberName(module, field.Name), + new QualifiedMemberName(module, string.IsNullOrEmpty(field.Name) ? "Foo" : field.Name), parent, parent, - field.Name, + string.IsNullOrEmpty(field.Name) ? "Foo" : field.Name, null, false, false, @@ -212,8 +212,7 @@ public Declaration(ComField field, Declaration parent, QualifiedModuleName modul null, Selection.Home, false, - null, - true) { } + null) { Debug.Assert(module != null && !string.IsNullOrEmpty(_qualifiedName.MemberName)); } private string FolderFromAnnotations() { @@ -285,18 +284,7 @@ public bool IsTypeSpecified private readonly QualifiedMemberName _qualifiedName; public QualifiedMemberName QualifiedName { get { return _qualifiedName; } } - private ParserRuleContext _context; - public ParserRuleContext Context - { - get - { - return _context; - } - set - { - _context = value; - } - } + public ParserRuleContext Context { get; set; } private ConcurrentBag _references = new ConcurrentBag(); public IEnumerable References @@ -391,24 +379,6 @@ public void AddReference( annotations)); } - //public void AddReference(IdentifierReference reference) - //{ - // if (reference == null || reference.Declaration.Context == reference.Context) - // { - // return; - // } - // if (reference.Context.Parent != _context - // && !_references.Select(r => r.Context).Contains(reference.Context.Parent) - // && !_references.Any(r => r.QualifiedModuleName == reference.QualifiedModuleName - // && r.Selection.StartLine == reference.Selection.StartLine - // && r.Selection.EndLine == reference.Selection.EndLine - // && r.Selection.StartColumn == reference.Selection.StartColumn - // && r.Selection.EndColumn == reference.Selection.EndColumn)) - // { - // _references.Add(reference); - // } - //} - public void AddMemberCall(IdentifierReference reference) { if (reference == null || reference.Declaration == null || reference.Declaration.Context == reference.Context) @@ -459,7 +429,7 @@ public string ProjectName public object[] ToArray() { - return new object[] { this.ProjectName, this.CustomFolder, this.ComponentName, this.DeclarationType.ToString(), this.Scope, this.IdentifierName, this.AsTypeName }; + return new object[] { ProjectName, CustomFolder, ComponentName, DeclarationType.ToString(), Scope, IdentifierName, AsTypeName }; } @@ -548,7 +518,7 @@ internal set DeclarationType.PropertyLet, DeclarationType.PropertyLet, DeclarationType.UserDefinedType, - DeclarationType.Constant, + DeclarationType.Constant }; public bool IsSelected(QualifiedSelection selection) From 10ae5c95154895f965ccf8e1cd20e4f855a0cc3b Mon Sep 17 00:00:00 2001 From: comintern Date: Sun, 25 Dec 2016 09:26:14 -0600 Subject: [PATCH 04/15] Apparently we DO care about COM structures. Add missed ctor initialization. --- Rubberduck.Parsing/ComReflection/ComField.cs | 30 +++++++------ Rubberduck.Parsing/ComReflection/ComModule.cs | 16 ++++--- .../ComReflection/ComProject.cs | 14 ++++-- Rubberduck.Parsing/ComReflection/ComStruct.cs | 45 +++++++++++++++++++ Rubberduck.Parsing/ComReflection/ComType.cs | 5 +++ Rubberduck.Parsing/Rubberduck.Parsing.csproj | 1 + .../Symbols/ClassModuleDeclaration.cs | 2 + Rubberduck.Parsing/Symbols/Declaration.cs | 6 +-- .../ReferencedDeclarationsCollector.cs | 21 ++------- 9 files changed, 96 insertions(+), 44 deletions(-) create mode 100644 Rubberduck.Parsing/ComReflection/ComStruct.cs diff --git a/Rubberduck.Parsing/ComReflection/ComField.cs b/Rubberduck.Parsing/ComReflection/ComField.cs index 28445884a1..74b82dd33b 100644 --- a/Rubberduck.Parsing/ComReflection/ComField.cs +++ b/Rubberduck.Parsing/ComReflection/ComField.cs @@ -1,6 +1,8 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; +using System.Xml.Schema; +using Rubberduck.Parsing.Symbols; using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC; using VARFLAGS = System.Runtime.InteropServices.ComTypes.VARFLAGS; @@ -9,31 +11,31 @@ namespace Rubberduck.Parsing.ComReflection [DebuggerDisplay("{Name}")] public class ComField { - public string Name { get; set; } - public int Index { get; set; } - public bool IsConstant { get; set; } + public string Name { get; private set; } + public int Index { get; private set; } + public DeclarationType Type { get; private set; } public object DefaultValue { get; set; } - public VarEnum DefaultValueType { get; set; } + public string ValueType { get; set; } public VARFLAGS Flags { get; set; } - public ComField(ITypeInfo info, VARDESC varDesc, int index) + public ComField(string name, VARDESC varDesc, int index, DeclarationType type) { + Name = name; Index = index; + Type = type; - var names = new string[255]; - int length; - info.GetNames(varDesc.memid, names, 255, out length); - Debug.Assert(length >= 1); - Name = names[0]; - - IsConstant = varDesc.varkind.HasFlag(VARKIND.VAR_CONST); Flags = (VARFLAGS)varDesc.wVarFlags; - if (IsConstant) + if (Type == DeclarationType.Constant) { var value = new ComVariant(varDesc.desc.lpvarValue); DefaultValue = value.Value; - DefaultValueType = value.VariantType; + string typeName; + ValueType = ComVariant.TypeNames.TryGetValue(value.VariantType, out typeName) ? typeName : "Object"; + } + else + { + } } } diff --git a/Rubberduck.Parsing/ComReflection/ComModule.cs b/Rubberduck.Parsing/ComReflection/ComModule.cs index 674a3304c5..7758b5bed8 100644 --- a/Rubberduck.Parsing/ComReflection/ComModule.cs +++ b/Rubberduck.Parsing/ComReflection/ComModule.cs @@ -11,7 +11,7 @@ namespace Rubberduck.Parsing.ComReflection { - public class ComModule : ComType, IComTypeWithMembers + public class ComModule : ComType, IComTypeWithMembers, IComTypeWithFields { private readonly List _members = new List(); public IEnumerable Members @@ -42,14 +42,18 @@ public ComModule(ITypeLib typeLib, ITypeInfo info, TYPEATTR attrib, int index) : private void GetComFields(ITypeInfo info, TYPEATTR attrib) { + var names = new string[255]; for (var index = 0; index < attrib.cVars; index++) { - IntPtr ppVarDesc; - info.GetVarDesc(index, out ppVarDesc); - var varDesc = (VARDESC)Marshal.PtrToStructure(ppVarDesc, typeof(VARDESC)); + IntPtr varPtr; + info.GetVarDesc(index, out varPtr); + var desc = (VARDESC)Marshal.PtrToStructure(varPtr, typeof(VARDESC)); + int length; + info.GetNames(desc.memid, names, 255, out length); + Debug.Assert(length == 1); - _fields.Add(new ComField(info, varDesc, index)); - info.ReleaseVarDesc(ppVarDesc); + _fields.Add(new ComField(names[0], desc, index, DeclarationType.Constant)); + info.ReleaseVarDesc(varPtr); } } diff --git a/Rubberduck.Parsing/ComReflection/ComProject.cs b/Rubberduck.Parsing/ComReflection/ComProject.cs index 2cd7cc4a43..7da6e49ad4 100644 --- a/Rubberduck.Parsing/ComReflection/ComProject.cs +++ b/Rubberduck.Parsing/ComReflection/ComProject.cs @@ -49,6 +49,12 @@ public IEnumerable Modules get { return _modules; } } + private readonly List _structs = new List(); + public IEnumerable Structs + { + get { return _structs; } + } + public IEnumerable Members { get @@ -56,7 +62,8 @@ public IEnumerable Members return _modules.Cast() .Union(_interfaces) .Union(_classes) - .Union(_enumerations); + .Union(_enumerations) + .Union(_structs); } } @@ -118,8 +125,9 @@ private void LoadModules(ITypeLib typeLibrary) if (type != null) KnownTypes.TryAdd(typeAttributes.guid, intface); break; case TYPEKIND.TKIND_RECORD: - //IIR these aren't available to VBA. I could quite possibly be wrong though. - throw new NotImplementedException(string.Format("Didn't expect to find a TKIND_RECORD in {0}.", Path)); + var structure = new ComStruct(typeLibrary, info, typeAttributes, index); + _structs.Add(structure); + break; case TYPEKIND.TKIND_MODULE: var module = type ?? new ComModule(typeLibrary, info, typeAttributes, index); _modules.Add(module as ComModule); diff --git a/Rubberduck.Parsing/ComReflection/ComStruct.cs b/Rubberduck.Parsing/ComReflection/ComStruct.cs new file mode 100644 index 0000000000..1ee9aac0c4 --- /dev/null +++ b/Rubberduck.Parsing/ComReflection/ComStruct.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using Rubberduck.Parsing.Symbols; +using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; +using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC; + +namespace Rubberduck.Parsing.ComReflection +{ + public class ComStruct : ComType, IComTypeWithFields + { + private readonly List _fields = new List(); + public IEnumerable Fields + { + get { return _fields; } + } + + public ComStruct(ITypeLib typeLib, ITypeInfo info, TYPEATTR attrib, int index) + : base(typeLib, attrib, index) + { + _fields = new List(); + Type = DeclarationType.UserDefinedType; + GetFields(info, attrib); + } + + private void GetFields(ITypeInfo info, TYPEATTR attrib) + { + var names = new string[255]; + for (var index = 0; index < attrib.cVars; index++) + { + IntPtr varPtr; + info.GetVarDesc(index, out varPtr); + var desc = (VARDESC)Marshal.PtrToStructure(varPtr, typeof(VARDESC)); + int length; + info.GetNames(desc.memid, names, 255, out length); + Debug.Assert(length == 1); + + _fields.Add(new ComField(names[0], desc, index, DeclarationType.UserDefinedTypeMember)); + info.ReleaseVarDesc(varPtr); + } + } + } +} diff --git a/Rubberduck.Parsing/ComReflection/ComType.cs b/Rubberduck.Parsing/ComReflection/ComType.cs index 5fd6e35c2e..e28cf8c112 100644 --- a/Rubberduck.Parsing/ComReflection/ComType.cs +++ b/Rubberduck.Parsing/ComReflection/ComType.cs @@ -17,6 +17,11 @@ public interface IComTypeWithMembers : IComType IEnumerable Members { get; } } + public interface IComTypeWithFields : IComType + { + IEnumerable Fields { get; } + } + [DebuggerDisplay("{Name}")] public abstract class ComType : ComBase, IComType { diff --git a/Rubberduck.Parsing/Rubberduck.Parsing.csproj b/Rubberduck.Parsing/Rubberduck.Parsing.csproj index e5e7873f4a..e30ba0b8e8 100644 --- a/Rubberduck.Parsing/Rubberduck.Parsing.csproj +++ b/Rubberduck.Parsing/Rubberduck.Parsing.csproj @@ -116,6 +116,7 @@ + diff --git a/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs b/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs index bdba10fbc5..85e1017361 100644 --- a/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs @@ -75,6 +75,8 @@ public ClassModuleDeclaration(ComCoClass coClass, Declaration parent, QualifiedM coClass.ImplementedInterfaces.Where(i => !i.IsRestricted && !IgnoredInterfaces.Contains(i.Name)) .Select(i => i.Name) .ToList(); + _supertypes = new HashSet(); + _subtypes = new HashSet(); } public ClassModuleDeclaration(ComInterface intrface, Declaration parent, QualifiedModuleName module, diff --git a/Rubberduck.Parsing/Symbols/Declaration.cs b/Rubberduck.Parsing/Symbols/Declaration.cs index 2c4d850dad..e0d991fe0c 100644 --- a/Rubberduck.Parsing/Symbols/Declaration.cs +++ b/Rubberduck.Parsing/Symbols/Declaration.cs @@ -200,15 +200,15 @@ public Declaration(ComEnumerationMember member, Declaration parent, QualifiedMod public Declaration(ComField field, Declaration parent, QualifiedModuleName module) : this( - new QualifiedMemberName(module, string.IsNullOrEmpty(field.Name) ? "Foo" : field.Name), + new QualifiedMemberName(module, field.Name), parent, parent, - string.IsNullOrEmpty(field.Name) ? "Foo" : field.Name, + field.Name, null, false, false, Accessibility.Global, - field.IsConstant ? DeclarationType.Constant : DeclarationType.UserDefinedType, //TODO: This is wrong. + field.Type, null, Selection.Home, false, diff --git a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs index ec14360b6e..40dc462f4d 100644 --- a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs +++ b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs @@ -150,7 +150,8 @@ public List GetDeclarationsForReference(IReference reference) output.Add(enumDeclaration); output.AddRange(members); } - var fields = module as ComModule; + + var fields = module as IComTypeWithFields; if (fields == null || !fields.Fields.Any()) { continue; @@ -224,23 +225,7 @@ private Declaration CreateMemberDeclaration(ComMember member, QualifiedModuleNam return new PropertyLetDeclaration(member, parent, module, attributes); default: Debug.Assert(false); - return new Declaration( - new QualifiedMemberName(module, member.Name), - parent, - parent, - string.Empty, // asTypeName.Name, - null, - false, - false, - Accessibility.Global, - member.Type, - null, - Selection.Home, - false, - null, - true, - null, - attributes); + return null as Declaration; } } } From 4eb9a25f9eca4d2c9bda9b785e74abd95e64143c Mon Sep 17 00:00:00 2001 From: comintern Date: Sun, 25 Dec 2016 10:04:03 -0600 Subject: [PATCH 05/15] Fix bad COM enum asTypeNames (woops). Removed asserts. --- Rubberduck.Parsing/Symbols/Declaration.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Rubberduck.Parsing/Symbols/Declaration.cs b/Rubberduck.Parsing/Symbols/Declaration.cs index e0d991fe0c..bd3076f763 100644 --- a/Rubberduck.Parsing/Symbols/Declaration.cs +++ b/Rubberduck.Parsing/Symbols/Declaration.cs @@ -162,7 +162,6 @@ public Declaration( _isArray = isArray; _asTypeContext = asTypeContext; _typeHint = typeHint; - Debug.Assert(!string.IsNullOrEmpty(_qualifiedName.MemberName)); } public Declaration(ComEnumeration enumeration, Declaration parent, QualifiedModuleName module) : this( @@ -181,13 +180,13 @@ public Declaration(ComEnumeration enumeration, Declaration parent, QualifiedModu null, true, null, - new Attributes()) { Debug.Assert(module != null && !string.IsNullOrEmpty(_qualifiedName.MemberName)); } + new Attributes()) { } public Declaration(ComEnumerationMember member, Declaration parent, QualifiedModuleName module) : this( new QualifiedMemberName(module, member.Name), parent, parent, - member.Name, + parent.IdentifierName, null, false, false, @@ -196,7 +195,7 @@ public Declaration(ComEnumerationMember member, Declaration parent, QualifiedMod null, Selection.Home, false, - null) { Debug.Assert(module != null && !string.IsNullOrEmpty(_qualifiedName.MemberName)); } + null) { } public Declaration(ComField field, Declaration parent, QualifiedModuleName module) : this( @@ -212,7 +211,7 @@ public Declaration(ComField field, Declaration parent, QualifiedModuleName modul null, Selection.Home, false, - null) { Debug.Assert(module != null && !string.IsNullOrEmpty(_qualifiedName.MemberName)); } + null) { } private string FolderFromAnnotations() { From 0336253d9beed8ec659e9b43d01652a1ab2b78c9 Mon Sep 17 00:00:00 2001 From: comintern Date: Sun, 25 Dec 2016 14:16:07 -0600 Subject: [PATCH 06/15] Fixed structure scope, parameter scope, simplify name creation in ctors. Fix Len{B} Declaration selection. --- .../Inspections/UnassignedVariableUsageInspection.cs | 5 +++-- Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs | 4 ++-- Rubberduck.Parsing/Symbols/Declaration.cs | 6 +++--- Rubberduck.Parsing/Symbols/FunctionDeclaration.cs | 2 +- Rubberduck.Parsing/Symbols/ParameterDeclaration.cs | 2 +- Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs | 6 +++--- Rubberduck.Parsing/Symbols/ProjectDeclaration.cs | 2 +- Rubberduck.Parsing/Symbols/PropertyGetDeclaration.cs | 2 +- Rubberduck.Parsing/Symbols/PropertyLetDeclaration.cs | 4 ++-- Rubberduck.Parsing/Symbols/PropertySetDeclaration.cs | 2 +- Rubberduck.Parsing/Symbols/SubroutineDeclaration.cs | 8 ++++---- 11 files changed, 22 insertions(+), 21 deletions(-) diff --git a/RetailCoder.VBE/Inspections/UnassignedVariableUsageInspection.cs b/RetailCoder.VBE/Inspections/UnassignedVariableUsageInspection.cs index 41cbaaf75f..a7cb3a8a95 100644 --- a/RetailCoder.VBE/Inspections/UnassignedVariableUsageInspection.cs +++ b/RetailCoder.VBE/Inspections/UnassignedVariableUsageInspection.cs @@ -30,8 +30,9 @@ public override IEnumerable GetInspectionResults() && !declaration.IsSelfAssigned && !declaration.References.Any(reference => reference.IsAssignment)); - var lenFunction = BuiltInDeclarations.SingleOrDefault(s => s.Scope == "VBE7.DLL;VBA.Strings.Len"); - var lenbFunction = BuiltInDeclarations.SingleOrDefault(s => s.Scope == "VBE7.DLL;VBA.Strings.LenB"); + //The parameter scoping was apparently incorrect before - need to filter for the actual function. + var lenFunction = BuiltInDeclarations.SingleOrDefault(s => s.DeclarationType == DeclarationType.Function && s.Scope.Equals("VBE7.DLL;VBA.Strings.Len")); + var lenbFunction = BuiltInDeclarations.SingleOrDefault(s => s.DeclarationType == DeclarationType.Function && s.Scope.Equals("VBE7.DLL;VBA.Strings.Len")); return from issue in declarations where issue.References.Any() diff --git a/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs b/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs index 85e1017361..c837ec0dc3 100644 --- a/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ClassModuleDeclaration.cs @@ -54,7 +54,7 @@ public ClassModuleDeclaration( public ClassModuleDeclaration(ComCoClass coClass, Declaration parent, QualifiedModuleName module, Attributes attributes) : base( - new QualifiedMemberName(module, coClass.Name), + module.QualifyMemberName(coClass.Name), parent, parent, coClass.Name, @@ -82,7 +82,7 @@ public ClassModuleDeclaration(ComCoClass coClass, Declaration parent, QualifiedM public ClassModuleDeclaration(ComInterface intrface, Declaration parent, QualifiedModuleName module, Attributes attributes) : this( - new QualifiedMemberName(module, intrface.Name), + module.QualifyMemberName(intrface.Name), parent, intrface.Name, true, diff --git a/Rubberduck.Parsing/Symbols/Declaration.cs b/Rubberduck.Parsing/Symbols/Declaration.cs index bd3076f763..885c0f2054 100644 --- a/Rubberduck.Parsing/Symbols/Declaration.cs +++ b/Rubberduck.Parsing/Symbols/Declaration.cs @@ -165,7 +165,7 @@ public Declaration( } public Declaration(ComEnumeration enumeration, Declaration parent, QualifiedModuleName module) : this( - new QualifiedMemberName(module, enumeration.Name), + module.QualifyMemberName(enumeration.Name), parent, parent, enumeration.Name, @@ -183,7 +183,7 @@ public Declaration(ComEnumeration enumeration, Declaration parent, QualifiedModu new Attributes()) { } public Declaration(ComEnumerationMember member, Declaration parent, QualifiedModuleName module) : this( - new QualifiedMemberName(module, member.Name), + module.QualifyMemberName(member.Name), parent, parent, parent.IdentifierName, @@ -199,7 +199,7 @@ public Declaration(ComEnumerationMember member, Declaration parent, QualifiedMod public Declaration(ComField field, Declaration parent, QualifiedModuleName module) : this( - new QualifiedMemberName(module, field.Name), + module.QualifyMemberName(field.Name), parent, parent, field.Name, diff --git a/Rubberduck.Parsing/Symbols/FunctionDeclaration.cs b/Rubberduck.Parsing/Symbols/FunctionDeclaration.cs index 794c77fd4f..3d20f41795 100644 --- a/Rubberduck.Parsing/Symbols/FunctionDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/FunctionDeclaration.cs @@ -50,7 +50,7 @@ public FunctionDeclaration( public FunctionDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, Attributes attributes) : this( - new QualifiedMemberName(module, member.Name), + module.QualifyMemberName(member.Name), parent, parent, member.ReturnType.TypeName, diff --git a/Rubberduck.Parsing/Symbols/ParameterDeclaration.cs b/Rubberduck.Parsing/Symbols/ParameterDeclaration.cs index 153bc8c739..264e6a2f5c 100644 --- a/Rubberduck.Parsing/Symbols/ParameterDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ParameterDeclaration.cs @@ -79,7 +79,7 @@ public ParameterDeclaration(QualifiedMemberName qualifiedName, public ParameterDeclaration(ComParameter parameter, Declaration parent, QualifiedModuleName module) : this( - new QualifiedMemberName(module, parameter.Name), + module.QualifyMemberName(parameter.Name), parent, parameter.TypeName, null, diff --git a/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs b/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs index 167e8529e4..951cbfbe15 100644 --- a/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs @@ -36,7 +36,7 @@ public ProceduralModuleDeclaration( public ProceduralModuleDeclaration(ComModule statics, Declaration parent, QualifiedModuleName module, Attributes attributes) : this( - new QualifiedMemberName(module, statics.Name), + module.QualifyMemberName(statics.Name), parent, statics.Name, true, @@ -49,7 +49,7 @@ public ProceduralModuleDeclaration(ComModule statics, Declaration parent, Qualif //This is the pseudo-module ctor for COM enumerations. public ProceduralModuleDeclaration(ComEnumeration enumeration, Declaration parent, QualifiedModuleName module) : this( - new QualifiedMemberName(module, string.Format("_{0}", enumeration.Name)), + module.QualifyMemberName(string.Format("_{0}", enumeration.Name)), parent, string.Format("_{0}", enumeration.Name), true, @@ -59,7 +59,7 @@ public ProceduralModuleDeclaration(ComEnumeration enumeration, Declaration paren //This is the pseudo-module ctor for anything else from COM with fields instead of members. public ProceduralModuleDeclaration(string pseudo, Declaration parent, QualifiedModuleName module) : this( - new QualifiedMemberName(module, pseudo), + module.QualifyMemberName(pseudo), parent, pseudo, true, diff --git a/Rubberduck.Parsing/Symbols/ProjectDeclaration.cs b/Rubberduck.Parsing/Symbols/ProjectDeclaration.cs index fef17fd587..39325b1327 100644 --- a/Rubberduck.Parsing/Symbols/ProjectDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ProjectDeclaration.cs @@ -33,7 +33,7 @@ public ProjectDeclaration( } public ProjectDeclaration(ComProject project, QualifiedModuleName module) - : this(new QualifiedMemberName(module, project.Name), project.Name, true) + : this(module.QualifyMemberName(project.Name), project.Name, true) { MajorVersion = project.MajorVersion; MinorVersion = project.MinorVersion; diff --git a/Rubberduck.Parsing/Symbols/PropertyGetDeclaration.cs b/Rubberduck.Parsing/Symbols/PropertyGetDeclaration.cs index 7d9f6e1c44..24cd658d0e 100644 --- a/Rubberduck.Parsing/Symbols/PropertyGetDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/PropertyGetDeclaration.cs @@ -51,7 +51,7 @@ public PropertyGetDeclaration( public PropertyGetDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, Attributes attributes) : this( - new QualifiedMemberName(module, member.Name), + module.QualifyMemberName(member.Name), parent, parent, member.ReturnType.TypeName, diff --git a/Rubberduck.Parsing/Symbols/PropertyLetDeclaration.cs b/Rubberduck.Parsing/Symbols/PropertyLetDeclaration.cs index c7ca4eac6a..24c4bf4e26 100644 --- a/Rubberduck.Parsing/Symbols/PropertyLetDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/PropertyLetDeclaration.cs @@ -47,7 +47,7 @@ public PropertyLetDeclaration( public PropertyLetDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, Attributes attributes) : this( - new QualifiedMemberName(module, member.Name), + module.QualifyMemberName(member.Name), parent, parent, string.Empty, //TODO: Need to get the types for these. @@ -61,7 +61,7 @@ public PropertyLetDeclaration(ComMember member, Declaration parent, QualifiedMod _parameters = member.Parameters.Select(decl => new ParameterDeclaration(decl, this, module)) .Cast() - .ToList(); + .ToList(); } public IEnumerable Parameters diff --git a/Rubberduck.Parsing/Symbols/PropertySetDeclaration.cs b/Rubberduck.Parsing/Symbols/PropertySetDeclaration.cs index 98782ea1f1..fe8fdac781 100644 --- a/Rubberduck.Parsing/Symbols/PropertySetDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/PropertySetDeclaration.cs @@ -46,7 +46,7 @@ public PropertySetDeclaration( public PropertySetDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, Attributes attributes) : this( - new QualifiedMemberName(module, member.Name), + module.QualifyMemberName(member.Name), parent, parent, string.Empty, //TODO: Need to get the types for these. diff --git a/Rubberduck.Parsing/Symbols/SubroutineDeclaration.cs b/Rubberduck.Parsing/Symbols/SubroutineDeclaration.cs index 37b7a4dc6d..648b4afc28 100644 --- a/Rubberduck.Parsing/Symbols/SubroutineDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/SubroutineDeclaration.cs @@ -1,5 +1,4 @@ -using System; -using Antlr4.Runtime; +using Antlr4.Runtime; using Rubberduck.Parsing.Annotations; using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA; @@ -47,7 +46,8 @@ public SubroutineDeclaration( public SubroutineDeclaration(ComMember member, Declaration parent, QualifiedModuleName module, Attributes attributes) - : this(new QualifiedMemberName(module, member.Name), + : this( + module.QualifyMemberName(member.Name), parent, parent, string.Empty, @@ -61,7 +61,7 @@ public SubroutineDeclaration(ComMember member, Declaration parent, QualifiedModu _parameters = member.Parameters.Select(decl => new ParameterDeclaration(decl, this, module)) .Cast() - .ToList(); + .ToList(); } public IEnumerable Parameters From 58d595f1140989b1d0fa4adf2b53b6365f7a70fb Mon Sep 17 00:00:00 2001 From: comintern Date: Sun, 25 Dec 2016 19:48:48 -0600 Subject: [PATCH 07/15] Fix constant types, finished the struct members I forgot, consolidate enums and types in 2 pseudo-modules. --- Rubberduck.Parsing/ComReflection/ComField.cs | 4 +- .../ComReflection/ComProject.cs | 11 +- Rubberduck.Parsing/Symbols/Declaration.cs | 23 +- .../Symbols/ProceduralModuleDeclaration.cs | 12 +- .../ReferencedDeclarationsCollector.cs | 234 +++++++++++++----- Rubberduck.Parsing/VBA/RubberduckParser.cs | 56 ++--- 6 files changed, 219 insertions(+), 121 deletions(-) diff --git a/Rubberduck.Parsing/ComReflection/ComField.cs b/Rubberduck.Parsing/ComReflection/ComField.cs index 74b82dd33b..b526c48e43 100644 --- a/Rubberduck.Parsing/ComReflection/ComField.cs +++ b/Rubberduck.Parsing/ComReflection/ComField.cs @@ -35,7 +35,9 @@ public ComField(string name, VARDESC varDesc, int index, DeclarationType type) } else { - + Debug.Assert(varDesc.varkind == VARKIND.VAR_PERINSTANCE); + string typeName; + ValueType = ComVariant.TypeNames.TryGetValue((VarEnum)varDesc.elemdescVar.tdesc.vt, out typeName) ? typeName : "Object"; } } } diff --git a/Rubberduck.Parsing/ComReflection/ComProject.cs b/Rubberduck.Parsing/ComReflection/ComProject.cs index 7da6e49ad4..ff16048b8d 100644 --- a/Rubberduck.Parsing/ComReflection/ComProject.cs +++ b/Rubberduck.Parsing/ComReflection/ComProject.cs @@ -59,6 +59,8 @@ public IEnumerable Members { get { + //Note - Enums and Types should enumerate *last*. That will prevent a duplicate module in the unlikely(?) + //instance where the TypeLib defines a module named "Enums" or "Types". return _modules.Cast() .Union(_interfaces) .Union(_classes) @@ -118,6 +120,10 @@ private void LoadModules(ITypeLib typeLibrary) if (type != null) KnownTypes.TryAdd(typeAttributes.guid, coclass); break; case TYPEKIND.TKIND_ALIAS: + //The current handling of this is wrong - these don't have to be classes or interfaces. In the VBE module for example, + //"LongPtr" is defined as an alias to "Long" (at least on a 32 bit system) - RD is currently treating is like a class. + //Unclear if these can *also* define alternative names for interfaces as well, but all the ones I've seen have been basically + //a C typedef. So... this needs work. Don't make any assumptions about these elsewhere in the code until this is nailed down. case TYPEKIND.TKIND_DISPATCH: case TYPEKIND.TKIND_INTERFACE: var intface = type ?? new ComInterface(typeLibrary, info, typeAttributes, index); @@ -138,10 +144,7 @@ private void LoadModules(ITypeLib typeLibrary) } info.ReleaseTypeAttr(typeAttributesPointer); } - catch (NullReferenceException) - { - return; - } + catch (COMException) { } } } } diff --git a/Rubberduck.Parsing/Symbols/Declaration.cs b/Rubberduck.Parsing/Symbols/Declaration.cs index 885c0f2054..7c23b61452 100644 --- a/Rubberduck.Parsing/Symbols/Declaration.cs +++ b/Rubberduck.Parsing/Symbols/Declaration.cs @@ -168,7 +168,7 @@ public Declaration(ComEnumeration enumeration, Declaration parent, QualifiedModu module.QualifyMemberName(enumeration.Name), parent, parent, - enumeration.Name, + "Long", //Match the VBA default type declaration. Technically these *can* be a LongLong on 64 bit systems, but would likely crash the VBE... null, false, false, @@ -182,6 +182,25 @@ public Declaration(ComEnumeration enumeration, Declaration parent, QualifiedModu null, new Attributes()) { } + public Declaration(ComStruct structure, Declaration parent, QualifiedModuleName module) + : this( + module.QualifyMemberName(structure.Name), + parent, + parent, + structure.Name, + null, + false, + false, + Accessibility.Global, + DeclarationType.UserDefinedType, + null, + Selection.Home, + false, + null, + true, + null, + new Attributes()) { } + public Declaration(ComEnumerationMember member, Declaration parent, QualifiedModuleName module) : this( module.QualifyMemberName(member.Name), parent, @@ -202,7 +221,7 @@ public Declaration(ComField field, Declaration parent, QualifiedModuleName modul module.QualifyMemberName(field.Name), parent, parent, - field.Name, + field.ValueType, null, false, false, diff --git a/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs b/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs index 951cbfbe15..68b8c0ec03 100644 --- a/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs @@ -46,17 +46,7 @@ public ProceduralModuleDeclaration(ComModule statics, Declaration parent, Qualif IsPrivateModule = statics.IsRestricted; } - //This is the pseudo-module ctor for COM enumerations. - public ProceduralModuleDeclaration(ComEnumeration enumeration, Declaration parent, QualifiedModuleName module) - : this( - module.QualifyMemberName(string.Format("_{0}", enumeration.Name)), - parent, - string.Format("_{0}", enumeration.Name), - true, - new List(), - new Attributes()) { } - - //This is the pseudo-module ctor for anything else from COM with fields instead of members. + //This is the pseudo-module ctor for COM enumerations and types. public ProceduralModuleDeclaration(string pseudo, Declaration parent, QualifiedModuleName module) : this( module.QualifyMemberName(pseudo), diff --git a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs index 40dc462f4d..808fa79c13 100644 --- a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs +++ b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; -using NLog; using Rubberduck.Parsing.ComReflection; using Rubberduck.Parsing.VBA; using Rubberduck.VBEditor; @@ -15,9 +15,7 @@ namespace Rubberduck.Parsing.Symbols { public class ReferencedDeclarationsCollector { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private readonly RubberduckParserState _state; - + #region Native Stuff // ReSharper disable InconsistentNaming // ReSharper disable UnusedMember.Local /// @@ -45,69 +43,88 @@ private enum REGKIND [DllImport("oleaut32.dll", CharSet = CharSet.Unicode)] private static extern int LoadTypeLibEx(string strTypeLibName, REGKIND regKind, out ITypeLib TypeLib); // ReSharper restore InconsistentNaming + #endregion + + private readonly RubberduckParserState _state; + private SerializableProject _serialized; + private readonly Dictionary _treeLookup = new Dictionary(); + private readonly List _declarations = new List(); - public ReferencedDeclarationsCollector(RubberduckParserState state) + private const string EnumPseudoName = "Enums"; + private Declaration _enumModule; + private const string TypePseudoName = "Types"; + private Declaration _typeModule; + + private static readonly HashSet IgnoredInterfaceMembers = new HashSet { - _state = state; - } + "QueryInterface", + "AddRef", + "Release", + "GetTypeInfoCount", + "GetTypeInfo", + "GetIDsOfNames", + "Invoke" + }; - private static readonly HashSet IgnoredInterfaceMembers = new HashSet { "QueryInterface", "AddRef", "Release", "GetTypeInfoCount", "GetTypeInfo", "GetIDsOfNames", "Invoke" }; + private readonly string _referenceName; + private readonly string _path; + private readonly int _referenceMajor; + private readonly int _referenceMinor; - private bool SerializedVersionExists(string name, int major, int minor) + public ReferencedDeclarationsCollector(RubberduckParserState state, IReference reference) { - var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations"); - if (!Directory.Exists(path)) - { - return false; - } - //TODO: This is naively based on file name for now - this should attempt to deserialize any SerializableProject.Nodes in the directory and test for equity. - var testFile = Path.Combine(path, string.Format("{0}.{1}.{2}", name, major, minor) + ".xml"); - if (File.Exists(testFile)) + _state = state; + _path = reference.FullPath; + _referenceName = reference.Name; + _referenceMajor = reference.Major; + _referenceMinor = reference.Minor; + } + + public bool SerializedVersionExists + { + get { - return true; + var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations"); + if (!Directory.Exists(path)) + { + return false; + } + //TODO: This is naively based on file name for now - this should attempt to deserialize any SerializableProject.Nodes in the directory and test for equity. + var testFile = Path.Combine(path, string.Format("{0}.{1}.{2}", _referenceName, _referenceMajor, _referenceMinor) + ".xml"); + return File.Exists(testFile); } - return false; } - private List LoadSerializedBuiltInReferences(string name, int major, int minor) + public List LoadDeclarationsFromXml() { var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations"); - var file = Path.Combine(path, string.Format("{0}.{1}.{2}", name, major, minor) + ".xml"); + var file = Path.Combine(path, string.Format("{0}.{1}.{2}", _referenceName, _referenceMajor, _referenceMinor) + ".xml"); var reader = new XmlPersistableDeclarations(); var deserialized = reader.Load(file); return deserialized.Unwrap(); } - public List GetDeclarationsForReference(IReference reference) + public List LoadDeclarationsFromLibrary() { - var output = new List(); - var path = reference.FullPath; - - if (SerializedVersionExists(reference.Name, reference.Major, reference.Minor)) - { - Logger.Trace(string.Format("Deserializing reference '{0}'.", reference.Name)); - return LoadSerializedBuiltInReferences(reference.Name, reference.Major, reference.Minor); - } - Logger.Trace(string.Format("COM reflecting reference '{0}'.", reference.Name)); - ITypeLib typeLibrary; // Failure to load might mean that it's a "normal" VBProject that will get parsed by us anyway. - LoadTypeLibEx(path, REGKIND.REGKIND_NONE, out typeLibrary); + LoadTypeLibEx(_path, REGKIND.REGKIND_NONE, out typeLibrary); if (typeLibrary == null) { - return output; + return _declarations; } - var type = new ComProject(typeLibrary) { Path = path }; + var type = new ComProject(typeLibrary) { Path = _path }; - var projectName = new QualifiedModuleName(type.Name, path, type.Name); + var projectName = new QualifiedModuleName(type.Name, _path, type.Name); var project = new ProjectDeclaration(type, projectName); + _serialized = new SerializableProject(project); + _declarations.Add(project); - output.Add(project); foreach (var module in type.Members) { - var moduleName = new QualifiedModuleName(reference.Name, path, module.Name); - + var moduleName = new QualifiedModuleName(_referenceName, _path, module.Name); + var attributes = new Attributes(); if (module.IsPreDeclared) { @@ -117,19 +134,44 @@ public List GetDeclarationsForReference(IReference reference) { attributes.AddGlobalClassAttribute(); } - var declaration = CreateModuleDeclaration(module, moduleName, project, attributes); - output.Add(declaration); + + var declaration = CreateModuleDeclaration(module, + module.Type == DeclarationType.Enumeration || module.Type == DeclarationType.UserDefinedType + ? projectName + : moduleName, project, attributes); + + if (declaration.IdentifierName.Equals(EnumPseudoName)) + { + if (_enumModule == null) + { + _enumModule = declaration; + AddToOutput(_enumModule, null); + } + } + else if (declaration.IdentifierName.Equals(TypePseudoName)) + { + if (_typeModule == null) + { + _typeModule = declaration; + AddToOutput(_typeModule, null); + } + } + else + { + AddToOutput(declaration, null); + } + var membered = module as IComTypeWithMembers; if (membered != null) { foreach (var item in membered.Members.Where(m => !m.IsRestricted && !IgnoredInterfaceMembers.Contains(m.Name))) { var memberDeclaration = CreateMemberDeclaration(item, moduleName, declaration); - output.Add(memberDeclaration); + AddToOutput(memberDeclaration, declaration); var hasParams = memberDeclaration as IDeclarationWithParameter; if (hasParams != null) { - output.AddRange(hasParams.Parameters); + AddRangeToOutput(hasParams.Parameters, memberDeclaration); } var coClass = memberDeclaration as ClassModuleDeclaration; if (coClass != null && item.IsDefault) @@ -138,17 +180,25 @@ public List GetDeclarationsForReference(IReference reference) } } } + var enumeration = module as ComEnumeration; if (enumeration != null) { - var enumDeclaration = new Declaration(enumeration, declaration, projectName); - var members = - enumeration.Members.Select( - e => - new Declaration(e, enumDeclaration, - new QualifiedModuleName(reference.Name, path, enumeration.Name))); - output.Add(enumDeclaration); - output.AddRange(members); + var qualified = new QualifiedModuleName(_referenceName, _path, EnumPseudoName); + var enumDeclaration = new Declaration(enumeration, declaration, qualified); + var members = enumeration.Members.Select(e => new Declaration(e, enumDeclaration, qualified)); + AddToOutput(enumDeclaration, null); + AddRangeToOutput(members, enumDeclaration); + } + + var structure = module as ComStruct; + if (structure != null) + { + var qualified = new QualifiedModuleName(_referenceName, _path, TypePseudoName); + var typeDeclaration = new Declaration(structure, declaration, qualified); + var members = structure.Fields.Select(f => new Declaration(f, typeDeclaration, qualified)); + AddToOutput(typeDeclaration, null); + AddRangeToOutput(members, typeDeclaration); } var fields = module as IComTypeWithFields; @@ -157,9 +207,42 @@ public List GetDeclarationsForReference(IReference reference) continue; } var declarations = fields.Fields.Select(f => new Declaration(f, declaration, projectName)); - output.AddRange(declarations); + AddRangeToOutput(declarations, declaration); + } + _state.BuiltInDeclarationTrees.TryAdd(_serialized); + return _declarations; + } + + private void AddToOutput(Declaration declaration, Declaration parent) + { + _declarations.Add(declaration); + //if (parent == null) + //{ + // var tree = new SerializableDeclarationTree(declaration); + // _treeLookup.Add(declaration, tree); + // _serialized.AddDeclaration(tree); + //} + //else + //{ + // var tree = new SerializableDeclarationTree(declaration); + // Debug.Assert(!_treeLookup.ContainsKey(declaration)); + // _treeLookup.Add(declaration, tree); + // _treeLookup[parent].AddChildTree(tree); + //} + } + + private void AddRangeToOutput(IEnumerable declarations, Declaration parent) + { + //Debug.Assert(parent != null); + //var tree = _treeLookup[parent]; + foreach (var declaration in declarations) + { + // Debug.Assert(!_treeLookup.ContainsKey(declaration)); + // var child = new SerializableDeclarationTree(declaration); + // _treeLookup.Add(declaration, child); + // tree.AddChildTree(child); + _declarations.Add(declaration); } - return output; } private Declaration CreateModuleDeclaration(IComType module, QualifiedModuleName project, Declaration parent, Attributes attributes) @@ -167,7 +250,14 @@ private Declaration CreateModuleDeclaration(IComType module, QualifiedModuleName var enumeration = module as ComEnumeration; if (enumeration != null) { - return new ProceduralModuleDeclaration(enumeration, parent, project); + //There's no real reason that these can't all live in one pseudo-module. + return _enumModule ?? new ProceduralModuleDeclaration(EnumPseudoName, parent, project); + } + var types = module as ComStruct; + if (types != null) + { + //There's also no real reason that *these* can't all live in one pseudo-module. + return _typeModule ?? new ProceduralModuleDeclaration(TypePseudoName, parent, project); } var coClass = module as ComCoClass; var intrface = module as ComInterface; @@ -185,13 +275,7 @@ private Declaration CreateModuleDeclaration(IComType module, QualifiedModuleName } return output; } - var normal = module as ComModule; - if (normal != null && normal.Fields.Any()) - { - //These are going to be UDTs or Consts. Apparently COM modules can declare *either* fields or members. - return new ProceduralModuleDeclaration(string.Format("_{0}", normal.Name), parent, project); - } - return new ProceduralModuleDeclaration(normal, parent, project, attributes); + return new ProceduralModuleDeclaration(module as ComModule, parent, project, attributes); } private Declaration CreateMemberDeclaration(ComMember member, QualifiedModuleName module, Declaration parent) @@ -224,9 +308,33 @@ private Declaration CreateMemberDeclaration(ComMember member, QualifiedModuleNam case DeclarationType.PropertyLet: return new PropertyLetDeclaration(member, parent, module, attributes); default: - Debug.Assert(false); - return null as Declaration; + throw new InvalidEnumArgumentException(string.Format("Unexpected DeclarationType {0} encountered.", member.Type)); } } + + //private SerializableProject GetSerializableProject(ProjectDeclaration declaration, List declarations) + //{ + // var project = new SerializableProject(declaration); + // var children = new List(); + // var nodes = declarations.Where(x => x.ParentDeclaration.Equals(declaration)).ToList(); + // foreach (var item in nodes) + // { + // children.Add(GetSerializableTreeForDeclaration(item, declarations)); + // } + // project.Declarations = children; + // return project; + //} + + //private SerializableDeclarationTree GetSerializableTreeForDeclaration(Declaration declaration, List declarations) + //{ + // var children = new List(); + // var nodes = declarations.Where(x => x.ParentDeclaration.Equals(declaration)).ToList(); + // declarations.RemoveAll(nodes.Contains); + // foreach (var item in nodes) + // { + // children.Add(GetSerializableTreeForDeclaration(item, declarations)); + // } + // return new SerializableDeclarationTree(declaration, children); + //} } } diff --git a/Rubberduck.Parsing/VBA/RubberduckParser.cs b/Rubberduck.Parsing/VBA/RubberduckParser.cs index 571d1eac0f..ded3a6ff3e 100644 --- a/Rubberduck.Parsing/VBA/RubberduckParser.cs +++ b/Rubberduck.Parsing/VBA/RubberduckParser.cs @@ -432,24 +432,25 @@ private void SyncComReferences(IReadOnlyList projects) { try { - Logger.Trace(string.Format("Loading referenced type '{0}'.", reference.Name)); - var comReflector = new ReferencedDeclarationsCollector(State); + Logger.Trace(string.Format("Loading referenced type '{0}'.", localReference.Name)); - var items = comReflector.GetDeclarationsForReference(localReference); - var root = items.OfType().SingleOrDefault(); - var serialize = new List(items); - foreach (var declaration in serialize) + var comReflector = new ReferencedDeclarationsCollector(State, localReference); + if (comReflector.SerializedVersionExists) { - State.AddDeclaration(declaration); + Logger.Trace(string.Format("Deserializing reference '{0}'.", localReference.Name)); + foreach (var declaration in comReflector.LoadDeclarationsFromXml()) + { + State.AddDeclaration(declaration); + } + } + else + { + Logger.Trace(string.Format("COM reflecting reference '{0}'.", localReference.Name)); + foreach (var declaration in comReflector.LoadDeclarationsFromLibrary()) + { + State.AddDeclaration(declaration); + } } - serialize.Remove(root); - - //var project = GetSerializableProject(root, serialize); - //if (project != null) - //{ - // var added = State.BuiltInDeclarationTrees.TryAdd(project); - // //if (!added) { throw new Exception();} - //} } catch (Exception exception) { @@ -492,31 +493,6 @@ private void SyncComReferences(IReadOnlyList projects) } } - private SerializableProject GetSerializableProject(ProjectDeclaration declaration, List declarations) - { - var project = new SerializableProject(declaration); - var children = new List(); - var nodes = declarations.Where(x => x.ParentDeclaration.Equals(declaration)).ToList(); - foreach (var item in nodes) - { - children.Add(GetSerializableTreeForDeclaration(item, declarations)); - } - project.Declarations = children; - return project; - } - - private SerializableDeclarationTree GetSerializableTreeForDeclaration(Declaration declaration, List declarations) - { - var children = new List(); - var nodes = declarations.Where(x => x.ParentDeclaration.Equals(declaration)).ToList(); - declarations.RemoveAll(nodes.Contains); - foreach (var item in nodes) - { - children.Add(GetSerializableTreeForDeclaration(item, declarations)); - } - return new SerializableDeclarationTree(declaration, children); - } - private void UnloadComReference(IReference reference, IReadOnlyList projects) { var referencedProjectId = GetReferenceProjectId(reference, projects); From a4911b7d09b8b4a72810bce4332f072f63e45f72 Mon Sep 17 00:00:00 2001 From: comintern Date: Sun, 25 Dec 2016 22:20:02 -0600 Subject: [PATCH 08/15] De-consolidate enums and types again (:sigh:), hook serialization back up. --- .../Symbols/ProceduralModuleDeclaration.cs | 17 +- .../ReferencedDeclarationsCollector.cs | 162 ++++++------------ .../Symbols/SerializableDeclaration.cs | 8 + 3 files changed, 74 insertions(+), 113 deletions(-) diff --git a/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs b/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs index 68b8c0ec03..302cc6e046 100644 --- a/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/ProceduralModuleDeclaration.cs @@ -46,12 +46,21 @@ public ProceduralModuleDeclaration(ComModule statics, Declaration parent, Qualif IsPrivateModule = statics.IsRestricted; } - //This is the pseudo-module ctor for COM enumerations and types. - public ProceduralModuleDeclaration(string pseudo, Declaration parent, QualifiedModuleName module) + //These are the pseudo-module ctor for COM enumerations and types. + public ProceduralModuleDeclaration(ComEnumeration pseudo, Declaration parent, QualifiedModuleName module) : this( - module.QualifyMemberName(pseudo), + module.QualifyMemberName(pseudo.Name), parent, - pseudo, + pseudo.Name, + true, + new List(), + new Attributes()) { } + + public ProceduralModuleDeclaration(ComStruct pseudo, Declaration parent, QualifiedModuleName module) + : this( + module.QualifyMemberName(pseudo.Name), + parent, + pseudo.Name, true, new List(), new Attributes()) { } diff --git a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs index 808fa79c13..d021d149d8 100644 --- a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs +++ b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; @@ -47,14 +46,8 @@ private enum REGKIND private readonly RubberduckParserState _state; private SerializableProject _serialized; - private readonly Dictionary _treeLookup = new Dictionary(); private readonly List _declarations = new List(); - private const string EnumPseudoName = "Enums"; - private Declaration _enumModule; - private const string TypePseudoName = "Types"; - private Declaration _typeModule; - private static readonly HashSet IgnoredInterfaceMembers = new HashSet { "QueryInterface", @@ -95,13 +88,29 @@ public bool SerializedVersionExists } } + private static readonly HashSet ProceduralTypes = + new HashSet(new[] + { + DeclarationType.Procedure, DeclarationType.Function, DeclarationType.PropertyGet, + DeclarationType.PropertyLet, DeclarationType.PropertySet + }); + public List LoadDeclarationsFromXml() { var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Rubberduck", "Declarations"); var file = Path.Combine(path, string.Format("{0}.{1}.{2}", _referenceName, _referenceMajor, _referenceMinor) + ".xml"); var reader = new XmlPersistableDeclarations(); var deserialized = reader.Load(file); - return deserialized.Unwrap(); + + var declarations = deserialized.Unwrap(); + + foreach (var members in declarations.Where(d => d.ParentDeclaration.DeclarationType == DeclarationType.ClassModule && + ProceduralTypes.Contains(d.DeclarationType)) + .GroupBy(d => d.ParentDeclaration)) + { + _state.CoClasses.TryAdd(members.Select(m => m.IdentifierName).ToList(), members.First().ParentDeclaration); + } + return declarations; } public List LoadDeclarationsFromLibrary() @@ -123,7 +132,10 @@ public List LoadDeclarationsFromLibrary() foreach (var module in type.Members) { - var moduleName = new QualifiedModuleName(_referenceName, _path, module.Name); + var moduleName = new QualifiedModuleName(_referenceName, _path, + module.Type == DeclarationType.Enumeration || module.Type == DeclarationType.UserDefinedType + ? string.Format("_{0}", module.Name) + : module.Name); var attributes = new Attributes(); if (module.IsPreDeclared) @@ -135,31 +147,10 @@ public List LoadDeclarationsFromLibrary() attributes.AddGlobalClassAttribute(); } - var declaration = CreateModuleDeclaration(module, - module.Type == DeclarationType.Enumeration || module.Type == DeclarationType.UserDefinedType - ? projectName - : moduleName, project, attributes); - - if (declaration.IdentifierName.Equals(EnumPseudoName)) - { - if (_enumModule == null) - { - _enumModule = declaration; - AddToOutput(_enumModule, null); - } - } - else if (declaration.IdentifierName.Equals(TypePseudoName)) - { - if (_typeModule == null) - { - _typeModule = declaration; - AddToOutput(_typeModule, null); - } - } - else - { - AddToOutput(declaration, null); - } + var declaration = CreateModuleDeclaration(module, moduleName, project, attributes); + var moduleTree = new SerializableDeclarationTree(declaration); + _declarations.Add(declaration); + _serialized.AddDeclaration(moduleTree); var membered = module as IComTypeWithMembers; if (membered != null) @@ -167,11 +158,16 @@ public List LoadDeclarationsFromLibrary() foreach (var item in membered.Members.Where(m => !m.IsRestricted && !IgnoredInterfaceMembers.Contains(m.Name))) { var memberDeclaration = CreateMemberDeclaration(item, moduleName, declaration); - AddToOutput(memberDeclaration, declaration); + _declarations.Add(memberDeclaration); + + var memberTree = new SerializableDeclarationTree(memberDeclaration); + moduleTree.AddChildTree(memberTree); + var hasParams = memberDeclaration as IDeclarationWithParameter; if (hasParams != null) { - AddRangeToOutput(hasParams.Parameters, memberDeclaration); + _declarations.AddRange(hasParams.Parameters); + memberTree.AddChildren(hasParams.Parameters); } var coClass = memberDeclaration as ClassModuleDeclaration; if (coClass != null && item.IsDefault) @@ -184,21 +180,27 @@ public List LoadDeclarationsFromLibrary() var enumeration = module as ComEnumeration; if (enumeration != null) { - var qualified = new QualifiedModuleName(_referenceName, _path, EnumPseudoName); - var enumDeclaration = new Declaration(enumeration, declaration, qualified); - var members = enumeration.Members.Select(e => new Declaration(e, enumDeclaration, qualified)); - AddToOutput(enumDeclaration, null); - AddRangeToOutput(members, enumDeclaration); + var enumDeclaration = new Declaration(enumeration, declaration, moduleName); + _declarations.Add(enumDeclaration); + var members = enumeration.Members.Select(e => new Declaration(e, enumDeclaration, moduleName)).ToList(); + _declarations.AddRange(members); + + var enumTree = new SerializableDeclarationTree(enumDeclaration); + moduleTree.AddChildTree(enumTree); + enumTree.AddChildren(members); } var structure = module as ComStruct; if (structure != null) { - var qualified = new QualifiedModuleName(_referenceName, _path, TypePseudoName); - var typeDeclaration = new Declaration(structure, declaration, qualified); - var members = structure.Fields.Select(f => new Declaration(f, typeDeclaration, qualified)); - AddToOutput(typeDeclaration, null); - AddRangeToOutput(members, typeDeclaration); + var typeDeclaration = new Declaration(structure, declaration, moduleName); + _declarations.Add(typeDeclaration); + var members = structure.Fields.Select(f => new Declaration(f, typeDeclaration, moduleName)).ToList(); + _declarations.AddRange(members); + + var typeTree = new SerializableDeclarationTree(typeDeclaration); + moduleTree.AddChildTree(typeTree); + typeTree.AddChildren(members); } var fields = module as IComTypeWithFields; @@ -206,58 +208,25 @@ public List LoadDeclarationsFromLibrary() { continue; } - var declarations = fields.Fields.Select(f => new Declaration(f, declaration, projectName)); - AddRangeToOutput(declarations, declaration); + var declarations = fields.Fields.Select(f => new Declaration(f, declaration, projectName)).ToList(); + _declarations.AddRange(declarations); + moduleTree.AddChildren(declarations); } _state.BuiltInDeclarationTrees.TryAdd(_serialized); return _declarations; } - private void AddToOutput(Declaration declaration, Declaration parent) - { - _declarations.Add(declaration); - //if (parent == null) - //{ - // var tree = new SerializableDeclarationTree(declaration); - // _treeLookup.Add(declaration, tree); - // _serialized.AddDeclaration(tree); - //} - //else - //{ - // var tree = new SerializableDeclarationTree(declaration); - // Debug.Assert(!_treeLookup.ContainsKey(declaration)); - // _treeLookup.Add(declaration, tree); - // _treeLookup[parent].AddChildTree(tree); - //} - } - - private void AddRangeToOutput(IEnumerable declarations, Declaration parent) - { - //Debug.Assert(parent != null); - //var tree = _treeLookup[parent]; - foreach (var declaration in declarations) - { - // Debug.Assert(!_treeLookup.ContainsKey(declaration)); - // var child = new SerializableDeclarationTree(declaration); - // _treeLookup.Add(declaration, child); - // tree.AddChildTree(child); - _declarations.Add(declaration); - } - } - private Declaration CreateModuleDeclaration(IComType module, QualifiedModuleName project, Declaration parent, Attributes attributes) { var enumeration = module as ComEnumeration; if (enumeration != null) { - //There's no real reason that these can't all live in one pseudo-module. - return _enumModule ?? new ProceduralModuleDeclaration(EnumPseudoName, parent, project); + return new ProceduralModuleDeclaration(enumeration, parent, project); } var types = module as ComStruct; if (types != null) { - //There's also no real reason that *these* can't all live in one pseudo-module. - return _typeModule ?? new ProceduralModuleDeclaration(TypePseudoName, parent, project); + return new ProceduralModuleDeclaration(types, parent, project); } var coClass = module as ComCoClass; var intrface = module as ComInterface; @@ -311,30 +280,5 @@ private Declaration CreateMemberDeclaration(ComMember member, QualifiedModuleNam throw new InvalidEnumArgumentException(string.Format("Unexpected DeclarationType {0} encountered.", member.Type)); } } - - //private SerializableProject GetSerializableProject(ProjectDeclaration declaration, List declarations) - //{ - // var project = new SerializableProject(declaration); - // var children = new List(); - // var nodes = declarations.Where(x => x.ParentDeclaration.Equals(declaration)).ToList(); - // foreach (var item in nodes) - // { - // children.Add(GetSerializableTreeForDeclaration(item, declarations)); - // } - // project.Declarations = children; - // return project; - //} - - //private SerializableDeclarationTree GetSerializableTreeForDeclaration(Declaration declaration, List declarations) - //{ - // var children = new List(); - // var nodes = declarations.Where(x => x.ParentDeclaration.Equals(declaration)).ToList(); - // declarations.RemoveAll(nodes.Contains); - // foreach (var item in nodes) - // { - // children.Add(GetSerializableTreeForDeclaration(item, declarations)); - // } - // return new SerializableDeclarationTree(declaration, children); - //} } } diff --git a/Rubberduck.Parsing/Symbols/SerializableDeclaration.cs b/Rubberduck.Parsing/Symbols/SerializableDeclaration.cs index d30c53a7a7..a6143d8a63 100644 --- a/Rubberduck.Parsing/Symbols/SerializableDeclaration.cs +++ b/Rubberduck.Parsing/Symbols/SerializableDeclaration.cs @@ -36,6 +36,14 @@ public SerializableDeclarationTree(SerializableDeclaration node, IEnumerable declarations) + { + foreach (var child in declarations) + { + _children.Add(new SerializableDeclarationTree(child)); + } + } + public void AddChildTree(SerializableDeclarationTree tree) { _children.Add(tree); From e74a1ff81a0b61b293fda87c2a5c39d3d15638e7 Mon Sep 17 00:00:00 2001 From: comintern Date: Sun, 25 Dec 2016 22:27:25 -0600 Subject: [PATCH 09/15] Ooops, fixed NRE in deserialization. --- Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs index d021d149d8..97d831d522 100644 --- a/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs +++ b/Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs @@ -104,7 +104,8 @@ public List LoadDeclarationsFromXml() var declarations = deserialized.Unwrap(); - foreach (var members in declarations.Where(d => d.ParentDeclaration.DeclarationType == DeclarationType.ClassModule && + foreach (var members in declarations.Where(d => d.DeclarationType != DeclarationType.Project && + d.ParentDeclaration.DeclarationType == DeclarationType.ClassModule && ProceduralTypes.Contains(d.DeclarationType)) .GroupBy(d => d.ParentDeclaration)) { From 841ab81daa4367bdc0d0f542ba2ff75cf95d8c04 Mon Sep 17 00:00:00 2001 From: comintern Date: Sun, 25 Dec 2016 23:26:14 -0600 Subject: [PATCH 10/15] Use window visibililty settings on startup. --- RetailCoder.VBE/Settings/WindowSettings.cs | 37 +++++++++++++++++++ .../CodeExplorerDockablePresenter.cs | 8 ++-- .../SearchResultsDockablePresenter.cs | 4 +- .../UI/DockableToolwindowPresenter.cs | 12 +++++- ...entifierReferencesListDockablePresenter.cs | 2 +- .../ImplementationsListDockablePresenter.cs | 2 +- .../CodeInspectionsDockablePresenter.cs | 8 ++-- .../UI/ParserErrors/ParserErrorsPresenter.cs | 2 +- .../SourceControlDockablePresenter.cs | 6 ++- .../ToDoExplorerDockablePresenter.cs | 8 ++-- .../TestExplorerDockablePresenter.cs | 8 ++-- 11 files changed, 76 insertions(+), 21 deletions(-) diff --git a/RetailCoder.VBE/Settings/WindowSettings.cs b/RetailCoder.VBE/Settings/WindowSettings.cs index 8fb6fd39ba..e841ddb32e 100644 --- a/RetailCoder.VBE/Settings/WindowSettings.cs +++ b/RetailCoder.VBE/Settings/WindowSettings.cs @@ -1,4 +1,11 @@ using System.Xml.Serialization; +using Rubberduck.Properties; +using Rubberduck.UI; +using Rubberduck.UI.CodeExplorer; +using Rubberduck.UI.Inspections; +using Rubberduck.UI.SourceControl; +using Rubberduck.UI.ToDoItems; +using Rubberduck.UI.UnitTesting; namespace Rubberduck.Settings { @@ -9,6 +16,8 @@ public interface IWindowSettings bool SourceControlVisibleOnStartup { get; set; } bool TestExplorerVisibleOnStartup { get; set; } bool TodoExplorerVisibleOnStartup { get; set; } + + bool IsWindowVisible(DockableToolwindowPresenter candidate); } [XmlType(AnonymousType = true)] @@ -34,5 +43,33 @@ public WindowSettings(bool codeExplorerVisibleOnStartup, bool codeInspectionsVis public bool SourceControlVisibleOnStartup { get; set; } public bool TestExplorerVisibleOnStartup { get; set; } public bool TodoExplorerVisibleOnStartup { get; set; } + + public bool IsWindowVisible(DockableToolwindowPresenter candidate) + { + //I'm sure there's a better way to do this, because this is a lazy-ass way to do it. + //We're injecting into the base class, so check the derived class: + if (candidate is CodeExplorerDockablePresenter) + { + return CodeExplorerVisibleOnStartup; + } + if (candidate is CodeInspectionsDockablePresenter) + { + return CodeInspectionsVisibleOnStartup; + } + if (candidate is SourceControlDockablePresenter) + { + return SourceControlVisibleOnStartup; + } + if (candidate is TestExplorerDockablePresenter) + { + return TestExplorerVisibleOnStartup; + } + if (candidate is ToDoExplorerDockablePresenter) + { + return TodoExplorerVisibleOnStartup; + } + //Oh. Hello. I have no clue who you are... + return false; + } } } diff --git a/RetailCoder.VBE/UI/CodeExplorer/CodeExplorerDockablePresenter.cs b/RetailCoder.VBE/UI/CodeExplorer/CodeExplorerDockablePresenter.cs index 34eabae1f2..4bc79bed61 100644 --- a/RetailCoder.VBE/UI/CodeExplorer/CodeExplorerDockablePresenter.cs +++ b/RetailCoder.VBE/UI/CodeExplorer/CodeExplorerDockablePresenter.cs @@ -1,11 +1,13 @@ -using Rubberduck.VBEditor.SafeComWrappers.Abstract; +using Rubberduck.Settings; +using Rubberduck.SettingsProvider; +using Rubberduck.VBEditor.SafeComWrappers.Abstract; namespace Rubberduck.UI.CodeExplorer { public class CodeExplorerDockablePresenter : DockableToolwindowPresenter { - public CodeExplorerDockablePresenter(IVBE vbe, IAddIn addIn, CodeExplorerWindow view) - : base(vbe, addIn, view) + public CodeExplorerDockablePresenter(IVBE vbe, IAddIn addIn, CodeExplorerWindow view, IConfigProvider settings) + : base(vbe, addIn, view, settings) { } } diff --git a/RetailCoder.VBE/UI/Controls/SearchResultsDockablePresenter.cs b/RetailCoder.VBE/UI/Controls/SearchResultsDockablePresenter.cs index 2d70723701..e3099835b8 100644 --- a/RetailCoder.VBE/UI/Controls/SearchResultsDockablePresenter.cs +++ b/RetailCoder.VBE/UI/Controls/SearchResultsDockablePresenter.cs @@ -4,8 +4,8 @@ namespace Rubberduck.UI.Controls { public class SearchResultsDockablePresenter : DockableToolwindowPresenter { - public SearchResultsDockablePresenter(IVBE vbe, IAddIn addin, IDockableUserControl view) - : base(vbe, addin, view) + public SearchResultsDockablePresenter(IVBE vbe, IAddIn addin, IDockableUserControl view) + : base(vbe, addin, view, null) { } diff --git a/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs b/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs index 11103d10ad..55f7e502a5 100644 --- a/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs +++ b/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs @@ -1,7 +1,10 @@ using System; +using System.Configuration; using System.Runtime.InteropServices; using System.Windows.Forms; using NLog; +using Rubberduck.Settings; +using Rubberduck.SettingsProvider; using Rubberduck.VBEditor.SafeComWrappers.Abstract; namespace Rubberduck.UI @@ -23,13 +26,18 @@ public abstract class DockableToolwindowPresenter : IDockablePresenter, IDisposa private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly IWindow _window; private readonly UserControl _userControl; + private readonly WindowSettings _settings; //Storing this really doesn't matter - it's only checked on startup and never persisted. - protected DockableToolwindowPresenter(IVBE vbe, IAddIn addin, IDockableUserControl view) + protected DockableToolwindowPresenter(IVBE vbe, IAddIn addin, IDockableUserControl view, IConfigProvider settingsProvider) { _vbe = vbe; _addin = addin; Logger.Trace(string.Format("Initializing Dockable Panel ({0})", GetType().Name)); _userControl = view as UserControl; + if (settingsProvider != null) + { + _settings = settingsProvider.Create(); + } _window = CreateToolWindow(view); } @@ -69,7 +77,7 @@ private IWindow CreateToolWindow(IDockableUserControl control) EnsureMinimumWindowSize(toolWindow); - toolWindow.IsVisible = false; //hide it again + toolWindow.IsVisible = _settings != null && !_settings.IsWindowVisible(this); userControlHost.AddUserControl(control as UserControl, new IntPtr(_vbe.MainWindow.HWnd)); return toolWindow; diff --git a/RetailCoder.VBE/UI/IdentifierReferences/IdentifierReferencesListDockablePresenter.cs b/RetailCoder.VBE/UI/IdentifierReferences/IdentifierReferencesListDockablePresenter.cs index 7fa0b98150..946e401bd6 100644 --- a/RetailCoder.VBE/UI/IdentifierReferences/IdentifierReferencesListDockablePresenter.cs +++ b/RetailCoder.VBE/UI/IdentifierReferences/IdentifierReferencesListDockablePresenter.cs @@ -7,7 +7,7 @@ namespace Rubberduck.UI.IdentifierReferences public class IdentifierReferencesListDockablePresenter : DockableToolwindowPresenter { public IdentifierReferencesListDockablePresenter(IVBE vbe, IAddIn addin, SimpleListControl control, Declaration target) - : base(vbe, addin, control) + : base(vbe, addin, control, null) { BindTarget(target); } diff --git a/RetailCoder.VBE/UI/IdentifierReferences/ImplementationsListDockablePresenter.cs b/RetailCoder.VBE/UI/IdentifierReferences/ImplementationsListDockablePresenter.cs index 60f34ea64f..617d6206c8 100644 --- a/RetailCoder.VBE/UI/IdentifierReferences/ImplementationsListDockablePresenter.cs +++ b/RetailCoder.VBE/UI/IdentifierReferences/ImplementationsListDockablePresenter.cs @@ -9,7 +9,7 @@ namespace Rubberduck.UI.IdentifierReferences public class ImplementationsListDockablePresenter : DockableToolwindowPresenter { public ImplementationsListDockablePresenter(IVBE vbe, IAddIn addin, IDockableUserControl control, IEnumerable implementations) - : base(vbe, addin, control) + : base(vbe, addin, control, null) { BindTarget(implementations); } diff --git a/RetailCoder.VBE/UI/Inspections/CodeInspectionsDockablePresenter.cs b/RetailCoder.VBE/UI/Inspections/CodeInspectionsDockablePresenter.cs index d67207936e..2101db325b 100644 --- a/RetailCoder.VBE/UI/Inspections/CodeInspectionsDockablePresenter.cs +++ b/RetailCoder.VBE/UI/Inspections/CodeInspectionsDockablePresenter.cs @@ -1,11 +1,13 @@ -using Rubberduck.VBEditor.SafeComWrappers.Abstract; +using Rubberduck.Settings; +using Rubberduck.SettingsProvider; +using Rubberduck.VBEditor.SafeComWrappers.Abstract; namespace Rubberduck.UI.Inspections { public class CodeInspectionsDockablePresenter : DockableToolwindowPresenter { - public CodeInspectionsDockablePresenter(IVBE vbe, IAddIn addin, CodeInspectionsWindow window) - :base(vbe, addin, window) + public CodeInspectionsDockablePresenter(IVBE vbe, IAddIn addin, CodeInspectionsWindow window, IConfigProvider settings) + : base(vbe, addin, window, settings) { } } diff --git a/RetailCoder.VBE/UI/ParserErrors/ParserErrorsPresenter.cs b/RetailCoder.VBE/UI/ParserErrors/ParserErrorsPresenter.cs index 4d9584ee24..e7a7dce4ab 100644 --- a/RetailCoder.VBE/UI/ParserErrors/ParserErrorsPresenter.cs +++ b/RetailCoder.VBE/UI/ParserErrors/ParserErrorsPresenter.cs @@ -17,7 +17,7 @@ public interface IParserErrorsPresenter public class ParserErrorsPresenter : DockableToolwindowPresenter, IParserErrorsPresenter { public ParserErrorsPresenter(IVBE vbe, IAddIn addin) - : base(vbe, addin, new SimpleListControl(RubberduckUI.ParseErrors_Caption)) + : base(vbe, addin, new SimpleListControl(RubberduckUI.ParseErrors_Caption), null) { _source = new BindingList(); var control = UserControl as SimpleListControl; diff --git a/RetailCoder.VBE/UI/SourceControl/SourceControlDockablePresenter.cs b/RetailCoder.VBE/UI/SourceControl/SourceControlDockablePresenter.cs index eaa5fcb19e..f851c03569 100644 --- a/RetailCoder.VBE/UI/SourceControl/SourceControlDockablePresenter.cs +++ b/RetailCoder.VBE/UI/SourceControl/SourceControlDockablePresenter.cs @@ -1,4 +1,6 @@ using System.Diagnostics; +using Rubberduck.Settings; +using Rubberduck.SettingsProvider; using Rubberduck.VBEditor.SafeComWrappers.Abstract; namespace Rubberduck.UI.SourceControl @@ -8,8 +10,8 @@ namespace Rubberduck.UI.SourceControl /// public class SourceControlDockablePresenter : DockableToolwindowPresenter { - public SourceControlDockablePresenter(IVBE vbe, IAddIn addin, SourceControlPanel window) - : base(vbe, addin, window) + public SourceControlDockablePresenter(IVBE vbe, IAddIn addin, SourceControlPanel window, IConfigProvider settings) + : base(vbe, addin, window, settings) { } diff --git a/RetailCoder.VBE/UI/ToDoItems/ToDoExplorerDockablePresenter.cs b/RetailCoder.VBE/UI/ToDoItems/ToDoExplorerDockablePresenter.cs index 7f65b0b2e3..b23fdcb13c 100644 --- a/RetailCoder.VBE/UI/ToDoItems/ToDoExplorerDockablePresenter.cs +++ b/RetailCoder.VBE/UI/ToDoItems/ToDoExplorerDockablePresenter.cs @@ -1,4 +1,6 @@ -using Rubberduck.VBEditor.SafeComWrappers.Abstract; +using Rubberduck.Settings; +using Rubberduck.SettingsProvider; +using Rubberduck.VBEditor.SafeComWrappers.Abstract; namespace Rubberduck.UI.ToDoItems { @@ -7,8 +9,8 @@ namespace Rubberduck.UI.ToDoItems /// public class ToDoExplorerDockablePresenter : DockableToolwindowPresenter { - public ToDoExplorerDockablePresenter(IVBE vbe, IAddIn addin, ToDoExplorerWindow window) - : base(vbe, addin, window) + public ToDoExplorerDockablePresenter(IVBE vbe, IAddIn addin, ToDoExplorerWindow window, IConfigProvider settings) + : base(vbe, addin, window, settings) { } } diff --git a/RetailCoder.VBE/UI/UnitTesting/TestExplorerDockablePresenter.cs b/RetailCoder.VBE/UI/UnitTesting/TestExplorerDockablePresenter.cs index ddaa53ec33..a69eeef3d6 100644 --- a/RetailCoder.VBE/UI/UnitTesting/TestExplorerDockablePresenter.cs +++ b/RetailCoder.VBE/UI/UnitTesting/TestExplorerDockablePresenter.cs @@ -1,11 +1,13 @@ -using Rubberduck.VBEditor.SafeComWrappers.Abstract; +using Rubberduck.Settings; +using Rubberduck.SettingsProvider; +using Rubberduck.VBEditor.SafeComWrappers.Abstract; namespace Rubberduck.UI.UnitTesting { public class TestExplorerDockablePresenter : DockableToolwindowPresenter { - public TestExplorerDockablePresenter(IVBE vbe, IAddIn addin, TestExplorerWindow view) - : base(vbe, addin, view) + public TestExplorerDockablePresenter(IVBE vbe, IAddIn addin, TestExplorerWindow view, IConfigProvider settings) + : base(vbe, addin, view, settings) { } } From 3c36eda4e57db297eb2905b8fd6c0155d892de6d Mon Sep 17 00:00:00 2001 From: comintern Date: Sun, 25 Dec 2016 23:33:42 -0600 Subject: [PATCH 11/15] True = true now for visibility. (oops.) --- RetailCoder.VBE/UI/DockableToolwindowPresenter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs b/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs index 55f7e502a5..ba7f1f7b3c 100644 --- a/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs +++ b/RetailCoder.VBE/UI/DockableToolwindowPresenter.cs @@ -77,7 +77,7 @@ private IWindow CreateToolWindow(IDockableUserControl control) EnsureMinimumWindowSize(toolWindow); - toolWindow.IsVisible = _settings != null && !_settings.IsWindowVisible(this); + toolWindow.IsVisible = _settings != null && _settings.IsWindowVisible(this); userControlHost.AddUserControl(control as UserControl, new IntPtr(_vbe.MainWindow.HWnd)); return toolWindow; From 45552df7e881c5b56b535ce03c38a844294fb134 Mon Sep 17 00:00:00 2001 From: comintern Date: Mon, 26 Dec 2016 12:02:57 -0600 Subject: [PATCH 12/15] Add RD version to logging, log startup environment. Closes #2461 --- RetailCoder.VBE/App.cs | 21 ++++++++++++++++++++- RetailCoder.VBE/NLog.dll.nlog | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/RetailCoder.VBE/App.cs b/RetailCoder.VBE/App.cs index a3d71d721a..9a871d8f2c 100644 --- a/RetailCoder.VBE/App.cs +++ b/RetailCoder.VBE/App.cs @@ -1,6 +1,9 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; +using System.Reflection; using Infralution.Localization.Wpf; using NLog; +using NLog.Fluent; using Rubberduck.Common; using Rubberduck.Parsing; using Rubberduck.Parsing.Symbols; @@ -145,6 +148,7 @@ private void UpdateLoggingLevel() public void Startup() { EnsureLogFolderPathExists(); + LogRubberduckSart(); LoadConfig(); CheckForLegacyIndenterSettings(); _appMenus.Initialize(); @@ -223,6 +227,21 @@ private void CheckForLegacyIndenterSettings() } } + private void LogRubberduckSart() + { + var version = GetType().Assembly.GetName().Version.ToString(); + GlobalDiagnosticsContext.Set("RubberduckVersion", version); + var headers = new List + { + string.Format("Rubberduck version {0} loading:", version), + string.Format("\tOperating System: {0} {1}", Environment.OSVersion.VersionString, Environment.Is64BitOperatingSystem ? "x64" : "x86"), + string.Format("\tHost Product: {0} {1}", Application.ProductName, Environment.Is64BitProcess ? "x64" : "x86"), + string.Format("\tHost Version: {0}", Application.ProductVersion), + string.Format("\tHost Executable: {0}", Path.GetFileName(Application.ExecutablePath)), + }; + Logger.Log(LogLevel.Info, string.Join(Environment.NewLine, headers)); + } + private bool _disposed; public void Dispose() { diff --git a/RetailCoder.VBE/NLog.dll.nlog b/RetailCoder.VBE/NLog.dll.nlog index 1f855f436c..030cd2140c 100644 --- a/RetailCoder.VBE/NLog.dll.nlog +++ b/RetailCoder.VBE/NLog.dll.nlog @@ -7,7 +7,7 @@ xsi:type="File" name="file" fileName="${specialfolder:folder=ApplicationData}/Rubberduck/Logs/RubberduckLog.txt" - layout="${longdate};${uppercase:${level}};${logger};${message};${exception:format=tostring}" + layout="${longdate};${uppercase:${level}}-${gdc:item=RubberduckVersion};${logger};${message};${exception:format=tostring}" archiveFileName="${specialfolder:folder=ApplicationData}/Rubberduck/Logs/archives/RubberduckLog.{#}.txt" archiveAboveSize="5242880" archiveNumbering="Sequence" From f891c7160496b20759d61a825d5e0194f43b2092 Mon Sep 17 00:00:00 2001 From: comintern Date: Mon, 26 Dec 2016 20:55:11 -0600 Subject: [PATCH 13/15] Add reflection based host discovery (should be much more robust than caption scanning). --- .../Extensions/IDEExtensions.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Rubberduck.VBEEditor/Extensions/IDEExtensions.cs b/Rubberduck.VBEEditor/Extensions/IDEExtensions.cs index efc6215bba..3e9bbf0c2c 100644 --- a/Rubberduck.VBEEditor/Extensions/IDEExtensions.cs +++ b/Rubberduck.VBEEditor/Extensions/IDEExtensions.cs @@ -5,14 +5,43 @@ using Rubberduck.VBEditor.Application; using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.VBEditor.SafeComWrappers.MSForms; +using Exception = System.Exception; namespace Rubberduck.VBEditor.Extensions { public static class VBEExtensions { + private static readonly Dictionary HostAppMap = new Dictionary + { + {"EXCEL.EXE", typeof(ExcelApp)}, + {"WINWORD.EXE", typeof(WordApp)}, + {"MSACCESS.EXE", typeof(AccessApp)}, + {"POWERPNT.EXE", typeof(PowerPointApp)}, + {"OUTLOOK.EXE", typeof(OutlookApp)}, + {"WINPROJ.EXE", typeof(ProjectApp)}, + {"MSPUB.EXE", typeof(PublisherApp)}, + {"VISIO.EXE", typeof(VisioApp)}, + {"ACAD.EXE", typeof(AutoCADApp)}, + {"CORELDRW.EXE", typeof(CorelDRAWApp)}, + {"SLDWORKS.EXE", typeof(SolidWorksApp)}, + }; + /// Returns the type of Office Application that is hosting the VBE. public static IHostApplication HostApplication(this IVBE vbe) { + var host = Path.GetFileName(System.Windows.Forms.Application.ExecutablePath).ToUpperInvariant(); + //This needs the VBE as a ctor argument. + if (host.Equals("SLDWORKS.EXE")) + { + return new SolidWorksApp(vbe); + } + //The rest don't. + if (HostAppMap.ContainsKey(host)) + { + return (IHostApplication)Activator.CreateInstance(HostAppMap[host]); + } + + //Guessing the above will work like 99.9999% of the time for supported applications. var project = vbe.ActiveVBProject; { if (project.IsWrappingNullReference) @@ -114,6 +143,10 @@ public static IHostApplication HostApplication(this IVBE vbe) /// Returns whether the host supports unit tests. public static bool HostSupportsUnitTests(this IVBE vbe) { + var host = Path.GetFileName(System.Windows.Forms.Application.ExecutablePath).ToUpperInvariant(); + if (HostAppMap.ContainsKey(host)) return true; + //Guessing the above will work like 99.9999% of the time for supported applications. + var project = vbe.ActiveVBProject; { if (project.IsWrappingNullReference) From bcfbfb131a0c5f93f7ee8213eafcf2859ceace08 Mon Sep 17 00:00:00 2001 From: comintern Date: Mon, 26 Dec 2016 21:40:07 -0600 Subject: [PATCH 14/15] Add utility class for event hooks, stubbed code in VBE. --- Rubberduck.VBEEditor/Native/WinEvents.cs | 226 ++++++++++++++++++ .../Rubberduck.VBEditor.csproj | 1 + .../SafeComWrappers/VBA/VBE.cs | 31 ++- 3 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 Rubberduck.VBEEditor/Native/WinEvents.cs diff --git a/Rubberduck.VBEEditor/Native/WinEvents.cs b/Rubberduck.VBEEditor/Native/WinEvents.cs new file mode 100644 index 0000000000..67baf8a3b6 --- /dev/null +++ b/Rubberduck.VBEEditor/Native/WinEvents.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; + +namespace Rubberduck.VBEditor.Native +{ + public static class WinEvents + { + #region Debugging symbol lookups + public static readonly Dictionary EventNameLookup = new Dictionary + { + {0x1, "EVENT_SYSTEM_SOUND"}, + {0x2, "EVENT_SYSTEM_ALERT"}, + {0x3, "EVENT_SYSTEM_FOREGROUND"}, + {0x4, "EVENT_SYSTEM_MENUSTART"}, + {0x5, "EVENT_SYSTEM_MENUEND"}, + {0x6, "EVENT_SYSTEM_MENUPOPUPSTART"}, + {0x7, "EVENT_SYSTEM_MENUPOPUPEND"}, + {0x8, "EVENT_SYSTEM_CAPTURESTART"}, + {0x9, "EVENT_SYSTEM_CAPTUREEND"}, + {0xa, "EVENT_SYSTEM_MOVESIZESTART"}, + {0xb, "EVENT_SYSTEM_MOVESIZEEND"}, + {0xc, "EVENT_SYSTEM_CONTEXTHELPSTART"}, + {0xd, "EVENT_SYSTEM_CONTEXTHELPEND"}, + {0xe, "EVENT_SYSTEM_DRAGDROPSTART"}, + {0xf, "EVENT_SYSTEM_DRAGDROPEND"}, + {0x10, "EVENT_SYSTEM_DIALOGSTART"}, + {0x11, "EVENT_SYSTEM_DIALOGEND"}, + {0x12, "EVENT_SYSTEM_SCROLLINGSTART"}, + {0x13, "EVENT_SYSTEM_SCROLLINGEND"}, + {0x14, "EVENT_SYSTEM_SWITCHSTART"}, + {0x15, "EVENT_SYSTEM_SWITCHEND"}, + {0x16, "EVENT_SYSTEM_MINIMIZESTART"}, + {0x17, "EVENT_SYSTEM_MINIMIZEEND"}, + {0x8000, "EVENT_OBJECT_CREATE"}, + {0x8001, "EVENT_OBJECT_DESTROY"}, + {0x8002, "EVENT_OBJECT_SHOW"}, + {0x8003, "EVENT_OBJECT_HIDE"}, + {0x8004, "EVENT_OBJECT_REORDER"}, + {0x8005, "EVENT_OBJECT_FOCUS"}, + {0x8006, "EVENT_OBJECT_SELECTION"}, + {0x8007, "EVENT_OBJECT_SELECTIONADD"}, + {0x8008, "EVENT_OBJECT_SELECTIONREMOVE"}, + {0x8009, "EVENT_OBJECT_SELECTIONWITHIN"}, + {0x800A, "EVENT_OBJECT_STATECHANGE"}, + {0x800B, "EVENT_OBJECT_LOCATIONCHANGE"}, + {0x800C, "EVENT_OBJECT_NAMECHANGE"}, + {0x800D, "EVENT_OBJECT_DESCRIPTIONCHANGE"}, + {0x800E, "EVENT_OBJECT_VALUECHANGE"}, + {0x800F, "EVENT_OBJECT_PARENTCHANGE"}, + {0x8010, "EVENT_OBJECT_HELPCHANGE"}, + {0x8011, "EVENT_OBJECT_DEFACTIONCHANGE"}, + {0x8012, "EVENT_OBJECT_ACCELERATORCHANGE"}, + }; + + public static readonly Dictionary ObjectIdNameLookup = new Dictionary + { + { 0x00000000, "OBJID_WINDOW" }, + { 0xFFFFFFFF, "OBJID_SYSMENU" }, + { 0xFFFFFFFE, "OBJID_TITLEBAR" }, + { 0xFFFFFFFD, "OBJID_MENU" }, + { 0xFFFFFFFC, "OBJID_CLIENT" }, + { 0xFFFFFFFB, "OBJID_VSCROLL" }, + { 0xFFFFFFFA, "OBJID_HSCROLL" }, + { 0xFFFFFFF9, "OBJID_SIZEGRIP" }, + { 0xFFFFFFF8, "OBJID_CARET" }, + { 0xFFFFFFF7, "OBJID_CURSOR" }, + { 0xFFFFFFF6, "OBJID_ALERT" }, + { 0xFFFFFFF5, "OBJID_SOUND" }, + { 0xFFFFFFF4, "OBJID_QUERYCLASSNAMEIDX" }, + { 0xFFFFFFF0, "OBJID_NATIVEOM" }, + }; + #endregion + + #region System enumerations + // ReSharper disable InconsistentNaming + //See https://msdn.microsoft.com/en-us/library/windows/desktop/dd318066(v=vs.85).aspx + public enum EventConstant : uint + { + EVENT_MIN = 0x1, + EVENT_SYSTEM_SOUND = 0x1, + EVENT_SYSTEM_ALERT = 0x2, + EVENT_SYSTEM_FOREGROUND = 0x3, + EVENT_SYSTEM_MENUSTART = 0x4, + EVENT_SYSTEM_MENUEND = 0x5, + EVENT_SYSTEM_MENUPOPUPSTART = 0x6, + EVENT_SYSTEM_MENUPOPUPEND = 0x7, + EVENT_SYSTEM_CAPTURESTART = 0x8, + EVENT_SYSTEM_CAPTUREEND = 0x9, + EVENT_SYSTEM_MOVESIZESTART = 0xa, + EVENT_SYSTEM_MOVESIZEEND = 0xb, + EVENT_SYSTEM_CONTEXTHELPSTART = 0xc, + EVENT_SYSTEM_CONTEXTHELPEND = 0xd, + EVENT_SYSTEM_DRAGDROPSTART = 0xe, + EVENT_SYSTEM_DRAGDROPEND = 0xf, + EVENT_SYSTEM_DIALOGSTART = 0x10, + EVENT_SYSTEM_DIALOGEND = 0x11, + EVENT_SYSTEM_SCROLLINGSTART = 0x12, + EVENT_SYSTEM_SCROLLINGEND = 0x13, + EVENT_SYSTEM_SWITCHSTART = 0x14, + EVENT_SYSTEM_SWITCHEND = 0x15, + EVENT_SYSTEM_MINIMIZESTART = 0x16, + EVENT_SYSTEM_MINIMIZEEND = 0x17, + EVENT_OEM_DEFINED_START = 0x0101, + EVENT_OEM_DEFINED_END = 0x01FF, + EVENT_AIA_START = 0xA000, + EVENT_AIA_END = 0xAFFF, + EVENT_UIA_EVENTID_START = 0x4E00, + EVENT_UIA_EVENTID_END = 0x4EFF, + EVENT_UIA_PROPID_START = 0x7500, + EVENT_UIA_PROPID_END = 0x75FF, + EVENT_OBJECT_START = 0x8000, + EVENT_OBJECT_CREATE = 0x8000, + EVENT_OBJECT_DESTROY = 0x8001, + EVENT_OBJECT_SHOW = 0x8002, + EVENT_OBJECT_HIDE = 0x8003, + EVENT_OBJECT_REORDER = 0x8004, + EVENT_OBJECT_FOCUS = 0x8005, + EVENT_OBJECT_SELECTION = 0x8006, + EVENT_OBJECT_SELECTIONADD = 0x8007, + EVENT_OBJECT_SELECTIONREMOVE = 0x8008, + EVENT_OBJECT_SELECTIONWITHIN = 0x8009, + EVENT_OBJECT_STATECHANGE = 0x800A, + EVENT_OBJECT_LOCATIONCHANGE = 0x800B, + EVENT_OBJECT_NAMECHANGE = 0x800C, + EVENT_OBJECT_DESCRIPTIONCHANGE = 0x800D, + EVENT_OBJECT_VALUECHANGE = 0x800E, + EVENT_OBJECT_PARENTCHANGE = 0x800F, + EVENT_OBJECT_HELPCHANGE = 0x8010, + EVENT_OBJECT_DEFACTIONCHANGE = 0x8011, + EVENT_OBJECT_ACCELERATORCHANGE = 0x8012, + EVENT_OBJECT_INVOKED = 0x8013, + EVENT_OBJECT_CONTENTSCROLLED = 0x8015, + EVENT_SYSTEM_ARRANGMENTPREVIEW = 0x8016, + EVENT_OBJECT_LIVEREGIONCHANGED = 0x8019, + EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED = 0x8020, + EVENT_OBJECT_DRAGSTART = 0x8021, + EVENT_OBJECT_DRAGCANCEL = 0x8022, + EVENT_OBJECT_DRAGCOMPLETE = 0x8023, + EVENT_OBJECT_DRAGENTER = 0x8024, + EVENT_OBJECT_DRAGLEAVE = 0x8025, + EVENT_OBJECT_DRAGDROPPED = 0x8026, + EVENT_OBJECT_IME_SHOW = 0x8027, + EVENT_OBJECT_IME_HIDE = 0x8028, + EVENT_OBJECT_IME_CHANGE = 0x8029, + EVENT_OBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED = 0x8030, + EVENT_OBJECT_TEXTSELECTIONCHANGED = 0x8014, + EVENT_OBJECT_END = 0x80FF, + EVENT_MAX = 0x7FFFFFFF + } + // possible marshaling unmanaged type conflict/problem between 32/64 bit + + public enum ObjId : uint + { + OBJID_WINDOW = 0x00000000, + OBJID_SYSMENU = 0xFFFFFFFF, + OBJID_TITLEBAR = 0xFFFFFFFE, + OBJID_MENU = 0xFFFFFFFD, + OBJID_CLIENT = 0xFFFFFFFC, + OBJID_VSCROLL = 0xFFFFFFFB, + OBJID_HSCROLL = 0xFFFFFFFA, + OBJID_SIZEGRIP = 0xFFFFFFF9, + OBJID_CARET = 0xFFFFFFF8, + OBJID_CURSOR = 0xFFFFFFF7, + OBJID_ALERT = 0xFFFFFFF6, + OBJID_SOUND = 0xFFFFFFF5, + OBJID_QUERYCLASSNAMEIDX = 0xFFFFFFF4, + OBJID_NATIVEOM = 0xFFFFFFF0 + } + + public enum WinEventFlags : uint + { + WINEVENT_OUTOFCONTEXT = 0x0000, + WINEVENT_SKIPOWNTHREAD = 0x0001, + WINEVENT_SKIPOWNPROCESS = 0x0002, + WINEVENT_INCONTEXT = 0x0004 + } + // ReSharper restore InconsistentNaming + #endregion + + #region API declarations + + public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, uint idObject, uint idChild, uint dwEventThread, uint dwmsEventTime); + + [DllImport("user32.dll")] + public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, IntPtr lpfnWinEventProc, uint idProcess, + uint idThread, uint dwFlags); + + [DllImport("user32.dll")] + public static extern int GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("user32.dll")] + public static extern bool UnhookWinEvent(IntPtr hWinEventHook); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + #endregion + + #region Extension methods + + public static string ToObjectIdString(this uint objectId) + { + return ObjectIdNameLookup.ContainsKey(objectId) ? ObjectIdNameLookup[objectId] : objectId.ToString(CultureInfo.InvariantCulture); + } + + public static string ToEventIdString(this uint eventId) + { + return EventNameLookup.ContainsKey(eventId) ? EventNameLookup[eventId] : eventId.ToString(CultureInfo.InvariantCulture); + } + + public static string ToClassName(this IntPtr hwnd) + { + var buffer = new StringBuilder(256); + if (hwnd != IntPtr.Zero) + { + return GetClassName(hwnd, buffer, buffer.Capacity) != 0 ? buffer.ToString() : string.Empty; + } + return string.Empty; + } + + #endregion + } +} diff --git a/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj b/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj index fb24c607c5..c868389a43 100644 --- a/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj +++ b/Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj @@ -127,6 +127,7 @@ + diff --git a/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs b/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs index 0d598b96c8..110d6890f3 100644 --- a/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs +++ b/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs @@ -1,18 +1,33 @@ using System; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; +using Rubberduck.VBEditor.Native; using Rubberduck.VBEditor.SafeComWrappers.Abstract; using Rubberduck.VBEditor.SafeComWrappers.Office.Core; using Rubberduck.VBEditor.SafeComWrappers.Office.Core.Abstract; +using IAddIns = Rubberduck.VBEditor.SafeComWrappers.Abstract.IAddIns; +using IWindow = Rubberduck.VBEditor.SafeComWrappers.Abstract.IWindow; +using IWindows = Rubberduck.VBEditor.SafeComWrappers.Abstract.IWindows; using VB = Microsoft.Vbe.Interop; namespace Rubberduck.VBEditor.SafeComWrappers.VBA { public class VBE : SafeComWrapper, IVBE { + // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable + //private readonly WinEvents.WinEventDelegate _events; + //private static IntPtr _hook; + public VBE(VB.VBE target) :base(target) { + //_events = WinEventProc; + //uint proc; + //WinEvents.GetWindowThreadProcessId(new IntPtr(target.MainWindow.HWnd), out proc); + //_hook = WinEvents.SetWinEventHook((uint) WinEvents.EventConstant.EVENT_MIN, + // (uint) WinEvents.EventConstant.EVENT_MAX, IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(_events), proc, 0, + // (uint) WinEvents.WinEventFlags.WINEVENT_OUTOFCONTEXT); } public string Version @@ -86,11 +101,12 @@ public IWindows Windows public override void Release(bool final = false) { + //WinEvents.UnhookWinEvent(_hook); if (!IsWrappingNullReference) { VBProjects.Release(); CodePanes.Release(); - //CommandBars.Release(); + CommandBars.Release(); Windows.Release(); AddIns.Release(); base.Release(final); @@ -130,5 +146,18 @@ public static void SetSelection(IVBProject vbProject, Selection selection, strin var pane = module.CodePane; pane.Selection = selection; } + + //private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, uint idObject, uint idChild, uint dwEventThread, uint dwmsEventTime) + //{ + ////I don't care about the mouse pointer right now. + //if (idObject == (uint)WinEvents.ObjId.OBJID_CURSOR) return; + + //Debug.WriteLine("Intercepted event {0} for hwnd {1:X8} ({4}), object {2}, child {3}.", + // eventType.ToEventIdString(), + // hwnd.ToInt32(), + // idObject.ToObjectIdString(), + // idChild, + // hwnd.ToClassName()); + //} } } From 4f43877476d1efcb513af7340d9f66615b5e6ef9 Mon Sep 17 00:00:00 2001 From: comintern Date: Tue, 27 Dec 2016 17:23:00 -0600 Subject: [PATCH 15/15] Disable CommandBars.Release() again to synch. --- Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs b/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs index 110d6890f3..32cc72e61e 100644 --- a/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs +++ b/Rubberduck.VBEEditor/SafeComWrappers/VBA/VBE.cs @@ -106,7 +106,7 @@ public override void Release(bool final = false) { VBProjects.Release(); CodePanes.Release(); - CommandBars.Release(); + //CommandBars.Release(); Windows.Release(); AddIns.Release(); base.Release(final);