Excel VBA Code Race Condition Not Fixed by Wait, Sleep, DoEvents, etc - excel

SOLVED! See the code below for the solution!
I have an Excel file containing multiple shape objects next to a series of text. I wrote a script to identify the location of each shape, identify how many cells to the right and down the text extends to, sets that as a range and then imports that into a chart object so I can save it as a .jpg.
The trouble is that between the creation of the chart and the pasting of the string there exists a Race Condition. If I step through the script it works fine, but as soon as I run it I get nothing but blank images.
I've tried Application.ScreenUpdating = True; Application.PrintCommunication = True; and DoEvents
I've also tried Application.Wait, but even having it wait ten seconds doesn't do the trick, when stepping through the code the chart is loaded in less than 2 seconds.
Recently I tried the kernel32 sleep method as well, and that doesn't seem to work either. Again, the amount of time I let the system sleep far exceeded my stepping. I also added all of the above methods between each line within the With statement (obviously not as a solution, but as a test) and that didn't work either..
At this point I'm completely at a loss.
If I place a stop at .Chart.Paste and then run the script (F5), and just keep hitting Run then the script works wonderfully. I just don't want users to have to sit there and hit run 600 times.
There are obvious redundancies written in between the creation of the chart and pasting of the text. This is all in the attempt at getting the code to work properly when run, and once a solution is found most of that code will be removed.
Option Explicit
Public Function ChartCheck() As String
ReCheckChart:
DoEvents
If ActiveWorkbook.ActiveSheet.ChartObjects.Count > 0 Then
GoTo ContinuePaste:
Else
GoTo ReCheckChart:
ContinuePaste:
End If
End Function
Public Function GetFolder() As String
Dim fldr As FileDialog
Dim sItem As String
Set fldr = Application.FileDialog(msoFileDialogFolderPicker)
With fldr
.Title = "Select a Folder to Save the Images In"
.AllowMultiSelect = False
If .Show -1 Then GoTo NextCode:
sItem = .SelectedItems(1)
End With
NextCode:
GetFolder = sItem
Set fldr = Nothing
End Function
Private Sub DNImageExtraction_Click()
Dim fileName As String
Dim targetWorkbook As Excel.Workbook
Dim targetWorksheet As Excel.Worksheet
Dim saveLocation As Variant
Dim saveName As String
Dim targetShape As Shape
Dim workingRange As Excel.Range
Dim bottomRow As Long
Dim workingRangeWidth As Double
Dim workingRangeHeight As Double
Dim tempChart As ChartObject
Application.ScreenUpdating = False
Application.DisplayAlerts = False
DNImageExtraction.AutoSize = False 'This is necessary to prevent the system I use from altering the font on the button
DNImageExtraction.AutoSize = True
DNImageExtraction.Height = 38.4
DNImageExtraction.Left = 19.2
DNImageExtraction.Width = 133.8
fileName = Application.GetOpenFilename("Excel Files (*.xls*),*.xls*", , "Please select Excel file...")
Set targetWorkbook = Workbooks.Open(fileName)
Set targetWorksheet = targetWorkbook.ActiveSheet
saveLocation = GetFolder
For Each targetShape In targetWorksheet.Shapes
Set workingRange = targetWorksheet.Cells(targetShape.TopLeftCell.Row, targetShape.TopLeftCell.Column).Offset(1, 0)
saveName = workingRange.Text
If workingRange.Offset(0, 1).Value "" Then
If workingRange.Offset(1, 1).Value = "" Then
Set workingRange = Nothing
Set workingRange = targetWorksheet.Cells(targetShape.TopLeftCell.Row, targetShape.TopLeftCell.Column).Resize(, 2)
Else
bottomRow = workingRange.Offset(0, 1).End(xlDown).Row
Set workingRange = targetWorksheet.Cells(targetShape.TopLeftCell.Row, targetShape.TopLeftCell.Column).Resize((bottomRow + 2 - workingRange.Row), 2)
End If
workingRangeWidth = workingRange.Width
workingRangeHeight = workingRange.Height
End If
workingRange.CopyPicture Appearance:=xlPrinter, Format:=xlPicture
Set tempChart = targetWorksheet.ChartObjects.Add(0, 0, workingRangeWidth, workingRangeHeight)
Application.ScreenUpdating = True
Application.PrintCommunication = True
DoEvents
Call ChartCheck
tempChart.Chart.Paste
Application.ScreenUpdating = False
tempChart.Chart.Export fileName:=saveLocation & "\DN " & saveName & ".jpg", Filtername:="JPG"
tempChart.Delete
Set tempChart = Nothing
Next
Application.Workbooks(targetWorkbook.Name).Close savechanges:=False
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Any assistance in either a solution to the Race Condition, or re organizing the script to avoid the Race Condition altogether would be greatly appreciated.
(Code above was updated per suggestions made by Macro Man, and then re-modified once again to add in all previous suggestions on how to fix Race Condition issues after the changes were not effective.)

