VBA extensibility library for VB

the_net_2.0

Banned
Local time
Today, 16:10
Joined
Sep 6, 2010
Messages
812
does anyone know if VB6/VB2010 or any other version of VB carries a library similar to the extensibility library in VBA??

I am currently working with a VB6 application that contains about 40 modules and about 400K lines of code. I'd like to write a small procedure in a separate module with the project where I can use this sort of library to document all of the procedure names and the Call statements in each so I can get an idea of the code structure before I start changing it.

Anyone know if this is possible? tried google, but apparently it can't give any results.

thanks guys!
 
Adam.

Perhaps a little caution before attempting to write something like that…

One of the reasons you may not find it on Google, or anywhere else for that matter, is that it could be impossible to document all calls to procedures. If the documentation is to be automatic, and include all calls to procedures, then we could start by looking at a single bad scenario:-

Code:
Sub TestCall()
    Dim strCall   As String
    Dim strSuffix As String
    
    strSuffix = "2"
    
    strCall = "TargetProcedure_" & strSuffix
    Application.Run strCall

End Sub


Sub TargetProcedure_1()
    MsgBox "In Procedure: TargetProcedure_1()"
End Sub


Sub TargetProcedure_2()
    MsgBox "In Procedure: TargetProcedure_2()"
End Sub

You haven’t said the type of output you require but one would guess at a one to many table construct with the Called Procedure on the one side and the Calls on the many side.
The above would be difficult to automate and does not even go into the concept of scope from Caller to Called.

So, 400k lines of code could be broken by 2 lines of code:-
strCall = "TargetProcedure_" & strSuffix
Application.Run strCall

If you can automattically resolve the above two line of code you have a chance at the other 400k lines… but only a chance.

Chris.
 
thanks so much for the answer Chris. I think I may have no explained it right though. for instance, consider this as one simple module in my vb6 project:

Code:
function test1()
   call myOtherTest
end function

sub test2()
   call myOtherTest2
end function

I want a module that does this:

Code:
for each module in THISproject
   for i = 0 to module.CountOfLines
      if instr(module.lines(i), "Call ") > 0 then
         MSGBOX("there is a call statement in module " & module.name)
      end if
   next i
next module

Obviously that's not realistic, but that's the idea. Does that make sense? I already have this written in VBA for Access. to get the object structures for the vars MODULE and THISproject, you have to add "Microsoft Visual Basic for Applications 5.x Extensibility".

someone already wrote an app in vb5/vb6 that counts lines of code, but the package has like 20 files in it: http://www.vbforums.com/showthread.php?t=448824

There has got to be an easier way to manipulate VB6's IDE. for vba, the actual file that allows my module is VBE6EXT.OLB. So I was thinking that there had to be an equivalent for VB6. Something like VB6EXT?? Do you think?
 
Adam.

Far too many assumptions have been made.
Your original question was about all calls to procedures, not just call lines prefixed with 'Call'.

The use of the word Call is somewhat of a novice idea.
If you are going to get 400k lines of code to document you can not rely on the Call usage.

Code:
Sub TestIt()
    [color=red]MsgBox Call Fred()[/color]
End Sub


Function Fred()
    MsgBox "We didn't even get here!"
End Function

The assumption that a Function will be preceded by the word Call is incorrect.

Spend as much time as you like but in the long run I doubt you can document all calls to procedures.

Chris.
 
Chris,

thanks again for another response. But nothing there really answered the question I asked, sir. ;)

You are right about call statements. Everything you said was right, but I don't want to bother everybody here about "details" when that is unimportant to what I want. I've been a member here for years and I think pretty much everyone on your level of expertise that still checks this board would know that I've already covered the basics here (such as what you described in your last post).

Not trying to sound like I'm lecturing you, but again the question was about the extensibility for vb6. Let me worry about everything else. That's what this company pays me for. You shouldn't be wasting your time posting theory about the project you're not getting paid to work on it. ;) So do you know how I can make the extensibility object structure happen easier than what's in the link I provided 2 posts ago?

