Форум — Ответы     (  К темам )
 ?  Andrew: Метод класса в DLL (24-01-2003 13:58:45)
Можно ли засунуть метод класса в DLL для динамического выбора различных методов обработки?
Из DLL должен быть доступ к свойствам класса.
 Георгий (24-01-2003 17:45:36)
DLL — часть Windows которая не обьектная. т.е. и DLL — не может содержать обьекты.
 Petro (24-01-2003 22:47:33)
2 Георгий:

Не гони, в длл можно описывать классы и создавать объекты. Можно все тоже самое, что и в обычном ехе.

2 Andrew:

Можно засунуть. Доступ к свойствам будет, если класс в длл описан. Но проще выбирать методы в основной проге.

Подробней напиши, может без длл можно обойтись.
 Георгий (26-01-2003 06:14:51)
Petro — покажи как ты динамически линкуешь такую "обьектную" DLL!
Как с помошью функций Win32 API получаеш доступ к конструкторам, особенно в случае работы с абстрактными классами!
Проще говоря — Не гони!!!
 Георгий (26-01-2003 06:19:36)
по поводу того, что понимать под DLL — DLL — это, то что поймёт любая программа, а не написанная только на C++ Builder X.XX
 Petro (26-01-2003 16:29:20)
2 Георгий

Стоп-стоп. :) Ты написал, что длл не может содержать объекты. Гон? Гон. :) Если ты говоришь об _экспорте_ объектов, об этом речи не было. Длл линкуется независимо от основной проги. Ты можешь экспортировать из длл все что имеет фикс. адрес — функции и переменные. И методы класса в том числе. И объекты. И получаешь доступ ко всему этому при помощи GetProcAddress. Другой вопрос — как их потом использовать в вызывающей проге.

>по поводу того, что понимать под DLL — DLL — это, то что поймёт любая >программа, а не написанная только на C++ Builder

Да, я примерно так и представлял себе. ;)
 Petro (26-01-2003 16:29:34)
2 Георгий

Стоп-стоп. :) Ты написал, что длл не может содержать объекты. Гон? Гон. :) Если ты говоришь об _экспорте_ объектов, об этом речи не было. Длл линкуется независимо от основной проги. Ты можешь экспортировать из длл все что имеет фикс. адрес — функции и переменные. И методы класса в том числе. И объекты. И получаешь доступ ко всему этому при помощи GetProcAddress. Другой вопрос — как их потом использовать в вызывающей проге.

>по поводу того, что понимать под DLL — DLL — это, то что поймёт любая >программа, а не написанная только на C++ Builder

Да, я примерно так и представлял себе. ;)
 Георгий (26-01-2003 18:37:32)
Нет ты покажи, как работаешь с обьектом (с помошью ф-ции GetProcAddress), который находится в DLL!
и как борешься с различным выравниванием полей (без знания структуры DLL).
и как экспортируешь метод класса, в отрыве от контекста!
А теперь по поводу вопроса:
"Можно ли засунуть метод класса в DLL для динамического выбора различных методов обработки?
Из DLL должен быть доступ к свойствам класса."
Фактически это — экспорт если не всего обьекта, то покрайней мере экспорт его методов подразумевался.
В лоб это решить нельзя (попробуй доказать обратное), а можно примерно так:
1. в конструкторе обьекта загрузить DLL и прочитать адрес ф-ции (ИМЕННО ФУНКЦИИ), котораю будет что-то обрабатывать.
2. в методе, который как бы в DLL, используя ,полученный на первом шаге, адрес вызвать функцию обработки из DLL.
3. в деструкторе выгрузить DLL.
Если это ТЫ Petro считаешь ЭКСПОРТОМ метода, то я сдаюсь.
Но даже, если предположить, что МЕТОДЫ можно хранить в DLL и потом насильно загружать в основную программу, то как быть с виртуальными методами, с системными таблицами адресов этих методов — их тоже надо вручную править???
 Devnvd (26-01-2003 20:10:23)
Приведу вам пример статического использования DLL
по поводу описания метода класса в DLL. Может быть можно было написать несколько иначе и проще. Но я не экспериментировал. Небыло необходимости.
Пример работающий.
Это будет реальной основой для продолжения дискуссии.



//========== ExpFun.dll ========


#include <vcl.h>