Consider using Application.OnTime which is good feature. It allows the scheduling of some code to be run at a certain time, most often one adds a few seconds to the current time.
Excel VBA is single-threaded and so there is no real synchronization but there is a message pump to keep order. The great thing about Application.OnTime is that it will not run despite being scheduled until the current graph of code has completed.
Because Application.OnTime uses the message pump as that is a FIFO structure it is possible to interleave the execution of code.
I think this might help here.
You can schedule a "hasItFinished" procedure which checks the existence of the shape/chart objects and if not re-schedules itself.
P.S. Can be a little tricky to debug, refactor as much code as possible outside of the procedure that you will schedule and unit test them separately. Please don't expect the lovely Edit,Debug and Continue flow that you normally get with VBA if you go down this path.

Try getting rid of the error handlers and labels, and working with objects directly instead of searching through workbook/worksheet collections. Also using meaningful variable names and proper indentation will help follow the code easily should you have any issues.
If your code works when stepping through, that usually suggests there is some issue with the use of ActiveWorkbook when workbooks are being opened/closed. Working with workbooks as objects allows us to overcome that problem because no matter if the workbook is active or not, we are always using the same instance of that workbook.
Private Sub DNImageExtraction_Click()
Dim fileName As String
Dim targetWorkbook As Excel.Workbook
Dim targetWorksheet As Excel.Worksheet
Dim saveLocation As Variant
Dim saveName As String
Dim targetShape As Shape
Dim workingRange As Excel.Range
Dim bottomRow As Long
Dim workingRangeWidth As Double
Dim workingRangeHeight As Double
Dim tempChart As ChartObject
Application.ScreenUpdating = False
Application.DisplayAlerts = False
fileName = Application.GetOpenFilename("Excel Files (*.xls*),*.xls*", , "Please select Excel file...")
Set targetWorkbook = Workbooks.Open(fileName)
Set targetWorksheet = targetWorkbook.ActiveSheet
saveLocation = GetFolder
For Each targetShape In targetWorksheet.Shapes
Set workingRange = targetWorksheet.Cells(targetShape.TopLeftCell.Row, targetShape.TopLeftCell.Column).Offset(1, 0)
saveName = workingRange.Text
If workingRange.Offset(0, 1).value <> "" Then
If workingRange.Offset(1, 1).value = "" Then
Set workingRange = Nothing
Set workingRange = targetWorksheet.Cells(targetShape.TopLeftCell.Row, targetShape.TopLeftCell.Column).Resize(, 2)
Else
bottomRow = workingRange.Offset(0, 1).End(xlDown).Row
Set workingRange = targetWorksheet.Cells(targetShape.TopLeftCell.Row, targetShape.TopLeftCell.Column).Resize((bottomRow + 2 - workingRange.Row), 2)
End If
workingRangeWidth = workingRange.Width
workingRangeHeight = workingRange.Height
End If
workingRange.CopyPicture Appearance:=xlPrinter, Format:=xlPicture
Set tempChart = targetWorksheet.ChartObjects.Add(0, 0, workingRangeWidth, workingRangeHeight)
With tempChart
.Chart.Paste
.Chart.Export FileName:=saveLocation & "\DN " & saveName & ".jpg", Filtername:="JPG"
.Delete
End With
Set tmpChart = Nothing
DoEvents
Next
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub

Related

VBA Date import files issue

