I have to create a program which saves Excel attachments from the mail inbox.
At the moment I am saving all attachments from incoming mails via an event handler, but it seems like that the event is not always triggered but rather 3 from 4 mails only. I don't know the reason though.
So I was thinking about looping through the inbox mails, look for mails with specific subject title and save the attached Excel files.
But how can I do that? Other solutions shows only via add in, but I want to use a Windows service for that.
So far my code (this doesn't work every time though, maybe someone knows a reason for that?)
public partial class MyService : ServiceBase
{
public string AttachPath = #"[mypath to save attachments]";
public MyService()
{
InitializeComponent();
}
public void RunAsConsole(string[] args)
{
Console.WriteLine("This service is executed as a console application.");
Console.WriteLine("Application active.");
OnStart(args);
Console.WriteLine("Press q to exit.");
string userInput = Console.ReadLine();
while (userInput != "q")
{
userInput = Console.ReadLine();
}
Console.WriteLine("Finished! \nPress any key to exit...");
Console.ReadLine();
OnStop();
}
protected override void OnStart(string[] args)
{
Outlook.NameSpace outlookNameSpace;
Outlook.MAPIFolder inbox;
Outlook.Items items;
Outlook.Application oApp = new Outlook.Application();
outlookNameSpace = oApp.GetNamespace("MAPI");
inbox = outlookNameSpace.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
items = inbox.Items;
items.ItemAdd +=
new Outlook.ItemsEvents_ItemAddEventHandler(items_ItemAdd);
}
void items_ItemAdd(object Item)
{
string filter = "[myFilter]";
Outlook.MailItem mail = (Outlook.MailItem)Item;
if (Item != null)
{
if (mail.Subject.ToUpper().Contains(filter.ToUpper()))
{
Console.WriteLine(DateTime.Now.ToShortTimeString() + " Mail found!: " + mail.Subject);
if (mail.Attachments.Count > 0)
{
for (int i = 1; i - 1 < mail.Attachments.Count; ++i)
{
Console.WriteLine($#"Saving {mail.Attachments[i].FileName}");
//Console.WriteLine(Path.Combine(AttachPath, mail.Attachments[i].FileName));
string filepath = Path.Combine(AttachPath, mail.Attachments[i].FileName);
mail.Attachments[i].SaveAsFile(filepath);
//if (File.Exists(filepath))
//{
// mail.Delete(); //after saving the file delete the mail
//}
}
}
else
{
Console.WriteLine("No attachments found: execute auto reply...");
Outlook.MailItem replyMail = mail.Reply();
replyMail.HTMLBody = $#"Some answer for reply";
replyMail.Send();
}
Console.WriteLine("Delete mail: " + mail.Subject.ToString());
mail.UnRead = false; //mark as read
mail.Delete();
}
}
}
protected override void OnStop()
{
//nothing
}
}
At the moment, the service can be executed as a console application and a Windows service, so please don't pay too much attention at that point, it's for debugging reasons.
Other Solutions shows only via add in, but I want to use a windows service for that.
Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.
If you are building a solution that runs in a server-side context, you should try to use components that have been made safe for unattended execution. Or, you should try to find alternatives that allow at least part of the code to run client-side. If you use an Office application from a server-side solution, the application will lack many of the necessary capabilities to run successfully. Additionally, you will be taking risks with the stability of your overall solution. Read more about that in the Considerations for server-side Automation of Office article.
As a workaround, you may consider using a low-level API on which Outlook is based on - Extended MAPI or just any wrappers around that API such as Redemption.
If you deal with Exchange only, you may consider using Graph API or EWS, see Start using web services in Exchange for more information.
Please refer to Eugene's answer on the architecture side of things. But from the perspective of extracting the emails with a filter, you can try this code.
Instead of getting items = inbox.Items, try finding the emails with a filter query which returns an Outlook.Table
Then you can iterate on this table to get the emails.
const string PropTag = "http://schemas.microsoft.com/mapi/proptag/";
var filter = "#SQL=" + "\"" + PropTag
+ "0x0037001E" + "\"" + " ci_phrasematch " + "\'" + strFilter + "\'";
Outlook.Table table = inbox.GetTable(filter, Outlook.OlTableContents.olUserItems);
while (!table.EndOfTable)
{
Outlook.Row nextRow = table.GetNextRow();
try
{
Outlook.MailItem mi;
try
{
string entryId = nextRow["EntryID"];
var item = outlookNameSpace.GetItemFromID(entryId);
mi = (Outlook.MailItem)item;
}
catch (InvalidCastException ex)
{
Console.WriteLine("Cannot cast mail item, so skipping. Error: {0}", e);
continue;
}
//Extract the attachments here and archive or reply - put your logic here
}
catch (Exception e)
{
Console.WriteLine("An error occurred: '{0}'", e);
}
}
Related
I need to write a console application that essentially will:
Access a group/shared mailbox's inbox.
Select all emails older than n minutes.
Move the selected emails to another folder "AgedEmails".
So far the program I wrote works well connecting to my email account, even not passing credentials.
The challenge is to access the shared emailbox not mine. The program will run in a server on a frequency set in Windows task scheduler. I have read many postings where the problem is the same I have, but could not find a solution that works.
I have tried the nameSpace.Logon method but it always connects to my employee's email account.
These are the many ways I tried to login to the shared email account (none work):
outlookNameSpace.Logon("mailboxname#company.com", "", true, true);
outlookNameSpace.Logon("mailboxname#company.com", "theRealPassword");,
and this is how I try to get a handle into the inbox:
inboxFolder = outlookNameSpace.Folders["mailboxname#company.com"].Folders["Inbox"];
I am looking for some one to put me in the right direction to achieve the goal. This application will run unattended.
Thanks in advance for your support. Here is the source code:
static async Task MoveEmailsAsync()
{
StreamWriter logFile = new StreamWriter(path, append: true);
Application outLookApplication = null;
NameSpace outlookNameSpace = null;
MAPIFolder inboxFolder = null;
MAPIFolder sourceFolder = null;
MAPIFolder testFolder = null;
Items mailItems = null;
string sSourceFolder = ConfigurationManager.AppSettings["SourceFolder"];
string destinationFolder = ConfigurationManager.AppSettings["DestinationFolder"];
try
{
int minutesAged = Convert.ToInt16(ConfigurationManager.AppSettings["MinutesAged"]);
DateTime age = DateTime.Now;
outLookApplication = new Application();
outlookNameSpace = outLookApplication.GetNamespace("MAPI");
inboxFolder = outlookNameSpace.GetDefaultFolder(OlDefaultFolders.olFolderInbox);
sourceFolder = inboxFolder.Folders[sSourceFolder.ToString()];
testFolder = inboxFolder.Folders[destinationFolder.ToString()];
mailItems = sourceFolder.Items;
string from = null;
string subject = null;
int counter = mailItems.Count;
int i = 0;
for (int k = counter; k >= 1; k--)
{
try
{
i++;
if (true) //this condition will be removed
{
from = null;
subject = null;
Microsoft.Office.Interop.Outlook.MailItem mailitem = (Microsoft.Office.Interop.Outlook.MailItem)mailItems[k];
TimeSpan ts = DateTime.Now - mailitem.ReceivedTime;
if (ts.TotalMinutes > minutesAged)
{
if (!((mailitem.SenderEmailAddress == null) || (mailitem.SenderEmailAddress == "")))
{
from = mailitem.SenderEmailAddress.ToString();
}
if (!((mailitem.Subject == null) || (mailitem.Subject == "")))
{
subject = mailitem.Subject;
}
await logFile.WriteLineAsync(i + ". From: " + from + " - Subject: " + subject);
mailitem.Move(testFolder);
}
}
}
catch (Exception e)
{
await logFile.WriteLineAsync("Exception caught: " + e.ToString());
}
}
await logFile.WriteLineAsync(DateTime.Now.ToString() + " - End of Job.");
}
catch (Exception e)
{
await logFile.WriteLineAsync("Exception caught: " + e.ToString());
}
logFile.Flush();
logFile.Close();
}
}
Firstly, Namespace.Logon takes the name of an existing profile (as shown in Control Panel | Mail | Show Profiles), not an address of a mailbox.
Secondly, to open a folder from another user's mailbox, replace the GetDefaultFolder call with Namespace.CreateRecipient / Namespace.GetSharedDefaultFolder
Answers to your questions don't make any sense due to the following statement:
The program will run in a server on a frequency set in Windows task scheduler.
That is the key statement in your post because MS states the following for such scenarios:
Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.
If you are building a solution that runs in a server-side context, you should try to use components that have been made safe for unattended execution. Or, you should try to find alternatives that allow at least part of the code to run client-side. If you use an Office application from a server-side solution, the application will lack many of the necessary capabilities to run successfully. Additionally, you will be taking risks with the stability of your overall solution.
Read more about that in the Considerations for server-side Automation of Office article.
If you deal with Exchange accounts you may consider using EWS instead, see Explore the EWS Managed API, EWS, and web services in Exchange for more information. In case of Office365 consider using the Graph API, see Use the Microsoft Graph API.
This is my first time posting a question although I have been lurking and learning from all of you for a while now. I have been developing a C# Windows Form application that automates several day to day activities. One of the simplest pieces of this application is really giving me a hard time.
I need to insert two jpg files into an Email that I am responding to. I can accomplish this by pulling the files directly from the drive but would prefer them to be stored as a resource in the executable. This way I can pass the EXE to others and they can use it also. Here is an example of the code that works when it is stored locally. what I would prefer to do however is replace #"H:\ISOIR\PhishMeIcon.jpg" with Resource1.PhishMeIconJPG . I have seen several discussion about streams and converting the file to Byte format but this does not seem to interact well with Outlook.
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Make sure you have the Email reporting the incident Open");
string inc_num;
inc_num = incident_number.Text;
Microsoft.Office.Interop.Outlook.Application aPP = new Microsoft.Office.Interop.Outlook.Application(1);
Microsoft.Office.Interop.Outlook.MailItem mail = (Microsoft.Office.Interop.Outlook.MailItem)aPP.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
Microsoft.Office.Interop.Outlook.Folder f = aPP.Session.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderDrafts) as Microsoft.Office.Interop.Outlook.Folder;
Microsoft.Office.Interop.Outlook.Inspector inSpect = null;
Microsoft.Office.Interop.Outlook.MailItem sMail = null;
Microsoft.Office.Interop.Outlook.MailItem rMail = null;
Microsoft.Office.Interop.Outlook.Attachment phishICO = null;
try
{
inSpect = aPP.ActiveInspector();
sMail = inSpect.CurrentItem as Microsoft.Office.Interop.Outlook.MailItem;
rMail = sMail.ReplyAll();
phishICO = rMail.Attachments.Add(#"H:\ISOIR\PhishMeIcon.jpg", Microsoft.Office.Interop.Outlook.OlAttachmentType.olEmbeddeditem, null, "name");
string imageCid = "PhishMeIcon.jpg#123";
phishICO.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E", imageCid);
rMail.Subject = ("INC- ") + inc_num;
rMail.HTMLBody = Resource1.SPAM_Response_P1 + String.Format("<body><img src=\"cid:{0}\"></body>", imageCid) + Resource1.SPAM_Response_P2 + rMail.HTMLBody;
rMail.HTMLBody = rMail.HTMLBody.Replace("XXXX", inc_num);
rMail.Save();
MessageBox.Show("Your Email has been saved in your DRAFT Folder for review");
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message,
"An exception is occured in the code of add-in.");
}
finally
{
if (sMail != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(sMail);
if (rMail != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(rMail);
if (inSpect != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(inSpect);
}
}
How do you grant someone access to select Reporting Services from the drop down within Management Studio as per the below image.
Also, I have a colleague who has full admin access to this however when they run the below with the credentials set to DefaultCredentials we seem still be getting an error:
namespace ReportingServicesJobsUtility
{
public class Program
{
public static void Main(string[] args)
{
ListJobSSRS();
}
public static void ListJobSSRS()
{
//create instance of ReportingService2010 called server
server.ReportingService2010 rs = new server.ReportingService2010();
//user credentials running application to be used
rs.Credentials = CredentialCache.DefaultCredentials;
//rs.Credentials = new System.Net.NetworkCredential("","");
//create array of jobs
Job[] jobs = null;
try
{
jobs = rs.ListJobs();
ListRunningJobs(jobs);
}
catch (SoapException e)
{
Console.WriteLine(e.Detail.InnerXml.ToString());
}
Console.ReadLine();
}
//make this a void?
public static bool ListRunningJobs(server.Job[] jobs)
{
int runningJobCount = 0;
Console.WriteLine("Current Jobs");
Console.WriteLine("================================" + Environment.NewLine);
server.Job job = default(server.Job);
foreach (var j in jobs)
{
Console.WriteLine("--------------------------------");
Console.WriteLine("JobID: {0}", job.JobID);
Console.WriteLine("--------------------------------");
Console.WriteLine("Action: {0}", job.JobActionName);
Console.WriteLine("Description: {0}", job.Description);
Console.WriteLine("Machine: {0}", job.Machine);
Console.WriteLine("Name: {0}", job.Name);
Console.WriteLine("Path: {0}", job.Path);
Console.WriteLine("StartDateTime: {0}", job.StartDateTime);
Console.WriteLine("Status: {0}", job.JobStatusName);
Console.WriteLine("Type: {0}", job.JobTypeName);
Console.WriteLine("User: {0}" + Environment.NewLine, job.User);
runningJobCount += 1;
}
Console.Write("There are {0} running jobs. ", runningJobCount);
//returning a true for no reason
return true;
}
}
}
The error message is as follows and we believe this is down to credentials, unless anyone can also shed some light on this?
edit
If I use rs.Credentials = new System.Net.NetworkCredential(#"developmentserver\Administrator","password"); on our development server then this runs with no problems, so it seems that it does not like using DefualtCredentials, either that or mine and my collegue's AD credentials are not sufficient, so back to the original question how do we grant full access to our logons.
Have you tried adding the accounts you want in the Report Manager and setting the role assignment there?
you are asking for advice on two very different issues and i have some idea for the second one only.
you are not describing the layout of the servers involved so i make the assumption that you are on a client machine accessing a webservice that is a bridge to reach a SSRS instance; webservice and SSRS are not on the same machine.
if this is the case then check if you are running into the double hop issue.
I have an Outlook add-in that is working successfully for about 100 users. It interacts with our application and creates/updates Tasks and Appointment items. However, one client cannot get it to work for anyone who is not a network administrator. Through some logging, I can see that it works fine until it gets to the part where it should save the Task, and then it throws the infamous "The operation failed." error. I have tried in vain to get more error details by investigating inner exceptions and such, but "The operation failed." is all I can seem to get out of it.
To make it simpler to debug, I eliminated our application from the picture and wrote them a test add-in that just creates 9 tasks (hard-coded to "Task 1", "Task 2", etc.) and it fails in the same place with the same error. I have asked them to write a macro to see if a macro is able to create tasks for these users. I am awaiting their reply on that.
Anyway, I have been on MSDN for over a month trying to get help on the issue and nobody has been able to help. I am hoping someone here could shed some light. If you can provide any insight on what might be happening or suggestions to help me track down the problem, it would be greatly appreciated!
This is the function I am using to create the sample tasks in the test add-in:
private void CreateTasks()
{
int i = 1;
Outlook.TaskItem t = null;
Outlook.UserProperties ups = null;
Outlook.UserProperty up = null;
string name = string.Empty;
while (i < 10)
{
name = string.Format("Task {0}", i.ToString());
t = Application.CreateItem(Outlook.OlItemType.olTaskItem);
t.Subject = name;
t.Status = Outlook.OlTaskStatus.olTaskNotStarted;
t.Body = string.Format("Task {0} description", i.ToString());
t.Categories = "Test Task";
t.Importance = Outlook.OlImportance.olImportanceNormal;
t.ActualWork = 0;
t.TotalWork = 5 * 60;
//mimic dates that might come in main add-in
DateTime st = Convert.ToDateTime("12/10/2013");
st = st.AddDays(i);
t.StartDate = st;
DateTime end = Convert.ToDateTime("01/02/2014");
end = end.AddDays(i);
string EP = end.ToShortDateString() + " 5:00:00 PM";
end = Convert.ToDateTime(EP);
t.DueDate = end;
//mimic how we keep track of our items in the main add-in
ups = t.UserProperties;
up = ups.Find("ID");
if (up == null)
{
up = ups.Add("ID", Outlook.OlUserPropertyType.olText);
}
up.Value = string.Format("ID {0}", i.ToString());
//This is where the "The Operation Failed." error occurs on every single task.
try
{
((Outlook._TaskItem)t).Save();
}
catch (Exception ex)
{
//logs message to file
}
//Release objects
if (up != null) Marshal.ReleaseComObject(up);
if (t != null) Marshal.ReleaseComObject(t);
i++;
}
GC.Collect();
}
According to MSDN:
Saves the Microsoft Outlook item to the current folder or,
if this is a new item, to the Outlook default folder for the item type.
Does the "non administrator user" have access to this folder?
I'd hedge a bet it is something to do with Access permissions. This would be the first place to start.
Is this in an Exchange mailbox? I've seen this error before when connection to the server becomes spotty. If it is, see if the error occurs if you switch from cached mode to online mode. I don't think it's a code issue.
I want to get all emails in my ASP.NET application that have a certain CC-recipient. To use this for future emails I didn't want to polling all the time to get them. But I can't find a way, how I can use push to get the emails instantly. Are their any frameworks in C# to help me for this?
I want to connect with my application to a mail server and register a method 'X'. Always when a new message arrived to the mail server, my application have to be notified and my application should execute the method 'X'.
I hope that this is possible with code like this:
void Application_Start()
{
...
ConnectWithTheSmtpServer();
RegisterMethodForNotification(DoSomethink);
...
}
void DoSomethink(Mail newMail)
{
// Do Somethink with the mail
}
EDIT:
I did it with the MailSystem.Net. It works very fine and is very easy to implement.
Sample Code:
void Application_Start()
{
var worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(StartIdleProcess);
if (worker.IsBusy)
worker.CancelAsync();
worker.RunWorkerAsync();
}
private void StartIdleProcess(object sender, DoWorkEventArgs e)
{
if (_imap != null && _imap.IsConnected)
{
_imap.StopIdle();
_imap.Disconnect();
}
_imap = new Imap4Client();
_imap.ConnectSsl(server-name, 993);
_imap.Login(username, passwort);
var inbox = _imap.SelectMailbox("INBOX");
_imap.NewMessageReceived += new NewMessageReceivedEventHandler(NewMessageReceived);
inbox.Subscribe();
_imap.StartIdle();
}
public static void NewMessageReceived(object source, NewMessageReceivedEventArgs e)
{
// Do something with the source...
}
You are approaching this from the wrong angle.
SMTP does not support receiving mail (never mind PUSH mail). POP3 is what you can use for retrieving mail, but it does not have support for PUSH either (so you would have to pull for mail).
The IMAP4 IDLE extension is what most refer to as PUSH mail - so you will need to find a library for C# that supports IMAP4 IDLE. I found some information that will get you going in the right direction (no reason to duplicate it here):
Using C# .Net Libraries to Check for IMAP Messages
Accessing IMAP in C#
Keep in mind when choosing a solution that it needs to support IDLE.
I really like the look of MailSystem.Net as it fulfills your requirements.
Remember that your mail server also needs to have IMAP4 and IMAP4 IDLE enabled. Some mail servers don't support it, so you might be clean out of luck (and will have to use POP3 pulling).
You could send a copy of your emails(i.e. using /etc/aliases file in PostFix) to a MAIL SERVER YOU CAN HANDLE. Once there, you can implement a MAIL PROCESSOR that do whatever you want anytime a mail that MEET CERTAIN CONDITIONS arrives.
Hope that helps,
You can try this:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using EAGetMail; //add EAGetMail namespace
namespace receiveemail
{
class Program
{
static void Main(string[] args)
{
// Create a folder named "inbox" under current directory
// to save the email retrie enter code here ved.
string curpath = Directory.GetCurrentDirectory();
string mailbox = String.Format("{0}\\inbox", curpath);
// If the folder is not existed, create it.
if (!Directory.Exists(mailbox))
{
Directory.CreateDirectory(mailbox);
}
// Gmail IMAP4 server is "imap.gmail.com"
MailServer oServer = new MailServer("imap.gmail.com",
"gmailid#gmail.com", "yourpassword", ServerProtocol.Imap4 );
MailClient oClient = new MailClient("TryIt");
// Set SSL connection,
oServer.SSLConnection = true;
// Set 993 IMAP4 port
oServer.Port = 993;
try
{
oClient.Connect(oServer);
MailInfo[] infos = oClient.GetMailInfos();
for (int i = 0; i < infos.Length; i++)
{
MailInfo info = infos[i];
Console.WriteLine("Index: {0}; Size: {1}; UIDL: {2}",
info.Index, info.Size, info.UIDL);
// Download email from GMail IMAP4 server
Mail oMail = oClient.GetMail(info);
Console.WriteLine("From: {0}", oMail.From.ToString());
Console.WriteLine("Subject: {0}\r\n", oMail.Subject);
// Generate an email file name based on date time.
System.DateTime d = System.DateTime.Now;
System.Globalization.CultureInfo cur = new
System.Globalization.CultureInfo("en-US");
string sdate = d.ToString("yyyyMMddHHmmss", cur);
string fileName = String.Format("{0}\\{1}{2}{3}.eml",
mailbox, sdate, d.Millisecond.ToString("d3"), i);
// Save email to local disk
oMail.SaveAs(fileName, true);
// Mark email as deleted in GMail account.
oClient.Delete(info);
}
// Quit and purge emails marked as deleted from Gmail IMAP4 server.
oClient.Quit();
}
catch (Exception ep)
{
Console.WriteLine(ep.Message);
}
}
}
}