Ir al contenido


Foto

Evitar valor basura en atributo de objeto


  • Por favor identifícate para responder
10 respuestas en este tema

#1 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 15 mayo 2016 - 07:09

En mi aplicacion obtengo un mensaje de error read of address en la siguiente instruccion:


delphi
  1. ShowMessage(login.sign);

y observando el valor de "sign" tiene un residuo de la memoria.. a fsign no acedo porque es privado...la clase login esta en otra unidad y el atributo sign se setea dentro de un metodo.

 

lo unico que se me viene a la cabeza es(de la estructurada):


delphi
  1. setsign='';

antes de setear..pero no es correcto..


  • 0

#2 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 15 mayo 2016 - 09:29

No vemos nada del codigo, asi que queda adivinar...

 

Asumiendo que login.sign es un string, que es un tipo manejado, por lo tanto no hay que preocuparse por como se aloja/desaloja la memoria

 

Asumiendo que login es un clase, si debes manejar manualmente la memoria, con llamadas explicitas a Create y Free

 

Basicamente tienes un "dangling pointer", es decir un puntero a una zona de memoria que no esta "marcada como ocupada" por el manejador de memoria, entonces tiene basura. Al referenciar al string, obtenes en el depurador la memoria "plana" que habia en la direccion a la que apunta

 

El string es manejado automaticamente por el compilador, pero cuando esta metido adentro de otra estructura, por ejemplo, un objeto, el "manejo automatico" ocurre al crear/destruir el objeto

 

Hace una prueba sencilla:

1. Crea el objeto

2. Inspecciona el valor de Sign despues de crearla

3. Asignale un valor

4. Inspecciona el valor de Sign nuevamente

5. Libera el objeto

6. Inspecciona el valor de Sign nuevamente


Editado por Agustin Ortu, 15 mayo 2016 - 09:30 .

  • 0

#3 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 18 mayo 2016 - 10:58

esto pasa la primera vez..despues anda bien..me devuelve el contenido del archivo xml que es lo que debe haber. 

bueno lo que pasa es que el metodo set del atributo al igual que los otros setter son privados...

y en el constructor solo construyo el objeto asignando a "servicio", el sign y el token los seteo despues..

 

asi quedo el metodo estatico donde llamo al constructor:


delphi
  1. class function TLogin.unicoLogin:TLogin;
  2.  
  3. begin
  4. if login<>nil then
  5. begin
  6. if login.expiro=False then
  7. begin
  8. Result:=login;
  9. end
  10. else
  11. begin
  12. login.actualizarsigntoken;
  13. Result:=login;
  14. end;
  15. end
  16. else
  17. begin
  18. login:=Tlogin.Loguearse('wsfe');
  19. login.respuestaXML(login.generarCMS(login.guardarXML()));
  20.  
  21. end;
  22. end;

quizas debo contruir el objeto asignando vacio(' ') a sign y token??

 

este es el metodo donde seteo sign y token..a lo mejor hay un problema con los tipos


delphi
  1. procedure TLogin.respuestaXML(const s:string);
  2. var
  3. RIOLogin:THTTPRIO;
  4. xml3:IXMLDocument;
  5. content,expira,nodo:string;
  6. nodohijo,nodohijosign,startnode,startnodesign:IXMLNode;
  7. begin
  8.  
  9. RIOLogin:=THTTPRIO.Create(nil);
  10. with RIOLogin do
  11. begin
  12. WSDLLocation:='https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl';
  13. Port:='LoginCms';
  14. Service:='LoginCMSService';
  15.  
  16. end;
  17.  
  18.  
  19. xml3:=NewXMLDocument;
  20. content:=(RIOLogin as LoginCms).loginCms(s);
  21. xml3.XML.Text:=content;
  22. xml3.Active:=True;
  23. xml3.SaveToFile(ExtractFilePath(Application.ExeName) + 'respuestaxml.xml');
  24. startnode:=xml3.ChildNodes[1];
  25. nodohijo:=startnode.ChildNodes[0];
  26. nodo:=nodohijo.ChildNodes['expirationTime'].Text;
  27. expira:=Copy(nodo,12,8);
  28. Setexpiration(expira);
  29. //setear sign y token
  30. startNodesign := xml3.ChildNodes[1];
  31. nodohijosign:=startnodesign.ChildNodes[1];
  32.  
  33.  
  34. Fsign:=nodohijosign.ChildNodes['sign'].Text;
  35. Ftoken:=nodohijosign.ChildNodes['token'].Text;
  36.  
  37. end;


  • 0

#4 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 18 mayo 2016 - 01:14

La clave esta en IXMLNode

 

Al menos por el nombre del tipo,  ya que por convencion las interfaces en Delphi empiezan con la letra I, tal y como las clases comienzan con T

 

Las referencias a las interfaces son manejadas automaticamente por el compilador. No hay necesidad de destruir los objetos explicitamente. 

 

El compilador lleva la cuenta de la cantidad de referencias a cada interface (usa la misma tecnica para strings y record, a estos tipos de datos se los conoce como "managed types"). Cuando esa cuenta de referencias sea 0, se invoca el destructor del objeto y se libera de memoria

 

Hay otro concepto que entra en juego aca. El "scope" o alcance de una variable. Las variables locales de los metodos tienen como scope el "metodo". Podria decirse que es el tiempo de vida de la variable. Cuando se produce una asignacion en una variable interface (constructor, metodo de clase, asignacion de parametro, cualquier asignacion):

 

1. Se verifica el contador de referencias de la variable antes de asignar, ya que puede estar apuntando a una estructura valida en memoria. Si es el caso, se decrementa en uno el contador de referencias

 

2. Se asigna a la variable para que apunte a la interface. Se incrementa el contador de referencias (no se copia la estructura, simplemente es una referencia a la misma zona de memoria)

 

Cuando el metodo termina, el alcance de la variable local "expira", y el compilador automaticamente decrementa en uno el contador de refencias de esa variable; normalmente este llega a 0 y se destruye la interface. 

 

No se que es FSign y FToken; tampoco se que es lo que le estas guarando (nunca use las interfaces IXml)

 

Tampoco se que es (del primer post) "login.sign". 

 

Pone puntos de ruptura en los destructores de esas interfaces a ver si se esta eliminando algo de memoria


  • 0

#5 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 18 mayo 2016 - 01:25

Sign y token son atributos de la clase login..cuando llamo a fsign y a ftoken lo que quiero es setear los atributos..osea yo proceso el xml devuelto por la AFIP . Usando ixml.. accedo a los nodos para conocer el valor de sign y token..

Y de hecho lo corri con f4 y el error lo da en el svowmessage dentro de la unidad vcl.controls..

La segunda vez que lo corro el metodo unicologin funciona bien..me devuelve la instancia y me muestra el cartel con el mismo sign cada vez que apreto...el error es solo la primera vez..


Enviado desde mi SM-G530M mediante Tapatalk
  • 0

#6 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 18 mayo 2016 - 04:46

Subi el codigo entero a algun repositorio tipo Git y lo clonamos; ya sino son demasiadas conjeturas y te puedo estar diciendo cualquier cosa

 

Eso o pon el minimo codigo necesario para que yo copie y pege y reproduzca el problema


Editado por Agustin Ortu, 18 mayo 2016 - 04:47 .

  • 0

#7 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 19 mayo 2016 - 11:57

ok muchas gracias agustin.. este es el codigo del button


delphi
  1. procedure TForm1.BitBtn1Click(Sender: TObject);
  2. var
  3. admin:TIniFile;
  4. ECUIT:string;
  5. login:TLogin;
  6. begin
  7. admin:=TIniFile.Create(ExtractFilePath(Application.ExeName )+ 'caja.ini');
  8. ECUIT:=Copy(MaskEdit1.Text,0,2) + Copy(MaskEdit1.Text,4,8) + Copy(MaskEdit1.Text,13,1);
  9. // Fsign:=obtenersign(respuestaXML));
  10. login:=TLogin.unicoLogin;
  11.  
  12. ShowMessage(login.sign);
  13. end;

para lo mismo la unidad donde esta el boton incluye esta otra:


delphi
  1. unit UAutenticacionAFIP;
  2.  
  3. interface
  4. uses
  5. XMLIntf, XMLDoc,shellAPI,dialogs,Classes,SOAPHTTPClient,sysutils,DateUtils;
  6.  
  7. type
  8. TLogin = class
  9. private
  10.  
  11. Fservicio: string;
  12. Fsign: string;
  13. Ftoken: string;
  14. Fexpiration: string;
  15. procedure Setservicio(const Value: string);
  16. procedure Setsign(const Value: string);
  17. procedure Settoken(const Value: string);
  18. procedure Setexpiration(const Value: string);
  19.  
  20. public
  21. property expiration:string read Fexpiration write Setexpiration;
  22. property token:string read Ftoken write Settoken;
  23. property sign:string read Fsign write Setsign;
  24. property servicio:string read Fservicio write Setservicio;
  25.  
  26. destructor destroy;
  27. function guardarXML():IXMLDocument;
  28. function armarsource:string;
  29. function generarCMS(const xml1:IXMLDocument):string;
  30. procedure respuestaXML(const s:string);
  31. function expiro():boolean;
  32. class function unicoLogin:TLogin;
  33. procedure actualizarsigntoken;
  34. constructor Loguearse(s:string);
  35. end;
  36. var
  37. login:TLogin;
  38. implementation
  39.  
  40. { TLogin }
  41.  
  42. uses tra,IniFiles,Forms, LoginCms1, loginresponse;
  43. procedure TLogin.actualizarsigntoken;
  44. var
  45. xml1:IXMLDocument;
  46. cms:string;
  47. begin
  48. xml1:=NewXMLDocument();
  49. xml1:=login.guardarXML();
  50. cms:=login.generarCMS(xml1);
  51. login.respuestaXML(cms);
  52. end;
  53.  
  54. function TLogin.armarsource: string;
  55. var
  56. archivoini:TIniFile;
  57. source,o,serialNumber,C,cn:string;
  58. begin
  59. archivoini:=TIniFile.Create(ExtractFilePath(Application.ExeName)+ 'caja.ini');
  60. o:=archivoini.ReadString('EMPRESA','O','');
  61. serialNumber:=archivoini.ReadString('EMPRESA','CUIT','');
  62. cn:=archivoini.ReadString('EMPRESA','cn','');
  63. C:=archivoini.ReadString('EMPRESA','C','');
  64. source:='C='+C+',o='+o+',serialNumber='+serialNumber+',cn='+cn;
  65. Result:=source;
  66. end;
  67.  
  68. destructor TLogin.destroy;
  69. begin
  70. inherited;
  71. end;
  72.  
  73. function TLogin.expiro: boolean;
  74. var
  75. time:string;
  76. begin
  77.  
  78. if MinutesBetween(now,StrToDateTime(expiration))<2 then
  79. begin
  80. result:=True;
  81. end
  82. else
  83. begin
  84. result:=False;
  85. end;
  86. end;
  87.  
  88. function TLogin.generarCMS(const xml1:IXMLDocument):string;
  89. var
  90. rutabat,textoCMS,linea:string;
  91. archivoCMS:TStringList;
  92.  
  93. begin
  94.  
  95. rutabat:=ExtractFilePath(Application.ExeName)+'certificado\'+'cms.bat';
  96. ShellExecute(0,'open',PWideChar(rutabat),nil, nil,0);
  97. archivoCMS:=TStringList.Create;
  98.  
  99. archivoCMS.LoadFromFile(ExtractFilePath(Application.ExeName) + 'loginticketrequest.xml.cms');
  100.  
  101. textocms:=Copy(archivoCMS.Text,23,2714);
  102.  
  103. result:=textoCMS;
  104.  
  105. end;
  106.  
  107. function TLogin.guardarXML():IXMLDocument;
  108. var
  109. loginticketrequest:IXMLLoginTicketRequestType;
  110. loginheader:IXMLHeaderType;
  111. archivoini:TIniFile;
  112. diai,diaf,horai,time,horae,expiration:string;
  113. XML1:IXMLDocument;
  114. XMLanterior:IXMLDocument;
  115. startnode,nodohijo,nodoexp:IXMLNode;
  116. begin
  117.  
  118. time:=FormatDateTime('hh:mm:ss',now);
  119.  
  120. //crear archivo xml
  121. XML1 := NewXMLDocument;
  122.  
  123. XML1.FileName:='loginticketrequest.xml';
  124.  
  125.  
  126. loginticketrequest:=NewloginTicketRequest;
  127. //asginar valores para loginticketrequest.xml
  128. // version
  129.  
  130. loginticketrequest.Version:='1.0';
  131. //header
  132.  
  133. // uniqueid son constantes
  134.  
  135. loginticketrequest.header.UniqueId:=4325399;
  136. //hora del pedido.. es ahora
  137. diai:=FormatDateTime('yyyy-mm-dd',Now);
  138. diaf:=FormatDateTime('yyyy-mm-dd',IncHour(Now,23));
  139. horai:=FormatDateTime('hh:mm:ss',IncMinute(now,-40));
  140.  
  141. loginticketrequest.header.GenerationTime:=diai+ 'T' + horai;
  142. //hora de finalizacion (1 dia es el maximo);
  143. loginticketrequest.header.ExpirationTime:=diaf+'T'+horai;
  144.  
  145. //servicio a acceder
  146. loginticketrequest.Service:=Fservicio;
  147. XML1.XML.Add(loginticketrequest.XML);
  148. XML1.Active:=True;
  149.  
  150. XML1.SaveToFile(ExtractFilePath(Application.ExeName) + 'loginticketrequest.xml');
  151.  
  152. Result:=XML1;
  153.  
  154.  
  155.  
  156. end;
  157.  
  158. constructor TLogin.Loguearse(s:string);
  159. begin
  160. self.Fservicio:=s;
  161. end;
  162.  
  163. procedure TLogin.respuestaXML(const s:string);
  164. var
  165. RIOLogin:THTTPRIO;
  166. xml3:IXMLDocument;
  167. content,expira,nodo:string;
  168. nodohijo,nodohijosign,startnode,startnodesign:IXMLNode;
  169. begin
  170.  
  171. RIOLogin:=THTTPRIO.Create(nil);
  172. with RIOLogin do
  173. begin
  174. WSDLLocation:='https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl';
  175. Port:='LoginCms';
  176. Service:='LoginCMSService';
  177.  
  178. end;
  179.  
  180.  
  181. xml3:=NewXMLDocument;
  182. content:=(RIOLogin as LoginCms).loginCms(s);
  183. xml3.XML.Text:=content;
  184. xml3.Active:=True;
  185. xml3.SaveToFile(ExtractFilePath(Application.ExeName) + 'respuestaxml.xml');
  186. startnode:=xml3.ChildNodes[1];
  187. nodohijo:=startnode.ChildNodes[0];
  188. nodo:=nodohijo.ChildNodes['expirationTime'].Text;
  189. expira:=Copy(nodo,12,8);
  190. Setexpiration(expira);
  191. //setear sign y token
  192. startNodesign := xml3.ChildNodes[1];
  193. nodohijosign:=startnodesign.ChildNodes[1];
  194.  
  195.  
  196. Fsign:=nodohijosign.ChildNodes['sign'].Text;
  197. Ftoken:=nodohijosign.ChildNodes['token'].Text;
  198.  
  199. end;
  200.  
  201.  
  202.  
  203. procedure TLogin.Setexpiration(const Value: string);
  204. begin
  205. Fexpiration := Value;
  206. end;
  207.  
  208. procedure TLogin.Setservicio(const Value: string);
  209. begin
  210. Fservicio := Value;
  211. end;
  212.  
  213. procedure TLogin.Setsign(const Value: string);
  214. begin
  215. Fsign := Value;
  216. end;
  217.  
  218.  
  219. procedure TLogin.Settoken(const Value: string);
  220. begin
  221. Ftoken := Value;
  222. end;
  223.  
  224.  
  225. class function TLogin.unicoLogin:TLogin;
  226.  
  227. begin
  228. if login<>nil then
  229. begin
  230. if login.expiro=False then
  231. begin
  232. Result:=login;
  233. end
  234. else
  235. begin
  236. login.actualizarsigntoken;
  237. Result:=login;
  238. end;
  239. end
  240. else
  241. begin
  242. login:=Tlogin.Loguearse('wsfe');
  243. login.respuestaXML(login.generarCMS(login.guardarXML()));
  244.  
  245. end;
  246. end;
  247.  
  248. end.


  • 0

