How to add attachments to mailitem using Outlook late binding - c#

I'm trying to create a mail item and add some attachments to it using late binding. I've already managed to create the mail item, but I cannot invoke the Attachments property.
object objApp;
object objEmail;
Type objClassType = Type.GetTypeFromProgID("Outlook.Application");
objApp = Activator.CreateInstance(objClassType);
// Microsoft.Office.Interop.Outlook.OlItemType.olMailItem = 0
objEmail = objApp.GetType().InvokeMember("CreateItem", BindingFlags.InvokeMethod, null, objApp, new object[] { 0 });
mailItemType.InvokeMember("Subject", BindingFlags.SetProperty, null, objEmail, new object[] { subject });
// THIS RETURNS NULL?!
PropertyInfo att = mailItemType.GetProperty("Attachments", BindingFlags.GetProperty);
What can I do when there's no Attachments property (or method) to invoke? With early binding it's simply objEmail.Attachments.Add(...)

The problem was I called the GetProperty directly. It should be InvockeMember with BindingFlags.GetProperty. I think this is because the interface is IUnknown and only method invoking works.
I also discovered that you can get the Attachments type from CLSID
Type attachmentsType = Type.GetTypeFromCLSID(new Guid("0006303C-0000-0000-C000-000000000046"));
and then call
attachmentsType.InvokeMember("Add", BindingFlags.InvokeMethod, null, attachments, new object[] { ... });
This example is for Office 2003.

I think the GetProperty stmt isn't quite right, I got this to work by doing the following:
object oMailItemAttachments = oMailItem.GetType().InvokeMember("Attachments", System.Reflection.BindingFlags.GetProperty, null, oMailItem, null);
parameter = new object[4];
parameter[0] = #sFileName;
parameter[1] = 1;
parameter[2] = Type.Missing;
parameter[3] = Type.Missing;
oMailItemAttachments.GetType().InvokeMember("Add", System.Reflection.BindingFlags.InvokeMethod, null, oMailItemAttachments, parameter);

Related

MvcMailer, Find the file name of the email

I'm using MvcMailer to save emails to a specified directory locally in my asp.net mvc web application. However I would like to save the file name (e.g. 90b871cd-038f-400a-b4d7-01f87e8c3c26.eml) of the email in the database which will later be accessed using another exe to send emails from the pick up folder.
Could you please advise me on how to retrieve the file name from the mail object?
var mail = Mailer.Example_Mail()
mail.To.Add("some#somedomain.com");
mail.Send();
<smtp from="some#somedomain.com" deliveryMethod="SpecifiedPickupDirectory">
<network host="localhost" />
<specifiedPickupDirectory pickupDirectoryLocation="c:\temp\" />
</smtp>
Thanks in advance!
I thought it might be helpful for someone who will be seeking the answer for the same question. I managed to overcome the issue writing a reflection of System.Net.Mail.MailMessage.Send() as follows.
public static string SaveToTemp(this MailMessage Message)
{
SmtpClient smtp = new SmtpClient();
string fileName = Guid.NewGuid().ToString() + ".eml";
string fileNameWithPath = Path.Combine(smtp.PickupDirectoryLocation, fileName);
Assembly assembly = typeof(SmtpClient).Assembly;
Type _mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
using (FileStream _fileStream = new FileStream(fileNameWithPath, FileMode.Create))
{
// Get reflection info for MailWriter contructor
ConstructorInfo _mailWriterContructor =
_mailWriterType.GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new Type[] { typeof(Stream) },
null);
// Construct MailWriter object with our FileStream
object _mailWriter = _mailWriterContructor.Invoke(new object[] { _fileStream });
// Get reflection info for Send() method on MailMessage
MethodInfo _sendMethod =
typeof(MailMessage).GetMethod(
"Send",
BindingFlags.Instance | BindingFlags.NonPublic);
// Call method passing in MailWriter
_sendMethod.Invoke(
Message,
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new object[] { _mailWriter,true, true },
null);
// Finally get reflection info for Close() method on our MailWriter
MethodInfo _closeMethod =
_mailWriter.GetType().GetMethod(
"Close",
BindingFlags.Instance | BindingFlags.NonPublic);
// Call close method
_closeMethod.Invoke(
_mailWriter,
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new object[] { },
null);
}
return fileNameWithPath;
}
Caller:
var mail = Mailer.Example_Mail()
mail.To.Add("some#somedomain.com");
var fileName = mail.SaveToTemp(); // Instead of mail.Send();

Where to find the System.Net.Mail.MailWriter class? [duplicate]

So, the below code used to work in .NET 4 to get a System.Net.Mail.MailMessage object as a MemoryStream, however with the release of .NET 4.5 beta a runtime exception occurs.
Assembly assembly = typeof(SmtpClient).Assembly;
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
using (MemoryStream stream = new MemoryStream())
{
ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);
object mailWriter = mailWriterContructor.Invoke(new object[] { stream });
MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true }, null);
.....
}
Runtime exception occurs on sendMethod.Invoke().
Managed to figure out how to get this working again in .NET 4.5 beta. The private API Send() method in MailMessage has changed to: internal void Send(BaseWriter writer, bool sendEnvelope, bool allowUnicode)
Please find updated code below.
Assembly assembly = typeof(SmtpClient).Assembly;
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
using (MemoryStream stream = new MemoryStream())
{
ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);
object mailWriter = mailWriterContructor.Invoke(new object[] { stream });
MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true, true }, null);
.....
}
This might be usable if you don't want to go with unsupported hacks and don't mind extra performance hit.
public static class MailMessageExtensions
{
public static string RawMessage(this MailMessage m)
{
var smtpClient = new SmtpClient { DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory };
using (var tempDir = new TemporaryDirectory())
{
smtpClient.PickupDirectoryLocation = tempDir.DirectoryPath;
smtpClient.Send( m );
var emlFile = Directory.GetFiles( smtpClient.PickupDirectoryLocation ).FirstOrDefault();
if ( emlFile != null )
{
return File.ReadAllText( emlFile );
}
else
return null;
}
return null;
}
}
class TemporaryDirectory : IDisposable
{
public TemporaryDirectory()
{
DirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory( DirectoryPath );
}
public string DirectoryPath { get; private set; }
public void Dispose()
{
if ( Directory.Exists( DirectoryPath ) )
Directory.Delete( DirectoryPath, true );
}
}
for checking if extra boolean i use :
If _sendMethod.GetParameters.Length = 2 Then
_sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True}, Nothing)
Else
_sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True, True}, Nothing)
End If
The proposed solution with the extra TRUE works beautifully.
I started to getting the error while running my project in VS2012 even though I am not using .net 4.5 but 4.0 in all my libraries.
The error only happens on the machine where you have installed VS2012, looks like VS2012 makes reference to .net 4.5 while you are debugging. When you deploy and run the application in clients running .net 4.0 everything works fine.
Thus : If you run 4.0 - do not add the extra TRUE, if you run 4.5 add it.
We fought with the mail message conversion for a long time. Ultimately the solution was to use MimeKit.
var memoryStream = new MemoryStream();
var mimeMessage = MimeMessage.CreateFromMailMessage(message);
mimeMessage.WriteTo(memoryStream);
If you use the methods above you will get really close and it will work in most cultures but eventually the subject encoding will defeat you.
For those, who are struggling with mailWriterContructor being null in .NET 5 or facing Parameter count mismatch exception, take a closer look on my solution usable for any stream. Link here

Get current opened document

I'm trying to figure out how to get the currently opened document on Lotus Notes through C#, but I cannot. Even though I researched half a day on Google, I couldn't find anything useful.
With my code I get the view I want, the database I want, etc, but I just would like to get the opened document. I tried something like IsUIDocOpen, but none of the full collection contains it as true.
Does someone know if there is any different between an opened document and a non-opened document trough Domino API? My workaround is to get the subject of the email and the size of the email and compare each one and when it matches get the Entry ID and then get the information I need - but that takes too long, especially when the inbox is big.
Any suggestions?
Here is my code:
NotesSession session = new NotesSession();
session.Initialize(sPassword);
notedb = session.GetDatabase(server, filename, false);
if (notedb.IsOpen)
{
mailView = notedb.GetView("$Inbox");
mailDoc = mailView.GetLastDocument();
//mailDoc = mailView.GetDocumentByKey();
try
{
while (mailDoc != null)
{
NotesItem item = mailDoc.GetFirstItem("From");
if (item != null)
{
MessageBox.Show("From = " + item.Text);
}
}
}
}
Solution: should be something like: mailDoc = mailView.GetCurrentDocument(); // But obviously this method does not exist :D
=====================================================================================
Solution code:
Type NotesUIWorkspaceType = Type.GetTypeFromProgID("Notes.NotesUIWorkspace", true);
object workspace = Activator.CreateInstance(NotesUIWorkspaceType);
object uiDoc = NotesUIWorkspaceType.InvokeMember("CurrentDocument", BindingFlags.GetProperty, null, workspace, null);
Type NotesUIDocument = uiDoc.GetType();
object Subject = NotesUIDocument.InvokeMember("FieldGetText", BindingFlags.InvokeMethod, null, uiDoc, new Object[] { "Subject" });
string subject = "test";
NotesUIDocument.InvokeMember("FieldSetText", BindingFlags.InvokeMethod, null, uiDoc, new Object[] { "Subject", subject });
object Body = NotesUIDocument.InvokeMember("FieldGetText", BindingFlags.InvokeMethod, null, uiDoc, new Object[] { "Body" });
What you actually need is the Notes OLE classes.
The C# Interop classes are based on the Notes COM classes. The COM classes only have access to the "back end". I.e., the root object is Lotus.NotesSession, and all the classes work against data stored in .NSF files. They have no access to anything in the Notes UI.
The Notes OLE classes have access to both the "back end", with the root object Notes.NotesSession, and the "front end" with the root object Notes.NotesUIWorkspace. As you can tell by the name of that class, it's the front end classes that give you access to elements of the Notes client UI.
Note the subtle difference: the prefix for the OLE classes is "Notes.", instead of the prefix "Lotus." for the COM classes.
In old-style VB late binding, the OLE classes are instantiated this way:
CreateObject("Notes.NotesUIWorkspace")
I'm not sure how that translates into C#.
Anyhow, once you have the NotesUIWorkspace object, the currently opened document is available with the NotesUIWorkspace.CurrentDocument method.
IF you are using the Interop Classes you need to use NotesUIView.CurrentView.Documents to get what you want... see here.
You'll need to get the NotesUIWorkspace first, then use the CurrentDocument property
NotesUIWorkspace workspace = new NotesUIWorkspace();
NotesUIDocument uidoc = workspace.CurrentDocument();

C# Late binding command COM obj commands fails due to desired copy back setting

I know I should write this in Vb.net but for my own reasons I'm attempting late binding against a Com Object in C# .net framework 3.5. The error I'm getting is "DISP_E_BADCALLEE" and I'm only getting that based on when the copybacks I want are set to true. Am I dealing with a security issue? My research lead me to try dropping "[assembly: AllowPartiallyTrustedCallers()]" in the AssemblyInfo file but that didn't do the trick. I dropped some details in a condensed version of the code below. It's failing on the 4th step. I'd appreciate your help. Thanks.
//step 1
_atlDirectorObject = Activator.CreateInstance(Type.GetTypeFromProgID("atlDirectorObject.atlDirector"));
//step 2
object[] parms = { "3270", 1, true, true, 0, _atl3270Tool, _ErrMsg };
Boolean[] CopyBack2 = new Boolean[7];
CopyBack2[5] = true; //atl3270Tool
CopyBack2[6] = true; //ErrMsg
Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateCall(_atlDirectorObject, _atlDirectorObject.GetType(), "CreateTool", parms, null, null, CopyBack2, false);
_atl3270Tool = parms[5];
//step 3
// now using _atl3270Tool we navigate mainframe screens
object[] parms3 = { Screen, SubScreen, _Number, _ErrMsg };
Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateCall(_atl3270Tool, _atl3270Tool.GetType(), "ShowScreen", parms3, null, null, null, false);
//>>>>>>>>>>>>>>CODE FAILS ON THIS STEP<<<<<<<<<<<<
//step 4
Boolean[] CopyBack4 = new Boolean[5];
CopyBack4[3] = true; //screentext
CopyBack4[4] = true; //_errmsg
object ScreenText = String.Empty;
object[] parms4 = { Row, Col, Length, ScreenText, _ErrMsg};
Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateCall(_atl3270Tool, _atl3270Tool.GetType(), "ReadScreen", parms4, null, null, CopyBack4, false)
// if it were working the code would read screen data into parms4[3] object
// If CopyBack4[3 and/or 4] are set to true = error
// Is just CopyBack4[0,1,2] are true = no error but no result
//Errors out with "Invalid callee. (Exception from HRESULT: 0x80020010 (DISP_E_BADCALLEE))"
return Convert.ToString(parms4[3]);
ScreenText should be set to null, not string.empty

Error when I use Open Office API in ASP .NET

I use an Open Office API in my ASP .NET application for reading text content from *.doc files.
public static bool getTextV2(string siteURL, string[] search)
{
//Create a new ServiceManager Type object
Type tServiceManager = Type.GetTypeFromProgID("com.sun.star.ServiceManager", true);
//Create a new ServiceManager Com object using our
//ServiceManager type object
object oServiceManager = System.Activator.CreateInstance(tServiceManager);
//Create our Desktop Com object
object oDesktop = Invoke(oServiceManager, "createinstance",
BindingFlags.InvokeMethod,
"com.sun.star.frame.Desktop");
//Create an array for our load parameter
Object[] arg = new Object[4];
arg[0] = siteURL;
arg[1] = "_blank";
arg[2] = 0;
arg[3] = new Object[] { };
//Create our new blank document
object oComponent = Invoke(oDesktop,
"loadComponentFromUrl",
BindingFlags.InvokeMethod,
arg
);
//Create an empty array for the getText method
arg = new Object[0];
//Get our Text Com object
Object oText = Invoke(oComponent,
"getText",
BindingFlags.InvokeMethod,
arg
);
Object Text = Invoke(oText,
"getString",
BindingFlags.InvokeMethod,
arg
);
string content = Text.ToString();
content = content.ToLower();
bool flag = true;
foreach (string current in search)
{
if (!content.Contains(current)) flag = false;
}
arg = new Object[0];
Invoke(oComponent,
"dispose",
BindingFlags.InvokeMethod,
arg
);
return flag;
}
public static object Invoke(object obj, string method, BindingFlags binding, params object[] par)
{
return obj.GetType().InvokeMember(method, binding, null, obj, par);
}
But I have the following error:
Retrieving the COM class factory for component with CLSID {82154420-0FBF-11D4-8313-005004526AB4} failed due to the following error: 80080005.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Runtime.InteropServices.COMException: Retrieving the COM class factory for component with CLSID {82154420-0FBF-11D4-8313-005004526AB4} failed due to the following error: 80080005.
Source Error:
Line 56: //Create a new ServiceManager Com object using our
Line 57: //ServiceManager type object
Line 58: object oServiceManager = System.Activator.CreateInstance(tServiceManager);
Line 59: //Create our Desktop Com object
Line 60: object oDesktop = Invoke(oServiceManager, "createinstance",
So, there is a error at line 58.
Any ideas?
Quick guess: could it be that user under which your are running app never opened OpenOffice before. Try login as that user and run OpenOffice it will ask few questions, then close OpenOffice and logout. Then try to run your app again....

Categories

Resources