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