Open new email window in Windows desktop e-mail client from C# - c#

I am trying to open a filled email window with the method below which is called from a separate STA thread.
private void SendMailMessage(object ignore)
{
MAPIHelperInterop.MapiMessage message = new MAPIHelperInterop.MapiMessage();
using(RecipientCollection.InteropRecipientCollection interopRecipients
= fRecipientCollection.GetInteropRepresentation())
{
message.Subject = fSubject;
message.NoteText = fBody;
message.Recipients = interopRecipients.Handle;
message.RecipientCount = fRecipientCollection.Count;
// Check if we need to add attachments
if(fFiles.Count > 0)
{
// Add attachments
message.Files = AllocateFileAttachments(out message.FileCount);
}
// Signal the creating thread (make the remaining code async)
fManualResetEvent.Set();
int error = MAPIHelperInterop.MAPISendMailW(IntPtr.Zero, IntPtr.Zero, message, 0x8, 0);
if(fFiles.Count > 0)
{
// Deallocate the files
DeallocateFileAttachments(message);
}
// Check for error
if(error != SUCCESS_SUCCESS)
{
LogMAPIError(error);
}
}
}
I have been testing this with Outlook and I keep getting error code 2 (MAPI_E_FAILURE) and nothing visible happens in Outlook (eventually it should work with any mail client, but Outlook is the main use case and so an extendable solution that works for Outlook would be a good first step). It works only if I either start Outlook as an administrator, or have Outlook closed when I run the code. I have tried calling MAPISendMailW with a handle to Outlook and with different combinations of flags but that did not work either.
The closest I have found to my problem is this https://social.msdn.microsoft.com/Forums/office/en-US/63e9f5b2-f5f2-4cf8-bdc2-ca1fad88ebe5/problem-with-outlook-and-mapisendmail-returns-mapiefailure-when-outlook-is-running?forum=outlookdev. In order to follow the suggested solution tried to run the SendMailMessage method in a separate AppDomain like this:
public void ShowDialog()
{
Evidence e = new Evidence();
e.AddHostEvidence(new Zone(SecurityZone.MyComputer));
AppDomain appDomain = AppDomain.CreateDomain("Outlook Launcher", e);
appDomain.DoCallBack(SendMailMessage);
}
If I use "SecurityZone.MyComputer" then I get the same error as before and if I use any other SecurityZone I get this error message: "Request for the permission of type 'System.Security.Permissions.ReflectionPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed." Maybe that is not what they suggested in the above post but this is anything I can think off.
Thank you for your help.

I have used MAPI in the past and it can be a pain to get working well across different Outlook versions.
A very simple work around is to use mailto links. The advantage is that it will work with different mail clients automatically.
Process.Start("mailto:hello#test.com&subject=This is the subject&body=This is the body");
This will open up your default mail client with the subject and body filled in.

Related

