Página 1 de 1

Multi-Threads - Exemplo com Minigui

Enviado: 18 Ago 2013 00:15
por yugi386
Prezados amantes da Minigui (este exemplo se refere a minigui de Roberto Lopez),

Estive vendo como é simples utilizar threads com harbour. No entanto, quando fazia isto o formulário do sistema congelava e ás vezes até travava.
Aqui passo a estratégia que utilizei para sanar este problema. Neste exemplo a rotina principal apresenta uma barra de progresso enquanto duas
Threads geram o mapa do HD listando todos os arquivos da unidade C: e todos os arquivos da unidade D: em dois arquivos de texto.
Ao final de cada thread é apresentado o valor da variável inicio.

Primeiramente vamos ao código fonte do arquivo Main:

Código: Selecionar todos

#include <hmg.ch>
#include "hbthread.ch"

memvar inicio // variavel acessivel dentro das threads
Function Main
        Load Window Main
        Main.Center
        Main.Activate
Return

function iniciar()
   inicio := 0
   
   // Veja como a simples a chamada das threads
   // Observe que as threads herdam os parametros da variavel memvar inicio
   nThreadId := hb_ThreadStart(  HB_THREAD_INHERIT_MEMVARS, @vthread() )
   nThreadId2 := hb_ThreadStart(  HB_THREAD_INHERIT_MEMVARS, @vthread2() )
           
   do while (inicio <= 100)
       Main.texto.value := str(inicio)
       Main.bar.value:= inicio
       Main.show()  // Necessário para dar o refresh na tela... é o pulo do gato...
       inicio = inicio + 2.5
       inkey(.5)
   enddo

   Main.bar.value:= 100
   msgbox("fim")
return

procedure vThread()
      for ct:= 1 to 1
         comando := "cmd /c dir c:\*.* /a/s > a.txt"
         run &comando
      next
      msgbox("fim da thread 1 " + str(inicio))
return NIL

procedure vThread2()
      comando := "cmd /c dir d:\*.* /a/s/on > b.txt"
      run &comando
      msgbox("fim da thread 2 " + str(inicio))
return NIL
Reparem na linha onde está Main.show(). Se você não colocar este comando a barra de progresso trava e somente é atualizada no final... Este é o pulo do gato!!!

O código fonte do formulário segue abaixo:

Código: Selecionar todos

* HMG-IDE Form Designer Generated Code
* (c) Roberto Lopez - http://sites.google.com/site/hmgweb

DEFINE WINDOW TEMPLATE AT 196 , 331 WIDTH 550 HEIGHT 350 VIRTUAL WIDTH Nil VIRTUAL HEIGHT Nil TITLE "Exemplo de Thread em Harbour" ICON NIL MAIN CURSOR NIL ON INIT Nil ON RELEASE Nil ON INTERACTIVECLOSE Nil ON MOUSECLICK Nil ON MOUSEDRAG Nil ON MOUSEMOVE Nil ON SIZE Nil ON MAXIMIZE Nil ON MINIMIZE Nil ON PAINT Nil BACKCOLOR Nil NOTIFYICON NIL NOTIFYTOOLTIP NIL ON NOTIFYCLICK Nil ON GOTFOCUS Nil ON LOSTFOCUS Nil ON SCROLLUP Nil ON SCROLLDOWN Nil ON SCROLLLEFT Nil ON SCROLLRIGHT Nil ON HSCROLLBOX Nil ON VSCROLLBOX Nil

    DEFINE PROGRESSBAR Bar
        ROW    150
        COL    50
        WIDTH  450
        HEIGHT 60
        RANGEMIN 1
        RANGEMAX 100
        VALUE 0
        TOOLTIP ""
        HELPID Nil
        VISIBLE .T.
        SMOOTH .F.
        VERTICAL .F. 
        BACKCOLOR NIL
        FORECOLOR NIL
    END PROGRESSBAR

    DEFINE BUTTON Button_1
        ROW    60
        COL    70
        WIDTH  100
        HEIGHT 28
        ACTION Iniciar()
        CAPTION "Iniciar"
        FONTNAME "Arial"
        FONTSIZE 9
        TOOLTIP ""
        FONTBOLD .F.
        FONTITALIC .F.
        FONTUNDERLINE .F.
        FONTSTRIKEOUT .F.
        ONGOTFOCUS Nil
        ONLOSTFOCUS Nil
        HELPID Nil
        FLAT .F.
        TABSTOP .T.
        VISIBLE .T.
        TRANSPARENT .F.
        MULTILINE .F.
        PICTURE Nil
        PICTALIGNMENT TOP
    END BUTTON

    DEFINE TEXTBOX texto
        ROW    70
        COL    320
        WIDTH  120
        HEIGHT 24
        FONTNAME "Arial"
        FONTSIZE 9
        TOOLTIP ""
        ONCHANGE Nil
        ONGOTFOCUS Nil
        ONLOSTFOCUS Nil
        FONTBOLD .F.
        FONTITALIC .F.
        FONTUNDERLINE .F.
        FONTSTRIKEOUT .F.
        ONENTER Nil
        HELPID Nil
        TABSTOP .T.
        VISIBLE .T.
        READONLY .F.
        RIGHTALIGN .F.
        DISABLEDBACKCOLOR Nil
        DISABLEDFONTCOLOR Nil
        BACKCOLOR NIL
        FONTCOLOR NIL
        INPUTMASK Nil
        FORMAT Nil
        VALUE ""
    END TEXTBOX

