Creating a new word document if not present - vba

I am trying to create a dashboard in excel using VBA. This is what i am trying to do :
Create a temporary word file in a specified directory.
Populate this temporary word document with charts and tables from the excel.
Mail the document to a person specified in the excel.
Delete the temporary word file.
So, When i started to code, I used the below statement to open a word document from a directory.
Set objDoc = objWord.Documents.Open(Cells(4, 6) + “Temp.docx”)
*Cells(4,6) has the file path
This works, if the file "Temp.docx" is already present in the directory.
What If i want to create a new file "Temp.Docx" if it is not present in the specified directory?

Give this a try:
On Error Resume Next 'allow code to continue even if there's an error
Set objDoc = objWord.Documents.Open(Cells(4, 6) + “Temp.docx”)
if err.number > 0 'check to see if there's an error
'if there was an error opening the document, create a new one instead
Set objDoc = objWord.Documents.Add(Cells(4, 6) + “Temp.docx”)
end if
on error goto 0 'reset error handling to stop on error
You'll probably want to put better error handling in for your entire code, but this little block should take care of your particular question.
You may also want to confirm that cells(4,6) ends with a \, or your path\filename won't be correct. You can do that with:
if right(cells(4,6),1) <> "\" then
cells(4,6) = Cells(4,6) & "\"
end if
Then you can execute the first block of code knowing that you have the proper delimiter in place

Related

Update an excel file by multiple users at same time without opening the file

Scenario
I have an excel file that contains data. There are multiple users accessing the file at the same time.
Problem
There will be problem if multiple users tried to input data to that excel file at the same time due to only one user is allowed to open the file at one time
Question
Is there any way whereby I can update the excel file (Eg: add a value to a cell, delete a value from a cell, find a particular cell etc) without opening it so that multiple users can update it at the same time using excel VBA?
I went to the direction of using shared files. But later found out to be excel shared files are very buggy. If use shared file, excel/macro can be very slow, intermittent crashes and sometime the whole file may get corrupted and could not be opened or repaired afterwards. Also depends on how many users use the file, the file size can grow quite big. So it is best not to use shared workbook. Totally not worth trying. Instead if need multiple users to update data simultaneously, it is better to use some database such as MSAccess, MSSql (Update MSSQL from Excel) etc with excel. For my situation since number of users are less, I didn't use any database, instead put a prompt for the user to wait until the other user close that file. Please see the codes below to check if a file is opened and if so, to prompt user. I got this code from stack overflow itself and I modified to suit my needs.
Call the module TestFileOpened as below.
Sub fileCheck()
Call TestFileOpened(plannerFilePathTemp)
If fileInUse = True Then
Application.ScreenUpdating = True
Exit Sub
End If
End Sub
Here plannerFilePathTemp is the temporary file location of your original file. Whenever an excel file opened, a temp file will be created. For example, your original file location is as below
plannerFilePath = "C:\TEMP\XXX\xxx.xlsx"
Thus your temporary file location will be
plannerFilePathTemp = "C:\TEMP\XXX\~$xxx.xlsx"
or in other words, temporary file name will be ~$xxx.xlsx
The following codes will be called upon Call TestFileOpened(plannerFilePathTemp)
Public fileInUse As Boolean
Sub TestFileOpened(fileOpenedOrNot As String)
Dim Folder As String
Dim FName As String
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(fileOpenedOrNot) Then
fileInUse = True
MsgBox "Database is opened and using by " & GetFileOwner(fileOpenedOrNot) & ". Please wait a few second and click again", vbInformation, "Database in Use"
Else
fileInUse = False
End If
End Sub
Function GetFileOwner(strFileName)
Set objWMIService = GetObject("winmgmts:")
Set objFileSecuritySettings = _
objWMIService.Get("Win32_LogicalFileSecuritySetting='" & strFileName & "'")
intRetVal = objFileSecuritySettings.GetSecurityDescriptor(objSD)
If intRetVal = 0 Then
GetFileOwner = objSD.Owner.Name
Else
GetFileOwner = "Unknown"
End If
End Function
I encountered Out of memory issues also when used shared files. So during the process, I figured out the following methods to minimize memory consumption
Some tips to clear memory

How to save Outlook mails as .msg file with categories and other details?

