Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пространства имен. Классы. Создание DLL.doc
Скачиваний:
2
Добавлен:
28.09.2019
Размер:
425.98 Кб
Скачать

Void print();

};

void Kirov::print() {

Console::WriteLine("Дом №{0}, тип санузла:{1}", Dom, Closet);

}

typedef array<Kirov^> Towns; // новый тип данных

int main(array<System::String ^> ^args)

{

Towns ^T = gcnew Towns(20);

for (int i = 0; i < T->Length; i++) {

T[i] = gcnew Kirov(i, i%2?"Фаянс":"Фарфор");

}

for each (Kirov ^k in T) {

k->print();

}

Console::ReadKey();

return 0;

}

Сначала объявляется новый тип данных Towns с помощью ключевого слова typedef (аналогично стандартному С++) – массив объектов класса Kirov. Заметьте, что мы указали тип Kirov^, т.е. не просто объекты, а ссылки(указатели) на объекты этого класса. Затем объявляем переменную данного типа и выделяем память под массив в 20 элементов с помощью gcnew. После этого в цикле создаем объекты, меняя при этом в зависимости от четности номера тип «санузла» (оператор «условие ? истина : ложь» здесь работает как и в языке С), и выводим на экран, с использованием цикла for each для итерации по элементам массива (нововведение в С++\CLI, в стандартном С++ такого оператора нет). Так как и объекты, и сам массив были созданы посредством gcnew, то очищать память не требуется.

Объектно-ориентированное программирование ООП в управляемом С++ поддерживает множество других приемов при работе, свойственных ООП: абстрагирование, наследование, перегрузка, полиморфизм и т.д., но рассмотрение данных вопросов выходит за рамки этого раздела. Дополнительную информацию можно найти в справочной литературе по программированию.

В заключение стоит отметить, что в управляемом С++\CLI платформы .NET все переменные и массивы являются объектами: int, String, double, array и т.д., у них есть свои методы для работы (например, мы использовали метод T->Length класса array чтобы получить размер массива в примере выше), и все они унаследованы от единого базового объекта System::Object. Поэтому если в описаниях стандартных функций во входных параметрах встречается тип Object^, то это означает, что фактически можно подставить на это место переменную любого типа. Например если посмотреть описание функции Console::Write() или просто набрать в коде

