Skocz do zawartości
  • 👋 Witaj na MPCForum!

    Przeglądasz forum jako gość, co oznacza, że wiele świetnych funkcji jest jeszcze przed Tobą! 😎

    • Pełny dostęp do działów i ukrytych treści
    • Możliwość pisania i odpowiadania w tematach
    • System prywatnych wiadomości
    • Zbieranie reputacji i rozwijanie swojego profilu
    • Członkostwo w jednej z największych społeczności graczy

    👉 Dołączenie zajmie Ci mniej niż minutę – a zyskasz znacznie więcej!

    Zarejestruj się teraz

[MegaTuT][C/C++]Prosty system wtyczek DLL.


Rekomendowane odpowiedzi

Opublikowano

Witam. W tym poradniku chcę wam przedstawić mój system wtyczek do aplikacji w C i C++.

Ma on wiele zalet, takich jak: możliwość modyfikowania czynności aplikacji, dodawanie nowych funkcji aplikacji, itp.

Jedną z wad jest to, że w C++ deklaracje funkcji muszą być poprzedzane extern "C" aby zostały poprawnie wyeksportowane.

 

1. Piszemy prostą funkcję zwracającą nam uchwyty do naszych wtyczek:

 

W tej części stworzymy obiektowy system wczytywania wtyczek. Na początku musimy stworzyć klasę i jej funkcję zwracającą uchwyty do naszych wtyczek.

Plik PluginLoader.h:

#ifndef PLUGINLOADER_H
#define PLUGINLOADER_H

# if defined(_WIN32) // (1)
    #include <windows.h> // (2)
# elif defined(__unix__)
    #include <dlfcn.h> // (3)
# endif

#include <io.h> // (4)
#include <vector> // (5)
#include <string>
#include <cstring>

using namespace std;

typedef void(*voidPtr)(); // (6)

class PluginLoader
{
public:
    PluginLoader();
    ~PluginLoader();

# if defined(_WIN32)
    vector < HINSTANCE > dllsHandles; // (7.1)
    vector< HINSTANCE > loadDLLs(string path); // (8.1)

# elif defined(__unix__)
    vector < void* > dllsHandles; // (7.2)
    vector< void* > loadDLLs(string path); // (8.2)
# endif
};
#endif
Opis:

1. Sprawdzamy wersje systemu.

2. Jeżeli jest to nasz ukochany windows, to dołączamy bibliotekę windows.h

3. Jeżeli natomiast jest to system rodziny unix/linux/bsd dołączamy bibliotekę dlfcn.h

Obie te biblioteki posłużą nam do pobrania uchwytów do naszych wtyczek

4. Funkcji z biblioteki io.h użyjemy do pobrania listy wtyczek z folderu

5. Vector czyli dynamiczna tablica posłuży nam za listę uchwytów do wtyczek

6. Wprowadzimy sobie pomocniczy wskaźnik na funkcję typu void bez argumentów. Nie pytaj na razie po co. Zobaczysz.

7.1 Tutaj deklarujemy vector w którym umieścimy nasze uchwyty

8.1 Tu natomiast deklarujemy funkcję, która nam te uchwyty zwróci.

7.2 Jest to 7.1 dla linux'ów

8.2 To jest 8.1 dla linux'ów

 

Plik PluginLoader.cpp:

#include "PluginLoader.h"
PluginLoader::PluginLoader(){}
PluginLoader::~PluginLoader(){}
# if defined(_WIN32)
vector< HINSTANCE > PluginLoader::loadDLLs(string path = "")
{
    vector< HINSTANCE > dllHandles; // (1)
    struct _finddata_t fdata; // (2)
    if(path!="")
        path += "/";
    string newPath = path + "*.dll";
    long fhandle = _findfirst(newPath.c_str(), &fdata); // (3)
    if(fhandle == -1L) // (4)
        return dllHandles;
    do
    {
        char chrpath[256];
        strcpy(chrpath, path.c_str());
        HINSTANCE dllHandle = LoadLibrary(strcat(chrpath, fdata.name)); // (5)
        if(dllHandle)
            dllHandles.push_back(dllHandle); // (6)
    }while(_findnext(fhandle, &fdata) == 0); // (7)
    _findclose(fhandle); // (8)
    this->dllsHandles = dllHandles; // (9)
    return dllHandles;
}
# elif defined(__unix__)
vector< void* > PluginLoader::loadDLLs(string path = "")
{
    vector< void* > dllHandles; // (1)
    struct _finddata_t fdata; // (2)
    if(path!="")
        path += "/";
    string newPath = path + "*.so";
    long fhandle = _findfirst(newPath.c_str(), &fdata); // (3)
    if(fhandle == -1L) // (4)
        return dllHandles;
    do
    {
        char chrpath[256];
        strcpy(chrpath, path.c_str());
        void* dllHandle = dlopen(strcat(chrpath, fdata.name)); // (5)
        if(dllHandle)
            dllHandles.push_back(dllHandle); // (6)
    }while(_findnext(fhandle, &fdata) == 0); // (7)
    _findclose(fhandle); // (8)
    this->dllsHandles = dllHandles; // (9)
    return dllHandles;
}
#endif
Opis:

