Send-Page-as-eMail, Take III

Tech Tip: Click here to run a free scan for Windows Errors and optimize PC performance

From: mr unreliable (ReplyToNewsgroup_at_notmail.com)
Date: 10/22/04


Date: Fri, 22 Oct 2004 17:38:19 -0400

Uh-oh. I was wrong, wrong, wrong!!! Sounds like a Sen. Kerry speech, eh?

In the above posting, I suggested using sendkeys to dismiss the saveas
dialog, WRONG!

You can't use sendkeys because the "oIE.ExecWB" command is "synchronous(?)",
i.e., it doesn't finish until the saveas dialog has been dismissed.

Circumvention Attempt Nr. 1. Initially, I attempted to use an old "ugly
hack" for dealing with modal situations, where you want control back (in
this case to dismiss the saveas dialog). The "ugly hack" is to set a timer,
which will (normally) get control back so you can do what you want. So, I
"borrowed" a timer from IE:

    oIE.Document.ParentWindow.SetTimeout GetRef("Timeout_Event"), 1000
    ...
    Sub Timeout_Event()
      ' find the saveas dialog and dismiss it
    End Sub

Surprisingly, this construction works, BUT the timer doesn't fire until
ExecWB returns, so it wasn't useful to solve this problem. Apparently, IE
is not going to do anything, including fire timer events, until ExecWB gets
done, period!

Circumvention Attempt Nr. 2. O.K., so an IE timer doesn't do it. Next I
thought I'd try a third-party control such as Steve McMahon's ultra-reliable
sSubTmr.dll, a subclassing and timer control found on
http://vbaccelerator.com:

  Set oTMR = WScript.CreateObject("SSubTimer.CTimer", "oTMR_")
  ...
  Sub oTMR_ThatTime() ' timer event...
    ' find dialog and dismiss
    ' oTMR.Interval = 0 ' and turn off the timer here (one-shot)...
  End Sub

This timer worked too, BUT again, the timer didn't fire until ExecWB was
done. Apparently, IE has found a way to STOP EVERYTHING on the thread,
while waiting for ExecWB to complete.

Circumvention Attempt Nr. 3. O.K., so IE can block the thread. I next
thought about starting a new thread. This means spinning off a "helper"
script, onto another thread, to do the job.

  ' create helper script (on a SEPARATE THREAD) to close the saveas
dialog...
Const sScriptFileSpec = "c:\windows\helper.vbs"
Const bOverwrite = True
Dim oFile : Set oFile = oFSO.CreateTextFile(sScriptFileSpec, bOverwrite)

  oFile.WriteLine("Set wshShell = WScript.CreateObject(""WScript.Shell"")")
  oFile.WriteLine("Do") ' wait for saveas dialog
  oFile.WriteLine("WScript.Sleep 100") ' wait-a-bit
  oFile.WriteLine("Loop Until wshShell.AppActivate(""Save HTML Document"")")
' until dlg appears
  oFile.WriteLine("WScript.Sleep 1000") ' wait (briefly show saveas)...
  oFile.WriteLine("wshShell.SendKeys ""%s"" ") ' send alt-s (to dismiss
saveas)
  oFile.WriteLine("WScript.Quit")
  oFile.Close

  wshShell.Run sScriptFileSpec ' run the script

This did work, finally.

Then, it was on to dealing with the outlook express dialog. This was
interesting, because there are numerous enquiries in the scripting
newsgroups, asking how to automate OE. The response is always that
"it-can't-be-done" because OE has no automation interface. BUT, IE does
seem to do the job (as part of the mailto: command). That is, OE gets
called up by IE and all the blanks are filled in for you. Does Microsoft
know about some interface that it's not telling us about? Or, is ms just
using api's to fill in the blanks?

Anyway, to get on with it, OE is on a separate thread, and so sendkeys will
work directly, (no helper scripts are needed). The rest of the script is
more-or-less a straight-forward use of sendkeys (ugh!).

Here, in all it's glory, is a scripting version of "Send-Page-as-eMail":

--- <snip> ---
Option Explicit

Const READYSTATE_COMPLETE = 4

  Dim oIE : Set oIE = CreateObject("InternetExplorer.Application")

  With oIE ' move/resize ie window...
    .left = 40 : .top = 40 : .width = 600 : .height = 200 : End With

  oIE.Navigate2 "http://msdn.microsoft.com" ' load up a dummy page...

  Do ' wait until page finished loading...
    WScript.Sleep 10 ' allow for processing events
  Loop Until oIE.ReadyState = READYSTATE_COMPLETE

  ' oIE.Visible = True ' don't need to show IE for this part...

Dim wshShell : Set wshShell = WScript.CreateObject("WScript.Shell")
Dim oFSO : Set oFSO = CreateObject("Scripting.FileSystemObject")

Const sSaveHTMLFile = "c:\windows\temp\ExecWB.htm"
Const sScriptFileSpec = "c:\windows\helper.vbs"

  ' delete any previous instance (to avoid overwrite dlg)
  oFSO.DeleteFile(sSaveHTMLFile)

  ' create helper script (on a SEPARATE THREAD) to close the saveas
dialog...
Const bOverwrite = True
Dim oFile : Set oFile = oFSO.CreateTextFile(sScriptFileSpec, bOverwrite)

  oFile.WriteLine("Set wshShell = WScript.CreateObject(""WScript.Shell"")")
  oFile.WriteLine("Do") ' wait for saveas dialog
  oFile.WriteLine("WScript.Sleep 100") ' wait-a-bit
  oFile.WriteLine("Loop Until wshShell.AppActivate(""Save HTML Document"")")
' until dlg appears
  oFile.WriteLine("WScript.Sleep 1000") ' wait (briefly show saveas)...
  oFile.WriteLine("wshShell.SendKeys ""%s"" ") ' send alt-s (to dismiss
saveas)
  oFile.WriteLine("WScript.Quit")
  oFile.Close

  wshShell.Run sScriptFileSpec ' run the script

Const OLECMDID_SAVEAS = 4
Const OLECMDID_ALLOWUILESSSAVEAS = 46
Const OLECMDEXECOPT_DONTPROMPTUSER = 2

  oIE.ExecWB OLECMDID_SAVEAS, OLECMDEXECOPT_DONTPROMPTUSER, sSaveHTMLFile,
null

  ' construct a "MailTo" command...
Const sMailTo = "mailto:"
Const sEMailAdr = "mikHar@mvp.com"
Const sCC = "cc=alDunbar@msdosbat.com" ' called name=value pairs...
Const sBCC = "&bcc=alex@mvp.com"
Const sSubjectLine = "Forwarding_Todays_Hot_Picks"
Dim sSubject : sSubject = "&subject=" & sSubjectLine
Const sBody = "&body=hey Mike, thought you might like to see the latest
techie stuff "
Const sBodyLine2 = "&body=Second line. " ' note: 2nd, 3rd lines don't work
Const sBodyLine3 = "&body=cheers, jw " ' with outlook express
Dim sMailToCmd

  sMailToCmd = sMailTo & sEMailAdr & "?" & sCC & sBCC & sSubject & sBody

  ' place the mailto command into the address bar (and hopefully execute)...
  oIE.Navigate2 sMailToCmd

  ' wait for the outlook express dialog to appear...
  Call WaitForDialog(sSubjectLine)
  WScript.Sleep 2000 ' give OE some visibility

  ' send alt-i and alt-a to get file attachment dialog...
  wshShell.SendKeys "%(ia)"

  ' wait for "Insert Attachment" dialog to appear...
  Call WaitForDialog("Insert Attachment")

  ' focus ought to be on the file name textbox, but just-in-case it's not...
  wshShell.SendKeys "%n" ' shift focus to file name...
  WScript.Sleep 10

  ' so enter the html filename in the dialog...
  wshShell.SendKeys sSaveHTMLFile
  WScript.Sleep 10

  ' click the "attach" button...
  wshShell.SendKeys "%a" ' send alt-a
  WScript.Sleep 1000 ' quickly review results

  ' finished with the outlook express dialog, send the message...
  wshShell.SendKeys "%s" ' send alt-s
  WScript.Sleep 10

  Call WaitForDialog("Send Mail") ' wait for "Send Mail" dialog...

  ' focus is on the "ok" button, so click it...
  wshShell.SendKeys "{ENTER}" ' send a vbCr

oIE.Quit
Set oIE = Nothing
Set wshShell = nothing
Set oFSO = nothing
WScript.Quit

Sub WaitForDialog(sDlgCaption)
  Do ' wait loop, waiting for the designated dialog to appear...
    WScript.Sleep 100 ' wait-a-bit
  ' note: appactivate is being used as a function,
  ' when the dialog appears, appactivate returns true...
  Loop Until wshShell.AppActivate(sDlgCaption)
End Sub

--- </snip> ---

Yes, yes, I know what you're thinking. Should have used "Microsoft.XMLHTTP"
and "adodb.stream" to save the web page "in silent mode". Or, should have
used a "helper script" to simply click the "Send" => "Page as eMail" menu
items of IE directly. Shoulda, Woulda, Coulda...

cheers, jw

p.s. I subsequently discovered that:

    oIE.document.execCommand "SaveAs", OLECMDEXECOPT_DONTPROMPTUSER, etc...

DOES allow the timer to fire BEFORE the saveas has completed. But by then I
had moved on the the "other thread" helper script approach. And besides,
even though it says DONTPROMPTUSER, the saveas dialog still shows up...


Quantcast