Pular para o conteúdo principal

Postagem em destaque

🚀 Oferecendo Serviços Remotos de Desenvolvedor AdvPL e Mais 🖥️

🚀 Oferecendo Serviços Remotos de Desenvolvedor AdvPL e Mais 🖥️ Olá pessoal, Espero que este post encontre todos vocês bem! É com grande entusiasmo que compartilho que estou expandindo meus serviços como Desenvolvedor AdvPL para novos desafios e colaborações. Com mais de duas décadas de experiência sólida, minha jornada profissional tem sido enriquecedora, com a oportunidade de participar de projetos empolgantes ao longo dos anos. Agora, estou ansioso para trazer minha experiência e habilidades para novas equipes e projetos, trabalhando de forma remota. Minha expertise abrange não apenas AdvPL, mas também outras tecnologias-chave, incluindo JS, SQL, Infraestrutura e Otimização de Processos. Acredito que essa combinação de conhecimentos me permite oferecer soluções abrangentes e eficazes para uma variedade de necessidades de desenvolvimento. Acredito que a tecnologia tem o poder de transformar negócios e impulsionar o sucesso, e estou comprometido em ajudar meus clientes a alcançar seu

Protheus :: Recuperando Variáveis da Pilha de Chamadas

Dia desses um conhecido disse-me: "иαldσ, preciso implementar o Ponto de Entrada 'MT360GRV' que será executado logo após a gravação da Condição de Pagamento, Programa 'MATA360'. Só que preciso saber o conteúdo do parâmetro 'nOpcao' que é de escopo 'Local' na função de gravação A360Grava e perguntou-me: Existe alguma forma de eu obter o conteúdo desse parâmetro pra uso na Implementação do Ponto de Entrada?"

Minha resposta costumeira, foi: Hummmm, deixe-me pensar...... e disse-lhe: Existem duas formas de se obter o conteúdo do parâmetro: 

A primeira é entrando em contato com a Totvs e solicitando a melhoria na chamada do Ponto de Entrada, passando como parâmetro o valor de 'nOpcao' de forma a permitir recuperá-lo através de 'ParamIxb' e, a segunda, é tentar obter o conteúdo do parâmetro 'nOpcao'  da 'Pilha de Chamadas'. Como desconheço a existência de alguma função que retorne a 'Pilha de Chamadas', recomendo que utilize a rotina de tratamento de erros pra esse fim. Ou seja: "Force um erro no sistema e, no 'exception', obtenha da propriedade ErrorEnv, do objeto de erro, o conteúdo do parâmetro 'nOpcao'. Obviamente que essa segunda solução é  'paliativa' até que a primeira seja, de fato, implementada. 

E, considerando que não dou 'Ponto sem Nó', implementei as funções abaixo para, se alguém mais passar por uma situação parecida como essa tenha como contornar. A solução é geral mas, infelizmente, incompleta. ErrorEnv é uma string e informações de 'Arrays', 'Blocos de Códigos' e  'Objetos' não estarão disponíveis. Perfeita seria se o objeto de erro contivesse um array com a pilha de chamadas, variáveis, seus tipos e seus valores. Mas, para o caso em questão, que é recuperar um valor numérico, a solução vai atender em 100%.

Vamos ao que interessa, ao código:


#INCLUDE "TRYEXCEPTION.CH"

#DEFINE STACK_INDEX_PARAMETER 1
#DEFINE STACK_INDEX_SCOPE  2
#DEFINE STACK_INDEX_TYPE  3
#DEFINE STACK_INDEX_VALUE  4

#DEFINE STACK_INDEX_ELEMENTS 4 

/*/
 Funcao:  ReadStackParameters
 Autor:  Marinaldo de Jesus
 Data:  19/01/2011
 Uso:  Retornar informacoes de Variaveis da Pilha de Chamadas
 Sintaxe: StaticCall(U_STACKPUSH,ReadStackParameters,cStack,cParameter,cScope,cModule)
/*/
Static Function ReadStackParameters( cStack , cParameter , cScope , cModule )

 Local aStackParameters

 Local bAscan

 Local lScope
 Local lModule

 Local nStack
 Local nParameter
 
 Local uValue

 BEGIN SEQUENCE

  aStackParameters := GetStackParameters()
  
  IF Empty( aStackParameters )
   BREAK
  EndIF
  
  lModule  := !Empty( cModule )

  IF ( lModule )
   nStack := aScan( aStackParameters , { |x| ( x[ 1 ] == cStack ) .and. ( cModule $ x[ 2 ] ) } )
  Else
   nStack := aScan( aStackParameters , { |x| x[ 1 ] == cStack } )
  EndIF 
  
  IF ( nStack == 0 )
   BREAK
  EndIF

  lScope  := !Empty( cScope )

  IF ( lScope )
   nParameter := aScan( aStackParameters[ nStack ][ 3 ] , { |x| ( x[ STACK_INDEX_PARAMETER ] == cParameter ) .and. ( x[ STACK_INDEX_SCOPE ] == cScope ) } )
  Else
   nParameter := aScan( aStackParameters[ nStack ][ 3 ] , { |x| ( x[ STACK_INDEX_PARAMETER ] == cParameter ) } )
  EndIF

  IF ( nParameter == 0 )
   BREAK
  EndIF

  uValue := aStackParameters[ nStack ][ 3 ][ nParameter ][ STACK_INDEX_VALUE ]

 END SEQUENCE

