I have a problem. I am trying to get records from the data base to Excel and afterwards format the Excel file. If I implement the whole function then it will run fine on one occasion. But if I run it again it sometimes gives the runtime 91 error. I was going through the debugging and if I only have the first ApXl in the function the code will work fine with no issues. However, if I add more of the code to reformat the Excel sheet then it will eventually give me the run time 91 even though the variables are already declared. Below is part of my code with some of the formatting.
Public Function ExportToExcelEM(Numbcases, strObjectType As String, strObjectName As String, Optional strSheetName As String, Optional strFileName As String)
Dim rst As DAO.Recordset
Dim ApXL As Object
Dim xlWBk As Object
Dim xlWSh As Object
Dim intCount As Integer
Const xlToRight As Long = -4161
Const xlCenter As Long = -4108
Const xlBottom As Long = -4107
Const xlContinuous As Long = 1
Dim OBJ As Object
On Error GoTo ExportToExcel_Err
DoCmd.Hourglass True
Select Case strObjectType
Case "Table", "Query"
Set rst = CurrentDb.OpenRecordset(strObjectName, dbOpenDynaset, dbSeeChanges)
Case "Form"
Set rst = Forms(strObjectName).RecordsetClone
Case "Report"
Set rst = CurrentDb.OpenRecordset(Reports(strObjectName).RecordSource, dbOpenDynaset, dbSeeChanges)
End Select
If rst.RecordCount = 0 Then
MsgBox "No records to be exported.", vbInformation, GetDBTitle
DoCmd.Hourglass False
Else
On Error Resume Next
Set ApXL = GetObject(, "Excel.Application")
If Err.Number <> 0 Then
Set ApXL = CreateObject("Excel.Application")
End If
Err.Clear
On Error GoTo ExportToExcel_Err
Set xlWBk = ApXL.Workbooks.Add
ApXL.Visible = False
Set xlWSh = xlWBk.Worksheets("Sheet1")
If Len(strSheetName) > 0 Then
xlWSh.Name = Left(strSheetName, 31)
End If
xlWSh.Range("A1").Select
Do Until intCount = rst.Fields.Count
ApXL.ActiveCell = rst.Fields(intCount).Name
ApXL.ActiveCell.Offset(0, 1).Select
intCount = intCount + 1
Loop
rst.MoveFirst
xlWSh.Range("A2").CopyFromRecordset rst
** ** ** With ApXL
.Range("A1").Select
.Range(.Selection, .Selection.End(xlToRight)).Select
.Selection.Interior.Pattern = xlSolid
.Selection.Interior.PatternColorIndex = xlAutomatic
.Selection.Interior.TintAndShade = -0.25
.Selection.Interior.PatternTintAndShade = 0
.Selection.Borders.LineStyle = xlNone
.Selection.AutoFilter
.Cells.EntireColumn.AutoFit
.Cells.EntireRow.AutoFit
.Range("B2").Select
.ActiveWindow.FreezePanes = True
.ActiveSheet.Cells.Select
.ActiveSheet.Cells.WrapText = False
.ActiveSheet.Cells.EntireColumn.AutoFit
.Visible = False
End With********
With ApXL
xlWSh.Rows(1).Select
With Selection
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.WrapText = True
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
End With
xlWBk.SaveAs FileName:=strFileName, FileFormat:=51
xlWBk.Close
Set xlWBk = Nothing
Set xlWSh = Nothing
Set ApXL = Nothing
' end of doing anything with excel
End If
rst.Close
Set rst = Nothing
DoCmd.Hourglass False
ExportToExcel_Exit:
DoCmd.Hourglass False
Exit Function
ExportToExcel_Err:
DoCmd.SetWarnings True
Set xlWBk = Nothing
Set xlWSh = Nothing
Set ApXL = Nothing
rst.Close
MsgBox Err.Description, vbExclamation, Err.Number
DoCmd.Hourglass False
Resume ExportToExcel_Exit
End Function
debugging: works once after adding more formatting in my code but then gives me a runtime 91 error. I added more code chunks to the code till it was the complete excel function I desired. Throughout the process it would give me the finish piece I wanted but afterwards gives me a runtime error 91.
After running procedure, check if instance of Excel is still showing in Task Manager. This can be result of using Active______ referencing. Use explicit reference instead.
When I compile your procedure, I get "Method or data member not found." error on each line in the xlWSh.Rows(1).Select block. Use With .Rows(1) instead.
Could probably perform this process without actually selecting anything. Use explicit Cells or Range reference: With .Range("A1:E1") - construct reference with variables.
Modified your code to apply and it works:
intCount = rst.Fields.Count
Set xlWSh = xlWBk.worksheets("Sheet1")
With xlWSh
If strSheetName <> "" Then .Name = Left(strSheetName, 31)
For x = 1 To intCount
.Cells(1, x).Value = rst(x - 1).Name
Next
.Range("A2").CopyFromRecordset rst
With .Range(.Cells(1, 1), .Cells(1, intCount))
.Interior.Pattern = xlSolid
.Interior.PatternColorIndex = xlAutomatic
.Interior.TintAndShade = -0.25
.Interior.PatternTintAndShade = 0
.Borders.LineStyle = xlNone
.AutoFilter
.EntireColumn.AutoFit
End With
With .Rows(1)
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.WrapText = True
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.shrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
.Rows.AutoFit
End With
ApXL.Windows(1).SplitColumn = 1
ApXL.Windows(1).SplitRow = 1
ApXL.Windows(1).FreezePanes = True
Related
The following code makes a list of sheets:
Sub Listofcontent()
Dim objSheet As Worksheet
Dim intRow As Integer
Dim strCol As Integer
Dim GCell As Range
SearchText = "Content"
Set GCell = Worksheets("Front page").Cells.Find(SearchText).Offset(2, 0)
GCell.End(xlDown).ClearContents
intRow = GCell.Row
strCol = GCell.Column
For Each objSheet In ActiveWorkbook.Sheets
ActiveWorkbook.Worksheets("Front page").Hyperlinks.Add Anchor:=ActiveWorkbook.Worksheets("Front page").Cells(intRow, strCol), Address:="", SubAddress:= _
"'" & objSheet.name & "'!A1", TextToDisplay:=objSheet.name
With ActiveWorkbook.Worksheets("0.0 Forside").Cells(intRow, strCol).Font
.name = "Calibri"
.FontStyle = "Normal"
.Size = 11
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ThemeColor = xlThemeColorLight1
.TintAndShade = 0
.ThemeFont = xlThemeFontMinor
End With
intRow = intRow + 1
Next objSheet
End Sub
It works now. Thanks. However, I want it to run whenever a sheet is added, deleted, renamed, moved, copied. I added this to the workbook code pane:
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Application.EnableEvents = False
Listofcontent
Application.EnableEvents = True
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
Application.EnableEvents = False
Listofcontent
Application.EnableEvents = True
End Sub
Change this
For Each objSheet In ActiveWorkbook.Sheets.Count
To
For Each objSheet In ActiveWorkbook.Sheets
I have a macro ("List_of_sheets") that creates a list of all the sheets in the workbook, and places the list in the "Sheetlist"-sheet underneath the "Header"-word.
The macro deletes the previous list and creates a new list, whenever I run the macro. I do this manually whenever I delete, add, copy or change the name of sheet. However, I want this to run automatically.
Thanks in advance!
Sub List_of_sheets()
Dim objSheet As Worksheet
Dim intRow As Integer
Dim strCol As Integer
Dim GCell As Range
SearchText = "Header"
Set GCell = Worksheets("Listsheet").Cells.Find(SearchText).Offset(2, -1)
GCell.End(xlDown).ClearContents
intRow = GCell.Row
strCol = GCell.Column
For Each objSheet In ActiveWorkbook.Sheets
ActiveWorkbook.Worksheets("Listsheet").Hyperlinks.Add Anchor:=ActiveWorkbook.Worksheets("Listsheet").Cells(intRow, strCol), Address:="", SubAddress:= _
"'" & objSheet.Name & "'!A1", TextToDisplay:=objSheet.Name
With ActiveWorkbook.Worksheets("Listsheet").Cells(intRow, strCol).Font
.Name = "Calibri"
.FontStyle = "Normal"
.Size = 11
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ThemeColor = xlThemeColorLight1
.TintAndShade = 0
.ThemeFont = xlThemeFontMinor
End With
intRow = intRow + 1
Next objSheet
End Sub
you have to go with Workbooks events, although they don't cover the case of a sheet name change
but as a workaround you could use Workbook_SheetActivate since when you change the name of a sheet and then you want to see if the list has been updated you have to activate the list sheet
so place in ThisWorkbook code pane the following:
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Application.EnableEvents = False
List_of_sheets
Application.EnableEvents = True
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
Application.EnableEvents = False
List_of_sheets
Application.EnableEvents = True
End Sub
and you could consider the following refactoring of your code
Option Explicit
Sub List_of_sheets()
Dim objSheet As Worksheet
Dim intRow As Integer
Dim strCol As Integer
Dim GCell As Range
Dim SearchText As String
SearchText = "Header"
Set GCell = Worksheets("Listsheet").UsedRange.Find(what:=SearchText, lookat:=xlWhole, LookIn:=xlValues).Offset(2, -1)
GCell.End(xlDown).ClearContents
intRow = GCell.Row
strCol = GCell.Column
Dim listSheet As Worksheet
With ActiveWorkbook
Set listSheet = .Worksheets("Listsheet")
For Each objSheet In .Sheets
listSheet.Hyperlinks.Add Anchor:=listSheet.Cells(intRow, strCol), Address:="", SubAddress:= _
"'" & objSheet.Name & "'!A1", TextToDisplay:=objSheet.Name
intRow = intRow + 1
Next objSheet
End With
With listSheet.Cells(GCell.Row, strCol).Resize(Sheets.Count).Font
.Name = "Calibri"
.FontStyle = "Normal"
.Size = 11
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ThemeColor = xlThemeColorLight1
.TintAndShade = 0
.ThemeFont = xlThemeFontMinor
End With
End Sub
Just as a foreword, I'm not much of a programmer. Just trying to band-aid a macro for a one off use.
Currently need to cycle through ~3700 lines in excel and find certain words in around ~100 word documents.
Basically line 1 needs to find the word "donkey" (cell A1) and searches the document listed in cell A4, then produces how many times the word was found and if greater than 2 times, marks the cell "YES".
My problem is, sometimes it will run to completion, sometimes when opening the next document I receive the error
Run-time error '91': Object variable or With block variable not set
Usually restarting excel or changing the file path of the documents being search fixes it for one or two runs. This makes me believe it has something to do with memory, but I'm unsure.
Any ideas what the issue is? Thanks!
Here's the code as-is, yes it's sloppy.
Sub FindName()
Dim wrdApp As Object
Dim wrdDoc As Object
Dim maxRowCount As Integer
Dim TP As String
Dim FindWord As String
Dim result As String
Dim RowCount As Integer
Dim i As Long
Dim iCount As Integer
TP = "003"
i = 115
maxRowCount = 140
On Error Resume Next
Set wrdApp = GetObject(, "Word.Application")
If Err.Number > 0 Then Set wrdApp = CreateObject("Word.Application")
On Error GoTo 0
wrdApp.Visible = True
Set wrdDoc = wrdApp.Documents.Open("C:\TP\X-" & TP & ".docx")
For i = i To maxRowCount
If Cells(i, 4).Text = TP Then
FindWord = Cells(i, 1).Text
'// Defines selection for Word's find function
wrdDoc.ActiveWindow.Selection.GoTo What:=wdGoToSection, Which:=wdGoToFirst
wrdDoc.SelectAllEditableRanges
iCount = 0
'// Word Find Method Setup Block
With wrdDoc.ActiveWindow.Selection.Find
.Text = FindWord
.Replacement.Text = ""
.Forward = True
.Wrap = 1 ' wdFindContinue (Word constant not defined in Excel)
.Format = False
.MatchCase = True
.MatchWholeWord = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
Do While wrdDoc.ActiveWindow.Selection.Find.Execute
iCount = iCount + 1
wrdDoc.ActiveWindow.Selection.MoveRight
' MsgBox iCount
Loop
End With
'// Unnecessary storing, I know
result = iCount
Cells(i, 6).Value = result
If result > 1 Then
Cells(i, 7).Value = "YES"
Else
Cells(i, 7).Value = "NO"
End If
Else
TP = Cells(i, 4).Text
FindWord = Cells(i, 1).Value
'// Close and don't save application
wrdApp.Quit SaveChanges:=0 ' wdDoNotSaveChanges (Word constant not defined in Excel)
Set wrdApp = Nothing
Set wrdDoc = Nothing
On Error Resume Next
Set wrdApp = GetObject(, "Word.Application")
If Err.Number > 0 Then Set wrdApp = CreateObject("Word.Application")
On Error GoTo 0
Set wrdDoc = wrdApp.Documents.Open("C:\TP\X-" & TP & ".docx")
'// Defines selection for Word's find function
wrdDoc.ActiveWindow.Selection.GoTo What:=wdGoToSection, Which:=wdGoToFirst
wrdDoc.SelectAllEditableRanges
iCount = 0
'// Word Find Method Setup Block
With wrdDoc.ActiveWindow.Selection.Find
.Text = FindWord
.Replacement.Text = ""
.Forward = True
.Wrap = 1 ' wdFindContinue (Word constant not defined in Excel)
.Format = False
.MatchCase = True
.MatchWholeWord = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
Do While wrdDoc.ActiveWindow.Selection.Find.Execute
iCount = iCount + 1
wrdDoc.ActiveWindow.Selection.MoveRight
Loop
End With
'// Unnecessary storing, I know
result = iCount
Cells(i, 6).Value = result
If result > 1 Then
Cells(i, 7).Value = "YES"
Else
Cells(i, 7).Value = "NO"
End If
End If
Next i
End Sub
Your syntax on the error line seems correct. You can:
- verify the filename by insrting a debug.print line to show the filename expression
- verify the Word instanciation by opening Word manually, removing the On error in your code, and running it again.
I am trying to modify code from http://www.ozgrid.com/forum/showthread.php?t=174699
which looks in all word documents in a folder and returns an 'x' in columns if a searched value is found.
The column names are the documents in the folder. The row names are the searched strings.
I would like the routine to return rather a value or a string that is found in a word document to the right or next to the searched strings.
This would be a great tool to collect dates, invoice values, names etc. from unstructured data in word documents to excel table.
With oDOC.Content.Find
.ClearFormatting
.Text = rCell.Value
.MatchCase = False
.MatchWholeWord = False
.Execute
If .Found Then
'Sheet1.Cells(rCell.Row, lngCol).Value = "x" , returns an "x" if the word is found.
End If
End With
The complete code looks as follows:
Public Sub SearchDocs()
Dim oWRD As Object '** Word.Application
Dim oDOC As Object '** Word.Document
Dim oFound As Object '** Word.Range
Dim rCell As Excel.Range
Dim lngCol As Long
Dim strFile As String
On Error GoTo ErrHandler
Application.ScreenUpdating = False
lngCol = 1
'** Set oWRD = New Word.Application
Set oWRD = CreateObject("Word.Application")
oWRD.Visible = True
'// XL2007 specific
Sheet1.Range("B2:XFD100000").ClearContents
strFile = Dir$(Sheet1.Range("B1").Value & "\*.doc?")
lngCol = 2
'// loop matching files
Do While strFile <> vbNullString
'open
Set oDOC = oWRD.Documents.Open(Sheet1.Range("B1").Value & "\" & strFile)
With Sheet1.Cells(2, lngCol)
.Value = strFile
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.WrapText = False
.Orientation = 90
.EntireColumn.ColumnWidth = 3.35
End With
For Each rCell In Sheet1.Range("A3:A" & Range("A" & Rows.Count).End(xlUp).Row)
With oDOC.Content.Find
.ClearFormatting
.Text = rCell.Value
.MatchCase = False
.MatchWholeWord = False
.Forward = False
.Execute
If .Found Then
'Selection.Collapse wdCollapseEnd
'Selection.Expand wdWord
'Sheet1.Cells(rCell.Row, lngCol).Value = "x"
'Sheet1.Cells(rCell.Row, lngCol).Value = .Text
Sheet1.Cells(rCell.Row, lngCol).Value = .Parent.Selection.Text
End If
End With
Next
Application.ScreenUpdating = True
DoEvents
Application.ScreenUpdating = False
lngCol = lngCol + 1
oDOC.Close
'// get next file
strFile = Dir$()
Loop
MsgBox "Finshed...", vbInformation
ErrHandler:
Application.ScreenUpdating = True
oWRD.Application.Quit
End Sub
I am not able to find in the net, or figure out, how to return a range of the found text and then offset it to return the text/value to the right. I am aware that offset exists in vba excel. But how to offset the range of the found string and return the value found in this offset range to excel?
This approach might work. Start by initializing a Range object to the range you want to search
Set oFound = oDOC.Content
Then instead of With oDOC.Content.Find do
With oFound.Find
When .Found = True, oFound will be moved to the found text. You can then move oFound by 1 word with something like:
With oFound
.MoveEnd Unit:=wdWord, Count:=1
.MoveStart Unit:=wdWord, Count:=1
End With
You can adjust Unit and Count per your requirements. Depending on your needs the related range object methods MoveEndUntil, MoveEndWhile, MoveStartUntil and MoveStartWhile may give better functionality. Check out these and other Range.Move methods here.
Hope that helps
The credit goes to xidgel. Thanks so much. It works like a charm.
The edited code, according to xidgel's direction may be of help to others, let me paste it:
Public Sub SearchDocs()
Dim oWRD As Object '** Word.Application
Dim oDOC As Object '** Word.Document
Dim oFound As Object '** Word.Range
Dim rCell As Excel.Range
Dim lngCol As Long
Dim strFile As String
'On Error GoTo ErrHandler
Application.ScreenUpdating = False
lngCol = 1
'** Set oWRD = New Word.Application
Set oWRD = CreateObject("Word.Application")
oWRD.Visible = True
'// XL2007 specific
Sheet1.Range("B2:XFD100000").ClearContents
strFile = Dir$(Sheet1.Range("B1").Value & "\*.doc?")
lngCol = 2
'// loop matching files
Do While strFile <> vbNullString
'open
Set oDOC = oWRD.Documents.Open(Sheet1.Range("B1").Value & "\" & strFile)
Set oFound = oDOC.Content
With Sheet1.Cells(2, lngCol)
.Value = strFile
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.WrapText = False
.Orientation = 90
.EntireColumn.ColumnWidth = 3.35
End With
For Each rCell In Sheet1.Range("A3:A" & Range("A" & Rows.Count).End(xlUp).Row)
With oFound.Find 'With oDOC.Content.Find
Debug.Print rCell.Value
.ClearFormatting
.Text = rCell.Text
.MatchCase = False
.MatchWholeWord = False
.Forward = True
.MatchWildcards = True
.Wrap = wdFindContinue
.Execute
Debug.Print .Found
If .Found Then
With oFound
.Collapse wdCollapseEnd
.Expand wdWord
.MoveStart Unit:=wdWord, Count:=1
.MoveEnd Unit:=wdWord, Count:=5
End With
Sheet1.Cells(rCell.Row, lngCol).Value = oFound.Text
Debug.Print oFound.Text
End If
End With
Next
Application.ScreenUpdating = True
DoEvents
Application.ScreenUpdating = False
lngCol = lngCol + 1
oDOC.Close
'// get next file
strFile = Dir$()
Loop
MsgBox "Finshed...", vbInformation
ErrHandler:
Application.ScreenUpdating = True
oWRD.Application.Quit
End Sub
I want to loop through 2000 csv files, convert them to xls, make changes to the spreadsheet.
I can't work out how to save and close the workbook before moving on to the next.
I tried the following at the end of the script.
Application.ActiveWindow.Close SaveChanges:=False
ActiveWorkbook.Close SaveChanges:=False
I get a Minor loss of fidelity message.
The full code
Sub batchconvertcsvxls()
Dim wb As Workbook
Dim strFile As String, strDir As String, strOut_Dir As String, myNewFileName As String
strDir = "C:\csv\" 'location of csv files
strOut_Dir = "C:\converted\" 'location of xls files
strFile = Dir(strDir & "*.csv")
Do While strFile <> ""
Set wb = Workbooks.Open(filename:=strDir & strFile, Local:=True)
With wb
.SaveAs strOut_Dir & Replace(wb.Name, ".csv", ".xls"), 56
.Close True
End With
Set wb = Nothing
Set wb = Workbooks.Open(filename:=strOut_Dir & Replace(strFile, ".csv", ".xls"))
Rows("1:1").Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorDark2
.TintAndShade = -0.249977111117893
.PatternTintAndShade = 0
End With
Selection.RowHeight = 60
Selection.ColumnWidth = 30
With Selection
.HorizontalAlignment = xlGeneral
.VerticalAlignment = xlBottom
.WrapText = True
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
With Selection
.HorizontalAlignment = xlGeneral
.VerticalAlignment = xlTop
.WrapText = True
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
Columns("E:E").Select
With Selection
.HorizontalAlignment = xlLeft
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
Cells.Select
Selection.AutoFilter
Range("E2").Select
ActiveWindow.FreezePanes = True
For i = 1 To ActiveSheet.UsedRange.Columns.Count
DataFound = False
j = 2
While DataFound = False And j <= ActiveSheet.UsedRange.Rows.Count
If Cells(j, i).Value <> "" Then
DataFound = True
End If
j = j + 1
Wend
If DataFound = False Then
Columns(i).Hidden = True
End If
Next
strFile = Dir
Application.ActiveWindow.Close SaveChanges:=True ActiveWorkbook.Close
SaveChanges:=False
Loop
End Sub
You can try wb.Close SaveChanges:=False since you have set the workbook as wb that may get it to close without the message box popping up.
Also you can try Application.DisplayAlerts = False(this goes at the start of your code), it stops most types of messages popping up (there are some errors that cannot be stopped though(I dont have a list to hand but "out of memory" is an error that cannot be prevented for instance)).
I'll look into it a bit more sine im doing this off the top of my head and update the post if I get any more