PDA

View Full Version : Solved: Question on Debugging Technique



Ice-Tea-Jan
09-03-2009, 09:28 PM
Hello,

This is probably easy, but I guess I have a mental block on figuring this out.

I want to simply run only a section of my code (say perhaps a message box), and not the entire routine.

I recognize that I could simply paste that section of code into a new sub; however I wish to avoid that step (if possible) every time I need to test a section of code.

I have unsuccessfully fiddled with the debugging menu.

If this is possible, can somebody roughly outline the steps to do this?

:hide:

fumei
09-04-2009, 02:57 PM
This is somewhat mutually exclusive, if I understand correctly. Say you have:

Sub yadda()

instruction_1
instruction_2
instruction_3
instruction_4
instruction_5
instruction_6
instruction_7
instruction_8
instruction_9
End Sub

Are you asking about how to START to execute from (say) instruction_4 onwards?

Are you asking about how to START to execute from (say) instruction_4 to instruction_7 - ignoring 1 to 3, and 8,9?

No can do.

You can use BREAK to execute 1 to 3, then Step through 4 to 7.

My feeling if this is something you actually need to do, then you should be breaking up your procedure into separate called chunks...which is a good idea anyway.

It makes for easier and better debugging, for precisely the reason you are asking about.

Ice-Tea-Jan
09-04-2009, 07:42 PM
Thanks Fumei!

I’ve used the "call" method before, but you are right, I probably should use it more often to break the code into segments for easy identification when debugging.

I believe I have the hang of breakpoints now; however (forgive my ongoing ignorance) – but what does "step over" do then?

"Step over" is what made me believe that one could skip over (or perhaps fast forward through) codes. :wot

I tried using "step over" in conjunction with breakpoints – and it does not seem to work.

Can somebody give me a basic example of how "step over" is used?

As always, this forum is tremendously valuable and helpful!

Janet

Paul_Hossler
09-05-2009, 05:41 PM
ALso Janet, if you're in the VBE you can open the 'Immediate Window' using Control-G

If you just wanted to execute (e.g.) the "instruction_6" sub, you can just type (or paste) it into the window and hit enter.

Works if there is no assumptions about the previous subs being run to initialize data

As you know, F5 will execute the Sub that the cursor is in (if there are no required parameters)

From (2007) Help:



Step Into, Step Over, Step Out Commands (Debug Menu)
Step Into
Executes code one statement at a time.
When not in design mode, Step Into enters break mode (http://www.vbaexpress.com/forum/ms-help://MS.WINWORD.DEV.12.1033/WINWORD.DEV/content/HV01200929.htm) at the current line of execution. If the statement is a call to a procedure, the next statement displayed is the first statement in the procedure.
At design time (http://www.vbaexpress.com/forum/ms-help://MS.WINWORD.DEV.12.1033/WINWORD.DEV/content/HV01200929.htm), this menu item begins execution and enters break mode before the first line of code is executed.
If there is no current execution point, the Step Into command may appear to do nothing until you do something that triggers code, for example click on a document.
Keyboard shortcut: F8.
Step Over
Similar to Step Into. The difference in use occurs when the current statement contains a call to a procedure.
Step Over executes the procedure as a unit, and then steps to the next statement in the current procedure. Therefore, the next statement displayed is the next statement in the current procedure regardless of whether the current statement is a call to another procedure. Available in break mode only.
Keyboard shortcut: SHIFT+F8.
Step Out
Executes the remaining lines of a function in which the current execution point lies. The next statement displayed is the statement following the procedure call. All of the code is executed between the current and the final execution points. Available in break mode only.
Keyboard shortcut: CTRL+SHIFT+F8




Sub yadda()
instruction_1 ' Cursor here, F5 to run yadda
instruction_2
instruction_3 ' selct line and F9 for Break Point
instruction_4
instruction_5
instruction_6
instruction_7
instruction_8
instruction_9
End Sub


So when you hit F5, then _1 and _2 will execute, and the _3 will be the line waiting to go

a. If you hit Shift-F8 (Step over) , _3 will run completely and then _4 will be the line in waiting

b. If you hit F8 (Single Step), the first line inside of the sub _3 will execute, and from inside sub _3, you can

b1. single step though Sub _3 using F8, OR
b2. select a line, and Control-F8 and execute to that line, OR
b3. enter CTRL+SHIFT+F8 to finish running sub _3, and go back to _4 waiting to execute

c. If you hit F5, then the rest of the 'yadda' and subs will run to completion

It sounds much more compliated that it really is (or I might not be explaining as well as the more experienced VBXers), but a little experimenting, and you'll have it in no time

Paul

fumei
09-08-2009, 12:17 PM
Paul, I think that is a pretty good explanation.

Janet, I suggest you play around with Paul's suggestions, but in the end, actually breaking up your procedure into other (called) procedures is a far better way to keep control of your code.

In most cases, using Call to execute other code requires the use of parameters...which is a GOOD thing to know how to use efficiently. Here is a example, using the ONE procedure example to start.

Sub yadda()

instruction_1
instruction_2
instruction_3
instruction_4
instruction_5
instruction_6
instruction_7
instruction_8
instruction_9
End Sub


Now - just for example sake - instruction_2... uses a string that comes from instruction_1. If instruction_2 is in the SAME procedure (in the example above), then there is no problem/issue. The variable is in Scope. This is crucial! If you move instruction_2 (and say 3 and 4 as well) to a new procedure, then you most likely will have to pass the parameter along with it. This keeps the parameter value in Scope.

But that is OK, this is what breaking up procedures into more easily worked chunks is for!

Sub yadda()

instruction_1
' this calls out to Step2 with a string
' the Sub Step2 contains instructions_2, _3, _4
Call Step2(somethingAsString)

' this calls out to Step3 with a Range
' the Sub Step3 contains instructions_5, _6, _7
Call Step3(rngWhatever)
instruction_8
instruction_9
End Sub

Sub Step2(strIn As String)
instruction_2 ' that uses the passed in parameter string
instruction_3
instruction_4

End Sub

Sub Step3 (rngIn As Range)
instruction_5
instruction_6
instruction_7
End Sub


Here is a real world example. As you may know, the contents of a table cell in Word contains both the actual text AND the end-of-cell marker. In most cases, we do not want those end-of-cell markers...we want to work with just the text inside the cell. So, obviously, there is an advantage to working with just the text, which means you have to strip off that end-of-cell marker.

Yes, you could do the action with explicit instructions in every procedure you ever create that works with table cell text.

But...why? This is where jumping out (either by using a Call to a Sub, or using a Function) comes in very handy. Here are two Functions.

Function CellText_A(oCell As Cell) As String
Dim k As Long
k = Len(oCell.Range.Text)
CellText_A = Left(oCell.Range.Text, k - 2)
End Function

Function CellText_B(strIn As String) As String
CellText_B = Left(strIn, Len(strIn) - 2)
End Function


Notice they use different parameters - even though the intended results is identical. That is, you want a string of the text content of the cell. One uses a Cell object as the input parameter, the other uses a String as the input parameter.

Which one to use depends entirely on what the requirements are. Here are two different Subs demonstrating each of the two different Functions.

Sub testTabletext()
MsgBox CellText_A(ActiveDocument.Tables(1).Cell(2, 2))
End Sub

Sub textTabletext2()
Dim strYadda As String
strYadda = ActiveDocument.Tables(1).Cell(2, 2).Range.Text
MsgBox CellText_B(strYadda)
End Sub

BOTH of those will give a messagebox with the text content of Cell(2,2) of the first table in the document.

Why even have different ones? Because - again depending on your requirements - it may be VERY advantageous to create and use a cell object. Or not.

Sub BlahBlahYellow(oCell As Cell)

Set oCell = oTable.Cell(2,2)
' oCell is a cell object and can be used as an object
If InStr(1, CellText_A(oCell), "Bob") > 0 Then
With oCell
.Range.Text = Replace(CellText_A(oCell), "Bob", "Harry")
.Range.Font.Size = 16
.Shading.BackgroundPatternColorIndex = wdYellow
End With
End If
End Sub
I hope you can see the difference. By using a cell object (rather than just the text of the cell), you can:

1. perform test on the text (string) - the other procedure ONLY does this;

AND

2. perform actions on the cell itself.

In the above example, the procedure:

gets the text string - CellText_A

tests to see if contains "Bob" - If InStr(1, CellText_A(oCell), "Bob") > 0

If it does, changes "Bob" to "Harry", AND makes the cell font size = 16, AND makes the cell background color = yellow. All using one object.

You can go further. Let's make the Sub BlahBlahYellow not be specific to a cell. Instead it can take ANY cell object.
Sub BlahBlahYellow(oCell As Cell)
If InStr(1, CellText_A(oCell), "Bob") > 0 Then
With oCell
.Range.Text = Replace(CellText_A(oCell), "Bob", "Harry")
.Range.Font.Size = 16
.Shading.BackgroundPatternColorIndex = wdYellow
End With
End If
End Sub
' niow let's use that for ALL cells in a table

Sub WholeTableCheck()
Dim oTable As Table
Dim oCell As Cell
Set oTable = ActiveDocument.Bookmarks("ClientData") _
.Range.Tables(1)
For Each oCell in oTables.Range.Cells
Call BlahBlahYellow
Next
End Sub


Notice how clean and simple the Sub WholeTableCheck is? It is VERY easy to debug because it does not do anything complicated. Any problems can most likely be happening in the called sub, so you look THERE.

And THAT sub is not all that conplicated, and if there is any problems it is likely to be in the called function it calls. So you can look there.

Let me see if I can put it another way.

Test the Function CellText_A. It is simple to test. Does it work? If yes, then you know it works anywhere.

Test Sub BlahBlahYellow. Does it work? If the Function it uses works (and you have already checked that), then there is little to debug...and if there IS a problem, you know it is not the Function CellText_A. So if Sub BlahBlahYellow works, then............

Test Sub WholeTableTest(). If you have done the previous two steps, then the ONLY thing that can be wrong/in-error with Sub WholeTableTest() is mis-declarations of the variables (Dim oTable, Dim oCell) - and using Option Explicit will catch most of those; or incorrect use of the Set instruction.

Let me repeat that. The ONLY thing can can be in error are those two things. Because you have already checked/tested the called procedures. If you have thoroughly tested/checked them...then you never have to do so again!

Say you are:

1. creating a new Word table of X rows, Y columns
instruction_1
instruction_2

2. inserting data content into various cells (say from Excel)
instruction_3

3. performing some test on the text content
instruction_4
instruction_5
instruction_6

4. depending on the results of those test, change some text.
instruction_7

5. and change the format/color/whatever of the cell with the changed text
instruction_8
instruction_9

The question is: does the actual instructions to get the text content of the cells have to be in this procedure?

The answer is - emphatically - NO!

Sub DoMyStuff()
Call makeMyTable() ' #1 above
Call getMYData() ' #2 above
Call checkCellText ' #3 above
' which can do #4 , and #5 within itself!!!!!!
End Sub


This way, you can do debugging in neat realistic chunks. Do you see how cleaner that is? With good naming, the procedure is very very clear, both to you, and to anyone else that may need to read it.

makeMytable
getMYData
checkCellText

Not only that, but if you DO get the dreaded run-time error, it will most likely jump you to the offending line in THAT procedure.

So if there is something wrong with getMYData, then THAT is where you will go. You do not need to check makeMyTable...as it did not produce errors.

Otherwise, procedures become what is known as "spagetti code". These are procedures so long and complicated that it is simply a mess of lines that are utterly confusing to follow.

While there are many many positions/opinions on what is a "good" number of lines for a procedures - either Sub or Function - IMO, if a procedure is greater than 40 lines...it likely can use a rethink/redesign. There are of course exceptions, but if they do go more than that, then I make much greater use of comments. With a <40 line procedure, more often than not you can get away with minimal comments. Why? Because it is small and likely obvious.

I have written complicated actions that - in TOTAL - use literally thousands of lines of code. But rarely are any individual procedures > 40 lines.

Paul_Hossler
09-08-2009, 01:26 PM
fumei's explaination of the value of modularizing is absolutely right-on, and the "Step" debugginng techniques will work a lot more effectively and quicker since you have a much better starting point when you know which is the troublesome subroutine.

1. Control-L will show the call stack (how you got to that subroutine) and you can 'back-step' by clicking if you want to see (e.g.) "Where did THAT come from?"

2. I suggest that understanding at least the basics of the object model will save a lot of time and trouble. This is a VBApplications environment. Many times I've seen code written as if it were a Visual Basic (or other language) application, with programmer written nested For loops, etc. instead of just calling the appropriate object and applying the desired fomatting, etc.

fumei -- you might consider writing a KB article with all this good stuff

Paul

fumei
09-08-2009, 02:16 PM
Once I retire (hopefully I just have two years to go) I intend on posting the whole Word VBA course I teach. It is an internal course for our organization, as there really are no commercial VBA courses.

It is two separate five days instructor-led (me!) courses covering general VBA/coding principles and best practices, as well as in-depth coverage of Word document and template design and usage, plus in-depth use of Word VBA.

A lot of the longish posts I put here come from that course material.

I totally agree that all too often "programmers" simply do not appear to really understand the use of objects, and write long long long long long pieces of code that can be handled with ONE...OK, maybe more than one...instruction using the appropriate object.

I will relate a story I have mentioned here (and other forums) previously. However, it can handle repeating.

I am a technical learning/training person. That is my job. I go in and do something that rarely gets done properly - a real needs analysis. Then I research what learning components are needed to meet those needs. Then I either enter contract negotiations with vendors for existing courses (AIX for example is proprietary so you can ONLY get it from IBM), or work with stakeholders to create course material.

One of the perks is that I often get to "audit" courses from vendors. I get to sit in as a participant in courses WAAAAAY out of my field of knowledge. The primary "reason" is to assess how good the material is (is it clearly written etc. etc.; is there enough), and to assess (IMO of course) how good is the instructor.

I sat in on ("audited") a five days Object Oriented Programming and Design course - this is many years ago - that I arranged for our senior programmers. Let me emphasize that. Our senior programmers. BTW: we have 1,200 programmers on staff, actual employees.

The instructor (who was very good) was floored and shocked, when the whole group voiced the opinion that they did not have time to do design for applications. They are paid to write code.

The instructor's response? "Then you will write bad code."

I got a lot of dirty looks when I jumped and applauded.

In one example (A "University of Mars" student grading/marking system), the instructor asked how many objects are in the example.

I was the ONLY one (12 participants - 11 senior programmers, and me) who got the correct answer. One. There was one object in the example. Many properties. Many methods. But only one object.

The others answers ranged from 4, to one person stating there were 51 objects.

In a lab exercise, my answer was four lines of code. (Correct, BTW)

My lab "partner" used a plethora of CASE tools and came up with over 700 lines of code.

I know, I know, as I have demostrated here (and elsewhere) I sometimes am not the most diplomatic of people. I admit it. I have been in meetings with some of these people and have had to be physically restrained as I am lunging across the table to choke some sense into someone.

BTW: that is not hyperbole. I did literally try to leap across a table once, in total fury, when one of those "senior" programmers stated it was going to take six weeks to add one item to a drop down menu.

Mind you this was MicroFocusCOBOL, and screen elements are NOT objects, but actual pixel locations. But still....

My point at the time was that the whole lot of programmers should be fired and we hire two kids fresh out of college who can program in OOP, and they could likely have the whole system done in three months. Instead, that particular project cost $22,000,000....and was never used as it did not work.





Sigh.

Paul_Hossler
09-08-2009, 02:36 PM
:wine: Best I can do for you

Been there, suffered through development projects where I know most would be thrown away or at least never used enough to justify the time and expense.

Current project is being designed by coders with no system architect or engineer or human factors analysis. Mgt is proud of the effeciency, and we have metrics on metrics, to 2 decimal points, about how good we're doing

Would you believe that 95% of the UI is basically internal table maintance and bears no resemblence at all to the way the users think and work?:banghead:

Paul

fumei
09-08-2009, 03:54 PM
"Would you believe that 95% of the UI is basically internal table maintance and bears no resemblence at all to the way the users think and work?"


Oh yes, I would believe that in a heartbeat.

This gets to the bottom of the issue. In my experience, real needs analysis is rarely done. Or, if they call it that, it is done by systems people who never go out and see how the systems is actually used, by real people, in real situations.

For example, I fought like hell to get our "new" system to have data for one function, all together, on one screen.

The front-end clerks - dealing with real people/clients - when trying to input/process a maternity claim (for unemployment benefits) have to work with.....wait for it....17 different screens.

One piece of data input on the BN09 screen. Which has to be closed, then a BN03 screen called up. One piece of data entered. The BN03 screen has to be closed. Then the AR10 screen called up. Two pieces of data entered. The AR10 screen must be closed. And on and on and on.

Not only is this a pain from a data entry (user) POV, but it is also a ridiculous waste of I/O. Why? Because each screen (BN09, BN03 etc.), when closed sends its field information - BN09 with 31 fields of which 30 are blank, as only ONE field is used for the maternity claim - back to the database to update records.

So to do ONE claim, there are 17 screens to be called up (many with only one applicable field to be used), and EACH screen must be closed and data sent before you can use the next.

I redesigned the UI - not touching the data records - so that the "Maternity" screen had all the elements to input a maternity claim. One screen. One I/O back to the database.

That was 20 years ago. They are still using the same old system.

fumei
09-08-2009, 03:56 PM
We are going off into the weeds with this thread.

Janet, how are you doing with this? Are you OK with debugging your code? Is there anything else we can possibly help with?

Paul_Hossler
09-08-2009, 05:31 PM
Yes, Janet -- sorry, but this pushed (as you can see) some hot buttons.

If you're happy with the results of this thread, You can mark it "Solved" using "Thread Tools" as the top.

If there is anything else, I promise I'll try to be more focused :yes

Paul

Ice-Tea-Jan
09-08-2009, 06:23 PM
Paul & Fumei,

I need to absorb and step through your suggestions before I can reply thoughtfully.

I got sidetracked at home and at work, so give me a day or so and I will reply.

As always, your help, knowledge and skill is appreciated.

Thanking you,
Janet