Looping through Excel Cells and writing them to Word - vba

I am using a Macro in Excel to loop through cells and write the data into a Template in Word. Everything worked completely fine until I wanted to add more cells to grab data from. Everything still works fine except once the variable I have name "j" gets to the value of 25, I get an error saying "Run-time error '5941': The requested member of the collection does not exist."
I've played around with using different rows and columns and every combination works. It is only when the "j" reaches 25 does the error occur. It is failing when it reaches the wrd.ActiveDocument.Tables(1).Rows(j)... line.
Sub Label_ExcelToWord_Single()
'Variable that stores file path for
'word template
Dim strpath As String
strpath = Cells(28, 8)
'opens Microsoft Word to edit template
Call OpenWord
Set wrd = GetObject(, "Word.Application")
wrd.Visible = True
wrd.Activate
wrd.ActiveDocument.Close savechanges:=False
wrd.Documents.Open strpath
'Variables used for loop data manipulation
Dim k As Integer
Dim j As Integer
k = 1
j = 1
'Primary loop responsible for exporting Excel
'data to word template
For Col = 1 To 3
For Row = 3 To 32
wrd.ActiveDocument.Tables(1).Rows(j).Cells(((Row - 3) Mod 7) + k).Range.Text = Cells(Row, Col) & vbCrLf & Cells(Row, Col)
If k = 7 Then
k = 0
j = j + 2
End If
If Col = 3 Then
If Row = 32 Then
'When we reach the last cell containing data exit routine
Exit Sub
End If
End If
k = k + 1
Next
Next
End Sub

I think it's a lot easier to use DocVariables in Word and push your data from Excel cells into the DocVariables. The end game is about the same, but I think the code is a lot easier to setup and maintain.
Sub PushToWord()
Dim objWord As New Word.Application
Dim doc As Word.Document
Dim bkmk As Word.Bookmark
sWdFileName = Application.GetOpenFilename(, , , , False)
Set doc = objWord.Documents.Open(sWdFileName)
'On Error Resume Next
objWord.ActiveDocument.variables("FirstName").Value = Range("FirstName").Value
objWord.ActiveDocument.variables("LastName").Value = Range("LastName").Value
objWord.ActiveDocument.variables("AnotherVariable").Value = Range("AnotherVariable").Value
objWord.ActiveDocument.Fields.Update
'On Error Resume Next
objWord.Visible = True
End Sub
Set a reference ot the MS Word Object Library.

Looks like the table might not have enough rows. If so, a brute-force way is to add rows as you need them:
...
For Col = 1 To 3
For Row = 3 To 32
' vvv new lines vvv
Do While wrd.ActiveDocument.Tables(1).Rows.Count < j
wrd.ActiveDocument.Tables(1).Rows.Add
Loop
' ^^^ new lines ^^^
...
I also recommend renaming j, k, Row, and Col to more descriptive names. You have Excel row/column indices and Word-table row/column indices, and it's easy to get them confused unless the names are clear.
(Note: Yes, the above code is slow, clunky, unoptimized, yadda yadda. Hopefully it will help the OP. :) )

Related

Object or With Variable Not Set

