I have two mailboxes in MS Outlook. I need to get mail from one of them. How to select a specific mailbox? I have Office 365 installed. Something needs to be set in Logon ('', '', true, true); ?
My code
zOutlook := TOutlookApplication.Create(nil);
try
zOutlook.ConnectKind := ckNewInstance;
try
zOutlook.Connect;
try
zNameSpace := zOutlook.GetNamespace('MAPI');
zNameSpace.Logon('', '', true, true);
try
zInbox := zNameSpace.GetDefaultFolder(olFolderInbox);
zInboxUnread := zInbox.Items.Restrict('[Unread]=true');
FCodeSite.Send('zInboxUnread.Count ' + IntToStr(zInboxUnread.Count));
for i := 1 to zInboxUnread.Count do
begin
//..
end;
finally
zNameSpace.Logoff;
end;
finally
zOutlook.Disconnect;
end;
except
on E: SysUtils.Exception do
begin
FCodeSite.SendError(E.Message);
end;
end;
finally
zOutlook.Free;
end;
Loop though the Namespace.Stores collection and call Store.GetDEefaultFolder(olFolderInbox) instead of NameSpace.GetDefaultFolder.
You might want to look at the Store.ExchangeStoreType property to make sure you only process primary Exchange mailboxes.
I have fixed your code.
The idea is to iterate the folders at two levels: the mailboxes and the folders in the mailboxes.
Tested with Oultook 2019. Should work with other versions as well.
procedure TForm1.Button1Click(Sender: TObject);
var
zOutlook : TOutlookApplication;
zNameSpace : _NameSpace;
zInbox : MAPIFolder;
zFolder : MAPIFolder;
zInboxUnread : _Items;
I : Integer;
Found : Boolean;
MailBoxName : String;
InboxName : String;
begin
MailBoxName := 'francois.piette#company.com';
InboxName := 'Boîte de réception';
zOutlook := TOutlookApplication.Create(nil);
try
zOutlook.ConnectKind := ckNewInstance;
try
zOutlook.Connect;
try
zNameSpace := zOutlook.GetNamespace('MAPI');
zNameSpace.Logon('', '', true, true);
try
Found := FALSE;
for I := 1 to zNameSpace.Folders.Count do begin
zFolder := zNameSpace.Folders.Item(I);
if SameText(zFolder.Name, MailBoxName) then begin
Found := TRUE;
break;
end;
end;
if not Found then
Exit;
Found := FALSE;
for I := 1 to zFolder.Folders.Count do begin
zInbox := zFolder.Folders.Item(I);
if SameText(zInbox.Name, InboxName) then begin
Found := TRUE;
break;
end;
end;
if not Found then
Exit;
zInboxUnread := zInbox.Items.Restrict('[Unread]=true');
for i := 1 to zInboxUnread.Count do begin
//..
end;
finally
zNameSpace.Logoff;
end;
finally
zOutlook.Disconnect;
end;
except
on E: System.SysUtils.Exception do begin
Memo1.Lines.Add(E.Message);
end;
end;
finally
zOutlook.Free;
end;
end;
Related
I am using Delphi Rio to develop a service to make an update program.
I have created a TComponent to encapsulated all the work.
The component work as expected used in a normal VCL project, but is not working as expected when used in a service project.
here is the code:
function TTaurineUpgrade.DownloadFile(serverURL, localFile: String): Boolean;
var workConnection : TclDownLoader;
strError : String;
timeOut : Integer;
tmpInteger : Int64;
begin
Result := False;
strError := '';
if not IsURLExist(serverURL, tmpInteger) then begin
raise Exception.Create(Format('Fisierul %S nu exista pe server!', [serverURL]));
end;
try
try
WriteFeedback(EVENTLOG_INFORMATION_TYPE, eInformation, 'Conectare server Elite Soft Media pentru download file...', '', True);
WriteNotifyEventFeedback(_InfoVisibleDownloadInfo);
workConnection := TclDownLoader.Create(Application);
if Assigned(fProgressBar) then begin
fProgressBar.InternetControl := workConnection;
end;
workConnection.OnStatusChanged := DownLoaderMainStatusChanged;
workConnection.OnError := DownLoaderMainError;
workConnection.URL := serverURL;
workConnection.LocalFolder := writingPathFiles;
workConnection.LocalFile := localFile;
workConnection.Start(True);
fStatusTransfer := psUnknown;
IsAbortDownload := False;
while (fStatusTransfer <> psSuccess) do begin
WriteFeedback(EVENTLOG_INFORMATION_TYPE, eInformation, Format('fStatusTransfer = %S', [GetProcessStatusAsString(fStatusTransfer)]), '', False);
if IsAbortDownload then begin
WriteFeedback(EVENTLOG_WARNING_TYPE, eWarning, 'IsAbortDownload=True, Break', '', True);
Break;
end;
Sleep(1000);
ApplicationProcessMessages;
end;
workConnection.CloseConnection;
WriteFeedback(EVENTLOG_INFORMATION_TYPE, eInformation, 'Inchidere conexiune server Elite Soft Media...', '', True);
if IsAbortDownload then begin
WriteFeedback(EVENTLOG_WARNING_TYPE, eWarning, 'IsAbortDownload=True, Abort', '', True);
Abort;
end;
Result := True;
except
on e : Exception do begin
strError := e.Message;
end;
end;
finally
if Assigned(workConnection) then begin
FreeAndNil(workConnection);
end;
WriteNotifyEventFeedback(_InfoNotVisibleDownloadInfo);
end;
Sleep(500);
ApplicationProcessMessages;
if strError <> '' then begin
raise Exception.Create(strError);
end;
end;
procedure TTaurineUpgrade.DownLoaderMainStatusChanged(Sender: TObject; Status: TclProcessStatus);
begin
case Status of
psErrors : WriteFeedback(EVENTLOG_ERROR_TYPE, eError, 'Eroare in functia DownLoaderMainStatusChanged', '', True);
end;
fStatusTransfer := Status;
end;
procedure TTaurineUpgrade.DownLoaderMainError(Sender: TObject; const Error: String; ErrorCode: Integer);
begin
raise Exception.Create((Sender as TclDownLoader).Errors.Text);
end;
when using the component in service it stay forever in the while loop. (in the most of the cases) sometime is working (rarely)
anyone have a hint?
You should not be using ApplicationProcessMessages but instead ServiceThread.ProcessRequests when dealing with Service Applications
I have a Delphi MDI application that has a customer search child form which can only be opened once (checking isAssigned), however the view / edit form can be opened multiple times so that the end user can open multiple customers at once (Tabbed), what I'd like to do is be able to stop them from opening the same customer record more than once, on the open of the customer form I set the caption to the customers account reference and if that form exists I would like to .BringToFront, if not I'll create it.
What would be the best way to achieve this please, as I'm scratching my head!
Thanks in advance.
procedure TfrmCustomerSearch.ViewCustomerExecute(Sender: TObject);
begin
screen.cursor := crappstart;
if not IsMDIChildOpen(frmMainMenu, 'frmCustomerView', pfrmCaption) then
frmCustomerView := TfrmCustomerView.createform(nil,dmCustomerSearchfrm.FDQCustSearchreference.Value,cxGrid1DBTableView1.DataController.FocusedRecordIndex)
else
frmCustomerView.BringToFront;
screen.cursor := crdefault;
end;
function TfrmCustomerSearch.IsMDIChildOpen(const AFormName: TForm; const AMDIChildName, AMDICaption : string): Boolean;
var
i: Integer;
begin
Result := False;
for i := Pred(AFormName.MDIChildCount) DownTo 0 do
if (AFormName.MDIChildren[i].name = AMDIChildName) then
begin
if (AFormName.MDIChildren[i].caption = AMDICaption) then
begin
Result := True;
Break;
end
end;
end;
Try something more like this instead:
procedure TfrmCustomerSearch.ViewCustomerExecute(Sender: TObject);
begin
Screen.Cursor := crAppStart;
try
frmCustomerView := TfrmCustomerView(FindMDIChildOpen(frmMainMenu, TfrmCustomerView, pfrmCaption));
if frmCustomerView = nil then
frmCustomerView := TfrmCustomerView.CreateForm(nil, dmCustomerSearchfrm.FDQCustSearchreference.Value, cxGrid1DBTableView1.DataController.FocusedRecordIndex);
frmCustomerView.BringToFront;
finally
Screen.Cursor := crDefault;
end;
end;
function TfrmCustomerSearch.FindMDIChildOpen(const AParentForm: TForm; const AMDIChildClass: TFormClass; const AMDICaption : string): TForm;
var
i: Integer;
Child: TForm;
begin
Result := nil;
for i := Pred(AParentForm.MDIChildCount) DownTo 0 do
begin
Child := AParentForm.MDIChildren[i];
if Child.InheritsFrom(AMDIChildClass) and
(Child.Caption = AMDICaption) then
begin
Result := Child;
Exit;
end;
end;
end;
I've a sample ISAPI module hosts on IIS7 on Windows Vista Ultimate in Virtual Machine. According to the IIS 7.0 Editions and Windows, the simultaneous request execution limit is ten for Windows Vista Ultimate. I write a sample application for testing the IIS7 simultaneous request execution limit:
procedure TForm1.Button1Click(Sender: TObject);
const c_Max = 20;
var P: TProc<string>;
o: TThread;
i: integer;
A: array of TThread;
begin
P :=
procedure(aValue: string)
begin
Memo1.Lines.Add(aValue);
end;
SetLength(A, c_Max);
for i := Low(A) to High(A) do begin
o := TMyThread.Create(P, edHostName.Text, Format('test%d', [i + 1]), True);
o.FreeOnTerminate := True;
A[i] := o;
end;
for o in A do
o.Start;
end;
constructor TMyThread.Create(const aMethod: TProc<string>; const aHostName,
aValue: string; const aCreateSuspended: boolean);
begin
inherited Create(aCreateSuspended);
FMethod := aMethod;
FHostName := aHostName;
FValue := aValue;
end;
procedure TMyThread.Execute;
var C: TSQLConnection;
o: TServerMethods1Client;
S: string;
begin
C := TSQLConnection.Create(nil);
C.DriverName := 'DataSnap';
C.LoginPrompt := False;
with C.Params do begin
Clear;
Values[TDBXPropertyNames.DriverUnit] := 'Data.DBXDataSnap';
Values[TDBXPropertyNames.HostName] := FHostName;
Values[TDBXPropertyNames.Port] := IntToStr(80);
Values[TDBXPropertyNames.CommunicationProtocol] := 'http';
Values[TDBXPropertyNames.URLPath] := 'MyISAPI/MyISAPI.dll';
end;
S := Format('%d=', [ThreadID]);
try
try
C.Open;
o := TServerMethods1Client.Create(C.DBXConnection);
try
S := S.Format('%s%s', [S, o.EchoString(FValue)]);
finally
o.Free;
end;
except
on E: Exception do
S := S.Format('%s%s', [S, E.Message]);
end;
finally
Synchronize(
procedure
begin
FMethod(S);
end
);
C.Free;
end;
end;
When i click the Button1, the Memo1 displays 20 lines of string (ThreadID=Result of EchoString), even though i change the constant variable c_Max to 500, i still can't get any line of string (ThreadID=Exception message) displays on Memo1. My question is how to test IIS7 simultaneous request execution limit?
I use the following code to eval the msg. content (body / lines) of an E Mail msg received with the INDY 10 components
function LinesFromMsg(aMsg: TIdMessage): TStrings;
var
i: Integer;
begin
for i := 0 to aMsg.MessageParts.AttachmentCount-1 do
begin
if (amsg.MessageParts.Items[i].ContentType ='HTML') then
begin
if (amsg.MessageParts.Items[i] is Tidtext) then
Result := TidText(amsg.MessageParts.Items[i]).body;
end;
end;
end;
regarding this code I have 2 questions :
a) is this the correct way of finding the Tlines part in an arbitray mail message ?
( consider the advice shown at INDY 10 EMAIL MSG PARTS )
b) where can I find a tutorial of all the different Contenttype string values?
The correct ContentType value to look for is text/html. Use Indy's IsHeaderMediaType() function to check it, as the ContentType value may have additional attributes associated with it that your comparison needs to ignore.
You also need to take the TIdMessage.ContentType into account as well, as HTML emails may not be MIME encoded and thus not use the TIdMessage.MessageParts` collection at all.
And lastly, you loop needs to use the MessageParts.Count property instead of the MessageParts.AttachmentsCount property.
Try this:
function HTMLFromMsg(aMsg: TIdMessage): TStrings;
var
i: Integer;
Part: TIdMessagePart;
begin
Result := nil;
if IsHeaderMediaType(aMsg.ContentType, 'text/html') then
begin
Result := aMsg.Body;
Exit;
end;
for i := 0 to aMsg.MessageParts.Count-1 do
begin
Part := aMsg.MessageParts.Items[i];
if (Part is TIdText) and IsHeaderMediaType(Part.ContentType, 'text/html') then
begin
Result := TIdText(Part).Body;
Exit;
end;
end;
end;
With that said, this is technically not the correct way to handle MIME. Officially, a conforming reader is supposed to loop backwards through the MIME parts, as they are ordered from the simpliest form downwards towards the most complex form. So you loop backwards, taking MIME nesting into account, looking for the most complex form you support. Something more like this (untested):
procedure DisplayPlainText(Body: TStrings);
begin
// display plain text as needed...
end;
procedure DisplayHTML(Body: TStrings);
begin
// display html as needed...
end;
procedure DisplayMultiPartAlternative(aMsg: TIdMessage; aParentIndex, aLastIndex: Integer);
var
Part: TIdMessagePart;
i: Integer:
begin
for i := aLastIndex-1 downto aParentIndex+1 do
begin
Part := aMsg.MessageParts.Items[i];
if (Part.ParentPart = aParentIndex) and (Part is TIdText) then
begin
if IsHeaderMediaType(Part.ContentType, 'text/html') then
begin
DisplayHTML(TIdText(Part).Body);
Exit;
end;
if IsHeaderMediaType(Part.ContentType, 'text/plain') then
begin
DisplayPlainText(TIdText(Part).Body);
Exit;
end;
end;
end;
// nothing supported to display...
end;
procedure DisplayMultiPartMixed(aMsg: TIdMessage; aParentIndex, aLastIndex: Integer);
var
Part: TIdMessagePart;
i: Integer;
begin
for i := aLastIndex-1 downto aParentIndex+1 do
begin
Part := aMsg.MessageParts.Items[i];
if (Part.ParentPart = aParentIndex) and (Part is TIdText) then
begin
if IsHeaderMediaType(Part.ContentType, 'multipart/alternative') then
begin
DisplayMultiPartAlternative(aMsg, ParentPart.Index, aLastIndex);
Exit;
end;
if IsHeaderMediaType(ParentPart.ContentType, 'text/html') then
begin
DisplayHTML(TIdText(Part).Body);
Exit;
end;
if IsHeaderMediaType(Part.ContentType, 'text/plain') then
begin
DisplayPlainText(TIdText(Part).Body);
Exit;
end;
aLastIndex := i;
end;
end;
// nothing supported to display...
end;
procedure DisplayMsg(aMsg: TIdMessage);
var
ContentType: string;
begin
ContentType := ExtractHeaderMediaType(aMsg.ContentType);
case PosInStrArray(ContentType, ['multipart/mixed', 'multipart/alternative', 'text/html', 'text/plain'], False) of
0: begin
DisplayMultiPartAlternative(aMsg, -1, aMsg.MessageParts.Count);
Exit;
end;
1: begin
DisplayMultiPartMixed(aMsg, -1, aMsg.MessageParts.Count);
Exit;
end;
2: begin
DisplayHTML(aMsg.Body);
Exit;
end;
3: begin
DisplayPlainText(aMsg.Body);
Exit;
end;
else
// nothing supported to display...
end;
end;
I try to get the name of all forms of the loaded page. I have done this:
procedure TForm2.Button2Click(Sender: TObject);
var
L: TStringList;
begin
L := TStringList.Create;
try
Chromium1.Browser.MainFrame.VisitDomProc(
procedure (const doc: ICefDomDocument)
procedure IterateNodes(Node: ICefDomNode);
begin
if not Assigned(Node) then Exit;
repeat
if Node.ElementTagName = 'FORM' then
L.Add(Node.GetElementAttribute('name'));
if Node.HasChildren then IterateNodes(Node.FirstChild);
Node := Node.NextSibling;
until not Assigned(Node);
end;
begin
IterateNodes(doc.Body);
end
);
ShowMessage(L.Text);
finally
FreeAndNil(L);
end;
end;
But I don't have any result. Any idea?
Thanks
With XE2 Update 4
I have realized that the program flow continues when running the procedure parameter so that upon reaching the ShowMessage still has not run this procedure and therefore the TStringList is empty.
I have put a boolean variable control and it worked right, but this is not a elegant solution.
Here the new code:
procedure TForm2.Button2Click(Sender: TObject);
var
L: TStringList;
Finish: Boolean;
begin
L := TStringList.Create;
Finish := False;
try
Chromium1.Browser.MainFrame.VisitDomProc(
procedure (const doc: ICefDomDocument)
procedure IterateNodes(Node: ICefDomNode);
begin
if not Assigned(Node) then Exit;
repeat
if SameText(Node.ElementTagName, 'FORM') then
begin
L.Add(Node.GetElementAttribute('name'));
end;
if Node.HasChildren then
IterateNodes(Node.FirstChild);
Node := Node.NextSibling;
until not Assigned(Node);
end;
begin
IterateNodes(doc.Body);
Finish := True;
end
);
repeat Application.ProcessMessages until (Finish);
ShowMessage(L.Text);
finally
FreeAndNil(L);
end;
end;
I managed to get the whole page like this:
Inject a DOM element - text.
ChromiumWB.Browser.MainFrame.ExecuteJavaScript('$("body").prepend(''<input type="text" id="msoftval" value=""/>'')', '', 0);
Use jquery or js to get body html into injected element.
mResult := '';
ChromiumWB.Browser.MainFrame.ExecuteJavaScript('$("#msoftval").val($("body").html());', '', 0);
ChromiumWB.Browser.MainFrame.VisitDomProc(getResult);
while mResult = '' do Application.ProcessMessages;
Memo1.Text := mResult;
wait untill 'VisitDomProc' finish- make it sync.
procedure TForm44.getResult(const doc: ICefDomDocument);
var
q: ICefDomNode;
begin
q := doc.GetElementById('msoftval');
if Assigned(q) then
mResult := q.GetValue
else
mResult := '-';
end;