' ********************************************************************************
' Program: SWiP.PF3      SWiP-SmartWare Windows Printing    v3.2.0.0 - 25 May 2004 BETA
'
' Produced By:  I O Systech Ltd           Author: Neil Parrish
' Website:      www.iosystech.co.uk        Email: enquiries@iosystech.co.uk
' Phone:        +44 (0)7041 420182           Fax: +44 (0)8707 064666
'               +44 (0)1784 432058
' In association with: Smart Systems (Essex) Ltd - Alan Wilson +44 (0)1206 549291
'
' The functions of this library integrate Microsoft Word and Seagate Software/
' Crystal Decisions Crystal Reports printing with SmartWare. They depend on the
' ODBC driver of SmartWare 2000 & 4 and have been developed with Crystal version 7
' & Word 97 (also tested with Crystal v8.5 and Word 2000 & 2002/XP on Windows 9.x,
' NT 4 & XP Pro). Users require Word and Crystal reports support (royalty free
' distribution) installed.
'
' SWiP includes VBA code for your Word document templates and the SWiPConf.exe
' printer configurator for the optional automatic printer/tray selection features
' that can be used with both Crystal and Word printing. It also includes sample
' master Word template and ini files. See the documentation provided with SWiP
' for function instructions. The professional version of the product includes this
' source code, the Delphi source for SWiPConf.exe and additional documentation.
'
' Source code may not be distributed or used as part of a competitive product. Any
' variations of the product produced from the source code require licences for
' distribution to external clients.
' ********************************************************************************

GLOBAL _FindSWWinHandle()   'finds SmartWare windows handle (uses EnumWindows)
PUBLIC _SWiP_EnumWinProc(1) 'callback function used by _FindSWWinHandle()
PUBLIC _SWiP_CLoad()        'preloads the Crystal reports DLL
PUBLIC _SWiP_CUnLoad()      'unloads the Crystal reports DLL
PUBLIC _SWiP_Crystal(5)     'prints/previews a Crystal report
PUBLIC _SWiP_Print(5)	   'prints/previews a Crystal report
PUBLIC _SWiP_Word(5)        'prints/processes a Word template (via VBA code)
PUBLIC _SWiP_WordWait(1)    'waits/checks for the finish of previous Word job
PUBLIC _SWiP_ShortPN(1)     'returns the short version of a path
PUBLIC _SWiP_Windialog(3)   'windows dialog box function - returns button pressed
PUBLIC _SWiP_GetVersion(2)  'returns version number of a file
PUBLIC _SWiP_EnumWordWin(1) 'callback used to check if Word(2000) is running
GLOBAL _GetDirs(1)          'returns system, windows or temp directory
GLOBAL _PrinterDetails(1)   'reads printer parameters from ini file
GLOBAL _VarCheck(1)         'defaults any SWiP variables set incorrectly

PUBLIC #SWiP_SWWinH #SWiP_WPreWait #SWiP_WPostWait #SWiP_WWaitMS #SWiP_WordVer
PUBLIC #SWiP_CDialog #SWiP_Duplex #SWiP_CMax #SWiP_CPrintBtn #SWiP_CPSetupBtn
PUBLIC #SWiP_CExportBtn #SWiP_CCollated  #SWiP_CPrevWinWait #SWiP_CMajorV
PUBLIC #SWiP_CMinorV #SWiP_Error #SWiP_ErrMess
PUBLIC $SWiP_WinDir $SWiP_SysDir $SWiP_TempDir $SWiP_Version $SWiP_WordPath
PUBLIC $SWiP_DefaultWI $SWiP_WordIni $SWiP_PrintIni $SWiP_CrystalDir

GLOBAL #procID #enumWinCB #handle #psource1 #psource2 #crw_hnd #retlength
GLOBAL #dllkernel32 #dlluser32 #retbuff #procstatus #foundWord
GLOBAL $retstring $device $port $driver $default

MAIN
	$SWiP_Version = "SWiP v3.2.0.0 - 26 May 2004 BETA"

	#SWiP_SWWinH = _FindSWWinHandle()
	$SWiP_SysDir = _GetDirs(1)
	$SWiP_WinDir = _GetDirs(2)
	$SWiP_TempDir = _GetDirs(3)

	#SWiP_Error = 0
	$SWiP_CrystalDir = ""
	#procID = 0     'set to stop Word waiting when first job is run
	$SWiP_WordPath = ""
	#SWiP_WPreWait = 1    '0 off, 1 wait, 2 check
	#SWiP_WPostWait = 0   '0 off, 1 on
	#SWiP_WWaitMS = 1000
	#SWiP_WordVer = 0
	$SWiP_DefaultWI = "SWiPWord.ini"
	$SWiP_WordIni = $SWiP_DefaultWI
	$SWiP_PrintIni = "SWiPrint.ini"
	#SWiP_CDialog = 1
	#SWiP_Duplex = 1      '1 simplex, 2 vertical, 3 horizontal
	#SWiP_CMax = 0
	#SWiP_CPrintBtn = 1
	#SWiP_CPSetupBtn = 1
	#SWiP_CExportBtn = 1
	#SWiP_CCollated = 2   '0 uncollated, 1 collated, 2 default
	#SWiP_CPrevWinWait = 0
	#SWiP_CMajorV = 0
	#SWiP_CMinorV = 0
	#SWiP_ErrMess = 1
END MAIN


