Newsletters #7

Many people asked how to access clipboard from WSH and how to print from WSH scripts. Well, during updating my German title Inside Windows Script Host, 2nd edition (Microsoft Press), I covered this issue. Due to postings in distinct newsgroups I was inspired to investigate Internet Explorer, and I found some tricks how to print simple documents or exchange data with the clipboard. I also wrote some ActiveX controls that provides the requested methods. In newsletter #6 I already introduced a way how to create your own power scripts that uses Imaging as a helper to access graphics, print them, copy and paste to/from clipboard or to convert files. But Imaging is a bit an overkill, if you intend to print just simple text or html files or if you intend to copy a simple string to/from clipboard. So let's talk about the details.

(c) Günter Born, http://www.borncity.de

Warning: The material submitted as a newsletter comes AS-IS without any warranty of any kind. In no event shall the author be liable for damages and losses of any kind. The material discussed herein is copyrighted by the author. You get a free right to use it. This newsletter may be distribute freely as long as: a) the text remains intact and unmodified, b) a reference to the WSH Bazaar www.borncity.de is given and c) the newsletter is not shipped with other commercial material. If you like to ship the newsletter with CD-ROMs etc. please ask. Permission will be granted in normal cases, but I like to know where my material goes into.

Printing: Take some help from Notepad?

The most simple way to print text files (and other files containing text) is offered by the program Notepad. This little editor is available in all Windows versions, and you may invoke the editor using the following command:

Notepad.exe /p c:\test\mytext.txt

The command above invokes Notepad and load the file submitted in the 2nd argument. The switch /p forces Notepad to print the file (using the default printer). After printing, Notepad terminates automatically. Using this know how it is only a short way to print from WSH scripts:

'************************************************
' File:   PrintTxt.vbs (WSH-sample in VBScript) 
' Author: G. Born www.borncity.com
'
' Demonstrates how to print a text file using Notepad.
' The sample prints the script's source code.
' 
' See also the Microsoft Press title:
' Microsoft Windows Script Host 2.0 Developer's Guide
'************************************************
Option Explicit

Dim oWSHShell

' create Shell object instance for Run method
Set oWSHShell = WScript.CreateObject("WScript.Shell")

' shell out a command to print with Notepad
oWSHShell.Run "Notepad /p " _
              & """" & WScript.ScriptFullName & """" ,_
              7, true
WScript.Echo "Printing"    ' inform user
'*** End

The script show above just prints its own source code using notepad. The parameters submitted to Run forces Windows to show the window minimized (2nd parameter set to 7). The third parameter set to True causes the script to wait until Notepad terminates.

Printing, using MSHTML.dll

Internet Explorer installs also a library MSHTML.dll that provides some functions to print documents. The advantages, compared to Notepad, are: The program show the Print dialog box, so a user may select the printer and some print options. Also MSHTML.dll supports beside text files also HTML documents and RTF files. Unfortunately there is a small problem: MSHTML.dll is a system library and WSH scripts can't access library functions (if these functions are not exported as methods of COM objects). Fortunately there is Rundll32.exe, a small program that allows calling API functions in system libraries. So you can use the following command to invoke the MSHTML.dll print function:

Rundll32.exe MSHTML.dll,PrintHTML filename

Note that the command is case-sensitive, and also there is no blank following the comma separating the library name from the function name. The function PrintHTML accepts one argument containing the document's file name. The next listing uses MSHTML.dll to print distinct files:

'************************************************
' File:   PrintDocs.vbs (WSH-sample in VBScript) 
' Author: G. Born www.borncity.com
'
' Demonstrates how to print several document files
' using MSHTML.dll.
'************************************************
Option Explicit

' Define some files.
'Const file = "C:\Text.txt"
Const file = "C:\Text.htm"
'Const file = "C:\Text.rtf"

Dim oWSHShell

' create Shell object instance for Run method
Set oWSHShell = WScript.CreateObject("WScript.Shell")

' shell out a command to print source with MSHTML
 oWSHShell.Run "Rundll32.exe MSHTML.dll,PrintHTML " & _
                 file, 1, true
WScript.Echo "I guess we are printing " & file
'*** End

If the script displays the Print dialog box, just select the print options and click OK. Take care to prepare a document file and set the right file and path name in the file constant. The parameters submitted to Run forces Windows to show the dialog box in normal mode.

