Opinion from Doug Robbins and Peter Hewett Requested

G

Greg Maxey

Doug, Peter

I have mauled together a few of your macros in an attempt to:

Find and Replace in a Batch of Files
Find and Replace multiple expressions using a Word List
Find and Replace in all story ranges

Follows is the code that appears to work but seems very slow (could just be
my ancient computer)
Your comments are invited for improvements.

Public Sub BatchFileMultiFindReplace()

'Macro by Doug Robbins - 1st March 2004
'with additional input from Peter Hewett to replace text in all the
documents in a folder
'and input from Greg Maxey to faclitate using a table for multiple find and
replace words

Dim myFile As String
Dim PathToUse As String
Dim myDoc As Document
Dim rngstory As Word.Range

'Close any documents that may be open
If Documents.Count > 0 Then
Documents.Close SaveChanges:=wdPromptToSaveChanges
End If
' Get the folder containing the files
With Dialogs(wdDialogCopyFile)
If .Display <> 0 Then
PathToUse = .Directory
Else
MsgBox "Cancelled by User"
Exit Sub
End If
End With

If Left(PathToUse, 1) = Chr(34) Then
PathToUse = Mid(PathToUse, 2, Len(PathToUse) - 2)
End If

myFile = Dir$(PathToUse & "*.doc")

While myFile <> ""
'Open each file and make the replacement
Set myDoc = Documents.Open(PathToUse & myFile)
'Fix the skipped blank Header/Footer problem
MakeHFValid
'Iterate through all story types in the current document
For Each rngstory In ActiveDocument.StoryRanges
'Iterate through all linked stories
Do
SearchAndReplaceInStory rngstory
' Get next linked story (if any)
Set rngstory = rngstory.NextStoryRange
Loop Until rngstory Is Nothing
Next
'Close the file, saving the changes.
myDoc.Close SaveChanges:=wdSaveChanges
myFile = Dir$()
Wend
End Sub
Public Sub SearchAndReplaceInStory(ByVal rngstory As Word.Range)
'This routine supplied by Peter Hewett and modified by Greg Maxey

Dim Source As Document
Dim i As Integer
Dim Find As Range
Dim Replace As Range
Set Source = ActiveDocument
' Change the path and filename in the following to suit where you have your
list of words
Set WordList = Documents.Open(FileName:="D:\My Documents\Word Documents\Word
Tips\Find and Replace List.doc")
Source.Activate
'I stetted out thsi Do line because it appeard to be redundant form the main
macro
'Do Until (rngstory Is Nothing)
For i = 2 To WordList.Tables(1).Rows.Count
Set Find = WordList.Tables(1).Cell(i, 1).Range
Find.End = Find.End - 1
Set Replace = WordList.Tables(1).Cell(i, 2).Range
Replace.End = Replace.End - 1
With rngstory.Find
..ClearFormatting
..Replacement.ClearFormatting
..Text = Find
..Replacement.Text = Replace
..Forward = True
..Wrap = wdFindContinue
..Format = False
..MatchCase = False
..MatchWholeWord = False
..MatchAllWordForms = False
..MatchSoundsLike = False
..MatchWildcards = False
..Execute Replace:=wdReplaceAll
End With
Next i
'Stetted out the follow for same reason
'Set rngstory = rngstory.NextStoryRange
'Loop

End Sub
Public Sub MakeHFValid()
'And this too
Dim lngJunk As Long
' It does not matter whether we access the Headers or Footers property.
' The critical part is accessing the stories range object
lngJunk = ActiveDocument.Sections(1).Headers(1).Range.StoryType
End Sub
 
K

Klaus Linke

I did not mean to exclude participation by others with the Subject line.


Hi Greg,

In that case ;-) ...

You could read the find/replace list into an array at the start:

Dim ListArray
Dim WordList As Document
' Change the path and filename in the following to suit where you have your
list of words
Set WordList = Documents.Open(FileName:="C:\Mist.doc")
ListArray = WordList.Tables(1).Range.Text
ListArray = Split(ListArray, Chr(13) & Chr(7))
WordList.Close

You'd then pass ListArray to the sub SearchAndReplaceInStory, and change
the replacement code to

For i = LBound(ListArray) To UBound(ListArray) - 1 Step 3
With rngstory.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ListArray(i)
.Replacement.Text = ListArray(i + 1)
' ...

That should be faster than reading the .Find.Text and
..Find.Replacement.Text from the table each time for each document and each
story range.


I'd also change
myFile = Dir$(PathToUse & "*.doc")
to
myFile = Dir$(PathToUse & "*.*")

That way, the macro can deal with other formats (*.rtf, *.html, ...) as
well.

A question of taste: I'd probably have set MatchCase to True, and perhaps
MatchWholeWord also.

Regards,
Klaus
 
G

Greg Maxey

Klause,

I knew that I would eat crow for the subject line :)
The
You'd then pass ListArray has me stumped. I am clueless.

Here is what I have now which generates a run time error 13 type mismatch on
this line:
For i = LBound(ListArray) To UBound(ListArray) - 1 Step 3


Public Sub Test()

'Macro by Doug Robbins - 1st March 2004
'with additional input from Peter Hewett to replace text in all the
documents in a folder
'and input from Greg Maxey to faclitate using a table for multiple find and
replace words

Dim myFile As String
Dim PathToUse As String
Dim myDoc As Document
Dim rngstory As Word.Range
Dim ListArray
Dim WordList As Document

'Close any documents that may be open
If Documents.Count > 0 Then
Documents.Close SaveChanges:=wdPromptToSaveChanges
End If
' Change the path and filename in the following to suit where you have your
list of words
Set WordList = Documents.Open(FileName:="D:\My Documents\Word Documents\Word
Tips\Find and Replace List.doc")
ListArray = WordList.Tables(1).Range.Text
ListArray = Split(ListArray, Chr(13) & Chr(7))
WordList.Close
' Get the folder containing the files
With Dialogs(wdDialogCopyFile)
If .Display <> 0 Then
PathToUse = .Directory
Else
MsgBox "Cancelled by User"
Exit Sub
End If
End With

If Left(PathToUse, 1) = Chr(34) Then
PathToUse = Mid(PathToUse, 2, Len(PathToUse) - 2)
End If

myFile = Dir$(PathToUse & "*.*")

While myFile <> ""
'Open each file and make the replacement
Set myDoc = Documents.Open(PathToUse & myFile)
'Fix the skipped blank Header/Footer problem
MakeHFValid
'Iterate through all story types in the current document
For Each rngstory In ActiveDocument.StoryRanges
'Iterate through all linked stories
Do
SearchAndReplaceInStory rngstory
' Get next linked story (if any)
Set rngstory = rngstory.NextStoryRange
Loop Until rngstory Is Nothing
Next
'Close the file, saving the changes.
myDoc.Close SaveChanges:=wdSaveChanges
myFile = Dir$()
Wend

End Sub
Public Sub SearchAndReplaceInStory(ByVal rngstory As Word.Range)
'This routine supplied by Peter Hewett and modified by Greg Maxey

Dim Source As Document
Dim i As Integer
Dim Find As Range
Dim Replace As Range
Set Source = ActiveDocument
Source.Activate
For i = LBound(ListArray) To UBound(ListArray) - 1 Step 3
With rngstory.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ListArray(i)
.Replacement.Text = ListArray(i + 1)
..Wrap = wdFindContinue
..Format = False
..MatchCase = True
..MatchWholeWord = False
..MatchAllWordForms = False
..MatchSoundsLike = False
..MatchWildcards = False
..Execute Replace:=wdReplaceAll
End With
Next i
End Sub
Public Sub MakeHFValid()
'And this too
Dim lngJunk As Long
' It does not matter whether we access the Headers or Footers property.
' The critical part is accessing the stories range object
lngJunk = ActiveDocument.Sections(1).Headers(1).Range.StoryType
End Sub
 
K

Klaus Linke

You'd then pass ListArray has me stumped. I am clueless.

You'd pass ListArray as an argument:

Do
SearchAndReplaceInStory rngstory, ListArray
' ...

Public Sub SearchAndReplaceInStory(ByVal rngstory As Word.Range, ByRef
ListArray as Variant)

Regards,
Klaus
 
P

Peter Hewett

Hi Greg Maxey

See my follow up post in this NG to Ann Scharpf's post <Massive Search & Replace in Word>.
It's highly relevant to this. Namely:

Using Word to perform lots of operation on lots of files consecutively (without starting
and stopping Word) can make Word *very* unstable. So you either have to break the
documents down into small batches of use a VB Script to start and stop Word for each
operation. Also, I view a log file for this type of operation as mandatory. If the batch
updates I'm doing are critical (and when are they not?) I even log the number of non zero
changes for each Search/Replace. Who gives that you end up with a verbose log file.

One of the major reason this so-and-so slow is that it iterates *all* StroryRanges, the
good news is that Word 2003 has 5 extra new StoryRanges so it can now be even sloooower
than ever!!!

I'll take the path that does it reliably and thoroughly every time.

Klaus's ListArray is just a two dimensional array passed in using a Variant (which can of
course handle any data type) so don't let it flumox you. In my methodology of processing
single document there's no real benefit, and anyway, there's so many slow Search and
Replace operations going on you'll never see the performance advantage of using an array.

HTH + Cheers - Peter
 
G

Greg Maxey

Peter,

You along with Doug and Klaus are in the big league while I play in the sand
lot (or worse yet, sand box). I sometimes manage to combine and copy
existing code that you fellows provide or add a bell and whistle here in
there but I haven't a clue how to do from scratch any of the the processes
you mention (except of couse make small batches).

I will certainly have a look at the post you sent Ann as it was here
question that got me interested to begin with.

With another follow up by Klaus I managed to get the code with the array
working. Since it works I will leave well enough alone ;-)

Perhaps I will bug you guys in the future for small hints how to start and
stop Word between operations and how to create the log file.

The "slowes" is an annoyance. I played around with a vbMsgbox result and IF
statement in an attempt to do a quick search just on the Main Text.
Something like "Do you want to replace in the main text only?" IF vbResult
is vbYes Then Set rngstory = wdMainTextStory...

Alas no joy.

Thanks Peter, you are always a great help
 
J

Jean-Guy Marcil

Bonjour,

Dans son message, < Peter Hewett > écrivait :
In this message, < Peter Hewett > wrote:

(...)
||
|| One of the major reason this so-and-so slow is that it iterates *all*
StroryRanges, the
|| good news is that Word 2003 has 5 extra new StoryRanges so it can now be
even sloooower
|| than ever!!!
||
(...)

I haven't had a chance to install 2003 yet, still waiting for an opportunity
that will combine no work, money and time so that I can rebuild my machine
from scratch...
Anyway, you got me curious here. What are those 5 new storyranges? Have they
got anything to do with the new protection features?

--
Salut!
_______________________________________
Jean-Guy Marcil - Word MVP
(e-mail address removed)
Word MVP site: http://www.word.mvps.org
 
P

Peter Hewett

Hi Greg Maxey

The log file actually dead easy. Add a class module to your project and using the
Property window in the VBA IDE change its name from "Class1" to "LogIt", then cut and
paste the following code into the Logit Class:

<===========Start of Class Module LogIt================>
Option Explicit

Private mstrFile As String
Private mstrPath As String
Private mboolEnabled As Boolean
Private mboolTimeStamp As Boolean

Public Sub Output(ByVal strText As String)
Const cProcedureName As String = "Log::Output"

Dim hFile As Long

On Error GoTo OutputError

' Everything must be right for us to log the passed text
If CanOutput Then

' Prefix log text with timestamp
If TimeStamp Then strText = Now & vbTab & strText

' Open log file for append
hFile = FreeFile
Open mstrPath & mstrFile For Append Access Write As hFile

' Write the output and tidy up
Print #hFile, strText
Close hFile
End If

OutputExit:
Exit Sub

OutputError:
mboolEnabled = False
Err.Raise Err.Number, cProcedureName, Err.Description
End Sub ' Output

Public Sub Kill()
On Error GoTo HandleError

' Delete the current log file if it exists
If Len(Dir$(mstrPath & mstrFile)) > 0 Then
Kill mstrPath & mstrFile
End If

ExitHere:
Exit Sub

HandleError:
mboolEnabled = False
Err.Raise vbObjectError + 8001, "Log::Kill", _
Err.Description
Resume ExitHere
End Sub ' Reset

Public Property Get Path() As String
Path = mstrPath
End Property
Public Property Let Path(ByVal strPath As String)
strPath = Trim$(strPath)
If Right$(strPath, 1) <> "\" Then strPath = strPath & "\"
mstrPath = Trim$(strPath)
End Property

Public Property Get File() As String
File = mstrFile
End Property
Public Property Let File(ByVal FileName As String)
mstrFile = FileName
End Property

Public Property Get Enabled() As Boolean
Enabled = mboolEnabled
End Property
Public Property Let Enabled(ByVal EnableLogFile As Boolean)
mboolEnabled = EnableLogFile
End Property