1. Deklarujemy pusty wektor na uchwyty

2. Deklarujemy strukturę, w której będą znajdować się dane o znalezionym pliku.

3. Szukamy pierwszego pliku w wyznaczonym folderze i zwracamy uchwyt do listy plików do zmiennej long fhandle oraz odbieramy dane o znalezionym pliku do struktury fdata.

4. Jeżeli funkcja nie znajdzie żadnego pliku, to zwraca -1L. W tym momencie my zwracamy pustą listę uchwytów.

5. Pobieramy uchwyt do wtyczki i umieszczamy go w tymczasowej zmiennej.

6. Jeżeli funkcji uda się pobrać uchwyt, to dodajemy go do naszej w/w listy uchwytów.

7. Powtarzamy tę czynność do czasu, gdy obsłużymy wszystkie pliki.

8. Zamykamy połączenie wyszukujące pliki.

9. Deklarujemy naszą tymczasową listę tako wartość klasowej listy uchwytów, a następnie zwracamy tę listę.

 

Teraz mamy gotową funkcję zwracającą nam uchwyty dll'ek z danego folderu.

 

2. Piszemy prostą wtyczkę oraz pobieramy jej funkcje

 

 

W tej części stworzymy funkcję pobierającą nam 2 funkcje z wtyczki oraz tę wtyczkę stworzymy.

 

Do pliku PluginLoader.h dodajemy deklaracje funkcji:

vector< voidPtr > getEnableFunctions();
vector< voidPtr > getDisableFunctions();
Pierwsza będzie pobierała funkcję wykonywaną przy załadowaniu wtyczki, a druga przy zamknięciu jej.

Dodaj tę funkcję w sekcji publicznej i niezależnej od wersji systemu. Będzie to wyglądało tak:

#ifndef PLUGINLOADER_H
#define PLUGINLOADER_H

# if defined(_WIN32) // (1)
    #include <windows.h> // (2)
# elif defined(__unix__)
    #include <dlfcn.h> // (3)
# endif

#include <io.h> // (4)
#include <vector> // (5)
#include <string>
#include <cstring>

using namespace std;

typedef void(*voidPtr)(); // (6)

class PluginLoader
{
public:
    PluginLoader();
    ~PluginLoader();
    vector< voidPtr > getEnableFunctions();
    vector< voidPtr > getDisableFunctions();
# if defined(_WIN32)
    vector < HINSTANCE > dllsHandles; // (7.1)
    vector< HINSTANCE > loadDLLs(string path); // (8.1)

# elif defined(__unix__)
    vector < void* > dllsHandles; // (7.2)
    vector< void* > loadDLLs(string path); // (8.2)
# endif
};
#endif
Teraz ciało tych funkcji. Będą się one w sumie różnić tylko 1 string'iem oraz nazwami zmiennych wewnętrznych.

Jeżeli chcesz, to możesz to sobie udynamicznić. Ja dla ułatwienia zostawię 2 funkcje.

 

Ich ciało będzie wyglądało mniej więcej tak:

#if defined(_WIN32)
vector< voidPtr > PluginLoader::getEnableFunctions()
{
    vector< voidPtr > onEnable; // (1)
    for(int i = 0; i < (int)this->dllsHandles.size(); i++)
    {
        voidPtr onEnableFunction = (voidPtr)GetProcAddress(this->dllsHandles[i], "onEnable"); // (2)
        onEnable.push_back(onEnableFunction); // (3)
    }
    return onEnable; // (4)
}
vector< voidPtr > PluginLoader::getDisableFunctions()
{
    vector< voidPtr > onDisable;// (1)
    for(int i = 0; i<this->dllsHandles.size(); i++)
    {
        voidPtr onDisableFunction = (voidPtr)GetProcAddress(this->dllsHandles[i], "onDisable");// (2)
        onDisable.push_back(onDisableFunction);// (3)
    }
    return onDisable;// (4)
}
#elif defined(__unix__)
vector< voidPtr > PluginLoader::getEnableFunctions()
{
    vector< voidPtr > onEnable;// (1)
    for(int i = 0; i<this->dllsHandles.size(); i++)
    {
        voidPtr onEnableFunction = (voidPtr)dlsym(this->dllsHandles[i], "onEnable"); // (2)
        onEnable.push_back(onEnableFunction); // (3)
    }
    return onEnable;// (4)
}
vector< voidPtr > PluginLoader::getDisableFunctions()
{
    vector< voidPtr > onDisable;// (1)
    for(int i = 0; i<this->dllsHandles.size(); i++)
    {
        voidPtr onDisableFunction = (voidPtr)dlsym(this->dllsHandles[i], "onDisable"); // (2)
        onDisable.push_back(onDisableFunction); // (3)
    }
    return onDisable;// (4)
}
#endif
Opis:

1. Tworzymy vector z listą funkcji, których kolejność będzie odpowiadała kolejności uchwytów.

2. Pobieramy do podanego uchwytu odpowiedni nazwaną funkcję i przypisujemy ja do tymczasowego wskaźnika.

3. Dodajemy ten wskaźnik do vectora.

4. Zwracamy vector.

 

Teraz czas na upragnioną wtyczkę. mój przykład nie będzie zbyt zaawansowany, ale sam możesz go przecież rozwinąć ;)

Jej kod będzie wyglądał tak:

#include <cstdio>

extern "C" void onEnable()
{
    printf("Wtyczka włączona");
}
extern "C" void onDisable()
{
    printf("Wtyczka włączona");
}

Do czego jest to extern "C" ?

Ze względu na manglowanie/zmienianie nazw funkcji w C++ musimy wtyczkę napisać w strukturalnym C (Pracuję nad systemem pozwalającym na tworzenie własnych klas i funkcji w C++ oraz własne ich nazwy(deklarowane przez twórcę wtyczki)). to extern "C" mówi kompilatorowi, że dana funkcja/struktura/.. jest napisana w C.

 

Nasza wtyczka przy załadowaniu wyświetli komunikat: "Wtyczka załadowana", a przy zamknięciu jej "Wtyczka wyłączona"

 

W następtym rozdziale będzie opiane jak tego użyć we własnej aplikacji.

 

3. Implementujemy system w swojej aplikacji.

 

 

W tym rozdziale pokażę, jak użyć naszego systemu.

 

Plik main.cpp:

#include "PluginLoader.h"

using namespace std;

int main()
{
    // inicjalizacja aplikacji
    PluginLoader pluginLoader;

#if defined(_WIN32)
    vector<HINSTANCE> plugins = pluginLoader.loadDLLs("plugins"); // (1)
#elif defined(__unix__)
    vector< void* > plugins = pluginLoader.loadDLLs("plugins"); // (1)
#endif
    vector < voidPtr > enableFunctions = pluginLoader.getEnableFunctions(); // (2)
    vector < voidPtr > disableFunctions = pluginLoader.getDisableFunctions();
    for(int i = 0; i<(int)enableFunctions.size(); i++)
    {
        enableFunctions[i](); // (3)
    }

    // ciało aplikacji

    for(int i = 0; i<(int)disableFunctions.size(); i++)
    {
        disableFunctions[i](); // (4)
    }

    // zamykanie aplikacji

    return 0;
}

Opis:

1. Z wcześniej utworzonego obiektu typu PluginLoader używamy funkcji, która zwraca uchwyty wtyczek i te zapisujemy do vector'a

2. Pobieramy funkcje inicjalizujące i deinicjalizujące wtyczkę i zapisujemy je do vector'ów.

