Ir para conteúdo
  • Cadastre-se

dev botao

ACBrPosPrinter com suporte a USB por Hook


  • Este tópico foi criado há 1556 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Recommended Posts

  • Fundadores

Olá pessoal,

Introduzi no componente ACBrPosPrinter, um novo mecanismo de acesso a Impressora

Agora poderemos acessar algumas impressoras, usando a Sintaxe:

  ACBrPosPrinter1.Porta := 'DLL:MARCA';

Onde MARCA, será o nome da Marca do Fabricante da Impressora... Até o momento, temos suporte para as marcas "ELGIN", e "EPSON"

A ideia por traz dessa nova sintaxe de Porta, é permitir usar a DLL/SO do Fabricante, para Imprimir diretamente na Impressora...

Ok.. o ACBrPosPrinter,  já conseguia acessar impressoras Não Fiscais, pela Porta USB, usando a Sintaxe "RAW:"

 ACBrPosPrinter1.Porta := 'RAW:Nome da Impressora no Windows';

 

Mas então porque desenvolvemos essa nova forma de acesso ?

A nova sintaxe "DLL:", tem algumas vantagens, em relação a sintaxe "RAW:"

  • Não depende da instalação do Driver de Spool da Impressora.. (note porém, que em alguns casos, o Driver de Spool não pode estar instalado, pois ele bloqueia o acesso a USB)
  • Podemos Ler Informações da Impressora (o que não é possível no modo RAW)

Entretanto, como foi dito antes, dependemos de DLL exclusiva do fabricante, para o acesso a Impressora pela USB...

  • Quais são essas DLLs ?
  • Para onde eu devo copiá-las ?

Vejamos como foi descrito no ACBrSerial-change-Log.txt

Citar

Data: 01/02/2019
-- ACBrPosPrinter --
[*] Melhorias na detecção de Porta Ativa e Controle de Porta
[+] Adicionado novo suporte a Impressoras através da Porta USB. Agora o
    ACBrPosPrinter aceita a sintaxe de portas como:  "DLL:MARCA", onde
    "MARCA" é o nome do Fabricante.
    Com isso o ACBrPosPrinter usará a DLL do fabricante para acesso a Impressora,
    pela porta USB.

-- ACBrEscPosHook --
[+] Implementado Classe genérica para criação de Hooks

-- ACBrEscPosHookElginDLL --
[+] Implementação de Hook com as DLLs da "ELGIN":  HprtPrinter.dll, hprtio.dll
    Use em Porta := "DLL:ELGIN", e mantenha as DLLs indicadas na mesma pasta do
    seu Executável

-- ACBrEscPosHookEpsonDLL --
[+] Implementação de Hook com a DLL da "EPSON":  InterfaceEpsonNF.dll
    Use em Porta := "DLL:EPSON", e mantenha a DLL indicada na mesma pasta do
    seu Executável

    (por: DSA)

Creio que isso responde as duas perguntas, correto ?

Você pode encontrar as DLLs no nosso SVN, na pasta: \ACBr\DLLs\PosPrinter, ou ainda pela Web: http://svn.code.sf.net/p/acbr/code/trunk2/DLLs/PosPrinter/

Você pode ainda baixar uma versão do Demo PosPrinterTeste,  atualizada, compilado em Lazarus/FPC no link abaixo:

 

 

Como funciona essa nova técnica ?

Quem faz todo acesso as Portas suportadas pelo ACBr, é um subcomponente chamado ACBrDevice, e há um bom tempo, esse componente já possui uma possibilidade de Integração por Hooks

O que é Hook ?
https://pt.wikipedia.org/wiki/Hooking

A ideia por trás dos Hooks, é instalar ganchos, em eventos, que nos permitam interceptar algumas ações e chamadas... Veja esse trecho de código

  FDevice.HookAtivar := PosPrinterHookAtivar;
  FDevice.HookDesativar := PosPrinterHookDesativar;
  FDevice.HookEnviaString := PosPrinterHookEnviaString;
  FDevice.HookLeString := PosPrinterHookLeString; 

Aqui instruímos o subcomponente ACBrDevice, a chamar nossos eventos, quando ele precisar  "Ativar", "Desativar" uma porta e também quando ele for "EnviarString" e "LeString", de uma determinada porta...

Então no interior do componente ACBrPosPrinter, implementamos os eventos indicados acima (PosPrinterHookAtivarPosPrinterHookDesativar, etc) ...

Com isso, o ACBrDevice executará um código nosso, ao invés do que ele normalmente executaria...  Veja que dentro dos eventos de ativação e desativação usamos uma Classe de Hook (leia mais abaixo)

procedure TACBrPosPrinter.PosPrinterHookAtivar(const APort: String; Params: String);
begin
  if Assigned(FHook) then
    FHook.Open(APort);
end;

procedure TACBrPosPrinter.PosPrinterHookDesativar(const APort: String);
begin
  if Assigned(FHook) then
    FHook.Close;
end;  

 

FHook por sua vez, é uma variável interna ao ACBrPosPrinter, que contem uma Classe de Hook (TACBrPosPrinterHook), e implementa os comandos necessários, para transmitir essas ações, a DLL do fabricante...

Veja o exemplo abaixo, como fica a implementação dos Hooks de  Ativar e Desativar, da ELGIN... observe que chamamos métodos Externos, da DLL da Elgin, como: "PrtPortOpenW" e "PrtPortClose"

procedure TElginUSBPrinter.Open(const APort: String);
var
  errorNo: Integer;
begin
  if Connected then
    Exit;

  inherited Open(APort);

  try
    errorNo := xPrtPortOpenW(FPrinter, WideString(fpPort));   // <------- A Q U I -------
    if (errorNo <> E_SUCCESS) then
      raise Exception.CreateFmt(CERROR_OPEN, [fpPort, fpPrinterName]);
  except
    fpConnected := False;
    fpPort := '';
    raise;
  end;
end;

procedure TElginUSBPrinter.Close;
var
  errorNo: Integer;
begin
  if not Connected then
    Exit;

  errorNo := xPrtPortClose(FPrinter);          // <------- A Q U I -------
  if (errorNo <> E_SUCCESS) then
    raise Exception.CreateFmt(CERROR_CLOSE, [fpPort, fpPrinterName]);

  inherited Close;
end;

 

Com isso, conseguimos usar a DLL do Fabricante, para estabelecer um túnel entre o ACBrPosPrinter e o equipamento...

 

Como posso implementar um Hook para um novo modelo ?

Os Primeiros passos, são verificar:

  • Se o Fabricante disponibiliza uma DLL para acesso direto ao equipamento (sem depender do Spooler)
  • Se há nessa DLL, um método que nos permita Escrever e Ler Dados da Porta USB

Ou seja, não precisamos de métodos de alto nível, que façam a formatação de caracteres, ou manipulem a impressora... Pois continua sendo o ACBrPosPrinter, quem montará toda a Sintaxe de comandos a serem enviados para a Impressora, usando a linguagem Esc/Pos... e igualmente, será o ACBrPosPrinter que fará a leitura de respostas, quando for necessário...

Na DLL da Elgin, temos um ótimo exemplo de método para isso...

function PrtDirectIO(printer:Pointer;    //  Ponteiro com a Impressora instanciada por PrtPrinterCreatorW
                     writeData:PByte;    //  Buffer com dados a serem enviados
                     writeNum:integer;   //  Número de Bytes em "writeData" (tamanho do Buffer)
                     readData:PByte;     //  Ponteiro com o Retorno a ser Lido  (Buffer de saída)
                     readNum:integer;    //  Numero de bytes disponíveis para escrita em "readData" (tamanho disponível no Buffer de Saída)
                     preadedNum:PInteger //  Número de bytes realmente escritos em "readData"
                    ): Integer; cdecl;   //  Status de retorno  E_SUCCESS = 0;

 

Tendo isso em mãos, podemos criar uma cópia de uma das Units já existentes, como por exemplo a Unit ACBrEscPosHookElginDLL.pas, e implementar o suporte usando a nova DLL, e efetuar os ajustes referente a nova Marca

 

  • Curtir 11
  • Obrigado 3
Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Link para o comentário
Compartilhar em outros sites

  • 10 meses depois ...
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.