This is the code with which we start Outlook and open a shared calendar folder for a user defined by primary SMTP address AUserSMTP:
const
scxOutlookApp = 'outlook.application';
scxNameSpace = 'MAPI';
olFolderCalendar = $00000009; // Outlook default calendar folder
function TDataModuleSyncOutlook.ConnectToOutlook(AUserSMTP: String = ''): Boolean;
var
lRecipient,
lVar : OleVariant;
lLog,
lLoginSMTP: String;
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 TSyncLogger.LogError(E.Message);
end;
end;
if Result then
begin
FNameSpace := FOutlookApp.GetNamespace(scxNameSpace);
// From http://stackoverflow.com/questions/18053110/retrieve-outlook-logged-in-user-smtp-address-after-connecting-through-ole/
lLog := Format('Connected to Outlook; Application.DefaultProfilename: %s, Application.Name: %s, Application.Version: %s, NameSpace.CurrentProfileName: %s, NameSpace.ExchangeMailboxServerName: %s, NameSpace.Type: %s',
[FOutlookApp.DefaultProfileName,
FOutlookApp.Name,
FOutlookApp.Version,
FNameSpace.CurrentProfileName,
FNameSpace.ExchangeMailboxServerName,
FNameSpace.Type]);
TSyncLogger.LogDebug(lLog);
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.LogDebug('Primary Exchange SMTP address detected as: ' + lLoginSMTP);
end
else
begin
TSyncLogger.LogError('No Exchange Server account found in Outlook');
DisConnectFromOutlook;
Exit;
end;
if LowerCase(AUserSMTP) <> Lowercase(lLoginSMTP) then
begin // Open shared calendar if it is a different user
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.LogDebug(lLog);
except
on E:Exception do
begin
Result := false;
TSyncLogger.LogError(Format('Cannot open shared calendar for %s',[AUserSMTP])); // <== error here
end;
end;
end
else // ... otherwise open default calendar folder
begin
FCalendarFolder := FNameSpace.GetDefaultFolder(olFolderCalendar);
TSyncLogger.LogDebug('Opened default calendar folder, folder path = ' + FCalendarFolder.FolderPath);
end;
end;
FOleInitialized := Result;
if Result then TSyncLogger.LogDebug('Connected to Outlook') else TSyncLogger.LogAlways('Connection to Outlook failed');
end;
We have an issue at a company that changed domain names, and I think that is the reason:
Initially user#olddomain.com had granted editor access to admin#olddomain.com for their calendar (or the administrator had taken access).
The company changed name and users primary SMTP addresses were changed from #olddomain.com to #newdomain.com.
The code runs on a machine where admin#olddomain.com is still the logged in user, so Outlook starts with that account.
When admin#olddomain.com starts Outlook, he can edit and delete appointments in the calendar of user#newdomain.com.
However, our code gives the 'Cannot open shared calendar' error (see 'error here') when we call the above procedure with either user#olddomain.com or user#newdomain.com.
While debugging the issue with an admin, he changed the primary SMTP for our test user back to user#olddomain.com, but the code still failed.
Questions:
Can anyone confirm that my suspicion of a cross-domain issue is correct? Does this sound familiar?
Could it be that our opening the default olFolderCalendar no longer is the correct way (because of the domain name change)?
Other things that are suspect in the code?
Other things that we could test or try to make this work?
Two things just cross my mind while writing this and I have not tested/verified them yet:
I am assuming that admin#newdomain.com is now also the primary SMTP for admin, like everyone else.
What if the Outlook profile on that machine is changed so that admin#newdomain.com starts Outlook?
Related
I'm trying to send response back to servers requesting digest access authentication
....
FResponseHeader.Text := FResponseText;// received header.
FResponseHeader.ProcessHeaders;
....
WriteLn(FResponseHeader.WWWAuthenticate); //WWW-Authenticate: Digest realm="xxxx.com", nonce="fq1uvocyzvr17e6a5syproazd5phwdvhvlc5", stale=false, algorithm=MD5, qop="auth"
LIdAuthentication := TIdDigestAuthentication.Create;
try
LIdAuthentication.Username := FUser;
LIdAuthentication.Password := FPass;
LIdAuthentication.Uri := FURI;
LIdAuthentication.Method := GetMsgTypeString(FResponseHeader.RequestMethods);
LIdAuthentication.Params.Values['Authorization'] := FResponseHeader.WWWAuthenticate;
LIdAuthentication.AuthParams.AddValue('Digest', FResponseHeader.WWWAuthenticate);
for I := 0 to LIdAuthentication.Steps do
LIdAuthentication.Next;
Result := LIdAuthentication.Authentication;
finally
LIdAuthentication.Free;
end;
I got 401 from the server.
What is the correct way to create the Authorization Header ?
TIdDigestAuthentication (and other TIdAuthentication-derived classes) are intended to be used with TIdHTTP, not standalone.
If you are using TIdHTTP to communicate with a server, you do not need to manage Digest authentication manually at all. If the server requests Digest in its WWW-Authenticate header, and if IdAuthenticationDigest (or IdAllAuthentications) is in your uses clause, then TIdHTTP will automatically send a Digest response for you. The only thing you have to concern yourself with doing is:
set the TIdHTTP.Request.Username and TIdHTTP.Request.Password properties for the initial authentication attempt.
set a TIdHTTP.OnAuthorization event handler to handle the possibility of the server rejecting the current Username/Password so you can supply new values for retry, optionally after prompting the user.
optionally set a TIdHTTP.OnSelectProxyAuthorization event handler to choose which authentication scheme to use if multiple schemes are requested by the server, and/or if you want to control which scheme takes priority over others.
For example:
uses
..., IdHTTP, IdAuthenticationDigest;
...
IdHTTP1.OnAuthorization := AuthRequested;
IdHTTP1.Request.Username := ...; // initial username
IdHTTP1.Request.Password := ...; // initial password
IdHTTP1.Get(...);
...
procedure TMyClass.AuthRequested(Sender: TObject; Authentication: TIdAuthentication; var Handled: Boolean);
begin
if (new credentials are available) then
begin
Authentication.Username := ...; // new username
Authentication.Password := ...; // new password
Handled := True;
end else
Handled := False;
end;
That being said, if you want to use TIdDigestAuthentication standalone, then you should use it similarly to how TIdHTTP uses it, eg:
LIdAuthentication := TIdDigestAuthentication.Create;
try
LIdAuthentication.SetRequest(FGetMsgTypeString(FResponseHeader.RequestMethods), FURI);
LIdAuthentication.Username := FUser;
LIdAuthentication.Password := FPass;
LIdAuthentication.Params.Values['Authorization'] := LIdAuthentication.Authentication;
LIdAuthentication.AuthParams := FResponseHeader.WWWAuthenticate; // assuming WWWAuthenticate is a TIdHeaderList...
repeat
case LIdAuthentication.Next of
wnAskTheProgram:
begin
// set LIdAuthentication.Username and LIdAuthentication.Password to new credentials to retry...
end;
wnDoRequest:
begin
// send new request with LIdAuthentication.Authentication in the 'Authorization' header...
Result := LIdAuthentication.Authentication;
Exit;
end;
wnFail:
begin
// error handling ...
Result := '';
Exit;
end;
end;
until False;
finally
LIdAuthentication.Free;
end;
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?
I have a multi user Delphi program which needs a shared folder over network to store data. I want the program changes the files in that folder but not normal users (who can see this folder) or network viruses...
I want to protect this folder with a password (windows 7) but I need to write new files or edit existing files via my program and I don't know how to do this.
Briefly I need to connect and disconnect to a shared folder via code like this
ConnectToFolder(\\myServerMachine\mySharedfolder username:me password:myPassword);
disConnectToFolder(\\myServerMachine\mySharedfolder username:me password:myPassword);
Is this possible?
Something like this would probably do the trick
function ConnectShare(Drive, RemotePath, UserName, Password : String):Integer;
var
NRW : TNetResource;
begin
with NRW do
begin
dwType := RESOURCETYPE_ANY;
if Drive <> '' then
lpLocalName := PChar(Drive)
else
lpLocalName := nil;
lpRemoteName := PChar(RemotePath);
lpProvider := '';
end;
Result := WNetAddConnection2(NRW, PChar(Password), PChar(UserName), 0);
end;
function DisconnectShare(Drive : String):Integer;
begin
Result := WNetCancelConnection2(PChar(Drive), 0, false);
end;
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.
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")