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 )
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
Como sempre, o Naldo "mata o problema e mostra o código!". Esta solução (mesmo que temporária) atendeu perfeitamente no meu caso.
ResponderExcluirNaldo,
ResponderExcluirFantastico...
Dificil acreditar que era possível uma solução assim....
Ferne$ deixou um novo comentário sobre a sua postagem "Protheus :: Recuperando Variáveis da Pilha de Cham...":
ResponderExcluirQue 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
Cara,
ResponderExcluirjá 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.
¬¬
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.
ResponderExcluir[]s
иαldσ dj
Tenho uns amigos malucos de ciência da computação,
ResponderExcluirvou 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!
¬¬
hehehe... Essa eu quero ver
ResponderExcluirNaldo,
ResponderExcluirMuito 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 ?
Garoto experto... Falha minha não ter incluido a chamada ao #include "protheus.ch", vou corrigir no original e disponibilizar para "Download".
ResponderExcluirUma 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