Option Explicit
Public Sub consolidateList()
DeleteTableRows (ThisWorkbook.Worksheets("Master").ListObjects("MasterSheet"))
FillTableRows
End Sub
Private Sub FillTableRows()
'set up worksheet objects
Dim wkSheet As Worksheet
Dim wkBook As Workbook
Dim wkBookPath As String
Set wkBook = ThisWorkbook
wkBookPath = wkBook.Path
Set wkSheet = wkBook.Worksheets("Master")
'set up file system objects
Dim oFile As Object
Dim oFSO As Object
Dim oFolder As Object
Dim oFiles As Object
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFolder = oFSO.GetFolder(wkBookPath)
Set oFiles = oFolder.Files
'set up loop
Dim checkBook As Excel.Workbook
Dim reportDict As Dictionary
Application.ScreenUpdating = False
'initial coordinates
Dim startRow As Long
Dim startColumn As Long
startColumn = 3
Dim i As Long 'tracks within the row of the sheet where information is being pulled from
Dim k As Long 'tracks the row where data is output on
Dim j As Long 'tracks within the row of the sheet where the data is output on
Dim Key As Variant
j = 1
k = wkSheet.Range("a65536").End(xlUp).Row + 1
Dim l As Long
'look t Set checkBook = Workbooks.Open(oFile.Path)hrough folder and then save it to temp memory
On Error GoTo debuger
For Each oFile In oFiles
startRow = 8
'is it not the master sheet? check for duplicate entries
'oFile.name is the name of the file being scanned
'is it an excel file?
If Mid(oFile.Name, Len(oFile.Name) - 3, 4) = ".xls" Or Mid(oFile.Name, Len(oFile.Name) - 3, 4) = ".xlsx" Then
Set checkBook = Workbooks.Open(oFile.Path)
For l = startRow To 600
If Not (IsEmpty(Cells(startRow, startColumn))) Then
'if it is, time do some calculations
Set reportDict = New Dictionary
'add items of the payment
For i = 0 To 33
If Not IsEmpty(Cells(startRow, startColumn + i)) Then
reportDict.Add Cells(4, startColumn + i), Cells(startRow, startColumn + i)
End If
Next i
For i = startRow To 0 Step -1
If Not IsEmpty(Cells(i, startColumn - 1)) Then
reportDict.Add "Consumer Name", Cells(i, startColumn - 1)
Exit For
End If
Next i
'key is added
For Each Key In reportDict
'wkSheet.Cells(k, j) = reportDict.Item(Key)
Dim myInsert As Variant
Set myInsert = reportDict.Item(Key)
MsgBox (myInsert)
wkSheet.ListObjects(1).DataBodyRange(2, 1) = reportDict.Item(Key)
j = j + 1
Next Key
wkSheet.Cells(k, j) = wkSheet.Cells(k, 9) / 4
wkSheet.Cells(k, j + 1) = oFile.Name
'
k = k + 1
' Set reportDict = Nothing
j = 1
Else
l = l + 1
End If
startRow = startRow + 1
Next l
checkBook.Close
End If
' Exit For
Next oFile
Exit Sub
debuger:
MsgBox ("Error on: " & Err.Source & " in file " & oFile.Name & ", error is " & Err.Description)
End Sub
Sub DeleteTableRows(ByRef Table As ListObject)
On Error Resume Next
'~~> Clear Header Row `IF` it exists
Table.DataBodyRange.ClearContents
'~~> Delete all the other rows `IF `they exist
Table.DataBodyRange.Offset(1, 0).Resize(Table.DataBodyRange.Rows.count - 1, _
Table.DataBodyRange.Columns.count).Rows.Delete
On Error GoTo 0
End Sub
Greetings. The above code consolidates a folder of data that's held on excel spreadsheets into one master excel spreadsheet. The goal is to run a macro on Excel Spreadsheet named master on the worksheet named master which opens up other excel workbooks in the folder, takes the information, and puts it into a table in the worksheet "master". After which point, it becomes easy to see the information; so instead of it being held on hundreds of worksheets, the records are held on one worksheet.
The code uses a dictionary (reportDict) to temporarily store the information that is needed from the individual workbooks. The goal then is to take that information and place it in the master table at the bottom row, and then obviously add a new row either after a successful placement or before an attempted placement of data.
The code fails at the following line:
wkSheet.ListObjects(1).DataBodyRange(2, 1) = reportDict.Item(Key)
The failure description is "object or with variable not set" and so the issue is with the reportDict.Item(Key). My guess is that somehow VBA is not recognizing the dictionary item as stable, but I don't know how to correct this. Eventually the goal is to have code which does:
for each key in reportDict
- place the item which is mapped to the key at a unique row,column in the master table
- expand the table to accomodate necessary data
next key
Implicit default member calls are plaguing your code all over.
reportDict.Add Cells(4, startColumn + i), Cells(startRow, startColumn + i)
That's implicitly accessing Range.[_Default] off whatever worksheet is currently the ActiveSheet (did you mean that to be wkSheet.Cells?), to get the Key - since the Key parameter is a String, Range.[_Default] is implicitly coerced into one, and you have a string key. The actual dictionary item at that key though, isn't as lucky.
Here's a MCVE:
Public Sub Test()
Dim d As Dictionary
Set d = New Dictionary
d.Add "A1", Cells(1, 1)
Debug.Print IsObject(d("A1"))
End Sub
This procedure prints True to the debug pane (Ctrl+G): what you're storing in your dictionary isn't a bunch of string values, but a bunch of Range object references.
So when you do this:
Dim myInsert As Variant
Set myInsert = reportDict.Item(Key)
You might as well have declared myInsert As Range, for it is one.
This is where things get interesting:
MsgBox (myInsert)
Nevermind the superfluous parentheses that force-evaluate the object's default member and pass it ByVal to the MsgBox function - here you're implicitly converting Range.[_Default] into a String. That probably works.
So why is this failing then?
wkSheet.ListObjects(1).DataBodyRange(2, 1) = reportDict.Item(Key)
Normally, it wouldn't. VBA would happily do this:
wkSheet.ListObjects(1).DataBodyRange.Cells(2, 1).[_Default] = reportDict.Item(Key).[_Default]
And write the value in the DataBodyRange of the ListObject at the specified location.
I think that's all just red herring. Write explicit code: if you mean to store the Value of a cell, store the Value of a cell. If you mean to assign the Value of a cell, assign the Value of a cell.
I can't replicate error 91 with this setup.
This, however:
DeleteTableRows (ThisWorkbook.Worksheets("Master").ListObjects("MasterSheet"))
...is also force-evaluating a ListObject's default member - so DeleteTableRows isn't receiving a ListObject, it's getting a String that contains the name of the object you've just dereferenced... but DeleteTableRows takes a ListObject parameter, so there's no way that code can even get to run FillTableRows - it has to blow up with a type mismatch before DeleteTableRows even gets to enter. In fact, it's a compile-time error.
So this is a rather long answer that doesn't get to the reason for error 91 on that specific line (I can't reproduce it), but highlights a metric ton of serious problems with your code that very likely are related to this error you're getting. Hope it helps.
You need to iterate through the dictionary's Keys collection.
dim k as variant, myInsert As Variant
for each k in reportDict.keys
debug.print reportDict.Item(k)
next k

