Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Visual C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 07.02.2009, 04:56   #1
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,619
Сообщение Работа с DLL в C++ (Microsoft Visual C++)

Тема не очень сложная, но при этом очень важная. В этой теме я расскажу как создавать DLL и загружать ее в проект двумя способами - статически и динамически.

Итак, начнем с создания. Используется Microsoft Visual Studio 2005
File->New->Project->Win32 Project, затем вписываем имя, я назову свою DLL просто - MyDLL. Затем нажимаем ОК. В появившемся окне щелкаем на кнопку Next. Ставим галочку напротив DLL. Поля ATL и MFC оставляем пустыми. Empty project тоже нужно поставить галочку. Будем писать все с нуля. Нажимаем Finish.
Теперь нам нужно добавить в проект *.cpp файл с исходным кодом.
Правой кнопкой по папке Source Files в Solution Explorer ->New Item...
Выбираем cpp-файл, я дал ему имя Source.cpp.
Теперь допишем сюда нашу функцию. Моя функция будет считать количество вхождений символа в строку, и возвращать значение типа int.
Но сначала добавим в этот проект еще один файл - MyDll.h, и скопируем туда вот этот код
Код:
//MyDll.h
#ifndef MYDLLH
#define MYDLLH

extern "C" _declspec(dllexport) int SymbolCounter( const char *, char, int );

#endif
Как вы поняли int SymbolNumber(const char *, char, int) - это моя функция. Здесь я написал прототип.
extern "C" _declspec(dllexport) - это означает, что моя функция будет экспортироваться в ехе файл программы.
Дальше пишем реализацию в файл Source.cpp
Итак, готовый код
Код:
//Source.cpp
#include <windows.h>

extern "C" _declspec(dllexport) int SymbolCounter(const char *lpSource, char lpMask, int size)
{
	int number = 0;
	if( lpSource == NULL )
		return 0;
	else
	{
		for(int i = 0; i < size; i++ )
		{
			if(lpSource[i] == lpMask)
				number++;
		}
	}
	return number;
}
Теперь нажимаем F7 и линкуем нашу DLL.
Наша DLL готова.
Следующий пост я напишу как загрузить мою функцию из DLL в проект.
MaTBeu вне форума Ответить с цитированием
Старый 07.02.2009, 05:23   #2
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,619
По умолчанию

Итак, как загрузить DLL в программу. Существует два способа - статически и динамически. Первый загружает DLL в память на этапе компиляции, а второй - на этапе выполнения программы.
1)Статическое подключение
Когда мы слинковали нашу DLL, мы получили 3 файла.
1. MyDll.dll
2. MyDll.lib
3. MyDll.exp
Первый файл - собственно DLL. Второй - таблица импорта. Третий - таблица экспорта.
Чтобы подключить статически DLL к программе, нам нужно:
1. Подключить h-файл этой DLL к нашей программе (если такого файла нет, то подключать ничего не нужно )
2. Подключить lib-файл к нашей программе.
В нашем случае нужно написать две строки в самом начале программы (после инклудов)
Код:
#include "MyDll.h"                              //подключаем наш h-файл
#pragma comment(lib, "MyDll.lib")         //подключаем lib-файл
Перед этим неплохо бы скинуть эти два файла в папку с программой. Или прописать полный путь к этим файлам.
Все, дальше можно вызывать эту функцию как обычную функцию.
Для примера привожу готовый код программы, которая импортирует функцию из DLL. На WinApi
File->New->Project->Win32 Project->Windows Application->Empty Project->Finish
Source Files->New Item->Main.cpp
Код:
//----------------------Main.cpp-----------------------------------
#include <Windows.h>
#include "MyDll.h"
#pragma comment(lib, "MyDll.lib")

/*Сегмент данных*/
CHAR				wName[]		="App";
CHAR				cName[]	            ="Windowsclass";	
CHAR				lpszAppName[]	="Application";								
static HWND			hListBox		= NULL;				
static HWND			hExecute		= NULL;								

HINSTANCE			hInstance;			
HINSTANCE			hDllInstance;
HWND hwnd;															
LPSTR				message		= new char[128];
char				mask			='e';
CHAR				*lpEditInfo1		= new char[256];

/*Конец сегмента данных*/
/*Сегмент кода*/

LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_PAINT:											
		break;
	case WM_CREATE:											
		{
			hListBox = CreateWindowEx(						
				WS_EX_CLIENTEDGE,							
				"LISTBOX", "",								
				LBS_STANDARD  |								
				LBS_DISABLENOSCROLL |						
				WS_CHILD |									
				WS_VISIBLE |								
				WS_HSCROLL,									
				5, 25, 350, 400,							
				hwnd,										
				NULL,										
				hInstance,									
				NULL										
				);
			hExecute = CreateWindowEx( 
				WS_EX_WINDOWEDGE, 
				"BUTTON", 
				"Выполнить",
				BS_PUSHBUTTON | 
				BS_TEXT | 
				WS_CHILD | 
				WS_VISIBLE,
				375, 25, 150, 30,
				hwnd, 
				NULL,
				hInstance, 
				NULL );
			SendMessage( hListBox, LB_RESETCONTENT, 0, 0 );
		}
		break;								
	case WM_COMMAND :										
		{
			switch( LOWORD( wParam ) )						
			{
			case BN_CLICKED:								
				{	
					if( lParam == ( LPARAM )hExecute )
					{
						lstrcpy( message, "Recieved message" );
						int n = SymbolCounter( message, mask, lstrlen( message ) );                  //вот здесь мы вызываем функцию из нашей DLL

						wsprintf( lpEditInfo1, "A number of entries of symbol '%c' in string = %d", mask, n );
						SendMessage( hListBox, LB_INSERTSTRING, ( WPARAM )-1, ( LPARAM )lpEditInfo1 );
					}
				}
				break;
			}
		}
		break;
	case WM_DESTROY:				
		{
			FreeLibrary( hDllInstance );
			PostQuitMessage(0);			
			return 0;
		}
		break;
	}
	return (DefWindowProc(hwnd, msg, wParam, lParam));
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	WNDCLASSEX	windowsclass;														
	HWND		hwnd;
	MSG			msg;																
	windowsclass.cbSize		= sizeof(windowsclass);								
	windowsclass.style		= CS_VREDRAW | CS_HREDRAW | CS_OWNDC;	
	windowsclass.lpfnWndProc	= MainWinProc;										
	windowsclass.cbClsExtra	= 0;												
	windowsclass.cbWndExtra	= 0;
	windowsclass.hInstance	= hInstance;										
	windowsclass.hIcon		= LoadIcon(NULL, IDI_APPLICATION);	
	windowsclass.hCursor		= LoadCursor(NULL, IDC_ARROW);						
	windowsclass.hbrBackground	= (HBRUSH)GetStockObject(LTGRAY_BRUSH);	
	windowsclass.lpszMenuName	= NULL;												
	windowsclass.lpszClassName	= cName;												
	windowsclass.hIconSm	= LoadIcon(NULL, IDI_APPLICATION);	
				
	RegisterClassEx(&windowsclass);

	hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, 
		cName, 
		wName, 
		WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
		180, 180, 550, 500, 
		NULL, 
		NULL, 
		hInstance, 
		NULL);
	ShowWindow(hwnd, nShowCmd);
	UpdateWindow(hwnd);
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return(msg.wParam);
}
/*Конец сегмента кода*/
Апишный код объяснять не буду, так как тут это не главное.
MaTBeu вне форума Ответить с цитированием
Старый 07.02.2009, 05:53   #3
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,619
Сообщение Динамическое подключение DLL

2)Динамическое подключение
Чтобы загрузить DLL в программу на этапе выполнения, нужно пройти несколько шагов.
Шаг 1:
Использовать функцию LoadLibrary или LoadLibraryEx, чтобы загрузить DLL.
Шаг 2:
Использовать функцию GetProcAddress, чтобы получить указатель на интересующую нас функцию.
Шаг 3:
По окончании работы не забыть выгрузить DLL с помощью функции FreeLibrary.

В своей программе я пользовался функцией LoadLibraryEx, потому что у нее есть параметр, который определяет, каким образом загружать DLL. Эта функция возвращает дескриптор DLL.
Код:
HINSTANCE hDllInstance = LoadLibraryEx( "MyDll.dll", 0, DONT_RESOLVE_DLL_REFERENCES );
Первый параметр - путь к DLL. Второй параметр зарезервирован и всегда равен 0. Третий параметр - флаг подключения DLL. В данном случае - этот флаг указывает компилятору, что мы загружаем DLL просто как набор функций. Не используя функцию инициализации DLL - DllMain.

Дальше - нам нужно получить указатель на функцию. Но функция у нас не простая, а с кучей параметров. Тоесть нужно определить тип указателя. Это делается строкой (в нашем случае)
Код:
typedef int(*functionDll)( const char *, char, int );
Тоесть мы определили functionDll как тип, который указывает на функцию, которая принимает три параметра и возвращает значение int.
Далее - дело за малым.
Код:
functionDll fpFunction = (functionDll)GetProcAddress(hDllInstance,"SymbolCounter");
Мы объявили указатель, и с помощью функции GetProcAddress получаем адрес интересующей нас функции.
Первый параметр - дескриптор DLL. Второй - имя функции.
Теперь можно вызывать эту функцию с параметрами
Код:
int n = fpFunction( message, mask, lstrlen( message ) );
Привожу код программы, которая использует динамическую загрузку DLL. Создаем проект и называем его DynamicLink
File->New->Project->Win32 Project->Windows Application->Empty project.
Source files->New Item->Main.cpp

Код:
//-----------------------------Main.cpp-----------------------------
#include <Windows.h>

typedef int(*functionDll)( const char *, char, int );

/*Сегмент данных*/
CHAR				wName[]				="Dynamic link DLL";
CHAR				cName[]				="Windowsclass";	
CHAR				lpszAppName[]		="Application";								
static HWND			hListBox			= NULL;				
static HWND			hExecute		= NULL;								

HINSTANCE			hInstance;			
HINSTANCE			hDllInstance;
HWND				hwnd;															
LPSTR				message		= new char[128];
char				mask			='a';
CHAR				*lpEditInfo1		= new char[256];

/*Конец сегмента данных*/
/*Сегмент кода*/

LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_PAINT:											
		break;
	case WM_CREATE:											
		{
			hListBox = CreateWindowEx(						
				WS_EX_CLIENTEDGE,							
				"LISTBOX", "",								
				LBS_STANDARD|								
				LBS_DISABLENOSCROLL|						
				WS_CHILD|									
				WS_VISIBLE|								
				WS_HSCROLL,									
				5, 25, 350, 400,							
				hwnd,										
				NULL,										
				hInstance,									
				NULL										
				);
			hExecute = CreateWindowEx( 
				WS_EX_WINDOWEDGE, 
				"BUTTON", 
				"Выполнить",
				BS_PUSHBUTTON | 
				BS_TEXT | 
				WS_CHILD | 
				WS_VISIBLE,
				375, 25, 150, 30,
				hwnd, 
				NULL,
				hInstance, 
				NULL );
			SendMessage( hListBox, LB_RESETCONTENT, 0, 0 );
		}
		break;								
	case WM_COMMAND :										
		{
			switch( LOWORD( wParam ) )						
			{
			case BN_CLICKED:								
				{	
					if(lParam == (LPARAM)hExecute)
					{
						hDllInstance = LoadLibraryEx(
							"MyDll.dll", 
							0, 
							DONT_RESOLVE_DLL_REFERENCES
							);			//загружаем DLL
						if(hDllInstance)
						{
							functionDll fpFunction = (functionDll)GetProcAddress(hDllInstance,"SymbolCounter");//получаем указатель
							if(fpFunction)
							{
								lstrcpy(message, "Recieved message");
								int n = fpFunction(message, mask, lstrlen(message));	//вот тут мы вызываем нашу функцию
								wsprintf(lpEditInfo1, "A number of entries of symbol '%c' in string = %d", mask, n);
								SendMessage(hListBox, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)lpEditInfo1);
							}
							else
							{
								wsprintf(lpEditInfo1, "Error number %d", GetLastError());
								SendMessage(hListBox, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)lpEditInfo1);
							}
						}
						else
						{
							wsprintf(lpEditInfo1, "Error number %d", GetLastError());
							SendMessage(hListBox, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)lpEditInfo1);
						}
					}
				}
				break;
			}
		}
		break;
	case WM_DESTROY:				
		{
			FreeLibrary(hDllInstance);
			PostQuitMessage(0);			
			return 0;
		}
		break;
	}
	return (DefWindowProc(hwnd, msg, wParam, lParam));
}
//тут еще функция WinMain - ее возьмите из предыдущего поста
/*Конец сегмента кода*/
Весь апишный код - чисто окно, кнопка и листбокс.