Printing with a little help form your Windows Shell

If you right-click a document file, Windows Shell shows you a context menu, and in most cases you'll find the Print command. If you select Print, Windows forces the registered application to load the document, print it and terminate. Why not use a similar feature from WSH? Unfortunately WSH can't access the Shell API (there are only a few methods offered by Shell.Application). Therefore I wrote a small ActiveX control that provides access to some Shell API. The control WSHShellExtend.ocx may be dowloaded from the ActiveX page of the WSH Bazaar. After installing this control (see also newsletter #3), you can use the methods provided by this control. The next listing uses this control to print document files:

'************************************************
' File:   PrintExtender.vbs (WSH-sample in VBScript) 
' Author: G. Born www.borncity.de
'
' Demonstrate how to print document files using an 
' ActiveX control. Need WSHShellExtend.ocx.
'************************************************
Option Explicit

Dim oShExt, code, tmp

' create object reference
Set oShExt = WScript.CreateObject("ShellExtension.ShellObjects")

If MsgBox ("Print bmp file?", vbYesNo + vbQuestion, _
            "Print Extender") = vbYes Then
' print a BMP file
 code = oShExt.WSHShellExecute ( _
    "print", GetPath() & "TestImg.bmp", "", "", 1)
 If code > 32 Then
  MsgBox "We are printing ... "
 Else
  MsgBox "Error, code is " & code
 End if
End if

If MsgBox ("Print pdf-file", vbYesNo + vbQuestion, _
            "Print Extender") = vbYes Then
' print a PDF file (requires Acrobat reader installed)
 code = oShExt.WSHShellExecute ( _
    "print", GetPath() & "TestDoc.pdf", "", "", 1)
 If code > 32 Then
  MsgBox "We are printing ... "
 Else
  MsgBox "Error, code is " & code
 End if
End if

'##########################
Function GetPath
' Retrieve path to the script file
 DIM path
 path = WScript.ScriptFullName  ' script file name
 GetPath = Left(path, InstrRev(path, "\"))
End Function
'*** End

The script instantiate the object ShellExtension.ShellObjects and accesses then the WSHShellExecute method. This method requires 6 parameters. The first parameter contains a verb ("print", "open" for instance), the 2nd parameter contains the path and the file name. The other parameters (for working directories etc.) are set to "" or to 1 as shown above. Further details about using this methods along with the source code of the ActiveX control is provided in my German Microsoft Press title Inside Windows Script Host, 2. Ausgabe. Also my upcoming English MS Press title Advanced Development with Microsoft Windows Script Host 2.0 will cover this topic.

Hello Clipboard, WSH is calling you

Many scripters wish to interact with the clipboard. Although WSH doesn't provide methods to do that, you can use a few tricks. Let's go step by step.

The most efficient way will be to write an ActiveX control that provides you with methods allowing you to access Windows clipboard. I wrote such a control and shipped it with the MS Press title Inside Windows Script Host, 2. Ausgabe (and there are also plans that my upcoming English MS Press title Advanced Development with Microsoft Windows Script Host 2.0 will contain this control). But till now I'm not ready to release this stuff in the WSH Bazaar (because the description is still in German). That's odd, but fortunately there are a few way to overcome this situation.

Well, maybe you intend to control two applications using WSH, and your script need to force one program to transfer data to the other. In this case you can force the application to copy data to clipboard and paste it into the document area of the other program. This will be used. The script launches Notepad and Calc, forces Calc to evaluate an expression and transfer the result to Notepad.

'************************************************
' File:   AppActivateWSH2.vbs (WSH 2.0 VBScript sample)   
' Author: (c) G. Born www.borncity.de
' 
' Launch Calc and Notepad, use AppActivate to 
' switch between applications. Transfers data
' between Calc and Notepad using Clipboard (controlled
' by SendKeys.
' Attention: The script requires WSH 2.0 !!!
'************************************************
Option Explicit

' Define the title strings of the application windows
' Use the title's begin or end, if a file name changes
' Important: strings depend on the localized Windows version
Const Edit_Title = "Notepad"          ' window title
Const Calc_Title = "Calculator"        ' window title

DIM Wsh

' Create the Shell object, required for Run and AppActivate
Set Wsh = WScript.CreateObject("WScript.Shell")

' we try to launch two applications - to assure that the
' last application receives the focus, we delay the script
Wsh.Run "Calc.exe",1     ' launch Windows calculator
WScript.Sleep 300        ' delay, till calc gets the focus
Wsh.Run "Notepad.exe",1  ' launch Windows editor Notepad
WScript.Sleep 300        ' delay, till notepad gets the focus

' set the focus back to the calculator window & calulate
If Wsh.AppActivate (Calc_Title) Then
 Wsh.SendKeys "10"       ' send 10
 Wsh.SendKeys "{+}"      ' send +
 Wsh.SendKeys "2"        ' send 2
 Wsh.SendKeys "="        ' send =

 Wsh.SendKeys "^{c}"     ' send Ctrl+C, copy to clipboard
End if

' set the focus to the editor window
MsgBox "Paste result into editor window?", vbOkOnly + vbSystemModal
If Wsh.AppActivate (Edit_Title) Then
 Wsh.SendKeys "Result of 10 {+} 2 ="
 Wsh.SendKeys "^{v}"     ' send Ctrl+v, paste from clipboard
End if
'* End

You can use similar techniques also with other application programs. Then you need to send keystrokes to select portions of the document area (which can be a problem).

Ask Internet Explorer (a good fried) for assistance

If you need to access some clipboard text within your script, the trick shown above won't fit. But I found another way to exchange text data between script variable and clipboard without using an ActiveX control. Let's go into details. You can use Internet Explorer to show a form that contains a text field. And you can use the methods execCommand('Paste') and execCommand('Copy') to transfer data to and from a selected area. The following form demonstrates how to do this: Enter a text into the Send text box and click Copy. The content of the text box will be transfered to clipboard. If you click the Paste button, the clipboard's contend will be pasted into the Read text box. You can use another applications to copy text to the clipboard and paste it into the Read text box. Or you can paste the text obtained from Send in other document windows (using for instance the SendKeys trick shows above).

Copy and Paste text to/from clipboard

Send:
Read:

The HTML source code of the form shown above is rather simple:

Send: <input type=text id="input" size="40"
value="John Brown's body ...">
<input type="button" id="x1" value="Copy "
onClick={input.select();execCommand('Copy')}>
<br>
Read: <input type=text id="result" size="40" value="">
<input type="button" id="x2" value="Paste"
onClick={result.select();execCommand('Paste')}>

Well, that's the beef. You can use a simple HTML form to exchange clipboard text data with a WSH script. The script can use the Internet Explorer object model to access the necessary document elements and functions. This will be shown in the following VBScript sample. The sample creates an Internet Explorer instance and writes a small form on the fly in the document area. Then the script can read or write data to the text box located in the form, and the script can access the execCommand method to echange data with clipboard. Details may be found in the following listing.

'************************************************
' File:    HTMLClipboard.vbs (WSH sample in VBScript) 
' Author:  (c) Günter Born www.borncity.de
'
' Demonstrates how to access the clipboard using
' VBscript and Internet Explorer.
' Details may be found in the MS Press Titel:
' Microsoft Windows Script Host 2.0 Developer's Guide
'************************************************
Option Explicit

Dim oIE                      ' global object for IE
Dim txt                      ' handles clipboard text 

' Create Internet Explorer instance for clipboard access
 MakeIEDoc

' Ask user for a text 
 txt = InputBox ("Enter a text", "Paste text to clipboard", "")

' write a text into text box
 oIE.Document.All.exch.Value = txt
' Write text into IE document's text box ...
 oIE.Document.All.exch.select()
' ... and transfer it to clipboard
 oIE.Document.execCommand("Copy")

' Allow user to check the clipboard content
 WScript.Echo "Written to clipboard: " & vbCRLF & txt & vbCRLF & _
              vbCRLF & "Please check and alter the clipboard"

' read data from clipboard into text box ...
 oIE.Document.execCommand("Paste")
' and show data obtained from this text box
 WScript.Echo "Clipboard text:" & vbCRLF & oIE.Document.All.exch.Value

 oIE.Quit                     ' close Internet Explorer instance
' Ready
'###################
' Helper procedures
'###################
Sub MakeIEDoc ()
' Launch Internet Explorer & prepare a page with a text box
Dim html
' define HTML code with a text area
html = "<html><head><title>Clipboard Exchange Helper</title></head>" & _
       "<body bgcolor='silver'>" & _
       "<textarea name='exch' rows='8' cols='80'></textarea>" & _
       "</body></html>"

' *** launch Internet Explorer ***
Set oIE = WScript.CreateObject("InternetExplorer.Application")
  oIE.left=50             ' window position
  oIE.top = 100           ' and other properties
  oIE.height = 200
  oIE.width = 580
  oIE.menubar = 0         ' no menu
  oIE.toolbar = 0
  oIE.statusbar = 0
  oIE.navigate "about:" & html ' Helper window
' we keep the browser window invisible! Uncomment the
' next line, if you like to view the browser window for tests
'  oIE.visible = 1         ' keep visible

  Do While (oIE.Busy):Loop ' Important: wait till MSIE is ready
End Sub
'* End

Launch the script with a double-click to its file icon. The script shows an input dialog asking for a text. After closing the dialog, the input is pasted to the clipboard. You can use Notepad to check the clipboard's content. Then you may copy some text from Notepad to the clipboard. After closing the script's dialog box, the clipboard contend is shown in a second dialog box. BTW: uncomment the oIE.visible=1 statement in MakeIEDoc procedure will show Internet Explorer's window (the window is hidden by default).

Update for the clipboard-solution (May 2004)

Michael Turenne, one of my readers, has noticed me that the script to access the clipboard shown above fails, if more than one instance of Internet Explorer is running. In this case, WSH causes a runtime error

Error:   Object doesn't support this property or method: 'Document.All.exch'
Code:   800a01b6
Source: Microsoft VBScript runtime error

Don't know why that happens - maybe a bad implementation within IE. But there is a solution to overcome this problem. We can use the clipboardData-object and its methods setData ("Text", txt) and getData("Text") to access the clipboard from WSH scripts. Here is the sample code of a VBScript program.

'************************************************
' File:    HTMLClipboard.vbs (WSH sample in VBScript) 
' Author:  (c) Günter Born
'
' Demonstrates how to access clipboard from a
' WSH script using Internet Explorer.
' Depending on IE security settings, a warning dialog
' will occur during inserting and reading clipboard data.
'************************************************
Option Explicit

Dim oIE                      ' global object IE
Dim txt                      ' clipboard text 

' create IE instance - used for clipboard access
  Set oIE = WScript.CreateObject("InternetExplorer.Application")
  oIE.navigate "about:blank"   ' empty document
  oIE.visible = 0              ' hidden

  Do While (oIE.Busy)          ' wait till IE ready
   WScript.Sleep 50
  Loop

' query text from user
 txt = InputBox ("Please enter a text", _
    "Copy text to clipboard", "")

' copy text to clipboard
 oIE.Document.parentWindow.clipboardData.setData "Text", txt

' allow user to check clipboard
 WScript.Echo "Text copied to clipboard: " & vbCRLF & txt & vbCRLF & _
              vbCRLF & "You may check the clipboard now"

' Read text back from clipboard ...
 txt = oIE.document.parentWindow.clipboarddata.getData("text")

' show result
 WScript.Echo "Text aus Zwischenablage:" & vbCRLF & txt

 oIE.Quit                     ' close IE
'* End

Launch the script with a double-click to its file icon. The script shows an input dialog asking for a text. After closing the dialog, the input is pasted to the clipboard. You can use Notepad to check the clipboard's content. Then you may copy some text from Notepad to the clipboard. After closing the script's dialog box, the clipboard contend is shown in a second dialog box.

Further details and other tricks may be found in my German Microsoft Press title Inside Windows Script Host, 3. Ausgabe and in my English Microsoft Press title Microsoft Windows Script Host 2.0 Developer's Guide.

Planned topics for newsletter #8 and future newsletters:

Enyoy scripting, till the next newsletter arrives...

... but currently (since 2 years) I'm terrible busy with a lot of other projects (from Linux to data base acces in StarOffice/OpenOffice, from Windows inside tricks to .NET-programming). So my spare time is rare at the moment. But don't give up hope - maybe there will be some spare time to transform the material located on my hard disc to further newsletters.


(c) G. Born, 7 - May 2004 - www.borncity.de