Return( uValue )

/*/
 Funcao:  GetStackParameters
 Autor:  Marinaldo de Jesus
 Data:  19/01/2011
 Uso:  Obtem Array com a Pilha de Chamadas que sera usado pela ReadStackParameters
 Sintaxe: StaticCall(U_STACKPUSH,GetStackParameters)
/*/
Static Function GetStackParameters()

 Local aStackEnv
 Local aStackParameters := {}
 
 Local cStack
 Local cModule
 Local cStackEnv
 
 Local nStack
 Local nIndexEnv
 Local nStackEnv

 Local oException
 
 TRYEXCEPTION

  UserException( "IGetStackParameters" )

 CATCHEXCEPTION USING oException

     cStackEnv := oException:ErrorEnv
     cStackEnv := StrTran( cStackEnv , "  " , CRLF )
     cStackEnv := StrTran( cStackEnv , "STACK " , CRLF + "STACK " )
     aStackEnv := StrTokArr( cStackEnv , CRLF )

     cStackEnv := NIL

     nIndexEnv := 0
     nStackEnv := Len( aStackEnv )

     While ( ( ++nIndexEnv ) <= nStackEnv )

      IF ( "Public" $ aStackEnv[ nIndexEnv ] )

       IF ( "Publicas" $ aStackEnv[ nIndexEnv ] )
        Loop
       EndIF

       nStack := aScan( aStackParameters , { |x| ( x[1] == "PUBLIC" ) } ) 
      
       IF ( nStack == 0 )
        aAdd( aStackParameters , { "PUBLIC" , "" , Array(0) } )
        nStack := Len( aStackParameters )
       EndIF

       cStackEnv := aStackEnv[ nIndexEnv ] 
       AddStackParameters( @aStackParameters , @nStack ,  @cStackEnv  )

            ElseIF ( "STACK" == SubStr( aStackEnv[ nIndexEnv ] , 1 , 5 ) )
             
             cStackEnv := AllTrim( StrTran( aStackEnv[ nIndexEnv ] , "STACK" , "" ) )
             cStack  := SubStr( cStackEnv , 1 , AT( "(" , cStackEnv ) - 1 )
             cModule  := StrTran( cStackEnv , cStack , "" )

       nStack   := aScan( aStackParameters , { |x| ( x[1] == cStack ) } ) 

       IF ( nStack == 0 )
        aAdd( aStackParameters , { cStack , cModule , Array(0) } )
        nStack := Len( aStackParameters )
       EndIF

             While (;
                ( ( ++nIndexEnv ) <= nStackEnv );
                .and.;
                !( "STACK" == SubStr( aStackEnv[ nIndexEnv ] , 1 , 5 ) );
                .and.;
                !( "FILES" == Upper( SubStr( aStackEnv[ nIndexEnv ] , 1 , 5 ) ) );
               ) 

        cStackEnv := aStackEnv[ nIndexEnv ] 
        AddStackParameters( @aStackParameters , @nStack ,  @cStackEnv  )

             End While

             --nIndexEnv

   ElseIF ( "FILES" == Upper( SubStr( aStackEnv[ nIndexEnv ] , 1 , 5 ) ) )
   
    Exit

            EndIF
       
     End While

 ENDEXCEPTION

Return( aStackParameters )