Again, I appreciate your theory, but I really do have everything covered in that regard. This request of mine is not part of their project. It is for my own learning purposes. Does that make more sense perhaps?
 
Adam.

If I may partially quote from your first post in this thread:-
"where I can use this sort of library to document all of the procedure names and the Call statements"

I'm not saying it can't be done, I'm saying it might be impossible.

When faced with enough knowledge to confront a problem I go for the difficult first; the easy just falls into place.
By going the easy way first we can consume a lot of time which can simply be rendered worthless in 'the eleventh hour'.

It is indeed a luxury to perceive a failure before spending too much time going down 'that Dark Avenue from which there is no return'.


Forget the garbage in that link, size alone does not screw the pooch; here is the easy way:-
Code:
Public Sub CountCallsInModules()
    Dim lngModuleCount As Long
    Dim lngLineCount   As Long

    For lngModuleCount = 0 To Modules.Count - 1
        With Modules.Item(lngModuleCount)
            For lngLineCount = 1 To .CountOfLines
                If InStr(.Lines(lngLineCount, 1), "Call ") Then
                   MsgBox "There is a 'Call ' word in module " & .Name
                End If
            Next lngLineCount
        End With
    Next lngModuleCount

End Sub

And that code will not solve your stated problem.

Chris.
 
Code:
Public Sub CountCallsInModules()
    Dim lngModuleCount As Long
    Dim lngLineCount   As Long

    For lngModuleCount = 0 To Modules.Count - 1
        With Modules.Item(lngModuleCount)
            For lngLineCount = 1 To .CountOfLines
                If InStr(.Lines(lngLineCount, 1), "Call ") Then
                   MsgBox "There is a 'Call ' word in module " & .Name
                End If
            Next lngLineCount
        End With
    Next lngModuleCount

End Sub

Chris,

is the code quoted above actual code that will work in a VB6 module, or is that simply the "corrected" code from the PSUEDO that I provided in the previous post?

Unless you tell me that latter, I will give this a try at lunch today. Yes that's all I wanted really. And of course like always, I have an absolutely terrible time explaining exactly what I'm looking for because I always forget that there are rules to follow that I don't think of before I speak.

That pretty much is an effect of being a natural salesperson in a programming role. I get it all the time. So sorry for that! You're not the first one I've done it to. :p

and if the above is the code for VB6, can I assume that this:
Code:
Modules.Item(lngModuleCount)
can be followed with an intellisense dropdown of props and methods for a MODULE object when I type the next period? e.g. -
Code:
Modules.Item(lngModuleCount).lines(5).WHATEVER

thanks again sir!
 
Chris,

for some reason I'm missing something here. Here's what I have for VBA:

Code:
Function CountVbaLines()

On Error GoTo ErrorHandler

Dim TotalLines As Double
Dim vbEditor As VBIDE.VBE
Dim VBProj As VBIDE.VBProject
Dim VBobj As VBIDE.VBComponent

Set vbEditor = Application.VBE
Set VBProj = vbEditor.ActiveVBProject

   For Each VBobj In VBProj.VBComponents

      TotalLines = TotalLines + VBobj.CodeModule.CountOfLines
   
   Next VBobj
   
      CountVbaLines = TotalLines

ErrorHandler:
   Set vbEditor = Nothing
   Set VBProj = Nothing

End Function

I want to write the equivalent in VB6, but I have that at work and I'm testing this junk on VB2010 express at home because I can't find VB6 anymore to download.

Do you possibly know, off hand, what has changed in vb2010 in terms of the above referenced code? What I'm finding is that there is no CodeModule for the VBComponent in 2010. That seems to be the only thing I'm missing here.

Thanks.
 
bob larsen knows more than me about this, but does MZ-Tools include any analysis features for stuff like this?
 
What I found confusing is if it is required for Visual Basic or VBA in Access.

In Access 2003, help says the language is ‘Microsoft Visual Basic 6.3’ even though we know it is ‘Visual Basic for Applications’. It doesn’t help when Microsoft seems to confuse the words.

Added to that, this question was asked in the:-
Access World Forums > Microsoft Access Discussion > General
forum rather than the:-
Access World Forums > Apps and Windows > Visual Basic
forum.

In any case, what I wrote was written in a Module in Access 2003 and that makes it VBA not VB.
So, if the requirement is for it to run in VB I can’t help because I have very little knowledge of VB and no way to test it.

Sorry if I added to the confusion.

Chris.
 
CHRIS,

it's no big deal, bud. Actually, I'm still having issues with how to create a flowchart of function and sub calls, but I did manage to write code in an Access module that creates a fairly nice log file. this is the stage I will stall on until I figure out how to write code for actual CALL flows. I'm assuming that would be complex. here's what I currently produce as a logfile:

Code:
Function PrintProceduresAsItemsOfModules()

On Error GoTo ErrorHandler

Dim vbEditor As VBIDE.VBE
Dim VBProj As VBIDE.VBProject
Dim VBobj As VBIDE.VBComponent
Dim lngCount As Long
Dim lngCountDecl As Long
Dim lngI As Long
Dim strProcName As String
Dim intI As Integer
Dim lngR As Long
Dim arrCounter As Long
Dim FileNumber
Dim i As Long
Dim j As Long
Dim k As Long
Dim moduleNames() As Variant
Dim procNames() As Variant

arrCounter = 0
FileNumber = FreeFile
Kill "c:\myfile.txt" 'KILL OLD FILE
Open "c:\myfile.txt" For Output As #FileNumber
    
Set vbEditor = Application.VBE
Set VBProj = vbEditor.ActiveVBProject

   'GET LIST OF MODULE NAMES ONLY (no forms, etc..)
    For Each VBobj In VBProj.VBComponents
        If VBobj.Type = vbext_ct_StdModule Then
            ReDim Preserve moduleNames(arrCounter)
                moduleNames(arrCounter) = VBobj.name
                arrCounter = arrCounter + 1
        End If
    Next VBobj

arrCounter = 0

    'SORT LIST OF MODULE NAMES
    Call SortArray(moduleNames())

    'LOOP MODULE COLLECTION AND PRINT INFO FOR MODULES IN ALPHA ORDER
    For i = LBound(moduleNames()) To UBound(moduleNames())
        For j = 1 To UBound(moduleNames()) + 1
        
            'IF FOUND THE RIGHT MODULE, CONTINUE
            If VBProj.VBComponents(j).name = moduleNames(i) Then
                Set VBobj = VBProj.VBComponents(j)
                    With VBobj.CodeModule
            
                        lngCount = .CountOfLines
                        lngCountDecl = .CountOfDeclarationLines
                        
                        'GET LIST OF PROCEDURE NAMES
                         ReDim procNames(arrCounter)
                         
                            'GET FIRST PROC NAME
                             procNames(arrCounter) = .ProcOfLine(lngCountDecl + 1, lngR)

                        intI = 0
                
                        'LOOP MODULE'S NON-DECLARATION CODE LINES
                        For lngI = lngCountDecl + 1 To lngCount
                        
                            'IF REACHED NEXT PROC
                            If procNames(arrCounter) <> .ProcOfLine(lngI, lngR) Then
                                intI = intI + 1
                                arrCounter = arrCounter + 1
                                ReDim Preserve procNames(arrCounter)
                                    procNames(arrCounter) = .ProcOfLine(lngI, lngR)
                            End If
                        Next lngI

                        arrCounter = 0
                        
                        'SORT PROC NAMES
                        Call SortArray(procNames())
                        Print #FileNumber, PrintHeader(VBobj.name)
                        
                            'PRINT PROC NAMES TO FILE
                            For k = LBound(procNames()) To UBound(procNames())
                                If procNames(k) <> "" Then
                                    Print #FileNumber, procNames(k) & "()"
                                End If
                            Next k
            
                    End With
                    
                'SPACE FOR NEXT MODULE PRINTOUT
                Print #FileNumber, vbCrLf
            End If

        Next j
    Next i