#8 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 22 mayo 2016 - 09:01

lo que pasa es que obtengo un innaccessible value..al intentar usar un atributo.. dice self y al lado innaccessible value..el error salta en la linea de minutesbetween..al querer comparar con el atributo expiration...de la variable login es una variable global de tipo login..pero la verdad no se porque no puedo dentro de un metodo de la misma clase acceder a un atributo...


  • 0

#9 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 23 mayo 2016 - 12:50

bueno el error esta en esta funcion expiro..esta funcion devuelve true cuando la diferencia en minutos entre la fecha de expiracion del certificado escrita en el xml devuelto por afip es menor a la actual..en ese caso se vuelve a pedir un certificado..

 

el error esta en la linea


delphi
  1. if MinutesBetween(now,StrToDateTime(expiration))<2 then

de que manera puedo comparar la hora actual con la del xml si la cadena del xml tiene este formato:

 

2016-05-24T03:46:28.173-03:00.. ademas luego de obtener la diferencia debo comparar si es mayor a 0...


  • 0

#10 giulichajari

giulichajari

    Advanced Member

  • Miembros
  • PipPipPip
  • 477 mensajes

Escrito 23 mayo 2016 - 12:58

se soluciono usando minuteSpan....

 

y otra cosa... 

 

esto es mal:


delphi
  1. login:=TLogin.unicoLogin;

debe ser esto: porque login es global y el metodo no es de instancia sino de clase


delphi
  1. TLogin.unicoLogin;


  • 0

#11 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 23 mayo 2016 - 01:43

Disculpame que no lo haya podido probar aun; tengo un parcial el jueves de probabilidades y tambien varios quilombos "de sistemas" en esta semana, asi que ando sin nada de tiempo

 

Me alegro de que lo hayas podido solucionar ;)


  • 0