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);
}
}
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.
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);
}
}
I have made small C# WFP application that sends emails with embedded images using C# and Outlook Interop. It seems that many mail clients reacts diffently to emails sent from this application compared to manually created emails where images are pasted directly in using CTRL+V.
I have tried to use OutlookSpy to compare the two emails, but I can't see any difference that could cause the different way the emails are being handled.
When an email sent from the C# application is received in an Outlook Application on an Iphone, the images are blank. It works just fine when the image is manually created and images are pasted in using CTRL+V.
1) How do I make sure that the emails sent from my application are handled the same way as when I manually create an email in Outlook and copy an image using CTRL+V?
If I can make this work, then it should be possible to make the images display correctly in Outlook for Iphone.
2) How does outlook handle an image when it is pasted in using CTRL+V? It seems that it uses Cid, the same way as I do in my application, but how does it refer to the image if it is not attached?
Here is the code for the sample application:
private void SendEmailButton_OnClick(object sender, RoutedEventArgs e)
{
SendMail();
}
public void SendMail()
{
var application = GetOutlookApplication();
MailItem newMail = (MailItem)application.CreateItem(OlItemType.olMailItem);
newMail.To = "!!!EnterValidEmailAddress!!!";
newMail.Subject = "Image test";
//Create image as embedded attachment
Attachment attachment1 = newMail.Attachments.Add(#"C:/SomeImage1.png", OlAttachmentType.olByValue);
string imageCid1 = "SomeImage_1";
//The property Accessor to be able to refer to the image from Email HTML Body using CID
attachment1.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E", imageCid1);
//Set property Accesor to hide attachment
attachment1.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x7FFE000B", true);
//Refer the attachment using cid
newMail.HTMLBody = String.Format("<img src=\"cid:{1}\">", "google.dk", imageCid1);
newMail.Save();
newMail.Display();
}
public static OutlookApplication GetOutlookApplication()
{
// Start outlook process if it is not startet already
if (!IsOutlookRunning())
{
using (Process p = new Process())
{
p.StartInfo.FileName = "OUTLOOK.EXE";
p.Start();
if (!p.WaitForInputIdle(10000))
Thread.Sleep(5000);
}
}
// Start the outlook application (API)
OutlookApplication oApp = null;
if (Process.GetProcessesByName("OUTLOOK").Any())
{
try
{
oApp = Marshal.GetActiveObject("Outlook.Application") as OutlookApplication;
}
catch (System.Exception)
{
if (oApp == null)
oApp = new OutlookApplication();
}
return oApp;
}
oApp = new OutlookApplication();
return oApp;
}
private static bool IsOutlookRunning()
{
Process[] p = Process.GetProcessesByName("Outlook");
return p.Length != 0;
}
Make sure that you get a well-formed HTML markup finally. The following line of code just adds the <a> tag to the end of body string:
newMail.HTMLBody += String.Format("<img src=\"cid:{1}\">", "google.dk", imageCid1);
Instead, you need to find a right place for the image between the <body> and </body> tags.
I am currently working on an application for the company I work for. Part of this application deals with discrepancy reporting and sending emails out when a new discrepancy number has been created. First, it is important to realize that I am redeveloping this application from VB.NET to C#. In the old application the developer chose to read an XML file for a few email addresses. I've read to use alternate options unless the XML file is full of information. This is not intended to be an opinionated question and sorry if this sounds like one. However, I am looking for the correct way of doing this. Should these email addresses be kept in a database table for easy adding/deleting or is there a more standardized way to do this? Please find the current code below.
public void PrepareEmail(string subject, string message)
{
if (MessageBox.Show(#"Are you sure you want to save and send Discrepancy Report: " + tbxDRNumber.Text + #"?\n Click YES to save\n Click NO to cancel", #"Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
SendEmail(subject, message);
}
}
public Array AddEmail()
{
string[] dRemail = { "", "", "" };
if (File.Exists(#"\\fs01\Applications\EMS-Manager\DREmailAddresses.xml"))
{
XmlReader emailDocument = new XmlTextReader(#"\\fs01\Applications\EMS-Manager\DREmailAddresses.xml");
while (emailDocument.Read())
{
var type = emailDocument.NodeType;
switch (type)
{
case XmlNodeType.Element:
if (emailDocument.Name == "DRCreatedAddEmail")
{
dRemail[0] = emailDocument.ReadInnerXml();
}
if (emailDocument.Name == "DRActionNeededAddEmail")
{
dRemail[1] = emailDocument.ReadInnerXml();
}
if (emailDocument.Name == "DRPendingAddEmail")
{
dRemail[2] = emailDocument.ReadInnerXml();
}
else
{
MessageBox.Show(#"The file: 'DREmailAddresses.xml' was not found at: \\fs01\Applications\EMS-Manager");
}
break;
}
}
}
return dRemail;
}
public void SendEmail(string subjectText, string bodyText)
{
string[] email = (string[])AddEmail();
//object oOutlook = default(Microsoft.Office.Interop.Outlook.Application);
var oMessage = default(MailItem);
Activator.CreateInstance(Type.GetTypeFromProgID("Outlook.Application"));
if (subjectText == "New Discrepancy Created. DR" + tbxDRNumber.Text + " ")
{
oMessage.To = email[0];
oMessage.Subject = subjectText;
oMessage.HTMLBody = bodyText;
try
{
oMessage.Send();
}
catch (System.Exception e)
{
MessageBox.Show(#"Send Failed with error: " + e);
throw;
}
}
else if (subjectText == tbxDRNumber.Text + " - Action Needed")
{
oMessage.To = email[1];
oMessage.Subject = subjectText;
oMessage.HTMLBody = bodyText;
try
{
oMessage.Send();
}
catch (System.Exception e)
{
MessageBox.Show(#"Send Failed with error: " + e);
throw;
}
}
else if (subjectText == tbxDRNumber.Text + "DR Pending Approval")
{
oMessage.To = email[2];
oMessage.Subject = subjectText;
oMessage.HTMLBody = bodyText;
try
{
oMessage.Send();
}
catch (System.Exception e)
{
MessageBox.Show(#"Send Failed with error: " + e);
throw;
}
}
}
There isn't necessarily anything wrong with flat file configuration files. It really depends on your use case. How often do the emails in the file change? How many entries are there? Do users of the application edit the list? Are there any complex rules where different addresses will get sent different emails?
If this just a simple mailing list that doesn't change often it's probably fine to use the xml file. You could also just create an email distribution list to send to instead, like ApplicationDiscrepancyReport. That way you can just manage the recipients in active directory. If you stick with the XML email addresses, at the very least I would try to get rid of the hardcoded path to the xml file located on your file share.
If the email addresses change a lot, and are changed by users of the application, I would recommend moving this to a SQL database. If the recipients list is changing frequently you may want to add tracking of when these addresses get edited as well.
While building an app, i have been getting problems getting the advertisements to display once the app is compiled and run on an android device. The biggest problem is with testing where the error is, since I can't access my debug log while on my android device.
So here is my question: Is there an existing function or plugin I can use to print the debug log to the bottom of the screen on my device? Or will I need to create one myself?
Note: If I end up writing one up first, I'll post my code as an answer for everyone else. If any one has some code that they use in their own programs, i invite them to post it here.
set permission in your AndroidManifest
<uses-permission android:name="android.permission.READ_LOGS" />
and use this code to get log cat
public String getLogCat() {
InputStreamReader reader = null;
String logCat = "";
Process process;
try {
process = Runtime.getRuntime().exec(new String[] { "logcat", "-d", "-s", "-v", "time", "*:w" });
reader = new InputStreamReader(process.getInputStream());
BufferedReader in = new BufferedReader(reader);
String line = "";
while ((line = in.readLine()) != null) {
logCat += line;
}
in.close();
reader.close();
} catch (IOException e) {
logCat = "";
}
return logCat;
}
now, call this method in you code..
String logCat = getLogCat();