Using Outlook Contacts In Delphi XE7 - delphi

I am trying to get a list of all outlook contacts using delphi.
I found a couple of examples, all seemed to be outdated or flawed.
Currently I have the following code, but when I excecute the command at runtime I get the below error:
Code:
procedure Tinvite_friends.BitBtn2Click(Sender: TObject);
const
olFolderContacts = $0000000A;
var
outlook, NameSpace, Contacts, Contact: OleVariant;
i: Integer;
begin
Try
outlook:=GetActiveOleObject('Outlook.Application');
Except
outlook:=CreateOleObject('Outlook.Application');
End;
NameSpace := outlook.GetNameSpace('MAPI');
Contacts := NameSpace.GetDefaultFolder(olFolderContacts);
for i := 1 to Contacts.Items.Count do
begin
Contact := Contacts.Items.Item(i);
{now you can read any property of contact. For example, full name and
email address}
ShowMessage(Contact.FullName + ' <' + Contact.Email1Address + '>');
end;
Outlook := UnAssigned;
end;
Error Message:
Project appname.exe raised exception class EOLeSysError with message 'Invalid class string'.
The project does not get passed the below code before throwing the error.
Try
outlook:=GetActiveOleObject('Outlook.Application');
Except
outlook:=CreateOleObject('Outlook.Application');
End;
Is there an effective way to get a list of all contacts from outlook imported to a memo for example?

Maybe it's case sensitivity? I test for outlook.application:
const
scxOutlookApp = 'outlook.application';
scxNameSpace = 'MAPI';
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
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 // Breakpoint 'Handle subsequent exceptions'
begin
FNameSpace := FOutlookApp.GetNamespace(scxNameSpace);
// Solution 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.LogDetail(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.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 if it's 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.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;
Notes:
1. This opens the default calendar for any user, but you would not need to go that far (besides, your issue is earlier)
2. TSyncLogger is our logging handler
3. FOleInitialized, FWasCreated: Boolean; FOutlookApp, FNameSpace, FCalendarFolder: OleVariant; are some private properties we maintain
4. Essential to this code is that it first does a GetActiveOleObject to catch a running instance of Outlook; if that fails it does a CreateOleObject

Related

SMTP Implicit TLS and Explicit TLS using Indy 10.6.2.5341 in Delphi

A bit of an odd one here, when setting "UseTLS" to utUseExplicitTLS and then connecting to a mail server on its Implicit TLS port, the first attempt allows the connection and sends the email, subsequent attempts on that port correctly fail.
Just wondering if anyone has any ideas on how to avoid the false positive on the initial connect and send.
The check is to handle that non-standard ports may be getting used for a user's mail server. Pretty much all examples I've seen assume that the correct information will always be provided.
Below is the code portion that handles it (excluding error logging):
function SendTestEmail(EmailAddress: String): Boolean;
var
EmailMessage: TidMessage;
begin
IdSMTPEmail.AuthType := satDefault
IdSMTPEmail.Username := ...;
IdSMTPEmail.Password := ...;
IdSMTPEmail.Port := 465;
IdSMTPEmail.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTPEmail);
IdSMTPEmail.UseTLS := utUseExplicitTLS;
TIdSSLIOHandlerSocketOpenSSL(IdSMTPEmail.IOHandler).SSLOptions.Method := sslvTLSv1_2;
try
// Connect
IdSMTPEmail.Connect('smtp.gmail.com');
try
// Create
EmailMessage := TidMessage.Create(nil);
try
// Set values
EmailMessage.Body.Add('Test Email');
EmailMessage.Subject := 'Test Email';
// Set sender details
EmailMessage.From.Address := 'test#test.com';
EmailMessage.From.Name := 'SSL Test';
// Set recipient
EmailMessage.Recipients.Add.Address := EmailAddress;
try
// Send message
IdSMTPEmail.Send(EmailMessage);
except
// Exception
on E: EIdSMTPReplyError do
begin
// Result
Result := False;
end;
end;
finally
// Free email
EmailMessage.Free;
end;
finally
// Disconnect
IdSMTPEmail.Disconnect;
end;
except
// Exception
on E: Exception do
begin
IdSMTPEmail.Disconnect;
// Result
Result := False;
end;
end;
end;
Using the below code, correctly failed on all attempts of trying to email to port 465.
I don't even pretend to understand why this works but my original didn't.
function SendTestEmail(EmailAddress: String): Boolean;
var
EmailMessage: TidMessage;
IdSSLHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
IdSMTPEmail.AuthType := satDefault;
IdSSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create;
IdSSLHandler.SSLOptions.Method := sslvTLSv1_2;
IdSMTPEmail.IOHandler := IdSSLHandler;
IdSMTPEmail.UseTLS := utUseExplicitTLS;
IdSMTPEmail.Username := ...;
IdSMTPEmail.Password := ...;
IdSMTPEmail.Port := 465;
try
// Connect
IdSMTPEmail.Connect('smtp.gmail.com');
try
// Create
EmailMessage := TidMessage.Create(nil);
try
// Set values
EmailMessage.Body.Add('Test Email');
EmailMessage.Subject := 'Test Email';
// Set sender details
EmailMessage.From.Address := 'test#test.com';
EmailMessage.From.Name := 'SSL Test';
// Set recipient
EmailMessage.Recipients.Add.Address := EmailAddress;
try
// Send message
IdSMTPEmail.Send(EmailMessage);
except
// Exception
on E: EIdSMTPReplyError do
begin
// Result
Result := False;
end;
end;
finally
// Free email
EmailMessage.Free;
end;
finally
// Disconnect
IdSMTPEmail.Disconnect;
end;
except
// Exception
on E: Exception do
begin
IdSMTPEmail.Disconnect;
// Result
Result := False;
end;
end;
end;

