The following VBA code gets stuck at the While loop:
Sub SaveAsText2(MyMail As MailItem)
' Export email (with PowerShell script in body) as a text file
MyMail.SaveAs "c:\scripts\outlook.ps1", olTXT
' Create a response email
Dim reMail As Outlook.MailItem
Set reMail = MyMail.Reply
' wait till transcript is available
Dim MyFSO As FileSystemObject
Set MyFSO = New FileSystemObject
If MyFSO.FileExists("C:\Scripts\email_transcript.txt") Then
' This bit works correctly
' MsgBox "The file Exists"
Else
' This bit works correctly as well
' MsgBox "The file Does Not Exist"
End If
' This part fails to evaluate regardless if the file is there or not
While Not MyFSO.FileExists("C:\Scripts\email_transcript.txt")
' WScript.Sleep 1000
Application.Wait (Now + TimeValue("0:00:01"))
MsgBox "The file Does Not Exist"
Wend
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("c:\scripts\testfile.txt", True)
a.WriteLine ("This is a test.")
a.Close
' attach the transcript and send it back
reMail.Attachments.Add "C:\Scripts\email_transcript.txt"
reMail.Send
MyFSO.DeleteFile ("C:\Scripts\email_transcript.txt")
End Sub
If the email_transcript.txt file exists, then the While loop gets skipped (which is correct) and the rest of the script runs. No issues here.
If the email_transcript.txt file does NOT exist, then the While loop will wait until the file exists. However, even when the file exists at this point, the While loop never validates and therefore it doesn't process the rest of the script.
The MsgBox in the While loop doesn't trigger when the file does NOT exist.
The MsgBox call stops any code execution until it is closed:
' This part fails to evaluate regardless if the file is there or not
While Not MyFSO.FileExists("C:\Scripts\email_transcript.txt")
' WScript.Sleep 1000
Application.Wait (Now + TimeValue("0:00:01"))
MsgBox "The file Does Not Exist"
Wend
Try to replace it with a Debug.Print statements, so the loop could continue:
' This part fails to evaluate regardless if the file is there or not
While Not MyFSO.FileExists("C:\Scripts\email_transcript.txt")
' WScript.Sleep 1000
Application.Wait (Now + TimeValue("0:00:01"))
Debug.Print "The file Does Not Exist"
Wend
The While/Wend structure has a logic fail: if at the moment of the first evaluation the expected file yet don't exists, the MsgBox alert will be fired, even if in the next second the file became properly saved.
You can change this as follows:
lngTimer = Timer
Do
DoEvents
Application.Wait (Now + TimeValue("0:00:01"))
If Timer > lngTimer + 10 Then Exit Do
Loop Until MyFSO.FileExists("C:\Scripts\email_transcript.txt") = True
Using a Do/Loop structure with a 'scape valve' of a Timer comparison will ensure a correct check for the file's existence, avoiding an eternal loop. Adapt the timeout parameter for the file to be saved (10 in the example).
Fixed the issue. It's to do with Application.Wait, which doesn't work in Outlook. Solution is here:
Wait for 5-10 seconds then run Outlook code
Sub SaveAsText2(MyMail As MailItem)
' Export email (with PowerShell script in body) as a text file
MyMail.SaveAs "c:\scripts\outlook.ps1", olTXT
' Create a response email
Dim reMail As Outlook.MailItem
Set reMail = MyMail.Reply
' wait till transcript is available
Dim MyFSO As FileSystemObject
Set MyFSO = New FileSystemObject
While Not MyFSO.FileExists("C:\Scripts\email_transcript.txt")
Sleep 1
Wend
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("c:\scripts\testfile.txt", True)
a.WriteLine ("This is a test.")
a.Close
' attach the transcript and send it back
reMail.Attachments.Add "C:\Scripts\email_transcript.txt"
reMail.Send
MyFSO.DeleteFile ("C:\Scripts\email_transcript.txt")
End Sub
Public Sub Sleep(ByVal SleepSeconds As Single)
Dim Tmr As Single
Tmr = Timer
Do While Tmr + SleepSeconds > Timer
DoEvents
Loop
End Sub
Related
I've tried to use the below code which I found on this conversation How To Search And Replace Across Multiple Files In Word? supplied by Charles Kenyon. However, it doesn't seem to work for me. I've enabled macros on my word and added the below code as a new module in Macros. When I go to replace all, it'll replace the text as per normal, but after doing this, when I open up the other macros enabled word doc, I find that the same text is still in these docs, without being replaced. Am I doing something wrong? Namely, I also wish to add a wildcard entry into my replace all, will the below code work or can someone suggest a better alternative? I have tested the below code with and without wildcard entries to no avail. I've also tried the code on this page in my macros but it also didn't work How to find and replace a text in multiple Word documents using VBAThanks for any help!
Option Explicit
Public Sub BatchReplaceAll()
Dim FirstLoop As Boolean
Dim myFile As String
Dim PathToUse As String
Dim myDoc As Document
Dim Response As Long
PathToUse = "C:\Test\"
'Error handler to handle error generated whenever
'the FindReplace dialog is closed
On Error Resume Next
'Close all open documents before beginning
Documents.Close SaveChanges:=wdPromptToSaveChanges
'Boolean expression to test whether first loop
'This is used so that the FindReplace dialog will
'only be displayed for the first document
FirstLoop = True
'Set the directory and type of file to batch process
myFile = Dir$(PathToUse & "*.doc")
While myFile <> ""
'Open document
Set myDoc = Documents.Open(PathToUse & myFile)
If FirstLoop Then
'Display dialog on first loop only
Dialogs(wdDialogEditReplace).Show
FirstLoop = False
Response = MsgBox("Do you want to process " & _
"the rest of the files in this folder", vbYesNo)
If Response = vbNo Then Exit Sub
Else
'On subsequent loops (files), a ReplaceAll is
'executed with the original settings and without
'displaying the dialog box again
With Dialogs(wdDialogEditReplace)
.ReplaceAll = 1
.Execute
End With
End If
'Close the modified document after saving changes
myDoc.Close SaveChanges:=wdSaveChanges
'Next file in folder
myFile = Dir$()
Wend
End Sub
I am limited to using VBA to accomplish this. The goal is to programmatically Open a new IE window which is a duplicate of a window already open.
I need to display this window for limited amount of time (in this example I am waiting 15 seconds), then I want to close one of the two IE windows I have open.
I have cobbled together code fragments from a few examples I have found and this is partially working, but the results are not as I would expect.
First I am able to find the IE instances but even though I think I have coded an exit, both windows are closed.
The MsgBox I am using for debugging never appears.
With each run of the code the error message below appears
Below is the code I am trying to get to work, but failing with.
Private Sub OpenReport()
Dim IE
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
IE.Navigate "http://~~~~~~~~~.net/reports/views/result/reportResult.faces"
' Wait for a period of time contained in TimeValue
Application.Wait (Now + TimeValue("00:00:15"))
' Now close ONE of the IE windows (Currently closing all of them)
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Process")
' Find IE Instances
For Each objItem In colItems
If objItem.Name = "iexplore.exe" Then
On Error Resume Next
objItem.Terminate ' Terminates all instead of exiting after finding one IE window
MsgBox objItem.Name & " " & objItem.ProcessID & " " & objItem.CommandLine 'Doesn't appear
Exit For
End If
Next
End Sub
I appreciate the input but had to go a slightly different route to get this working as it should...
The key to getting one instance of IE to close was solved by using TaskKill (commandline WScript).
Below is the full solution
Private Sub OpenReport()
Dim IE
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
IE.Navigate "http://~~~~~~~~~~~net/reports/views/result/reportResult.faces"
' Wait for a period of time contained in TimeValue
Application.Wait (Now + TimeValue("00:00:15"))
' Now close ONE of the IE windows (Currently closing all of them)
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Process")
' Find an IE instance
For Each objitem In colItems
If objitem.Name = "iexplore.exe" Then
On Error Resume Next
Shell ("TaskKill /PID " & objitem.ProcessID)
Exit For
End If
Next
End Sub
Is it possible to set a macro that would trigger each time I open a word document and check if it has an editing restriction. If so, try password from a list of passwords (hardcoded). In case one password is successfull, keep it in memory, remove restriction, and re-apply the restriction when I close the document.
In this way, if I always use the same password for the documents I use and restrict, I could open them on my computer as if there was no restriction, but the restriction would still apply to other users.
Note: the macro in Private Sub Document_Open() would need to trigger on all documents I open from my computer only. Documents must be .docx and not .docm.
Thank you.
Note 1: You will need to put this into a .dotm file and ultimately save as a global template on your PC (google).
Note 2: This will fail if you open more than 1 doc because only 1 password is stored - you could write the password as a document property (which you would retrieve & delete before saving and relocking).
Depending on whether or not you are happy to add code to the Normal.dotm template (personally I'm not) will influence how you do this.
If NOT using Normal.dotm then you will need to setup a global template AND trigger the code by creating your own application events as described here: https://wordmvp.com/FAQs/MacrosVBA/PseudoAutoMacros.htm
If using Normal.dotm then in ThisDocument add:
Private Sub Document_Open()
MsgBox ActiveDocument.Name
Dim oDoc As Object
Set oDoc = ActiveDocument
unlocker oDoc
End Sub
And (for testing) in a regular module add the following (you'll likely want to split this into separate units of code later):
Sub unlocker(ByVal docToUnlock As Document)
If Not docToUnlock.Type = wdTypeDocument Then
' this is a template, don't try anything
MsgBox "Not a doc"
GoTo endOfSub
Else
MsgBox "Is a doc"
End If
Dim passWords() As String
passWords = Split("pw1,pw2,pw3", ",")
Dim iLoop As Long
iLoop = LBound(passWords)
On Error GoTo err_Test:
Do While Not ActiveDocument.ProtectionType = wdNoProtection
If iLoop > UBound(passWords) Then Exit Do
oldpassword = passWords(iLoop)
ActiveDocument.Unprotect oldpassword
iLoop = iLoop + 1
Loop
If Not ActiveDocument.ProtectionType = wdNoProtection Then
' unable to unlock document, quit
oldpassword = vbNullString
MsgBox "Failed to Unlock"
GoTo endOfSub
Else
MsgBox "Unlocked"
End If
' Do Stuff
If Not oldpassword = vbNullString Then
ActiveDocument.Protect wdAllowOnlyReading, Password:=oldpassword
End If
endOfSub:
Exit Sub
err_Test:
If Err.Number = 5485 Then
' ignore error due to wrong password
Err.Clear
Resume Next
Else
' handle unexpected error
End If
End Sub
I am checking if a file is open or not that is a .txt file
Private Sub CommandButton1_Click()
Dim strFileName As String
' Full path and name of file.
strFileName = "D:\te.txt"
' Call function to test file lock.
If Not FileLocked(strFileName) Then
' If the function returns False, open the document.
MsgBox "not open"
Else
MsgBox "open"
End If
End Sub
Function FileLocked(strFileName As String) As Boolean
On Error Resume Next
' If the file is already opened by another process,
' and the specified type of access is not allowed,
' the Open operation fails and an error occurs.
Open strFileName For Binary Access Read Write Lock Read Write As #1
Close #1
' If an error occurs, the document is currently open.
If Err.Number <> 0 Then
' Display the error number and description.
MsgBox "Error #" & Str(Err.Number) & " - " & Err.Description
FileLocked = True
Err.Clear
End If
End Function
It turns out .txt when opened using notepad doesn't lock the file, so it can not be known if a .txt file is open or not. And hence, if that .txt file is opened in Wordpad or Sakura, etc., your code should work or at least other code from the net should work.
I found that if a text file is opened using FileSystemObject, then the file is not locked and can still be edited by other users. As a potential workaround, you could make a file with a single bit to indicate when the other file is in use, and include checking that bit in your code. Here's my code as a rough example:
'FSO parameters
Const ForAppending = 8
Const ForReading = 1
Const ForWriting = 2
Sub WriteToFile()
Set fso = CreateObject("Scripting.FileSystemObject")
'Check the current lock bit (1 is locked, 0 is unlocked)
Set FileLock = fso.OpenTextFile("C:\FileLock.txt", ForReading)
Dim LockBit As Integer
LockBit = FileLock.ReadAll
FileLock.Close
'If the bit is 1 (file in use) then wait 1 second and try again (up to 10 times)
For try = 1 To 10
If LockBit = 1 Then
Application.Wait (Now + TimeValue("0:00:1"))
Set FileLock = fso.OpenTextFile("C:\FileLock.txt", ForReading)
LockBit = FileLock.ReadAll
FileLock.Close
Else: GoTo Line1 'when the bit is 0 (file available)
End If
If try = 10 Then
MsgBox "File not available"
Exit Sub
End If
Next try
Line1:
Call LockTheFile(fso, True) 'Change the lock bit to "1" to show the file's in use
Set WriteFile = fso.OpenTextFile("C:\WriteFile.txt", ForWriting)
'Do what you will with the file
MsgBox "Write Successful"
WriteFile.Close
Call LockTheFile(fso, False) 'Change the lock bit to "0" to show the file's available
End Sub
I made this sub separate to make the main code more streamlined
Sub LockTheFile(fso, SetLock As Boolean)
'Write "1" to a lock file to indicate the text file is in use, or "0" to indicate not in use
Set BitFile = fso.CreateTextFile("C:\FileLock.txt", True)
If SetLock = True Then
BitFile.WriteLine "1"
Else
BitFile.WriteLine "0"
End If
BitFile.Close
End Sub
To start off I have a function in VBA Excel that tells me if a Word Document is open or not open. For testing purposes I don't have the Word Document Open and expect 'False' to be returned.
Function IsFileOpen(filename As String) As Boolean
Dim filenum As Integer, errnum As Integer
On Error Resume Next ' Turn error checking off.
filenum = FreeFile() ' Get a free file number.
' Attempt to open the file and lock it.
Open filename For Input Lock Read As #filenum
Close filenum ' Close the file.
errnum = Err ' Save the error number that occurred.
On Error GoTo 0 ' Turn error checking back on.
' Check to see which error occurred.
Select Case errnum
' No error occurred.
' File is NOT already open by another user.
Case 0
IsFileOpen = False
' Error number for "Permission Denied."
' File is already opened by another user.
Case 70
IsFileOpen = True
' Another error occurred.
Case Else
Error errnum
End Select
End Function
Called Via:
...
If IsFileOpen("C:/Temp/test.docx") = True Then
MsgBox objWord.ActiveDocument.Name & " already open" 'ERROR FROM PIC HERE
Set objDoc = objWord.ActiveDocument
Else
Set objDoc = objWord.Documents.Open("C:/Temp/test.docx", Visible:=True)
End If
...
However when running the code I get that the Document is open (Returned True from the IsFileOpen function from case 70) yet I get an error on 'objWord.ActiveDocument.Name' that no Document is Open
On Windows 7 Task Manager this is what I have. Word Application is closed but it appears there are background processes open of Word. However I close all documents I don't use so these processes shouldn't be running
I think a better test to check to see if your Word File is open is to use the actual Word Documents collection
So for your example, use something like this:
With objWord
For Each doc In .Documents
If doc.Name = "test.docx" Then
found = True
Exit For
End If
Next doc
If found <> True Then
.Documents.Open FileName:="C:/Temp/test.docx"
Else
.Documents("test.docx").Activate
End If
End With