Error if macro "Run", No error if single stepped! Help?

J

Julian

It was a reliable piece of code, and stuff around it has changed but the key
point is that if I "Run" the macro (which copies from a bookmark in one doc
and pastes the text into another) the pastespecial fails (empty clipboard),
but if I single step through the code it all works perfectly (NB I have
over-ridden the Ctrl-V keyboard shortcut for paste, but what should that
have to do with it?).

I removed all the modules from Normal.dot and then re-imported them (not
sure if I quit in-between though) but on this occasion it made no
difference... have I missed a trick?

Ideas?

TIA

Julian
 
F

fumei via OfficeKB.com

"It was a reliable piece of code"....except we have no idea what that was/is.

"I have over-ridden the Ctrl-V keyboard shortcut for paste" - hmmm, which
means you have overridden the actual command. You do not just override the
shortcut, you override the command. I would say that this may have something
to do with it.

Again, since we have no idea what you have done, it is virtually impossible
to suggest anything.
 
J

Julian

The key diagnostic to me was that the code ran perfectly when single stepped
but failed when run either by Alt-F8 from Word or as "Run" in the VBE...
given which I thought the specifics probably irrelevant (but included some
info just in case)

I also have my own event handling, but that is specifically disabled at the
start of the macro...

I know that Word VBA can get corrupted and can sometimes behave oddly, but I
think I eliminated that possibility by the remove/reimport.

However, allow me to clarify as per your questions

fumei via OfficeKB.com said:
"It was a reliable piece of code"....except we have no idea what that
was/is.

I don't really know whether you wanted the actual code, I thought that might
be a bit OTT but here's the key section...

this_doc = ActiveDocument.Name
Documents.add DocumentType:=wdNewBlankDocument
temp_doc = ActiveDocument.Name
wd_total = 0
For i = 0 To UBound(bkmrk, 2)
Documents(this_doc).Activate
Documents(this_doc).Bookmarks(bkmrk(0, i)).Select
Selection.Copy
Documents(temp_doc).Activate
On Error Resume Next ' if null copied, paste fails - there
should never actually be a null source
Selection.PasteSpecial DataType:=wdPasteText ' there are
colons there, they're just thin in the Windows Mail newsreading font...
On Error GoTo 0
bkmrk(1, i) =
Documents(temp_doc).ComputeStatistics(wdStatisticWords)
Selection.WholeStory
Selection.Delete unit:=wdCharacter, count:=1
wd_total = wd_total + bkmrk(1, i)
Next
Documents(this_doc).Activate

overall, the macro iterates through a set of bookmarks spanning a single
column in a number of tables, places the plain text in another document,
counts the words, stores the results in the bookmark array and then later
writes it out a a log file.
"I have over-ridden the Ctrl-V keyboard shortcut for paste" - hmmm, which
means you have overridden the actual command. You do not just override
the
shortcut, you override the command. I would say that this may have
something
to do with it.

Revised cut/copy/paste macros are all variants of the following, which
serves to generate special journal entries according to where the action
takes place
I have only set new key-bindings - I was unaware that altering a keybinding
causes the underlying command to get redirected too, especially when the
command is not invoked by keystrokes. Can you confirm that's what you are
saying?

Sub myCopy()
If Selection.Type = wdNoSelection Or Selection.Type = wdSelectionIP Then
Exit Sub
If Not (Selection.Document.ActiveWindow.View.SplitSpecial =
wdPaneRevisions And Selection.Document.ActiveWindow.ActivePane.index = 2)
Then
createCutCopyPasteEditPoint
On Error Resume Next
Selection.Copy
If Err.Number <> 0 Then
Application.StatusBar = "Copy Failed!"
End If
On Error GoTo 0
End If
End Sub

If you have any ideas I'd love to hear them

Thanks

Julian
 
J

Jay Freedman

Because you're passing your text through the Windows clipboard, you
could have a timing problem, especially if the documents and/or the
bits of text inside the bookmarks are fairly large.

A solution for this is to use two ranges, one in the document
containing the bookmarks and the other in the temporary document, and
assign the .FormattedText property of one to the .FormattedText
property of the other. This accomplishes the same thing as a
copy/paste without passing through the clipboard -- and probably
without the problem.

The following snippet does the same thing as the code you showed, but
without all the overhead (and with some considerable streamlining in
other areas as well):

