Skip to content

Commit 6521b35

Browse files
committed
Merge branch 'next' into ResolveExpressionInNext
2 parents 4f034c9 + 5b12624 commit 6521b35

File tree

93 files changed

+1796
-5457
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+1796
-5457
lines changed

Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitDefaultMemberAccessInspection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ namespace Rubberduck.Inspections.Concrete
4545
/// arg.ConnectionString = bar
4646
/// End Sub
4747
/// ]]>
48+
/// </example>
4849
public sealed class ImplicitDefaultMemberAccessInspection : IdentifierReferenceInspectionBase
4950
{
5051
public ImplicitDefaultMemberAccessInspection(RubberduckParserState state)

Rubberduck.CodeAnalysis/Inspections/Concrete/ImplicitUnboundDefaultMemberAccessInspection.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace Rubberduck.Inspections.Concrete
2424
/// bar = arg
2525
/// End Sub
2626
/// ]]>
27+
/// </example>
2728
/// <example hasresult="true">
2829
/// <![CDATA[
2930
/// Public Sub DoSomething(ByVal arg As Object)
@@ -39,6 +40,7 @@ namespace Rubberduck.Inspections.Concrete
3940
/// bar = arg.SomeValueReturningMember
4041
/// End Sub
4142
/// ]]>
43+
/// </example>
4244
/// <example hasresult="false">
4345
/// <![CDATA[
4446
/// Public Sub DoSomething(ByVal arg As Object)

Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml

Lines changed: 190 additions & 187 deletions
Large diffs are not rendered by default.

Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,10 @@ public InspectionResultsControl()
1111
{
1212
InitializeComponent();
1313
}
14+
15+
private void InspectionResultsGrid_RequestBringIntoView(object sender, System.Windows.RequestBringIntoViewEventArgs e)
16+
{
17+
e.Handled = true;
18+
}
1419
}
1520
}
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.Main/ComClientLibrary/UI/DockableWindowHost.cs

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Diagnostics.CodeAnalysis;
55
using System.Drawing;
66
using System.Runtime.InteropServices;
7+
using System.Text;
78
using System.Windows.Forms;
89
using Rubberduck.VBEditor;
910
using Rubberduck.VBEditor.WindowsApi;
@@ -145,7 +146,7 @@ private void RemoveChildControlsFromExposedControl()
145146
public int /* IOleObject:: */ Close([In] uint dwSaveOption)
146147
{
147148
_logger.Log(LogLevel.Trace, "IOleObject::Close() called");
148-
int hr = _userControl.IOleObject.Close(dwSaveOption);
149+
var hr = _userControl.IOleObject.Close(dwSaveOption);
149150

150151
// IOleObject::SetClientSite is typically called with pClientSite = null just before calling IOleObject::Close()
151152
// If it didn't, we release all host COM objects here instead,
@@ -386,7 +387,7 @@ public void AddUserControl(UserControl control, IntPtr vbeHwnd)
386387

387388
control.Dock = DockStyle.Fill;
388389
_userControl.Controls.Add(control);
389-
390+
390391
AdjustSize();
391392
}
392393

@@ -429,14 +430,79 @@ private void AdjustSize()
429430
}
430431
}
431432

