Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > Получить адрес функции у класса


Автор: Ak47black 1.12.2007, 16:03
Здравствуйте.
Меня интересует как правильно получить адрес функции у класса.
(Я хочу потом использовать функцию у класса как CallBack_function, поэтому для этого мне нужно получить адрес)
Например вот такой код
Код

unit Unit2;

interface

type
  TTest = class
  public
    constructor Create;
    function Sample: Integer;
  end;

implementation

function TTest.Sample: Integer;
begin

end;

constructor TTest.Create;
var
  i: Cardinal;
begin
  i:= @Sample; // Тут ошибка Variable required
end;

end.


Подскажите пожалуйста как тут правильно сделать?  smile 

Автор: Alexeis 1.12.2007, 16:23
  Можно присвоить метод переменной типа метод, а потом извлечь адрес функции.

Автор: Ak47black 1.12.2007, 16:41
Alexeis, ага так вышло.
Ну всетаки почему напрямую нельзя получить, то до меня недоходит  smile .

Добавлено @ 16:53
Alexeis, а такой код который ниже реально сделать вообще?
Код

  CallBackProc:= waveOutProc;
  WaveOutOpen(@hwo, WAVE_MAPPER, @wfx, Cardinal(@CallBackProc), 0, callback_function);

waveOutProc - функция принадлежащая классу.
При выполнении получаю ошибку
Цитата

Project Test.exe raised exception class EAccessViolation with message 'Access violation at address 0014DB60. Write of address FFFFFC43'.

Пытаюсь процедуру обработки привязать к классу  smile .

Автор: MetalFan 1.12.2007, 21:26
низя метод назначить в кач.колбэка апи функции

Автор: Alexeis 1.12.2007, 22:18
Цитата(MetalFan @  1.12.2007,  21:26 Найти цитируемый пост)
низя метод назначить в кач.колбэка апи функции 

  Почему? Метод фактически не отличается от функции. Есть свои особенности, метод реализуется как функция.

Автор: OdesitVadim 1.12.2007, 23:15
Вся загвоздка в том, что в методах есть неявный параметр, токорый указывает на объект. Вам то это не видно, а линковщик, когда собирает, обнаруживает несоответсвие параметров.

Автор: Ak47black 1.12.2007, 23:27
Хмм поэтому.
Вообщем у меня у цель, то сделать класс, при использовании которого, каждый экземпляр которого имел-бы свою CallBack функцию и проигровали звук независимо друг от друга.
Как-бы тут сделать может какие идеи есть?

Автор: Alexeis 2.12.2007, 00:31
Ak47black, так что тот 
Цитата(OdesitVadim @  1.12.2007,  23:15 Найти цитируемый пост)
Вам то это не видно, а линковщик, когда собирает, обнаруживает несоответсвие параметров. 

  Ну эту проблему как раз обойти не сложно, если постараться, то функции можно впарить чего угодно. Можно попробовать такой способ

Код

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure WaveOutProc(hwo : HWAVEOUT; uMsg : UINT; dwInstance : PDWORD;
                          dwParam1 : PDWORD); stdcall;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.WaveOutProc(hwo : HWAVEOUT; uMsg : UINT; dwInstance : PDWORD;
                             dwParam1 : PDWORD);
var
  dwParam2 : PDWORD;
Begin
  dwParam2 := dwParam1; dwParam1 := dwInstance; dwInstance := Pointer(uMsg);
  uMsg := hwo; hwo := Integer(self); Self := TForm1(dwInstance);

end;

procedure TForm1.FormCreate(Sender: TObject);
var
  tmp : Pointer;
begin
  waveOutOpen(tmp, 0, tmp, Cardinal(@TForm1.WaveOutProc), Cardinal(self), 0);
end;


Суть его в том что винда думает будто у функции 5 параметров, каждый из которых DWORD у нас так е есть 5м параметром будет Self. Винда конечно же не знает что мы там ждем указатель на объект и пихает туда первый параметр, потому сразу после вызова нужно либо расставить параметры по своим местам, либо переименовать так чтобы не запутаться. Теперь обязательный момент. waveOutOpen разрешает калбэк передать дополнительный параметр, в нем передаем указатель на реальный объект, после вызова в Self мы должны записать это значение см Self := TForm1(dwInstance); Теперь по идее все на своих местах. Просили 5 параметров типа DWORD мы их вам дали по модели stdcall.

  Сомнение у меня вызывает только, то что по модели stdcall параметры передаются справа-налево, если это касается и указателя на класс, то проблем не должно возникнуть.

Автор: Ak47black 2.12.2007, 11:35
Alexeis, с этим то кодом Erorr у меня выходит
Цитата

---------------------------
Project Project1.exe raised exception class EAccessViolation with message 'Access violation at address 0042D2B0 in module 'Project1.exe'. Read of address FFFFFFEC'. Process stopped. Use Step or Run to continue.
---------------------------

