Invalid Pointer na liberação do formulário

410 views
Skip to first unread message

Samuel N. Junior

unread,
Nov 16, 2010, 5:13:11 AM11/16/10
to DUG-RS - Delphi Users Group Rio Grande do Sul
Bom dia pessoal,

Tenho um sistema que cria uma tabela dinamicamente (Table) onde incluo alguns campos calculados.

Uso uma rotina similar a seguinte:

:
var
    Campo: TField;
    wcbTotal: TFloatField;
    Elementos: Integer;
:
:

Procedure Form1.CriaTabela;
var
    Cont: Integer;
Begin
:
:
    With TnxMemTable1 do Begin
       StoreDefs:=True;
       FieldDefs.Clear;
       IndexDefs.Clear;
       TableName:='TMP$0001'
       FieldDefs.Add('cbCODIGO',       ftString,15,False);
       FieldDefs.Add('cbDESCRICAO',    ftString,40,False);
       For Cont:=1 to Elementos do
            FieldDefs.Add('cbValor'+IntToHex(Cont,4),    ftFloat,0,False);
   
       for Cont:=0 to TnxMemTable1.FieldDefs.Count-1 do
            Campo:=TnxMemTable1.FieldDefs[Cont].CreateField(TnxMemTable1);

       if Not Assigned(wcbTotal) then
          wcbTOTAL := TFloatField.Create(
TnxMemTable1);

       wcbTOTAL.FieldName := 'cbTOTAL';
       wcbTOTAL.FieldKind := fkCalculated;
       wcbTOTAL.Calculated := True;
       wcbTOTAL.DataSet :=
TnxMemTable1;
       Fields.Add(wcbTOTAL);
       IndexDefs.Add('FF$PRIMARY','cbCODIGO',[ixUnique]);
       Open;
    End;
:
:
End;


No evento OnClose:

Begin
     TnxMemTable1.close;
     If Assigned(Campo) Then
         Campo.Free;
     If Assigned(wcbTotal) Then
         wcbTotal.Free;
end;


Tudo funciona muito bem, porém quando vou liberar o formulário da memória recebo o erro "invalid pointer operation".

Onde estou errando no código ? Deve ter um erro muito "idiota" que não estou conseguindo ver.

O TnxMemTable é um componente que cria a tabela em memória.

[ ]s



Samuel Natali Junior
NatSam Consultoria Ltda
Fone: (11) 4688-1000
Skype: Hipercusto
www.natsam.com.br
www.hipercusto.com.br
Conheça o Hipercusto e veja como ele pode ajudar sua indústria.
Faça um download e avalie o produto gratuitamente durante 20 dias.


www.tr7.com.br o seu site de buscas na web

Secaio

unread,
Nov 16, 2010, 5:18:26 AM11/16/10
to dug...@googlegroups.com
opa, como tu ta liberando ele (o form) da memoria?
 
 


 
2010/11/16 Samuel N. Junior <sam...@natsam.com.br>
--
Você recebeu esta mensagem porque está inscrito no "DUG-RS -
Delphi Users Group Rio Grande do Sul" em Grupos do Google.
Acesse o nosso BLOG em http://www.dug-rs.org e contribua com a comunidade Delphi do Rio Grande do Sul
Para postar neste grupo, envie um e-mail para dug...@googlegroups.com
Para cancelar a sua inscrição neste grupo, envie um e-mail para
dug-rs-un...@googlegroups.com
Para ver mais opções, visite este grupo em
http://groups.google.com.br/group/dug-rs?hl=pt-BR
Twitter: @dugrs



--
Secaio´s Corporation

Samuel N. Junior

unread,
Nov 16, 2010, 5:32:25 AM11/16/10
to dug...@googlegroups.com
A rotina que cria o formulário (e depois a libera) é a seguinte:

procedure TMenu_Sistema.Modulo(zFormulario: TFormClass; var wFormulario);
Begin
:
:
     Application.CreateForm(zFormulario, TForm(wFormulario));
     Botao_Formulario(TForm(wFormulario));
     Ajusta_Cores(wFormulario);
     With TForm(wFormulario) do
         Try
             ShowModal;
         Finally
              Release;
     End;
:
:
End;

Essa função cria todos os formulários do projeto e só dá problema nesse formulário específico e apenas se eu criar os campos manualmente.

[ ]s


Samuel Natali Junior
NatSam Consultoria Ltda
Fone: (11) 4688-1000
Skype: Hipercusto
www.natsam.com.br
www.hipercusto.com.br
Conheça o Hipercusto e veja como ele pode ajudar sua indústria.
Faça um download e avalie o produto gratuitamente durante 20 dias.


