Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Exe, dll и соединение с БД


Автор: Serggggg 15.6.2004, 11:23
И ещё раз здравствуйте!
Приключения продолжаются. Возникают все новые и новые проблемы.
1. Handle базы данных все-таки передается в dll – для этого надо в uses подключить BDE – тогда Delphi распознают тип hDBIDb. У меня есть пара обработчиков, связанных с таймером:
Код

// Это после соединения с БД в основной программе.
procedure TFMain.DBAfterConnect(Sender: TObject);
begin
ConnTime:=Time;   //Засекается время
Tmr.Enabled:=True;  //Врубается таймер
StatBar.Refresh;  //Обновляется строка статуса
end;

// А это обработчик OnTimer
procedure TFMain.TmrTimer(Sender: TObject);
begin
// Вычисляется время соединения с БД
StatBar.Panels[2].Text:=' Время соединения:  ' + TimeToStr(Time-ConnTime);
StatBar.Refresh;  // Обновляется строка статуса
end;

Без подключения в список USES модуля BDE все это работало. Теперь – ошибка. Судя по всему – при обращении к виндовой функции Time.
‘(’ expected, but ‘;’ found
Вопрос следующий: Что это за глюк и как с этим бороться?

2. Как я уже сказал, в dll передается хэндлер БД. Строка вызова процедуры такая:
Код

// Application и Screen передаются для нормальной работы дочерних MDI-форм
InitPlugin(integer(Application), integer(Screen), DB.Handle);

После этого в самой DLL внутри InitPlugin происходит присвоение глобальной переменной типа hDBIDb нашего хэндлера (дескриптора).
После InitPlugin срабатывает такая функция
Код

function CreateMDI: integer; StdCall;
begin
// Создали дочернее MDI-окно
Result:=integer(TFComp.Create(Application));
// А теперь компоненту TQuery на этой форме прописываем сей дескриптор
FComp.Query1.Database.Handle:=HDB_;
end;

Интересно то, что работает только строка
FComp.Query1.Database.Handle:=HDB_;
Строчка FComp.Query1.DBHandle:=HDB_; выдает ошибку – нельзя присвоить что-то свойству объекта, которое ReadOnly

После этого (т.к. форма MDI – после Create она сразу становится видимой) в обработчике OnShow происходит открытие запроса.
Но до метода OnShow мы не добираемся – при присвоении дескриптора вылетает Access Violation……
Вопрос тот же: Что это за глюк и как с этим бороться?
Огромное спасибо за внимание. Подскажите, кто знает.

Автор: Pakshin A. S. 15.6.2004, 12:28
Свойство доступно только для чтения!!! Присваивание не покатит!

Вообще: забей на dll: засунь форму в приложение, настрой связи и работай спокойно...

Автор: Serggggg 15.6.2004, 12:36
Pakshin A. S., я бы с удовольствием, но НАДО сделать это через DLL - если бы все было так просто, я бы и не спрашивал.

Автор: Pakshin A. S. 15.6.2004, 12:43
По-моему, нужна связь такая:
Database1.DataSource -> DataSource1 <- Query1.DataSource
(*Мог и ошибку написать, работаю по памяти*)
Следовательно надо создать переменную
DS: TDataSource;

вызов процедуры ....(...DataSource1...);

в процедуре
YourForm.Query1.DataSource:=DS;

P. S.
Интересная задачка, надо будет теорию на практику переложить. Получится - выложу.
Добавлено @ 12:53
Даа, давно не работал с БД, такую фигню написать.

В Query заполни только DatabaseName ( тип string)
В Database есть то самое DatabaseName. => выбираешь Alias, заполняешь DatabaseName и запоминаешь, передаешь в Dll, заполняешь в Query и должно заработать...

и ещё
Может делать отдельный коннект?

Автор: Serggggg 15.6.2004, 14:02
Если не юзать MDI, то элементарно!
В таком случае нужно передать в DLL login и password и законнектиться автоматом (в DLL компоненте TDatabase присваивается LoginPromt=False, забиваются полученные параметры login и password, а потом Connected:=True - работает, как часы).
А при работе с MDI это не совсем удобно. Чтобы законнектиться, надо сделать компоненте TDatabase Create. Это возможно при создании формы. А при создании MDI-формы её уже не спрячешь. Пихать по 1 компоненту TDatabase на каждую форму, ИМХО, не совсем рационально.

А по поводу передачи датасетов - сейчас буду пробовать. Спасибо.

Автор: Pakshin A. S. 15.6.2004, 14:18
Не Сет'ы, а Имя Базы Данных в компоненте DataBase
Добавлено @ 14:19
Может это одно и то же?!

Автор: Serggggg 15.6.2004, 15:16
Именно DataBaseName предлагаешь передать?
sad.gif Пробовал - не работает.
Кто-то когда-то говорил мне, что под dll выделяется другое адресное пространство, а для нормальной работы с соединением надо, чтобы было одно адресное пространство. В принципе, я полагал, что передача TScreen и TApplication эту проблему решат, но, видимо, ошибался.
Короче, я в тупике. butbut.gif

Автор: Pakshin A. S. 15.6.2004, 17:17
Может наладить мосты с робительским окном напрямую, ведь у MDI-формы есть "родитель", к которому можно свободно обращаться... А в родителе есть компоненты доступа к БД и т. п...
Добавлено @ 17:25
Ещё идея.
В DLL делаешь фенкцию с возвратом TFrom. У любой формы есть такая штука: MDIChildren[i:integer]:TFrom. Значит управлять дочерней формой можно из родительской, а самое главное - это проще!

Автор: Serggggg 16.6.2004, 08:14
Pakshin A. S., буду весьма благодарен, если поможешь куском кода в качестве примера.
А последняя твоя идея весьма неплоха. Единственное - как именно? Я первый разработаю с MDI в DLL.

Автор: Pakshin A. S. 16.6.2004, 10:23
Код

(*DLL*)
function ShowForm(<>):TForm;
<>
begin
<>
Result:=YourForm;
end;

(*EXE*)
// Есть свойство, отвечающее за кол-во MDI-форм
Form1.MDIChildren[10]:=ShowForm(<>);
<>
Form1.MDIChildren[10].Query1...


Идея такова...

Автор: Serggggg 16.6.2004, 17:34
Выход из ситуации был найден. Только, правда, пришлось задействовать 2 коннекта. baaa.gif
Если кому пригодится, буду очень рад.
Итак, внутри dll объявляется переменная типа TDatabase, которая в дальнейшем коннектится с сервером и позволяет нормально работать всем запросам и таблицам, находящимся внутри dll. Привожу мой код. Если кто-то найдет более рациональное решение, поделитесь пожалуйста.

EXE-файл, главный модуль.
Код

// Объявляем типы процедур: инициализация dll, выход из нее, создание окна
type
 TInitProc   = procedure(App, Scr: integer; log, pass: string); StdCall;
 TDoneProc   = procedure; StdCall;
 TCreateProc = function: integer; StdCall;
..........
// Секция PRIVATE
private
   LibHandle  : THandle;
   InitPlugin : TInitProc;
   DonePlugin : TDoneProc;
   CreateMDI  : TCreateProc;
..........
procedure TFMain.MTopCompClick(Sender: TObject);
begin
// Только при условии, что мы соединены с БД
if DB.Connected then begin
   // Загружаем dll в память
   LibHandle := LoadLibrary(PChar('Topics.dll'));
   // Определяем адреса нужных процедур
   @InitPlugin := GetProcAddress(LibHandle, PChar('InitPlugin'));
   @DonePlugin := GetProcAddress(LibHandle, PChar('DonePlugin'));
   @CreateMDI  := GetProcAddress(LibHandle, PChar('CreateMDI'));
   // Запускаем процедуру инициализации, передаем LOGIN и PASSWORD
   InitPlugin(integer(Application), integer(Screen), DB.Params.Values['USER NAME'], DB.Params.Values['PASSWORD']);
   // А это флаг того, что библиотека загружена в память
   TopFlag:=True;
   // Запускаем процедуру создания окна
   CreateMDI;
end
else ShowMessage('Установите соединение с БД!');
end;
..........
// А это происходит при выгрузке главной формы из памяти
procedure TFMain.FormDestroy(Sender: TObject);
begin
// Если dll была загружена, то она освобождается
if TopFlag=True then begin
   DonePlugin;
   FreeLibrary(LibHandle);
   end;
end;


Теперь то, что происходит внутри dll (код целиком):
Код

library Topics;

uses
 SysUtils,
 DB,
 DBTables,
 DBGrids, // Кстати, без этого модуля нельзя присвоить TDatabase свойство Connected=True - вылетит ошибка
 Forms,
 Dialogs,
 Classes,
 Comp in 'Comp.pas' {FComp};

{$R *.res}

var
TopDB :TDatabase;
DA : TApplication;
DS : TScreen;
Flag : Boolean;

procedure Connect(login,password:string);
begin
with TopDB do begin
// Присваиваем все необходимые параметры
 AliasName:='TKD';
 DatabaseName:=AliasName;
// Окно диалога нам не нужно
 LoginPrompt:=False;
// Зачистка параметров
 Params.Clear;
// Добавляем полученные из основной программы LOGIN и PASSWORD
 Params.Add('USER NAME=' + login);
 Params.Add('PASSWORD=' + password);
 Connected:=True;
// Законнектились и устанавливаем признак коннекта  равным True
 Flag:=True;
end;
end;

procedure InitPlugin(App, Scr: integer; log, pass: string); StdCall;
var DA: TApplication;
     DS: TScreen;
begin
// Переприсваиваем значения Screen и Application из полученных
DS:=Screen;
Screen:=TScreen(Scr);
DA:=Application;
Application:=TApplication(App);
// Если соединение ещё не устанавливалось
if Flag=False then begin
   TopDB:=TDatabase.Create(Application);
   Connect(log, pass);
   end;
end;

procedure DonePlugin; StdCall;
begin
// Присваиваем Screen и Application обратно
Screen:=DS;
Application:=DA;
end;

function CreateMDI: integer; StdCall;
begin
// Просто создаем MDI-окно - соединение с БД УЖЕ есть - при старте окно само откроет запрос
Result:=integer(TFComp.Create(Application));
end;

exports
InitPlugin,
DonePlugin,
CreateMDI;

begin
end.


Надеюсь, кому-то это поможет. cool.gif Pakshin A. S., спасибо за участие.

Автор: Pakshin A. S. 16.6.2004, 17:49
Всегда рад подкинуть идейки... если что, обращайся. biggrin.gif

Автор: _hunter 17.6.2004, 20:08
Сорри что раньше не писал ( сесия sad.gif )

ссылка на DataSource ( о которой я раньше писал ) -- просто параметр типа Db::TDataSource
т.е. создаеш где-то DataSource ( можно и на форме )
и передаеш в dll его
в dll: Query.DataSourc = DataSource ( ссылка )

должно работать

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)