Андрей Смирнов
Время чтения: ~10 мин.
Просмотров: 351

Создание динамически загружаемой DLL библиотеки на C#

DLL (Dynamic Link Library) – динамически подключаемая библиотека функций. Для библиотек DLL предполагается многократное использование различными программами. Поговорим о том, как создать библиотеку DLL в Visual Studio, используя языки программирования C и C#.

Создание dll на языке Си

Создаем в Visual Studio новый проект – консольное приложение.

createprojectDLL.png

В запустившемся “Мастере приложений Win32″ жмем кнопку “Далее”. В следующем окне выбираем тип приложения: “Библиотека DLL”; также ставим галочку напротив параметра “Пустой проект”. Жмем кнопку “Готово”.

createDLL.png

Теперь необходимо создать два файла исходного кода: “main.cpp” и “main.def”. Для этого в “обозревателе решений” нажмем правой кнопкой мыши на папку “Файлы исходного кода”, далее – “Добавить” – “Создать элемент”.

dllcreatemain.png

В появившемся окне “Добавление нового элемента” во вкладке “Код” представлены типы файлов, которые нам необходимы. Создадим “main.cpp” и “main.def”.

dllcreatemain1.png

В итоге обозреватель решений будет выглядеть вот так:

DLLsoursecode.png

Перейдем к файлу “main.cpp”. Для примера, напишем две функции add и sub, выполняющих сложение и вычитание соответственно для двух целых чисел.

Перед каждой функцией необходимо добавить модификатор __declspec(dllexport) (два нижних подчеркивания). Этот модификатор разрешает экспорт функции из библиотеки DLL для ее использования в других приложениях. В итоге получим такой код:

Теперь перейдем к файлу “main.def”. В нем необходимо указать: название библиотеки DLL, ключевое слово EXPORTS, а после него названия функций, экспорт которых необходимо разрешить. В нашем случае, получится вот так:

Осталось построить решение. Для этого во вкладке “ПОСТРОЕНИЕ” нажмем кнопку “Построить решение” (Ctrl+Shift+B).

В итоге, в папке с проектом будет создан файл DLLvscode.dll.

DLLresult.png

На этом создание DLL-файла завершено.

Если вы хотите скачать исходник проекта в Visual Studio, то нажмите на кнопку ниже:

Создание dll на языке C#

В языке программирования C#, в отличие от языка C, при создании dll-файлов, не нужно указывать модификатор __declspec(dllexport). И в наличии файла “main.def” также нет необходимости.

Создадим в Visual Studio новый проект на языке C#: “Библиотеку классов”.

createdllcsharp.png

На основе функций add и sub, описанных выше, создадим в классе vscode соответствующие методы. Пространство имен указывать не нужно.

Построим решение проекта (Ctrl+Shift+B). В папке Debug будет лежать созданный dll-файл.

dllcsharpcomplete.png

Скачать исходник проекта можно ниже:

Поделиться в соц. сетях:

CodeNet / Языки программирования / C / C++ / Borland C++ Builder / Приложения и распространение программ

Сейчас мы рассмотрим для чего нужны DLL (Dynamic Link Library — динамически компануемая библиотека) и как их создавать. DLL- это участок кода хранимый в файле с расширением .dll. Код может быть использован другими программами, но сама посебе библиотека прораммой не является. Вобщем-то, динамически компонуемые библиотеки представляют собой набао скомпилированныых функций. Но у ютих библиотек есть свой особенности, так например, если каккието две или более программы для Windows одновременно исполняются и используют функции, находящиеся в одной DLL, то в памяти будет постоянно находится только одна библиотека, обеспечивая тем самым экономное расходование памяти. Загрузка библиотеки в память может быть статической и динамической.

При статической загрузке DLL автоматически загружается при запуске исользующего ее приложения. Такая DLL содержит экспортируемые функции, описание которых находится в файле библиотеки импорта(import library file — .lib). Для использования статической загрузки вы должны на этапе компоновки к программе додключить .lib файл вашей DLL. В C++ Builder это сводится к включения в проект .lib файла через менджер проектов.

При диамической загрузке вы можете загружать DLL при необходимости, выгрузить ее когода она ненужна. Однако работать с такими библиотеками сложнее чем со статическими. Рассмотрим созздание и использование DLL статической загрузки.

Статическая загрузка

Создадим сперва проект (File / New / DLL). Будет создан проект, содержащий следующее:

  int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) {      return 1;  }  

и длинный коментарий предупреждающий вас о том, что для работо способности вашей DLL необходимо снеи обеспечить поствку некоторых dll если вы используете экземпляры класса String.

Для экспорта и импорта из DLL необходимо использовать моди фикаторы __export и __import соответсвенно. Но в C++ Builder можно использовать новое ключевое слово __delspec() с параметрами dllexport и dllimport соответсвенно. Сами понимаете, что для того чтобы эспортировать функции из библиотеки еужен один заголовочный файл с описаниями _delspec(dllexport) для экспортируемых функций, для импорта функций в приложение вам необходимо будет поставить анологичный заголовочный файл но с _delspec(dllimport) описаниями, что достаточно неудобно. Эта проблема решается легко: добавте в заголовочный файл библиотеки следующее:

  #if defined(BUILD_DLL)  #  define DLL_EXP __declspec(dllexport)  #else  # if defined(BUILD_APP)  #  define DLL_EXP __declspec(dllimport)  # else  #  define DLL_EXP  # endif  #endif  

