I wrote some code to read emails from Outlook Inbox and collect attachments.
This is working just fine. I am using Outlook 2019/Office 365.
I can use both Mail.SenderEmailAddress or Mail.Sender.Address to get the email address of the sender.
When deploying my application to another computer with Outlook 2016, I get this error:
EOleError: Method 'Sender' not supported by automation object
Same for Mail.SenderEmailAddress
Outlook 2016 or 2019 have the same code stream. See Outlook versions, build numbers and other trivia.
Could you help understanding why I get such error on my client's computer and how can I fix that problem?
Are you aware of a free/commercial library or components that could do that smoothly?
This is somehow linked with my other question about Sending Outlook Email with Delphi.
try
Outlook:=GetActiveOleObject('Outlook.Application') ;
except
Outlook:=CreateOleObject('Outlook.Application') ;
end;
try
oNameSpace := Outlook.GetNamespace('MAPI');
oNameSpace.Logon;
Inbox:= oNameSpace.GetDefaultFolder(6);
iNbMail:= Inbox.Items.Count;
for i:= iNbMail downto 1 do
begin
if VarIsNull(Inbox.Items[i]) or VarIsEmpty(Inbox.Items[i]) then
Continue;
Mail:= Inbox.Items[i];
EmailAddress:= Mail.Sender.Address;
// EmailAddress:= Mail.SenderEmailAddress;
UnReadFlag:= Mail.UnRead;
iNbAttach := Mail.Attachments.Count;
for j := iNbAttach downto 1 do
begin
Attachment:= Mail.Attachments[j];
if ExtractFileExt(Attachment.FileName) = '.pdf' then
begin
SaveName:= TPath.Combine(InboxFolder, Attachment.FileName);
Attachment.SaveAsFile(SaveName);
end;
end;
Mail.UnRead:= False;
end;
finally
Outlook:= Unassigned;
oNameSpace:= Unassigned;
Inbox:= Unassigned;
Mail:= Unassigned;
end;
Inbox folder in Outlook is not restricted to contain only mail messages. When iterating through its items you can encounter various item classes like MailItem, PostItem, MeetingItem, TaskRequestItem and many others. All of these items support different sets of properties and not all of them have Sender, SenderEmailAddress or Attachments property. If you're interested only in mail items then you need to check item's Class property:
const
olMail = $0000002B;
{ ... }
for i := iNbMail downto 1 do
begin
Mail := Inbox.Items[i];
if Mail.Class <> olMail then
Continue;
{ here we can assume we're working with MailItem instance }
end;
I doubt that Outlook ever returns null or empty item, so the check you do in your code is pointless. If you're interested in other item classes then check out OlObjectClass enumeration.
I'm not sure why you prefer to use late-binding, because Delphi already comes with imported type library Outlook2010.pas to automate Outlook in strongly typed manner. The library is located in OCX\Servers subfolder of installation folder. In case you need to support pre-2010 Outlook versions you can use unit OutlookXP or even Outlook2000 instead. The code fore iterating mail items using the type library could look like this:
uses
System.SysUtils, System.Variants, Winapi.ActiveX, Outlook2010;
function GetOutlookApplication: OutlookApplication;
var
ActiveObject: IUnknown;
begin
if Succeeded(GetActiveObject(OutlookApplication, nil, ActiveObject)) then
Result := ActiveObject as OutlookApplication
else
Result := CoOutlookApplication.Create;
end;
procedure ProcessInboxItems;
var
Outlook: OutlookApplication;
Inbox: Folder;
Index: Integer;
LItems: Items;
LMailItem: MailItem;
begin
Outlook := GetOutlookApplication;
Outlook.Session.Logon(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
Inbox := Outlook.Session.GetDefaultFolder(olFolderInbox);
LItems := Inbox.Items;
for Index := LItems.Count downto 1 do
begin
if Supports(LItems.Item(Index), MailItem, LMailItem) then
begin
{ do whatever you wish with LMailItem }
end;
end;
end;
Related
If I start Outlook manually and then run my program that talks to it through OLE, all my (renamed) categories with colors are visible:
If I do not start Outlook manually, but have it startup from code, then start it up manually as well, I suddenly miss my renamed category info:
In an individual appointment we see that the renamed categories still exist:
but the link between these category names and some 'master list' (?) that defines the colors seems to be missing.
Luckily my code does not seem to have a problem, the categories it retrieves for an appointment are the renamed ones.
But I would like to change the behavior under point 2 - when my program is running and I open Outlook it's very nice for debugging if I see the correct data in Outlook ;-)
Here is the code that starts Outlook:
function TDataModuleSyncOutlook.ConnectToOutlook(AUserSMTP: String = ''): Boolean;
var
lRecipient,
lVar : OleVariant;
lLog,
lLoginSMTP: String;
begin
Result := false;
FWasCreated := False; // Breakpoint 'Ignore subsequent exceptions'
try
FOutlookApp := GetActiveOleObject(scxOutlookApp); // Application object. This code fails if Outlook is not yet running...
Result := True;
except
try
FOutlookApp := CreateOleObject(scxOutlookApp); // ... and then this creates the Outlook instance
FWasCreated := True;
Result := True;
except
on E:Exception do TSyncLogger.LogError(E.Message);
end;
end;
if Result then // Breakpoint 'Handle subsequent exceptions'
begin
FNameSpace := FOutlookApp.GetNamespace(scxNameSpace);
// Oplossing uit http://stackoverflow.com/questions/18053110/retrieve-outlook-logged-in-user-smtp-address-after-connecting-through-ole/
lVar := FOutlookApp.Session; // NameSpace object for the current session
if not VarIsClear(lVar) then lVar := lVar.CurrentUser; // Recipient object for the currently logged-on user
if not VarIsClear(lVar) then lVar := lVar.AddressEntry; // AddressEntry object for the recipient
if not VarIsClear(lVar) then lVar := lVar.GetExchangeUser; // Returns an ExchangeUser object that represents the AddressEntry
if not VarIsClear(lVar) then lVar := lVar.PrimarySmtpAddress; // String representing the SMTP address for the ExchangeUser
if not VarIsClear(lVar) then
begin
lLoginSMTP := FOutlookApp.Session.CurrentUser.AddressEntry.GetExchangeUser.PrimarySmtpAddress;
TSyncLogger.LogDetail('Primary Exchange SMTP address detected as: ' + lLoginSMTP);
end
else
begin
TSyncLogger.LogError(sErrNoExchangeAccount);
DisConnectFromOutlook;
Exit;
end;
if LowerCase(AUserSMTP) <> Lowercase(lLoginSMTP) then
begin // Open shared calendar for different user. This does not apply in my test case
lRecipient := FNameSpace.CreateRecipient(AUserSMTP);
try
FCalendarFolder := FNameSpace.GetSharedDefaultFolder(lRecipient, olFolderCalendar);
lLog := Format('Logging in as different user (%s), created recipient for %s, GetSharedDefaultFolder folder path = %s',[AUserSMTP,lRecipient.Address,FCalendarFolder.FolderPath]);
TSyncLogger.LogAlways(lLog);
except
on E:Exception do
begin
Result := false;
TSyncLogger.LogError(Format(sErrOpenGedeeldeAgenda,[AUserSMTP]));
end;
end;
end
else // ... otherwise open default calendar folder
begin
FCalendarFolder := FNameSpace.GetDefaultFolder(olFolderCalendar);
TSyncLogger.LogDetail('Opened default calendar folder, folder path = ' + FCalendarFolder.FolderPath);
end;
end;
FOleInitialized := Result;
if Result then TSyncLogger.LogDetail('Connected to Outlook') else TSyncLogger.LogAlways('Connection to Outlook failed');
end;
Any ideas/suggestions what to do?
Additional info:
Outlook 2007 connected to Exchange 2013 RTM, under Win7-64
I have several mail profiles set up for this computer, the one for connecting to Exchange 2013 is set up as the default profile that Outlook starts with. Every time Outlook starts (also from code) I get prompted for the password.
Keep in mind that the categories are stored on the per store basis. Do both items come from the same store? Or is one of them residing in a delegate mailbox?
Exchange Web Services has a ResolveNames() function that I can use to retrieve (among other things) the primary SMTP address for the Active Directory user that logged on to Exchange Server through EWS.
I am now programming through OLE against Outlook and would like the same functionality.
I have been browsing through the Outlook object model but can't find an appropriate object or method.
Does anyone know of an object/method that I can use to get the primary SMTP address?
Below is the current Delphi code that I use to connect to Outlook.
For the default user logging in (AUserSMTP='') it returns the OutlookApp COM Object (through GetActiveOleObject or CreateOleObject), a NameSpace (through GetNameSpace) and a Folder (through GetDefaultFolder) object, but I could not find where to go from there.
I thought lNameSpace.CurrentUser (a Recipient object) might lead somewhere, but its Address property only returns a string like '/o=TimeTell/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=developer' without email address...
Any suggestions about the route to take?
function TDataModuleSyncOutlook.ConnectToOutlook(AUserSMTP: String = ''): Boolean;
var
lNameSpace, lRecipient: OleVariant;
begin
Result := false;
FWasCreated := False;
try
FOutlookApp := GetActiveOleObject(scxOutlookApp);
Result := True;
except
try
FOutlookApp := CreateOleObject(scxOutlookApp);
FWasCreated := True;
Result := True;
except
on E:Exception do ...
end;
end;
if Result then
begin
lNameSpace := FOutlookApp.GetNameSpace(scxNameSpace);
if AUserSMTP <> '' then // This part not applicable to the question
begin // Open shared calendar als er een expliciete gebruiker is opgegeven...
lRecipient := lNameSpace.CreateRecipient(AUserSMTP);
try
FCalendarFolder := lNameSpace.GetSharedDefaultFolder(lRecipient, olFolderCalendar);
except
on E:Exception do ...
end;
end
else // ... anders de default calendar folder openen
FCalendarFolder := lNameSpace.GetDefaultFolder(olFolderCalendar);
end;
FOleInitialized := Result;
if Result then TSyncLogger.LogAlways('Connected to Outlook') else TSyncLogger.LogAlways('Connection to Outlook failed');
end;
Try to use Application.Session.CurrentUser.AddressEntry.GetExchangeUser.PrimarySmtpAddress (you would of course need to check for nulls).
As for the account order, you can either use Extended MAPI and IOlkAccountManager.GetOrder (you can play with that object in OutlookSpy (I am its author) if you click IOlkAccountManager button) or you can use Redemption (I am also its author) and its RDOSession.Accounts.GetOrder method (see http://www.dimastr.com/redemption/RDOAccounts.htm). The first account in the returned collection will be the default one.
I found it. I have to go through the Accounts object in the namespace:
for i := 1 to lNameSpace.Accounts.Count do
if lNameSpace.Accounts.Item[i].AccountType = olExchange then
begin
lAccount := lNameSpace.Accounts.Item[i];
Break;
end;
if VarIsClear(lAccount) then
begin
DisConnectFromOutlook;
Exit;
end;
lLoginSMTP := lAccount.SmtpAddress;
The only thing I would still like is to determine the default account.
How to get body texts of all e-mail messages from a certain IMAP mailbox in Delphi ? For example, from the INBOX mailbox ?
There are many ways to retrieve all body texts of all messages from the selected mailbox. I've used the one, where you iterate the mailbox and Retrieve every single message from the mailbox one by one. This way allows you to modify the code, so that you'll be able to break the loop when you need or e.g. replace Retrieve by RetrievePeek which won't mark the message as read on server like the first mentioned does. When the message is retrieved from server, all its parts are iterated and when it's the text part, its body is appended to a local S variable. After the iteration the S variable is added to the output BodyTexts string list. So, as the result you'll get string list collection where each item consists from the concatenated message's text part bodies and where each item means one message.
uses
IdIMAP4, IdSSLOpenSSL, IdText, IdMessage, IdExplicitTLSClientServerBase;
procedure GetGmailBodyTextParts(const UserName, Password: string;
BodyTexts: TStrings);
var
S: string;
MsgIndex: Integer;
MsgObject: TIdMessage;
PartIndex: Integer;
IMAPClient: TIdIMAP4;
OpenSSLHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
BodyTexts.Clear;
IMAPClient := TIdIMAP4.Create(nil);
try
OpenSSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
try
OpenSSLHandler.SSLOptions.Method := sslvSSLv3;
IMAPClient.IOHandler := OpenSSLHandler;
IMAPClient.Host := 'imap.gmail.com';
IMAPClient.Port := 993;
IMAPClient.UseTLS := utUseImplicitTLS;
IMAPClient.Username := UserName;
IMAPClient.Password := Password;
IMAPClient.Connect;
try
if IMAPClient.SelectMailBox('INBOX') then
begin
BodyTexts.BeginUpdate;
try
for MsgIndex := 1 to IMAPClient.MailBox.TotalMsgs do
begin
MsgObject := TIdMessage.Create(nil);
try
S := '';
IMAPClient.Retrieve(MsgIndex, MsgObject);
MsgObject.MessageParts.CountParts;
if MsgObject.MessageParts.TextPartCount > 0 then
begin
for PartIndex := 0 to MsgObject.MessageParts.Count - 1 do
if MsgObject.MessageParts[PartIndex] is TIdText then
S := S + TIdText(MsgObject.MessageParts[PartIndex]).Body.Text;
BodyTexts.Add(S);
end
else
BodyTexts.Add(MsgObject.Body.Text);
finally
MsgObject.Free;
end;
end;
finally
BodyTexts.EndUpdate;
end;
end;
finally
IMAPClient.Disconnect;
end;
finally
OpenSSLHandler.Free;
end;
finally
IMAPClient.Free;
end;
end;
This code requires OpenSSL, so don't forget to put the libeay32.dll and ssleay32.dll libraries to a path visible to your project; you can download OpenSSL libraries for Indy in different versions and platforms from here.
1st off I am Still a little green to Delphi so this might be a "mundane detail" that's being over looked. [sorry in advance]
I have a need to create a TSQLDataset or TClientDataSet from an Oracle 11g cursor contained in a package. I am using Delphi XE2 and DBExpress to connect to the DB and DataSnap to send the data back to the client.
I'm having problems executing the stored procedure from the Delphi code.
Package Head:
create or replace
PACKAGE KP_DATASNAPTEST AS
procedure GetFaxData(abbr varchar2, Res out SYS_REFCURSOR);
END KP_DATASNAPTEST;
Package Body:
create or replace
PACKAGE body KP_DATASNAPTEST AS
procedure GetFaxData(abbr varchar2, Res out SYS_REFCURSOR)is
Begin
open Res for
SELECT Name,
Address1,
City,
fax_nbr
FROM name
JOIN phone on name.Abrv = phone.abrv
WHERE phone.fax_nbr is not null and name.abrv = abbr;
end;
END KP_DATASNAPTEST;
I have no problem executing this procedure in SQL Developer the problem resides in this code on the DataSnap server:
function TKPSnapMethods.getCDS_Data2(): OleVariant;
var
cds: TClientDataSet;
dsp: TDataSetProvider;
strProc: TSQLStoredProc;
begin
strProc := TSQLStoredProc.Create(self);
try
strProc.MaxBlobSize := -1;
strProc.SQLConnection:= SQLCon;//TSQLConnection
dsp := TDataSetProvider.Create(self);
try
dsp.ResolveToDataSet := True;
dsp.Exported := False;
dsp.DataSet := strProc;
cds := TClientDataSet.Create(self);
try
cds.DisableStringTrim := True;
cds.ReadOnly := True;
cds.SetProvider(dsp);
strProc.Close;
strProc.StoredProcName:= 'KP_DATASNAPTEST.GetFaxData';
strProc.ParamCheck:= true;
strProc.ParamByName('abbr').AsString:= 'ZZZTOP';
strProc.Open; //<--Error: Parameter 'Abbr' not found.
cds.Open;
Result := cds.Data;
finally
FreeAndNil(cds);
end;
finally
FreeAndNil(dsp);
end;
finally
FreeAndNil(strProc);
self.SQLCon.Close;
end;
end;
I have also tried assigning the param value through the ClientDataSet without any luck.
I would not be apposed to returning a TDataSet from the function if its easier or produces results. The data is used to populate custom object attributes.
As paulsm4 mentioned in this answer, Delphi doesn't care about getting stored procedure parameter descriptors, and so that you have to it by yourself. To get params of the Oracle stored procedure from a package, you can try to use the GetProcedureParams method to fill the list with parameter descriptors and with the LoadParamListItems procedure fill with that list Params collection. In code it might look like follows.
Please note, that following code was written just in browser according to documentation, so it's untested. And yes, about freeing ProcParams variable, this is done by the FreeProcParams procedure:
var
ProcParams: TList;
StoredProc: TSQLStoredProc;
...
begin
...
StoredProc.PackageName := 'KP_DATASNAPTEST';
StoredProc.StoredProcName := 'GetFaxData';
ProcParams := TList.Create;
try
GetProcedureParams('GetFaxData', 'KP_DATASNAPTEST', ProcParams);
LoadParamListItems(StoredProc.Params, ProcParams);
StoredProc.ParamByName('abbr').AsString := 'ZZZTOP';
StoredProc.Open;
finally
FreeProcParams(ProcParams);
end;
...
end;
I don't think Delphi will automagically recognize Oracle parameter names and fill them in for you. I think you need to add the parameters. For example:
with strProc.Params.Add do
begin
Name := 'abbr';
ParamType := ptInput;
Value := ZZZTOP';
...
end;
Some of our applications which work fine with different ways of email integration, using mailto:, simulated "Send To...", and SMTP in Windows 2000 and 2003 environments, now move to a new Windows 2008 system with Exchange 2010 and Outlook 2010 clients.
We have one use case where the application creates a new mail, sets recipient(s) and subject, adds one or more attachments and then opens it in the default mail client so it can be edited by the user before sending.
Do you know a solution which works in the new environment? Should we use a third party library? Or is there some OLE automation code available which is known to work, using Outlook.Application?
We use JclSimpleBringUpSendMailDialog from the jclMapi unit in the Jedi JCL library.
I once had an app where we built in a user option to specify whether they wanted to use SMTP or MAPI and then all sorts of mail server settings but the Jedi library call makes life so much easier. If end users have gone to the trouble of setting up all their settings in a MAPI client then why would they want to set them all up again in my/our software.
The trouble with the mailto:// stuff is that it's often not quite configurable enough or the mail client doesn't handle the parameters in the same/standard way - then users think your software's rubbish rather than believe they have a dodgy mail client.
So we just use the MAPI interface. Easy.
I use this unit - it credits Brian Long ages ago...
unit UArtMAPI;
interface
procedure ArtMAPISendMail(
const Subject, MessageText, MailFromName, MailFromAddress,
MailToName, MailToAddress: String;
const AttachmentFileNames: array of String);
implementation
uses
SysUtils,
Windows,
UArtLibrary,
Dialogs,
Forms,
MAPI;
procedure ArtMAPISendMail(
const Subject, MessageText, MailFromName, MailFromAddress,
MailToName, MailToAddress: String;
const AttachmentFileNames: array of String);
//Originally by Brian Long: The Delphi Magazine issue 60 - Delphi And Email
var
MAPIError: DWord;
MapiMessage: TMapiMessage;
Originator, Recipient: TMapiRecipDesc;
Files, FilesTmp: PMapiFileDesc;
FilesCount: Integer;
begin
FillChar(MapiMessage, Sizeof(TMapiMessage), 0);
MapiMessage.lpszSubject := PAnsiChar(AnsiString(Subject));
MapiMessage.lpszNoteText := PAnsiChar(AnsiString(MessageText));
FillChar(Originator, Sizeof(TMapiRecipDesc), 0);
Originator.lpszName := PAnsiChar(AnsiString(MailFromName));
Originator.lpszAddress := PAnsiChar(AnsiString(MailFromAddress));
// MapiMessage.lpOriginator := #Originator;
MapiMessage.lpOriginator := nil;
MapiMessage.nRecipCount := 1;
FillChar(Recipient, Sizeof(TMapiRecipDesc), 0);
Recipient.ulRecipClass := MAPI_TO;
Recipient.lpszName := PAnsiChar(AnsiString(MailToName));
Recipient.lpszAddress := PAnsiChar(AnsiString(MailToAddress));
MapiMessage.lpRecips := #Recipient;
MapiMessage.nFileCount := High(AttachmentFileNames) - Low(AttachmentFileNames) + 1;
Files := AllocMem(SizeOf(TMapiFileDesc) * MapiMessage.nFileCount);
MapiMessage.lpFiles := Files;
FilesTmp := Files;
for FilesCount := Low(AttachmentFileNames) to High(AttachmentFileNames) do
begin
FilesTmp.nPosition := $FFFFFFFF;
FilesTmp.lpszPathName := PAnsiChar(AnsiString(AttachmentFileNames[FilesCount]));
Inc(FilesTmp)
end;
try
MAPIError := MapiSendMail(
0,
Application.MainForm.Handle,
MapiMessage,
MAPI_LOGON_UI {or MAPI_NEW_SESSION},
0);
finally
FreeMem(Files)
end;
case MAPIError of
MAPI_E_AMBIGUOUS_RECIPIENT:
Showmessage('A recipient matched more than one of the recipient descriptor structures and MAPI_DIALOG was not set. No message was sent.');
MAPI_E_ATTACHMENT_NOT_FOUND:
Showmessage('The specified attachment was not found; no message was sent.');
MAPI_E_ATTACHMENT_OPEN_FAILURE:
Showmessage('The specified attachment could not be opened; no message was sent.');
MAPI_E_BAD_RECIPTYPE:
Showmessage('The type of a recipient was not MAPI_TO, MAPI_CC, or MAPI_BCC. No message was sent.');
MAPI_E_FAILURE:
Showmessage('One or more unspecified errors occurred; no message was sent.');
MAPI_E_INSUFFICIENT_MEMORY:
Showmessage('There was insufficient memory to proceed. No message was sent.');
MAPI_E_LOGIN_FAILURE:
Showmessage('There was no default logon, and the user failed to log on successfully when the logon dialog box was displayed. No message was sent.');
MAPI_E_TEXT_TOO_LARGE:
Showmessage('The text in the message was too large to sent; the message was not sent.');
MAPI_E_TOO_MANY_FILES:
Showmessage('There were too many file attachments; no message was sent.');
MAPI_E_TOO_MANY_RECIPIENTS:
Showmessage('There were too many recipients; no message was sent.');
MAPI_E_UNKNOWN_RECIPIENT:
Showmessage('A recipient did not appear in the address list; no message was sent.');
MAPI_E_USER_ABORT:
Showmessage('The user canceled the process; no message was sent.');
SUCCESS_SUCCESS:
Showmessage('MAPISendMail successfully sent the message.');
else
Showmessage('MAPISendMail failed with an unknown error code.');
end;
end;
end.
Maybe this can be useful for you. But I can't test it, because I'm thunderbird user.
I dug up this example. It's from 2002, so uses an older outlook version, but the idea should still be the same. It was used in an application to send newly registered users their userid and password (don't start me on that, it was just a way to restrict access to a site, nothing sensitive there). The messages are saved in the Outbox, not opened, but if you dig through the OleServer and Outlook8 (number will be higher by now), you should find ways to open the mail for user attention instead of saving it straight to the outbox.
The entire Outlook object model can be found on msdn: http://msdn.microsoft.com/en-us/library/aa221870%28v=office.11%29.aspx
Another good source for information about Office Automation are Deborah Pate's pages: http://www.djpate.freeserve.co.uk/Automation.htm
function TStapWerkt_data.GenerateMailsOutlook(SendDBF: boolean): boolean;
var
Outlook: TOutlookApplication;
olNameSpace: NameSpace;
MailIt: TMailItem;
AttachedFile: OleVariant;
i: integer;
emailaddress: string;
begin
Result := true;
Outlook := TOutlookApplication.Create( nil );
try
Outlook.ConnectKind := ckNewInstance;
try
Outlook.Connect;
try
olNameSpace := Outlook.GetNamespace('MAPI');
olNameSpace.Logon('', '', False, False);
try
if SendDBF then begin
MailIt := TMailItem.Create( nil );
MailIt.ConnectTo( Outlook.CreateItem( olMailItem ) as MailItem );
try
MailIt.Recipients.Add( 'info#bjmsoftware.com' );
MailIt.Subject := 'StapWerk.dbf';
MailIt.Body := 'Stap'#13#10;
AttachedFile := IncludeTrailingBackslash( ExtractFilePath(
Stap_db.Stap_Database.DatabaseName ) ) + 'stapwerk.dbf';
MailIt.Attachments.Add( AttachedFile, EmptyParam, EmptyParam, EmptyParam );
MailIt.Save;
finally
MailIt.Free;
end;
end;
for i := 0 to FNewUsers.Count - 1 do begin
MailIt := TMailItem.Create( nil );
MailIt.ConnectTo( Outlook.CreateItem( olMailItem ) as MailItem );
try
emailaddress := TStapper( FNewUsers.Items[i] ).Email;
if emailaddress = '' then begin
emailaddress := C_MailUnknownAddress;
end;
MailIt.Recipients.Add( emailaddress );
MailIt.Subject := C_MailSubject;
MailIt.Body := Format( C_MailBody,
[TStapper( FNewUsers.Items[i] ).UserId,
TStapper( FNewUsers.Items[i] ).Password] );
MailIt.Save;
finally
MailIt.Free;
end;
end;
finally
olNameSpace.Logoff;
end;
finally
Outlook.Disconnect;
// We kunnen ondanks ckNewInstance quit niet gebruiken
// Outlook lijkt de connectkind te negeren en hoe dan ook te koppelen
// naar een bestaande instantie. En die gooi je dan dus ook dicht met quit.
// Outlook.quit;
end;
finally
Outlook.free;
end;
except
on E: Exception do begin
Result := false;
end;
end;
end;
Maybe this is the easiest way to open an email window
System("mailto:someone#example.com?Subject=Hello%20again&body=your%20textBody%20here")