Link to home
Start Free TrialLog in
Avatar of enigmasolutions
enigmasolutions

asked on

tOLEContainer CreateOleObject tIDHTMLMessageBuilder and MS WORD as HTML Email Editor with email stored/retrieved from dataset

Hi All,

I am trying to create a delphi application that uses MS Word (preferably within a Delphi form) to create HTML emails (with embedded images) and then use SMTP (or Outlook) to send the emails.  I want to use this system to create one-off emails as well as bulk emails (via mail merge) - all integrated into my application.  

I have managed to get a lot of it working - which is a good start.

Note: I am using Delphi 2009.

This is a big question, so there is scope for a many correct answers, I plan to award points to all good answers, and increase points if necessary. I have attached a ZIP of the sample application - it is very concise and should help to understand this question.

I have a dataset with the following fields:
* EmailSubject                   tStringField
* EmailBody                       tMemoField
* EmailAttachments           tBlobField

My plan is to get Delphi to call MS Word to create an HTML email and then store the HTML in EmailBody and the Attachments as multiple memory streams within a single memory stream that is stored in EmailAttachments (Arguably I could go with a second table here).

IMPORTANT - My preferred method for creating and editing my HTML documents is via the InFormEditButton method.  Please focus on this.

So the following questions arise (in order of importance):
1) I want the InFormEditButton to open my HTML - but I can't get it to work?
2) In the EmailerWordEditorForm - why is there no Save/Update function in MS Word?
3) How do I retrieve changes as a result of the InFormEditButton?
4) In the EmailerWordEditorForm I want to create a button to drop place holder strings (eg <FirstName> that I can later perform search and replace for Mail Merge Purposes).
5) How can I change the InFromEditButton so I don't have to create a temportary file on the hard drive for teh image?

6) Why does the preview button display <\Div> in the preview screen?
7) Is there a way to get the Preview button to work withotu creatign teh temporary file?
8) Why does Button 4 have a bug?

9) With the NewButton and EditButton - is there a way to wait on teh Word App?
10) With the NewButton and EditButton - is there a way to avoid creating the temporary files?
11) How do I know WordApp has actually made changes?
12) How do I retrieve changes from WordApp?

13) I would like to see if I can get the OutlookButton to send my email via outlook - how?

14) Anyone wanting to have a go at tWordApplication is welcome also.

15) Pros & Cons of various methods appreciated

16) the EmailSMTP works well - enjoy & good luck...

In summary - I am looking for all constructive input that helps me (and the community) to understand OLE.

Below is the critical parts of the source code of the main form.  I have also attached the full sample application.  When I finally close this question I will upload the final source.


unit EmailerFormU;

//form header stuff here

procedure TEmailerForm.EditButtonClick(Sender: TObject);
var
  tf:TextFile;
  fs:TFileStream;
  tempPath:string;
  PathLen:DWORD;
  s:string;

  WordApp:OLEVariant;
begin
  SetLength(TempPath,MAX_PATH);
  PathLen:=GetTempPath(MAX_PATH, PChar(TempPath));
  SetLength(TempPath,PathLen);

  s:=ClientDataSet1.FieldByName('EmailBody').AsString;
  s:=ReplaceText(s,'TestImage.jpg',TempPath+'TestImage.jpg');

  AssignFile(tf, TempPath+'EmailBody.HTML');
  ReWrite(tf);
  Write(tf,s);
  CloseFile(tf);

  TBlobField(ClientDataSet1.FieldByName('EmailAttachments')).SaveToFile(TempPath+'TestImage.jpg');

  WordApp := CreateOleObject('Word.Application') ;
  wordApp.Visible:=true;
  WordApp.Documents.Open(TempPath+'EmailBody.HTML');
  WordApp:=Unassigned;
end;

procedure TEmailerForm.EmailButtonClick(Sender: TObject);
var
  tf:TextFile;
  fs:TFileStream;
  tempPath:string;
  PathLen:DWORD;
  HTMLBd:string;

  WordApp:OLEVariant;

  IdSMTP : TIdSMTP;
  IdMsg: TIdMessage;
  IdAttachment: TIdAttachmentFile;
  IMH:TIdMessageBuilderHtml;