VBA: Array cell reference Mismatch error

UPDATED 3/30
So I adjusted the code and it runs error free now but the issue is that it does not pull the correct data. X basically starts with cell(X,1) and goes on from there. How do I link X to the selected listbox options in the array?
OLD Message:
I have a userform that allows for multi-select of Countries and also Questions about that specific country. These are stored in arrCountries & arrQuestion respectively. This then feeds to my main sub which calls for a Web Query Import from the CIA World Factbook site. I keep however getting a mismatch error that I cannot seem to sort out how to get around:
If I had to guess it is because when I am filling the array from the listbox's it is just adding a string and not the cell reference that the string is located at (or I am completely wrong).
My worksheet has only 1 sheet when started called Countries and the Column A is the URL and Column B is the Country name. I have Defined Public arrCountry(), Public arrQuestion(), and Public X as variant.
Code here:
Userform Code when click okay:
'Handles when the user clicks okay
Private Sub cbOkay_Click()
'Me.Hide
'Capture ticker selection(s) from list box.
Dim cI As Long
Dim cX As Long
Dim qI As Long
Dim qX As Long
'Stores the Countries selected into an array
If lbCountries.ListIndex <> -1 Then
For cI = 0 To lbCountries.ListCount - 1
If lbCountries.Selected(cI) Then
ReDim Preserve arrCountry(cX)
arrCountry(cX) = lbCountries.List(cI)
cX = cX + 1
End If
Next cI
End If
If cX = 0 Then MsgBox "Please select at least one country to analyse."
'MsgBox Join(arrCountry, vbCrLf)
'Stores the Questions selected into an array
If lbQuestions.ListIndex <> -1 Then
For qI = 0 To lbQuestions.ListCount - 1
If lbQuestions.Selected(qI) Then
ReDim Preserve arrQuestion(qX)
arrQuestion(qX) = lbQuestions.List(qI)
qX = qX + 1
End If
Next qI
End If
If qX = 0 Then MsgBox "Please select at least one question to analyse."
'MsgBox Join(arrQuestion, vbCrLf)
'Unload the form
Unload Me
cancel = False
End Sub
The message boxes return the correctly selected Listbox items so I know they are being stored correctly.
The WebQuery Code I am getting the error on:
UPDATED CODE:
So I added a loop counter:
Sub webQueryimport(arrCountry())
Dim mystr As String
Dim X As Integer
Dim selected As Variant
For Each selected In arrCountry
X = X + 1
Worksheets("Countries").Select
Worksheets("Countries").Activate
mystr = Cells(X, 1)
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = selected
With ActiveSheet.QueryTables.Add(Connection:=mystr, Destination:=Range("$A$1"))
.WebSelectionType = xlEntirePage 'this tells VBA what to select and import
.WebFormatting = xlWebFormattingNone 'this turns off web formatting, otherwise text is various sizes
.Refresh BackgroundQuery:=False 'if commented out, doesn't add any data
End With
Next selected
End Sub
Again, now that loop works and will import but it always starts with the A1 no matter what is selected in the listbox and in arrCountries
Any thoughts/assistance would be great!
Got it:
Sub webQueryimport(arrCountry())
Dim mystr As String
Dim X As Integer
Dim rng As Range
Dim selected As Variant
Set rng = Range("B1")
For Each selected In arrCountry()
For X = 1 To 5 'rng.Offset(0, 0).End(xlDown).Rows.count
Worksheets("Countries").Select
Worksheets("Countries").Activate
If Cells(X, 2).Value = selected Then
mystr = Cells(X, 1).Value
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = selected
With ActiveSheet.QueryTables.Add(Connection:=mystr, Destination:=Range("$A$1"))
.WebSelectionType = xlEntirePage 'this tells VBA what to select and import
.WebFormatting = xlWebFormattingNone 'this turns off web formatting, otherwise text is various sizes
.Refresh BackgroundQuery:=False 'if commented out, doesn't add any data
End With
End If
Next X
Next selected
End Sub
I needed to add in a counter and the IF statement to check to see if the value in the array matched the cell value in the sheet and then return the appropriate cell for the import.

Adding items from a listbox into the clipboard in a excel paste friendly format

The Aim: using VBA (excel) to populate the clipboard with a table (from a listbox) in an excel friendly format so that I can then paste (ctrl+V) into excel and the values are split into the correct columns and rows as they were in the original listbox.
The Problem: I can't seem to get the formatting right; when I paste the contents of my clipboard into excel each row is contained in one cell (the first column) separated with "," (which is what I have tried to use as a sort of "delimiter" - not sure if this is correct or not?)
I am using the API workaround method to populate the clipboard and I don't want to wall you to death with code but here is the most important bit (I feel anyway - ask me if you need to see more code).
Private Sub btnCopyTable_Click()
Dim I As Long
Dim J As Long
Dim tmp As String
Dim arrItems() As String
Dim clipboard As DataObject
ReDim arrItems(0 To lbIPActions.ColumnCount - 1)
For J = 0 To lbIPActions.ListCount - 1
For I = 0 To lbIPActions.ColumnCount - 1
On Error Resume Next ' Handles null values
arrItems(I) = lbIPActions.Column(I, J)
On Error GoTo 0
Next I
tmp = tmp & Join(arrItems, ",") & vbCrLf
Next J
MsgBox tmp
ClipBoard_SetData tmp
End sub
The Current Output:
1,1,Low,Controls,,LS,Do this,02-Oct-2015,Note 1,Brev
2,1,Low,Controls,,LS,Do that,02-Oct-2015,Note 2,Brev
The Current State: I have had a little look around and found a way to do it in code to a specific sheet, but my end user requires the freedom to copy it into numerous sheets in a very ad hoc fashion. I know there is always the option to right click -> import text wizard, but again ctrl+V is the preferred method if possible.
As always, any help/advice is appreciated, thanks in advance.
Separate your string with Tab in each line and it'll work. Like so:
Dim I As Long
Dim J As Long
Dim textItem As String
Dim copyText As String
Dim clipboard As DataObject
For J = 0 To lbIPActions.ListCount - 1
For I = 0 To lbIPActions.ColumnCount - 1
textItem = vbNullString
On Error Resume Next ' Handles null values
textItem = lbIPActions.Column(I, J)
On Error GoTo 0
If I > 0 Then copyText = copyText & vbTab
copyText = copyText & textItem
Next I
If J < lbIPActions.ListCount - 1 Then copyText = copyText & vbNewLine
Next J
ClipBoard_SetData clipboardText

Improve / optimize Excel macro to search for text phrases within a folder of text reports