Outlook send mail via COM with user settings

I have a working application that can access Outlook via COM and send, save or show emails I create inside this app.
What I want is all the settings of the account in Outlook getting applied on my mail too, so this means which mail-type (text, html or rich), custom fonts, signatures, and so on.
here a SSCCE (the the rest of the code is just some logging, and the form only contains the most neccessary controls):
...
strict private
FOutlook: _Application;
...
procedure TMainForm.ShowMailDlg(aModal: Boolean);
var
mail: _MailItem;
begin
Connect();
mail := FOutlook.CreateItem(olMailItem) as _MailItem;
mail.Recipients.Add(Trim(EdTo.Text));
mail.CC := Trim(EdCc.Text);
mail.Subject := Trim(EdSubject.Text);
mail.Body := EmailText.Lines.Text;
mail.SendUsingAccount := GetAccountForEmailAddress(Trim(EdFrom.Text));
//mail.Attachments.Add('Path1', olByValue, 1, 'Caption1');
//mail.Attachments.Add('Path2', olByValue, 2, 'Caption2');
mail.Display(aModal);
end;
procedure TMainForm.Connect;
begin
FOutlook := CreateOleObject('Outlook.Application') as _Application;
end;
function TMainForm.GetAccountForEmailAddress(const aSmtp: string): _Account;
var
accounts: _Accounts;
account: _Account;
i: Integer;
begin
accounts := FOutlook.Session.Accounts;
for i := 1 to accounts.Count do begin
account := accounts.Item(i);
if LowerCase(account.SmtpAddress) = LowerCase(aSmtp) then begin
Result := account;
Exit;
end;
end;
raise Exception.Create('No Account with SMTP address ' + aSmtp + ' found!');
end;
How can I get the MailItem to use all formatting-options from the chosen account?
I still haven't found a real solution, but here is a workaround.
The trick is to use the CreateItemFromTemplate-method, where your template contains all the settings. Oviously the user is required to create a template for this purpose, but it's a one-time-action which shoulnd't be too hard.
procedure TMainForm.DoMailAction(aAction: TMailAction);
var
mail: _MailItem;
folder: OleVariant;
begin
Connect();
folder := FOutlook.Session.GetDefaultFolder(olFolderDrafts);
mail := FOutlook.CreateItemFromTemplate('C:\\Users\\fkoch\\default.oft', folder) as _MailItem;
...
Additionally, the selected folder "Drafts" causes the signature getting attached to the mailbody, as long as the mailItem is manually send by the user in the mail-dialog (mail.display(False)). This doesn't happen when directly processed via mail.send() or mail.save().
I've found the solution now. I'v set the body the wrong way, thats why it didn't work.
procedure CreateMail(aMailInfo.TMailInfo)
var
...
insp: _Inspector;
editor: OleVariant;
begin
FMailItem := FOutlook.CreateItem(olMailItem) as _MailItem;
...
insp := FMailItem.GetInspector;
if (insp.EditorType = olEditorWord) then begin
editor := insp.WordEditor;
editor.Characters.item(1).InsertBefore(mailText);
end else begin
if FMailItem.BodyFormat = olFormatHTML then begin
regex := TRegEx.Create(cReplaceNewline);
FMailItem.HTMLBody := regex.Replace(mailText, '<br />');
end else
FMailItem.Body := mailText;
end;
...
end;

Free Mailitem in Delphi