www.tr7.com.br o seu site de buscas na web


Secaio

unread,
Nov 16, 2010, 6:00:29 AM11/16/10
to dug...@googlegroups.com
bah da uma olhada se isso nao te resolve.... 
 
  wFormulario := TwFormulario.Create(Application);
 
veja só:   procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
 
ex:   Application.CreateForm(TFormPrincipal, FormPrincipal);
 
note que no teu código abaixo que tem alguma coisa diferente disso... veja se nao eh isso q ta te incomodando....
 
    Application.CreateForm(ZFormulario, TForm(wFormulario));

     Botao_Formulario(TForm(wFormulario));
     Ajusta_Cores(wFormulario);
     With TForm(wFormulario) do
         Try
             ShowModal;
         Finally
              Release;
     End;

Samuel N. Junior

unread,
Nov 16, 2010, 6:55:37 AM11/16/10
to dug...@googlegroups.com
Acho que o problema não está na criação do formulário e sim na maneira com que os campos TFields estão sendo criados dentro ele.

A função que cria e libera os formulários funciona perfeitamente e mesmo quando eu crio esse formulário específico com o código abaixo recebo a mensagem de erro na liberação do formulário:

    Try
        Conta_Contabil := TConta_Contabil.Create(Application);
        Conta_Contabil.ShowModal;
    Finally
         Conta_Contabil.Free;
    End;

O erro "Invalid Pointer Operation" só ocorre se eu criar dentro desse formulário os campos TFields como eu fiz. Se eu retirar essa rotina de criação dos campos o formulário é liberado sem problemas.

Acredito que o problema esteja na maneira com que estou criando os campos TFields antes de criar os campos calculados (em vermelho):


Alguma outra sugestão ?

[ ]s


Samuel Natali Junior
NatSam Consultoria Ltda
Fone: (11) 4688-1000
Skype: Hipercusto
www.natsam.com.br
www.hipercusto.com.br
Conheça o Hipercusto e veja como ele pode ajudar sua indústria.
Faça um download e avalie o produto gratuitamente durante 20 dias.


www.tr7.com.br o seu site de buscas na web


Em 16/11/2010 09:00, Secaio escreveu:
wFormulario := TwFormulario.Create(Application);
 

Secaio

unread,
Nov 16, 2010, 7:12:18 AM11/16/10
to dug...@googlegroups.com
hummmm pq tu nao usa clientDataSet entao pra armazenar essa tua tabela em memoria ??? 

2010/11/16 Samuel N. Junior <sam...@natsam.com.br>
Acho que o problema não está na criação do formulário e sim na maneira com que os campos TFields estão sendo criados dentro ele.

--
Você recebeu esta mensagem porque está inscrito no "DUG-RS -
Delphi Users Group Rio Grande do Sul" em Grupos do Google.
Acesse o nosso BLOG em http://www.dug-rs.org e contribua com a comunidade Delphi do Rio Grande do Sul
Para postar neste grupo, envie um e-mail para dug...@googlegroups.com
Para cancelar a sua inscrição neste grupo, envie um e-mail para
dug-rs-un...@googlegroups.com
Para ver mais opções, visite este grupo em
http://groups.google.com.br/group/dug-rs?hl=pt-BR
Twitter: @dugrs



--
Secaio´s Corporation

Samuel N. Junior

unread,
Nov 16, 2010, 7:39:28 AM11/16/10
to dug...@googlegroups.com
Porque todo meu sistema está estruturado com base nos componentes do NexusDB e ao usar o TnxMemTable eu posso conectar componentes TnxSQL na mesma e trabalhar com scripts SQL sempre que necessário.



Samuel Natali Junior
NatSam Consultoria Ltda
Fone: (11) 4688-1000
Skype: Hipercusto
www.natsam.com.br
www.hipercusto.com.br
Conheça o Hipercusto e veja como ele pode ajudar sua indústria.
Faça um download e avalie o produto gratuitamente durante 20 dias.


www.tr7.com.br o seu site de buscas na web


Jair Roberto Silva

unread,
Nov 16, 2010, 8:40:14 AM11/16/10
to dug...@googlegroups.com
Olá, Samuel!

Observando essa parte do seu código:

No evento OnClose:

Begin
     TnxMemTable1.close;
     If Assigned(Campo) Then
         Campo.Free;
     If Assigned(wcbTotal) Then
         wcbTotal.Free;
end;