Dim this_doc As Document
Dim temp_doc As Document
Dim src_rg As Range
Dim dest_rg As Range
Dim wd_total As Long, i As Long
Dim bkmrk(1, 2) As Variant
' some code to fill column 0 of bkmrk with bookmark names
bkmrk(0, 0) = "bk1"
bkmrk(0, 1) = "bk2"
bkmrk(0, 2) = "bk3"

Set this_doc = ActiveDocument
Set temp_doc = Documents.Add
wd_total = 0
For i = 0 To UBound(bkmrk, 2)
Set src_rg = this_doc.Bookmarks(bkmrk(0, i)).Range
Set dest_rg = temp_doc.Range

' This does a "copy/paste" without the clipboard;
' since it overwrites anything that's already there,
' you don't have to delete it when you're done.
dest_rg.FormattedText = src_rg.FormattedText

bkmrk(1, i) = dest_rg.ComputeStatistics(wdStatisticWords)
wd_total = wd_total + bkmrk(1, i)
Next i

' discard the temp_doc, which reactivates this_doc
temp_doc.Close Savechanges:=wdDoNotSaveChanges
 
J

Julian

Jay - that was *really* useful - thanks.

Jay Freedman said:
Because you're passing your text through the Windows clipboard, you
could have a timing problem, especially if the documents and/or the
bits of text inside the bookmarks are fairly large.

I had *assumed* that copy would not return until it had completed... the
document *is* karge (>750pp, ~6000 comments, ~19MB), and some of the
bookmarks are large (18,000 words excluding associated comments and other
text in adjacent columns) although the 1st bookmark on ~p3 which also fails
is small ~240 words in a 10 row, 5 column table). A timing problem makes a
great deal of sense.
The following snippet does the same thing as the code you showed, but
without all the overhead (and with some considerable streamlining in
other areas as well):

"Considerable streamlining" LOL! My bricks respond to gravity perfectly well
thank you <g> [I'm supposed to be writing, not coding <g>, the supporting
extensions to word I have written for commenting functionality, in-table
outlining, journalling and other stuff do run to several thousand lines...
and yes, I know it shouldn't all be in normal.dot but...]

However, back the problem and potential solution. I tried your code and
found a couple of problems

1/ I said that the bookmarks span a column, but didn't make it clear that
there are several columns... the range based FormattedText assignment takes
all text between the bookmark ends not just that in the bookmarked column...
which I think is why I did a select/copy/paste - bookmark ranges in tables
mark table spanning end-endranges, but when selected, the range corresponds
to the visually inspired expectation (i.e. that a bookmark in one column
contains only what is in that column....)

2a The ComputerStatistics on the result completely kills Word (Word
2002)... seen from the VBE it just locked up, trying again and looking at
Word and manually Ctrl-A selecting all the text that had appeared, I chose
Tools...Word Count and after coming up with a count (incorrect, BTW) Word
then attempted repagination and hung. The only suggestion I had for that was
perhaps the presence of Unicode characters carried across from Col 1...
2b/ When I tried to test that hypothesis Word locked up on the
FormattedText assignment, eating 50% of CPU time. I need to give it more
thought and test data other than the real data... why it worked (as far as
the range assignment) once and never since I have no idea either...

NB Col 1 of my table provides "outlining" capabilities with Unicode block
triangles pointing right and down to indicate collapsed/expanded rows; Cols
3 & 4 are never both "visible" - either one or the other is set to a very
small width and hidden text, and Col 4 may contain embedded images; Col 5
contains the text of interest with maybe 1-50+ potentially
overlapping/intersecting (in scope) comments, which may contain hyperlinks
(and possibly other stuff)

I think it might be easier/quicker just to try getting data from the
clipboard through another object and waiting until that is successful before
doing a paste - if it is a timing issue that approach could confirm and
resolve at the same time... 'tis such things that lead to the atrophy of
elegance...

Or is there some other way to avoid such timing issues - can one make VBA
code sync rather than async?

Very grateful for your input though

Julian
--
 
J

Julian

Update...

Because the range of a bookmarked column spans all cells between the
bookmark ends I went back to Selection.Copy. There are still issues of
"elegance" insofar as in principle I ought to allow for bookmarks that
contain no text... but since there's no way of testing that range directly I
choose to assume pro tem that they are not empty.

Firstly I initialise the clipboard by finding and copying the first non-null
word in the document and placing it on the clipboard, i.e. the clipboard is
not empty and contains text different to the first bookmark text.

Then for each bookmark I use a data object and GetClipText to get the text -
if no error fine, otherwise I set the receviving variable to "" (not
interested in why there might be an error given the above!)

Then select the bookmark and copy...(with Stop on error...) turn off error
trapping and loop on GetFromClipboard while err.number <> 0 (should also
have an emergency get out, but I settled for DoEvents to allow me to Break
into the loop...). Bookmark text (and only the text in that column) is now
on the clipboard

When I can get from the clipboard I do so, and if the text return is not the
same as previously (hence the initialisation) I know I did in fact
successfully copy to the clipboard, so then I assign the text to the
destination range and do the word count...

I can see however that if it were a timing issue this shouldn't have worked
either... but it does, suggesting that perhaps it wasn't timing, that Copy
is synchronous and that it was some other peculiarity...

Anyway, I can now count words again! Takes ~30s to work through the ~34
tables to completion - and the result is right. It's still a brick but at
least it's falling straight down again.

But - if you do know of a better way to deal with a column range directly
let me know!

Thanks for the input - just needed a little prod along...

Julian

PS Would it help if I painted wings on my bricks? <g>
 
J

Jay Freedman

Winged bricks... hmmm... nice idea!

If your code works now, you may not want to mess with it. :)

If you want to pursue improvements in performance, here are some
suggestions. I would still look for a way to avoid the clipboard, because
it's a cross-process call that will always be inefficient. In fact, you
don't need to copy the text to a new document at all, because the Range
object has the same ComputeStatistics method as the Document object has --
in fact, I used it in the code I posted before. So you can call it on any
properly defined Range in the original document, and jettison the whole
copying apparatus.

Now, you're correct about the miserable behavior of Ranges when dealing with
table columns -- a Range extends from the starting point into the next cell
to its right, to the end of the row, into the first cell of the next row,
and so on until the end. There's no such thing as a Range that includes only
a single column in a multicolumn table. So... treat the bookmark as a series
of Range objects, each one covering only a single cell in the desired
column. Iterate down the column, totaling the word counts as you go.

There are a couple of ways to do the iteration. Probably the simplest one is
to find the starting and ending row numbers and just go through the cells in
that series in a For...Next loop.

Dim bkRg As Range, subRg As Range
Dim bkName As String
Dim oTbl As Table
Dim wdct As Long
Dim ncol As Integer
Dim nrowtop As Integer, nrowbot As Integer
Dim nCurrRow As Integer

bkName = "bk2"

If Not ActiveDocument.Bookmarks.Exists(bkName) Or _
(Not ActiveDocument.Bookmarks(bkName).Range _
.Information(wdWithInTable)) Then
Exit Sub
End If

Set bkRg = ActiveDocument.Bookmarks(bkName).Range
Set oTbl = bkRg.Tables(1) ' the table containing the bookmark
ncol = bkRg.Information(wdStartOfRangeColumnNumber)
nrowtop = bkRg.Information(wdStartOfRangeRowNumber)
nrowbot = bkRg.Information(wdEndOfRangeRowNumber)

wdct = 0
For nCurrRow = nrowtop To nrowbot
Set subRg = oTbl.Cell(nCurrRow, ncol).Range
' exclude the end-of-cell marker
subRg.MoveEnd wdCharacter, -1
wdct = wdct + subRg.ComputeStatistics(wdStatisticWords)
Next

MsgBox bkName & " contains " & wdct & " words"

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so
all may benefit.
 
J

Julian

Oh great! Thanks a bunch! There's nothing more annoying that *knowing* - not
merely suspecting - there's a better way to do things but not really being
able to justify the effort of putting something that works into even better
shape <g> {"Let not the best be the enemy of the good")

Missed the fact that the computestatistics applies equally to ranges
otherwise I would have already done the same myself... of course I would,
wouldn't I...? Probably due to finding something that works and then
stopping the search...

See http://berossus.blogspot.com/2007/07/not-last-place-you-look.html for
related insights <g>

Nice work - very nice... expect to be bothered with more tricky Word VBA in
due course!

Now... if we only used the same naming conventions I could just grab your
code and...

Thanks for the help :)

Julian
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top