Hallo,
ich habe eine Aufgabe in meinem Praktikum bekommen die ich jetzt heruntergebrochen habe, da ich erst versuchen wollte ob dies funktioniert. Ich habe mittels Eclipse ein c++ code geschrieben:
#include <string>
using namespace std;
void stringme()
{
string text1 = "Ein String";
}
dies habe ich dann als .dll Datei abgespeichert und möchte es jetzt in VS13 Importieren:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication10
{
class Program
{
[DllImport("libStringTest.dll")]
public static extern int stringme();
static void Main(string[] args)
{
stringme();
}
}
}
dabei kommt folgende Fehlermeldung: > Fehlermeldung:
Die DLL "libStringTest.dll": Das angegebene Modul wurde nicht gefunden. (Ausnahme von HRESULT: 0x8007007E) kann nicht geladen werden.
kann mir jemand sagen was ich da machen muss? Des weiteren wenn ich ein main() importiere funktionierte es eine "hallo Welt" -Code funktionierte leider auch nicht (denke das eine .dll Datei auch nicht wirklich was ausgeben muss). Sollte alle Einstellungen in Eclipse richtig vorgenommen haben da ich mich schon einige zeit mit dem kompilieren eines C++ codes in Windows befasst habe.
Bin auch schon so einige Foren durchgegangen, da das Problem ja schon öfters aufgetreten ist. Mir halfen jedoch die Lösungsansätze nicht weiter.
ich hoffe ihr könnt mir hier weiterhelfen
MFG
TOM
Das grundsätzliche Problem heißt "Mangling" - der C++ Compiler erweitert zwecks Eindeutigkeit den Funktionsnamen.
Interop funktioniert nur mit C Funktionen, nicht mit C++. D.h. Du musst mit
#ifdef __cplusplus
extern "C" {
#endif
/* ... */
#ifdef __cplusplus
}
#endif
in C++ arbeiten. Deine Fehlermeldung legt aber auch noch andere Probleme nahe, als ob Pfade nicht stimmen oder die Compiler Switches falsch sind - warum kompilierst Du C++ nicht auch in Visual Studio?
Oder Du nimmst gleich C++ CLI - siehe zum Beispiel Wrapping Around a Native C++ Class.
okay das mit void und int war ein Übertragungsfehler meiner Seite.
Ich wusste nicht das ich in VS auch eine .dll Datei erstellen kann. Werde das gleich mal probieren, meinst du das ich dann die Pfad Probleme nicht mehr habe? Trotz allem ändert es nichts an der Tatsache also auch wenn ich VS verwende, dass ich DllImport nur mit C und nicht mit C++ Compilern kann wen ich das richtig verstehe.
Mfg
tom
Trotz allem ändert es nichts an der Tatsache also auch wenn ich VS verwende, dass ich DllImport nur mit C und nicht mit C++ Compilern kann wen ich das richtig verstehe.
Nein, das bedeutet nur, dass du die Funktionen, die du extern verwenden willst, explizit als 'extern "C"' deklarieren musst:
extern "C"
{
int _stdcall stringme()
{
string text1 = "Ein String";
return 0;
}
}
Gruß
wolpertinger
alles klar.
also auf der C++ Seite sieht es nun so aus
/*
* Strinr.cpp
*
* Created on: 01.10.2014
* Author: xy
*/
#include <string>
using namespace std;
extern "C"
{
int _stdcall stringme()
{
string text1 = "Ein String";
return 0;
}
}
und auf der C# Seite dann so
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication10
{
class Program
{
[DllImport("libStrinr.dll")]
public static extern string stringme();
static void Main(string[] args)
{
stringme();
}
}
}
Erfreulicherweise wurde laut der nächsten Fehlermeldung jetzt wenigstens die DLL gefunden, jedoch findet er den Einstiegspunkt noch nicht
Fehlermeldung:
Der Einstiegspunkt "stringme" wurde nicht in der DLL "libStrinr.dll" gefunden
dann kann der Weg zum Durchbruch nicht mehr all zu weit sein:D Ich habe mir jetzt mal ILspy gedownloadet will mal sehen was in so einer .dll Datei drinnen steht.
MFG
Tom
Hallo Tom,
schau mal mit dem http://www.dependencywalker.com/ nach, was die DLL überhaupt exportiert.
Gruß
Maik
Auch hier wieder meines erachtens ein Deklarationsfehler. Gleicher Fehler wie zuvor.
In cpp gibst Du int zurück, in C# deklarierst Du einen String als Return. Sowas muss auffallen.
Ich bin da nicht mehr 100% drin, aber ich denke es muss statt
int _stdcall stringme()
auch
__declspec(dllexport) int _stdcall stringme()
heißen und mit
[DllImport("libStrinr.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int stringme();
aufgerufen werden.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hey,
beim DllImport muss es naürlich "CallingConvention.StdCall" heißen.
extern "C" __declspec(dllexport) int _stdcall stringme()
[DllImport("libStrinr.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int stringme();
Ohne Argumente und Interesse am ReturnWert müsste aber stdcall wie cdecl kompilieren, aber ist natürlich inkorrekt. Mit echten x64-binaries ist das alles egal, da gibts nur eine Calling Convention. Und wenn du void zurückgibst, und dann doch fälschlicherweise der Inhalt vom eax/rax register als ein int interpretiert wird, dürfte auch nichts schlimmen machen. Aber ja, auch die Return-Typen sollten natürlich übereinstimmen.
**
dumpbin
** und **
dependency walker
** sind zwei tools die dir hier beim Fehler finden helfen können.
beste Grüße
zommi
Hallo ihr lieben,
ich habe mich mit dem Dependency Walker jetzt mal etwas beschäftigt jedoch kann ich keine Fehler der .dll entdecken, oder habe ich etwas übersehen? im Anhang ein bild vom Dependency Walker. Das gestaltet sich alles schwieriger als gedacht für mich 😦
Hey,
du müsstest mal mit dem Dependency Walker direkt die Win32Project3.dll öffnen und dann schauen, ob in dem rechten, mittleren Fenster du eine exportierte C-Funktion mit dem Name "stringme" findest.
beste Grüße
zommi
also als erstes wundert euch mal nicht das es einmal win32Project3.dll oder LIBSTRINGR.DLL heißt. Ich experimentiere mit mehreren unterschiedlichen "Codes" um das ganze zum laufen zu bringen.
ich habe eine _Z8stringmev gefunden wie im Bild zu sehen ist, wie gehe ich dann weiter vor?
grüße
tom
Hey,
sieht ja ganz so aus, als ob da immer noch ein Name Mangling am Werk ist, schließlich heißt die Methode _Z8stringmev
und nicht stringme
.
Versuch mal auf __cdecl
auf C++ Seite und natürlich CallingConvention.Cdecl
auf C# Seite umzustellen.
Es scheint, als würde MSVC++ immernoch mangeln.
(Stackoverflow: Why extern “C” still cannot remove name mangling in the following case)
Eventuell hilft ja die Linker-Anweisung aus diesem Stackoverflow-Artikel um absolut kein Name-Mangling vorzunehmen:
#pragma comment(linker, "/EXPORT:ExportSymbol=DecoratedName");
Oder aber du gibst auf C# Seite den gemangelten Namen im DllImport-Attribut an:
EntryPoint="_Z8stringmev"
beste Grüße
zommi
hey,
also mit EntryPoint = "_Z8stringmev" komme ich schon mal durch, deine anderen Vorschläge werde ich gleich mal versuchen durchzusetzten. Würde mich zwischenzeitlich mal gerne bei dir für deine Hilfe bedanken
auf der C# Seite steht ja schon CallingConvention.Cdecl wenn ich mich nicht irre?
[DllImport("libStrinr.dll", CallingConvention = CallingConvention.Cdecl)]
und auf C++ seite auch? hier nochmal die aktuellen cods
C++ Seite:
#include <string>
#define DllImport __declspec( dllimport )
#define DllExport __declspec( dllexport )
#pragma comment(linker, "/EXPORT:ExportSymbol=DecoratedName");
using namespace std;
extern "C"
{
__declspec(dllexport) int _stdcall stringme()
{
string text1 = "Ein String";
return 0;
}
}
C# Seite:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication10
{
class Program
{
[DllImport("libStrinr.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int stringme();
static void Main(string[] args)
{
stringme();
}
}
}
lf tom
Hey Tom,
und auf C++ seite auch?
Ja, du solltest dich für eine Aufruf-Konvention entscheiden: stdcall oder cdecl.
Und das natürlich auf C++ wie auf C# Seite, damit sie korrekt miteinander kommunzieren können.
(es geht in erster Linie darum, ob der Aufrufer (hier C#) oder der Aufegerufene (hier C++) den Stack aufräumt - und darüber sollten sich beide einig sein!)
Und du solltest beim C++ Code das Keyword mit den zwei Unterstrichen verwenden. Also __stdcall
sowie __cdecl
.
Mit einem Unterstrich wird es zwar auch vom Compiler akzeptiert, ist aber undokumentiert.
Probier einfach mal beides auf cdecl zu stellen.
beste Grüße
zommi
hey zommi,
alles klar funktioniert soweit soll mir auch recht sein wenn die Funktionen etwas anders heißen dann gebe ich das einmal an und gut ist. Was mir nun allerdings schon wieder spanisch vorkommt ist, wenn ich mich an mein eigentliches Project wage hab ich auf der C++ Seite eine Funktion Namens "int win32Test()" also sollte diese auch i-wie in meiner .dll Datei auftauchen jedoch ist das in keinsterweise der Fall.
Gibt es zufälligerweise eine Suchfunktion bei Despendency Walker habe jetzt KERNEL32.DLL , MSVCRT.DLL durchgesehen jedoch ohne Erfolg.
Hey,
also hier nochmal ein Test-Snippet:
#include <iostream>
#include <string>
using namespace std;
extern "C" __declspec(dllexport) int __cdecl stringme() {
string message = "Hello from C++";
cout << message << endl;
return 42;
}
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("library.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int stringme();
static void Main()
{
int result = stringme();
Console.WriteLine("Got return value: {0}", result);
}
}
Das ganze kompiliert und ausgeführt mit den 32bit tools unter meinem 64bit Windows funktioniert einwandfrei
"%VS110COMNTOOLS%\..\..\VC\vcvarsall.bat"
cl /LD library.cpp
csc /platform:x86 main.cs
main.exe
Und exportiert wird auch der pure Name "stringme".
Interessanterweise, wenn ich bei C++ auf __stdcall umstelle, dann wird bei mir "_stringme@0" exportiert.
Wenn ich nun auf C# Seite auch auf StdCall umstelle, dann findet er sie sogar... also das .NET Framework sucht sogar nach gemangelten Namen, clever clever.
Aber eben nur, wenn man auch auf stdcall umstellt.
Auf jeden fall MUSST du sicherstellen, dass beides stdcall oder beides cdecl ist. Sonst crashed es mit hoher Wahrscheinlichkeit, sobald echte Argumente oder was anderes als int zurückgegeben wird.
Also wenn beim DependencyWalker nichts angezeigt wird, dann wird auch nichts exportiert.
Gibt es zufälligerweise eine Suchfunktion bei Despendency Walker habe jetzt KERNEL32.DLL , MSVCRT.DLL durchgesehen jedoch ohne Erfolg.
Du brauchst auch lediglich die Top-Level-DLL EOLINK.DLL
im Baum links anzuschauen
Der Baum zeigt dir lediglich diejenigen Bibliotheken an, die wiederum intern von der EOLINK.DLL
verwendet werden. Das interessiert dich aber nicht.
Dieser Baum ist beispielsweise dann interessant um zu gucken, welche Abhängigkeiten zu Dritt-DLLs noch bestehen, da es oft den Fehler gibt, dass ein Programm wegen fehlender DLLs crashed. Du kennst sicherlich Fehlermeldungen wie "Programm ... konnte nicht gestartet werden da xyz.dll nicht gefunden wurde."
Wähle links deine DLL. Und rechts in der Mitte siehst zu die Exporte.
Siehst du nichts, wird nichts exportiert und du hast die falsche DLL.
Du kannst auch rein über die Kommandozeile arbeiten - ohne dependency walker.
[pre]dumpbin /EXPORTS library.dll[/pre]
(Das Tool gehört zu Visual Studio, musst also eine Visual Studio Konsole starten, oder eben die vcvarsall.bat vorher ausführen, siehe vorheriger Post)
Hier nochmal das generelle Vorgehen zum exportieren von Funktionen für eine DLL mit allen drei Varianten, wobei du sicherlich beim __declspec(dllexport)
bleiben kannst:
/EXPORT (Exports a Function)
beste Grüße
zommi
hey zommi,
das heißt ich habe bei der Erstellung der .dll- Datei einen Fehler gemacht?
grüße
tom
ich möchte mich bei allen bedanken die so tatkräftig mitgearbeitet haben, mein ganz besonderen dank möchte ich an zommi aussprechen. Komme bestimmt bald wieder auf euch zurück 😄
Hi kise1990,
aus einer Nachricht von dir habe ich jetzt entnommen, dass du deine native DLL mit MinGW/gcc kompilierst.
Rein theoretisch könnte das natürlich auch funktionieren, das Verwenden einer MinGW/gcc-DLL aus .NET via DLL/Interop.
Aber bisher habe ich das noch nicht in freier Wildbahn erlebt. Ich habe hier starke Bedenken, dass das einfach so funktioniert.
Möglicherweise könntest du mit dem nativen Visual Studio Compiler (cl.exe) eine native Wrapper-DLL schreiben, die deine gcc-Bibliothek statisch linked.
(schon das ist nicht-trivial: MSVC and MinGW DLLs)
Dann verhält sie sich nach außen zumindest wie eine originäre Windows-DLL, mit echter Microsoft C/C++-Runtime und dazu konformen malloc/new sowie Exception Handling.
Dort duplizierst du dann alle relevanten Funktionen, exportierst sie erneut, kompilierst diese mit cl.exe und nutzt nur diese DLL dann aus C# heraus.
Und bitte bitte bitte, nutze cdecl.
StdCall ist ursprünglich eine Microsoft-Geschichte. Und auch wenn gcc das prinzipiell auch unterstützt, nimm einen Standard, der in beiden Welten (gcc + cl) beheimatet ist: cdecl.
beste Grüße
zommi
hallo,
damit hätte ich eig. begonnen aus der C++ Datei hab ich wie folgt eine .dll erstellt und zwar unter Settings-> ToolSettings->Miscellaneous-> Linker flags: (-static-libgcc -static-libstdc++)
wenn das anders leichter zu lösen ist werde ich das gerne tun sitze wie gesagt schon seit 3 Wochen daran und es geht nur schleppend voran. Dachte das es jetzt nur noch ein Katzensprung ist um den einen Fehler: > Fehlermeldung:
An unhandled exception of type 'System.AccessViolationException' occurred in ConsoleApplication11.exe
Additional information: Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist. zu korrigieren.
cdecl nimmt er mir auf C++ Seite nicht an darum habe ich auf beiden Seiten _stdcall verwendet.
mfg
tom
Hey,
damit hätte ich eig. begonnen aus der C++ Datei hab ich wie folgt eine .dll erstellt und zwar unter Settings-> ToolSettings->Miscellaneous-> Linker flags: (-static-libgcc -static-libstdc++)
Und libgcc
sowie libstdc++
sind eben die GNU-Implementierungen, und eben nicht die msvcrt
von Microsoft, welche aber innerhalb .NET verwendet wird. Und daran könnte es eventuell liegen.
cdecl nimmt er mir auf C++ Seite nicht an
Fehlermeldung beim Kompilieren?
Eigentlich müsste es auch über Mono klappen: mit MinGW das Mono-Projekt kompilieren und dann mit diesem Mono deine C#-Anwendung kompilieren. Diese sollte dann auch die MinGW DLL p/invoken können.
Aber versuch es erstmal mit einer nativen microsoft wrapper-DLL.
beste Grüße
zommi
@kise1990:
Dein problem ist, das du nicht verstehst das du einen anderen Compiler verwendest als zommi.
Beim arbeiten mit GCC kannst du natürlich nicht MS Pragmas benutzen.
er wirft mir einen Syntax Error aus deswegen habe ich stdcall geschrieben...
muss mich jetzt erst einmal mit dem wrappen auseinandersetzen da ich da gar nicht weis was ich machen muss. Sobald ich etwas schlauer(einen Ansatz habe) bin melde ich mich wieder.
Wenn Du Hilfe erwartest, dann sei so nett und gib den Leuten auch die genaue Fehlermeldung und nicht nur "geht nicht", oder "Syntaxfehler".
Keiner hat die Lust stupide rum zu raten oder der Fehlermeldung hinter her zu rennen, damit man helfen "darf".
[Hinweis] Wie poste ich richtig? Punkt 5
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
entschuldigt bitte 1 für den Doppel-Post und zweitens war es nicht böse gemeint wenn ich bei eclipse cdecl verwende dann wird alles rot unterkringelt und wenn ich darüber fahre wird lediglich Syntax-Fehler angezeigt. Ich kann aber trotzdem eine dll Datei machen und diese dann auf C# Seite mit dem selben Fehler ausgeben> Fehlermeldung:
System.AccessViolationException
mfg
tom
Also ich habe das mal bei mir getestet, und interessanterweise geht es ohne Verrenkungen:
Meine Settings:
Mit korrekt gessetzten Pfaden kompiliere ich dann meine oben bereits gezeigten Demo-Dateien - die DLL als 32bit mit mingw und das .NET executable für x86. So klappt alles. Auch ein __cdecl ist kein compiler error.
g++ -m32 -shared -o library.dll library.cpp
csc /platform:x86 main.cs
main.exe
Und auch ohne explizites statisches binden der C/C++ libraries. (Das macht MinGW glaub ich automatisch)
Funktioniert das bei dir auch?
beste Grüße
zommi
hallo,
ich habe eben zommi eine private Nachricht geschrieben da mir das jetzt etwas zu schnell ging und ich den Thread hier nicht voll müllen will. Sollte ich zu weiteren Ergebnissen kommen werde ich sie schnellst möglichst posten
Meine Settings:
grüße
tom