Publish an excel worksheet as an HTML file using a command button - vba

So I have tons of data in my workbook that I need to pass on to my users in a way that allows them to interact with the workbook..., but also limits what they can actually do. So I have made it so they can access certain pages to Add needed data, then I've given access to a menu page so they can run a report.
This report I have found is best if it's an html page.
To that end, I have tried several different ways, save as...and publish. I like the publish version, but I can not for the life of me get this working. All the samples I see, appear to be the same. Here is the line of code in question:
ActiveWorkbook.PublishObjects.Add(xlSourceSheet, ActiveWorkbook.Path & ".htm, "Sheet1", "", xlHtmlStatic, "", "Report Title").Publish True
Every time I get a run time error '1004':
Method 'Publish' of object 'PublishObject' failed
I have the above line of code in a sub, am I missing something? Do I need to set up the publish differently? Thanks for any ideas.

I have had a similar mysterious problem that sometimes it (.Publish) works and sometimes it doesn't, when I try publish.
However, I think the problem might be, in cases when it doesn't work right off the bat, to first save the relevant region as a webpage so that it exists already on the server. After that region has been saved at least once (manually or else maybe with .SaveAs .. see below), then the publish might work.
Here is an example of how I get this to work more consistently, something to convey the structure that works for me:
wkb=ActiveWorkbook.Name 'save wb for later access
url="http://a.com/b.htm" 'save to Internet
sheetname="c"
Sheets(sheetname).Range("d1:e2").Select 'activating sheet may be necessary
On Error Resume Next 'anticipate error to handle it
'Now comes the publish line the first time... which may work.. or not
ActiveWorkbook.PublishObjects.Add(xlSourceRange,url,sheetname,"d1:e2",xlHtmlStatic,"blah","moreblah").Publish (True) 'may fail to create page on website if page didn't already exist
theerr=Err.Number
On Error GoTo 0 'back to default error handling (none)
If theerr <> 0 Then 'if here perhaps because page nonexistent
'msgbox "maybe add warning here [FYI]"
Sheets("dummysheetwithlittleornodata").Copy 'will create workbook
On Error Resume Next
ActiveWorkbook.SaveAs url 'may fail to create webpage first time
ActiveWorkbook.saveAs url 'twice needed for some reason sometimes. [works for me]
On Error GoTo 0
'with fresh dummy html page created, now publish should hopefully work.. let's try again
ActiveWorkbook.Close savechanges:=False 'clean up and avoid popup
Workbooks(wkb).Activate 'get back to correct wkb
Sheets(sheetname).Range("d1:e2").Select
ActiveWorkbook.PublishObjects.Add(xlSourceRange,url,sheetname,"d1:e2",xlHtmlStatic,"blah","moreblah").Publish (True) 'hopefully if failed first time, now it succeeded ! good luck.
End If
The above code structure has allowed me to solve several publish problems I was having. All the techniques together have been enough so far to allow me to save a range from a sheet as a webpage (to server I have write access to) without having to do anything manually (like a manual save or even click on popup). Good Luck.
[The different non-obvious techniques include activating a range before trying a range publish, using the error handling approach and anticipating failure at certain points, the 2x saveas to compensate for mysterious inconsistent failures when using one saveas only, closing and getting back to original workbook cleanly, and using saveas to "guarantee" page exists in form that will make publish to succeed more consistently.]

Related

Word VBA macro errors opening files when run on some computers but not others?

This has been a thorn in my side for months. It happens off and on for a small (but growing) number of users at my company, but not others running the same exact Word VBA Macro. The users get "5097:Word encountered an issue" or "4605: Command failed" runtime errors on lines of the code that open word or txt files. All are running the same build of windows and have the same permissions (according to the tech team.) The files are on a shared network drive.
Public Function readData(datafile As String) As String
'datafile = "\\network\unc\path\folder\folder\data.txt"
'I have confirmed this passes to the function correctly.
Dim db As Document
'Before it is suggested, I've tried early-binding, late-binding, and
'diming as Variant, Object, etc. to no effect
'Errors on this line
Set db = Documents.OpenNoRepairDialog(fileName:=datafile, ConfirmConversions:=False, _
ReadOnly:=True, Visible:=False)
'I have tried Documents.Open to no avail, in addition to toggling all of the parameters to
'or removing parameters see if I would get a different result.
'I have tried declaring the Word.Application and Application objects and prepending that
'to the function. Nadda. I also tried just the function, without setting as an object. No dice.
'---rest of code---'
End Function
I've used Debug.Print to check the file path was passed to the function correctly--it is. Also this issue does not have to do with the file being a txt file. If I comment out this function in the main sub, I get the same error on the next function that attempts to open a word doc.
Again, this exact macro runs flawlessly on some workstations.
Here's the real kicker:
The users are all able to navigate to the file paths the macro is trying to open in File Explorer without problem. When I run the "Documents.Open(datafile)" or "Dir(datafile)" functions in the immediate window they work perfectly, no issue. What gives?

Problem handling errors with FileSystemWatcher

To start with it has been many years since I have done much programming, probably about 15 years, in fact VB 6 was still being taught then so I'm not up to date on anything and can get lost in what I'm reading but I am trying. I'm using Visual Studio 2019 and trying to create a VB Windows Forms App that uses the FileSystemWatcher. Unfortunately the only solutions to my problem that I could find are in C#. I tried to transfer them to my app but couldn't get them to work. Actually Visual Studio wasn't happy with what I put in and wouldn't run at all, probably due to incorrect syntax.
This part of my little app is supposed to copy a file that another program creates, while the other program is running, and place the copy in another folder. It seemed simple, detect that the file had been changed and then copy the changed file. Well I came across a few problems.
The first problem is that if I create an empty file the code works most times with no problems but occassionally 2 copies of the file are created. I could live with that but.
If I replace the empty file with a larger file, the file that it is meant to copy, then it can make 3 or 4 copies and most times produces a messagebox telling me that it can't access the file because it is in use. The main program is also minimised to display the error message, which I don't want.
Even if my app is the only program running and I replace the watched file manually with the larger file I still get the file in use error, so I am assuming that the event has been triggered twice and one of them is the process that is using the file and causing the error.
If I tell the error message to cancel then my program continues and produces only 1 copy of the file. Which is what I do want.
The second problem is that the file being copied is also accessed by another program for a short period imediately after it has been changed and this also causes an extra few copies of it to be made and sometimes a file in use error message box appears, and again the main program is minimised to display the error message. I originally had other IO.NotifyFilters in place that weren't really necassary and thought that they may have been triggering the errors so I removed them, but it made no difference.
The most common error is that the file is in use but there has also been the odd "Unhandled exception has occured in your application. could not find file.". The only reason that I can think of for this error is that the file may have been renamed to a backup file and a new version created by the main program at the exact time my program tried to access it.
From what I have read the FileSystemWatcher can trigger multiple times and I presume that this is what is causing the duplicate copies from both problems.
I need my app to make only 1 copy without the error messagebox appearing as this program needs to run in the background with no user input. Essentially this part of my app is an external program to backup a file when the file changes because the main program changes this file often but only seems to back up the file every few hours.
The following code is what I have used but nothing that I have tried has caught the error so I removed the error event handler. This code was copied from something else that was similar and in C# then modified for my purpose. I thought that the {} were used for C# not VB but it seems to work and if I take them out it won't.
My code for the FileSystemwatcher is:-
WatchFile = New System.IO.FileSystemWatcher With {
.Path = Path.GetDirectoryName(strArkMapFileNamePath),
.Filter = Path.GetFileName(strArkMapFileNamePath),
.NotifyFilter = IO.NotifyFilters.LastWrite
}
' add the handler to each event
AddHandler WatchFile.Changed, New FileSystemEventHandler(AddressOf OnLastWrite)
'Set this property to true to start watching
WatchFile.EnableRaisingEvents = True
The event handler is:-
Private Sub OnLastWrite(sender As Object, e As FileSystemEventArgs)
'Copy the Save file to a new folder and rename it.
My.Computer.FileSystem.CopyFile(
strArkMapFileNamePath,
strBackupPath & "\" & strArkMapFileName & "_" &
DateTime.Now.ToString("dd.MM.yyyy_hh.mm.ss") & ".ark",
Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs,
Microsoft.VisualBasic.FileIO.UICancelOption.DoNothing)
End Sub
I added an error event handler after AddHandler WatchFile.Changed, New FileSystemEventHandler(AddressOf OnLastWrite) but that did nothing.
I tried to add an on error statement before the end sub and that did nothing either, I presume because the Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs, caught the error first.
I got frustrated and tried to add a statement before the Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs, but it didn't like that and I didn't expect it to work.
So how do I catch the errors before the FileSystemWatcher acts on the error?
I don't know what I am doing wrong so any help would be appreciated.
Also if anybody could offer code for what I need to do can it please be code for a Windows Forms App because I don't seem to have much luck in converting C# or anything else.
Edit
I have replaced the My.Computer.FileSystem.CopyFile with the File.Copy method as suggested and added a few extra bits..
Private Sub OnLastWrite(sender As Object, e As FileSystemEventArgs)
'Verify that source file exists
If File.Exists(strArkMapFileNamePath) Then
'Copy the Save file to a new folder and rename it.
Dim i As Integer
For i = 0 To 3
Try
' Overwrite the destination file if it already exists.
File.Copy(strArkMapFileNamePath, strBackupPath & "\" & strArkMapFileName & "_" & DateTime.Now.ToString("dd.MM.yyyy_hh.mm.ss") & ".ark", True)
i = 3
' Catch exception if the file was already copied.
Catch copyError As IOException
'If copy failed reset counter
i += 1
End Try
Next
End If
End Sub
Although this shouldn't be required because this is done before the FileSystemWatcher is enabled I have added an If statement to check that the files exists before attempting to copy the file.
I'm not entirely happy with the loop but I know under normal conditions that it's not going to be an endless loop.
Is there a way to just catch the specific errors that I would need to deal with or will it be fine the way it is?
I will probably still need to work out how to stop the FileSystemWatcher from sending more than 1 event or somehow make multiple events appear as 1.
The If statement was the last thing that I added and for some reason the program now seems to only make 1 copy and appears to be faster.
The other external program that accesses the file being copied must be slower to react than my program because now it displays a message that it's waiting until my program has finished copying the file.
My program is now performing as it was intended but I believe that it is just a matter of timing rather than the correct code.

VBA error when opening shared file - reading tags is treated as attempt to modify

I have come across a strange error and am looking for some insights.
Scenario:
A powerpoint file on a shared drive is opened by user A. User B now wants to open the same file, and is presented with a "open as read only?" dialog. User clicks "OK".
The file is opened, and an add-in runs (whenever a file is opened) to check for certain tags on slides, indicating presence of confidential material. This causes an error in the following function:
Function taggedSlide(tagName As String)
' find the slide which is tagged with tagName
Dim oSl As Slide
Set oSl = Nothing
For Each oSl In ActivePresentation.Slides
If Len(oSl.Tags(tagName)) > 0 Then <<<<<<<<<<<<<<< this is the line that causes error
Set taggedSlide = oSl
Exit Function
End If
Next oSl
Set taggedSlide = Nothing
End Function
The function ostensibly loops over all slides in the presentation, looks for a tag called tagName, and returns the slide (or Nothing). It looks like this only involves "read" operations, but the code throws an error at the indicated line.
To make things more interesting, the behavior is different if I simply mark a file as "read only", save it, and open it. The difference seems to be that I can modify the file in that case - I just can't save it. But this file cannot be edited at all, even if I don't save it. And the above "read" operation is treated as a "modification"...
I have the following questions:
Is there a document property that I can read in VBA to tell me this is a "cannot modify" file? I am looking for something akin to ActivePresentation.ReadOnly, but that is set for a "read only" file, and this is different.
Why does the line If Len(oSl.Tags(tagName)) > 0 Then get treated as a "modifying file" operation?
It was difficult to reproduce this error, because I really needed to have two users open the same file (saving the file as read-only was not enough) to make it happen. Looking forward to your insights / comments / answers!

Can I call a dialog, while other dialog is opened?

I have a macro, which is called on event SelectionChange. This macro have to check, what template is attached to the document. It is possible, that attached template doesn't exist on computer that is opening the document. I need to know, when this occurs, so I can't use ActiveDocument.AttachedTemplate (it would show simply Normal.dot, when template doesn't exist). So, I use:
Application.Dialogs(wdDialogToolsTemplates).Template
And that works fine.
But, when I try to find something in document by ctrl+F, selection is changed while searching and event fires. Macro is called, but on the line above I get an error:
This method or property is not available because the find and replace dialog box is active
So, the question is - is there a way to use this property, while the find and replace dialog box is active...? Or mabe - is there a way to check, if find and replace dialog box is active?
As I suggested in comment you could try to use On Error Resume Next to get rid of the error you have. However, I made some tests and that could be interesting for you what I have found out. You could add error handling in two ways which will have different outcomes.
'1st attempt will keep Find-Replace window and it will omit error
On Error Resume Next
Debug.Print Application.Dialogs(wdDialogToolsTemplates).Template
On Error Goto 0
'2nd attempt will close Find-Replace window and will return template name
On Error Resume Next 'this seems to be unnecessary anyway
Dim tmpDialog As Dialog
Set tmpDialog = Application.Dialogs(wdDialogEditFind)
'Find-Replace window will be closed at this stage
Debug.Print Application.Dialogs(wdDialogToolsTemplates).Template
Tried and tested for Office-Word-2010.

Sparkline Printing to PDF

I have an issue where when I export to PDF via VBA my sparkline graphs are not printed. I've browsed your site, and a few others, trying to come up with a solution. Unfortunately I can't get it to work.
I'm the only one that uses the application, so the process is completely visible. I've tried to do all of the following before the export line in an effort to get the sparklines to 'refresh':
application.screenupdating = false then application.screenupdating = true
application.visible = true (based on forum here, even though it was never hidden)
select the cell where sparkline is located
select entire sheet where sparkline(s) are located
select.copy the cell where sparkline is located
application.wait to see if it would refresh
application.calculate to see if it would refresh
I really can't think of anything else to try. The spreadsheet is designed to create a report for a single entity, print the report, and then move on to create the next report for a different entity (pulls data from Access, creates over 200 10 page reports).
Any help is appreciated.
Thanks - Kris.
I had the same issue and I tried all of your listed ideas as well as DoEvents, but after testing the code line for line I found the offending code was:
.Axes.Vertical.MinScaleType = xlSparkScaleGroup
For some reason the xlSparkScaleGroup interferred with the update of the sparklines on the page and when I tried to update-print-update-print--- the sheet the sparklines were missing. My solution was to simply remove this code and then manually set the scales. Something like this:
.Axes.Vertical.MinScaleType = xlSparkScaleCustom
.Axes.Vertical.CustomMinScaleValue = Application.WorksheetFunction.Min(Range(SLAddress))
.Axes.Vertical.MaxScaleType = xlSparkScaleCustom
.Axes.Vertical.CustomMaxScaleValue = Application.WorksheetFunction.Max(Range(SLAddress))
where SLAddress was the address of the sparkline data I was using. I hope this helps solve your issue and maybe Microsoft will actually fix this issue.
Had to use a temp file to make it happen. Basically saved the current file as a temp file using 'savecopyas', then open the temp file (which allowed it to refresh the sparklines) do the print, close the temp file, and then start the process over again.
Hope they fix this at some point.
Kris.