PowerPoint 2016 Presentation.Close does NOT remove entry from Application.Presentations - vba

Settings
PowerPoint 2016
.NET com-addin (CP 4) with
WindowActivate DISPID(2009)
for test purposes changed to just "do nothing but the necessities"
Windows 10
Create an empty file in a folder using windows explorer (no slides
inside said file).
Problem description
Using integrated VBA i ran this code three times
Option Explicit
Sub test()
Dim objPresentation As PowerPoint.Presentation
Set objPresentation = Application.Presentations.Open("c:\test\folder\file.pptx")
objPresentation.Close
Set objPresentation = Nothing
Debug.Print Presentations.Count
For Each objPresentation In Presentations
Debug.Print objPresentation.FullName, vbTab, objPresentation.Windows.Count
Next
End Sub
Please find result below
2
C:\Users\admin\Desktop\kh_tests.pptm 1
c:\test\folder\file.pptx 0
3
C:\Users\admin\Desktop\kh_tests.pptm 1
c:\test\folder\file.pptx 0
c:\test\folder\file.pptx 0
4
C:\Users\admin\Desktop\kh_tests.pptm 1
c:\test\folder\file.pptx 0
c:\test\folder\file.pptx 0
c:\test\folder\file.pptx 0
It confuses not only my addin (is a presentation open or not), it seems not to be intentional since one could expect ppt would at least reuse the entry it left behind last time, but the entry seems to be corrupt.
I've encountered this behavior on several systems with PPT2016.
PPT2010 does not show this behavior.
Thank you for reading
KH
Explanation/Hypothesis
I think i came closer to the reason (sorry for the time it took but you know how it is some times); i came to the believe that "someone" is still pointing to that particular presentation so it might not close; so i decided to have another go for it.
I narrowed it down by selective disabling everything in my com-addin, but to no result: even doing nothing (just "return") the behavior remains the same; but if i did not connect my connection-point (WindowActivate, DISPID 2009) in the first place it suddenly worked and all went well. According to my logfile the event has always been processed after the close, not interfering with anything, but who knows. I tried to force things with DoEvents, but to no avail.
Hypothesis:
The presence of WindowActivate event sinks will keep PowerPoint2016 from discarding the presentation WHICH HAS BEEN ACTIVE at that time, even if closing this presentation is the reason for the event in the first place.
Test:
Unfortunately i found no easy way to have an vba/inapp test. Any ideas?
But with my fully fledged com addin present (okay, still much deactivated, by sink connected), i tested this:
Option Explicit
Dim objCloak As PowerPoint.Presentation
Sub test()
Dim objPresentation As PowerPoint.Presentation
Set objPresentation = Application.Presentations.Open("C:\test\folder\file.pptx")
If objCloak Is Nothing Then
Set objCloak = Presentations.Add ' change to the intermediate presentation
End If
DoEvents ' relic from previous tests
objCloak.Windows(1).Activate ' ' be paranoid
DoEvents ' relic from previous tests
objPresentation.Close
DoEvents ' relic from previous tests
If (Not (Nothing Is objCloak)) Then
objCloak.Saved = True
objCloak.Close
End If
Set objCloak = Nothing
For Each objPresentation In Presentations
Debug.Print objPresentation.FullName, vbTab, objPresentation.Windows.Count
Next
End Sub
And the result is
C:\Users\admin\Desktop\kh_tests.pptm 1
C:\Users\admin\Desktop\kh_tests.pptm 1
C:\Users\admin\Desktop\kh_tests.pptm 1
Switch back and force between the two variants yields the results as described above. So i think there's something here.
Note: the PowerPoint Task now closes without any problems.

Related

Unused function in Excel addin causes crash only on second run and when run via VBScript

