Here is my situation.
I've made a Application Windows Form which is editing an Excel sheet depending on what the user is doing (buttons / toggle / text box / etc..).
Once the edtion is complete the new Excel file is generated.
My programm then selects a range of cells, and copies it.
It goes to Clipboard.
I've done multiple tests (if(){}else{} / saving into a .jpg / etc..) and everything is checked true.
I don't want to ATTACH my image.
I don't want to save it, even temporarily, then paste it in the body through the saved .jpg file.
I "just" want to make a Ctrl+V, into my Outlook eMail Body.
here's how i get the image from my clipboard ("MessageBody" is declared at the top as a public string so that i can call it through different regions):
public void ReadData()
{
Excel excel = new Excel(#"E:\c#\Project#XX\Resources\TEST.xlsx", 1);
excel.CopyRange(0, 0, 32, 12);
IDataObject iData = Clipboard.GetDataObject();
if (iData.GetDataPresent(DataFormats.Bitmap))
{
Bitmap MessageBody = (iData.GetData(DataFormats.Bitmap, true) as Bitmap);
//pbx.Image = Image;
//image.Save(#"E:\c#\Project#XX\Resources\bitmap1.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
//MessageBody = image;
}
excel.Close();
excel.Quit();
}
Here's my message building code :
private void flatCustButton013_Click(object sender, EventArgs e)
{
ReadData();
string MessageSubject = $"SomeSubject";
Outlook.MailItem newMail = (Outlook.MailItem)application.CreateItem(Outlook.OlItemType.olMailItem);
newMail.Subject = MessageSubject;
newMail.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
MessageHTMLBody = "<html><body>SomeText.<img src="cid:MessageBody"</img></body></html>";
newMail.HTMLBody = MessageHTMLBody;
//newMail.Body = System.Windows.Input.ApplicationCommands.Paste;
//newMail.Display(false);
//newMail.BodyFormat = Outlook.OlBodyFormat.olFormatHTML; ;
//newMail.HTMLBody = System.Windows.Input.ApplicationCommands.Paste;
//newMail.Body = MessageBody;
newMail.To = ToAddress;
newMail.SentOnBehalfOfName = FromAddress;
newMail.CC = CcAddress;
newMail.BCC = BccAddress;
//System.Windows.Input.ApplicationCommands.Paste;
//wrdEdit = application._Inspector.WordEditor
newMail.Send();
}
In the previous version, people had to open an Excel file, change the Cells manually, and then click a button (with a macro in VB).
It would open a Outlook eMail item (display:true) with the image pasted already, then just needed to click "Send" and it was ok.
My 2.0 version is meant to automatise this by just click a "Send //Generated Excel Sheet//"Button.
Here's the macro code in VB :
Sub SendMail()
Dim Messager As New Outlook.Application
Dim Mail As Outlook.MailItem
Dim WrdEdit
Range("my_range").CopyPicture
Set Messager = New Outlook.Application
Set Mail = Messager.CreateItem(olMailItem)
With Mail
.To = "some folks"
.CC = "some folks"
'.BCC = ""
.Subject = "SomeSubject"
.Display
.HTMLBody = Mess + .HTMLBody
End With
Set WrdEdit = Messager.ActiveInspector.WordEditor
WrdEdit.Application.Selection.Paste
Set WrdEdit = Nothing
Set Messager = Nothing
Set Mail = Nothing
ActiveWorkbook.Close SaveChanges:=False
End Sub
But i don't get why i would pass through the Inspector (which i have no knowledge about) if i'm succeeding in retrieving the clipboard image.
I found a way by adding an attachment from a local saved file to the mail and displaying it into the body with HTML.
Like so :
newMail.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
Outlook.Attachment attachment = newMail.Attachments.Add(#"path.imageformat", Outlook.OlAttachmentType.olEmbeddeditem, null, $"someTitle");
string imagecid = "whatever";
attachment.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3613041E", imagecid);
newMail.HTMLBody = String.Format("<body><img src=\"cid:{0}\"></body>", imageCid);
the "http://schemas.microsoft.com/mapi/proptag/0x3712001E" code is here : http://www.outlookcode.com/codedetail.aspx?id=1915.
And it works.
However, i really don't like having urls in my code, is there another way ?
And also, it looks like whatever i put into my 'imagecid' string nothing changes.
I'm trying hard to understand what i did with the code, and not just letting it work as it is.
Looks like i need some authorisation from Office to work through an application ? And especially when i want to paste image in a body (which can contain other stuff than just pixel data).
If i did not need this autorization with the VB script, i suppose, it is because i had to "physicly/humanly" press "Send Mail" button in Outlook ?
Are there any other possibilities ? I'm still using a file though to paste in my body, can't i just make a Ctrl+C Ctrl+V programmatically ? :(
Related
I am trying to paste text, image and pdf documents to clipboard first and then to whatsapp textbox control.
1) For Text.
Clipboard.SetText(message);
Ctrl + v pastes the text into the whatsapp textbox.
2) For Image
var imagePath = "E:\Downloads_SSD\goodmorning.jpg";
Clipboard.SetImage(Image.FromFile(#imagePath ));
Ctrl + v pastes the Image into whatsapp.
3) For Documents.
StringCollection paths = new StringCollection();
paths.Add(#"E:\Downloads_SSD\Hatred-in-the-belly_-Politics-behind-the-appropriation-of-Dr-Ambedkars-writings.pdf");
Clipboard.SetFileDropList(paths);
Ctrl + v works when copying into a folder, but does not work for copying into whatsapp message textbox.
How do I make it to work in case of pdf documents.
To directly answer your question - copying & pasting .pdf files from the clipboard will not work - that is not the way WhatsApp web does it. Surprisingly there is a way to achieve something similar which does not involve pasting.
Basically you have to trigger a click event on that clip-button and run some javascript.
You did not mention what programing language you are using so I will use what works for me. In this example I am using C# PuppeteerSharp.
// load your pdf bytes here
byte[] fileBytes = YourFunctionToGetFileBytes("https://link-to-pdf-file");
if (fileBytes != null)
{
// first, we get the clip-button
var clipImage = _whatsAppPage.QuerySelectorAsync("div[data-testid=\"conversation-clip\"]").Result;
// focus and click on that button
await clipImage.FocusAsync();
Thread.Sleep(600);
await clipImage.ClickAsync();
Thread.Sleep(600);
// wait for the documents button to become available
await _whatsAppPage.WaitForSelectorAsync("li[data-testid=\"mi-attach-document\"]");
// this is where the real magic happens
// see the next block of code for full details
await SetPdfBlobFile(fileBytes, "test-file-name.pdf", "document.querySelector(\"li[data-testid='mi-attach-document']\").querySelector(\"input[type='file']\")");
Thread.Sleep(600);
// after the pdf is finally set, we need to wait for the send button to become available
await _whatsAppPage.WaitForSelectorAsync("div[role=\"button\"][aria-label=\"Send\"]");
var sendButtonPic = _whatsAppPage.QuerySelectorAsync("div[role=\"button\"][aria-label=\"Send\"]").Result;
Thread.Sleep(600);
await sendButtonPic.ClickAsync();
Thread.Sleep(1600);
}
If you inspect the "documents" button, you will see it contains a hidden input button which is in reality the one responsible for loading your files. My "SetPdfBlobFile" function is simply targeting that input button.
Please note that the next code block is basically setting our .pdf file into that hidden "input" file element. You must understand that it is not possible to simply set the "value" field of the input button to the path of your .pdf file. But it is possible to set a .pdf file from a blob. Here's the code for accomplishing that:
private async Task<Newtonsoft.Json.Linq.JToken> SetPdfBlobFile(byte[] pdfBytes, string fileName, string selector)
{
var pasteResults = await _whatsAppPage.EvaluateFunctionAsync(#"(pdfBytes, fileName) => {
return new Promise(async (resolve, reject) => {
// logic to create our blob
const base64Response = await fetch(pdfBytes);
const pdfBlob = await base64Response.blob();
try {
const fileInput = " + selector + #";
const dataTransfer = new DataTransfer();
// create a new file using our pdf blob
const file = new File([pdfBlob], fileName, { type: 'application/pdf' });
// add our file to the hidden input button
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
// our file has been added to the hidden input button
// but lastly we need to trigger a 'change' event
// so WhatsApp loads the send button
fileInput.dispatchEvent(new window.Event('change', { bubbles: true }));
resolve(true);
} catch (ex) {
resolve(ex.message);
}
});
}"
, "data:application/pdf;base64," + Convert.ToBase64String(pdfBytes), fileName);
return pasteResults;
}
That's it! Have fun sending .pdf files!
I'm trying to solve a little stuck in my code. I'm sending email via Domino server, all mails were successfully sent, but mail is not visible in Sent email in Lotus Notes at all. Can you help me? Thanks
NotesSession notesSession = new NotesSession();
notesSession.Initialize(passw);
NotesDatabase nd = notesSession.GetDatabase("","names.nsf", bCreateonfail: false);
if (!nd.IsOpen)
{
nd.Open();
}
NotesDocument notesDocument = nd.CreateDocument();
notesDocument.SaveMessageOnSend = true;
notesDocument.ReplaceItemValue("Form", "Main Topic");
// set notes memo fields (To: CC: Bcc: Subject etc)
notesDocument.ReplaceItemValue("SendTo", emailSup);
notesDocument.ReplaceItemValue("CopyTo", copyTo);
// Subject is the name of pdf file without .pdf
notesDocument.ReplaceItemValue("Subject", pdfFiles[i].Remove(pdfFiles[i].Length - 4, 4));
// Create the body of the email. This allows you to use the appendtext
NotesRichTextItem richTextItem = notesDocument.CreateRichTextItem("Body");
//Path of attachment (pdf file)
string AttachPath = #"c:\...\PDF\" + pdfFiles[i];
string txtPath = #"c:\...\emailtxt.txt";
System.IO.StreamReader txt = new System.IO.StreamReader(txtPath, Encoding.GetEncoding("windows-1250"));
// Add email text from txt file.
richTextItem.AppendText(txt.ReadToEnd());
// Attach file to e-mail
NotesEmbeddedObject obj_notesEmbeddedObject = richTextItem.EmbedObject(EMBED_TYPE.EMBED_ATTACHMENT, "", AttachPath, "Attachment");
notesDocument.SaveMessageOnSend = true;
notesDocument.Save(true, false);
notesDocument.Send(false);
You are creating a database object:
NotesDatabase nd = notesSession.GetDatabase("","names.nsf", bCreateonfail: false);
Then you are creating a document object in that database object:
NotesDocument notesDocument = nd.CreateDocument();
And then you are saving the document object in that database object:
notesDocument.Save(true, false);
Do you see the problem?
You are saving the document in names.nsf on the local machine where your code is running. If you look in a view in names.nsf that selects #All, you will find it there.
I am working with Microsoft Visual Studio 2015 and I am trying to convert something from list form to string form. I have found some solutions for similar problems but not this one in particular.
I would like to eventually have this code work:
Dim info As Byte() = New UTF8Encoding(True).GetBytes(Utilities.GetEmailInfo(msg.Message).Attachments)
My end game is to take the text from Attachments and write it to a file. If I use some of the other data types listed below such as ToData, the file turns out properly but I encounter an error with the code above because GetBytes cannot get the text from a list. Is there another function I could use to get the text from the list?
The class that I need to convert contains the following:
Public Class EmailInfo
Public FromData As String = vbNullString 'FROM:
Public ToData As String = vbNullString 'TO:
Public DateData As String = vbNullString 'DATE:
Public SubjectData As String = vbNullString 'SUBJECT:
Public MessageBody As EmailItem 'contents of message body
Public AlternateViews As New Collections.Generic.List(Of EmailItem) 'list of alternate views
Public Attachments As New Collections.Generic.List(Of EmailItem) 'list of attachments
End Class
The resource that I want to access is EmailInfo.Attachments. This resource is stored as a list of type EmailItem. The code for this type is as follows:
Public Class EmailItem
Public ContentType As String = vbNullString 'CONTENT-TYPE data
Public ContentTypeData As String = vbNullString 'filename or text encoding
Public ContentTypeDataIsFilename As Boolean = False 'True if ContentTypeData specifies a filename
Public ContentEncoding As String = vbNullString 'CONTENT-TRANSFER-ENCODING data
Public ContentBody As String = vbNullString 'raw data of block
End Class
I have tried using some code such as String.Join but I end up with a blank string.
Please pardon my ignorance as I am new to VB.
Thank you all for all of your help!
Ryan
This no trivial task. Attachments could be any number of proprietary formats: ".pdf", ".doc", ".xls", ".ppt", ".csv", ".vsd", ".zip", ".rar", ".txt", ".html", ".proj", etc, etc , etc.
Good news is all the work has already been done for you and I will show you how to generically read almost any file format and extract the text in this answer:
Generically read any file format and convert it to .txt format
Make sure you read the Info to set it up paragraph.
So go ahead and reference TikaOnDotnet & TikaOnDotnet.TextExtractor to your project using NuGet (Tools menu > NuGet Package Manager).
I am assuming you have written code to extract the email attachments, using an Outlook Add-In MSDN How to: Programmatically Save Attachments from Outlook E-Mail Items or just an app that uses Outlook via Interop, eg:
In C#:
private TextExtractor _textExtractor;
private string _attachmentTextFilepath = #"c:\temp\EmailAttachmentText.txt";
static void IterateMessages(Outlook.Folder folder)
{
var fi = folder.Items;
if (fi != null)
{
foreach (Object item in fi)
{
Outlook.MailItem mi = (Outlook.MailItem)item;
var attachments = mi.Attachments;
if (attachments.Count != 0)
{
for (int i = 1; i <= mi.Attachments.Count; i++)
{
//Save email attachments
mi.Attachments[i].SaveAsFile(#"C:\temp\" + mi.Attachments[i].FileName);
//Use TIKA to read the contents of the file
TextExtractionResult textExtractionResult = _textExtractor.Extract(#"C:\temp\" + mi.Attachments[i].FileName);
//Save attachment text to a txt file
File.AppendAllText(_attachmentTextFilepath, textExtractionResult.Text);
}
}
}
}
}
In VB.Net:
Private _textExtractor As TextExtractor
Private _attachmentTextFilepath As String = "c:\temp\EmailAttachmentText.txt"
Private Shared Sub IterateMessages(folder As Outlook.Folder)
Dim fi = folder.Items
If fi IsNot Nothing Then
For Each item As [Object] In fi
Dim mi As Outlook.MailItem = DirectCast(item, Outlook.MailItem)
Dim attachments = mi.Attachments
If attachments.Count <> 0 Then
For i As Integer = 1 To mi.Attachments.Count
'Save email attachments
mi.Attachments(i).SaveAsFile("C:\temp\" + mi.Attachments(i).FileName)
'Use TIKA to read the contents of the file
Dim textExtractionResult As TextExtractionResult = _textExtractor.Extract("C:\temp\" + mi.Attachments(i).FileName)
'Save attachment text to a txt file
File.AppendAllText(_attachmentTextFilepath, textExtractionResult.Text)
Next
End If
Next
End If
End Sub
I am trying to make a button or hyperlink to open local html file. I have tried everything but none of them are not working. have tried to use LinkButton, Button and Hyperlink. Below is the code what I have tried.
byte[] buffer = (byte[])data.GetValue(i);
File.WriteAllBytes(#"C:\test.html", buffer);
//LinkButton sysInfo = new LinkButton();
//sysInfo.Text = "Please click to see more info";
//sysInfo.CommandName = "ID";
//sysInfo.CommandArgument = id.ToString();
//sysInfo.Command += new CommandEventHandler(sysInfo_Click);
//sysInfo.Click += new EventHandler(this.sysInfo_Click);
HyperLink link = new HyperLink();
link.Target = "_blank";
link.Text = "Please click to see more information";
link.Attributes.Add("onclick", "window.open('" + ResolveUrl("file:///c:/test.html") + "');");
link.NavigateUrl = ResolveUrl("file:///c:/test.html");
fieldCl.Controls.Add(link);
//sysInfo.Click += new EventHandler(sysInfo_Click);
//sysInfo.CommandName = "ID";
//sysInfo.CommandArgument = id.ToString();
//sysInfo.Command += new CommandEventHandler(sysInfo_Click);
What I was trying to do is there are bytes for html in sql server and retrieve the bytes to create file on C: local. Then I want to open the local html file on new window.
It seems ok to retrieve bytes and create a file. But when I used the hyperlink, it does not do anything. I heard that it won't allow me to open the local file. So I have tried to use the button instead but when I used a button and create Click event, it didnt even go into the Click event.
If there is anyway to just open the file without saving, that would be great but if can't, that's fine.
I would just get your link into a string. It would just need to be: c:\\test.html
Once you get your link into a string I would try: System.Diagnostics.Process.Start(link);
If that is not working, I would place a break in your code and grab the content of the string - paste that into your address bar and see what happens.
I am saving the attachments from a mail item. I am checking first the body format so that I only get true attachments. Please ignore the if else statements below with only comments as the next statements. I'll code that once I fix my problem. Now, my problem is I am getting this error when getting the filename of the attachment of a RichText body format mail. All is well in plain and HTML format.
'currentMailItem.Attachments[1].FileName' threw an exception of type 'System.Runtime.InteropServices.COMException'
base {System.Runtime.InteropServices.ExternalException}: {"Outlook cannot perform this action on this type of attachment."}
public static void SaveData(MailItem currentMailItem)
{
if (currentMailItem != null)
{
string PR_ATTACH_METHOD = "http://schemas.microsoft.com/mapi/proptag/0x37050003";
string PR_ATTACH_FLAGS = "http://schemas.microsoft.com/mapi/proptag/0x37140003";
if (currentMailItem.Attachments.Count > 0)
{
for (int i = 1; i <= currentMailItem.Attachments.Count; i++)
{
var attachMethod = currentMailItem.Attachments[i].PropertyAccessor.GetProperty(PR_ATTACH_METHOD);
var attachFlags = currentMailItem.Attachments[i].PropertyAccessor.GetProperty(PR_ATTACH_FLAGS);
if (currentMailItem.BodyFormat == OlBodyFormat.olFormatPlain)
//no attachment is inline
else if (currentMailItem.BodyFormat == OlBodyFormat.olFormatRichText)
{
if (attachMethod == 6)
//attachment is inline
else
//attachment is normal
}
else if (currentMailItem.BodyFormat == OlBodyFormat.olFormatHTML)
{
if (attachFlags == 4)
//attachment is inline
else
//attachment is normal
}
currentMailItem.Attachments[i].SaveAsFile(#"C:\TestFileSave\" + currentMailItem.Attachments[i].FileName);
}
}
}
}
For the RTF format, you have embedded OLE objects used by the RTF body to show the attachment preview and to edit it in-place. You can either filter out such attachment by the attachment type (Attachment.Type == olAttachmentType.olOLE (6)) or extract the attachment data using either
Extended MAPI (C++ or Delphi only) - call IAttach::OpenProperty(PR_ATTACH_DATA_OBJ, IID_IStorage, ...) to open the attachment data as IStorage and then extract file data from one of the IStorage streams. The exact stream name and format depends on the particular application used to create it (Word, Adobe, Paintbrush, Excel, etc.). You can see the data in OutlookSpy (I am its author): select a message with an attachment like that, click IMessage button on the OutlookSpy ribbon, go to the GetAttachmentTable tab, double click on the attachment, select the PR_ATTACH_DATA_OBJ property, right click, select OpenProperty, select IID_IStorage.
Redemption (any language - I am also its author): its RDOAttachment object is smart enough to extract the actual file data for the OLE attachments when you call SaveAsFile or access the FileName property.
This may be old question ...
I have seen similar issue. If image is attached in the body of rich text formatted mailItem then I get similar exception.
However, if the attachment is not image, say a .doc extension then I don't see the error. As far as I understand the issue is with getting attached image's file name. Some how Outlook doesn't know how to get in body attached image's file name from the rich text formatted email item.