I am currently developing a small application in C# . Net allowing to perform different tasks, but here in one of my tasks I have to open a new default mail from Lotus. However I don’t find much documentation on it, so I’m a little lost and that’s why I’m in your hands :/
So I just need to be able to open Lotus with a new default email.
Thank in advance ;)
I suggest you use the selenium web driver to access their website and automatically create an email account.
Manipulating the application it self can be quite complex you can use the .NET windows Input Simulator Nugat package to simulate user input to the application or write directly to memory using the windows API.
I don't think there is a supported way of doing that by Lotus.
But if you can use Gmail there is a Gmail API
https://developers.google.com/gmail/api/guides/
Use the .NET httpClient to interact with it.
And to create a Gmail account
https://developers.google.com/admin-sdk/directory/v1/guides/manage-users
public void ComposeMemo(String sendto, String subject, String body)
{
//BLOC1 instantiate a Notes session and workspace
Type NotesSession = Type.GetTypeFromProgID("Notes.NotesSession");
Type NotesUIWorkspace = Type.GetTypeFromProgID("Notes.NotesUIWorkspace");
Object sess = Activator.CreateInstance(NotesSession);
Object ws = Activator.CreateInstance(NotesUIWorkspace);
//BLOC2 open current user's mail file
String mailServer = (String)NotesSession.InvokeMember("GetEnvironmentString", BindingFlags.InvokeMethod, null, sess, new Object[] { "MailServer", true });
String mailFile = (String)NotesSession.InvokeMember("GetEnvironmentString", BindingFlags.InvokeMethod, null, sess, new Object[] { "MailFile", true });
NotesUIWorkspace.InvokeMember("OpenDatabase", BindingFlags.InvokeMethod, null, ws, new Object[] { mailServer, mailFile });
Object uidb = NotesUIWorkspace.InvokeMember("GetCurrentDatabase", BindingFlags.InvokeMethod, null, ws, null);
Object db = NotesUIWorkspace.InvokeMember("Database", BindingFlags.GetProperty, null, uidb, null);
Type NotesDatabase = db.GetType();
//BLOC3 compose a new memo
Object uidoc = NotesUIWorkspace.InvokeMember("ComposeDocument", BindingFlags.InvokeMethod, null, ws, new Object[] { mailServer, mailFile, "Memo", 0, 0, true });
Type NotesUIDocument = uidoc.GetType();
NotesUIDocument.InvokeMember("FieldSetText", BindingFlags.InvokeMethod, null, uidoc, new Object[] { "EnterSendTo", sendto });
NotesUIDocument.InvokeMember("FieldSetText", BindingFlags.InvokeMethod, null, uidoc, new Object[] { "Subject", subject });
NotesUIDocument.InvokeMember("FieldSetText", BindingFlags.InvokeMethod, null, uidoc, new Object[] { "Body", body });
//BLOC4 bring the Notes window to the front
String windowTitle = (String)NotesUIDocument.InvokeMember("WindowTitle", BindingFlags.GetProperty, null, uidoc, null);
Interaction.AppActivate(windowTitle);
}
The BLOC1 perform, it open the application but from BLOC2 the application no longer responds but I don’t know why, more in BLOC4 the word Interaction is not recognized
Related
I am trying to follow the approach suggested in a post by Allan Eagle in code-project. This same approach was working fine up until .NET Core 3.1, but not with .NET 5.0 . Here is the save method I created,
private void Save(MailMessage message, string filePath)
{
var assembly = typeof(SmtpClient).Assembly;
var mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
var mailWriterContructor = mailWriterType.GetConstructors(bindingFlags)[0];
var mailWriter = mailWriterContructor.Invoke(new object[] { fileStream });//<-- This line throws error saying parameter mismatch
var sendMethod = typeof(MailMessage).GetMethod("Send", bindingFlags);
sendMethod.Invoke(message, bindingFlags, null, new[] { mailWriter, true, true }, null);
var closeMethod = mailWriter.GetType().GetMethod("Close", bindingFlags);
closeMethod.Invoke(mailWriter, bindingFlags, null, new object[] { }, null);
}
}
I checked all the available underlying invoke methods and tried working with them passing needed parameters but non of them worked for me.
Error Message: "Parameter count mismatch."
Inner Exception: null
Stack Trace:
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.ConstructorInfo.Invoke(Object[] parameters)
at..//user written line info
Any help to solve this issue or new approach to achieve same thing using System.Net.Mail will be highly appreciated.
To explicitly give you an answer based on first comment,
var encodeForTransport = false;
var mailWriter = mailWriterContructor.Invoke(new object[] { fileStream, encodeForTransport });
As I mentioned in the comments, this article is obsolete in 2020. Despite the date, it was actually written in 2009 and stopped working in 2014, when some of the internal methods changed signature. In any case, the System.Net.Mail namespace shouldn't be used because, as Microsoft strongly warns:
Important
We don't recommend that you use the SmtpClient class for new development because SmtpClient doesn't support many modern protocols. Use MailKit or other libraries instead. For more information, see SmtpClient shouldn't be used on GitHub.
MailKit (or rather the MimeKit library it's built upon) already supports saving and loading Mail messages. From Q: How do I save messages? the answer is a simple:
message.WriteTo("message.eml");
You can use MailKit's POP3 or IMAP4 clients to retrieve messages. The DownloadMessages example show how to download messages from GMail and save them:
using (var client = new Pop3Client (new ProtocolLogger ("pop3.log"))) {
client.Connect ("pop.gmail.com", 995, SecureSocketOptions.SslOnConnect);
client.Authenticate ("username", "password");
for (int i = 0; i < client.Count; i++) {
var message = client.GetMessage (i);
// write the message to a file
message.WriteTo (string.Format ("{0}.msg", i));
// mark the message for deletion
client.DeleteMessage (i);
}
client.Disconnect (true);
}
I'm referring this thread to refresh the windows explorer, I want to refresh some windows only, which means I want to filter the opened windows according to their title or path. Let me copy the code from that thread for more clarification:
Guid CLSID_ShellApplication = new Guid("13709620-C279-11CE-A49E-444553540000");
Type shellApplicationType = Type.GetTypeFromCLSID(CLSID_ShellApplication, true);
object shellApplication = Activator.CreateInstance(shellApplicationType);
object windows = shellApplicationType.InvokeMember("Windows", System.Reflection.BindingFlags.InvokeMethod, null, shellApplication, new object[] { });
Type windowsType = windows.GetType();
object count = windowsType.InvokeMember("Count", System.Reflection.BindingFlags.GetProperty, null, windows, null);
for (int i = 0; i < (int)count; i++)
{
object item = windowsType.InvokeMember("Item", System.Reflection.BindingFlags.InvokeMethod, null, windows, new object[] { i });
Type itemType = item.GetType();
string itemName = (string)itemType.InvokeMember("Name", System.Reflection.BindingFlags.GetProperty, null, item, null);
if (itemName == "Windows Explorer")
{
// Here I want to check whether this window need to be refreshed
// based on the opened path in that window
// or with the title of that window
// How do I check that here
itemType.InvokeMember("Refresh", System.Reflection.BindingFlags.InvokeMethod, null, item, null);
}
}
What I understood from the above code is: By using this line windowsType.InvokeMember("Item", System.Reflection.BindingFlags.InvokeMethod, null, windows, new object[] { i }); we will get the current window object, and then we are using .InvokeMember("Name".. to get the name of that object, like wise what should I pass to InvokeMember method to get the path of that object or the title of that window? or can anyone tell me the possible alternative values for "Name" in the above statement?
What I'm expecting is some code like the following:
string itemPath = (string)itemType.InvokeMember("Something here", System.Reflection.BindingFlags.GetProperty, null, item, null);
OR
string itemTitle = (string)itemType.InvokeMember("Something here", System.Reflection.BindingFlags.GetProperty, null, item, null);
I can give you more information if you need, expecting expert's suggestion to solve this issue,
Thanks in advance
This is the way you had to write late-bound COM client code in the Bad Old Days. Considerable pain and suffering to get it going, what is in the snippet is not close yet. I'll first propose a very different way to do this, there just isn't any point in doing it late-bound since these COM objects are available on any Windows version and are never going to change anymore. The "Embed Interop Types" feature supported since VS2010 removes any good reason to avoid it.
Project > Add Reference > COM tab. Tick "Microsoft Internet Controls" and "Microsoft Shell Controls and Automation". Now you can write it early-bound, nice and compact with all the benefits of IntelliSense to help you find the correct members and avoid typos:
var shl = new Shell32.Shell();
foreach (SHDocVw.InternetExplorer win in shl.Windows()) {
var path = win.LocationURL;
if (!path.StartsWith("file:///")) continue;
path = System.IO.Path.GetFullPath(path.Substring(8));
if (path.StartsWith("C")) win.Refresh();
}
A slightly silly example, it refreshes any Explorer window who's displayed path is located on the C drive. Note how the Path property is not useful to discover what is displayed, LocationURL is needed. You might have to find the distinction between Internet Explorer and Windows Explorer windows (aka "File Explorer"), albeit that IE can also display directory content so I think this is the most correct version.
If you really want to do this late-bound then use the dynamic keyword to minimize the suffering. In this case almost identical:
dynamic shl = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
foreach (var win in shl.Windows()) {
string path = win.LocationURL;
if (!path.StartsWith("file:///")) continue;
path = System.IO.Path.GetFullPath(path.Substring(8));
if (path.StartsWith("C")) win.Refresh();
}
Answering your question explicitly, use "LocationURL".
I try to get the content ID's of the attachments of mails in outlook. I use Add-In Express for my Add-In, but it has no function to get it.
I know of the Redemption library, but it's not free and thus not an option for me.
In Outlook 2007+ there is the PropertyAccessor, which should me allow to do it, but I can't get it to work.
Here my code:
string uniqueId = "";
object props = a.GetType().InvokeMember("PropertyAccessor", BindingFlags.Public | BindingFlags.GetField | BindingFlags.GetProperty, null, a, null);
object[] args = new object[1];
args[0] = #"urn:schemas:mailheader:content-id";
object value = props.GetType().InvokeMember("GetProperty", BindingFlags.Public | BindingFlags.InvokeMethod, null, props, args);
if ((string)value != null) {
uniqueId = (string)value;
}
"a" is an attachment gotten from an Add-In Express MailItem.
It just throws the following exception: "Exception has been thrown by the target of an invocation."
Can someone help me?
I tried your URN as well and it did not work.
Using DASL, this works: http://schemas.microsoft.com/mapi/proptag/0x3712001F
Outlook Spy has a free trial period. I would download that in the meantime (which is where I found this value). And even when it expires, this valuable tool is less than Redemption itself.
I have a program that does batch processing on some drawings. One of the drawings throws an exception "Error Decrypting Data" when I try to open it. This drawing in particular was generated by a third-party tool other than AutoCAD. In addition, this problem only occurs in AutoCAD 2011. In AutoCAD 2010 it prompts the user that the file was generated outside of AutoCAD but they can click and the batch will continue. I've tried opening it using both the managed .NET API and the COM Interop API but both give the same error.
Here is a post from the AutoCAD formus though it didn't provide me with a solution:
http://forums.autodesk.com/t5/NET/Error-Decrypting-Data-Acad-2011/td-p/2661762/highlight/true
Managed API
string drawingFilePath = #"C:\Drawings\MyDrawing.dwg";
Application.DocumentManager.Open(drawingFilePath, false);
COM Interop
string drawingFilePath = #"C:\Drawings\MyDrawing.dwg";
Object comAutoCAD = Application.AcadApplication;
Object comDocuments = comAutoCAD.GetType().InvokeMember("Documents", BindingFlags.GetProperty, null, comAutoCAD, new object[] { });
Object comDocument = comDocuments.GetType().InvokeMember("Open", BindingFlags.InvokeMethod, null, comDocuments,
new object[] { drawingFilePath, false, Type.Missing });
Document.FromAcadDocument(comDocument);
Someone from the AutoCAD forums posted an answer that works for me.
http://forums.autodesk.com/t5/NET/Error-Decrypting-Data-Acad-2011/td-p/2661762/page/2
Here is an example:
const string systemVar_DwgCheck = "DWGCHECK";
Int16 dwgCheckPrevious = (Int16)Application.GetSystemVariable(systemVar_DwgCheck);
Application.SetSystemVariable(systemVar_DwgCheck, 2);
Document document = Application.DocumentManager.Open(#"C:\Drawings\MyDrawing.dwg", false);
// Do stuff...
Application.SetSystemVariable(systemVar_DwgCheck, dwgCheckPrevious);
I'm using MSWord interop to check grammar/spell in my application. I'm using these steps to do this:
Create a new Single Thread Apartment to not lock my application's form
Disable the input of my application
Using reflection (to be MSOffice version independent)
I'm using this code to open Word:
objWord = System.Activator.CreateInstance(Type.GetTypeFromProgID("Word.Application"));
Object objDocuments = objWord.GetType().InvokeMember("Documents", BindingFlags.GetProperty, null, objWord, null);
objDoc = objDocuments.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, objDocuments, null);
objContent = objDoc.GetType().InvokeMember("Content", BindingFlags.GetProperty, null, objDoc, null);
IDataObject oldObjData = Clipboard.GetDataObject();
Clipboard.SetDataObject(text);
objContent.GetType().InvokeMember("Paste", BindingFlags.InvokeMethod, null, objContent, null);
objDoc.GetType().GetMethod("CheckGrammar").Invoke(objDoc, null);
objWord.GetType().GetProperty("Visible").SetValue(objWord, false, null);
objContent.GetType().InvokeMember("Cut", BindingFlags.InvokeMethod, null, objContent, null);
IDataObject objData = Clipboard.GetDataObject();
objDoc.GetType().GetProperty("Saved").SetValue(objDoc, true, null);
objDoc.GetType().GetMethod("Close").Invoke(objDoc, new Object[] { null, null, null });
objWord.GetType().GetMethod("Quit").Invoke(objWord, new Object[] { null, null, null });
But when I call this, only in Windows Vista, the SpellCheck window opens in the back of my application, and I need to use ALT+TAB to show the Word's window.
Anybody had this problem or have a suggestion how to solve?
I tried to call
objDoc.GetType().GetMethod("Activate").Invoke(objDoc, null);
but it doesn't work. Other "Focus" methods neither.
Thanks
Try calling Activate on Word's Application object. (Not the Document object)
EDIT: Try calling it before displaying the spell-check dialog.