в исходном файле DLL напишите #define BUILD_DLL, а вместо __declspec(dllexport) пишите DLL_EXP. При написании программы добавте строчку #define BUILD_APP, и просто подключите заголовочный файл DLL.

Пример DLL: файл P.cpp

  //---------------------------------------------------------------------------  #define BUILD_DLL  #include   #include "p.h"  #pragma hdrstop    //---------------------------------------------------------------------------  // Important note about DLL memory management when your DLL uses the  // static version of the RunTime Library:  //  // If your DLL exports any functions that pass String objects (or structs/  // classes containing nested Strings) as parameter or function results,  // you will need to add the library MEMMGR.LIB to both the DLL project and  // any other projects that use the DLL. You will also need to use MEMMGR.LIB  // if any other projects which use the DLL will be perfomring new or delete  // operations on any non-TObject-derived classes which are exported from the  // DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling  // EXE's to use the BORLNDMM.DLL as their memory manager. In these cases,  // the file BORLNDMM.DLL should be deployed along with your DLL.  // To avoid using BORLNDMM.DLL, pass string information using "char *" or  // ShortString parameters.  //  // If your DLL uses the dynamic version of the RTL, you do not need to  // explicitly add MEMMGR.LIB as this will be done implicitly for you  //-------------------------------------------------------------------------  int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) {  return 1;  }    //-------------------------------------------------------------------------    void Message(char *s) {  i=10;  Application->MessageBox(s,"From DLL",IDOK);  }  

Файл P.h

  #if defined(BUILD_DLL)  # define DLL_EXP __declspec(dllexport)  #else  # if defined(BUILD_APP)  # define DLL_EXP __declspec(dllimport)  # else  # define DLL_EXP  # endif  #endif    DLL_EXP void Message(char *s);  DLL_EXP int i;  

Скомпилируйте проект.

Если вы нажмете Run то после завершенния построения будет выдано сообщение что данная программа не можнт быть исполнена (естественно). Теперь напишем вызывающую программу. Втомже каталоге создайде новый проект (File / New Application) в форму поместите одну кнопку и создай обработчик события OnClick. Ваш исполняемый файл должен представлять собой слдующее:

  //---------------------------------------------------------------------------  #include   #define BUILD_APP  #pragma hdrstop  #include "p.h"  #include "Unit1.h"  #include   //---------------------------------------------------------------------------  #pragma package(smart_init)  #pragma resource "*.dfm"  TForm1 *Form1;  //-------------------------------------------------------------------------  __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {      }    //-------------------------------------------------------------------------  void __fastcall TForm1::Button1Click(TObject *Sender) {      char c[10];      Message("roma");      for( ; i>0;i--) {          sprintf(c,"Example %d",i );          Application->MessageBox("Example of using DLL variable",(char*)c,IDOK);      }  }    //-------------------------------------------------------------------------  

Не забудьте об объявлениях в начале файла. Зайдите в менеджер проектов.Там откройте свой проект и добавте .lib файл из предыдушего проект с DLL( правый клик, пункт ADD). Запустите проект.

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

Динамическая загрузка

Динамическая загрузка горазда сложнее. Однако для динамической загрузки требуется только сама DLL ( не ненужен ни .lib ни заголовочный файл, хотя его можно исполбзовать для описания экспортируемых функций для предполагемого пользователя).

Давайте рассмотрим на примере, как производится динамическая загрузка. Создайте новый прокт DLL и внесите в него следующее:

  extern "C" void __export  Message(char *s)  {  Application->MessageBox(s,"From DLL",IDOK);  }  

Cкомпилируйте проект, в результате чего будет создана DLL.

Теперь создайте проект приложения анологичный проекту для использования статической загрузки (форма с кнопкой и обработчиком события кнопки OnClick) ниже приведен код приложения:(Unit11.cpp)

  //---------------------------------------------------------------------------  #include   #pragma hdrstop  #include "Unit11.h"  #include   //---------------------------------------------------------------------------  #pragma package(smart_init)  #pragma resource "*.dfm"  TForm1 *Form1;  //---------------------------------------------------------------------------  __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {  }  //---------------------------------------------------------------------------    void __fastcall TForm1::Button1Click(TObject *Sender) {  void (__stdcall *Message)(char *s);  HINSTANCE dllp = LoadLibrary("p.dll");  if (dllp) {  Message= (void(__stdcall *) (char*))  GetProcAddress(dllp, "_Message");  if (Message) Message("Hi From Dinamic DLL");  }  FreeLibrary(dllp);  }  //---------------------------------------------------------------------------  

