System.Runtime.InteropServices.COMException (0x80030020): The operation failed - c#

I'm trying to save a Outlook.MailItem to a folder using:
MailItem.SaveAs(path, Outlook.OlSaveAsType.olMSG)
This works fine most of the time. However, when there's about 20 emails hitting Outlook at once, for a few of the I get the following exception:
System.Runtime.InteropServices.COMException (0x80030020): The operation failed.
at Microsoft.Office.Interop.Outlook._MailItem.SaveAs(String Path, Object Type)
I checked the code 0x80030020 which seems to mean "A share violation has occurred." However, I'm not sure what it means. In my application, I have the two below lines where the first one saves the email to a folder and the second method trying to POST that file to an API:
file = MailItemHelper.SaveMailItemToOFEFolder(mailItem);
bool isEmailSentByHttp = MailItemHelper.SendMailItemToOFEApi(_userid, file)
The path has the subject of the email which is cleaned to remove non-alpha-numeric characters by doing:
Regex nonAlphaNumericRgx = new Regex(#"\W|_");
subject = nonAlphaNumericRgx.Replace(mail.Subject, "_");

I guess I can't tell directly what is the problem, but I can try giving you some tips.
According to MS documentation, Outlook seems to be using the StgOpenStorage function, throwing the STG_E_SHAREVIOLATION error:
Access denied because another caller has the file open and locked.
That means that either Outlook is messing up (the user clicked on the "message" and outlooks locks the file, or something else) or maybe after changing the file name with your regex your string ends up to be the same of another file, while it is in the middle of writing the other.
How are you hooking up to outlook? Is your app using a new thread per mail arrived? You could add a log4net and log all operations in your code, like:
// receive and email and store subject name on var subject
log.Info($"E mail with subject '{subject}' arrived.");
// convert name and save on var newSubject
log.Info($"Mail will be saved as '{newSubject}'.");
// notify the beginning of the operation
log.Info("Calling MailItemHelper.SaveMailItemToOFEFolder.");
// notify the end of the operation
log.Info("Mail saved successfully.");
// notify Post in REST API
log.Info("Sending in MailItemHelper.SendMailItemToOFEApi");
// notify end of Post in REST API
log.Info("MailItemHelper.SendMailItemToOFEApi sent");
This will give a clear idea of how all mails are being retrieved and if there is some thread issues you can check which ones are finishing/starting first.

Related

open specific word doc. depending on user selection c#

i currently have a word doc. file merging program and am attempting to open a specific word doc. depending on user selection.
// sample of code used:
string outputFolder = null;
...
// file selection
...
string outcomeFolder = outputFolder;
string outputFile = "Combined Folder " + fileDate + " # " + fileTime + ".docx";
string outputFileName = Path.Combine(outcomeFolder, outputFile);
in the program, outputFolder is selected by the user via a fileBrowserDialog
currently, the program runs correctly and merges the files in the folder selected by the user however it fails to open Microsoft Word as well as the outcome document merged.
i've attempted to use:
Microsoft.Office.Interop.Word.Application officeApp =
new Microsoft.Office.Interop.Word.Application();
...
// merging code
...
Document documentTest = officeApp.Documents.Open(outputFileName);
i've noticed that though the program fails to launch Word, Task Manager continues to create a new instance of Word. The merged document formed also cannot be deleted as it claims the file is currently in use. It's as if program it's opening in the background however not physically launching. The Task Manager instance of Word must then be killed before the merged file can be edited / deleted
Any suggestions as to remedy this? Am i missing something simplistic or is the issue due to the non-static file path? - if any additional information is required please ask. thank you
Update 1:
Since implementing the officeApp.Visible = true; the program now launches the file created which can then be edited / re-saved etc. However if i immediately run the program again, attempting to create another merged file within the same folder etc. I am presented with "RPC server is unavailable. (Exception from HRESULT: 0x800706BA)"
Update 2:
As listed above, I was getting a generic HRESULT error code which I have now since remedied. I moved the "new officeApp" into the "Merge" handler which seems to be allowing multiple merges in quick succession without throwing errors.
Update 3:
To make things more simplistic, i've experimented with implementing Process.Start(outputFileName); to open the document. This is due to the additional check box I am now introducing which allows the user to decide whether the merged doc. will be launched / presented once created. This new code also prevents the additional Word.exe's from being created if the file visibility is set to false.
thank you all for your suggestions and help.
Have you tried making Word visible?
officeApp.Visible = true;

OpenSharedItem for opening .MSG files showing Error in Outlook C#

I am using the following code to open the signed/unsigned
Outlook messages and I display the content in WebBrowser control.
Microsoft.Office.Interop.Outlook.Application app = new Microsoft.Office.Interop.Outlook.Application();
var item = app.Session.OpenSharedItem(msgfile) as Microsoft.Office.Interop.Outlook.MailItem;
string message = item.HTMLBody;
app.Session.Logoff();
It is working fine for the first time the file is opening, but after
closing the Outlook file trying to reopen the file it showing the
following error:
"Cannot open file: C:\tion.msg. The file may not exist, you may not
have permission to open it, or it may be open in another program.
Right-click the folder that contains the file, and then click
Properties to check your permissions for the folder."
After some time later it is opening fine. For this strange behavior
what could be the reason and how to rectify the the error message?
Outlook manages its own cache of items when you are opening and closing messages. Your best bet would be to use a randomly generated filename (i.e. Path.GetRandomFilename) when opening via OpenSharedItem so that you don't get issues. I would also use a temporary path instead of root c:\ (i.e. Path.GetTempPath).
You can try and free the MailItem reference (i.e. setting it to null), but there is no guarantee when Outlook will release the item from its cache.
Would any combination of the Quit[1], Close[2] or ReleaseComObject[3] methods work for you? My code worked better but not perfect after I used them.[4]
using Outlook = Microsoft.Office.Interop.Outlook;
.
.
.
var app = new Outlook.Application();
var item = app.Session.OpenSharedItem(msgfile) as Outlook.MailItem;
//Do stuff with the mail.
item.Close(OlInspectorClose.olDiscard);
app.Quit();
Marshal.ReleaseComObject(item);
Another solution, according to Microsoft - Help and Support[5], is to delay the opening of the file. However, it doesn't sound like a good solution to me since, like #SliverNinja also said, you'll never know when Outlook releases its lock of the file.
Notes and references
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook._application.quit.aspx, read 2014-10-14, 16:19.
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook._mailitem.close%28v=office.15%29.aspx, read 2014-10-14, 16:19.
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.releasecomobject.aspx, read 2014-10-14, 16:19.
For example, if I had opened Outlook for som regular work, the Quit-method would close that window as well.
http://support2.microsoft.com/kb/2633737, read 2014-10-08, 16:19.
Hello you have two options .
set the Read-only attribute to the msg file
or
disable the following permissions for the users or usergroups to the parent folder:
Write Attributes
Write Extended Attributes
the msg file can now open multiple times but is write protect
I had this problem, in my case it was the space in the file name
import win32com.client
import os
path = 'C:/testes/mail'
files = [f for f in os.listdir(path) if '.msg' in f]
for file in files:
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
msg = outlook.OpenSharedItem(os.path.join(path, file))
att=msg.Attachments
for i in att:
i.SaveAsFile(os.path.join('C:/testes/email_download', i.FileName))
I don't know if in your case the OpenSharedItem method can help ...
Firstly, try to release the message as soon as you are done with it using Marshal.ReleaseComObject(). This may or may not help since Outlook likes to cache its last opened item.
Secondly, you are logging off from Outlook while it might still be running and visible to the user - Outlook is a singleton, so you will end up with the existing instance if it was already running. Either don't call Logoff at all, or check that there are no open inspectors and explorers (Application.Explorers.Count == 0 && Application.Inspectors.Count == 0).
Thirdly, reading HTMLBody alone won't work properly if there are embedded images - they are stored as regular attachments. You can save the message as an MHTML file (which most browsers would be happy to show) using MailItem.SaveAs(..., olMHTML).
You can also use Redemption (I am its author) for that - call RDOSession.GetMessageFromMsgFile.
If you need to release the message immediately after you are done, call Marshal.ReleaseComObject()
In case of Redemption, you can also cast RDOMail object to the IDisposable interface and call IDisposable.Dispose(). In addition to the MHTML format, Redemption can also save in the HTML format with image attachments converted into embedded images- use RDOMail.SaveAs(..., olHTMLEmbeddedImages) (olHTMLEmbeddedImages == 1033).

Modify File Properties in C# Other Than Date

I'm working in .NET 3.5, Visual Studio 2010. I'm working on an Outlook Add-In that saves some email into a folder. I've gotten it to work using the Microsoft.Office.Interop.Outlook.MailItem.SaveAs function. However, the file properties have only the current time (time when the file was exported through the Add-In) as their Date Modified/Date Created etc., and other properties such as To, From, CC, BCC are not there.
If you open a folder in Windows Explorer (I'm using Windows 7), go to the top where it says Name, Date Modified, Type, etc., you can click on More, and see other various columns that might be relevant like "Album Artist", "To", "From", etc.
C# has a really easy way to modify the timings, File.SetCreationTime(filename, DateTime object);. However, there's no .SetTo or .SetAlbumArtist or anything like that. How would I go about modifying those properties?
Update 1: through research, I found this link: Read/Write 'Extended' file properties (C#), so that might contain the answer...but I have no idea how. The accepted answer mentions running a method on a shell using a .dll. The second answer contains C# code, a commenter then asked basically what I want to know (how to modify the properties of a particular file), and the next commenter responded with "you can't set these"...so I'm still at square 1.
Update 2: I also tried the following:
foreach (Object selectedObject in explorer.Selection)
{
Outlook.MailItem email = (selectedObject as Outlook.MailItem);
//Modify the information about the email
email.To = "I filled in To";
email.SaveAs(filename, OlSaveAsType.olMSG);
}
This code successfully grabs the selected email(s) and save them under filename. However, the email.To = "I filled in To" changes the information when you open the .msg, but not the file properties.
This cannot be changed, because it actually is not any file property in the terms of the filesystem (like file creation or modification datetime).
Columns in Windows Explorer you are talking about are "virtual" and they are "only" the feature of Windows Explorer. It "understand" content of some file types and it can handle showing and sorting columns like that.
If you want to change To, From etc. you have to change the content of the file you are saving, i.e. change the To or From in the message.
To do so, if You have an Microsoft.Office.Interop.Outlook.MailItem object (which you are just saving), set desired properties on that object before you save it to file, i.e.:
MailItem mail = ...;
mail.To = "some new to";
mail.Subject = "new subject";
mail.SaveAs(fileToSave, OlSaveAsType.OlMSG);
I don't know if it also changes email stored in the Outlook, if it so, create a copy of the email before changing properties
MailItem copyOfMailToSave = (MailItem)mail.Copy();

Get the location of windows event log files

We are using EventLog to log exceptions. there is a background thread which check once the eventlog get full and programmaticaly transfers the entries into an XML file and then clear the event log.
This works fine but it seems like there is too much work getting done, I thought it would be better to simply copy the .evt file used for logging the current application and then clear the event log.
is there any way to find the location/path of the file which will work on every windows OS?
its suggested to use
Registry.LocalMachine.OpenSubKey("System\\CurrentControlSet\\Services\\EventLog\\" + e.Log);
but then my application log names dont have a File property.
How are you archiving them now? Maybe that method can be improved to gain performance.
Here's an example.
EventLogSession els = new EventLogSession();
els.ExportLogAndMessages("Security", // Log Name to archive
PathType.LogName, // Type of Log
"*", // Query selecting all events
"C:\\archivedLog.evtx", // Exported Log Path
false, // Stop archive if query is invalid
CultureInfo.CurrentCulture);
Or you can use the ClearLog() method.
EventLogSession els = new EventLogSession();
// Clears all the events and archives them to the .evtx file
els.ClearLog("System", // Channel to Clear
"c:\\myLog.evtx"); // Backup File Path
More information can be found here:
Export, Archive, and Clear Event Logs

Get email folder from MailItem via MAPI interface

I'm creating a program to automatically generate reports from incoming email attachments and it is almost complete save for one area. The incoming emails are automatically filtered into folders which differentiate which client and server they originate from. I can't figure out how to get the path of the folder from the email Item.
I'm using the NewMailEx event to call the method below and this.AppNamespace and this.ReportFolder are confirmed to be instantiated properly.
void AppClass_NewMailEx(string EntryIDCollection)
{
Outlook.MailItem Item = (Outlook.MailItem)this.AppNamespace.GetItemFromID(EntryIDCollection, this.ReportFolder.StoreID);
string FolderName = ""; //How do I get this?
}
The MSDN on MailItem is here. Am I missing something or approaching this the incorrect way?
I think there's a Parent that you can check - it should return a MAPIFolder that you can check the Name of.

Categories

Resources