Importowane funkcje z bilioteki DLL wyrzucają błąd przy wywołaniu

Odpowiedz Nowy wątek
2018-09-09 15:33
0

Witam, mam taki o to problem:
Importuje DLL do programu, mam w niej 3 funkcję które przy wywoływaniu wyrzucają błąd

Kod:

DLL:

// PlanyLekcji.h
#pragma once

#ifdef PLANYLEKCJI_EXPORTS
#define PLANYLEKCJI_API __declspec(dllexport)
#else
#define PLANYLEKCJI_API __declspec(dllimport)
#endif

#undef min
#undef max

#include <iostream>
#include <SFML/Graphics.hpp>

// EXPORT PLUGIN
extern "C" PLANYLEKCJI_API int TEST();
extern "C" PLANYLEKCJI_API sf::RenderTexture* initPlugin(int width, int height);
extern "C" PLANYLEKCJI_API void update();
// PlanyLekcji.cpp
#include "stdafx.h"
#include "PlanyLekcji.h"

sf::RenderTexture *screen = nullptr;
int width, height;

int TEST() {
    return 16;
}

sf::RenderTexture* initPlugin(int width, int height)
{
    std::cout << "createPlugin()" << std::endl;
    screen = new sf::RenderTexture();
    screen->setSmooth(true);
    screen->create(width, height);

    return screen;
}

void update()
{
    sf::RectangleShape bg;
    bg.setSize(sf::Vector2f(width, height));
    bg.setFillColor(sf::Color::Blue);

    screen->draw(bg);
}

Moja aplikacja:

// Globalnie
typedef int(_stdcall *TEST)();
typedef sf::RenderTexture*(__stdcall *plugin_init)(int, int);
typedef void(__stdcall *plugin_update)();

// W definicji klasy
plugin_init init = nullptr;
plugin_update update = nullptr;
HINSTANCE DLL = nullptr;

bool SingleSlide::loadFromJSON(JSON j)
{ /* NIE WAŻNE W PROBLEMIE
    try {
        this->json = j;

        this->title = j["title"];
        this->author = j["author"];
        this->date = j["date"];
        this->duration = j["duration"];
        this->arg = j["arg"];

        if (json["type"] == "TEXT")
            this->type = Type::TEXT;
        else if (json["type"] == "PLUGIN") {
            this->type = Type::PLUGIN; */

            std::cout << "Loading " << arg << "..." << std::endl;
            std::string pluginPath = "plugins/" + arg;

            DLL = LoadLibrary(pluginPath.c_str());

            if (!DLL) {
                std::cout << "Cannot load " << pluginPath << std::endl;
                return false;
            }

            this->init = (plugin_init)GetProcAddress(DLL, "initPlugin");
            this->update = (plugin_update)GetProcAddress(DLL, "update");
            if (!init || !update) {
                std::cout << "Could not locate all plugin funcions. (init: " << init << ") (update: " << update << ")" << std::endl;
                return false;
            }

            std::cout << "TEST: " << TEST(GetProcAddress(DLL, "TEST"))() << std::endl;
        }
/* NIE WAŻNE W PROBLEMIE
    }
    catch (JSON::exception &e) {
        std::cerr << e.what() << std::endl;
        return false;
    } */
    return true;
}

Kod powyżej działa bez zarzutów oraz wyświetla TEST: 16

ALE
Kiedy wykonuję się ta intrukcja, już po wywołaniu loadFromJSON()

this->screen = this->init(1280, 720);

i późniejszym używaniu screen

dostaję błąd:
Exception thrown: Access violation executing location 0x0F4913FC.

Moje pytanie brzmi: dlaczego?
Obstawiam że to dlatego że nie wolno mi zapisać i potem używać adresu sf::RenderTexture ale w takim razie jak wykonać system pluginów, które będą coś dla mnie rysować, a ja potem będe to coś wyświetlał?

edytowany 1x, ostatnio: furious programming, 2018-09-09 16:12

Pozostało 580 znaków

2018-09-09 19:22

co JSON ma wspólnego z ładowaniem DLL-ki? Taka kombinacją jest ewidentnym wskaźnikiem na spaghetti code.
loadFromJSON powinno dostawać JSON-a i zwracać jakiś obiekt generowany na podstawie tylko JSON-a. Powinno być zero kontaktu z Win API, tym bardziej nie powinno wołać LoadLibrary albo GetProcAddress.

@topic:
W dll-ce masz extern "C" __declspec(dllexport) sf::RenderTexture* initPlugin(int width, int height);

  • czyli ABI jest w stylu C (extern "C" w zasadzi służy temu by wyłączyć "name mangling"), ABI zachowuje się jak __cdecl.

