Visual Studio: создание и использование загружаемой библиотеки DLL (Dynamic Link Library) Печать
Добавил(а) microsin   

Рассмотрен по шагам процесс создания в Visual Studio файла динамически загружаемой библиотеки *.dll и методы вызова функций из неё. Все описанное далее делается на примере среды разработки Visual Studio 2003 Version 7.1.3088, но также подходит и к Visual Studio 2005. Для простоты папки создаваемых проектов будут находиться в директории C:\VSPROJ\.

[Создание библиотеки DLL]

1. File -> New -> Project, в дереве Project Types: выбираем Visual C++ Projects -> Win32, в окошке Templates: выбираем Win32 Console Project. В поле Name: вводим имя проекта для DLL, например MyDLL, в поле ввода Location: выбираем путь C:\VSPROJ (можно воспользоваться кнопкой Browse...). Жмем ОК.

DLL-VisualStudio01.PNG

2. Появится окошко мастера настройки свойств проекта Win32 Application Wizard - MyDLL. Щелкаем на Application Settings, Application type: выбираем DLL, в Additional options: ставим галочку Empty project, жмем Finish.

DLL-VisualStudio02.PNG

3. Создадим заголовочный файл для модуля наших функций в создаваемой DLL. В дереве браузера проекта выбираем Header Files -> Add -> Add New Item..., в дереве Categories: выбираем Visual C++ -> Code, в шаблонах Templates: выбираем Header File (.h). В поле Name: вводим любое имя файла, например mydllmodule, жмем Open.

DLL-VisualStudio03.PNG

Создастся новый файл, к котором предстоит ввести декларацию класса и методов функций, которые мы хотим добавить в файл DLL. Создадим класс DummyClass, и в нем две функции Beep и Msg. Одна будет выдавать короткий звуковой сигнал, вторая будет выводить окошко с сообщением. Вводим следующий текст:

// mydllmodule.h

namespace dllfuncs
{
    class DummyClass
    {
    public:
        // Делаем бип
        static __declspec(dllexport) void ShortBeep (void);

        // Выводим окошко с сообщением
        static __declspec(dllexport) void Msg (char* msgstr);
    };
}

Вариант без использования классов: 

// mydllmodule.h

__declspec(dllexport) void ShortBeep (void);
__declspec(dllexport) void Msg (char* msgstr);

4. Создадим файл для модуля наших функций в создаваемой DLL, в котором будет сам код функций. В дереве браузера проекта выбираем Source Files -> Add -> Add New Item..., в дереве Categories: выбираем Visual C++ -> Code, в шаблонах Templates: выбираем C++ File (.cpp). В поле Name: вводим то же самое имя файла, которое вводили на шаге 3 - mydllmodule, жмем Open.

DLL-VisualStudio04.PNG

Создастся новый файл, к котором будет код функций, добавляемых в файл DLL. Вводим в него следующий текст:

// mydllmodule.cpp
// compile with: /EHsc /LD

#include "Windows.h"
#include "mydllmodule.h"

#include

using namespace std;

namespace dllfuncs
{
    void DummyClass::ShortBeep(void)
    {
        Beep(1000,100); //частота 1000 Гц, длительность 100 мс
    }

    void DummyClass::Msg(char* msgstr)
    {
        MessageBox(NULL, msgstr, "Message from DLL", MB_OK);
    }
}

Вариант без использования классов: 

// mydllmodule.cpp

#include "Windows.h"
#include
#include "mydllmodule.h"

using namespace std;

void ShortBeep(void)
{
     Beep(1000,100); //частота 1000 Гц, длительность 100 мс
}

void Msg(char* msgstr)
{
     MessageBox(NULL, msgstr, "Message from DLL", MB_OK);
}

После всех этих действий появится папка C:\VSPROJ\MyDLL\, в которой будут находиться файлы mydllmodule.cpp и mydllmodule.h, а также конфигурационные файлы проекта MyDLL.

5. Чтобы наш проект скомпилировался в DLL, это должно быть настроено в свойствах проекта. Проверим настройки: MyDLL -> Properties... -> Configuration Properties -> General -> Configuration Type должно быть установлено в Dynamic Library (.dll). Жмем OK.

DLL-VisualStudio05.PNG

