Protheus :: "Script Linux CentOs/RedHat start/stop/restart TotvsdbAccess/TopConnect"
Para aqueles que usam o SGBD da Oracle
e o TotvsdbAccess/TopConnect em Linux, um pequeno "script" para
"start,stop and restart" do "daemon" do TotvsdbAccess/TopConnect.
Adapte-o às suas necessidades e, atenção para a seguinte linha:
NLS_LANG='Brazilian Portuguese_Brazil.WE8ISO8859P1'; export NLS_LANG
Altere-a conforme a configuração de NLS_LANG configurada no SGBD
#script file: dbacces
#!/bin/bash
# Avoid using root's TMPDIR
unset TMPDIR
RETVAL=0
TMP=/tmp; export TMP
TMPDIR=$TMP; export TMPDIR
ORACLE_BASE=/u01/app/oracle; export ORACLE_BASE
ORACLE_HOME=$ORACLE_BASE/product/10.2.0/db_1; export ORACLE_HOME
PATH=/usr/sbin:$PATH; export PATH
PATH=$ORACLE_HOME/bin:$PATH; export PATH
LD_LIBRARY_PATH=$ORACLE_HOME/lib32:/lib:/usr/lib; export LD_LIBRARY_PATH
NLS_LANG='Brazilian Portuguese_Brazil.WE8ISO8859P1'; export NLS_LANG
start() {
ulimit -n 65526
ulimit -s 1024
ulimit -c 0
ulimit -f unlimited
ulimit -v unlimited
ulimit -u unlimited
ulimit -t unlimited
ulimit -m unlimited
ulimit -l unlimited
ulimit -d unlimited
KIND="TotvsdbAccess"
echo -n $"Iniciando $KIND: "
cd /totvs/dba/dbaccess/multi
/totvs/dba/dbaccess/multi/totvsdbaccess &
RETVAL=$?
echo
}
stop() {
KIND="TotvsdbAccess"
echo -n $"Parando $KIND: "
killall /totvs/dba/dbaccess/multi/totvsdbaccess
RETVAL=$?
echo
echo ""
return $RETVAL
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo $"Usage: $0 {start|stop|restart}"
exit 2
esac
exit $?
quarta-feira, 21 de janeiro de 2009
Protheus :: Programação Básica em Advpl “Combo XL”
Classificando um Array Unidimensional e Bidimensional:
Da mesma forma que podemos passar um bloco para aScan(), também podemos passar um bloco para aSort(). A função aSort() classifica o Array passada por parâmetro, na ordem ascendente, mas, em substituição, podemos passar para ela um bloco de codificação para fazer a comparação. Quando fizermos isso, em vez de aSort() comparar os elementos internamente, ela passará para o Bloco de Codificação os dois elementos necessários para a comparação. A avaliação do Bloco, então, retornará verdadeiro se os dois elementos estiverem classificados em ordem; caso contrário, retornará falso. Quando o Bloco retornar falso, aSort() trocará os dois elementos.
É importante observar que aSort() passa o elemento com o número mais baixo do Array como primeiro parâmetro do bloco; desta forma, saberemos quem é quem! É igualmente importante notar que não teremos conhecimento dos dois elementos que estaremos comparando; só saberemos que o primeiro parâmetro é um elemento com número menor do que o segundo. aSort() assegura a classificação do Array, determina os elementos a comparar e sabe quando a classificação termina.
Para classificar um Array Unidimensional em ordem ascendente a passagem do bloco é opcional. Mas, para ordená-lo em ordem descendente, teremos passar o bloco informado a regra para a classificação:
Exemplos de classificação de Array unidimensional
Da mesma forma que podemos passar um bloco para aScan(), também podemos passar um bloco para aSort(). A função aSort() classifica o Array passada por parâmetro, na ordem ascendente, mas, em substituição, podemos passar para ela um bloco de codificação para fazer a comparação. Quando fizermos isso, em vez de aSort() comparar os elementos internamente, ela passará para o Bloco de Codificação os dois elementos necessários para a comparação. A avaliação do Bloco, então, retornará verdadeiro se os dois elementos estiverem classificados em ordem; caso contrário, retornará falso. Quando o Bloco retornar falso, aSort() trocará os dois elementos.
É importante observar que aSort() passa o elemento com o número mais baixo do Array como primeiro parâmetro do bloco; desta forma, saberemos quem é quem! É igualmente importante notar que não teremos conhecimento dos dois elementos que estaremos comparando; só saberemos que o primeiro parâmetro é um elemento com número menor do que o segundo. aSort() assegura a classificação do Array, determina os elementos a comparar e sabe quando a classificação termina.
Para classificar um Array Unidimensional em ordem ascendente a passagem do bloco é opcional. Mas, para ordená-lo em ordem descendente, teremos passar o bloco informado a regra para a classificação:
Exemplos de classificação de Array unidimensional
Local aNomes := {;
“Vinicius”,;
“Aline”,;
“Marinaldo”,;
“Michele”,;
“Camara”,;
“Andre”,;
“Valeria”,;
“João”,;
“Adriana”;
}
Local aSvNomes := aClone( aNomes ) //Efetua a cópia do array
Local bSort
//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes nao ordenados” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )
//Ordenando o Array Unidimensional em ordem ascendente sem
//utilizar o Bloco de Codificação:
aSort( aNomes )
//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes ordenados sem Bloco” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )
aNomes := aClone( aSvNomes ) //Restaurando aNomes
//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes nao ordenados” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )
//Monta Bloco para Classificação em Ordem Ascendente
bSort := { |x,y| x < y }
aSort( aNomes , NIL , NIL , bSort )
//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes ordenados ( asc ) utilizando Bloco” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )
//Monta Bloco para Classificação em Ordem Descendente
bSort := { |x,y| x > y }
aSort( aNomes , NIL , NIL , bSort )
//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes ordenados ( desc ) utilizando Bloco” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )
aNomes := aClone( aSvNomes ) //Restaurando a Nomes
//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes nao ordenados” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )
//Ordenando o Array em ordem Ascendente considerando apenas //alguns elementos
aSort( aNomes , 1 , 3 , { |x,y| x < y } )
//Mostrando as informações do Array no Console
ConOut( “Lista de Nomes Ordenados. Elem 1 a 3” )
ConOut( “” )
aEval( aNomes , { |cNome| ConOut( cNOme ) } )
ConOut( “” )
Exemplos de classificação de Array Bidimensional:
Local aStru := {;
{ "SNome" , "C" , 10 , 0 },;
{ "PNome" , "C" , 10 , 0 },;
{ "Ender1" , "C" , 30 , 0 },;
{ "Ender2" , "C" , 40 , 0 } ;
}
Local aSvStru := aClone( aStru ) //Efetua a cópia do array
Local bSort
/Mostrando as informações do Array no Console
ConOut( "Lista nao ordenada" )
ConOut( “” )
aEval( aStru , { |aElem| ConOut( aElem[1] ) } )
ConOut( “’ )
//Definindo bloco para ordenacao ascendente
bSort := { |x,y| x[1] < y[1] }
//Ordenando Array
aSort( aStru , NIL , NIL , bSort )
//Mostrando as informações do Array no Console
ConOut( "Lista ordenada ascendente" )
ConOut( "" )
aEval( aStru , { |aElem| ConOut( aElem[1] ) } )
ConOut( "" )
//Definindo bloco para ordenacao descendente
bSort := { |x,y| x[1] > y[1] }
//Ordenando Array
aSort( aStru , NIL , NIL , bSort )
//Mostrando as informações do Array no Console
ConOut( "Lista ordenada descendente" )
ConOut( “” )
aEval( aStru , { |aElem| ConOut( aElem[1] ) } )
ConOut( “” )
Protheus :: Programação Básica em Advpl “Combo XXXIX”
Buscando um Array Bidimensional:
Exemplos:
Local aStru := {;
{ “SNome” , “C” , 10 , 0 },;
{ “PNome” , “C” , 10 , 0 },;
{ “Ender1” , “C” , 30 , 0 },;
{ “Ender2” , “C” , 40 , 0 } ;
}
Local bExemplo := { |uElem| ConOut( uElem ) }
aEval( aStru[1] , bExemplo )
/*/
SNome
C
10
0
/*/
Local aStru := {;
{ “SNome” , “C” , 10 , 0 },;
{ “PNome” , “C” , 10 , 0 },;
{ “Ender1” , “C” , 30 , 0 },;
{ “Ender2” , “C” , 40 , 0 } ;
}
Local bExemplo := { |uElem| ConOut( uElem ) }
Local nLoop
Local nLoops
//Utilizando um Loop For/Next
nLoops := Len( aStru )
For nLoop := 1 To nLoops
aEval( aStru[ nLoop ] , bExemplo )
Next nLoop
//Ou aEval de forma recursiva
aEval( aStru , { |aElem| aEval( aElem , bExemplo ) } )
/*/
SNome
C
10
0
PNome
C
10
0
Ender1
C
30
0
Ender2
C
40
0
/*/
Local aStru := {;
{ “SNome” , “C” , 10 , 0 },;
{ “PNome” , “C” , 10 , 0 },;
{ “Ender1” , “C” , 30 , 0 },;
{ “Ender2” , “C” , 40 , 0 } ;
}
//Monto bloco para comparação exata
Local bAscan := { |aCpo| Upper( AllTrim( aCpo[1] ) ) == “PNOME” }
Local nPos
nPos := aScan( aStru , bAscan ) //nPos irá retornar 2
IF ( nPos > 0 )
ConOut( "A Posicao eh:" + Str( nPos ) )
ConOut( "Conteudo:" + aStru[ nPos , 1 ] )
Else
ConOut( "Elemento nao encontrado" )
EndIF
Existem
várias funções de manipulação de Array. Uma delas é a aScan() que serve
para efetuar pesquisa em um Array e retorna o numero do elemento do
Array ( Índice ) se a pesquisa foi satisfeita e Zero (0) caso contrário.
Essa função poderá ser utilizada tanto em Arrays unidimensionais como
em Arrays Bidimensionais. aScan() recebe quatro parâmetros: O primeiro é
o Array a ser pesquisado, o segundo é um Bloco de Código com as
instruções para pesquisa, o terceiro é a posição inicial ( índice ou
elemento inicial ) para pesquisa e o quarto é a posição final para
pesquisa ( índice ou elemento final ). O primeiro parâmetro o Array a
ser pesquisado é obrigatório, já do segundo ao quarto são opcionais. O
segundo parâmetro deve ser passado quando o array a ser pesquisado for
um array Multidimensional ou Bidimensional e, quando o Bloco de
Codificação for passado aEval() se comportará de maneira diferente do
usual. Em vez de aScan() comparar internamente elementos do Array com o
que estamos procurando, ela permitirá que o bloco passado como parâmetro
faça isso. aScan(), como aEval(), faz um Loop em cada elemento do Array
passando cada elemento por sua vez para o bloco de codificação. O bloco
de codificação, passado como parâmetro, deve retornar um valor lógico,
indicando se o elemento que aScan() acabou de passar era o que estávamos
procurando; .T. ( true = verdadeiro ) significa que o atual elemento é o
que estamos procurando; .F. ( false = falso ) significa que não é. Se a
avaliação do Bloco de Código for verdadeira, aScan() interrompe a busca
e retorna o número do atual elemento. Se a avaliação do Bloco de Código
for falsa, aScan() salta para o próximo elemento e efetua a avaliação
do Bloco mais uma vez. aScan() prossegue seu processo de busca até
terminar todos os elementos, em cujo caso retornará um Zero (0), ou até a
avaliação do Bloco retornar verdadeiro. aScan() é semelhante a aEval(),
exceto pelo fato de não existir uma maneira de interromper aEval()
prematuramente.
Exemplos:
Local aStru := {;
{ “SNome” , “C” , 10 , 0 },;
{ “PNome” , “C” , 10 , 0 },;
{ “Ender1” , “C” , 30 , 0 },;
{ “Ender2” , “C” , 40 , 0 } ;
}
Local bExemplo := { |uElem| ConOut( uElem ) }
aEval( aStru[1] , bExemplo )
/*/
Neste Caso, processamos apenas os elementos do sub-array aStru[1] e Teremos a seguida saída no Console do Server:
SNome
C
10
0
/*/
Para processar todos os elementos do array poderíamos fazê-lo da seguinte forma:
Local aStru := {;
{ “SNome” , “C” , 10 , 0 },;
{ “PNome” , “C” , 10 , 0 },;
{ “Ender1” , “C” , 30 , 0 },;
{ “Ender2” , “C” , 40 , 0 } ;
}
Local bExemplo := { |uElem| ConOut( uElem ) }
Local nLoop
Local nLoops
//Utilizando um Loop For/Next
nLoops := Len( aStru )
For nLoop := 1 To nLoops
aEval( aStru[ nLoop ] , bExemplo )
Next nLoop
//Ou aEval de forma recursiva
aEval( aStru , { |aElem| aEval( aElem , bExemplo ) } )
/*/
Neste
Caso, processamos Todos os elementos do Array e seus sub-arrays.
Teremos, dessa forma, a seguida saída no Console do Server:
SNome
C
10
0
PNome
C
10
0
Ender1
C
30
0
Ender2
C
40
0
/*/
Quando
usarmos um Bloco de Codificação com aScan(), ela passara cada elemento
por vez para o bloco. Se esse elemento for outra Array o parâmetro
formar conterá uma referência para esse Array. O bloco poderá então usar
um só subscrito com seu parâmetro para acessar determinado elemento.
Por exemplo, o seguinte fragmento de código localiza o número do
elemento do Array que descreve o campo “PNome”
Local aStru := {;
{ “SNome” , “C” , 10 , 0 },;
{ “PNome” , “C” , 10 , 0 },;
{ “Ender1” , “C” , 30 , 0 },;
{ “Ender2” , “C” , 40 , 0 } ;
}
//Monto bloco para comparação exata
Local bAscan := { |aCpo| Upper( AllTrim( aCpo[1] ) ) == “PNOME” }
Local nPos
nPos := aScan( aStru , bAscan ) //nPos irá retornar 2
IF ( nPos > 0 )
ConOut( "A Posicao eh:" + Str( nPos ) )
ConOut( "Conteudo:" + aStru[ nPos , 1 ] )
Else
ConOut( "Elemento nao encontrado" )
EndIF
Protheus :: Programação Básica em Advpl “Combo XXXVIII”
Processando Array com aEval():
aEval() é uma “internal function” que é utilizada para processar variáveis do tipo Array. Em sua forma mais simples usa dois parâmetros, um Array e um bloco de codificação. aEval() simplesmente faz um Loop em cada elemento do Array, passando um de cada vez, como um parâmetro para o Bloco de Codificação. Se o Array contém 5 elementos, aEval() avalia o bloco 5 vezes. Como exemplo, a seguinte chamada a aEval() mostra no Console do Server cada elemento do Array.
Local aArray := {“Marinaldo”,“Paulo Lira”,“Paulo Martins”,“Sergio”,“Henry” }
aEval( aArray , { |cElem| ConOut( cElem ) } )
Observamos que aEval() está avaliando o bloco de codificação, passando um parâmetro por vez. Denominamos o parâmetro como cElem, mas ele pode ser atribuído com qualquer nome.
aEval() é, realmente, uma função muito simples. Poderíamos até criar a nossa. Por exemplo:
User Function aEval( aArray , bEval )
Local nLoop
Local nLoops
nLoops := Len( aArray )
For nLoop := 1 To nLoops
Eval( bEval , aArray[ nLoop ] )
Next nLoop
Return( NIL )
E se usarmos a nossa função para processar um array teríamos o mesmo resultado de aEval().
Local aArray := {“Marinaldo”,“Paulo Lira”,“Paulo Martins”,“Sergio”,“Henry” }
U_aEval( aArray , { |cElem| ConOut( cElem ) } )
Na prática usaremos a função interna ( bem mais rápida ), mas tivemos uma idéia de como ela pode ser implementada em Advpl.
aEval() é uma “internal function” que é utilizada para processar variáveis do tipo Array. Em sua forma mais simples usa dois parâmetros, um Array e um bloco de codificação. aEval() simplesmente faz um Loop em cada elemento do Array, passando um de cada vez, como um parâmetro para o Bloco de Codificação. Se o Array contém 5 elementos, aEval() avalia o bloco 5 vezes. Como exemplo, a seguinte chamada a aEval() mostra no Console do Server cada elemento do Array.
Local aArray := {“Marinaldo”,“Paulo Lira”,“Paulo Martins”,“Sergio”,“Henry” }
aEval( aArray , { |cElem| ConOut( cElem ) } )
Observamos que aEval() está avaliando o bloco de codificação, passando um parâmetro por vez. Denominamos o parâmetro como cElem, mas ele pode ser atribuído com qualquer nome.
aEval() é, realmente, uma função muito simples. Poderíamos até criar a nossa. Por exemplo:
User Function aEval( aArray , bEval )
Local nLoop
Local nLoops
nLoops := Len( aArray )
For nLoop := 1 To nLoops
Eval( bEval , aArray[ nLoop ] )
Next nLoop
Return( NIL )
E se usarmos a nossa função para processar um array teríamos o mesmo resultado de aEval().
Local aArray := {“Marinaldo”,“Paulo Lira”,“Paulo Martins”,“Sergio”,“Henry” }
U_aEval( aArray , { |cElem| ConOut( cElem ) } )
Na prática usaremos a função interna ( bem mais rápida ), mas tivemos uma idéia de como ela pode ser implementada em Advpl.
Protheus :: Programação Básica em Advpl “Combo XXXVII”
Arrays Multidimensionais:
Examine a seguinte declaração de um array bidimensional:
Local aValores[ 3 , 2 ]
/*/
que também pode ser declarado como:
Local aValores := Array( 3 , 2 )
Local aValores := { { NIL , NIL } , { NIL , NIL } , { NIL , NIL } }
Local aValores := { Array( 2 ) , Array( 2 ) , Array( 2 ) }
/*/
Em Advpl essa(s) declaração(ões) serão implementadas como um array de três elementos do tipo array e cada array com dois elementos ( onde os valores iniciais são NIL )
Podemos testar da seguinte forma
ValType( aValores ) //A
Len( aValores ) //3
ValType( aValores[1] ) ) //A
Len( aValores[1] ) //2
aValores[1,1] //NIL
ValType( aValores[1,1] ) //U
Conforme demonstrado acima, aValores é uma referência a um Array de Três elementos, em que cada elemento é uma referência à outra array de dois elementos.
Cada elemento de aValores é simplesmente outro array e podemos testá-lo dessa forma. Por exemplo, para localizar aValores[1] passe-o para a função aScan(), como em:
nPos := aScan( aValores[1] , { |x| x == “Naldo” } )
Para classificar aValores[2], passe-o para aSort() com:
aSort( aValores[2] )
ou
aSort( aValores[2] , NIL , NIL , { |x,y| x < y } )
Abaixo segue a estrutura de memória de um array bidimensional.
Examine a seguinte declaração de um array bidimensional:
Local aValores[ 3 , 2 ]
/*/
que também pode ser declarado como:
Local aValores := Array( 3 , 2 )
Local aValores := { { NIL , NIL } , { NIL , NIL } , { NIL , NIL } }
Local aValores := { Array( 2 ) , Array( 2 ) , Array( 2 ) }
/*/
Em Advpl essa(s) declaração(ões) serão implementadas como um array de três elementos do tipo array e cada array com dois elementos ( onde os valores iniciais são NIL )
Podemos testar da seguinte forma
ValType( aValores ) //A
Len( aValores ) //3
ValType( aValores[1] ) ) //A
Len( aValores[1] ) //2
aValores[1,1] //NIL
ValType( aValores[1,1] ) //U
Conforme demonstrado acima, aValores é uma referência a um Array de Três elementos, em que cada elemento é uma referência à outra array de dois elementos.
Cada elemento de aValores é simplesmente outro array e podemos testá-lo dessa forma. Por exemplo, para localizar aValores[1] passe-o para a função aScan(), como em:
nPos := aScan( aValores[1] , { |x| x == “Naldo” } )
Para classificar aValores[2], passe-o para aSort() com:
aSort( aValores[2] )
ou
aSort( aValores[2] , NIL , NIL , { |x,y| x < y } )
Abaixo segue a estrutura de memória de um array bidimensional.
É
possível referenciar um elemento do array aValores como aValores[1,1]
ou aValores[1] mas, aValores[ , 1 ], apesar de não ocasionar erro
durante a compilação gerará erro durante a execução. Isso significa que
não podemos classificar, localizar ou selecionar colunas diretamente de
um array bidimensional.
Nota: Em Advpl os Arrays Multidimensionais são implementados como Arrays de Arrays.
Nota: Em Advpl os Arrays Multidimensionais são implementados como Arrays de Arrays.
Protheus :: Programação Básica em Advpl “Combo XXXVI”
Elementos de Arrays como Parâmetros:
Em Advpl os elementos do array são sempre passados por valor. Para podermos modificar um elemento do array passado como parâmetro, devemos preceder a variável com o operador de referência @, conforme fragmento de código abaixo.
Local aTeste[1]
aTeste[1] := "Valor Antigo"
Alert( aTeste[1] ) //”Valor Antigo”
aTeste( aTeste[1] )
Alert( aTeste[1] ) //”Valor Antigo”
aTeste( @aTeste[1] )
Alert( aTeste[1] ) //”Novo Valor”
Static function aTeste( cTeste )
cTeste := "Novo Valor"
Return( NIL )
Em Advpl os elementos do array são sempre passados por valor. Para podermos modificar um elemento do array passado como parâmetro, devemos preceder a variável com o operador de referência @, conforme fragmento de código abaixo.
Local aTeste[1]
aTeste[1] := "Valor Antigo"
Alert( aTeste[1] ) //”Valor Antigo”
aTeste( aTeste[1] )
Alert( aTeste[1] ) //”Valor Antigo”
aTeste( @aTeste[1] )
Alert( aTeste[1] ) //”Novo Valor”
Static function aTeste( cTeste )
cTeste := "Novo Valor"
Return( NIL )
Protheus :: Programação Básica em Advpl “Combo XXXV”
Passando Arrays por Referência:
Verificamos
que as variáveis do tipo Array são realmente passadas por valor. Mas
podemos, também, passá-las por referência. Para que isso aconteça, temos
que preceder o parâmetro real com o símbolo @. Dessa forma, poderemos
mudar os elementos do array como antes, e a rotina continuará recebendo
uma referência aos elementos do array. Mas, neste caso, a diferença é
que, agora, podemos mudar aquilo a que o array se refere, uma vez que a
própria variável array foi passada por referência. Podemos ilustrar isso
da seguinte forma:
Local aNomes := { “P.A.Cabral” , “Santos Dumont” }
U_Test( @aNomes )
Alert( aNomes[1] ) // “Einstein”
User Function Test( aFormal )
Alert( aFormal[1] ) // P.A.Cabral
aFormal := { “Einstein” , “Leibiniz” }
Return( NIL )
Se quisermos que a rotina chamada nunca altere o valor do array original, podemos forçar que ela sempre receba uma cópia do array ao invés do próprio array. Isso pode ser feito utilizando a função aClone() da seguinte forma:
Local aNomes := { “P.A.Cabral” , “Santos Dumont” }
U_Test( aClone( aNomes ) )
Alert( aNomes[1] ) // “P.A.Cabral”
User Function Test( aFormal )
Alert( aFormal[1] ) // P.A.Cabral
aFormal[1] := “Einstein”
Alert( aFormal[1] ) //“Einstein”
Return( NIL )
A função aClone() para uma cópia do Array criando uma nova referência. Dessa forma, garantimos, na função chamadora a integridade do array mas, na função chamada podemos alterar o conteúdo da maneira que quisermos.
Para exemplificar melhor a passagem por referência, tomemos como exemplo o seguinte fragmento de código que referência uma variável do tipo numérica.
Local nVar := 1
Local cVar
U_Test( @nVar )
cVar := Str( nVar )
Alert( cVar ) //Aqui nVar terá o valor 3
User Funcion Test( nFormal )
nFormal := 3
Return( NIL )
Passamos nVar por referência para a função U_Test(); portanto, no retorno, nVar foi alterada. Um aspecto importante a observar é que, dentro da função U_Test(), não sabemos se nFormal foi passado por referência ou por valor. Apenas fizemos a atribuição. Este processo é diferente em linguagens como C, que não suportam envio por referência. Na linguagem C, se quisermos permitir que uma rotina altere um parâmetro, precisaremos passar o endereço do parâmetro. Conseqüentemente, a rotina receptora receberá um ponteiro para a verdadeira variável e a modificará por vias indiretas. Neste caso a rotina receptora precisa saber como o parâmetro foi passado ( o que não acontece em Advpl ).
Em Advpl ocorre uma via indireta “implícita” quando fazemos uma atribuição a um parâmetro formal que a rotina chamadora passa por referência. Isso poderá ser visualizado se imaginarmos que a codificação em Advpl recebesse um ponteiro para um parâmetro real e, toda vez que mudássemos um parâmetro passado por referência, estaríamos alterando o parâmetro real. O compilador ou o sistema, na execução, estarão implementando a via indireta.
Mas como isso se aplica aos arrays passados por referência? Já vimos que os arrays são implementados como ponteiros; portanto, ao passar um array por referência, na verdade, estamos passando um ponteiro para um ponteiro. O Advpl manipula a via indireta quando acessamos os elementos, mas, como recebemos uma referência à variável do array, podemos mudar aquilo para o qual o array aponta, além do seu conteúdo.
Local aNomes := { “P.A.Cabral” , “Santos Dumont” }
U_Test( @aNomes )
Alert( aNomes[1] ) // “Einstein”
User Function Test( aFormal )
Alert( aFormal[1] ) // P.A.Cabral
aFormal := { “Einstein” , “Leibiniz” }
Return( NIL )
Se quisermos que a rotina chamada nunca altere o valor do array original, podemos forçar que ela sempre receba uma cópia do array ao invés do próprio array. Isso pode ser feito utilizando a função aClone() da seguinte forma:
Local aNomes := { “P.A.Cabral” , “Santos Dumont” }
U_Test( aClone( aNomes ) )
Alert( aNomes[1] ) // “P.A.Cabral”
User Function Test( aFormal )
Alert( aFormal[1] ) // P.A.Cabral
aFormal[1] := “Einstein”
Alert( aFormal[1] ) //“Einstein”
Return( NIL )
A função aClone() para uma cópia do Array criando uma nova referência. Dessa forma, garantimos, na função chamadora a integridade do array mas, na função chamada podemos alterar o conteúdo da maneira que quisermos.
Para exemplificar melhor a passagem por referência, tomemos como exemplo o seguinte fragmento de código que referência uma variável do tipo numérica.
Local nVar := 1
Local cVar
U_Test( @nVar )
cVar := Str( nVar )
Alert( cVar ) //Aqui nVar terá o valor 3
User Funcion Test( nFormal )
nFormal := 3
Return( NIL )
Passamos nVar por referência para a função U_Test(); portanto, no retorno, nVar foi alterada. Um aspecto importante a observar é que, dentro da função U_Test(), não sabemos se nFormal foi passado por referência ou por valor. Apenas fizemos a atribuição. Este processo é diferente em linguagens como C, que não suportam envio por referência. Na linguagem C, se quisermos permitir que uma rotina altere um parâmetro, precisaremos passar o endereço do parâmetro. Conseqüentemente, a rotina receptora receberá um ponteiro para a verdadeira variável e a modificará por vias indiretas. Neste caso a rotina receptora precisa saber como o parâmetro foi passado ( o que não acontece em Advpl ).
Em Advpl ocorre uma via indireta “implícita” quando fazemos uma atribuição a um parâmetro formal que a rotina chamadora passa por referência. Isso poderá ser visualizado se imaginarmos que a codificação em Advpl recebesse um ponteiro para um parâmetro real e, toda vez que mudássemos um parâmetro passado por referência, estaríamos alterando o parâmetro real. O compilador ou o sistema, na execução, estarão implementando a via indireta.
Mas como isso se aplica aos arrays passados por referência? Já vimos que os arrays são implementados como ponteiros; portanto, ao passar um array por referência, na verdade, estamos passando um ponteiro para um ponteiro. O Advpl manipula a via indireta quando acessamos os elementos, mas, como recebemos uma referência à variável do array, podemos mudar aquilo para o qual o array aponta, além do seu conteúdo.
Protheus :: Programação Básica em Advpl “Combo XXXIV”
Arrays como Parâmetro:
Ao escrever uma função nós a definimos em termos de parâmetros. Chamamos esses parâmetros de Parâmetros Formais da função. Quando chamamos a função, fornecemos os parâmetros que denominamos Parâmetros Reais.
Quando passamos um Array como parâmetro, o Advpl faz o parâmetro formal apontar para o mesmo local indicado pelo parâmetro real. Dessa forma, poderemos mudar os elementos do Array fazendo atribuições a seus parâmetros.
Se o parâmetro real tiver um escopo Public ou Private, ele ficará também visível dentro da rotina chamada. Portanto, poderemos usar o operador == para verificar se eles realmente apontam para o mesmo array. Exemplo:
Private aNomes := { “P.A.Cabral” , “Santos Dumont” }
Test( aNomes )
Function Test( aFormal )
Return( aFormal == aNomes ) //.T.(apenas porque se referem ao //mesmo ponteiro)
Ao escrever uma função nós a definimos em termos de parâmetros. Chamamos esses parâmetros de Parâmetros Formais da função. Quando chamamos a função, fornecemos os parâmetros que denominamos Parâmetros Reais.
Quando passamos um Array como parâmetro, o Advpl faz o parâmetro formal apontar para o mesmo local indicado pelo parâmetro real. Dessa forma, poderemos mudar os elementos do Array fazendo atribuições a seus parâmetros.
Se o parâmetro real tiver um escopo Public ou Private, ele ficará também visível dentro da rotina chamada. Portanto, poderemos usar o operador == para verificar se eles realmente apontam para o mesmo array. Exemplo:
Private aNomes := { “P.A.Cabral” , “Santos Dumont” }
Test( aNomes )
Function Test( aFormal )
Return( aFormal == aNomes ) //.T.(apenas porque se referem ao //mesmo ponteiro)
Podemos, concluir, a partir disto, que os array são passados por referência;
acima de tudo, podemos mudar seus elementos. Entretanto, precisamos distinguir entre a própria variável array e os elementos a que ela se refere. Ao passar um array, como demonstrado, estamos passando uma referência aos elementos do array; conseqüentemente, a rotina receptora poderá alterá-los por meio de referência. Entretanto, ela pode mudar a própria referência? Ela pode fazer a referência apontar para outra array?
Examinemos o seguinte fragmento de programa:
Local aNomes := { “P.A.Cabral” , “Santos Dumont” }
U_Test( aNomes )
Alert( aNomes[1] ) // P.A.Cabral
User Function Test( aFormal )
Alert( aFormal[1] ) // P.A.Cabral
aFormal := { “Einstein” , “Leibiniz” }
Return( NIL )
A função U_Test() atribui seu parâmetro formal, aFormal, a um novo Array, mas isto não afeta o que aNomes indica. Ela simplesmente cria um novo Array e faz aFormal apontar para ele; aNomes permanece inalterado. É como se tivéssemos atribuído um tipo de dado diferente a aFormal – aNomes continua inalterado no retorno.
Disso podemos concluir que os Arrays são passados por valor! Não podemos mudar o parâmetro. Parece confuso mas: a variável array é passada por valor. Isto significa que não podemos mudar “aquilo para o qual ela estiver apontando”. Entretanto, como as variáveis de arrays são simples referências para os arrays, podemos mudar o conteúdo dos elementos do array, uma vez que sua rotina recebe uma referência a eles.
acima de tudo, podemos mudar seus elementos. Entretanto, precisamos distinguir entre a própria variável array e os elementos a que ela se refere. Ao passar um array, como demonstrado, estamos passando uma referência aos elementos do array; conseqüentemente, a rotina receptora poderá alterá-los por meio de referência. Entretanto, ela pode mudar a própria referência? Ela pode fazer a referência apontar para outra array?
Examinemos o seguinte fragmento de programa:
Local aNomes := { “P.A.Cabral” , “Santos Dumont” }
U_Test( aNomes )
Alert( aNomes[1] ) // P.A.Cabral
User Function Test( aFormal )
Alert( aFormal[1] ) // P.A.Cabral
aFormal := { “Einstein” , “Leibiniz” }
Return( NIL )
A função U_Test() atribui seu parâmetro formal, aFormal, a um novo Array, mas isto não afeta o que aNomes indica. Ela simplesmente cria um novo Array e faz aFormal apontar para ele; aNomes permanece inalterado. É como se tivéssemos atribuído um tipo de dado diferente a aFormal – aNomes continua inalterado no retorno.
Disso podemos concluir que os Arrays são passados por valor! Não podemos mudar o parâmetro. Parece confuso mas: a variável array é passada por valor. Isto significa que não podemos mudar “aquilo para o qual ela estiver apontando”. Entretanto, como as variáveis de arrays são simples referências para os arrays, podemos mudar o conteúdo dos elementos do array, uma vez que sua rotina recebe uma referência a eles.
Protheus :: Programação Básica em Advpl “Combo XXXIII”
Estruturas de Dados usando Arrays:
Local aNomes
Local aNomesNovos
Local aVar
aNomes := { “P.A.Cabral” , “Santos Dumont” }
aNomesNovos := { “P.A.Cabral” , “Santos Dumont” }
aVar := aNomes
aVar == aNomes // .T. (apenas porque referem-se ao mesmo ponteiro)
aNomes == aNomesNovos //.F.
aVar == aNomesNovos //.F.
Local aNomes
Local aNomesNovos
Local aVar
aNomes := { “P.A.Cabral” , “Santos Dumont” }
aNomesNovos := { “P.A.Cabral” , “Santos Dumont” }
aVar := aNomes
ArrayCompare( aVar , aNomes ) // .T.
ArrayCompare(aNomes , aNomesNovos ) //.T.
fCompArray( aVar , aNomes ) // .T.
fCompArray (aNomes , aNomesNovos ) //.T.
Podemos
dizer que em Advpl os Arrays são tratados como referências ou
ponteiros. Eles podem ser utilizados para armazenar estruturas de dados
complexas como outros arrays, dados numéricos, strings, objetos e até
blocos de codificação.
Arrays como Referência:
Ao declarar um array de nElementos, na verdade, estamos criando duas estruturas de memória. O Advpl cria uma estrutura para conter a própria variável e depois uma seqüência de estruturas para armazenar cada elemento do Array. Imagine uma variável contendo um array como separada dos próprios elementos do array. Pense na variável como se estivesse “apontando” ou “citando” os elementos. Presumindo-se a seguinte declaração:
Local aNomes[4]
Poderíamos afirmar que aNomes refere-se ao primeiro elemento do Array.
Ao declarar um array de nElementos, na verdade, estamos criando duas estruturas de memória. O Advpl cria uma estrutura para conter a própria variável e depois uma seqüência de estruturas para armazenar cada elemento do Array. Imagine uma variável contendo um array como separada dos próprios elementos do array. Pense na variável como se estivesse “apontando” ou “citando” os elementos. Presumindo-se a seguinte declaração:
Local aNomes[4]
Poderíamos afirmar que aNomes refere-se ao primeiro elemento do Array.
Em
Advpl podemos fazer com que outra variável Array faça referência ( ou
aponte ) para uma outra variável Array. Neste caso elas serão
equivalentes.
Ex.:
Local aNomes[4]
Local aVar
aVar := aNomes
Ex.:
Local aNomes[4]
Local aVar
aVar := aNomes
Qualquer alteração efetuada nos em aNomes será automaticamente refletida em aVar e vice-e-versa.
Podemos utilizar o operador == para determinar se duas variáveis do tipo Array se referem a um mesmo Array. Este operador não compara os elemento por elemento do Array; em vez disso, ele retorna verdadeiro se elas se referirem à mesma Array; caso contrário, falso. Por Exemplo:
Podemos utilizar o operador == para determinar se duas variáveis do tipo Array se referem a um mesmo Array. Este operador não compara os elemento por elemento do Array; em vez disso, ele retorna verdadeiro se elas se referirem à mesma Array; caso contrário, falso. Por Exemplo:
Local aNomes
Local aNomesNovos
Local aVar
aNomes := { “P.A.Cabral” , “Santos Dumont” }
aNomesNovos := { “P.A.Cabral” , “Santos Dumont” }
aVar := aNomes
aVar == aNomes // .T. (apenas porque referem-se ao mesmo ponteiro)
aNomes == aNomesNovos //.F.
aVar == aNomesNovos //.F.
Embora
os elementos dos Arrays aNomes e aNomesNovos contenham os mesmos
valores, eles são arrays distintas ( cada um ocupando uma referência
diferente na memória ) e, por isso, o operador == retorna falso.
Nota:
O operador == é uma sobreposição. Se for utilizado com string de
caracteres, ele retornará verdadeiro se as duas strings foram exatamente
iguais. Se for utilizado com duas variáveis arrays, ele retornará
verdadeiro se elas se referirem aos elementos do mesmo array.
Existem duas funções, desenvolvidas em AdvPl, que comparam dois arrays, elemento por elemento, e retorna .T. se forem iguais ou .F. caso contrário.
Existem duas funções, desenvolvidas em AdvPl, que comparam dois arrays, elemento por elemento, e retorna .T. se forem iguais ou .F. caso contrário.
ArrayCompare() e fCompArray()
Usando o mesmo exemplo acima, se utilizarmos ArrayCompare() ou fCompArray() teremos:
Usando o mesmo exemplo acima, se utilizarmos ArrayCompare() ou fCompArray() teremos:
Local aNomes
Local aNomesNovos
Local aVar
aNomes := { “P.A.Cabral” , “Santos Dumont” }
aNomesNovos := { “P.A.Cabral” , “Santos Dumont” }
aVar := aNomes
ArrayCompare( aVar , aNomes ) // .T.
ArrayCompare(aNomes , aNomesNovos ) //.T.
fCompArray( aVar , aNomes ) // .T.
fCompArray (aNomes , aNomesNovos ) //.T.
Neste caso ArrayCompare() e fCompArray() retornarão sempre .T. uma vez que os elementos no Array são iguais.
Protheus :: Programação Básica em Advpl “Combo XXXII”
Parâmetros de Blocos de Codificação:
As barras verticais utilizadas na montagem do Bloco de Codificação são as delimitadoras das especificações dos parâmetros formais e o que caracteriza a Montagem de um bloco de código, se elas não forem utilizadas o sistema interpretará que o valor que está sendo atribuído à variável é um array e não um Bloco de Código. A definição e uso desses parâmetros é opcional mas as barras não.
Exemplo:
bVar := { | nVar1 , nVar2 | nVar1 + nVar2 }
nRetult := Eval( bVar , 10 , 20 )
Quando Eval avaliar o bloco de codificação nVar1 terá o conteúdo 10 e nVar2 terá o conteúdo 20. Como em uma função, os parâmetros deverão ser passados na seqüência em que foram declarados. Outro exemplo poderia ser:
bVar := { |nVar1,nVar2,cVar3,aVar4| U_Teste(nVar1,nVar2,cVar3,aVar4)}
Eval( bVar , 10 , , “C” , {} )
Neste caso, Eval, na avaliação do bloco de codificação, irá efetuar a chamada à função U_Teste() com passagem de parâmetros. Observe que o segundo parâmetro não foi passado, sendo assim, U_Teste() receberá, respectivamente: 10, NIL, “C” e {} que serão os conteúdos reais das variáveis nVar1, nVar2, cVar3 e aVar4.
As barras verticais utilizadas na montagem do Bloco de Codificação são as delimitadoras das especificações dos parâmetros formais e o que caracteriza a Montagem de um bloco de código, se elas não forem utilizadas o sistema interpretará que o valor que está sendo atribuído à variável é um array e não um Bloco de Código. A definição e uso desses parâmetros é opcional mas as barras não.
Exemplo:
bVar := { | nVar1 , nVar2 | nVar1 + nVar2 }
nRetult := Eval( bVar , 10 , 20 )
Quando Eval avaliar o bloco de codificação nVar1 terá o conteúdo 10 e nVar2 terá o conteúdo 20. Como em uma função, os parâmetros deverão ser passados na seqüência em que foram declarados. Outro exemplo poderia ser:
bVar := { |nVar1,nVar2,cVar3,aVar4| U_Teste(nVar1,nVar2,cVar3,aVar4)}
Eval( bVar , 10 , , “C” , {} )
Neste caso, Eval, na avaliação do bloco de codificação, irá efetuar a chamada à função U_Teste() com passagem de parâmetros. Observe que o segundo parâmetro não foi passado, sendo assim, U_Teste() receberá, respectivamente: 10, NIL, “C” e {} que serão os conteúdos reais das variáveis nVar1, nVar2, cVar3 e aVar4.
Protheus :: Programação Básica em Advpl “Combo XXXI”
Blocos de Codificação:
Os Blocos de Codificação ou CodeBlock são variáveis que utilizamos para armazenar fragmentos de codificação do programa. Esses fragmentos podem ser passados para funções que irão executá-los.
Os Blocos de Codificação ou CodeBlock são variáveis que utilizamos para armazenar fragmentos de codificação do programa. Esses fragmentos podem ser passados para funções que irão executá-los.
Criamos um bloco de codificação à partir da seguinte sintaxe:
bVar := { || Teste() }
O caractere { significa “inicio do bloco de codificação” e o caractere “}”, “fim do bloco de codificação”. As duas barras verticais ( || ) delimitam a lista de parâmetros formais do bloco de codificação.
A variável bVar pode ser Local, Static, Private ou Public; não faz diferença. Ela só contém um fragmento de codificação e quando avaliado ele chama a função Test()
Para avaliar – ou executar – um bloco de código, use a função Eval(), como em: nResult := Eval( bVar ) esta chama a função Test(). Como toda função Eval() retorna um resultado ( que é o último elemento do bloco de código )
Ex.:
bRetVal := {|nVar1,nVar2,nVar3|nVar2:=nVar1+nVar2, nVar2*nVar3}
nResult := Eval( bRetVal , 1 , 2 , 3 )
//Após avaliar o bloco, Eval retornará para nResult 9 que é o resultado //da operação definida no bloco de código.
bVar := { || Teste() }
O caractere { significa “inicio do bloco de codificação” e o caractere “}”, “fim do bloco de codificação”. As duas barras verticais ( || ) delimitam a lista de parâmetros formais do bloco de codificação.
A variável bVar pode ser Local, Static, Private ou Public; não faz diferença. Ela só contém um fragmento de codificação e quando avaliado ele chama a função Test()
Para avaliar – ou executar – um bloco de código, use a função Eval(), como em: nResult := Eval( bVar ) esta chama a função Test(). Como toda função Eval() retorna um resultado ( que é o último elemento do bloco de código )
Ex.:
bRetVal := {|nVar1,nVar2,nVar3|nVar2:=nVar1+nVar2, nVar2*nVar3}
nResult := Eval( bRetVal , 1 , 2 , 3 )
//Após avaliar o bloco, Eval retornará para nResult 9 que é o resultado //da operação definida no bloco de código.
Protheus :: Programação Básica em Advpl “Combo XXX”
LOCAL, STATIC e MACROS:
Por exemplo:
nVar := 1
cVar := “nVar”
Alert( Str( &( cVar ) ) //ou Alert( Str( __ExecMacro( cVar ) )
nVar := 1
cType := Type( “nVar” )
Alert( cType ) //”N”
Local nVar := 1
Local cType := ValType( nVar )
Alert( cType ) //”N”
Local nVar
Local cType := ValType( nVar )
Alert( cType ) //”U”
As
variáveis Local e Static não são mantidas na tabela de símbolos de
execução, portanto, os programas que as utilizam executam com mais
velocidade e menos memória. Uma conseqüência disto, conteúdo, é que não
poderemos acessá-las a partir de expansão de macro. O operador de macros
& ou a internal function __ExecMacro() compilam a codificação,
durante o processo de execução, à partir de uma string de caracteres
contendo uma expressão. Se a expressão citar uma variável, o sistema na
execução precisará encontrá-la na tabela de símbolos. O problema com as
Locai e Static, evidentemente, é que o sistema, por não armazená-las na
tabela de símbolos, nas as encontra fazendo com que ocorra erro durante a
execução do sistema se foram expandidas por macro.
Por exemplo:
nVar := 1
cVar := “nVar”
Alert( Str( &( cVar ) ) //ou Alert( Str( __ExecMacro( cVar ) )
Se nVar for uma variável Local, o programa falhará. Observe que cVar
pode ser uma variável Local ou Static; mas só que qualquer coisa
contida na string da expressão não pode ser uma Local nem uma Static.
Uma forma de contornar isso seria usando blocos de codificação para
poder referir-se a variável Local ou Static definidas em outro programa
ou função.
Nota: Não é possível referir-se a variável Local ou Static dentro de uma expansão de macro.
A função Type() usa macros internamente. Use a função Type() para determinar o tipo de uma variável. Envie o nome da variável entre aspas, como em:
Nota: Não é possível referir-se a variável Local ou Static dentro de uma expansão de macro.
A função Type() usa macros internamente. Use a função Type() para determinar o tipo de uma variável. Envie o nome da variável entre aspas, como em:
nVar := 1
cType := Type( “nVar” )
Alert( cType ) //”N”
O
problema é que, se nVar for uma variável Local ou Static, a expansão
interna da macro não a encontrará na tabela de símbolos e a função
Type() retornará “U”, indicando que a variável não está definida.
Existe uma função semelhante, a ValType(). As principais diferenças entre ValType() e Type() são que ValType() realmente opera com variáveis Local e Static e não é preciso enviar os parâmetros de ValType() entre aspas, como em:
Existe uma função semelhante, a ValType(). As principais diferenças entre ValType() e Type() são que ValType() realmente opera com variáveis Local e Static e não é preciso enviar os parâmetros de ValType() entre aspas, como em:
Local nVar := 1
Local cType := ValType( nVar )
Alert( cType ) //”N”
Pelo
fato de não ser preciso colocar o parâmetro de ValType() entre aspas, a
variável citada precisa ser declarada, caso contrário ocorrerá erro
durante a execução. Uma variável pode ser declarada incluindo-a numa
lista de parâmetros formais, seja como Private, atribuindo-se um valor a
ela, seja como uma instrução de declaração explícita. Se ela não for
declarada obteremos um erro de execução por estar tentando passar uma
variável não definida para uma função. Observe, entretanto, que
ValType() ainda retornará “U” se enviarmos a ela uma variável declarada,
mas indefinida como em:
Local nVar
Local cType := ValType( nVar )
Alert( cType ) //”U”
Isso ocorre porque as variáveis, Private, Local e Static declaradas e
não inicializadas tem sempre um valor NIL. Portanto, estamos passando,
na realidade, NIL para ValType() e ela retornará um “U”.
Protheus :: Programação Básica em Advpl “Combo XXIX”
STATIC e PUBLIC:
TEST1.PRG
Static nVar := 0 //Static em nível de arquivo ( externa )
User Function A()
Alert( Str( nVar ) ) //nVar será zero aqui...
nVar := 10
Return( NIL )
User Function B()
Alert( Str( nVar ) ) //nVar será 10 aqui...
Return( NIL )
User Function C()
Static nVar //Static em nível de função ( interna )
DEFAULT nVar := 0
Return( ++nVar )
TEST2.PRG
User Function Test() //nVar não está disponível em Test2()
Local nLoop
Local nLoops
Local nRetC
U_A() //aqui acessando a variável Static Externa
U_B() //aqui acessando a variável Static Externa
nLoops := 15
For nLoop := 1 To nLoops
nRetC := U_C() //aqui acessando a variável Static Interna
Alert( Str( nRetC ) )//nVar em U_C será incrementada a cada
//iteração do Loop
Next nLoop
Return( NIL )
Estas duas
instruções também declaram variáveis. PUBLIC declara variáveis que
ficam visíveis durante todo o programa. Os problemas que examinamos com
relação as variáveis Private também ocorrem com as variáveis Public,
contudo com mais abrangência. Em vez de isolados a rotinas chamadas a
partir de uma função, os problemas ocorrem na aplicação inteira.
As variáveis STATIC podem ajudar a solucionar este problema. Existem dois tipos de variáveis static: internas e externas. A diferença está na abrangência. As variáveis static em nível de arquivo abrangem o arquivo: elas ficam visíveis em qualquer função ou rotina dentro do arquivo de programa no qual estão declaradas. As variáveis static declaradas dentro de uma função ( static internas ) tem escopo local, exatamente como as variáveis local.
Obs.: No Protheus, as variáveis Static criadas à partir do menu, serão liberadas sempre que se voltar para o menu. Já as Static criadas durante a entrada do sistema serão mantidas enquanto o sistema estiver sendo usado.
A característica principal da variável Static é que ela pode sair do escopo e não conseguirmos mais acessá-la, mas, na próxima vez que em que entrarmos no programa ou função que a declarou ela terá o mesmo valor da última vez em que o programa ou função foi executado. Exemplo:
As variáveis STATIC podem ajudar a solucionar este problema. Existem dois tipos de variáveis static: internas e externas. A diferença está na abrangência. As variáveis static em nível de arquivo abrangem o arquivo: elas ficam visíveis em qualquer função ou rotina dentro do arquivo de programa no qual estão declaradas. As variáveis static declaradas dentro de uma função ( static internas ) tem escopo local, exatamente como as variáveis local.
Obs.: No Protheus, as variáveis Static criadas à partir do menu, serão liberadas sempre que se voltar para o menu. Já as Static criadas durante a entrada do sistema serão mantidas enquanto o sistema estiver sendo usado.
A característica principal da variável Static é que ela pode sair do escopo e não conseguirmos mais acessá-la, mas, na próxima vez que em que entrarmos no programa ou função que a declarou ela terá o mesmo valor da última vez em que o programa ou função foi executado. Exemplo:
TEST1.PRG
Static nVar := 0 //Static em nível de arquivo ( externa )
User Function A()
Alert( Str( nVar ) ) //nVar será zero aqui...
nVar := 10
Return( NIL )
User Function B()
Alert( Str( nVar ) ) //nVar será 10 aqui...
Return( NIL )
User Function C()
Static nVar //Static em nível de função ( interna )
DEFAULT nVar := 0
Return( ++nVar )
TEST2.PRG
User Function Test() //nVar não está disponível em Test2()
Local nLoop
Local nLoops
Local nRetC
U_A() //aqui acessando a variável Static Externa
U_B() //aqui acessando a variável Static Externa
nLoops := 15
For nLoop := 1 To nLoops
nRetC := U_C() //aqui acessando a variável Static Interna
Alert( Str( nRetC ) )//nVar em U_C será incrementada a cada
//iteração do Loop
Next nLoop
Return( NIL )
segunda-feira, 19 de janeiro de 2009
Protheus :: Programação Básica em Advpl “Combo XXVIII”
LOCAL, PRIVATE:
Essas duas instruções declaram variáveis para uso subseqüente. Entretanto, existem diferenças importantes entre os tipos de variáveis que elas criam. PRIVATE declara variáveis visíveis na rotina na qual você as declara e também em qualquer rotina que você chame a partir do ponto da declaração.
User Function Test()
Private nI
U_Test1()
Return( NIL )
User Function Test1()
//aqui poderemos enxergar e fazer uso da variável nI criada em //U_Test()
Return( NIL )
A instrução LOCAL declara variáveis somente visíveis na rotina na qual foram declaradas; dessa forma:
User Function Test()
Local nI
U_Test1()
Return( NIL )
User Function Test1()
//aqui não poderemos enxergar e nem fazer uso da variável nI criada //em U_Test()
Return( NIL )
O problema com as Private:
As variáveis Private representam um dos maiores obstáculos para a criação de rotinas modulares e confiáveis. O principal problema é que elas não são privadas de modo algum e isto ocasiona sérios problemas. Por exemplo, examine o seguinte:
User Function Test()
Private nI := 0
Private aTest[10]
While ( ++nI <= 10 )
Test1()
//nI será sempre 4 aqui
End While
Return( NIL )
Static Function Test1()
nI := 0
While ( ++nI <= 3 )
aTest[ nI ] := nI
End While
Return( NIL )
No fragmento de código acima, a função Test1() usa uma variável nI para o controle do Loop, mas como a função Test(), sua chamadora, já possui uma variável nI, Test1() usa esta última. O resultado é que, no retorno a Test(), nI contém o valor 4, fazendo com que o programa fique em loop infinito. Uma solução para o problema seria declarar a variável nI como private em Test1(). Desta forma a variável nI criada em Test() será preservada no retorno de Test1(). A melhor solução, seria criar nI como Local, tanto em Test() como em Test1().
User Function Test()
Local nI := 0
Local aTest[10]
While ( ++nI <= 10 )
Test1( @aTest )
End While
Return( NIL )
Static Function Test1( aTest )
Local nI := 0
While ( ++nI <= 3 )
aTest[ nI ] := nI
End While
Return( NIL )
Obs.: Muito cuidado ao usar variáveis Private no Programa. Isso pode causar probemas muito difíceis de depurar. Use Variáveis Locais e abra mão da referência para obter retorno. Garanta que todas as Variáveis utilizadas no Programa tenham sido previamente declaradas.
Essas duas instruções declaram variáveis para uso subseqüente. Entretanto, existem diferenças importantes entre os tipos de variáveis que elas criam. PRIVATE declara variáveis visíveis na rotina na qual você as declara e também em qualquer rotina que você chame a partir do ponto da declaração.
User Function Test()
Private nI
U_Test1()
Return( NIL )
User Function Test1()
//aqui poderemos enxergar e fazer uso da variável nI criada em //U_Test()
Return( NIL )
A instrução LOCAL declara variáveis somente visíveis na rotina na qual foram declaradas; dessa forma:
User Function Test()
Local nI
U_Test1()
Return( NIL )
User Function Test1()
//aqui não poderemos enxergar e nem fazer uso da variável nI criada //em U_Test()
Return( NIL )
O problema com as Private:
As variáveis Private representam um dos maiores obstáculos para a criação de rotinas modulares e confiáveis. O principal problema é que elas não são privadas de modo algum e isto ocasiona sérios problemas. Por exemplo, examine o seguinte:
User Function Test()
Private nI := 0
Private aTest[10]
While ( ++nI <= 10 )
Test1()
//nI será sempre 4 aqui
End While
Return( NIL )
Static Function Test1()
nI := 0
While ( ++nI <= 3 )
aTest[ nI ] := nI
End While
Return( NIL )
No fragmento de código acima, a função Test1() usa uma variável nI para o controle do Loop, mas como a função Test(), sua chamadora, já possui uma variável nI, Test1() usa esta última. O resultado é que, no retorno a Test(), nI contém o valor 4, fazendo com que o programa fique em loop infinito. Uma solução para o problema seria declarar a variável nI como private em Test1(). Desta forma a variável nI criada em Test() será preservada no retorno de Test1(). A melhor solução, seria criar nI como Local, tanto em Test() como em Test1().
User Function Test()
Local nI := 0
Local aTest[10]
While ( ++nI <= 10 )
Test1( @aTest )
End While
Return( NIL )
Static Function Test1( aTest )
Local nI := 0
While ( ++nI <= 3 )
aTest[ nI ] := nI
End While
Return( NIL )
Obs.: Muito cuidado ao usar variáveis Private no Programa. Isso pode causar probemas muito difíceis de depurar. Use Variáveis Locais e abra mão da referência para obter retorno. Garanta que todas as Variáveis utilizadas no Programa tenham sido previamente declaradas.
Protheus :: Programação Básica em Advpl “Combo XXVII”
Instruções de Declarações:
Uma instrução de declaração é uma instrução para um compilador. Neste seção estamos interessados nas seguintes declarações:
LOCAL
PRIVATE
PUBLIC
STATIC
LOCAL, PRIVATE, PUBLIC e STATIC declaram variáveis.
Obs.: Para evitar ambigüidade entre campos e variáveis Private com o mesmo nome é recomendado a prefixação da variável Private com M->[Nome da Variável ] para identificar que é uma variável de memória e/ou prefixar o campo com o respectivo Alias usando o operador -> como em: [Alias]->[ Campo da Tabela ]. Obs.: Melhor do que declarar uma variável com o Mesmo nome do campo é declarar a variável utilizando a notação Húngara Modificada onde as variáveis são prefixadas com a letra minúscula correspondente ao seu tipo ( Em Inglês ).
Tipo: Exemplo:
"N"umeric nNumber "C"haracter cChar
"B"lock bBlock
"D"ate dDate
"L"ogic lOk
"O"ject oObj
"U"ndefined uVar
Lembrando que em Advpl as Variáveis não são prototipadas o tipo da variável só será conhecido apos a atribuição de algum valor a ela e que para testar o tipo podemos utilizar as funções ValType( ) ou Type( ). Onde: ValType avalia o conteúdo da variável passada como parâmetro para verificar o seu tipo, e Type(), primeiro, "macro-executa" a variável para obter seu conteúdo e de pois o avalia. Sendo assim, para usar a função Type() é necessário que a variável seja do Tipo Private ou Public
Uma instrução de declaração é uma instrução para um compilador. Neste seção estamos interessados nas seguintes declarações:
LOCAL
PRIVATE
PUBLIC
STATIC
LOCAL, PRIVATE, PUBLIC e STATIC declaram variáveis.
Obs.: Para evitar ambigüidade entre campos e variáveis Private com o mesmo nome é recomendado a prefixação da variável Private com M->[Nome da Variável ] para identificar que é uma variável de memória e/ou prefixar o campo com o respectivo Alias usando o operador -> como em: [Alias]->[ Campo da Tabela ]. Obs.: Melhor do que declarar uma variável com o Mesmo nome do campo é declarar a variável utilizando a notação Húngara Modificada onde as variáveis são prefixadas com a letra minúscula correspondente ao seu tipo ( Em Inglês ).
Tipo: Exemplo:
"N"umeric nNumber "C"haracter cChar
"B"lock bBlock
"D"ate dDate
"L"ogic lOk
"O"ject oObj
"U"ndefined uVar
Lembrando que em Advpl as Variáveis não são prototipadas o tipo da variável só será conhecido apos a atribuição de algum valor a ela e que para testar o tipo podemos utilizar as funções ValType( ) ou Type( ). Onde: ValType avalia o conteúdo da variável passada como parâmetro para verificar o seu tipo, e Type(), primeiro, "macro-executa" a variável para obter seu conteúdo e de pois o avalia. Sendo assim, para usar a função Type() é necessário que a variável seja do Tipo Private ou Public
Protheus :: Programação Básica em Advpl “Combo XXVI”
Recursividade:
As funções e rotinas podem ser recursivas, isto é, podem chamar a si mesmas. Cada chamada recebe uma nova cópia das variáveis Local e Private e de quaisquer parâmetros. Quando uma função retorna, os valores anteriores são restabelecidos. Obs.: Não são alocadas novas cópias de variáveis STATIC.
Exemplo:
User Function SaveArray( uArray , cFileName , nErr )
Local cValTypeuArray := ValType( uArray )
Local lSaveArray := .F.
Local aArray
Local nfHandle
Begin Sequence
IF !( cValTypeuArray $ "A/O" )
Break
EndIF
IF ( cValTypeuArray == "O" )
aArray := ClassDataArr( uArray )
Else
aArray := uArray
EndIF
lSaveArray := FileCreate( cFileName , @nfHandle , @nErr )
IF !( lSaveArray )
Break
EndIF
SaveArr( nfHandle , aArray ) //Aqui chamamos a função para salvar o Array
fClose( nfHandle )
End Sequence
Return( lSaveArray )
Static Function SaveArr( nfHandle , aArray )
Local cElemType
Local uCntSave
Local nLoop
Local nLoops
nLoops := Len( aArray )
uCntSave := ( "A" + StrZero( nLoops , 10 ) )
fWrite( nfHandle , uCntSave )
For nLoop := 1 To nLoops
cElemType := ValType( aArray[ nLoop ] )
IF ( cElemType $ "A/O" ) //Aqui efetuamos a chamada recursiva
IF ( cElemType == "A" )
SaveArr( nfHandle , aArray[ nLoop ] )
Else
SaveArr( nfHandle , ClassDataArr( aArray[ nLoop ] ) )
EndIF
Else
IF ( cElemType == "B" )
uCntSave := GetCBSource( aArray[ nLoop ] )
ElseIF ( cElemType == "C" )
uCntSave := aArray[ nLoop ]
ElseIF ( cElemType == "D" )
uCntSave := Dtos( aArray[ nLoop ] )
ElseIF ( cElemType == "L" )
uCntSave := IF( aArray[ nLoop ] , ".T." , ".F." )
ElseIF ( cElemType == "N" )
uCntSave := Transform( aArray[ nLoop ] , RetPictVal( aArray[ nLoop ] ) )
EndIF
uCntSave := ( cElemType + StrZero( Len( uCntSave ) , 5 ) + uCntSave )
fWrite( nfHandle , uCntSave )
EndIF
Next nLoop
Return( NIL )
As funções e rotinas podem ser recursivas, isto é, podem chamar a si mesmas. Cada chamada recebe uma nova cópia das variáveis Local e Private e de quaisquer parâmetros. Quando uma função retorna, os valores anteriores são restabelecidos. Obs.: Não são alocadas novas cópias de variáveis STATIC.
Exemplo:
User Function SaveArray( uArray , cFileName , nErr )
Local cValTypeuArray := ValType( uArray )
Local lSaveArray := .F.
Local aArray
Local nfHandle
Begin Sequence
IF !( cValTypeuArray $ "A/O" )
Break
EndIF
IF ( cValTypeuArray == "O" )
aArray := ClassDataArr( uArray )
Else
aArray := uArray
EndIF
lSaveArray := FileCreate( cFileName , @nfHandle , @nErr )
IF !( lSaveArray )
Break
EndIF
SaveArr( nfHandle , aArray ) //Aqui chamamos a função para salvar o Array
fClose( nfHandle )
End Sequence
Return( lSaveArray )
Static Function SaveArr( nfHandle , aArray )
Local cElemType
Local uCntSave
Local nLoop
Local nLoops
nLoops := Len( aArray )
uCntSave := ( "A" + StrZero( nLoops , 10 ) )
fWrite( nfHandle , uCntSave )
For nLoop := 1 To nLoops
cElemType := ValType( aArray[ nLoop ] )
IF ( cElemType $ "A/O" ) //Aqui efetuamos a chamada recursiva
IF ( cElemType == "A" )
SaveArr( nfHandle , aArray[ nLoop ] )
Else
SaveArr( nfHandle , ClassDataArr( aArray[ nLoop ] ) )
EndIF
Else
IF ( cElemType == "B" )
uCntSave := GetCBSource( aArray[ nLoop ] )
ElseIF ( cElemType == "C" )
uCntSave := aArray[ nLoop ]
ElseIF ( cElemType == "D" )
uCntSave := Dtos( aArray[ nLoop ] )
ElseIF ( cElemType == "L" )
uCntSave := IF( aArray[ nLoop ] , ".T." , ".F." )
ElseIF ( cElemType == "N" )
uCntSave := Transform( aArray[ nLoop ] , RetPictVal( aArray[ nLoop ] ) )
EndIF
uCntSave := ( cElemType + StrZero( Len( uCntSave ) , 5 ) + uCntSave )
fWrite( nfHandle , uCntSave )
EndIF
Next nLoop
Return( NIL )
Protheus :: Programação Básica em Advpl “Combo XXV”
Chamando Funções Indiretamente:
Em Advpl é possível efetuar a chamada de funções indiretamente usando o operador de macros. Este procedimento lhe permitirá usar aplicativos controlados por dados.
Pode-se usar um bando de dados ( à exemplo do SX3 ) para armazenar uma seqüência de funções a serem chamadas, e depois usá-lo para gerar telas, efetuar validação de campos, iniciar o conteúdo de campos, etc.
Para passar uma função como parâmetro, passe o nome da função como uma string de caracteres ou como uma chamada de função dentro de um bloco de codificação. Para chamá-la, use o operador de macros & ou a função __ExecMacro() quando estiver passando uma string de caracteres ou Eval() para avaliar o bloco de código.
Exemplo:
cExecTeste := “U_TESTE()”
&( cExecTeste )
ou
cExecTeste := “U_TESTE()” //Executa a função U_TESTE()
__ExecMacro ( cExecTeste ) //Executa a função U_TESTE()
ou ainda
bExecTeste := { || U_TESTE() )
Eval( bExecTeste ) //Executa a função U_TESTE()
Em Advpl é possível efetuar a chamada de funções indiretamente usando o operador de macros. Este procedimento lhe permitirá usar aplicativos controlados por dados.
Pode-se usar um bando de dados ( à exemplo do SX3 ) para armazenar uma seqüência de funções a serem chamadas, e depois usá-lo para gerar telas, efetuar validação de campos, iniciar o conteúdo de campos, etc.
Para passar uma função como parâmetro, passe o nome da função como uma string de caracteres ou como uma chamada de função dentro de um bloco de codificação. Para chamá-la, use o operador de macros & ou a função __ExecMacro() quando estiver passando uma string de caracteres ou Eval() para avaliar o bloco de código.
Exemplo:
cExecTeste := “U_TESTE()”
&( cExecTeste )
ou
cExecTeste := “U_TESTE()” //Executa a função U_TESTE()
__ExecMacro ( cExecTeste ) //Executa a função U_TESTE()
ou ainda
bExecTeste := { || U_TESTE() )
Eval( bExecTeste ) //Executa a função U_TESTE()
Protheus :: Programação Básica em Advpl “Combo XXIV”
Envio Por Referência ou por Valor:
Um aspecto importante do envio de parâmetros para as funções é se eles são passados por referência ou por valor. O método de envio de um argumento dependerá do método de chamada; ele não pode ser determinado no ponto em que a própria função é definida.
Quando os parâmetros são passados por referência, a função recebe ponteiros para os verdadeiros parâmetros, e quaisquer mudanças que ela introduzir neles serão imediatamente refletidas em seus valores:
User Function Test1()
Local nVar
nVar := 3
U_Test2( @nVar )
//Apos o retorno de U_Test2() nVar terá o conteúdo igual 1
Return( NIL )
User Function Test2( nVar1 )
nVar1 := 1
Return( NIL )
Para efetuarmos a passagem por referência devemos prefixar a variável com o operador de referência @.
Se passarmos um parâmetro por valor, o valor inicializará a variável local ou privada associada com o parâmetro formal, mais quaisquer mudanças introduzidas neste valor pela função desaparecerão. O verdadeiro parâmetro não é alterado pela operação da função.
User Function Test1()
Local nVar
nVar := 3
U_Test2( nVar )
//Apos o retorno de U_Test2() nVar terá o conteúdo igual 3
Return( NIL )
User Function Test2( nVar1 )
nVar1 := 1
Return( NIL )
A chamada a U_Test2( nVar ) determina o envio de parâmetros por valor, pois o operador de referência @ não foi utilizado.
Os campos de banco de dados são exceções. Eles sempre são passados por valor, independentemente do estilo de chamada; caso contrário, toda vez que o parâmetro formal fosse mudado, seria necessário atualizar o banco de dados. Mas isso só é valido se o campo do banco de dados estiver prefixado pelo alias. Exemplo:
dbSelectArea( “SRA” ) //Seleciona a área de trabalho SRA
U_Teste1( @SRA->RA_MAT ) //irá gerar erro durante a compilação
U_Teste1( @RA_MAT ) //irá gerar erro na execução
U_teste1( @_Field->RA_MAT ) //irá gerar erro na execução
SRA->( U_teste1( @RA_MAT ) ) //irá gerar erro na execução
Recomendação: Para que não ocorra erro de execução, sempre prefixe o campo com o alias correspondente para que seja possível identificar se um campo de tabela está sendo passado por referência.
Um aspecto importante do envio de parâmetros para as funções é se eles são passados por referência ou por valor. O método de envio de um argumento dependerá do método de chamada; ele não pode ser determinado no ponto em que a própria função é definida.
Quando os parâmetros são passados por referência, a função recebe ponteiros para os verdadeiros parâmetros, e quaisquer mudanças que ela introduzir neles serão imediatamente refletidas em seus valores:
User Function Test1()
Local nVar
nVar := 3
U_Test2( @nVar )
//Apos o retorno de U_Test2() nVar terá o conteúdo igual 1
Return( NIL )
User Function Test2( nVar1 )
nVar1 := 1
Return( NIL )
Para efetuarmos a passagem por referência devemos prefixar a variável com o operador de referência @.
Se passarmos um parâmetro por valor, o valor inicializará a variável local ou privada associada com o parâmetro formal, mais quaisquer mudanças introduzidas neste valor pela função desaparecerão. O verdadeiro parâmetro não é alterado pela operação da função.
User Function Test1()
Local nVar
nVar := 3
U_Test2( nVar )
//Apos o retorno de U_Test2() nVar terá o conteúdo igual 3
Return( NIL )
User Function Test2( nVar1 )
nVar1 := 1
Return( NIL )
A chamada a U_Test2( nVar ) determina o envio de parâmetros por valor, pois o operador de referência @ não foi utilizado.
Os campos de banco de dados são exceções. Eles sempre são passados por valor, independentemente do estilo de chamada; caso contrário, toda vez que o parâmetro formal fosse mudado, seria necessário atualizar o banco de dados. Mas isso só é valido se o campo do banco de dados estiver prefixado pelo alias. Exemplo:
dbSelectArea( “SRA” ) //Seleciona a área de trabalho SRA
U_Teste1( @SRA->RA_MAT ) //irá gerar erro durante a compilação
U_Teste1( @RA_MAT ) //irá gerar erro na execução
U_teste1( @_Field->RA_MAT ) //irá gerar erro na execução
SRA->( U_teste1( @RA_MAT ) ) //irá gerar erro na execução
Recomendação: Para que não ocorra erro de execução, sempre prefixe o campo com o alias correspondente para que seja possível identificar se um campo de tabela está sendo passado por referência.
Protheus :: Programação Básica em Advpl “Combo XXIII”
Funções/Parâmetros
Ao fazer uma chamada a uma função valores são fornecidos para os parâmetros. Esses valores podem assumir a forma de constantes, variáveis, campos, elementos de arrays ou expressões, e eles são citados como parâmetros reais ou argumentos de chamada.
Uma chamada de função pode, também, omitir argumentos para qualquer número de parâmetros, deixando um vazio onde o argumento deveria estar. Será necessário incluir as vírgulas delimitadoras na lista de argumentos, exceto para quaisquer argumentos omitidos no final da lista. Isto assegura não existir ambigüidade sobre quais parâmetros foram deixados em branco. Quaisquer argumentos omitidos de uma chamada de função serão tratados exatamente como se o valor NIL tivesse sido realmente passado. Não há como distinguir, dentro da definição de função, entre o argumento omitido e um valor NIL passado explicitamente. A chamada:
User Function Test( nS , nE , nI , nD )
Return
U_Test( nRow , , 2 )
À função U_Test(), conforme definida acima, resultará no corpo da função sendo executada como se os parâmetros formais tivessem sido inicializados assim:
nS := nRow
nE := NIL
nI := 2
nD := NIL
Ao fazer uma chamada a uma função valores são fornecidos para os parâmetros. Esses valores podem assumir a forma de constantes, variáveis, campos, elementos de arrays ou expressões, e eles são citados como parâmetros reais ou argumentos de chamada.
Uma chamada de função pode, também, omitir argumentos para qualquer número de parâmetros, deixando um vazio onde o argumento deveria estar. Será necessário incluir as vírgulas delimitadoras na lista de argumentos, exceto para quaisquer argumentos omitidos no final da lista. Isto assegura não existir ambigüidade sobre quais parâmetros foram deixados em branco. Quaisquer argumentos omitidos de uma chamada de função serão tratados exatamente como se o valor NIL tivesse sido realmente passado. Não há como distinguir, dentro da definição de função, entre o argumento omitido e um valor NIL passado explicitamente. A chamada:
User Function Test( nS , nE , nI , nD )
Return
U_Test( nRow , , 2 )
À função U_Test(), conforme definida acima, resultará no corpo da função sendo executada como se os parâmetros formais tivessem sido inicializados assim:
nS := nRow
nE := NIL
nI := 2
nD := NIL
sexta-feira, 16 de janeiro de 2009
Protheus :: "Windows® PowerShell® Script Stop and Start Services"
Como é do conhecimento de todos, os
serviços do Protheus tem um "pequeno" problema no que diz respeito ao
gerenciamento de memória (quando executado sobre a plataforma Windows
Server 32). Sendo assim, de vez em quando faz-se necessário "reiniciar"
esses serviços de forma a liberar memória e evitar efeitos colaterais.
Em função desse "pequeno problema", disponibilizo um "script" em Windows® PowerShell®
para "reiniciar" os serviços do Protheus. Para adaptá-lo às suas
necessidades altere: $IniPort; $EndPort; o nome do serviço a ser
"parado/reiniciado" e o intervalo de definição das "portas" (presumindo
que existam vários serviços do Protheus "escutando" em um mesmo
"server").
#Script Name P10Restart.ps1
$IniPort = 5110
$EndPort = 5911
for ( $Port = $IniPort ; $Port -le $EndPort ; $Port+=100 )
{
if ( get-service | where { $_.status -eq "running" -and $_.name -eq "P10Server"+$Port} )
{
Stop-Service -force P10Server$Port
Start-Service P10Server$Port
}
elseif ( get-service | where { $_.name -eq "P10server"+$Port} )
{
Start-Service P10Server$Port
}
}
Crie um agendamento (no meu caso, aos domingos) para que esse "script" seja executado.
Obs.:
Obs.:
- Para que o "script" possa ser executado no "Task Manager" faz-se necessário informar o caminho completo do programa que irá executá-lo e o "script" a ser executado. Ex.: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe c:\totvs\p10\scripts\P10Restart.ps1; e
- Para que o "script" possa ser executado faz-se necessário autoriza-lo através do Windows® PowerShell® , sendo assim, execute o Windows® PowerShell® e digite o seguinte comando: get-help about_signing. Esse comando irá listar as opções de "Diretivas de Assinatura e de Execução" de "Script", leia-as atentamente. Mas se quiser pular esse passo (eu não recomendo)digite o seguinte comando no Windows® PowerShell® : Set-ExecutionPolicy Unrestricted. Esse comando fará que qualquer "Script" seja executado em sua máquina (mas atente para os ítens de segurança listados à partir do comando get-help about_signing).
quinta-feira, 15 de janeiro de 2009
Protheus :: Programação Básica em Advpl “Combo XXII”
Funções:
As funções, em Advpl, podem ser entendidas como um mecanismo de sub-rotinas. Elas sempre retornam um valor. Uma função pode retornar qualquer tipo de dados ( array, numérico, string, objeto, lógico... ) e, se nenhum tipo de retorno for definido a função retornará NIL. Elas retornam seu valor como uma expressão após a instrução RETURN ( Obs.: a rotina chamadora da função não precisa, necessariamente, usar o valor de retorno ).
O uso adequado das funções possibilita a grande reutilização da codificação.
As funções são definidas uma só vez dentro de um programa, mas podem ser chamadas várias vezes. Elas podem ser definidas em qualquer ordem e você pode chamar uma antes de defini-la. Todas as funções são armazendas no Repositório de Objetos. E Podem ser declaradas como:
Públicas: Podem ser chamadas em qualquer programa:
Funções Reservadas ( Microsiga )
Function -> Reservado para desenvolvimento no ERP na Microsiga
Template Function -> Para a criação de Templates
Project Function -> Para a criação de Projetos
Funções Comuns ( Microsiga / Clientes usuários )
User Function -> Para criação de funções de usuário
Web Function -> Para criação de funções para uso na Web
Html Function -> Para criação de funções para uso na Web
Estáticas: Podem ser chamadas apenas no programa onde foram declaradas ( Static )
Static Function
Obs.: Consulte o Header ( arquivo de cabeçalho ) Protheus.ch ( \mp8\include\ para verificar os detalhes sobre o padrão de conversão das delcarações acima nas chamadas de função.
Parâmetros:
No Advpl, a forma de passagem de parâmetros para as funções é colocando os nomes dos parâmetros entre parênteses e delimitando-os por vírgula. Este método é conhecido como lista de parâmetros formais. Como em:
Function Teste( a , b , c )
User Function Teste( a , b , c )
Onde a, b, c são os parâmetros formais da função. O escopo destes parâmetros é Local ( tem visibilidade e duração enquanto durar a execução da função ).
As funções, em Advpl, podem ser entendidas como um mecanismo de sub-rotinas. Elas sempre retornam um valor. Uma função pode retornar qualquer tipo de dados ( array, numérico, string, objeto, lógico... ) e, se nenhum tipo de retorno for definido a função retornará NIL. Elas retornam seu valor como uma expressão após a instrução RETURN ( Obs.: a rotina chamadora da função não precisa, necessariamente, usar o valor de retorno ).
O uso adequado das funções possibilita a grande reutilização da codificação.
As funções são definidas uma só vez dentro de um programa, mas podem ser chamadas várias vezes. Elas podem ser definidas em qualquer ordem e você pode chamar uma antes de defini-la. Todas as funções são armazendas no Repositório de Objetos. E Podem ser declaradas como:
Públicas: Podem ser chamadas em qualquer programa:
Funções Reservadas ( Microsiga )
Function -> Reservado para desenvolvimento no ERP na Microsiga
Template Function -> Para a criação de Templates
Project Function -> Para a criação de Projetos
Funções Comuns ( Microsiga / Clientes usuários )
User Function -> Para criação de funções de usuário
Web Function -> Para criação de funções para uso na Web
Html Function -> Para criação de funções para uso na Web
Estáticas: Podem ser chamadas apenas no programa onde foram declaradas ( Static )
Static Function
Obs.: Consulte o Header ( arquivo de cabeçalho ) Protheus.ch ( \mp8\include\ para verificar os detalhes sobre o padrão de conversão das delcarações acima nas chamadas de função.
Parâmetros:
No Advpl, a forma de passagem de parâmetros para as funções é colocando os nomes dos parâmetros entre parênteses e delimitando-os por vírgula. Este método é conhecido como lista de parâmetros formais. Como em:
Function Teste( a , b , c )
User Function Teste( a , b , c )
Onde a, b, c são os parâmetros formais da função. O escopo destes parâmetros é Local ( tem visibilidade e duração enquanto durar a execução da função ).
Protheus :: Programação Básica em Advpl “Combo XXI”
Compilação Condicional:
A compilação condicional nos permite incluir ou excluir codificação baseada numa condição. O propósito usual é manter diferentes versões de um programa, sem precisar separar cópias da codificação de origem. Por exemplo, poderíamos ter uma verão utilizando o TopConNect e outra para CodBase,
As diretivas do pré-processador para a compilação condicional se parecem com as instruções IF/ELSE/ENDIF. O pré-processador inclui ou exclui codificação do arquivo de saída, baseado no resultado do teste. O teste, conseqüentemente, atua como um filtro. O aspecto importante a observar é que o pré-processador faz o teste, não o programa. Dessa forma, a codificação não-selecionada nem sequer existe no arquivo de saída.
As diretivas verificam se um símbolo está definido ou se ele tem um valor específico ( podemos definir um símbolo com a diretiva #DEFINE ). As diretivas do pré-processador são:
#IFDEF - Verifica se um símbolo está definido
#IFNDEF - Verifica se um símbolo não está definido
#ELSE - Inclui algo se #IFDEF ou uma #IFNDEF falhar
#ENDIF - Indica o final da codificação controlada por um #IFDEF, #IFNDEF ou #ELSE
Podemos definir um símbolo sem atribuir um valor a ele. Por Exemplo, podemos escrever:
#DEFINE TOP
E testá-la, em seguida, com:
#IFDEF TOP
Neste caso, estamos apenas verificando se ela está definida, não checamos o seu valor;
Observemos agora, como o pré-processador manipula um exemplo simples:
#DEFINE TOP
#IFDEF TOP
Local cquery := “SELECT * FROM...”
#ELSE
( cAlias )->( dbSeek( ... ) )
#ENDIF
A compilação condicional nos permite incluir ou excluir codificação baseada numa condição. O propósito usual é manter diferentes versões de um programa, sem precisar separar cópias da codificação de origem. Por exemplo, poderíamos ter uma verão utilizando o TopConNect e outra para CodBase,
As diretivas do pré-processador para a compilação condicional se parecem com as instruções IF/ELSE/ENDIF. O pré-processador inclui ou exclui codificação do arquivo de saída, baseado no resultado do teste. O teste, conseqüentemente, atua como um filtro. O aspecto importante a observar é que o pré-processador faz o teste, não o programa. Dessa forma, a codificação não-selecionada nem sequer existe no arquivo de saída.
As diretivas verificam se um símbolo está definido ou se ele tem um valor específico ( podemos definir um símbolo com a diretiva #DEFINE ). As diretivas do pré-processador são:
#IFDEF - Verifica se um símbolo está definido
#IFNDEF - Verifica se um símbolo não está definido
#ELSE - Inclui algo se #IFDEF ou uma #IFNDEF falhar
#ENDIF - Indica o final da codificação controlada por um #IFDEF, #IFNDEF ou #ELSE
Podemos definir um símbolo sem atribuir um valor a ele. Por Exemplo, podemos escrever:
#DEFINE TOP
E testá-la, em seguida, com:
#IFDEF TOP
Neste caso, estamos apenas verificando se ela está definida, não checamos o seu valor;
Observemos agora, como o pré-processador manipula um exemplo simples:
#DEFINE TOP
#IFDEF TOP
Local cquery := “SELECT * FROM...”
#ELSE
( cAlias )->( dbSeek( ... ) )
#ENDIF
Protheus :: Programação Básica em Advpl “Combo XX”
Macros do compilador:
Podemos visualizar uma macro do compilador como uma função diretamente na codificação de origem do programa onde poderíamos parametrizar uma seqüência de comandos, exatamente como uma função. A diferença é que as macros do pré-processador são substituídas diretamente no arquivo de origem ( elas não são chamadas do mesmo modo que uma função ) . Por exemplo, poderíamos escrever uma macro de pesquisa de mínimo como:
#DEFINE MIN( a , b ) IIF( a < b , a , b )
Quando, subseqüentemente, chamar-mos a MIN com:
nMin := MIN( nY , nZ )
O pré-processador a substituirá por sua definição como em:
nMin := IIF( nY < nZ , nY , nZ )
Defina a macro usando parâmetros formais, exatamente como uma função. Quando o pré-processador a expandir, ele os substituirá pelos verdadeiros parâmetros (argumentos). Isto é basicamente o que acontece quando você chama uma rotina ou função. A diferença é que a substituição dos argumentos ocorre no tempo do pré-processador com as macros do compilador, não durante a execução.
Podemos visualizar uma macro do compilador como uma função diretamente na codificação de origem do programa onde poderíamos parametrizar uma seqüência de comandos, exatamente como uma função. A diferença é que as macros do pré-processador são substituídas diretamente no arquivo de origem ( elas não são chamadas do mesmo modo que uma função ) . Por exemplo, poderíamos escrever uma macro de pesquisa de mínimo como:
#DEFINE MIN( a , b ) IIF( a < b , a , b )
Quando, subseqüentemente, chamar-mos a MIN com:
nMin := MIN( nY , nZ )
O pré-processador a substituirá por sua definição como em:
nMin := IIF( nY < nZ , nY , nZ )
Defina a macro usando parâmetros formais, exatamente como uma função. Quando o pré-processador a expandir, ele os substituirá pelos verdadeiros parâmetros (argumentos). Isto é basicamente o que acontece quando você chama uma rotina ou função. A diferença é que a substituição dos argumentos ocorre no tempo do pré-processador com as macros do compilador, não durante a execução.
Protheus :: Programação Básica em Advpl “Combo XIX”
Arqivos Include:
As constantes simbólicas só são conhecidas no arquivo de origem que as define. O pré-processador só conhece seus valores depois da tradução. Portanto se definirmos uma constante um programa ela só valerá durante a compilação deste programa. Para que uma constante possa valer para mais de um programa, devemos usar um recurso de inclusão de arquivos através da diretiva #include. Ou seja, as constantes seriam incluídas em um arquivo à parte e todo programa que fosse utilizar essas constantes teria a seguinte linha #include “”.
As constantes simbólicas só são conhecidas no arquivo de origem que as define. O pré-processador só conhece seus valores depois da tradução. Portanto se definirmos uma constante um programa ela só valerá durante a compilação deste programa. Para que uma constante possa valer para mais de um programa, devemos usar um recurso de inclusão de arquivos através da diretiva #include. Ou seja, as constantes seriam incluídas em um arquivo à parte e todo programa que fosse utilizar essas constantes teria a seguinte linha #include “”.
Protheus :: Programação Básica em Advpl “Combo XVIII”
Pré-Processador:
O pré-processador é um importante recurso no Advpl. Imagine-o como um programa que executa antes do compilador. Ele lê o programa como entrada e gera um arquivo pré-processado como saída. O arquivo de saída, então, serve como a entrada do compilador.
Podemos incluir comandos do pré-processador, chamadas de diretivas, no programa de origem. O pré-processador as manipulará; o compilador nunca as percebe, uma vez que ele só manipula o arquivo de saída.
O pré-processador do Advpl tem os seguintes recursos:
- constantes simbólicas ou manifestas
- arquivos include
- macros do compilador
- compilação condicional
- comandos definidos pelo usuário
Constantes Simbólicas:
No Advpl é possível a definição de constantes simbólicas para uso no processo de pré-compilação. Devemos abrir mão desse recurso para tornar os programas mas legíveis e fácil de entender. Por Exemplo, no fragmento de codificação:
IF ( nOpc == 1 )
Inclui()
ElseIF ( nOpc == 2 )
Altera()
ElseIF ( nOpc == 3 )
Exclui()
EndIF
Só conseguiremos identificar o que nOpc representa se acompanharmos todo o processo ou se o programa estiver documentado. Mas, se utilizássemos constantes, teríamos:
Obs.: Podemos definir constates com a diretiva do pré-processador #DEFINE.
#DEFINE INCLUI 1
#DEFINE ALTERA 2
#DEFINE EXCLUI 3
IF ( nOpc == INCLUI )
Inclui()
ElseIF ( nOpc == ALTERA )
Altera()
ElseIF ( nOpc == EXCLUI )
Exclui()
EndIF
Não precisaríamos avaliar todo o programa para saber a que se refere o valor de nOpc. E o código fica muito mais fácil de interpretar. Na realidade INCLUI, ALTERA e EXCLUI, serão substituídos no processo de pré-compilação por 1 , 2 e 3 ( respectivamente ).
Regra: A tradução de #DEFINE feita pelo pré-processador é sensível a letras maiúsculas e minúsculas.
Dica: Defina todas as constantes em letras maiúsculas com a diretiva #DEFINE
O pré-processador é um importante recurso no Advpl. Imagine-o como um programa que executa antes do compilador. Ele lê o programa como entrada e gera um arquivo pré-processado como saída. O arquivo de saída, então, serve como a entrada do compilador.
Podemos incluir comandos do pré-processador, chamadas de diretivas, no programa de origem. O pré-processador as manipulará; o compilador nunca as percebe, uma vez que ele só manipula o arquivo de saída.
O pré-processador do Advpl tem os seguintes recursos:
- constantes simbólicas ou manifestas
- arquivos include
- macros do compilador
- compilação condicional
- comandos definidos pelo usuário
Constantes Simbólicas:
No Advpl é possível a definição de constantes simbólicas para uso no processo de pré-compilação. Devemos abrir mão desse recurso para tornar os programas mas legíveis e fácil de entender. Por Exemplo, no fragmento de codificação:
IF ( nOpc == 1 )
Inclui()
ElseIF ( nOpc == 2 )
Altera()
ElseIF ( nOpc == 3 )
Exclui()
EndIF
Só conseguiremos identificar o que nOpc representa se acompanharmos todo o processo ou se o programa estiver documentado. Mas, se utilizássemos constantes, teríamos:
Obs.: Podemos definir constates com a diretiva do pré-processador #DEFINE.
#DEFINE INCLUI 1
#DEFINE ALTERA 2
#DEFINE EXCLUI 3
IF ( nOpc == INCLUI )
Inclui()
ElseIF ( nOpc == ALTERA )
Altera()
ElseIF ( nOpc == EXCLUI )
Exclui()
EndIF
Não precisaríamos avaliar todo o programa para saber a que se refere o valor de nOpc. E o código fica muito mais fácil de interpretar. Na realidade INCLUI, ALTERA e EXCLUI, serão substituídos no processo de pré-compilação por 1 , 2 e 3 ( respectivamente ).
Regra: A tradução de #DEFINE feita pelo pré-processador é sensível a letras maiúsculas e minúsculas.
Dica: Defina todas as constantes em letras maiúsculas com a diretiva #DEFINE
Protheus :: Programação Básica em Advpl “Combo XVII”
Loops:
O Advpl oferece os loops FOR e WHILE. A instrução FOR executa instruções até o NEXT correspondente durante um determinado número de vezes. Devemos informar a quantidade de vezes a iterar, como em
Local nLoop
Local nLoops := 10
For nLoop := 1 To nLoops
...
...
Next nLoop
A inicialização da variável para controle do Loop deverá é feita na declaração For nLoop :=, o NEXT incrementa-a automaticamente e, se ele não tiver ultrapassado seu limite, o controle retornará para a primeira instrução dentro do loop. O valor do incremente é igual a 1, a menos que especificado de outra maneira com a opção STEP.
Local nLoop
Local nLoops := 100
For nLoop := 1 To nLoops Step 10
…
Next nLoop
ou
Local nLoop
Local nLoops := 0
For nLoop := 100 To nLoops Step –10
…
Next nLoop
Podemos usar os comandos EXIT e LOOP dentro do corpo do loop FOR. Exit encerra o Loop, transferindo o controle para a instrução depois do NEXT; o LOOP dá um salto para o início, reavaliando a condição.
Obs.: Use os dois comandos com cuidado; eles podem dificultar o entendimento e a manutenção dos loops.
O Loop for sempre reavalia a expressão limite a cada passagem. Por exemplo, em:
Local nLoop
For nLoop := 1 To RecCount()
...
Next nLoop
RecCount() será sempre reavaliada a cada passagem do Loop, o que é um desperdício. Atribua o valor retornado per RecCount() à uma variável para melhorar a performance, como em:
Local nLoop
Local nLoops := RecCount()
For nLoop := 1 To nLoops
…
Next nLoop
Já um Loop WHILE continua até que sua condição venha a ser falsa. A condição é avaliada primeiro, para que o loop não possa ser inserido de modo algum. A avaliação da condição será interrompida assim for conhecido o resultado.
Obs.: Apenas o necessário de uma condição é avaliado para se determinar o resultado.
Para executar o loop pelo menos uma vez, simulando as cláusulas REPEAT... UNTIL ou DO ... UNTIL de outras linguagens, poderemos escrever:
Local lContinue := .T.
While ( lContinue )
...
IF
lContinue := .F.
EndIF
End While
Estaremos, essencialmente, testando a condição ao final do loop.
Obs.: Não uso While .T. como uma condição de loop. É melhor colocar a condição de saída na instrução. Desta maneira, poderemos ver claramente o que o loop está tentando alcançar.
O Advpl oferece os loops FOR e WHILE. A instrução FOR executa instruções até o NEXT correspondente durante um determinado número de vezes. Devemos informar a quantidade de vezes a iterar, como em
Local nLoop
Local nLoops := 10
For nLoop := 1 To nLoops
...
...
Next nLoop
A inicialização da variável para controle do Loop deverá é feita na declaração For nLoop :=, o NEXT incrementa-a automaticamente e, se ele não tiver ultrapassado seu limite, o controle retornará para a primeira instrução dentro do loop. O valor do incremente é igual a 1, a menos que especificado de outra maneira com a opção STEP.
Local nLoop
Local nLoops := 100
For nLoop := 1 To nLoops Step 10
…
Next nLoop
ou
Local nLoop
Local nLoops := 0
For nLoop := 100 To nLoops Step –10
…
Next nLoop
Podemos usar os comandos EXIT e LOOP dentro do corpo do loop FOR. Exit encerra o Loop, transferindo o controle para a instrução depois do NEXT; o LOOP dá um salto para o início, reavaliando a condição.
Obs.: Use os dois comandos com cuidado; eles podem dificultar o entendimento e a manutenção dos loops.
O Loop for sempre reavalia a expressão limite a cada passagem. Por exemplo, em:
Local nLoop
For nLoop := 1 To RecCount()
...
Next nLoop
RecCount() será sempre reavaliada a cada passagem do Loop, o que é um desperdício. Atribua o valor retornado per RecCount() à uma variável para melhorar a performance, como em:
Local nLoop
Local nLoops := RecCount()
For nLoop := 1 To nLoops
…
Next nLoop
Já um Loop WHILE continua até que sua condição venha a ser falsa. A condição é avaliada primeiro, para que o loop não possa ser inserido de modo algum. A avaliação da condição será interrompida assim for conhecido o resultado.
Obs.: Apenas o necessário de uma condição é avaliado para se determinar o resultado.
Para executar o loop pelo menos uma vez, simulando as cláusulas REPEAT... UNTIL ou DO ... UNTIL de outras linguagens, poderemos escrever:
Local lContinue := .T.
While ( lContinue )
...
IF
lContinue := .F.
EndIF
End While
Estaremos, essencialmente, testando a condição ao final do loop.
Obs.: Não uso While .T. como uma condição de loop. É melhor colocar a condição de saída na instrução. Desta maneira, poderemos ver claramente o que o loop está tentando alcançar.
Protheus :: Programação Básica em Advpl “Combo XVI”
Estruturas de controle:
Como acontece com a maioria das linguagens, o Advpl fornece instruções para suportar desvios e loops. As instruções de desvio permitem que seus programas selecionem entre alternativas; as instruções de loops permitem executar um grupo de instruções repetidas vezes.
Desvio:
No Advpl existem dois métodos de desvio: o bloco de instruções IF/ELSE/ELSEIF/ENDIF ou o bloco DO CASE/ENDCASE. ELSEIF é apenas uma abreviação que evita a necessidade de se escrever outro IF/ENDIF. Os dois exemplos que se seguem são equivalentes:
IF ( nX < 10 )
...
Else
IF ( nX > 20 )
...
EndIF
EndIF
ou
IF( nX < 10 )
...
ElseIF ( nX > 20 )
...
EndIF
Observe que, como o Advpl sempre insiste num ENDIF encerrando todo o bloco de IF, sempre saberemos a quem pertence um Else.
Obs.: Ao testar várias condições, é preferível uma instrução CASE a uma longa seqüência de IF/ELSEIFs. Na implementação das instruções CASE, o Advpl avalia uma condição de cada vez, até encontrar uma verdadeira. Em seguida ele avalia todas as instruções até o próximo CASE, ENDCASE ou OTHERWISE e, finalmente, salta para a instrução seguinte ao ENDCASE.
A cláusula OTHERWISE serve como última condição; ela localiza toda situação não correspondida por um CASE anterior.
BEGIN SEQUENCE/END
O par BEGIN SEQUENCE/END oferece outro método de desvio. Use-o para delimitar um bloco de codificação; em seguida, a partir de qualquer local dentro dele, você poderá emitir uma instrução BREAK. Ele passará o controle para a instrução seguinte ao END.
Exemplo:
BEGIN SEQUENCE
IF ...
IF ...
IF ...
Break
EndIF
EndIF
EndIF
...
END SEQUENCE
BEGIN SEQUENCE pode ser aninhado; um BREAK passará o controle para o END mais próximo
BEGIN SEQUENCE
IF !(... )
BREAK
ENDIF
BEGIN SEQUENCE
IF ...
BREAK
ELSEIF ...
BREAK
ENDIF
IF ...
BREAK
ELSEIF ...
BREAK
ENDIF
END SEQUENCE
...
END SEQUENCE
...
O uso básico de BEGIN SEQUENCE é manipular exceções. Ele fornece um local adequado para saltar quando ocorre um erro. Podemos usá-lo como um ponto de interrupção para a lógica profundamente aninhada. Por exemplo, vamos supor que precisemos testar três condições, mas só possa testar uma após o término com êxito da anterior ( como uma seqüência de bloqueios de arquivos ). Usando o aninhamento tradicional escreveríamos:
IF
...
...
IF
...
...
IF
...
...
ENDIF
ENDIF
ENDIF
Usando BEGIN SEQUENCE, poderíamos escrever:
BEGIN SEQUENCE
IF !
BREAK
ENDIF
...
...
IF !
BREAK
ENDIF
…
…
IF !
BREAK
ENDIF
END SEQUENCE
Pode-se, também, colocar uma instrução RECOVER dentro de um par BEGIN SEQUENCE/END. Quando seu programa executar, subseqüentemente, uma instrução de interrupção, o controle será transferido para a instrução RECOVER em vez de para o END.
Obs.: Tenha cuidado usar BEGIN SEQUENCE e BREAK; poderemos criar uma codificação ilegível
com o uso excessivo desse par.
Como acontece com a maioria das linguagens, o Advpl fornece instruções para suportar desvios e loops. As instruções de desvio permitem que seus programas selecionem entre alternativas; as instruções de loops permitem executar um grupo de instruções repetidas vezes.
Desvio:
No Advpl existem dois métodos de desvio: o bloco de instruções IF/ELSE/ELSEIF/ENDIF ou o bloco DO CASE/ENDCASE. ELSEIF é apenas uma abreviação que evita a necessidade de se escrever outro IF/ENDIF. Os dois exemplos que se seguem são equivalentes:
IF ( nX < 10 )
...
Else
IF ( nX > 20 )
...
EndIF
EndIF
ou
IF( nX < 10 )
...
ElseIF ( nX > 20 )
...
EndIF
Observe que, como o Advpl sempre insiste num ENDIF encerrando todo o bloco de IF, sempre saberemos a quem pertence um Else.
Obs.: Ao testar várias condições, é preferível uma instrução CASE a uma longa seqüência de IF/ELSEIFs. Na implementação das instruções CASE, o Advpl avalia uma condição de cada vez, até encontrar uma verdadeira. Em seguida ele avalia todas as instruções até o próximo CASE, ENDCASE ou OTHERWISE e, finalmente, salta para a instrução seguinte ao ENDCASE.
A cláusula OTHERWISE serve como última condição; ela localiza toda situação não correspondida por um CASE anterior.
BEGIN SEQUENCE/END
O par BEGIN SEQUENCE/END oferece outro método de desvio. Use-o para delimitar um bloco de codificação; em seguida, a partir de qualquer local dentro dele, você poderá emitir uma instrução BREAK. Ele passará o controle para a instrução seguinte ao END.
Exemplo:
BEGIN SEQUENCE
IF ...
IF ...
IF ...
Break
EndIF
EndIF
EndIF
...
END SEQUENCE
BEGIN SEQUENCE pode ser aninhado; um BREAK passará o controle para o END mais próximo
BEGIN SEQUENCE
IF !(... )
BREAK
ENDIF
BEGIN SEQUENCE
IF ...
BREAK
ELSEIF ...
BREAK
ENDIF
IF ...
BREAK
ELSEIF ...
BREAK
ENDIF
END SEQUENCE
...
END SEQUENCE
...
O uso básico de BEGIN SEQUENCE é manipular exceções. Ele fornece um local adequado para saltar quando ocorre um erro. Podemos usá-lo como um ponto de interrupção para a lógica profundamente aninhada. Por exemplo, vamos supor que precisemos testar três condições, mas só possa testar uma após o término com êxito da anterior ( como uma seqüência de bloqueios de arquivos ). Usando o aninhamento tradicional escreveríamos:
IF
...
...
IF
...
...
IF
...
...
ENDIF
ENDIF
ENDIF
Usando BEGIN SEQUENCE, poderíamos escrever:
BEGIN SEQUENCE
IF !
BREAK
ENDIF
...
...
IF !
BREAK
ENDIF
…
…
IF !
BREAK
ENDIF
END SEQUENCE
Pode-se, também, colocar uma instrução RECOVER dentro de um par BEGIN SEQUENCE/END. Quando seu programa executar, subseqüentemente, uma instrução de interrupção, o controle será transferido para a instrução RECOVER em vez de para o END.
Obs.: Tenha cuidado usar BEGIN SEQUENCE e BREAK; poderemos criar uma codificação ilegível
com o uso excessivo desse par.
Protheus :: Programação Básica em Advpl “Combo XV”
O último operador que examinaremos é o
$ ( contido em ). Use-o para verificar se uma string está contida em
outra. Por exemplo, para constatar se a variável de memória de caractere
isolado cTipoPeca é R, D, ou b, podemos escrever:
IF cTipoPeca $ “RDB”
...
Para verificar se uma variável de memória de dois caracteres é “PR”, “SP”, ou “AM”, escreva:
IF cEstado $ “PR_SP_AM”
...
Obs.: Os separadores “_” são necessários para invalidar combinações, como “RS” e “PA” que são estados válidos.
Dica: Use o operador $ ao invés de várias condições separadas. Ex.:
Do Case
Case cEstado == “PR” ; Execute(“PR”)
Case cEstado == “SP” ; Execute(“SP”)
Case cEstado == “AM” ; Execute(“AM”)
EndCase
Poderia ser escrito como
IF ( cEstado $ “PR_SP_AM” )
Execute(“PR_SP_AM”)
EndIF
Expressões condicionais:
IIF() ou IF() é uma expressão condicional. São abreviações de uma simples seqüência de IF/ELSE/ENDIF. Por exemplo, para encontrar o mínimo de a e b, poderíamos escrever:
nMinimo := IIF( a < b , a , b )
ou
nMinimo := IF( a < b , a , b )
que seria equivalente a escrever:
IF ( a < b )
nMinimo := a
Else
nMinimo := b
EndIF
As vantagens de IIF() e/ou IF() é que se acomodam em qualquer lugar em que uma expressão possa estar ( incluindo blocos de codificação ) e gera menos codificação.
IF cTipoPeca $ “RDB”
...
Para verificar se uma variável de memória de dois caracteres é “PR”, “SP”, ou “AM”, escreva:
IF cEstado $ “PR_SP_AM”
...
Obs.: Os separadores “_” são necessários para invalidar combinações, como “RS” e “PA” que são estados válidos.
Dica: Use o operador $ ao invés de várias condições separadas. Ex.:
Do Case
Case cEstado == “PR” ; Execute(“PR”)
Case cEstado == “SP” ; Execute(“SP”)
Case cEstado == “AM” ; Execute(“AM”)
EndCase
Poderia ser escrito como
IF ( cEstado $ “PR_SP_AM” )
Execute(“PR_SP_AM”)
EndIF
Expressões condicionais:
IIF() ou IF() é uma expressão condicional. São abreviações de uma simples seqüência de IF/ELSE/ENDIF. Por exemplo, para encontrar o mínimo de a e b, poderíamos escrever:
nMinimo := IIF( a < b , a , b )
ou
nMinimo := IF( a < b , a , b )
que seria equivalente a escrever:
IF ( a < b )
nMinimo := a
Else
nMinimo := b
EndIF
As vantagens de IIF() e/ou IF() é que se acomodam em qualquer lugar em que uma expressão possa estar ( incluindo blocos de codificação ) e gera menos codificação.
Protheus :: Programação Básica em Advpl “Combo XIV”
Conectivos Lógicos:
É possível negar uma expressão lógica com .NOT. ou com o ponto de exclamação, que é equivalente. No Advpl as expressões lógicas são avaliadas da esquerda para a direita ( parando assim que o resultado é conhecido ). Esse processo é chamado “avaliação de curto-cirquito”. Por exemplo:
#DEFINE NLIMITE 10
Local aArr[NLIMITE ]
Local nIndice := 1
Local nMax := 50
...
...
While ( ( nIndice <= NLIMITE ) .and. ( aArr[ nIndice ] < nMax ) )
Process()
nIndice := nIndice + 1 //ou ++nIndice
End While
Quando o valor de nIndice for 11, a primeira expressão provocará a interrupção da avaliação. ( no exemplo, a segunda expressão não será avaliada, caso contrário ocorreria o erro conhecido como “erro de subscrito” – O erro ocorreria porque “aArr” for definido com 10 elementos ( NLIMITE 10 ); se a segunda comparação fosse efetuada, teríamos aArr[11 ] para um array de 10 elementos apenas.
O Advpl avalia os operandos de operadores binários, tanto aritméticos como relacionais, da esquerda para a direita. Na maioria dos casos a ordem não é importante, mas:
IF A() < B()
Onde A() e B() são funções? O problema é que A() e B() poderiam provocar efeitos colateriais. Se elas dependerem de uma variável comum, a qual pode mudar ambas, a ordem de avaliação terá importância.
IF aArr1[ ++nI ] < aArr2[ nI++ ]
…
…
Se ambos os lados tiverem efeitos colaterais, seria necessário considerar a ordem de avaliação.
Os operadores, em Advpl, são sobrecarregados. Isso significa que eles se aplicam a operandos de diversos tipos. Por exemplo, pode-se utilizar o operador + para somar números, concatenar string e efetuar a aritmética de datas. De modo semelhante, podemos usar o operador <= para comparar datas, números e strings.
O Advpl suporta dois operadores de atribuições, = e := ( “torna-se igual a”). A diferença entre os dois é que := retorna um valor, enquanto = é sobrecarregado. Quando usado dentro de uma expressão, = efetua uma comparação; caso contrário, ele é uma atribuição. O operador := sempre efetua uma atribuição e ele retorna um valor no lado direito para que possamos usá-lo dentro de uma expressão, como em:
IF( nHandle := fOpen( “ERROS.TXT” ) ) != 0
…
…
A variável nHandle obtém o valor retornado pela abertura do arquivo ERROS.TXT. Este valor é, então, comparado com 0. Poderíamos obter o mesmo resultado com:
nHandle := fOpen( “ERROS.TXT )
IF ( nHandle != 0 )
...
Mas no modelo anterior uma instrução foi economizada. Na versão em linha inserimos a atribuição entre parênteses. Isso ocorreu porque o operador := tem menos precedência que o operador !=, portanto nHandle receberia, de outra maneira, um valor lógico, resultante da comparação do valor retornado por fOpen() com 0. Uma boa prática, então, é colocar entre parênteses as instruções de atribuição em linha, para evitar problemas de precedência.
O operador := associa da direita para a esquerda, portanto podemos escrecer:
cPNome := cSnome := cCep := cTel := Space( 10 )
que o efeito sera identico a escrever:
cPNome := ( cSnome := ( cCep := ( cTel := Space( 10 ) ) ) )
A vantagem do operador := é que ele pode aparecer dentro de expressões. Um bom uso deste operador é na determinação de causa de falha de uma complexa estrutura While. Por exemplo.
While TeclaValid() .and. NaFaixa()
...
End While
IF TeclaValid()
..
EndIF
Quando o loop terminar, precisamos verificar que condição provocou a saída dele; para tanto, precisamos chamar novamente uma função demorada. Podemos reescrever usando atribuições em-linha, assim:
While ( ( lTv := TeclaValid() ) .and. ( lNf := NaFixa() ) )
…
End While
IF ( lTv )
..
EndIF
Quando sairmos do Loop, não precisaremos chamar nenhuma das funções demoradas para determinar por que o loop terminou.
É possível negar uma expressão lógica com .NOT. ou com o ponto de exclamação, que é equivalente. No Advpl as expressões lógicas são avaliadas da esquerda para a direita ( parando assim que o resultado é conhecido ). Esse processo é chamado “avaliação de curto-cirquito”. Por exemplo:
#DEFINE NLIMITE 10
Local aArr[NLIMITE ]
Local nIndice := 1
Local nMax := 50
...
...
While ( ( nIndice <= NLIMITE ) .and. ( aArr[ nIndice ] < nMax ) )
Process()
nIndice := nIndice + 1 //ou ++nIndice
End While
Quando o valor de nIndice for 11, a primeira expressão provocará a interrupção da avaliação. ( no exemplo, a segunda expressão não será avaliada, caso contrário ocorreria o erro conhecido como “erro de subscrito” – O erro ocorreria porque “aArr” for definido com 10 elementos ( NLIMITE 10 ); se a segunda comparação fosse efetuada, teríamos aArr[11 ] para um array de 10 elementos apenas.
O Advpl avalia os operandos de operadores binários, tanto aritméticos como relacionais, da esquerda para a direita. Na maioria dos casos a ordem não é importante, mas:
IF A() < B()
Onde A() e B() são funções? O problema é que A() e B() poderiam provocar efeitos colateriais. Se elas dependerem de uma variável comum, a qual pode mudar ambas, a ordem de avaliação terá importância.
IF aArr1[ ++nI ] < aArr2[ nI++ ]
…
…
Se ambos os lados tiverem efeitos colaterais, seria necessário considerar a ordem de avaliação.
Os operadores, em Advpl, são sobrecarregados. Isso significa que eles se aplicam a operandos de diversos tipos. Por exemplo, pode-se utilizar o operador + para somar números, concatenar string e efetuar a aritmética de datas. De modo semelhante, podemos usar o operador <= para comparar datas, números e strings.
O Advpl suporta dois operadores de atribuições, = e := ( “torna-se igual a”). A diferença entre os dois é que := retorna um valor, enquanto = é sobrecarregado. Quando usado dentro de uma expressão, = efetua uma comparação; caso contrário, ele é uma atribuição. O operador := sempre efetua uma atribuição e ele retorna um valor no lado direito para que possamos usá-lo dentro de uma expressão, como em:
IF( nHandle := fOpen( “ERROS.TXT” ) ) != 0
…
…
A variável nHandle obtém o valor retornado pela abertura do arquivo ERROS.TXT. Este valor é, então, comparado com 0. Poderíamos obter o mesmo resultado com:
nHandle := fOpen( “ERROS.TXT )
IF ( nHandle != 0 )
...
Mas no modelo anterior uma instrução foi economizada. Na versão em linha inserimos a atribuição entre parênteses. Isso ocorreu porque o operador := tem menos precedência que o operador !=, portanto nHandle receberia, de outra maneira, um valor lógico, resultante da comparação do valor retornado por fOpen() com 0. Uma boa prática, então, é colocar entre parênteses as instruções de atribuição em linha, para evitar problemas de precedência.
O operador := associa da direita para a esquerda, portanto podemos escrecer:
cPNome := cSnome := cCep := cTel := Space( 10 )
que o efeito sera identico a escrever:
cPNome := ( cSnome := ( cCep := ( cTel := Space( 10 ) ) ) )
A vantagem do operador := é que ele pode aparecer dentro de expressões. Um bom uso deste operador é na determinação de causa de falha de uma complexa estrutura While. Por exemplo.
While TeclaValid() .and. NaFaixa()
...
End While
IF TeclaValid()
..
EndIF
Quando o loop terminar, precisamos verificar que condição provocou a saída dele; para tanto, precisamos chamar novamente uma função demorada. Podemos reescrever usando atribuições em-linha, assim:
While ( ( lTv := TeclaValid() ) .and. ( lNf := NaFixa() ) )
…
End While
IF ( lTv )
..
EndIF
Quando sairmos do Loop, não precisaremos chamar nenhuma das funções demoradas para determinar por que o loop terminou.
Protheus :: Programação Básica em Advpl “Combo XIII”
Operadores Relacionais:
No Advpl são permitidos os seguintes operadores relacionais:
Os operadores relacionais têm menos prioridade do que os aritméticos. Por exemplo:
nI < nLimite – 1
é avalidado como:
nI < ( nLimite – 1 )
e não como:
( nI < nLimite ) – 1
No Advpl são permitidos os seguintes operadores relacionais:
Os operadores relacionais têm menos prioridade do que os aritméticos. Por exemplo:
nI < nLimite – 1
é avalidado como:
nI < ( nLimite – 1 )
e não como:
( nI < nLimite ) – 1
Protheus :: Programação Básica em Advpl “Combo XII”
Operadores Unários:
O Advpl suporta um sinal negativo ( o chamado operador unário). Ele tem a maior prioridade entre os operadores aritméticos. Por Exemplo:
O Advpl suporta um sinal negativo ( o chamado operador unário). Ele tem a maior prioridade entre os operadores aritméticos. Por Exemplo:
- - 1 + 2
- resulta 1 e não -3.
O Advpl suporta os operadores de incrementos e decrementos unários
++ e --. O Operador ++ acrescenta 1 a seu operando, -- subtrai 1. Ambos
têm parceiros nas instruções regulares de atribuições. Exemplo:
nContador++ é idêntico a nContador := nContador + 1
Entretanto, a vantagem desses operadores unários é o fato de serem concisos e de poderem ser utilizados para deferir a operação até depois de processado o valor. Por Exemplo, para carregar um array de campo CPnome de cada registro de um banco de dados chamado Alunos, poderíamos escrever:
Local acPNomes
Local nContador := 1
Use Alunos
acPNomes := Array( RecCount() )
While ( Alunos )->( !Eof() )
acPNomes[ nContador++ ] := ( Alunos )->CPNOME
( Alunos )->( dbSkip() )
End While
O Subscrito do array usa o valor de nContador, depois acrescenta 1. Dizemos que o ++, neste caso, é um operador pós-determinado. Você pode usá-lo como um operador de prefixos, em cujo caso o valor será incrementado ANTES de ser usado. Por exemplo, a codificação anterior deixa o nContador com um valor superior em 1 unidade ao número de registros processados. Para deixá-lo com o valor real, poderíamos escrever:
Local acPNomes
Local nContador := 0
Use Alunos
acPNomes := Array( RecCount() )
While ( Alunos )->( !Eof() )
acPNomes[ ++nContador] := ( Alunos )->CPNOME
( Alunos )->( dbSkip() )
End While
No modo de prefixo, o computador aplica o incremento antes de usar o valor.
nContador++ é idêntico a nContador := nContador + 1
Entretanto, a vantagem desses operadores unários é o fato de serem concisos e de poderem ser utilizados para deferir a operação até depois de processado o valor. Por Exemplo, para carregar um array de campo CPnome de cada registro de um banco de dados chamado Alunos, poderíamos escrever:
Local acPNomes
Local nContador := 1
Use Alunos
acPNomes := Array( RecCount() )
While ( Alunos )->( !Eof() )
acPNomes[ nContador++ ] := ( Alunos )->CPNOME
( Alunos )->( dbSkip() )
End While
O Subscrito do array usa o valor de nContador, depois acrescenta 1. Dizemos que o ++, neste caso, é um operador pós-determinado. Você pode usá-lo como um operador de prefixos, em cujo caso o valor será incrementado ANTES de ser usado. Por exemplo, a codificação anterior deixa o nContador com um valor superior em 1 unidade ao número de registros processados. Para deixá-lo com o valor real, poderíamos escrever:
Local acPNomes
Local nContador := 0
Use Alunos
acPNomes := Array( RecCount() )
While ( Alunos )->( !Eof() )
acPNomes[ ++nContador] := ( Alunos )->CPNOME
( Alunos )->( dbSkip() )
End While
No modo de prefixo, o computador aplica o incremento antes de usar o valor.
Protheus :: Programação Básica em Advpl “Combo XI”
Operadores e Expressões:
O Advpl suporta os seguintes operadores binários:
O Advpl suporta os seguintes operadores binários:
Que
agrupados em ordem de precedência ( prioridade, a mais alta primeiro ).
Os operadores ** e ^ têm ambos a prioridade mais alta. Os operadores *,
/ e % tem níveis de prioridades iguais, assim como os operadores + e -.
Os operadores *, / e % tem uma precedência maior do que + e -. O Advpl
avalia os operadores, de igual precedência, da esquerda para a direita.
Protheus :: Programação Básica em Advpl “Combo X”
Variáveis Numéricas:
O Advpl, na leitura de uma variável numérica, atribui, automaticamente, dez posições antes da casa decimal, a despeito do seu tamanho atual. Portanto, esse tamanho é passível de ser aumentado. Já em relação ao número de posições depois do ponto decimal é determinado quando você o atribui a ela. Assim, se para montar um Get, tivermos o seguinte bloco de programa:
Local nValor := 15.1270
@ 10 , 10 Say “Digite um novo valor:” Get nValor
nValor terá 4 posições após o ponto decimal e duas posições antes do ponto decimal ( conforme valor atribuído à variável ); portanto o Get usa 7 colunas da tela: 2 para antes do ponto decimal, uma para o próprio ponto decimal e quatro depois dele. Ao inicializar variáveis numéricas para uso em um Get, certifique-se de atribuir a elas o número correto de casas decimais ou utilize picture como em:
Local nValor := 15.12
@ 10 , 10 Say “Digite um novo valor:” Get nValor Picture @R 999.99999
Apesar de nValor ter sido declarado como tendo apenas 2 posições após o ponto decimal e ele ainda possuir dez posições antes do ponto, ao delimitarmos a digitação da informação definindo a Picture teremos ( para digitação ): 3 posições antes do ponto decimal, 5 posições após o ponto decimal ( além do próprio ponto ) totalizando 9 o número de colunas no Get para a visualização e manutenção do valor.
Obs.: Ao contrário das variáveis do Tipo Array e Caractere, que utilizam a função Len() para retornar o seus tamanhos, em AdvPl não existe função ou operador que retorne o tamanho de uma variável do tipo numérica. Mas nada impede de que ela seja desenvolvida pelo próprio usuário do sistema.
O Advpl, na leitura de uma variável numérica, atribui, automaticamente, dez posições antes da casa decimal, a despeito do seu tamanho atual. Portanto, esse tamanho é passível de ser aumentado. Já em relação ao número de posições depois do ponto decimal é determinado quando você o atribui a ela. Assim, se para montar um Get, tivermos o seguinte bloco de programa:
Local nValor := 15.1270
@ 10 , 10 Say “Digite um novo valor:” Get nValor
nValor terá 4 posições após o ponto decimal e duas posições antes do ponto decimal ( conforme valor atribuído à variável ); portanto o Get usa 7 colunas da tela: 2 para antes do ponto decimal, uma para o próprio ponto decimal e quatro depois dele. Ao inicializar variáveis numéricas para uso em um Get, certifique-se de atribuir a elas o número correto de casas decimais ou utilize picture como em:
Local nValor := 15.12
@ 10 , 10 Say “Digite um novo valor:” Get nValor Picture @R 999.99999
Apesar de nValor ter sido declarado como tendo apenas 2 posições após o ponto decimal e ele ainda possuir dez posições antes do ponto, ao delimitarmos a digitação da informação definindo a Picture teremos ( para digitação ): 3 posições antes do ponto decimal, 5 posições após o ponto decimal ( além do próprio ponto ) totalizando 9 o número de colunas no Get para a visualização e manutenção do valor.
Obs.: Ao contrário das variáveis do Tipo Array e Caractere, que utilizam a função Len() para retornar o seus tamanhos, em AdvPl não existe função ou operador que retorne o tamanho de uma variável do tipo numérica. Mas nada impede de que ela seja desenvolvida pelo próprio usuário do sistema.
Protheus :: Programação Básica em Advpl “Combo IX”
Protheus :: Programação Básica em Advpl “Combo IX”
Tamanho:
Faz-se necessário observar o tamanho de variáveis de caracteres e numéricas uma vez que um problema difícil de analisar é a comparação de variáveis de tamanhos diferentes; os espaços de preenchimento podem ser significativos.:
Variáveis Caractere
Ex.:
Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local lEqual
lEqual := ( cUsuario == cUser )
No exemplo acima lEqual irá retornar .F. uma vez que o conteúdo armazenado na variável cUsuario ( apesar de parecer igual ) é diferente do conteúdo da variável cUser. O que diferencia o conteúdo entre uma variável e outra são os espaços à direita. Para resolver o problema na comparação teremos que abir mão de uma função que remova os espaços à direita ( RTrim( ) ).
Ex.:
Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local lEqual
lEqual := ( cUsuario == RTrim( cUser ) ) //Removendo espaços a //direita
ou
lEqual := ( cUsuario == AllTrim( cUser ) ) //Removendo espaços a //direita e à esquecerda
Nos dois exemplos acima lEqual irá ser igual à .T.
Se ao invés de usarmos o operador de comparação de igualdade == (exatamente igual ) utilizássemos o operador = ( que pode ser utilizado tanto para comparação de igualdade como para atribuição – neste caso depende do contexto ) – teríamos:
Ex.:
Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local lEqual
lEqual := ( cUsuario = cUser )
No Exemplo acima, lEqual será .T. pois não fizemos uma comparação exata.
Em AdvPl é possível obter o tamanho de uma variável do tipo caractere ou array utilizando a função Len( ).
Ex.:
Local aArray1 := { “1” , “2” , “3” }
Local aArray2 := Array( 10 )
Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local nArray1 := Len( aArray1 ) //3
Local nArray2 := Len( aArray2 ) //10
Local nUsuario := Len( cUsuario ) //13
Local nUser := Len( cUser ) //16
Tamanho:
Faz-se necessário observar o tamanho de variáveis de caracteres e numéricas uma vez que um problema difícil de analisar é a comparação de variáveis de tamanhos diferentes; os espaços de preenchimento podem ser significativos.:
Variáveis Caractere
Ex.:
Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local lEqual
lEqual := ( cUsuario == cUser )
No exemplo acima lEqual irá retornar .F. uma vez que o conteúdo armazenado na variável cUsuario ( apesar de parecer igual ) é diferente do conteúdo da variável cUser. O que diferencia o conteúdo entre uma variável e outra são os espaços à direita. Para resolver o problema na comparação teremos que abir mão de uma função que remova os espaços à direita ( RTrim( ) ).
Ex.:
Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local lEqual
lEqual := ( cUsuario == RTrim( cUser ) ) //Removendo espaços a //direita
ou
lEqual := ( cUsuario == AllTrim( cUser ) ) //Removendo espaços a //direita e à esquecerda
Nos dois exemplos acima lEqual irá ser igual à .T.
Se ao invés de usarmos o operador de comparação de igualdade == (exatamente igual ) utilizássemos o operador = ( que pode ser utilizado tanto para comparação de igualdade como para atribuição – neste caso depende do contexto ) – teríamos:
Ex.:
Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local lEqual
lEqual := ( cUsuario = cUser )
No Exemplo acima, lEqual será .T. pois não fizemos uma comparação exata.
Em AdvPl é possível obter o tamanho de uma variável do tipo caractere ou array utilizando a função Len( ).
Ex.:
Local aArray1 := { “1” , “2” , “3” }
Local aArray2 := Array( 10 )
Local cUsuario := “Administrador”
Local cUser := “Administrador “
Local nArray1 := Len( aArray1 ) //3
Local nArray2 := Len( aArray2 ) //10
Local nUsuario := Len( cUsuario ) //13
Local nUser := Len( cUser ) //16
quarta-feira, 14 de janeiro de 2009
Protheus :: Personalizando o RHOnLine
Neste artigo vou demonstrar como personalizar o seu RHOnLine, deixando-o com a cara de sua empresa e no padrão W3C de forma a funcionar nos principais "browsers" (IE, Firefox, Google Chrome, etc. O RHOnLine do padrão só funciona com o IE).
Neste exemplo não teremos todas as funcionalidades do RHOnLine padrão, apenas uma tela de abertura, o Demonsrtrativo de Pagamento e a opção para alteração de senha. Mas isso não impede que as demais funcionalidades sejam implementadas, bastando seguir o modelo aqui fornecido. Nesse modelo utilizamos uma ferramenta do site BarcodesInc® para gerar o código de barras para o Demonstrativo de Pagamento de forma a dar-lhe mais "credibilidade".
Tela de Apresentação
Tela para escolha do período para impressão do Demonstrativo
Tela de mensagem caso não existam informações de Demonstrativo
Tela do Demonstrativo
Tela de rodapé do Demonstrativo (com código de barra)
Tela para a Alteração de Senha
Para a implementação:
Neste exemplo não teremos todas as funcionalidades do RHOnLine padrão, apenas uma tela de abertura, o Demonsrtrativo de Pagamento e a opção para alteração de senha. Mas isso não impede que as demais funcionalidades sejam implementadas, bastando seguir o modelo aqui fornecido. Nesse modelo utilizamos uma ferramenta do site BarcodesInc® para gerar o código de barras para o Demonstrativo de Pagamento de forma a dar-lhe mais "credibilidade".
A personalização do RHOnLine terá as seguintes características:
Tela de Abertura
Tela de Apresentação
Tela para escolha do período para impressão do Demonstrativo
Tela de mensagem caso não existam informações de Demonstrativo
Tela do Demonstrativo
Tela de rodapé do Demonstrativo (com código de barra)
Tela para a Alteração de Senha
Para a implementação:
- Descompacte os arquivos disponibilizados para download;
- Abra o projeto gpe_tcf.prj através da IDE do protheus e compile-o;
- Copie a pasta tcf para o diretório onde está configurado o módulo web tcf;
- Altere as imagens conforme necessidade de sua empresa;
- Altere o DEFAULTPAGE de sua configuração web TCF de forma que fique com DEFAULTPAGE=u_w_tcfinit.apw;
- Altere as configurações do Menu do RHOnLine no SIGATCF. Programa: TCFA120 - Menu:SIGATCF.XNU, Opção do Menu: &Atualizações->Configuracoes->Menu Rh-online. Desabilite as opções padrões e inclua as seguintes chamadas: Para o Demonstrativo Inclua a rotina: W_TCFDEM; Em Alterar senha, inclua a rotina W_TCFPWD; e na opção "Informativo" inclua a rotina W_TCFINF. Se desejar obter o arquivo ISAM com as opções do menu para depois efetuar um "Append" na tabela RBC, clique aqui;
- Execute. Obs.: Se estiver utilizando o IE8, execute em modo de compatibilidade ("Compatibility View").
Links disponibilizados:
segunda-feira, 12 de janeiro de 2009
Protheus :: Programação Básica em Advpl “Combo VIII”
Conversão de variáveis:
No Advpl é possível converter os tipos usando uma função ou expressão condicional.
Tabela de Conversão de Tipos (Clique na Imagem para ampliá-la)
No Advpl é possível converter os tipos usando uma função ou expressão condicional.
Tabela de Conversão de Tipos (Clique na Imagem para ampliá-la)
Protheus :: Programação Básica em Advpl “Combo VII”
Tipo:
O Advpl suporta os seguintes Tipos:
Array
Bloco
Caractere ( Caractere isolado ou string até 1Mb )
Data
Lógico
Memo
Numérico
Objeto
Diferentemente das linguagens como C, C++, Java, etc..., as variáveis em Advpl não são prototipadas. Seus tipos são definidos no momento após a atribuição de valores. Para testar o tipo de uma variável pode-se utilizar a função Type() ou ValType(). Type() para variáveis públicas e privadas declaradas em outras instâncias do programa e ValType() para variáveis que pertencem à mesma instància.
Ex.:
Function Test1()
Local aTest
Private aTest1 := {}
Test2( aTest )
Return( NIL )
Function Test2( aTest )
IF ( Type( “aTest1” ) == “A” )//A variável deve estar entre aspas
IF ( ValType( aTest ) == “A” )
aTest := aTest1
Else
Alert( “A variavel aTest nao eh um Array” )
EndIF
EndIF
Return( NIL )
Antes de Atribuir um valor a uma variável, seu tipo é indefinido (U). Se a variável for declarada como Private, Local ou Static, seu valor, na declaração, será NIL. As variáveis declaradas como Public recebem automaticamente a atribuição de um valor lógico .F.. As variáveis só obterão o seu valor real na atribuição.
O Advpl suporta os seguintes Tipos:
Array
Bloco
Caractere ( Caractere isolado ou string até 1Mb )
Data
Lógico
Memo
Numérico
Objeto
Diferentemente das linguagens como C, C++, Java, etc..., as variáveis em Advpl não são prototipadas. Seus tipos são definidos no momento após a atribuição de valores. Para testar o tipo de uma variável pode-se utilizar a função Type() ou ValType(). Type() para variáveis públicas e privadas declaradas em outras instâncias do programa e ValType() para variáveis que pertencem à mesma instància.
Ex.:
Function Test1()
Local aTest
Private aTest1 := {}
Test2( aTest )
Return( NIL )
Function Test2( aTest )
IF ( Type( “aTest1” ) == “A” )//A variável deve estar entre aspas
IF ( ValType( aTest ) == “A” )
aTest := aTest1
Else
Alert( “A variavel aTest nao eh um Array” )
EndIF
EndIF
Return( NIL )
Antes de Atribuir um valor a uma variável, seu tipo é indefinido (U). Se a variável for declarada como Private, Local ou Static, seu valor, na declaração, será NIL. As variáveis declaradas como Public recebem automaticamente a atribuição de um valor lógico .F.. As variáveis só obterão o seu valor real na atribuição.
Protheus :: Programação Básica em Advpl “Combo VI”
Variáveis:
No Advpl, as regras para a nomeação de identificadores são parecidas com as das linguagens típicas de computadores. A nomeação poderá ser iniciada com uma letra ou um caractere de sublinhado e poderão conter letras, sublinhados e dígitos. Para manter compatibilidade com o “legado” foi mantida a limitação de dez primeiros caracteres como significativos. Por isso, deve-se ter muito cuidado com nomes extensos. Se for utilizar nomes extensos lembre-se que atualmente apenas os dez primeiros caracteres serão considerados mas, se declarar uma variável com nome extenso, utilize-a da forma como foi declarada para que, se um dia a limitação de dez caracteres foi modificada, não seja necessário estar modificando os programas.
A restrição em dez caracteres pode ser fonte de defeitos ardilosos. Por exemplo, as seguintes variáveis:
cSNome_do_Pai
cSNome_do_Avo
Serão consideradas idênticas. A truncagem dos identificadores longos é conseqüência do tamanho fixo dos nomes nas tabelas mantidas pelo compilador e pelo pré-processador, portanto os nomes completos dos identificadores são passados sem alteração para o pré-processador. Tenha cuidado, esse tipo de erro é muito difícil de se depurar.
O estilo de codificação recomendada é usar letras maúsculas para destacar partes significativas de um identificador ao inves de usar sublinhados para serpará-las. Dessa forma será possível criar identificadores menores, sem afetar a legibilidade. Como em:
cSNomeDoPai
cSNomeDoAvo
Apesar dos identificadores acima terem mais de dez caracteres, os 10 mais significativos não são conflitantes. O primeiro será considerado como cSNomeDoPa e o segundo como cSNomeDoAv
Lembre-se: Somente os dez primeiros caracteres do nome de uma variável são significativos.
Uma variável tem os seguintes atributos:
Tipo
Tamanho
Duração
Visibilidade
No Advpl, as regras para a nomeação de identificadores são parecidas com as das linguagens típicas de computadores. A nomeação poderá ser iniciada com uma letra ou um caractere de sublinhado e poderão conter letras, sublinhados e dígitos. Para manter compatibilidade com o “legado” foi mantida a limitação de dez primeiros caracteres como significativos. Por isso, deve-se ter muito cuidado com nomes extensos. Se for utilizar nomes extensos lembre-se que atualmente apenas os dez primeiros caracteres serão considerados mas, se declarar uma variável com nome extenso, utilize-a da forma como foi declarada para que, se um dia a limitação de dez caracteres foi modificada, não seja necessário estar modificando os programas.
A restrição em dez caracteres pode ser fonte de defeitos ardilosos. Por exemplo, as seguintes variáveis:
cSNome_do_Pai
cSNome_do_Avo
Serão consideradas idênticas. A truncagem dos identificadores longos é conseqüência do tamanho fixo dos nomes nas tabelas mantidas pelo compilador e pelo pré-processador, portanto os nomes completos dos identificadores são passados sem alteração para o pré-processador. Tenha cuidado, esse tipo de erro é muito difícil de se depurar.
O estilo de codificação recomendada é usar letras maúsculas para destacar partes significativas de um identificador ao inves de usar sublinhados para serpará-las. Dessa forma será possível criar identificadores menores, sem afetar a legibilidade. Como em:
cSNomeDoPai
cSNomeDoAvo
Apesar dos identificadores acima terem mais de dez caracteres, os 10 mais significativos não são conflitantes. O primeiro será considerado como cSNomeDoPa e o segundo como cSNomeDoAv
Lembre-se: Somente os dez primeiros caracteres do nome de uma variável são significativos.
Uma variável tem os seguintes atributos:
Tipo
Tamanho
Duração
Visibilidade
Protheus :: Programação Básica em Advpl “Combo V”
Comandos vs Literais:
Alguns comandos em AdvPl exigem argumentos literais. Uma literal é uma seqüencia de caracteres interpretada literalmente. Não será possível usar uma constante ou expressão no lugar da literal.
Ex.:
Use cTable
No exemplo acima, cTable é uma literal. Ela é interpretado literalmente, não como uma variável. O pré-compilador irá transformar a Literal em uma string de caracter da seguinte forma:
dbUseArea( .F.,, "cTable",, if(.F. .OR. .F., !.F., NIL), .F. )
Para que cTable não seja considerado como uma literal mas sim como uma variável coloque-a entre parenteses como em:
Use ( cTable )
Que o pré-processador converterá em:
dbUseArea( .F.,, ( cTable ),, if(.F. .OR. .F., !.F., NIL), .F. )
Alguns comandos em AdvPl exigem argumentos literais. Uma literal é uma seqüencia de caracteres interpretada literalmente. Não será possível usar uma constante ou expressão no lugar da literal.
Ex.:
Use cTable
No exemplo acima, cTable é uma literal. Ela é interpretado literalmente, não como uma variável. O pré-compilador irá transformar a Literal em uma string de caracter da seguinte forma:
dbUseArea( .F.,, "cTable",, if(.F. .OR. .F., !.F., NIL), .F. )
Para que cTable não seja considerado como uma literal mas sim como uma variável coloque-a entre parenteses como em:
Use ( cTable )
Que o pré-processador converterá em:
dbUseArea( .F.,, ( cTable ),, if(.F. .OR. .F., !.F., NIL), .F. )
Protheus :: Programação Básica em Advpl “Combo IV”
Constantes:
Constantes são Valores predefinidos que não mudam durante o programa. Elas podem ser strings de caracteres, números, valores lógicos, blocos de codificação, arrays ( vetores ). Ex.:
“pressione uma tecla para continuar”
15
1970.15
.T.
{ || MyTest() }
{“Domingo”,“Segunda”,“Terça”,“Quarta”,“Quinta”,“Sexta”,“Sábado”}
No Advpl, as constantes numéricas são sempre decimais. Não são suportados outros sistemas de números nem notação científica.
Não há diferença entre caracteres isolados “a” e strings “Isso é uma string”. Uma string é simplesmente um ou mais caracteres. Para definir uma constante do tipo string maior que uma linha, continue-a com um ponto e vírgula e junte as partes com o operador “+”
Ex.: “esta string será continuada”+;
“na próxima linha”
No AdvPl não existem constantes de datas. Será necessário contruí-las à partir de uma string de caracteres e com a função CToD() como no exemplo:
Ex.: Ctod( “18/02/2005”)
O resultado será uma data, mas ela não será realmente uma constante. Pois o sistema na execução a avaliará. As constantes são sempre avaliadas no momento da compilação.
Constantes são Valores predefinidos que não mudam durante o programa. Elas podem ser strings de caracteres, números, valores lógicos, blocos de codificação, arrays ( vetores ). Ex.:
“pressione uma tecla para continuar”
15
1970.15
.T.
{ || MyTest() }
{“Domingo”,“Segunda”,“Terça”,“Quarta”,“Quinta”,“Sexta”,“Sábado”}
No Advpl, as constantes numéricas são sempre decimais. Não são suportados outros sistemas de números nem notação científica.
Não há diferença entre caracteres isolados “a” e strings “Isso é uma string”. Uma string é simplesmente um ou mais caracteres. Para definir uma constante do tipo string maior que uma linha, continue-a com um ponto e vírgula e junte as partes com o operador “+”
Ex.: “esta string será continuada”+;
“na próxima linha”
No AdvPl não existem constantes de datas. Será necessário contruí-las à partir de uma string de caracteres e com a função CToD() como no exemplo:
Ex.: Ctod( “18/02/2005”)
O resultado será uma data, mas ela não será realmente uma constante. Pois o sistema na execução a avaliará. As constantes são sempre avaliadas no momento da compilação.
Protheus :: Programação Básica em Advpl “Combo III”
Comentários:
O compilador Advpl ignora as linhas em branco e a indentação. Ambas tornam o programas mais legível e, devemos abrir mão deste recurso para tornar os programas mais legíveis. Os comentários também são ignorados pelo compilador.
Na realidade, as linhas em branco, a identação e os comentários nos programas são tratados pelo pré-processador.
No Advpl são permitidas as seguintes formas de se comentar um programa:
//Para efetuar o comentário em uma linha. ( Tudo o que estiver à direita //das ( // ) será considerado como um comentário.
Obs.: Pode-se utilizar o sinal de * ou && no inicio da frase para se efetuar o comentário de uma única linha. Deve-se escolher uma por padrão e ser coerente.
/*
Para efetuar o comentário em mais de uma linha. ( Tudo o que estiver entre ( /* e */ ) será considerado como comentário.
*/
Obs.: para facilitar a utilização do modelo de comentário acima utilize /*/ para abrir o comentário e /*/ para garantir que um comentário nunca deixará de ser fechado.
É altamente recomendável que as rotinas importantes e de utilidade sejam comentadas com um bloco de abertura. Deverão ser especificados: o propósito da rotina, os tipos de parâmetros e, se for uma função que retorna algo, o valor de retorno.
/*/
Função : SomaUm( nLast )
Autor : Marinaldo de Jesus
Data : 18/02/2005
Versão : 1
SomaUm( nLast ) -> Retorna valor de nLast incrementado de 1
nLast -> Valor onde deverá ser somado um
/*/
Obs.: A melhor documentação de um programa é o próprio programa ( desde que bem escrito e seguindo-se as regras que foram estabelecidas )
O compilador Advpl ignora as linhas em branco e a indentação. Ambas tornam o programas mais legível e, devemos abrir mão deste recurso para tornar os programas mais legíveis. Os comentários também são ignorados pelo compilador.
Na realidade, as linhas em branco, a identação e os comentários nos programas são tratados pelo pré-processador.
No Advpl são permitidas as seguintes formas de se comentar um programa:
//Para efetuar o comentário em uma linha. ( Tudo o que estiver à direita //das ( // ) será considerado como um comentário.
Obs.: Pode-se utilizar o sinal de * ou && no inicio da frase para se efetuar o comentário de uma única linha. Deve-se escolher uma por padrão e ser coerente.
/*
Para efetuar o comentário em mais de uma linha. ( Tudo o que estiver entre ( /* e */ ) será considerado como comentário.
*/
Obs.: para facilitar a utilização do modelo de comentário acima utilize /*/ para abrir o comentário e /*/ para garantir que um comentário nunca deixará de ser fechado.
É altamente recomendável que as rotinas importantes e de utilidade sejam comentadas com um bloco de abertura. Deverão ser especificados: o propósito da rotina, os tipos de parâmetros e, se for uma função que retorna algo, o valor de retorno.
/*/
Função : SomaUm( nLast )
Autor : Marinaldo de Jesus
Data : 18/02/2005
Versão : 1
SomaUm( nLast ) -> Retorna valor de nLast incrementado de 1
nLast -> Valor onde deverá ser somado um
/*/
Obs.: A melhor documentação de um programa é o próprio programa ( desde que bem escrito e seguindo-se as regras que foram estabelecidas )
Protheus :: Programação Básica em Advpl “Combo II”
Instruções e Comandos:
As instruções são elementos fixos da linguagem, que lidam com afirmações e com o controle do programa;
c := IF( a > b , a , b )
While ( ( cAlias )->( !Eof() )
//... mais instruções
( cAlias )->( dbSkip() )
End While
nLoops := 10
For nLoop := 1 To nLoops
//... mais instruções
Next nLoop
...
Já os comandos, representam o conjunto de tipos de instruções da linguagem que não são nem rotinas nem chamadas de função e nem atribuições.
Replace cCampo1 With “Conteudo do Campo 1”,;
cCampo2 Whit “Conteudo do Campo 2”
Obs.: Como padrão, um caractere de final de linha encerra uma instrução. Para continuar na outra linha, use o caractere de ponto-e-vírgula.
Use cTable New Via “DBFCDX”
Copy To cNewTable Via “DBFCDX”
Totas as instruções são fixadas em determinada versão da linguagem e, todos os comandos são convertidos pelo pré-processador em chamadas de funções e atribuições.
Onde: Replace cCampo1 With “Conteudo do Campo 1”,;
cCampo2 Whit “Conteudo do Campo 2”
será convertido pelo pré-processador em :
_FIELD->cCampo1 := "Conteudo do Campo1";
_FIELD->cCampo2 := "Conteudo do Campo2”
Use cTable New Via “DBFCDX”
será convertido pelo pré-processador em :
dbUseArea( .T., "DBFCDX", "cTable",, if(.F. .OR. .F., !.F., NIL), .F. )
Copy To cNewTable Via “DBFCDX”
será convertido pelo pré-processador em :
__dbCopy( "cNewTable", { },,,,, .F., "DBFCDX" )
No Advpl são permitidas várias instruções numa linha ( separando-as com um caracter de ponto-e-vírgula ) como em:
i := 0 ; j := 1 ; a := “c” ; d := 99 //...
Esse recurso é interessante ao criar macros do compilador e blocos de codificação, mas se utilizado em todo lugar prejudica a legibilidade do programa.
As instruções são elementos fixos da linguagem, que lidam com afirmações e com o controle do programa;
c := IF( a > b , a , b )
While ( ( cAlias )->( !Eof() )
//... mais instruções
( cAlias )->( dbSkip() )
End While
nLoops := 10
For nLoop := 1 To nLoops
//... mais instruções
Next nLoop
...
Já os comandos, representam o conjunto de tipos de instruções da linguagem que não são nem rotinas nem chamadas de função e nem atribuições.
Replace cCampo1 With “Conteudo do Campo 1”,;
cCampo2 Whit “Conteudo do Campo 2”
Obs.: Como padrão, um caractere de final de linha encerra uma instrução. Para continuar na outra linha, use o caractere de ponto-e-vírgula.
Use cTable New Via “DBFCDX”
Copy To cNewTable Via “DBFCDX”
Totas as instruções são fixadas em determinada versão da linguagem e, todos os comandos são convertidos pelo pré-processador em chamadas de funções e atribuições.
Onde: Replace cCampo1 With “Conteudo do Campo 1”,;
cCampo2 Whit “Conteudo do Campo 2”
será convertido pelo pré-processador em :
_FIELD->cCampo1 := "Conteudo do Campo1";
_FIELD->cCampo2 := "Conteudo do Campo2”
Use cTable New Via “DBFCDX”
será convertido pelo pré-processador em :
dbUseArea( .T., "DBFCDX", "cTable",, if(.F. .OR. .F., !.F., NIL), .F. )
Copy To cNewTable Via “DBFCDX”
será convertido pelo pré-processador em :
__dbCopy( "cNewTable", { },,,,, .F., "DBFCDX" )
No Advpl são permitidas várias instruções numa linha ( separando-as com um caracter de ponto-e-vírgula ) como em:
i := 0 ; j := 1 ; a := “c” ; d := 99 //...
Esse recurso é interessante ao criar macros do compilador e blocos de codificação, mas se utilizado em todo lugar prejudica a legibilidade do programa.
Protheus :: Programação Básica em Advpl “Combo I”
A linguagem Advpl é um Super-Conjunto
da linguagem padrão xBase/CodBase e evoluiu à partir do padrão do
Clipper 5.3 e das bibliotecas do FiveWin. Ela comporta quase todos os
comandos e funções utilizados no Clipper 5.3 e no FiveWin.
O Advpl é a linguagem utilizada pela Totvs/Microsiga para o desenvolvimento do ERP e no desenvolvimento de rotinas específicas nas empresas usuárias desse sistema.
Os elementos sintáticos que formam as instruções de programação em Advpl podem ser divididas nos seguintes grupos: instruções, comandos, diretivas, funções, classes, métodos, constantes , campos e operadores.
O Advpl é a linguagem utilizada pela Totvs/Microsiga para o desenvolvimento do ERP e no desenvolvimento de rotinas específicas nas empresas usuárias desse sistema.
Os elementos sintáticos que formam as instruções de programação em Advpl podem ser divididas nos seguintes grupos: instruções, comandos, diretivas, funções, classes, métodos, constantes , campos e operadores.
quarta-feira, 7 de janeiro de 2009
Windows® PowerShell® "Script" para sincronização do Repositório do Protheus
Hoje Postarei uma dica de como criar um "script", utilizando o Windows® PowerShell® para sincronizar os "Repositórios de Objetos" do Protheus entre o ambiente de desenvolvimento e o de produção.
Crie o arquivo syncApos.ps1 e, utilizando um editor de texto de sua preferência (Notepad, Notepad++ ou Edit, etc.), inclua o código abaixo. Altere o "Path" <$path>, a "string" de busca <$find>, a "string" de substituição <$replace>,o caminho de onde o repositório deverá ser copiado e o diretório de destino do repositório para adequar às suas necessidades.
Para maiores informações de como instalar o Windows® PowerShell® e de como executar "scripts" leia o "Post" Conexão automática ao Claro3G.
Crie o arquivo syncApos.ps1 e, utilizando um editor de texto de sua preferência (Notepad, Notepad++ ou Edit, etc.), inclua o código abaixo. Altere o "Path" <$path>, a "string" de busca <$find>, a "string" de substituição <$replace>,o caminho de onde o repositório deverá ser copiado e o diretório de destino do repositório para adequar às suas necessidades.
Para maiores informações de como instalar o Windows® PowerShell® e de como executar "scripts" leia o "Post" Conexão automática ao Claro3G.
Tendo feito isso basta criar um agendamento utilizando o Agendador de Tarefas do Windows® ("Task Manager") e seus repositórios serão sincronizados.
Obs.:
Para que o "script" possa ser executado no "Task Manager" faz-se
necessário informar o caminho completo do programa que irá executá-lo e o
"script" a ser executado. Ex.:
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe
c:\totvs\p10\scripts\syncApos.ps1
Se desejar baixar os "scripts" de exemplo para a sincronização dos "apos/rpos" do Protheus clique nos respectivos links: mp8syncApos.ps1 e p10syncApos.ps1.
$path = "d:\totvs\p10\bin"
$find = "SourcePath=d:\\totvs\\p10\\rpos\\rpo1"
$iniFile = "totvs*.ini"
$match = get-childitem -path $path -include $iniFile -recurse | select-string $find -list
if ( $match -match $find )
{
$replace = "SourcePath=d:\totvs\p10\rpos\rpo2"
cd d:\totvs\p10\rpos\rpo2
xcopy \\100.0.9.125\p10rpo$\rpo1\tttp101.rpo /s /c /y /d
}
else
{
$find = "SourcePath=d:\\totvs\\p10\\rpos\\rpo2"
$replace = "SourcePath=d:\totvs\p10\rpos\rpo1"
cd d:\totvs\p10\rpos\rpo1
xcopy \\100.0.9.125\p10rpo$\rpo1\tttp101.rpo /s /c /y /d
}
get-childitem -path $path -include $iniFile -recurse | select-string $find -list |% { (get-content $_.Path) |% { $_ -replace $find, $replace } | set-content $_.Path }
Nenhum comentário:
Postar um comentário