Hallo zusammen,
im Rahmen einer Studienarbeit schreibe ich ein Programm mit
Benutzeroberfläche in C# zur Steuerung eines Prüfstandes. Dazu muss ich
3 Werte an ein anderes Programm, welches den Prüfstand steuert
übergeben. Momentan schreibe ich die Werte in eine txt Datei und starte
eine von mir geschriebene Konsolenanwendung, die die txt ausliest und
über die Funktionen einer Dll die übergabe realisiert. Das funktioniert
auch alles ganz wunderbar.
Mein Betreuer meinte das ist keine saubere Lösung (womit er ja auch
irgendwie recht hat) und ich soll probieren aus meinem C# Programm
direkt die Werte per Funktionsaufruf zu übergeben.
Dazu habe ich dieses kleine Tutorial gefunden aber es noch nicht
Umsetzten können. http://www.functionx.com/csharp2/libraries/cppcli.htm
Meine eigentliche Frage: Gibt es noch eine Alternative ? Ist die Lösung
mit dem schreiben in die txt Datei der Werte wirklich so viel langsamer
?
Bin für Anregungen und Vorschläge dankbar!
Quereinsteiger schrieb:> Meine eigentliche Frage: Gibt es noch eine Alternative ? Ist die Lösung> mit dem schreiben in die txt Datei der Werte wirklich so viel langsamer
ja ist sie, braucht viel mehr ringsrum - was ist wenn du keine
Schreibrechte hast, was ist wenn die datei gleichzeitig von mehre
Nutzern angelegt wird? Das geht zwar ob schön ist es nicht.
C++ dll hat den nachteil das die funktionen sehr merkwürdige namen
bekommen. Sicher das es c++? Dann eine normale dll kann man noch einfach
mit 3-4 Zeilen c# aufrufen.
Ja das ist C++.
Die Funktion die ich aufrufen will ist in der header Datei so
deklariert:
typedef long (*PUTPARAMETER)(const char* , double*);
Aufrufen tue ich in meiner Konsolenanwendung die Funktion so:
PutParameter("Rn2/Mdl/Ini/Pen/PosX", &Cxwert);
PutParameter("Rn2/Mdl/Ini/Pen/PosY", &Cywert);
Würde das dann inetwa so aus sehen ?
[DllImport("Ca.dll")]
public static extern long ??? (???, ???)
Quereinsteiger schrieb:> Die Funktion die ich aufrufen will ist in der header Datei so> deklariert:>> typedef long (*PUTPARAMETER)(const char* , double*);
Das ist ein Funktionspointer. Bist Du Dir da sicher?
Du kannst eine "C++-Funktion" nur aufrufen, wenn sie eine statische
Memberfunktion oder gar keine Memberfunktion ist. Andernfalls fehlt Dir
die erforderliche Laufzeitumgebung in Form des zugehörigen Objektes.
Die Namen der Funktionen in der DLL kannst Du mit
1
dumbin /exports meine.dll
herausfinden, vorausgesetzt, ein MS-C/C++-Compiler ist installiert.
Quereinsteiger schrieb:> Würde das dann inetwa so aus sehen ?
[DllImport("Ca.dll")]
public static extern long PutParameter(String s, out double d)
so in der art
Rufus Τ. Firefly schrieb:> Das ist ein Funktionspointer. Bist Du Dir da sicher?
Ja ganz sicher bin ich mir da nicht. Mit Pointern hatte ich in C++/C# so
noch nichts am Hut.
In der Header Datei die zu der dll gehört steht noch das zu der
Funktion.
long PutParameter(const char *pName, double *pVal);
Also meint ihr das würde funktionieren ? Dann häng ich mich da nochmal
rein und gucke ob ich das so zum laufen bekomme.
Ja das funktioniert einwandfrei. Gibt sogar Tools die dir aus einer DLL
eine Wrapper Klasse generieren können (funktioniert aber nur wenn die
DLL eine Typelib exportiert).
Das ganze nennt sich P/Invoke. Schau dir mal den P/Invoke Interop
Assistant an (frei verfügbar auf codeplex).
Quereinsteiger schrieb:> In der Header Datei die zu der dll gehört steht noch das zu der> Funktion.>> long PutParameter(const char *pName, double *pVal);
Wenn das nicht als Bestandteil einer Klassendefinition in der
Headerdatei drinsteht, dann musst Du nur noch den "dekorierten Namen"
der Funktion herausfinden - sofern sie überhaupt einen hat (beim
Erstellen von DLLs können Funktionen auch andere als die vom Compiler
vorgegebenen Namen zugewiesen werden).
Wie Du herausfinden kannst, wie die Namen der von der DLL exportierten
Funktionen heißen, habe ich bereits geschildert.
Man kann relativ einfach selbst eine Wrapper Classe schreiben. Ich habe
mal eine C++ DLL im C# Projekt verwendet. Habe dafür eine Klasse
erstellt die auf die Dll Funktionen zugreift. Soweit ich noch weiß,
musste man im Projekt die "unsafe" Programmierung zulassen, da C#
unmanaged Programmierung nicht zulässt. Knifflig wird es, wenn die C++
Funktionen Pointer zurückgeben.
Hier ist eine gekürzte Version:
//Wrapper Class:
[c]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace MYFUNCTION_Csharp
{
class MyFunction
{
[StructLayout(LayoutKind.Explicit)]
public struct System
{
[FieldOffset(0)]
public ushort Model;
[FieldOffset(2)]
public ushort Option;
}
[StructLayout(LayoutKind.Explicit)]
public class InfoIn
{
[FieldOffset(0)]
public int SrcID;
[FieldOffset(4)]
public int DataType;
}
[DllImport("irbgrab.dll", CharSet = CharSet.Auto)]
public static extern int SetParam
(int SrcID, int param, double
Value);
}
}
[c/]
Verwendung im Programm:
MyFunction.SetParam(123, Convert.ToInt32(textBox_SourceNum.Text));
Vielleicht hilft es dir weiter.
Das mit dem unsafe ist nur selten wirklich nötig. Pointer z.B. kann man
mit IntPtr ganz gut abbilden.
Die Laufzeitumgebung setzt den dann in einen "echten" Pointer.
Alternativ dazu kannst du Variablen auch mit dem Schlüsselwort "ref"
übergeben. Diese werden der c++ dll auch als Adressen gemarschallt.
Funktionspointer, wie z.B. für Callback Funktionen werden mit Delegaten
realisiert.
Hier mal ein Beispiel, dass ich schon seit längerem im Einsatz habe:
Dabei stellt RefData dann schlussendlich einen Pointer auf einen
benutzerspezifischen Datenhaufen aus Bytes dar.
Eventuell hilft auch [In, Out] so wie hier:
Erstmal vielen Dank für eure Hilfe!
Ich hab die .dll in meine Resources eingefügt und so importiert.
[DllImport("Ca.dll")]
public static extern long PutParameter(string s, double val);
Rufe die Funktion auf über:
PutParameter("Rns/Mdl/Ini/Pen/PosX", xwert);
PutParameter("Rns/Mdl/Ini/Pen/PosY", ywert);
Kompilieren tut er ohne Probleme. Wenn ich das dann am Prüfstand teste,
meldet er mir einen Fehler "Error load on Library" beim Versuch Daten zu
übergeben.
Bei meiner C++ Konsolenanwendung definiere ich zusätzlich
#define HOST "172.30.11.34"
#define HOST1 "deaktiv"
und rufe dann die Funktion "LoadCaDriver()" auf. Und öffne das
eigentliche Steuerungsprogramm mit ControlOpen(HOST1,0,1);
Habe jetzt in meinem C# Programm Host und Host 1 als String deklariert
und versuche die beiden Funktionen auch aufzurufen. Aber da spielt mein
Compiler nicht mit und meldet Fehler.
Was mich noch stutzig macht: Using Ca.dll; wird nicht akzeptiert. Das
sollte er doch eigendlich kennen oder täusch ich mich da ?
Ich bleib dran und berichte euch ob es klappt :)
Du hast mal mehrere Probleme.
Punkt 1: Du darfst kein kein using ca.dll machen. Das wird nicht
funktionieren. Stattdessen sagst du Visual Studio er soll die ca.dll an
die gleiche Stelle kopieren wie die exe die du erstellst, dann findet er
die DLL.
Ist wahrscheinlich auch falsch. Du kannst keinen String übergeben,
stattdessen übergibst du einen Pointer auf das erste Zeichen. Um das
zuVereinfachen gibt es die Marshal Klasse. Ebenfalls übergibst du den
double nicht als wert, sondern als Pointer, sieht dann etwa so aus (nur
aus demKopf jetzt, die genaue Schreibweise musst du dir selber suchen)
Damit wird dafür gesorgt dass du zwar einen String angeben kannst,
jedoch im Endeffekt der Pointer übergeben wird. 'val' wird ebenfalls als
Referenz übergeben.
Thomas Linder schrieb:> public static extern long PutParameter(string s, double val);> Ist wahrscheinlich auch falsch. Du kannst keinen String übergeben,> stattdessen übergibst du einen Pointer auf das erste Zeichen.
das mit dem string passt schon, das ist so üblich. Man kann sogar noch
als option mitgeben ob das als widecare oder nicht übergeben werden
soll. Da ref hatte gefehlt.
Danke für eure Unterstützung.
Ich hab die Tage leider vergebens versucht das so in den Griff zu
bekommen. Bekomme immer wieder verschiedene Fehler die zum Abbruch des
Programmes führen.
Mir ist die Idee gekommen die Speicheradresse der Werte im C# Programm
mit dem C++ Unterprogramm auszulesen und die Übergabe so ein wenig
eleganter zu gestalten. Habe dazu dieses Tutorial gefunden.
http://www.online-tutorials.net/security/speicherzugriff-tutorial-teil-1/tutorials-t-27-63.html
Das sollte doch schneller und Ressourcenschonender sein, als die Werte
aus der .txt Datei auszulesen oder ?