Using Microsoft Excel 2010, this macro searches for a list of phrases within a folder of text reports. For each phrase, it searches all of the reports and lists each report that contains the phrase.
I found some better macros to do each part of the macro - such as enumerating a directory, or finding a phrase within a text file - although I had a really hard time putting them together successfully. Despite it not being perfect, it may be helpful for others with the same problem, and I hope for some feedback on how to improve and optimize the macro.
Basic overview:
Column A: list of full path to text reports (for instance, "C:\path\to\report.txt")
Column B: name of report (such as "report.txt")
Column C: list of phrases to search for
Columns D+: output showing each report that contains the phrase (column C)
Areas for improvement:
Make the macro run faster! (This took over an hour for 360 reports and 1100 phrases)
Select the reports and report folder from a pop-up or other function (currently entered into the spreadsheet using another macro)
Filter reports by file name (for instance, only check reports with a word or phrase in the file name)
Filter reports by file extension (for instance, only check .txt files and not .xlsx files)
Detect the number of reports and phrases (currently this is hard coded)
Other suggestions / areas for improvement
Code:
Sub findStringMacro()
Dim fn As String
Dim lineString As String
Dim fileName As String
Dim searchTerm As String
Dim findCount As Integer
Dim i As Integer
Dim j As Integer
For i = 2 To 1109
searchTerm = Range("C" & i).Value
findCount = 0
For j = 2 To 367
fn = Range("A" & j).Value
fileName = Range("B" & j).Value
With CreateObject("Scripting.FileSystemObject").OpenTextFile(fn)
Do While Not .AtEndOfStream
lineString = .ReadLine
If InStr(1, lineString, searchTerm, vbTextCompare) Then
findCount = findCount + 1
Cells(i, 3 + findCount) = fileName
GoTo EarlyExit
End If
Loop
EarlyExit:
.Close
End With
Next j
Next i
End Sub
As #Makah pointed out, you're opening a lot of files, which is slow. To fix this, change the order of the loops (see the code below). This will switch from 407,003 file opens to 367. Along the same lines, lets create the FileSystemObject once, instead of once per file open.
Also, VBA is surprisingly slow at reading/writing data from/to Excel. We can deal with this by loading largw blocks of data into VBA all at once with code like
dim data as Variant
data = Range("A1:Z16000").value
And then writing it back to Excel in a large block like
Range("A1:Z16000").value = data
I have also added in code to dynamically check the dimension of your data. We assume that the data starts in cell A2, and if A3 is empty, we use the single cell A2. Otherwise, we use .End(xlDown) to move down to just above the first empty cell in column A. This is the equivalent of pressing ctrl+shift+down.
Note: the following code has not been tested. Also, it requires a reference to "Microsoft Scripting Runtime" for the FileSystemObjects.
Sub findStringMacro()
Dim fn As String
Dim lineString As String
Dim fileName As String
Dim searchTerm As String
Dim i As Integer, j As Integer
Dim FSO As Scripting.FileSystemObject
Dim txtStr As Scripting.TextStream
Dim file_rng As Range, file_cell As Range
Dim output As Variant
Dim output_index() As Integer
Set FSO = New Scripting.FileSystemObject
Set file_rng = Range("A2")
If IsEmpty(file_rng) Then Exit Sub
If Not IsEmpty(file_rng.Offset(1, 0)) Then
Set file_rng = Range(file_rng, file_rng.End(xlDown))
End If
If IsEmpty(Range("C2")) Then Exit Sub
If IsEmpty(Range("C3")) Then
output = Range("C2")
Else
output = Range(Range("C2"), Range("C2").End(xlDown))
End If
ReDim Preserve output(1 To UBound(output, 1), 1 To file_rng.Rows.Count + 1)
ReDim output_index(1 To UBound(output, 1))
For i = 1 To UBound(output, 1)
output_index(i) = 2
Next i
For Each file_cell In file_rng
fn = file_cell.Value 'Range("A" & j)
fileName = file_cell.Offset(0, 1).Value 'Range("B" & j)
Set txtStr = FSO.OpenTextFile(fn)
Do While Not txtStr.AtEndOfStream
lineString = txtStr.ReadLine
For i = 1 To UBound(output, 1)
searchTerm = output(i, 1) 'Range("C" & i)
If InStr(1, lineString, searchTerm, vbTextCompare) Then
If output(i, output_index(i)) <> fileName Then
output_index(i) = output_index(i) + 1
output(i, output_index(i)) = fileName
End If
End If
Next i
Loop
txtStr.Close
Next file_cell
Range("C2").Resize(UBound(output, 1), UBound(output, 2)).Value = output
Set txtStr = Nothing
Set FSO = Nothing
Set file_cell = Nothing
Set file_rng = Nothing
End Sub