433+
private static void ToggleDockable(IntPtr hWndVBE)
434+
{
435+
NativeMethods.SendMessage(hWndVBE, 0x1044, (IntPtr)0xB5, IntPtr.Zero);
436+
}
437+
432438
[ComVisible(false)]
433439
public class ParentWindow : SubclassingWindow
434440
{
441+
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
442+
443+
private const int MF_BYPOSITION = 0x400;
444+
435445
public event SubClassingWindowEventHandler CallBackEvent;
436446
public delegate void SubClassingWindowEventHandler(object sender, SubClassingWindowEventArgs e);
437447

438448
private readonly IntPtr _vbeHwnd;
439449

450+
private IntPtr _containerHwnd;
451+
private ToolWindowState _windowState;
452+
private IntPtr _menuHandle;
453+
454+
private enum ToolWindowState
455+
{
456+
Unknown,
457+
Docked,
458+
Floating,
459+
Undockable
460+
}
461+
462+
private ToolWindowState GetWindowState(IntPtr containerHwnd)
463+
{
464+
var className = new StringBuilder(255);
465+
if (NativeMethods.GetClassName(containerHwnd, className, className.Capacity) > 0)
466+
{
467+
switch (className.ToString())
468+
{
469+
case "wndclass_desked_gsk":
470+
return ToolWindowState.Docked;
471+
case "VBFloatingPalette":
472+
return ToolWindowState.Floating;
473+
case "DockingView":
474+
return ToolWindowState.Undockable;
475+
}
476+
}
477+
478+
return ToolWindowState.Unknown;
479+
}
480+
481+
private void DisplayUndockableContextMenu(IntPtr handle, IntPtr lParam)
482+
{
483+
if (_menuHandle == IntPtr.Zero)
484+
{
485+
_menuHandle = NativeMethods.CreatePopupMenu();
486+
487+
if (_menuHandle == IntPtr.Zero)
488+
{
489+
_logger.Warn("Cannot create menu handle");
490+
return;
491+
}
492+
493+
if (!NativeMethods.InsertMenu(_menuHandle, 0, MF_BYPOSITION, (UIntPtr)WM.RUBBERDUCK_UNDOCKABLE_CONTEXT_MENU, "Dockable" + char.MinValue))
494+
{
495+
_logger.Warn("Failed to insert a menu item for dockable command");
496+
}
497+
}
498+
499+
var param = new LParam {Value = (uint)lParam};
500+
if (!NativeMethods.TrackPopupMenuEx(_menuHandle, 0x0, param.LowWord, param.HighWord, handle, IntPtr.Zero ))
501+
{
502+
_logger.Warn("Failed to set the context menu for undockable tool windows");
503+
};
504+
}
505+
440506
private void OnCallBackEvent(SubClassingWindowEventArgs e)
441507
{
442508
CallBackEvent?.Invoke(this, e);
@@ -452,6 +518,28 @@ public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr
452518
{
453519
switch ((uint)msg)
454520
{
521+
case (uint)WM.WINDOWPOSCHANGED:
522+
var containerHwnd = GetParent(hWnd);
523+
if (containerHwnd != _containerHwnd)
524+
{
525+
_containerHwnd = containerHwnd;
526+
_windowState = GetWindowState(_containerHwnd);
527+
}
528+
break;
529+
case (uint)WM.CONTEXTMENU:
530+
if (_windowState == ToolWindowState.Undockable)
531+
{
532+
DisplayUndockableContextMenu(hWnd, lParam);
533+
}
534+
break;
535+
case (uint)WM.COMMAND:
536+
switch (wParam.ToInt32())
537+
{
538+
case (int)WM.RUBBERDUCK_UNDOCKABLE_CONTEXT_MENU:
539+
ToggleDockable(_vbeHwnd);
540+
break;
541+
}
542+
break;
455543
case (uint)WM.SIZE:
456544
var args = new SubClassingWindowEventArgs(lParam);
457545
if (!_closing) OnCallBackEvent(args);
@@ -462,6 +550,15 @@ public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr
462550
case (uint)WM.KILLFOCUS:
463551
if (!_closing) User32.SendMessage(_vbeHwnd, WM.RUBBERDUCK_CHILD_FOCUS, Hwnd, IntPtr.Zero);
464552
break;
553+
case (uint)WM.DESTROY:
554+
if (_menuHandle != IntPtr.Zero)
555+
{
556+
if (!NativeMethods.DestroyMenu(_menuHandle))
557+
{
558+
_logger.Fatal($"Failed to destroy the menu handle {_menuHandle}");
559+
}
560+
}
561+
break;
465562
}
466563
return base.SubClassProc(hWnd, msg, wParam, lParam, uIdSubclass, dwRefData);
467564
}

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 & 5 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,9 +45,8 @@ 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 && varDesc.desc.lpvarValue != IntPtr.Zero)
48+
// VBA constants are "static".... go figure.
49+
if (varDesc.IsValidVBAConstant())
5150
{
5251
_mapper.Add(_mapper.Count, i);
5352
}

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>

Rubberduck.VBEEditor/WindowsApi/NativeMethods.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@ namespace Rubberduck.VBEditor.WindowsApi
1313
/// </remarks>
1414
public static class NativeMethods
1515
{
16+
[DllImport("user32.dll")]
17+
public static extern IntPtr CreatePopupMenu();
18+
19+
[DllImport("user32.dll")]
20+
public static extern bool TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm);
21+
22+
[DllImport("user32.dll", EntryPoint = "InsertMenuW", SetLastError = true, CharSet = CharSet.Unicode)]
23+
public static extern bool InsertMenu(IntPtr hMenu, uint wPosition, uint wFlags, UIntPtr wIDNewItem, [MarshalAs(UnmanagedType.LPWStr)]string lpNewItem);
24+
25+
[DllImport("user32.dll")]
26+
public static extern bool DestroyMenu(IntPtr hMenu);
27+
1628
/// <summary> Sends a message to the OS. </summary>
1729
///
1830
/// <param name="hWnd"> The window handle. </param>
@@ -68,6 +80,14 @@ public enum PeekMessageRemoval : uint
6880
[DllImport("user32", ExactSpelling = true, CharSet = CharSet.Unicode)]
6981
public static extern int EnumChildWindows(IntPtr parentWindowHandle, EnumChildWindowsDelegate lpEnumFunction, IntPtr lParam);
7082

83+
/// <summary> Retrieves the name of the class to which the specified window belongs. </summary>
84+
/// <param name="hWnd">A handle to the window and, indirectly, the class to which the window belongs.</param>
85+
/// <param name="lpClassName">The class name string.</param>
86+
/// <param name="nMaxCount">The length of the <see cref="lpClassName" /> buffer, in characters. The buffer must be large enough to include the terminating null character; otherwise, the class name string is truncated to <see cref="nMaxCount" /> characters.</param>
87+
/// <returns>If the function succeeds, the return value is the number of characters copied to the buffer, not including the terminating null character. If the function fails, the return value is zero.To get extended error information, call GetLastError.</returns>
88+
[DllImport("user32.dll", EntryPoint = "GetClassNameW", SetLastError = true, CharSet = CharSet.Unicode)]
89+
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
90+
7191
/// <summary> Gets window text. </summary>
7292
///
7393
/// <param name="hWnd"> The window handle. </param>

0 commit comments

Comments
 (0)