Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Where does Word use up memory?

Status
Not open for further replies.

ycim

Programmer
Feb 9, 2005
95
0
0
CA
I have an Access program whereby I am importing some information into access from Word documents. The code also looks for certain criteria in the Word documents, and breaks it up into smaller word documents based upon those criteria. A typical word file that I am trying to break down into smaller units could contain as many as 500 different pieces, but each of those pieces is less than a page long (usually).

The code is very intense and quite lengthy so I won't post it all here, but basically, the flow is as follows..

1. Create an object (Set WD = createObject("Word.Application")
2. Open the original word document
3. Find my content in the document
4. Set a range for that content
5. Copy the range
6. Create a new document (and yes, another instance of Word running)
7. Paste in the content
8. Close the new document
9. Return to my original document and find the next piece, and then repeat steps 3-9.

I know that the code that I have is not opening tons of word applications. At most, I only have 2 sessions running.

The program successfully breaks down the documents and it copies about the first 20 documents, and then I get an "out of memory" problem, and I obviously can't get it to go further.


I am not asking anyone to write the code, but rather I am asking for advice on where to start looking for memory leaks? Although I am a power user in both Access and Word, this is my first program that I have accessed Word through VBA (I have done tons of Access VBA programming, though!)

Can anyone suggest where to start my hunt? I am afraid that I simply don't know where to look.

Thanks for your help.
 
I think I would try to do it with a single WDobj however;

I expect the problem lies in stages 6-9 (for which the code may not be very long) can you supply the code you are using there.

Hugh,

 
Code should look something like this:
Code:
Sub aTest()
Dim wordApp1 As Word.Application
Dim wordApp2 As Word.Application
Dim wordDoc1 As Word.Document
Dim wordDoc2 As Word.Document
Dim i As Integer

' Document to copy from
Set wordApp1 = CreateObject("Word.Application")
wordApp1.Visible = True
Set wordDoc1 = wordApp1.Documents.Add

' New application instance to copy to
Set wordApp2 = CreateObject("Word.Application")
wordApp2.Visible = True

For i = 1 To 5
Set wordDoc2 = wordApp2.Documents.Add
'Code here
wordDoc2.Close
Set wordDoc2 = Nothing

Next

wordApp2.Quit
Set wordApp2 = Nothing

wordDoc1.Close
wordApp1.Quit
Set wordDoc1 = Nothing
Set wordApp1 = Nothing
End Sub
 
I don't even understand why you need a second instance at all. You could do it all with just the one.
Code:
Sub aTest()
Dim wordApp As Word.Application

Dim SourceDoc As Word.Document
Dim CopyToDoc As Word.Document

Set wordApp = CreateObject("Word.Application")
wordApp.Visible = True

' Document to copy from
  Set SourceDoc = wordApp.Documents.Open _
       Filename:="[i]sourceDoc name and path[/i]"

' Document to copy to
  Set CopyToDoc = wordApp.Documents.Add

' return to source doc
  SourceDoc.Activate
  'Code here to get range from source doc
  
  With CopyToDoc
    .Activate
    Selection.Paste ' or Range.Paste
    ' assuming you are saving it...
    .SaveAs Filename:=[i]"SaveAs name and path[/i]"
    .Close
  End With
' if there was just the two (Source & CopyToDoc)
' then Source will automatically become Active
' but just in case...
  Source.Activate

' repeat; above could be a iterated loop

SourceDoc.Close
wordApp.Quit
End Sub
You are using Document objects for the source and the copied to documents. Word can easily handle two document objects. Just switch between them. I see no need for a second instance at all.

Gerry
My paintings and sculpture
 
Ooops, that should be:

' but just in case...
SourceDoc.Activate

In any case, could you explain the reasoning for using multiple instances? There may be a legitimate reason, but regarding what you seem to be doing, there is no actual need to do so.

Gerry
My paintings and sculpture
 
There doesn't seem to be any reason for it. As CBasicAsslember has noted in the code, there should be a step 8.5 in the OP where the extraneous instance of Word gets terminated - it is likely that this is where the memory is beng consumed. After all, Word is popular because of its functionality, not for its small footprint...[smile]

Steve

[small]"Every program can be reduced by one instruction, and every program has at least one bug. Therefore, any program can be reduced to one instruction which doesn't work." (Object::perlDesignPatterns)[/small]
 
You might want multiple instances of Word if you only want the user to see one of the documents. Although it probably doesn't apply here, multiple instances can be advantageous if you need to switch back and forth between documents.
 
Huh? You can switch back and forth between documents using one instance.

Dim ThisDoc As Document
Dim ThatDoc As Document
Set ThisDoc = whatever (active doc, open a doc, new etc.)
Set ThatDoc = whatever (active doc, open a doc, new etc.)

Whatever is .Activate is the one the user sees.

I can agree there are circumstances that multiple instances work best, but they are few and far between. Word has always been sloppy with memory (anyone who remembers Word 6.0 can attest to that!), and does not do a very good job of cleaning up after itself. Therefore, IMO, unless absolutely required, multiple instances of Word are to be avoided.

In most cases you do not need multiple instances. Certainly not for any control of documents the user sees. Word can handle multiple documents (including making new ones and using those) quite well.

OK, yes, you could have a second instance (Visible = False) and have a document there the user could not see. Frankly though, I would have to see a business case for that. Especially if you are doing action by code.

Gerry
My paintings and sculpture
 
Some good discussion...I am learning...

Here is how I have opened my instance of Word. Therefore, I believe that only 1 instance is being opened. That takes care of that part, but I still get the out of memory error "around" the same place in the document

Code:
'open an instance of word
    On Error Resume Next
    booCloseWord = False
    Set WD = GetObject(, "word.application")
    On Error GoTo Proc_err
    If TypeName(WD) = "Nothing" Then
      'Word was not open -- create a new instance
      Set WD = CreateObject("Word.Application")
      booCloseWord = True   'used to close word at the end of the application
    End If


'open the original word file

 Dim dlgOpen As FileDialog
    'Set dlgOpen = Access.FileDialog(FileDialogType:=msoFileDialogOpen)
    Set dlgOpen = Access.FileDialog(msoFileDialogFilePicker)
    With dlgOpen
        .AllowMultiSelect = False
        .InitialFileName = Left(DOCPATH, InStrRev(Left(DOCPATH, Len(DOCPATH) - 1), "\")) & "cust supplied docs\"
        .Show
    End With
    
    
    strFileName = dlgOpen.SelectedItems.Item(1)
    Set docOrig = WD.Documents.Open(Filename:=strFileName, ConfirmConversions:=False, _
                ReadOnly:=False, AddToRecentFiles:=False, PasswordDocument:="", _
                PasswordTemplate:="", Revert:=False, WritePasswordDocument:="", _
                WritePasswordTemplate:="", Format:=wdOpenFormatAuto, XMLTransform:="")

From here, I have some code to move around the word documetn looking for my criteria, and marking off my ranges that i need to pick up to copy. There are basically 2 word.ranges that will apply.

The basic movement of the doucment is that it will start at the beginning, and go until it reaches a new header in the document. It will take that header and assign it to PageHeader. Then it finds the next header, bookmarks it, and then assigns everything in between to PageInfo.

Both PageHeader and PageInfo have been declared earlier in the program as
Dim PageHeader as word.range
Dim PageInfo as word.range

This code below assumes that it has found a header which meets the criteria.

The code, in it's entirety below, is part of a do while -loop statment, so it will keep looping until i no longer find any header criteria that i need.

Code:
'I have to do some small modifications to the document.  'Becuase I need it to be one document in it's entirety on 'the "new" document, i need to take out some section breaks 'and page breaks (yes, the users forget to remove them in 'the new document and then wonder why their information 'isn't on the page!


'take out any page and/or section breaks!
    WD.Selection.WholeStory
                    
    With WD.Selection.Find
        .Text = "^m"
        .Replacement.Text = ""
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchCase = False
        .MatchWholeWord = False
        .MatchWildcards = False
        .MatchSoundsLike = False
        .MatchAllWordForms = False
    End With
    WD.Selection.Find.Execute Replace:=wdReplaceAll
    With WD.Selection.Find
        .Text = "^b"
        .Replacement.Text = ""
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchCase = False
        .MatchWholeWord = False
        .MatchWildcards = False
        .MatchSoundsLike = False
        .MatchAllWordForms = False
    End With
    WD.Selection.Find.Execute Replace:=wdReplaceAll
    
    'if this document has track changes on it, we have to accept them all
    WD.ActiveDocument.AcceptAllRevisions


'now go get the information that we need!
Set PageHeader = WD.Selection.Range

'move down 1 line, and bookmark that line as the place the content starts
    WD.Selection.Move unit:=wdLine, Count:=1
    WD.ActiveDocument.Bookmarks.Add Name:="Start_Content", Range:=WD.Selection.Range
    'go back up
    WD.Selection.Move unit:=wdLine, Count:=-1
    WD.Selection.HomeKey
            
    'go find the next heading
    WD.Selection.Goto what:=wdGoToHeading, which:=wdGoToNext
    WD.Selection.EndKey unit:=wdLine
    WD.Selection.HomeKey unit:=wdLine, Extend:=wdExtend
    
    'have to do a check to make sure that it did, indeed, go to a new section
    If PageHeader = WD.Selection Then
        WD.Selection.Goto what:=wdGoToBookmark, Name:="\endofdoc"
        WD.ActiveDocument.Bookmarks.Add Name:="Start_next_Heading", Range:=WD.Selection.Range
    Else
    WD.Selection.HomeKey
    WD.ActiveDocument.Bookmarks.Add Name:="Start_next_Heading", Range:=WD.Selection.Range
    WD.Selection.EndKey unit:=wdLine, Extend:=wdExtend
    Do While WD.Selection.Range.ComputeStatistics(wdStatisticCharacters) = 0
        WD.Selection.Goto what:=wdGoToHeading, which:=wdGoToNext
        WD.Selection.HomeKey
        WD.ActiveDocument.Bookmarks.Add Name:="Start_next_Heading", Range:=WD.Selection.Range
        WD.Selection.EndKey unit:=wdLine, Extend:=wdExtend
    Loop
    End If
    'assign pageinfo variable
    WD.Selection.HomeKey
    Set PageInfo = WD.ActiveDocument.Range(Start:=WD.ActiveDocument.Bookmarks("Start_Content").Range.Start, End:=WD.ActiveDocument.Bookmarks("Start_Next_Heading").Range.End)

'now go create this document
    If PageInfo.ComputeStatistics(wdStatisticCharacters) > 1 Then
        Set DocNew = WD.Documents.Add()
        
        docOrig.Activate
        PageInfo.Copy
        
        DocNew.Range.Paste
        'take out any indentations
        DocNew.Range.Select
        
        'check to see if there is a blank line in the beginning or at the end (this will inhibit the OUTDENT process and users also forget to remove them)
        WD.Selection.Collapse wdCollapseStart
        WD.Selection.EndKey wdLine, wdExtend
        Do While Len(WD.Selection) <= 1
            WD.Selection.Delete
            WD.Selection.EndKey wdLine, wdExtend
        Loop
        WD.Selection.WholeStory
        WD.Selection.Collapse wdCollapseEnd
        WD.Selection.Move wdParagraph, Count:=-1
        Do Until Len(WD.Selection) > 1
            WD.Selection.EndKey wdLine, wdExtend
            If Len(WD.Selection) = 1 Then
                WD.Selection.TypeBackspace
                WD.Selection.Move wdParagraph, Count:=-1
            End If
        Loop
        WD.Selection.WholeStory
        'no automatic indenting is allowed!
        WD.Selection.Paragraphs.Outdent
        WD.Selection.Paragraphs.Outdent
        WD.Selection.Paragraphs.Outdent
        
        'determine the new filename for this data.  The new filename is built up of a sequential number that the user has no control over.  It always must be 6 digits long, and marked version 1 at the end.  If the user needs to access this file, it will be later modified through the access program.
        strFileName = (Forms!frmmasterdata!FormNumber + 1)
        Forms!frmmasterdata!FormNumber = Forms!frmmasterdata!FormNumber + 1
        'the form number must be 6 digits long!
        Do While Len(strFileName) < 6
           strFileName = "0" & strFileName
        Loop
        
        DocNew.SaveAs EDITPATH & Me.txtDocNumber & "-" & strFileName & "v1.doc"
        strFileName = Me.txtDocNumber & "-" & strFileName & "v1.doc"
        DocNew.Close
    Else
        strFileName = ""

    End If

After this is loops back and starts all over again (i never revisit the opening of the word document code portion). Where it always "blows up" is on the same line, and "about-ish" the same place in the document.

I get this message...
Word has insufficient memory. You will not be able to undo this action once it is completed. Do you want to continue?

Then Access locks up, and I have to kill it (through Task Manager) before I can even do anything further.

So...this is a long message, but can you contribute to me what might be happening here?

Thanks for your time and help...
 
Jerry,
Huh? You can switch back and forth between documents using one instance.
I use duel monitors and often will have 2 instances open (one on each monitor) so I only have to click from one to the other. This is especially useful when automating a Word document as it allows me to easily check formatting and such with the original.

ycim,

Way too much code. For future reference trim out the stuff that isn't relevant. My approach would be to open task manager and step through the code. You should be able to see if you are creating multiple instances of WINWORD.EXE.
 
I have done that already. Task manager is only reporting 1 instance of Word.

I honestly don't beleive it is how many instances of Word running. That part is pretty tight in the code, and upon doing the step through, task manager is still showing 1 instance.

The reason I gave so much code (although I was hesitant to do so), was so that you could see the about of accessing to Word. Something I am doing it eating up the memory, but it behooves me what that might be.

Any other suggestions?
 
Can I assume that you have made the application visible and checked to see how many documents are open? Also, near the end of your code clear the memory:
Code:
strFileName = Me.txtDocNumber & "-" & strFileName & "v1.doc"
DocNew.Close
' Add this line
Set DocNew = Nothing
 
Good assumption! You are correct!
:)

 
Wait a minute! Just had a thought! Could it be the clipboard? I do an awful lot of copy > paste, and I don't empty the clipboard! DOH! Could it be that simple?

Hmm...I have never had to access the clipboard before. I will have to research and find out how to do that.

Stay tuned...

 
I would try remming out portions of your code until you find the cause.
e.g. Does the code run completely if you rem out stages 6, 7 and 8? If yes rem out stage 7 and try again etc.

I would prefer to see a
Set DocNew = Nothing
after your DocNew.Close

<<Task manager is only reporting 1 instance of Word>> I think it unlikely you have more than one instance running however; Is that on Task manager's Application tab or the Process tab. When testing (and crashing) such code it is likely that an instance of Word will get left behind, remove it via Task Manager - Processes or reboot befor the next test.

Ther have been a couple of posts since I began this; anyway...

Hugh
 
It IS the clipboard!

If I manually clear it out it is good...

Now the question is...can anyone save me some time (from srouring help file and this site) by telling me how to clear it?
 
Thanks...that looked easy! However, I don't seem to have "cutcopymode" as an option!

Is it possible I am missing a reference? Further research on this board shows that I have to load MS FORMS reference, but that is not an option on my reference list!

What am I missing here?
 
The application would be the Word application. Try:
Code:
WD.CutCopyMode = False
Don't forget to set it back to True.

Upon further review, it might be a better idea to set what you want to copy to some temp variable and then insert it.
 
WD.cutcopymode is not recognized. (method or datamember not found).

Is this a reference thing?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top