The macro was given to me by my predecessor.
I have an issue with the ‘date’ when importing data using the macro. It works well when I import a data file into a macro and transform it into a report, this all works well.
The issue is that if I import a 2nd data file today again after the 1st round it won’t work. I get a prompt message from the macro saying "No new rows to import. If this is wrong check the 'LastImportDates' sheet". It will only work the next day. This is the issue I am struggling with as I need to import several files on the same day.
Please see the VBA codes below, It shows the section of the VBA macro. I hope this is the one that caused the issue. I am hoping that you can point me to where I need to change it, allowing me a import multiple data files on the same day.
I hope everything makes sense. I will endeavor my best to assist you further if needed.
Best regards
V
Sub MainCopyData()
Set rsheet = mbook.Sheets("RAW")
rsheet.Activate
rsheet.Rows("2:100000").EntireRow.Delete
Call FindFile
Call CopyData
rsheet.Activate
tempbook.Close SaveChanges:=False
End Sub
Sub FindFile()
Dim fso As Object 'FileSystemObject
Dim fld As Object 'Folder
Dim fl As Object 'File
Dim Mask As String
Set fso = CreateObject("scripting.FileSystemObject") ' late binding
Set fldStart = fso.GetFolder(ActiveWorkbook.Path) ' <-- use your FileDialog code here
For Each fld In fldStart.Files
If InStr(1, fld.Name, "data_Checkout_Starts_ALL_TIME.csv") > 0 Then
Set fl = fld
Exit For
End If
Next
If fld Is Nothing Then
With Application.FileDialog(msoFileDialogFilePicker)
'Makes sure the user can select only one file
.AllowMultiSelect = False
'Show the dialog box
.Show
'Store in fullpath variable
Set fl = fso.GetFile(.SelectedItems.Item(1))
End WithEnd If
Set tempbook = Workbooks.Open(fl.Path, Local:=True)
End Sub
Sub CopyData()
lastimport = mbook.Sheets("ImportDates").Cells(1, 1).End(xlDown).Value
Set tempsht = tempbook.Sheets(1)
FirstR = 0
LastR = 0
dateC = findCol("EventDate", tempsht)
For x = 2 To tempsht.Cells(1, 1).End(xlDown).Row
If FirstR = 0 And tempsht.Cells(x, dateC) > lastimport Then
FirstR = x
End If
If tempsht.Cells(x, dateC).Value < Date Then
LastR = x
End If
Next x
If FirstR > 0 Then
mbook.Sheets("ImportDates").Cells(1, 1).End(xlDown).Offset(1, 2).Value = LastR - FirstR - 1
mbook.Sheets("ImportDates").Cells(1, 1).End(xlDown).Offset(1, 1).Value = Date
mbook.Sheets("ImportDates").Cells(1, 1).End(xlDown).Offset(1, 0).Value = Date - 1
Else
MsgBox ("No new rows to import. If this is wrong check the 'LastImportDates' sheet")
tempbook.Close SaveChanges:=False
End
End If
rsheet.Activate
tempsht.Rows(FirstR & ":" & LastR).Copy rsheet.Cells(2, 1)
End Sub```

Error 91 / 438 when working with Set and .Find Range

This Program is Supposed to Open A whole lot of Workbooks, the One at the top of the list (filename1, AKA Plan) is a place to upload many different cells of data, every other opened workbook is functionally the same. The file opening present no issues.
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
Dim fd As FileDialog
Dim FileName1 As String
Dim Plan, Leg As Workbook
Dim FileChosen As Integer
Dim CUILT As String
Dim CuiltRange As Range
Set fd = Application.FileDialog(msoFileDialogOpen)
FileChosen = fd.Show
fd.InitialView = msoFileDialogViewList
fd.AllowMultiSelect = True
fd.Filters.Clear
fd.Filters.Add "Plan Excel", "*.xlsx"
fd.Filters.Add "Leg Excel, "*.xlsm"
fd2.FilterIndex = 1
If FileChosen <> -1 Then
MsgBox "The Operation has been stopped"
Else
FileName1 = fd2.SelectedItems(1)
Workbooks.Open (FileName1)
Set Plan = ActiveWorkbook
For i = 2 To fd2.SelectedItems.Count
Workbooks.Open fd2.SelectedItems(i)
Set Leg = ActiveWorkbook
CUILT = Leg.Sheets(1).Range("I19").Value
Plan.Worksheets(1).Activate
Now comes the part of the code that's giving me trouble, with the help of some other post I was able to navigate through the finicky nature of the find:Variable function, but right after I was able to sort that error, the "Found Range Variable = Range" now just doesn't want to cooperate. It gives me error 91, it's as if CuiltRow is not actually saving any value in the previous Set operation, even when I try to MsgBox after "Set" to figure it out, if just tells me its not defined, Every other out I've tried has ended in
error 438 "object doesn't support property or method".
Set CuiltRow = Plan.Sheets(1).Range("C16:C421").Find(CUILT, LookIn:=xlValues, LookAt:=xlWhole)
CuiltRow.Offset(0, 5).Range("A1").Value = Leg.Sheets(1).Range("D14").Value
The Set CuiltRow wont work without the Set, and I know the set is the one causing the 91? Honestly, im at a loss, Any help Would be Appreciated!
Next i
'Else
'End If
MsgBox "Operation has been completed"
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
End If
End Sub
I Just want to use the range CuiltRow returns as a reference waypoint to set many offsets, but with my poor syntax in "Range(offset) = Range" function VBA believes I'm trying to set a Value into a Range Object, how can I express it differently so its clear?

Pass parameter from VbScript to vba function

I want to call a vba function from vbscript which has a parameter, I Know how to call a parameterized sub but having issue with function
Here is what I have tried, I tried the code here Calling vba function(with parameters) from vbscript and show the result , but this also didn't work, it gave an error as expected end of statement
Set xlObj = CreateObject("Excel.Application")
Set objWorkbook = xlObj.Workbooks.Open("E:\Headers.xlsm")
xlObj.Application.Visible = False
xlObj.Workbooks.Add
Dim result
result = xlObj.Application.Run("Headers.xlsm!Headers",filename)
xlFile.Close True
xlObj.Quit
this my vba function
Function Headers(filename As String) As String
Application.ScreenUpdating = False
Dim myWb As Workbook
Dim i As Integer
Dim flag As Boolean
Set myWb = Workbooks.Open(filename:=filename)
Dim arr
arr = Array("col1","col2")
For i = 1 To 2
If Cells(1, i).Value = arr(i - 1) Then
Headers = "True"
Else
Headers = "False , Not Found Header " & arr(i - 1)
Exit Function
End If
Next
myWb.Close
End Function
In your VBScript xlObj is set to be an application Set xlObj = CreateObject("Excel.Application"). That means xlObj.Application should be xlObj only.
In your VBScript Filename is not declared nor set to a value therefore it is empty. You need to define value to it.
Set xlObj = CreateObject("Excel.Application")
Set objWorkbook = xlObj.Workbooks.Open("E:\Headers.xlsm")
xlObj.Visible = False
xlObj.Workbooks.Add
Dim Filename 'declare filename and set a value to it
Filename = "E:\YourPath\Yourfile.xlsx"
Dim Result
Result = xlObj.Run("Headers.xlsm!Headers", Filename)
xlFile.Close True
xlObj.Quit
In your function you use Exit Function. This will stop the code immediately at this point, which means your workbook myWb will not be closed! It stays open because myWb.Close is never reached. Change Exit Function to Exit For to just exit the loop and continue to close the workbook.
Cells(1, i).Value is neither specified which workbook it is in nor which worksheet. This is not very reliable never call Cells or Range without specifying workbook and worksheet (or Excel will guess which one you mean, and Excel can fail if you are not precise).
Therfore I recommend to use something like myWb.Worksheets(1).Cells(1, i).Value if you always mean the first worsheet in that workbook. Alternatively if it has a defined name using its name would be more reliable: myWb.Worksheets("SheetName").Cells(1, i).Value
If you turn off ScreenUpdating don't forget to turn it on in the end.
Error handling in case filename does not exist would be nice to not break the function.
You can slightly improve speed by assuming Headers = "True" as default and just turn it False in case you find any non matching header. This way the variable is only set once to True instead of multiple times for every correct header.
Public Function Headers(ByVal Filename As String) As String
Application.ScreenUpdating = False
Dim flag As Boolean 'flag is never used! you can remove it
On Error Resume Next 'error handling here would be nice to not break if filename does not exist.
Dim myWb As Workbook
Set myWb = Workbooks.Open(Filename:=Filename)
On Error Goro 0 'always reactivate error reporting after Resume Next!!!
If Not myWb Is Nothing Then
Dim Arr() As Variant
Arr = Array("col1", "col2")
Headers = "True" 'assume True as default and just change it to False if a non matching header was found (faster because variable is only set true once instead for every column).
Dim i As Long 'better use Long since there is no benefit in using Integer
For i = 1 To UBound(arr) + 1 'use `ubound to find the upper index of the array, so if you add col3 you don't need to change the loop boundings
If Not myWb.Worksheets(1).Cells(1, i).Value = Arr(i - 1) Then 'define workbook and worksheet for cells
Headers = "False , Not Found Header " & Arr(i - 1)
Exit For '<-- just exit loop but still close the workbook
End If
Next i
Else
Headers = "File '" & Filename & "' not found!"
End If
Application.ScreenUpdating = True
myWb.Close
End Function

Adding new and removing old VBA to all workbooks in a folder

I have about 60 workbooks with several modules and I need to remove one sub routine in one module then add code to a specific worksheet.
I currently have code running every time you open the workbook asking to run and archive data to another worksheet, it works. Problem is we are in the workbooks several times, so every time we open them, we have to answer the question.
I found a more elegant way to ask to archive when I go to the first worksheet where we go to change data at the end of the month. Only when we open this are we needing to archive the old data. Some times we go here to look at the data, but it's not the usual. I have new code now for the specific worksheet using on select, that works.
I'm trying to update the code across all my workbooks without having to open them up 1 by 1 and make the changes, copy, paste, delete, save, open next file, repeat.
'code to remove from module named ArchiveHistoricalData
Sub Auto_Open()
AskArchive
End Sub
'Code to add to worksheet named Data Dump
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
AskArchive
End Sub
I'd like to remove the first sub, then add the second sub to a specific worksheet (Named the same across all workbooks). Then if I have changes in the future, I can easily update all my workbooks with other changes.
Posting another answer structured as generalized tools to delete and/or add or replace any number of procedures from any number of files. As mentioned earlier it is assumed that Trust Access to Visual Basics Project must be enabled.
In a new excel file with added reference to Microsoft Visual Basic for Application extensibility, add a module named “Copy_Module”. Specifically in your case, copy Worksheet_SelectionChange code in a module named “Copy_Module”.
Its AddReplaceProc function would copy any procedure from a module named “Copy_Module” in the source workbook while DeleteProc function would delete a procedure.
Sub test4()
Dim Wb As Workbook, ws As Worksheet
Dim Path As String, Fname As String
Dim Fno As Long
Path = "C:\Users\User\Documents\TestFolder\"
Fname = Dir(Path & "*.xlsm")
Fno = 1
Do While Fname <> ""
Set Wb = Application.Workbooks.Open(Path & Fname)
If Wb.VBProject.Protection = vbext_pp_none Then
Set ws = ThisWorkbook.ActiveSheet
Fno = Fno + 1
ws.Cells(Fno, 1).Value = Fname
'ws.Cells(Fno, 2).Value = AddReplaceProc(Wb, "ArchiveHistoricalData", "DoStuff2")
ws.Cells(Fno, 2).Value = DeleteProc(Wb, "ArchiveHistoricalData", "Auto_Open")
ws.Cells(Fno, 3).Value = AddReplaceProc(Wb, Wb.Worksheets("Data Dump").CodeName, "Worksheet_SelectionChange")
Wb.Close True
Else
Wb.Close False
End If
Fname = Dir
Loop
End Sub
Private Function DeleteProc(Wb As Workbook, CompName As String, ProcName As String) As Boolean
Dim Vbc As CodeModule, Vbcomp As VBComponent
DeleteProc = False
For Each Vbcomp In Wb.VBProject.VBComponents
If Vbcomp.Name = CompName Then
Set Vbc = Vbcomp.CodeModule
On Error GoTo XExit
If Vbc.ProcStartLine(ProcName, 0) > 0 Then
Vbc.DeleteLines Vbc.ProcStartLine(ProcName, 0), Vbc.ProcCountLines(ProcName, 0)
DeleteProc = True
Exit For
End If
End If
Next Vbcomp
XExit: On Error GoTo 0
End Function
Private Function AddReplaceProc(Wb As Workbook, CompName As String, ProcName As String) As Boolean
Dim Vbc As CodeModule, Vbcomp As VBComponent
Dim VbcSrc As CodeModule, StLine As Long, EndLine As Long
Dim i As Long, X As Long
'Check for older version of the procedure and delete the same before coping new version
AddReplaceProc = DeleteProc(Wb, CompName, ProcName)
Debug.Print "Old Proc " & ProcName & " Found and Deleted : " & AddReplaceProc
AddReplaceProc = False
For Each Vbcomp In Wb.VBProject.VBComponents
If Vbcomp.Name = CompName Then
Set Vbc = Vbcomp.CodeModule
Set VbcSrc = ThisWorkbook.VBProject.VBComponents("Copy_Module").CodeModule
StLine = VbcSrc.ProcStartLine(ProcName, 0)
EndLine = StLine + VbcSrc.ProcCountLines(ProcName, 0) - 1
X = 0
For i = StLine To EndLine
X = X + 1
Vbc.InsertLines X, VbcSrc.Lines(i, 1)
Next i
AddReplaceProc = True
Exit For
End If
Next Vbcomp
End Function
Proper caution is a must for this type of remote changes. It is always wise to try the code first only to copies of target files and confirm proper working etc.
It only works with files with unprotected VBA projects. For files with protected VBA files refer SO post Unprotect VBProject from VB code.
Try the code from any workbook (not in the same target folder) module. Add reference to Microsoft visual basic for applications extensibility. and/or make vbext_pk_Proc as 0.
Sub test3()
Dim ws As Workbook
Dim Vbc As CodeModule
Dim Path As String, Fname As String
Dim Wx As Worksheet
Dim HaveAll As Boolean
Dim VbComp As VBComponent
Path = "C:\Users\User\Documents\TestFolder\"
Fname = Dir(Path & "*.xlsm")
Do While Fname <> ""
' Debug.Print Fname
Set ws = Application.Workbooks.Open(Path & Fname)
HaveAll = False
For Each VbComp In ws.VBProject.VBComponents
If VbComp.Name = "ArchiveHistoricalData" Then
'used erron handler instead of iterating through all the lines for keeping code short
On Error GoTo failex
If VbComp.CodeModule.ProcStartLine("Auto_Open", 0) > 0 Then
HaveAll = True
failex: Resume failex2
failex2: On Error GoTo 0
Exit For
End If
End If
Next VbComp
If HaveAll Then
HaveAll = False
For Each Wx In ws.Worksheets
If Wx.Name = "Data Dump" Then
HaveAll = True
Exit For
End If
Next Wx
End If
If HaveAll Then
Set Vbc = ws.VBProject.VBComponents("ArchiveHistoricalData").CodeModule
Vbc.DeleteLines Vbc.ProcStartLine("Auto_Open", vbext_pk_Proc), Vbc.ProcCountLines("Auto_Open", vbext_pk_Proc)
Set Vbc = ws.VBProject.VBComponents(ws.Worksheets("Data Dump").CodeName).CodeModule
Vbc.InsertLines 1, "Private Sub Worksheet_SelectionChange(ByVal Target As Range)"
Vbc.InsertLines 2, "AskArchive"
Vbc.InsertLines 3, "End Sub"
ws.Close True
Else
ws.Close False
End If
Debug.Print Fname, HaveAll
Fname = Dir
Loop
End Sub
However code will encounter error if the stated Worksheets, code modules and procedures are not available. Please take due care, if not confirmed about availability of the stated Worksheets, code modules and procedures in all the target files. (may use error handler or check for existence for the Sheets, code modules and procedures by iterating through after opening the target file and skip accordingly). Also Trust Access To Visual Basics Project must be enabled.

