Создание clr dll visual studio

Обновлено: 06.07.2024

Одним из наиболее важных новшеств в готовящейся к выпуску версии SQL Server 2005 (ранее — проект Yukon) является возможность исполнения кода на основе среды исполнения Common Language Runtime (CLR). Разработчики программного обеспечения и администраторы баз данных (DBA) наверняка уже слышали о CLR. Но администраторы, которые никак не связаны с разработкой для SQL Server, могли и не обращать внимания на CLR, полагая, что на их работу CLR никак не повлияет. На мой взгляд, это не так: поскольку роль каждого, кто работает с SQL Server, постепенно меняется, необходимо иметь представление об этой новой технологии, и неважно, кем вы сами себя считаете — DBA, разработчиком или программистом.

В SQL Server 2000 и более ранних версиях SQL можно было обращаться к внешнему коду из расширенной хранимой процедуры (extended stored procedure), которая вызывала предварительно скомпилированную библиотеку DLL, созданную с учетом спецификации Open Data Services (ODS) API. Расширенные процедуры использовались для выполнения сложных математических вычислений, с привлечением функциональности операционной системы, ресурсов процессора или просто для получения функциональности, отсутствовавшей в T-SQL. Проблема при работе с расширенными процедурами состояла в том, что SQL Server должен был полностью «доверять» внешнему коду. Поскольку при создании расширенных хранимых процедур использовался неуправляемый код, а процедуры работали в адресном пространстве SQL Server, проблемы, возникающие в расширенных процедурах, могли нарушить работу SQL Server — фактически дело могло дойти до «голубого экрана смерти». Исполняемый код, созданный на базе CLR, означает, что разработчики SQL Server и DBA получают возможность безопасного наращивания возможностей своих процедур.

Знакомство с CLR

Рис. 2 иллюстрирует принцип работы CLR. Visual Studio запускает CLR-компилятор, который получает блок кода, называемый сборкой (assembly), и генерирует динамическую библиотеку, DLL. В ней содержится код в форме промежуточного языка (intermediate language, IL). CLR конвертирует этот код в машинный код и выполняет его.

Будем считать, что вызов DLL из T-SQL подобен вызову внешнего (по отношению к основному процессу) COM-объекта. Проверенный принцип программирования гласит: пореже обращайтесь к внешним процедурам, передавайте им большее число параметров и стремитесь к тому, чтобы на долю продуктивного времени в процедурах отводилось максимально возможное время. Говоря о продуктивном времени работы приложения во внешнем коде, я подразумеваю время продуктивного и эффективного использования циклов процессора.

Когда внешняя библиотека, CLR DLL, вызывается в первый раз, SQL Server должен загрузить код в память, скомпилировать исполняемый код и затем выполнить его. После того как сервер SQL выполнит эти действия, вызов внешнего объекта потребует меньших затрат по сравнению с первоначальным вызовом, но все же окажется более «дорогим», нежели вызов внутрипроцессного кода. Результат? Вызов CLR-объекта может оказаться более медленным, чем вызов хранимой процедуры T-SQL.

На уже упоминавшейся встрече директоров и редакторов рассматривались некоторые ситуации, когда сборки CLR для SQL Server могут оказаться жизнеспособными альтернативами процедурам T-SQL. Разработчики SQL Server уже реализовали решения фактически для всех подобных случаев, используя расширенные хранимые процедуры и библиотеки COM DLL. Однако в каждом из перечисленных ниже случаев было бы целесообразно переписать существующие решения на CLR-код.

  • Замена или наращивание функциональности сложных хранимых процедур T-SQL, особенно тех из них, в которых выполняются математические вычисления с использованием, например, сложных бизнес- или научных формул. Математические возможности T-SQL не намного лучше моих собственных, поэтому перенос сложной математики в CLR DLL может оказаться целесообразным.
  • Обращение к файлу Microsoft Project для обновления сложных задач, связанных с планированием. Логика CLR-процедур позволяет приложениям взаимодействовать со структурами данных Project, к которым T-SQL не может обратиться напрямую. Аналогичный подход может применяться для любых внешних структур данных, которые не имеют традиционного интерфейса доступа к данным через процессы или файлы.
  • Реализация сложных алгоритмов картографии. Используя координаты Geographic Positioning Satellite (GPS), один из региональных директоров составил запрос: "Показать все аэропорты в пяти милях от города". С помощью CLR-процедуры, работающей с геометрическими функциями, решить поставленную задачу оказалось гораздо проще, чем если бы пришлось писать сложный запрос в базу данных картографии.
  • Выполнение сложных инженерных вычислений. К примеру, моя дочь купила справочник толщиной 15 см с формулами химического машиностроения. С помощью CLR я мог бы закодировать эти формулы и с легкостью обращаться к ним из T-SQL как к функциям. Но напоминаю, что следует быть внимательным при оценке времени, необходимого для вычисления формул при стандартном подходе и при переходе на CLR.

В каждом из этих примеров CLR-код расширяет возможности и сферу применения языка запросов T-SQL. Именно для этого и создаются процедуры, основанные на использовании CLR.

Тестирование

Конечно, элементарные преобразования — это не то, ради чего создаются хранимые процедуры на CLR; они предназначены для сложных и ресурсоемких вычислений либо для тех случаев, когда решить задачу средствами традиционного T-SQL невозможно. Хранимые процедуры на CLR задумывались как замена расширенных хранимых процедур, а не как их расширение.

После консультаций с Петером Блекберном (Peter Blackburn), моим техническим гуру из Великобритании, я решил попробовать немного усложнить тест — сделать его более реалистичным. На этот раз речь шла о реализации функции RSA-шифрования с привлечением провайдера RSACryptoServiceProvider, на вход которому подается строка и возвращается зашифрованный байтовый массив. Почти три недели спустя новое приложение было готово к тестированию. На рис. 3 представлена схема его работы. В ходе разработки я обнаружил несколько проблем, из-за которых мне пришлось создать большую часть проекта в пакетах T-SQL. Кроме того, обнаружилась новая возможность отладки (ее описание приведено во врезке «Отладка процедур T-SQL и SQL CLR»). Приобретенный опыт позволил поближе познакомиться с новым инструментарием SQL Server 2005 — SQL Server Management Studio (ранее — проект Workbench), который заменил Enterprise Manager, Query Analyzer и еще несколько утилит. Microsoft в настоящее время интегрирует инструментарий Management Studio в Visual Studio 2005, чтобы упростить обслуживание баз данных SQL Server.

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

Как показано на экране 1, программа получает номер и срок действия кредитной карты пользователя. Когда пользователь нажимает кнопку Submit, из основного кода происходит вызов хранимой процедуры, которая в свою очередь вызывает сборку CLR. Последняя генерирует секретный ключ шифрования RSA и использует этот ключ для шифрования номера кредитной карты пользователя. Хранимая процедура записывает срок действия кредитной карты и шифрованные данные в базу данных (на этот раз в виде байтового массива). Основной код сохраняет секретный ключ шифрования (private encryption key, PEK) и отображает его в клиентском приложении для облегчения процедуры отладки. Однако PEK на сервере не хранится; таким образом, только сам пользователь может дешифровать закодированное значение на сервере, и только в том случае, если клиентское приложение хранит это значение на локальном диске. Я не могу сравнивать приведенный пример программ шифрования/дешифрации с эквивалентным кодом на T-SQL, такая функциональность в T-SQL просто не поддерживается, но этот пример демонстрирует механику создания CLR-кода, и описанный неформальный тест показал, что он прекрасно работает. Однако я бы посоветовал поэкспериментировать со своим CLR-кодом в условиях, приближенных к конкретной бизнес-среде.

