Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 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
14-44848
Barlok
2003-06-25 23:39
2003.07.14
Углы треугольника?


1-44657
Manfred
2003-07-02 11:24
2003.07.14
Вызов прцедур во время события OnCreate()


1-44660
Mishenka
2003-06-30 00:13
2003.07.14
Изменение разрешения...


1-44698
Mishenka
2003-06-30 15:13
2003.07.14
MaskEdit...


14-44847
sapsi
2003-06-26 08:35
2003.07.14
Ваши впечатления





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский