I'm currently working at a Uni project using Excel VBA and I'm trying to find a way to create an excel that will self destruct if it doesn't find the "Key". And in the event it's entirely impossible, then at least ensure there's none of the data or VBA code left.
The idea is that, using
Private Sub Workbook_Open()
Dim direct As String, name As String
name = ActiveWorkbook.Name
ChDir ThisWorkbook.Path
direct = ThisWorkbook.Path
Call Checker(direct, name)
End Sub
Upon opening the excel, it will look for "Key.txt" which should be within the same folder as the excel and check if the one string of text inside the document matches with "Code" which is a "Public Const" inside the project.
I've been trying to find a way to read the "Key.txt" without opening it, but haven't turned up anything.
On the other hand, I've been testing out various ways of making the program delete the original excel file, varying levels of success. So a somewhat roundabout way I've been testing out is to have the excel SaveAs a ".xlsx" and erase the original file before closing itself, but it doesn't work out as I've hoped for.
This is the code I've used for the "Self-Destruct" method:
Option Explicit
Function Checker(MyPath As String, name As String)
On Error Resume Next
Application.DisplayAlerts = False
Dim myPath2 As String
Dim ws As Worksheet
Dim FSO As Object
myPath2 = MyPath
ActiveWorkbook.SaveAs Filename:=MyPath & "\0_0.xlsx", FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
Sheets.Add After:=Sheets(Sheets.Count)
ActiveSheet.Name = "1"
For Each ws In Worksheets 'Deletes all other sheets
If ws.Name <> "1" Then ws.Delete
Next
Set FSO = CreateObject("scripting.filesystemobject")
If Right(MyPath, 1) = "\" Then MyPath = Left(MyPath, Len(MyPath) - 1)
If FSO.FolderExists(MyPath) = False Then MsgBox MyPath & " doesn't exist"
FSO.deletefile MyPath & "\" & name, True 'Deletes original file
ActiveWorkbook.Save
ActiveWorkbook.Close
End Function
Having a self-destructing file is an interesting idea. The short answer is that you cannot delete a running VBA macro. Therefore, no VBA macro can essentially self destruct. Yet, there are two options I can think of:
(1) Remove everything but the running macro. The following code might be helpful to achieve that.
http://www.erlandsendata.no/english/index.php?d=envbavbedeleteallmacros
(2) Close the file and ask Windows to delete the file afterwards for you using the task planner. The following post on SO might be able to help with that.
Using Excel vba Macro to be run through Windows Schedule Task
Other tempting solutions might be to save the file as .xlsx or to create a new Excel file, copy the above code from option 1 into that file, call the newly created code, which essentially deletes the original file.
But the real questions I'd ask myself would be: How did this person got the self-destructing file? Was it by email and the file is still in the email? Has the file been duplicated along the way? Are there hidden copies in some Temp-Folders (remember Excel Auto-Safe every xx minutes in case of computer crash to preserve your work)? Is the person enabling macros when opening the file (to allow self-destruction)? Can the person resore the file with the many free programs out there to recover files from a Windows machine (extremely easy as Windows does not delete the file or writes cryptical data on top to ensure it cannot be recovered, but merely "marks" that space on the HD where the file was as free to use for the next file to come)?
Related
I have a huge Excel spreadsheet that I need to allow access to a large set of users so they can manipulate it for their customers, but I don't want them to be able to overwrite the original file (a variable easily set in Excel) or save their file outside the current folder - so I want to force them in a "saveas" mode, and force the file to be saved in that folder. Otherwise, they won't be able to save. I'm not much of a VBA person, and I've found a lot of examples that may work, but nothing seems to be exactly what I need or maybe I'm not smart enough to figure it out. I found this code, but I'm not sure it FORCES the issue. Help?
I've tried to manage this in GPOs but everything seems to give them access to download the folder and save in other places.
Sub ExampleToSaveWorkbookSet()
Dim wkb As Workbook
'Adding New Workbook
Set wkb = Workbooks.Add
'Saving the Workbook
wkb.SaveAs "C:\WorkbookName.xls"
'OR
'wkb.SaveAs Filename:="C:\WorkbookName1.xls"
End Sub
Expected output is the amended Excel file saved in the original directory with a different name, or not at all.
Here's a macro that runs on open and immediately saves as .xlsx to a user location you can specify. Unfortunately the original needs to be .xlsm to store a macro.
This macro is to be located in the "ThisWorkbook" object. It will exit before making a copy when you open the workbook.
Private Sub Workbook_Open()
Dim wb As Workbook
Set wb = ActiveWorkbook
vWbName = wb.Name
vUserProf = Environ("USERPROFILE")
vx = InStr(1, vUserProf, "Users\")
If "<Use your own profileID>" = Mid(vUserProf, vx + 6) Then Exit Sub
vDir = vUserProf & "\Downloads\"
vWbName = Left(vWbName, Len(vWbName) - 5) & ".xlsx"
wb.SaveAs vDir & vWbName, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
MsgBox "You are now using a copy of the original"
End Sub
I am new to coding (learning on my own and as I go) and have a small problem I just don't get. I have a code that has been working for a while and now seems to not work. I created a Service work order sheet to help keep accurate tabs on jobs for our service department. The code is pretty simple, they user enters all information in the cells they need. When finished they can press a button that updates a separate file. Press another and it saves a copy of the active work sheet to a specified networked location, prints copies, saves the work sheet according to a specified cell, then clears specified cells and updates the PO# cell.
The code has worked well until it stopped saving the file copies to the directory indicated and I don't know why it changed.
Sub SaveWorkOrderByAddress()
ChDir "\\BG-PC\SharedDocs\Service work orders\Service work orders"
Dim NewFN As Variant
Dim stFileName As String
'Save a new copy
NewFN = Range("C2").Value & ".xlsx"
stFileName = Range("C2").Value & ".xlsx"
If Dir(stFileName) <> "" Then
MsgBox "File already exists, please rename."
Else
ActiveSheet.Copy
ActiveWorkbook.SaveAs NewFN, FileFormat:=xlOpenXMLWorkbook
ActiveWorkbook.Close
PrintCurrentWO
NextWorkOrderNumber
ThisWorkbook.Save
End If
Any help would be most appreciated and again I am new so I am sorry if it's a newb mistake.
Changing the working directory doesn't help here. Just combine the path with the filename when calling ActiveWorkbook.SaveAs.
Like this:
NewFN = "\\BG-PC\SharedDocs\Service work orders\Service work orders\" & Range("C2").Value & ".xlsx"
Using the following code to copy some data on a master sheet, then add a new workbook, then paste the data. I then need to prompt using the msoFileDialogSaveAs because I need the user to be able to select different file types each time.
The problem is that when the box comes up to save the file, I can type a name then hit save, but it doesn't actually write the file.
Public Sub ArchiveSheet()
Dim NewBook As Workbook
Dim CopyRange As Range
Set CopyRange = ActiveSheet.UsedRange
Dim lngCount As Long
CopyRange.Cells.Copy
Set NewBook = Workbooks.Add
Range("A1").PasteSpecial Paste:=xlPasteValues
With NewBook
.Title = "Archive"
End With
With Application.FileDialog(msoFileDialogSaveAs)
.Show
End With
Application.CutCopyMode = False
End Sub
You are currently asking to user to indicate where he/she wants to save the file. But you are not using it. You need to save the return string like so:
dim strFileSelected as String
strFileSelected = Application.GetSaveAsFilename(FileFilter:="Excel Files (*.xls), *.xls", Title:="Save Excel file...")
Afterwards you can check if the user actually gave you a path and file name to save it to:
If strFileSelected = "False" Then
MsgBox "You have cancelled"
else
MsgBox "Saving file here:" & chr(10) & strFileSelected
ThisWorkbook.SaveAs Filename:=strFileSelected, FileFormat:=xlWorkbookNormal
End If
Note that you cannot save all Excel files using any kind of file extension. Example: if you have an open XML format Excel file open and try to save it using the .xls extension then you'll probably get an error message and you'll loose any kind of VBA code attached to the file (if you ignore the error message).
In short: you might want to elaborate on the above solution in order to make sure that the file format matches the selected extension using Debug.Print ThisWorkbook.FileFormat.
I know this is old but I had this issue today, have not seen this answer anywhere...
And the solution was very simply to add the .Execute line, as below.
Now the user hits Save (or Enter) and the selection executes.
With Application.FileDialog(msoFileDialogSaveAs)
.Show
.Execute
End With
My first post here, sorry about my obvious lack of skills. I am not an advanced user.
Thanks.
I'm trying to save a macro-enabled Excel workbook as a csv file, overwriting the old one (below I had to change the name of the folder and the Sheet, but that doesn't seem to be the issue).
Sub SaveWorksheetsAsCsv()
Dim SaveToDirectory As String
Dim CurrentWorkbook As String
Dim CurrentFormat As Long
CurrentWorkbook = ThisWorkbook.FullName
CurrentFormat = ThisWorkbook.FileFormat
SaveToDirectory = "\MyFolder\"
Application.DisplayAlerts = False
Application.AlertBeforeOverwriting = False
Sheets("My_Sheet").Copy
ActiveWorkbook.SaveAs Filename:=SaveToDirectory & "My_Sheet" & ".csv", FileFormat:=xlCSV
ActiveWorkbook.Close SaveChanges:=False
ThisWorkbook.Activate
ThisWorkbook.SaveAs Filename:=CurrentWorkbook, FileFormat:=CurrentFormat
Application.DisplayAlerts = True
Application.AlertBeforeOverwriting = True
End Sub
Sometimes it fails with
Runtime Error 1004: method saveas of object _workbook failed**)
The debugger points out:
ActiveWorkbook.SaveAs Filename:=SaveToDirectory & "My_Sheet" & ".csv", FileFormat:=xlCSV
I googled and some of the solutions I tried were:
Specifiying that the directory is a string
Avoid any special character in the file name or folder (seen here)
Copy paste the worksheet as value before saving it as .csv (seen here)
Specifying the FileFormat with the .csv code number (seen here)
Disabling/Re-enabling some of the alerts
Adding other fields in the ActiveWorkbook.SaveAs row, regarding passwords, creating backups etcetc
Still, it might run correctly up to 50-60 times in a row, and then at some point fail again.
Any suggestion, except stop using VBA/Excel for this task, which will happen soon, but I can't for now.
EDIT: Solved thanks to Degustaf suggestion. I made only two changes to Degustaf's suggested code:
ThisWorkbook.Sheets instead of CurrentWorkbook.Sheets
FileFormat:=6 instead of FileFormat:=xlCSV (apparently is more robust
to different versions of Excel)
Sub SaveWorksheetsAsCsv()
Dim SaveToDirectory As String
Dim CurrentWorkbook As String
Dim CurrentFormat As Long
Dim TempWB As Workbook
Set TempWB = Workbooks.Add
CurrentWorkbook = ThisWorkbook.FullName
CurrentFormat = ThisWorkbook.FileFormat
SaveToDirectory = "\\MyFolder\"
Application.DisplayAlerts = False
Application.AlertBeforeOverwriting = False
ThisWorkbook.Sheets("My_Sheet").Copy Before:=TempWB.Sheets(1)
ThisWorkbook.Sheets("My_Sheet").SaveAs Filename:=SaveToDirectory & "My_Sheet" & ".csv", FileFormat:=6
TempWB.Close SaveChanges:=False
ThisWorkbook.SaveAs Filename:=CurrentWorkbook, FileFormat:=CurrentFormat
ActiveWorkbook.Close SaveChanges:=False
Application.DisplayAlerts = True
Application.AlertBeforeOverwriting = True
End Sub
I generally find that ActiveWorkbook is the problem in these cases. By that I mean that somehow you don't have that workbook (or any other) selected, and Excel doesn't know what to do. Unfortunately, since copy doesn't return anything (the copied worksheet would be nice), this is a standard way of approaching this problem.
So, we can approach this as how can we copy this sheet to a new workbook, and get a reference to that workbook. What we can do is create the new workbook, and then copy the sheet:
Dim wkbk as Workbook
Set Wkbk = Workbooks.Add
CurrentWorkbook.Sheets("My_Sheet").Copy Before:=Wkbk.Sheets(1)
Wkbk.SaveAs Filename:=SaveToDirectory & "My_Sheet" & ".csv", FileFormat:=xlCSV
Wkbk.Close SaveChanges:=False
Or, there is an even better approach in a situation like this: WorkSheet supports the SaveAs method. No copy necessary.
CurrentWorkbook.Sheets("My_Sheet").SaveAs Filename:=SaveToDirectory & "My_Sheet" & ".csv", FileFormat:=xlCSV
I will warn you to resave the workbook to its original name afterwards, if it is staying open, but you already have that in your code.
This is a year old, but I'll add something for future readers
You won’t find a lot of documentation in Excel help for Run-time error 1004 as Microsoft doesn't consider it to be an Excel error.
The answers above are 100% valid but sometimes it helps to know what is causing the problem so you can avoid it, fix it earlier or fix it more easily.
The fact that this is an intermittent fault, and it is fixed by saving with the full path and file name tells me that either your macro may be trying to save an .xlsb file to the autorecover directory after an auto file recovery.
Alternatively, you may have edited the file's path or filename yourself.
You can check the path and filename with:-
MsgBox ThisWorkbook.FullName
You should see something like this in the message box.
C:\Users\Mike\AppData\Roaming\Microsoft\Excel\DIARY(version 1).xlxb
If so the solution is (as stated above by others) to save your file to its correct path and file name. This can be done with VBA or manually.
I am now in the habit of manually saving the file with its correct path and filename as a matter of course after any autorecover action as it takes seconds and I find it quicker (if this is not a daily occurrence). Thus, the macros will not encounter this fault you run it. Remember that while my habit of manually saving .xlxb files to .xlsm files immediately after a recovery won't help a novice that you give the worksheet to.
A note on Hyperlinks
After this error: If you have hyperlinks in your worksheet created with Ctrl+k in all likelihood, you will have something like "AppData\Roaming\Microsoft\", "\AppData\Roaming\", "../../AppData/Roaming/"or "....\My documents\My documents\" in multiple hyperlinks after file recovery. You can avoid these by attaching your hyperlinks to a text box or generating them with the HYPERLINK function.
Identifying and Repairing them is a little more complicated
First, examine the hyperlinks and determine the erroneous strings and the correct string for each error. Over time, I have found several.
Excel doesn't provide a facility in the 'Go To Special' menu to search for hyperlinks created with Ctrl+k.
You can automate the identification of erroneous hyperlinks in a helper column, say column Z and using the formula
=OR(ISNUMBER(SEARCH("Roaming", Link2Text($C2),1)),ISNUMBER(SEARCH("Roaming", Link2Text($D2),1)))
where Link2Text is the UDF
Function Link2Text(rng As Range) As String
' DO NOT deactivate.
' Locates hyperlinks containing 'roaming' in column Z.
' Identify affected hyperlinks
If rng(1).Hyperlinks.Count Then
Link2Text = rng.Hyperlinks(1).Address
End If
End Function
My VBA to correct the errors is as follows
Sub Replace_roaming()
' Select the correct sheet
Sheets("DIARY").Select
Dim hl As Hyperlink
For Each hl In ActiveSheet.Hyperlinks
hl.Address = Replace(hl.Address, "AppData\Roaming\Microsoft\", "")
Next
For Each hl In ActiveSheet.Hyperlinks
hl.Address = Replace(hl.Address, "AppData\Roaming\", "")
Next
For Each hl In ActiveSheet.Hyperlinks
hl.Address = Replace(hl.Address, "../../AppData/Roaming/", "..\..\My documents\")
Next
For Each hl In ActiveSheet.Hyperlinks
hl.Address = Replace(hl.Address, "..\..\My documents\My documents\", "..\..\My documents\")
Next
Application.Run "Recalc_BT"
' Move down one active row to get off the heading
ActiveCell.Offset(1, 0).Select
' Check active row location
If ActiveCell.Row = 1 Then
ActiveCell.Offset(1, 0).Select
End If
' Recalc active row
ActiveCell.EntireRow.Calculate
' Notify
MsgBox "Replace roaming is now complete."
End Sub
I also recommend you get in the habit of doing regular backups and not relying on autorecover alone. If it fails, you have nothing since your last full backup.
While the worksheet is being fragile backup often, like every hour or after any significant import of new data.
The following shortcuts will backup your worksheet in seconds: Ctrl+O, [highlight the filename], Ctrl+C, Ctrl+V, [ X ]. Regular backups allow you to go immediately to your most recent backup without having to restore from last night's backup file especially if you have to make a request of another person to do this.
It's been a while since the last answer here, but I want to share my experience from today:
After weeks of reliable operation, I ran into the same error all of a sudden without having anything changed in the code section where the workbook is saved.
Thanks to the previous answers I updated my saveas statement from a simple
wb.saveas strfilename
to
wb.saveas Filename:=strfilename, Fileformat:= xlWorkbookDefault
et voilà: it worked again.
Sometimes the Microsoft applications behave really strange...
Try combining the Path and the CSV file name into a string variable and drop the .csv; that is handled by the FileFormat. Path must be absolute starting with a drive letter or Server Name:
Dim strFullFileName as String
strFullFileName = "C:\My Folder\My_Sheet"
If on a Server then it would look something like this:
strFullFileName = "\\ServerName\ShareName\My Folder\My_Sheet"
Substiture ServerName with your Server name and substitute ShareName with the your network Share name e.g. \\data101\Accounting\My Folder\My_Sheet
ActiveWorkbook.SaveAs Filename:=strFullFileName,FileFormat:=xlCSVMSDOS, CreateBackup:=False
I had a similar issue however for me the problem was I was creating the Filename based on strings extracted from a workbook and sometimes these strings would have characters that can't be in a filename.
Removing these characters did the trick for me!
For me there was an issue with not all formulas being calculated, despite having it on "Automatic". I pressed calculate on the bottom left 100 times and then it magically worked.
I am wanting to write a macro which will copy columns and rows from different excel documents and put them into one document but on different pages.
I have a folder on my c drive which containts several different excel documents, each document has different page, the only page I want to copy (duplicate) is a page called 'users'. What I want to do is copy everything from this one page (in the different docuemnts) and put it into another excel document, each having there own page and each page to be called with what the original document on the c drive was named.
Hope I have explained this clearly enough, is a macro able to do what I want it do to. At the moment I just copy and paste everything across from this 'users' page into the one document ( it is very time consuming).
The bad news is, based on your comments above, you have a moderate amount of work ahead of you before you will be able to write this program. The good news is the program itself will be relatively simple, and the knowledge you obtain in the process will prove extremely useful (and if you enjoy learning it should be fun, too).
A basic overview of what you'll want your program to do to accomplish your task is:
Tell your program where the source files are stored (presumably they are all stored in a directory together; this will make it a lot easier)
Open each document one at a time
Copy and paste the sheet you are interested in (this can be done in one statement)
Close the document
Repeat 2-4
When finished, exit the procedure
The best way to do this is to split up the whole thing into pieces. The first/top piece will look something like this:
Sub CopyAllTheUserWorksheets()
'Get the folder/directory location with your source files:
Dim MyDirectory As String
MyDirectory = SelectFolder
'SelectFolder is another function you write separately that will open a
'file dialog box and return the path of the folder you choose. If you
'cancel the file dialog, it will just return a "\"
'Now test to make sure a directory was chosen:
If MyDirectory = "\" Then Exit Sub
'Create the file name you will loop over to open all the Excel files:
Dim MyFile As String
MyFile = Dir(MyDirectory & "*.xlsx")
'Change .xlsx to .xls if your source files are the old Excel file format
'Open each file one at a time, perform the actions you want to perform,
'then close the file. You accomplish this using what is called a Loop.
'There are a handful of different kinds of Loops. For this task I suggest a
'Do While loop. Before the loop, we make a Workbook variable in which to
'store the opened workbook so we can work with it inside of the loop.
Dim wb As Workbook
Do While MyFile <> ""
'Open the Excel workbook:
Set wb = Workbooks.Open(Filename:=MyDirectory & MyFile)
'Here you put the task you want to do with the workbook.
'We'll put that in a separate procedure as well to keep things tidy.
Call CopyTheUserWorksheetToThisWorkbook(wb)
'Now that we are done copying the worksheet, close the Excel workbook
'without saving any changes
wb.Close SaveChanges:=False
'Proceed to the next file name. If there are no more files that match
'*.xlsx, then myFile will be equal to an empty string, which is "", and
'the Do While loop with end at that point.
myFile = Dir
Loop
End Sub
See if you can make sense of this. I'll add some more later (for the SelectFolder function and the CopyTheUserWorksheetToThisWorkbook procedure).
Note that this link was very helpful in formulating this answer, and might help you understand what is going on a bit better.
EDIT: Here is the SelectFolder function called by the procedure above:
Function SelectFolder() As String
'Make a string variable to hold the folder path
Dim MyFolder As String
'Make a FileDialog object for choosing the folder
Dim MyFolderChooser As FileDialog
'Make the FileDialog object
Set MyFolderChooser = Application.FileDialog(msoFileDialogFolderPicker)
'Give it a title (optional)
MyFolderChooser.Title = "Select A Target Folder"
'Disable MultiSelect (you only want one folder selected)
MyFolderChooser.AllowMultiSelect = False
'This is a normal dialog box, so it could be Cancelled, or Closed. We
'need to tell the procedure what to do if that happens. The statement
'below basically says "If the dialog is cancelled,skip everything until
'NextStep." Without this, you would get an error if you canceled or closed
'the file dialog.
If MyFolderChooser.Show <> -1 Then GoTo NextStep
'Assign the MyFolder variable the value given to the FileDialog object
MyFolder = MyFolderChooser.SelectedItems(1)
'The (1) means the first value. Since we turned MultiSelect off, there
'will be no (2), (3), etc, but VBA isn't smart enough to figure this
'out on its own so you have to tell it.
NextStep:
'Now just set the name of the function (SelectFolder) equal to the result
'you want, and that value will be returned to the procedure that called
'the function.
SelectFolder = MyFolder & "\"
'If no directory was chosen, the function will return just "\"
End Function