FUNCTION _FindSWWinHandle()
' This function is needed as Angoss does not declare it's handle anywhere.
' The use of EnumWindows and callbacks ensures the correct handle is found
' unlike the standard GetActiveWindow & FindWindow calls which both have drawbacks
	DLL LOAD "kernel32.dll" #dllkernel32
		DLL CALL #dllkernel32 FUNCTION "GetCurrentProcessId" STACK ""
		#procID = GETREG(AX)
	DLL UNLOAD #dllkernel32
	DLL LOAD "user32.dll" #dlluser32
		#enumWinCB = CALLBACK_CREATE("_SWiP_EnumWinProc",2,2)
		DLL CALL #dlluser32 FUNCTION "EnumWindows" STACK "LL" #enumWinCB 0
		CALLBACK_DELETE(#enumWinCB)
	DLL UNLOAD #dlluser32
	RETURN #handle
END FUNCTION

FUNCTION _SWiP_EnumWinProc(#hnd)
' Callback function used by _FindSWWunHandle()
 LOCAL #param #testPID #PIDmem $str
	UNPACK #hnd "LL" #handle #param
	MEMALLOC #PIDmem SIZEOF "P"
	MEMPACK #PIDmem "P" 0
	DLL CALL #dlluser32 FUNCTION "GetWindowThreadProcessId" STACK "LP" #handle #PIDmem
	MEMUNPACK #PIDmem "P" #testPID
	MEMFREE #PIDmem
	IF #testPID = #procID
		$str = REPEAT(" ",21)
		DLL CALL #dlluser32 FUNCTION "GetClassNameA" STACK"LPL" #handle DOSPTR($str) 20
		IF LEFT($str,GETREG(AX)) = "ANGOSS Frame" OR LEFT($str,GETREG(AX)) = "SmartWare Frame"
			SETREG(AX,0)
		ELSE
			SETREG(AX,1)
		END IF
	ELSE
		SETREG(AX,1)
	END IF
END FUNCTION

FUNCTION _SWiP_CLoad()
' Loads the Crystal DLL - called by _SWiP_Crystal() but may be called on application
' initialisation to avoid delay when first calling _SWiP_Crystal()
	IF #crw_hnd = 0   'no need to reload the DLL if it has already been loaded
		IF $SWiP_CrystalDir <> ""
			DLL LOAD $SWiP_CrystalDir|"crpe32.dll" #crw_hnd
		END IF
		IF #crw_hnd = 0
			DLL LOAD $SWiP_SysDir|"crpe32.dll" #crw_hnd
		END IF
		IF #crw_hnd = 0
			IF #SWiP_ErrMess
				_SWiP_Windialog("Crystal reports DLL cannot be loaded for SWiP !"|CHR(13)|"Has Crystal support been installed on this PC ?","Crystal report (SWiP)",4112)
			END IF
			#SWiP_Error = 1040
			RETURN 0
		ELSE  'Loaded - now check the version
			DLL CALL #crw_hnd FUNCTION "PEGetVersion" STACK "L" 100
			#SWiP_CMajorV = INT(BITAND(GETREG(AX),65280)/256)
			#SWiP_CMinorV = BITAND(GETREG(AX),255)
			IF #SWiP_CMajorV < 7
				IF #SWiP_ErrMess
					_SWiP_Windialog("Crystal reports version ("|STR(#SWiP_CMajorV)|"."|STR(#SWiP_CMinorV)|") is too low for SWiP !"|CHR(13)|"SWiP supports Crystal version 7 and above.","Crystal report (SWiP)",4112)
 				END IF
				#SWiP_Error = 1050
				_SWIP_CUnLoad()
				RETURN 0
			END IF
		END IF
	END IF
	RETURN 1
END FUNCTION

FUNCTION _SWiP_CUnLoad()
' Can be called to unload the Crystal DLL - unloaded automaticaly when SmartWare closes.
	IF #crw_hnd <> 0
		DLL CALL #crw_hnd FUNCTION "PECloseEngine"
		DLL UNLOAD #crw_hnd
		#crw_hnd = 0
	END IF
END FUNCTION

' FUNCTION _SWiP_Crystal($report,$mode,#printer,#copies,$misc)

FUNCTION _SWiP_Crystal($report,$mode,#printer,#copies,$misc)
'prints report as requested AND then prints to the screen as well: introduced to circumvent Printing problem
'on change to Smartware

	_SWIP_PRINT($report,$mode,#printer,#copies,$misc)

' 	_SWIP_PRINT($report,"S",#printer,#copies,"Print Now")	' REM this line out when screen print not needed

END FUNCTION '_SWiP_Crystal()

FUNCTION _SWiP_Print($report,$mode,#printer,#copies,$misc)
'Previews or prints a Crystal report, takes 5 parameters
'  $report  - name & path of the Crystal report (.rpt can be omitted)
'  $mode    - "P" print, "S" screen preview, "E" user specified export
'             "EP" export to PDF - (upper or lower case)
'  #printer - printer number for automatic printing (0 for Win default)
'  #copies  - number of copies to print, only applies to "P" mode
'  $misc    - usage varies by mode
'              mode "S"  - window title for preview window
'              mode "EP" - name of file to be created
'Variables also affect the function. See SWiP documentation for full details.
 LOCAL #cwdefault #pjob #windopt #winspool #devmode #dmfields #orient #pages
 LOCAL #ph1 #pl1 #ph2 #pl2 #ph3 #pl3 #dm2 #temphnd #prntopt #style #i #j #k #repeat
 LOCAL #formatopt #diskopt #expopt
	#SWiP_Error = 0
	#cwdefault = 0x80000000
	_VarCheck("C")
	$mode = UPPER($mode)
	IF CASE($mode)("P",0)("S",0)("E",0)("EP",0) ELSE 1
		IF #SWiP_ErrMess
			_SWiP_Windialog("Invalid mode selected for output","Crystal report"&$report,4112)
		END IF
		#SWiP_Error = 1010
		RETURN 0
	END IF
	IF LEFT($mode,1) = "E"
		#printer = 0
	END IF
	IF $mode <> "P"
		#copies = 1
	END IF
	IF LOWER(RIGHT($report,4)) <> ".rpt"
		$report = $report|".rpt"
	END IF
	IF NOT(FILE(_SWiP_ShortPN($report)))
		IF #SWiP_ErrMess
			_SWiP_Windialog("The report file does not exist","Crystal report"&$report,4112)
		END IF
		#SWiP_Error = 1020
		RETURN 0
	END IF
	IF NOT(ISNUMBER(#copies)) OR #copies < 1
		IF #SWiP_ErrMess
			_SWiP_Windialog("Invalid number of copies specified","Crystal report"&$report,4112)
		END IF
		#SWiP_Error = 1030
		RETURN 0
	END IF
	IF _SWiP_CLoad() = 0 'Load the Crystal DLL (may already be loaded)
		RETURN 0     'Error number already set and reported by function
	END IF
	#printer = _PrinterDetails(#printer)
	IF #printer <> 0
		DLL LOAD "winspool.drv" #winspool  'N.B. winspool is a drv not dll
		DLL CALL #winspool FUNCTION "DocumentPropertiesA" STACK "LLPPPL" \
			0 0 DOSPTR($device) 0 0 0
		BUFFER #devmode SIZE GETREG(AX)
		DLL CALL #winspool FUNCTION "DocumentPropertiesA" STACK "LLPPPL" \
			0 0 DOSPTR($device) DOSPTR(#devmode) 0 2  '2=DM_OUT_BUFFER
		DLL UNLOAD #winspool
		PEEK 0 DOSPTR(#devmode)+40 "P" #dmfields
	END IF
	DLL CALL #crw_hnd FUNCTION "PEOpenEngine" STACK ""
	DLL CALL #crw_hnd FUNCTION "PEOpenPrintJob" STACK "P" DOSPTR($report)
	#pjob = GETREG(AX)

'If a printer is specified look to see if the report is landscape. Modify the devmode for the printer
'if it is and also if we want duplex printing. Paper source will be set later on.
	IF #printer <> 0
		#ph1 = 0
		#pl1 = 0
		#ph2 = 0
		#pl2 = 0
		#ph3 = 0
		#pl3 = 0
		#dm2 = 0
		DLL CALL #crw_hnd FUNCTION "PEGetSelectedPrinter" STACK "LPPPPPPP" \
			#pjob DOSPTR(#ph1) DOSPTR(#pl1) DOSPTR(#ph2) DOSPTR(#pl2) DOSPTR(#ph3) DOSPTR(#pl3) DOSPTR(#dm2)
		#orient = 0
		IF #dm2 <> 0
			PEEK 0 DOSPTR(#dm2) "P" #temphnd
			#dm2 = #temphnd
			PEEK 0 #dm2+44 "I" #orient
		END IF
		IF #orient = 0
			#dmfields = BITOR(#dmfields,64)
		ELSE
			#dmfields = BITOR(#dmfields,65)
			POKE 0 DOSPTR(#devmode)+44 "I" #orient
		END IF
		IF CASE #SWiP_Duplex(2,1)(3,1) ELSE 0
			#dmfields = BITOR(#dmfields,512)
			POKE 0 DOSPTR(#devmode)+62 "I" #SWiP_Duplex
		END IF
		POKE 0 DOSPTR(#devmode)+40 "P" #dmfields
	END IF

'Control the displaying of the progress dialog box
	DLL CALL #crw_hnd FUNCTION "PEEnableProgressDialog" STACK "LL" #pjob #SWiP_CDialog

'Initialise and populate the print options structure
	IF (#psource1 <> #psource2 AND $mode = "P") OR #SWiP_CCollated < 2
		BUFFER #prntopt SIZE 522
		POKE 0 DOSPTR(#prntopt) "W" 522
		DLL CALL #crw_hnd FUNCTION "PEGetPrintOptions" STACK "LP" #pjob DOSPTR(#prntopt)
		IF #SWiP_CCollated < 2
			POKE 0 DOSPTR(#prntopt)+8 "W" #SWiP_CCollated
		END IF
	END IF

	#pages = 0
	IF $mode = "P" AND (#SWiP_CCollated = 1 OR #SWiP_CCollated = 2) AND #psource1 <> #psource2 AND #copies > 1
		#repeat = #copies
		#copies = 1
	ELSE
		#repeat = 1
	END IF

	FOR #i = 1 TO #repeat
		IF (#psource1 <> #psource2 AND $mode = "P") OR #SWiP_CCollated < 2
			IF #psource1 <> #psource2 AND $mode = "P"
				IF CASE #SWiP_Duplex(2,1)(3,1) ELSE 0
					POKE 0 DOSPTR(#prntopt)+2 "WW" 1 2
				ELSE
					POKE 0 DOSPTR(#prntopt)+2 "WW" 1 1
				END IF
			END IF
			IF #SWiP_CCollated = 0 AND #copies > 1 AND (CASE #SWiP_Duplex(2,1)(3,1) ELSE 0) AND #printer <> 0
				POKE 0 DOSPTR(#prntopt)+2 "WW" 1 2
			END IF
			DLL CALL #crw_hnd FUNCTION "PESetPrintOptions" STACK "LP" #pjob DOSPTR(#prntopt)
		END IF

'If a printer is selected then put the first tray details in the devmode and select the printer for the report
		IF #printer <> 0
			POKE 0 DOSPTR(#devmode)+56 "I" #psource1
			DLL CALL #crw_hnd FUNCTION "PESelectPrinter" STACK "LPPPP" \
				#pjob DOSPTR($driver) DOSPTR($device) DOSPTR($port) DOSPTR(#devmode)
			IF GETREG(AX) = 0 AND #SWiP_ErrMess  'Warn that the printer could not be selected
				_SWiP_Windialog("Crystal reports cannot select the specified printer"|CHR(13)|"The default printer will be used","Crystal report (SWiP)",4144)
			END IF
		END IF

		IF $mode = "S"  'N.B. v7 dev help omits last 2 PEWindowOptions params - showToolbarTips(true) - showDocumentTips(false)
			BUFFER #windopt SIZE 30
			PACK #windopt "WIIIIIIIIIIIIII" 30 0-1 0-1 0-1 0-1 #SWiP_CPrintBtn #SWiP_CExportbtn 0-1 0-1 0-1 0-1 #SWiP_CPSetupBtn 0-1 0-1 0-1
			DLL CALL #crw_hnd FUNCTION "PESetWindowOptions" STACK "LP" #pjob DOSPTR(#windopt)
			IF #SWiP_CMax
				#style =  16777216 + 268435456 + 262144 + 524288 + 65536 + 131072 'maximised + standard
			ELSE
				#style = 0 'default
			END IF
			DLL CALL #crw_hnd FUNCTION "PEOutputToWindow" STACK "LPLLLLLL" \
				#pjob DOSPTR($misc) #cwdefault #cwdefault #cwdefault #cwdefault #style 0
			DLL CALL #crw_hnd FUNCTION "PEStartPrintJob" STACK "LL" #pjob 1
		ELSEIF LEFT($mode,1) = "E"
			IF $mode = "E"
'allow room for expansion in the buffers
				BUFFER #formatopt SIZE 50
				PACK #formatopt "W" 50
				BUFFER #diskopt SIZE 24
				PACK #diskopt "W" 50
				BUFFER #expopt SIZE 158
				PACK #expopt "W>64C>PP>64C>PP>W>W" 158 DOSPTR(#formatopt) DOSPTR(#diskopt)
				DLL CALL #crw_hnd FUNCTION "PEGetExportOptions" STACK "LP" #pjob DOSPTR(#expopt)
			ELSE
				BUFFER #formatopt SIZE 10
				PACK #formatopt "WPWW" 10 0 0 0
				BUFFER #diskopt SIZE 6
				PACK #diskopt "WP" 6 DOSPTR($misc)
				BUFFER #expopt SIZE 150
				PACK #expopt "W>64CPP>64CPPWW" 150 0 DOSPTR(#formatopt) 0 DOSPTR(#diskopt) 0 0
				PACK #expopt[3] "C" ASC("c")
				PACK #expopt[4] "C" ASC("r")
				PACK #expopt[5] "C" ASC("x")
				PACK #expopt[6] "C" ASC("f")
				PACK #expopt[7] "C" ASC("_")
				PACK #expopt[8] "C" ASC("p")
				PACK #expopt[9] "C" ASC("d")
				PACK #expopt[10] "C" ASC("f")
				for #ph1 = 11 to 66
					PACK #expopt[#ph1] "C" 0
				end for
				PACK #expopt[75] "C" ASC("u")
				PACK #expopt[76] "C" ASC("2")
				PACK #expopt[77] "C" ASC("d")
				PACK #expopt[78] "C" ASC("d")
				PACK #expopt[79] "C" ASC("i")
				PACK #expopt[80] "C" ASC("s")
				PACK #expopt[81] "C" ASC("k")
				for #ph1 = 82 to 138
					PACK #expopt[#ph1] "C" 0
				end for
			END IF
			DLL CALL #crw_hnd FUNCTION "PEExportTo" STACK "LP" #pjob DOSPTR(#expopt)
			DLL CALL #crw_hnd FUNCTION "PEStartPrintJob" STACK "LL" #pjob 1
		ELSEIF (CASE #SWiP_Duplex (2,1)(3,1) ELSE 0) AND #SWiP_CCollated = 0 AND #copies > 1 AND #printer <> 0
			FOR #k = 1 to #copies
				DLL CALL #crw_hnd FUNCTION "PEOutputToPrinter" STACK "LL" #pjob 1
				IF #i = 1 AND #k = 1
					DLL CALL #crw_hnd FUNCTION "PEGetNPages" STACK "L" #pjob
					#pages = BITAND(GETREG(AX),65535)
				END IF
				DLL CALL #crw_hnd FUNCTION "PEStartPrintJob" STACK "LL" #pjob 1
			END FOR
		ELSE
			DLL CALL #crw_hnd FUNCTION "PEOutputToPrinter" STACK "LL" #pjob #copies
			IF #i = 1
				DLL CALL #crw_hnd FUNCTION "PEGetNPages" STACK "L" #pjob
				#pages = BITAND(GETREG(AX),65535)
			END IF
			DLL CALL #crw_hnd FUNCTION "PEStartPrintJob" STACK "LL" #pjob 1
		END IF

		IF #pages > 2-NOT(#SWiP_Duplex-1) AND #printer <> 0   '#pages will be 0 for screen preview & export modes
			IF #psource1 <> #psource2
				POKE 0 DOSPTR(#devmode)+56 "W" #psource2   'tray value
				DLL CALL #crw_hnd FUNCTION "PESelectPrinter" STACK "LPPPP" \
					#pjob DOSPTR($driver) DOSPTR($device) DOSPTR($port) DOSPTR(#devmode)
			END IF
 			IF (CASE #SWiP_Duplex (2,1)(3,1) ELSE 0) AND #SWiP_CCollated = 0 AND #copies > 1
				FOR #j = 1 TO INT((#pages-1)/2)
					POKE 0 DOSPTR(#prntopt)+2 "WW" (#j*2)+1 (#j*2)+2
					DLL CALL #crw_hnd FUNCTION "PESetPrintOptions" STACK "LP" #pjob DOSPTR(#prntopt)
					FOR #k = 1 to #copies
						DLL CALL #crw_hnd FUNCTION "PEOutputToPrinter" STACK "LL" #pjob 1
						DLL CALL #crw_hnd FUNCTION "PEStartPrintJob" STACK "LL" #pjob 1
					END FOR
				END FOR
			ELSEIF #psource1 <> #psource2
				IF CASE #SWiP_Duplex (2,1)(3,1) ELSE 0
					POKE 0 DOSPTR(#prntopt)+2 "WW" 3 #pages
				ELSE
					POKE 0 DOSPTR(#prntopt)+2 "WW" 2 #pages
				END IF
				DLL CALL #crw_hnd FUNCTION "PESetPrintOptions" STACK "LP" #pjob DOSPTR(#prntopt)
				DLL CALL #crw_hnd FUNCTION "PEOutputToPrinter" STACK "LL" #pjob #copies
				DLL CALL #crw_hnd FUNCTION "PEStartPrintJob" STACK "LL" #pjob 1
			END IF
		END IF
	END FOR

'If required, wait for the preview window to be closed. Don't put in a milli-wait to
'reduce CPU usage - Crystal will also wait as it is called via the DLL.
	IF $mode = "S" and #SWiP_CPrevWinWait
		DLL CALL #crw_hnd FUNCTION "PEGetWindowHandle" STACK "L" #pjob
		WHILE GETREG(AX)
			DLL CALL #crw_hnd FUNCTION "PEGetWindowHandle" STACK "L" #pjob
		END WHILE
	END IF

	DLL CALL #crw_hnd FUNCTION "PEClosePrintJob" STACK "L" #pjob
'*** Can't unload the dll - WINOAC will crash if the report is still open
'*** Speeds up the next report - 5Mb+ takes a while to load
'*** Automatically unloaded when SmartWare is closed
	RETURN 1
END FUNCTION

FUNCTION _SWiP_Word($docpathname,#mode,#printer,#copies,$savepath)
'Prints/handles a Word template, takes 5 parameters
'  $docpathname - name & path of the Word template (.dot can be omitted)
'  #mode        - 1 merge, print & close
'                 2 merge document and edit
'                 3 merge, print dialog, print & close
'                 4 merge, forced save & edit
'                 5 merge, save as ($savepath) & edit
'                 99 edit template (unmergred)
'  #printer     - printer number for automatic printing (0 for Win default),
'                 applies to modes 1 & 3 only
'  #copies      - number of copies to print, modes 1 & 3 only
'  $savepath    - default path (mode 4) or path/name (mode 5) to save to
'Variables also affect the function. See SWiP documentation for full details.
 LOCAL $wordpathname #prefiledlg $inisection $inikey
	#SWiP_Error = 0
	_VarCheck("W")
	IF LOWER(RIGHT($docpathname,4)) <> ".dot"
		$docpathname = $docpathname|".dot"
	END IF
	IF NOT(FILE(_SWiP_ShortPN($docpathname)))
		IF #SWiP_ErrMess
			_SWiP_Windialog("The Word document template does not exist","Word document "&$docpathname,4112)
		END IF
		#SWiP_Error = 2010
		RETURN 0
	END IF
	IF case #mode (1,0)(2,0)(3,0)(4,0)(5,0)(99,0) else 1
		IF #SWiP_ErrMess
			_SWiP_Windialog("Invalid output mode specified","Word document "&$docpathname,4112)
		END IF
		#SWiP_Error = 2020
		RETURN 0
	END IF
	IF NOT(ISNUMBER(#copies)) OR #copies < 1
		IF #SWiP_ErrMess
			_SWiP_Windialog("Invalid number of copies specified","Word document "&$docpathname,4112)
		END IF
		#SWiP_Error = 2030
		RETURN 0
	END IF

	IF #SWiP_WPreWait
		IF _SWiP_WordWait(#SWiP_WPreWait-1) = 0
			IF #SWiP_ErrMess
				_SWiP_Windialog("The previous Word output has not finished yet","Word document "&$docpathname,4112)
			END IF
			#SWiP_Error = 2040
			RETURN 0
		END IF
	END IF

	$inisection = "WordMerge"
	$inikey = "ProgramName"
	$default = "No Word Path !!"
	IF MATCH($SWiP_WordIni,"\") = 0   'no path details so default to windows dir
		$SWiP_WordIni = $SWiP_WinDir|$SWiP_WordIni
	END IF
	IF NOT(FILE($SWiP_WordIni))
		IF FILE($SWiP_WordPath|$SWiP_DefaultWI)
			TOOLS FILE COPY $SWiP_WordPath|$SWiP_DefaultWI TO $SWiP_WordIni
		END IF
	END IF
	SET-ENVIRONMENT "SWiPWordIni="|$SWiP_WordIni
	#retlength = 256
	$retstring = REPEAT(" ",#retlength+1)
	DLL LOAD "kernel32.dll" #dllkernel32
	DLL CALL #dllkernel32 FUNCTION "GetPrivateProfileStringA" STACK "PPPPLP" \
		DOSPTR($inisection) DOSPTR($inikey) DOSPTR($default) DOSPTR($retstring) \
		#retlength DOSPTR($SWiP_WordIni)
	#retlength = GETREG(AX)
	$wordpathname = LEFT($retstring,#retlength)
	IF $wordpathname = $default or NOT(FILE($wordpathname))
		_SWiP_Windialog("Please locate the Microsoft Word executeable in the next dialog box","Word document"&$docpathname,4144)
		SMARTPEEK $_filedlg #prefiledlg
		IF #prefiledlg = 0
			SMARTPOKE $_filedlg 1
		END IF
		$wordpathname = _SWiP_ShortPN(fileprompt("Please locate Microsoft Word ?","EXE",0,0,0))
		IF #prefiledlg = 0
			SMARTPOKE $_filedlg 0
		END IF
		IF NOT(FILE($wordpathname))
			IF #SWiP_ErrMess
				_SWiP_Windialog("Microsoft Word has not been located","Word document "&$docpathname,4112)
			END IF
			DLL UNLOAD #dllkernel32
			#SWiP_Error = 2050
			RETURN 0
		END IF
		DLL CALL #dllkernel32 FUNCTION "WritePrivateProfileStringA" STACK "PPPP" \
			DOSPTR($inisection) DOSPTR($inikey) DOSPTR($wordpathname) DOSPTR($SWiP_WordIni)
	END IF
	IF #SWiP_WordVer < 8
		#SWiP_WordVer = VAL(_SWiP_GetVersion($wordpathname,1))
		IF #SWiP_WordVer < 8  '8 = Word97, 9 = Word2000
			IF #SWiP_ErrMess
				_SWiP_Windialog("Microsoft Word 97, or later, is required","Word document "&$docpathname,4112)
			END IF
			DLL UNLOAD #dllkernel32
			#SWiP_Error = 2055
			RETURN 0
		END IF
	END IF
	IF #mode <> 1 AND #mode <> 3  'ignore printer number for modes that don't need it
		#printer = 0
	END IF
	#printer = _PrinterDetails(#printer)
	$inikey = "Copies"
	$retstring = STR(#copies)
	DLL CALL #dllkernel32 FUNCTION "WritePrivateProfileStringA" STACK "PPPP" \
		DOSPTR($inisection) DOSPTR($inikey) DOSPTR($retstring) DOSPTR($SWiP_WordIni)
	$inikey = "Mode"
	$retstring = STR(#mode)
	DLL CALL #dllkernel32 FUNCTION "WritePrivateProfileStringA" STACK "PPPP" \
		DOSPTR($inisection) DOSPTR($inikey) DOSPTR($retstring) DOSPTR($SWiP_WordIni)
	IF #mode = 4 or #mode = 5         'forced save mode
		$inikey = "SavePath"
		$retstring = $savepath
		DLL CALL #dllkernel32 FUNCTION "WritePrivateProfileStringA" STACK "PPPP" \
			DOSPTR($inisection) DOSPTR($inikey) DOSPTR($retstring) DOSPTR($SWiP_WordIni)
	END IF
	$inikey = "Device"
	$retstring = $device
	DLL CALL #dllkernel32 FUNCTION "WritePrivateProfileStringA" STACK "PPPP" \
		DOSPTR($inisection) DOSPTR($inikey) DOSPTR($retstring) DOSPTR($SWiP_WordIni)
	$inikey = "PaperSource1"
	$retstring = STR(#psource1)
	DLL CALL #dllkernel32 FUNCTION "WritePrivateProfileStringA" STACK "PPPP" \
		DOSPTR($inisection) DOSPTR($inikey) DOSPTR($retstring) DOSPTR($SWiP_WordIni)
	$inikey = "PaperSource2"
	$retstring = STR(#psource2)
	DLL CALL #dllkernel32 FUNCTION "WritePrivateProfileStringA" STACK "PPPP" \
		DOSPTR($inisection) DOSPTR($inikey) DOSPTR($retstring) DOSPTR($SWiP_WordIni)
	DLL UNLOAD #dllkernel32

' If Word is already running then Word 2000, etc. will close the reported process id and switch to the already running one.
' So if we are a version later than Word97 (v8) then we must wait for Word to close if Word is already running
' The restriction can be relaxed if we are not checking for Word to finish and the mode we use will not close Word in code
' Note that if Word is already running it will not pick up a newly created environment variable for the Word ini file.
	IF (#SWiP_WordVer > 8) AND \
				((#SWiP_WPreWait <> 0) OR (#SWiP_WPostWait <> 0) OR	(#mode = 1) OR (#mode = 3) OR (#mode = 4))
		#foundWord = 1
		WHILE #foundWord
			#foundWord = 0
			DLL LOAD "user32.dll" #dlluser32
			#enumWinCB = CALLBACK_CREATE("_SWiP_EnumWordWin",2,2)
			DLL CALL #dlluser32 FUNCTION "EnumWindows" STACK "LL" #enumWinCB 0
			CALLBACK_DELETE(#enumWinCB)
			DLL UNLOAD #dlluser32
			IF #foundWord
				_SWiP_Windialog("Microsoft Word is already running"|CHR(13)|"Close Word to allow this document to continue","Word document "&$docpathname,4160)
			END IF
		END WHILE
	END IF
	#procID = PROCESS_CREATE($wordpathname,$docpathname)
	IF #procID = 0
		IF #SWiP_ErrMess
			_SWiP_Windialog("Microsoft Word cannot be started from SmartWare","Word document "&$docpathname,4112)
		END IF
		#SWiP_Error = 2060
		RETURN 0
	END IF

	IF #SWiP_WPostWait
		_SWiP_WordWait(0)
	END IF
	RETURN 1
END FUNCTION

FUNCTION _SWiP_WordWait(#check)
' Waits or checks (#check 0 or 1) for the previous Word job to finish
 LOCAL #ctrlz
	IF #procID = 0
		RETURN 2
	END IF
	DLL LOAD "kernel32.dll" #dllkernel32
	DLL CALL #dllkernel32 FUNCTION "OpenProcess" STACK "LLL" \
		1024 0 #procID    '1024 = PROCESS_QUERY_INFORMATION
	#handle = GETREG(AX)
	IF #handle = 0
		DLL UNLOAD #dllkernel32
		RETURN 2
	END IF
	BUFFER #retbuff SIZEOF "P"
	#procstatus = 259         '259 = STILL_ACTIVE
	SMARTPEEK $_cz #ctrlz
	WHILE #procstatus = 259
		DLL CALL #dllkernel32 FUNCTION "GetExitCodeProcess" STACK "LL" \
			#handle DOSPTR(#retbuff)
		PEEK 0 DOSPTR(#retbuff) "P" #procstatus
		IF #check OR #procstatus <> 259
			EXIT WHILE
		END IF
		MILLI-WAIT #SWiP_WWaitMS
'Odd problem found on NT with VBA hanging problem while setting ActivePrinter
'if czbreak is off - toggling czbreak resolves it !
		IF NOT(#ctrlz)
			CZBREAK ON
			CZBREAK OFF
		END IF
	END WHILE
	DLL CALL #dllkernel32 FUNCTION "CloseHandle" STACK "L" #handle
	DLL UNLOAD #dllkernel32
	IF #procstatus = 259
		RETURN 0
	ELSE
		RETURN 1
	END IF
END FUNCTION

FUNCTION _SWiP_ShortPN($long)
' Returns the short path equivalent of a full file name and path
	#retlength = 256
	$retstring = REPEAT(" ",#retlength)
	DLL LOAD "kernel32.dll" #dllkernel32
	DLL CALL #dllkernel32 FUNCTION "GetShortPathNameA" STACK "PPL" DOSPTR($long) DOSPTR($retstring) #retlength
	#retlength = GETREG(AX)
	$retstring = LEFT($retstring,#retlength)
	DLL UNLOAD #dllkernel32
	RETURN $retstring
END FUNCTION

FUNCTION _SWiP_Windialog($text,$title,#type)
' For multi line text put a carriage return in your text
'   e.g. $ text = "Line 1"|chr(13)|"Line 2"|chr(13)|"Line 3"
' Select the required buttons with #type
'   0 = OK
'   1 = OK Cancel
'   2 = Abort Retry Ignore
'   3 = Yes No Cancel
'   4 = Yes No
'   5 = Retry Cancel
' Default is no icon - add one of the following to #type to get one
'   Add 16 for the Error X
'   Add 32 for the confirmation question mark
'   Add 48 for the warning exclamation mark
'   Add 64 for the information i
' First button is the default unless another number is added to #type
'   Add 256 - second button is the default
'   Add 512 - third button is the default
' The priority of the box can also be changed
'   Add 4096 - to make the box system modal
'   Add 262144 - to make the box appear in the foreground
' Return value is the button pressed - Esc = Cancel if there is a Cancel buttton
 LOCAL #dlluser32wd #result
	DLL LOAD "user32.dll" #dlluser32wd
	DLL CALL #dlluser32wd FUNCTION "MessageBoxA" STACK "LPPL" #SWiP_SWWinH DOSPTR($text) DOSPTR($title) #type
	#result = GETREG(AX)
	DLL UNLOAD #dlluser32wd
	RETURN GROUP("OK Cancel Abort Retry Ignore Yes No",#result)
END FUNCTION

FUNCTION _SWiP_GetVersion($filename,#parts)
' Returns the version number of the $filename file, including path and extension, as a
' string "n.n.n.n". #parts specifies the number of parts returned (1-4). "-1" = error.
 LOCAL #dllversion #temp #size #verinfo #buff #info #n1 #n2 #n3 #n4 $temp
	DLL LOAD "version.dll" #dllversion
	IF #dllversion = 0
		RETURN "-1"
	END IF
	#temp = 0
	DLL CALL #dllversion FUNCTION "GetFileVersionInfoSizeA" STACK "PP" \
		DOSPTR($filename) DOSPTR(#temp)
	#size = GETREG(AX)
	IF #size = 0
		DLL UNLOAD #dllversion
		RETURN "-1"
	END IF
	BUFFER #verinfo SIZE #size
	DLL CALL #dllversion FUNCTION "GetFileVersionInfoA" STACK "PLLP" \
		DOSPTR($filename) 0 #size DOSPTR(#verinfo)
	IF GETREG(AX) = 0
		DLL UNLOAD #dllversion
		RETURN "-1"
	END IF
	$temp = "\"
	BUFFER #buff SIZEOF "P"
	DLL CALL #dllversion FUNCTION "VerQueryValueA" STACK "PPPP" \
		DOSPTR(#verinfo) DOSPTR($temp) DOSPTR(#buff) DOSPTR(#temp)
	IF GETREG(AX) = 0
		DLL UNLOAD #dllversion
		RETURN "-4"
	END IF
	DLL UNLOAD #dllversion
	PEEK 0 DOSPTR(#buff) "P" #info
	PEEK 0 #info+8 "WWWW" #n2 #n1 #n4 #n3
	$temp = STR(#n1)
	IF #parts > 1
		$temp = $temp |"."|STR(#n2)
	END IF
	IF #parts > 2
		$temp = $temp |"."|STR(#n3)
	END IF
	IF #parts > 3
		$temp = $temp |"."|STR(#n4)
	END IF
	RETURN $temp
END FUNCTION

FUNCTION _SWiP_EnumWordWin(#hnd)
' Callback function used by _SWiP_Word() when checking if Word is running
 LOCAL #param #PIDmem $str
	UNPACK #hnd "LL" #handle #param
	MEMALLOC #PIDmem SIZEOF "P"
	MEMPACK #PIDmem "P" 0
	DLL CALL #dlluser32 FUNCTION "GetWindowThreadProcessId" STACK "LP" #handle #PIDmem
	MEMFREE #PIDmem
	$str = REPEAT(" ",21)
	DLL CALL #dlluser32 FUNCTION "GetClassNameA" STACK"LPL" #handle DOSPTR($str) 20
	IF LEFT($str,GETREG(AX)) = "OpusApp"
		SETREG(AX,0)
		#foundWord = 1
	ELSE
		SETREG(AX,1)
	END IF
END FUNCTION

FUNCTION _GetDirs(#dir)
' Returns one of the o/s defined directories
' #dir  1 = System  2 = Windows  3 = Temp
	#retlength = 256
	$retstring = REPEAT(" ",#retlength+1)
	DLL LOAD "kernel32.dll" #dllkernel32
	if #dir = 1
		DLL CALL #dllkernel32 FUNCTION "GetSystemDirectoryA" STACK "PL" DOSPTR($retstring) #retlength
	elseif #dir = 2
		DLL CALL #dllkernel32 FUNCTION "GetWindowsDirectoryA" STACK "PL" DOSPTR($retstring) #retlength
	elseif #dir = 3
		DLL CALL #dllkernel32 FUNCTION "GetTempPathA" STACK "LP" #retlength DOSPTR($retstring)
	end if
	#retlength = GETREG(AX)
	$retstring = LEFT($retstring,#retlength)
	DLL UNLOAD #dllkernel32
	IF RIGHT($retstring,1) <> "\"
		$retstring = $retstring|"\"
	END IF
	RETURN $retstring
END FUNCTION

FUNCTION _PrinterDetails(#printern)
' Retrieves printer details (device name, trays, etc.) from the ini file
 LOCAL $pinisection $pinikey
	IF #printern <> 0
		IF MATCH($SWiP_PrintIni,"\") = 0   'no path details so default to windows dir
			$SWiP_PrintIni = $SWiP_WinDir|$SWiP_PrintIni
		END IF
		IF NOT(FILE($SWiP_PrintIni))
			_SWiP_Windialog("Printers have not been configured"|CHR(13)|"Unable to direct output to a specific printer"|CHR(13)|"Windows default printer will be used","Printer selection for SWiP printing",48)
			$device = ""
			$driver = ""
			$port = ""
			#psource1 = 1
			#psource2 = 1
			RETURN 0
		END IF
		#retlength = 256
		$retstring = REPEAT(" ",#retlength+1)
		$pinisection = STR(#printern)
		$pinikey = "Device"
		$default=""
		DLL LOAD "kernel32.dll" #dllkernel32
		DLL CALL #dllkernel32 FUNCTION "GetPrivateProfileStringA" STACK "PPPPLP" \
			DOSPTR($pinisection) DOSPTR($pinikey) DOSPTR($default) DOSPTR($retstring) \
			#retlength DOSPTR($SWiP_PrintIni)
		#retlength = GETREG(AX)
		$device = LEFT($retstring,#retlength)
		#retlength = 256
		$retstring = REPEAT(" ",#retlength+1)
		$pinikey = "Driver"
		DLL CALL #dllkernel32 FUNCTION "GetPrivateProfileStringA" STACK "PPPPLP" \
			DOSPTR($pinisection) DOSPTR($pinikey) DOSPTR($default) DOSPTR($retstring) \
			#retlength DOSPTR($SWiP_PrintIni)
		#retlength = GETREG(AX)
		$driver = LEFT($retstring,#retlength)
		#retlength = 256
		$retstring = REPEAT(" ",#retlength+1)
		$pinikey = "Port"
		DLL CALL #dllkernel32 FUNCTION "GetPrivateProfileStringA" STACK "PPPPLP" \
			DOSPTR($pinisection) DOSPTR($pinikey) DOSPTR($default) DOSPTR($retstring) \
			#retlength DOSPTR($SWiP_PrintIni)
		#retlength = GETREG(AX)
		$port = LEFT($retstring,#retlength)
		$default = 1
		$pinikey = "PaperSource1"
		DLL CALL #dllkernel32 FUNCTION "GetPrivateProfileIntA" STACK "PPLP" \
			DOSPTR($pinisection) DOSPTR($pinikey) $default DOSPTR($SWiP_PrintIni)
		#psource1 = GETREG(AX)
		$pinikey = "PaperSource2"
		DLL CALL #dllkernel32 FUNCTION "GetPrivateProfileIntA" STACK "PPLP" \
			DOSPTR($pinisection) DOSPTR($pinikey) $default DOSPTR($SWiP_PrintIni)
		#psource2 = GETREG(AX)
		DLL UNLOAD #dllkernel32
	ELSE
		$device = ""
		$driver = ""
		$port = ""
		#psource1 = 1
		#psource2 = 1
	END IF
	RETURN #printern
END FUNCTION

FUNCTION _VarCheck($mode)
'Defaults any SWiP variables set to invalid values - called by both the Crystal
'& Word functions. The $mode parameter is expected to be "C" or "W" (use of <>
'avoids any problems). Variables are defaulted rather than causing an error.

	IF NOT(ISNUMBER(#SWiP_ErrMess))
		#SWiP_ErrMess = 1
	END IF

	IF $mode <> "W"  'check Crystal related variables
		IF NOT(ISNUMBER(#SWiP_CDialog))
			#SWiP_CDialog = 1
		END IF
		IF NOT(ISNUMBER(#SWiP_Duplex)) OR (CASE #SWiP_Duplex (1,0)(2,0)(3,0) ELSE 1)
			#SWiP_Duplex = 1
		END IF
		IF NOT(ISNUMBER(#SWiP_CMax))
			#SWiP_CMax = 0
		END IF
		IF NOT(ISNUMBER(#SWiP_CPrintBtn))
			#SWiP_CPrintBtn = 1
		END IF
		IF NOT(ISNUMBER(#SWiP_CPSetupBtn))
			#SWiP_CPSetupBtn = 1
		END IF
		IF NOT(ISNUMBER(#SWiP_CExportBtn))
			#SWiP_CExportBtn = 1
		END IF
		IF NOT(ISNUMBER(#SWiP_CCollated)) OR (CASE #SWiP_CCollated (0,0)(1,0)(2,0) ELSE 1)
			#SWiP_CCollated = 2
		END IF
		IF NOT(ISNUMBER(#SWiP_CPrevWinWait))
			#SWiP_CPrevWinWait = 0
		END IF
	END IF

	IF $mode <> "C"  'check Word related variables
		IF NOT(ISNUMBER(#SWiP_WPreWait)) OR (CASE #SWiP_WPreWait (0,0)(1,0)(2,0) ELSE 1)
			#SWiP_WPreWait = 1
		END IF
		IF NOT(ISNUMBER(#SWiP_WPostWait))
			#SWiP_WPostWait = 0
		END IF
		IF NOT(ISNUMBER(#SWiP_WWaitMS)) OR #SWiP_WWaitMS < 1
			#SWiP_WWaitMS = 1000
		END IF
	END IF
END FUNCTION