Public Property Get TimeStamp() As Boolean
TimeStamp = mboolTimeStamp
End Property
Public Property Let TimeStamp(ByVal TimeStampOutput As Boolean)
mboolTimeStamp = TimeStampOutput
End Property

Public Property Get CanOutput() As Boolean
CanOutput = LenB(Path) > 0 And LenB(File) > 0 And Enabled
End Property

Private Sub Class_Initialize()
Me.Enabled = True
End Sub
<============End of Class Module LogIt================>

Now you then need to create an instance of your class (called instantiation) so that you
can use the class. Add this code to a standard Module:

Private mlogFile As LogIt

Public Sub DoSomething()

' Create a new logfile using the Folder and Name specified
Set mlogFile = New LogIt
mlogFile.Path = "f:\templates\test documents\"
mlogFile.File = "change log.txt"

' Write something to the log file
mlogFile.Output "Current document: " & ActiveDocument.FullName
mlogFile.Output "Uses template: " & ActiveDocument.AttachedTemplate.FullName
End Sub

I think you get the idea. Rather than use a standard Search and Replace procedure I use
one with a counter, that counts the number of changes made. This of course makes it
slower but does enable a comprehensive log to be produced.

Cheers - Peter

Peter,

You along with Doug and Klaus are in the big league while I play in the sand
lot (or worse yet, sand box). I sometimes manage to combine and copy
existing code that you fellows provide or add a bell and whistle here in
there but I haven't a clue how to do from scratch any of the the processes
you mention (except of couse make small batches).

I will certainly have a look at the post you sent Ann as it was here
question that got me interested to begin with.

With another follow up by Klaus I managed to get the code with the array
working. Since it works I will leave well enough alone ;-)

Perhaps I will bug you guys in the future for small hints how to start and
stop Word between operations and how to create the log file.

The "slowes" is an annoyance. I played around with a vbMsgbox result and IF
statement in an attempt to do a quick search just on the Main Text.
Something like "Do you want to replace in the main text only?" IF vbResult
is vbYes Then Set rngstory = wdMainTextStory...

Alas no joy.

Thanks Peter, you are always a great help

HTH + Cheers - Peter
 
P

Peter Hewett

Hi Jean-Guy Marcil

I don't have 2003 installed at the moment (again, though I had it for a couple of weeks!).
Here they are as you can see they don't relate to protection:

wdEndNoteContinuationNoticeStory
wdEndNoteContinuationSeparatorStory
wdEndNoteSeparatorStory
wdFootNoteContinuationNoticeStory
wdFootNoteContinuationSeparatorStory
wdFootNoteSeparatorStory

As you will see from the above not only am I illiterate but I'm innumerate as well. I
never could manage to count pass 5. So my abject and unabashed apologies as there are of
course 6 new StroryRanges.

Cheers - Peter


Bonjour,

Dans son message, < Peter Hewett > écrivait :
In this message, < Peter Hewett > wrote:

(...)
||
|| One of the major reason this so-and-so slow is that it iterates *all*
StroryRanges, the
|| good news is that Word 2003 has 5 extra new StoryRanges so it can now be
even sloooower
|| than ever!!!
||
(...)

I haven't had a chance to install 2003 yet, still waiting for an opportunity
that will combine no work, money and time so that I can rebuild my machine
from scratch...
Anyway, you got me curious here. What are those 5 new storyranges? Have they
got anything to do with the new protection features?

HTH + Cheers - Peter
 
K

Klaus Linke

Hi Peter,
Klaus's ListArray is just a two dimensional array
(one-dimensional)

passed in using a Variant (which can of course handle any data type)
so don't let it flumox you. In my methodology of processing
single document there's no real benefit, and anyway, there's so many
slow Search and Replace operations going on you'll never see the
performance advantage of using an array.

I agree that the actual replacements will tend to take much, perhaps most,
of the time.
But don't forget that the old code constantly switched between the open doc
and the doc with the replacement table.
In fact, the speed doubled for me (may depend heavily on the length of the
replacement table, number of docs, number of actual replacements ...).
As a side benefit, the changed code makes it easier to view the opened docs
on screen.

As you said in the other thread, it's good to put an
ActiveDocument.UndoClear after each find/replace, if you have very many
replacements in your replacement table. In any case, I would never use the
macro on the original documents. Better work on copies. Not only because
the macro might crash, but also because you might discover later that the
replacement options (match whole word, match case, ...) have led to
undesirable results.

Regards,
Klaus
 
J

Jean-Guy Marcil

Bonjour,

Dans son message, < Peter Hewett > écrivait :
In this message, < Peter Hewett > wrote:

|| Hi Jean-Guy Marcil
||
|| I don't have 2003 installed at the moment (again, though I had it for a
couple of weeks!).
|| Here they are as you can see they don't relate to protection:
||
|| wdEndNoteContinuationNoticeStory
|| wdEndNoteContinuationSeparatorStory
|| wdEndNoteSeparatorStory
|| wdFootNoteContinuationNoticeStory
|| wdFootNoteContinuationSeparatorStory
|| wdFootNoteSeparatorStory

Thanks, I guess the goal is to give more flexibility in the handling of
end/foot notes...

||
|| As you will see from the above not only am I illiterate but I'm
innumerate as well. I
|| never could manage to count pass 5. So my abject and unabashed apologies
as there are of
|| course 6 new StroryRanges.
||

lol
At least you make up for it by being a creative writer!

--
Salut!
_______________________________________
Jean-Guy Marcil - Word MVP
(e-mail address removed)
Word MVP site: http://www.word.mvps.org
 
J

Jonathan West

Peter Hewett said:
Hi Greg Maxey

See my follow up post in this NG to Ann Scharpf's post <Massive Search & Replace in Word>.
It's highly relevant to this. Namely:

Using Word to perform lots of operation on lots of files consecutively (without starting
and stopping Word) can make Word *very* unstable. So you either have to break the
documents down into small batches of use a VB Script to start and stop Word for each
operation.

That is not universally true.

I'll accep that it is true running Word 97 on Windows 98. But I have written
and run macros that spent an entire weekend unattended doing intensive
document processing on about 30k pages of documents, using Word 2000 on
Windows 2000. The same process running in Word 97 under Windows 98 dies with
"out of memory" errors after about 3 hours.

That said, even under Word2K/Win 2K or later you do need to take certain
precautions when creating a macro that does such a long process, in order to
avoid the alert regarding the Undo stack, avoid "document too complex"
alerts or even out of memory errors. Performing intermediate saves at
regular intervals is usually sufficient.
 
P

Peter Hewett

Hi Klaus Linke

I've been tinkering with this kind of stuff over many years for various clients and I've
sort-of distilled it now. The other day I had to pull something together for a client
where I needed to programmatically changes the Project References in 400+ templates.
Because the operation was what I'd class as relatively trivial I didn't create a shell but
used Application.FileSearch and Opened/Modified the document. Even then it still did not
stop Word (XP SP3) crashing 3-4 times!!

So the message here is that I should have done what I normally do:

1. Create a VB Script using FileSystemObject to create a list of files to be modified
(*always* a copy of the originals)
2. For each file that is to be processed Instantiate Word, open, process and save the
document (if necessary) and then destroy the Word instance.
3. Because I don't like VBScript all that much (you've probably read my rants about
variants) I create a Word AddIn to do the actual processing, I trigger it using the
DocumentOpen event.

This way I overcome the frequent Word stability problems, and because I reload any data
each time Word is instantiated there are no performance benefits. But I agree in pure
programming esthetics an array of UDTs is a nice way to handle it - even with *no*
performance benefits. Sometimes doing it right just feels good!

HTH + Cheers - Peter
 
P

Peter Hewett

Hi Jonathan

I don't mean "document to complex", etc. errors I mean application crashes (as in ooops
where did Word go?) or hangs.

I think you know me well enough to trust that I'm careful with these things. I've got an
application with a client that uses 3 PCs and churns out 6-7,000 documents a day (printed,
faxed, imaged, emailed). But every now and then one of them just hangs and believe me it
aint the code!

So I stand by what I said - the bullet proof way is to Instantiate Word use it and then
dispose of it - for each document! In my experience this rather laborious approach does
obviate the nasties. Lets be realistic about this it wasn't what they built Word for even
if we use it like we do!

Cheers - Peter


That is not universally true.

I'll accep that it is true running Word 97 on Windows 98. But I have written
and run macros that spent an entire weekend unattended doing intensive
document processing on about 30k pages of documents, using Word 2000 on
Windows 2000. The same process running in Word 97 under Windows 98 dies with
"out of memory" errors after about 3 hours.

That said, even under Word2K/Win 2K or later you do need to take certain
precautions when creating a macro that does such a long process, in order to
avoid the alert regarding the Undo stack, avoid "document too complex"
alerts or even out of memory errors. Performing intermediate saves at
regular intervals is usually sufficient.

HTH + Cheers - Peter
 
G

Greg

Peter,

<<I think you get the idea.

You assume too much. I don't get the idea. I managed to
create the class module well enough but when you shifted
to generalizations the fog rolled in:

<<Now you then need to create an instance of your class
(called instantiation) so that you can use the class. Add
this code to a standard Module:

What Standard Module? Do you mean the same module that
has the find and replace macro?

Private mlogFile As LogIt

Public Sub DoSomething()

' Create a new logfile using the Folder and Name cified
Set mlogFile = New LogIt
mlogFile.Path = "f:\templates\test documents\"
mlogFile.File = "change log.txt"

' Write something to the log file
mlogFile.Output "Current document: " &
ActiveDocument.FullName
mlogFile.Output "Uses template: " &
ActiveDocument.AttachedTemplate.FullName
End Sub






-----Original Message-----
Hi Greg Maxey

The log file actually dead easy. Add a class module to your project and using the
Property window in the VBA IDE change its name
from "Class1" to "LogIt", then cut and
paste the following code into the Logit Class:

<===========Start of Class Module LogIt================>
Option Explicit

Private mstrFile As String
Private mstrPath As String
Private mboolEnabled As Boolean
Private mboolTimeStamp As Boolean

Public Sub Output(ByVal strText As String)
Const cProcedureName As String = "Log::Output"

Dim hFile As Long

On Error GoTo OutputError

' Everything must be right for us to log the passed text
If CanOutput Then

' Prefix log text with timestamp
If TimeStamp Then strText = Now & vbTab & strText

' Open log file for append
hFile = FreeFile
Open mstrPath & mstrFile For Append Access Write As hFile

' Write the output and tidy up
Print #hFile, strText
Close hFile
End If

OutputExit:
Exit Sub

OutputError:
mboolEnabled = False
Err.Raise Err.Number, cProcedureName, Err.Description
End Sub ' Output

Public Sub Kill()
On Error GoTo HandleError

' Delete the current log file if it exists
If Len(Dir$(mstrPath & mstrFile)) > 0 Then
Kill mstrPath & mstrFile
End If

ExitHere:
Exit Sub

HandleError:
mboolEnabled = False
Err.Raise vbObjectError + 8001, "Log::Kill", _
Err.Description
Resume ExitHere
End Sub ' Reset

Public Property Get Path() As String
Path = mstrPath
End Property
Public Property Let Path(ByVal strPath As String)
strPath = Trim$(strPath)
If Right$(strPath, 1) <> "\" Then strPath = strPath & "\"
mstrPath = Trim$(strPath)
End Property

Public Property Get File() As String
File = mstrFile
End Property
Public Property Let File(ByVal FileName As String)
mstrFile = FileName
End Property

Public Property Get Enabled() As Boolean
Enabled = mboolEnabled
End Property
Public Property Let Enabled(ByVal EnableLogFile As Boolean)
mboolEnabled = EnableLogFile
End Property

Public Property Get TimeStamp() As Boolean
TimeStamp = mboolTimeStamp
End Property
Public Property Let TimeStamp(ByVal TimeStampOutput As Boolean)
mboolTimeStamp = TimeStampOutput
End Property

Public Property Get CanOutput() As Boolean
CanOutput = LenB(Path) > 0 And LenB(File) > 0 And Enabled
End Property

Private Sub Class_Initialize()
Me.Enabled = True
End Sub
<============End of Class Module LogIt================>

Now you then need to create an instance of your class
(called instantiation) so that you
can use the class. Add this code to a standard Module:

Private mlogFile As LogIt

Public Sub DoSomething()

' Create a new logfile using the Folder and Name specified
Set mlogFile = New LogIt
mlogFile.Path = "f:\templates\test documents\"
mlogFile.File = "change log.txt"

' Write something to the log file
mlogFile.Output "Current document: " & ActiveDocument.FullName
mlogFile.Output "Uses template: " & ActiveDocument.AttachedTemplate.FullName
End Sub

I think you get the idea. Rather than use a standard
Search and Replace procedure I use
 
A