END WINDOW

Por padrão a minigui não compila o executável com a threads ativas. Para isto você deve habilitar este recurso na IDE, Gerenciador de Projeto, Aba configuração. Nesta aba configure o campo Multi-Thread para .T.

Saudações a todos!!

Multi-Threads - Exemplo com Minigui

Enviado: 18 Ago 2013 04:56
por Jairo Maia
Legal... Parabéns!

No meu caso o arquivo b.txt ficou vazio no primeiro teste, porque minha unidade D é o leitor de cd/dvd. Alterei pra F (meu slave) e listou também. Mostrou que seu exemplo funciona. Que venham outros...

Multi-Threads - Exemplo com Minigui

Enviado: 18 Ago 2013 13:33
por yugi386
Prezado Jairo,

Para dar mais uma contribuição neste sentido desenvolvi uma estratégia para rodar uma thread com um arquivo de lote ou programa externo sem apresentar a interface deste programa.
Isto é bom quando você precisa executar um arquivo de lote e não deseja que o usuário veja a tela do DOS ou interrompa o processo. O próprio arquivo de lote é criado dinamicamente então fica tudo um pouco mais seguro.

Vamos ao código: O exemplo da Thread é o mesmo do anterior, gerando dois mapas de arquivos de duas unidades do HD (C e D) neste caso. Se necessário basta alterar a unidade que o resultado será o mesmo.

Código: Selecionar todos

#include <hmg.ch>
#include "hbthread.ch"

#define QUEBRA CHR(13) + CHR(10)  // quebra de linha

memvar inicio // variavel acessivel dentro das threads
Function Main
        Load Window Main
        Main.Center
        Main.Activate
Return

function iniciar()
   inicio := 0
   
   // Veja como a simples a chamada das threads
   // Observe que as threads herdam os parametros da variavel memvar inicio
   nThreadId := hb_ThreadStart(  HB_THREAD_INHERIT_MEMVARS, @vthread() )
   nThreadId2 := hb_ThreadStart(  HB_THREAD_INHERIT_MEMVARS, @vthread2() )
           
   do while (inicio <= 100)
       Main.texto.value := str(inicio)
       Main.bar.value:= inicio
       Main.show()  // Necessário para dar o refresh na tela...
       inicio = inicio + 2.5
       inkey(.5)
   enddo

   Main.bar.value:= 100
   msgbox("fim")
return

procedure vThread()
      comando := "dir c:\*.* /a/s > a.txt"
      memowrit("a.bat",comando)
      executaBatch("a.bat","a.vbs")
      msgbox("fim da thread 1 " + str(inicio))
return NIL

procedure vThread2()
      comando := "dir d:\*.* /a/s/on > b.txt"
      memowrit("b.bat",comando)
      executaBatch("b.bat","b.vbs")
      msgbox("fim da thread 2 " + str(inicio))
return NIL