Excel VBA crashes after subroutine finishes

This script is to reset a template, by copying a hidden worksheet template and deleting the existing sheet (after repopulating some reference data). I have tested it and it runs fine in debugging mode.
Option Explicit
Sub reset_PrintLayout_byCopy()
'the script replace the used printlayout with a copy from the hidden master.
Dim MeetingData() As String
Dim i As Integer
Dim j As Integer
Dim currentSheet As String
Dim datacolumns() As String
Dim userConfirm As String
ReDim Preserve MeetingData(3, 2)
ReDim Preserve datacolumns(2)
'warning about deleting data
userConfirm = MsgBox(Prompt:="Resetting the template will erase all data on the " _
& "PrintLayout Template. Choose ""Cancel"", if you wish to save the file first", _
Buttons:=vbOKCancel, Title:="Data to be erased!")
If (userConfirm = vbCancel) Then
Exit Sub
End If
'set parameters
datacolumns(0) = "D1"
datacolumns(1) = "I1"
'stop screen updating and displaying warnings
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'set active sheet
currentSheet = ActiveSheet.Name
'capture meeting data already filled out
For j = 0 To UBound(datacolumns) - 1
For i = 1 To 3
If Worksheets(currentSheet).Cells(i, Range(datacolumns(j)).Column).Value <> "" Then
MeetingData(i - 1, j) = Worksheets(currentSheet).Cells(i, Range(datacolumns(j)).Column).Value
End If
Next i
Next j
'make hidden template visible
Worksheets("hiddenPrintLayoutTemplate").Visible = True
'Rename current Sheet
Sheets(currentSheet).Name = "used_Print_Layout"
''add a new sheet
' ActiveWorkbook.Worksheets.Add(before:=Sheets("used_Print_Layout")).Name = "PrintLayout Template"
'copy hiddentemplate before current sheet
Worksheets("hiddenPrintLayoutTemplate").Copy before:=Sheets("used_Print_Layout")
ActiveSheet.Name = currentSheet
'set rowheight for title rows
Range("A12").EntireRow.RowHeight = 24
Range("A18").EntireRow.RowHeight = 24
'delete current used printlayout
Worksheets("used_Print_Layout").Delete
'refilled meeting data
For j = 0 To UBound(datacolumns) - 1
For i = 1 To 3
If MeetingData(i - 1, j) <> "" Then
Worksheets(currentSheet).Cells(i, Range(datacolumns(j)).Column).Value = MeetingData(i - 1, j)
End If
Next i
Next j
'hide PrintLayout template
'Worksheets("hiddenPrintLayoutTemplate").Visible = xlSheetVeryHidden
'Sheets("PrintLayout Template").Select
'activate screenupdating and display warnings
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
When run it in macro mode on button, it runs, but excel crashes, when it is done. I cannot find what the issue is. Any ideas?
I am not sure if by debugging you mean stepping through line by line, but you could try inserting stop statements at key points in the code. So for example, you could put a stop statement in the following part:
'capture meeting data already filled out
For j = 0 To UBound(datacolumns) - 1
For i = 1 To 3
If Worksheets(currentSheet).Cells(i, Range(datacolumns(j)).Column).Value <> "" Then
MeetingData(i - 1, j) = Worksheets(currentSheet).Cells(i,Range(datacolumns(j)).Column).Value
End If
Next i
Next j
stop
'make hidden template visible
Worksheets("hiddenPrintLayoutTemplate").Visible = True
You could see if the code runs fine up until that point (i.e. run it without debugging). If it does, remove the stop statement and place it further down the code. Repeat this until you find the statements which cause your crash - perhaps the reason would appear then.
In general, if you get an odd crash in Excel VBA, try switching the Windows default printer to Microsoft XPS Document Writer. Seems odd, but that has worked for me on problems where I've wasted many hours only to find this is the culprit.