SwiftPack: An API for SwiftPublish: PCL Document Packaging from Multiple Sources. External Spec Release 2.8 1/15/2007 Glenn Widener SwiftPack is an API to the SwiftPublish printer driver that allows application programmers to collect PCL data from multiple sources into a single package, which is a single SwiftView PCL document file. The PCL data can be PCL files, PCL data streams from an application, or a print job sent to SwiftPublish from any Windows application capable of standard Windows printing. The resulting package can be uploaded to SwiftSend by SwiftPublish or written to a local file. It can then be viewed with SwiftView or used like any normal PCL print file. SwiftPack has the following features: Inherent in SwiftPublish: - Installs either from a website (one-click download and install) or with a double-click on any Win32 system. - Installs a LaserJet 4 printer driver (if necessary) and sets its characteristics (paper size defaults, etc) as close as possible to the user's default printer. - Uploads each print job sent to this driver to the configured SwiftSend web site, and simultaneously browses to that site to allow the user to specify disposition of the job. New with SwiftPack: - Provides a graphical SwiftPublish user interface and a programmer DLL interface enabling 'start package' and 'stop package'. All jobs printed to that driver by any Windows application program between these two events become part of the package. The GUI provides a convenient interface for adding files to a package, regardless of the type of file. PCL files are appended directly; all other files are printed using the application registered to print that type of file. This is usually more convenient than explicitly starting each application, opening the desired file from that application, and printing it. - Provides file compression as an option, and encryption with a password required to decrypt as a further option. The SwiftView viewer (as of version 5.3) provides for accepting a password from the user or an application if required, and uncompressing the document. (SwiftPublish already provides unconditional zipping without passwords.) - Adds ICS commands if desired to the file to control SwiftView defaults, UI, messages and other aspects of the final viewing process. - Uses SwiftStamp to add a Universal SwiftView license. SwiftStamp+SwiftPack can be licensed for a 3rd-party software package, or licensed only for interactive creation of packages on a particular PC, using the supplied SwiftPublish user interface. In the future, we plan to provide "stamps-per-month" licensing, where the stamp count is checked on a license server, with the documents generated on any number of networked PC's. Price Structure and Licensing SwiftPack is distributed as an integral part of SwiftPublish, enabled by an optional for-fee license. Without a license, base printing, the API, and therefore the GUI disallow saving the output package to a file or stamping; only uploading to a SwiftPublish-Web-licensed server is allowed. During the 30-day demo period all functions are enabled except stamping. (apparently, before v2.5 (B#3450), batch save to file was disabled by the eval too?) SwiftPublish can be licensed in one of the following ways: - SwiftPublish can be enabled with a license token in a SwiftView LAN, WAN, or system (workstation or app server) license. There are three tokens - an old license, the newer SwiftPublish feature flag, and the even newer (v2.7.1) SwiftImport feature flag. Note that stamping for free use of SwiftView Pro Viewer or SwiftReprint is allowed from the GUI, but not from batch. This is the standard end-user workstation licensing on the price list. (before v2.5 (B#3450), batch save to file was not enabled either.) - For generating any number of files, with optional stamping, only from the SwiftPublish GUI, on a single system. The customer gets an encrypted license password tied to the system name and the customer name, plus an installer with a matching API spack.dll. This customer name appears in all stamped files (via F8 in SwiftView). The password only functions when given interactively to the GUI. Once entered, the password is stored on the system for future use. This used to be the standard end-user licensing, but we switched to the license file method above, the only difference is that the customer name doesn't appear in the stamp. (Future feature - add SwiftReprint's author info.) - Using SwiftPack API/spbatch from a 3rd-party application. A license password is provided for the customer to pass to the API from their 3rd party application code or to supply interactively to the GUI. The key is locked only to the customer name, which again is embedded in the API spack.dll in a customer-specific SwiftPublish installer. This method is available as an alternate to the license file, under the same pricing. It does not include stamping capability. For either of the last two methods above, generating any number of stamped/zipped/plain files that allow use of SwiftView Viewer, including by printing to a base printer can also be enabled. This feature is not included in the standard pricing, as it is equivalent to a full batch SwiftStamp license. Contact sales@swiftview.com for more information. To install this capability we provide two encrypted license strings and a matching customer-specific installer. These two strings are installed in the registry: HKEY_LOCAL_MACHINE\SOFTWARE\SwiftView\SwiftPublish\License (String) - the license password in GUI-only encrypted form HKEY_LOCAL_MACHINE\SOFTWARE\SwiftView\SwiftPublish\FullLicense (String) - another encryption of the full license password. For evaluation, the license is locked to a single system. For final OEM integration, the license is locked only to the customer name recorded in the spack.dll component of the customer's custom SwiftPublish installer. Application API The API is specified in terms of a C interface. The mapping to Visual Basic is straightforward, and specified below, except for a couple of complex calls for which VB equivalents are not yet available. The API is implemented in a .dll linked into the app, plus the changes to SwiftPublish - no separate application is required. We provide a simple "Package Builder" user interface on top of this API to interactively build PCL packages from files - see below. For developer convenience, a batch utility spbatch.exe is included in SwiftPublish that calls the SwiftPack API to create packages and handles low-level API operations and syncronization. Run c:\Program Files\SwiftView\spbatch -h for usage. Note that when doing batch processing it is recommended that you select a long timeout, e.g. 5 minutes, in case a printing application hangs. The source code is available as an example. Integer return values are all error codes: SUCCESS (0) for success, other values for failure. Most common (negative) error codes are defined in spack.h. Other return values return NULL on error, with an error code to the error parameter as needed, except INVALID_HANDLE_VALUE for the HANDLE return. Codes can be mapped to user-displayable strings (translatable in the future if needed) using the SPackErrorMsg() function. The caller may also check the global errno and call GetLastError(), and SPackGetLastError() for additional error codes that may be useful to report to SwiftView, Inc. for debugging purposes. To call the API, #include "spack.h" in your C or C++ code. Link your application to the dynamic spack.dll by linking to the import library spack.lib, or call LoadLibrary("spack.dll"), GetProcAddress(), and FreeLibrary() as needed. In C++ code, the required extern "C" linkage is provided by spack.h: #include "spack.h" SPack SPackOpen(void) SPack SPackCreate(void) (not implemented) int SPackRealize(SPack pack) (not implemented) SPack SPackOpenBase(void) - SPackOpen() and SPackCreate() create a new SwiftPack document package instance. SPack is a positive integer handle that is only valid within this process. Packages are created from a "base printer template" specified by the SwiftPublish attribute. This template may have a Windows printer associated with it. If the base template has a printer and the WindowsPrinter attribute is true, at SPackOpen() or SPackRealize() a Windows printer associated with the package is created. The base template printer's driver and properties are copied into the package printer. The printer instance appears in the print dialog's printer list, labeled with the friendly name. (If the friendly name is changed, it may not be updated in the printer list until the application is restarted.) Users can print to this printer from any app. The SwiftPack printer is removed when the package is closed or aborted. SPackCreate() does not create the printer, allowing the caller to configure the base SwiftPublish template and WindowsPrinter attribute beforehand, preventing expensive Windows operations. It is an error to follow SPackCreate() with SPackClose() without an intervening SPackRealize(). The new package and it's printer are created with default attribute and printer settings matching the base template (see SPackSetAttribute()). SPackOpenBase() opens a handle that accesses the default base printer. It's properties can be accessed with SPackSet/GetAttribute(), and individual print jobs, one file per job, can be sent with SPackAddFile(). Note that SPackSetAttribute() sets the parameter permanently for the current base printer. Currently there is only one global handle, ERROR_BASE_PRINTER. Setting the SwiftPublish attribute for an open base printer handle selects a different base printer, globally for all open base printer handles. SPackClose() should be called as usual when done. SPackBusy() works normally, except that it does not syncronize final file output, so serial spbatch operations with different attributes must occur on different printers, else attributes will affect the prior job. To facilitiate spbatch/DirMon operations, SPackSetAttribute(pack, "SwiftPublish", val) with a non-existent base printer name creates a new base printer with the same attributes as the current base printer. SPackAddFile() cannot currently copy pcl/text files directly to the output; sview will be called to print them. All other calls with an ERROR_BASE_PRINTER handle will return an error. If the open was unsuccessful, SPackOpen(), SPackOpenBase(), and SPackCreate() return a negative error code value other than ERROR_BASE_PRINTER. int SPackGetAttribute(SPack pack, char *name, char *valbuf) int SPackSetAttribute(SPack pack, char *name, char *value) - Get or set an attribute of the package. Name is a case-sensitive string naming an attribute. Valbuf is a client-supplied buffer of length MAX_PATH+1 (261) into which SPackGetAttribute() writes a value. Value is either a simple string or a case-insensitive string representing a boolean value: "y" or "Y" : True "n" or "N" : False The following attributes can be gotten and set: FriendlyName (string) - the "friendly printer name" for this package. NULL/"" restores the default value (recomputed based on the current packages.) SwiftPublish (string) - the friendly name of a "base SwiftPublish printer". NULL/"" restores the default. A non-existent name creates a new base printer with the same attributes as the current base printer; if pack is a base printer, subsequent attribute settings affect only the new printer. See SPackListSwiftPublishers(). WindowsPrinter (bool) - If true, a windows printer named FriendlyName is created. All printing to this printer is appended to the package. Note that if the base SwiftPublish printer, SPackGetAttribute() will return false for WindowsPrinter regardless of it's setting, and no package printer is created. Share (bool) - If true, the printer is shared as a network printer, using an automatically generated name available from the ShareName attribute. Sharing is necessary for the caller to implement port capture on NT/2K/XP+. Not implemented on 9x. Filename (string) - The name of a file to overwrite with the assembled package PCL stream. See the notes below. FilenameEnabled (bool)- if false, ignore Filename. OutputFileOnly (bool) - ignore web upload and prompt the user for a filename. Filename+FilenameEnabled overrides this setting. OutputProgram (string)- Program+args to run after SPackClose. One %s in the command is replaced with the value of Filename. Environment variables (e.g. $ProgramFiles on 2K+) may be used (see Filename). Program is run by CreateProcess() with any DOS box suppressed, as the printing user. This attribute is ignored if uploading to SwiftSend. Note that the program assumes responsibility for deleting the output Filename. Zip (bool) - zip the file? ZipPassword (string) - a zip password, NULL/"" means no encryption. ZipPasswordEnabled (bool)- if false, ignore Password, no encryption. Stamp (bool) - stamp the file? ICSCmd - an ICS command string, ignored if Stamp is false. ICSCmdEnabled (bool) - if false, ignore ICSCmd. UserID - The user name with which to login to SwiftSend. UserPassword - The password with which to login to SwiftSend. BatchDisposition(bool)- send ss_* attributes to the SwiftSend registration page, with no user interaction. ConfiguredDisposition(bool)- send ss_* attributes to the SwiftSend registration page, then perform the usual disposition page user interaction. (New in 2.4) AppendMimes - list of file mime types to append to packages instead of running the associated application. Base printer default: "application/vnd.hp-PCL;text/plain" (New in 2.3) AppendSuffixes - list of file suffixes to append to packages. Base printer default: "pcl;txt". Also added to GUI file open dialog file types. (New in 2.3) AppendTypes - list of intuited file types to append to packages. Legal values: "pcl5,pcl6,hpgl". Base printer default: "pcl5;pcl6" (New in 2.3) MimeType - The MIME type to be reported to the server at document upload. (New in 2.3) Base printer default: "application/vnd.hp-PCL" DriverType - driver data type; "pcl6" or "pcl5". Reported to server on upload. PCL packages may contain a mix of 5&6 data regardless of this setting. Ignore if MimeType isn't application/vnd.hp-PCL. Base printer is always set per driver, pcl6 if driver is pcl6, else pcl5. (New in 2.3) Color (bool) - True if the data is likely to contain color (i.e. the driver is configured to produce color data). Does not reflect manual post-installation changes to driver or driver settings. Some drivers (e.g. the HPGL+RTL 750C Mono 2K) report they do color, but only do greyscale; they report color=True here. All other greyscaling drivers I've tried do not report as color drivers. Base printer is always set per driver. Reported to server on upload. (New in 2.3) The following attributes can be gotten, but are not settable (no error occurs if they are set, but setting them is not appropriate and may have no effect): DriverName PortName - These two strings, in conjunction with the FriendlyName, constitute the "printer ID triple" that applications can use to print to the printer. In particular, they are the strings that are filled into the "printto" command entry in the Windows registry which is associated with a file suffix to print a file to a given printer (see SPackAddFile() for an example). Server - the web server root URL to publish to This will become a settable attribute in a future release. ItemCount - number of print jobs and files added to the package so far (New in 2.0.3) NULL/""=0 ShareName - printer is shared as this share name if Share is true. Currently, unset on 9x as the printer is not shared, and same as printer name otherwise. UserName (string) - The login name of the user that created this package. The base printer identified by SwiftPublish determines the printer driver, and can provide preconfigured values for any other attributes. The default values of SwiftPublish and FriendlyName at SPackOpen() or SPackCreate() are: SwiftPublish - the first installed base SwiftPublish template. See SPackListSwiftPublishers(). FriendlyName - "SwiftPublish package N", where N is the lowest positive integer not currently assigned to a package (including those still uploading in the background). For all other attributes, per-user defaults can be set and gotten by calling SPackSetAttribute() and SPackGetAttribute() with a NULL pack argument. The attributes in effect and returned by SPackGetAttribute() with a non-NULL pack argument are determined as follows: - the last setting from SPackSetAttribute(package...), if any, - else the value in the base SwiftPublish printer, if not NULL/"", - else the per-user default, if not NULL/"", - else the following fixed default values: Filename - NULL FilenameEnabled - false OutputProgram - NULL Zip - true ZipPassword - NULL ZipPasswordEnabled - false Stamp - false ICSCmd - NULL ICSCmdEnabled - false UserID - NULL UserPassword - NULL BatchDisposition - false ConfiguredDisposition - false WindowsPrinter - true Share - false When SwiftPublish is changed, all values defined by the former base printer are cleared, and the settings from the new base printer and per-user and fixed defaults are applied. Note that printer settings (e.g. in the Printer Properties dialog) are also reset to those of the base printer, discarding any user settings. Because of this, SwiftPublish is not modified by SPackSetAttribute unless the new value is actually different. If BatchDisposition, UserID, and UserPassword are all provided, no interaction with the user occurs on SPackClose(), unless an error occurs (XXX do we need to suppress error messages?) If both UserID and UserPassword are non-empty, no login dialog is presented to the user, and authentication occurs automatically, with no login dialog on failure. Also, no SwiftPublish installed version check is performed. A non-empty value for either overrides any per-user value saved from a login dialog in the registry, but does not replace it for future operations. If BatchDisposition is true, all attributes named "ss_*" are sent to the SwiftSend server to register the document, rather than starting a browser on the Registration page. A suitable default (usually empty, except for ss_title) is provided by the SwiftSend server for any empty or unset attributes. The following attributes are currently defined by SwiftSend: ss_workgroup - name of selected user's workgroup ss_title - document title string, default package name. ss_subject - document subject string ss_message - body of email message ss_ackrequired - "0" or "1" - recipient must acknowlege reciept ss_authrequired - "0" or "1" - recipient must sign in to SwiftSend ss_recipients - string of comma or whitespace separated email addresses. These recipients are not visible to each other in the email message. If ConfiguredDisposition is true, all attributes named "ss_*" are sent to the SwiftSend server, then the browser is started on the Registration page. SwiftSend initializes the Registration page from the ss_* attributes. This allows combining preconfigured attributes in your application with additional options known only to the user and the SwiftSend server. Both ConfiguredDisposition and BatchDisposition may not be supplied. The Filename attribute overrides the destination web site of SwiftPublish. Filename can contain environment variables that are replaced from the current program environment. An environment variable is "$" followed by a string of characters that are not one of "\/. $". Filename can contain the string "XXXXXX"; the "XXXXXX" is replaced with a string that makes the filename unique (up to 1000000 existing files). If Filename is a simple filename (no '/' or '\' characters), the file is placed in the Windows temporary dir, as determined from the environment variable $TMP. Relative paths should not be used, as current working directory is not defined with an asyncronous SPackClose(). Otherwise, any missing directories in the path are created as needed. For example either of: $TMP\spXXXXXX spXXXXXX will put the output file in the Windows TMP directory, named "sp", without overwriting any similarly-named existing files. Note - $TMP may be undefined, or may be defined differently than the setting for the logged-in user when printing to the base printer or closing a package asyncronously on NT/win2k, so $TMP\xxx files may be placed in "\", usually "c:\", and relative files may be placed in a different user's temporary directory, the Windows directory, or a user's application data directory. If you need to place the file in a predicatable directory writable by all users, we recommend "$ProgramFiles\SwiftView\temp\spXXXXXX.zhp". SPackGetAttribute() returns the length of the value string on success. It returns an error code on failure, zero if the attribute does not exist, and an empty value string in either case. See "Visual Basic Use" below for how to process the value in VB. You can associate data with a package by defining a client attribute with SPackSetAttribute(). The name of such an attribute must begin with "_" to prevent collisions with future attributes defined by SwiftView, Inc. If an attribute name or value is invalid, SPackSetAttribute() may return ERROR_BAD_CALL or ERROR_NOLIC; however, names and values are not completely validated on this call. Note that SPackSetAttribute("FriendlyName") is subject to Windows printer name length and character set restrictions, and cannot duplcate the name of an existing printer or the former name of a package still being uploading in the background). As a result, the new name may be modified or rejected. You should always call SPackGetAttribute("FriendlyName") to find out what the new name is. Note also that the user can change the FriendlyName at any time, and you should always call SPackGetAttribute to get the current name. int SPackSetLicenseMode(char *str, int mode) - Sets a license key that enables SwiftStamping and output to destinations other than a SwiftSend web site, enables SwiftReprint, or both. License is global to the API, for all packages and for both SwiftPublish and SwiftReprint. License is valid until modified or removed with an NULL or empty str. If the license is valid, empty, or NULL, the function returns true, regardless of which products it enables. Mode values: SPUB_TB_LICENSE: time-bomb - not saved permanently in registry SPUB_GUI_LICENSE: Saved in registry, for GUI use only SPUB_FULL_LICENSE: Saved in registry, full batch base printing licensing Except for SPUB_TB_LICENSE, the license is stored permanently in the registry, where it is also stored and read by the SwiftPublish Package Builder GUI and SwiftReprint GUI, and read to enable batch SwiftPublish printing mode. Note that in the SPUB_TB_LICENSE case the license doesn't make it to all SwiftPublish modules; because of this, it is only useful internally in SwiftPublish to setup a timebomb license that is also setup in the swiftu.dll module. int SPackSetLicense(char *str) - Same as SPackSetLicenseMode(str, SPUB_FULL_LICENSE). This function is the normal call for 3rd-party developers to apply their license password obtained from SwiftView, Inc. void SPackGetLicensee(char *customer, char *hostname, char *address) - Get the customer name, hostname, and IP address used for licensing. Purpose is to report licensing problems. Customer, hostname, and address must point to buffers of at least 128, 257, and 16 bytes respectively. int SPackIsLicensed(void) - Returns > 0 if SwiftPack has been licensed to output packages and base print jobs to a file (i.e. other than uploading to a web site), 0 otherwise. SwiftPack is licensed by setting a valid License attribute (returns SPUB_FULL_LICENSE), if the SwiftPack library and SwiftStamp are in the demo period (returns SPUB_TB_LICENSE), or if a node-locked, LAN or WAN license is found with SwiftPublish and/or SwiftReprint GUI features enabled (returns SPUB_LAN_LICENSE, SREPR_LAN_LICENSE, or SPUB_SREPR_LAN_LICENSE). SPUB_TB_LICENSE or SPUB_FULL_LICENSE also allow base printing zipping and stamping; node-locked; LAN and WAN licenses allow only package zipping and stamping. If not licensed, current Stamp attribute setting is treated as "n", Stamp and FilenameEnabled attributes cannot be set true, Filename cannot be set, and SPackAddIcsCmd() does nothing and returns SUCCESS. void SPackGetVersion(char *buf, int buflen) - Return the version string of the SwiftPack library to the supplied buffer. 550 bytes is recommended buffer length; string will be truncated if buffer is too short. New in v2.2, revised in 2.3 to give license info. int SPackBoolFromChar(char *value) char *SPackBoolFromInt(int value) void SPackVBBoolFromInt(int value, char *bool_ret) - Convert SPack boolean attribute value strings to/from integer boolean values. SPackVBBoolFromInt() is an alternative interface for VB use. It returns a string of length 1 to a caller-supplied buffer bool_ret of length 1. See "Visual Basic Use" below for more info on string buffer argument handling in VB. int SPackWrite(SPack pack, char *data, int len) - Send PCL data to this package. It is guaranteed that the data from SPackAddFile() will follow the data from a previous SPackWrite(). It is not guaranteed that the data from SPackWrite() will follow the data from a previous SPackAddFile() unless SPackAppDone() called with the HANDLE returned by SPackAddFile() returns TRUE in-between. However, the window of mis-ordering is just from when the SPackAddFile() starts the application to when the app opens the printer, which in most cases will be less than a couple of seconds. Therefore in most cases GUI apps will prefer to allow the user to start multiple print jobs in parallel, with a slight risk of mis-ordered data, rather than being unresponsive until the first application completes. Note also that if SPackClose() is called during this application startup window, the package will be closed without collecting the data from the application. Apps that want to absolutely guarantee print order must poll the HANDLE returned by SPackAddFile() with SPackAppDone() while processing Windows events, before calling SPackWrite() or SPackAddFile(). Processing Windows events is necessary, because otherwise blocking in a Windows app while waiting for another app can cause deadlocks. SPackWrite first calls SPackBusy(), and if SPackBusy() does not succeed, it's return value is returned. The caller should put up a busy indicator and retry the call once a second until success or an appropriate timeout; 30 seconds is recommended (and is hard-coded in spbatch.exe). The ItemCount attribute is incremented for each call to this function. HANDLE SPackAddFile(SPack pack, char *filename, int *errcode) - Start printing the specified file into the package. Printing for all files except PCL, text, and ICS is done using the "printto" entry in the Windows registry associated with the file suffix, e.g: HKEY_CLASSES_ROOT\.pcx: Default value: image/pcx HKEY_CLASSES_ROOT\image/pcx\Shell\printto\command: Default value: "c:\program files\swiftview\sview" -v1 -c"set quotechar '|ldoc '%1'|printer number 98 type MS_WIN command ' ' alias '%2,%3,%4'|plot 98 all" In version 2.8, a print utility can be privately integrated into SwiftPublish, without interferring with the Windows-registered printto command. The following registry entries in HKEY_LOCAL_MACHINE\SOFTWARE\SwiftView\SwiftPublish\printutil supercede those in HKEY_CLASSES_ROOT: key: .: Default value: command line password: encrypted password key use: . If the default value is present, it is a command line, same as printto, plus an optional "%5" that is replaced with a license password decrypted from the password value. It can include environment variables like "$ProgramFiles"; the variable name is ended by any of "\/. $". If the default value is not present and "use" is, it references another suffix with a command and password to use. The command "spprintinst -p password" will encrypt a password and display the result. PCL and plain DOS text files are directly appended to the package stream (using SPackAppendFile()), rather than printing with an application. This preserves the original PCL/text data (e.g. instead of rendering at 300dpi with SwiftView). A file is treated as PCL if it's suffix maps to the Content Type "application/vnd.hp-PCL" in the Windows registry, if the suffix is ".pcl", or if the contents are positively identifiable as PCL5 or PCL6 (using the same file content recognition rules as SwiftView). A file is treated as plain text if the suffix is registered to the content type "text/plain" or if the suffix is ".txt". To insure proper handling, e.g. that text files start on a new page, a PCL reset (ESC-E) is appended to each PCL or plain text file. (It is assumed that a PCL6 file is complete and correct, exiting PCL6 mode at the end as required.) In version 2.2, pcl/text files are appended only if SwiftPublish is using a PCL5 driver. In version 2.3, which suffixes and registered MIME types for the file's suffix are appended are configurable options - see AppendSuffixes and AppendMimes below. If stamping is enabled, ICS files have their contents appended to the ICS commands embedded in the stamp (the ICS file should not contain ldoc or onpage use commands). Otherwise, they are processed by printing the document defined by the ICS file to the package with SwiftView (in which case the ICS file should contain ldoc or onpage use commands). ICS files are recognized by .ics suffix or the usual "ICS" first line of the file. On successful completion of a text/PCL/ICS append, the return value is INVALID_HANDLE_VALUE, *errcode (if not NULL), is set to SUCCESS(0), and the data has been appended. In this case, you can immediately call SPackAddFile() again or SPackClose(). On an error, SPackAddFile() function returns INVALID_HANDLE_VALUE, and sets *errcode to a non-zero value. On an ERROR_NO_PRINT_COMMAND or ERROR_FAILED_START_PRINTJOB, if SPackHasOpenCmd() returns true, the application should ask the user to print the file by hand and call SPackOpenFile(). ERROR_FILE_LOCKED, ERROR_TIMED_OUT or ERROR_CANT_LOCK_PRINTER can also be returned, see SPackWrite() for handling. If the file was handled by starting an application, the SPackAddFile() return value is a win32 process handle (e.g. from CreateProcess()). The printing proceeds asyncronously, and when complete, the application will close the printer, and most applications will exit. The data is appended in the order that the printer was opened by the application, regardless of when the application started or when printing completed. As a result, immediately calling SPackAddFile() again or SPackClose() could cause misordering or loss of data. You can call SPackAppDone() to determine if the application has exited, guaranteeing in almost all cases that the printer has been opened by that point. The exceptions to this are: - An application that DDE's to an already-running copy of itself to perform the print job, then immediately exits. - Some apps such as Acrobat that never exit. - If SPack DDE'ed to a running process, SPackAddFile() returns INVALID_HANDLE_VALUE and ERROR_BUSY (170) to *errcode. In any of these cases, the only way to insure the printer is opened is to monitor SPackBusy(). So we recommend the following syncronization procedure: - If SPackAddFile() returns INVALID_HANDLE_VALUE and *errcode == SUCCESS(0) (e.g. pcl file), proceed. - Else if SPackAddFile() returns *errcode == ERROR_FILE_LOCKED, ERROR_TIMED_OUT or ERROR_CANT_LOCK_PRINTER, retry the SPackAddFile() for, say, 30 seconds, as discussed under SPackWrite(). - Else if SPackAddFile() returns INVALID_HANDLE_VALUE and *errcode != ERROR_BUSY, it's an error, so abort. - Else call SPackBusy() and SPackAppDone(), at 500 ms intervals (while processing Windows events if in a Windows app), and wait for any one of the following: - SPackAddFile() returned a valid handle (!INVALID_HANDLE_VALUE) and SPackAppDone() != STILL_ACTIVE (this step is used by spbatch.exe only with the -d option) - a transition from busy to idle reported by SPackBusy() (Could wait only until a transition to busy for better GUI interactivity, but for batch it's more efficient to wait for idle.) - a period of constant idleness reported by SPackBusy(). 30 seconds is probably enough for interactive use, 2 minutes for heavily-loaded batch operation. (an idle period is accepted by spbatch.exe without -t -1) Most of the time you will see one of the first two cases, guaranteeing syncronization without any unnecessary delays. This works on the assumption that you do this after every SPackAddFile(), and do not print to the package by any other method. If you are working with apps that DDE to themselves and exit, you should not call SPackAppDone(). The caller must release the returned handle with CloseHandle() if it is not INVALID_HANDLE_VALUE. int SPackOpenFile(char *pFileName) - Open the file with the application registered for normal viewing of files of this suffix (e.g. by double clicking in Explorer). This is based on the ShellExecute() win32 call, which uses the following registry entries: HKEY_CLASSES_ROOT\.: Default value: HKEY_CLASSES_ROOT\\Shell\xxx\command: Default value: where xxx is edit, else open, else the default value of HKEY_CLASSES_ROOT\\Shell The function returns immediately, without waiting for the command to exit. On error, the ShellExecute error code can be retrieved with SPackGetLastError(). See SPackAddFile() for syncronization with SPackBusy(). Note - there is no absolute rule whether edit or open is the best choice for some unusal file types. Clearly edit is right for scripts like .bat, while open (in a browser) rather than edit (with an html editor) is better for .htm. Some types only supply edit. int SPackDefaultPrintFile(char *pFileName) - Print the file with the registered application for files of this suffix to the default printer. This is based on the ShellExecute() win32 call, which uses the following registry entries: HKEY_CLASSES_ROOT\.: Default value: HKEY_CLASSES_ROOT\\Shell\Print\command: Default value: The function returns immediately, without waiting for the command to exit. On error, the ShellExecute error code can be retrieved with SPackGetLastError(). int SPackHasOpenCmd(char *pFileName) - Returns true if there is a registered program for files of this file's suffix, so that SPackOpenFile() can succeed. int SPackAppendFile(SPack pack, char *filename) - Append the data from the supplied filename directly to the print stream, regardless of it's type. The file must contain PCL data (note that plain text is PCL data, as long as the CR/LF handling matches). The data is appended with SPackWrite(); see SPackWrite() for handling ERROR_FILE_LOCKED, ERROR_TIMED_OUT and ERROR_CANT_LOCK_PRINTER return codes. int SPackAppDone(HANDLE hProcess) - A utility function to determine if a process has finished, and collect it's exit status. (Calls win32 GetExitCodeProcess().) Returns STILL_ACTIVE (259) if not done, 0 on successful app exit, all others are errors: ERROR_CHECKING_PROCESS (when GetExitCodeProcess() returns 0 = failed), (int)INVALID_HANDLE_VALUE, or the program's exit code. int SPackBusy(SPack pack, HANDLE *pmutex) - Checks that the package is not busy. Returns SUCCESS if not busy, or for Returns ERROR_FILE_LOCKED if a process still has the file returned by SPackGetTempFile() open, ERROR_TIMED_OUT if a print job is still being processed, or ERROR_CANT_LOCK_PRINTER if data is being written by AppendFile() or SPackWrite(), or the package is being closed or aborted. On other errors that won't eventually clear, (e.g. ERROR_PRINTER_PAUSED), the operation should be aborted with an error. Note that it's the callers responsibility to first check that no apps are preparing to start a print job. On SUCCESS, the package is locked against writes, closes, and aborts by another thread, and the associated mutex is returned to pmutex. Otherwise pmutex is set to NULL. This mutex must be released with SPackReleaseLock() so printing can continue. void SPackReleaseLock(HANDLE mutex) - Release the previously locked printer, by releasing the mutex returned by SPackBusy(). Noop if mutex is NULL. int SPackAddIcsCmd(SPack pack, char *cmd, int terminate) - Add ICS commands to the package's SwiftStamp, appending to any prior ones. May be issued at any time before SPackClose(). The ICSCmd attribute is appended at SPackClose(), overriding any corresponding commands applied with this call. The Stamp attribute must be set. As usual, ICS commands may be pipe-separated in the cmd string. If terminate is true, a newline is appended if not supplied in the cmd string. Returns an errcode on error, or SUCCESS. int SPackGetTempFile(SPack pack, char *namebuf) - Returns the name of a temporary file containing the package generated so far. It is not yet stamped or zipped, and no ICS commands are added. The file should not be modified by the caller, but can be opened and read, e.g. to preview the output so far. The buffer should be length MAX_PATH+1. (Note that the temp filename is not the same as the Filename attribute.) Note that the file will be deleted on SPackClose() or SPackAbort(). SPackGetTempFile() returns the length of the name string on success, a negative error code on failure. See "Visual Basic Use" below for more info on string buffer argument handling in VB. int SPackClose(SPack pack, int sync) - Finish the package, post processing the data as specified by the current attributes. Delete/cache the associated SwiftPack printer depending on whether caching is enabled in this installation. Implicitly calls SPackBusy() and SPackFree(). If SPackBusy() does not succeed, it's return value is returned. For any of the SPackBusy() busy errors, the caller should display busy status to the user, wait one second, and retry the call. 30 retries are recommended before giving up and displaying an error, though more may be needed if very large print jobs are submitted. The user should be allowed to retry the operation. Note, however, that there will be a delay between SPackAddFile() and the start of an application's print job, so to insure that no data is lost you must call SPackAppDone() to determine if the printing process is complete before calling SPackClose(). For any other error except ERROR_FAILED_PRINTER_DELETE, the package remains active, and can be aborted with SPackAbort(), or SPackClose() retried (e.g. ERROR_USER_CANCEL: the user canceled a dialog). On most common errors an error message has been displayed to the user. No message is displayed on ERROR_USER_CANCEL. If sync is 0, this call may return before package post processing is complete, when uploading to a web site but not when saving the package to a file. int SPackAbort(SPack pack) - Terminate the package. Cancel all print jobs and discard all temporary data. Delete/cache the associated SwiftPack printer depending on whether caching is enabled in this installation. Implicitly calls SPackFree(). This is unconditional, and the printer is guaranteed to be deleted when SPackAbort() returns. However, a delay of up to 10 seconds may occur before returning. The only error returned are ERROR_BAD_SPACK or ERROR_CANNOT_FIND_SPACK. int SPackFree(SPack pack) - Frees any in-process memory associated with the SPack, without closing or aborting the package. The package remains active, and can be subsequently reopened with SPackListActive(). Utility functions: char *SPackErrorMsg(int errcode) int SPackVBErrorMsg(int errcode, char *buf, int buflen) - Returns an error message string suitable for display to the user, corresponding to the supplied SPack error code. The message is not newline-terminated. Error code values are listed in spack.h. The string should not be modified by the caller. SPackVBErrorMsg is an alternative interface for VB use. The caller supplies a string buffer of length buflen into which SPackVBErrorMsg() writes the message, returning the string length as its value. The recommended buflen to read the largest possible error message is 256. See "Visual Basic Use" below for more info on string buffer argument handling in VB. int SPackGetLastError(void) - Returns an extra error code in addition to the error code returned from the last call, when the call returns an error. Intended for display for debug purposes only. This error code, errno, and GetLastError() all may be useful to display. Zero is OK. SPack *SPackListActive(int *count) - Returns a list of SPack objects that have been created but not closed or aborted. The number of objects is returned to the count parameter, if not NULL. All of the objects are open and all other calls are allowed on them. The caller must call SPackFree(), SPackClose(), or SPackAbort() on each package when finished. If there are no packages open, the function sets count to zero and returns (SPack *)NULL. On error, a negative error code is returned to count. The list should be freed by the caller using SPackFreePackageList(), if non-NULL. int SPackFreePackageList(SPack *package_list) - Frees a package list returned by SPackListActive(). The packages themselves are unaffected. If package_list is NULL, does nothing. char **SPackListData(SPack *package) int SPackVBListData(SPack *package, char *buf, int buflen) - Returns an array of strings, each string containing a single-line description of the data that has been sent to the package for an item that has been sent. Strings are listed in the order sent, and are not newline-terminated. If NULL is returned, no data has been sent. XXX details TBD - should give total size of each, timestamp?. Note which are from print jobs and give the print job title, which are from SPackWrites, so client code would be able to replace written data items with a filename. SPackVBListData is an alternative interface for VB use. The caller supplies a buffer into which SPackVBListData() writes the lines, each line terminated by cr+lf. If the buffer is too small, SPackVBListData() returns the required buffer size as it's value, else it returns 0 on success. XXX not implemented for now. XXX Both TBD char **SPackListSwiftPublishers(int *count) - Returns a list of the permanently installed base SwiftPublish templates, each of which sends the output to a configured destination. The list is (most likely) in the order of installation; the first one in the list is the default printer on SPackOpen(). The number of strings returned is returned to the count parameter, if not NULL. The strings are "friendly printer names", and are valid settings for the SwiftPublish attribute. If there are no printers, count is set to zero and NULL is returned. The list should be freed by the caller using SPackFreeStringList(). int SPackFreeStringList(char **list, int count) - Frees a list of printer strings as returned by SPackListSwiftPublishers() or SPackListData(). If count is zero or list is NULL, does nothing. Note that if a package close is interrupted (e.g. by a system crash), on reboot it will be restored to the active state. Visual Basic Use The spack.dll can be called from VB. Here are the VB declarations you need to call the API: Public Declare Function SPackOpen Lib "spack" _ () As Long Public Declare Function SPackClose Lib "spack" _ (ByVal spack As Long, _ ByVal sync As Long) As Long Public Declare Function SPackAddFile Lib "spack" _ (ByVal spack As Long, _ ByVal filename As String, _ ByRef errcode As Long) As Long Public Declare Function SPackOpenFile Lib "spack" _ (ByVal filename As String) As Long Public Declare Function SPackHasOpenCmd Lib "spack" _ (ByVal filename As String) As Long Public Declare Function SPackWrite Lib "spack" _ (ByVal spack As Long, _ ByVal data As String, _ ByVal length As Long) As Long Public Declare Function SPackAppendFile Lib "spack" _ (ByVal spack As Long, _ ByVal filename As String) As Long Public Declare Function SPackAddIcsCmd Lib "spack" _ (ByVal spack As Long, _ ByVal cmd As String, _ ByVal terminate As Long) As Long Public Declare Function SPackListActive Lib "spack" _ (count As Long) As Any XXX return array??? Public Declare Function SPackListSwiftPublishers Lib "spack" _ (count As Long) As Any XXX return array of string????_ Public Declare Function SPackFreePackageList Lib "spack" _ (ByVal package_list As Long) As Long Public Declare Function SPackFreeStringList Lib "spack" _ (ByVal strlist As Any, _ XXX can we use As String here? ByVal count As Long) As Long Public Declare Function SPackFree Lib "spack" _ (ByVal spack As Long) As Long Public Declare Function SPackAbort Lib "spack" _ (ByVal spack As Long) As Long Public Declare Function SPackGetTempFile Lib "spack" _ (ByVal spack As Long, _ ByVal namebuf As String) As Long Public Declare Function SPackBusy Lib "spack" _ (ByVal spack As Long, _ ByRef pmutex As Long) As Long Public Declare Sub SPackReleaseLock Lib "spack" _ (ByVal mutex As Long) Public Declare Function SPackAppDone Lib "spack" _ (ByVal handle As Long) As Long Public Declare Function SPackSetAttribute Lib "spack" _ (ByVal spack As Long, _ ByVal name As String, _ ByVal value As String) As Long Public Declare Function SPackGetAttribute Lib "spack" _ (ByVal spack As Long, _ ByVal name As String, _ ByVal valbuf As String) As Long Public Declare Function SPackSetLicense Lib "spack" _ (ByVal value As String) As Long Public Declare Sub SPackGetLicensee Lib "spack" _ (ByVal customer As String, _ ByVal hostname As String, _ ByVal address As String) Public Declare Function SPackIsLicensed Lib "spack" _ () As Long Public Declare Function SPackBoolFromChar Lib "spack" _ (ByVal value As String) As Long Public Declare Sub SPackVBBoolFromInt Lib "spack" _ (ByVal value As Long, _ ByVal bool_ret As String) Public Declare Function SPackVBErrorMsg Lib "spack" _ (ByVal errcode As Long, _ ByVal buf As String, _ ByVal bufsiz As Long) As Long Public Declare Function SPackGetLastError Lib "spack" () As Long If VB calls a C function like SPackGetAttribute() that takes a char * buffer, the C code simply fills the buffer with a NULL terminated string, and the VB code must do the following: - Define a string of the necessary buffer length, and fill it with NULL's. This is a very important step, because at this point this is an array of characters and not a Visual Basic String. - Call the function with this String. - After the call, use VB's Left() to convert the string into Visual Basic BSTR format: cb_str = String(261, 0) cb_size = SPackGetAttribute(spack, "FriendlyName", cb_str) If cb_size >= 0 Then ' Resize the string to the length returned by the dll cb_str = Left(cb_str, cb_size) ... See the vb sample program for an example. Known Problems SPackListData is not implemented. SPackListActive, SPackListSwiftPublishers not available from VB. Developers should note carefully the timeout return codes that they must handle. For example, ERROR_CANT_LOCK_PRINTER should only occur when packages are being shared by multiple users.