W aplikacji masz typedef sf::RenderTexture*(__stdcall *plugin_init)(int, int);
Czyli ABI jest nieco inne z powodu __stdcall.

  • __stdcall

    The callee cleans the stack, so the compiler makes vararg functions __cdecl.

  • __cdecl

    __cdecl is the default calling convention for C and C++ programs. Because the stack is cleaned up by the caller, it can do vararg functions.

Czyli jako, że masz funkcję o stałej liczbie argumentów dll-ka zachowuje się tak, że funkcja nie usuwa argumentów ze stosu (to jest odpowiedzialność wywołującego).
Natomiast po stronie aplikacji typedef mówi, że za czyszczenie argumentów funkcji odpowiada wywołana funkcja.
Ta niekonsekwencja powoduje uszkodzenie stosu prowadzące do SEG FAULT.

Wywal __stdcall albo zmień je na __cdecl

BTW: polecam boost:dll, bardziej przyjemne w użyciu.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 9x, ostatnio: MarekR22, 2018-09-09 22:59

Pozostało 580 znaków

2018-09-10 10:10
1

Dodam jeszcze, że błąd zapewne pojawi się przy próbie zwolnienia po stronie aplikacji obiektu zwróconego przez initPlugin. Biblioteka i aplikacja mogą operować na różnych stertach.

Jeśli to są pluginy, to to zwracanie obiektów sf::RenderTexture, czy generalnie jakichkolwiek klas niepełniących roli interfejsów, nie jest dobrym pomysłem.

edytowany 2x, ostatnio: _0x666_, 2018-09-10 10:17
tego nie widać w tym kodzie i tym pytaniu. To się może stać jeśli jedna z części programu linkuje się nie dynamicznie do biblioteki standardowej. W innym wypadku heap jest współdzielony i problemu nie ma. - MarekR22 2018-09-10 10:24
Owszem, pod warunkiem, że kompilator, który skompilował wtyczkę, korzysta z tej samej biblioteki standardowej. W przypadku wtyczek takiej pewności mieć nie możesz, dlatego bezpiecznie jest przyjąć prostą zasadę: jeśli wtyczka przydziela jakąś pamięć, to wtyczka powinna tę pamięć zwolnić. - _0x666_ 2018-09-10 10:44

Pozostało 580 znaków

2018-09-11 13:37
0
MarekR22 napisał(a):

co JSON ma wspólnego z ładowaniem DLL-ki? Taka kombinacją jest ewidentnym wskaźnikiem na spaghetti code.
loadFromJSON powinno dostawać JSON-a i zwracać jakiś obiekt generowany na podstawie tylko JSON-a. Powinno być zero kontaktu z Win API, tym bardziej nie powinno wołać LoadLibrary albo GetProcAddress.

nazwa loadFromJSON dlatego, ponieważ kod na podstawie informacji o pluginie w JSON wczytuje plugin i ustawia o nim informacje. Jest to tylko prywatny projekt do poćwiczenia więc tak go sobie nazwałem :x

@topic:
W dll-ce masz extern "C" __declspec(dllexport) sf::RenderTexture* initPlugin(int width, int height);

  • czyli ABI jest w stylu C (extern "C" w zasadzi służy temu by wyłączyć "name mangling"), ABI zachowuje się jak __cdecl.

W aplikacji masz typedef sf::RenderTexture*(__stdcall *plugin_init)(int, int);
Czyli ABI jest nieco inne z powodu __stdcall.

  • __stdcall

    The callee cleans the stack, so the compiler makes vararg functions __cdecl.

  • __cdecl

    __cdecl is the default calling convention for C and C++ programs. Because the stack is cleaned up by the caller, it can do vararg functions.

Czyli jako, że masz funkcję o stałej liczbie argumentów dll-ka zachowuje się tak, że funkcja nie usuwa argumentów ze stosu (to jest odpowiedzialność wywołującego).
Natomiast po stronie aplikacji typedef mówi, że za czyszczenie argumentów funkcji odpowiada wywołana funkcja.
Ta niekonsekwencja powoduje uszkodzenie stosu prowadzące do SEG FAULT.

Wywal __stdcall albo zmień je na __cdecl

Coś tam z tym próbowałem ale sie już nie przykładałem, ponieważ kiedy napisałeś:

BTW: polecam boost:dll, bardziej przyjemne w użyciu.

Od razu się na to rzuciłem i działa idealnie tak jak chciałem :D

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0