С такими параметрами и недолжно работать по идее. 
Цитата

что в методах есть неявный параметр


Автор: Alexeis 2.12.2007, 14:20
Цитата(Ak47black @  2.12.2007,  11:35 Найти цитируемый пост)
С такими параметрами и недолжно работать по идее. 

  Ну я понадеялся, что хватит сообразительности вместо пустых параметров поставить реальные  smile 

Автор: Ak47black 2.12.2007, 14:58
Alexeis, а мне кажется что error изза того что стёк портится.

Добавлено через 2 минуты и 41 секунду
При вызове waveOutProc все параметры сдвинуты на 1 влево.

Добавлено через 3 минуты и 47 секунд
И эррор появляется только после первого выполнения waveOutProc.

Автор: Ak47black 2.12.2007, 15:37
Хотя вроде нет
Вот так вызов происходит в Winmm.dll
Код

76B454E6   FF75 20          PUSH DWORD PTR SS:[EBP+20]
76B454E9   53               PUSH EBX
76B454EA   FF75 18          PUSH DWORD PTR SS:[EBP+18]
76B454ED   FF75 14          PUSH DWORD PTR SS:[EBP+14]
76B454F0   56               PUSH ESI
76B454F1   FFD7             CALL EDI

А вот сама пустая процедура CallBack 
Код

0044D938  /. 55             PUSH EBP
0044D939  |. 8BEC           MOV EBP,ESP
0044D93B  |. 83C4 F0        ADD ESP,-10
0044D93E  |. 894D F4        MOV DWORD PTR SS:[EBP-C],ECX
0044D941  |. 8955 F8        MOV DWORD PTR SS:[EBP-8],EDX
0044D944  |. 8945 FC        MOV DWORD PTR SS:[EBP-4],EAX
0044D947  |. 8BE5           MOV ESP,EBP
0044D949  |. 5D             POP EBP
0044D94A  \. C2 0C00        RETN 0C

Чтото я запутался тут  smile 
Какиенибудь идеи есть?
И еше при вызове  WaveOutOpen, парметры ненужно смешать, я пробовал waveOutProc как внизу 
Код

WaveOutOpen(@hwo, WAVE_MAPPER, @wfx, Cardinal(@PWaveOutProc), 0, CALLBACK_FUNCTION);

waveOutProc вызывается один раз, после прохождения получаю error :|

Автор: Alexeis 2.12.2007, 17:31
Епрст ну что за 0! я же говорил там должен быть указатель на класс!

Автор: Ak47black 2.12.2007, 18:37
Эммм это в моем случае 0? Где?  smile 

Автор: Ak47black 2.12.2007, 19:04
Пытаюсь понять что именно вызывает ошибку чтобы както предотвратить.
Покопав понял, что изза того что стек портиться прога прыгает(возвращается) на 00000000 адрес
Код

76B454E6   FF75 20     PUSH DWORD PTR SS:[EBP+20] // Запоняем стёк параметрами
76B454E9   53             PUSH EBX
76B454EA   FF75 18     PUSH DWORD PTR SS:[EBP+18]
76B454ED   FF75 14     PUSH DWORD PTR SS:[EBP+14]
76B454F0   56              PUSH ESI
76B454F1   FFD7          CALL EDI // тут вызывается CallBack функция в коде программы 0044D938
76B454F3   33C0             XOR EAX,EAX
76B454F5   40               INC EAX
76B454F6   5F               POP EDI
76B454F7   5E               POP ESI
76B454F8   5B               POP EBX
76B454F9   5D               POP EBP
76B454FA   C2 1C00          RETN 1C // Тут изза того что стёк испортился прыгает(возвращается) на EIP 00000000

Я предполагаю что вот этот лишний параметр както его и портит, только как?

Автор: Alexeis 2.12.2007, 19:50
WaveOutOpen(@hwo, WAVE_MAPPER, @wfx, Cardinal(@PWaveOutProc), 0, CALLBACK_FUNCTION);
сразу за PWaveOutProc

Автор: Ak47black 2.12.2007, 20:12
Тут просто неудачно назвал код то такой вышел у меня
Код

constructor TSoundPlayObject.Create;
var
  ...
  WaveOutProc2: TwaveOutProc;
begin
...
  WaveOutProc2:= Self.WaveOutProc;
  WaveOutOpen(@hwo, WAVE_MAPPER, @wfx, Cardinal(@WaveOutProc2), Cardinal(@Self), CALLBACK_FUNCTION);

Код

  TwaveOutProc = procedure (hwo: HWAVEIN; uMsg: UINT; dwInstance: DWORD; dwParam1: DWORD; dwParam2: DWORD) of object; stdcall;


Добавлено через 13 минут и 14 секунд
При выполнении этого кода получаю один вызов Self.WaveOutProc (это CallBack функция) с параметрами 
Цитата