#include <windows.h>

#pragma hdrstop

//--------------------------------------------------


#pragma argsused

class TMyClass{

private:

int Member;

public:

__fastcall TMyClass(){Member=1234;}

__fastcall ~TMyClass(){};

int __import __stdcall exportfun();

int __fastcall localfun();

};

int __export __stdcall TMyClass::exportfun()

{

return Member;

}

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*
lpReserved)

{

return 1;

}

//---------------------------------------------------


//========== Unit1.cpp ==========

#include <vcl.h>

#pragma hdrstop


#pragma link "ExpFun.lib"


#include "Unit1.h"

//---------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm1 *Form1;

class TMyClass{

private:

int Member;

public:

__fastcall TMyClass(){Member=1234;}

__fastcall ~TMyClass(){};

int __import __stdcall exportfun();

int __fastcall localfun();

};

int __fastcall TMyClass::localfun()

{

return Member;

}

TMyClass MyClass;

//----------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

{

ShowMessage("Export Func="+IntToStr(MyClass.exportfun()));

ShowMessage("Local Func="+IntToStr(MyClass.localfun()));

}



 Devnvd (26-01-2003 20:55:10)
Приведу вам пример статического использования DLL
по поводу описания метода класса в DLL. Может быть можно было написать несколько иначе и проще. Но я не экспериментировал. Небыло необходимости.
Пример работающий.
Это будет реальной основой для продолжения дискуссии.

Что-то разбросало пример, сделаю компактнее вид и несколько понятнее.
//========== ExpFun.dll ========
#include
#include
#pragma hdrstop
//-------------------------------------
#pragma argsused
//Описание класса
class TMyClass{
private:
int Member;
public:
__fastcall TMyClass();
__fastcall ~TMyClass();
int __fastcall localfun();
int __import __stdcall exportfun();
};
//функция может использовать только члены класса
//Но не функции класса, реализованные в другом месте.
int __export __stdcall TMyClass::exportfun()
{
return Member;
}
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
//========== Unit1.cpp ==========
#include
#pragma hdrstop

#include "Unit1.h"
#pragma link "ExpFun.lib"
//----------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//Описание класса
class TMyClass{
private:
int Member;
public:
__fastcall TMyClass();
__fastcall ~TMyClass();
int __fastcall localfun();
int __import __stdcall exportfun();
};
//Здешние функции класса
__fastcall TMyClass::TMyClass(){Member=1234;}
__fastcall TMyClass::~TMyClass(){;}
int __fastcall TMyClass::localfun()
{
return Member;
}
//-----------------------------------------

TMyClass MyClass;
//-------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
//Вызываем функцию класса из DLL
ShowMessage("Export Func="+IntToStr(MyClass.exportfun()));
//Вызываем функцию класса здешнюю
ShowMessage("Local Func="+IntToStr(MyClass.localfun()));
}
 Devnvd (27-01-2003 09:58:01)
Теперь пример динамического использования DLL.
Один из методов класса описан в DLL.
Пример работающий.
//========== ExpFun.dll ========
#include
#include
#pragma hdrstop
//-----------------------------------------

#pragma argsused
class TMyClass{
private:
int Member;
public:
__fastcall TMyClass();
__fastcall ~TMyClass();
int __fastcall localfun();
int __fastcall exportfun(int param1, int param2);
};
int __export __fastcall TMyClass::exportfun(int param1,int param2)
{
return param1+param2+Member;
}
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
//========== Unit1.cpp ==========
#include
#pragma hdrstop

#include "Unit1.h"
//При статическом использовании ExpFun.dll
//#pragma link "ExpFun.lib"
//-------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
class TMyClass{
private:
int Member;
public:
__fastcall TMyClass();
__fastcall ~TMyClass();
int __fastcall localfun();
int __fastcall exportfun(int param1, int param2);
};
__fastcall TMyClass::TMyClass(){Member=1234;}
__fastcall TMyClass::~TMyClass(){;}
int __fastcall TMyClass::localfun()
{
return Member;
}
TMyClass MyClass;
//-------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
HINSTANCE h=LoadLibrary("ExpFun.dll");
if(h)
{
//Первый параметр в функции класса, это указатель на экземпляр класса
typedef int __fastcall (*TFunDll)(void *,int,int);
// Название искомой функции выглядит, не совсем приятно
TFunDll funDll=(TFunDll)GetProcAddress(h,"@TMyClass@exportfun$qqrii");
if(funDll)
{
int ResultDll=funDll(&MyClass,1,10); //1+10+MyClass.Member;
ShowMessage("Export Func="+IntToStr(ResultDll));
}
FreeLibrary(h);
}
// При статическом использовании ExpFun.dll
// ShowMessage("Local Func="+IntToStr(MyClass.exportfun(1,10)));
ShowMessage("Local Func="+IntToStr(MyClass.localfun()));
}
//==========================
Функцию можно расписать также и в Unit1.cpp
int __fastcall TMyClass::exportfun(int param1,int param2)
{
return -param1-param2-Member;
}
И использовать наравне с другими функциями класса

Выводы сделаете сами, что можно, а что нельзя делать в DLL.
 Andrew (27-01-2003 10:35:18)
Большое спасибо всем принявшим участие в ответе на вопрос. Код сейчас попробую.
To Devnvd : можно пожалуйста поподробнее по поводу "$qqrii" в строке
TFunDll funDll=(TFunDll)GetProcAddress(h,"@TMyClass@exportfun$qqrii");
 Devnvd (27-01-2003 17:08:15)
Это,,"@TMyClass@exportfun$qqrii", непонятное на первый взгляд имя вы можете узнать для своих функций посмотрев в файл ExpFun.def, созданный с помощью:

impdef.exe -h ExpFun.def Expfun.dll
Также для Exe:
impdef.exe -h Project1.def Project1.exe

В таком виде представляются CPP-ные функции после компиляции.
"ii" в конце это параметры функции.
Функция без параметров () имела бы "qqrv", v — void.
По поводу оставшихся qqr вам нескажу. Но если вас интересует, то пробуйте разные опции компилятора "Advanced Compiler" касаемые Calling convention.

Если в Unit1.cpp добавить к какой-нибудь функции "__export":
int __export __fastcall TMyClass::localfun()
{
return Member;
}
то в DLL вы сможете получить её адрес и вызвать эту функцию:
int __export __fastcall TMyClass::exportfun(int param1,int param2)
{
//Получим адрес функции TMyClass::localfun
// в месте где функция реализована необходимо
// добавить __export: int __export __fastcall TMyClass::localfun();
//И воспользуемся ею
//Чтобы получить адрес функции обращаемся к Exe так же как и к DLL
HMODULE h=GetModuleHandle(0);
if(h)
{
typedef int __fastcall (*TFunExe)(void *);
TFunExe funExe=(TFunExe)GetProcAddress(h,"@TMyClass@localfun$qqrv");
if(funExe)
param1 +=funExe(this);
}
return param1+param2+Member;
}
Аналогично можно поступать и c функциями не являющимися членами классов.
Вы можете получить доступ к функциям Exe на этапе загрузки DLL(LoadLibrary) и произвести необходимые действия не по инициативе Exe, а сами непосредственно из DLL.
 Георгий (29-01-2003 01:13:58)
cool — наконец то!
Devnvd — спасибо большое, за аргументированный ответ.
Но я хочу обратить твоё внимание на следующую строку:
int ResultDll=funDll(&MyClass,1,10);
здесь ты вызвал метод класса как ФУНКЦИЮ и воспользовавшись соглашением о передаче параметров (к сожалению не смотрел, формат вызова методов компиляторами производства не Borland) подсунул этой ФУНКЦИИ конкретный экземпляр обьекта.
Что я могу сказать:
1. под экспортом метода (обьекта) я подразумевал возможность обратиться к методу(обьекту), физически расположенному в DLL, так, чтобы программист на некоторых этапах не задумывался о том, что этот метод (обьект) находится в DLL.
2. сила языка C++ имено в таких трюках, но что если вдруг нужно будет воспользоваться этим методом (обьектом) из программы созданной на другом компиляторе (например Watcom C++ 11.0), где аргументы функций по возможности передаются через регистры?
3. в принципе любой обьект (но без механизма наследования и виртуальных методов) можно реализовать как набор функций и структур, что ты и сделал (но замаскировал под псевдо вызов метода).
На основе этих рассуждения я снова ставлю вопрос:
Как быть с механизмом наследования?
class A//прототип и в DLL и в основной программе
{
public:
virtual int method(void)=0;
};
class B:public A//прототип в DLL
{
public
int method(void);
};
int B::method(void){return 5;};//реализация в DLL
A* anyFunction(void);//ф-ция в DLL и возвращает обьект 'B'
void main (void)
{
A* ptrA;
ptrA=anyFunction();
cout<<ptrA->method();//что будет?
};
вот, если в DLL хранить потомка (потомков) обьекта A, то каким образом основная программа сможет после подключения этой DLL воспользоваться РЕАЛИЗАЦИЕЙ методов именно потомка?
В случае много модульной программы (это когда код (ну обьектный код) хранится в OBJ файлах) этим вопросом занимается компилятор и для разрешения именно этих вопросов и создаются таблицы методов (виртуальных).
А при динамической загрузке DLL таблицы УЖЕ созданы и именно по этому я ответил на этот вопрос, что МЕТОД нельзя импортировать.
Надеюсь хоть на какой-то ответ.
 Devnvd (29-01-2003 19:26:53)