Is there a way to handle/turn off dialogs from Outlook while sending emails with encryption in place (with C# WPF)?

I have a C# WPF application, one of its functionalities it to send series of emails through local Outlook/account. It utilizes Microsoft.Office.Interop.Outlook COM. The thing is that I need to encrypt emails. I'm handling it with following approach:
using Outlook = Microsoft.Office.Interop.Outlook;
(...)
Outlook.Application olkApp = new Outlook.Application();
Outlook.MailItem otlMail = olkApp.CreateItemFromTemplate(templateFullPath) as Outlook.MailItem;
otlMail.To = (...)
try
{
const string PR_SECURITY_FLAGS = http://schemas.microsoft.com/mapi/proptag/0x6E010003";
long prop = Convert.ToInt64(otlMail.PropertyAccessor.GetProperty(PR_SECURITY_FLAGS));
var ulFlags = 0x0;
ulFlags = (ulFlags | 0x1); // SECFLAG_ENCRYPTED
//ulFlags = (ulFlags | 0x2); // SECFLAG_SIGNED
otlMail.PropertyAccessor.SetProperty(PR_SECURITY_FLAGS, ulFlags);
}
catch (Exception ex)
(...)
try
{
otlMail.Send();
}
catch (Exception ex)
{
(...)
}
Since I send these emails over to company's accounts, in theory all employees should have valid certificates (PKI card). In practice some folks for sure will not comply that requirement (for some reasons). So, when I'm testing the app, simulating such problem, the Outlook displays a dialog asking for decision:
Microsoft Outlook had problems encrypting this message because the following recipients had missing or invalid certificates, or conflicting or unsupported encryption capabilities: (some email address).
Continue will encrypt and send the message but listed recipients may not be able to read it.
[Send Unencrypted] [Continue] [Cancel]
The application is waiting endlessly for user decision. I find it inconvenient for user.
In case such a problem occurs I'd see either:
disable such dialogs and ensure the exception would be thrown so that I can trace problematic accounts, or...
handle such dialogs in an automated manner somehow.
Registry changes are not an option.
Can anybody help please?
Regards,
Kris
The Outlook object model doesn't provide anything for suppressing such dialogs automatically.

Automatically create rule in Outlook c#

I need to create a set of rules in Microsoft Outlook via C#.
I have found a lot of documentation online, but unfortunately it is not working for me.
I create the rule using the function below:
{
Outlook.Application OutlookApplication = Marshal.GetActiveObject("Outlook.Application") as Outlook.Application;
Outlook.MAPIFolder OutlookInbox = (Outlook.MAPIFolder)OutlookApplication.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
Outlook.Rules rules = null;
try
{
rules = OutlookApplication.Session.DefaultStore.GetRules(); //Gets list of outlook rules
}
catch
{
Debug.WriteLine("Could not obtain rules collection.");
return;
}
string ruleName = "TestRule";
Outlook.Rule rule = rules.Create(ruleName, Outlook.OlRuleType.olRuleReceive); //Creates new rule in collection
rule.Name = ruleName;
//From condition
rule.Conditions.From.Recipients.Add("allixhd#gmail.com");
rule.Conditions.From.Enabled = true;
//Subject condition
rule.Conditions.Subject.Text = new string[] { "#test" };
rule.Conditions.Subject.Enabled = true;
//Move action
Outlook.MAPIFolder ruleFolder = OutlookInbox.Folders["TestFolder"]; //Gets the folder with name TestFolder
rule.Actions.MoveToFolder.Folder = ruleFolder;
rule.Actions.MoveToFolder.Enabled = true;
rule.Enabled = true;
//Save rules
try
{
rules.Save(true);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
The problem I face is that the rule is created successfully in my Outlook instance. I am able to open the Rule window in Outlook and I can see my rule. When I open the rule, all the settings appear correct. However when I run the rule it does not work.
If I manually create an identical rule in Outlook it does work.
I have also found the following:
- Create the rule in Outlook via my C# function
- Open the rule window in Outlook
- Tick and untick a checkbox and save
- The rule runs correctly.
- Note: If I only change the name, rather than unticking and reticking a checkbox, this does not work.
- I tried to replicate this with a hack in the code forcing this behaviour, and the rule did not work.
I wonder if there is some setting I am missing that is applied when you use the rule interface in Outlook?
Any advice would be very welcome.
Thanks
I'd suggest starting from releasing all underlying com objects in the code. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object.
To release all COM objects you need to break the chain of calls and declare the each property and method call on a separate line of code.
OutlookApplication.Application.ActiveExplorer().Session.GetDefaultFolder
There is no need to call the Application property of the Application class. You have already got an instance of the Application class.
Also there is no need to call the ActiveExplorer method of the Application class. You can use the Session property or the GetNamespace method of the Application class. Review the code and release all underlying COM objects.
rules.Save(true);
Do you get any exceptions when calling the Save method?
Anyway, I'd suggest creating a rule manually and then exploring its properties to create the same rule programmatically.
I have solved this question.
The domain of the From email address was in a hierarchy in outlook and so not being recognized correctly. I have included all the email addresses for the user and now the rule works.

VSTO Outlook User Properties (Custom) not syncing

I have the problem that when i add a custom UserProperty to an Outlook MailItem, it does not get synced to other connected Outlooks.
What can i do to force Outlook to sync the whole email?
My overal problem:
I've got a shared Exchange Mailbox, opened on two clients (in Outlook)
I would like to lock a mail item, if it gets opened in one Outlook and show the second Outlook user a message like "The user XX is currently reading this email"
My way to solve the problem:
Creating a Outlook Plugin.
When user "A" is opening the Email, I am adding a "LockingUser" UserProperty to the MailItem object. If user "B" is trying to open the Email, I am first looking if a "LockingUser" Property exists.
I have disabled the cached mode.
I have tried to update the subject of the email: this works perfectly and gets synced immediatly (but is not a solution for my problem)
private void SetLockingUser(Outlook.MailItem mail)
{
var lockingUserProperty = mail.UserProperties.Find("LockingUser");
if (lockingUserProperty != null)
{
MessageBox.Show("Email locked by: " + lockingUserProperty.Value);
return;
}
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
var username = identity != null ? identity.Name : "";
lockingUserProperty = mail.UserProperties.Add("LockingUser", Outlook.OlUserPropertyType.olText, false, 1);
lockingUserProperty.Value = username;
mail.Save();
}
Please show the relevant snippet of your code and make sure you call MailItem.Save. Also keep in mind that there will always be lag since the changes will take up to a couple minutes to sync to Exchange and then to another user if cached mode is used. You'd be better off using some external sync mechanism instead of a user property.

Send Message with Custom Properties with Redemption

I’m using Redemption.dll to set custom properties to my messages with set_Filed() and get_field() in C#. Everything works perfectly until the moment I send my messages.
From Outlook I use RDOMail.Send() and this sent the message to the Drafts folder. Then I read in the Redemption FAQ that I should use the IMessage::Submit() method (that I couldn’t find anywhere in the dll for .NET) and then use DeliverNow(), method that I did use but to my surprise when I receive my messages I lose the properties I had set.
This is really completely critical to our project since if Outlook can’t send mails it’s worth nothing.
Here is part of my code.
private void adxOutlookEvents_ItemSend(object sender, AddinExpress.MSO.ADXOlItemSendEventArgs e)
{
try
{
RDOSessionClass _RDOSession= MessagesActions.GetRDOSession();
Outlook.MailItem _MailItem= e.Item as Outlook.MailItem;
RDOMail _RdoMail = MessagesActions.GetRDOMail(_RDOSession, _MailItem);
_RdoMail.Send(); // Send using Redeption
e.Cancel = true; // Only send using Redeption
if (_RdoMail != null && Marshal.IsComObject(_RdoMail))
Marshal.ReleaseComObject(_RdoMail);
Redemption.MAPIUtils _MAPIUtils = new MAPIUtils();
_MAPIUtils.DeliverNow(0, 0);
if (_MAPIUtils != null && Marshal.IsComObject(_MAPIUtils))
Marshal.ReleaseComObject(_MAPIUtils);
CurrentInspector.Close(Outlook.OlInspectorClose.olDiscard);
}
catch
{
}
}
Thanks.
When a message is sent through SMTP (unlike between 2 Exchange mailboxes in the same domain), the message is converted to MIME, and all MAPI specific properties are lost.
You can force Outlook to send the message in the TNEF (the infamous winmail.dat) format if yo uset a special named property called UseTnef:
RDOMail.Fields["http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/8582000B"] = true;

How do I popup the compose / create mail dialog using the user's default email client?

The use case is simple. At a certain point of time, I need to be able to show the user his familiar compose email dialog (Outlook or other) with
fields like from, to, Subject already filled up with certain application determined values.
The email would also have an attachment along with it.
The mail should not be sent unless the user explicitly okays it.
I did this once back in the ol' VB6 days.. can't figure out how now.. I just remember that it was quite easy.
Managed app, C#, .net 3.0+
Update#1: Yeah seems like mailto removed support for attachments (as a security risk?). I tried
You need to include ShellExecute signature as described here. All I got from this was a 5 SE_ERR_ACCESSDENIED and a 2 just for some variety
string sMailToLink = #"mailto:some.address#gmail.com?subject=Hey&body= yeah yeah yeah";
IntPtr result = ShellExecute(IntPtr.Zero, "open", sMailToLink, "", "", ShowCommands.SW_SHOWNORMAL);
Debug.Assert(result.ToInt32() > 32, "Shell Execute failed with return code " + result.ToInt32());
The same MailtoLink works perfectly with Process.Start... but as long as thou shalt not mention attachments.
System.Diagnostics.Process.Start(sMailToLink);
The other options are using the Outlook Object model to do this.. but I've been told that this requires you to add assembly references based to the exact version of Outlook installed. Also this would blow up if the user doesn't prefer MS for email.
The next option are Mapi and something called Mapi33.. Status still IN PROGRESS. Ears still open to suggestions.
You can create a process object and have it call "mailto:user#example.com?subject=My+New+Subject". This will cause the system to act on the mailto with its default handler, however, while you can set subjects and such this wont handle adding an attachment. I'll freely admit im not entirely sure how you'd go about forcing an attachment without writing some mail plugin.
The process code is:
System.Diagnostics.Process.Start("mailto:user#example.com?subject=My+New+Subject");
It's probably not the most efficient or elegant way, but shelling a "mailto:" link will do what you want, I think.
EDIT: Sorry, left out a very important "not".
Since mailto does not support attachments, and since MAPI is not supported within managed code, your best bet is to write (or have someone write) a small non-managed program to call MAPI functions that you can call with command-line arguments. Pity that .NET does not have a cleaner alternative.
See also : MAPI and managed code experiences?
Could it be that you used the mailto: protocol?
Almost all of what you highlight can be done, but I am quite sure, that you cant do attachments.
Microsoft MailTo Documentation
Starting process with mailto: arguments is the simplest approach. Yet, it does not allow anything more or less complex.
Slightly different approach involves creating email template and then feeding it to the Process.Start:
var client = new SmtpClient();
var folder = new RandomTempFolder();
client.DeliveryMethod =
SmtpDeliveryMethod.SpecifiedPickupDirectory;
client.PickupDirectoryLocation = folder.FullName;
var message = new MailMessage("to#no.net",
"from#no.net", "Subject","Hi and bye");
// add attachments here, if needed
// need this to open email in Edit mode in OE
message.Headers.Add("X-Unsent", "1");
client.Send(message);
var files = folder.GetFiles();
Process.Start(files[0].FullName);
Scenarios for the default email handler:
Outlook express opens
Windows: Outlook - does not handle by default, Outlook Express is called instead
Windows: The Bat! - message is opened for viewing, hit Shift-F6 and Enter to send
I've also tested with Mono and it worked more or less.
Additional details are available in this post: Information integration - simplest approach for templated emails
PS: in the end I went for slightly more complex scenario:
Defined interface IEmailIntegraton
Code above went into the DefaultEmailIntegration
Added implementations for OutlookEmailIntegration (automation) and theBat! email integration (using their template format).
Allowed users of the SmartClient to select their scenario from the drop-down (alternatively this could've been implemented as "Check the system for the default email handler and decide automatically")
You're making the assumption that they will have an email client installed, of course.
The option I've taken in the past (in a corporate environment where everyone has at least one version of Outlook installed) was to use the Outlook interop - you only need to reference the earliest version you need to support.
You could look at P/Invoking MAPISendDocuments (which I'd try and avoid, personally), or the other option would be to create your own "compose" form and use the objects from the System.Net.Mail namespace.
you can use a trick if you intend to use Outlook[this code is based on outlook 2010[v14.0.0.]]
Create Outlook MailItem
and transmit file (ie download)
if user opens the file (.msg) the compose message dialog opens automatically
here is the code ...
Microsoft.Office.Interop.Outlook.Application outapp = new Application();
try
{
_NameSpace np = outapp.GetNamespace("MAPI");
MailItem oMsg = (MailItem)outapp.CreateItem(OlItemType.olMailItem);
oMsg.To = "a#b.com";
oMsg.Subject = "Subject";
//add detail
oMsg.SaveAs("C:\\Doc.msg", OlSaveAsType.olMSGUnicode);//your path
oMsg.Close(OlInspectorClose.olSave);
}
catch (System.Exception e)
{
status = false;
}
finally
{
outapp.Quit();
}
then transmit the file you created say "Doc.msg"
string filename ="Doc.msg";//file name created previously
path = "C:\\" + filename; //full path ;
Response.ContentType="application/outlook";
Response.AppendHeader("Content-Disposition", "filename=\"" + filename + "\"");
FileInfo fl = new FileInfo(path);
Response.AddHeader("Content-Length", fl.Length.ToString());
Response.TransmitFile(path,0,fl.Length);
Response.End();

Categories