It is easy to save emails in Outlook VBA with MailItem.SaveAs
But I don't see any option to save additional details like i.e. the Author and Categories.
The 3rd party program MessageSave allows to save mails with Categories and Author in .msg format. In Windows Explorer the columns Author and Categories show the same information like in Outlook.
Does anybody know how to save messages using Outlook VBA including these additional information?
I bought MessageSave and it's a good program but they don't allow their save function to be used in VBA. The only workaround is to let MessageSave save messages when they "arrive" in a specific folder. If necessary I can use this function but this is just a workaround.
Here is a sample how the emails saved with MessageSave are shown in Windows Explorer:
here is a process i followed: (win7 64)
web search "windows vba set extended file property"
first hit: StackOverfow 16989882
web search: "DSOFile.OleDocumentProperties"
hit microsoft: The Dsofile.dll files lets you edit Office document properties when you do not have Office installed
https://support.microsoft.com/en-us/help/224351/the-dsofile.dll-files-lets-you-edit-office-document-properties-when-yo
that is not a typo ... it ends in "when-yo"
download: DsoFileSetup_KB224351_x86.exe
open DsoFileSetup_KB224351_x86.exe using 7-zip program (from 7-zip.org)
copy dsofile.dll from DsoFileSetup_KB224351_x86.exe (using 7-zip) into a folder desktop (named "testFiles" in this example) (this could be anywhere ... maybe windows system32 or syswow64 ... i only tried on desktop )
open command prompt window as administrator
navigate to folder that contains dsofile.dll
execute following: regsvr32 dsofile.dll
should receive success confirmation
start outlook ... vba editor ... tools ... references
and find "DSO OLE Document Properties Reader 2.1" and check the box on left
back to vba editor ... create new module
paste in the following: (this is just a minimal test script)
Sub extendedProperties()
Dim objFile As OleDocumentProperties
Set objFile = CreateObject("DSOFile.OleDocumentProperties")
objFile.Open ("C:\Users\js\Desktop\testFiles\myMessage.msg") ' adjust to match your system
objFile.SummaryProperties.Subject = "My Subject"
objFile.Save
Set objFile = Nothing
End Sub
copy (drag&drop) an email "myMessage" from outlook to folder (on desktop in this example)
right-click on folder column header ... click on more ... find "subject" ...
click checkbox
ran script
subject column should contain "My Subject" next to myMessage.msg (or whatever your message is named)
there may be a simpler way ... maybe windows PowerShell has a command that could be called from vba
here is a more usable script
it has no error checking
no check for duplicate message names
no check for illegal filenames (except for ":" character)
just select a bunch of emails in any outlook folder and run this
' make sure you have a reference to "DSO OLE Document Properties Reader"
Sub extendedProperties()
Dim msg As mailItem
Dim objFile As OleDocumentProperties
' Set objFile = CreateObject("DSOFile.OleDocumentProperties")
Set objFile = New OleDocumentProperties
Dim fileName As String
Dim subjectText As String
' !!!!!!!! select a bunch of messages before running this !!!!!!!!
For Each msg In ActiveExplorer.Selection
subjectText = Replace(msg.Subject, ":", "_") ' get rid of illegal file name character (there are others)
' adjust the destination folder for your liking
fileName = "C:\Users\js\Desktop\testFiles\" & subjectText & ".msg"
Debug.Print fileName
msg.SaveAs fileName
objFile.Open fileName
objFile.SummaryProperties.Subject = "My Subject"
'objFile.Save
objFile.Close True ' save and close !!!!! duplicate filenames get overwritten !!!!!
' stop ' uncomment this line and the code will stop. press F5 to run, F8 to single-step
Next msg
Set msg = Nothing
Set objFile = Nothing
End Sub

How to skip to next in a list if vba can't find a file

I have a code that uses a list of company names, a code then converts these to file destinations and then another code goes through each and opens them and takes values off of each.
e.g. Admiral_Group-2015-AR converts to C:\Users\Jon\Desktop\CodeBackups\Companies\Admiral_Group-2015-AR.xlsx (FSOURCE)
The issue I have is that I do not have all the files for the ones in the list yet and so the code errors when it cannot find a file. How can I make it skip to the next file in the loop instead?
This is the part of code that I have:
For startno = 1 To endno
If IsEmpty(WS_Companies.Range("A:A").Find(what:="File Name").Offset(startno, 0).Value) = False Then
FSource = WS_Companies.Range("A:A").Find(what:="File Name").Offset(startno, 1).Value
Set WB_Report = Workbooks.Open(FSource)
Thanks
Check for its existence with Dir$() before attempting to open it:
If Len(Dir$(FSource)) then
'// file exists on disk
Set WB_Report = Workbooks.Open(FSource)
...
End if