запустите это проект, при нажатии на кнопку должно выдаватся сообшение. Теперь разберемся, как это работает.

  • void (__stdcall *Message)(char *s);-объявление указателя на функцию.
  • HINSTANCE dllp = LoadLibrary(«p.dll»);— загрузка библиотеки в память.
  • Message= (void(__stdcall *) (char*)) GetProcAddress(dllp, «_Message»); присвоение указателю адреса функции DLL.
  • Message(«Hi From Dinamic DLL»); рабочий вызов фунциий (собственно то для чего все это и делается).
  • FreeLibrary(dllp);— выгрузка библиотеки из памяти.

Обратите внимание на то, что призагрузке можно указать точное местоположние библиотеки (необезательно в том же каталоге где и приложение).

  • Чулан*
Задача

Не так давно у нас появилась необходимость создать расширение для одной программы на языке C# с использованием WPF. Расширение представляет собой динамически загружаемую dll библиотеку, о создании которой и пойдет речь в данной статье. Нам не удалось найти стандартное средство в языке C# для создание динамической dll, по этому были рассмотрены обходные методы: Первый способ – это создание динамически загружаемой dll на managed C++, из которой, производилось обращение к сборке на C# Второй способ – создание сборки на языке C# и путем декомпиляции в IL код и обратно, приведение этой библиотеки к нужному виду. О втором способе расскажу подробнее:

Создание динамической dll путем пере компиляции в il

Для реализации нам потребуются две утилиты ilasm и ildasm, эти утилиты должны поставляться вместе со средой разработки, у меня они лежали в этих папках:C:WindowsMicrosoft.NETFrameworkv4.0.30319C:Program Files (x86)Microsoft SDKsWindowsv7.0ABinNETFX 4.0 Tools Для удобства я добавил эти пути в Path, потому что придется часто пользоваться этими утилитами в командной строке. Далее необходимо создать библиотеку классов на языке C#. Функции которые необходимо экспортировать, должны быть публичными и статическими, и находиться в публичном классе. В качестве параметров и возвращаемого значения можно использовать простые типы, такие как string, int. Пример кода:public class TestExportClass { public static void Run() { MessageBox.Show("Привет из C#"); } } Скомпилируем проект, получаем dll библиотеку. (у меня TestDll.dll) Теперь необходимо преобразовать dll библиотеку в il код. Для этого нужно запустить ildasm в командной строке со следующими параметрами ildasm TestDll.dll /OUT:TestDll.il Далее необходимо проделать некоторые манипуляции с получившимся il кодом. Во-первых, меняем флаг.corflags 0×00000001 на.corflags 0×00000002 Во-вторых, над началом описания классов (обычно эта область отделена комментарием CLASS MEMBERS DECLARATION) добавляем код:.vtfixup [1] int32 fromunmanaged at VT_01 .data VT_01 = int32(0) В-третьих, в методе который необходимо сделать экспортируемым добавляем код:.vtentry 1 : 1 .export [1] as Run Здесь Run – это название метода, по которому его будет вызывать сборка, написанная на неуправляемом коде. Теперь необходимо скомпилировать dll библиотку, для этого воспользуемся утилитой ilasm, с параметрами:ilasm TestDll.il /OUT:TestDll.dll /DLL Все после этого dll можно использовать как обычную динамически загружаемую dll библиотеку.

Динамическая dll + WPF

Если вы планируете создавать расширение с использованием технологий, требующих, чтобы приложение работало в STA потоке (Например WPF), то необходимо учесть, что поток в котором будет выполняться расширение наследуется от сборки запустившей библиотеку, а значит может быть не в режиме STA. Для того чтобы обойти эту проблему было решено в расширении создать STA поток и в нем запускать окна на WPF. Вот так можно модернизировать код для запуска WPF окна:public static void Run() { Thread WPFThread = new Thread(OpenWTPWindow); WPFThread.ApartmentState = ApartmentState.STA; WPFThread.Start(); WPFThread.Join(); } public static void OpenWTPWindow() { System.Windows.Window WPFWindow = new System.Windows.Window(); WPFWindow.ShowDialog(); } Обратите внимание что окно запускается не с помощью функции Show() а ShowDialog(), функция Show() работает некорректно, при использовании её не в основном потоке.

Заключение

Такой метод зарекомендовал себя при разработки расширений для различных программ которые требуют, в качестве подключаемого модуля динамически загружаемую DLL библиотеку. Справедливости ради стоит заметить, что после завершения вызванной процедуры, подгруженная dll библиотека продолжает находиться в памяти, что вызывает некоторые проблемы: Например, когда мы создавали расширение для сервиса который постоянно запущен в системе, чтобы установить пользователю обновленное расширение, приходилось временно приостанавливать сервис.

Ссылки по теме

Хорошая статья. Если у вас остались еще какие то вопросы по созданию динамической dll, рекомендую обратиться к ней.Используемые источники:

  • https://vscode.ru/articles/kak-sozdat-dll-v-visual-studio.html
  • http://www.codenet.ru/progr/bcb/dll.php
  • https://habr.com/sandbox/30366/

Рейтинг автора
5
Подборку подготовил
Андрей Ульянов
Наш эксперт
Написано статей
168
Ссылка на основную публикацию
Похожие публикации