Форум: Форум C++Разное
Новые темы: 00
C++. Мастер-класс в задачах и примерах. Авторы: Кузнецов М.В., Симдянов И.В. Самоучитель PHP 5 / 6 (3 издание). Авторы: Кузнецов М.В., Симдянов И.В. PHP. Практика создания Web-сайтов (второе издание). Авторы: Кузнецов М.В., Симдянов И.В. PHP Puzzles. Авторы: Кузнецов М.В., Симдянов И.В. MySQL на примерах. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум C++

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Как подключить к своей программе DLL-библиотеку?
 
 автор: asker++   (28.01.2012 в 09:36)   письмо автору
 
 

Давно мечтаю засовать свои наработоки в динамические библиотеки, но всегда как-то страшно с ними было работать, да и как-то не удобно их соединять с программой через проект... а тут копаясь в MSDN обнаружил функцию LoadLibrary() - вроде выглядит не страшно. Правильно ли я понимаю, что можно просто в любой момент подключиться к DLL, а потом отсоединиться и не надо библиотеку прописывать в связях проекта? cheops, если не сложно покажите как с ней работать? Ну если конечно "hellow world!" для этого примера не нужно дней пять писать :-) Работаем понятно в Visual Studio.

  Ответить  
 
 автор: cheops   (28.01.2012 в 10:09)   письмо автору
 
   для: asker++   (28.01.2012 в 09:36)
 

В общем да, в имеется две стратегии: связывание на этапе построения, когда вы добавляете DLL-библиотеку в проект или динамическое связывание во время работы программы. Именно для последнего случая предназначена LoadLibrary(), да собственно и сами DLL-библиотеки. Там не то чтобы сложно - "этикету" больше, но если один раз разобраться - потом не "страшно" :)))
Я так понимаю, с созданием DLL-библиотеки проблем не возникает? На всякий случай я приведу код простейшей библиотеки с одной функцией, HelloWorld(), которая этот самый "Hellow world" и выводит.
#include <Windows.h>
#include <iostream>
using namespace std;

