Форум: "Потрепаться";
Текущий архив: 2003.07.14;
Скачать: [xml.tar.bz2];
ВнизЗагрузка DFM-ки в run-time Найти похожие ветки
← →
Минина Карина (2003-06-13 18:43) [0]Уважаемые Мастера!
Поскажите, пожалуйста, как можно в run-time для пустой формы подгрузить другой dfm-файл, который содержит разные компоненты?
Заранее благодарна за вашу помощь.
← →
Johny B. (2003-06-13 21:22) [1]ага, и сорец другой подрузить ))
← →
Юрий Зотов (2003-06-13 22:06) [2]> Johny B. (13.06.03 21:22)
Зря смеетесь. Если можно подгрузить один DFM, то почему нельзя подгрузить другой?
> Минина Карина © (13.06.03 18:43)
Для нормальной работы класс формы должен содержать published-поля для всех ее компонентов. Поэтому обычно подгружают DFM того же класса, но вот расположение и настройка подгружаемых компонентов может быть в одном DFM - одна, в другом - другая и т.д. А сама подгрузка делается очень просто.
1. Пустая форма создается ОБЯЗАТЕЛЬНО через Create New, а не через просто Create (потому что просто Create сразу зальет ресурс класса формы и она не будет пустой).
2. В зависимости от того, где и в каком виде хранится DFM, вызывается одна из функций ReadComponentResFile, ReadComponentRes или ReadComponentResEx, либо DFM в тестовом виде считывается в поток, а затем вызывается ObjectTextToBinary и метод TStream.ReadComponent. Вот и все.
← →
Минина Карина (2003-06-15 15:25) [3]2Юрий Зотов © (13.06.03 22:06)
Очень благодарна Вам за Ваш ответ.
Покажите, пожалуйста, примерный код, который мог бы выполнять такую задачу. А то у меня возникло очень много вопросов, на которые я думаю, лучшим ответом была бы Ваша реализация.
Ещё раз Большое Спасибо за то, что откликнулись.
← →
Palladin (2003-06-15 15:34) [4]:) почему то я подумал, во время чтения ответа, будет именно просьба о коде...
набери TForm.CreateNew и вызови справку, там и пример есть маленький
← →
Минина Карина (2003-06-15 17:24) [5]1. streams out an external .DFM file;
2. creates a new form disassociated from any .DFM resource file;
3. streams in the external .DFM file and binds it to this new form.
WriteComponentResFile("Temp.dfm", Form1);
…
Form2 := TForm.CreateNew(Application);
ReadComponentResFile("Temp.dfm", Form2);
(c)Delphi Help
либо DFM в тестовом виде считывается в поток, а затем вызывается ObjectTextToBinary и метод TStream.ReadComponent
Подскажите, пожалуйста, как реализовать второй метод, используя класс TStream?
← →
Palladin (2003-06-15 18:14) [6]var
LoadetForm:TForm;
fs:TFileStream;
ms:TMemoryStream;
begin
fs:=TFileStream.Create("temp.dfm",fmOpenRead);
ms:=TMemoryStream.Create;
try
ObjectTextToBinary(fs,ms);
ms.Seek(0,soFromBeginning);
LoadetForm:=ms.ReadComponent(nil);
finally
ms.free;
fs.free;
end;
end;
только зачем тебе второй метод?
← →
Минина Карина (2003-06-15 18:35) [7]Такой вопрос:
А если DFM-файл содержит компоненты, классы которых не зарегистрированы в IDE Delphi, как с ними быть?
К тому же, кто-то из коллег мне подсказал, что перед этим нужно зарегистрировать в коде все классы, которые будут в DFM-ке.
Я так понимаю, это нужно делать через RegisterClass?
← →
Palladin (2003-06-15 18:50) [8]как вы собираетесь это сделать если делфи понятия не имеет о классе этого компонента, вам необходимо использовать модуль с описанием этого компонента и вызывать RegisterClass в run-time для правильной загрузки из dfm
другими словами:
если ваша программа загружающая dfm будет создана на компьютере1,
а dfm создана на компьютере2 причем с использованием компонента который не установлен на компьютере1, то эта dfm не загрузится корректно (если чесно :) я не знаю что произойдет, но вы можете попробовать, расскажете потом) ибо программа столкнется с неизвестным для нее классом...
← →
Юрий Зотов (2003-06-15 19:35) [9]Проект содержит главную форму TFormLoader (ее код приводится ниже) и включает в себя еще один модуль Unit1 со следущим кодом.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage("Привет!")
end;
Обратите внимание на следующее:
- форма Form1 убрана из AutoCreate;
- переменная Form1 и директива {$R *.DFM} из кода удалены;
- файл Unit1.dfm не используется и тоже удален;
- при сохранении Unit1 среда будет предлагать удалить объявления Button1 и Memo1 - этого делать ни в коем случае нельзя, надо нажать кнопку Cancel.
Далее, есть два файла Unit11.dfm и Unit12.dfm. Они не подключены к проекту, а просто спроектированы в Delphi вне всяких проектов, как совершенно отдельные формы, просто лежат где-то на диске и содержат следующий текст:
=== Unit11.dfm ===
object Form1: TForm1
Left = 242
Top = 177
Width = 324
Height = 134
Caption = "Form1"
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = "MS Sans Serif"
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 120
TextHeight = 16
object Memo1: TMemo
Left = 8
Top = 4
Width = 185
Height = 89
Lines.Strings = (
"Memo1")
TabOrder = 0
end
object Button1: TButton
Left = 216
Top = 36
Width = 75
Height = 25
Caption = "Button1"
TabOrder = 1
OnClick = Button1Click
end
end
=== Unit12.dfm ===
object Form1: TForm1
Left = 230
Top = 186
Width = 407
Height = 137
Caption = "Form1"
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = "MS Sans Serif"
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 120
TextHeight = 16
object Memo1: TMemo
Left = 208
Top = 8
Width = 185
Height = 89
Lines.Strings = (
"Memo1")
TabOrder = 0
end
object Button1: TButton
Left = 48
Top = 32
Width = 75
Height = 25
Caption = "Button1"
TabOrder = 1
OnClick = Button1Click
end
end
Снова обратите внимание на следующее:
- эти две формы одного и того же класса TForm1, но они отличаются настройками и расположением Button1 и Memo1;
- обе формы могут иметь соответствуюшие PAS, но в проекте эти PAS все равно не используются и нужны только при визуальном проектировании форм (после завершения которого эти PAS можно даже удалить).
И вот, наконец, главное - код модуля с формой TFormLoader.
unit FormLoaderUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TFormLoader = class(TForm)
LoadButton: TButton; // Положить на форму
OpenDialog: TOpenDialog;
procedure FormCreate(Sender: TObject);
procedure LoadButtonClick(Sender: TObject);
private
{ Private declarations}
public
{ Public declarations }
end;
var
FormLoader: TFormLoader;
implementation
{$R *.DFM}
uses
Unit1;
function CreateNewForm(DfmFile: TFileName): TForm1;
var
FileStream: TFileStream;
MemoryStream: TMemoryStream;
begin
MemoryStream := TMemoryStream.Create;
try
FileStream := TFileStream.Create(DfmFile, fmOpenRead or fmShareDenyWrite);
try
ObjectTextToBinary(FileStream, MemoryStream)
finally
FileStream.Free
end;
MemoryStream.Position := 0;
Result := TForm1.CreateNew(Application);
try
MemoryStream.ReadComponent(Result)
except
Result.Free;
raise
end
finally
MemoryStream.Free
end
end;
procedure TFormLoader.FormCreate(Sender: TObject);
begin
RegisterClasses([TForm1, TButton, TMemo])
end;
procedure TFormLoader.LoadButtonClick(Sender: TObject);
begin
if OpenDialog.Execute then
with CreateNewForm(OpenDialog.FileName) do
try
ShowModal
finally
Free
end
end;
end.
Запускаем проект и жмем кнопку LoadButton. Открывается OpenDialog, в нем мы выбираем один из файлов Unit11.dfm или Unit12.dfm - и на экране появляется форма TForm1, в которой Button1 и Memo1 соответствуют их расположению в выбранном DFM. Причем обработчик Button1.OnClick нормально работает.
То есть, создается одна и та же пустая форма TForm1, а все ее свойства и компоненты подгружаются именно из выбранного DFM. Что и требовалось.
← →
Ученик (2003-06-15 20:04) [10]Развитие темы:
1. Почему для компонентов формы, созданных в Design-Time не требуется RegisterClass перед загрузкой DFM, но если компоненты на форме созданы динамически в Run-Time и эта форма сохранена в DFM-файле, но при ее загрузке возможны сообщения "Класс ??? не зарегистрирован".
2. Как быть со свойством TForm.Visible, если при сохранении в DFM-файле оно в состоянии True, а форму загружаемую из DFM-файла нужно показать модально (т.е через ShowModal)
← →
Минина Карина (2003-06-15 20:32) [11]2Юрий Зотов © (15.06.03 19:35)
Очень благодарна Вам за столь развёрнутый ответ. Чесно говоря, я даже не ожидала, что здесь реально получить настолько детализированный и подробный исходник;)
Но у меня возник следующий вопрос:
в первом исходнике
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
я так понимаю, поля формы должны содержать все те экземпляры классов, что и в подгружаемой DFM-ке. А если они будут повторяться по типам, то их нужно будет резервировать?
И опять же, возможен ли вариант универсальной пустой формы, для которой заранее неизвестно, какие компоненты и какое их количество будет загружено, но известно лишь множество классов, из которого будут представленны эти компоненты, напр., [TButton, TMemo, TPanel, TList]? И как быть с полями обработчиков для этих компонентов, (хотя они пока мне не нужны для обработки)?
← →
reonid (2003-06-16 18:29) [12]>Ученик © (15.06.03 20:04)
1. При загрузке из потока формы Reader должен каким-то образом
преобразовать имя класса компонента, хранящееся в потоке, в ссылку на класс.
Он делает это следующим образом:
Сначала ищет класс в специальной RTTI-таблице, ссылку на
которую хранит RTTI-таблица published полей.
В этой таблице хранятся классы всех published полей (не св-в)
формы. Поэтому все созданные визуально в Дизайн-тайм или занесённые
программистом в секцию published (не как св-ва, а как поля)
компоненты не нуждаются в RegisterClass.
При этом для разрешения имени класса достаточно одного поля на класс.
Если не находит класса в RTTI, ищет в глобальном списке зарегистрированных классов (GetClass).
Затем вызывает обработчик события OnFindComponentClass,
в котором пользователю предоставляется возможность самому
определить класс по имени.
2. Тут всё просто - свойство Visible имеет по умолчанию значение
True (оно в дфм не пишется). А в конструкторе оно устанавливается
в False.
>Минина Карина © (15.06.03 20:32)
Объясни генеральную идею, что ты хочешь в результате получить.
Вариант пустой формы возможен. Нужно лишь зарегистрировать эти классы.
RegisterClasses([TButton, TMemo, TPanel {, TList - не комонент и даже не персистент}]);
И безо всяких полей.
Только какой смысл в форме без обработчиков???
На счёт обработчиков событий - как и классы компонентов, они в ДФМ хранятся в виде имён.
Для преобразования имени в метод Reader ищет их в published методах формы (MethodAddress).
Контролировать тип метода он при этом не может.
Также Reader имеет событие OnFindMethod, в котором пользователь может определить метод по имени из каких-то других соображений.
(Однако TMethod.Data здесь нельзя переопределить, она всегда
будет указывать на форму).
Если в дфм, как в примере, установлен обработчик
нажатия кнопки
object Button1: TButton
...
OnClick = Button1Click
end
- то у формы должен быть published метод Button1Click.
Т.е., у формы должны быть написаны все обработчики,
упомянутые в дфм.
Или же обработчики можно назначать в рантайме.
← →
Минина Карина (2003-06-16 18:41) [13]>Только какой смысл в форме без обработчиков???
У меня есть много дфм-ок, которые содержат много компонентов определённого множества типов. Мне нужно пройтись по этим всем компонентам и вытащить их некие свойства и переприсвоить им значения из базы даных. Обработчики мне не нужны, поскольку они не используются в даной задаче. Дфм-ки представляют собой видеокадры, в компонентах которых бегут цифры...
← →
Минина Карина (2003-06-23 17:26) [14]2Юрий Зотов © (15.06.03 19:35)
Я сделала так, как Вы предложили.
Но при попытке загрузить ДФМ-ку выдает ошибку типа EParserError with message "OBJECT expected on line 1".
В чем может быть проблемма/ошибка?
← →
Бурундук (2003-06-23 18:33) [15]Дфм-ка в текстовом представлении должна начинаться
со слова object (или inherited).
Посмотри в текстовом редакторе эту дфм-ку - может, в ней
какая-то ошибка (например, ты её по случайности в
бинарный формат перевела)
← →
Юрий Зотов (2003-06-23 18:40) [16]> Минина Карина © (23.06.03 17:26)
Скорее всего, Бурундук прав - виноват файл DFM. Надо было бы привести хотя бы первую его строку, раз уж парсер ругается именно на нее.
← →
Минина Карина (2003-06-23 19:02) [17]2Юрий Зотов © (23.06.03 18:40)
В том то и дело, что я эту ДФМ-ку скопировала с Вашего примера.
Привожу её целиком.
object Form1: TForm1
Left = 242
Top = 177
Width = 324
Height = 134
Caption = "Form1"
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = "MS Sans Serif"
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 120
TextHeight = 16
object Memo1: TMemo
Left = 8
Top = 4
Width = 185
Height = 89
Lines.Strings = (
"Memo1")
TabOrder = 0
end
object Button1: TButton
Left = 216
Top = 36
Width = 75
Height = 25
Caption = "Button1"
TabOrder = 1
OnClick = Button1Click
end
end
← →
Минина Карина (2003-06-23 19:08) [18]В хелпе я нашла вот такой пример:
function StringToComponent(Value: string): TComponent;
var
StrStream:TStringStream;
BinStream: TMemoryStream;
begin
StrStream := TStringStream.Create(Value);
try
BinStream := TMemoryStream.Create;
try
ObjectTextToBinary(StrStream, BinStream);
BinStream.Seek(0, soFromBeginning);
Result := BinStream.ReadComponent(nil);
finally
BinStream.Free;
end;
finally
StrStream.Free;
end;
end;
А это попытки воспользоваться этой функцией.
procedure TFormLoader.btnComponentToStringClick(Sender: TObject);
var NewComponent:TControl;
begin
NewComponent:= TControl.Create(self);
//TControl(NewComponent).Parent:=FormLoader;
NewComponent:=TControl(StringToComponent(mmComponentLikeString.Lines.Text));
{With NewComponent do begin
Parent:=FormLoader;
end;}
end;
Содержание mmComponentLikeString.Lines.Text:
object Memo1: TMemo
Left = 8
Top = 4
Width = 185
Height = 89
Lines.Strings = (
"Memo1")
TabOrder = 0
end
В результате ругается, что
EInvalidOperation with message "Control "Memo1" has no parent window" :(((
Подскажите, пожалуйста, в чём ошибка.
Буду очень признательна)
← →
reonid (2003-06-23 20:03) [19](aka Бурундук)
Код, приведённый Юрием Зотовым, работает нормально
с приведённой тобой ДФМ-кой. (У меня. по крайней мере).
Твой код не работает потому, что для установки св-ва
Lines для объекта TMemo требуется родительское окно.
NewComponent:= TControl.Create(self);
// Никогда так не делай.
// Какой смысл создавать объект типа TControl ?
// Ты должна была создать TMemo
//TControl(NewComponent).Parent:=FormLoader;
Но то, что ты здесь создала, не имеет никакого значения,
поскольку внутри StringToComponent
компонент будет создан заново:
Result := BinStream.ReadComponent( nil);
И у него никакого родительского окна нет.
Для того, чтобы прочитать из потока уже созданный
компонент, необходимо передать его в ReadComponent
вместо nil.
← →
Минина Карина (2003-06-24 16:46) [20]Всем спасибо за вашу помощь!
Я наконец-то разобралась, в чём проблемма.
Привожу исправленный исходник:
var
_New:TMemo;
....
procedure TFormLoader.btnComponentToStringClick(Sender: TObject);
begin
if Assigned(_New) then _New.Free;
_New:=TMemo.Create(self);
_New.Parent:=FormLoader;
_New:=TMemo(StringToComponent(mmComponentLikeString.Lines.Text));
end;
← →
Andrey (2003-06-25 19:30) [21]Еще пару вопросов.
1. У TReader есть два события OnFindComponentClass и OnFindMethod. Но в функции TMemoryStream.ReadComponent это самый TReader создается динамически и присвоить этим событиям какие-то обработчики нет никакой возможности. А перекрывать TMemoryStream.ReadComponent ой как нехочется. Может посоветуете другой метод. Чтоб и TMemoryStream.ReadComponent не перекрывать и в OnFindComponentClass и OnFindMethod можно было обработчики присвоить.
2. Опять же про обработчики.
все как в примере Юрий Зотов © (15.06.03 19:35)
if not Assigned(SomePanel) then
begin
SomePanel:=TPanel.Create(Form1);
SomePanel.Parent:=Form1;
end;
try
MemoryStream.ReadComponent(SomePanel)
except
SomePanel.Free;
raise;
end
а DFM который у меня имеется в MemoryStream выглядит вот так
object Panel1: TPanel
Left = 0
Top = 0
Width = 536
Height = 100
Align = alTop
TabOrder = 1
object Button2: TButton
Left = 200
Top = 10
Width = 75
Height = 25
Caption = "Button2"
TabOrder = 0
OnClick = Button1Click
end
end
в Form1 существует процедура Button1Click. Но постоянно выдается ошибка "Error reading Button2.OnClick: Invalid property value".
Покопавшись в исходниках я пришел к выводу, что Reader ищет их не в published-методах Form1, а в published-методах Panel1. Отсюда вопрос: как загрузить DFM в которой корневой объект не наследник от TForm?
В принципе это можно было бы сделать если TReader-у определить OnFindMethod, но это уже больше к первому вопросу.
>reonid © (16.06.03 18:29)
>Поэтому все созданные визуально в Дизайн-тайм или занесённые
>программистом в секцию published (не как св-ва, а как поля)
>компоненты не нуждаются в RegisterClass.
Хм... А у меня на форме лежит один TButton и пока я не неписал в OnCreate формы RegisterClasses([ TButton, TPanel]) вылезала ошибка про неизвестный класс.
P.S. Очень интересная тема.
← →
reonid (2003-06-25 21:34) [22]1. Для этого нужно самому создать Reader читать с его помощью:
Reader := TReader.Create(BinStream, 4096);
Reader.OnFindMethod := ...;
...
try
BinStream.Position := 0;
//BinStream.ReadResHeader; // Если ДФМ-ка изначально
// бинарная, а не текстовая
// RootComp := TXXX.Create(...);
// RootComp.Parent := ...;
RootComp := Reader.ReadRootComponent(nil{RootComp});
finally
Reader.Free;
end;
2. На самом деле Ридер ищет обработчики (и классы) в РТТИ-таблицах корневого компонента, кторый читается с помощью
Reader.ReadRootComponent.
Просто в исходном примере это была форма, и я, чтобы не усложнять, так и сказал.
← →
777 (2003-06-25 21:47) [23]Может я чего-то не понял, нооооооооооооо
http://www.ua7.net/resume/
ууууууууууууууууууууууу
весело однако %)
← →
reonid (2003-06-26 02:10) [24]2Andrey © (25.06.03 19:30)
Метод TReader.ReadRootComponent заточен, в основном,
именно под формы (ну там ещё датамодули).
При чтении же других компонент с помощью этого метода
возникают разные проблемы - как мы уже здесь видели.
Для таких случаев лучше, как мне кажется,
использовать TReader.ReadComponent:
procedure TForm1.BCreateClick(Sender: TObject);
var
TxtStream, BinStream: TStream;
Reader: TReader;
Comp: TComponent;
begin
TxtStream := TMemoryStream.Create;
try
MemoDfm.Lines.SaveToStream(TxtStream);
// В MemoDfm хранится ДФМ - конкретно твоя панель
BinStream := TMemoryStream.Create;
try
Reader := TReader.Create(BinStream, 4096);
TxtStream.Position := 0;
ObjectTextToBinary(TxtStream, BinStream);
try
BinStream.Position := 0;
Reader.Owner := Self;
Reader.Parent := Self;
Reader.Root := Self;
// Владелец, родитель для создаваемого из потока
// компонента и корневой компонент (в РТТИ-таблицах
// которого ридер будет искать обработчики событий,
// классы и т.д.)
Reader.OnSetName := {Self.}ReaderSetName;
// Для разрешения конфликтов имён
Reader.BeginReferences;
try
Reader.ReadSignature;
Comp := Reader.ReadComponent(nil);
Reader.FixupReferences;
// инициализирует свойства-ссылки на другие
// компоненты, хранящиеся в ДФМ как имена
finally
Reader.EndReferences;
end;
finally
Reader.Free;
end;
finally
BinStream.Free;
end;
finally
TxtStream.Free;
end;
end;
procedure TForm1.ReaderSetName(Reader: TReader; Component: TComponent;
var Name: string);
begin
if Reader.Owner.FindComponent(Name) <> nil then Name := "";
// Это самое простое, что можно придумать, чтобы избежать конфликта имён.
// Всё равно в рантайме имена не слишком важны
end;
← →
Andrey (2003-06-26 12:35) [25]>777
:) там внизу еще и ссылка на форум есть :)
>reonid
Агромаднейшее спасибо. Теперь осталось еще в Reader.Root присвоить объект из динамически загружаемой Dll и механизм настраиваемых плагинов на 95% готов :)
← →
Юрий Зотов (2003-06-26 13:34) [26]> Andrey © (26.06.03 12:35)
Ну, положим, с DLL могут возникнуть проблемы несовместимости одинаковых классов, а вот если вместо DLL использовать BPL, то можно считать, что уже не 95, а 99%.
← →
Andrey (2003-06-26 16:53) [27]>Юрий Зотов (26.06.03 13:34)
Непонятно. Немогли бы вы объяснить более детально.
У меня возникла другая проблемма.
Dll-ка
library SomeDll;
uses
SysUtils,
Classes,
Dialogs,
ExtCtrls,
StdCtrls;
type
TSomeComp = class(TComponent)
published
procedure ShowSomeForm(Sender: TObject);
end;
var SomeComp: TSomeComp;
procedure TSomeComp.ShowSomeForm(Sender: TObject);
begin
ShowMessage("Ура!!!!");
end;
function CreateSomeComp: TSomeComp; cdecl;
begin
SomeComp:=TSomeComp.Create(nil);
Result:=SomeComp;
end;
procedure FreeSomeComp; cdecl;
begin
SomeComp.Free;
end;
exports CreateSomeComp, FreeSomeComp;
begin
RegisterClasses([TPanel, TButton]);
end.
Проэкт:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
BLoad: TButton;
BFree: TButton;
procedure BLoadClick(Sender: TObject);
procedure BFreeClick(Sender: TObject);
procedure CreatePanelFromFile(DfmFile: string);
private
public
end;
var
Form1: TForm1;
FromDFMComp, CodeContainerComp: TComponent;
h: integer;
CreaProc: function: TComponent; cdecl;
FreeProc: procedure; cdecl;
implementation
{$R *.dfm}
procedure TForm1.CreatePanelFromFile(DfmFile: string);
var FileStream: TFileStream;
BinStream: TMemoryStream;
Reader: TReader;
begin
BinStream:=TMemoryStream.Create;
try
FileStream := TFileStream.Create(DfmFile, fmOpenRead or fmShareDenyWrite);
try
ObjectTextToBinary(FileStream, BinStream)
finally
FileStream.Free
end;
BinStream.Position := 0;
Reader := TReader.Create(BinStream, 4096);
try
Reader.Owner:=Self;
Reader.Parent:=Self;
Reader.Root:=CodeContainerComp;
// (*Reader.OnSetName := {Self.}ReaderSetName;*)
// пока случаи с конфликтами имен я не рассматриваю
Reader.BeginReferences;
try
Reader.ReadSignature;
FromDFMComp:=Reader.ReadComponent(nil);
Reader.FixupReferences;
finally
Reader.EndReferences;
end
finally
Reader.Free;
end;
finally
BinStream.Free
end
end;
procedure TForm1.BLoadClick(Sender: TObject);
begin
h:=LoadLibrary(PChar("с:\SomeDll.dll"));
if h>HINSTANCE_ERROR then
begin
CreaProc:=GetProcAddress(h, "CreateSomeComp");
FreeProc:=GetProcAddress(h, "FreeSomeComp");
CodeContainerComp:=CreaProc;
end;
CreatePanelFromFile("c:\SomeDFM.dfm");
end;
procedure TForm1.BFreeClick(Sender: TObject);
begin
FromDFMComp.Free;
FreeProc;
FreeLibrary(h);
end;
end.
При вызовеFromDFMComp:=Reader.ReadComponent(nil);
выскакивал Access Violation. После некоторых исследований VCL было выяснено: функция GetFieldClassTable (в GetFieldClass) получает неправильный указатьель на VMT TSomeObject. Результатом является Access Violation. Чесно говоря причина мне непонятна, но все заработало после того как проэкт и библиотека были скомпилированы with Runtime Packages...
Может быть при Runtime Packages совместно (проэктом и библиотекой) используется э...даже незнаю как сказать... таблица зарегестрированых классов... Даже незнаю существует ли такая.
В принципе это все не проблемма т.к. у меня текущий проэкт собирается весь with Runtime Packages, но для э...полного прояснения ситуации, хотелось бы послушать как вы (reonid, Юрий Зотов,...) прокоментируете такое поведение проэкта.
← →
Юрий Зотов (2003-06-26 17:07) [28]Все просто. DLL и Exe компилируются, как два совершенно раздельных проекта, которые друг о друге ничего не знают. Поэтому в каждом из них для какого-либо класса TSomeClass компилятор создает свою собственную VMT, свою RTTI и пр. В итоге получается, что TSomeClass в Exe и тот же самый TSomeClass в DLL - это два совершенно разных класса. Вот поэтому я и написал: "могут возникнуть проблемы несовместимости одинаковых классов".
Но если этот же TSomeClass помещен в BPL (т.е. в пакет), а Exe и DLL откомпилированы с этим пакетом ("with Runtime Packages"), то тогда и в Exe, и в DLL этот самый TSomeClass действительно будет одним и тем же классом. Еще точнее, при этом он уже будет и не в Exe, и не в DLL, а в этом самом ОБЩЕМ для всех них пакете. Вот почему я и написал - "вместо DLL использовать BPL".
← →
Дебил (2003-06-26 17:59) [29]новая Лана
← →
Andrey (2003-06-26 19:41) [30]>Юрий Зотов
Хм... Понятно. Но, exe совершенно незачем изначально знать какие классы будут реализованы в Dll... Да и в случае использования этого механизма для реализации плагинов exe и неможет знать о тех классах которые реализованы в Dll. О этих классах может знать только сама Dll и DFM соответствующий объекту Root. И по этому RegisterClasses нужно запускать только в Dll.
Наверно в таком случае лучше перевести загрузку DFM полностью в Dll и передавать ей Owner-а и Parent-а.
← →
Ученик (2003-06-26 22:46) [31]>reonid © (16.06.03 18:29)
procedure TForm1.Button1Click(Sender: TObject);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create("c:\test.dfm", fmCreate);
try
Stream.WriteComponent(Self)
finally
Stream.Free
end
end;
procedure TForm1.Button2Click(Sender: TObject);
var
Stream: TFileStream;
Form2: TForm1;
begin
Stream := TFileStream.Create("c:\test.dfm", fmOpenRead);
try
Form2 := TForm1.CreateNew(Self);
try
Stream.ReadComponent(Form2);
Form2.ShowModal
finally
Form2.Free
end
finally
Stream.Free
end
end;
Сначала нажимаем на Button1, потом на Button2, не работает, ругается при Form2.ShowModal, что не так ?
← →
nikkie (2003-06-26 23:21) [32]>777
>Может я чего-то не понял
это просто Сатир развивает свою идею...
http://delphimaster.net/view/6-1055432687/
>Дебил
>новая Лана
нуу... уровень знаний Ланы Розановой и Мининой Карины просто не сравнимы.
но я вот чего понять не могу - зачем вам-то шифроваться, уважаемый ДедушкаКо?
Страницы: 1 вся ветка
Форум: "Потрепаться";
Текущий архив: 2003.07.14;
Скачать: [xml.tar.bz2];
Память: 0.65 MB
Время: 0.009 c