Создание dll библиотеки для windows на языке c++

163
03 января 2020, 09:40

Доброго времени суток.

Пытаюсь вынести пару функций в dll библиотеку. Пробовал уже несколько вариантов:

  • пример с http://www.mingw.org/wiki/sampleDLL.
  • пример из Рихтер "Создание эффективных Win-23 приложений"
  • и множество других примеров с просторов сети.

В приложенном коде используется вариант, используемый моим одногруппником, у него все работает корректно. Я использую компилятор 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(&currentPoint);
            ScreenToClient(hWnd, &currentPoint);
            updateWnd(hWnd, hdc, pen, DrawFlag, currentPoint, scene);
            break;
        }
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        }
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
Answer 1

У вас нет главного, точки входа в длл: 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.

READ ALSO
регулярные выражения C++

регулярные выражения C++

Есть строка: AnyNameFileSetupC_FORMAT_x64exe

219
Конструктор принимающий тип LocalDate

Конструктор принимающий тип LocalDate

Глупый вопрос наверное, но что-то не могу найти ответУ меня есть конструктор, в нем я хочу обозначить некоторые данные

231
JPA запрос с Join

JPA запрос с Join

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

211
javafx to exe, вес

javafx to exe, вес

нормально-ли что exe версия приложения с одним окном весит больше 200 метров, а его jar 14? Использовал нативный конвертер intelijIdeaЕсли это не норма,...

317