Plateforme Level Extreme
Abonnement
Profil corporatif
Produits & Services
Support
Légal
English
Printing to dot matrix
Message
De
18/08/2001 06:23:00
 
 
À
17/08/2001 09:29:42
Information générale
Forum:
Visual FoxPro
Catégorie:
Codage, syntaxe et commandes
Divers
Thread ID:
00538025
Message ID:
00545817
Vues:
16
>>Then I guess you don't need it - ??? still shifts hDC contexts between printer openings (each time you issue a SET PRINTER, or use a Windows printing action like a report, ?, ?? interspersed with ???, which forces a new spool context) and you are quite limited in mixing Windows and non-Windows printer behavior, since VFP natively can only have one printer context in effect at a time, resulting in a page feed whenever a context shift occurs. Also, all VFP printer operations require that the spooler be enabled - you can't set up a printer to print directly to printer without any spool context for the printer as defined in the Windows printer setup dialog. Like I said, I use the API and have no problems related to Win GDI behavior.
>
>I, personally, would like to see your tools posted here, Ed.

A preliminary version of the class, DIRPRTCLASS.PRG, has been submitted to UT as of this AM. The full source for this class is contained in the message body below as well
**************************************************
*-- Class Library:  c:\program files\microsoft visual studio\vfp98\dirprt.vcx
**************************************************


**************************************************
*-- Class:        DirectPrintOutput (c:\program files\microsoft visual studio\vfp98\dirprtclass.prg)
*-- ParentClass:  Line
*-- BaseClass:    Line
*-- Time Stamp:   08/07/01 09:39:08 PM
*
*
*!*	   This class provides direct printing to a Windows printer, Network printer UNC or defined port
*!*	   without the interpretation/intervention of the Windows GDI, and without the normal Win behavior
*!*	   of resetting the printer at the start of the spool job, standard Windows page treatment, or the
*!*	   automatic skip to top of the next page at the end of the spooled output. This permits
*!*	   direct, uninterpreted writing to a printer on a spool job by spool job basis, although it is
*!*	   written to via a method of the object rather than via the VFP printer command set.  It also
*!*	   provides a method to spool a file to a printer without the GDI interpreting it's content,
*!*	   letting you specify if the spooled file is deleted automatically after it is printed.

*!*	   Please note that you are responsible for ALL printer control handling; if you want to force a new line, you have
*!*	   to issue a CR/LF pair explicitly when writing output to the printer; similarly, you have to issue form feeds to
*!*	   force skip to top of next page, any necessary PRINTER CONTROL codes selecting fonts or page orientation, and
*!*	   generating ANY graphical image strings to include graphical content, which includes writing text for those
*!*	   fonts which Windows usually translates automatically when using the GDI functions OR handling preparation
*!*	   OF the print image. Using the direct, uninterpreted output will not let you rely on normal Windows GDI
*!*	   services.

*!*	   This class relies heavily on the direct spooler manipulation functions which are delivered through the Windows
*!*	   spooler driver, WINSPOOL.DRV.  The details of these functions are contained in the MSDN Platform SDK docs, and
*!*	   the constants and detailed structure typedefs can be found in WINSPOOL.H in VC++'s \Include folder. A few other
*!*	   common API calls have been used; these can be found in the Platform SDK and WINBASE.H in VC++. I've also used
*!*	   the heap management services of my CLSHeap class; you'll need to include CLSHEAP.PRG in your program so that
*!*	   it can be added as required to your SET PROCEDURE list during runtime. I leverage both the heap management
*!*	   methods OF the heap class and a few of the data conversion UDFs IN the CLSHeap.PRG file.

*!*	   CLASS Interface:

*!*	   PUBLIC Properties:
*!*	      cPrinterName      R/O      NAME OF LAST PRINTER OPENED FOR OUTPUT; NULL indicates none open by class
*!*	      cDocName          R/O      NAME OF the CURRENT spool DOCUMENT being written TO; NULL indicates none active
*!*	      nJobID		R/O		NUMBER OF the CURRENT spool job being written TO; NULL indicates none active
*!*	      cErrorMsg         R/O      TEXT OF errors encountered while performing last method; NULL indicates none,
*!*	                                 may contain multiple lines when several errors occurred. In most cases, if
*!*	                                 several errors are given, the errors list conditions which, while NOT routine,
*!*	                                 are still permitted, though change the state of the active object in
*!*	                                 effect prior to executing the method invoked. These errors are reported, and
*!*	                                 any action taken to correct the condition as well as it's result are noted,
*!*	                                 but the requested method is processed, with the noted errors interpreted as
*!*	                                 warnings which will be ignored. As an example, it's possible to issue a
*!*	                                 DocOpen(), write into it, and then issue another DocOpen(), with a warning
*!*	                                 issued that there was already an Open document, and it was closed before the
*!*	                                 new document is opened.  The DocOpen() suceeds, but the warning is recorded
*!*	                                 in the cErrorMsg property on completion, the DocOpen() reports success.
*!*	   PUBLIC methods:
*!*	      INIT                    At invocation; adds CLSHeap to the PROCEDURE LIST as necessary, instantiates
*!*	                              a heap OBJECT, and defines the API calls required for the class to execute.
*!*	                              If unable to create a heap object, the method returns .F. and the object does
*!*	                              not instantiate.

*!*	      RELEASE		      Closes any open printer or document, releases the oHeap object, and invokes
*!*	                              the DESTROY() method

*!*	      PrinterOpen                PARAMETERS:
*!*	                                    cPrinterName -   String naming a WINDOWS PRINTER, a defined LOCAL DEVICE, OR
*!*	                                       the UNC OF a network printer. This parameter is required.
*!*	                              Opens the specified print device for use in creating or spooling documents
*!*	                              * returns Logical

*!*	      PrinterIsOpen	      reports if a print device is open, as indicated by it's nhPrinter property
*!*	                              * returns Logical

*!*	      DocIsOpen		      reports IF a spool document is open, AS indicated by it's nJobID property
*!*	                              * returns Logical

*!*	      DocOpen			PARAMETERS:
*!*	                                    cDocName - 	String naming the spool job in the spooler.  If omitted, an
*!*	                                                arbitrary name is assigned using SYS(2015). This parameter is optional
*!*	                              Opens a new spool document for output. A printer must be open to succeed.
*!*	                              * returns Logical

*!*	      DocClose		      If a document is presently open for spooling, Closes it

*!*	      PrinterClose	      IF a printer is open, Closes it. If a document is open for spooling when
*!*	                              invoked, a DocClose() is issued

*!*	      DocWrite			 PARAMETERS:
*!*	                                    cOutput -	The string to be output to the current spool document. It must
*!*	                                    be a CHARACTER type. This parameter is required.
*!*	                              Writes the requested output without interpretation to the current spool
*!*	                              document. A spool document must be open for it to succeed.
*!*	                              * returns NULL IF failure
*!*	                              * returns a STRING in the form "xxx - yyy" where xxx is the length of the output
*!*	                              string passed, and yyy is the length of the data written to the spool document.
*!*	                              The ONLY TIME this may differ is if the spool file runs out of disk space, or if
*!*	                              the STRING is in UniCode and the printer only accepts ANSI.

*!*	         ForcePage            Forces the spooler to indicate closing of current page, and starts a new one.
*!*	                              The spooler does NOT perform the standard GDI page initialization, it is
*!*	                              offered to permit a spooled document to be restarted from a specified point -
*!*	                              if a print error occurs while writing to the print andND no page marks are
*!*	                              present in the document, the spooler would be forced to restart printing at the
*!*	                              beginning of the document. A spool document must be open for it to succeed.
*!*	                              * returns Logical

*!*	         SpoolFile		PARAMETERS:
*!*	                                    cSourceFile -	Name of file to spool. This must be a character string naming
*!*	                                                   a file that already exists and is NOT open. This parameter is required
*!*	                                    lDelete -		indicates if the source file is to be deleted once spooled. If
*!*	                                                   omitted or .F. the file is NOT deleted. This parameter is optional.
*!*	                              Spools specified file for printing on the open printer. A printer must be
*!*	                                                   open for it to succeed.
*!*	                              * returns NULL on failure
*!*	                              * returns Numeric spooled JobID if success

*!*	         This has been tested under Win98, WinME, NT 4.0 and Win2K with VFP 5, 6 and VFP7 RC1. The class is released as-is in
*!*	         SOURCE form, for PUBLIC USE. Copyright 2001, Ed Rauh. Inclusion of this class in other developers' work requires
*!*	         written acknowledgement of the author and Copyright in both source code and product documentation. With such attribution,
*!*	         the developer may include and use this code in their software without payment of royalties.