6. Теперь скомпилируем нашу библиотеку Build -> Build MyDLL. В папке C:\VSPROJ\MyDLL\Debug появятся два файла MyDLL.dll и MyDLL.lib. Первый файл MyDLL.dll - динамически загружаемая библиотека наших функций, она должна находится в папке исполняемого файла, который использует эти функции (см. [Создание приложения, которое использует функции из DLL]). Второй файл MyDLL.lib - статическая библиотека, которая может быть присоединена на этапе компиляции к приложению, использующему функции проекта MyDLL (см. [Создание приложения, которое использует функции из статической библиотеки lib]).

[Создание приложения, которое использует функции из загружаемой DLL]

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

1. File -> New -> Project, в дереве Project Types: выбираем Visual C++ Projects -> Win32, в окошке Templates: выбираем Win32 Console Project. В поле Name: вводим имя проекта для приложения, использующего загрузку DLL, например DLLtest, в поле ввода Location: выбираем путь C:\VSPROJ (можно воспользоваться кнопкой Browse...). Также выберем радиокнопку Add to Solution, это просто добавит в нашу группу проектов (в котором уже есть проект MyDLL) новый проект DLLtest. Жмем ОК.

DLL-VisualStudio06.PNG

2. Настроим свойства тестового приложения. Выберите тип приложения Console application и нажмите Finish.

DLL-VisualStudio07.PNG

После этого автоматически создастся папка C:\VSPROJ\DLLtest\, в ней появится файл DLLtest.cpp, и туда введется пустой код тела функции _tmain. В функцию _tmain мы впоследствии добавим вызовы функций из модуля MyDLL.

3. Нужно настроить в проекте DLLtest ссылки на загружаемую DLL. Щелкаем правой кнопкой на папку DLLtest в дереве Solution Explorer - DLLtest, выберем Add Reference..., откроется окно выбора внешних ссылок на загружаемые библиотеки. На закладке Projects выберите MyDLL c:\Vsproj\MyDLL\ и нажмите кнопку Select, а затем OK. В проекте DLLtest в дереве проекта появится папка References и подпапка MyDLL, в которой с помощью Object Browser можно просмотреть функции из нашей библиотеки MyDLL.

DLL-VisualStudio08.PNG

DLL-VisualStudio09.PNG

Весь код для загрузки DLL и инициализации указателей на используемые функции будет сгенерирован автоматически в процессе компиляции приложения.

4. Вставим в приложение вызовы функций Msg и ShortBeep, для этого добавим в модуль DLLtest.cpp включаемый заголовок mydllmodule.h, и установим добавочные пути поиска файлов заголовков проекта DLLtest. Жмем правую кнопку на DLLtest -> выбираем Properties -> Configuration Properties -> C/C++ -> General -> Additional Include Directories -> $(ProjectDir)\..\MyDLL и жмем OK.

DLL-VisualStudio10.PNG

Добавим в модуль DLLtest.cpp вызовы функций ShortBeep и Msg, код модуля DLLtest.cpp получится следующий:

// DLLtest.cpp : здесь определена точка входа для консольного приложения.
//

#include "stdafx.h"
#include "mydllmodule.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{

  dllfuncs::DummyClass::ShortBeep();
  dllfuncs::DummyClass::Msg("Hello, world!");
  return 0;
}

Вариант без использования классов: 

// DLLtest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "mydllmodule.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
  ShortBeep();
  Msg("Hello, world!");
  return 0;
}

5. Осталось скомпилировать наше приложение (Build -> Build Solution или правой кнопкой на папку DLLtest, выбираем Build), переписать скомпилированную C:\VSPROJ\MyDLL\Debug\MyDLL.dll в папку C:\VSPROJ\DLLtest\ (иначе при старте DLLtest.exe будет выдаваться сообщение, что не найден файл библиотеки MyDLL.dll), и запустить приложение DLLtest на выполнение. Щелкаем правой кнопкой в Solution Explorer на DLLtest, выбираем в контекстном меню Set as StartUp Project. Затем выбираем Debug -> Start. Прозвучит короткий звуковой сигнал и появится окно с сообщением "Hello, world!". Отлично - наша DLL загружается и работает!

DLL-VisualStudio11.PNG

[Пути поиска библиотек DLL при запуске приложения]

При запуске приложения Windows выполняет поиск библиотек DLL в следующей последовательности:

1. Каталог, в котором находится исполняемый модуль текущего процесса.
2. Текущий каталог.
3. Системный каталог Windows (обычно это папка System32). Путь к этому каталогу извлекается с помощью функции GetSystemDirectory.
4. Каталог Windows. Путь к этому каталогу извлекается с помощью функции GetWindowsDirectory.
5. Каталоги, указанные в переменной среды PATH.