Static Function AddStackParameters( aStackParameters , nStack ,  cStackEnv  )

 Local aToken  := StrTokArr( cStackEnv , ":" )
 
 Local cType
 Local cScope
 Local cParameter
 
    Local nToken  := Len( aToken )
    Local nParameter

 Local uValue
 
 IF ( nToken >= 1 )
  cScope := Upper( AllTrim( StrTokArr( aToken[ 1 ] , " " )[1] ) )
 Else
  cScope := "UNDEFINED"
 EndIF 

 IF ( nToken >= 2 )
  cStackEnv := aToken[ 2 ]
     cParameter := AllTrim( SubStr( cStackEnv , 1 , AT( "(" , cStackEnv ) - 1 ) )
     cType  := SubStr( cStackEnv , AT( "(" , cStackEnv ) + 1 , 1 )
    Else
     cParameter := "NULL"
     cType  := "U"
    EndIF 

 IF ( nToken >= 3 )
  uValue  := aToken[ 3 ]
 Else
  uValue  := NIL
 EndIF 

 TRYEXCEPTION

  Do Case
   Case ( cType == "N"  )
    uValue := Val( uValue )
   Case ( cType == "D"  )
    uValue := Ctod( uValue )
   Case ( cType == "L"  )
    uValue := &( uValue )
   Case ( cType == "B"  )
    uValue := &( uValue )
   Case ( cType == "A"  )
    uValue := {}
   Case ( cType $ "U/O" )
    uValue := NIL
  End Case 
  
 CATCHEXCEPTION

  uValue := NIL
 
 ENDEXCEPTION     

 aAdd( aStackParameters[ nStack ][3] , Array( STACK_INDEX_ELEMENTS ) )
 
 nParameter := Len( aStackParameters[ nStack ][3] )

 aStackParameters[ nStack ][3][ nParameter ][ STACK_INDEX_PARAMETER ] := cParameter
 aStackParameters[ nStack ][3][ nParameter ][ STACK_INDEX_SCOPE  ] := cScope
 aStackParameters[ nStack ][3][ nParameter ][ STACK_INDEX_TYPE  ] := cType
 aStackParameters[ nStack ][3][ nParameter ][ STACK_INDEX_VALUE  ] := uValue

Return( NIL )    
       


Com as funções acima, conseguiremos recuperar o conteúdo de uma variável da 'Pilha de Chamadas' através do 'Tratamento de Erros'.

Um exemplo de uso seria:


/*/
 Funcao:  U_MT360GRV()
 Autor:  Marinaldo de Jesus
 Data:  20/01/2011
 Uso:  Implementacao do Ponto de Entrada MT360GRV que sera executado apos a Gravacao das Condicoes de pagamento
    Demonstra o Uso das funcoes pra recuperar conteudo de variaveis da Pilha de Chamadas
/*/
User Function MT360GRV()

 /*/
  Chamo Static Function ReadStackParameters que se encontra compilada no 'Modulo/Programa' U_STACKPUSH para obter o
  conteudo do Parametro nOpcao. ReadStackParameters espera que todos os parametros sejam passados em   "Upper Case"
  em funcao da forma de armazenamento. Os parametros obrigatorios sao o 'Funcao' a partir de  onde deseja-se  obter 
  o conteudo do Parametro e o parametro em si. Os parametros cScope e cModule sao opcionais, serao usados para  uma
  busca mais refinada. Ira retornar o conteudo do parametro se seu tipo for Numerico, Caractere, Logico, Data  e/ou
  Bloco de Codigo, lembrando que esse ultimo nao podera ser avalidado pois seu 'ponteiro' nao se encontra na declara
  cao original.
 /*/
 
 Local nOpcao := StaticCall(U_STACKPUSH,ReadStackParameters,"A360GRAVA","NOPCAO")
 
 IF ( nOpcao == 3 )
  //Implementacao especifica para o Ponto de Entrada
 EndIF

Return( NIL )
       
Que poderá ser baixado ao clicar aqui.

Se alguém souber uma forma de obter a "pilha de chamadas e parâmetros" sem ser via tratamento de erro, ficaria imensamente feliz em aprender.

E, como todo conhecimento não compartilhado torna-se nulo, sempre que aprender algo novo compartilhe para que seja perpetuado.

Esse é o meu lema.

[]s
иαldσ dj

Comentários

  1. Como sempre, o Naldo "mata o problema e mostra o código!". Esta solução (mesmo que temporária) atendeu perfeitamente no meu caso.

    ResponderExcluir
  2. Naldo,
    Fantastico...
    Dificil acreditar que era possível uma solução assim....

    ResponderExcluir
  3. Ferne$ deixou um novo comentário sobre a sua postagem "Protheus :: Recuperando Variáveis da Pilha de Cham...":

    Que show Naldo, a algum tempo atras precisei de algo parecido no template ACD, precisava obter um valor da tela em um ponto de entrada, abri um chamado para Totvs solicitando que a variavel fosse enviada via ParamIXB e minha solucao paleativa foi usar a funcao (VTSave) responsavel por montar um Array com as informacoes da tela.
    Muito util sua solucao

    ResponderExcluir
  4. Cara,
    já fiz cada gambi pra conseguir recuperar valores de escopo LOCAL em um determinado PE.

    Sempre pensei em uma forma de recuperar esses valores, apesar de ser um dado volatil, sera que seria possivel obte-lo da memoria.

    ¬¬

    ResponderExcluir
  5. Deve ter uma forma mas eu a desconheço. As variáveis estão lá, na Pilha... só esperando serem encontradas. A unica forma que encontrei de recuperá-las foi através do Tratamento de Erros. Então se deseja obter uma variável de escopo Local do Tipo Numérica, Data, Caractere ou Lógica, poderá abrir mão do código que disponibilizei para "Download". Esse código serve para recuperár os valores de Static, Private e Public também. Para as variáveis de escopo Local e Static não obteremos Arrays, Blocos de Código e Objetos, Limitação essa que não existe para as de escopo Private e Public.


    []s
    иαldσ dj

    ResponderExcluir
  6. Tenho uns amigos malucos de ciência da computação,
    vou ver com eles esse sistema de endereçamento de memoria, se existe alguma assinatura do protheus que as identifique.
    Nem que tenhamos que buscar esses valores descendo ao nível mais baixo e criando uma DLL ou EXE para ser executado externamente ao Protheus.

    Vou questiona-los!
    Abs

    Amonimo!
    ¬¬

    ResponderExcluir
  7. Naldo,
    Muito boa sua solução porém, quando fui compilar ocorreu um erro de sintaxe na linha 30 do rdmake U_StackPush.prg, com a instrução DEFAULT aStackParameters := GetStackParameters().
    Não estaria faltando o include do PROTHEUS.CH ?

    ResponderExcluir
  8. Garoto experto... Falha minha não ter incluido a chamada ao #include "protheus.ch", vou corrigir no original e disponibilizar para "Download".

    Uma observação. NÃO É RDMAKE. RDMAKE era um "Pseudo Compilador" que convertia o código, escrito em CodBase, de forma que o "SIGA ADVANCED" pudesse interpretá-lo já em advpl, criamos verdadeiras "Functions" que são executadas exatamente igual às "Functions" criadas pela equipe de IP.

    []s
    иαldσ dj

    ResponderExcluir

Postar um comentário

Postagens mais visitadas deste blog

BlackTDN :: RLeg ~ Desvendando a Função ParamBox

Para quem precisar desenvolver uma interface de entrada de dados, coisa rápida, e não quer ter aquele trabalhão danado que todos já sabemos, o Protheus tem uma função que ajuda muito, é uma interface semelhante a função Pergunte, porém com muito mais opção de objeto de entrada de dados, alias até colocar o scrollbox desta interface com todos os objetos em outra MsDialog ou Wizard é simples. Vejam o exemplo abaixo, boa sorte! Rleg. //---------------------------------------------------------- // Função exemplo utilizando a função ParamBox() //---------------------------------------------------------- User Function xParamBox() Local aRet := {} Local aParamBox := {} Local aCombo := {"Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"} Local i := 0 Private cCadastro := "xParambox" // ---------------

BlackTDN :: Customizando a interface de Login no Protheus e by You

A publicação “ BlackTDN :: By You e sua nova tela de login ”  de nosso amigo OBona deu o que falar e, em função disso, esse que a muito não vos escreve resolveu criar uma versão onde será possível personalizar, “por completo”, a tela de login no Protheus/by You. Considerando que OBona já havia “mapeado, identificado e customizado” as imagens peguei-as emprestadas para o exemplo que se segue: O primeiro passo para a customização “total” da interface de login do Protheus/by You será implementar o “Ponto de Entrada” ChgPrDir (Diretório de impressão) . Usaremos esse PE juntamente como programa U_FindMsObject.prg (apresentado pela primeira vez em: Protheus :: ADVPL : The Container : Presents Pandora's box ). Diferente do exemplo proposto por OBona, que substitui, durante o processo de compilação, as imagens padrões do sistema (excluindo-as) por imagens customizadas (com o mesmo nome) este novo exemplo mantém, no RPO, as imagens padrões adicionando novas imagens customizadas que serã

Protheus :: Chamando Funções do Menu Diretamente e sem a Necessidade de Login

Ferne$ perguntou: "...é possível abrir alguma rotina do sistema sem solicitar login ao usuário, como por exemplo a rotina MATA010..." Sim Ferne$, é possível sim. Abaixo um Exemplo para a Chamada à função MATA010 sem a necessidade de Login no sistema. #INCLUDE "PROTHEUS.CH" #INCLUDE "TBICONN.CH" /*/ Funcao: MATA010Ex Data: 30/04/2011 Autor: Marinaldo de Jesus Descricao: Executar a Funcao MATA010 diretamente sem a necessidade de LOGIN no Protheus Sintaxe: 1 ) U_MATA010Ex ( Chamada diretamente na Tela de Entrada do Sistema ) ; ou 2 ) totvsclient.exe -q -p=u_MATA010Ex -a=01;01 -c=rnp_local -e=rnp -m -l ( Chamada Via Linha de Comando ) /*/ User Function MATA010Ex( cEmpFil ) Local aEmpFil Local bWindowInit := { || __Execute( "MATA010()" , "xxxxxxxxxxxxxxxxxxxx" , "MATA010" , "SIGAFAT" , "SIGAFAT", 1 , .T. ) } Local cEmp Local cFil Local cMod Local cModName := "SIGAFAT" DEFA