I have a Mailitem and make a reply for that.
Now I register an OnSend EventHandler and display the Item with modal FALSE.
Everything works as desired.
My Problem is that I don't know how to free the MailItem.
If I display the Item modal I can free it in the finally block at the end of the function,
but if I display the Item non-modal, my eventhanlder (AOnSend) clearly will never be called, cause the mailitem with the registered handler is thrown away.
But to simply not call MailItem.Free will produce a Mem-Leak, so my Question: How to correctly free this MailItem?
function InternalReply(AFolder, AMailID, ASender, ACC: String; AWithoutTo: TList<String>; AModal: Boolean; AOnSend: TMailItemSend; var AErrorText: String; AReplyAll: Boolean = FALSE): Boolean; overload;
var AOutlookApplication: TOutlookApplication;
ANewInstance: Boolean;
AMAPIFolder: MAPIFolder;
AMailItem: MailItem;
AMail: TMailItem;
begin
AErrorText := '';
AOutlookApplication := Nil;
AMailItem := Nil;
AMail := TMailItem.Create(Nil);
try
try
Result := OpenOutlookInstance(AOutlookApplication, ANewInstance, AErrorText);
if Result then begin
AMAPIFolder := IntGetFolderByName(AOutlookApplication, UpperCase(AFolder), AErrorText);
if Assigned(AMAPIFolder) then begin
Result := IntGetMailFromMAPIFolderByID(AOutlookApplication, AMAPIFolder, AMailID, AMailItem, AErrorText);
if Result and Assigned(AMailItem) then begin
AMailItem := AMailItem.ReplyAll;
if Assigned(AOnSend) then begin
AMail.ConnectTo(AMailItem);
AMail.OnSend := AOnSend;
end;
if Assigned(AMailItem) then begin
...
AMailItem.Display(AModal);
end
else begin
Result := TRUE;
end;
end
else begin
Result := FALSE;
AErrorText := AErrorText + ' Mail not found! MailID: ' + AMailID;
end;
end
else begin
Result := FALSE;
AErrorText := AErrorText + ' Folder not found! Name: ' + AFolder;
end;
CloseOutlookInstance(AOutlookApplication, ANewInstance, AErrorText);
end;
except
on E: Exception do begin
Result := FALSE;
AErrorText := AErrorText + ' ' + 'Reply: Internal Error! Message: ' + E.Message;
end;
end;
finally
AMail.Free // IF I DO THIS THEN I LOSE MY HANDLER
end;
end;
You can use a global object container for this purpose: TObjectList.
When you create a new mail, add it to the container.
In the OnSend eventhandler, you can remove the mail from the container.
If you work like this, you can have multiple mails open at the same time:
uses
Contnrs,
...
var
Mails : TObjectList;
...
// create the container at application startup
// do not forget to free the container at application termination
Mails := TObjectList.Create;
...
// create mail
function InternalReply()
...
if Assigned(AOnSend) then begin
AMail.ConnectTo(AMailItem);
AMail.OnSend := AOnSend;
// add it to the container
Mails.Add(AMail);
end;
...
end;
// in your OnSend handler, remove mail from the list
// this will automatically free the mail
procedure AOnSend(Sender: TObject; var Cancel: WordBool);
begin
...
Mails.Remove(Sender); // sender is our Mail object
end;

Delphi - get user EmailAddress from active directory

I'm trying to get the e-mail address of a user by using sAMAccountName from Active Directory but I'm getting this error message:
The directory property cannot be found in the cache.
I can get fullname, department and discretion, but why am I getting this error for e-mail ?
uses
ActiveDs_TLB, adshlp;
procedure TMainForm.btnFillInfoClick(Sender: TObject);
var
Usr: IAdsUser;
lStr: HRESULT;
xStrg: string;
ChkPRN: string;
RemoveDot: string;
begin
//connect to AD and try exrtact the info /
lStr := ADsGetObject('WinNT://10.120.200.16/'+edtPRN.Text, IADsUser, usr); // edtPRN.Text >> sAMAccountName
if Succeeded(lStr) then
begin
Usr.GetInfo;
EmpFullName := Usr.FullName;
RemoveDot := StringReplace(EmpFullName, '.', '',[rfReplaceAll, rfIgnoreCase]);
xStrg := Usr.FullName;
edtLastName.Text := GetLastWord(xStrg);
xStrg := StringReplace(RemoveDot, edtLastName.Text, '',[rfReplaceAll, rfIgnoreCase]);
EdtMidName.Text := GetLastWord(xStrg);
xStrg := StringReplace(RemoveDot, EdtMidName.Text, '',[rfReplaceAll, rfIgnoreCase]);
xStrg := StringReplace(xStrg, edtLastName.Text, '',[rfReplaceAll, rfIgnoreCase]);
edtFirstName.Text := GetLastWord(xStrg);
edtEmail.Text := Usr.EmailAddress; // <<<<< this is the error
end;
end;
The email address attribute is not available using WinNT:// provider, you need to use LDAP:// provider. The name of the attribute is 'mail' not 'emailaddress'. Here is a link to ASDI and Delphi. ASDI examples.

