GARANTIR DESCONTO

Fórum Sobre ponteiros de métodos #275913

06/04/2005

0

pessoal como faço para criar um ponteiro de método (função) sem deixar definido seus parâmetros, ou seja posso passar qualquer parâmetro para esse ponteiro indiferente de seus métodos?


Bruno Belchior

Bruno Belchior

Responder

Posts

06/04/2005

Massuda

Não tem como fazer isso... Pascal (e consequentemente, Delphi) é uma linguagem fortemente tipada, de modo que os parametros que você passa para uma function/procedure tem do mesmo tipo dos argumentos que você declarou.


Responder

Gostei + 0

06/04/2005

Bruno Belchior

obrigado massuda... era o que eu imaginei... pois é o mesmo que acontece com os eventos...


Responder

Gostei + 0

06/04/2005

Michael

Olá colega!

Uma solução seria vc criar um parâmetro do tipo array do tipo Variant. Veja:

function MinhaFuncao(Matriz : array of Variant) : boolean;
(...)

O problema das Variants é que elas consomem mais recursos do que se uma variável tipada.

[]´s


Responder

Gostei + 0

06/04/2005

Bruno Belchior

Olá colega! Uma solução seria vc criar um parâmetro do tipo array do tipo Variant. Veja: function MinhaFuncao(Matriz : array of Variant) : boolean; (...) O problema das Variants é que elas consomem mais recursos do que se uma variável tipada. []´s
obrigado colega... porém o problema vai um pouquinho além disso, não quero utilizar um parâmetro que não tem um tipo (no caso array of variants) mas sim um [b:064db646bd]ponteiro[/b:064db646bd] para um método, assim como um evento faz internamente, porém isso só é possível quando temos um tipo (classe) de ponteiro de método previamente implementado, na realidade a intenção era criar uma classe de ponteiro que suportasse receber qualquer tipo de método, independente da quantidade de parâmetros...


Responder

Gostei + 0

06/04/2005

Beppe

Vc pode tratar uma rotina ou método como ponteiro:
procedure Oi(A, B: Integer);
procedure Ola(A, B: String);
...
P: Pointer;
...
P := @Oi;
...
P := @Ola;

O problema que se vc não souber exatamente a assinatura(parâmetros e seus tipos) da rotina, não poderá chamar via ponteiro.


Responder

Gostei + 0

07/04/2005

Yallebr

O problema que se vc não souber exatamente a assinatura(parâmetros e seus tipos) da rotina, não poderá chamar via ponteiro.
Correto mas como eu iria [b:541be4a84b]chamá-la em outra classe[/b:541be4a84b] por exemplo?
Ou seja o método que passarei como ponteiro esta em uma classe, porém será outra classe quem vai executá-lo...


Responder

Gostei + 0

07/04/2005

Beppe

mas como eu iria [b:8ce7017788]chamá-la em outra classe[/b:8ce7017788] por exemplo?

Isto é simples, dado que um método é apenas uma rotina com um parâmetro implícito. Por exemplo, os dois métodos são exatamente iguais quanto à síntese:
procedure Button1Click(Self: TForm1; Sender: TObject);
begin
  ShowClass(Self);
  ShowClass(Sender);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowClass(Self);
  ShowClass(Sender);
end;

Mas para chamar um método vc precisa mais do que apenas um ponteiro, precisa também do objeto sobre o qual o método agirá. Para isso, use TMethod.
var
  M: TMethod;
begin
  M.Code := @TForm1.Button1Click;
  M.Data := Form1;
  TNotifyEvent(M)(Sender);
end;

Ou ainda
type
  T2P = procedure(Self: TForm1; Sender: TObject);
var
  P: Pointer;
begin
  P := @TForm1.Button1Click;
  T2P(P)(Form1, Sender);
end;

As duas formas se equivalem, a diferença está no trato de Self(como método ou simples subrotina).


Responder

Gostei + 0

07/04/2005

Nildo

Olha... você pode criar uma função sem parametros, e dentro dela você pode ler quantos itens você quiser da Stack, que serão PUSHados antes de você chamar a função. Mas deve ter um auxilio do Assembly InLine. Aqui vai um exemplo onde eu passo 2 parâmetros para uma função declarada sem parametros, e os leio dentro dela

function SuaFuncao: Integer; stdcall;
var
   _ESP: Pointer;

   Param1: Integer;
   Param2: Integer;
begin
   asm
      mov _ESP, ESP
   end;

   Param1 := PInteger( Integer( _ESP ) + ( 12 * 4 ) )^;
   Param2 := PInteger( Integer( _ESP ) + ( 11 * 4 ) )^;

   ShowMessage( IntToStr( Param1 ) );
   ShowMessage( IntToStr( Param2 ) );
end;


E para testar, chame-a da seguinte maneira:

procedure TForm1.Button1Click(Sender: TObject);
begin
   asm
      push eax

      push 1
      push 2
      call SuaFuncao
      pop eax
      pop eax

      pop eax
   end;

end;


Partindo deste princípio você pode criar um parametro que seria a contagem dos parametros, daí você faz um FOR e sai lendo os itens da Stack. É complicado isso, nem é muito legal usar, mas foi só para mostrar que é possível :wink:


Responder

Gostei + 0

07/04/2005

Beppe

nildo, uma correção quanto aos offsets: os argumentos começam a partir de 3 * 4. Isto se deve as 3 locais da função. Mas desta forma vc deve se privar de usar tratamento de exceções e strings(try/finally implícito envolvendo seu código). E possíveis variáveis temporárias que o Delphi criar para a função, complica enormemente, na minha opinião.


Responder

Gostei + 0

07/04/2005

Massuda

...É complicado isso, nem é muito legal usar, mas foi só para mostrar que é possível
Acho que esse é o ponto mais importante deste tópico... só quem já teve que procurar a causa de um stack corrompido sabe o pesadelo que são esses truques relacionados com manipulação de stack e conversão de ponteiros de método para ponteiros de procedure/função.

Se for para programar dessa forma, estaríamos retrocedendo para os tempos onde todas as variáveis tinham que ser globais e subrotinas não podiam receber parametros (algo como retornar aos anos 60?).


Responder

Gostei + 0

07/04/2005

Nildo

nildo, uma correção quanto aos offsets: os argumentos começam a partir de 3 * 4. Isto se deve as 3 locais da função. Mas desta forma vc deve se privar de usar tratamento de exceções e strings(try/finally implícito envolvendo seu código). E possíveis variáveis temporárias que o Delphi criar para a função, complica enormemente, na minha opinião.


Por isso eu disse que nem vale a pena. Então, eu também achei que seria 3*4, mas quando eu testei deu erro, dai eu debugei pela CPU e ví que na Stack, o Delphi acrescentou uns 8 itens.. Dai pra funcionar (é sempre especifico de função para função) eu peguei o 11º e 12º item da Stack. Pode testar e você vai ver que funciona.

mas eu estranhei o Delphi ter PUSHado um monte de coisa numa função simples... mas fazer o que né! rs


Responder

Gostei + 0

07/04/2005

Beppe

Me parecou apenas curiosidade do brunovicenteb. Se alguém tiver intenção de fazer isto na prática, acho que dá pra chegar numa solução eficiente, dada algumas restrições. A princípio, a solução do NerdeX(ou ainda usar array of const) é a mais geral, e ao menos eu nunca tive o que reclamar dela.


Responder

Gostei + 0

07/04/2005

Nildo

só quem já teve que procurar a causa de um stack corrompido sabe o pesadelo que são esses truques relacionados com manipulação de stack e conversão de ponteiros de método para ponteiros de procedure/função.


É realmente um pesadelo, principalmente quando você tem que aplicar um patch na Stack por conta de um erro estranho na compilação. Mas enfim, depois que você começa a estudar a estrutura do processador você tira isso de letra

Se for para programar dessa forma, estaríamos retrocedendo para os tempos onde todas as variáveis tinham que ser globais e subrotinas não podiam receber parametros (algo como retornar aos anos 60?).


Isso sim eu chamo de [b:022f8d5df1]Programar[/b:022f8d5df1]! Eu gosto disso porque acabo aprendendo a base da coisa.. Como ela funciona internamente.. O que o processador faz quando executa nossos códigos, etc..


Responder

Gostei + 0

07/04/2005

Beppe

Por isso eu disse que nem vale a pena. Então, eu também achei que seria 3*4, mas quando eu testei deu erro, dai eu debugei pela CPU e ví que na Stack, o Delphi acrescentou uns 8 itens.. Dai pra funcionar (é sempre especifico de função para função) eu peguei o 11º e 12º item da Stack. Pode testar e você vai ver que funciona.

Nem precisa testar...estou vendo agora o IntToStr, que cria strings. Se não fossem por eles, neste caso, os offsets seriam os óbvios.


Responder

Gostei + 0

07/04/2005

Thiago Vidal

Já utilizei muitas vezes este método pra chamada de DLLs, principalmente para fazer uma DLL compartilhar de uma TSQLConnection que está no executável, e sempre funcionou sem problemas...

O motivo de o Delphi PUSHar tantos itens no stack, é pq o Delphi passa os parâmetros padrão ´register´ via registradores; para passar via stack, vc deve declarar o método como ´stdcall´. Como por Exemplo:

na Dll:
type
  PSQLConnection = ^TSQLConnection;

function Abrir(cnn: PSQLConnection): LongBool; stdcall;
begin
  with TForm1.Create(Application) do
  try
    SQLDataSet1.Connection := cnn;
    Result := (ShowModal = mrOk);
  finally
    Free;
  end;
end;


no Executável:
procedure TForm1.Button1Click(Sender: TObject);
type
  PSQLConnection = ^TSQLConnection;
var
  DLLHandle: THandle;
  cnn: PSQLConnection;
  Metodo: Pointer;
  Resultado: LongBool;
begin
  DLLHandle := LoadLibrary(´minhadll.dll´);
  if (DLLHandle <> 0) then
  try
    cnn := @SQLConnection1;
    Metodo := GetProcAddress(DLLHandle, ´Abrir´);
    asm
      push cnn
      call Metodo
      mov [Resultado], eax
    end;

    if (Resultado) then
      ShowMessage(´Sucesso!´);
  finally
    FreeLibrary(DLLHandle);
  end;
end;


Realmente não é a forma mais recomendada de se fazer as coisas, mas funciona, e para um sistema como o meu, em que um executável enxuto carrega diversas DLLs, é importante manter apenas 1 conexão com o banco ativa... desde que se faça um rigoroso controle, para evitar que as DLLs tentem acessar a conexão simultaneamente.


Responder

Gostei + 0

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar