Datentypen Visual C++ / native C++
-
So,
ich hoffe dieses mal ist das hier das richtige Forum
Mein Problem ist, dass ich eine Klasse in Visual Studio 2005 geschrieben habe, die eine Funktion einer DLL (geschrieben in C++ unter Eclipse) aufruft.Die DLL lade ich dynamisch mit LoadLibrary() und den Enstiegspunkt bekomme ich mit GetProcAddress(). Ich rufe dabei eine einfache Print-Funktion auf.
Wenn ich aber die Printfunktion mit einem Übergabeparameter aufrufen möchte, gibt es einen Speicherfehler, da das Programm in VS die Funktion aus der DLL nicht finden kann.
Jetzt frage ich mich warum das so ist! Ich übergebe dabei eine einfache int-Variable.
Kommt es dabei zu Konflikten in den Datentypen??hier der Code der Visual-C++-Klasse:
testen.cpp:
#include "stdafx.h" #include <iostream> #include "Wrapper.h" int main() { Wrapper::Wrapper_Class *cP; cP = new Wrapper::Wrapper_Class(); cP->callPrint(133); return 0; }
Wrapper.h:
#include "Magnetostatic_Simulation.h" namespace Wrapper { class Wrapper_Class { public: Wrapper_Class(); ~Wrapper_Class(); void callPrint(int zahl); private: HINSTANCE hLibrary; }; }
Wrapper.cpp:
#include "stdafx.h" #include <windows.h> #include "Wrapper.h" BOOL WINAPI DLLMain ( HINSTANCE hDLL, DWORD dwREASON, LPVOID Reserved ) { switch (dwREASON) { case DLL_PROCESS_ATTACH: { break; } case DLL_PROCESS_DETACH: { break; } } return TRUE; } namespace Wrapper { Wrapper_Class::Wrapper_Class() { hLibrary = LoadLibrary("Magnetostatic_Simulation.dll"); } Wrapper_Class::~Wrapper_Class() { FreeLibrary(hLibrary); } void Wrapper_Class::callPrint(int zahl) { typedef void (*FARPROC)(int); FARPROC print; print = (FARPROC)GetProcAddress(hLibrary, "_ZN4pmdm24Magnetostatic_Simulation8printoutEi"); //print enthaelt nach Aufruf immer Adresse 0x00000000. Dies ist eine "falsche" Adresse std::cout << *print << std::endl; (*print)(zahl); //Programm stuerzt hier ab, da Adresse 0x00000000 nicht gefunden wird } }
Folgende Fehlermeldung wird ausgegeben:
Unbehandelte Ausnahme bei 0x00000000 in testen.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x00000000.
Ich hoffe ihr könnt mir helfen, bin am Verzweifeln...
Grüße Stefan
-
Hi,
1. Überprüfe hLibrary, das ist nämlich 0, wenn die Library nicht geladen werden konnte.
2. Wenn 1. erfüllt, dass hLibrary != 0 ist, dann überprüfe den return Wert von GetProcAddress(..), der return Wert ist 0, wenn die Funktion nicht gefunden wurde. Überprüfe mit dem Dependency Walker 1, ob und wie die Funktionen wirklich exportiert wurden.
3. Ich würde deinen eigenen typedef für den Funktionszeiger nicht FARPROC nennen, denn das gibts schon. Du kannst z.B. typedef void (*PrintFuncPtr)(int);
4. Der Aufruf des Funktionszeigers durch den Aufruf print(zahl);
5. Die Ausgabe des dereferenzierten Funktionszeigers würde ich weglassen. Wenschon, dann den Zeiger selbst ausgeben.Simon
1http://www.dependencywalker.com/
Edit:
BTW: Es ist doch das falsche Forum, WinAPI wäre besser.Edit2:
Nur der vollständigkeitshalber. Dein Code ist Ansi C++ und hat nichts mit managed Code zu tun. Auch mit Visual Studio 2005 kann native C++ geschrieben werden.
Desweiteren ist die Verwendung von Code kompiliert (und gelinkt) mit Kompiler A unter Code kompiliert (und gelinkt) mit Kompiler B generel heikel. Das bedeutet, es gibt Einschränkungen und gewisse Dinge zu beachten, wobei ich mir aber zuerst die von mir erwähnten Punkte anschauen würde. Generell ist ein C- Interface (mit Export via *def File oder __declspec(dllexport) / extern "C") geeignet für dein Vorhaben. Die Calling Convetion muss auf beiden Seiten gleich sein (__cdecl).
Mehr Informationen zu dem Thema gibts im Buch "Imperfect C++".
-
theta schrieb:
Hi,
1. Überprüfe hLibrary, das ist nämlich 0, wenn die Library nicht geladen werden konnte.
2. Wenn 1. erfüllt, dass hLibrary != 0 ist, dann überprüfe den return Wert von GetProcAddress(..), der return Wert ist 0, wenn die Funktion nicht gefunden wurde. Überprüfe mit dem Dependency Walker 1, ob und wie die Funktionen wirklich exportiert wurden.
3. Ich würde deinen eigenen typedef für den Funktionszeiger nicht FARPROC nennen, denn das gibts schon. Du kannst z.B. typedef void (*PrintFuncPtr)(int);
4. Der Aufruf des Funktionszeigers durch den Aufruf print(zahl);
5. Die Ausgabe des dereferenzierten Funktionszeigers würde ich weglassen. Wenschon, dann den Zeiger selbst ausgeben.Simon
1http://www.dependencywalker.com/
Edit: BTW: Es ist doch das falsche Forum, WinAPI wäre besser.
irgendwie hab ichs mit den Foren hier nicht so^^
an Punkt 2 scheiterts!! Die Funktion wird nicht gefunden, aber ich verstehe absolut nicht warum! Weil wenn ich der Funktion, die aufgerufen werden soll dabei nichts übergebe, funktioniert es!
-
Dann vermute ich mal folgendes:
Du exportierst die Funktion nicht mit extern "C", was den Kompiler veranlasst, die Funktion via Name Mangeling den Funktionsnamen zu dekorieren. D.h. der Kompiler generiert einen Namen, wo der Rückgabe Type und die Anzahl und Typen der Parameter hineincodiert sind.So ist dann aber der Name der exportierten Funktion nicht gleich bei zwei Funktionen, einmal mit und einmal ohne parameter int.
Hier hilft der Dependency Walker, um zu sehen, wie die exportierten Funktionen tatsächlich heissen.
Ausserdem unbedingt extern "C" verwenden!!!
Simon
-
_ZN4pmdm24Magnetostatic_Simulation8printoutEi ist der Name der Print-Funktion, gelesen aus Dependency Walker!
Die Print-Fkt in der C++-DLL sieht jetzt so aus aber es funktioniert trotzdem nicht:
Magnetostatic_Simulation.cpp:
extern "C" DLLExport void Magnetostatic_Simulation::printout(int zahl) { cout << "Es funktioniert! " << zahl << endl; }
Muss ich noch zusätzlich ein .def-File verwenden?
1.Edit:ah sorry jetzt hab ich erst deine 2Edits von den vorigen Posts gelesen! DLLExport ist bei mir ein define für __declspec(dllexport)! D.h. keine Verwendung von .def??
2.Edit:
hier mal Auszüge der Codes der .h- und .cpp-Datei aus der aufgerufenen DLL:Magnetostatic_Simulation.h:
#ifndef _MAGNETOSTATIC_SIMULATION_H #define _MAGNETOSTATIC_SIMULATION_H #include <windows.h> #include <iostream> #include <cstdlib> #include <cmath> #include <list> #define DLLExport __declspec(dllexport) using std::cout; using std::endl; using std::list; namespace pmdm { class Magnetostatic_Simulation { public: DLLExport void printout(int zahl); }; } #endif
Magnetostatic_Simulation.cpp:
#include "Magnetostatic_Simulation.h" BOOL WINAPI DLLMain ( HINSTANCE hDLL, DWORD dwREASON, LPVOID Reserved ) { switch (dwREASON) { case DLL_PROCESS_ATTACH: { break; } case DLL_PROCESS_DETACH: { break; } } return TRUE; } namespace pmdm { extern "C" DLLExport void Magnetostatic_Simulation::printout(int zahl) { cout << "Es funktioniert! " << zahl << endl; } }
jupp so siehts aus...
-
Entweder extern "C" / __declspec(dllexport) ODER *.def File.
Also ich würde bei ersterem bleiben.Du musst printout(..) global machen. In C gibt es keine Klassen, wesshalb so exportierte Funktionen Probleme machen. Ausserdem ist printout(..) momentan eine nicht- statische Member- Funktion, d.h. es würde eine Instanz der Klasse benötigt.
Aber eben, das umgehst Du, indem Du die Funktion global machst.
Zur Information:
Es gibt einige Fallstricke beim Import/Export von Funktionalität. Dies wird umso schwieriger, wenn Compiler und Linker nicht gleich sind (Hersteller, Version) oder die Einstellungen unterschiedlich sind (Calling Convention, Allignment).Für diesen Fall (Compiler sind nicht gleich) gibts eigentlich nur zwei gangbare Varianten:
1.) C- Interface: Import und Export der Funktionalität erfolgt über C- Funktionen. Darunter kann ein C++ Subsystem sein, welches gewrappt wird.
2.) Import und Export erfolgt über Factory Funktionen und Abstrakte Interfaces. Dieser Ansatz entspricht mehr dem C++ Stil. Im Ansatz werden so bei COM Klassen instanziert.Simon
-
als globale Funktion kann ich die printout() nicht machen, da es beabsichtigt ist, dass sie eine Instanz der Klasse benötigt. Sie ist nämlich nur eine Test-Funktion für andere Funktionen die ich per Instanz später aufrufen möchte.
Werde mir die Möglichkeit mit den C-Interfaces anschauen, bitte lass mich nicht hängen
Grüße Stefan
EDIT:
So, konnte das Problem eingrenzen:Dass ich eine Speicherfehlermeldung bekam lag am DLL-Namen. Ich habe sie einfach umbenannt und jetzt geht es! Wahrscheinlich ein Konflikt mit irgendwelchen Dateien?!
Wenn ich die print-Fkt. global mache, gibt er mir die Zahl korrekt aus:
DLL void printit(int *zahl) { cout << "Ohne Klasse: " << *zahl << endl; }
wenn ich sie dagegen mit
DLL void Magnetostatic_Simulation::printout(int *zahl) { cout << "Mit Klasse: " << *zahl << endl; }
aufrufe, wird irgend eine Zahl ausgegeben...
Ich muss aber die Klassenstruktur aufrecht erhalten, da ich später auf private Variablen usw zugreifen muss!
Kannst du mir vielleicht nochmal das Vorgehen mit den C-Interfaces anhand meines Codes erklären? Blick da noch nicht so richtig durch..