delphi 实现虚拟打印, 远程集中打印

 

技术重点:

说白了就是利用已安装在电脑中的打开印驱动, 打印出Prn文件.再用Prn文件在其它地方相同驱动的打印机上打印.

1.从注册表(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers)中读出要监控的打印机的端口(Port)和设置(Attributes)保存备份.

2.在注册表(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports)中创建一个指向一个文件名的端口.

3.修改注册表(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers)令Port为(2.)所创建的端口, 令Attributes := Attributes or $00000100.

   这样可令打印任务在Spooler列表中打印到打定(文件)端口并且打印完成后不会自动删除已完成的任务.

4.重新启动打印任务服务(Spooler).

5.定时读取Spooler的打印任务列表的打印任务信息, 如果打印任务已完成, 则处理打印出来的文件, 并删除打印任务.

6.这样的结果是,打印任务不再到印到打印机, 而是打印到一个*.prn文件(这里是C:\1.prn), 我们就可以将此文件保存到数据库或上传到服务器, 在任何其他地方可取出来再打印.

   这样就可以实现远程打印.

 

以上就是制作出Prn打印文件的原理,在制作prn打印文时并不需要打印机, 只要装上了打印机的驱动即可,

我写成一个类, 源码:

 

 {
  VB声明
  Declare Function SetJob Lib "winspool.drv" Alias "SetJobA" (ByVal hPrinter As Long, ByVal JobId As Long, ByVal Level As Long, pJob As Byte, ByVal Command As Long) As Long
  说明
  对一个打印作业的状态进行控制
  返回值
  Long,非零表示成功,零表示失败。会设置GetLastError
  参数表
  参数 类型及说明
  hPrinter Long,指定一个打开打印机的句柄(用OpenPrinter取得)
  JobId Long,要修改的作业的编号
  Level Long,0,1或2
  pJob Byte,指定一个缓冲区。如级别(Level)设为1或2,那该缓冲区就包含了一个JOB_INFO_1或JOB_INFO_2结构。
  如级别为0,缓冲区为NULL(变成ByVal As Long,以便传递零值)。
  如指定了一个结构,则来自那个结构的信息会用于改变打印作业的设置
  (除JobId,pPrinterName,pMachineName,pDriverName,Size,Submitte以及Time字段外)
  Command Long,下述常数之一:
  JOB_CONTROL_CANCEL 取消作业
  JOB_CONTROL_PAUSE 暂停作业
  JOB_CONTROL_RESTART 重新启动一个已开始打印的作业
  JOB_CONTROL_RESUME 恢复一个暂停的作业


  Attributes: 打印机属性, 否脱机使用打印机也是这个属性控制
  0×0     立即开始打印(默认)
  0×1     在后台处理完最后一页时开始打印
  0×2     直接打印到打印机


  以上设置只有一个会生效,


  0×80    挂起不匹配文档
  0×100   保留打印的文档
  0×200   首先打印后台文档
  0×800   双向打印
}


{
Record 作为参数:
  procedure F(r: JOB_INFO_1);这种方式传的是内容,你那RECORD里面只有8X4==32字节...这么大小的RECORD,整个压栈也没事....因为是值原样复制,函数里面修改了也不会影响到外面.
  procedure F(p: PJobInfo1A);这种方式你传的是4字节地址值.
注:
  当用传值方式传比较长的RECORD, 栈会溢出.
  传进去的那块内存空间因为是在栈中, 所以不用释放, 函数返回它就释放了.
}


unit VirtualPrinter;


interface


uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Dialogs, Forms,
  ExtCtrls, DateUtils, IniFiles, Registry, Printers, WinSVC, WinSpool;


