Hello everyone.
I wrote this function which fills MSWord templates, like the mail merge function, but customized to my needs. It basically extracts a query definition's SQL string, it concatenates the where clause and, using the first record, it finds the field names of the SQL string in the docx template, the placeholders in the template use this format {{fieldName}}. It then generates a file in the temporary folder of Windows and returns the path.
There's something trimming my strings to 255 characters. As you can see in the code, when the field is longer than 255 characters, it sends the contents of the field to the clipboard and then pastes it, instead of just using the field.value property, which would trim the text to 255 characters. That behaviour worked great until I decided to use a function to add 'minus' symbols to keep filling the blanks when the text ends.
Sounds weird, maybe? I'll try to explain further: Imagine a template with 4 blank lines that you have to fill, but your text is only half a line long, how do you fill the rest? well I chose the path of doing it with the Access Query Builder like this:
Suppose I need to fill 500 characters:
But if those 'minus' symbols exceed the length of 255, they get trimmed, sometimes it outputs n?n or ?;? at the end.
Please check the attached sample database. I suspect the problem is actually in the clipboard code, which is this:
I suspect that because I wrote a custom function to not use the built-in String function.
But it returns the same.
What could it be?
I wrote this function which fills MSWord templates, like the mail merge function, but customized to my needs. It basically extracts a query definition's SQL string, it concatenates the where clause and, using the first record, it finds the field names of the SQL string in the docx template, the placeholders in the template use this format {{fieldName}}. It then generates a file in the temporary folder of Windows and returns the path.
Code:
Function templateReplacer( _
querydef_name As String, _
where_clauses As String, _
template_path As String, _
desired_filename As String) _
As String
On Error GoTo errHandler
Dim sql1 As String
sql1 = CurrentDb.QueryDefs(querydef_name).SQL
Dim sql2 As String
sql2 = Replace(sql1, ";", " WHERE " & where_clauses)
' Debug.Print sql2
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset(sql2)
Dim oWord As Object
Set oWord = CreateObject("Word.Application")
Dim oDoc As Object
Set oDoc = oWord.Documents.Open(template_path)
' |------------------- REPLACE {{FIELDS}} -------------------|
Dim field As Variant
Dim emptyFields As String
emptyFields = ""
Dim oStory As Object
For Each field In rs.Fields
' Debug.Print field.Name, field.Value
For Each oStory In oDoc.StoryRanges
Do
With oStory.Find
If Len(field) > 255 Then
ClipBoard_SetData field.Value
.Execute FindText:="{{" & field.Name & "}}", ReplaceWith:="^c", _
Format:=True, Replace:=2
Else
.Execute FindText:="{{" & field.Name & "}}", ReplaceWith:=Nz(field, ""), _
Format:=True, Replace:=2
End If
End With
Set oStory = oStory.Next
Loop Until oStory Is Nothing
Next oStory
If IsNull(field) Then
emptyFields = emptyFields & vbCr & field.Name
End If
Next field
' |------------------- FILE NAMING AND SAVING -------------------|
Dim tempFolder As String
tempFolder = Environ("Temp")
Dim outputFullPath As String
outputFullPath = tempFolder & "\" & desired_filename & Format(Date, "ddmmyy") & Format(Time, "hhmmss") & ".docx"
oDoc.SaveAs2 outputFullPath
Set oWord = Nothing
Set oDoc = Nothing
If Len(emptyFields) > 0 Then
MsgBox "Empty fields: " & vbCr & emptyFields
End If
templateReplacer = outputFullPath
errHandler:
If Err.Number <> 0 Then
MsgBox Err.Number & vbCr & Err.Description
End If
Set oWord = Nothing
Set oDoc = Nothing
End Function
There's something trimming my strings to 255 characters. As you can see in the code, when the field is longer than 255 characters, it sends the contents of the field to the clipboard and then pastes it, instead of just using the field.value property, which would trim the text to 255 characters. That behaviour worked great until I decided to use a function to add 'minus' symbols to keep filling the blanks when the text ends.
Sounds weird, maybe? I'll try to explain further: Imagine a template with 4 blank lines that you have to fill, but your text is only half a line long, how do you fill the rest? well I chose the path of doing it with the Access Query Builder like this:
Suppose I need to fill 500 characters:
IIf(Len[field]<500,String(500-Len([field]), "-"),"")
But if those 'minus' symbols exceed the length of 255, they get trimmed, sometimes it outputs n?n or ?;? at the end.
Please check the attached sample database. I suspect the problem is actually in the clipboard code, which is this:
Code:
Option Compare Database
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal wFlags As LongPtr, _
ByVal dwBytes As LongPtr) As LongPtr
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As LongPtr
Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function EmptyClipboard Lib "user32" () As LongPtr
Private Declare PtrSafe Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, _
ByVal lpString2 As Any) As LongPtr
Private Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As LongPtr, _
ByVal hMem As LongPtr) As LongPtr
#Else
Private Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, _
ByVal dwBytes As Long) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function EmptyClipboard Lib "user32" () As Long
Private Declare Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, _
ByVal lpString2 As Any) As Long
Private Declare Function SetClipboardData Lib "user32" (ByVal wFormat _
As Long, ByVal hMem As Long) As Long
#End If
Const GHND = &H42
Const CF_TEXT = 1
Const MAXSIZE = 4096
Function ClipBoard_SetData(MyString As String)
'PURPOSE: API function to copy text to clipboard
'SOURCE: www.msdn.microsoft.com/en-us/library/office/ff192913.aspx
#If VBA7 Then
Dim hGlobalMemory As LongPtr, lpGlobalMemory As LongPtr
Dim hClipMemory As LongPtr, x As LongPtr
#Else
Dim hGlobalMemory As Long, lpGlobalMemory As Long
Dim hClipMemory As Long, x As Long
#End If
'Allocate moveable global memory
hGlobalMemory = GlobalAlloc(GHND, Len(MyString) + 1)
'Lock the block to get a far pointer to this memory.
lpGlobalMemory = GlobalLock(hGlobalMemory)
'Copy the string to this global memory.
lpGlobalMemory = lstrcpy(lpGlobalMemory, MyString)
'Unlock the memory.
If GlobalUnlock(hGlobalMemory) <> 0 Then
MsgBox "Could not unlock memory location. Copy aborted."
GoTo OutOfHere2
End If
'Open the Clipboard to copy data to.
If OpenClipboard(0&) = 0 Then
MsgBox "Could not open the Clipboard. Copy aborted."
Exit Function
End If
'Clear the Clipboard.
x = EmptyClipboard()
'Copy the data to the Clipboard.
hClipMemory = SetClipboardData(CF_TEXT, hGlobalMemory)
OutOfHere2:
If CloseClipboard() = 0 Then
MsgBox "Could not close Clipboard."
End If
End Function
I suspect that because I wrote a custom function to not use the built-in String function.
Code:
Public Function StringX(amt As Long, str As String) As String
Dim finalString As String
Dim i As Long
For i = 1 To amt
finalString = finalString & str
Next
StringX = finalString
End Function
What could it be?