Laden...

c++ code in c# code importieren: Das angegebene Modul wurde nicht gefunden.

Erstellt von kise1990 vor 9 Jahren Letzter Beitrag vor 9 Jahren 14.723 Views
K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren
c++ code in c# code importieren: Das angegebene Modul wurde nicht gefunden.

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

W
872 Beiträge seit 2005
vor 9 Jahren

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.

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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

W
123 Beiträge seit 2008
vor 9 Jahren

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

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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

S
64 Beiträge seit 2008
vor 9 Jahren

Hallo Tom,

schau mal mit dem http://www.dependencywalker.com/ nach, was die DLL überhaupt exportiert.

Gruß
Maik

16.842 Beiträge seit 2008
vor 9 Jahren

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.

1.361 Beiträge seit 2007
vor 9 Jahren

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

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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 😦

1.361 Beiträge seit 2007
vor 9 Jahren

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

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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

1.361 Beiträge seit 2007
vor 9 Jahren

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

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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

1.361 Beiträge seit 2007
vor 9 Jahren

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

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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.

1.361 Beiträge seit 2007
vor 9 Jahren

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.

1.361 Beiträge seit 2007
vor 9 Jahren

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

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

hey zommi,

das heißt ich habe bei der Erstellung der .dll- Datei einen Fehler gemacht?

grüße
tom

1.361 Beiträge seit 2007
vor 9 Jahren

Wahrscheinlich

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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 😄

1.361 Beiträge seit 2007
vor 9 Jahren

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

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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

1.361 Beiträge seit 2007
vor 9 Jahren

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

F
10.010 Beiträge seit 2004
vor 9 Jahren

@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.

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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.

16.842 Beiträge seit 2008
vor 9 Jahren

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

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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

1.361 Beiträge seit 2007
vor 9 Jahren

Also ich habe das mal bei mir getestet, und interessanterweise geht es ohne Verrenkungen:

Meine Settings:

  • Windows 7 x64
  • MinGW x64 mit gcc 4.8.1
  • Visual Studio 2012

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

K
kise1990 Themenstarter:in
16 Beiträge seit 2014
vor 9 Jahren

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:

  • Windows 7 x64
  • MinGW x64 mit gcc 4.8.1
  • Visual Studio 2013

grüße
tom