É possível que o NexusDB libere os campos da tabela criada em memória no método "Close". Nesse caso as variáveis que contém os campos que você tenta liberar posteriormente passam a ter ponteiros inválidos. Experimente liberar os campos antes de fechar a tabela.

Begin
     If Assigned(Campo) Then
         Campo.Free;
     If Assigned(wcbTotal) Then
         wcbTotal.Free;
     TnxMemTable1.Close;
end;


Também não entendi a utilidade da variável "Campo", pelo menos analisando esse pedaço do código que você enviou. Considerando a parte do código onde você usa essa variável, não vi utilidade para ela:

       for Cont:=0 to TnxMemTable1.FieldDefs.Count-1 do 
            Campo:=TnxMemTable1.FieldDefs[Cont].CreateField(TnxMemTable1); 

Ela irá conter somente o último campo criado. Você não faz nenhuma checagem e nem referencia essa variável em outra parte dessa rotina. Provavelmente você referencie a outra variável "wcbTotal" em outros momentos, já que ela está atrelada diretamente a um campo específico da tabela criada em memória, porém a variável "Campo" não me parece ter a mesma utilidade. Nesse caso bastaria você rodar o método CreateField:

       for Cont:=0 to TnxMemTable1.FieldDefs.Count-1 do 
            TnxMemTable1.FieldDefs[Cont].CreateField(TnxMemTable1); 


Um abraço,
Jair

Samuel N. Junior

unread,
Nov 16, 2010, 8:54:37 AM11/16/10
to dug...@googlegroups.com
Jair.

Colocar o "Campo" foi uma tentativa de resolver o problema, o comando estava sem ele antes. Colocar o TnxMemTable1.Close depois de eliminar as variáveis também não resolveu o problema.

Aliás, o loop For..to..FieldDefs.Count só existe por causa do campo calculado que eu preciso usar.

Para contornar esse problema (=gambiarra) eu criei o campo calculado como um campo normal da tabela e sempre que atualizo os outros campos eu uso o evento OnCellChanged para calcular o total (em vez de usar o OnCalcFields), só que essa solução que arrumei está me incomodando e por isso quero descobrir o que pode estar errado no código.

[ ]s


Samuel Natali Junior
NatSam Consultoria Ltda
Fone: (11) 4688-1000
Skype: Hipercusto
www.natsam.com.br
www.hipercusto.com.br
Conheça o Hipercusto e veja como ele pode ajudar sua indústria.
Faça um download e avalie o produto gratuitamente durante 20 dias.


www.tr7.com.br o seu site de buscas na web


Jair Roberto Silva

unread,
Nov 16, 2010, 9:31:23 AM11/16/10
to dug...@googlegroups.com
Não trabalho com o NexusDB, Samuel, por isso não tenho como realizar testes mais específicos, mas ajudaria se você conseguisse identificar através do debugger do Delphi qual linha do código está disparando a exceção.


Um abraço,
Jair

Samuel N. Junior

unread,
Nov 16, 2010, 9:50:32 AM11/16/10
to dug...@googlegroups.com
O erro ocorre no _FreeMem do TObject (System.pas):

procedure TObject.FreeInstance;
begin
  CleanupInstance;
  _FreeMem(Self);
end;


Em anexo está todo o log de erro.

Será algum bug do componente ?

[ ]s

Erro.zip

Felipe Dal Pizzol

unread,
Nov 16, 2010, 10:04:37 AM11/16/10
to dug...@googlegroups.com
cara,

nao consegui ler todos os emails, mas tu chegou a fazer um loop para
limpar os campos da tabela?
  Ex: for i := fields.count-1 downto 0 do fields[i].free

Eu vi que em determinado momento tu da um campo.free, mas nao
percorre todos os campos....

Outra coisa, porque criar variaveis globais (Campo: TField; wcbTotal: TFloatField;)
se tu estas criando o campo e adicionando eles a
um container. Usa variavel local, cria, adiciona e ja era
essa variavel ja nao serve pra mais nada. Se tu der varios
creates em cima da mesma variavel global, tu só vai ter a
ultima instancia dele...


Quem sabe isso ajuda em alguma coisa?!!?

Abraço, DAl'Pizzol.
--

Bons treinos... HAPKI!!!


--
Você recebeu esta mensagem porque está inscrito no "DUG-RS -
Delphi Users Group Rio Grande do Sul" em Grupos do Google.
Acesse o nosso BLOG em http://www.dug-rs.org e contribua com a comunidade Delphi do Rio Grande do Sul
Para postar neste grupo, envie um e-mail para dug...@googlegroups.com
Para cancelar a sua inscrição neste grupo, envie um e-mail para
dug-rs-un...@googlegroups.com
Para ver mais opções, visite este grupo em
http://groups.google.com.br/group/dug-rs?hl=pt-BR
Twitter: @dugrs



--
Felipe Dornelles Dal'Pizzol

Felipe Dal Pizzol

unread,
Nov 16, 2010, 10:08:46 AM11/16/10
to dug...@googlegroups.com
aH E PARA complementar a resposta, se tu tiver essa tabela ligada a um datasource,
que contem os campos ligados a alguns objetos de edição, no momento que tu da o
teu Campo.Free, certamente o invalid pointer operation vai surgir.... nesse caso, ja
ajuda se tu "desligar" o teu datasource....

Carlos Henrique Agnes

unread,
Nov 16, 2010, 10:39:45 AM11/16/10
to dug...@googlegroups.com
Senhores,

Também não tive tempo de ler todos os e-mails, então, me corrijam se eu estiver errado, mas os campos estão sendo criados com owner, ou seja, não é necessário liberá-los da memória, esta função já foi delegada, pra falar a verdade eles já devem estar destruídos para este erro acontecer. Samuel, tu tem como verificar se realmente está ficando algum memory leak ?

Valeu,
Tatu!
Carlos Henrique Agnes
Analista de Sistemas
Aquasoft Tecnologia da Informação
http://www.aquasoft.com.br
(51) 3022.3188 - Porto Alegre/RS - Brasil
Parceira Embarcadero no RS

Jair Roberto Silva

unread,
Nov 16, 2010, 10:47:09 AM11/16/10
to dug...@googlegroups.com
Samuel:

Não é bug. Quando foi dado o Free nesse objeto que gerou o erro, o endereço de memória para onde ele apontava já tinha sido liberado pelo Fields.Clear, executado anteriormente.

Pela pilha de chamadas é possível ver que a exceção foi disparada pelo método Free de um TField. Considerando a parte do código que você divulgou, foi no "Campo.Free", o que confere com o meu raciocínio.

Quando você chama TnxMemTable1.Close, por ser criada em memória, a tabela é destruída. Ao ser destruída, todos os TCollectionItens ligados à tabela também são destruídos, como é o caso dos Fields, items da coleção FieldDefs. Ocorre que você tem duas variáveis referenciando campos da tabela: "Campo" e "wcbTotal". Nesse caso, não importa onde você chame o método Free dessas campos, ocorrerá o erro de ponteiro. Se você der Free no Campo antes do Close da tabela, o Close da tabela vai gerar o erro de ponteiro, pois tentará liberar o Campo que já foi liberado. Se for ao contrário, como está no seu código, quando você der Free no Campo, ele já terá sido liberado pelo Close da tabela. Isso ocorre porque as variáveis está referenciando ponteiros para campos que integram a tabela, sendo que a tabela já se encarrega de destruir esses objetos, liberando a memória alocada para eles. Portanto bastaria você executar o método "Close" da tabela. Se quiser, pode atribuir "nil" às variáveis TField. Só não deve usar o método Free nas variáveis que aponta para os campos porque a tabela já terá feito isso no método Close.


Um abraço,
Jair



Felipe Dal Pizzol

unread,
Nov 16, 2010, 11:02:17 AM11/16/10
to dug...@googlegroups.com
tens razão quanto ao Owner, grande guru!!! aí provamos que nao li todos emails (e nem todas linhas até o final);

abraço.

Samuel N. Junior

unread,
Nov 16, 2010, 11:21:39 AM11/16/10
to dug...@googlegroups.com
Antes de mais nada, obrigado a todos que enviaram e-mails & sugestões.

"Condensando" os últimos e-mails :-)

##Felipe:
#######
Sim, eu tentei fazer um loop para limpar os campos e a hora que tento limpar campo calculado também recebo o erro Invalid Pointer Operation.

As variáveis foram criadas como local e como recebi o erro mudei para global na tentativa de resolver o problema.

Com relação ao segundo e-mail que você enviou, a única coisa "ligada" ao Datasource é um DBGrid e também já tentei setar o DataSource := Nil antes de fechar o formulário mas recebi o mesmo erro.

##Carlos:
########
O comando Free nas variáveis foi colocado na tentativa de resolver o problema. Com ou sem ele recebo o mesmo erro quando encerro o formulário

##Jair:
#####
Destruindo ou não as variáveis eu recebo o erro Invalid Pointer Operation.


