Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

VFP + Outlook Email SEND Question 1

Status
Not open for further replies.

JRB-Bldr

Programmer
May 17, 2001
3,281
US
When 'driving' MS Outlook via VFP automation, I have noticed something that I'd like to confirm (or not).

By creating an Outlook object and using automation, my VFP application can successfully pass emails to Outlook. And the application issues the SEND command after handing Outlook each separate email.

But unless Outlook is actually Open (Launched) on the workstation desktop (before or after the VFP application runs), the SEND does not appear to really be performed. It seems as though the email document is held within Outlook's Draft folder with a pending SEND until the workstation's Outlook is actually launched.

In order to resolve the Has-to-be-Launched problem I have added VFP code to actually launch Outlook at the start of the SendEmail routine, not just create an object within the routine.
Code:
tcEXEFile = ADDBS(mcOutlookPath) + "OUTLOOK.EXE"
tcParameters = ""
tcCmd = "Open"
tcCmd = PROPER(m.tcCmd)

* API Call to communicate with an application based on the registered file-type.
* For example:
* On my computer txt is notepad; DOC is word

DECLARE INTEGER ShellExecute ;
   IN SHELL32.DLL ;
   INTEGER nWinHandle,;
   STRING cOperation,;
   STRING cFileName,;
   STRING cParameters,;
   STRING cDirectory,;
   INTEGER nShowWindow

lnFileStatus = ShellExecute(0,"&tcCmd",tcEXEFile,tcParameters,"",1)

mnAppHndl = 0
mlRet = .T.
DO CASE
   CASE m.lnFileStatus > 32
      * successful open
      mnAppHndl = m.lnFileStatus

   CASE m.lnFileStatus = 2
      MESSAGEBOX("Bad Association (for example, invalid URL)",;
      "AppLauch Method -- AppLauch Method")

   CASE m.lnFileStatus = 29
      MESSAGEBOX("Failure to load application","AppLauch Method -- AppLauch Method")

   CASE m.lnFileStatus = 30
      MESSAGEBOX("Application is busy","AppLauch Method -- AppLauch Method")

   CASE m.lnFileStatus = 31
      MESSAGEBOX("No application association with specified "+;
         "command: " + m.tcCmd,"AppLauch Method -- AppLauch Method")

   OTHERWISE
      MESSAGEBOX("Unknown Error","AppLauch Method -- AppLauch Method")
      mlRet = .F.
ENDCASE

