RegisterLog In/Log OutView Cart
O'Reilly Ron's VB Forum Ron's VB Forum
BooksSafari BookshelfConferencesO'Reilly NetworkO'Reilly GearLearning Lab
 


Traveling to
a tech show?

Hotel Search
Hotel Discounts
Discount Hotels
Chicago Hotels
Canada Hotels
California Hotels
Hotels




Date: Jun 30 1999
From: James Skipper
To: ron@oreilly.com
Subject: VBIDE and Add-Ins

I just purchased the O'Reilly book Developing Visual Basic Add-ins by Steven Roman. It is great and has given me a great headstart on my current project. However, everytime I try to invoke one of the CodeModule methods, I get a runtime error 35, Sub or Function not defined.

Here is a code snippet:



Dim vbp As VBProject


Dim w As Window
Dim vbc As VBComponent
Dim vbcm As CodeModule
Dim vbm As Member
Set vbp = VBInstance.ActiveVBProject
For Each vbc In vbp.VBComponents
    frmAddIn.List1.AddItem vbc.Name
    Set vbcm = vbp.VBComponents _(vbc.Name).CodeModule
     For Each vbm In vbcm.Members
         LineNumber = vbcm.ProcBodyLine _
           (vbm.Name,vbm.Type)
     Next
Next


Hi James,

I'd have to say that the Visual Basic's displaying the "Sub or Function not defined" error message in this case is a worthy candidate for inclusion in our book, Developing Windows Error Messages. The problem with your call to the ProcBodyLine property (which syntactically appears to be a method rather than a property) is not that the procedure or function is not defined, but rather that one of the arguments that you've supplied is incorrect.

Your code snippet iterates the components (that is, the objects listed in the Project Explorer) in a project, and then iterates each member of the code module belonging to a particular component. For each member, you call the ProcBodyLine procedure to retrieve the line number on which the member begins. To do this, you pass two arguments to the code module's ProcBodyLine property: the name of the member whose line number is to be retrieved, and the member's type. This code looks like it should work, except, of course, that it doesn't; instead, that pesky error message appears.

To see what's wrong with the code, let's begin by taking a closer look at a code module's Members collection. According to the documentation, the collection consists of Member objects, each of which represents a module-level property, method, and or event contained within that code module. Each member object, however, has a Type property, which (again according tot he documentation) returns one of the following members of the vbext_MemberType enumeration: (click here for code example)

This suggests that the documentation for the Members collection is not wholly accurate, and that the collection contains module-level variables and constants as well as procedures. Modifying your code fragment to display the objects of the Members collection confirms this, as Figure 1 shows.


Figure 1

Now let's look a bit more closely at the ProcBodyLine property of the CodeModule object, which returns the starting line number of a procedure. It takes two parameters -- the name of the procedure, and the procedure's type. This latter parameter, though, seems unnecessary; you would think that the procedure name should be sufficient to identify the procedure. However, this isn't accurate; within a single code module, multiple property procedures can have the same name. Hence, the ProcBodyLine's type argument requires one of the following members of the vbext_ProcKind enumeration (and not of the vbext_MemberType enumeration): (click here for code example)

In other words, in your original code snippet, supplying vbm.Type as an argument to ProcBodyLine raises the "Sub or Function not defined" error. Instead, the following variation of your code snippet correctly retrieves the starting line number of functions, procedures, and properties: (click here for code example)

Getting the line number of functions, procedures, and event handlers is easy enough, since each of these members has a type property whose value is vbext_mt_Method. There is, however, a complication in getting the line number for a particular property procedure: there is just one Member object in the Members collection for each set of property procedures. Presumably, the Member object's CodeLocation property returns the line number of the first of the possible set of property procedures, but its values appear to be inaccurate. Instead, the code adopts the admittedly kludgy expedient of using the CodeModule object's ProcBodyLine property to retrieve the starting line number of each of the three possible types of property procedures However, if the relevant property is not present, a runtime error is generated. The code snippet relies on the On Error Resume Next statement to insure that this does not interrupt program execution.

I hope that this helps you with your project, and I'm glad that you're enjoying Developing Visual Basic Add-ins.

-- Ron

Return to: Ron's VB Forum



O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website: | Customer Service: | Book issues:

All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.