По описанному мной механизму, ясно что главным условием является одинаковость описания класса, именно той части, которая требует выделения памяти. Обычные методы класса ничем не отличаются от простых функций. Компилятор с ними так и обращается.
Моё сейчашное понимание:
Для виртуальных же методов строится отдельная таблица для каждого составляющего класс классов, в которой размещаются реальные адреса методов.
И следовательно требуется наличие в этом же месте, месте описания класса, кода реализации этого метода.
Поэтому нельзя описать в классе виртуальный метод в Exe и в DLL, а реализацию его разместить только допустим в DLL.
Но надо проверить.
 Георгий (29-01-2003 20:15:17)
Вот и я тогоже мнения, но:
1. в таблице хранятся адреса — нет необходимости в том, чтобы код находился там же, где и основная программа (но возможно очень неудобно в таблицу записать нужный адрес).
2. при статичиском линковании нет проблем (не проверял, но слышал)
Моё понимание:
в конкретном экземпляре класса, хранится адрес записи в таблице виртуальных методов — что то типа:
1. адрес метода класса А
2. адрес метода класса B, наследника А
3. адрес метода класса C, наследника B
и для моего примера указатель ptrA указывает на обьект, у которого у соответствующем поле находится адрес "2", что позволяет и вызвать виртуальный метод и, при необходимости, вызвать метод родительского класса.
Вопрос в следующем — как влезть в эту таблицу в моём примере (из DLL, во время её инициализации?) и не бояться, что в новой версии компилятора, она будет находится где-то в другом месте.
 Devnvd (30-01-2003 16:51:55)
Ваш пример, Георгий, я проверил. Всё работает правильно.
Перед тем как собрать я сделал ассемлеровский листинг кода DLL. И не увидев там никаких таблиц, а только коды подпрограмм, решил собирать полностью.
Всё заработало сразу. Поэтому можете пробовать усложнять класс. Никаких дополнительных работ с таблицами виртуальных методов на горизонте не предвидется. Также никаких особенностей касаемых разных компиляторов, тоже не предвидется.
Полный текст проверки:
===== ExpFun.Dll =======
#include
#include
#pragma hdrstop
//----------------------------------------------

#pragma argsused
class A//прототип и в DLL и в основной программе
{
public:
virtual int method(void)=0;
};
class B:public A//прототип в DLL
{
public:
int method(void);
};
int B::method(void) //реализация в DLL
{
return 5;
}
B b; //Экземпляр класса
//Вспомогательная Функция для получения
// в Exe адреса на экземпляр класса
extern "C" A* __export __stdcall anyFunction();
A* __stdcall anyFunction()//ф-ция в DLL и возвращает обьект 'B'
{
return &b;
}
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
============ Exe — Unit1.cpp ===========
#include
#pragma hdrstop

#include "Unit1.h"
//----------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
class A//прототип и в DLL и в основной программе
{
public:
virtual int method(void)=0;
};
//---------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
HINSTANCE h=LoadLibrary("ExpFun.dll");
if(h)
{
typedef A* __stdcall ( *TFunDll)(void);
TFunDll anyFunction=(TFunDll)GetProcAddress(h,"anyFunction");
if(anyFunction)
{
A* ptrA;
ptrA=anyFunction();
if(ptrA)
{
int ResultDll=ptrA->method();//что будет?
ShowMessage("Export Func="+IntToStr(ResultDll));
}
else ShowMessage("PtrA=0");
}
else ShowMessage("No find anyFunction");
FreeLibrary(h);
}
}
Тема довольно таки серьёзная и жаль, что она уходит с первой страницы форума.
Для таких тем необходим отдельный раздел.
 Георгий (30-01-2003 18:17:07)
раз нет таблиц виртуальных методов, то я не понимаю как это работает.
сам я писал проги на разных языках (ФОКАЛ и БЕЙСИК на БК-0010-01, PASCAL C++ Assembler на PC) но остановился на C++ из-за его строгости (ясности физической реализации и, как следствие, простоты эквивалентных реализаций — т.е. нет функций которые нельзя самому сделать (в паскале это write)) и эффективности разработки больших приложений (по сравнению с ассемблером), а тут вот тебе и фокус, который я не понимаю:
если есть 2 потомка класса A: B и C и функция anyFunction возвращает как результат указатель на один из этих классов, то как при вызове метода этого базового класса компилятор (а кто кроме него?) определяет какой реальный метод вызвать? или в структуре описывающей класс есть адрес реализации метода, но как тогда реализуется вызов родительского метода, который обычно вызывается ::method() (т.е. через две точки), из которого в свою очередь можно вызвать его родительский (покрайней мере у меня такое понимание обьектно ориентированного программирования)
и кстати чем ты DLL дизассемблировал? у меня есть wdis (из комплекта Watcom C++), но он работает только с obj.
 Георгий (30-01-2003 20:22:09)
кажется понял!
на закладке Project -> options-> C++ раздел Virtual tables прочёл внимательно помошь и "погулял" по EXE в окне View->Debug windows-> CPU
в результате чего пришёл к выводу:
1. таблицы виртуальных функций создаются компилятором и используются линкёром (сборщиком) на этапе разрешения связей и ни для каких других целей не используются (в программу они не должны попадать т.к. уже не нужны).
2. обьект представляется в виде структуры (т.е. даже на С (не С++ а С) можно реализовать виртуальные функции) в которой есть скрытые поля (не доступные программисту), соответствующие виртуальным функциям, в которые при создании обьекта (судя по всему до вызова конструктора) записывается адрес реализации метода, соответствующего данному экземпляру класса, и в процессе работы вызов метода выполняется именно по этому адресу (проверил в пошаговой отладке).
3. из DLL нельзя вызвать метод класса "А" даже не смотря на то, что он виртуальный — редактор связей (линкёр) пишет, что не может разрешить связь!!!
это ещё раз подтверждает пункт 1 (и разрушает мои иллюзии относительно принципов ООП в C++)
class A
{
public:
virtual int method(void);//реализация в EXE
};
class B:public A
{
public:
int method(void);
};
class C:public A
{
private:
int asd;
char a[5];
public:
int method(void);
};
class D:public B
{
public:
int method(void);
};
class E:public A
{
public:
int method(void);
};
class F:public E
{
public:
int method(void);
};
int F::method(void)
{
return 12+E::method();
};
int E::method(void)
{
return 10+A::method();//не хочет линковать!!!
};
int D::method(void)
{
return 8+B::method();//заменяет на вызов B::method() по относительному адресу (т.е. никакой виртуальности здесь нет! — вызов вполне конкретной функции)
};
int C::method(void)
{
return 8;
};
int B::method(void)
{
return 5;
}
4. из EXE можно вызывать виртуальный метод, где бы он ни находился -
mov edx, ptrA  — загружается указатель на структуру (обьект)
mov ecx,[edx]  — из структуры считывается адрес
call dword ptr ecx — вызов виртуальной функции
как ты видишь — нет никакой разницы где находится реализация виртуального метода
т.е. возможет обратный процесс обсуждаемому — в функцию DLL можно передать указатель на обьект и вызвать виртуальный метод (реализованный в EXE) этого обьекта из DLL (не проверял, но должно работать)
5. каждый виртуальный метод увеличавает размер экземпляра класса (и всех его потомков) на 4 байта (сюда наверное можно добавить и выравнивание, если оно включено)
6. не очевидно, но факт — наследники (те, что в DLL) не могут вызвать конструктор базового класса, если он реализован в EXE — опять "виноват" линкер.
случайно обратил внимание — механизм RTTI тоже использует скрытые поля обьектов.
Осталось придумать, как экспортировать именно обьекты из динамически линкуемой DLL. т.е. описание обьекта в EXE, а реализация — в DLL и как там будет с виртуальными функциями (я их методами больше не могу назвать;-).
 Devnvd (30-01-2003 20:47:03)
Дизассемблирую я непосредственно исходный Cpp-текст в Builder5.
Для этого расписываю класс в отдельном файле и добавляю в методе:
_asm nop;
Увидя эту строку компилятор любезно генерит мне asm-файл. Со всеми коментариями и исходными строками.

В продолжение ваших исследований предложу вам следующий пример для уяснения механизма работы с виртуальными функциями.
===========================
#pragma hdrstop
#include
#include

class TObj;
typedef void (TObj::* TFun)();
class TObj
{
public:
TObj(){};
~TObj(){};
void Func(){printf("TObj::Func ");}
virtual void VFunc(){printf("TObj::VFunc ");}
};
class TMObj:public TObj
{
public:
TMObj():TObj(){};
~TMObj(){};
virtual void VFunc(){printf("TMObj::VFunc ");}
};

#pragma argsused
int main(int argc, char* argv[])
{
TObj *Obj=new TObj;
TMObj *MObj=new TMObj;
TFun fun=&TObj::Func; //Взяли указатель на функцию
Obj->Func(); //Прямой вызов функции
(Obj->*fun)(); // от имени класса Obj запускаем через указатель функцию Func.
(MObj->*fun)(); //от имени класса MObj запускаем через указатель функцию Func.

fun=&TMObj::Func; //Взяли указатель на функцию
(Obj->*fun)(); // от имени класса Obj запускаем через указатель функцию TObj::Func.
(MObj->*fun)(); //от имени класса MObj запускаем через указатель функцию TMObj::Func.

fun=&TObj::VFunc; //Взяли указатель на виртуальную функцию
(Obj->*fun)(); // от имени класса Obj запускаем через указатель функцию TObj::VFunc.
(MObj->*fun)(); //от имени класса MObj запускаем через указатель функцию TMObj::VFunc.
printf("nShould be:n");
//В результате должно получится
printf("TObj::Func TObj::Func TObj::Func TObj::Func
TObj::Func TObj::VFunc TMObj::VFunc");

getch();
return 0;
}
==========================
 Георгий (31-01-2003 05:47:58)
конечно спасибо, но это ты показал обычное явное указание типа — тоже самое, что
struct strA
{
char A;
short int B;
long int C;
} structA;
struct strB
{
char arr[7];
};
void* ptr=&structA;
((strB*)ptr).arr[1]=13;
((strB*)ptr).arr[2]=0;
в результате в structA.B==13, хотя формально работа велась с strB
тоже самое ты показал, но применительно обьектам
 Георгий (31-01-2003 05:52:26)
забыл звёздочку добавить
(*(strB*)ptr).arr[1]=13;
(*(strB*)ptr).arr[2]=0;
 Devnvd (31-01-2003 10:09:20)
Давайте попробуем сделать промежуточный вывод.
1. Методы класса, какие они бы нибыли, это всего лишь подпрограммы.
2. Описание класса — инструкция компилятору, а не работающей программе.
3. Основным при работе является указатель на экземпляр класса.
4. Доступ к необходимым переменным класса и виртуальным методам производится
по относительным смещениям определяемым из описания класса.
5. Вы можете обращаться с классом как с обычной структурой.

Из всего этого следует:
Для совместной работы с классами Exe и DLL необходимым условием является одинаковость описания класса в Exe и в DLL. Порядок расположения объявлений виртуальных методов и членов класса должен быть неизменным. Так как на основании этого порядка компилятор определяет смещения. Доступ к обычным методам класса производится так же как и к обычным подпрограммам, с одной лишь разницей: наличие дополнительного первого параметра в методе класса — указателя на экземпляр класса.
 Георгий (31-01-2003 21:05:53)
Да... коротко и точно.
Теперь осталось придумать практическое приложение этому всему.
Удивительно, но я нигде не видел описания физической реализации обьектов (ведь это наверное стандарт?), хотя статей о C++ много прочёл...