begin
  SetLength(TempPath,MAX_PATH);
  PathLen:=GetTempPath(MAX_PATH, PChar(TempPath));
  SetLength(TempPath,PathLen);

  HTMLBd:=ClientDataSet1.FieldByName('EmailBody').AsString;
  HTMLBd:=ReplaceText(HTMLBd,'TestImage.jpg',TempPath+'TestImage.jpg');

  //AssignFile(tf, TempPath+'EmailBody.HTML');
  //ReWrite(tf);
  //Write(tf,s);
  //CloseFile(tf);

  TBlobField(ClientDataSet1.FieldByName('EmailAttachments')).SaveToFile(TempPath+'TestImage.jpg');


  IMH:=TIdMessageBuilderHtml.Create;
  IdMsg:=TIdMessage.Create(nil);
  IdSMTP:=TIdSMTP.Create(nil);
  try
    //IdAttachment:=TIDAttachmentFile.Create(IdMsg.MessageParts,Attachment);
    IdMsg.Date:=Now;
    IdMsg.Recipients.EMailAddresses:= uiEmailTo.Text;
    IdMsg.Subject:=  ClientDataSet1.FieldByName('EmailSubject').AsString;
    //IdMsg.Body.Text:= HTMLBd;
    IdMsg.From.Name:=     'Test HTML Emailer';
    IdMsg.From.Address:=  uiEmailTo.Text;
    //IdMsg.CCList.EmailAddresses:=GetAddressString(CCAddress);
    //IdMsg.BCCList.EmailAddresses:=GetAddressString(BCCAddress);
    //IdMsg.ReplyTo.EmailAddresses:=GetAddressString(ReplyToAddress);

    //IMH.PlainText.Text:='Plain Text';
    IMH.Html.Text:=HTMLBd;
    IMH.HtmlFiles.Add(TempPath+'TestImage.jpg');
    IMH.FillMessage(IdMsg);

    IdSMTP.Host:=     uiSMTPServer.Text;
    IdSMTP.Username:= uiSMTPUser.Text;
    IdSMTP.Password:= uiSMTPPassword.Text;
    IdSMTP.AuthType:= satNone;

    IdSMTP.Connect;
    IdSMTP.Send(IdMsg);
    IdSMTP.Disconnect;

  finally
    IMH.Free;
    IdMsg.Free;
    IdSMTP.Free;
  end;

end;

procedure TEmailerForm.FormCreate(Sender: TObject);
var
  BStream:tMemoryStream;
begin
  ClientDataSet1.CreateDataSet;
  ClientDataSet1.Append;
  ClientDataSet1.FieldByName('EmailSubject').AsString:='Test Email';
  ClientDataSet1.FieldByName('EmailBody').AsString:=Memo1.Text;

  BStream:=tMemoryStream.Create;
  try
    Image1.Picture.Graphic.SaveToStream(BStream);
    TBlobField(ClientDataSet1.FieldByName('EmailAttachments')).LoadFromStream(BStream);
  finally
    BStream.Free;
  end;

  ClientDataSet1.Post;
end;

procedure TEmailerForm.InFormEditButtonClick(Sender: TObject);
var
  tf:TextFile;
  fs:TFileStream;
  tempPath:string;
  PathLen:DWORD;
  s:string;
  f:TEmailerWordEditorForm;
  CreateInfo: tCreateInfo;
  WordApp:OleVariant;
