Бесценные методические приказания
В этой части надо разработать обычную библиотеку DLL, которая будет экспортировать одну функцию, например, возвращающую объем свободного пространства на выбранном пользователем диске. Естественно, никто не запрещает поместить в библиотеку несколько функций.
Шаг 1. Создание каркаса приложения-библиотеки RegDLL.
Выполните, как обычно при создании нового проекта, команду FileèNew Project и в появившемся окне выберите Visual C++ èMFC, а на вкладке справа – MFC DLL (рис. 1). В поле Nameзадайте имя библиотеки(в данном примере RegDLL). В поле Location выберите каталог, например, C:\Temp или D:. Не рекомендую хранить проект на флешке во время его разработки, так как это сильно замедляет работу, а время – деньги, да еще какие!
Под страхом смерти не устанавливайте переключатель Create directory for solution или снимите его, если он уже установлен. Навалитесь на ОК, на вкладке Application Settings оставьте выбор мастера по умолчанию DLL type==Regular DLL using shared MFC DLL, прилягте на Finish.
Просмотрите и проанализируйте файл RegDll.cpp, наибольший интерес в котором на данном этапе представляет комментарий практически в начале файла. В нем настоятельно рекомендуется в самом начале каждой (вашей) функции, экспортируемой библиотекой и вызывающей какие-либо функции MFC, добавить макрос
AFX_MANAGE_STATE(AfxGetStaticModuleState());
Этот макрос надо указывать именно в самом начале функции, даже до каких-либо объявлений. Если сказать кратко, то он обеспечивает надежное функционирование библиотечных MFC-функций. Более подробные сведения – в MSDN.
Рис. 1. Выбор каркаса проекта
Шаг 2. Включение в библиотеку собственной функции.
Добавьте в конец файла RegDLL.cpp, т.е. после определения функции CRegDLLApp::InitInstance(), текст экспортируемой функции:
if (pGetDiskFreeSpaceEx) { fResult = pGetDiskFreeSpaceEx (DrivePath, (PULARGE_INTEGER)&i64FreeBytesToCaller, (PULARGE_INTEGER)&i64TotalBytes, (PULARGE_INTEGER)&i64FreeBytes); if(fResult) return i64FreeBytes; else { CString Msg; Msg.Format(_T("Код ошибки %d"),GetLastError()); Msg=_T("Ошибка при вызове функции GetDiskFreeSpaceEx()\n")+Msg; AfxMessageBox(Msg); return 0; } } else { // пытаемся использовать устаревшую функцию, поддерживающую // диски размером до 2ГБ fResult = GetDiskFreeSpaceA (DrivePath, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters); |
if(fResult) return (__int64)dwFreeClusters*dwSectPerClust*dwBytesPerSect; else { CString Msg; Msg.Format(_T("Код ошибки %d"),GetLastError()); Msg=_T("Ошибка при вызове функции GetDiskFreeSpaceA()\n")+Msg; AfxMessageBox(Msg); return 0; } } } |
Вывод сообщений об ошибке в данной функции неуместен для реального мира, но он здесь сделан исключительно ради демонстрации самой возможности вывода сообщения в библиотечной функции с помощью MFC-функции AfxMessageBox(). На практике вместо вывода сообщения об ошибке следовало бы, в свете последних новаций в этой сфере, генерировать исключительную ситуацию (exception) или использовать функцию SetLastError() для того, чтобы передать вызывающей программе пренеприятнейшее известие.
Шаг 3. Постройте библиотеку с помощью команды меню Build→Build RegDll.dll – это должно получиться, если расположение звезд для вас благоприятно.
В результате в папке Debug проекта вы должны обнаружить, среди множества других, файлы RegDLL.dll и RegDLL.lib, которые нам еще понадобятся. В качестве активной конфигурации рекомендую использовать Debug. Воистину: все гениальное – просто! Или вы не согласны?
На этом разработка библиотеки закончена и далее ее надо тщательно тестировать.
Теперь постараемся подключить нашу библиотеку RegDLL, являющуюся гениальным бизнес-проектом, к не менее гениальному приложению Lab1, с создания которого мы и начнем, и продолжим, но не закончим.
Для того чтобы можно было как-то более или менее по-человечески тестировать функцию из библиотеки мы создадим простейшее консольное клиентское приложение.
Шаг 4. Создание каркаса клиентского приложения, использующего разработанную библиотеку.
Закрываем текущий проект, выполняем команду FileèNew Project, в правой панели выбираем Visual C++ èWin32, а на панели справа выбираем Win32 Console Application вместо MFC DLL, а в поле Name указываем имя проекта Lab1или другое ваше любимое. В поле Location,как и прежде, надо выбрать каталог, в котором должен создаваться проект. Нажимаем кнопку ОК и переходим к следующему шагу.
В следующем окне жизнерадостно щелкаем ЛКМ по Application Type и устанавливаем переключатель MFC без изменения других свойства приложения.
Шаг 5. Подключение библиотеки RegDLL.
Для дальнейшей разработки программы необходимо:
· скопировать в каталог текущего проекта файл RegDLL.lib, чтобы компоновщик смог «увидеть» функцию DiskFree(), экспортируемую библиотекой;
· скопировать в каталог \Debug текущего проекта файл RegDLL.dll;
· выбрать на вкладке Solution Explorer имя проекта Lab1 (не решения), выполнить команду ProjectèLab1 Properties,выбрать папку Cofiguration PropertiesèLinkerèInput и добавить ссылку на файл RegDLL.lib (рис. 2). Не забудьте о кнопке «Применить».
И еще одно действие: добавьте в начало файла Lab1.cpp, после директив препроцессора, описание импортируемой (из библиотеки RegDLL) функции
extern "C" __declspec(dllimport) __int64 DiskFree(int Drive);
Рис. 2. Добавление ссылки на файл библиотеки RegDLL.lib
Шаг 6. Вызов функции DiskFree.
Функции DiskFree() надо передать номер диска, выбранного пользователем, но пользователю удобнее указывать не номер диска, а его букву.
Замечание. Пользователь может указать любую букву от ‘A’ от ‘Z’, в то время как в реальной системе существуют далеко не все 26 дисков. В разделе конспекта «Получение информации о компьютере и операционной системе» приводится описание функции GetLogicalDrives(), с помощью которой можно получить список реально имеющихся в системе томов и логических дисков.
Один из возможных способов вызова функции DiskFree() следующий.
/* В следующем цикле в переменную m_strFreeBytes помещаем по три цифры, начиная с наименее значащих цифр. Каждая тройка отделяется от другой пробелом */ do { int Mod=FreeBytes%1000; FreeBytes/=1000; CString Temp; Temp.Format(_T("%03d"),Mod); m_strFreeBytes=Temp+m_strFreeBytes; m_strFreeBytes=_T(" ")+m_strFreeBytes; } while(FreeBytes); /*Удаляем самый первый пробел и ведущие нули*/ m_strFreeBytes.Delete(0,1); while(m_strFreeBytes[0]=='0')m_strFreeBytes.Delete(0,1); wcout<<_T("Свободной памяти на диске ")<<chDisk<< _T(": ")<<m_strFreeBytes.GetBuffer()<<_T(" байт")<<endl; } } |
Очевидно, что для получения работающей программы достаточно поместить вызов этой функции в функции _tmain(), а для поддержки ввода/вывода символов кириллицы достаточно поместить вызов функции setlocale(LC_ALL,"rus")в главной функции.
Хочется верить, что приведенный код не нуждается в дополнительных комментариях. В крайнем случае воспользуйтесь своими мыслительными способностями и MSDN. Соберите приложение и запустите на выполнение. Проверьте корректность вывода сведений об имеющихся в системе дисках.
Попробуйте также выбрать не существующий в системе диск или, например, пустой привод компакт-диска – вы увидите (по крайней мере должны увидеть, если функция DiskFree() реализована подобающим образом) окно с сообщением об ошибке и ее кодом (рис. 3).
|
Рис. 3. Окно с сообщением об ошибке
И какой прок в этом коде ошибки, как его интерпретировать? Вызовите утилиту MVS-2010 командой ToolsèError Lookup, введите код ошибки, помолитесь и после нажатия подходящей волшебной кнопки вы увидите текстовое сообщение об ошибке. А как в программе получить текстовое сообщение об ошибке по ее коду, возвращенному функцией GetLastError()? Да запросто – см. Win32-функцию FormatMessage() (не путать с функцией-членом CString::FormatMessage()). Другой вариант – ищите функцию GetErrorMessage() в конспекте лекций.
Шаг 7. Отладка библиотеки DLL.
Как, при необходимости, трассировать код библиотечных функций и, например, просмотреть значения переменных, если библиотека не может исполняться как самостоятельное приложение? Загрузите в ИС проект библиотеки RegDLL и попробуйте запустить отладку с помощью F5, предварительно установив контрольную точку в библиотечной функции DiskFree() (это можно сделать и потом или использовать другие команды и приемы отладки). При первом таком запуске библиотеки на отладку вам будет показано окно с сообщением об ошибке: невозможно запустить библиотеку.
Выход на самом деле есть: надо для библиотеки указать вызывающий процесс и тогда отладчик запустит его и позволит отлаживать библиотеку. Последовательность действий такова.
1. Во вкладке Solution Explorer выберите проект библиотеки: в нашем случае это RegDLL.
2. В меню View выполите команду Property Pages.
3. В окне RegDLL Property Pages в выпадающем списке Configuration выберите Active(Debug) В папке Configuration Properties выберите категорию Debugging.
4. В списке Debugger to launch list выберите Local Windows Debugger.
5. Откройте выпадающий список Command, выберите в нем команду Browse и с ее помощью укажите расположение и имя вызывающего процесса, в нашем случае это Lab1.exe. Закончите работу нажатием Применить и ОК.
Заметьте, что после выполнения этих настроек библиотеки ее надо вновь построить и вновь скопировать файл RegDLL.dll в папку с исполняемым файлом Lab1.exe.