*!*	         This code is released as-is; I DO NOT guarentee that it operates in all environments.  I will respond to bug reports
*!*	         or suggestions for enhancements as time permits. I released this as Source code so that others can extend, enhance
*!*	         and modify as they desire. As far as code alterations, if you change the code AND it breaks, you own both pieces. IF
*!*	         you make improvements to the code, OR fix bugs in the source, Please drop me a line describing your fixes and/or mods -
*!*	         if I include them in an upgrade, I'll attribute them to you, and if not, I'll let you know my issues, AND you'll be
*!*	         free to release your own version of the class, with appropriate attribution.'

*!*	         Send email to edr@esolserv.com OR erauh@snet.net

*!*		Example usage:

*!*	   SET PROC TO DIRPRTCLASS.PRG ADDITIVE
*!*	   * Note - CLSHeap.PRG needs to be in your SET PROC list, or part of your project
*!*	   oDirPrt = CREATEOBJ('DirectPrintOutput')
*!*	   WITH oDirPrt
*!*	      .PrinterOpen('LPT1:') && Arg can be a Win Printer name, port, or a print queue - works with both MSNetwork
*!*	                            && and NetWare queues.  It also works with CAPTUREd or NET USEd printer ports
*!*	      IF .PrinterIsOpen()
*!*	         .DocOpen('MySpoolJob')
*!*	         .DocWrite('This is sample text that prints after the first close' + CHR(13) + CHR(10) + CHR(9) + 'and this next line is indented one tab stop')
*!*	         .DocWrite(' this continues the line')
*!*	         .DocWrite(CHR(12)+'And this form feeds')
*!*	         .DocWrite(' without a Windows page mark')
*!*	         .SpoolFile('MyFileToDeleteAfterItPrints.TMP', .T.) && Spool file and delete after printed
*!*            && the spooled file prints first because the first document you're working is still open
*!*	         .DocWrite(CHR(13) + CHR(10) + CHR(10) + 'this is still written to the first spool job' + CHR(13) + CHR(10))
*!*	         .DocWrite('*** still the same print document ***' + CHR(13))  && this line will get overwritten since CR w/o LF
*!*	         .DocWrite(CHR(9) + 'You can embed any control sequence' + CHR(10) + CHR(10))
*!*	         .DocClose()
*!*            .SpoolFile('AnotherFileToPrinterWithoutDeleting')
*!*	         .DocOpen()  && assign a random job name
*!*	         .DocWrite('<*** and note that the next print job picks up right where you left off')
*!*	         .PrinterClose()  && close both current job and the printer
*!*	      ENDIF
*!*	   ENDWITH

DEFINE CLASS DirectPrintOutput AS LINE


   HEIGHT = 0
   VISIBLE = .F.
   WIDTH = 0
   NAME = "directprintoutput"

*-- current print handle
   PROTECTED nhPrinter
   nhPrinter = NULL

*-- Flag to indicate if currently executing an internal method
   PROTECTED lInternal
   lInternal = .T.

*-- Heap object instance
   PROTECTED oHeap
   oHeap = NULL

*-- Name of printer now open
   cPrinterName = NULL

*-- name of document currently being spooled
   cDocName = NULL

*-- JobID of active spool document
   nJobID = NULL

*-- Error messages reported for most recent method invoked
   cErrorMsg = NULL
   PROTECTED HEIGHT
   HEIGHT = 0
   PROTECTED WIDTH
   WIDTH = 0
   PROTECTED BASECLASS
   PROTECTED BORDERCOLOR
   PROTECTED BORDERSTYLE
   PROTECTED BORDERWIDTH
   PROTECTED CLICK
   PROTECTED COLORSOURCE
   PROTECTED DBLCLICK
   PROTECTED DRAG
   PROTECTED DRAGDROP
   PROTECTED DRAGICON
   PROTECTED DRAGMODE
   PROTECTED DRAGOVER
   PROTECTED DRAWMODE
   PROTECTED LINESLANT
   PROTECTED MIDDLECLICK
   PROTECTED MOUSEDOWN
   PROTECTED MOUSEICON
   PROTECTED MOUSEMOVE
   PROTECTED MOUSEPOINTER
   PROTECTED MOUSEUP
   PROTECTED MOUSEWHEEL
   PROTECTED MOVE
   PROTECTED OLECOMPLETEDRAG
   PROTECTED OLEDRAG
   PROTECTED OLEDRAGDROP
   PROTECTED OLEDRAGMODE
   PROTECTED OLEDRAGOVER
   PROTECTED OLEDRAGPICTURE
   PROTECTED OLEDROPEFFECTS
   PROTECTED OLEDROPHASDATA
   PROTECTED OLEDROPMODE
   PROTECTED OLEGIVEFEEDBACK
   PROTECTED OLESETDATA
   PROTECTED OLESTARTDRAG
   PROTECTED RIGHTCLICK
   PROTECTED UIENABLE
   PROTECTED VISIBLE
   VISIBLE = .F.
   PROTECTED ZORDER
	lInternal = .F.
	