hwo: 955
uMsg: 1244680
dwInstance: 0
dwParam1: 0
dwParam2: 1926305213

Как видно 955 должно быть у uMsg, а не у hwo.
955 соотвествует MM_WOM_OPEN.
Шас после двойного вызова Self.WaveOutProc получаю ошибку
Цитата

Project Project2.exe raised exception class EAccessViolation with message 'Access violation at address 0014DBB8. Write of address 00000001'. Process stopped. Use Step or Run to continue.

Автор: Alexeis 2.12.2007, 20:49
Cardinal(@Self) - не нужно брать адрес. Self и так адрес

Добавлено через 54 секунды
Ak47black, приатач тестовый проект, проще будет разобраться.

Автор: Ak47black 2.12.2007, 20:53
Alexeis, лан тут наверно нереально сделать.
Попробую через по другому какому-нибудь пути сделать, спасибо всем за участвие.

Автор: Alexeis 3.12.2007, 14:56
Цитата(Ak47black @  2.12.2007,  20:53 Найти цитируемый пост)
Alexeis, лан тут наверно нереально сделать.

  Если так быстро будешь сдаваться, то плохой из тебя выйдет программист.
Вот полностью рабочий код
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, MMSystem, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }

    Hdr  : TWaveHdr;
    WO   : HWAVEOUT;
    Data : Pointer;
    Size : Integer;

    procedure WaveOutProc(uMsg : UINT; dwInstance : PDWORD;
                          dwParam1 : PDWORD; dwParam2 : PDWORD); stdcall;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.WaveOutProc(uMsg : UINT; dwInstance : PDWORD;
                             dwParam1 : PDWORD; dwParam2 : PDWORD);
{var
  hwo : HWAVEOUT;  }

Begin // hwo := Integer(self); //åñëè ïàðàìåòð íóæåí
  Self := TForm1(dwInstance);
  if uMsg = WOM_DONE
  then
    Begin
      waveOutUnprepareHeader(WO, @Hdr, SizeOf(Hdr));
      waveOutClose(WO);
      FreeMem(Data, Size);
    end;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
    Fmt : tWAVEFORMATEX;
    Ch  : ^SmallInt;
    i   : Integer;
begin
  Size := 88200;
  GetMem(Data, Size);
  Ch := Data;
  For i := 0 to 22050 do
   begin
    Ch^ := SmallInt(Round(32000 * Sin(20 / pi * i)));
    Ch  := Ptr(Cardinal(Ch) + 2);
    Ch^ := SmallInt(Round(32000 * Sin(20 / pi * i + pi/4)));
    Ch  := Ptr(Cardinal(Ch) + 2);
   end;

  ZeroMemory(@Fmt, SizeOf(Fmt));
  ZeroMemory(@Hdr, SizeOf(Hdr));

  Fmt.wFormatTag      := WAVE_FORMAT_PCM;
  Fmt.nChannels       := 2;
  Fmt.nSamplesPerSec  := 22050;
  Fmt.wBitsPerSample  := 16;
  Fmt.cbSize          := SizeOf(Fmt);
  Fmt.nBlockAlign     := 4;
  Fmt.nAvgBytesPerSec := 88200;

  waveOutOpen(@WO, 0, @Fmt, Cardinal(@TForm1.WaveOutProc),
                 Cardinal(self), CALLBACK_FUNCTION)

  Hdr.lpData         := Data;
  Hdr.dwBufferLength := Size;

  waveOutPrepareHeader(WO, @Hdr, SizeOf(Hdr));
  waveOutWrite(WO, @Hdr, SizeOf(Hdr));
end;

end.


Автор: Ak47black 3.12.2007, 17:06
Ёмаё @ ненадо оказывается.  smile 
Спасибо тебе большое, я уже думал что просто неподерживается просто с классами.  smile 

Автор: Ak47black 3.12.2007, 17:41
Хмм ты еше и
Код

    procedure WaveOutProc(uMsg : UINT; dwInstance : PDWORD;
                          dwParam1 : PDWORD; dwParam2 : PDWORD); stdcall;

Поменял.

Автор: Alexeis 3.12.2007, 17:50
  Да чтобы не нужно было сдвигать параметры.

Автор: Ak47black 3.12.2007, 18:01
 smile Ну ок вроде роботает.
Надеюсь будет работать стабильно.

Автор: MetalFan 3.12.2007, 18:10
Ak47black, а куда оно денеца?

Автор: Alexeis 3.12.2007, 18:24
MetalFan, есть куда. Этот метод вызывается в чужом потоке, потому круг действий которые можно в нем производить ограничен, либо нужно синхронизировать, так что шанс заглючить еще есть  smile 

Автор: MetalFan 3.12.2007, 18:28
Цитата(Alexeis @  3.12.2007,  18:24 Найти цитируемый пост)
в чужом потоке

ого) пусть автор про это не забудет)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)