Excel VBA custom header code works on 32-bit but not 64-bit

Context: I'm writing a list excel doc that has two sheets (Equipment List and List Inputs). I want the user to be able to input the general document info (name and date etc.) on the Inputs sheet, and this data to be captured as a snapshot and inserted into the left, centre and right header boxes. It has two different headers - one for the first page and one for every page after.
I wrote the code on excel 2013 32-bit (and it worked), only to realise that it doesn't work on any 64-bit machines. When I say it doesn't work I mean the images generated don't appear in the print preview in the header.
I'm a VBA novice and I really don't know where this code is failing, however I think it might be at the .Chart.Paste step.
I have looked into other threads on 32 to 64 bit conversion but they all reference PtrSafe which I don't think is relevant to my code.
How can I update my code to run on 64-bit?
Any advice is much appreciated.
Thanks.
Workbook:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call Module1.AddHeaderToAll_FromCurrentSheet
End Sub
Private Sub Workbook_Open()
End Sub
General (Module 1) Save_Object_As_Picture:
Sub AddHeaderToAll_FromCurrentSheet()
Dim ws As Worksheet
Dim tempFilePath As String
Dim tempPFilePath As String
Dim tempTBFilePath As String
Dim tempPic As Shape
Dim tempPrimeroPic As Shape
Dim tempTiBlkPic As Shape
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Set tempPic = ThisWorkbook.Sheets("List Inputs").Shapes("RevBlkPic")
Set tempPrimeroPic = ThisWorkbook.Sheets("List Inputs").Shapes("PrimeroPic")
Set tempTiBlkPic = ThisWorkbook.Sheets("List Inputs").Shapes("TiBlkPic")
tempFilePath = Environ("temp") & "\image.jpg"
Save_Object_As_Picture tempPic, tempFilePath
tempPFilePath = Environ("temp") & "\image2.jpg"
Save_Object_As_Picture tempPrimeroPic, tempPFilePath
tempTBFilePath = Environ("temp") & "\image3.jpg"
Save_Object_As_Picture tempTiBlkPic, tempTBFilePath
For Each ws In ActiveWorkbook.Worksheets
'ws.PageSetup.FirstPage.CenterHeaderPicture
'With ActiveSheet.PageSetup.DifferentFirstPageHeaderFooter = True
'First Page Headers
ws.PageSetup.DifferentFirstPageHeaderFooter = True
ws.PageSetup.FirstPage.CenterHeader.Picture.Filename = tempFilePath
ws.PageSetup.FirstPage.CenterHeader.Text = "&G"
ws.PageSetup.FirstPage.RightHeader.Picture.Filename = tempPFilePath
ws.PageSetup.FirstPage.RightHeader.Text = "&G"
ws.PageSetup.FirstPage.LeftHeader.Picture.Filename = tempTBFilePath
ws.PageSetup.FirstPage.LeftHeader.Text = "&G"
'Different Page Headers
ws.PageSetup.RightHeaderPicture.Filename = tempPFilePath
ws.PageSetup.RightHeader = "&G"
ws.PageSetup.LeftHeaderPicture.Filename = tempTBFilePath
ws.PageSetup.LeftHeader = "&G"
ws.PageSetup.CenterHeaderPicture.Filename = tempFilePath
ws.PageSetup.CenterHeader = ""
' ws.PageSetup.RightHeaderPicture.Filename = tempPFilePath
' ws.PageSetup.RightHeader = "&G"
' ws.PageSetup.LeftHeaderPicture.Filename = tempTBFilePath
' ws.PageSetup.LeftHeader = "&G"
Next ws
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Private Sub Save_Object_As_Picture(saveObject As Object, imageFileName As String)
'Save a picture of an object as a JPG/JPEG/GIF/PNG file
'Arguments
'saveObject - any object in the CopyPicture method's 'Applies To' list, for example a Range or Shape
'imageFileName - the .gif, .jpg, or .png file name (including folder path if required) the picture will be saved as
Dim temporaryChart As ChartObject
Application.ScreenUpdating = False
saveObject.CopyPicture xlScreen, xlPicture
Set temporaryChart = ActiveSheet.ChartObjects.Add(0, 0, saveObject.Width + 1, saveObject.Height + 1)
With temporaryChart
.Border.LineStyle = xlLineStyleNone 'No border
.Chart.Paste
.Chart.Export imageFileName
.Delete
End With
Application.ScreenUpdating = True
Set temporaryChart = Nothing
End Sub

Resources