Когда пользователю нужно извлечь номер своей кредитной карты, чтобы серверная функция смогла использовать его, приложение на стороне клиента вызывает соответствующую хранимую процедуру. Процедура извлекает ключ шифрования, находит запись клиента по его ID и сроку действия и дешифрует значение при помощи другой процедуры на CLR. Для облегчения отладки приложения код возвращает расшифрованный номер карты в клиентское приложение (в «боевой» программе серверное приложение может, например, просто разблокировать полученное значение для выполнения дальнейшей обработки).

Нужно иметь в виду, что приложение вызывает хранимые процедуры на CLR из процедур T-SQL. Я нахожу такой подход более удобным, чем непосредственный вызов CLR-процедур, и полагаю, что большинство разработчиков придут к такому же выводу. Другой подход состоит в кодировании CLR-процедур в виде функций и встраивании их в код T-SQL.

Итак, мы рассмотрели некоторые концепции хранимых CLR-процедур. В статье было показано, как работают такие процедуры и где они в большей мере соответствуют высокопроизводительным системам управления базами данных (DBMS). В дальнейшем я собираюсь рассказать о том, как применять Visual Studio 2005 и SQL Server 2005 для кодирования и тестирования хранимых CLR-процедур, используемых в тестовом приложении, которое было представлено выше.

В качестве примера приведу текст программы, выводящий на экран возраст объекта:
исходный текст программы, чтобы было понятно:
using System;

namespace ConsoleApplication_Test_Csharp
public class SomeClass
int age;
public int GetAge()
age = 22;
return age;
>
>
public sealed class Program
<
public static void Main()
System. Console .Write( "My age is " );
SomeClass me = new SomeClass();
int myAge;
myAge = me.GetAge();
System. Console .WriteLine(myAge);
Console .ReadLine();
>

>
>

* This source code was highlighted with Source Code Highlighter .

И так приступим:

CLR (Common language runtime) — общеязыковая исполняющая среда. Она обеспечивает интеграцию языков и позволяет объектам благодаря стандартному набору типов и метаданным), созданным на одном языке, быть «равноправными гражданами» кода, написанного на другом.

Компилятор, помимо ассемблера IL создает полные метаданные.