##TODOS:
#########
Acredito que o problema esteja na criação do campo calculado, pois se eu não incluí-lo na tabela o formulário é encerrado normalmente.

Alguém conhece  alguma outra maneira para criar um campo calculado em uma tabela além da que eu usei (abaixo) ?

var
  wcbTOTAL: TFloatField;
:
Begin
    :
    wcbTOTAL := TFloatField.Create(
TnxMemTable1);
    wcbTOTAL.FieldName := 'cbTOTAL';
    wcbTOTAL.FieldKind := fkCalculated;
    wcbTOTAL.Calculated := True;
    wcbTOTAL.DataSet :=
TnxMemTable1;
    Fields.Add(wcbTOTAL);
    :
       
[ ]s

Samuel Natali Junior
NatSam Consultoria Ltda
Fone: (11) 4688-1000
Skype: Hipercusto
www.natsam.com.br
www.hipercusto.com.br
Conheça o Hipercusto e veja como ele pode ajudar sua indústria.
Faça um download e avalie o produto gratuitamente durante 20 dias.


www.tr7.com.br o seu site de buscas na web


Samuel N. Junior

unread,
Nov 16, 2010, 12:50:46 PM11/16/10
to dug...@googlegroups.com
Parece que descobri o problema,

Na criação do campo calculado bastou eu tirar o Fields.Add (em vermelho) e o formulário agora é encerrado normalmente:

var
  wcbTOTAL: TFloatField;
:
Begin
    :
    wcbTOTAL := TFloatField.Create(
TnxMemTable1);
    wcbTOTAL.FieldName := 'cbTOTAL';
    wcbTOTAL.FieldKind := fkCalculated;
    wcbTOTAL.Calculated := True;
    wcbTOTAL.DataSet :=
TnxMemTable1
;
    Fields.Add(wcbTOTAL); <---- Eliminei essa linha
    :


Espero que isso não traga nenhum "efeito colateral" no sistema :-)

Obrigado a todos.

[ ]s


Samuel Natali Junior
NatSam Consultoria Ltda
Fone: (11) 4688-1000
Skype: Hipercusto
www.natsam.com.br
www.hipercusto.com.br
Conheça o Hipercusto e veja como ele pode ajudar sua indústria.
Faça um download e avalie o produto gratuitamente durante 20 dias.


www.tr7.com.br o seu site de buscas na web

Jair Roberto Silva

unread,
Nov 17, 2010, 7:46:51 AM11/17/10
to dug...@googlegroups.com
Pois é, Samuel... é uma daquelas coisas que estão ali mas passam despercebidas. A explicação é que, como estava, o mesmo campo era incluído duas vezes na coleção de campos da tabela:
:
with TnxMemTable1 do
begin
  :
  wcbTOTAL := TFloatField.Create(TnxMemTable1);
  :
  wcbTOTAL.DataSet := TnxMemTable1;
  :
  Fields.Add(wcbTOTAL);
end;

Não tive tempo para investigar o suficiente para concluir se a definição da tabela como Owner na criação do campo já o insere na lista de campos, mas isso ocorre ao definir a propriedade "DataSet" do campo.

Aproveitei para pesquisar na Internet sobre o tema e encontrei duas abordagens diferentes a esse tipo de necessidade:

      TaxAmount := TFloatField.Create(MyTable);
      with TaxAmount do
      begin
        FieldName := 'TaxAmount';
        Calculated := True;
        Currency := True;
        DataSet := MyTable;
        Name := MyTable.Name + FieldName;
        MyTable.FieldDefs.Add(Name, ftFloat, 0, false);
      end;


var
T: TFloatField;
begin
SQLDataSet1.Close;
T := TFloatField.Create(SQLDataSet1);
T.Precision := 2;
T.FieldName := 'Amount';
T.Name := SQLDataSet1.Name + T.FieldName;
T.Index := SQLDataSet1.FieldCount;
T.DataSet := SQLDataSet1;
SQLDataSet1.FieldDefs.UpDate;
SQLDataSet1.Open;
end;

Pela primeira abordagem parece necessário incluir o novo campo na coleção FieldDefs (não em Fields) antes de criar a tabela.
Pela segunda abordagem, disponível no docwiki da própria Embarcadero, é possível deduzir que o método Update da coleção FieldDefs já se encarrega de incluir o novo campo nessa coleção.

Acredito que você poderia testar essas possibilidades para ver se continua funcionando.


Um abraço,
Jair
Reply all
Reply to author
Forward
0 new messages