Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 07.08.2010, 20:20   #1
Altera
Старожил
 
Аватар для Altera
 
Регистрация: 29.01.2008
Сообщений: 2,406
Вопрос Метод обратного вызова?

Всем привет. Интересует следующий вопрос.
Вот при создании класса окна в tWinControl ему в качестве оконной процедуры назначают InitWndProc но при первом же её вызове (т.е. при получении сообщения WM_CREATE в момент создания окна InitWndProc меняет адрес оконной процедуры со своего на адрес метода экземпляра объекта tWinControl. Т.е. на tWinControl.wndProc и вызывает его.
Код:
function InitWndProc(HWindow: HWND; Msg: UINT; WParam: WPARAM; LParam: LPARAM): LRESULT;
begin
  CreationControl.FHandle := HWindow;
  if IsWindowUnicode(HWindow) then
  begin
    SetWindowLongW(HWindow, GWL_WNDPROC, Longint(CreationControl.FObjectInstance));
    if (GetWindowLongW(HWindow, GWL_STYLE) and WS_CHILD <> 0) and
      (GetWindowLongW(HWindow, GWL_ID) = 0) then
      SetWindowLongW(HWindow, GWL_ID, HWindow);
  end else
  begin
    SetWindowLong(HWindow, GWL_WNDPROC, Longint(CreationControl.FObjectInstance));
    if (GetWindowLong(HWindow, GWL_STYLE) and WS_CHILD <> 0) and
      (GetWindowLong(HWindow, GWL_ID) = 0) then
      SetWindowLong(HWindow, GWL_ID, HWindow);
  end;

  SetProp(HWindow, MakeIntAtom(ControlAtom), THandle(CreationControl));
  SetProp(HWindow, MakeIntAtom(WindowAtom), THandle(CreationControl));

  asm
        PUSH    LParam
        PUSH    WParam
        PUSH    Msg
        PUSH    HWindow
        MOV     EAX,CreationControl
        MOV     CreationControl,0
        CALL    [EAX].TWinControl.FObjectInstance
        MOV     Result,EAX
  end;
end;
Кстати, не мог не заметить, что сначала проверяется, использует ли окно юникод и если да то вызывается GetWindowLongW/SetWindowLongW, в противном случае вызывается GetWindowLong/SetWindowLong, прикол в том, что в Embarcadero CodeGear RAD Studio 2010 Archtect функции без A и W по умолчанию ссылаются на W[CODE]function SetWindowLong; external user32 name 'SetWindowLongW';

Код:
function SetWindowLong; external user32 name 'SetWindowLongW';
function SetWindowLongA; external user32 name 'SetWindowLongA';
function SetWindowLongW; external user32 name 'SetWindowLongW';
Не знаю, какой смысл делать там лишнюю проверку?


Короче, как правильно указать winApi ссылку на метод, что бы он видел его как функцию и всё было бы ОК?

И интересен вот ещё какой вопрос, описание оконной процедуры выглядит, сами можете видеть каким образом, а вот опись метода tWinControl.wndProc выглядит вот так:
Код:
procedure WndProc(var Message: TMessage); override;
который объявлен ещё в tControl как виртуальный, непонятно только зачем, наверное что бы неоконные компоненты тоже могли бы обрабатывать сообщение, но тогда почему-то он обрабатывает WM_ а не CM_ или CN_?

В общем если проясним ответы на эти вопросы, будет здорово

з.ы. Быть может я не правильно избавил код от {$IF DEFINED(CLR)}, {$IFDEF LINUX} и т.п. По любому вы сами можете докопаться до него )

Последний раз редактировалось Stilet; 09.08.2010 в 09:07.
Altera вне форума Ответить с цитированием
Старый 08.08.2010, 10:18   #2
Ins
Форумчанин
 
Регистрация: 29.12.2007
Сообщений: 137
По умолчанию

Сообщения WM_ посылаются неоконным контролам не от системы непосредственно, разумеется, а искусственно, от его оконного родителя
Ins вне форума Ответить с цитированием
Старый 09.08.2010, 09:55   #3
Altera
Старожил
 
Аватар для Altera
 
Регистрация: 29.01.2008
Сообщений: 2,406
По умолчанию

Код:
unit Unit1;

interface

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

type
  tMyThreadProc = function (const lParam: pChar): integer of object; stdCall;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    function myThread(const lParam: pChar): integer; stdCall;
  end;

var
  Form1: TForm1;

const
  tempStr = 'my string';

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  myThreadProc: tMyThreadProc;
  ID: cardinal;
begin
  myThreadProc := myThread;
  createThread(nil, 0, @myThreadProc, pChar(tempStr), 0, ID);
end;

function TForm1.myThread(const lParam: pChar): integer;
begin
  setWindowText(findWIndow('tform1', nil), lParam);
end;

end.
Проблема с параметрами. Как решить?
Altera вне форума Ответить с цитированием
Старый 09.08.2010, 13:32   #4
Ins
Форумчанин
 
Регистрация: 29.12.2007
Сообщений: 137
По умолчанию

По MakeObjectInstance у Антона Григорьева подробно расписано:
http://www.delphikingdom.com/asp/vie...talogid=169#07
Ins вне форума Ответить с цитированием
Старый 09.08.2010, 13:52   #5
GunSmoker
Старожил
 
Регистрация: 13.08.2009
Сообщений: 2,581
По умолчанию

Если вы целиком контролируете своё окно - я бы посоветовал использовать Get/SetWindowLong с GWL_USERDATA вместо хака MakeObjectInstance.
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
GunSmoker вне форума Ответить с цитированием
Старый 09.08.2010, 13:57   #6
Altera
Старожил
 
Аватар для Altera
 
Регистрация: 29.01.2008
Сообщений: 2,406
По умолчанию

Цитата:
Сообщение от Ins Посмотреть сообщение
По MakeObjectInstance у Антона Григорьева подробно расписано:
http://www.delphikingdom.com/asp/vie...talogid=169#07
Ага конечно, спасибо. Я его как раз читаю книгу "О чём не пишут в книгах по Delphi". Иначе у меня не возник бы этот вопрос, дело в том, что как раз на мой вопрос там нет ответа. Я эту главу 3 раза прочёл.
Altera вне форума Ответить с цитированием
Старый 09.08.2010, 14:05   #7
Ins
Форумчанин
 
Регистрация: 29.12.2007
Сообщений: 137
По умолчанию

1. Во-первых, не используй CreateThread, а используй BeginThread, иначе тебе явно не понравится, когда программа зависнет намертво при попытке выделить в потоке память
2. Во-вторых, методы класса от обычных функций отличаются тем, что в них НЕЯВНО передается дополнительный параметр - Self, соответственно с этим параметром они просто не соответствуют прототипу. Обход - это назначит все-таки функцию с правильным прототипом, которая вызывает метод, но эта функция должна откуда-то взять Self. Стоит ли овчинка?
Ins вне форума Ответить с цитированием
Старый 09.08.2010, 17:39   #8
Altera
Старожил
 
Аватар для Altera
 
Регистрация: 29.01.2008
Сообщений: 2,406
По умолчанию

Цитата:
Сообщение от Ins Посмотреть сообщение
1. Во-первых, не используй CreateThread, а используй BeginThread, иначе тебе явно не понравится, когда программа зависнет намертво при попытке выделить в потоке память
А с чего она должна зависнуть? Никогда не зависала.

Цитата:
Сообщение от Ins Посмотреть сообщение
2. Во-вторых, методы класса от обычных функций отличаются тем, что в них НЕЯВНО передается дополнительный параметр - Self, соответственно с этим параметром они просто не соответствуют прототипу. Обход - это назначит все-таки функцию с правильным прототипом, которая вызывает метод, но эта функция должна откуда-то взять Self. Стоит ли овчинка?
Это я знаю. Вот я и спрашиваю, как это организовать. Ведь VCL реализовала, но я разобраться не могу.
Altera вне форума Ответить с цитированием
Старый 09.08.2010, 18:08   #9
Ins
Форумчанин
 
Регистрация: 29.12.2007
Сообщений: 137
По умолчанию

Цитата:
А с чего она должна зависнуть? Никогда не зависала.
Значит тебе везло. Загляни в код функции BeginThread и посмотри что она делает. А именно, она:
1. Переводит менеджер памяти в потокобезопасный режим, без этого попытка одновременного обращения к менеджеру памяти из разных потоков может закончиться непредсказуемо
2. Заворачивает код функции потока в SEH-фрейм, чтобы необработанное исключение в потоке не рушило все приложение
3. Инициализирует FPU (для правильной работы чисел с плавающей точкой)

Цитата:
Ведь VCL реализовала, но я разобраться не могу.
VCL делает это достаточно грязно - создает в памяти код, который вызывает метод. Проще объявить свою функцию, назначить ее и вызвать в ней метод
Ins вне форума Ответить с цитированием
Старый 09.08.2010, 19:34   #10
Altera
Старожил
 
Аватар для Altera
 
Регистрация: 29.01.2008
Сообщений: 2,406
По умолчанию

Цитата:
Сообщение от Ins Посмотреть сообщение
Значит тебе везло. Загляни в код функции BeginThread и посмотри что она делает. А именно, она:
1. Переводит менеджер памяти в потокобезопасный режим, без этого попытка одновременного обращения к менеджеру памяти из разных потоков может закончиться непредсказуемо
2. Заворачивает код функции потока в SEH-фрейм, чтобы необработанное исключение в потоке не рушило все приложение
3. Инициализирует FPU (для правильной работы чисел с плавающей точкой)
Не, я понял почему у меня не зависало. Я никогда в потоках не освобождал память. А если просто при вызове функции нужно выделить память под переменные, тоже зависнет?

Ладно, спасибо, учту.


Цитата:
Сообщение от Ins Посмотреть сообщение
VCL делает это достаточно грязно - создает в памяти код, который вызывает метод. Проще объявить свою функцию, назначить ее и вызвать в ней метод
Ок, ясно. VCL меня частенько пугает своими выкрутасами. Как бы они не до добра бы не довели.
Altera вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Таймер обратного отсчёта HellkilleR Microsoft Office Excel 21 18.03.2015 12:49
Таймер обратного отсчета Bilargo Помощь студентам 6 30.09.2009 17:02
Функция обратного вызова и таймер на WinApi flug Win Api 4 17.09.2009 16:06
Содержание прямого или обратного набора Big-kit Общие вопросы Delphi 1 04.06.2009 17:02
Таймер обратного отсчета на С Raptor Помощь студентам 6 08.01.2008 01:11