Доброго времени суток.
Пытаюсь вынести пару функций в dll библиотеку. Пробовал уже несколько вариантов:
В приложенном коде используется вариант, используемый моим одногруппником, у него все работает корректно. Я использую компилятор MinGW.org GCC-6.3.0-1.
Мог бы кто-то объяснить на пальцах или дать ссылку на реальный пример создания и явного связывания dll, как экспортировать функции и структуры. Спасибо
// testdll.h
#include <windows.h>
#include <tchar.h>
#include <string>
#include <stdio.h>
#include <vector>
using std::vector;
extern "C++"{
struct line{
POINT start;
POINT end;
};
__declspec(dllexport) void reDraw(HWND hWnd, HDC hdc, HPEN pen, bool df, POINT cp, vector< struct line > &lines);
__declspec(dllexport) WNDCLASS InitializeWndClass(HINSTANCE hInstance, WNDPROC WProc, LPCTSTR ClassName,
LPCTSTR MenuName, UINT style, HICON Icon, HCURSOR cursor,
HBRUSH Bground, int ClsExtra, int WndExtra);
}
// testdll.cpp
#include "testdll.h"
extern "C++"
{
extern "C++" __declspec(dllexport) void reDraw(HWND hWnd, HDC hdc, HPEN pen, bool df, POINT cp, vector< struct line > &lines)
{
hdc = GetDC(hWnd);
SelectObject(hdc, pen);
if (df){
InvalidateRect(hWnd, NULL, true);
UpdateWindow(hWnd);
lines.back().end = cp;
for (const auto curLine : lines){
POINT tmp[2] = {curLine.start, curLine.end};
Polyline(hdc, tmp, 2);
}
}
}
extern "C++" __declspec(dllexport) WNDCLASS InitializeWndClass(HINSTANCE hInstance, WNDPROC WProc,
LPCTSTR ClassName, LPCTSTR MenuName,
UINT style, HICON Icon, HCURSOR cursor,
HBRUSH Bground, int ClsExtra, int WndExtra)
{
WNDCLASS *wc = new WNDCLASS;
wc->hInstance = hInstance;
wc->lpszClassName = ClassName;
wc->lpfnWndProc = WProc;
wc->style = style;
wc->hIcon = Icon;
wc->hCursor = cursor;
wc->lpszMenuName = MenuName;
wc->cbClsExtra = ClsExtra;
wc->cbWndExtra = WndExtra;
wc->hbrBackground = Bground;
return *wc;
}
}
// test.cpp
#include <windows.h>
#include <tchar.h>
#include <string>
#include <math.h>
#include <stdio.h>
#include <vector>
using std::vector;
HINSTANCE hDLL;
typedef void(*reDraw_func_type)(HWND hWnd, HDC hdc, HPEN pen, bool df, POINT cp, vector< struct line > &lines);
typedef WNDCLASS(*classInit_func_type)(HINSTANCE hInstance, WNDPROC WProc, LPCTSTR ClassName, LPCTSTR MenuName,
UINT style, HICON Icon, HCURSOR cursor, HBRUSH Bground, int ClsExtra, int WndExtra);
reDraw_func_type updateWnd;
classInit_func_type clsInit;
HDC hdc;
HPEN pen = CreatePen(PS_SOLID, 4, RGB(255, 0, 0));
POINT currentPoint;
vector< struct line > scene;
bool DrawFlag = false;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE This, HINSTANCE Prev, LPSTR cmd, int mode)
{
hDLL = LoadLibrary("testdll.dll");
if (!hDLL){
printf("fail to load dll");
return 0;
}
else{
printf("Dll loaded\n");
updateWnd = (reDraw_func_type)GetProcAddress(hDLL, "reDraw");
if (!updateWnd){
printf("Error to load reDraw func\n");
}
else{
printf("reDraw func had been loaded");
}
clsInit = (classInit_func_type)GetProcAddress(hDLL, "InitializeWndClass");
if (!clsInit){
printf("Error to load clsInit func\n");
}
else{
printf("clsInit func had been loaded");
}
if (!updateWnd || !clsInit){
FreeLibrary(hDLL);
return 0;
}
}
WNDCLASS wc = clsInit(This, WndProc, "MainFrame", NULL, (CS_HREDRAW | CS_VREDRAW), LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), 0, 0);
if (!RegisterClass(&wc)) {
MessageBox(NULL, "register class error", "Error", MB_OK);
return 0;
}
HWND hWnd = CreateWindow("MainFrame", "template", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, HWND_DESKTOP, NULL, This, NULL);
ShowWindow(hWnd, mode);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
FreeLibrary(hDLL);
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:{
struct line newLine = {
currentPoint, // start point
currentPoint // end point
};
if (!DrawFlag) DrawFlag = true;
scene.push_back(newLine);
break;
}
case WM_RBUTTONDOWN: {
DrawFlag = false;
break;
}
case WM_MOUSEMOVE: {
GetCursorPos(¤tPoint);
ScreenToClient(hWnd, ¤tPoint);
updateWnd(hWnd, hdc, pen, DrawFlag, currentPoint, scene);
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
У вас нет главного, точки входа в длл: https://docs.microsoft.com/en-us/windows/desktop/dlls/dllmain
BOOL APIENTRY DllMain(HINSTANCE hinstDLL,
DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH: // Подключение DLL
// Выполняем все необходимые
// действия по инициализации
// если инициализация прошла
// успешно возвращаем TRUE
// в противном случае – FALSE
return 1;
case: DLL_PROCESS_DETACH: // Отключение DLL
// Выполняем все действия
// по деинициализации
break;
case DLL_THREAD_ATTACH: // Создание нового потока
// Переходим на многопоточную версию,
// если необходимо
break;
case DLL_THREAD_DETACH: // Завершение потока
// Освобождаем переменные, связанные с потоком
break;
}
return TRUE; // Возвращаем что-нибудь (все равно
// код возврата игнорируется)
}
и например динамическая загрузка вашей длл в приложении:
main()
{
// Загружаем библиотеку
HINSTANCE h = LoadLibrary("Test.dll");
if (!h){ printf("Ошибка загрузки DLL"); return;}
FreeLibrary(h); // Выгружаем библиотеку
}
или статистическая линковка путем указания флага в командной строке gcc -lTest
Правильный подход - определить и использовать собственные флаги для сборки библиотеки и приложения.
Когда собираете приложение, с помощью определенных ранее флагов, функции должны определяться как dllimport, когда библиотеку dllexport, тогда заголовок можно использовать в обеих случаях, для сборки библиотеки и приложения,например:
#if defined(BUILD_DLL)
#define DLLOPT extern "C" __declspec(dllexport)
#else
#define DLLOPT extern "C" __declspec(dllimport)
#endif
и соответственно в файле заголовков определять функции и классы через этот макрос:
void DLLOPT myfunction(...);
Командная строка для сборки библиотеки для gcc соответственно будет:
gcc ... -DBUILD_DLL=1 ...
Для сборки приложения этот флаг не указывается.
Хеадер вашей библиотеки должен выглядеть следующим образом:
#if defined(BUILD_DLL)
# define DLLOPT __declspec(dllexport)
#else
# define DLLOPT __declspec(dllimport)
#endif
struct line
{
POINT start;
POINT end;
};
#ifdef __cplusplus
extern "C" {
#endif
void DLLOPT reDraw(
HWND hWnd, HDC hdc, HPEN pen, bool df,
POINT cp, vector<struct line> &lines);
WNDCLASS DLLOPT InitializeWndClass(
HINSTANCE hInstance, WNDPROC WProc, LPCTSTR ClassName,
LPCTSTR MenuName, UINT style, HICON Icon, HCURSOR cursor,
HBRUSH Bground, int ClsExtra, int WndExtra);
#ifdef __cplusplus
}
#endif
И удалить из вашего .cpp файла библиотеки все упоминания extern "C++".
Пример который вы привели неполный, и мягко говоря странный для структуры .dll http://www.mingw.org/wiki/sampleDLL
Это больше напоминает заготовку для сборки в формате .a/.so и нацелен на операционные системы *nix.
Сборка персонального компьютера от Artline: умный выбор для современных пользователей