Modifying ActiveInlineResponseWordEditor from a Outlook 2007 add-in - c#

I have an Outlook 2007 plugin developed that on the Application.ItemSend event makes a small change to each hyperlink in the email. This is accomplished by getting the Inspector.WordEditor property of the active inspector and looking through the Hyperlinks property.
Unfortunately since the introduction of Office 2013, this method does not work with the inline response feature meaning quick replies are ignored.
I am making modifications to this plugin to try and make this work but I am encountering some strange behaviour.
When running locally, both with and without the debugger, I can use reflection to get the ActiveInlineResponseWordEditor property of Application.ActiveExplorer() and make the same changes and all looks good.
When I package this solution and install it on a test machine, the code still executes are expected (I can see this from some logging I am doing), the changes I make are not actually persisted in the sent email – only for inline responses, all the other functionality works correctly.
I get access to a document using the following snippet:
Word.Document doc = null;
var explorer = Application.ActiveExplorer();
var wrapper = InspectorWrapper.GetWrapperFor(mailItem.GetInspector, logger);
try
{
doc = (Word.Document)explorer.GetType().GetProperty("ActiveInlineResponseWordEditor").GetValue(explorer, null);
}
catch (TargetInvocationException) { /*Silently fail */ }
if (doc == null)
{
if (wrapper is MailItemWrapper)
{
doc = wrapper.Inspector.WordEditor as Word.Document;
logger.Log("Have inspector document.");
}
}
else
{
logger.Log("Have in-line document.");
}
And I use this document to modify all links which is done using this snippet:
foreach (Word.Hyperlink link in doc.Hyperlinks)
{
var uriBuilder = new UriBuilder(link.Address);
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
query.Set("id", 1);
uriBuilder.Query = query.ToString();
var newLink = uriBuilder.ToString();
logger.Log(string.Format("{0} to {1}", link.Address, newLink));
link.Address = newLink;
}
I have tried to just access the MailItem.GetInspector property alone, but the same symptoms exit. I have also tried calling mailItem.Save() after completing the changes, again the symptoms exist.
My hunch is that I am not getting a valid reference to the Word editor and thus the changes are not persisted but does anyone else have any ideas?

Please have a look at this link, this might be of some help to your question:
http://www.add-in-express.com/creating-addins-blog/2012/10/19/customize-outlook2013-inline-response/
Important section is:
How to get an inline response item in Outlook 2013
Here they mention pasting from the link:
"Please remember, if you develop an add-in which supports earlier Outlook versions as well using version neutral interops, you need to use the late binding technology to access the ActiveInlineResponse property."
code looks like:
explorer = OutlookApp.ActiveExplorer();
// response = explorer.ActiveInlineResponse;
response = explorer.GetType().InvokeMember("ActiveInlineResponse",
System.Reflection.BindingFlags.GetProperty |
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public,
null, explorer, null) as Outlook.MailItem;
My understanding is for using the property "ActiveInlineResponseWordEditor" is that you need to use the late binding, since you have the addin compatible with earlier versions - 2007, 2010. Please try if this modification works for you and you are able to get a doc object, which works similar to earlier versions of Outlook.
You should try the following code:
doc = explorer.GetType().InvokeMember("ActiveInlineResponseWordEditor",
System.Reflection.BindingFlags.GetProperty |
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public,
null, explorer, null) as Word.Document;

Try to read the HTMLBody of the outgoing message, load it into an IHTMLDocument object, modify the links, then reset the HTMLBody property.

Related

C# Selenium Edge Driver unable to download file - Keep file prompt shows up

I am using C# with Selenium for QA automation, and I am having issues with downloading an .xml file, because a prompt is always showing up asking if I want to keep the file. It also opens a second tab to execute the download, closing it after the prompt shows up.
[keep file prompt][1]
Using Chrome I do not see this behavior.
I searched all over and could not find a EdgeOptions() and/or AddArguments() capable of taking care of this issue.
Any ideas?
You need to use JS to interact with elements in another browser. I have had such experience and I used if else statement in my method to handle that problem. Just look trough the Selenium documentation, JS with selenium examples and so long so for.
Just add this to your OneTimeSetup method. Make sure to run Visual Studio as administrator. This works since Edge 105+:
public void SetEdgeXmlDownloadPolicy()
{
var keyName = "Software\\Policies\\Microsoft\\Edge\\";
var valueName = "ExemptFileTypeDownloadWarnings";
var valueData = #"{""domains"": ["" * ""], ""file_extension"": ""xml""}";
var currentUser = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64);
var currentKey = currentUser.OpenSubKey(keyName, true);
if (currentKey == null)
currentKey = currentUser.CreateSubKey(keyName);
if (currentKey.GetValue(valueName) == null)
currentKey.SetValue(valueName, valueData);
}

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.

How to access a user property of a custom outlook mail item with Redemption in C#

I’m trying to write a C# application that opens up a outlook custom mail item and fill in multiple user properties. I was able to do so by using the Microsoft Outlook Interop. But I had the annoying security warning each time I tried to change some user property. I found out that Redemption is the perfect tool to avoid this. But when I try to change a user property, Redemption creates a new one instead of using the already existing one.
This is the code I used to change the property with Outlook Interop (Pops up a security warning) :
string customPropertyNamespace = "http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/";
Outlook._Items oItems = oPublicFolder.Folders["Babillards"].Folders["SYSOTI"].Folders["MEP"].Items;
Outlook._MailItem oMep = oItems.Add("ipm.note.mep");
oMep.PropertyAccessor.SetProperty(customPropertyNamespace + "prop1", "SomeText");
oMep.Display(false);
This all works fine, except for the security warning...
This is the redemption code I'm trying to use :
string customPropertyGUID = "{00020329-0000-0000-C000-000000000046}";
Outlook._Items oItems = oPublicFolder.Folders["Babillards"].Folders["SYSOTI"].Folders["MEP"].Items;
Outlook._MailItem oMep = oItems.Add("ipm.note.mep");
Redemption.SafeMailItem Mep = new Redemption.SafeMailItem();
Mep.Item = oMep;
Mep.set_Fields(Mep.GetIDsFromNames(customPropertyGUID, "prop1"), "SomeText");
oMep.Display(false);
From what I understood this should work. But instead, my mail page opens with all my fields empty. By using OutlookSpy I found out that Redemption creates a new property with this DASL :
http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/prop1/0x0000001F
instead of :
http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/prop1
Can somebody help me? I also tried using the Redemption.MAPIUtils but I ended up with exactly the same result. Is there a way to change a user property by passing the DASL instead of a GUID and ID ?
The two property names are exactly the same - the last part (0x0000001F) is simply property type (= PT_UNICODE). What exactly do you mean by "my mail page opens with all my fields empty"? Do you have a custom form with controls bound to user fields?
Also keep in mind that Outlook might not see all the latest changes made with MAPI until the item is completely dereferenced and reopened. Do o use the data after you restart Outlook and reopen the existing item?
To avoid Outlook caching problem, try to create the message using Redemption, set the property, and only then open the message using Outlook. Somethiong like the following (off the top of my head):
string customPropertyNamespace = "http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/";
Outlook.Forlder oFolder = oPublicFolder.Folders["Babillards"].Folders["SYSOTI"].Folders["MEP"];
Redemption.RDOSession session = new RDOSesssion();
session.MAPUIOBJECT = Application.Session.MAPIOBJECT;
Redemption.RDOFolder rFolder = session.(RDOFolder)session.GetRDOObjectfromOutlookObject(oFolder);
Redemption.RDOMail rMsg = rFolder.Items.Add("ipm.note.mep");
rMsg.Fields[customPropertyNamespace + "prop1"] = "SomeText";
rMsg.Save();
//reopen in Outlook and display. Or you can use rMsg.Display()
Outlook._MailItem oMep = Application.Session.GetItemFromID(rMsg.EntryID);
oMep.Display(false);

microsoft.interop.selection text

Before asking my question I would like to describe briefly background of my problem: I'm developing ms word COM addin on C# and I need to handle user's text selections. Now I'm able to catch selection event - it's look like
Microsoft.Interop.Word._Application app;
app = (Word._Application )Application; // Application object comes on addin's connection
app.Application.WindowSelectionChange+=
new Word.ApplicationEvents4_WindowSelectionChangeEventHandler(selChange);
///
void selChange(Word.Selection selection){
MessageBox.Show(selection.Text); // this is my problem, Text property is not available
}
// property Text doesn't exist,but documentation tells that it exists. I suspect, that this property is not available for ms word 2007 - in the documentation only 2003,2010 versions are mentioned. But how I can do something like selection.getSelectedText()? I tryed to play with selection.Rows, selection.Rows[0],selection.Words,selection.Words[0] - no success.
According to the documentation, the Selection.Text property should be available for Word 2007 as well. I made a small sample implementation of your case to test it, and I cannot make it fail on Word 2010 and 2013 at least:
var wordApplication = new Application() { Visible = true };
wordApplication.Documents.Add();
wordApplication.WindowSelectionChange += delegate(Selection mySelection) { Console.WriteLine(mySelection.Text); };
So, I suggest you check that you have included the right namespaces and that the Selection interface you are using are actually the one from the Microsoft.Office.Interop.Word namespace.

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