Ten behoeve van een project waarbij er vanuit een VO applicatie grote hoeveelheden brieven verzonden moeten worden ben ik opnieuw in het mailmergen met MS Word gedoken.
Ik voorkomende gevallen in het verleden gebruikte ik een eigen zoek en vervang routine met behulp van oWord:document:find:Execute(), maar de verwachting was dat dit bij de aantallen brieven die ik nu zou moeten kunnen verzenden niet toerijkend zou zijn.
Aangezien MSWord is uitgerust met mailmerge functionaliteit, werd het tijd die eens aan een nader onderzoek te onderwerpen.
Hieronder mijn eerste voorbeeld. Voor de duidelijkheid gebruik ik geen door VO gegenereerde base-classes. In de uiteindelijke toepassing worden die natuurlijk wel gebruikt.
Een en ander is redelijk rechttoe-rechtaan. Er is wordt een document geopend en enkele poperties ingesteld. De Word-Mailmergeclass wordt geinstantieerd en er wordt een verbinding gemaakt met de MS-Jet database, waarna de mailmerge wordt uigevoerd.
METHOD lateboundmailmerge() CLASS StandardShellWindow
LOCAL oWOrd AS OBJECT //OleAutoObject
LOCAL oDocs AS OBJECT
LOCAL oMain AS OBJECT
LOCAL oMerge AS OBJECT
LOCAL sSource AS STRING
LOCAL sConnect AS STRING
LOCAL sQuery AS STRING
LOCAL oConnection AS ConnectionString
oWord := OleAutoObject{ "Word.Application"}
oword:Visible := TRUE
oDocs := oWord:Documents
sDocument := "F:\Apps\PUB\Winparkeer\Standaarddocumenten\test1.doc"
oMain := odocs:Open( sDocument )
sSource := "F:\apps\pub\winparkeer\data\vergun.mdb"
sConnect := "Provider=Microsoft.Jet.OLEDB.4.0;ID=Admin;Datasource=" + sSource +";"
oMerge := oMain:Mailmerge
oMerge:SuppressBlankLines := TRUE
oMerge:Destination := wdSendToNewDocument
sQuery := "SELECT TOP 20 * FROM VERG_PRINT"
oMerge:OpenDataSource( ;
sSource ,; //Name
wdOpenFormatAuto ,; //Format
FALSE ,; //ConfirmConversions
FALSE ,; //ReadOnly
TRUE ,; //LinkToMain
FALSE ,; //AddToRecentFiles
"" ,; //PasswordDocument
"" ,; //PasswordTemplate
FALSE ,; //Revert
"" ,; //WritePasswordDocument
"" ,; //WritePasswordTemplate
sConnect ,; //Connection
sQuery ,; //SQLStatement
"" ) //SQLStatement1
oMerge:Execute()
oMain:close()
Bovenstaande code werkt. Tenminste met Office XP en zolang je niet alle records uit de tabel wilt gebruiken. Want bij aQuery := “SELECT * FROM HOUDERS” gaat het mis, een foutmelding van Word dat de verbinding met de database niet gelegd kan worden.
Word 2000 heeft sowieso problemen met deze benadering. Vraag me niet waarom, maar het werkt niet. Word blijft zeuren over het niet kunnen openen van de database. Tenminste zolang aangestuurd middels automation, want vanuit Word zelf is de actie gewoon uit te voeren.
Ik weet niet hoe het jullie vergaat, maar dit zijn van die problemen waarbij ik eerst een hele tijd in mijn code zit te zoeken wat ik fout doe, vervolgens met de netwerkbeheerder op zoek ga welke mogelijke beperkingen in rechten het probleem veroorzaakt. Na een uur of wat puzzelen en trial-and-error kom je er dan achter dat het gewoon aan Office zelf ligt. Want met Offcie XP werk eea wel.
En hoewel de klant over Office XP licenties beschikt, wordt er thans en in de toekomst met Office 2000 gewerkt. De uitdaging lag er dus in om eea toch onder Office 2000 aan de praat te krijgen.
De oplossing is uiteindelijk gevonden door te werken met een tussenbestand (Robert, alsnog bedankt!). Voorafgaand aan de merge maken we een txt bestand aan en dit bestand geven we door als datasource.
Bij het gebruik van een txt bestand als datasource, gaat MS-Word ervan uit dat de eerste regel de kolomtitels bevat. De tweede regel wordt als eerste dataregel gebruikt. Hiermee zullen we dus rekening moeten houden bij het aanmaken van het txt bestand.
We maken natuurlijk gebruik van VO2ADO.
sQuery := "SELECT * FROM VERG_PRINT WHERE (VERGUNNING.VERGNUMMER)="+AsString( wVergnr )
oRS := AdoRecordSet{}
oRS:open( sQuery, AdoGetConnection(),adOpenStatic,adLockReadOnly, adcmdText)
IF oRS:EOF
BREAK "Geen gegevens gevonden"
ENDIF
// als we hier zijn, hebben we gegevens om te mergen
sText := ""
// eerst de eerste regel met de veldnamen samenstellen
aVelden := oRS:FIELDs:ASArray()
wLen := ALen( aVelden )
FOR n := 1 UPTO wLen)
sText += aVelden[n]:Name
IF n < wLen
sText += ";"// Velden scheiden met een puntkomma
ENDIF
NEXT
sText += CRLF
// Nu de data. oRecordSet:GetString geeft de mogelijkheid om het veld en regel
// scheidingsteken in te stellen
sText += oRS:GetString( adClipString, NIL,";",CRLF, "")
sTemp := "C:\temdatasource.txt"
IF !MemoWrit( sTemp, sText )
BREAK "Probleem met schrijven naar tijdelijk bestand"
ENDIF
Nu terug naar het eerste voorbeeld. Die kan grotendeels hetzelfde blijven. We hoeven alleen maar de naam van het datasource bestand door te geven om een succesvolle merge uit te kunnen voeren.
oMerge:OpenDataSource( ;
sTemp ,; //Name
wdOpenFormatAuto ,; //Format
FALSE ,; //ConfirmConversions
FALSE ,; //ReadOnly
TRUE ,; //LinkToMain
FALSE ,; //AddToRecentFiles
"" ,; //PasswordDocument
"" ,; //PasswordTemplate
FALSE ,; //Revert
"" ,; //WritePasswordDocument
"" ,; //WritePasswordTemplate
"" ,; //Connection
"" ,; //SQLStatement
"" ) //SQLStatement1
oMerge:Execute()
Eigenlijk een hele simpele oplossing, niet?
Erik Visser