Примечание: переменная среды LIBPATH не используется.

[Ошибки в проектах с DLL

1. <имя_программы> fatal error LNK1104: cannot open file 'путь_до_папки_проекта_DLL\Debug\имя_файла_библиотеки.lib', например:

DLLtest fatal error LNK1104: cannot open file '\Vsproj\MyDLL\Debug\MyDLL.lib'

Такая ошибка произошла потому, что в модуле кода DLL (в нашем примере это mydllmodule.cpp) забыли подключить заголовок mydllmodule.h (по сообщению об ошибке догадаться невозможно, в чем проблема). Именно в этом заголовке используются атрибуты функций DLL (__declspec(dllexport)). Исправление ошибки: в нашем примере добавить в файл mydllmodule.cpp строчку #include "mydllmodule.h".

2. Не удалось найти компонент - "Приложению не удалось запуститься, поскольку < имя.dll > не был найден. Повторная установка приложения может исправить эту проблему."

DLL-VisualStudio12.PNG

Проблема решается, если положить в путь поиска нужную DLL (для нашего примера файл MyDLL.dll нужно положить в папку C:\VSPROJ\DLLtest\). Самое лучшее решение - настроить команду в Post-Build Event, которая будет автоматически выполнить копирование DLL в папку отладки программы, которая использует эту DLL. Настраивается Post-Build Event просто, процесс по шагам, на нашем примере проектов MyDLL и DLLtest (предполагается, что обе папки проектов находятся на одном уровне в файловой системе, и входят в один Solution среды разработки Visual Studio, см. [3]):

1. Сначала нужно настроить порядок компиляции проектов в Solution. DLL должна компилироваться первой. Порядок компиляции настраивается через свойства Solution, контекстное меню Project Build Order...

DLL-VisualStudio13.PNG

Порядок проектов в списке можно поменять с помощью закладки Dependencies, которая определяет, какой проект от какого зависит. Первым в списке в нашем примере должен быть проект MyDLL.

2. Теперь осталось в проекте MyDLL настроить Post Build Event, копирующее результат компиляции - файл MyDLL.dll в папку Debug проекта DLLtest. Щелкаем правой кнопкой в Solution Explorer на проекте MyDLL, выбираем Properties -> Configuration Properties -> Build Events -> Post-Build Event -> вставляем в поле Command Line строку cmd /C copy /Y $(TargetPath) $(ProjectDir)\..\DLLtest\Debug\$(TargetFileName).

DLL-VisualStudio14.PNG

[Как отлаживать код DLL в Visual Studio]

В IDE есть приятная возможность отладки DLL по тексту исходного кода, однако это настраивается не слишком очевидно. Процесс по шагам (на нашем примере проектов MyDLL и DLLtest).

1. Сначала нужно задать при отладке стартовым приложением в Solution проект с DLL.

2. Нужно убедиться, что оба проекта MyDLL и DLLtest скомпилированы в конфигурации для отладки Debug.

3. Нужно настроить в свойствах проекта MyDLL запуск внешнего приложения, вызывающего нашу DLL. Щелкаем правой кнопкой в Solution Explorer на проекте MyDLL, выбираем Properties -> Configuration Properties -> Debugging -> в поле Command вводим строку $(ProjectDir)\..\DLLtest\Debug\DLLtest.exe.

Теперь можно ставить точки останова в исходном коде DLL, и отлаживать его, как обычный код. Если у Вас не работает отладка по коду DLL, то возможно, что-то не так с символами отладки (отладочной информацией). Если отладочная информация по коду DLL недоступна, то точки останова в DLL будут в виде коричневых кружков со знаками вопроса. Разобраться с проблемой загрузки символов может помочь просмотр модулей Debug -> Windows -> Modules. На скриншоте показана как раз такая проблема с отладкой.

DLL-VisualStudio15.PNG

Напротив модуля MyDLL.dll в столбце Information стоит No Symbols Loaded, что означает, что отладочная информация по коду DLL недоступна или ошибочна. Решить проблему может помочь полная рекомпиляции обоих проектов MyDLL и DLLtest, входящих в Solution. Выбираем в меню Build -> Rebuild Solution, выбираем Debug -> Start, и теперь отладка по исходному коду DLL работает.

DLL-VisualStudio16.PNG

[Ссылки]

1. Walkthrough: Creating and Using a Dynamic Link Library.
2. Search Path Used by Windows to Locate a DLL.
3. Исходный код проектов MyDLL и DLLtest .
4bphelper - мастер поиска ошибок при отладке DLL