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

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ł?

6

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.

The callee cleans the stack, so the compiler makes vararg functions __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.

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.

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.

The callee cleans the stack, so the compiler makes vararg functions __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

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