C++/CLI Dll in C/C++ einbinden
-
Hallo erstmal,
ich bin schon sei längerem auf der Suche mit folgendem Problem:
Ich habe eine dll die in C++/CLI geschrieben ist und auf Funktionen des .NET Frameworks zugreift.
Diese dll soll nun in ein vorhandenes Projekt eingebunden werden, welches aber nur in C/C++ geschrieben wurde.Die Einbindung der dll in eine CLI Konsolenanwendung war kein Problem und funktioniert dabei Problemlos. Nachstehend mein bisheriges Vorgehen.
Zur Konsolenanwendung(Testanwendung.cpp):
Über das Kontextmenü ->Verweise->Neu Verweise habe ich den Verweis auf die dll gesetzt.Testanwendung.cpp:
using namespace DLLNamespace; int main() { InputVlaues ^ actualConditions = gcnew InputValues(20); InputValues ^ referenceConditions = gcnew InputValues(15); OutputVlaues ^ dllOutputValues = gcnew OutputValues(); int errorCode = Interface::Calc( actualConditions, referenceConditions, dllOuputValues); }
Im Projekt der Dll sind Folgende Klassen definiert:
InputValues, OutputValues, Interface
InputValues.h:namespace DLLNamespace { public ref class InputValues { InputValues(int wert1); } }
OutputValues.h:
namespace DLLNamespace { public ref class OutputVlaues { OutputValues(void); } }
Interface.h:
namespace DLLNamespace { public ref class Interface { public: static int Calc( InputValues ^ actualConditions, InputValues ^ referenceConditions, OutputValues ^ dllOutputValues); }; }
Nun zu meinem Problem:
Was muss ich alles ändern, wenn meine Testanwendung nicht in CLI geschrieben wird, die dll aber schon?
Wie binde ich in einem klassischen C++ Project diese CLI dll ein?
In einem Test in C/C++ habe ich die dll und die Testanwendung erstellt und diese eingebunden. Dazu habe ich die dll und die lib in mein Projekt einfach hinzugefügt, und folgende Quelltextdateien erstellt:Testanwendung.cpp
extern "C" __declspec(dllimport) int Calc( InputValues actualConditions, InputValues referenceConditions, OutputValues &refdllOuputValues); int main { InputValues actualConditions = {20}; // InputValues sind als struct in einem header File definiert InputValues referenceConditions = {15}; OutputValues dllOuputValues; // OutputValues sind als struct in einem header File definiert OutputValues &refdllOutputValues = dllOutputValues; int errorCode = Calc( actualConditions, referenceConditions, refdllOutputValues); }
Zur dll:
Interface.cppextern "C" __declspec(dllexport) int DynCalc( InputValues actualConditions, InputValues referenceConditions, OutputValues &refdllOutpuValues); int Calc( InputValues actualConditions, InputValues referenceConditions, OutputValues &refdllOutputValues) { return -1; }
Da dies ein erste Stand meiner dll war, sind die InputValues und OutputValues noch als struct implementiert. Sie sollen aber als Objekte der dll implementiert sein.
Ich hoffe ich konnte einigermaßen gut erklären was ich vorhabe.Vielen Dank schonmal für die Unterstützung
-
Was muss ich alles ändern, wenn meine Testanwendung nicht in CLI geschrieben wird, die dll aber schon? Wie binde ich in einem klassischen C++ Project diese CLI dll ein?
Du musst nichts ändern, wenn Du auch die Testanwendung mit /clr kompilierst.
Wenn Du die Testanwendung nicht mit /clr kompilieren möchtest, musst Du ein C bzw. C++ Interface zur Verfügung stellen - das heisst die C++/CLI Typen kapseln.
Probiere es doch mal mit /clr in der Testanwendung.
-
Hallo theta,
vielen Dank für die Antwort. Du schreibst von kapseln, wenn ich nicht mit clr kopilieren kann/will.
theta schrieb:
Wenn Du die Testanwendung nicht mit /clr kompilieren möchtest, musst Du ein C bzw. C++ Interface zur Verfügung stellen - das heisst die C++/CLI Typen kapseln.
Kannst du das kurz erklären wie ich mir das vorstellen kann?
Bzw. wie ein Code dann ausschauen würde.Vielen Dank
-
Wenn Du ein C/C** Programm hast, würde ich in deinem Programm *eine* Datei machen, welche die Schnittstelle zu dieser Assembly herstellt.
Und diese *eine* Datei muss Du dann in den Projekteinstellungen auf "/clr" einstellen.
Damit wird diese eine Datei mit .NET ausgeführt und alles andere bleibt so wie es ist. Ich empfehle nicht Dein gesamtens Projekt auf .NET (/clr) umzustellen.Wenn Du die Sourcen zu der .NET-Assembly hast, kannst Du diese eine Datei eben auch in diese Assembly reinmachen und dann _normal_ (also mit import usw.) gegen diese DLL linken!
-
Hallo Jochen,
vielen Dank für die Antwort. Ich bin gerade nochmal meine Bücher durchgegangen.
Jochen Kalmbach schrieb:
Wenn Du ein C/C** Programm hast, würde ich in deinem Programm *eine* Datei machen, welche die Schnittstelle zu dieser Assembly herstellt.
Und diese *eine* Datei muss Du dann in den Projekteinstellungen auf "/clr" einstellen.
Damit wird diese eine Datei mit .NET ausgeführt und alles andere bleibt so wie es ist. Ich empfehle nicht Dein gesamtens Projekt auf .NET (/clr) umzustellen.Soweit mir das mein C++ Buch weis machen will, kann man /clr nur für die ganze Projektmappe einstellen. Daher wäre meiner Meinung nach eine Möglichkeit die Mappe komplett umzustellen, und mit #pragma unmanaged die "alten" Bereiche aus CLR auszuklammern. Hier kommt aber dazu, dass die "alten" bereiche deutlich mehr Quelltext umfassen als meine DLL.
Also nicht so gut oder sehe ich das falsch?Jochen Kalmbach schrieb:
Wenn Du die Sourcen zu der .NET-Assembly hast, kannst Du diese eine Datei eben auch in diese Assembly reinmachen und dann _normal_ (also mit import usw.) gegen diese DLL linken!
Meinst du damit folgendes?
Ich habe ja den Code der dll. Diese weiter in /clr belassen, und dann eine C++ Methode schreiben zum Einbinden der DLL und zum starten der Funtkionen der DLL?Vielen Dank
-
Du kannst auf eine einzelne Datei gehen und nur dort "/clr" aktivieren... (rechts-Klick und dann Properties)
Zum letzteren: Ja, das was so gemeint... geht aber nur in bestimmten Fällen, wenn man z.B. nur eine bestimmte Methode aufrufen will ohne dass man extensiv viele Parameter austauschen muss...
-
chucky11 schrieb:
Soweit mir das mein C++ Buch weis machen will, kann man /clr nur für die ganze Projektmappe einstellen.
Das spricht aber gegen das Buch.
Ich fange immer mit einem unmanaged C++ DLL-Projekt an und nur für die cpp-Dateien, die das brauchen, ändere ich die Einstellung auf /clr.
Man muss nur für die C++/CLI Teile ein paar weitere Compilereinstellungen ändern und die Einstellungen für die vorkompilierten Header zwischen managed und native aufteilen.
-
So nach langem hin und her habe ich es endlich geschafft. Vielen Dank für die Unterstützung aus dem Forum.
Für die Nachwelt hier meine Lösung. Falls es anders oder besser geht, könnt ihr das gerne äußern.
Meine Projektmappe besteht jetzt aus folgenden Komponenten:-Interface
-TestanwendungCMein Interface(Dll) habe ich nicht verändert. Diese ist immernoch in /clr geschrieben. Folgende Dateien sind enthalten:
Interface.h
Interface.cpp
InputValues.h
InputValues.cpp
OutputValues.h
OutputValues.cppInterface.h:
// Interface.h #pragma once #include "InputValues.h" #include "OutputValues.h" using namespace System; namespace Interface { public ref class Interface { public: static int Calc( InputValues ^actualConditions, InputValues ^referenceConditions, OutputValues ^outputValues); }; }
Interface.cpp:
#include "stdafx.h" #include "Interface.h" using namespace System; namespace Interface { int Interface::Calc( InputValues ^ actualConditions, InputValues ^ referenceConditions, OutputValues ^ outputValues) { return 0; } }
InputValues.h:
#pragma once using namespace System; namespace Interface { public ref class InputValues { double var1; int var2; int Type; public: // Constructor InputValues( double var1, int var2, int Type ); }; }
OutputValues.h:
#pragma once namespace Interface { public ref class DynOutputValues { double PowerActual; double PowerReference; public: OutputValues(void); // Properties property double Power { double get(); } property double PowerActual { void set(double PowerActual); } property double PowerReference { void set(double PowerReference); } }; }
Im Kontextmenü der TestanwendungC habe ich unter Verweise das Interface eingebunden. Weiterhin als Startprojekt festgelegt (Kontextmenü).
Im Projekt der Testanwendung gibt es 3 Files:-TestanwendungC.cpp
-DllLinking.h
-DllLinking.cppTestanwendungC.cpp:
#include "stdafx.h" #include <iostream> #include "DllLinking.h" using namespace std; int _tmain(int argc, _TCHAR* argv[]) { DllLinking LinkToInterface; return 0; }
DllLinking.h:
#pragma once class DllLinking { public: DllLinking(); };
DllLinking.cpp:
#include "DllLinking.h" #pragma managed DllLinking::DllLinking() { using namespace System; using namespace Interface; // Create actualConditions object InputValues ^ actualConditions = gcnew InputValues( 1000 1 1 ); // Create referenceConditions object InputValues ^ referenceConditions = gcnew InputValues( 1200, 0, 1 ); OutputValues ^ outputValues = gcnew OutputValues(); // Call Dll to run dyn and to report Values int returnCode = Interface::Calc( actualConditions, referenceConditions, outputValues); // Check Dll Call Console::WriteLine("Power sollte 1 sein und beträgt: " + outputValues->Power); }
Unter Eigenschaften der DllLinking.cpp musste ich folgendes einstellen:
C/C++->Allgemein->Debuginformationsformat->Programmdatenbank (Zi)
C/C++->Allgemein->Common Language Runtime-Unterstützung->Common Language Runtime-Unterstützung (/clr)
C/C++->Codegenerierung->C++-Ausnahmen aktivieren->Ja bei SEH-Ausnahmen (/EHa)
C/C++->Codegenerierung->Vollständige Laufzeitüberprüfung->StandardNochmals vielen Dank, und für Vorschläge und Verbesserungen habe ich ein offenes Ohr.