Разработка и использование «обычных» библиотек DLL (CUI)
Разработка и использование «обычных» библиотек DLL (CUI)
Есть моменты, когда все удается. Не ужасайтесь, это пройдет
Закон Мерфи
Цель работы – освоение технологии разработки и использования «обычных» библиотек DLL (Regular DLL using shared MFC DLL). В рамках этой работы мы построим обычную DLL-библиотеку, экспортирующую одну-единственную функцию. Далее надо разработать простое клиентское приложение, использующее эту библиотеку. Таким образом, мы получим аж два приложения – и все это за 2 час. с перекурами!
Задание. Рекомендуемая последовательность выполнения работы:
1. Ознакомиться с теоретической частью работы, для чего придется изучить раздел конспекта «Разработка и использование собственных DLL», особенно подраздел «Основы DLL».
2. Создать обычную (regular) библиотеку DLL, экспортирующую не класс, а просто одну или несколько функций.
3. Создать клиентское приложение, использующее разработанную библиотеку.
Разработка и использование DLL-расширения
Студент, лишенный способности посмеяться над собой вместе с другими, не сможет долго выносить программирование
Фредерик Брукс-младший
Цель работы – освоение технологии разработки и использования DLL-расширений (MFC Extension DLL). В рамках этой работы мы построим библиотеку DLL-расширение, которая будет экспортировать класс CGraphic, предназначенный для построения графика любой (одномерной) функции. Далее надо разработать простое клиентское приложение, использующее эту библиотеку (2 час.).
Задание. Рекомендуемая последовательность выполнения работы:
1. Ознакомиться с теоретической частью работы, для чего придется изучить раздел конспекта «Разработка и использование собственных DLL», особенно подраздел «Основы DLL».
2. Создать библиотеку DLL-расширение, экспортирующую некоторый класс.
3. Создать клиентское приложение, подключив к нему библиотеку DLL-расширение. Это приложение будет создавать объект класса, тип которого описан в библиотеке.
Class CGraphic
на следующую
class AFX_EXT_CLASS CGraphic
Макрос AFX_EXT_CLASS намекает на то, что этот класс экспортируется библиотекой. Клиентское приложение, использующее библиотеку, тоже должно в соответствующем заголовочном файле (Graphic.h) содержать этот макрос.
Шаг 6. Постройте библиотеку с помощью команды меню BuildèBuild ExtDLL.dll.
В папке …\ExtDLL\Debug вы должны обнаружить файлы ExtDLL.dll и ExtDLL.lib. Если их там нет, значит, они были съедены вирусами, которым очень нравятся библиотеки.
Разработка внутрипроцессного сервера и клиентского приложения с использованием ATL
Число студентов в бригаде имеет тенденцию возрастать независимо от объема лабораторной работы, которую надо выполнить
Наблюдение в ХАИ
Цель работы – освоить разработку и использование простого СОМ объекта, реализованного в виде внутрипроцессного сервера (библиотеки DLL), и клиентского консольного приложения (4 час. на одного человека или 180мин./число_членов_бригады).
Задание. В первой части работы описан сценарий создания внутрипроцессного сервера. В СОМ объекте этого сервера требуется реализовать по крайней мере два интерфейса и в первом из них – минимум два метода, а во втором – по крайней мере один. Реализуемые методы и решаемую задачу выбрать по своему усмотрению, если, конечно, вы не хотите показаться обезьянкой, которая просто кому-то подражает. Также надо снабдить СОМ объект каким-нибудь свойством для чтения и записи.
Обязательные требования к реализуемым методам:
1. Метод должен возвращать значение через один из своих параметров.
2. Минимум один из методов должен получать в качестве параметра массив, выполнять над ним какое-нибудь нетривиальное действие и возвращать результат посредством параметра. Число элементов массива также надо передавать этому методу через параметр или через свойство.
Во второй части работы изложен сценарий создания клиентского приложения, которое предлагается реализовать в виде консольного приложения с поддержкой MFC.
Object,
Uuid(2E4FB046-98D8-4E74-8572-84D7133FFEF4),
pointer_default(unique)
]
interface IMathem : IUnknown{
[] HRESULT Cube([in] DOUBLE Arg, [out] DOUBLE* Res);
};
Эти скопированные строки надо разместить непосредственно ниже оригинальных.
Далее необходимо изменить имя интерфейса (в данном примере я выбрал имя IMathem2), удалить строку с описанием метода Cube и заменить идентификатор uuid на новый, воспользовавшись командой ToolsèCreate Guid. В окне мастера генерации uuidпроще всего выбрать четвертый формат представления уникального идентификатора (Registry uuid). Кроме того, новый интерфейс необходимо добавить в описание сокласса (coclass Mathem), но уже без спецификатора default, так как только один интерфейс может быть интерфейсом по умолчанию. В результате этих действий содержимое файла библиотеки DLLMathServer.idl должно стать таким (комментарии в начале файла опущены, а добавленные строки выделены цветом и комментариями. Копировать текст нельзя, так как идентификаторы uuid должны быть уникальными):
import "oaidl.idl";
import "ocidl.idl";
[
Object,
Uuid(2E4FB046-98D8-4E74-8572-84D7133FFEF4),
pointer_default(unique)
]
interface IMathem : IUnknown{
[] HRESULT Cube([in] DOUBLE Arg, [out] DOUBLE* Res);
};
// начало добавлений
[
Object,
Uuid(946AB57D-27B2-480A-B3A9-D90E2398D356),
Version(1.0),
]
Library DLLMathServerLib
{
importlib("stdole2.tlb");
[
Uuid(DA4D3129-504E-4E42-AE02-BCBB9771118C)
]
Coclass Mathem
{
[default] interface IMathem;
Public IMathem,
CMathem()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_MATHEM)
DECLARE_NOT_AGGREGATABLE(CMathem)
BEGIN_COM_MAP(CMathem)
COM_INTERFACE_ENTRY(IMathem)
COM_INTERFACE_ENTRY(IMathem2) // добавлено
END_COM_MAP()
После этих модификаций библиотека должна компилироваться и собираться без ошибок – поверьте, но проверьте!
Шаг 5.Добавление новых методов и свойств.
Для добавления новых методов необходимо действовать так же, как и при добавлении методов к интерфейсу по умолчанию (см. инструкции к шагу 3). Возможно, что реализацию методов нового интерфейса придется добавлять вручную в файл реализации Mathem.cpp. Также, очевидно, надо будет добавить объявление (прототип) новых методов в заголовочный файл Mathem.h по аналогии с методами первого интерфейса.
Самостоятельно выберите какой-нибудь метод, обрабатывающий массив, и добавьте его в интерфейс IMathem2. Параметр-массив надо объявлять как указатель, например, LONG *, CHAR * или DOUBLE *, так как методы СОМ объектов не поддерживают параметры структурных типов. Добавьте также еще один метод к любому интерфейсу и свойство (property). Тип свойства опять-таки выберите по своему усмотрению, напишите реализацию методов установки и чтения значения свойства. Для хранения значения свойства добавьте в класс CMathem член-переменную для достижения этой возвышенной цели.
В качестве примера полезного метода рассмотрим такую задачу. Во многих инженерных расчетах используются исходные данные, заданные таблично. Например, на рис. 3 приведена иллюстрация метода использования таблично-заданной функции Y(X) для получения значения функции YK для заданного значения аргумента XK.
|
Рис. 3. Интерполяция таблично-заданных значений функции Y(X)
Для знающего математику даже в объема школьного курса понятно, что в качестве исходных данных надо иметь:
· массив значений функции Y;
· массив значений аргумента функции X;
· значение аргумента Xk, для которого требуется определить значение функции Yk.
Также совершенно очевидно, что задача определения Yk по Xk сводится к линейной интерполяции значений функции на известном отрезке Xi<=Xk<=Xi+1, что также не выходит за рамки начальных знаний геометрии на плоскости.
Этот пример метода не только полезный, но и необходимый для реализации в вашем сервере.
При добавлении в интерфейс свойства с помощью мастера (последовательность действий такая же, как и для метода) в окне Add Property Wizard достаточно указать только тип свойства (выбрать в списке Property type) и ввести его имя (поле Prpoperty name). Больше ничего вводить или изменять в данном случае не нужно. Например, если задать имя свойства Foo и выбрать для него тип BSTR, то в файле DLLMathServer.idl появятся строки, выделенные полужирным шрифтом:
interface IMathem : IUnknown{
[helpstring("method Cube")] HRESULT Cube([in] DOUBLE Arg,
[out] DOUBLE* Res);
[propget, helpstring("property Foo")] HRESULT Foo([out, retval] BSTR* pVal);
[propput, helpstring("property Foo")] HRESULT Foo([in] BSTR newVal);
};
Для доступа к свойству Foo мастер сгенерирует два метода:
/////////////// в файле Mathem.h
STDMETHOD(get_Foo)(BSTR* pVal);
STDMETHOD(put_Foo)(BSTR newVal);
////////////// в файле Mathem.cpp
STDMETHODIMP CMathem::get_Foo(BSTR* pVal)
{
// TODO: Add your implementation code here
return S_OK;
}
STDMETHODIMP CMathem::put_Foo(BSTR newVal)
{
// TODO: Add your implementation code here
return S_OK;
}
Если мы собираемся хранить значение этого свойства в классе CMathem (а не получать его от господа бога или дьявола), то надо, очевидно, добавить в этот класс соответствующий компонент. Например, опишем в классе CMathem (файл Mathem.h) такой член-данное и инициализируем его в конструкторе:
BSTR m_bstrFoo; // добавлено
public:
CMathem()
{
m_bstrFoo=0; // добавлено
}
Реализация методов чтения и записи значения свойства Foo может быть выполнена следующим образом (файл Mathem.cpp):
STDMETHODIMP CMathem::get_Foo(BSTR* pVal)
{
*pVal=SysAllocString(m_bstrFoo);
return S_OK;
}
STDMETHODIMP CMathem::put_Foo(BSTR newVal)
{
SysFreeString(m_bstrFoo);
m_bstrFoo=SysAllocString(newVal);
return S_OK;
}
Обращаю ваше внимание на то, что работа со строками BSTR существенно отличается от работы с другими типами строк: для выделения и освобождения памяти надо использовать специальные функции выделения и освобождения памяти: SysAllocString() и SysFreeString() вместо привычных операций new и delete.
В клиентском приложении использование свойства (но только с применением интеллектуальных указателей на интерфейс) будет выглядеть так же, как и в Object Pascal, а именно:
pMathem->Foo=_T("Property testing");
wcout<<"Property Foo=="<<pMathem->Foo<<endl;
Как пишут писатели, внимательный читаттель обратит внимание на то, что метод get_Foo() извращает результат не посредством своего имени, а посредством параметра, в то время как вызов этого метода выглядит так, как будто это функция, возвращающая строку?! Чудеса, глюки или и то и другое вместе? Разбирайтесь.
Регистрация сервера в реестре Windows.
В версии MVS-2010 (как и в версиях 2005 и 2008) сервер автоматически регистрируется после его успешной сборки. Альтернативный способ регистрации сервера, как уже указывалось выше, состоит в подаче команды
Regsvr32 путь\имя_сервера
из командной строки. Например:
Regsvr32 C:\Temp\Lab4\DLLMathServer\Debug\DLLMathServer.dll
Разрегистрировать сервер можно с помощью команды
Regsvr32 /u путь\имя_сервера
Сервер надо разрегистрировать и повторно зарегистрировать, если вы изменяете путь к нему или перемещаете его в другое место.
Для тестирования сервера надо разработать клиентское приложение, которое может быть консольным приложением, приложением Win32, оконным приложением и др. Вот за него, с большим энтузиазмом, и возьмемся двумя руками.
Разработка и использование «обычных» библиотек DLL (CUI)
Есть моменты, когда все удается. Не ужасайтесь, это пройдет
Закон Мерфи
Цель работы – освоение технологии разработки и использования «обычных» библиотек DLL (Regular DLL using shared MFC DLL). В рамках этой работы мы построим обычную DLL-библиотеку, экспортирующую одну-единственную функцию. Далее надо разработать простое клиентское приложение, использующее эту библиотеку. Таким образом, мы получим аж два приложения – и все это за 2 час. с перекурами!
Задание. Рекомендуемая последовательность выполнения работы:
1. Ознакомиться с теоретической частью работы, для чего придется изучить раздел конспекта «Разработка и использование собственных DLL», особенно подраздел «Основы DLL».
2. Создать обычную (regular) библиотеку DLL, экспортирующую не класс, а просто одну или несколько функций.
3. Создать клиентское приложение, использующее разработанную библиотеку.