// Главная функция
BOOL WINAPI DllMain(HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
{
  switch(dwReason)
  {
    case DLL_PROCESS_ATTACH:
      break;
    case DLL_THREAD_ATTACH:
      break;
    case DLL_THREAD_DETACH:
      break;
    case DLL_PROCESS_DETACH:
      break;
  }
  return TRUE;
}

extern "C" __declspec(dllexport) void HelloWorld(void)
{
  cout << "Hello world" << endl;
}
Главная функция DllMain() практически пустая, события не обрабатываются, нам не надо, так как у нас нет никакой динамической памяти, параллельных потоков и прочего. Теоретически можно вообще DllMain() не создавать - она должна добавляться автоматически компилятором. Функция HelloWorld() объявлена без параметров и возвращаемых результатов, но можно и с тем и с другим - никаких сложностей не возникает. Чтобы функцию было "видно" из вне, она объявляется с атрибутами extern и __declspec(dllexport), "C" - это порядок чтения аргументов, у нас тут их вообще нет, но приводим для единообразия.

Вообще если вы находитесь в VisualStudio, лучше в рамках одного решения создать два проекта - один с DLL-библиотекой, другой - с кодом, тестирующим эту библиотеку. Первый проект понятно DLL, второй - можно консольный. Исполняемым (выделенным жирным) у вас должен быть консольный проект, ему же кстати можно добавить зависимость от DLL-проекта, тогда никаких проблем вообще быть не должно - сначала будет компилироваться DLL, а потом консольный проект.

При условии, что DLL-проект называется dll, а библиотека на выходе соответственно dll.dll, консольный проверочный проект может выглядеть следующим образом
#include <Windows.h>
#include <iostream>
#include <tchar.h>
using namespace std;

int main()
{
  // Дескриптор DLL-библиотеки
  HMODULE hDll;
  // Указатель на функцию
  void (*dllHelloWorld) (void);

  // Загружаем динамически подключаемую библиотеку
  hDll = LoadLibrary(_T("dll.dll"));
  if(!hDll)
  {
    cout << _T("Динамическая библиотека не загружена") << endl;
    return GetLastError();
  }
  // Настраиваем адрес функции
  dllHelloWorld = (void (*)(void))GetProcAddress(hDll, "HelloWorld");
  if(!dllHelloWorld)
  {
    cout << _T("Ошибка получения адреса функции") << endl;
    return GetLastError();
  }
  // Вызываем функцию из библиотеки
  dllHelloWorld();

  // Отключаем библиотеку
  if(!FreeLibrary(hDll))
  {
    cout << _T("Ошибка выгрузки библиотеки из памяти") << endl;
    return GetLastError();
  }
  
  // Устанавливаем паузу перед завершением программы
  system("pause");
  return 0;
}
PS Все проекты в UNICODE, т.е. строки обрамляем макросом _T, если программе нужны параметры, вместо main() используем _tmain(). Кстати, обратите внимание, что GetProcAddress() принимает обычные 8-битные строки, поэтому если у вас названия функций в LPTSTR/LPCTSTR-строках, придется переводить.

  Ответить  
 
 автор: asker++   (28.01.2012 в 10:38)   письмо автору
 
   для: cheops   (28.01.2012 в 10:09)
 

Офигеть, работает!

А можно что-нибудь более сложно-полезное вызывать, например, вот такую функцию? Не очень ясно, как тут с параметрами?
void ShowMessage(LPCTSTR title, LPCTSTR text)
{
  MessageBox(
          NULL,
          text,
          title,
          MB_OK | MB_ICONEXCLAMATION);
}

  Ответить  
 
 автор: cheops   (28.01.2012 в 10:51)   письмо автору
 
   для: asker++   (28.01.2012 в 10:38)
 

В этом случае ваша DLL-библиотека должна выглядеть следующим образом
#include <Windows.h>

extern "C" __declspec(dllexport) void ShowMessage(LPCTSTR title, LPCTSTR text)
{
  MessageBox(
          NULL,
          text,
          title,
          MB_OK | MB_ICONEXCLAMATION);
}
Тогда программа, загружающая библиотеку dll.dll и вызывающая функцию ShowMessage() может выглядеть следующим образом
// Главный заголовочный файл
#include <Windows.h>
#include <tchar.h>

// Главная входная точка Windows-программ
int WINAPI WinMain(HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  LPSTR lpcmdline,
  int ncmdshow)
{
  // Дескриптор DLL-библиотеки
  HMODULE hDll;
  // Указатель на функцию
  void (*dllShowMessage) (LPCTSTR, LPCTSTR);
  // Загружаем динамически подключаемую библиотеку
  hDll = LoadLibrary(_T("dll.dll"));
  if(!hDll)
  {
    MessageBox(
          NULL,
          _T("Динамическая библиотека не загружена"),
          _T("Ошибка"),
          MB_OK | MB_ICONEXCLAMATION);
    return GetLastError();
  }
  // Настраиваем адрес функции
  dllShowMessage = (void (*)(LPCTSTR, LPCTSTR))GetProcAddress(hDll, "ShowMessage");
  if(!dllShowMessage)
  {
    MessageBox(
          NULL,
          _T("Невозможно получить адрес функции ShowMessage()"),
          _T("Ошибка"),
          MB_OK | MB_ICONEXCLAMATION);
    return GetLastError();
  }
  // Вызываем функцию из библиотеки
  dllShowMessage(_T("Hello world!"), _T("Hello world!"));

  // Отключаем библиотеку
  if(!FreeLibrary(hDll))
  {
    MessageBox(
          NULL,
          _T("Ошибка выгрузки библиотеки из памяти"),
          _T("Ошибка"),
          MB_OK | MB_ICONEXCLAMATION);
    return GetLastError();
  }

  // Выход из программы
  return 0;
}

PS Проверки, конечно, по уму надо бы через исключения делать, а то полезность dllShowMessage() как-то теряется, а сложность возрастает :)))

  Ответить  
 
 автор: asker++   (28.01.2012 в 11:37)   письмо автору
 
   для: cheops   (28.01.2012 в 10:51)
 

Огромное спасибо!

  Ответить  
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования