(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Об использовании в Delphi классов, созданных в MS VC++

Источник: delphikingdom

1. Требования

Предполагается: знание Delphi на уровне использования DLL, а также написания собственных; знание С++ на уровне написания простейшего приложения в среде MS VC++.

Желательно: общее понимание соглашений о вызове функций; общее представление о способах передачи параметров и возврата значения.
Используемые инструменты: Borland Delphi 6, MS VC++ 6.0

2. Обоснование

Необходимость использования чужого кода в своей программе возникает регулярно. Вставка готовых удачных решений позволяет не изобретать велосипед заново. В хороших случаях чужой код написан на том же языке, что и свой, либо решение оформлено в виде DLL или компонента. Однако, бывают случаи похуже. Например, приобретается PCI-плата расширения с программным обеспечением для нее, а это ПО оказывается файлами исходного кода на С или С++, в то время как проект уже начат на Delphi, и, кроме того, в команде разработчиков С++ знают плохо.

3. Варианты решения

В принципе, можно весь проект писать на С++. Если такая возможность есть - не исключено, что это лучший выход. Но пользовательский интерфейс в Delphi разрабатывается быстрее, чем в MS VC++ (не только мое мнение, но хорошую цитату не нашел), кроме того, в группе могут плохо знать С++. И если даже С++ знают хорошо, но проект уже начат на Delphi, переписывать готовое - значит, тратить неоплачиваемое время.

Можно переписать код С++ на Delphi. Для этого требуется время, и, возможно, немалое, а главное - знание С++ на уровне существенно выше начального ("читаю со словарем"). При этом, многие языковые конструкции С++ не имеют прямых аналогов в Delphi, и их перенос чреват появлением ошибок, в том числе, совершенно дурацких, и потому трудноотлавливаемых. В частности, прекрасный пример из обсуждения статьи "ЯП, ОПП и т.д. и т.п. в свете безопасности программирования":

for(;P('\n'),R-;P('/')) for(e=C;e-;P('_'+(*u++/8)%2))P('/ '+(*u/4)%2);

Можно попробовать засечь время и перевести это на Pascal. Станет примерно понятно, сколько времени уйдет на перевод класса, где подобные конструкции не единичны.

Можно воспользоваться интерфейсами и технологией СОМ (пожалуй, точнее - моделью СОМ и технологией ActiveX). Но - вот цитата из [1], глава " Модель многокомпонентных объектов":
"И еще одно замечание: не думайте, что путь будет легким. Крейг Брокшмидт говорил, что перед тем, как он начал понимать эти концепции, у него был " шесть месяцев туман в голове." Минимальная предпосылка - исчерпывающее знание языка С++." Конец цитаты. И, хотя "модель СОМ предоставляет унифицированный, открытый, объектно-ориентированный протокол связи между программами " (цитата оттуда же), она требует такой квалификации от программиста, которая редко встречается в среде непрофессионалов.

Можно реализовать код, который необходимо использовать, в виде DLL. Один из существенных плюсов DLL - неважно, на каком языке она написана, если соблюдены некоторые условия, такие, как соглашение о вызове и совместимость типов.

С учетом того, что в группе разработчиков в основном о С++ поверхностные представления, а СОМ - незнакомая аббревиатура, и, при этом, срок сдачи проекта - традиционно - вчера, ничего лучше варианта с DLL у нас придумать не получилось.

4. Немного теории

Передать, точнее, экспортировать несколько функций из DLL - не проблема. Приводим типы, соглашения о вызовах, заполняем список экспортируемых функций - и всё (в основном). Об этом написано немало, например, в [2], в параграфе "Использование DLL, разработанных в С++".

Экспортировать класс несколько сложнее. Даже если и DLL, и основная программа написаны на Delphi, возникают проблемы с распределением памяти, которые решаются использованием модуля ShаreMem, первым в списке uses как проекта, так и DLL [2, 3]. Причем, этот модуль можно, в принципе, заменить самодельным менеджером памяти [там же, 3]. Но как использовать ShаreMem, если DLL написана на другом языке, или написать собственный менеджер для двух языков? Наверное, можно и так, но, напоминаю, срок сдачи проекта - вчера. Если есть и другие возражения, часто время - определяющий фактор.

Можно создавать экземпляр класса при загрузке DLL, ликвидировать при выгрузке (используя события DLL_PROCESS_ATTACH/DETACH), а для методов класса (функций- членов, раз уж класс на С++) написать однострочные функции-обертки, не являющиеся членами, а просто вызывающие соответствующие функции-члены. Некрасиво, и много лишней работы. Попробуем все же экспортировать класс.

В [2], сказано: "Библиотеки DLL не могут экспортировать классы и объекты, если только вы не используете специализированную технологию Microsoft под названием СОМ или какую-либо другую усовершенствованную технологию". Впрочем, там же есть замечание: "На самом деле объекты могут быть переданы из DLL в вызывающую программу в случае, если эти объекты спроектированы для использования в качестве интерфейсов или чистых абстрактных классов". Кроме этого замечания, в [2] об экспорте объектов почти всё, но уже хорошо, что есть шанс "сделать это по-быстрому".

И, наконец, в [4] находим параграф "Экспорт объектов из DLL". Там сказано: "К объекту и его методам можно получить доступ, даже если этот объект содержится внутри DLL. Но к определению такого объекта внутри DLL и его использованию предъявляются определенные требования. Иллюстрируемый здесь подход применяется в весьма специфических ситуациях, и, как правило, такого же эффекта можно достичь путем применения пакетов или интерфейсов". Наша ситуация вполне специфическая; пакеты здесь неприменимы, так как они все же для использования с Delphi, про использование интерфейсов и СОМ уже сказано, а использовать интерфейсы без СОМ вне Delphi, судя по [2], нельзя.

И, пожалуй, самое важное из [4]:
"На экспорт объектов из DLL накладываются следующие ограничения:

  1. Вызывающее приложение может использовать лишь те методы объекта, которые были объявлены как виртуальные.
  2. Экземпляры объектов должны создаваться внутри DLL.
  3. Экспортируемый объект должен быть определен как в DLL, так и в вызывающем приложении с помощью методов, определенных в том же порядке.
  4. Из объекта, содержащегося вутри DLL, нельзя создать объект-потомок.

Здесь перечислены лишь основные ограничения, но возможны и некоторые другие. "

Далее там рассказывается о работе с DLL, написанной в Delphi, но полученной информации достаточно для работы с DLL, создаваемой в MS VC++.

Мастер MS VC++ позволяет создать обычную (regular) DLL и DLL-расширение (extension). Обычная DLL может экспортировать только С-функции и не способна экспортировать С++-классы, функции-члены или переопределенные функции [1]. Стало быть, надо использовать DLL-расширение. Мастер создаст заготовку, затем в каталог проекта надо будет скопировать два файла - заголовочный и файл кода (*.h и *.cpp), содержащие класс, с экземпляром которого предстоит поработать. Затем подключить их к проекту DLL и немного доработать в соответствии с указанными ограничениями.

Во-первых, все экспортируемые открытые методы (ну, или функции-члены, как хотите) необходимо объявить с директивой __stdcall, по понятным причинам. Во-вторых, их также необходимо объявить виртуальными. Это делается для того, чтобы точки входа оказались записанными в таблицу виртуальных функций (VMT), через которую и будет осуществлятся экспорт-импорт. В-третьих, класс требуется объявить с макроопределенной директивой AFX_EXT_CLASS или AFX_CLASS_EXPORT, это синонимы. Сделанные изменения не влияют на работоспособность класса в ехе-проекте, даже директива экспортируемого класса.

Далее в файл .срр проекта DLL нужно добавить функции создания и ликвидации объекта. Пример в [4] обходится без функции ликвидации, видимо, потому, что в приведенном там примере и DLL и импортирующее приложение написаны на Delphi, так что можно освободить память методом Free, который есть у всех наследников TObject и отсутствует у объектов С++, не имеющих общего класса-предка. Функция создания объекта должна просто вызывать конструктор, передать ему полученные от приложения параметры и вернуть указатель на созданный объект. Функция ликвидации принимает указатель на объект и вызывает деструктор. И обязятельно вписать эти функции в список экспортируемых.

И всё! Пятнадцать минут работы, при минимальном знании С++. Остальное в Delphi.

В импортирующей программе необходимо объявить класс, содержащий виртуальные открытые функции в том же порядке. Также необходимо объявить сложные структуры данных, используемые в DLL и передаваемые через ее границу в любом направлении. Имеются в виду структуры С++, они же записи Паскаля. И, конечно же, нужно объявить импортируемые функции создания и уничтожения класса. Теперь для создания экземпляра класса вызывается соответствующая функция DLL, когда объект перестает быть нужным - снова вызывается соответствующая функция DLL, а методы вызываются традиционно - "Объект.Метод(Параметры)". При этом обзывать методы в Delphi можно как угодно, важен лишь порядок их следования и списки параметров.

Если в С++ функция-член возвращает значение, в Delphi соответствующий метод должен быть тоже функцией. Если же функция-член возвращает void, в Delphi соответствующий метод - процедура.

Если в С++ параметр передается по значению, то и в Delphi тоже, если же по ссылке (то есть как указатель), то в Delphi такой параметр должен быть объявлен с ключевым словом var.

Чуть подробнее о параметрах и их типах. Практически везде, где говорится о DLL, упоминается, что, если хотите обеспечить совместимость DLL с программами на других языках, необходимо обеспечить совместимость типов. То есть, стремиться использовать стандартные типы ОС Windоws. Такие типы, как string или file вообще не совместимы с С++, с TDateTime можно поэкспериментировать, вообще-то, он соответствует стандарту, принятому в OLE-автоматизации ([3]). Опять же, [3] заявляет о соответствии типов single и double Delphi с float и double в С++ соответственно. Хотя в [5] есть такой совет со ссылкой на News Group: "Если вы создаете DLL не с помощью Delphi, то избегайте чисел с плавающей точкой в возвращаемом значении. Вместо этого используйте var-параметр (указатель или ссылочный параметр в С++) Причина кроется в том, что Borland и Microsoft применяют различные способы возврата чисел с плавающей точкой. Borland С++ и Delphi могут использовать один и тот же метод".

Таблица соответствия типов Delphi и С++:
Тип С++ Байтов Тип Delphi
int ?(4) integer
unsigned int ?(4) cardinal
char, __int8 1 shortint
short, __int16 2 smallint
long, __int32 (int) 4 longint (integer)
__int64 8 int64
unsigned char 1 byte
unsigned short 2 word
unsigned long 4 longword
float 4 single
double 8 double
char *   PChar

5. Немного практики

Для примера будет использован несложный и бесполезный класс на С++, состряпанный на ходу. В MS VC++ создадим проект, используя MFC AppWizard(exe), без использования представления "Документ-вид", на основе диалога, и обзовем его "example_exe". Добавим два новых файла - example.cpp и example.h.

Файл example.h:

//*****************************************************************************
// традиционный финт ушами во избежание
// повторного включения файла .h
#if !defined(EXAMPLE__INCLUDED)
#define EXAMPLE__INCLUDED
// введем парочку структур для демонстрации работы с ними
typedef struct
{
  int n;
  int i;
  short j;
  char k;
}struct_1;

typedef struct
{
  int n2;
  short a[3];
}struct_2;

// Класс-пример. Ничего полезного, просто демонстрация.
class CExample
{
private:
  int Field;
  CString Name;
  void Show(CString str);
public:
  // конструктор и деструктор, как полагается
  CExample(int F, CString N);
  ~CExample();
  // просто сообщение
  void Message(CString str, int Digit);

  // "процедура" и "функция"
  void Proc(int * Digit);
  int Func(int Number);

  // работа с закрытым полем
  void SetF(int F);
  int GetF();

  // работа со структурами
  struct_2 * Struct1to2(struct_1 s1);
};
#endif //if !defined(EXAMPLE__INCLUDED)
//*****************************************************************************

В классе есть пара закрытых полей, закрытая функция-член, набор открытых функций. Конструктор принимает два параметра. Строковый параметр будем интерпретировать, как имя объекта. Функция Message нужна для отображения на экране хоть каких-то сообщений, демонстрирующих, что что-то происходит. Proc имитирует процедуру, то есть, не возвращает значения, зато изменяет что-то в программе, в нашем случае, переданный параметр. Func и есть функция, то есть, ничего не изменяет, зато вычисляет некоторое значение и возвращает его. Плюс здесь же установщик и считыватель закрытого поля, а также простенькая демонстрация работы со структурами.

Файл example.срр:

//*****************************************************************************
#include "stdafx.h"
#include "Example.h"

// конструктор инициализирует два закрытых поля
// и выдает сообщение об успешном создании
// при помощи закрытой функции
CExample::CExample(int F, CString N)
{
  this->Field = F;
  this->Name = N;
  this->Show(N + " Created successfully");
}

// деструктор только сообщcellpadding ает о самоликвидации
CExample::~CExample()
{
  this->Show("Deleted successfully");
}

// закрытая функция, по сути - оболочка MessageBox'а
// заголовком бокса будет имя класса
void CExample::Show(CString str)
{
  ::MessageBox(NULL, str, this->Name, MB_OK);
}

// открытая функция, выводит строку и число в десятичном виде.
void CExample::Message(CString str, int Digit)
{
  str.Format(str + " %d", Digit);
  this->Show(str);
}

// "процедура" не возвращает значение, зато изменяет параметр
void CExample::Proc(int * Digit)
{
  *Digit *= 2;
}

// "функция" не изменяет параметр, зато возвращает значение
int CExample::Func(int Number)
{
  int Result;
  Result = Number * 2;
  return Result;
}

// банально присваиваем значение параметра закрытому полю.
void CExample::SetF(int F)
{
  this->Field = F;
}

// еще банальнее...
int CExample::GetF()
{
  return this->Field;
}

// присваиваем значения полей одной структуры полям другой
struct_2 * CExample::Struct1to2(struct_1 s1)
{
  struct_2 * s2 = new struct_2;

  s2->n2 = s1.n * 2;
  s2->a[0] = s1.i;
  s2->a[1] = s1.j;
  s2->a[2] = s1.k;
  return s2;
}
//*****************************************************************************

Для примера более, чем достаточно. Теперь надо посмотреть, как это работает.

В файле Example_exeDlg.h в описании класса CExample_exeDlg где-нибудь в секции public надо вписать
CExample * ex;
то есть, объявить переменную-член, указатель на наш учебно-тренировочный класс, и в конструкторе Example_exeDlg вписать
ex = NULL;
Можно ex сделать и не членом, в принципе, и инициализировать при объявлении. И, конечно, не забыть наверху этого же файла вклеить заголовочный файл класса:
#include "Example.h"

На диалоговую форму накидаем кнопок и создадим их обработчики:

void CExample_exeDlg::OnBtCreate()
{
if (ex == NULL)
ex = new CExample(7, "Example");
}
Если объект еще не создан - создаем и инициализируем пару закрытых полей.

void CExample_exeDlg::OnBtDestroy()
{
delete ex;
ex = NULL;
}
Освобождаем память и устанавливаем указатель в "пусто"

void CExample_exeDlg::OnBtMessage()
{
ex->Message("Any digit - ", 3);
}
Демонстрационное сообщение.

void CExample_exeDlg::OnBtProc()
{
int k = 5;
ex->Message("before K = ", k);
ex->Proc(&k);
ex->Message("after K = ", k);
}
Показываем в последовательных сообщениях, какое значение переменная имела до выполнения процедуры, и какое стала иметь после.

void CExample_exeDlg::OnBtFunc()
{
int k = 5, l;
ex->Message("before K = ", k);
l = ex->Func(k);
ex->Message("after K = ", k);
ex->Message("Result of Func = ", l);
}
Примерно то же самое - значение до выполнения, значение после выполнения и результат выполнения.

void CExample_exeDlg::OnBtGet()
{
ex->Message("", ex->GetF());
}

void CExample_exeDlg::OnBtSet()
{
ex->SetF(ex->GetF() + 1);
}

Эти две - без комментариев. Должно быть так все понятно... Функцию для работы со структурами в этом проекте не буду трогать, не интересно, тут весь фокус, как их передать через границу DLL. Кроме того, не будем возиться с полями ввода, а передадим параметры непосредственно в коде. Наглядность это уменьшает ненамного, а работы меньше. Еще момент - ID кнопок по-умолчанию поменял с BUTTON1 на BT_CREATE и так далее, для наглядности.

Всё! На форме только кнопки, вывод информации через MessageBox. Можно проверить работу.

Сделаем DLL для этого класса. В MS VC++ создадим проект, используя MFC AppWizard(dll), назовем "example_dll". В каталог этого проекта копируем готовые example.cpp и example.h, добавим их к проекту. Будем изменять, в соответствии с выясненными правилами. Начнем с объявления класса:

// Можно использовать AFX_EXT_CLASS, это синонимы.
class AFX_CLASS_EXPORT CExample

Затем из
void Message(CString str, int Digit);
делаем
virtual void __stdcall Message(CString str, int Digit);
и так со всеми открытыми методами, кроме конструктора и деструктора. И на этом бы всё, да CString - несовместимый, опасный тип. Меняем объявление:
virtual void __stdcall Message (char * str, int Digit);
и определение:
void CExample::Message (char* str, int Digit)
{
//добавляем CString:
CString s = str;
//и немного изменяем работу со строкой:
//str.Format(str + " %d", Digit);
s.Format(s + " %d", Digit);
//this->Show(str);
this->Show(s);
}
то есть, приходим к совместимому типу "указатель на нуль-терминальную строку", но, чтобы не потерять функциональность класса CString, объявляем локальную переменную этого класса и используем ее. Осталось еще полторы детали.
Первая деталь - в файле example_dll.cpp в конце пишем:

// вставляем функцию инициализации..
CExample * __stdcall InitExample(int F, char * N)
{
CExample * ex;
// транслируем конструктору принятые параметры
ex = new CExample(F, N);
// и возвращаем указатель на созданный объект
return ex;
}

// ..и ликвидации
void __stdcall DelExample(CExample * ex)
{
delete ex;
}

И половина детали - в файле EXAMPLE_DLL.def в конце дописываем пару строчек, так, чтоб получилось:

;*****************************************************************************
; EXAMPLE_DLL.def : Declares the module parameters for the DLL.

LIBRARY "EXAMPLE_DLL"
DESCRIPTION 'EXAMPLE_DLL Windows Dynamic Link Library'

EXPORTS
; Explicit exports can go here
InitExample
DelExample
;*****************************************************************************

После компиляции DLL готова. Подготовим проект в Delphi, чтобы продемонстрировать ее работу. Создадим проект "Example_Delphi", и в модуле главной формы, перед объявлением класса формы, впишем четыре типа. Два - структуры struct1 и 2:

TRec1 = record
n : integer;
i : integer;
j : smallint;
k : shortint;
end;
TRec2 = record
n2 : integer;
a : array[0..2] of smallint;
end;

Третий - указатель на вторую структуру:

PRec2 = ^TRec2;

А четвертый - наш класс, с которым будем работать:

TExample = class
public
procedure Mess_(str : PChar; Digit : integer); virtual; stdcall; abstract;
procedure Proc(var Digit : integer); virtual; stdcall; abstract;
function Func(Number : integer): integer; virtual; stdcall; abstract;
procedure SetF(F : integer); virtual; stdcall; abstract;
function GetF(): integer; virtual; stdcall; abstract;
function Struct1to2(rec1 : TRec1): PRec2; virtual; stdcall; abstract;
end;

Директивы virtual и stdcall в пояснениях не нуждаются. О них сказано выше. А зачем abstract? Очень просто. Без нее компилятор будет ругаться на неправильное упреждающее объявление функции, ведь описания ее у нас нет, описание - в DLL. Директивы должны идти именно в этом порядке. Иначе компилятору не нравится.

Обратите внимание на первый метод. Остальные названы так же, как и в С++, но слово Message в Delphi зарезервированное, и использовать его не по назначению не стоит. Хорошо, назовем иначе, важно, что она стоит на первом месте среди виртуальных функций, как и в С++, значит, ее найдут по номеру в VMT. Имя роли не играет.

Еще надо добавить объявление экспортируемых функций создания/ликвидации, в конце секции interface:

function InitExample(F: integer; N : PChar) : TExample; stdcall;
external '..\Example_DLL\debug\Example_DLL.dll';
procedure DelExample(ex : TExample); stdcall;
external '..\Example_DLL\debug\Example_DLL.dll';

Здесь предполагается, что DLL лежит там, где и появилась после компиляции, а директории "Example_dll" и "Example_Delphi" имеют общую родительскую. Больше нигде ее искать не будут. Если же указать только имя, приложение будет искать библиотеку в своей папке, папках WINDOWS, SYSTEM32 и прописанных в переменной окружения PATH. Впрочем, это азбука.

Всё, класс можно использовать. Давайте опять наделаем кнопок, а вывод в поле Memo, благо, в Delphi с ним работать быстрее и проще, чем в MS VС++.

Вот обработчики кнопок:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(Self.ex) then
    Self.ex := InitExample(10, 'Ex_Delphi');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  DelExample(Self.ex);
  Self.ex := nil;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Self.ex.Mess_(PChar('Некоторая цифра - '), 5);
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  j : integer;
begin
  j := 15;
  Self.Memo1.Lines.Add('j До - ' + IntToStr(j));
  Self.ex.Proc(j);
  Self.Memo1.Lines.Add('j После - ' + IntToStr(j));
end;

procedure TForm1.Button5Click(Sender: TObject);
var
  j : integer;
begin
  j := 20;
  Self.Memo1.Lines.Add('j До - ' + IntToStr(j));
  Self.Memo1.Lines.Add('Результат - ' + IntToStr(Self.ex.Func(j)));
  Self.Memo1.Lines.Add('j После - ' + IntToStr(j));
end;

procedure TForm1.Button6Click(Sender: TObject);
begin
  Self.Memo1.Lines.Add(IntToStr(Self.ex.GetF));
end;

procedure TForm1.Button7Click(Sender: TObject);
begin
  Self.ex.SetF(Self.ex.GetF + 1);
end;

То же самое, что и в С++, и работает так же. Что и требовалось. И добавим кнопку для функции, которая принимает и возвращает структуры. Вот ее обработчик:

procedure TForm1.Button8Click(Sender: TObject);
var
  s1 : TRec1;
  s2 : PRec2;
begin
  // здесь компилятор будет ругаться, но в данном
  // случае это не важно. Просто посмотрим, что
  // до инициализации s2 - это всякая чушь...
  Self.Memo1.Lines.Add('s2 до:' + #9 +
    IntToStr(s2.n2) + #9 +
    IntToStr(s2.a[0]) + #9 +
    IntToStr(s2.a[1]) + #9 +
    IntToStr(s2.a[2]) );

  // инициализация s1
  s1.n := 10;
  s1.i := 1;
  s1.j := 2;
  s1.k := 3;

  // если функция возвращает указатель на запись (структуру) -
  // надо подготовить указатель. Это вам не класс.
  // s2 - типа PRec2, а не TRec2
  s2 := Self.ex.Struct1to2(s1);

  // ... а потом - то, что мы требовали.
  Self.Memo1.Lines.Add('s2 после:' + #9 +
    IntToStr(s2.n2) + #9 +
    IntToStr(s2.a[0]) + #9 +
    IntToStr(s2.a[1]) + #9 +
    IntToStr(s2.a[2]) );
end;

Что делает функция - понятно, тут другая тонкость. Конструктор возвращает (в коде на С++) указатель на класс, а мы присваиваем возвращаемое значение переменной, которая, вроде бы, не указатель. Struct1to2 тоже возвращает указатель - и его надо подготовить. Это объясняется в [3]: "Объект - это динамический экземпляр класса. Объект всегда создается динамически, в "куче", поэтому ссылка на объект фактически является указателем (но при этом не требует обычного оператора разыменования "^"). Когда вы присваиваете переменной ссылку на объект, Delphi копирует только указатель, а не весь объект. Используемый объект должен быть освобожден явно."

А в С++ структура отличается от класса несколько меньше, и работа с ними почти одинакова.

И еще пара тонкостей. Если в DLL добавить еще виртуальную функцию-член, обязательно в конце, после имеющихся, такая DLL будет совместима со старой программой, где в абстрактном классе эта функция не объявлена. И если изменить имеющуюся функцию, добавив в конец параметров параметр по-умолчанию, такая DLL будет совместима со старой программой, где в абстрактном классе эта функция не имеет такого параметра.

Разумеется, можно вынести описание абстрактного класса, объявление экспортируемых функций, используемых типов и тому подобное в отдельный модуль. Возможно, это лучше, чем запихивать всё в один файл. По крайней мере, я так и делаю. Но это уже детали, касающиеся стиля, а не функциональности.

6. Заключение

При таком подходе нельзя обращаться к полям данных напрямую. Хотя, это не проблема, можно использовать функции-считыватели и установщики. Нельзя использовать закрытые функции. А зачем их использовать? Для использования есть открытые. Те, кто знакомы с моделью СОМ, могут сказать, что это извращенный вариант нормальной технологии. Но для создания полноценного СОМ-сервера нужно несколько больше знаний. Показанный способ позволяет использовать открытые методы класса, не вдаваясь в подробности реализации класса и не затрачивая много времени. Это дает возможность быстро получить работоспособный вариант программы, и уже потом доводить ее до ума. А то и просто получить удовлетворительный результат.

7. Используемая литература

  1. Круглински Д., Уингоу С., Шеферд Дж.
    Программирование на Microsoft Visual C++ 6.0 для профессионалов /Пер. с англ. - СПб: Питер; М.:Издательско-торговый дом "Русская редакция", 2001 - 864 с.:ил.
  2. Кэнту М.
    Delphi 6 для профессионалов (+СD). - Питер, 2002. - 1088с.: ил.
  3. Лишнер Р.
    Delphi. Справочник. - Пер. с англ. - СПб: Символ-Плюс, 2001. - 640 с., ил.
  4. С. Тейксейра, К. Пачеко
    Delphi 5. Руководство разработчика.
  5. Озеров В.
    Delphi. Советы программистов. - СПб: Символ-Плюс, 2002.-912 с.,ил.

К материалу прилагаются файлы:

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 23.12.2008 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Delphi Professional Named User
Enterprise Connectors (1 Year term)
Advanced Data Export VCL Suite (with sources) + 1 Year Maintenance
SAP CRYSTAL Server 2013 WIN INTL 5 CAL License
Zend Guard 1 Year Subscription
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
Реестр Windows. Секреты работы на компьютере
СУБД Oracle "с нуля"
3D и виртуальная реальность. Все о Macromedia Flash MX.
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100