Closed wqweto closed 6 years ago
Neither of us have an old copy of Visual Studio that supports VB6, so we've not tested it. The add-in is designed with Office VBA in mind. I know the unit testing framework isn't compatible though.
You might be able to get it to work if you know how to manually register the add-in, but I'm not sure it's compatible. Please feel free to test it and/or submit a pull request adding support.
@wqweto we have an actual Rename refactoring scheduled for version 1.3, much better than Find&Replace (even with regex) if you asked me ;)
As for VB6 support, that would be awesome...like @ckuhn203 said, unit testing wouldn't work, but I can see the improved navigation, code inspections and refactorings be useful in a VB6 environment.. only thing is, I don't have a VB6 IDE to play with - if you have one, and want to contribute, we'll be more than happy to accept a pull request that makes our ducky work in VB6!
@retailcoder: I don't think current ANTLR parser is ready for rename a la VS.Net style. There is no symbol tables implementation and you can't move beyond parse trees (to AST) so you cannot tell if an identifier is a method or a local variable. Current parser cannot tell if A(i) is call method A with arg i or access array A with index i.
In computer science, a symbol table is a data structure used by a language translator such as a compiler or interpreter, where each identifier in a program's source code is associated with information relating to its declaration or appearance in the source, such as its type, scope level and sometimes its location. http://en.wikipedia.org/wiki/Symbol_table
That sounds an awful lot like what the IdentifierUsageInspector class is trying to achieve. [not-so-far-in-the-]future versions will see a better implementation, as it has already been noted that it's working harder than it needs to.
Thanks for putting us on the do-it-right track! Refactor/Rename will happen, I promise. :)
I think it will be awful lot of work to do it with current (buggy) grammar. Besides, you need a two-pass parser as VBA allows usage without forward declaration (which is required in C/C++ outside classes).
For instance if you have a module with Public Test() As Long
array and then in a form you have this:
Private Sub Form_Load()
Dim result As Long
result = Test(2)
End Sub
Private Function Test(ByVal Idx As Long) As Long
Test = 42
End Function
Here Form_Load
calls Test
function and does not access global Test
array.
So, unfortunately yes, you'll need a first pass to build symbol tables or you can never be sure about symbol reference/usage. (How to implement Refactor->Rename
on the global Test
array from sample?)
Besides, current grammar has no idea about preprocessor and if does by chance it needs inside #If ... Then
/#Else
/#End If
to find valid statement which is not always the case, i.e.
#If True Then
Select Case Idx
#Else
Select Case Idx + 1
#End If
Case 1
Test = 123
Case 2
Test = 987
End Select
is valid VBA code
@wqweto the grammar does understand pre-processor. https://github.com/retailcoder/Rubberduck/blob/GrammarIsFun/Rubberduck.Parsing/VBA.g4#L770-L773
I'm unsure if we've taken it into account in our current code base though.
Btw, it's documented in the header that statements cannot be parsed if split by the preprocessor.
https://github.com/retailcoder/Rubberduck/blob/GrammarIsFun/Rubberduck.Parsing/VBA.g4#L39
Back to the supporting VB6 topic - I think all I'm missing is exactly where to register the add-in. For Office the key goes under HKCU/Software/Microsoft/VBA/VBE/6.0/Addins[64]
- I believe if we simply verified if there's a key for the VB6 IDE we can register Rubberduck under it and it would just work.
Of course there's a number of features that would require some more thought - namely unit testing and source control integration - although SC would actually be simpler to implement in VB6 than in VBA... but I have no idea how I'd approach unit testing without an Application.Run
method in the host (this limitation is also why unit tests don't work in Outlook).
Bottom line, I don't think there's any major showstopper in supporting VB6 - and given how VB6 projects are certainly larger than most VBA projects, the code inspections, refactorings and navigation features would definitely be more than welcome in that environment as well.
Relevant. I'm not sure which version of VS supported VB6. http://www.mztools.com/articles/2011/MZ2011012.aspx
I found the key buried in this old MSDN article.
https://msdn.microsoft.com/en-us/library/aa239603%28v=vs.60%29.aspx
= new RegKey(RegKey.getRootKey(RegKey.USER_ROOT),
"Software\\Microsoft\\VisualStudio\\6.0\\Addins\\
MyAddin.Connect ", RegKey.KEYOPEN_CREATE);
regKey.setValue("Description", "Addin description goes
here.");
Here is a sample VB6 add-in registration
[HKEY_CURRENT_USER\Software\Microsoft\Visual Basic\6.0\Addins\vbAdvance.Connect]
"Description"="#101"
"FriendlyName"="#100"
"SatelliteDllName"="vbAdvance.dll"
"CommandLineSafe"=dword:00000001
"LoadBehavior"=dword:00000003
Description
and FriendlyName
are either resource ids in SatelliteDllName
are just plain strings.
Looks like extending the VB6 IDE requires a different interface?
This message box is Rubberduck's OnConnection
code running:
public void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
{
try
{
_app = new App((VBE)Application, (AddIn)AddInInst);
}
catch (Exception exception)
{
MessageBox.Show(exception.Message, "Rubberduck Add-In Could Not Be Loaded", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Asked on SO: http://stackoverflow.com/q/29294260/1188513
I think I got a definitive answer from a trustworthy source:
This is an Office add-in, it can only run in-process inside an Office app like Excel or Word. Trying to use it from the VB6 IDE is not meaningful, it is not anything like an Office app. There's an ancient KB article still around that covers Office extension development in VB6. Your existing code will not be helpful with that. — Hans Passant 35 secs ago
VB6 isn't going to happen, at least not without some major parts getting rewritten.
Removing by-design and, heck, status-declined tags. If other add-ins can do it, we can too.
The merge of #2289 is now making this very possible.
I don't have a VB6 IDE handy ATM, but I believe if the registry VB6 keys are created, Rubberduck will now try to run with the VB6 wrappers.... which will throw a NotImplementedException
before long, but hey, progress!
The wrapper types that need to be implemented are under Rubberduck.VBEditor.SafeComWrappers.VB6. Right now there seems to be a problem with the VB6 IDE extensibility library we're using, looks like a lot of important members are missing, which makes implementing these wrappers pretty hard.
Oh, and Hans didn't know what he was talking about.
I have a VB6 license (it's part of Visual Studio 6.0). I could take a look, but this looks like it could get a bit hairy...
@mansellan pretty much everything is setup already - you basically "just" need to implement the SafeComWrapper<T>
types' members under the VB6 wrappers namespace, mirrorring what we have in the VBA wrappers namespace.
And some COM registration I'm guessing.
@mansellan already taken care of. AFAICT all we need is wrappers that don't throw new NotImplementedException();
@retailcoder cool, I'll take a look.
OK, so there's more to this than initially thought.
We have a reference to a VB6IDE interop assembly Microsoft.VB6.Interop.VBIDE
in the Rubberduck.VBEditor
assembly (from \libs\Microsoft.VB6.Interop.VBIDE.dll
. This IA was created by tlbimport, with its dependant Office assembly imported from the Office 12 typelibs (see https://chat.stackexchange.com/transcript/message/39454960#39454960).
Unfortunately, the VB6 extensibility API provides objects which implement the Office 8 interfaces, which are incompatible and thus cause runtime COM exceptions. To resolve this, we need to recreate the VB6IDE IA using an Office 8 tlb as a reference. That's the easy bit.
Were we then to reference the resultant IA from the Rubberduck.VBEditor
, it would cause widespread typename collisions with the already referenced Office 12 types, as they both import to a namespace of Micorsoft.Office.Core
There are several options available:
Therefore, Option 1 appears to be a dead end.
Import both Office assemblies into the Rubberduck.VBEditor
project, and alias them both to disambiguate. This would work, but litter the code with alias noise.
Move the SafeComWrappers
for VBA and VB6 into separate projects.
Note also, that once this is resolved there will be further work to do, as the Office 8 API is missing some members we use on the VBA side, and has a different system for events.
Let's have Rubberduck.VBEditor.VBA.dll
and Rubberduck.VBEditor.VB6.dll
assemblies.
IIRC that's more inline with how MZTools does it.
3 is easily my first pick.
I've completed my initial investigations into what's needed. I was wrong about the namespace clash - Office 8 imports to a namespace of 'Office', which separates it neatly from Office 12's 'Microsoft.Office.Core'. That helps us tremendously, and means that we don't need to (and shouldn't) create additional projects.
I would recommend:
I've reviewed all the interop assemblies to note differences in any members we're currently abstracting:
Not in Office.v8:
ICommandBar.Id - currently only used in hash\equality, along with many other properties. Use Name instead. ICommandBarButton.Click() - Events exposed from VBE. Major hassle, but fixable. ICommandBarButton.Picture, Mask - Workaround available (PasteFace) ICommandBarControl.IsPriorityDropped - Unused, appears redundant. Remove. IVBComponents.AddMTDesigner - Unused and undocumented. Remove?
Not in VB6 Extensibility:
IVBProject.Application - Only contains a version property, unused. Reachable from many other places, remove. IVBProject.Mode - No equivalent. May need to hack from window caption. IVBProject.Protection - Not applicable. Return "Unprotected". IVBProject.Open(string) - Equivalent is AddFromFile(string)?
With respect to enums, there are some differences, but the values that overlap match numerically. Suggest we just comment where enum values are applicable to one target only.
IVBProject.Mode
looks very similar to Private Declare Function EbMode Lib "vba6" () As Long
although the API returns global state for the IDE (not per project).
To be fair I don't understand what Mode
(design, running, break/debug) has to do with a VBProject. It definitely belongs at the IDE level.
Been away for a while, but back now and looking at this again.
Getting there...
YEAH!
Command bar events working:
Implementation of the VB6 wrappers may be suboptimal and needs to be checked for leaks. I haven't touched the VBA side (save for a few namespace edits). From a quick click-test, most functionality appears to be working.
Note however that we will need a grammer that's tweaked for VB6 - the VBA grammar chokes on VBForms (looks like an attributes issue from the call stack), and likely other places too.
Doing final diff and lint before PR.
So, I’m a little out of the loop as far as what we’ve done with the grammar, but VBA and VB6 are really the same language, are they not?
Unless we’ve put VBA specific things in the grammar, I would expect we could share a single grammar. Can you elaborate a bit?
Awesome work btw. I know there are some folks who’ve been wanting this for a long time.
this is the exception I get if I try and parse a VBForm:
I have no idea how to read \ debug it!
@Mansellan likely a fairly simple assumption that the grammar is making. Paste an exported MSForm's attributes in a new issue if you're not comfortable making the grammar changes, we'll fix this. The format can't have changed so much as to require a totally new grammar. I don't think the TypeLib API will work with VB6, so we'll likely need to keep the attributes pass for VB6 hosts.
Is this add-in supposed to work in VB6 IDE?
I think ANTLR parser has great potential for really useful features in the IDE. RegExp find&replace within routines with {Name} placeholder comes to mind.