Форум: "Потрепаться";
Текущий архив: 2002.04.15;
Скачать: [xml.tar.bz2];
ВнизГоловоломка на знание принципа работы компилятора Object Pascal Найти похожие ветки
← →
Алексей Петров (2002-03-05 16:35) [0]Суть следующая: как создать объект-потомок от TComponent зная его имя и не используя регистрации объектов.
Задачка всплыла в процессе обсуждения http://delphi.mastak.ru/cgi-bin/forum.pl?look=1&id=1015316367&n=3
Сразу предупреждаю, что готового кода, решающего задачу я не писал и в качестве решения не прошу, т.к. его будет довольно много.
Нужно только описание идеи.
← →
Алексей Петров (2002-03-05 16:49) [1]> MBo © (05.03.02 16:36)
> а ограничения?
> Если мы в runtime попросим создать компонент, скажем, stringgrid, о котором Exe никакого понятия не имеет - и финиш...
Если в Exe нет такого компонента, то естественно создать его будет нельзя.
← →
vuk (2002-03-05 17:15) [2]Кстати, если в лоб адреса перебирать (начиная с адреса TObject) множно много чего найти...
← →
VuDZ (2002-03-05 18:18) [3]хватит на людьми издеваться, расказывай свою мыслю
← →
Алексей Петров (2002-03-05 18:33) [4]Да я на нее несколько раз намекал.
и vuk вполне в тему пишет...
← →
Алексей Петров (2002-03-05 18:34) [5]Подробно утром напишу, когда времени побольше будет...
← →
vuk (2002-03-05 18:41) [6]Я могу сказать что то, что я предлагаю, это решение довольно-таки тупое и сделанное "в лоб", методом грубой силы. Однако список всех классов получить удалось. Есть, похоже, правда одно место в котором алгоритм дает небольшой сбой. Да и работает все это дело долго, а вникать в подробности просто лень. :o)
← →
Shaman_Naydak (2002-03-05 22:33) [7]Мда, Алексей, задал ты задачку..
Основная проблема в том, что классы хранятся в виде дерева, где потомок указывает на предка, а наоборот не пройтись ;(
Посмотрел я как подымает TReader.
Ну что я могу сказать..
Тут комбинируются 2 способа.
1. Общеизвестный поискать FindClass"ом (найдет тех, кого через RegisterClass загнали в ClassList.
2. От заданного класса конечного класса он проходится по всем полям объектам, если не находит подымается вверх.
=> из 2 могу предложить след. уродский способ:
пройтись по всем компонентам TApplication"a, передав класс каждого из них в искалку.
Искалка проходится по всем полям объектам и для КАЖДОГО! вызывает рекурсивно себя же..
после списка полей переходит на проверку ParentClass и.т.д.
Результат складируется в TList"e (Жаль, что до борландовского ClassList"a не достучаться, он в implementation"e).
Так можно найти не только TComponent"ы..
Правда ясен пень создать здоровый объект можно только у наследников TComponent"ы, так как у него конструктор виртуальный.
Жутко заинтересовался, буду ждать стоящих идей решения.
Могу в принципе запрограммить то, что наговорил..
правда мне этот алгоритм не нравится, однако!
← →
MBo (2002-03-06 07:26) [8]>Shaman_Naydak
Я тоже вчера копался с reader, пытался подправить FindComponentClass, не вышел пока каменный цветок.
>заданного класса конечного класса
в этом-то и проблема, задать класс по имени. Насколько я понимаю, только прямой вызов RegisterClass ДЛЯ КАЖДОГО КЛАССА или создание его аналога создает удобоваримый доступ к классам через ClassList. А с нахождением класса по имени через
vmtClassName=-44; у меня пока тоже проблемы- не хватает знаний о системе хранения объектов/классов.
>ClassList"a не достучаться, он в implementation"e
а толку-поправив classes, можно, а он пустой (или не все классы)
← →
MBo (2002-03-06 08:39) [9]вот подход, позволяющий создавать компоненты, имеющиюхся на форме или ее подэлементах (при введении рекурсивного обхода) типов
Не радикально, но чем богаты...
function TForm1.FindSuch(Root:TComponent;const s: String): TComponent;
var i:integer;
begin
Result:=nil;
for i:=0 to root.componentcount-1 do begin
if sametext(root.Components[i].classname,s) then begin
Result:=Root.Components[i];
Break;
end;
end;
end;
procedure TForm1.Button6Click(Sender: TObject);
type
TWCClass = class of TWinControl;
var
Obj:TWinControl;
ObjClass:TWCClass;
s:string;
c:tcomponent;
begin
s:=edit1.text;
c:=FindSuch(TComponent(self),s);
if c<> nil then begin
ObjClass:=TWCClass(c.classtype);
if ObjClass<>nil then begin
Obj:=ObjClass.Create(self);
Obj.Parent:=self;
Obj.SetBounds(10,10,100,20);
end;
end;
end;
← →
Алексей Петров (2002-03-06 08:41) [10]> Shaman_Naydak
Таким образом вы многое повытаскиваете. А еще если к вашей искалке добавить проход по ParentClass до TObject вы сможете очень хороший кусок иерархии объектов выкусить.
Расскажу свою мысль. В общем vuk решение уже называл по сути и я коротко на нее как-то намекал в форуме :)
Но как обещал, идея хаккерская и к сожалению код, её реализующий, может потребовать модификации при переходе с версии на версию.
Идея:
Для того, чтоб создать экземпляр класса нужно найти его VMT. Далее адрес VMT класса заносим в переменную типаclass of ...
и спомощью этой переменной можно вызвать виртуальный констуктор (потому только TComponent, как и подметил Shaman_Naydak). Основная сложность - вытащить список VMT-ов.
Несколько наблюдений по структуре exe-файла "made by Delphi":
1. В памяти (конкретно в сегменте кода) описания объектов лежат последовательно без пустот и вкрапления прочей информации (не считая выравнивания на двойное слово).
2. TObject хранится всегда самым первым. И ссылку на его VMT у нас уже есть:Pointer(TObject)
Собственно оснавная сложность: понять, сколько байт нужно пропустить, чтоб попасть на следующий объект.
Перый подход: Минимальный размер VMT - -vmtSelfPtr = 76 (для D5) - это размер служебной информации для класса.
Прибавляем к предыдущеум указателю -vmtSelfPtr и далее проверяем не на объект ли попали. Если нет - добавляем 4 и снова проверяем, пока не найдем или пока не уйдем за пределы сегмента кода. (Предварительно стоит выяснить, где кончается нужная память с помощью VirtualQuery).
Проверка того, что поинтер может быть TClass-ом довольно проста:
1) Pointer((Ptr+vmtTypeInfo)^) обязан показывать на кусок самого exe-шника, что выясняется с помощью VirtualQuery
2) PShortString((Ptr+vmtClassName)^) должен быть и быть корректным идентификатором по синтаксису Delphi.
3) (Ptr+vmtInstanceSize)^ должен быть разумным: как минимум положительным (>=4). Можно еще ограничение наложить <1GB а можно и побольше немного
4) Pointer((Ptr+vmtParent)^) должен указывать на ранее выкопанный объект (можно проверять просто на объект по выше приведенным критериям), кроме объекта TObject у которого тут nil.
← →
Алексей Петров (2002-03-06 08:57) [11]Есть некоторая вероятность ложных срабатываний при первом подходе к сканированию, но она очень мала. А вот скорость сканирования - получится так себе.
Второй подход: пошустрее в Run-time, но с существенно большим объемом кодирования: Нужно для объекта посчитать размер его VMT и всех потрохов.
в отрицательной части VMT прописаны указатели на дополнительные данные об объекте. Все они находятся между нулевм смещением текущего VMT и началом следующей таблицы. Описание структуры всех этих таблиц есть в моулях System.pas и TypInfo.pas. Единственная таблица, для которой я не нашел явного описания - таблица динамических методов, но для нее структура легко восстанавливается при просмотре функции GetDynaMethod из модуля System.
Размер для них всех считается. Таким образом получается конец описания текущего объекта, а прямо следом за ним должен быть SelfPtr следующего объекта.
← →
Shaman_Naydak (2002-03-06 12:30) [12]>> Алексей Петров
Да, так можно их повытаскивать, но геморрно.
Я так и думал, что они лежат подряд.. но вечером ковыряться было неохота..
А если после классов они в сегмент еще что-нидь положат, это ж будет совсем не гуд, однако.
Кстати, а не проверял ли как оно выглядит в памяти при компиляции с пакетами, там ситуация значительно осложняется :(
И что это ребята из Borland примитивную цепочку не сбацали, где один класс ссылается на следующий. Всем бы было хорошо, да и им не пришлось бы костыли делать в виде RegisterClass, GetClass.
← →
vuk (2002-03-06 12:49) [13]to Алексей Петров:
Есть еще одна тонкость - когда остановить перебор адресов в памяти. Я решил ее так: в главном модуле проекта объявил класс и прописал создание его экземпляра с последующим удалением (чтобы компилятор его не выкинул). Суть в том, что этот класс всегда будет располагаться в памяти последним и имя его я знаю. Как только обнаруживаю этот класс, прекращаю перебор.
to Shaman_Naydak:
>А если после классов они в сегмент еще что-нидь положат, это ж
>будет совсем не гуд, однако.
Ну оно, вроде бы, так и есть, поскольку между адресами последнего класса в одном модуле и первого класса в другом обычно получается некоторый разрыв. Страшного в этом ничего нет. Ну попали не туда - в крайнем случае AV вылезет, его и проигнорировать можно. :o)
>Всем бы было хорошо, да и им не пришлось бы костыли делать в
>виде RegisterClass, GetClass.
RegisterClass не всю VCL всего раза 3 встречается. Да и практическая применимость (да и необходимость) этого метода с поиском всех классов довольно-таки сомнительна...
← →
Алексей Петров (2002-03-06 15:23) [14]С пакетами все на порядок проще будет.
Для каждого класса в пакете есть своя точка входа через которые все вытаскивается.
А вот для exe, который использует пакеты - нужно будет взять любой "точно свой" класс и пройтись по нему не только вперед, но и назад таким-же образом (здесь правда метод №2 не пройдет.)
← →
VuDZ (2002-03-06 16:54) [15]Если я правильно понял, то в exe Делфи сохраняет имя класса?
← →
vuk (2002-03-06 17:22) [16]to VuDZ:
Да. Это является частью модели RTTI.
← →
VuDZ (2002-03-06 17:35) [17]всё ясно, я забыл - у меня в VC по-умолчанию это отключено
Страницы: 1 вся ветка
Форум: "Потрепаться";
Текущий архив: 2002.04.15;
Скачать: [xml.tar.bz2];
Память: 0.5 MB
Время: 0.005 c