dvv |
Отправлено: 17.02.2006, 10:42 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 43
|
Например, я хочу создать класс TMyRichEdit, который
будет выполнять все функции стандартного TRichEdit
и при нажатии на нем правой кнопкой мыши будет выдавать
сообщение о том, что это мой RichEdit.
Допустим я определяю класс:
CODE | class TMyRichEdit : public TRichEdit
{
public:
__fastcall TMyRichEdit(TComponent* AOwner)
:TRichEdit(AOwner) {OnMouseDown = RE_OnClick;};
void __fastcall RE_OnClick(TObject *Sender,
TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if(Button==mbRight)
ShowMessage("Привет, это мой рич-едит!");
};
};
|
Насколько я знаю, в dll можно помещать функции. А можно ли
поместить в dll целый класс?
Я хочу поместить в dll все, что касается определения класса.
Что бы из программы вызывать класс как-нибудь так:
CODE |
TMyRichEdit* Re=new TMyRichEdit(Form1);
Re->Parent=Form1;
Re->Lines->Text="Всем большой привет!";
|
Можно ли это сделать? Как потом компилировать программу,
как вызывать этот класс?
|
|
Schumi |
Отправлено: 17.02.2006, 13:06 |
|
Машинист паровоза
Группа: Участник
Сообщений: 206
|
У меня только что возникла похожая задача.
Хочу сделать так: в длл реализовать класс, а экспортировать только 2 функции — первая будет создавать в длл объект класс и возвращать его указатель, а вторая — удалять объект по указателю.
Соответственно в exe объявляю указатель на класс (в хедере описан этот класс). Гружу длл, экспортирую функции, и создаю объект.
Ну это в теории должно быть.
В exe линковщик говорит "Unresolved external" везде, где только есть обращения к методам класса.
Я не очень представляю, как правильно, но понятно, что реализованы методы ведь в длл.
И как же все же правильно реализовать класс в длл, а потом использовать его в ехе?
|
|
Doga |
Отправлено: 17.02.2006, 14:40 |
|
Мастер участка
Группа: Участник
Сообщений: 575
|
extern — пудрим мозги линкеру
CODE |
Category
Storage class specifiers
Syntax
extern <data definition>;
[extern] <function prototype>;
Description
Use the extern modifier to indicate that the actual storage and initial value of a variable, or body of a function, is defined in a separate source code module. Functions declared with extern are visible throughout all source files in a program, unless you redefine the function as static.
The keyword extern is optional for a function prototype.
Use extern "c" to prevent function names from being mangled in C++ programs.
|
|
|
Schumi |
Отправлено: 17.02.2006, 14:59 |
|
Машинист паровоза
Группа: Участник
Сообщений: 206
|
Doga, я, конечно, здесь слаб в мат. части, но не думаю что это правильно.
Я гружу библиотеку динамически, а если мне нужно будет работать с несколькими объектами этого класса?
Пока как сделал — просто в определении класса задал открыте методы виртуальными.
"Вроде" работает, но как-то меня это смущает...
ЗЫ: dvv, извини, что влез в чужой топик, но мне кажется мой вопрос как раз в тему...
Отредактировано Schumi — 17/02/2006, 15:02
|
|
dvv |
Отправлено: 25.02.2006, 18:47 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 43
|
Англичанин я никакой, то что написанно по английски про extren ничего не получилось. А вот virtual — сработало. Большое спасибо.
|
|
dvv |
Отправлено: 28.02.2006, 22:22 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 43
|
Не все так просто, как может показатся с первого раза.
Вот проект моей DLL. Файл _ClassDll.cpp, тут описываются точки входа в библиотеку.
CODE | #include <windows.h>
#pragma hdrstop
#include "RRichEdit.hpp"
#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
//---------------------------------------------------------------------------
// Функция создает мой класс и возвращает указатель на него
//---------------------------------------------------------------------------
extern "C" __declspec(dllexport) RRichEdit* __stdcall RRichEditCreate(TComponent* AOwner)
{
RRichEdit* RE=new RRichEdit(AOwner);
return RE;
}
|
Файл RRichEdit.сpp, здесь находится описание класса:
CODE |
#include <ComCtrls.hpp>
//---------------------------------------------------------------------------
// Описание компонетта RRichEdit
//---------------------------------------------------------------------------
class __stdcall RRichEdit : public TRichEdit
{
public:
__fastcall RRichEdit(TComponent* AOwner);
virtual void __fastcall SetPrivet();
};
|
Файл RRichEdit.сpp, здесь описываются методы моего класса:
CODE |
#include "RRichEdit.hpp"
//---------------------------------------------------------------------------
// Конструктор компонента RRichEdit
//---------------------------------------------------------------------------
__fastcall RRichEdit::RRichEdit(TComponent* AOwner):TRichEdit(AOwner)
{
Parent=(TWinControl*)AOwner;
}
//---------------------------------------------------------------------------
// Метод компонента RRichEdit
//---------------------------------------------------------------------------
void __fastcall RRichEdit::SetPrivet()
{
Lines->Text="Всем очень большой, горячий и пламенный привет!!";
}
|
Если в свойствах проекта стоит галка "Build with runtime packeges", то проблем пока не замечено. Стоит эту галку снять (не хочу я пакеджей), как начинаются проблемы. Линкер выдает кучу ошибок вида:
CODE |
Unresolved external '__tpdsc__ Comctrls::TRichEdit' referenced from F:\...\RRICHEDIT.OBJ
Unresolved external 'Comctrls::TRichEdit::' referenced from F:\...\RRICHEDIT.OBJ
Unresolved external '__fastcall Stdctrls::TCustomEdit::DefaultHandler(void *)' referenced from F:\...\RRICHEDIT.OBJ
Unresolved external '__fastcall Comctrls::TCustomRichEdit::~TCustomRichEdit()' referenced from F:\...\RRICHEDIT.OBJ
Unresolved external '__fastcall Stdctrls::TCustomMemo::~TCustomMemo()' referenced from F:\...\RRICHEDIT.OBJ
Unresolved external '__fastcall Controls::TWinControl::~TWinControl()' referenced from F:\...\RRICHEDIT.OBJ
Unresolved external '__fastcall Controls::TControl::~TControl()' referenced from F:\...\RRICHEDIT.OBJ
...
|
Видно, что линкер не хочет хватать некоторые модули. Какие, почему не хочет? Как заставить подхватить эти модули? Как мне скомпилировать dll-ку без пакеджей? |
|
Schumi |
Отправлено: 01.03.2006, 09:05 |
|
Машинист паровоза
Группа: Участник
Сообщений: 206
|
У меня работает без пакетов, правда мой класс не vcl и никаких parent у него нет.
Единственное, что я использую менеджер:
CODE |
USELIB("..\..\..\Lib\memmgr.lib");
USELIB("..\..\..\Lib\usebormm.lib");
|
И еще — я в своих функциях создания/удаления объекта передаю в качестве параметра указатель на void, и его уже преобразовываю к типу классу.
Но у тебя, видимо, причина в другом, и я не знаю
|
|
olegenty |
Отправлено: 01.03.2006, 09:52 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
на xportal тема плотно и много обсуждалась
IMHO непонятно, зачем выносить класс в DLL, тогда уж не лучше ли вынести его в LIB? DLL, экспортирующую функции, можно юзать откуда угодно, в то время как DLL, экспортирующую классы — только при работе с тем же компилятором, с которым компилировалась DLL (т.е. эта DLL "неявно" превращается в ту же LIB).
Отредактировано olegenty — 01/03/2006, 10:55
|
|
Schumi |
Отправлено: 01.03.2006, 18:39 |
|
Машинист паровоза
Группа: Участник
Сообщений: 206
|
Раз уж пошла такая свистопляска, то хотел выяснить один момент (более или менее в топик подходит):
как создать в bcb статическую библиотеку, ну чтобы создавался один большой lib, а не один большой dll и маленький lib ?
|
|
dvv |
Отправлено: 04.03.2006, 09:39 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 43
|
Скомпилировать без пакеджей удалось, включив в проект библиотеку VCL.LIB. Но приложение все же не заработало.
Теперь, в конструкторе класса, во время выполнения строки: CODE | Parent=(TWinControl*)AOwner; |
Выдается сообщение QUOTE | Cannot assign a TFont to a TFont |
Причем если дополнить конструктор следующим образом:
CODE | Name="MyControl";
Parent=(TWinControl*)AOwner; |
то имя компоненту присваивается, а вот с Parent проблема.
Подскажите, в чем тут дело?
PS. У меня возникли сомнения, а возможно ли то что я делаю?
- Я вызываю в программе, которая имеет свою область памяти, некорую функцию передавая ей указатель на область памяти приложения.
- Функция пытается записать данные в область памяти, которая ей не принадлежит.
Возможно проблема именно в этом. Использовании пакеджей равносильно использованию статической DLL.
Хотя тогда непонятно почему отрабатывает Name="MyControl"?
Отредактировано dvv — 04/03/2006, 10:59 |
|
klen |
Отправлено: 04.03.2006, 10:54 |
|
Машинист паровоза
Группа: Участник
Сообщений: 239
|
QUOTE (olegenty @ 01/03/2006, 09:52) | на xportal тема плотно и много обсуждалась
IMHO непонятно, зачем выносить класс в DLL, тогда уж не лучше ли вынести его в LIB? DLL, экспортирующую функции, можно юзать откуда угодно, в то время как DLL, экспортирующую классы — только при работе с тем же компилятором, с которым компилировалась DLL (т.е. эта DLL "неявно" превращается в ту же LIB). |
Это нужно при сопровождении проекта.
Например если класс собран в длл, и при работе приложения обнаружена ошибка то простой пересборкой dll и отправкой по епочте заказчику проблема решается. иначе приходится пересобирать все части проекта.
Я сам так и не смог засунуть класс в длл и вызывать его методы по его указателю. Приходится врапер писать. |
|
Schumi |
Отправлено: 04.03.2006, 16:16 |
|
Машинист паровоза
Группа: Участник
Сообщений: 206
|
klen, а что это такое враппер?
|
|
klen |
Отправлено: 05.03.2006, 11:15 |
|
Машинист паровоза
Группа: Участник
Сообщений: 239
|
Че такое врапер?
Это экспорт из dll всех методов касса. Вобщем всерано чер задницу получается, как бута и класа никакого нет. Но в результате не нада клиена dll пересобирать — dll поправил и все (интерфейс то тот же осталься)
тут ведь какая концептуальная проблема:
1. Если использовать либы для dll:
a. Адреса методов и свойств класса узнаются из либы автоматом и линкер собирает клиентское по отношении к dll приложение. Но если вы dll изменили то поползли адреса и тд -> нада с НОВОЙ!!! либой собирать клиента что не удобно. Теряется смысл dll (разве что только модульность).
2. Если использовать динамическую загрузку то клиенту знания указателя на клас МАЛО , исходя из хидерника он не найдет адреса методов и свойств. Поэтому приходится делать экспортные копии dll-функций которые вызывют методы класса внутри себя и которые GetProcAddrtess позволит получить (что я называю врапером). Но решена проблема с клиентом — его каждый раз пресобирать не нада. Вобщем опятже не то что хотели.
|
|
dvv |
Отправлено: 06.03.2006, 07:24 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 43
|
QUOTE | Поэтому приходится делать экспортные копии dll-функций которые вызывют методы класса внутри себя и которые GetProcAddrtess позволит получить | Что-то я не совсем понял. В вышеописанном примере есть функция, которая описана внутри DLL, и которая вызывает метод класса (конструктор) внутри себя CODE | //---------------------------------------------------------------------------
// Функция создает мой класс и возвращает указатель на него
//---------------------------------------------------------------------------
extern "C" __declspec(dllexport) RRichEdit* __stdcall RRichEditCreate(TComponent* AOwner)
{
RRichEdit* RE=new RRichEdit(AOwner);
return RE;
}
|
Где и каким образом мне нужно сделать экспортную копию этой dll-функции?
Адрес какой функции (RRichEditCreate или ее копии) мне получать через GetProcAddrtess? |
|