George is a software engineer in the System Design and Verification group at Cadence Design Systems. He can be reached at [email protected].
Automating Web Page Prints
by Jack Xue
Among the threads I run across on the Internet are: 1. Internet Explorer (IE) users who frequently want to print out specified web pages but don't want to click the OK button on a printing dialog, and 2. web application developers who need to push an HTML file to the client side and print it out without the prompt.
The printing template technique offered by IE 5.5 (and later) may solve some of these problems. Here, I present two more complete alternative solutions that apply to IE 4.0 (and later) running on Windows 98/NT/2000. They work on the client and server side, and also fit for the web service. The first solution is ScriptX, a JavaScript program that uses a free ActiveX control (http://www.meadroid.com/). The second is KiXtart, a standalone application driving another free Windows script (http://www.kixtart.org/).
ScriptX is a nonvisual ActiveX control developed to provide full control over document printing. The basic functionality of ScriptX, which my solution relies on, is freely distributable. Listing One downloads http://www.google.com/ and prints it at the user's default printer without interaction. The ScriptX object is included in the HTML file via the tag <object>. A frameset refers to the web page. Every time the metacommand refresh reloads the frameset, the web page is reloaded through a frame in the frameset. The method onload verifies the existence of the control and sets the header and footer of the print job before calling the method PrintThis, which applies the control's print method. The parameter false for the method print means "do not prompt." The ScriptX package should be downloaded and installed in a directory pointed to by the codebase. Generating this HTML file with a JSP or an ASP, a web application enables its IE users to regularly download and print a desired HTML file. In other words, this automation can be initiated not only from the client but also from the server side. However, when security is important, or if a mismatch exists in user sessions and refreshing time intervals, the clients may be not satisfied with this solution, which motivates us to look for a better method.
If IE users are willing and able to run a standalone application, a more powerful automation can be provided. This solution is based on using RUNDLL32.EXE and MSHTML.DLL (part of the Windows Print API) and also relies on KiXtart, a pure Windows scripting package, which programmatically clicks the OK button in the printing dialog (okay, it's ugly, but bear with methis is cool). The Java class AutoPrint.java (Listing Two) provides the refreshing time interval to download the web page and save it in a local directory, to start a Windows batch file to run RUNDLL32.EXE with MSHTML.DLL, and finally to run KiXtart to close the prompt. This batch file (Listing Three) is a boiled-down version of PrintPDF.bat written by Rob van der Woude (http://www.robvanderwoude.com/). The solution lets you use any networked printer. The path to MSHTML.DLL may be different on a different machine. The printer path can be found in the Registry. The directory where KiXtart is installed should be a part of the PATH. The user authentication can be implemented as a part of the URL connection process. Moreover, this solution provides a framework to host the web service client-side code, which carries out the object internalization and externalization and the implementation of SSL. Thanks to Jerry Mead of Mead Inc. and Rob van der Woude for their valuable help.
Building a Static Library For Device Drivers
by Alan MacInnes
This tip might seem straightforward to users of Visual Studio or other GUI-enhanced, one-stop-shopping centers that most Windows-based development environments have become today. However, the Windows Driver Development Kit (DDK) is still rather neolithic, and the most commonly used model is to start with a standard example and modify it. To that end, I present a static library of routines used by device drivers that can be easily built under the DDK. This fills a hole in the DDK documentation, as far as I can tell, and should be helpful to some readers. The static library, which is a .lib file (of course), can then be included in any build for a device driver that calls one or more routines in the static library.
An example SOURCES file for building a static library is as follows:
!IF 0
This is the "SOURCES" file for building the
static library.
!ENDIF
TARGETNAME=StaticLibraryTemplate
TARGETTYPE=DRIVER_LIBRARY
TARGETPATH=$(DDK_LIB_DEST)
SOURCES=StaticLibraryRoutineOne.c
StaticLibraryRoutineTwo.c
The generated .lib file is placed in the folder specified by the environment variable DDK_LIB_DEST. This environment variable is automatically defined for you by the DDK build environment. In the SOURCES file used for building a device driver, the following line would be included to refer to this static library:
TARGETLIBS=
$(DDK_LIB_PATH)\StaticLibraryTemplate.lib
The environment variable DDK_LIB_PATH refers to the same folder as DDK_LIB_DEST. Listing Four is the source code for the trivial sample routines in the static libraryStaticLibraryRoutineOne.c, StaticLibraryRoutineTwo.c, and StaticLibraryTemplate.h.
DDJ
Listing One
<html> <head> <meta http-equiv="Refresh" content="60"> <!-- MeadCo ScriptX --> <object id="factory" viewastext style="display:none" classid="clsid:1663ed61-23eb-11d2-b92f-008048fdd814" codebase="c:\scriptx\ScriptX.cab#Version=6,1,430,5"> </object> <script defer> function onload(){ if (!factory.object){ alert("Onload cannot find ScriptX Control."); return; } factory.printing.header = "Test Header"; factory.printing.footer = "Test Footer"; PrintThis(); } function PrintThis(){ if (!factory.object){ alert("PrintThis cannot find ScriptX Control."); return; } factory.printing.print(false); } </script> </head> <frameset name="frameset" rows="*" onload="onload()"> <frame name="frame_for_web_site" src="http://www.google.com"> </frameset> </html>
Listing Two
import java.io.*; import java.net.*; import java.util.*; public class AutoPrint{ public static void main(String arg[]){ // params for calling printHtml.bat String args[] = new String[3]; args[0] = new String("c:\\autoprint\\printHtml.bat"); args[1] = new String("c:\\autoprint\\testpage.html"); args[2] = new String("\\\\printer_server\\a_printer"); String testSite = "http://www.google.com"; // the main loop while (true) { // retrieve the web page StringBuffer sb = new StringBuffer(100000); try { URL url = new URL(testSite); InputStream in = url.openStream(); in = new BufferedInputStream(in); Reader reader = new InputStreamReader(in); int c; while ((c = reader.read()) != -1){ sb.append((char)c); } in.close(); } catch (MalformedURLException e) { System.err.println("Malformed: " + e); } catch (IOException e) { System.err.println("I/O Exception: " + e); } // save to a file File file = new File(args[1]); try{ FileWriter fw = new FileWriter(file); fw.write(sb.toString()); fw.close(); } catch (Exception e){ System.out.println(e.toString()); } // is the file readable? if (!file.canRead()){ System.out.println("Cannot read the file."); return; } // call printHtml.bat Process p = null; try { p = Runtime.getRuntime().exec(args); } catch (Exception e) { System.out.println(e.toString()); } // wait for printHtml.bat to finish try { p.waitFor(); } catch (InterruptedException e) { // need to do nothing. } // make a log Date date = new Date(); System.out.println(date.toString() + " printHtml.bat called."); // be ready for next iteration try { Thread.sleep(600000); if(!file.delete()) { System.out.println("Cannot delete the file."); return; } } catch (Exception e) { System.out.println(e.toString()); } } } }
Listing Three
@ECHO OFF SETLOCAL SET File="%1" SET Printer="%2" IF NOT DEFINED File GOTO End IF NOT DEFINED Printer GOTO End :: Create a temporary Kix file to "press" Print button> %TEMP%.\%~n0.kix ECHO.; Wait a few seconds for the Print dialog to appear> >%TEMP%.\%~n0.kix ECHO SLEEP 2> >%TEMP%.\%~n0.kix ECHO.; Press "Print" (Enter) using SendKeys function> >%TEMP%.\%~n0.kix ECHO IF SETFOCUS("Print") = 0> >%TEMP%.\%~n0.kix ECHO $RC = SENDKEYS("{ENTER}")> >%TEMP%.\%~n0.kix ECHO ENDIF :: Actual print command START RUNDLL32.EXE c:\winnt\system32\MSHTML.DLL,PrintHTML %File% %Printer% :: Call the temporary Kix file to "press" Print button, then delete it START /WAIT KIX32.EXE %TEMP%.\%~n0.kix DEL %TEMP%.\%~n0.kix :End ENDLOCAL
Listing Four
/* StaticLibraryRoutineOne.c */ #include <ntddk.h> #include "StaticLibraryTemplate.h" VOID FirstRoutine() { DbgPrint("First routine entered in Static Library\n"); } /* StaticLibraryRoutineTwo.c */ #include <ntddk.h> #include "StaticLibraryTemplate.h" VOID SecondRoutine() { DbgPrint("Second routine entered in Static Library\n"); } /* StaticLibraryTemplate.h */ // Function prototypes for the two routines in the static library. VOID FirstRoutine(); VOID SecondRoutine();