What does "Invalid address specified to RtlFreeHeap( 06450000, 08387460 )" mean?

Sometimes I experience random crashes in my Delphi program. My program halts, and the Debugger outputs:
Invalid address specified to RtlFreeHeap( 06450000, 08387460 )
What does that mean? And what can possibly cause it?
This is where the CPU Inspector stopped:
77BA0845 C6052582BD7700 mov byte ptr [$77bd8225],$00
Please note that they are very random (for me). Sometimes they don't appear at all.
I am using the Skype4COM.dll from Skype - there's no source though.
In case you need it, here is the code. I have commented most of the calls to Synchronize, so you know what they do.
////////////////////////////////////////////////////////////////////////////////
/// Execute
////////////////////////////////////////////////////////////////////////////////
procedure TContactDeletor.Execute;
Var
I : Integer;
UserObj : PUser;
User : IUser;
PauseEvent : TEvent;
begin
inherited;
FreeOnTerminate := True;
if Terminated then
Exit;
CoInitialize(Nil);
// The F-Flags are to make sure TSkype events do not fire (from my Main Thread)
FAllowUI := False;
FUserIsBeingDeleted := False;
FUseGroupUsersEvent := False;
FUseRenameEvent := False;
SkypeThr := TSkype.Create(Nil);
SkypeThr.Attach(10,False);
SkypeThr.Cache := False;
MyList := TStringList.Create;
PauseEvent := TEvent.Create(True);
try
// This fills my Stringlist
Synchronize(GrabList);
if Terminated then Exit;
iMax := MyList.Count;
// This sets the Max of my Progressbar
Synchronize(SetMax);
Try
for I := 0 to MyList.Count - 1 do
begin
{while SkypeThr.AttachmentStatus <> apiAttachSuccess do
begin
SkypeThr.Attach(10,False);
Synchronize(Procedure Begin Log('Skype Unavailable - Trying to reconnect ...'); End);
PauseEvent.WaitFor(5000);
end; }
CurUser := '';
User := SkypeThr.User[MyList[I]];
CurUser := MyList[I];
Try
User.IsAuthorized := False;
User.BuddyStatus := budDeletedFriend;
Except on E:Exception do
begin
ExErr := E.Message;
ExLog := 'Error while deleting contacts: ';
ExMsg := 'An Error has occured while deleting contacts: ';
Synchronize(
Procedure
Begin
Log(ExLog+ExErr+sLineBreak+' - Last logged Handle: '+CurUser);
End
);
end;
end;
iProgress := I+1;
// This updates my log and my progressbar.
Synchronize(UpdatePG);
PauseEvent.WaitFor(100);
if (I mod 200 = 0) and (I > 0) then
begin
// Calls to Synchronize updates my log
Synchronize(SyncPauseBegin);
PauseEvent.WaitFor(3000);
Synchronize(SyncPauseEnd);
end;
end;
// Except
Except on E:Exception do
begin
ExErr := E.Message;
ExLog := 'Error while deleting contacts: ';
ExMsg := 'An Error has occured while deleting contacts: ';
Synchronize(
Procedure
Begin
Log(ExMsg+ExErr+sLineBreak+' - Last logged Handle: '+CurUser);
ErrMsg(ExMsg+ExErr+sLineBreak+sLineBreak+' - Last logged Handle: '+CurUser);
End
);
Exit;
end;
end;
// This synchronizes my visual list.
Synchronize(SyncList);
finally
FUserIsBeingDeleted := False;
FUseGroupUsersEvent := True;
FUseRenameEvent := True;
FAllowUI := True;
Synchronize(
Procedure
Begin
frmMain.UpdateStatusBar;
PleaseWait(False);
ToggleUI(True);
end);
PauseEvent.Free;
SkypeThr.Free;
MyList.Free;
CoUninitialize;
end;
end;
Najem was correct, it is because your heap is corrupted. To debug it easier, you should enable PageHeap, also, use the debug CRT (or debug delphi runtime) as much as possiable until you find out what's corrupting your memory.
A lot of the time, the corruption may only spill over a few bytes. Then your application can run fine for a very long time, so you will not notice anything wrong until much later, if at all.
Try to exit your application cleanly when your looking for memory corruption bugs, dont just stop the debugger, when your process is exiting, it should "touch" most of the memory it allocated earlier and it will give you a chance to detect the failure.

Resources