Метаданные — набор из таблиц данных, описывающих то, что определено в модуле. Также есть таблицы, указывающие на что ссылается управляемый модуль (например, импортируемые типы и числа). Они расширяют возможности таких технологий как библиотеки типов и файлы языка описания интерфейсов (IDL). Метаданные всегда связаны с файлом с IL кодом, фактически они встроены в *.exe или *.dll.
Таким образом метаданные это таблицы, в которых есть поля, говорящие о том, что такой-то метод находится в таком-то файле и принадлежит такому-то типу(классу).
Вот как выглядят метаданные для моего примера (таблицы метаданных просто преобразованы в понятный вид с помощью дизассемблера ILdasm.exe. На самом деле это часть *.exe файла программы:

metadata

Разобравшись с основными понятиями, давайте посмотрим из чего же состоит тот самый управляемый модуль (или просто наш файл ConsoleApplication_Test_Csharp.exe, который выполняет вывод на экран возраста объекта):

И так, что же происходит, когда запускается впервые программа?
Сперва происходит анализ заголовка, чтобы узнать какой процесс запустить (32 или 64 разрядный). Затем загружается выбранная версия файла MSCorEE.dll ( C:\Windows\System32\MSCorEE.dll для 32разрядных процессоров)
После чего вызывается метод, расположенный MSCorEE.dll, который и инициализирует CLR, сборки и точку входа функции Main() нашей программы.

static void Main()
System. Console .WriteLine( "Hello " );
System. Console .WriteLine( "Goodbye" );
>

* This source code was highlighted with Source Code Highlighter .

Для выполнения какого-либо метода, например System.Console.WriteLine(«Hello „), IL должен быть преобразован в машинные команды (те самые нули и единицы) Этим занимается Jiter или just-in-time compiler.

Сперва, перед выполнением Main() среда CLR находит все объявленные типы (например тип Console).
Затем определяет методы, объединяя их в записи внутри единой “структуры» (по одному методу определенному в типе Console).
Записи содержат адреса, по которым можно найти реализации методов (т.е. те преобразования, которые выполняет метод).

Jit

При первом обращение к функции WriteLine вызывается JiT-compiler.
JiTer 'у известны вызываемый метод и тип, которым определен этот метод.
JiTer ищет в метаданных соответствующей сборки — реализацию кода метода (код реализации метода WriteLine(string str) ).
Затем, он проверяет и компилирует IL в машинный код (собственные команды), сохраняя его в динамической памяти.
После JIT Compiler возвращается к внутренней «структуре» данных типа (Console) и заменяет адрес вызываемого метода, на адрес блока памяти с исполняемыми процессорными командами.
После этого метод Main() обращается к методу WriteLine(string str) повторно. Т.к. код уже скомпилирован, обращение производится минуя JiT Compiler. Выполнив метод WriteLine(string str) управление возвращается методу Main().

Из описания следует, что «медленно» работает функция только в момент первого вызова, когда JIT переводит IL код в инструкции процессора. Во всех остальных случаях код уже находится в памяти и подставляется как оптимизированный для данного процессора. Однако если будет запущена еще одна программа в другом процессе, то Jiter будет вызван снова для того же метода. Для приложений выполняемых в х86 среде JIT генерируется 32-разрядные инструкции, в х64 или IA64 средах — соответственно 64-разрядные.

IL может быть оптимизирован, т.е. из него будут удалены IL — команды NOP (пустая команда). Для этого при компиляции нужно добавить параметры

Debug версия собирается с параметрами: /optimize -, /debug: full
Release версия собирается с параметрами: /optimize +, /debug: pdbonly

Чем же отличается управляемый код от неуправляемого?

Неуправляемый код компилируется для конкретного процессора и при вызове просто исполняется.

В управляемой среде компиляция производится в 2 этапа:

Взаимодействие с неуправляемым кодом:

— управляемый код может вызывать направляемую функцию из DLL посредствам P/Invoke (например CreateSemaphore из Kernel32.dll).
— управляемый код может использовать существующий COM-компонент (сервер).
— неуправляемый код может использовать управляемый тип (сервер). Можно реализовать COM — компоненты в управляемой среде и тогда не нужно вести подсчет ссылок интерфейсов.

Параметр /clr позволяет скомпилировать Visual С++ код в управляемые IL методы (кроме когда, содержащего команды с ассемблерными вставками ( __asm ), переменное число аргументов или встроенные процедуры ( __enable, _RetrurAddress )). Если этого сделать не получится, то код скомпилируется в стандартные х86 команды. Данные в случае IL кода не являются управляемыми (метаданные не создаются) и не отслеживаются сборщиком мусора (это касается С++ кода).

В дополнение хочу рассказать о системе типов CTS, принятой Microsoft.

— CTS поддерживает только единичное наследование (в отличие от С++)
— Все типы наследуются от System.Object (Object — имя типа, корень все остальных типов, System — пространство имен)

По спецификации CTS любой тип содержит 0 или более членов.

Поле — переменная, часть состояния объекта. Идентифицируются по имени и типу.
Метод — функция, выполняющая действие над объектом. Имеет имя, сигнатуру(число параметров, последовательность, типы параметров, возвр. значение функции) и модификаторы.
Свойство — в реализации выглядит как метод (get/set) а для вызывающей стороны как поле ( = ). Свойства позволяют типу, в котором они реализованы, проверить входные параметры и состояние объекта.
Событие — обеспечивает механизм взаимного уведомления объектов.

Public — метод доступен любому коду из любой сборки
Private — методы вызывается только внутри типа
Family (protected) — метод вызывается производными типами независимо от сборки
Assembly (internal) — метод вызывается любым кодом из той же сборки
Family or Assembly
(protected internal) — метод вызывается производными типами из любой сборки и + любыми типами из той же сборки.

CLS

Пример проверки на соответствие CLS

Атрибут [assembly: CLSCompliant(true)] заставляет компилятор обнаруживать любые доступные извне типы, содержащие конструкции, недопустимые в других языках.

  1. using System;
  2. [assembly: CLSCompliant( true )]
  3. namespace SomeLibrary
  4. // возникает предупреждение поскольку тип открытый
  5. public sealed class SomeLibraryType
  6. // тип, возвращаемый функцией не соответсвует CLS
  7. public UInt32 Abc()
  8. // идентификатор abc() отличается от предыдущего, только если
  9. // не выдерживается соответсвие
  10. public void abc()
  11. // ошибки нет, метод закрытый
  12. private UInt32 ABC()
  13. >
  14. >

Первое предупреждение: UInt32 Abc() возвращает целочисленное целое без знака. Visaul Basic, например, не работает с такими значениями.
Второе предупрждение: два открытых метода Abc() и abc() — одиноквые и отличаются лишь регистром букв и возвращаемым типом. VisualBasic не может вызывать оба метода.

Убрав public и оставив только sealed class SomeLibraryType оба предупреждения исчезнут. Так как SomeLibraryType по-умолчанию будет internal и не будет виден извне сборки.

Превью к статье о создании C++ Windows Forms проекта

Шаг 0. А вдруг получится сразу?

В настоящее время IDE, поддерживающих Windows forms, не так много — буквально одна только Visual Studio, более известная как просто "студия". Поэтому будем рассматривать создание и решение проблем именно в этой среде разработки. Первым шагом запустим студию, начнём создавать новый проект и попытаемся найти Windows forms проект для C++:

Создаём новый проект в студии

Ищем Winfows Forms для C++

Если у вас более старая версия Visual Studio, то интерфейс будет выглядеть немного иначе, однако данная функциональность будет той же. Также не исключено, что у Вас может быть данный тип проекта для C++ (на некоторых версиях формы для C++ были доступны сразу после установки IDE). Если же у Вас, как и у нас поиск не дал нужных результатов, то переходим к следующему шагу.

Шаг 1. Создание CLR проекта

Поскольку непосредственно Windows Forms проекта у нас не оказалось, мы обхитрим студию и создадим пустой CLR проект на С++. Для этого в том же окне поиска необходимо найти и выбрать Новый CLR проект , ввести имя (если нужно, то поменять директорию расположения проекта) и немного подождать, пока студия сделает свою работу.

Ищем пустой CLR проект (.Net Framework)

Создаём новый пустой CLR проект

В результате Visual Stido создаст новый C++ CLR проект, который будет выглядеть примерно так:

Результат создания нового CLR проекта

Шаг 2. Добавить форму

Чтобы сделать CLR проект проектом Windows Forms, нужно просто добавить в него форму. Для этого в верхнем меню нужно выбрать Проект - Добавить новый элемент и в появившемся окне выбрать категорию Visual C++ - UI и затем выбрать Форма Windows Forms .



После данной операции нас ждёт разочарование в виде ошибки Исключение из HRESULT: 0x8000000A :

Вместо формы получили ошибку

Шаг 3. Исправляем появившуюся ошибку

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

В результате код файла Form1.cpp будет выглядеть следующим образом:

Добавление основной программы к форме

Шаг 4. Переоткрыть проект

Всё, что теперь осталось сделать — это закрыть текущее решение, а затем открыть его снова. Для этого можно закрыть саму студию, а можно выбрать в верхнем меню Файл - Закрыть решение , после чего в появившемся окне выбрать только что созданный проект и открыть его заново.

Форма создалась, можно добавлять компоненты

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

Фото Перминова Андрея, автора этой статьи

Выпускник МГУ им. М.В. Ломоносова

Programforyou — это сообщество, в котором Вы можете подтянуть свои знания по программированию, узнать, как эффективно решать те или иные задачи, а также воспользоваться нашими онлайн сервисами.

Рассмотрен по шагам процесс создания в 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

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

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

__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. Вводим в него следующий текст:

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);
>
>

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

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 : здесь определена точка входа для консольного приложения.
//

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.
//

using namespace std;

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

[Пути поиска библиотек 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-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. На скриншоте показана как раз такая проблема с отладкой.

Читайте также: