Форум: "Основная";
Текущий архив: 2003.06.02;
Скачать: [xml.tar.bz2];
ВнизПередача указателя на функцию Найти похожие ветки
← →
Top Gun (2003-05-11 14:32) [0]У меня есть класс, там в private объявлена функция
function WndProc(Hwn:HWND; Msg,wpr,lpr:cardinal):cardinal;stdcall;
Если я в одном из методов класса пишу:
WindowClass.lpfnWndProc := @(wndproc);
То компилятор сообщает [Error] QuickWinApi.pas(67): Not enough actual parameters
Но мне не нужно вызывать функцию, мне нужно записать в WindowClass.lpfnWndProc ее адрес !
← →
Ihor Osov'yak (2003-05-11 14:40) [1]:= @(ИмяКласса.WndProc);
← →
PVOzerski (2003-05-11 14:59) [2]Только вот использовать метод класса как API-шный callback для окна нельзя, так как помимо перечисленных Вами, метод имеет неявный параметр-ссылку на экземпляр класса. Между прочим, процедурные переменные для методов (напр., procedure of object, tNotifyEvent и т.п.) состоят фактически из двух указателей - на код и на данные. Так что, если я правильно понял то, что Вы хотите сделать, пересмотрите подход. В VCL, например, сообщения обрабатываются через PeekMessage, а callback"ом работает DefWindowProc. Я, в свое время, хаживал и другим путем: прицеплял через SetWindowLong к окошку указатель на экземпляр класса и делал свой универсальный callback для всех окон программы, который вызывал методы-обработчики, добираясь до них через GetWindowLong. А вообще, в VCL у форм есть и свой переопределяемый метод WndProc, которым пользоваться достаточно удобно.
← →
Top Gun (2003-05-11 15:01) [3]Ничего не понимаю. Так ведь функция WndProc у меня реализована
в моем классе. А если будет несколько экземпляров класса, то...
Давайте добавлю код, чтобы стало более понятно:
TMyClass = class
private
WindowClass: TWNDClass;
function WndProc(Hwn:HWND;Msg,wpr,lpr:cardinal):cardinal;stdcall;
constructor create;
end;
.....
constructor TMyClass.create
WindowClass.lpfnWndProc := @(wndproc) ;//Not enough actual parameters
end;
Теперь надеюсь понятно. То есть, в каждом экземпляре TMyCLass есть WindowClass, в свойстве lpfnWndProc которого должен быть записан адрес на функцию wndproc именно этого класса, а не TMyClass.wndproc
← →
PVOzerski (2003-05-11 15:17) [4]А Вы что, полагаете, что каждый экземпляр класса имеет свою копию кода для методов?
← →
Ihor Osov'yak (2003-05-11 15:24) [5]2 PVOzerski © (11.05.03 14:59)
так ведь вопрос был об ошибке синтаксиса. И предполагалось наличие встречного вопроса, типа "а мне нужно не класс вообще, а мой екземпляр" :-)
2 Top Gun (11.05.03 15:01)
Все методы класса получают неявный параметр-указатель на екземпляр класса. Поэтому их нельзя использавать как системные call-back - см. PVOzerski ©
← →
Top Gun (2003-05-11 16:25) [6]Все, понял, что вы имеете в виду. Тогда сделаю так: объявлю функцию wndproc универсальную для каждого экземпляра, а при ее вызове уже буду определять к какому экземпляру относится данное сообщение по параметру Hwn. Верно ? Можно так ?
← →
Ihor Osov'yak (2003-05-11 17:02) [7]wndproc как процедура окна не может быть методом класса.
Как это обходить? Посмотрите хотя бы на
procedure TWinControl.CreateWnd;
function InitWndProc(HWindow: HWnd; Message, WParam: Longint;
LParam: Longint): Longint; stdcall; в модуле controls;
Способов есть много - поищите на поисковиках что-то типа Delphi without VCL, посмотрите также исходники KOL - там также этот вопрос решен и наверное несколько иначе, чем в VCL..
Зы - а поиск по хендлу - не самое оптимальное решение, хотя бы потому, что список этих хендлов нужно хранить.. Лучше будет примерно так, как в VCL - через SetProp привязываем к виндозному окну указатель на свой екземпляр.. Не забудьте потом только о RemoveProp..
Еще раз - исходники unit controls, и особое внимание на использование
WindowAtom, ControlAtom ... Над каждым случаем использования думать долго, до полного просветления.
← →
Top Gun (2003-05-12 18:36) [8]через SetProp привязываем к виндозному окну указатель на свой екземпляр.. Не забудьте потом только о RemoveProp..
Ничего не понял :-(
Я вот все пытался разобраться - не помогает. Меня, наверное, уже ничего не спасет, кроме ваших мудрых советов. В VCL тоже смотрел - тоже не понял
Давайте повторю, что я бы хотел:
Есть класс:
TMyClass = class
private
WindowClass: TWNDClass;
Handle:hwnd;
constructor Create;
end;
В нем содержится класс окна WindowClass и сам handle созданного окна (окно создается в конструкторе TMyClass.Create). Соответственно хочется, чтобы каждый экземпляр класса обрабатывал сообщения своего окна. Конечно, можно написать одну функцию wndproc, которая будет обрабатывать сообщения окон всех экземпляров TMyClass. Но вот в чем беда. В ответ на определенные сообщения класс генерирует события. И если функция wndproc едина, как определять к какому окну какого экземпляра класса TMyClass пришло сообщение и в каком экземпляре генерировать событие ? Вот такая беда у меня !
Если можно - поподробнее, идеально - с примерами! Только не отфутболивайте, очень нужно !
Заранее спасибо!
← →
Skier (2003-05-12 18:42) [9]>Top Gun (12.05.03 18:36)
посмотри тип TWndMethod Не спасёт ?
← →
Serginio (2003-05-12 18:49) [10]Есть очень хорошая функция Помоему MakeObjectInstance которая создает код функции для обращения к функции конкретного экземпляра класса.
← →
Serginio (2003-05-12 18:55) [11]unit Classes;
function MakeObjectInstance(Method: TWndMethod): Pointer;
const
BlockCode: array[1..2] of Byte = (
$59, { POP ECX }
$E9); { JMP StdWndProc }
PageSize = 4096;
var
Block: PInstanceBlock;
Instance: PObjectInstance;
begin
if InstFreeList = nil then
begin
Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Block^.Next := InstBlockList;
Move(BlockCode, Block^.Code, SizeOf(BlockCode));
Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2], @StdWndProc));
Instance := @Block^.Instances;
repeat
Instance^.Code := $E8; { CALL NEAR PTR Offset }
Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
Instance^.Next := InstFreeList;
InstFreeList := Instance;
Inc(Longint(Instance), SizeOf(TObjectInstance));
until Longint(Instance) - Longint(Block) >= SizeOf(TInstanceBlock);
InstBlockList := Block;
end;
Result := InstFreeList;
Instance := InstFreeList;
InstFreeList := Instance^.Next;
Instance^.Method := Method;
end;
{ Free an object instance }
procedure FreeObjectInstance(ObjectInstance: Pointer);
begin
if ObjectInstance <> nil then
begin
PObjectInstance(ObjectInstance)^.Next := InstFreeList;
InstFreeList := ObjectInstance;
end;
end;
← →
浪人 (2003-05-12 19:12) [12]Оконная процедура по умолчанию, получаемая из метода
TWinControl.MainWndProc с помощью MakeObjectInstance,
вызывает метод, хранящийся в свойстве
TControl.WindowProc
(тип - TWndMethod = procedure(var Message: TMessage) of object)
procedure TWinControl.MainWndProc(var Message: TMessage);
begin
try
try
WindowProc(Message);
finally
FreeDeviceContexts;
FreeMemoryContexts;
end;
except
Application.HandleException(Self);
end;
end;
И имеет смысл не менять оконную процедуру, а поменять
это свойство.
Во-первых, поскольку оно именно с этой целью и сделано,
а во-вторых, в некоторых случаях оно вызывается напрямую, а не через оконную процедуру (например, при вызове Perform).
← →
Serginio (2003-05-12 19:29) [13]При использовании MakeObjectInstance желательно получить старый обработчик окна через GetWindowLong и вызывать его в новом обработчике.
← →
Top Gun (2003-05-21 00:05) [14]Ничего не понимаю :-(
У меня нету TWinControl !
Я сделал так:
type
TWndProc=function (Hwn:HWND; Msg,wpr,lpr:cardinal):cardinal;stdcall;
...
В классе объявил поле типа TWndProc:
WndProc:TWndProc;
В конструкторе класса пишу:
WindowClass.lpfnWndProc := @(wndproc) ;
Все равно при этом предупреждение:
"Not enough actual parameters"
Если объявлять
TWndProc=function (Hwn:HWND; Msg,wpr,lpr:cardinal):cardinal of object;stdcall;
Тоже самое. А кстати, чем будет отличаться такие объявление. Of object и без него ? Поле WndProc:TWndProc; все равно могу объявить.
Объясните поподробнее! Как можно справиться с моей проблемой, если кто что понял...
← →
Ihor Osov'yak (2003-05-21 00:42) [15]> Как можно справиться с моей проблемой
почитать не спеша и вдумчиво книжки. Для начала те, кде хорошо описан синтаксис.. В том числе, и работа с процедурными типами, указателями, в тч. на методы, процедуры и функции ..
И не лесть поначалу в такие дебри (поверь, дебри - это поначалу.. Если книжки читать будешь, и бужешь думать - то просветление настанет..)..
Говорят, Марк Кенту хорошие понаписывал...
По существу:
> У меня нету TWinControl
unit Controls;
>WndProc:TWndProc;
В конструкторе класса пишу:
WindowClass.lpfnWndProc := @(wndproc) ;
Ты эта, не делай так.. Здесь под wndproc подразумевается вызов процедуры.. А она параметров хочет..
Должно быть WindowClass.lpfnWndProc := @Здесь_должен_быть_идентификатор_процедуры
> TWndProc=function (Hwn:HWND; Msg,wpr,lpr:cardinal):cardinal of object;stdcall;
Эта декларация не соотв. спецификации оконной процедуры...
>А кстати, чем будет отличаться такие объявление. Of object и без него ?
Насколько я помню, тебе уже это обьясняли. RTFM, одним словом..
← →
Top Gun (2003-05-21 11:06) [16]Говорят, Марк Кенту хорошие понаписывал...
Фигню он написал, по моему (Тейксера и Пачеко намного лучше). Есть его книга - если скажешь в каком параграфе описана моя проблема, обязательно прочитаю.
> У меня нету TWinControl
unit Controls;
Это понятно. Только не могу я доп. модули подключать. Не могу и все. Задача такая.
WindowClass.lpfnWndProc := @(wndproc) ;
Ты эта, не делай так.. Здесь под wndproc подразумевается вызов процедуры.. А она параметров хочет..
Должно быть WindowClass.lpfnWndProc := @Здесь_должен_быть_идентификатор_процедуры
Блин. И что тогда вызовется, если я напишу WindowClass.lpfnWndProc := TWndProc ?!
Мне же нужно, чтобы в каждом экземпляре объекта существовал WindowClass и его свойство lpfnWndProc указывало на процедуру WndProc. Но не на общую WndProc, а на каждую для своего экземпляра.
И вот ты говоришь, что WindowClass.lpfnWndProc := @Здесь_должен_быть_идентификатор_процедуры. А почему когда я пишу прям в dpr файл (чистый WinApi), объявляю там просто функцию WndProc и класс WindowClass:TWndClass. А потом в теле главной функции пишу WindowClass.lpfnWndProc:=WndProc все проходит на ура ?
Насколько я помню, тебе уже это обьясняли. RTFM, одним словом..
Не объясняли...
← →
Top Gun (2003-05-21 11:07) [17]Давайте объясню по другому. Более жизненно. Особенно тем, кто не работал с окнами через WinApi.
Когда создаешь окно функцией CreateWindow нужно указать класс окна. Этот класс сначала надо зарегистрировать в системе функцей RegisterClass, которой в качестве параметра передается структура TWNDCLASS. Одно из полей этой структуры lpfnWndProc указывает на функцию, которая будет обрабатывать все сообщения, приходящие окну данного класса. Например:
var WindowClass:TWndClass;
...
function WndProc(Hwn:HWND; Msg:longworc; wpr:longword; lpr:longint):longword; stdcall;
Begin
//обработка событий окна
End;
BEGIN
WindowClass.lpfnWndProc:=@WndProc;
WindowClass.lpszClassName:="TMyForm";
//Дальнейшее заполнение структуры WindowClass
RegisterClass(WindowClass);
Createwindow("TMyForm",......);
END;
Все работает на ура. Почему-то здесь, когда пишешь WindowClass.lpfnWndProc:=@WndProc никто никаких параметров не требует.
Я хочу оформить каждое окно в класс. Что-то типа того:
TMyForm=class
Handle:HWND;
WindowClass: TWNDClass;
function WndProc(Hwn:HWND;Msg,wpr,lpr:cardinal):cardinal;stdcall;
constructor create;
end;
.....
constructor TMyForm.create;
begin
WindowClass.lpfnWndProc:=@WndProc; //ОШИБКА !
WindowClass.lpszClassName:="TMyForm";
//Дальнейшее заполнение структуры WindowClass
RegisterClass(WindowClass);
Handle:=Createwindow("TMyForm",......);
end;
Таким образом я могу обрабатывать сообщения, приходящие к моему окну:
function TMyForm.WndProc(Hwn:HWND;Msg,wpr,lpr:cardinal):cardinal;stdcall;
begin
beep;
end;
В таком контексте каждый раз, когда окну придет сообщение - будет звучать звуковой сигнал. Все хорошо, но:
WindowClass.lpfnWndProc:=@WndProc; //ОШИБКА !
Хотя в примере выше, когда все пишется без класса TMyForm все ок.
Можно конечно написать общую функцию WndProc (не как метод класса). И для всех окон назначать главной функцией ее. Но это в простейшем случае. Допустим, у класса есть флаг FlagBeep в зависимости от которого надо или подавать сигнал или нет. Было бы так:
TMyForm.WndProc(Hwn:HWND;Msg,wpr,lpr:cardinal):cardinal;stdcall;
begin
if FlagBeep then beep;
end;
Но так ведб нельзя и если функция будет общая:
function ndProc(Hwn:HWND;Msg,wpr,lpr:cardinal):cardinal;stdcall;
begin
if FlagBeep then beep;
end;
Так писать нельзя. Надо как-то по Hwn найти экземпляр класса, где есть это окно, потом прочитать его FlagBeep... и тогда уж действовать. Конечно, так можно... Но есть же методу полегкче ?
P.S. VCL я читал, но мало что понял :(
← →
Ihor Osov'yak (2003-05-21 12:01) [18]2 Top Gun (21.05.03 11:06)
>> Говорят, Марк Кенту хорошие понаписывал...
>Фигню он написал, по моему
Ню-ню...
>>unit Controls;
>Это понятно. Только не могу я доп. модули подключать.
Тебе не единыжды и русским языком говорили - смотри туда как на один из вариантов решения.. Тебе еще как сказать? По-українськи? English? Или на русском, но "канкретна"?
>Должно быть WindowClass.lpfnWndProc := @Здесь_должен_быть_идентификатор_процедуры
>Блин. И что тогда вызовется, если я напишу WindowClass.lpfnWndProc := TWndProc ?!
Ты что, читать не умеешь? Написано ведь Здесь_должен_быть_идентификатор_процедуры а не Здесь_должен_быть_идентификатор_типа. Это разные вещи. Одним словом - Кенту в зубы и от первой страницы до последней три раза. С воспитательной целью.. Количество попыток желательно увеличить, если просветления не настанет..
>Мне же нужно, чтобы в каждом экземпляре объекта существовал WindowClass и его свойство lpfnWndProc указывало на процедуру WndProc. Но не на общую WndProc, а на каждую для своего экземпляра.
Во-первых, очень непонятное желание. Во-вторых, есть подозрение, что ты путаешь понятие класс и экземпляр класса. А в третьих, не очень понимаешь, что же такое оконный класс. И что такое процедура окна. И почему она не может быть методом класса..
>И вот ты говоришь, что WindowClass.lpfnWndProc := @Здесь_должен_быть_идентификатор_процедуры. А почему когда я пишу прям в dpr файл (чистый WinApi), объявляю там просто функцию WndProc и класс WindowClass:TWndClass. А потом в теле главной функции пишу WindowClass.lpfnWndProc:=WndProc все проходит на ура ?
Пожалуйста, полные фрагменты кода. Или ходя бы полные декларации. Я не телепат.. Но, впрочем, смотри ниже. Есть соображения и на эту тему..
> Не объясняли...
Не единыжды. Только обьяснений ты не слушаешь. Или читать не умеешь. Или читаешь, но способностью воспринять прочитанное ты не обладаешь..
> Давайте объясню по другому. Более жизненно. Особенно тем, кто не работал с окнами через WinApi.
не нарывайси... <Xxxxxxx xxxxxxx xxxxxxxxx xxxxxxxx xxxxxx!!!>. Ты имеешь шансы быть следующим.
> Я хочу оформить каждое окно в класс. Что-то типа того:
Вот когда приведешь код полностью, или хотя бы полные декларации, то может тебе и укажут на ошибку.. Парадокс то в том, что код приведенный тобой, ошибки компиляции, в указаном тобой месте не вызывает.. Но вот если ты TMyForm сделаешь наследником от какого-то класса (а есть подозрение, что именно так ты делаешь - то все может быть. В том числе и такая ошибка..)
> Но есть же методу полегкче ?
Лично я тебе уже третий раз повторяю - смотри исходники vcl. Там это сделано довольно изящно и просто..
> P.S. VCL я читал, но мало что понял :(
Так может и с этого начнем?
Ps - Пока не будет просветления в понимании исходников vcl - не нужно браться за написание оьектных нахлобучек над оконными виндозными классами..
← →
rounin (2003-05-21 13:00) [19]2Top Gun
Если ты не можешь пользоваться модулем Forms,
то MakeObjectInstance для тебя недоступен.
Тогда ты можешь воспользоваться таким нехитрым приёмом:
function MyGlobalWindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall;
// Только для потомков класса TMyClass !!!
var
Instance: TMyClass;
Message: TMessage;
begin
Longint(Instance) := GetWindowLong(hWnd, GWL_USERDATA);
// Получаем указатель на экземпляр TMyClass
// !!! Когда экземпляр TMyClass создаст окно,
// он должен в GWL_USERDATA окна положить указатель на себя.
// Недостаток такого подхода в том, что пользователь класса
// всегда может поменять данные, хранящиеся в GWL_USERDATA,
// что приведёт, естественно, к фатальным результата :-(
Message.Msg := Msg;
Message.WParam := wParam;
Message.LParam := lParam;
Instance.WndProc(Message);
Result := Message.Result;
// Вызывается виртуальный метод класса TMyClass,
// который будет производить реальную обработку событий.
end;
← →
Ihor Osov'yak (2003-05-21 13:09) [20]> // !!! Когда экземпляр TMyClass создаст окно,
// он должен в GWL_USERDATA окна положить указатель на себя.
// Недостаток такого подхода в том, что пользователь класса
// всегда может поменять данные, хранящиеся в GWL_USERDATA,
// что приведёт, естественно, к фатальным результата :-(
Вот-вот.. И это обстоятельство, имхо достаточно для того, чтобы такой техникой не пользоваться. Имхо..
Ну народ, говорили же - смотрите vcl в части использования WindowAtom, ControlAtom (unit Controls) как на источних вдохновения и идей. Ну вряд-ли чего лучше придумаете..
← →
Digitman (2003-05-21 13:45) [21]
> Top Gun
ты сходи в модуль Forms и посмотри внимательно, как реализована ф-ция AllocateHWnd(метод_класса)
как раз в эту ф-цию передается параметром имя метода (то что тебе нужно), который ты реализовал в своем классе, этот метод и будет обрабатывать оконные сообщения, адресованные окну, что создается ф-цией AllocateHWnd()
← →
Top Gun (2003-05-21 14:25) [22]Парадокс то в том, что код приведенный тобой, ошибки компиляции, в указаном тобой месте не вызывает
А у меня вызывает. Именно в том месте и именно в такой формулировке. Компилирую в D7
И еще. Уважаемый, Ihor Osov"yak, не отвечайте больше в эту ветку, хорошо ? Кроме оскорблений и уничижений я ничего не увидел. Не будем развязывать войну, просто игнорируйте, пожалуйста!
rounin и Digitman, спасибо ! Попробую покопаться
← →
VMcL (2003-05-21 14:29) [23]>Top Gun (11.05.03 14:32)
Дополнение к >Digitman © (21.05.03 13:45)
Только не забудь DeallocateHWnd в конце сделать.
← →
VMcL (2003-05-21 14:42) [24]>Top Gun (21.05.03 14:25)
Почему не указал версию Delphi, когда вопрос постил в форум?
← →
Ihor Osov'yak (2003-05-21 14:46) [25]2 Кроме оскорблений и уничижений я ничего не увидел.
А зря. Мне Вас искренне жаль.
← →
Top Gun (2003-05-21 17:46) [26]Еще одно оскорбление. Скажите еще что-нибудь. Хорошо получается
← →
Palladin (2003-05-21 18:00) [27]А я скажу свою любимую, нефиг на третий этаж через окно лезти...
← →
Top Gun (2003-05-21 18:17) [28]Digitman, посмотрел на AllocateHWnd. Но к сожалению, не могу использовать MakeObjectInstance, так как Classes не подключен. А по коду MakeObjectInstance не понял, что она делает, чтобы реализовать аналог у себя...
Пока копаюсь над постом rounin
← →
VMcL (2003-05-21 18:37) [29]>Top Gun (21.05.03 18:17)
А скозлить эти процедуры/функции в свой модуль религия не позволяет?
← →
Top Gun (2003-05-21 18:49) [30]rounin, гениально! Лично для меня :-) Пока так и сделаю.
А в VCL так и сделано ? Исходники у меня есть, но я их плохо понимаю. Можете в двух словах рассказать, каким образом в VCL реализована моя проблема ? Схема действий в общем, в общих словах.
← →
rounin (2003-05-21 19:47) [31]2Top Gun
В VCL это сделано довольно хакерским методом.
Грубо говоря, насколько я понимаю
там выделяется блок памяти с флагом исполняемого кода
и в ней формируется много тел маленьких функций с
прошитым в ней указателем на объект - на каждый экземпляр
своя процедура (точнее, не ф-й, а thunk"ов, которые пихают в регистр указатель на метод (TMethod) и прыгают на глобальную
ф-ю, которая этот метод вызывает).
Но я тоже особенно не вникал во все тонкости и могу соврать.
PS
Кстати, ты зря не слушал Ihor Osov"yak.
Он не зря всё время пытался обратить твоё внимание на
атомы и SetProp
(также функции GlobalAddAtom, GlobalDeleteAtom, GlobalSetProp,
SetProp, GetProp и RemoveProp).
С помощью них можно метод, который я тебе предложил,
сделать гораздо более безопасным.
Можно хранить указатель на объект не через
GWL_USERDATA, а через
SetProp(hWnd, MakeIntAton(MyAtom), Longint(Self) ).
Посмотри, как в модуле Controls реализована ф-я FindControl.
(а мне уже пора бежать - ближняя проходная закрывается)
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2003.06.02;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.008 c