Console::Write(

то можно заметить, что одним из входных параметров является тип System::Object^. Действительно, мы уже неоднократно использовали эту функцию для вывода на экран переменных разных типов, без предварительного преобразования их в строку.

Создание DLL

Dinamic-link library это библиотека, состоящая из кода, данных и ресурсов в отдельном файле с раширением dll, которая используется основной программой во время работы. Динамачиские библиотеки используют, например, для выделения части часто используемого различными программами кода в отдельный файл, как это сделано в случае с Win32 API. Также используется в больших проектах, когда частым изменениям подвержен лишь небольшой участок кода, и каждый раз перекомпилировать весь проект, а потом заново распространять его между пользователями нецелесообразно.

В платформозависимых языках программирования (C++, Delphi, Visual Basic и.т.п.) в dll файл помещаются функции, которые затем загружаются основной программой во время выполнения.

В платформонезависимой среде .NET внутри dll находится отдельный класс, объявленный в пространстве имен, и имя пространтсва имен является именем dll файла.

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

Разработаем небольшую демонстрационную программу, которая будет запрашивать у пользователя два целых числа, и вызывать внешнюю DLL для арифметических операций с ними.

Начнем с хост-программы. Создайте новый проект CLR \ Windows Forms:

В качестве имени проекта укажем «myprog», для имени группы проектов (solution name) «dlldemo».

Для ввода числа А воспользуемся элементом управления MaskedTextBox, позволяющим осуществлять ввод в заданном формате. Поместите его на форму, выделите указателем мыши, и нажмите на небольшую стрелку в правом верхнем углу, чтобы вызвать меню Set Mask.

В появившемся окне выберите формат Numeric для ввода 5-значных цифр:

После установки маски ввода, необходимо добавить еще одно такое же поле для ввода второго числа В. Можно проделать те же действия, что и с первым полем, или просто сделать копию из уже имеющегося поля. Для этого снова выделите его указателем мыши, нажмите Ctrl+C, затем Ctrl+V, и перетащите новое поле в заданную позицию (в этом случае маску устанавливать не надо, так как она уже будет скопирована).

Далее добавьте обычное поле TextBox для вывода результата, и кнопку:

Присвойте имена компонентам: поле ввода числа А «mtbA», числа В «mtbB», результата «tbResult». Для наименования компонентов часто используется сокращенная транскрипция, например MaskedTextBox как mtb, TextBox как tb, Button как btn, Label как lbl и.т.д. Это позволяет позже, при написании кода программы, точно знать с каким компонентом вы работаете. На кнопку пока действий назначать не будем. Запустите программу и удостоверьтесь, что она работает.

Хост-программа готова, приступаем к созданию библиотеки DLL, которая будет являться отдельным проектом внутри группы проектов solution. Нажмите правой кнопкой на имени группы проектов «dlldemo» и выберите меню Add \ New Project.

Как уже упоминалось выше, DLL модулями являются отдельные классы, поэтому в качестве типа проекта выберите CLR \ Class Library, назовите проект «mydll»:

Visual Studio для нового проекта сгенерирует следующий код:

// mydll.h

#pragma once

using namespace System;

namespace mydll {

public ref class Class1

{

// TODO: Add your methods for this class here.

};

}

Внутри пространства имен mydll, которое также является и именем создаваемого DLL файла, объявляется пустой класс Class1 с модификатором public, чтобы другие внешние модули (хост-программа) получили доступ к этому классу.

Комментарий «Add your methods for this class here» (добавьте методы для этого класса здесь) ясно дает понять, где нам надо писать свой код. Изменим имя класса на «MyClass» и добавим метод для расчета, для начала просто сумма (не забудьте про ключевое слово public перед методом, иначе он будет недоступен внешнему коду):

namespace mydll {

public ref class MyClass

{

// TODO: Add your methods for this class here.

public:

int Calculate(int a, int b) {

return a+b;

}

};

}

Откомпилируйте программу и удостоверьтесь что все в порядке.

Теперь, когда у нас в группе проектов не один проект, а два, активный проект выделяется жирным шрифтом. Активный (Startup) проект – тот, что будет запускать Visual Studio. Если в качестве активного выбрать проект, не являющийся исполняемым EXE файлом (правой кнопкой мыши на имени проекта, пункт меню Set as Startup Project), то будет выведено соответствующее сообщение.

Чтобы подключить DLL к основному проекту, щелкните правой кнопкой мыши по имени проекта «myprog» в окне Solution Explorer (рисунок выше), и выберите меню References:

Нажмите кнопку Add New Reference, и в появившемся окне на закладке Projects выберите mydll.

Осталось добавить код для кнопки вычисления в хост-программе:

private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {

using namespace mydll;

MyClass ^C = gcnew MyClass();

try {

int a = Int32::Parse(mtbA->Text);

int b = Int32::Parse(mtbB->Text);

int r = C->Calculate(a, b);

tbResult->Text = String::Format("{0}", r);

} catch (Exception ^ex) {

MessageBox::Show(ex->Message, "Ошибка", MessageBoxButtons::OK, MessageBoxIcon::Error);

}

}

Так как класс MyClass объявлен внутри пространства имен mydll, мы воспользовались ключевым словом using, иначе для создания класса пришлось бы писать mydll::MyClass ^C = gcnew mydll::MyClass();

Для преобразования строк из полей ввода в числа используется статический метод Parse() класса Int32, для обратного преобразования результата в строку статический метод Format() класса String (функция Console::WriteLine() также использует эту функцию, поэтому задание строки форматирования аналогичное).

Если в поля не будут занесены числа, то возникнет ошибка преобразования типов и функция Parse создаст исключение, которое будет перехвачено конструкцией try {} catch {} и выведено сообщение об ошибке (подробнее об исключениях в следующих разделах).

Запустите программу на выполнение для проверки.

DLL может содержать не только код, но также и формы. Доработаем программу так, чтобы перед вычислением появлялось окно с выбором действия – сложить или вычесть. Щелкните правой кнопкой мыши по имени проекта mydll в окне Solution Explorer, выберите Add \ New Item:

Назначим имя новой форме «frmDialog1».

На эту форму поместите компонент GroupBox (закладка Containers в панели ToolBox), а на него два компонента RadioButton. Имя первого «rbAdd», второго «rbSub». В каждый момент времени может быть отмечен только один Radio button, что как раз подходит для нашей задачи выбора действия. Также добавьте кнопку:

В обработчике кнопки просто будем закрывать окно методом Close():

private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {

Close();

}

Модифицируем код функции Calculate() чтобы сначала отображалась форма:

#include "frmDialog1.h";

namespace mydll {

public ref class MyClass

{

// TODO: Add your methods for this class here.

public:

int Calculate(int a, int b) {

frmDialog1 ^f = gcnew frmDialog1();

f->ShowDialog();

return a+b;

}

};

}

Не забудьте про директиву #include "frmDialog1.h"; для подключения формы.

Запустите программу. Несмотря на то, что будет появляться окно для выбора действия, по-прежнему считается только сумма.

Теперь надо преобразовать метод MyClass::Calculate() так, чтобы он проверял, какое действие было выбрано в окне. Мы не можем непосредственно обратиться к компонентам rbAdd и rbSub из этого метода, так как они принадлежат другому классу (классу формы), и объявлены внутри него в секции private. Можно передать их состояние через public переменные внутри класса формы (как мы это уже делали ранее при создании новой формы и передавали ее заголовок через переменную Stroka), или же произвести вычисление в самом классе формы frmDialog1, где все компоненты формы будут доступны. Воспользуемся вторым способом.

В окне Class View добавим новую функцию в класс формы frmDialog1:

Функция будет принимать 2 числа типа int, возвращать тип int, иметь уровень доступа (access) public, имя CalcInForm.

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

Код для функции будет проверять состояние Checked компонентов RadioButton и выполнять соответствующе действие:

public:

int CalcInForm(int a, int b)

{

if (rbAdd->Checked) return a+b;

if (rbSub->Checked) return a-b;

}

Осталось модифицировать функцию Calculate в классе MyClass так, чтобы она вызывала функцию CalcInForm и возвращала в хост-программу то, что ей вернет эта функция:

public ref class MyClass

{

// TODO: Add your methods for this class here.

public:

int Calculate(int a, int b) {

frmDialog1 ^f = gcnew frmDialog1();

f->ShowDialog();

return f->CalcInForm(a, b);

}

};

В папке, где располагается группа проектов dlldemo, в директории Debug находятся файлы myprog.exe, mydll.dll и временные файлы *.ilk и *.pdb, которые для работы программы не требуются. Удалите файл mydll.dll, и запустите myprog.exe. Программа по-прежнему будет работать, запрашивать ввод чисел, но не вычислять, что подтверждает факт динамической загрузки DLL файла только в тот момент, когда он необходим.