Skip to content

Commit 8b0ee61

Browse files
committed
Improve the handling around the crashing in both VBA and OLE sides by using an extension method
1 parent 20f0e15 commit 8b0ee61

File tree

4 files changed

+44
-23
lines changed

4 files changed

+44
-23
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Runtime.InteropServices.ComTypes;
2+
3+
namespace Rubberduck.JunkDrawer.Hacks
4+
{
5+
public static class VarDescExtensions
6+
{
7+
/// <remarks>
8+
/// Use only with VBA-supplied <see cref="ITypeInfo"/> which may return a <see cref="VARDESC"/> that do not conform to
9+
/// the MS-OAUT in describing the constants. See section 2.2.43 at: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/ae7791d2-4399-4dff-b7c6-b0d4f3dce982
10+
///
11+
/// To further complicate the situation, on 64-bit platform, the <see cref="VARDESC.DESCUNION.oInst"/> is a 32-bit integer whereas
12+
/// the <see cref="VARDESC.DESCUNION.lpvarValue"/> is a pointer. On 32-bit platform, the sizes of 2 members are exactly same so no
13+
/// problem. But on 64-bit platform, setting the <c>oInst</c>to 0 does not necessarily zero-initialize the entire region. Thus, the
14+
/// upper 32-bit part of the <c>lpvarValue</c> can contain garbage which will confound the simple null pointer check. Thus to guard
15+
/// against this, we will check the <c>oInst</c> value to see if it's zero.
16+
///
17+
/// There is a small but non-zero chance that there might be a valid pointer that happens to be only in high half of the address...
18+
/// in that case, it'll be wrong but since VBA is always writing <see cref="VARKIND.VAR_STATIC"/> to the <see cref="VARDESC.varkind"/>
19+
/// field, we're kind of stuck...
20+
/// </remarks>
21+
/// <param name="varDesc">The <see cref="VARDESC"/> from a VBA <see cref="ITypeInfo"/></param>
22+
/// <returns>True if this is most likely a constant. False when it's definitely not.</returns>
23+
public static bool IsValidVBAConstant(this VARDESC varDesc)
24+
{
25+
return varDesc.varkind == VARKIND.VAR_STATIC && varDesc.desc.oInst != 0;
26+
}
27+
}
28+
}

Rubberduck.Parsing/ComReflection/ComModule.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR;
1212
using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC;
1313
using CALLCONV = System.Runtime.InteropServices.ComTypes.CALLCONV;
14+
using Rubberduck.VBEditor.ComManagement.TypeLibs.Abstract;
15+
using Rubberduck.JunkDrawer.Hacks;
1416

1517
namespace Rubberduck.Parsing.ComReflection
1618
{
@@ -58,7 +60,15 @@ private void GetComFields(ITypeInfo info, TYPEATTR attrib)
5860
info.GetNames(desc.memid, names, names.Length, out int length);
5961
Debug.Assert(length == 1);
6062

61-
var type = desc.desc.lpvarValue == IntPtr.Zero ? DeclarationType.Variable : DeclarationType.Constant;
63+
DeclarationType type;
64+
if(info is ITypeInfoWrapper wrapped && wrapped.HasVBEExtensions)
65+
{
66+
type = desc.IsValidVBAConstant() ? DeclarationType.Constant : DeclarationType.Variable;
67+
}
68+
else
69+
{
70+
type = desc.varkind == VARKIND.VAR_CONST ? DeclarationType.Constant : DeclarationType.Variable;
71+
}
6272

6373
_fields.Add(new ComField(this, info, names[0], desc, index, type));
6474
}

Rubberduck.VBEEditor/ComManagement/TypeLibs/TypeInfoConstantsCollection.cs

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Runtime.InteropServices.ComTypes;
3+
using Rubberduck.JunkDrawer.Hacks;
44
using Rubberduck.VBEditor.ComManagement.TypeLibs.Abstract;
55
using Rubberduck.VBEditor.ComManagement.TypeLibs.Unmanaged;
66
using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR;
@@ -45,33 +45,15 @@ public TypeInfoConstantsCollection(ITypeInfo parent, TYPEATTR attributes) :
4545
parent.GetVarDesc(i, out var ppVarDesc);
4646
var varDesc = StructHelper.ReadStructureUnsafe<VARDESC>(ppVarDesc);
4747

48-
// VBA constants are "static".... go figure. We can still infer it is a
49-
// constant rather than a field by checking the lpvarValue
50-
if (varDesc.varkind == VARKIND.VAR_STATIC && IsValidPointer(varDesc.desc.lpvarValue))
48+
// VBA constants are "static".... go figure.
49+
if (varDesc.IsValidVBAConstant())
5150
{
5251
_mapper.Add(_mapper.Count, i);
5352
}
5453
parent.ReleaseVarDesc(ppVarDesc);
5554
}
5655
}
5756

58-
/// <remarks>
59-
/// On 64-bit platform, VBA seems to like putting random values in the high 32-bit part of the lpvarValue. Sometimes it does give out
60-
/// null pointer but when ti does has no apparent reason or rhyme. Thus, to help avoid erroneous resolution of lpvarValue for vars that
61-
/// shouldn't be resolved, we will need to mask the high word and assume that any valid constants will have a address that is also in the low
62-
/// 32-bit part of the lpvarValue. That does not seem to exist on 32-bit VBA. Whee!
63-
/// Ref: https://github.com/rubberduck-vba/Rubberduck/issues/5241
64-
/// </remarks>
65-
private bool IsValidPointer(IntPtr ptr)
66-
{
67-
if(IntPtr.Size == 8)
68-
{
69-
return ((ulong)ptr & 0x00000000ffffffff) != 0;
70-
}
71-
72-
return ptr != IntPtr.Zero;
73-
}
74-
7557
public override int Count => _mapper.Count;
7658

7759
public override ITypeInfoVariable GetItemByIndex(int index) =>

Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<Reference Include="System.Windows.Forms" />
2323
</ItemGroup>
2424
<ItemGroup>
25+
<ProjectReference Include="..\Rubberduck.JunkDrawer\Rubberduck.JunkDrawer.csproj" />
2526
<ProjectReference Include="..\Rubberduck.Resources\Rubberduck.Resources.csproj" />
2627
</ItemGroup>
2728
<ItemGroup>

0 commit comments

Comments
 (0)