Arno,

thanks for both hints. I tried the second one first, because I don't 
like much to change component source code if avoidable, due to 
maintenance issues with following updates. It seems to work perfectly 
good, and after several tests, I couldn't get a message posted from 
OnSessionClosed, all PostMessage() calls were issued by OnRequestDone. 
And no more exception so far.

I have to investigate more on how it behaves when errors occur, but for 
basic functionality this is exactly what my app needs.

Thanks a lot for all help and hints. Maybe one day I could give 
something back to the community ...

Michael


Arno Garrels schrieb:

>Kochendoerfer, Michael wrote:
>  
>
>>Arno,
>>
>>now I implemented it all the way you suggested. I have a message
>>handler procedure, which decides whether a record results in a mail
>>or in a print job. It then calls either smtpCli.Connect() or
>>DoPrint() (own form method) accordingly.   
>>
>>When it is a mail job and it has been processed, the OnSessionClosed
>>posts the handler message again. When it's a print job, the very last
>>line of DoPrint() posts the handler message.  
>>
>>I'm starting the whole processing by an initial PostMessage() to this
>>handler procedure and write useful info into a log memo and a file.
>>And as you wrote earlier, there's another RequestDone (smtpQuit)
>>after a OnSessionClosed call. But even if I don't have any direct
>>calls to the message pump, my own handler message is processed before
>>the smtpQuit state occurs in OnRequestDone, so the smtpCli.Connect()
>>may take place _before_ OnRequestDone has finished processing the
>>previous mail. This sometimes results in a SMTP component not ready
>>exception.        
>>    
>>
>
>You are right this is a problem since the component posts a message to
>itself internally to trigger RequestDone delayed after OnSessionClosed,
>so your own message is of course placed before the component message in
>the message queue. 
>
>Please try the following (untested) it may be a fix:
>
>In (OverbyteIcs)SmtpProt.pas add afew lines to procedure 
>TCustomSmtpClient.TriggerRequestDone:
>
>procedure TCustomSmtpClient.TriggerRequestDone(ErrorCode: Word);
>begin
>         ..  
>            FHighLevelFlag := FALSE;
>
>         // <== begin add Test**
>         // If SessionClose has been triggered let's trigger RequestDone
>         // at once so users can post their own notification message from
>         // OnSessionClose.
>            if not FConnected then
>            begin
>                if Assigned(FOnRequestDone) then
>                    FOnRequestDone(Self, FRequestType, ErrorCode);
>            end
>            else
>         // <== end add Test **
>                PostMessage(Handle, FMsg_WM_SMTP_REQUEST_DONE, 0, ErrorCode);
>        end;
>    end;
>end;
>
>
>Another workaround may also help if the above did not work:
>In OnSessionClosed check property SmtpCli.State, it should be smtpReady
>if a request has already returned in OnRequestDone.
>Post your message from OnSessionClosed only if State = smtpReady, and 
>post your message from OnRequestDone if property Connected is False.
>It's a long time ago since I've been working with SMTP so I'm not sure
>if this will work reliable, it's untested.
>
>
>---
>Arno Garrels [TeamICS]
>http://www.overbyte.be/eng/overbyte/teamics.html
> 
>
>  
>
>>How can I prevent this situation from occuring? AFAIR, I shouldn't
>>have a loop somewhere testing for the component state, so I need
>>another mechanism to prevent it.  
>>
>>All other things are fine now, thanks a lot ;)
>>
>>Michael
>>
>>
>>    
>>
>>>-----Original Message-----
>>>From: [EMAIL PROTECTED]
>>>[mailto:[EMAIL PROTECTED]
>>>Behalf Of Arno Garrels
>>>Sent: Wednesday, January 17, 2007 10:55 AM
>>>To: ICS support mailing
>>>Subject: Re: [twsocket] Still problems while sending SMTP
>>>
>>>
>>>Kochendoerfer, Michael wrote:
>>>      
>>>
>>>>Arno,
>>>>
>>>>I think you addressed the problem correctly ;)  All subsequent
>>>>Connect() calls (except the first one) are located within the
>>>>OnRequestDone event procedure, in case of RqType=smtpQuit. I now see
>>>>this must be wrong.
>>>>
>>>>For a safe reconnect, should the message below be posted from
>>>>OnRequestDone or from OnSessionClose? I guess it's the latter
>>>>because it would be triggered after RqType=smtpQuit, right? Does
>>>>calling Abort() also call OnSessionClose?
>>>>        
>>>>
>>>SessionClose is the right place to post the message and yes Abort
>>>also triggers OnSessionClose. OnSessionClose fires when the
>>>connection is closed no matter who closed it, and that may occur at
>>>*ANY TIME*. Note that if a request is still pending OnRequestDone
>>>will be triggered afterwards as well. Calling Abort while the
>>>component is still waiting for a response or while it's sending data
>>>will cause OnRequestDone being triggered with an error code > 0,
>>>this was AFAIR (10053) Software caused connection abort.
>>>
>>>---
>>>Arno Garrels [TeamICS]
>>>http://www.overbyte.be/eng/overbyte/teamics.html
>>>
>>>
>>>      
>>>
>>>>Michael
>>>>
>>>>        
>>>>
>>>>>-----Original Message-----
>>>>>From: [EMAIL PROTECTED]
>>>>>[mailto:[EMAIL PROTECTED]
>>>>>Behalf Of Arno Garrels
>>>>>Sent: Wednesday, January 17, 2007 9:24 AM
>>>>>To: ICS support mailing
>>>>>Subject: Re: [twsocket] Still problems while sending SMTP
>>>>>
>>>>>
>>>>>In order to reconnect safely you need to get out of your loop
>>>>>after the session has been closed AND after OnRequestDone
>>>>>has been triggered RqType smtpQuit as well. You must assign event
>>>>>OnSessionClose to always get notified about connection close,
>>>>>this can happen before as well as after OnRequestDone triggered
>>>>>RqType smtpQuit, I'm not sure if you do something like that.
>>>>>
>>>>>In order to get out of your loop just post a custom message,
>>>>>something like:
>>>>>
>>>>>PostMessage(Form1.Handle, WM_MAILSENT, Integer(Something),
>>>>>Integer(Something));
>>>>>
>>>>>The message handler is declared like:
>>>>>
>>>>>const
>>>>>  WM_MAILSENT = WM_USER + 1;
>>>>>..
>>>>>protected
>>>>>  procedure WmMailSent(var Msg: TMessage); message WM_MAILSENT;
>>>>>..
>>>>>
>>>>>procedure TForm1.WmMailSent(var Msg: TMessage);
>>>>>begin
>>>>>  Display(IntToStr(Msg.WParam) + ' ' + IntToStr(Msg.LParam);
>>>>>  ..
>>>>>  In the message handler you can connect safely again.
>>>>>  SmtpCli.Connect;
>>>>>end;
>>>>>
>>>>>
>>>>>---
>>>>>Arno Garrels [TeamICS]
>>>>>http://www.overbyte.be/eng/overbyte/teamics.html
>>>>>
>>>>>
>>>>>
>>>>>Michael Kochendoerfer wrote:
>>>>>          
>>>>>
>>>>>>Arno and all,
>>>>>>
>>>>>>I realized and appreciated your hint to perform it all event-
>>>>>>driven and I tried to accomplish it the way you suggested.
>>>>>>However, I have some problems building the correct logic, it
>>>>>>seems. In short words, the mail sending part of my application is
>>>>>>as follows: 
>>>>>>
>>>>>>1. Opening a SQL server query
>>>>>>2. Fill the standard properties (like Host, Port etc.) which are
>>>>>>common between calls
>>>>>>3. Invoking my OnGetNextMailParam notify procedure *directly*, as
>>>>>>if it had been called from the OnRequestDone handler
>>>>>>3a. OnGetNextMailParam checks if the query has still records, read
>>>>>>some fields, sets HdrTo if the record contains an mail address,
>>>>>>calls Connect() and Next() for the query
>>>>>>3b. OnGetNextMailParam calls a message handler procedure if
>>>>>>there's no target mail address, which invokes 3 again.
>>>>>>4. OnRequestDone is built like the sample code in MailSnd1.pas,
>>>>>>except for the smtpQuit part. In my handler, OnGetNextMailParam is
>>>>>>called again, and if it reports a valid target address, it calls
>>>>>>Connect() again (if not, it should have been handled by 3b)
>>>>>>
>>>>>>This all should work from the beginning of the query to the end,
>>>>>>where each record containing a target address should invoke the
>>>>>>sending process and each other record should not (records without
>>>>>>an mail address are handled otherwise). But it doesn't - it calls
>>>>>>Connect() for two records and then it leaves.
>>>>>>
>>>>>>I don't like you all to analyze my procedures but I'm looking for
>>>>>>some basic framework which would do it. I first thought of
>>>>>>building the whole procedd into the smtpConnect part of
>>>>>>OnRequestDone, but this isn't possible due to the lack of mail
>>>>>>addresses in some of the records. I'm really stuck here and I now
>>>>>>realize my concept won't work as needed.
>>>>>>
>>>>>>The whole thing is not more or less than walking through a record
>>>>>>set and sending a mail to each receiver within that record set
>>>>>>having a mail address. Other records having no mail address are
>>>>>>handled otherwise, must be processed within the same loop but
>>>>>>don't invoke any mail sending process. And - of course - it
>>>>>>should be async ;)
>>>>>>
>>>>>>TIA,
>>>>>>Michael
>>>>>>
>>>>>>
>>>>>>Arno Garrels schrieb:
>>>>>>
>>>>>>            
>>>>>>
>>>>>>>>while not FlagDone do begin
>>>>>>>> //Application.ProcessMessages;  // Don't know whether or not to
>>>>>>>>use the message pump here   Sleep(50);
>>>>>>>>end;
>>>>>>>>
>>>>>>>>
>>>>>>>>                
>>>>>>>>
>>>>>>>This is bad design. Do not wait in a loop. While sleeping the
>>>>>>>calling thread is blocked. Instead let your derived component do
>>>>>>>the work in the background. In order to get notified when the job
>>>>>>>has finished add a custom event that fires when the work is done,
>>>>>>>or may be add another custom event that notifies the application
>>>>>>>when a single message has been sent/failed. In other words,
>>>>>>>control the application completely thru events while executing
>>>>>>>the mailing. So in the ButtonClick handler there the call to
>>>>>>>start the mailing should be the very last line.
>>>>>>>
>>>>>>>---
>>>>>>>Arno Garrels [TeamICS]
>>>>>>>http://www.overbyte.be/eng/overbyte/teamics.html
>>>>>>>
>>>>>>>
>>>>>>>Kochendoerfer, Michael wrote:
>>>>>>>
>>>>>>>
>>>>>>>              
>>>>>>>
>>>>>>>>You all are giving excellent information in this mailing list,
>>>>>>>>thanks a lot!
>>>>>>>>
>>>>>>>>I guess my problem is - as you describe - that the component is
>>>>>>>>still active, even if smtpQuit has been reached within
>>>>>>>>OnRequestDone. I don't currently check if it's still connected,
>>>>>>>>but I will change it. Errors will be checked and force to abort
>>>>>>>>the entire mail and write some log entries.
>>>>>>>>
>>>>>>>>As Arno said earlier, I'd like to have async components because
>>>>>>>>of their benefits. But in fact, for me it is a sync call, at
>>>>>>>>least for each single mail. IOW, I've to wait until each
>>>>>>>>particular mail has been finished before I'm advancing to the
>>>>>>>>next one. So I'm starting with Connect(), let the OnRequestDone
>>>>>>>>do the background stuff and set a flag if either aborted or
>>>>>>>>quit. Now I know I've to wait also for not Connected. But
>>>>>>>>what's the correct method to wait for completion? Currently, I
>>>>>>>>have a loop after calling Connect() looking like this:
>>>>>>>>
>>>>>>>>while not FlagDone do begin
>>>>>>>> //Application.ProcessMessages;  // Don't know whether or not to
>>>>>>>>use the message pump here   Sleep(50);
>>>>>>>>end;
>>>>>>>>
>>>>>>>>Any thoughts?
>>>>>>>>
>>>>>>>>TIA,
>>>>>>>>Michael
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>                
>>>>>>>>
>>>>>>>>>-----Original Message-----
>>>>>>>>>From: [EMAIL PROTECTED]
>>>>>>>>>[mailto:[EMAIL PROTECTED]
>>>>>>>>>Behalf Of DZ-Jay
>>>>>>>>>Sent: Tuesday, January 16, 2007 10:57 AM
>>>>>>>>>To: ICS support mailing
>>>>>>>>>Subject: Re: [twsocket] Still problems while sending SMTP
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>On Jan 16, 2007, at 02:49, Arno Garrels wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>                  
>>>>>>>>>
>>>>>>>>>>When the response to the Quit command is received the
>>>>>>>>>>connection (may) still be alive. So watch both, whether Quit
>>>>>>>>>>response has been received as well as the SessionClose event.
>>>>>>>>>>Call connect only after the session has been closed.
>>>>>>>>>>Don't start a loop directly from an event handler but post a
>>>>>>>>>>custom message to some Window, in it's message handler start
>>>>>>>>>>the next loop.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>                    
>>>>>>>>>>
>>>>>>>>>You could, in fact, re-use the connection if the next message
>>>>>>>>>is to be
>>>>>>>>>sent through the same server.  All you have to do is, after the
>>>>>>>>>DATA command is completed and the server acknowledges receipt,
>>>>>>>>>check SmtpCli.Connected, if you are still connected then reset
>>>>>>>>>your state-machine to start the cycle fromthe MAIL FROM
>>>>>>>>>command. Some servers required a "reset" (RSET) command be
>>>>>>>>>sent to reset state, and it doesn't hurt to send it anyway. 
>>>>>>>>>The important thing is to check the
>>>>>>>>>connection, because something may have happened -- and indeed,
>>>>>>>>>some servers have anti-spamming filters that will kick you out
>>>>>>>>>after receiving DATA that they determine is spam, and some
>>>>>>>>>won't allow you to
>>>>>>>>>re-send after one message.  So the algorithm would be something
>>>>>>>>>like:
>>>>>>>>>
>>>>>>>>>1. Connect
>>>>>>>>>2. HELO
>>>>>>>>>3. MAIL FROM
>>>>>>>>>4. RCPT TO
>>>>>>>>>5. DATA
>>>>>>>>>6. If connected:
>>>>>>>>>6.a (yes) RSET then back to 3
>>>>>>>>>7. QUIT
>>>>>>>>>8. back to 1
>>>>>>>>>
>>>>>>>>>Of course, you should check for errors after each step (in
>>>>>>>>>OnRequestDone, before changing states).  Keep in mind that
>>>>>>>>>some errors
>>>>>>>>>are recoverable (transient: 400+), some errors are not
>>>>>>>>>(non-transient:
>>>>>>>>>500+), and some are somewhere in between (like RCPT warnings,
>>>>>>>>>etc). Recoverable errors allow you to try again, or require a
>>>>>>>>>RSET and start
>>>>>>>>>                  
>>>>>>>>>
>>>>>>>>>>from step 3, while non-transient errors require closing the
>>>>>>>>>>                    
>>>>>>>>>>
>>>>>>>>>connection
>>>>>>>>>and starting from scratch.  If you are sending general messages
>>>>>>>>>to strange servers "in the wild" it gets pretty complicated,
>>>>>>>>>specially when you factor in all the non-RFC-compliant servers;
>>>>>>>>>but if your application is of limited purpose, sending using
>>>>>>>>>the same server all the time, the errors and issues that may
>>>>>>>>>occur are predictable and substantially less.
>>>>>>>>>
>>>>>>>>>Building this logic in a simple state-machine using
>>>>>>>>>OnRequestDone makes
>>>>>>>>>it fairly easy to make your application powerful and efficient
>>>>>>>>>-- the reason we always push for the use of async methods.
>>>>>>>>>
>>>>>>>>>dZ.
>>>>>>>>>
>>>>>>>>>--
>>>>>>>>>DZ-Jay [TeamICS]
>>>>>>>>>http://www.overbyte.be/eng/overbyte/teamics.html
>>>>>>>>>
>>>>>>>>>--
>>>>>>>>>To unsubscribe or change your settings for TWSocket mailing
>>>>>>>>>list please goto
>>>>>>>>>http://www.elists.org/mailman/listinfo/twsocket Visit our
>>>>>>>>>website at http://www.overbyte.be 
>>>>>>>>>                  
>>>>>>>>>
>>>>>--
>>>>>To unsubscribe or change your settings for TWSocket mailing list
>>>>>please goto http://www.elists.org/mailman/listinfo/twsocket
>>>>>Visit our website at http://www.overbyte.be
>>>>>          
>>>>>
>>>--
>>>To unsubscribe or change your settings for TWSocket mailing list
>>>please goto http://www.elists.org/mailman/listinfo/twsocket
>>>Visit our website at http://www.overbyte.be
>>>      
>>>


-- 
To unsubscribe or change your settings for TWSocket mailing list
please goto http://www.elists.org/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be

Reply via email to