The above Launch utility works well, but the problem encountered is that I often end up with multiple copies of Outlook running (Outlook might already be open from before, or by a user's manual action, etc.) which consumes workstation memory and brings everything down to a slow crawl.

First question - Is this how other's have observed Outlook to work with VFP automation?

Next question - Suggestions or advice as to how to get around the problem(s)?

Thanks,
JRB-Bldr
 
The Outlook MailItem.Send method sends the message to the Outbox. It's then up to Outlook to actually send the message on its next pass. This is expected behavior.

You shouldn't need to start Outlook with ShellExecute because issuing CREATEOBJECT("Outlook.Application") starts it.

Tamar
 
Tamar - thanks for the reply.

Well the CREATEOBJECT does indeed 'start' Outlook but it does not seem to be FULLY 'started' in that the email documents (about 100 of them) are still in the Draft folder and none of them are in the Sent Items folder when, after the utility has completed, I manually Launch Outlook to observe the results.

Note - Another guess - Perhaps my emails are going into the Draft folder instead of the Outbox due to my 'talking' to Outlook via Redemption.

Based solely on observation (not on certainty), it seems as though the CREATEOBJECT 'opens the back door' so that some operations are supported, but the SEND does not seem to REALLY output documents.

If I manually Launch Outlook before issuing the VFP automation utility to send the emails, the documents initially go into the Draft folder as expected, then I can watch them be processed by Outlook on a regular basis into the Sent Items folder as they are really SENT.

This last piece of Outlook execution does not seem to happen if Outlook is not Launched and is visible on the desktop (or visible in the Task bar).

"It's then up to Outlook to actually send the message on its next pass." Perhaps this is the part which is not being supported through Outlook when it is 'started' via a CREATEOBJECT.

If this guess is true, how can this be worked-around?

Thanks,
JRB-Bldr
 

JRB-Bldr,

I agree with Tamar that you don't need to explicitly launch Outlook in these circumstances.

I have often written code that creates a mail item, then calls its Send method. This will either send the item immediately, or place it in the Outbox - the choice probably depends on how Outlook is configured. I have never seen the Send method place an item in the Drafts folder. To do that, I would call the Save method.

It might be the case that the message will be sent to the Drafts folder if it hasn't got any valid recipients. Is that possible in your case?

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

My Visual FoxPro site: www.ml-consult.co.uk
 
Mike - No, out of 100+ emails I am certain that there may be a small percentage which are invalid. But, based on observing the program's behavior for months, the emails are most definitely all going to the DRAFT folder.

Here is the code I am using (remember that I am doing it through Redemption to get around the Outlook SP-2 confirmation issue)
Code:
* ------------------------------------
*  Enter here with most variables pre-defined by calling routine
*
*
FUNCTION SendEmail
PARAMETERS mcEmailAddr, mcSubject, mcAttach1, mcAttach2
LOCAL oMail

#DEFINE MAILITEM 0
#DEFINE IMPORTANCELOW 0
#DEFINE IMPORTANCENORMAL 1
#DEFINE IMPORTANCEHIGH 2

mcFromAddr = "MyClient <Leads@MyClient.com>"
mcReplyAddr = "No-Reply <No-Reply@MyClient.com>"

* --- If Necessary, Create Outlook Objects ---
DO WHILE TYPE('oRedem') # 'O' ;
      OR TYPE('oOutlook') # 'O';
      OR ISNULL(oRedem);
      OR ISNULL(oOutlook)
   * --- Eliminate Any Previous Outlook Objects ---
   IF TYPE('oNS') = 'O' ;
         AND !ISNULL(oNS)
      oNS.Logoff
   ENDIF

   RELEASE oMail
   RELEASE oRedem, oOutlook, oNS

   * --- Initialize Outlook If Necessary ---
   PUBLIC oRedem, oOutlook, oNS
   oRedem = CREATEOBJECT("Redemption.SafeMailItem")
   oOutlook = CREATEOBJECT("Outlook.Application")

   oNS = oOutlook.GetNamespace("MAPI")
   oNS.Logon
ENDDO

IF TYPE('mcCC') = "U"
   mcCC = ""
ENDIF

IF TYPE('mcBCC') = "U"
   mcBCC = ""
ENDIF

oMail = oOutlook.CreateItem(0)

oRedem.ITEM = oMail

* --- Specific Redemption Code To Insert 'Pseudo' FROM address ---
FromTag = oRedem.GetIDsFromNames("{00020386-0000-0000-C000-000000000046}", "From")
FromTag = BITOR(FromTag, 0x1E)  && VFP Version
FromTag = ALLTRIM(STR(FromTag,20)) + CHR(0)  && Convert to String - PT_STRING8 Format

* --- Build Outlook Email & Send It  ---
WITH oRedem
   .ITEM = oMail
   .TO = mcEmailAddr

   IF !EMPTY(mcCC) ;
         AND ("@" $ mcCC) ;
         AND ("." $ mcCC)
      .CC = mcCC
   ENDIF

   IF !EMPTY(mcBCC) ;
         AND ("@" $ mcBCC) ;
         AND ("." $ mcBCC)
      .BCC = mcBCC
   ENDIF

   .FIELDS[FromTag] = mcFromAddr  && Insert Pseudo FROM:
   .ReplyRecipients.ADD(mcReplyAddr)   && Insert Pseudo REPLY-TO:
   .Subject = mcSubject
   .Importance = IMPORTANCENORMAL
   .Body = mcBodyMsg

   IF !EMPTY(mcAttach1)
      .Attachments.ADD(mcAttach1) && Fully Pathed DOC File Attachment
   ENDIF

   IF !EMPTY(mcAttach2)
      .Attachments.ADD(mcAttach2) && Fully Pathed XLS File Attachment
   ENDIF

   .SEND
ENDWITH

* --- When Done, RELEASE The Objects ---
RELEASE oMail

RETURN .T.

In part it does not seem to matter if the email documents are going to the DRAFT folder or the OUTBOX, since once Outlook is 'really' open, the documents, on their own, begin going 'out-the-door' and into the Sent Items folder.

What I am really trying to fix is ending up with multiple copies of Outlook open within the workstation, thereby consuming memory and slowing things down.

Thanks,
JRB-Bldr
 
Maybe that's how Redemption gets around the confirmation process. (?) Sending them to the draft folder first.

Anyway, maybe you could check for Outlook running and if it ain't, fire it up. This function actually checks for an app running and is supposed to bring it to the forefront. But you can trim it up to only use what you need.

Run it like this:
Code:
IF !ACTIWIND("Outlook", .F., .T.)
   * Run outlook
ENDIF

Sorry, I forget the author or I would give proper credit:

Code:
** ACTIWIND.PRG
PARAMETERS zcTitle, zlExact, zlUpper

** Checks to see if the Title sent in (zcTitle) matches
**  the title of any active desktop window.
** The second parameter (zlExact) tells us to check the
**  beginning of the title for an exact match or check
**  anywhere in the title for zcTitle.
**
** zcTitle is CASE SENSITIVE unless zlUpper is .T.

** If the title is found then that window is brought
**  forward and .T. is returned.

IF TYPE("ZCTITLE") != "C" OR TYPE("ZLEXACT") != "L" ;
    OR TYPE("ZLUPPER") != "L"
   ** Bad parameter(s).
   RETURN .F.
ENDIF

IF zlUpper
   zcTitle = UPPER(zcTitle)
ENDIF

#DEFINE GW_HWNDNEXT              2
#DEFINE GW_CHILD                 5
#DEFINE SW_RESTORE               9

DECLARE INTEGER GetWindow IN USER32 INTEGER, INTEGER
DECLARE INTEGER GetDesktopWindow IN USER32
DECLARE INTEGER GetWindowText IN USER32 INTEGER, ;
   STRING @, INTEGER
DECLARE INTEGER IsIconic IN USER32 INTEGER
DECLARE INTEGER SetActiveWindow IN USER32 INTEGER
DECLARE INTEGER ShowWindow IN USER32 INTEGER, INTEGER

PRIVATE qnHwnd, qlReturn
qlReturn = .F.
** Get the handle of the first window on the Desktop.
qnHwnd = GetWindow(GetDesktopWindow(), GW_CHILD)

PRIVATE qcTitle, qnLen
** Loop through all active windows. 
DO WHILE qnHwnd > 0
   qcTitle = REPL(CHR(0), 128)
   qnLen = GetWindowText(qnHwnd, @qcTitle, 128)
   qnLen = MIN(qnLen, 128)
   qcTitle = SUBSTR(qcTitle, 1, qnLen)
   IF zlUpper
      qcTitle = UPPER(qcTitle)    
   ENDIF
   IF ! EMPTY(qcTitle)
      IF zlExact ;
          AND zcTitle==SUBSTR(qcTitle,1,LEN(zcTitle))
         qlReturn = .T.
         EXIT
      ENDIF
      IF ! zlExact AND zcTitle $ qcTitle
         qlReturn = .T.
         EXIT
      ENDIF
   ENDIF
    
   ** Get the next window handle.
   qnHwnd = GetWindow(qnHwnd, GW_HWNDNEXT)

ENDDO

*... this can be bypassed if you don't want outlook to gain focus
IF qlReturn
   ** Attempt to bring the found window forward.
   IF IsIconic(qnHwnd) > 0
      =ShowWindow(qnHwnd, SW_RESTORE)
   ENDIF
   =SetActiveWindow(qnHwnd)
ENDIF

RETURN qlReturn

-Dave Summers-
[cheers]
Even more Fox stuff at:
 
Dave - thanks for the code suggestion. I will give it a try.

One question - "Checks to see if the Title sent in (zcTitle) matches the title of any active desktop window." Does that also include identifying tasks running in the workstation which have been minimized and are only showing on the taskbar?

Mike - thanks for your suggestions.

Running without Redemption would possibly satisfy our curiosity as to how/why the email documents are getting into the Draft folder, but it would not resolve the problem. Unless I were to switch the application over to using CDO or something else, I need to use Redemption to get around the confirmation problem when sending 100's of email notifications to the client's customers each morning.

Thanks,
JRB-Bldr
 
JRB-Bld,

It's not just a question of satisfying your curiosity. It will tell you if the problem lies with Redemption or elsewhere. You can't solve the problem until you know what's causing.

If it does turn out to be Redemption, the next step would be to try an alternative way of avoiding the confirmation message. One possibility might be ClickYes (
Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

My Visual FoxPro site: www.ml-consult.co.uk
 
JRB-Bld,

This section of code is supposed to restore it and bring it forward if it's minimized, or just bring it forward if it's not active:
Code:
IF qlReturn
   ** Attempt to bring the found window forward.
   IF IsIconic(qnHwnd) > 0
      =ShowWindow(qnHwnd, SW_RESTORE)
   ENDIF
   =SetActiveWindow(qnHwnd)
ENDIF

-Dave Summers-
[cheers]
Even more Fox stuff at:
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top