C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Можно ли инкапсулировать класс в dll?, Можно ли инкапсулировать класс в dll?
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 — пудрим мозги линкеру biggrin.gif


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, и его уже преобразовываю к типу классу.
Но у тебя, видимо, причина в другом, и я не знаю sad.gif
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 smile.gif ?
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, а что это такое враппер? smile.gif
klen
Отправлено: 05.03.2006, 11:15


Машинист паровоза

Группа: Участник
Сообщений: 239



Че такое врапер?
Это экспорт из dll всех методов касса. Вобщем всерано чер задницу получается, как бута и класа никакого нет. Но в результате не нада клиена dll пересобирать — dll поправил и все (интерфейс то тот же осталься)

тут ведь какая концептуальная проблема:
1. Если использовать либы для dll:
a. Адреса методов и свойств класса узнаются из либы автоматом и линкер собирает клиентское по отношении к dll приложение. Но если вы dll изменили то поползли адреса и тд -> нада с НОВОЙ!!! либой собирать клиента что не удобно. Теряется смысл dll (разве что только модульность).
2. Если использовать динамическую загрузку то клиенту знания указателя на клас МАЛО sad.gif , исходя из хидерника он не найдет адреса методов и свойств. Поэтому приходится делать экспортные копии 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?

Вернуться в Вопросы программирования в C++Builder