type
//  TJobMonitorsEvent = procedure(AJob: JOB_INFO_1; AJobStatus: string) of object;
//  TJobPrintingEvent = procedure(AJob: JOB_INFO_1) of object;
//  TJobCompleteEvent = procedure(AJob: JOB_INFO_1) of object;
  TJobMonitorsEvent = procedure(AJobDocName: string; AJobPStatus: string; AJobStatus: DWORD) of object;
  TJobPrintingEvent = procedure(AJobDocName: string) of object;
  TJobCompleteEvent = procedure(AJobDocName: string; APageCount: integer) of object;


  JOB_INFO_1_ARRAY = Array of JOB_INFO_1; //PJobInfo1A


  TVirtualPrinter = class(TObject)
  private
    FPrinterName: string;
    FSaveFileName: string;
    FSpoolerJobs: JOB_INFO_1_ARRAY;
    FTimer: TTimer;
    FJobMonitorsEvent: TJobMonitorsEvent;
    FJobPrintingEvent: TJobPrintingEvent;
    FJobCompleteEvent: TJobCompleteEvent;
    //
    FMonitorDateTime: TDateTime;
    procedure OnTimer(Sender: TObject);
    // 正在打印的打印机名字,这里我的打印机时网打。这里你要自己改
    // GetSpoolerJobs('\ibmserverHP LaserJet 1100');
    function GetSpoolerJobs: JOB_INFO_1_ARRAY;
  protected
  public
    //保存制作打印机设置
    class procedure SetPreparePrinter(APrinterName: string);
    //保存打印打印机设置
    class procedure SetPrintPrinter(APrinterName: string);


    //打印打印打印机名
    class var PrintPrinter: string;
    class function GetPrintPrinter: string;
    //制作打印打印机名
    class var PreparePrinter: string;
    class function GetPreparePrinter: string;
    //制作打印打印机端口
    class var PreparePrinterPort: string;
    class function GetPreparePrinterPort: string;
    //制作打印打印机属性
    class var PreparePrinterAttributes: integer;
    class function GetPreparePrinterAttributes: Integer;


    //增加打印端口
    class procedure AddPrinterPort(APort: string);
    class procedure DelPrinterPort(APort: string);
    //取指定打印机名的端口
    class function GetPrinterPort(APrinterName: string): string;
    //设置打印机的端口
    class procedure SetPrinterPort(APrinterName: string; APort: string);
    //取指定打印机名的属性
    class function GetPrinterAttributes(APrinterName: string): Integer;
    class procedure SetPrinterAttributes(APrinterName: string; AAttributes: integer);


    //检查是否已设定制作打印的打印机
    class function CheckPreparePrinter: string;
    //检查是否已设定打印打印的打印机
    class function CheckPrintPrinter: string;


    //直接用命令行打印*.prn文件到打印机
    class procedure Print(AFileName, APort: string);
  public
    constructor Create(APrinterName, ASaveFileName: string);
    destructor Destroy; override;
    procedure BackUpReg;
    procedure RestoreReg;
    // 添加一个文件端口
    procedure AddPort;
    // 删除一个文件端口
    procedure DelPort;
    // 目的是将打印任务打印到文件
    procedure SetPort;
    // 设置打印机属性, 即打印属性中的保留文档选项, 目的是令打印任务完成后, 保留任务, 不自动取消.
    procedure SetAttrib;
    // 重新启动打印任务服务
    procedure RestSpooler;
    procedure SetPrintInfo;
    function CtrlService(ServiceName: string; Status: Boolean; OverTime: Integer): Boolean;
    function SetJobPort: Boolean;
    // 删除打印任务
    function RemoveJob(JobId: DWORD): Boolean;
    property SpoolerJobs: JOB_INFO_1_ARRAY read GetSpoolerJobs;


    //启动虚拟打印监控
    procedure Start;
    //停止虚拟打印监控
    procedure Stop;


    //Windows的打印任务列表事件
    property JobMonitorsEvent: TJobMonitorsEvent read FJobMonitorsEvent write FJobMonitorsEvent;
    property JobPrintingEvent: TJobPrintingEvent read FJobPrintingEvent write FJobPrintingEvent;
    property JobCompleteEvent: TJobCompleteEvent read FJobCompleteEvent write FJobCompleteEvent;
  end;


const
  // Key_Printers2 = 'SOFTWARE\System\CurrentControlSet\Control\Print\Printers';
  // Key_Printers1 = 'SOFTWARE\System\ControlSet001\Control\Print\Printers';
  Key_Printers = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers';
  // 这里改变后, Key_Printers1, Key_Printers2 在注册表中会自动被同步, 真神奇.
  Key_Ports = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports';


implementation


{ TVirtualPrinter }


class procedure TVirtualPrinter.SetPrintPrinter(APrinterName: string);
begin
  TVirtualPrinter.PrintPrinter := APrinterName;
  with TiniFile.Create(ExtractFilePath(Application.ExeName) + 'System\System.ini') do
  begin
    try
      WriteString('BillPrint', 'PrintPrinter', APrinterName);
    finally
      Free;
    end;
  end;
end;


class procedure TVirtualPrinter.SetPreparePrinter(APrinterName: string);
begin
  TVirtualPrinter.PreparePrinter := APrinterName;
  TVirtualPrinter.GetPreparePrinterPort;
  TVirtualPrinter.GetPreparePrinterAttributes;
  with TiniFile.Create(ExtractFilePath(Application.ExeName) + 'System\System.ini') do
  begin
    try
      WriteString('BillPrint', 'PreparePrinter', APrinterName);
    finally
      Free;
    end;
  end;
end;


class function TVirtualPrinter.GetPrintPrinter: string;
begin
  if Trim(TVirtualPrinter.PrintPrinter) = '' then
  begin
    with TiniFile.Create(ExtractFilePath(Application.ExeName) + 'System\System.ini') do
    begin
      try
        TVirtualPrinter.PrintPrinter := ReadString('BillPrint', 'PrintPrinter', '');
      finally
        Free;
      end;
    end;
  end;


  Result := TVirtualPrinter.PrintPrinter;
end;


class function TVirtualPrinter.GetPreparePrinter: string;
begin
  if Trim(TVirtualPrinter.PreparePrinter) = '' then
  begin
    with TiniFile.Create(ExtractFilePath(Application.ExeName) + 'System\System.ini') do
    begin
      try
        TVirtualPrinter.PreparePrinter := ReadString('BillPrint', 'PreparePrinter', '');
      finally
        Free;
      end;
    end;
  end;


  Result := TVirtualPrinter.PreparePrinter;
end;


class function TVirtualPrinter.GetPreparePrinterPort: string;
begin
  if TVirtualPrinter.PreparePrinterPort = '' then
    TVirtualPrinter.PreparePrinterPort := TVirtualPrinter.GetPrinterPort(TVirtualPrinter.GetPreparePrinter);
  Result := TVirtualPrinter.PreparePrinterPort;
end;


class function TVirtualPrinter.GetPreparePrinterAttributes: Integer;
begin
  if TVirtualPrinter.PreparePrinterAttributes = 0 then
    TVirtualPrinter.PreparePrinterAttributes := TVirtualPrinter.GetPrinterAttributes(TVirtualPrinter.GetPreparePrinter);
  Result := TVirtualPrinter.PreparePrinterAttributes;
end;


class procedure TVirtualPrinter.AddPrinterPort(APort: string);
var
  lList: TStringList;
  i: Integer;
begin
  with TRegistry.Create do
  begin
    try
      // 指定根键为HKEY—LOCAL—MACHINE
      RootKey := HKEY_LOCAL_MACHINE;
      // 打开主键
      if OpenKey(Key_Ports, false) then
      begin
        lList := TStringList.Create;
        try
          GetValueNames(lList);
          for i := 0 to lList.Count - 1 do
          begin
            if SameText(lList.Strings[i], APort) then
            begin
              Break;
            end;
          end;
          WriteString(APort, '');
        finally
          FreeAndNil(lList);
        end;
      end;
    finally
      // 关闭主键
      CloseKey;
      Free;
    end;
  end;
end;


class procedure TVirtualPrinter.DelPrinterPort(APort: string);
begin
  with TRegistry.Create do
  begin
    try
      // 指定根键为HKEY—LOCAL—MACHINE
      RootKey := HKEY_LOCAL_MACHINE;
      // 打开主键
      if OpenKey(Key_Ports, false) then
      begin
        If ValueExists(APort) then
          DeleteValue(APort);
      end;
    finally
      // 关闭主键
      CloseKey;
      Free;
    end;
  end;
end;


class function TVirtualPrinter.GetPrinterPort(APrinterName: string): string;
begin
  Result := '';
  if APrinterName <> '' then
  with TRegistry.Create do
  begin
    try
      // 指定根键为HKEY—LOCAL—MACHINE
      RootKey := HKEY_LOCAL_MACHINE;
      // 打开主键
      if OpenKey(Key_Printers + '\' + APrinterName, false) then
        Result := ReadString('Port');
    finally
      // 关闭主键
      CloseKey;
      Free;
    end;
  end;
end;


class procedure TVirtualPrinter.SetPrinterPort(APrinterName: string; APort: string);
begin
  if APrinterName <> '' then
  with TRegistry.Create do
  begin
    try
      // 指定根键为HKEY—LOCAL—MACHINE
      RootKey := HKEY_LOCAL_MACHINE;
      // 打开主键
      if OpenKey(Key_Printers + '\' + APrinterName, false) then
        WriteString('Port', APort);
    finally
      // 关闭主键
      CloseKey;
      Free;
    end;
  end;
end;


class function TVirtualPrinter.GetPrinterAttributes(APrinterName: string): Integer;
begin
  Result := 0;
  if APrinterName <> '' then
  with TRegistry.Create do
  begin
    try
      // 指定根键为HKEY—LOCAL—MACHINE
      RootKey := HKEY_LOCAL_MACHINE;
      // 打开主键
      if OpenKey(Key_Printers + '\' + APrinterName, false) then
        Result := ReadInteger('Attributes');
    finally
      // 关闭主键
      CloseKey;
      Free;
    end;
  end;
end;


class procedure TVirtualPrinter.SetPrinterAttributes(APrinterName: string; AAttributes: integer);
begin
  if APrinterName <> '' then
  with TRegistry.Create do
  begin
    try
      // 指定根键为HKEY—LOCAL—MACHINE
      RootKey := HKEY_LOCAL_MACHINE;
      // 打开主键
      if OpenKey(Key_Printers + '\' + APrinterName, false) then
        WriteInteger('Attributes', AAttributes);
    finally
      // 关闭主键
      CloseKey;
      Free;
    end;
  end;
end;


class function TVirtualPrinter.CheckPreparePrinter: string;
var
  sPrinterName: string;
begin
  Result := '';
  sPrinterName := GetPreparePrinter;
  if Trim(sPrinterName) = '' then
  begin
    Result := '未设置制单打印机';
  end else
  if Printer.Printers.IndexOf(sPrinterName) = -1 then
  begin
    Result := '未安装制单打印机';
  end;
end;


class function TVirtualPrinter.CheckPrintPrinter: string;
var
  sPrinterName: string;
begin
  Result := '';
  sPrinterName := GetPrintPrinter;
  if Trim(sPrinterName) = '' then
  begin
    Result := '未设置打单打印机';
  end else
  if Printer.Printers.IndexOf(sPrinterName) = -1 then
  begin
    Result := '未安装打单打印机';
  end;
end;


class procedure TVirtualPrinter.Print(AFileName, APort: string);
var
  sCmd: string;
begin
  sCmd := 'cmd /c copy ' + AFileName + ' ' + APort + ' /b';
  WinExec(PAnsiChar(AnsiString(sCmd)), SW_HIDE);
  //ShellExecute(0, nil, 'cmd '或 'Command.com ', PChar( '/c ' + 命令), 运行目录名, 显示方式 );
end;





constructor TVirtualPrinter.Create(APrinterName, ASaveFileName: string);
begin
  if Trim(APrinterName) = '' then
    raise Exception.Create('必须指定打印机名');
  if Trim(ASaveFileName) = '' then
    raise Exception.Create('必须指定文件全路径名');


  FPrinterName := Trim(APrinterName);
  FSaveFileName := Trim(ASaveFileName);


  BackUpReg;
  SetPrintInfo;


  FMonitorDateTime := Now;
  FTimer := TTimer.Create(nil);
  FTimer.Interval := 1000;
  FTimer.OnTimer := Self.OnTimer;
  //FTimer.Enabled := true;
end;


destructor TVirtualPrinter.Destroy;
begin
  FTimer.Enabled := False;
  FreeAndNil(FTimer);
  RestoreReg;
  inherited;
end;


procedure TVirtualPrinter.BackUpReg;
var
  sPort: string;
  iAttributes: Integer;
begin
  sPort := TVirtualPrinter.GetPrinterPort(FPrinterName);
  iAttributes := TVirtualPrinter.GetPrinterAttributes(FPrinterName);
  with TIniFile.Create(ExtractFileDir(ParamStr(0)) + '\System\System.ini') do
  begin
    try
      if Pos(UpperCase('LPT'), UpperCase(sPort)) > 0 then
      begin
        WriteString('BillPrint', 'Port', sPort);
        WriteInteger('BillPrint', 'Attributes', iAttributes);
      end;
    finally
      Free;
    end;
  end;
end;


procedure TVirtualPrinter.RestoreReg;
var
  sPort: string;
  iAttributes: Integer;
begin
  with TIniFile.Create(ExtractFileDir(ParamStr(0)) + '\System\System.ini') do
  begin
    try
      sPort := ReadString('BillPrint', 'Port', '');
      iAttributes := ReadInteger('BillPrint', 'Attributes', 0);
    finally
      Free;
    end;
  end;
  TVirtualPrinter.SetPrinterPort(FPrinterName, sPort);
  TVirtualPrinter.SetPrinterAttributes(FPrinterName, iAttributes);
  RestSpooler;
  //TVirtualPrinter.SetPrinterAttributes(FPrinterName, iAttributes);
  DelPort;
end;


procedure TVirtualPrinter.SetAttrib;
var
  iAttributes: Integer;
begin
  iAttributes := TVirtualPrinter.GetPrinterAttributes(FPrinterName);
  iAttributes := iAttributes or $00000100; //0x100 :即打印属性中的保留文档选项, 目的是令打印任务完成后, 保留任务, 不自动取消.
  TVirtualPrinter.SetPrinterAttributes(FPrinterName, iAttributes);
end;


procedure TVirtualPrinter.AddPort;
begin
  TVirtualPrinter.AddPrinterPort(FSaveFileName);
end;


procedure TVirtualPrinter.DelPort;
begin
  TVirtualPrinter.DelPrinterPort(FSaveFileName);
end;


procedure TVirtualPrinter.SetPort;
begin
  TVirtualPrinter.SetPrinterPort(FPrinterName, FSaveFileName);
end;


procedure TVirtualPrinter.SetPrintInfo;
var
  lList: TStringList;
  i: Integer;
begin
  with TRegistry.Create do
  begin
    try
      // 指定根键为HKEY—LOCAL—MACHINE
      RootKey := HKEY_LOCAL_MACHINE;
      // 打开主键
      if OpenKey(Key_Printers, false) then
      begin
        lList := TStringList.Create;
        try
          GetKeyNames(lList);
          for i := 0 to lList.Count - 1 do
          begin
            if SameText(lList.Strings[i], FPrinterName) then
            begin
              AddPort;
              SetPort;
              SetAttrib;
              RestSpooler;
              Break;
            end;
          end;
        finally
          FreeAndNil(lList);
        end;
      end;
    finally
      // 关闭主键
      CloseKey;
      Free;
    end;
  end;
end;


procedure TVirtualPrinter.RestSpooler;
begin
  CtrlService('Spooler', false, 30);
  CtrlService('Spooler', true, 30);
end;


function TVirtualPrinter.CtrlService(ServiceName: string; Status: Boolean; OverTime: Integer): Boolean;
// 功能:控制WINDOWS的服务启动与停止
// ServiceName 服务名称
// Status  true 启动,false 停止
// OverTime 为超时处理,单位秒
var
  lpServiceArgVectors: Pchar;
  hscmanager, hService: SC_HANDLE;
  returnstatus: TServiceStatus;
  i: Integer;
begin
  Result := true;
  lpServiceArgVectors := nil;


  // 打开service control manager database
  hscmanager := OpenSCManager(nil, nil, SC_MANAGER_ENUMERATE_SERVICE);
  if hscmanager = 0 then
  begin
    Result := false;
    exit;
  end;


  // 打开服务,检测服务是否存在
  hService := OpenService(hscmanager, Pchar(ServiceName), SERVICE_ALL_ACCESS);
  if hService = 0 then
  begin
    CloseServiceHandle(hscmanager);
    CloseServiceHandle(hService);
    Result := false;
    exit;
  end;


  // 是否可查看该Service的状态
  if not QueryServiceStatus(hService, returnstatus) then
  begin
    CloseServiceHandle(hscmanager);
    CloseServiceHandle(hService);
    Result := false;
    exit;
  end;


  i := 0;
  if Status then // 如果是启动服务
  begin
    if (returnstatus.dwCurrentState = SERVICE_STOPPED) and
      (not WinSVC.StartService(hService, 0, Pchar(lpServiceArgVectors))) then
      Result := false
    else
      while (i < OverTime) and (returnstatus.dwCurrentState <> SERVICE_RUNNING) do
      begin
        Sleep(1000);
        QueryServiceStatus(hService, returnstatus);
        Application.ProcessMessages;
        inc(i);
      end;
    CloseServiceHandle(hscmanager);
    CloseServiceHandle(hService);
    exit;
  end else // 如果是停止服务
  begin
    if (returnstatus.dwCurrentState = SERVICE_RUNNING) and
      (not ControlService(hService, SERVICE_CONTROL_STOP, returnstatus)) then
      Result := false
    else
      while (i < OverTime) and (returnstatus.dwCurrentState <> SERVICE_STOPPED) do
      begin
        Sleep(1000);
        QueryServiceStatus(hService, returnstatus);
        Application.ProcessMessages;
        inc(i);
      end;
    CloseServiceHandle(hscmanager);
    CloseServiceHandle(hService);
    exit;
  end;
end;


Function TVirtualPrinter.GetSpoolerJobs: JOB_INFO_1_ARRAY;
var
  sPrinterName: String;


  i: Integer;
  hPrinter: THandle;
  bResult: Boolean;
  cbBuf: DWORD;
  pcbNeeded: DWORD;
  pcReturned: DWORD;
  aJobs: Array [0 .. 99] of JOB_INFO_1;
begin
  sPrinterName := Self.FPrinterName;


  cbBuf := 1000;


  bResult := OpenPrinter(Pchar(sPrinterName), hPrinter, Nil);
  if NOT bResult then
  begin
    ShowMessage('Error opening the printer');
    exit;
  end;


  // EnumPrinters( ... )
  bResult := EnumJobs(hPrinter, 0, Length(aJobs), 1, @aJobs, cbBuf, pcbNeeded, pcReturned);
  if NOT bResult then
  begin
    ShowMessage('Error Getting Jobs information');
    exit;
  end;


  for i := 0 to pcReturned - 1 do
  begin
    if aJobs[i].pDocument <> Nil then
    begin
      SetLength(Result, Length(Result) + 1);
      Result[Length(Result) - 1] := aJobs[i];
    end;
  end;
  FSpoolerJobs := Result;
end;


function TVirtualPrinter.RemoveJob(JobId: DWORD): Boolean;
var
  sPrinterName: String;
  hPrinter: THandle;
  pd: PRINTER_DEFAULTS;
begin
  sPrinterName := Self.FPrinterName;
  // You need a printer handle, open the printer
  pd.DesiredAccess := PRINTER_ALL_ACCESS;
  pd.pDatatype := nil;
  pd.pDevMode := nil;
  // 打开打印机  hPrinter := GetCurrentPrinterHandle;
  if OpenPrinter(Pchar(sPrinterName), hPrinter, @pd) then
    Result := SetJob(hPrinter, JobId, 0, nil, JOB_CONTROL_DELETE)
  else
    Result := false;
end;


function TVirtualPrinter.SetJobPort: Boolean;
var
  sPrinterName: String;
  hPrinter: THandle;
  pd: PRINTER_DEFAULTS;
  pInfo: PPrinterInfo2;
  bytesNeeded: DWORD;
begin
  sPrinterName := Self.FPrinterName;
  pd.DesiredAccess := PRINTER_ALL_ACCESS;
  pd.pDatatype := nil;
  pd.pDevMode := nil;
  // 打开打印机
  if OpenPrinter(Pchar(sPrinterName), hPrinter, @pd) then
  begin
    pInfo := AllocMem(bytesNeeded);
    try
      GetPrinter(hPrinter, 2, pInfo, bytesNeeded, @bytesNeeded);
      pInfo.pPortName := PChar(FSaveFileName);
      Result := SetPrinter(hPrinter, 2, pInfo, PRINTER_CONTROL_SET_STATUS);
    finally
      FreeMem(pInfo);
    end;
  end else
    Result := false;
end;


procedure TVirtualPrinter.OnTimer(Sender: TObject);
var
  aJobs: JOB_INFO_1_ARRAY;
  aJob: JOB_INFO_1;
  sJobStatus: string;
  dJobDateTime: TDateTime;
  i: Integer;
begin
  FTimer.Enabled := false;
  try
    aJobs := Self.SpoolerJobs;
    for i := 0 to Length(aJobs) - 1 do
    begin
      aJob := aJobs[i];
      sJobStatus := '';
      dJobDateTime := SystemTimeToDateTime(aJob.Submitted) + 8 / 24;//由于时区问题, 我们是处于东8区所以同格林威治时间有8小时差距: 东8区就+8, 西8区-8.
      //dJobDateTime := EncodeDateTime(aJob.Submitted.wYear, aJob.Submitted.wMonth, aJob.Submitted.wDay, aJob.Submitted.wHour + 8, aJob.Submitted.wMinute, aJob.Submitted.wSecond, aJob.Submitted.wMilliseconds);


      //对打开监控前的任务不处理
      if dJobDateTime <= FMonitorDateTime then
        Continue;


      case aJob.Status of
        JOB_STATUS_PAUSED: sJobStatus := 'JOB_STATUS_PAUSED';
        JOB_STATUS_ERROR: sJobStatus := 'JOB_STATUS_ERROR';
        JOB_STATUS_DELETING: sJobStatus := 'JOB_STATUS_DELETING';
        JOB_STATUS_SPOOLING: sJobStatus := 'JOB_STATUS_SPOOLING';
        JOB_STATUS_PRINTING: sJobStatus := 'JOB_STATUS_PRINTING';
        JOB_STATUS_OFFLINE: sJobStatus := 'JOB_STATUS_OFFLINE';
        JOB_STATUS_PAPEROUT: sJobStatus := 'JOB_STATUS_PAPEROUT';
        JOB_STATUS_PRINTED: sJobStatus := 'JOB_STATUS_PRINTED';
        
        JOB_STATUS_DELETED: sJobStatus := 'JOB_STATUS_DELETED';
        JOB_STATUS_BLOCKED_DEVQ: sJobStatus := 'JOB_STATUS_BLOCKED_DEVQ';
        JOB_STATUS_USER_INTERVENTION: sJobStatus := 'JOB_STATUS_USER_INTERVENTION';
        JOB_STATUS_RESTART: sJobStatus := 'JOB_STATUS_RESTART';
        JOB_POSITION_UNSPECIFIED: sJobStatus := 'JOB_POSITION_UNSPECIFIED';
      end;


      if Assigned(JobMonitorsEvent) then
        JobMonitorsEvent(aJob.pDocument, sJobStatus, aJob.Status);


      if (aJob.Status in [JOB_STATUS_PRINTING, JOB_STATUS_SPOOLING]) then
      begin
        if Assigned(JobPrintingEvent) then
          JobPrintingEvent(aJob.pDocument);
      end else
      if (aJob.Status in [JOB_STATUS_PRINTED]) or ((aJob.Status = 4096) and (sJobStatus = '')) then
      begin
        Self.RemoveJob(aJob.JobId);
        if Assigned(JobCompleteEvent) then
          JobCompleteEvent(aJob.pDocument, aJob.TotalPages);
      end;
    end;
  finally
    FTimer.Enabled := true;
  end;
end;


procedure TVirtualPrinter.Start;
begin
  FTimer.Enabled := True;
end;


procedure TVirtualPrinter.Stop;
begin
  FTimer.Enabled := False;
end;


end.


调用例子:

procedure TForm1.FormCreate(Sender: TObject);
begin
  FVirtualPrinter := TVirtualPrinter.Create(sPrinterName, FBillFileName);
  FVirtualPrinter.JobPrintingEvent := Self.JobPrintingEvent;
  FVirtualPrinter.JobCompleteEvent := Self.JobCompleteEvent;
end;


procedure TForm1.JobPrintingEvent(AJobDocName: string);
begin
  labMessage.Caption := '正在打印文件'+AJobDocName+'... ...';
end;

procedure TForm1.JobCompleteEvent(AJobDocName: string);
var
  lFileFullNameList: TStringList;
begin

  //上传打印出来的prn文
  lFileFullNameList := TStringList.Create;
  try
    lFileFullNameList.Add(AJobDocName);
    TfrmUpLoadFile.AutoUploadFiles('BillPrint\', False, lFileFullNameList);
  finally
    FreeAndNil(lFileFullNameList);
  end;

  labMessage.Caption := '打印文件完成!';
  //ShowMyMsg('', '已生成打印文件' + AJobDocName);
end;

 

实际打印时的示例代码:

  sFileFullName := 'c:\1.prn';

  if FileExists(sFileFullName) then
  begin                       
    sPrinterName := '你的打印机名称';
    sPrinterPort := '你的打印机端口号'
    sCmd := 'cmd /c copy ' + sFileFullName + ' ' + sPrinterPort + ' /b';
    WinExec(PChar(sCmd), SW_HIDE);
  end;

 

在打印prn文件时注意:

1.必须统一打印机型号.
2.真实打印的打印机的驱动必须要与制作prn打印文件的打印驱动一致.
3.在装有(1.中所指打印机)的电脑上使用
  a.如果是本地的LPT口打印机:
    Copy /b C:\aaa.PRN PRN:
    或
    Copy /b C:\aaa.PRN LPT1:
    将*.PRN文件打印到默认打印机.(注: LPT1 是本地打印端口, 你的机有可能是 LPT2,3,...)
  b.如果是USB打印机或网络打印机
    1.去市场买条USB转COM口或转LPT口的线(这肯定行,在此不多说);
    2.我们用个办法来骗WINDOWS一下,
      先找到安装打印机的PC机名称,然后把打印机共享,
      然后在你要打印的那台电脑,进入DOS,
      用NET USE命令完成映射:
      NET USE  LPT1 \\安装打印机电脑名\共享打印机名  /persistent:yes
      回车就完成映射,完成后再执行NET USE命令,查看MAP是否成功,显示OK就表示成功了.

 

你可能感兴趣的:(Delphi,delphi,attributes,string,microsoft,service)