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. 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