Close #FileNumber
Shell "notepad c:\myfile.txt", vbMaximizedFocus

Exit Function

ErrorHandler:
MsgBox (err.Description)
   Set vbEditor = Nothing
   Set VBProj = Nothing

End Function '//LL

This is fine for now. It will transfer to vb6 just fine. vb6 has the same library that access does so it should be verbatim. The only issue might be things like Set vbEditor = Application.VBE. I'm not sure if vb6 uses Application as a qualifier or not. VBIDE is the same obviously, as office and vb6 both refer to vb code interfaces as the IDE. I put a thread in the modules forum here if you care to answer me there as well. Just need some advice on what kind of structure or diagram I should produce.
 
Last edited:
Seem to me that this sort of this thing would be done by doing something like this:

1) Gather all names of the functions and sub, which you seem to have already done with ProcOfLines.

2) Go through all modules and scan the procedure for any tokens that matches the list of procedure you've enumerated in #1. Make record for each call within that procedure.

By that time, you should have a table listing all instances where a token appears and in which procedure. That should then give you the sufficient data to then build something like treeview to illustrate the flow.


That said, I believe that one of reasons why charting the flow is comparatively rare nowadays is due to the fact that the language is event-driven. Thus there's no guarantee on which sequence of procedures will be invoked unlike the old days when programs all were started by a Main() function and had no events to respond but rather provided an output and waited for input from user.

Thus, even if you had a list you may be still left with question of which procedure will be the actual next procedure to be called, and whether a sequence of calls produce different results from another sequence of procedures.
 
BANANA,

but vb and vba execute in order, don't they? and what about tokens? I've never understood the token concept. care to shed some light for me?

and btw, could you tell me how you would check for call statements in each procedure? would you simply use INSTR()? that's all I can think of, but there are variables that come into play when you use that function, because CALL could be listed in a comment on the same line too.
 
About the code as posted…

It requires a reference to ‘Microsoft Visual Basic for Applications Extensibility 5.3’

There are no ‘SortArray’ and ‘PrintHeader’ procedures.

When they are fixed it compiles and runs.

‘------------

I think the aim would be to ignore output formatting until the results are stored in a table structure. Then, and only then, consider how to display it.

‘------------

In addition to Banana’s comments about event driven procedures I think we also need to consider the essence of late binding. Often people will only see late binding as resolving library references at runtime, this is not true. Late binding also applies to resolving anything that can’t be resolved at compile time.

Hence, we could have a procedure called by, say, the Eval() function and that can’t be resolved with early binding. The reason, when we think of it, is obvious. When something is data driven, and that data is not present at compile time, then it must be resolved at runtime when the data becomes available. In the case of library references the ‘data’ is the availability of the referenced file at runtime. In the case of the Eval() function the ‘data’ is the string it is required to process. But the compiler can not know, ahead of time, what ‘data’ the Eval() function might call. Therefore, and to that end, we can not say what function might be called. It may be that a function that does exist is never called. It may also be that a function that is called doesn’t exist. The reason for this situation is that the ‘Call’ does not exist at compile time. It can’t be processed by the compiler because it doesn’t exist. It can’t be documented for the same reason, it doesn’t exist. It does not exist in the source code text and it is the source code text you are trying to process. It is not the string that is being called it is the runtime meaning of the string which is being called. And it is that meaning which does not exist in the source code text and therefore can’t be documented.



So this is the essence of the matter as I see it…
If you try to document early binding then you might succeed. If the compiler can do it you stand a chance, but only a chance.
If you try to document late binding you will fail. If the compiler can’t do it, you will also fail.

This is why I may appear to be somewhat negative about this sort of thing. I would prefer to try the difficult things first and see if they work. If they work then move on to the next most difficult thing and see if it works. But if any one of the difficult things fail then the whole thing fails.

There is no real point in writing vast amounts of code, doing all the simple things first, only to find that the whole project fails, be it VB or VBA.

Chris.
 

Users who are viewing this thread

Back
Top Bottom