Ann Scharpf

Peter:

As I stated in my original post on the Word DOCUMENT
MANAGEMENT page, I don't know Visual Basic. So, I am
going to use whatever macro file Greg has finished and be
eternally grateful because it will be orders of magnitude
better than having our word processing team try to do
about 5,000 search & replace functions manually.

So, if there is no log file and I have the word processing
team run the replacement macros with Track Changes turned
on, do you think that will serve as an adequate log?

I really appreciate the input that all of you have made on
Greg's attempt to help me out!

Ann Scharpf
Systems Analyst/Help Developer
Planned Systems International
-----Original Message-----
Hi Greg Maxey

The log file actually dead easy. Add a class module to your project and using the
Property window in the VBA IDE change its name
from "Class1" to "LogIt", then cut and
paste the following code into the Logit Class:

<===========Start of Class Module LogIt================>
Option Explicit

Private mstrFile As String
Private mstrPath As String
Private mboolEnabled As Boolean
Private mboolTimeStamp As Boolean

Public Sub Output(ByVal strText As String)
Const cProcedureName As String = "Log::Output"

Dim hFile As Long

On Error GoTo OutputError

' Everything must be right for us to log the passed text
If CanOutput Then

' Prefix log text with timestamp
If TimeStamp Then strText = Now & vbTab & strText

' Open log file for append
hFile = FreeFile
Open mstrPath & mstrFile For Append Access Write As hFile

' Write the output and tidy up
Print #hFile, strText
Close hFile
End If

OutputExit:
Exit Sub

OutputError:
mboolEnabled = False
Err.Raise Err.Number, cProcedureName, Err.Description
End Sub ' Output

Public Sub Kill()
On Error GoTo HandleError

' Delete the current log file if it exists
If Len(Dir$(mstrPath & mstrFile)) > 0 Then
Kill mstrPath & mstrFile
End If

ExitHere:
Exit Sub

HandleError:
mboolEnabled = False
Err.Raise vbObjectError + 8001, "Log::Kill", _
Err.Description
Resume ExitHere
End Sub ' Reset

Public Property Get Path() As String
Path = mstrPath
End Property
Public Property Let Path(ByVal strPath As String)
strPath = Trim$(strPath)
If Right$(strPath, 1) <> "\" Then strPath = strPath & "\"
mstrPath = Trim$(strPath)
End Property

Public Property Get File() As String
File = mstrFile
End Property
Public Property Let File(ByVal FileName As String)
mstrFile = FileName
End Property

Public Property Get Enabled() As Boolean
Enabled = mboolEnabled
End Property
Public Property Let Enabled(ByVal EnableLogFile As Boolean)
mboolEnabled = EnableLogFile
End Property

Public Property Get TimeStamp() As Boolean
TimeStamp = mboolTimeStamp
End Property
Public Property Let TimeStamp(ByVal TimeStampOutput As Boolean)
mboolTimeStamp = TimeStampOutput
End Property

Public Property Get CanOutput() As Boolean
CanOutput = LenB(Path) > 0 And LenB(File) > 0 And Enabled
End Property

Private Sub Class_Initialize()
Me.Enabled = True
End Sub
<============End of Class Module LogIt================>

Now you then need to create an instance of your class
(called instantiation) so that you
can use the class. Add this code to a standard Module:

Private mlogFile As LogIt

Public Sub DoSomething()

' Create a new logfile using the Folder and Name specified
Set mlogFile = New LogIt
mlogFile.Path = "f:\templates\test documents\"
mlogFile.File = "change log.txt"

' Write something to the log file
mlogFile.Output "Current document: " & ActiveDocument.FullName
mlogFile.Output "Uses template: " & ActiveDocument.AttachedTemplate.FullName
End Sub

I think you get the idea. Rather than use a standard
Search and Replace procedure I use
 
P

Peter Hewett

Hi Greg

If you're not sure what going on create a new Template/Project and add the Class Module
code and the standard module code. Set a breakpoint at the statement "Set mlogFile = New
LogIt" and single step through the code using F8. This is a *really* good way of getting
to grips with what's going on (and it isn't as bad as you might think). Once you're
comfortable with what the codes doing integrate it into the larger project. This is
generally the way all larger pieces of code are built up. It much easier to abstract and
test the functionality of code (where possible) outside of a heap of other code, it allows
you to focus on just the one thing.

HTH + Cheers - Peter
 

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