Последний раз редактировалось MaTBeu; 11.01.2010 в 15:24.
MaTBeu вне форума Ответить с цитированием
Старый 06.08.2009, 14:27   #4
ds.Dante
Старожил
 
Аватар для ds.Dante
 
Регистрация: 06.08.2009
Сообщений: 2,992
По умолчанию

Когда я писал свой DLL и подключал его через LoadLibrary(), то я не мог обратиться к функции (через GetProcAddress) по имени - можно было только по номеру (а тот вообще был случайный). Проблема решилась добавлением в проект DLL файла .def примерно следущего содержания:
Код:
LIBRARY	"DLL"
EXPORTS
	Start		@1
	Stop		@2
	SetValue	@3
	GetValue	@4
Тут я задаю имена функций, и, заодно их номера. При этом использование _declspec(dllexport) не обязательно, функции по виду не отличаются от неэкспортируемых.

В коде программы, использующей DLL, объявление функции можно записать проще, типа:
void (*SetValue)(double) = (void(*)(double)) GetProcAddress (LibInst, "SetValue");
А потом вызывать, например, SetValue (1.25);
А еще можно все это занести в какую-нибудь функцию InitDLL() в *.h файл, который будет распространяться с вашим DLL. Тогда человеку, который будет ее использовать будет нужно добавить этот заголовочный файл и вызвать InitDLL(), и ему будут доступны все эти функции.
ds.Dante вне форума Ответить с цитированием
Старый 06.08.2009, 14:53   #5
ds.Dante
Старожил
 
Аватар для ds.Dante
 
Регистрация: 06.08.2009
Сообщений: 2,992
По умолчанию

Вот мой Starter Kit, демонстрирующий DLL.


Project DLL:

DLL.cpp (исходный код библиотеки):
Код:
#include <windows.h>

void SetValue (double x);
double GetValue();

double	*Value;

BOOL APIENTRY DllMain (HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		Value = new double; // Я использую указатель чтобы продемонстрировать работу стандартной функции DLLMain()
		*Value = 0;
		break;

	case DLL_PROCESS_DETACH:
		delete Value;
		break;
	}
	return TRUE;
}

void SetValue (double x)
{
	*Value = x;
}

double GetValue()
{
	return *Value;
}
DLL.def (задаем функции на экспорт):
Код:
LIBRARY	"DLL"
EXPORTS
	SetValue	@1    ; Название функции и ее номер
	GetValue	@2

Project DLLUse:

DLLUse.h (поставляется автором вместе с его *.dll
Код:
#define	DLL_LOAD_ERR	-1
#define DLL_FUNC_ERR	-2

void	(*SetValue)	(double);
double	(*GetValue)	();
HMODULE	LibInst;

int OpenDLL()
{
	LibInst		= LoadLibraryA ("DLL.dll");
	if (!LibInst)
		return DLL_LOAD_ERR;
	SetValue	= (void(*)(double))	GetProcAddress (LibInst, "SetValue");
	GetValue	= (double(*)())		GetProcAddress (LibInst, "GetValue");
	if (!SetValue || !GetValue)
		return DLL_FUNC_ERR;
	return 0;
}

void CloseDLL()
{
	FreeLibrary (LibInst);
}
DLLUse.cpp (пример использования):
Код:
#include <windows.h>
#include <iostream>
#include <conio.h>
#include "DLLUse.h"

using namespace std;

int main()
{
	double Value;

	if (OpenDLL())
	{
		cout << "Error: cannot open DLL.dll"; // А можно еще проверять возвращенное значение DLL_LOAD_ERR или
		_getch();                                      // DLL_FUNC_ERR (см. DLLUse.h)
		return -1;
	}

	cout << "Initial value from DLL: " << GetValue() << endl;  // DLL при инициализации задает ноль
	cout << "Enter new value: ";
	cin >> Value;
	SetValue (Value);
	cout << "Value form DLL: " << GetValue() << endl;
	_getch();
	CloseDLL();
	return 0;
}
Одно замечание: если вы случайно вызовете функцию GetValue без скобок, то программа запуститься, но выдаст адрес функции вместо результата ее работы.

Кстати, кто-нибудь знает, есть ли в Standart C++ (где-нибудь в <iostream>) альтернатива функции getch()?

P. S. Извините, но у меня размер таба - 4 символа.

Модератор: за информацию спасибо. Альтернатива getch() есть, cin.get()

Последний раз редактировалось MaTBeu; 10.08.2009 в 15:13.
ds.Dante вне форума Ответить с цитированием
Старый 05.01.2010, 03:07   #6
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

у меня вопрос по статическому подключению.
а как мне статически подключить чужую ДЛЛ?
(к которой у меня нет lib файла)
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 07.01.2010, 02:21   #7
counter
Участник клуба
 
Регистрация: 18.10.2008
Сообщений: 1,409
По умолчанию

Пепел Феникса, читни

http://forum.shelek.ru/index.php/topic,3895.0.html
http://forum.shelek.ru/index.php/topic,3621.0.html
http://www.wasm.ru/forum/viewtopic.php?id=33845
http://www.sql.ru/forum/actualthread.aspx?tid=30768

авось поможет.
counter вне форума Ответить с цитированием
Старый 03.02.2010, 19:21   #8
Assemblerru
Форумчанин
 
Регистрация: 28.01.2010
Сообщений: 224
По умолчанию

я по вопросу динамического присоединения а возможно попроше например (но я не уверен, если неверно то поясни!!!)

Загрузка библиотеки с помощью функции LoadLibrary:
HIHSTANCE diiInstance = LoadLibrary (“название необходимой dll");

Получим указатель на интересующую функцию.

typedef char (_import *FType (char*));//вводим тип FType (произвольное имя). При //объявлении указывается тип возвращаемого значения.

FType *MyFunc; // задает тип указателя на функцию

MyFunc = (FType*) GetProcAddres (diiInstance, “_MyFunction “);// для получения //значения этого указателя.
// тогда вызов функции из библиотеки:
char* S = MyFunc (“Привет вам”);
// выгрузить библиотеку из памяти необходимо по окончании работы с ней.
FreeLibrary (diiInstance);

В данном примере вызываемая функция char* MyFunction (char*)
всему свое время как зиме и весне
и каждому солнцу свой неба кусок

Последний раз редактировалось Assemblerru; 03.02.2010 в 19:26.
Assemblerru вне форума Ответить с цитированием
Старый 03.02.2010, 21:41   #9
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,619
По умолчанию

Я уже отвечал - она так и подключается - остальной код - это графический интерфейс.
MaTBeu вне форума Ответить с цитированием
Старый 04.02.2010, 20:25   #10
Assemblerru
Форумчанин
 
Регистрация: 28.01.2010
Сообщений: 224
По умолчанию

прости сразу не разобрался я на работе почитал и все в принцепе понятно. А ты не рботал с inpout32.dll. Где можно описания найти для динамического подключения ее к проекту (покрайней мере какие функции и с какими параметрами). Покрайней мере я ее хочу использовать для работы с портами ввода вывода. На форумах пообщался нащел только такое:
http://azbukavb.narod.ru/teorie/LPT.html
всему свое время как зиме и весне
и каждому солнцу свой неба кусок
Assemblerru вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Вопрос по Microsoft Visual C++ DiZZZ Visual C++ 3 06.04.2009 18:45
Microsoft Visual C++ 6.0 saleens7 Помощь студентам 6 26.12.2008 16:16
Работа с окнами в Microsoft Visual Studio 2005 M@STeR Visual C++ 1 24.09.2008 14:44
Delphi и Microsoft Visual Basic Dimon88 Общие вопросы Delphi 10 23.11.2007 14:46
Visual J# from Microsoft Visual Studio .NET Flash_ Общие вопросы по Java, Java SE, Kotlin 2 28.12.2006 10:16