PROCEDURE cPrinterName_Assign
   * Permit property to be altered by internal class processes, based on
   * the current state of the lInternal Property
   LPARAMETERS vNewVal
   IF THIS.lInternal
      THIS.cPrinterName = m.vNewVal
   ENDIF
ENDPROC


PROCEDURE cDocName_Assign
   * Permit property to be altered by internal class processes, based on
   * the current state of the lInternal Property
   LPARAMETERS vNewVal
   IF THIS.lInternal
      THIS.cDocName = m.vNewVal
   ENDIF
ENDPROC


PROCEDURE nJobID_Assign
   * Permit property to be altered by internal class processes, based on
   * the current state of the lInternal Property
   LPARAMETERS vNewVal
   IF THIS.lInternal
      THIS.nJobID = m.vNewVal
   ENDIF
ENDPROC


PROCEDURE cErrorMsg_Assign
   * Permit property to be altered by internal class processes, based on
   * the current state of the lInternal Property
   LPARAMETERS vNewVal
   IF THIS.lInternal
      THIS.cErrorMsg = m.vNewVal
   ENDIF
ENDPROC


*-- Unload class instance
PROCEDURE RELEASE
   WITH THIS
      .lInternal = .T.
      IF .PrinterIsOpen()
         .PrinterClose()
      ENDIF
      .oHeap = NULL
      .DESTROY()
   ENDWITH
ENDPROC


*-- Set error message content
PROTECTED PROCEDURE AddError
   LPARAMETER tcErrorMsg
   LOCAL lIntState
   WITH THIS
      lIntState = .lInternal  && preserve internal execution state
      .lInternal = .T.
      DO CASE
      CASE ISNULL(tcErrorMsg) && Clear all entries from cErrorMsg property
         .cErrorMsg = NULL
      CASE ISNULL(.cErrorMsg) && No prior content - assign directly to property
         .cErrorMsg = tcErrorMsg
      OTHERWISE && Property contains entries - append CR/LF at end, followed by new message
         .cErrorMsg = .cErrorMsg + CHR(13) + CHR(10) + tcErrorMsg
      ENDCASE
      .lInternal = lIntState
   ENDWITH
ENDPROC

PROCEDURE INIT
   WITH THIS
      IF ! 'CLSHEAP' $ UPPER(SET('PROCEDURE'))
         SET PROCEDURE TO CLSHeap ADDITIVE
      ENDIF
      .oHeap = CREATEOBJ('Heap')
      DECLARE INTEGER OpenPrinter IN WINSPOOL.DRV ;
         STRING @ pPrinterName, ;
         INTEGER @ phPrinter, ;
         INTEGER pDefault
      DECLARE INTEGER StartDocPrinter IN WINSPOOL.DRV ;
         INTEGER hPrinter, ;
         INTEGER LEVEL, ;
         STRING @ pDOC_INFO_1
      DECLARE INTEGER StartPagePrinter IN WINSPOOL.DRV ;
         INTEGER hPrinter
      DECLARE INTEGER WritePrinter IN WINSPOOL.DRV ;
         INTEGER hPrinter, ;
         STRING @ pBuf, ;
         INTEGER cdBuf, ;
         INTEGER @ pWritten
      DECLARE INTEGER EndPagePrinter IN WINSPOOL.DRV ;
         INTEGER hPrinter
      DECLARE INTEGER EndDocPrinter IN WINSPOOL.DRV ;
         INTEGER hPrinter
      DECLARE INTEGER ClosePrinter IN WINSPOOL.DRV ;
         INTEGER hPrinter
      DECLARE INTEGER GetLastError IN WIN32API
      DECLARE INTEGER AddJob IN WINSPOOL.DRV ;
         INTEGER hPrinter, ;
         INTEGER LEVEL, ;
         INTEGER pData, ;
         INTEGER cdBuf, ;
         INTEGER @ pcbNeeded
      DECLARE INTEGER ScheduleJob IN WINSPOOL.DRV ;
         INTEGER hPrinter, ;
         INTEGER JobID
      DECLARE INTEGER MoveFile IN WIN32API ;
         STRING @ lpszExisting, ;
         STRING @ lpszNew
      IF ISNULL(.oHeap) OR TYPE('.oHeap') # 'O'
         .oHeap = NULL
      ENDIF
      RETURN ! ISNULL(.oHeap)
   ENDWITH
ENDPROC

*-- Report if a printer handle is active
PROCEDURE PrinterIsOpen
   RETURN ! ISNULL(THIS.nhPrinter)
ENDPROC


*-- Indicate if currently spooling a document
PROCEDURE DocIsOpen
   RETURN ! ISNULL(THIS.nJobID)
ENDPROC


*-- Open a printer for spooling
PROCEDURE PrinterOpen
   LPARAMETER tcPrinter
   LOCAL lIntState
   WITH THIS
      lIntState = .lInternal
      .lInternal = .T.
      IF ! lIntState
         .AddError(NULL)
      ENDIF
      IF .PrinterIsOpen()
         .AddError('PrinterOpen - Already open; forcing close')
         .PrinterClose()
      ENDIF
      IF TYPE('tcPrinter') # 'C'
         .AddError('PrinterOpen - invalid printer parameter specified')
      ELSE
         LOCAL nResult, nHandle
         nHandle = 0
         nResult = OpenPrinter(tcPrinter,@nHandle,0)
         IF nResult = 0
            .AddError('OpenPrinter API Error ' + TRANSFORM(GetLastError()))
         ELSE
            .nhPrinter = nHandle
            .cPrinterName = tcPrinter
         ENDIF
      ENDIF
      .lInternal = lIntState
      RETURN .PrinterIsOpen()
   ENDWITH
ENDPROC


*-- Start a new spool document for the open printer
PROCEDURE DocOpen
   LPARAMETER tcDocName
   LOCAL lIntState
   WITH THIS
      lIntState = .lInternal
      .lInternal = .T.
      IF ! lIntState
         .AddError(NULL)
      ENDIF
      IF ! .PrinterIsOpen()
         .AddError('DocOpen - no printer open to create Doc')
         .lInternal = lIntState
         RETURN .F.
      ENDIF
      IF .DocIsOpen()
         .AddError('DocOpen -  Already open; forcing close')
         .DocClose()
      ENDIF
      IF TYPE('tcDocName') # 'C'
         tcDocName = SYS(2015)
      ENDIF
      LOCAL cDocInfo,nAllocPtr
      nAllocPtr = .oHeap.AllocString(tcDocName)
      cDocInfo = NumToDWORD(nAllocPtr) + REPL(CHR(0),8)
      .nJobID = StartDocPrinter(.nhPrinter, 1, cDocInfo)
      IF .nJobID # 0
         StartPagePrinter(.nhPrinter)
         .cDocName = tcDocName
      ELSE
         .AddError('StartDocPrinter API # ' + TRANSFORM(GetLastError()))
         .nJobID = NULL
      ENDIF
      .oHeap.DeAlloc(nAllocPtr)
      .lInternal = lIntState
      RETURN .DocIsOpen()
   ENDWITH
ENDPROC


*-- Close active document and release to spooler
PROCEDURE DocClose
   LOCAL lIntState
   WITH THIS
      lIntState = .lInternal
      .lInternal = .T.
      IF ! lIntState
         .AddError(NULL)
      ENDIF
      IF .DocIsOpen()
         =EndPagePrinter(.nhPrinter)
         =EndDocPrinter(.nhPrinter)
         .nJobID = NULL
         .cDocName = NULL
      ENDIF
      .lInternal = lIntState
   ENDWITH
ENDPROC