* =======================================================================================
/**
  Executa um arquivo de lote ou programa silenciosamente sem mostrar a tela do DOS ou
  qualquer janela!!!
  Funciona somente no windows pois utiliza VBscript
*/
* ========================================================================================
procedure executaBatch(arquivoBAT,arquivoVBS)
   if file(arquivoVBS)
      delete file &arquivoVBS
   endif
   
   comando:= 'Set WshShell = WScript.CreateObject("WScript.Shell")' + QUEBRA +;
            'Dim statusCode' +  QUEBRA +;
            'Dim fso' +  QUEBRA +;            
            'statusCode = WshShell.Run ("' +arquivoBAT+ '", 0, true)'  + QUEBRA +;
            'Set fso = CreateObject("Scripting.FileSystemObject")'  + QUEBRA +;
            'fso.MoveFile ' + '"' + arquivoBAT + '"' + ',' + '"' + arquivoBAT + '.tmp"'

   memowrit(arquivoVBS,comando)
   EXECUTE FILE arquivoVBS // executando o script VBS
   
   // Rotina para fazer com que a execução do script seja completada
   // antes de terminar a thread
   
   arq := arquivoBAT + ".tmp"
   do while.t.
      if file(arq)
         delete file &arq
         exit
      endif
      inkey(0.5)
   enddo
   
   // excluindo script VBS
   comando :=  arquivoVBS
   delete file &comando
   
return

O pulo do gato neste caso é o uso de VBscript (neste caso só vai funcionar no windows). A procedure executaBatch(arquivoBAT,arquivoVBS) recebe dois parametros:

1. arquivoBAT = arquivo de lote a ser executado silenciosamente (pode ser um executável também).
2 arquivoVBS = indica o nome do script temporário vbs (ex: "a.vbs", você pode dar qualquer nome, contanto que tenha a extensão .vbs)

assim é possível executar muitas threads em background, de modo invisível para o usuário final.

O código completo do exemplo segue em anexo.

Saudações a todos,

Viva Harbour, Minigui e Clipper!!!!

Multi-Threads - Exemplo com Minigui

Enviado: 19 Ago 2013 13:38
por HASA
:)) :-O
Nossa, ai sim,
:{ :-Y
HASA

Multi-Threads - Exemplo com Minigui

Enviado: 19 Ago 2013 16:16
por Pablo César
Parabéns yugi !

Gostei do modo que resolveu a tela preta tanto indesejada !

Obrigado por compartilhar.

Multi-Threads - Exemplo com Minigui

Enviado: 19 Ago 2013 16:54
por HASA
:))
Olá Pablo, faz tempo que não vejo seus sempre prestativos post´s por aqui ( forum da Minigui ). Yugi, não sei se estou viajando no código fonte mas... em tese qualquer pequeno truque em VB poderiamos executar dessa forma ?, seria muito bom,
:%
HASA

Multi-Threads - Exemplo com Minigui

Enviado: 20 Ago 2013 05:49
por Jairo Maia
Olá Pessoal,

Fiquei na contra mão nesse exemplo. O exemplo funcionou, mas a tela do DOS aparece.

O efeito foi o mesmo substituindo o comando VBscript por: Hb_Run( "Start dir c:\*.* /a/s > a.txt" ) e Hb_Run( "Start dir F:\*.* /a/s/on > b.txt" ). Não consegui ver o resultado como foi proposto.

HMG.3.1.5 Unicode - Windows XP SP3 e no Seven. Em ambos casos o mesmo resultado.

Multi-Threads - Exemplo com Minigui

Enviado: 20 Ago 2013 06:52
por yugi386
Prezado Jairo,

No primeiro exemplo a tela do DOS aparece mesmo.
No segundo exemplo ela não aparece. Baixe o pacote thread2.zip, descompacte e execute como está.

Para que a tela do DOS não apareça você não pode executar o arquivo VBS através do comando RUN ou Hb_RUN, deverá executar com o comando EXECUTE FILE, conforme está no exemplo. Exatamente por isto é que não é possível rodar um arquivo bat diretamente sem que a tela do DOS apareça.

Ao executar um comando externo com RUN ou Hb_run será sempre aberta uma janela do CMD (interpretador de comandos) e é exatamente isto que não queremos.
Execute o VBscript sempre com EXECUTE FILE.

Aproveitando para responder o HASA, acredito que qualquer VBscript, em tese, possa ser executado desta forma sim. Não sou programador VB e apenas utilizei a característica do VBscript para nos auxiliar a ocultar as janelas do CMD.

abraço,

Yugi.

Multi-Threads - Exemplo com Minigui

Enviado: 26 Out 2013 09:52
por asimoes
Excelente contribuição,

Apenas ter cuidado se a aplicação abortar, porque o cmd fica executando o comando infinitamente e é preciso terminar a execução pelo gerenciador de tarefas.