Rubberduck: No Way to Dock Tool Windows after Uncheck Dockable (even after Restart)

Created on 3 Aug 2017  路  11Comments  路  Source: rubberduck-vba/Rubberduck

The Dockable and Hide context menu items are missing from context menu shown when right-clicking on toolbar Ruberduck tool windows.
Because of this, if user ever right-clicks on the docked tool window's docked mode titlebar and unchecks > Dockable, then the tool window gets moved to a document tab, and, unlike all built-in or other add-in tool windows, there is no way to change it back to Dockable ever, even after unloading, restarting Access, clicking menubar entry for the window, etc.

I have to go to registry and delete workspace layout in order to get it back at that point.

ruberduck tool window cant be docked ever again

bug difficulty-03-duck enhancement hacktoberfest up-for-grabs user-interface

Most helpful comment

Woohoo! Thank You All, I've finally got it!

VBA code for 64 bit Office (for x32 you have to replace LongPtr by Long and remove PtrSafe):

Option Explicit

Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
                   ByVal lpClassName As String _
                 , ByVal lpWindowName As String _
) As LongPtr

Public Declare PtrSafe Function SendMessageA Lib "user32" ( _
                          ByVal hWnd As LongPtr _
                        , ByVal wMsg As Long _
                        , ByVal wParam As LongPtr _
                        , lParam As Any _
) As LongPtr

Sub MakeDockableAgain()

   Dim hWndVBE As LongPtr
   Dim t As Single
   Const timeout = 3

   hWndVBE = FindWindow("wndclass_desked_gsk", vbNullString)

   If hWndVBE = 0 Then
      MsgBox "Sorry, VBE not found", vbCritical
   ElseIf vbYes = MsgBox("After you click YES you have " & timeout & " seconds" _
                      & " to click on pane you want to switch Dockable... Are you ready?" _
                      , vbYesNo) Then
      t = Timer
      Do
         DoEvents
      Loop While Timer < t + timeout

      SendMessageA hWndVBE, &H1044, &HB5, 0
      MsgBox "OK, check it! :)", vbInformation
   Else
      MsgBox "Operation is cancelled.", vbExclamation
   End If

End Sub

All 11 comments

I did this by mistake today, but knew to kill the host process before the changes were persisted to the registry.

Shouldn't we just add this to the bottom of the context menu, as PE does:

image

Also, wondering why this is difficulty-03-duck, is there significant complexity known of?

I鈥檓 assuming the difficulty label is because it鈥檚 related to the dockable windows. Dockable windows are notoriously picky, difficult, and require a fair bit of knowledge around the WinAPI. Now, not all tool window work is that difficult and this may not be, but in general working with the dockable windows isn鈥檛 easy.

Just to note that though there is no command to Dock in VBIDE, it can be done by doing VBE.Windows(?).LinkedWindows.Add; so hopefully no win32 API is needed for that. Whether there's going to be any other fallback..... #NeedsTesting

Yes, LinkedWindows.Add() should work, as detailed here.

Thanks for pointing that out, @bclothier.

OK, so I've been looking into this.

My hope was that we could useLinkedWindows to add "Dockable" (and also "Hide") to the end of each context menu, for a 1-1 match with the built-in toolwindow context menu.

This fails for 2 reasons:

  1. The LinkedWindows collection doesn't seem to recognise our toolwindows. For example, directly after invoking _vbe.MainWindow.LinkedWindows.Add(codeExplorerToolWindow), the _vbe.MainWindow.LinkedWindows collection will not contain codeExplorerToolWindow. This means we can't use it to determine current dock state.

  2. LinkedWindows is not a direct analogue for the dockability status of a toolwindow. Rather, it records which windows are currently participating in a docking relationship. So removing a LinkedWindow is equivalent to dragging a docked window into free space - it's floating but still dockable. This is not the same as unchecking Dockable, which sets it floating and prevents it from docking again.

The upshot is that, whilst we can use LinkedWindows to recover from an undockable state, it's not the ideal solution. However, there must be a window message that handles the actual Dockable action and IsDocked queries, if we can find those we can send it directly to our windows.

I switched on logging of messages and events and then tried Project Explorer unchecking and rechecking the Dockable context menu item. Log attached:
PE dock log.txt

Of note are the Unknown windows messages which fall in the WM_USER range:

MSG: 1044 (Unknown), Hwnd 3C09D8 (wndclass_desked_gsk), wParam 00B5, lParam 0000
(Note, per the wiki, this corresponds to the Dockable action.)

MSG: 1043 (Unknown), Hwnd 3C09D8 (wndclass_desked_gsk), wParam 0000, lParam 0000
No info.

Woohoo! Thank You All, I've finally got it!

VBA code for 64 bit Office (for x32 you have to replace LongPtr by Long and remove PtrSafe):

Option Explicit

Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
                   ByVal lpClassName As String _
                 , ByVal lpWindowName As String _
) As LongPtr

Public Declare PtrSafe Function SendMessageA Lib "user32" ( _
                          ByVal hWnd As LongPtr _
                        , ByVal wMsg As Long _
                        , ByVal wParam As LongPtr _
                        , lParam As Any _
) As LongPtr

Sub MakeDockableAgain()

   Dim hWndVBE As LongPtr
   Dim t As Single
   Const timeout = 3

   hWndVBE = FindWindow("wndclass_desked_gsk", vbNullString)

   If hWndVBE = 0 Then
      MsgBox "Sorry, VBE not found", vbCritical
   ElseIf vbYes = MsgBox("After you click YES you have " & timeout & " seconds" _
                      & " to click on pane you want to switch Dockable... Are you ready?" _
                      , vbYesNo) Then
      t = Timer
      Do
         DoEvents
      Loop While Timer < t + timeout

      SendMessageA hWndVBE, &H1044, &HB5, 0
      MsgBox "OK, check it! :)", vbInformation
   Else
      MsgBox "Operation is cancelled.", vbExclamation
   End If

End Sub

Nice work @ATolokolnikov !

I thought I posted a solution to this, that doesn't involve sending messages or asking the user to click the toolwindow. It can be achieved by executing a CommandBarToggleButton and the ToolWindow state can be read using the CommandBarControl.

There was a small issue around the selection in the Toolwindow, where the CommandBarControl can't be relied upon if certain WPF controls are selected, but presumably RD can determine the parent Toolwindow for a given WPF selection?

Nonetheless, I'll retest the solution tomorrow, and post the Control's ID. It may be that a combination of solutions is viable here, particularly if the state can be read using Send message, as the CommandBarControl approach requires that the ToolWindow be active to read the state.

@ATolokolnikov - Thanks, that worked for me. Had to make the modifications for 32-bit, but still worked perfectly. :+1:

Was this page helpful?
0 / 5 - 0 ratings