*-- Close active printer handle
PROCEDURE PrinterClose
   LOCAL lIntState
   WITH THIS
      lIntState = .lInternal
      .lInternal = .T.
      IF ! lIntState
         .AddError(NULL)
      ENDIF
      IF .DocIsOpen()
         .DocClose()
      ENDIF
      IF .PrinterIsOpen()
         =ClosePrinter(.nhPrinter)
         .nhPrinter = NULL
         .cPrinterName = NULL
      ENDIF
      .lInternal = lIntState
   ENDWITH
ENDPROC


*-- write string directly to currently open document
PROCEDURE DocWrite
   LPARAMETER tcOutput
   LOCAL lIntState
   WITH THIS
      lIntState = .lInternal
      .lInternal = .T.
      IF ! lIntState
         .AddError(NULL)
      ENDIF
      LOCAL nResult, nWritten
      nResult = 0
      nWritten = NULL
      IF ! .DocIsOpen()
         .AddError('DocWrite - no open document')
      ELSE
         nWritten = 0
         nResult = WritePrinter(.nhPrinter,tcOutput,LEN(tcOutput),@nWritten)
         IF nWritten = 0
            .AddError('WritePrinter API # ' + TRANSFORM(nResult))
         ENDIF
      ENDIF
      .lInternal = lIntState
      RETURN IIF(ISNULL(nWritten),NULL,TRANSFORM(LEN(tcOutput))+'-'+TRANSFORM(nWritten))
   ENDWITH
ENDPROC


*-- mark end of page of active doc and start another
PROCEDURE ForcePage
   LOCAL lIntState
   WITH THIS
      lIntState = .lInternal
      .lInternal = .T.
      IF ! lIntState
         .AddError(NULL)
      ENDIF
      IF .DocIsOpen()
         =EndPagePrinter(.nhPrinter)
         =StartPagePrinter(.nhPrinter)
      ELSE
         .AddError('ForcePage - no document open')
      ENDIF
      .lInternal = lIntState
      RETURN .DocIsOpen()
   ENDWITH
ENDPROC


*-- Spool a file on active printer, optionally deleting noriginal file after printing it
PROCEDURE SpoolFile
   LPARAMETERS tcSource, tlMove
   LOCAL lIntState, lOK
   lOK = .T.
   WITH THIS
      lIntState = .lInternal
      .lInternal = .T.
      IF ! lIntState
         .AddError(NULL)
      ENDIF
      DO CASE
      CASE ! .PrinterIsOpen()
         .AddError('SpoolFile - no printer open')
         lOK = .F.
      CASE TYPE('tcSource') # 'C' OR ! FILE(FULLPATH(tcSource))
         .AddError('SpoolFile - illegal file name parameter')
         lOK = .F.
      CASE TYPE('tlMove') # 'L'
         .AddError('SpoolFile - illegal deletion parameter')
         lOK = .F.
      OTHERWISE
         LOCAL nAllocPtr, nBufLen, cDestFile, cBufStru, nJobID
         nBufLen = 0
         nJobID = 0
         nAllocPtr = .oHeap.AllocBLOB(REPL(CHR(0),301))
         =AddJob(.nhPrinter, 1, nAllocPtr, 300, @nBufLen)
         cBufStru = LEFT(.oHeap.CopyFrom(nAllocPtr),8)
         cDestFile = GetMemString(DWORDToNum(cBufStru))
         nJobID = DWORDToNum(RIGHT(cBufStru,4))
         .oHeap.DeAlloc(nAllocPtr)
         IF tlMove
            =MoveFile(FULLPATH(tcSource),cDestFile)
         ELSE
            COPY FILE (FULLPATH(tcSource)) TO (cDestFile)
         ENDIF
         =ScheduleJob(.nhPrinter,nJobID)
      ENDCASE
      .lInternal = lIntState
      RETURN IIF(lOK,nJobID,NULL)
   ENDWITH
ENDPROC


ENDDEFINE
*
*-- EndDefine: directprintoutput
**************************************************
EMail: EdR@edrauh.com
"See, the sun is going down..."
"No, the horizon is moving up!"
- Firesign Theater


NT and Win2K FAQ .. cWashington WSH/ADSI/WMI site
MS WSH site ........... WSH FAQ Site
Wrox Press .............. Win32 Scripting Journal
eSolutions Services, LLC

The Surgeon General has determined that prolonged exposure to the Windows Script Host may be addictive to laboratory mice and codemonkeys
Précédent
Suivant
Répondre
Fil
Voir

Click here to load this message in the networking platform