Форум: "Основная";
Текущий архив: 2006.03.05;
Скачать: [xml.tar.bz2];
ВнизКак в из метода сделать обычную процедуру? Найти похожие ветки
← →
Still Swamp (2006-02-04 16:03) [0]Как в из метода сделать обычную процедуру? Мне необходимо отправить метод класса как CallBack процедуру. к сожалению компилятор этого не позволяет.
Варианты описания обычной функции не подходят так как мне при отработке нужен указатель на сам объект. Держать в голбальной переменной его нельзя так как в приложении их может быть несколько. Передать как параметром функции так же не удается, так как это не предусмотрено самой структурой. Других вариантов я не могу придумать.
Подскажите.
← →
TUser © (2006-02-04 16:20) [1]Ну, адрес метода ты передать можешь, - через @TClassName.Method. Только из стека потом (при вызове метода) будут доставаться параметры, в т.ч. Self. Этот Self будет равен nil.
Параметры коллбеку передаются операционной системой, которая ни про какие методы и их неявно существующий параметр Self ничего не знает, и в стек их записывать не будет.
← →
Still Swamp (2006-02-04 16:46) [2]Я это все понимаю. Есть какие либо методы борьбы?
← →
Джо © (2006-02-04 16:56) [3]Универсального метода нет. Можно создавать код-"обертку" наподобие того, как это сделано в MakeObjectInstance для оконной процедуры-метода класса.
← →
Гаврила © (2006-02-04 17:10) [4]
> Держать в голбальной переменной его нельзя так как в
> приложении их может быть несколько.
Присваиваешь глоб. переменной, которая объявлена непосредственно надо функцией, и сразу вызываешь то, что тащит твой кал-бек. по окончании забываешь про глоб. переменную (или обниливаешь).
Если приложение многопоточное - глоб переменная как threadvar
и убедиться в отсутствии "processMessages" в коде между присвоением глоб. переменной и вызовом CallBack
и всех делов
← →
TUser © (2006-02-04 17:10) [5]Перепридумать логику приложения.
← →
Anatoly Podgoretsky © (2006-02-04 17:55) [6]Редизайн, редизайн и еще раз редизайн
← →
begin...end © (2006-02-04 20:22) [7]http://kladovka.net.ru/index.cgi?pid=board&rid=49
← →
Still Swamp (2006-02-05 12:37) [8]Зловещий исходник.... :(
Тем более он мне похоже не поможет. Ловлю сообщения от TAPI, и как обычно при работе с железом, расчитывать на CriticalSection не рискну.
Редизайн и "передумать".... тоже сложно... те тупик?
← →
jack128 © (2006-02-05 13:58) [9]Still Swamp (05.02.06 12:37) [8]
Ловлю сообщения от TAPI,
как я делал:
constructor TTAPIApplication.Create;
begin
inherited;
TapiCheck(lineInitialize(FlineApp, MainInstance,
TapiCallbackProc, nil, FDeviceCount));
end;
procedure TTAPIModem.OpenLine;
var
LineCallParams: TLINECALLPARAMS;
begin
if FLineCount = 0 then
TapiCheck(lineOpen(FTapiAppl.FLineApp, FDeviceId, FLine, FTapiVer, 0,
Cardinal(Self), LINECALLPRIVILEGE_OWNER, LINEMEDIAMODE_DATAMODEM,
LineCallParams));
inc(FlineCount);
end;
procedure TapiCallbackProc(hDevice,
dwMessage,
dwInstance,
dwParam1,
dwParam2,
dwParam3 : DWORD);
stdcall;
begin
if (dwInstance <> 0) then
TTAPIModem(dwInstance).MessageProc(dwMessage, dwParam1, dwParam2, dwParam3);
end;
procedure TTAPIModem.MessageProc(dwMessage, dwParam1, dwParam2,
dwParam3: DWORD);
begin
case dwMessage of
...
end;
end;
← →
begin...end © (2006-02-05 16:33) [10]> Still Swamp (05.02.06 12:37) [8]
> Зловещий исходник.... :(
> Тем более он мне похоже не поможет. Ловлю сообщения от TAPI,
> и как обычно при работе с железом, расчитывать на CriticalSection
> не рискну.
Критические секции нужны только в том случае, если у Вас в приложении работает несколько потоков.
Хоть я и не являюсь автором исходника, на который дана ссылка в [7], всё же позволю себе объяснить, как он работает. Возможно, после этого станет более понятно.
Главное в исходнике -- функции MakeInstanceStdCall/FreeInstanceStdCall. Остальное относится к примеру использования этих функций -- назначению метода объекта в качестве оконной процедуры (посмотрите в конструктор TWndObject -- там с помощью SetWindowLong созданному окну назначается новая процедура, которая, в основном, будет исполнять код метода TWndObject.ObjWindowProc).
Итак, что произойдёт, когда операционная система посчитает нужным вызвать callback-функцию? Система помещает в стек все необходимые параметры в соответствии с принятым соглашением о вызове (stdcall), после чего с помощью команды CALL передаёт управление по адресу начала функции. Заметьте -- именно с помощью команды CALL, т.е. перед началом выполнения кода функции в стеке сохраняется адрес возврата -- это адрес, по которому callback-функция должна будет передать управление после окончания своей работы. Функция сделает это командой RET.
Вначале разберёмся, в чём суть проблемы с методом. Пусть мы имеем некоторый метод, объявленный с соглашением о вызове stdcall (посмотрите в пример):function ObjWindowProc(Wnd: HWND; AMsg: UINT; wPrm: WPARAM; lPrm: LPARAM): LRESULT; stdcall
. Что будет, если в качестве адреса callback-функции передать адрес этого метода (т.е. адрес начала его исполняемого кода -- @TWndObject.ObjWindowProc)? Система сделает то, о чём написано в предыдущем абзаце, и передаст управление по этому адресу. Но методы в Delphi устроены таким образом, что содержат один неявный параметр -- Self, который при "нормальном" вызове метода всегда передаётся первым (независимо от типа соглашения о вызове). Код метода создаётся в расчёте на то, что стек перед исполнением метода (мы говорим именно о stdcall-методе ObjWindowProc) будет подготовлен так (сверху вниз -- от вершины стека):
1. адрес возврата
2. Self
3. Wnd
4. AMsg
5. wPrm
6. lPrm
Но система-то об этом ничего не знает! И в стек она поместит не 6 параметров, а только 5 -- исключив первый (Self). Поэтому, когда внутри метода мы будем обращаться к Self, мы будем получать значение Wnd, когда обратимся к Wnd -- получим значение AMsg, и т.д. А вместо параметра lPrm получим вообще мусор.
Но это ещё полбеды. Главная проблема и опасность вот в чём: метод ожидает, что ему через стек передано 6 параметров, и, поскольку именно он ответственен за балансировку стека (stdcall), он, отработав, удалит их стека именно 6 параметров (вместо 5). В результате стек балансируется неправильно, и его вершина с каждым вызовом всё больше и больше увеличивается.
Решение этой проблемы, приведённое в коде по ссылке в [7] -- ручная корректировка стека перед передачей управления методу. В функции MakeInstanceStdCall, которая делает из метода "обычную процедуру", происходит, в основном, следующее: выделяется область памяти, в неё помещается такой код:POP EAX (1)
PUSH значение_self (2)
PUSH EAX (3)
PUSH значение_адреса_метода (4)
RET (5)
(конечно, на момент вызова MakeInstanceStdCall объект должен быть уже создан), и указатель на эту выделенную область памяти с пятью командами передаётся в качестве адреса callback-функции.
Итак, сразу после вызова операционной системой этой функции стек будет сформирован следующим образом:
1. адрес возврата
2. Wnd
3. AMsg
4. wPrm
5. lPrm
После выполнения строки (1) кода адрес возврата окажется в EAX, а после строки (2) его место в стеке займёт Self. Третья строка поместит в стек сохранённый адрес возврата. В результате стек станет таким:
1. адрес возврата
2. Self
3. Wnd
4. AMsg
5. wPrm
6. lPrm
Посмотрите первый такой список в этом посте -- мы получили именно то, что и ожидает увидеть метод. Следующие две команды передают управление на его начало. Метод выполняется, удаляет из стека 6 параметров (но теперь это нормально, т.к. мы сами поместили в стек 1 дополнительный параметр, и их там действительно 6) и возвращает управление системе по тому адресу, который она сообщила нам перед вызовом callback-функции.
В результате получили возможность пользоваться Self, обращаться к параметрам по их настоящим именам, без плясок с бубном, и стек балансируется именно так, как нужно.
← →
Still Swamp (2006-02-05 19:34) [11]to jack128
Ок. В помощи по lineOpen написано буквально следующее:
dwCallbackInstance - User-instance data passed back to the application with each message associated with this line or addresses or calls on this line. This parameter is not interpreted by the Telephony API.
Те если это именно то что возвращается в dwInstance то это самый лучший выход в конкретной ситуации, без необходимости отправлять метод в качестве CallBack.
← →
Still Swamp (2006-02-05 19:36) [12]Ну да... Callback instance data passed back to the application in the callback. This DWORD is not interpreted by TAPI. Все извиняюсь, просто хелп недочитал.
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2006.03.05;
Скачать: [xml.tar.bz2];
Память: 0.5 MB
Время: 0.01 c