Sorry for the long title.
I have several .xlsm files which share a lot of code, so I moved the repeated parts to an addin .xlam file. I have been using a .vbs script to open all the files one after another and run a macro in each.
Problem
The problem I'm facing is that on the second run of the .vbs script, excel crashes and gives what seems to be a very generic error, said here to be an "Automation Error":
Script: C:\Users\~\Desktop\test\test.vbs
Line: 5
Char: 1
Error: The server threw an exception.
Code: 80010105
Source: (null)
To my surprise, I was able to reproduce this crash even after removing 99% of the content of my files.
test.vbs:
Dim xlApp
Dim xlBook
Set xlApp = CreateObject("Excel.Application")
Set xlBook = xlApp.Workbooks.Open("C:\Users\~\Desktop\test\test.xlsm")
xlApp.Run "Auto.Run" '<~~ error on this line
xlBook.Save
xlBook.Close (True)
xlApp.Quit
Set xlBook = Nothing
Set xlApp = Nothing
test.xlsm:
test.xlam has a module Module1, test.xlsm has a Module Auto and a Reference to test.xlam
test.xlsm, Auto:
Sub Run()
MsgBox "hello"
Test.Load
MsgBox "goodbye"
End Sub
test.xlam, Module1
Sub Load()
MsgBox "Load"
End Sub
Function Other()
End Function
With the function Other() commented out, the code works fine (saying hello, load and goodbye). It also works fine if the macro is run from within excel. Only when Other() is present, and Run() is run through the .vbs file is there an error (right after hello).
Workaround
If I open test.xlsm, save it, and close it again in between each run of test.vbs, there are no problems. I believe this has something to do with the addin, rather than the spreadsheet, because in my original script, which opened multiple excel files, only one file needs to be opened and saved.
I also noticed that the excel file is a little bigger in its "problem" state, and that once I open and save it, it returns to its slightly smaller original size. (EDIT: This is at least partly caused by new cache streams __SRP_4 and __SRP_5 inside the vbaProject.bin file, which I extracted using this answer (oh, and this). After manually deleting all SRP entries, I was able to run the .vbs script again without problems, although just like the open-save-close strategy, it's only temporary, and will then crash on the third run rather than the second.)
Question
Are addins not appropriate for shared code? May they not contain functions? Is there any way to work around this crash besides what I'm doing right now?
Any thoughts are appreciated.
It sounds to me like the first instance isn't being unloaded/released before the second instance is being called. Perhaps using the Application.Wait Method to wait a few seconds before each subsequent run in performed might help?
'Open file1
'Run macro from file1
'Close file1
Application.Wait(Now + TimeValue("0:00:10")) 'wait 10 seconds
'Open file1
'Run macro from file1
...
...
So on
To install your add-in to excel via vbscript you can use the following code
'Launch Excel
set objExcel = createobject("Excel.Application")
strAddIn = "ESP Assistant.xlam"
'~~> Path where the XLAM resides
SourcePath = "Your source path\" & strAddIn
'Add the AddIn
On Error Resume Next
With objExcel
'Add Workbook
.Workbooks.Add
'Show Excel
objExcel.Visible = True
.AddIns.Add(SourcePath, False).Installed = True
End With
If this fails you might have to clear your registry values first, then rerun the above script
'File to use just in case Add-In installation fails
'Refreshes Excel Registry Entries to allow for clean install of Add-In
Dim objFSO, objShell
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objShell = WScript.CreateObject ("WScript.shell")
objShell.Run "cmd /c ""C:\Program Files (x86)\Microsoft Office\Office14\excel.exe"" /unregserver && timeout /t 3 && tskill excel && ""C:\Program Files (x86)\Microsoft Office\Office14\excel.exe"" /regserver",1,True
Set objFSO = Nothing
Set objShell = Nothing
x=msgbox("Excel registry refreshed." ,0, "Registry Update")
wscript.quit
Unfortunately, I still don't know why this is happening, but I found an automated solution that I'm going to stick with.
As I mentioned in my question, the test.xlsm file was a little bigger in its "problem" state, due at least partially to some kind of cache, of which I could only find one offical mention here:
2.2.6 SRP Streams
Streams that specify an implementation-specific and version-dependent performance cache. MUST be
ignored on read. MUST NOT be present on write.
The name of each of these streams is specified by the following ABNF grammar:
SRPStreamName = "__SRP_" 1*25DIGIT
My solution was to remove the cache, which I did manually at first with this tool. When that seemed to work, I wrote a Java program to do it automatically (gist here). It's glue between java.util.zip and Apache POIFS.
I also added a line to call the Java at the end of the .vbs script:
CreateObject("WScript.Shell").Run "java -jar clear-excel-cache.jar C:\Users\~\Desktop\test\test.xlsm", 1, false
In my actual .vbs file, which calls multiple excel files in a loop, this line is just inside the loop. There is a little cmd window that opens after each file is run but it no longer crashes on the second run, so I'm calling that a success.
Your issue could be the same issue which I am trying to resolve - Random 64-bit Excel 2013 VBA crashes (VBE7.dll errors). You can check the Application Event logs for a VBE7.dll crash to confirm this.
In my case various XLSM files become intermittently corrupted through manual use.
My fix as an alternative to yours is the following VBS (anything to trigger a VBA "recompile").
Resave "myfile.xlsm"
Sub Resave(filename)
Set objExcel = CreateObject("Excel.Application")
currentDirectory = left(WScript.ScriptFullName,(Len(WScript.ScriptFullName))-(len(WScript.ScriptName)))
objExcel.Application.AutomationSecurity = 3 ' Disable to avoid crash
objExcel.Application.enableevents = False
objExcel.Application.Workbooks.open(currentDirectory + "\" + filename)
objExcel.Application.Visible = True
objExcel.Application.DisplayAlerts = False
Set objSheet = objExcel.ActiveWorkbook.Sheets.Add
objSheet.Delete
objExcel.Application.DisplayAlerts = True
objExcel.Application.enableevents = True
objExcel.ActiveWorkbook.Save
objExcel.ActiveWorkbook.Close
objExcel.Application.Quit
Set objExcel = Nothing
End Sub
FYI - Microsoft released a patch which fixes the issue in Excel 2013 on 3rd May 2016.
https://support.microsoft.com/en-us/kb/3085486

Outlook VBA - Bulk Move of Email Between Subfolders

I routinely have to move a decent amount of email (150+) from a subfolder to another. There are many folders in the mailbox that I perform this task on. It seems like it would be an easy macro to write, but what I have is substantially slower than doing a Ctrl+A, drag to destination folder.
I have reviewed previous questions about moving Outlook emails and Microsoft's documentation, but I am unable to figure out how to accomplish moving the emails in a a fast and reliable manner. I would appreciate any information on where I am going wrong and if there is another solution besides VBA.
My current code is below. My end goal would be to loop through a list of folder names (instead of me selecting the folder).
Thanks in advance.
Sub MoveEmailsToDone()
On Error Resume Next
Dim ns As Outlook.NameSpace
Dim AnalystFolder As Outlook.MAPIFolder
Dim MoveToFolder As Outlook.MAPIFolder
Set ns = Application.GetNamespace("MAPI")
Set AnalystFolder = Application.ActiveExplorer.CurrentFolder
Set MoveToFolder = ns.Folders("username#domain.com").Folders(AnalystFolder.Name & "-DONE")
For i = AnalystFolder.Items.Count To 1 Step -1
AnalystFolder.Items(i).Move MoveToFolder
Next i
Set ns = Nothing
Set AnalystFolder = Nothing
Set MoveToFolder = Nothing
End Sub
From experience Move and Delete are slow.
http://computer-programming-forum.com/1-vba/17216b85e9c096d3.htm
07 Jul 2003
The following code loops through each mail item in a specified folder
and moves the item to another folder. For 1100 items, it takes more
than 5 min. It doesn't move that slow when I select all and move in
the user interface.
.
Outlook uses Extended MAPI to implement a move operation, namely
IMAPIFolder::CopyMessages() which takes a list of entryids, hence it does not
need to open each message. Store provider completes the whole operation on the
server without sending lots of data back and forth as apparently happens when
you run your code.
Dmitry Streblechenko
https://stackoverflow.com/users/332059/dmitry-streblechenko
DoEvents lets you use Outlook while the code runs.
For i = AnalystFolder.Items.Count To 1 Step -1
DoEvents
AnalystFolder.Items(i).Move MoveToFolder
Next i
MsgBox "MoveEmailsToDone is finally done."

Reading, Writing and controlling Autocad using external VBA

I'm using MS-Access 2010 and Autocad 2012 64bit and work in manufacturing.
I want to be able to at the very least, populate fields in a title block, better still I would like to use data in my access database to write data into a sheet set (the current system works by reading the sheet set values such as sheet title and number into my title block).
The following code is all I have at the moment and it will open autocad and write the date into the command line.
Private Sub OpenAutocad_Click()
Dim CadApp As AcadApplication
Dim CadDoc As AutoCAD.AcadDocument
On Error Resume Next
Set CadApp = GetObject(, "AutoCAD.Application")
If Err.Number <> 0 Then
Set CadApp = CreateObject("AutoCAD.Application")
End If
On Error GoTo 0
CadApp.Visible = True
CadApp.WindowState = acMax
Set CadDoc = CadApp.ActiveDocument
CadDoc.Utility.Prompt "Hello from Access, the time is: " & TheTime
Set CadApp = Nothing
End Sub
I have no idea where to go from here. What are the commands to control the sheet set manager and change data, and can the .dst file be edited without even opening up autocad? is there a list of all available autocad vba commands and functions?
If you are declaring CadApp as AcadApplication you must have added a reference to AutoCAD.
That means you should be able to see the object model using your Object Browser in your VBA IDE. No?
There is also a very helpful site www.theswamp.org which has a whole section devoted to AutoCAD VBA.
If I understand your question correctly, you want to automate filling attributes in a drawing title blocks (such as title, drawer, part number, etc) right from MS Access.
Your code can access the Autocad command line already, but Autocad doesn't seem to have the exact command for filling drawing attribute. (command list)
So looks like you need to fill the attributes programatically using the COM API.
The following question appears to be relevant with yours and the accepted answers does provide a sample code:
Is it possible to edit block attributes in AutoCAD using Autodesk.AutoCAD.Interop?
Note that in that question the asker was developing a standalone application in C# .NET, where as you will be using VB Automation from MS Access. Shouldn't be too different since the Component Object Model (COM) being used is the same.
What are the commands to control the sheet set manager and change data and can the .dst file be edited without even opening up autocad?
(sorry can't post more than 2 links)
docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/files/WS1a9193826455f5ffa23ce210c4a30acaf-7470.htm
No mention about data change, though.
is there a list of all available autocad vba commands and functions?
Yes.
%ProgramFiles%\Common Files\Autodesk Shared\acad_aag.chm - Developer's Guide
%ProgramFiles%\Common Files\Autodesk Shared\acadauto.chm - Reference Guide
Online version:
help.autodesk.com/cloudhelp/2015/ENU/AutoCAD-ActiveX/files/GUID-36BF58F3-537D-4B59-BEFE-2D0FEF5A4443.htm
help.autodesk.com/cloudhelp/2015/ENU/AutoCAD-ActiveX/files/GUID-5D302758-ED3F-4062-A254-FB57BAB01C44.htm
More references here:
http://usa.autodesk.com/adsk/servlet/index?id=1911627&siteID=123112
:) Half the way gone ;)
If you has a open autocad with a loaded drawing you can access the whole thing directly.
Sub block_set_attribute(blo As AcadBlockReference, tagname, tagvalue)
Dim ATTLIST As Variant
If blo Is Nothing Then Exit Sub
If blo.hasattributes Then
tagname = Trim(UCase(tagname))
ATTLIST = blo.GetAttributes
For i = LBound(ATTLIST) To UBound(ATTLIST)
If UCase(ATTLIST(i).TAGSTRING) = tagname Or UCase(Trim(ATTLIST(i).TAGSTRING)) = tagname & "_001" Then
'On Error Resume Next
ATTLIST(i).textString = "" & tagvalue
Exit Sub
End If
Next
End If
End Sub
Sub findtitleblock(TITLEBLOCKNAME As String, attributename As String,
attributevalue As String)
Dim entity As AcadEntity
Dim block As acadblcck
Dim blockref As AcadBlockReference
For Each block In ThisDrawing.BLOCKS
For Each entity In block
If InStr(LCase(entity.objectname), "blockref") > 0 Then
Set blockref = entity
If blockref.effectivename = TITLEBLOCKNAME Then
Call block_set_attribute(blockref, attributename, attributevalue)
exit for
End If
End If
End If
Next
Next
End Sub
call findtitleblock("HEADER","TITLE","Bridge column AXIS A_A")
So assume you has a title block which has the attribute TITLE then it will set the Attribute to the drawing name. it mioght also possible you has to replace the thisdrawing. with your Caddoc. I usually control Access and Excel form autocad and not vice versa ;)
consider also to use "REGEN" and "ATTSYNC" if "nothing happens"
thisdrawing.sendcommens("_attsync" 6 vblf )

VBA list of filepaths of linked objects in document

I have a number of large Microsoft Word documents with many linked files from many Microsoft Excel spreadsheets. When opening a Word document, even with the 'update linked files at open' option unchecked:
Word still checks each link at its source by opening and closing the relevant excel spreadsheet for each individual link (so for x number of links, even if from the same spreadsheet, Word will open and close the spreadsheet x times). This means opening documents takes a very long time.
I have found that documents open faster if the spreadsheets containing the source of linked objects are already open, so Word doesn't keep opening, closing, reopening them.
So far, the beginnings of a solution I have is to create a list of all the filepaths of the linked objects, done by following VBA code:
Sub TypeArray()
Dim List(), Path As String
Dim i, x As Integer
Dim s As InlineShape
Dim fso As FileSystemObject, ts As TextStream
Set fso = New FileSystemObject
Set ts = fso.OpenTextFile("C:\MyFolder\List.txt", 8, True)
With ts
.WriteLine (ActiveDocument.InlineShapes.Count)
End With
For Each s In ActiveDocument.InlineShapes
Path = s.LinkFormat.SourcePath & "\" _
& s.LinkFormat.SourceName
With ts
.WriteLine (Path)
End With
Next s
End Sub
'--------------------------------------------------------------------------------------
Private Sub WriteStringToFile(pFileName As String, pString As String)
Dim intFileNum As Integer
intFileNum = FreeFile
Open pFileName For Append As intFileNum
Print #intFileNum, pString
Close intFileNum
End Sub
'--------------------------------------------------------------------------------------
Private Sub SendFileToNotePad(pFileName As String)
Dim lngReturn As Long
lngReturn = Shell("NOTEPAD.EXE " & pFileName, vbNormalFocus)
End Sub
which works well, but can only be used after a document is already open, which defeats its purpose.
So, finally, my question(s) are these:
1) Is there a way to run this code (or any better, more efficient code - suggestions are welcome) before opening a Word document and waiting through the long process of checking each link at its source?
2) Is there a way to avoid all this and simply have Word not check the links when it I open a document?
Sorry for the long question, and thank you for the help!
If I am not wrong there should be Document_Open event according to msdn. This should actually be a before open document and should be fired before updating links (at least it in excel it is fired before calculation).
Try opening the files on document open. Then you will face another problem, and so when to close the files, but that is a much easier thing to do. (probably document_close event...)
EDITTED:
As comments state, this is too late. You can create a word opener (as a single app or as an addin). The logic basically is:
'1) on something_open run GetOpenFileName dialog
'2) before opening the real thing, open all files accompanied
'3) open the document itself
'4) close all files
'5) close the opener itself
This is not the most trivial way, but I use this logic for exampe to make sure, that my applications always runs in a fresh copy of excel etc. But I understand that this is a workaround rather then a solution.
If you are still looking for something on this front, I created the following in a combination of VBA and VB.NET (in VS 2010) to show what can be done quite easily using that system. If VB.NET is no use to you, sorry, but there are reasons why I don't really want to spend time on the pure VBA approach.
At present, it is a "console" application which means you'll probably see a box flash up when it runs, but also means that you are more likely to be able to create this app without VS if you absolutely had to (AFAICR the VB.NET /compiler/ is actually free). It just fetches the link info. (i.e. there's currently no facility to modify links).
The overview is that you have a small piece of VBA (say, in your Normal template) and you need an open document. The VBA starts a Windows Shell, runs the VB.NET program and passes it the full path name of the document you want to open.
The VB.NET program opens the .docx (or whatever) and looks at all the Relationships of type "oleObject" that are referenced from the Main document part (so right now, the code ignores headers, footers, footnotes, endnotes and anywhere else you might have a link)
The VB.NET program automates Word (which we know is running) and writes each link URL into a sequence of Document Variables in the active document. These variables are called "Link1", "Link2", etc. If there are no links (I haven't actually tested that path properly) or the program can't find the file, "Link0" should be set to "0". Otherwise it should be set to the link count.
The shell executes synchronously, so your VBA resumes when it's done. Then you either have 0 links, or a set of links that you can process.
The VBA is like this:
Sub getLinkInfo()
' the full path name of the program, quoted if there are any spaces in it
' You would need to modify this
Const theProgram As String = """C:\VBNET\getmaindocumentolelinks.exe"""
' You will need a VBA reference to the "Windows Script Host Object Model"
Dim oShell As WshShell
Set oShell = CreateObject("WScript.Shell")
' plug your document name in here (again, notice the double quotes)
If oShell.Run(theProgram & " ""c:\a\testdocexplorer.docx""", , True) = 0 Then
With ActiveDocument.Variables
For i = 1 To CInt(.Item("Link0").Value)
Debug.Print .Item("Link" & CStr(i))
Next
End With
Else
MsgBox "Attempt to retrieve links failed"
End If
End Sub
For the VB.NET, you would need the Office Open XML SDK (I think it's version 2.5). You need to make references to that, and Microsoft.Office.Interop.Word.
The code is as follows:
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.IO
Imports System.Xml
Imports System.Xml.Linq
Imports DocumentFormat.OpenXml.Packaging
Imports Word = Microsoft.Office.Interop.Word
Module Module1
Const OLEOBJECT As String = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject"
Sub Main()
Dim s() As String = System.Environment.GetCommandLineArgs()
If UBound(s) > 0 Then
Dim wordApp As Word.Application
Try
wordApp = GetObject(, "Word.Application")
Dim targetDoc As Word.Document = wordApp.ActiveDocument
Try
Dim OOXMLDoc As WordprocessingDocument = WordprocessingDocument.Open(path:=s(1), isEditable:=False)
Dim linkUris As IEnumerable(Of System.Uri) = From rel In OOXMLDoc.MainDocumentPart.ExternalRelationships _
Where rel.RelationshipType = OLEOBJECT _
Select rel.Uri
For link As Integer = 0 To linkUris.Count - 1
targetDoc.Variables("Link" & CStr(link + 1)).Value = linkUris(link).ToString
Next
targetDoc.Variables("Link0").Value = CStr(linkUris.Count)
OOXMLDoc.Close()
Catch ex As Exception
targetDoc.Variables("Link0").Value = "0"
End Try
Finally
wordApp = Nothing
End Try
End If
End Sub
End Module
I originally wrote the .NET code as a COM object, which would be slightly easier to use from VBA, but significantly harder to set up on the .NET side and (frankly) much harder to modify & debug as you have constantly to close Word to release the references to the COM DLLs.
If you actually wanted to fix up the LINK paths, as far as I can tell, modifying them in the relationship records is enough to get Word to update the relevant LINK fields when it opens Word, which saves having to modify the XML code for the LINK fields as well. But that's another story...
I just found out that you can set/modify a DelayOleSrvParseDisplayName registry entry and a NoActivateOleLinkObjAtOpen registry entry to modify the global behaviour:
See http://support.microsoft.com/kb/970154
I also found that activedocument.fields can contain links to external objects (in my case, an Excel sheet).
Use this code to parse them:
for each f in activedocument.fields
debug.print f.code
next
And use activedocument.fields(FIELDNUMBER) to select each object, to figure out where it is in the document.
Maybe also activedocument.Variables and activedocument.Hyperlinks can contain links to external objects? (not in my case).

How do I make Outlook purge a folder automatically when anything arrives in it?

I hope it's okay to ask this kind of question. Attempting to write the code myself is completely beyond me at the moment.
I need a macro for Outlook 2007 that will permanently delete all content of the Sent Items folder whenever anything arrives in it. Is it possible? How do I set everything up so that the user doesn't ever have to click anything to run it?
I know I'm asking for a fish, and I'm embarrassed, but I really need the thing...
edit:
I've pasted this into the VBA editor, into a new module:
Public Sub EmptySentEmailFolder()
Dim outApp As Outlook.Application
Dim sentFolder As Outlook.MAPIFolder
Dim item As Object
Dim entryID As String
Set outApp = CreateObject("outlook.application")
Set sentFolder = outApp.GetNamespace("MAPI").GetDefaultFolder(olFolderSentMail)
For i = sentFolder.Items.Count To 1 Step -1
sentFolder.Items(i).Delete '' Delete from mail folder
Next
Set item = Nothing
Set sentFolder = Nothing
Set outApp = Nothing
End Sub
It's just a slightly modified version of a piece of code I found somewhere on this site deleting Deleted Items. It does delete the Sent Items folder when I run it. Could you please help me modify it in such a way that it deletes Sent Items whenever anything appears in the folder, and in such a way that the user doesn't have to click anything to run it? I need it to be a completely automated process.
edit 2: Please if you think there's a better tool to achieve this than VBA, don't hesitate to edit the tags and comment.
edit 3: I did something that works sometimes, but sometimes it doesn't. And it's ridiculously complicated. I set a rule that ccs every sent email with an attachment to me. Another rule runs the following code, when an email from me arrives.
Sub Del(item As Outlook.MailItem)
Call EmptySentEmailFolder
End Sub
The thing has three behaviors, and I haven't been able to determine what triggers which behavior. Sometimes the thing does purge the Sent Items folder. Sometimes it does nothing. Sometimes the second rule gives the "operation failed" error message.
The idea of acting whenever something comes from my address is non-optimal for reasons that I'll omit for the sake of brevity. I tried to replace it with reports. I made a rule that sends a delivery report whenever I send an email. Then another rule runs the code upon receipt of the report. However, this has just one behavior: it never does anything.
Both ideas are so complicated that anything could go wrong really, and I'm having trouble debugging them. Both are non-optimal solutions too.
Would this be an acceptable solution? Sorry its late but my copy of Outlook was broken.
When you enter the Outlook VB Editor, the Project Explorer will be on the left. Click Ctrl+R if it isn't. It will look something like this:
+ Project1 (VbaProject.OTM)
or
- Project1 (VbaProject.OTM)
+ Microsoft Office Outlook Objects
+ Forms
+ Modules
"Forms" will be missing if you do not have any user forms. It is possible "Modules" is expanded. Click +s as necessary to get "Microsoft Office Outlook Objects" expanded:
- Project1 (VbaProject.OTM)
- Microsoft Office Outlook Objects
ThisOutlookSession
+ Forms
+ Modules
Click ThisOutlookSession. The module area will turn white unless you have already used this code area. This area is like a module but have additional privileges. Copy this code to that area:
Private Sub Application_MAPILogonComplete()
' This event routine is called automatically when a user has completed log in.
Dim sentFolder As Outlook.MAPIFolder
Dim entryID As String
Dim i As Long
Set sentFolder = CreateObject("Outlook.Application"). _
GetNamespace("MAPI").GetDefaultFolder(olFolderSentMail)
For i = sentFolder.Items.Count To 1 Step -1
sentFolder.Items(i).Delete ' Move to Deleted Items
Next
Set sentFolder = Nothing
End Sub
I have taken your code, tidied it up a little and placed it within an event routine. An event routine is automatically called when the appropriate event occurs. This routine is called when the user has completed their log in. This is not what you requested but it might be an acceptable compromise.
Suggestion 2
I have not tried an ItemAdd event routine on the Sent Items folder before although I have used it with the Inbox. According to my limited testing, deleting the sent item does not interfere with the sending.
This code belongs in "ThisOutlookSession".
Option Explicit
Public WithEvents MyNewItems As Outlook.Items
Private Sub Application_MAPILogonComplete()
Dim NS As NameSpace
Set NS = CreateObject("Outlook.Application").GetNamespace("MAPI")
With NS
Set MyNewItems = NS.GetDefaultFolder(olFolderSentMail).Items
End With
End Sub
Private Sub myNewItems_ItemAdd(ByVal Item As Object)
Debug.Print "--------------------"
Debug.Print "Item added to Sent folder"
Debug.Print "Subject: " & Item.Subject
Item.Delete ' Move to Deleted Items
Debug.Print "Moved to Deleted Items"
End Sub
The Debug.Print statements show you have limited access to the sent item. If you try to access more sensitive properties, you will trigger a warning to the user that a macro is assessing emails.