3. Wywołujemy wszystkie funkcje inicjalizujące z wszystkich wtyczek.

4. Przy wyłączaniu aplikacji funkcje deinicjalizujące są wywoływane.

 

Jest to prosty przykład i chyba nikt, kto był w stanie zrozumieć powyższe rozdziały, nie powinien mieć problemu z jego zrozumieniem.

 

4. Hej! Ale co z pozostawionymi w pamięci wtyczkami?

 

 

Właśnie! Przy wywoływaniu musimy uwolnić wtyczki z pamięci naszego procesu. Jak to zrobić?

Do pliku PluginLoader.h dodaj deklaracje funkcji uwalniające wtyczki. Będzie to wyglądało tak:

#ifndef PLUGINLOADER_H
#define PLUGINLOADER_H

# if defined(_WIN32)
    #include <windows.h>
# elif defined(__unix__)
    #include <dlfcn.h>
# endif

#include <io.h>
#include <vector>
#include <string>
#include <cstring>

using namespace std;

typedef void(*voidPtr)();

class PluginLoader
{
public:
    PluginLoader();
    ~PluginLoader();
    vector< voidPtr > getEnableFunctions();
    vector< voidPtr > getDisableFunctions();
# if defined(_WIN32)
    vector < HINSTANCE > dllsHandles;
    vector< HINSTANCE > loadDLLs(string path);
    void unloadDLLs(vector<HINSTANCE> dlls);
# elif defined(__unix__)
    vector < void* > dllsHandles;
    vector< void* > loadDLLs(string path);
    void unloadDLLs(vector<void*> dlls);
# endif
};
#endif
Teraz ciało tych funkcji:

//Wersja Windowsowa:
void PluginLoader::unloadDLLs(vector < HINSTANCE > dlls)
{
    for(int i = 0; i<(int)dlls.size(); i++)
    {
        FreeLibrary(dlls[i]); // (1)
    }
}

//Wersja Linuxowa:
void PluginLoader::unloadDLLs(vector < void* > dlls)
{
    for(int i = 0; i<dlls.size(); i++)
    {
        dlclose(dlls[i]); // (1)
    }
}
Opis:

1. Uwalniamy z pamięci podaną wtyczkę.

 

Dodaj te funkcje w odpowiednich sekcjach pliku PluginLoader.cpp

 

 

Jest to mój pierwszy poradnik i myślę, że się wam spodoba. Nie bijcie. :D

 

Źródła:

 

 


Changelog:

 

2.09.13 - Poprawienie rozszerzenia dla bibliotek linuxowych z .dll na .so

6.07.13 - Poprawnienie literówek

5.07.13 - Utworzenie poradnika

 

Opublikowano

Nie żebym sie czepiał, ale ten drugi plik to chyba "Pluginloader.cpp" a nie "Pluginloader.h".


Pomagam w projektach dotyczących programowania (C++/C/Java/C#/inne). Jak masz jakiś problem, napisz do mnie, wspólnie poszukamy rozwiązania ;).

Opublikowano

Dzięki za informację. Poprawione. Mam nadzieję, że się ten poradnik przyda. Na swojej stronce (w profilu) będę umieszczał rozbudowaną wersję tej biblioteki. Będzie to coś na kształt wtyczek do serwerów bukkit.

  • 2 miesiące temu...
Opublikowano

Bardzo dobra robota chociaż trochę dziwnie tłumaczone, ale brawo mało gdzie to widziałem

/ GA-970A-UD3 / FX-6300 / Sapphire Xtreme 5830 / OCZ ZS 550W / Brutus M23 /

| MPC Coders Team | MPC Gold Member | C#, C++, PHP, (N)ASM, AutoIT, Python, Java |

Opublikowano

Wkrótce postaram się zmienić poradnik na bardziej przyjemny do czytania oraz poprawię opisy i nazwy funkcji i zmiennych bo nie za bardzo funkcja "loadDLLs" pasuje do plików .so

Dzięki za pozytywną ocenę.

 

Pytanko: Wolicie podstawową wersje biblioteki czy bardziej rozbudowany system? (na pewno będzie na moim github'ie -> profil)

Zarchiwizowany

Ten temat przebywa obecnie w archiwum. Dodawanie nowych odpowiedzi zostało zablokowane.

×
×
  • Dodaj nową pozycję...