begin
  SetLength(TempPath,MAX_PATH);
  PathLen:=GetTempPath(MAX_PATH, PChar(TempPath));
  SetLength(TempPath,PathLen);

  s:=ClientDataSet1.FieldByName('EmailBody').AsString;
  s:=ReplaceText(s,'TestImage.jpg',TempPath+'TestImage.jpg');

  AssignFile(tf, TempPath+'EmailBody.HTML');
  ReWrite(tf);
  Write(tf,s);
  CloseFile(tf);

  TBlobField(ClientDataSet1.FieldByName('EmailAttachments')).SaveToFile(TempPath+'TestImage.jpg');

  //This link explains how to load the OleContainer without having to create the file - but I couldn't get it to work
  //http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_21821816.html

  f:=TEmailerWordEditorForm.Create(self);
  try
    //f.OleContainer1.AllowInPlace:=true;
    //f.OleContainer1.AutoActivate:=aaManual;
    f.OleContainer1.CreateObject('Word.Document',false);
    WordApp:=f.OleContainer1.OleObject;
    WordApp.Open(TempPath+'EmailBody.HTML');
    //WordApp.FileOpen(TempPath+'EmailBody.HTML');
    //WordApp.Documents.Open(TempPath+'EmailBody.HTML');
    //f.OleContainer1.OleObject.Documents.FileOpen(TempPath+'EmailBody.HTML');
    //f.OleContainer1.OleObject.Documents.Open(TempPath+'EmailBody.HTML');
    //f.OleContainer1.LoadFromFile(TempPath+'EmailBody.HTML');
    //f.OleContainer1.DoVerb(ovShow);
    f.ShowModal;
  finally
    f.Free;
  end;

end;

procedure TEmailerForm.NewButtonClick(Sender: TObject);
var
  tf:TextFile;
  fs:TFileStream;
  tempPath:string;
  PathLen:DWORD;
  s:string;

  WordApp:OLEVariant;
begin
  SetLength(TempPath,MAX_PATH);
  PathLen:=GetTempPath(MAX_PATH, PChar(TempPath));
  SetLength(TempPath,PathLen);

  AssignFile(tf, TempPath+'EmailBody.HTML');
  ReWrite(tf);
  Write(tf,'This is a New Email');
  CloseFile(tf);


  WordApp := CreateOleObject('Word.Application') ;
  wordApp.Visible:=true;
  WordApp.Documents.Open(TempPath+'EmailBody.HTML');
  WordApp:=Unassigned;

  //WordApp.wait;

  //WordDocument := WordApplication.Documents.Add;
  //WordApplication.Selection.TypeText('Hello world') ;
  //WordDocument.SaveAs(FileName := 'C:\Doc.Doc',
  //                     AddToRecentFiles := False) ;
  //WordApplication.Quit(False)


end;

procedure TEmailerForm.OutlookButtonClick(Sender: TObject);
var
  tf:TextFile;
  fs:TFileStream;
  tempPath:string;
  PathLen:DWORD;
  s:string;

  OutApp:OLEVariant;
begin
  SetLength(TempPath,MAX_PATH);
  PathLen:=GetTempPath(MAX_PATH, PChar(TempPath));
  SetLength(TempPath,PathLen);

  s:=ClientDataSet1.FieldByName('EmailBody').AsString;
  s:=ReplaceText(s,'TestImage.jpg',TempPath+'TestImage.jpg');

  AssignFile(tf, TempPath+'EmailBody.HTML');
  ReWrite(tf);
  Write(tf,s);
  CloseFile(tf);

  TBlobField(ClientDataSet1.FieldByName('EmailAttachments')).SaveToFile(TempPath+'TestImage.jpg');

  OutApp := CreateOleObject('Outlook.Application') ;
  OutApp.Visible:=true;
  //OutApp.Documents.Open(TempPath+'EmailBody.HTML');
  //OutApp.Documents.Send;
  OutApp:=Unassigned;
end;

procedure TEmailerForm.Button4Click(Sender: TObject);
var
  BStream:tMemoryStream;
begin
  BStream:=tMemoryStream.Create;
  try
    TBlobField(ClientDataSet1.FieldByName('EmailAttachments')).SaveToStream(BStream);
    Image2.Picture.Graphic.LoadFromStream(BStream);
  finally
    BStream.Free;
  end;
end;

procedure TEmailerForm.PreviewButtonClick(Sender: TObject);
var
  tf:TextFile;
  fs:TFileStream;
  tempPath:string;
  PathLen:DWORD;
  s:string;
  sl:tstringlist;
begin
  SetLength(TempPath,MAX_PATH);
  PathLen:=GetTempPath(MAX_PATH, PChar(TempPath));
  SetLength(TempPath,PathLen);

  s:=ClientDataSet1.FieldByName('EmailBody').AsString;
  s:=ReplaceText(s,'TestImage.jpg',TempPath+'TestImage.jpg');

  sl:=tstringlist.Create;
  sl.Text:=s;
  sl.SaveToFile(TempPath+'EmailBody.HTML');
  sl.Free;

  //AssignFile(tf, TempPath+'TestBody.HTML');
  //ReWrite(tf);
  //Write(tf,s);
  //CloseFile(tf);

  TBlobField(ClientDataSet1.FieldByName('EmailAttachments')).SaveToFile(TempPath+'TestImage.jpg');

  WebBrowser1.Navigate(TempPath+'EmailBody.HTML');
end;

end.

Open in new window

EmailerWordApp.zip
Avatar of enigmasolutions
enigmasolutions

ASKER

Woops, one more thing...

The EmailSMTP does not quite work.  The problem is that the image that is embedded in the HTML of the email is ONLY visible on my PC.  Because it has a link to the image in my Windows Temp directory.

So one more question - how do I get the image to be embedded?
I just came accross a website with quite a lot of information on the subject (but it is all quite old) - see here:

http://www.djpate.freeserve.co.uk/AutoWord.htm 

But, at the very bottom is a link with source code that adds functionality to tOleContainer.

http://www.djpate.freeserve.co.uk/wordcntr.zip 

Perhaps this will solves some of the issues.
For the record we thought about using a delphi HTML Editor component (the first one below seems the best).  But I feel that it might be better to use MS Word if possible (more functionaliity / more widely used).

http://www.profgrid.com/dhtmledit.html 
http://delphi2.software.informer.com/download-delphi-component-wysiwyg-html-editor/ 
http://coding.derkeiler.com/Archive/Delphi/borland.public.delphi.thirdpartytools.general/2004-10/0380.html 
http://delphi2.software.informer.com/download-delphi-html-editor-vcl-component/
We believe we will have a fix for the EmailSMTP button soon.  We were getting an attachment (with extension .dat) instead of having the image embedded.  Anyway our research tells us that this was a bug in the version of INDY we have that came with Delphi 2009.  We are in the process of upgrading INDY (which is not that easy).  My guess is that for anyone running Delphi 10 or later it will work.
Woo hoo - We got EmailSMTP button working.  Now to get tOLEContainer working which should be easy.  We will sort that out on Monday (if everything goes as planned).

FYI - We tried using Report Builder with Pragnaan Exports to create an HTML report.  Unfortunately I worked out that I can’t use Report Builder and Pragnaan to send HTML emails because MS Word & Outlook do not support the "position" HTML command.  Refer to this article:

http://superuser.com/questions/146453/css-absolute-position-dont-work-in-ms-word 

We are going to use Report Builder anyway to send RTF reports (eg for customer statements).  When we do this we will use the same technique to send RTF emails.  We will attached code for this later.
OK, I have managed to resolve issues 6 onwards.  I will soon attach source code.

I am still struggling with tOLEContainer.

Specifically with the following commands:

    f.OleContainer1.CreateObject('Word.Document',false);
    f.OleContainer1.LoadFromFile(TempPath+'EmailBody.HTML');

I get "Invalid Stream Format" on the second command.

Why?
ASKER CERTIFIED SOLUTION
Avatar of Ephraim Wangoya
Ephraim Wangoya
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I will award points to contributors.  But for now my most pressing issue is:

In the EmailerWordEditorForm I want to create a button to drop place holder strings (eg <FirstName> that I can later perform search and replace for Mail Merge Purposes).

The problem is the form behaves strangely - ie the MS Word Ribbon dissapears when you lose focus.
I have posted a separate question on this to see if I get any better response.

See here: https://www.experts-exchange.com/questions/26847218/How-to-stop-MS-Word-Ribbon-disappearing-when-using-tOLEContainer-and-switching-focus.html 

As you can see from the above I have a work around.  So we are nearing teh finish of this post.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial

This question should be added to the knowledgebase - it has some good info.  I am prepared to award points.  But I am dissappointed with the overall response.  Which is why I left it open - hoping for more responses.

Points go to ewangoya for the contribution.  

Although B grade because I would have liked some of the other parts of the question answered considering the maximum award of 500 points.

I also set some of my own contrinbutions as soltions because they are in-fact the soluution to much of this question.