MS Office Forum / Word / Programming / January 2005
My VB6 MS Word VBA code crashes Word - Word97
|
|
Thread rating:  |
Angus Comber - 10 Jan 2005 13:20 GMT Hello
My Visual Basic application writes some text to a Word document. It usually works but on two computers on a site it crashes my application.
Anyone got any ideas on where to start looking. Is removing Word, cleaning up and re-installing the only option?
I am going on site to run the same VBA code from Excel to see if that works. I can then check line for line what happens.
The basic code is here:
Private Sub cmdTestWord_Click()
Dim iSequence As Integer 'used to locate correct contact Dim sDear As String 'Salutation on letter Dim sTemp As String 'temp string to create address
Dim sLetter1 As String Dim sLetter2 As String Dim sLetter3 As String Dim sSalute1 As String Dim sSalute2 As String Dim sSalute3 As String
Dim Title As String Dim Firstname As String Dim Initials As String Dim Surname As String Dim strPosition As String Dim Familiar As String Dim Ind As Integer Dim ret As Long Dim strToWord As String
'On Error GoTo WordErr 'Time bootup 'Dim dblSTime As Double 'dblSTime = Timer
'dblSTime = (Timer - dblSTime) 'MsgBox "Time taken: " & dblSTime 'Debug.Print "Time taken: " & dblSTime
Dim objWord As Word.Application Dim objWordDoc As Word.Document '''''''''''''''''**** Dim docspath As String
'Time loading of Word 'Dim dblSTime As Double 'dblSTime = Timer '''''''''''''''''''''''''''
On Error Resume Next 'WordStart LogError 0, "Just before Set objWord = GetObject(, Word.Application)", "TestWord" Set objWord = GetObject(, "Word.Application") LogError 0, "After Set objWord = GetObject(, Word.Application)", "TestWord" If Err.Number = 429 Then Set objWord = New Word.Application ' CreateObject("Word.Application") LogError 0, "After Set objWord = New Word.Application", "TestWord" End If If objWord Is Nothing Then LogError Err.Number, "Unable to load Word object. Word is Nothing", "Letter in frmContacts"
Exit Sub End If
'' REMOVE THIS objWord.Visible = True LogError 0, "After objWord.Visible = True", "TestWord" 'On Error GoTo WordErr
Dim strLetter As String strLetter = "letter.dot"
docspath = "C:\"
docspath = docspath & strLetter
If FileExists(docspath) = False Then MsgBox "File: " & docspath & " does not exist check your document template path and check that the selected template document exists"
Exit Sub End If
Title = "Mr" Firstname = "Angus" Initials = "R" Surname = "Comber" Familiar = "Angus" strPosition = "Director"
'sDear stuff sDear = Familiar 'Dear
'Now need to address AddressLine strToWord = Firstname & " " & Surname
strToWord = strToWord & VBA.Chr(13) & "5 Enmore Gardens"
strToWord = strToWord & VBA.Chr(13) & "East Sheen"
strToWord = strToWord & VBA.Chr(13) & "London, SW14 8RF"
LogError Err.Number, "Just before Set objWordDoc = objWord.Documents.Add(docspath, False)", "TestWord"
Set objWordDoc = objWord.Documents.Add(docspath, False) ' Sometime get a valid objWord object but problem creating document based on template. ' So still need to check a valid objWordDoc created If objWordDoc Is Nothing Then MsgBox "Unable to create Word Document object based on template: " & docspath & " Unable to proceed with Word creation", vbCritical, "Word Error" LogError Err.Number, "Unable to create objWordDoc object. 'Set objWordDoc = objWord.Documents.Add(docspath, Fales)'", "Letter in frmContacts" objWord.Visible = True
Exit Sub End If
objWord.Application.ScreenUpdating = False 'objWord.Visible = True
Dim bRet As Boolean
'Find <Address> & replace with strToWord 'objWord.Selection.Find.Execute findtext:="<Address>", replacewith:=strToWord ', Replace:=wdReplaceAll ' Just in case named arguements can cause a problem - remove them! 'objWord.Selection.Find.Execute "<Address>", , , , , , , , , strToWord, True 'objWord.ActiveDocument.Range.WholeStory
'bRet = objWordDoc.Range.Find.Execute("<Address>", , , , , , , , , strToWord, Word.wdReplaceAll) LogError 0, "Just before first LateBindingReplace objWordDoc, < Address >, strToWord", "TestWord" LateBindingReplace objWordDoc, "<Address>", strToWord
'Find <Dear> & replace with sDear 'objWord.Selection.WholeStory 'objWord.Selection.Find.Execute findtext:="<Dear>", replacewith:=sDear ', Replace:=wdReplaceAll 'objWord.Selection.Find.Execute "<Dear>", , , , , , , , , sDear, True 'objWordDoc.Range.Find.Execute "<Dear>", False, False, , , , , , , sDear, True LateBindingReplace objWordDoc, "<Dear>", sDear
LogError 0, "After last LateBindingReplace objWordDoc, < Dear >, strToWord", "TestWord"
'objWordDoc.Range.Find.Execute 'Find <Start> & place cursor for user to start typing 'objWord.Selection.WholeStory 'objWord.Selection.Find.Execute findtext:="<Start>", replacewith:="" ', Replace:=wdReplaceAll
'objWordDoc.Range.WholeStory ' select all text 'objWordDoc.Range.Find.Execute "<Address>", False, False, False, False, False, True, Word.wdFindContinue, False, "Abba", Word.wdReplaceAll 'If Err.Number <> 0 Then ' LateBindingReplace objWordDoc, "<Address>", strToWord 'End If 'objWordDoc.Range.Find.text = "<Address>" 'objWordDoc.Range.Find.Replacement.text = "Abba"
'objWordDoc.Range.Find.Execute LogError 0, "Just before objWord.ActiveDocument.Variables(SiteID).Value =", "TestWord"
objWord.ActiveDocument.Variables("SiteID").Value = 3 objWord.ActiveDocument.Variables("HType").Value = "Letter" objWord.ActiveDocument.Variables("HComputer").Value = "MyComputername"
LogError 0, "After last objWord.ActiveDocument.Variables(HComputer).Value =", "TestWord"
LogError 0, "Just before objWord.Application.ScreenUpdating = True", "TestWord" objWord.Application.ScreenUpdating = True LogError 0, "Just after objWord.Application.ScreenUpdating = True", "TestWord" objWord.Visible = True LogError 0, "Just after objWord.Visible = True", "TestWord" objWord.Application.WindowState = Word.wdWindowStateMaximize LogError 0, "Just after objWord.Application.WindowState = ", "TestWord"
LogError 0, "Just before bjWord.Application.Activate", "TestWord" objWord.Application.Activate LogError 0, "Just after bjWord.Application.Activate", "TestWord"
'dblSTime = (Timer - dblSTime) 'MsgBox "Time taken: " & dblSTime
End Sub
Public Sub LogError(ByVal errnum As Integer, ByVal ErrDescription As String, Optional ByVal ProcName As String) On Error Resume Next Dim msg As String Dim iFilenum As Integer 'To get a free filenumber msg = Date & vbTab & Time & vbTab & ProcName & vbTab & errnum & vbTab & ErrDescription iFilenum = FreeFile Open "C:\Errors.log" For Append As #iFilenum 'Write new value Print #iFilenum, msg Close #iFilenum ' MsgBox msg 'Debug.Print msg End Sub
' Due to http://support.microsoft.com/default.aspx?scid=kb;EN-US;292744 ' which is a COM problem with Word's Find object clashing with an old version of Excel ' sometimes Find method will NOT work. So have to use Late binding to accomplish ' Find replace. Requires a Word Document object plus search and replace strings Public Function LateBindingReplace(oWordDoc As Word.Document, strFind, strReplace) As Long On Error Resume Next Dim oFind As Object ' Word.Find 'MsgBox "we are in latebindingreplace now!" Set oFind = oWordDoc.Content.Find
If oFind Is Nothing Then LogError Err.Number, "Unable to create oFind object. '", "LateBindingReplace in modMisc" LateBindingReplace = -1 Exit Function End If oFind.Execute strFind, False, False, False, _ False, False, True, Word.wdFindContinue, False, strReplace, _ Word.wdReplaceAll
LateBindingReplace = Err.Number
End Function
Private Function FileExists(strFile As String) As Boolean Dim MyFile As String ' Returns filename if it exists. MyFile = Dir(strFile)
If MyFile <> "" Then FileExists = True Else FileExists = False End If End Function
Jonathan West - 10 Jan 2005 13:31 GMT Hi angus,
I suspect the problem is that you have a mix of early and late binding. You have set a reference to the Word object library in the project, but are using either GetObject (late binding) or New Word.Application (early binding) in the code depending or the execution path.
This is a recipe for problems.
Because you are presumably designing for compatibility with multiple versions of Word, you should stick to late binding exclusively.
Remove the reference to Word in the References list, declare the objWord and objWordDoc variables as Object, and use CreateObject("Word.Application") to create a new instance of Word if there is not an already running instance
 Signature Regards Jonathan West - Word MVP www.intelligentdocuments.co.uk Please reply to the newsgroup
> Hello > [quoted text clipped - 262 lines] > End If > End Function Angus Comber - 10 Jan 2005 14:05 GMT That's interesting. I was not aware that late binding would be the best approach here.
I also found this out:
Some interesting feedback on this now. I recently upgraded my development machine to Office 2003. So in Visual Basic my reference was to the Word 2003 object library. My compiled VB6 application works on my Word2003 version fine. But I tested on another Word 97 computer and it crashed. So I recompiled my VB app using my old laptop which still runs Word 97 and everything works fine.
The version comiled on my Word 97 machine works OK on my Word 2003 machine.
So is the the moral of the tale is to compile using Word 97 so you can deploy on Word97 onwards?
As early binding is more 'efficient' then should this latter approach be prefereable to using late binding throughout?
Angus Comber angus@NOSPAMiteloffice.com
> Hi angus, > [quoted text clipped - 208 lines] > > > > LogError 0, "After last objWord.ActiveDocument.Variables(HComputer).Value
> > =", "TestWord" > > [quoted text clipped - 73 lines] > > End If > > End Function Tom Winter - 10 Jan 2005 16:12 GMT The moral is good, assuming you are only using features of Word that are present in Word 97. If you need features of Word that are only available in later versions, then you run in to a problem.
For my major project, I target Word 2000 and later. I compile using a reference to Word 2000, use early binding, and everything works fine.
The reason you can't go backwards (that is, compile with a reference to Word 2003 and then expect things to work under earlier versions of Word) is that some of the methods in Word have changed over time. For example, Documents.Open has changed over the different versions of Word, each version adding new parameters. The rules of COM don't allow you to do that, so Word actually cheats. If you use OLEVIEW to look at Word 2003's type library, you'll see methods called Documents.OpenOld, Documents.Open2000 and Documents.Open2002. Those methods are now hidden in Word 2003. When you compile with 2003, your app will be using the latest greatest Open method that 2003 supplies. But older versions of Word do not have that method, so when you try to call it, your application will crash. (Hope that makes sense. I can explain more if you need.)
Here's some stuff I wrote up for an earlier post. Hope it helps:
----
So what strategy should I use for working with multiple versions of Office applications? Well, first, pick a good base version that contains most of the functionality you want. For my project, I picked 9.0/2000, even though I wanted to work with 8.0/97 all the way through 11.0/2003. You can then reference the type library by selecting it from the PROJECT | REFERENCES list in VB (assuming VB6 here!). If the version you want is not there (only ONE version will be ever listed), you can BROWSE to the appropriate OLB file. You don't need to have the whole application installed on your system. VB only cares about the information in the OLB file. The OLB file doesn't need to be registered for VB6 to use it, so you can copy it to a common network location and have all the developers reference it there.
Then you need to remember what version you are using while programming and be careful about what objects and methods you use. You'll only be able to early bind to objects that exist in the version you've chosen (or older version). Newer objects you'll have to Dim As Object. There's no way around that.
Now what if you want to call a method using parameters that are only available in a newer version that what you've referenced? Well, that's tricky. You can't just add the extra parameters on the end of the method call, even if you late bind it. You'll get an error when you're on a system with an older version. You have to check the version first. Here's what you do (it's from actual production code):
' Assume we are referencing the Word 2000 type library... ' Word 2000's Open method supports the Visible parameter, but Word 97 does not.
Dim oWord As Word.Application ' Note early bound is OK here!
Dim oDocuments As Object ' Late bound reference to the Documents object for Word 97
Set oWord = ..... ' Get Word from somewhere
If IsWord97(oWord) Then
Set oDocuments = oApplication.Documents ' Note this is an early bound call, which is OK. The Documents property of the Application object is the same for all versions.
oDocuments.Open FileName:="Something.doc" ' Note this is a late bound call.
Else
oWord.Documents.Open FileName:="Something.doc", Visible:=True ' Note this is an early bound call.
End If
OK, now what if you want to call a method that only exists in a newer version than what you've referenced (or a method that has new parameters)? Here's our above example, a little different. Now we are referencing the Word 97 type library and want to worry about newer versions. (This example is NOT from production code, but you get the idea.)
' Assume we are referencing the Word 97 type library... ' Word 2000's Open method supports the Visible parameter, but Word 97 does not.
Dim oWord As Word.Application ' Note early bound is OK here!
Dim oDocuments As Object ' Late bound reference to the Documents object for Word 2000 or newer
Set oWord = ..... ' Get Word from somewhere
If IsWord2000orLater(oWord) Then
Set oDocuments = oApplication.Documents ' Note this is an early bound call, which is OK. The Documents property of the Application object is the same for all versions.
oDocuments.Open FileName:="Something.doc", Visible:=True ' Note this is a late bound call.
Else
oWord.Documents.Open FileName:="Something.doc" ' Note this is an early bound call.
End If
----
Also, Jonathan was not really correct in stating that GetObject gives you late binding. How you get the object doesn't matter. An OBJECT is not late bound or early bound. An object is just an object. It's METHOD CALLS that are early or late bound. When you are calling methods (or properties) on an object that has been DIM'ed as some specific object type, the method calls are early bound. When the object has been DIM'ed AS OBJECT, the method calls are late bound. Take a look at this:
Dim oDocumentsEarly As Word.Documents
Dim oDocumentsLate As Object
Set oDocumentsEarly = oWord.Documents ' Doesn't matter where we got oWord from....
oDocumentsEarly.Open "Something.doc" ' This call is early bound.
Set oDocumentsLate = oDocumentsEarly
oDocumentsLate.Open "SomethingElse.doc" ' This call is late bound.
 Signature Tom Winter Tom@NoSpam.AmosFiveSix.com
==============
> That's interesting. I was not aware that late binding would be the best > approach here. [quoted text clipped - 229 lines] > > > 'objWordDoc.Range.Find.Execute > > > LogError 0, "Just before objWord.ActiveDocument.Variables(SiteID).Value
> > > =", > > > "TestWord" [quoted text clipped - 84 lines] > > > End If > > > End Function Angus Comber - 10 Jan 2005 18:16 GMT Thanks that was very helpful. I think Word VBA syntax prior to Word 97 was very different and I am not bothering to support that. But I do want to support Word97 up. So I have installed Word97 to a custom location and in my VB projects I will use the Word 8.0 reference from now on.
Thanks again.
Angus
> The moral is good, assuming you are only using features of Word that are > present in Word 97. If you need features of Word that are only available in [quoted text clipped - 468 lines] > > > > End If > > > > End Function Jonathan West - 10 Jan 2005 21:55 GMT > Thanks that was very helpful. I think Word VBA syntax prior to Word 97 > was > very different and I am not bothering to support that. But I do want to > support Word97 up. So I have installed Word97 to a custom location and in > my VB projects I will use the Word 8.0 reference from now on. If you want to be able to support and test compatibility with multiple versions of Word, then this is not a very good arrangement, since all versions of Office write files to common areas. and it may well be that Word 97 has no overwritten newer versions of files that Word 2003 needs.
If you must have multiple versions of Office available on the same machine for testing, then I strongly recommend you get a copy of Microsoft VirtualPC, and create a number of virtual machines, each of which can be totally separate from the others.
In order to run VirtualPC, your machine will need to be fairly powerful, and you will need lots of memory and spare disk space. But this is probably cheaper than having multiple test machines.
 Signature Regards Jonathan West - Word MVP www.intelligentdocuments.co.uk Please reply to the newsgroup
Tom Winter - 11 Jan 2005 13:51 GMT I agree with Jonathan that VirtualPC is probably the best way to go. If you don't have the resources for that though, installing multiple versions of Word on the same machine has never been a problem for me. I've always started with the oldest version first and always installed them in different locations. I've had Word 6.0, Word 95, Word 97, Word 2000 and Word 2003 (sorry, no XP!) all on the same machine and I've never had any problems with them all working correctly.
 Signature Tom Winter Tom@NoSpam.AmosFiveSix.com
> > Thanks that was very helpful. I think Word VBA syntax prior to Word 97 > > was [quoted text clipped - 15 lines] > you will need lots of memory and spare disk space. But this is probably > cheaper than having multiple test machines. Jonathan West - 11 Jan 2005 13:57 GMT >I agree with Jonathan that VirtualPC is probably the best way to go. If you > don't have the resources for that though, installing multiple versions of [quoted text clipped - 5 lines] > with > them all working correctly. I agree that if you're a competent developer and experienced user of Office, you can get different versions of Word to coexist. And you are right in saying that the trick is to install the oldest first.
But it is not a representative test platform, and if you want to assure yourself that the code will work on other machines, then at some point it is necessary to carry out testing on a PC that has a single version of Office installed.
 Signature Regards Jonathan West - Word MVP www.intelligentdocuments.co.uk Please reply to the newsgroup
Tom Winter - 11 Jan 2005 16:20 GMT I definitly agree with you on the testing. -Tom
> >I agree with Jonathan that VirtualPC is probably the best way to go. If you > > don't have the resources for that though, installing multiple versions of [quoted text clipped - 14 lines] > necessary to carry out testing on a PC that has a single version of Office > installed. Angus Comber - 10 Jan 2005 14:00 GMT Some interesting feedback on this now. I recently upgraded my development machine to Office 2003. So in Visual Basic my reference was to the Word 2003 object library. My compiled VB6 application works on my Word2003 version fine. But I tested on another Word 97 computer and it crashed. So I recompiled my VB app using my old laptop which still runs Word 97 and everything works fine.
The version comiled on my Word 97 machine works OK on my Word 2003 machine.
Has anyone come across this behaviour before?
So is the the moral of the tale is to compile using Word 97 so you can deploy on Word97 onwards?
Angus Comber angus@NOSPAMiteloffice.com
> Hello > [quoted text clipped - 252 lines] > End If > End Function
|
|
|