Deleting a file immediately after closing it - 'Permission Denied' error

I want to save an email as a Word file through a macro, and then replace this with a converted PDF file via Word. The conversion part is working fine - the issue is when I try to delete the original .doc file.
Dim objWrd As Object
Dim objWrdDoc As Object
Dim wrdCurrentPrinter As String
Set objWrd = CreateObject("Word.Application")
Set objWrdDoc = objWrd.Documents.Open(filePath & fileName)
''Print' file as PDF - current printer is stored so it can be reverted back afterwards
wrdCurrentPrinter = objWrd.ActivePrinter
objWrd.ActivePrinter = "Microsoft Print To PDF"
'File name is specified here to avoid Save As prompt. PrintToFile property is 'True'
objWrd.PrintOut , , , filePath & Replace(fileName, ".doc", ".pdf"), , , , , , , True
objWrd.ActivePrinter = wrdCurrentPrinter
objWrd.Quit
Set objWrd = Nothing
Kill filePath & fileName
At Kill filePath & fileName I get error 70 - Permission denied.
I am able to delete the file manually without any problems, and if I add a breakpoint and step through the 'Kill' line, it works when there is even a slight delay between Word closing and the Kill command. Therefore I suspect that the code is being processed so quickly so that the file is still open at the time of running the command.
I have a feeling I may need to go down the route of creating a delay, which I have been having trouble with in Outlook. Is this the likely solution or is there something else that I have missed?
I have been able to get this to work consistently by simply closing the Word document before quitting Word altogether:
objWrdDoc.Close
objWrd.Quit
Set objWrd = Nothing
The error has not appeared since adding this line and testing with various emails.

Use VBA to retrieve and call VBA code from a text file

I am working on a project to process some incoming Outlook messages using rule-triggered VBA code.
However, I don't want to have to manually update the code for each user's inbox any time the code needs to change. So my idea is to put a text file on a shared drive and have the VBA pull that text file down and treat it like code. Essentially, I want to use that text file like a little code library.
I was able to find this link that has gotten me very close to my goal. However, I'm having a few issues with it.
Here is the code I put together. It is attached to the click event of a Rectangle shape I inserted into an Excel file. Eventually, I'll move this over to Outlook, but I'm just doing a basic test with Excel VBA first.
Sub Rectangle1_Click()
On Error GoTo Err_Handler
Dim enviro As String
Dim myFile As String
'Pull code "library" from text file on user's desktop
'This will eventually be changed to reside on a shared drive
enviro = CStr(Environ("USERPROFILE"))
myFile = enviro & "\Desktop\hello_vba.txt"
'If the "Library" module already exists, delete it
For Each a In Modules
If a.Name = "Library" Then
a.Delete
Exit For
End If
Next
'Add a new module
Set m = Application.Modules.Add
'Rename it to "Library"
m.Name = "Library"
'Insert the text from the other file to this new module
m.InsertFile myFile
'Call the hello() subroutine from the retrieved text file
Library.Hello
Exit_Here:
'Cleanup code goes here
Exit Sub
Err_Handler:
MsgBox Err.Description
Resume Exit_Here
Exit Sub
And here is the content of the external text file named "hello_vba.txt":
Sub Hello()
MsgBox "Hello"
End Sub
The first time I run this, using the debugger I can see that it creates the new module and then gets to the line that says:
m.Name = "Library"
And then a window pops up in the debugger that says:
Can't enter break mode at this time
When I click continue on that message, I get an
Object Required
error message. If it run it again, then I get some more error messages but I do eventually get a successful "Hello" message box pop up.
I'm wondering if I may not have "Dim"ed the "a" or "m" variables properly or if there is a problem trying to pull in a text file and immediately treat it like code?
Any ideas?
To run your new code, try using Application.Run instead of Library.Hello
It would be written:
Application.Run("Hello")
Does that work?