Servus,
ich habe ein bestehendes Programm in C# in Visual Studio. Muss nun in
der Praktika-arbeit ein Kraftmesser mit der Zur Verfügung stehender
H-und DLL Datei
(http://www.me-systeme.de/software/programmierung/megsv.html) anbinden.
DLL anbinden geht nicht, gibt ein Fehler raus. Laut Bedienungsanleitung
muss ich Header-Datei MEGSV.H mit dem Programm verbinden.
1
Die Anbindung an die Programmiersprachen C und C++ erfolgt mit der Header->Datei
2
MEGSV.H sowie einer Link-Library, die zum Linken als Bibliothek mit angegeben wird und
3
die Verbindung zur DLL herstellt. Link-Libraries für viele Compiler und Entwicklungsumgebungen
4
können mit Hilfe dazugehöriger HIlfsprogramme direkt aus der DLL erstellt
5
werden. Für Microsoft® VisualC++® 6.0 (Eingetragene Warenzeichen der Microsoft Corp.)
6
liegt eine derartige Bibliothek bereits bei (MEGSV.LIB). Die Anwendung kann dem
7
Demoprogramm und der zugehörigen Stapelverarbeitungsdatei DEMOMSCV.BAT
8
entnommen werden.
Ich verstehe leider aus dieser Bedienungsanleitung oben nur Bahnhof.
Kann mir einer ein Tipp geben wie ich die Bibliothek anbinden kann?
Gruss Stefan
Stefan schrieb:> Ich verstehe leider aus dieser Bedienungsanleitung oben nur Bahnhof.
naja, da geht es auch um C und nicht um .net.
du musst die dll aufrufe aus der Header Datei in C# umwandeln.
https://msdn.microsoft.com/de-de/library/cc431203.aspx
Dafür sollte man aber zumindest C(++) können, damit man die Headerdatei
auch versteht.
Da gibt es zwei Möglichkeiten: P/Invoke (siehe Link von Peter) oder ein
C++/CLI-Wrapper.
Ich würde in diesem Fall P/Invoke verwenden, da die meisten Signaturen
sehr einfach sind:
int WINAPI GSVresetStatus(int no);
long WINAPI GSVgetScale(int no);
int WINAPI GSVsetBridgeInternal(int no, int bi);
int WINAPI GSVreadSamplingRate(int no, double *freq, int *factor);
...
Sporadisch noch ein 'void *buffer' als Parameter.
Teilweise könnte man einfach mit Copy & Paste arbeiten oder ein kleines
Script (z.B. mit Python) schreiben, da ziemlich viele Funktionen jeweils
die gleiche Signatur haben.
Du könntest eine Datei erstellen, in der du die C-Prototypen
entsprechend sortierst; dann geht es evtl. schneller. Natürlich erst
einmal in P/Invoke einlesen und es mit ein paar Funktionen testen
(initialisieren und dann GSVversion z.B.).
Hallo,
danke für die schnelle Antwort. An sich ist das Tutorial klar. Jedoch
wenn ich dem Folge, bekomme ich beim Aufruf der Funktion GSVactivate(9,
100) einen Fehler. Dabei habe ich versucht die DLL und die H Datei
einzulesen. Bede geben den gleichen Fehler.
1
{"An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)"}
Dies ist mein Code:
1
using System.Runtime.InteropServices; // for importing of the C++ files
die Meldung deutet auf ein 32 vs 64bit Problem hin.
Dein C# Programm muss passend zur dll sein. Es darf nicht auf "any cpu"
stehen.
Außerdem sollte keine absoluten Verzeichnisse verwenden, lege die DLL
einfach ins gleiche Verzeichnis wie die exe und verwende dann nur den
dll namen.
@Stefan
Ich habe es eben mal ausprobiert; funktionierte auf Anhieb. Wie Peter
schon schrieb: Das ist bei dir ein 32-/64-Bit-Problem.
Falls du die 64-Bit-DLL aus megsv_x64.zip verwendest: Vergiss die für
den Moment (oder überhaupt; 32-Bit-Programme laufen auch unter
64-Bit-Windows einwandfrei).
Installiere setup-kit150.exe; dabei wird die 32-Bit-Version der DLL im
Systemverzeichnis abgelegt. Entweder du lässt sie einfach dort oder du
verschiebst sie in dein Projektverzeichnis, ziehst sie in den Solution
Explorer und änderst die Option zum Kopieren in den Eigenschaften (dann
wird die DLL automatisch ins gleiche Verzeichnis kopiert, in dem deine
exe liegen wird). In beiden Fällen musst du bei DllImport keinen Pfad
angeben.
Um Peter zu wiederholen: Achte darauf, dass deine Anwendung als
x86-Assembly erzeugt wird, also nicht x64 oder "Any CPU" (denn das ist
auf einem 64-Bit-System auch x64), sonst kann es nicht klappen.
Noch ein Punkt, denn du vermutlich übersehen hast: Du musst dir die
Größe der Typen genau ansehen. Z.B. ist ein 'long' in C# immer 64 Bit
lang, in der 32-Bit MEGSV.dll aber 32 Bit.
Falsch wäre also
[DllImport("MEGSV.dll")]
public static extern long GSVversion();
in 32 Bit (DLL und dein Programm).
Dann wird ein 64-Bit-Wert erwartet (long in C#), aber nur ein
32-Bit-Wert geliefert. D.h, dass die DLL zwar geladen wird etc., im
Ergebnis hier aber zur Hälfte Müll steht und es entsprechend zufällig
wäre.
In 32 Bit (was ich, wie gesagt, empfehlen würde) wäre das hier korrekt:
[DllImport("MEGSV.dll")]
public static extern int GSVversion();
Entsprechend funktioniert auch dieses kleine Konsolenprogramm (als
Version wird 1.50 angezeigt):
1
using System;
2
using System.Runtime.InteropServices;
3
4
// Nur für die 32-Bit MEGSV.dll. Im .NET-Projekt muss x86 ausgewählt sein.
5
6
namespace megsv
7
{
8
public static class MEGSV
9
{
10
public static bool IsErrorCode(this int value) => value == GSV_ERROR;
11
12
[DllImport("MEGSV.dll")]
13
public static extern int GSVversion();
14
15
private const int GSV_ERROR = -1;
16
}
17
18
class Program
19
{
20
static void Main(string[] args)
21
{
22
var ver = MEGSV.GSVversion();
23
if (ver.IsErrorCode())
24
{
25
Console.WriteLine("Fehler beim Aufruf einer Funktion.");
Peter II schrieb:> das sollte doch dann gar nicht gehen? Was übersehe ich?
Das ist (wie "Split") eine Erweiterungsmethode.
https://msdn.microsoft.com/de-de/library/bb383977.aspx
Du kannst auch schreiben
[2]
if (MEGSV.IsErrorCode(ver))
und (optional) das "this" in IsErrorCode löschen:
public static bool IsErrorCode(int value) => value == GSV_ERROR;
Oder gleich mit GSV_ERROR vergleichen (muss dann natürlich public sein):
[3]
if (ver == MEGSV.GSV_ERROR)
Persönlich finde ich
[1]
if (ver.IsErrorCode())
aber am eingängigsten; außerdem ist der Wert von GSV_ERROR dann wie in
[2] nicht öffentlich, was ich bevorzugen würde.
Allerdings sollte man es generell mit Erweiterungen der eingebauten
numerischen Typen nicht übertreiben.
BTW: Du hast nichts zu der C#-Version geschrieben, darum bin ich von C#
6 (VS 2015) ausgegangen. Falls du eine (etwas) ältere Version benutzt,
solltest du aus
1
public static Tuple<short, short> Split(this int value)
Das war aber ohnehin bloß als Konsolen-Mini-Beispiel gedacht; du kannst
es ja gestalten, wie du willst. Allerdings würde ich schon eine eigene
Klasse (wie hier "MEGSV") für die DllImport-Attribute, Deklarationen und
evtl. ein paar Hilfsmethoden verwenden und diese auf keinen Fall in eine
Form stopfen ...
Falls dich das "MEGSV." vor den Aufrufen stört (MEGSV.GSVversion() etc.)
- die Funktionen sind ja ohnehin dadurch "gekennzeichnet", dass ihre
Namen alle mit GSV beginnen - und du C# 6 verwendest, kannst du dir mal
"static using" ansehen.
Wurfbrot schrieb:> Das ist (wie "Split") eine Erweiterungsmethode.> https://msdn.microsoft.com/de-de/library/bb383977.aspx
ok, das kannte ich noch nicht. Finde es hier aber nicht sinnvoll.
Das Abfrage eines Fehler wird schnell mal vergessen, GetVersion sollte
einfach eine Exception werfen, wenn es nicht abgefragt werden kann.
@Peter
Ich hatte nicht gesehen, dass der Beitrag von dir war ... Wirkt darum
nun etwas komisch, weil ich in meinem den TO angesprochen habe.
Peter II schrieb:> Das Abfrage eines Fehler wird schnell mal vergessen, GetVersion sollte> einfach eine Exception werfen, wenn es nicht abgefragt werden kann.
Ja; dann müsste man Wrapper-Methoden schreiben. Vielleicht in dieser Art
(oder wie auch immer im Detail):
1
public static class MEGSV
2
{
3
public static Tuple<short, short> GetVersion()
4
{
5
var ver = GSVversion();
6
if (IsErrorCode(ver))
7
{
8
throw new ...Exception(...);
9
}
10
else
11
{
12
return ver.Split();
13
}
14
}
15
16
[DllImport("MEGSV.dll")]
17
private static extern int GSVversion();
18
19
private const int GSV_ERROR = -1;
20
21
private static bool IsErrorCode(int value) => value == GSV_ERROR;
Hallo Wurfbrot, Hallo Peter II,
also das sieht gut aus, danke für eure Hilfe. Ich habe nun paar
Funktionen ausprobiert und das funktioniert soweit. Bei eine Umwandlung
von Datentypen habe ich jedoch etwas Probleme. Es geht um diese Funktion
aus
1
//MEGSV.h
2
int WINAPI GSVread(int no, double *ad);
in meinem Code habe ich geschrieben
1
public static class MEGSV
2
{
3
[DllImport("MEGSV.dll")]
4
public unsafe static extern short GSVread(short no, double *ad);
Zeiger auf Gleitkommavariable doppelter Genauigkeit.
3
Gelesener Meßwert ohne Berücksichtigung von Eingangsempfindlichkeit,
4
Verstärkung und Anzeigenormierung.
5
(Delphi™ und VB: Die Gleitkommavariable wird als Referenz übergeben.)
Programm startet ohne Probleme, zeigt mir jedoch einen Fehler an sobald
connectGSV() aufgerufen wird.
Den Fehler den ich bekomme ist: {"Attempted to read or write protected
memory. This is often an indication that other memory is corrupt."}
Gruss und Danke