Starthilfe Plugin-Entwicklung C/C++/C#

  • Hallo zusammen,

    ich wollte mich mal an der Plugin-Schnittstelle versuchen, nehme aber nicht wahr, dass mein erstelltes Plugin ausgeführt wird.

    Lt. Lexikon sollte es auch möglich sein, die Plugins mit anderen Programmiersprachen als Object Pascal zu entwickeln. Ich versuche es zur Zeit mit der Sprache C.

    Hierfür sind von Feder bereits die "Typen- und Funktionsdefinitionen für C, C++ und C#" im Lexikonartikel vorzufinden, an denen ich mich orientiere.

    Dazu vllt. vorab ein Hinweis: Es wird darin der Datentyp "byte" verwendet, welcher allerdings bei C und C++ nicht existiert (wenn ich da richtig informiert bin). Als Alternative habe ich bei C "char" gewählt.


    Aber nun hier meine *.c-Datei:

    Die Solution mit dieser C-Datei habe ich mit Visual Studio "gebuildet" (im Debug-Build-Modus! und x86) und so die *.dll-Datei erhalten. Danach entsprechend in den Ordner "Plugins" im LOTUS-Hauptverzeichnis kopiert und die *.ini-Datei aus Lexikon hinzugefügt:

    Normalerweise müsste der obige Quellcode dazu führen, dass eine Textdatei erstellt wird und bei jedem Funktionsaufruf gefüllt wird.
    Die Datei finde ich allerdings nirgendwo (müsste ja eigentlich irgendwo im LOTUS-Ordner erscheinen).

    Ich habe nochmal meine Logfiles angehangen, ich bin mir nicht sicher, ob man da Informationen zum Plugin vorfindet.


    Anbei vllt. noch zwei weitere Fragen:
    -C# und C++ sind objektorientiert, also müssen die Funktionen in irgendeine Klasse gepackt werden... Wo sucht LOTUS nach den Funktionen? In der als Entrypoint definierten Klasse?

    -In C# existiert kein "typedef". Sollen dann Klassen für die nicht vorhandenen Objekte angelegt werden?



    Viele Grüße :)

    Dateien

    • logfile.ldl

      (279,59 kB, 193 Mal heruntergeladen, zuletzt: )
    • logfile.txt

      (16,32 kB, 200 Mal heruntergeladen, zuletzt: )
  • Guten Morgen :).


    Ich habe mich noch nicht mit der Plugin-Entwicklung auseinandergesetzt. Mein Ansatz wäre, dass du in der Zeile

    C
    1. file_pointer = fopen("name_of_file.txt", "w");

    einen relativen Pfad angegeben hast. Falls dem so ist, versuche mal einen absoluten Pfad anzugeben. Außerdem immer nach den (nicht "dem" ^^) Rechten schauen. Es kann durchaus sein, dass du gar keine Schreibrechte für den Ordner besitzt.


    Wie gesagt, ich kenne mich mit der Plugin-Materie nicht so aus. Das wären nur meine ersten Ansätze als Programmierer ^^°.

  • Es wird darin der Datentyp "byte" verwendet, welcher allerdings bei C und C++ nicht existiert

    Es entspricht auf jeden Fall dem UInt8.

    -C# und C++ sind objektorientiert, also müssen die Funktionen in irgendeine Klasse gepackt werden... Wo sucht LOTUS nach den Funktionen? In der als Entrypoint definierten Klasse?

    -In C# existiert kein "typedef". Sollen dann Klassen für die nicht vorhandenen Objekte angelegt werden?

    Meines Wissen müssen die Funktionen in C++ nicht in Klassen gepackt werden! Mit C# kenne ich mich leider nicht aus, aber es wäre plausibel, weil die Tendenz ja eher dazu ging, reine objektorientierte Programmiersprachen zu entwickeln.


    Auf jeden Fall aber müssen sämtliche Methoden "flach" sein, also dürfen zu keiner Klasse gehören! Wenn das nicht geht, musst Du zur Not auf eine "niedrigere" Programmiersprache wechseln, wie C. Aber wie gesagt: Sämtlichen Erfahrungen gemäß sollte das in C++ kein Problem sein.

  • Anbei vllt. noch zwei weitere Fragen:
    -C# und C++ sind objektorientiert, also müssen die Funktionen in irgendeine Klasse gepackt werden... Wo sucht LOTUS nach den Funktionen? In der als Entrypoint definierten Klasse?

    -In C# existiert kein "typedef". Sollen dann Klassen für die nicht vorhandenen Objekte angelegt werden?

    C++ ist zwar objektorientiert, das heißt aber nicht, dass du Klassen nutzen musst. Es ist völlig legitim und im Fall der zu exportierenden Funktionen auch notwendig, auf Klassen zu verzichten. C# ist zur Plugin-Entwicklung grundsätzlich nicht geeignet, weil die erzeugten DLLs keinen Native Code sondern Managed Code enthalten. Dieser kann von LOTUS nicht ohne Weiteres aufgerufen werden.


    Ich habe eine DLL in C++ geschrieben. Dabei habe ich allerdings folgende Funktionsdefinitionen verwendet, die von Feders (für mich z.T. unverständlichen) Definitionen abweichen:

    Das hat soweit gut geklappt. Nur stürzt LOTUS leider ab, wenn SetButton oder PluginFinalize aufgerufen werden. Ich weiß leider nicht, woran das liegt...?/


    Das Schreiben einer Datei klappt bei mir auch. Ich nutze folgenden Code:

    Wenn ich nun LOTUS mit dem GT6N starte und einmal einen neuen GT6N spawne, liegt myfile.txt im LOTUS-Hauptverzeichnis und sieht folgendermaßen aus:

    Code: myfile.txt
    1. PluginStart
    2. Connected with vehicle GT6N
    3. Unconnected vehicle
    4. Connected with vehicle GT6Nô
    5. Unconnected vehicle

    Wobei daraus auch ersichtlich ist, dass char* für shortstring noch nicht das Wahre ist. ;)

  • Danke euch schon mal :)

    Gut zu wissen, dass man in C++ auch Funktionen außerhalb von Klassen anlegen kann. Schon ein Paar Tage nicht mehr damit programmiert...


    Captain Vimes Habe es mal mit C++ versucht und ich finde immer noch keine Datei, deshalb einige Fragen:


    -"Buildest" du unter Release oder Debug?

    -"Buildest" du unter x86 oder x64?

    -Irgendwelche anderen Build-Events oder Besonderheiten?

    -SInd in deinem Quellcode statt "..." andere - zu Ausführung relevanten - Anweisungen?

    -Ich sehe es doch richtig, dass dein erster Quelltext in der *.h und der zweite in der *.cpp ist?

    -Nutzt du Visual Studio zur Programmierung?

    Danke :)

  • Ich nutze Visual Studio 2017 und erstelle über Neues Projekt -> Windows Desktopassistent -> Dynamic Link Library ein neues Projekt (nicht leer sondern das was VS von sich aus generiert). Den Header fstream hab ich eigentlich in stdafx.h included. Daher die Punkte. Ansonsten sind dort nur die Funktionsdeklarationen (extern "C" usw, d.h. ich habe die in der .cpp). Ich erstelle dann eine .def-Datei, in der die zu exportierenden Funktionen aufgelistet sind und bei den Projekteigenschaften trage ich die Datei als Moduldefinitionsdatei ein. Dann klick ich auf Build (d.h. Standardeinstellungen, Debug und x86 glaube ich). Visual Studio meldet dann, dass es die Datei nicht ausführen kann, die DLL liegt dann aber im Debug-Ordner im Projektverzeichnis. Wenn du möchtest, kann ich dir morgen mal mein Projekt zur Verfügung stellen. Dann kannst du dir das selbst mal angucken. ;)

  • Moin,

    Dazu vllt. vorab ein Hinweis: Es wird darin der Datentyp "byte" verwendet, welcher allerdings bei C und C++ nicht existiert (wenn ich da richtig informiert bin). Als Alternative habe ich bei C "char" gewählt.


    [...]


    -In C# existiert kein "typedef". Sollen dann Klassen für die nicht vorhandenen Objekte angelegt werden?

    "byte" gibt es in C als Grundtype nicht, aber mit "typedef unsigned char byte" gibt es byte plötzlich doch. ;-)

    "unsigned char" ist aber völlig in Ordnung, genau wie (wenn <windows.h> eh dabei ist) "BYTE". (was es eigentlich sein sollte, auweiha)


    Nein, einfach typedef weglassen und nur "struct" verwenden. ShortString ist eine reine Datenstruktur, keine Klasse.


    Die exportierten Funktionsnamen der DLL sind schon korrekt, oder? Gerade Visual Studio ziert sich manchmal, name-mangling zu deaktivieren. Das kannst du z.B. damit einfach prüfen. Einfach die DLL in das Fenster ziehen, im zweiten Fenster rechts müssten dann alle exportieren Funktionen korrekt aufgelistet werden.

    Zitat von Captain Vimes

    (für mich z.T. unverständlichen)

    Bis auf ShortString kann man die typedefs natürlich auch weglassen und gleich die Grundtypen verwenden. Der Hintergrundgedanke war, dass es dadurch leichter wird, Delphi-Codebeispiele nach C zu portieren.

    Da gibt es ja einige Fallstricke, mit der <windows.h> wird ja gerne (pre-C99) BOOL verwendet, was aber nicht unsigned char ist - sondern int. :-)

    (Wobei ich gerade merke, dass da auch statt "Single" eigentlich "Word" stehen sollte...)


    Code: myfile.txt
    1. PluginStart
    2. Connected with vehicle GT6N
    3. Unconnected vehicle
    4. Connected with vehicle GT6Nô
    5. Unconnected vehicle

    Wobei daraus auch ersichtlich ist, dass char* für shortstring noch nicht das Wahre ist. ;)

    Richtig, sonst läufst du da einfach in den Speicher hinter dem String bis zum ersten zufälligen Nullbyte. Ein Delphi-ShortString ist so aufgebaut:


    [Länge, immer ein Byte][Eigentlicher String]


    Beispiel: ("FF" stellt den restlichen Speicher da)

    Meldet LOTUS als String "DüWag GT8" zeigt der Pointer nicht auf folgendes:


    [..] FF FF FF 44 fc 57 61 67 20 47 54 38 00 FF FF FF [...]


    Sondern:

    [..] FF FF FF 09 44 fc 57 61 67 20 47 54 38 FF FF FF [...]


    Das erste Byte ist die Länge des nachfolgenden Strings. Da die Länge immer relativ kurz ist, wird beim ersten Byte immer ein nicht-druckbares Steuerzeichen rauskommen.

    Der String ist aber NICHT Nullterminiert. Das ist KEINE C-Zeichenkette.


    Viele Grüße

    6 Mal editiert, zuletzt von Feder () aus folgendem Grund: Korrigierte Version im Spoiler

  • "Buildest" du unter x86 oder x64?

    Das muss zur jeweiligen LOTUS.exe passen. Momentan liefern wir ja nur die 32-Bit-Version aus, d.h. momentan kompilierst Du auf x86. Später dann musst Du beide kompilieren und die erhalten dann unterschiedliche Dateinamen; beide werden in der ini-Datei vermerkt.

  • Moin Feder,


    erstmal danke für deine Antwort. Den Shortstring habe ich jetzt so definiert wie du und dann klappt das auch alles. Der Schreibfehler bei Single war auch eine Sache, die mich stutzig gemacht hat. Gut, dass das jetzt klar ist. :)


    Was ich allerdings immernoch nicht verstehe ist, warum du die value-Parameter der ReceiveVar...-Funktionen als Pointer übergibst. Das ist in der Pascal-Version nicht so und soweit mein eingeschränktes Testen gezeigt hat auch nicht nötig bzw. falsch. Ebenso sollte SetFloat meiner Meinung nach einen Float zurückgeben und nicht PFloat. Dann ist es auch mit SetButton konsistent. Falls ich mich da irre, entschuldige bitte. ?/


    Lukas_

    Ich werde es leider heute nicht mehr schaffen, dir das Projekt zu schicken. Ich habe mich dazu entschieden, gleich eine fertige Projektvorlage zu machen, damit das alles ein bisschen schöner ist. Außerdem musste ich noch den shortstring fixen und ein bisschen testen. Ich werde aber auf jeden Fall versuchen das morgen hochzuladen. ;)

  • Moin,


    ... das ist natürlich völlig korrekt und richtig, danke dir. Und ich frage mich die ganze Zeit, warum das bei euch nicht funktioniert, bei mir aber schon. Man sollte nie nur die Hälfte kopieren und den Rest händisch anpassen. Oh weia. :-D


    Grüße


  • So, ich habe jetzt für Visual Studio (2017) eine Projektvorlage erstellt, bei der man eigentlich einfach auf Build klicken kann und dann sollte das Plugin funktionieren. ;)
    Lotus Plugin.zip
    Ich habe Kommentare beigefügt, die ein bisschen was erläutern. (Auf Englisch, damit die Vorlage massenkompatibel ist.)

    Zum Installieren einfach den *.zip-Ordner ohne die Dateien zu extrahieren in den Ordner Dokumente -> Visual Studio 2017 -> Templates -> ProjectTemplates -> Visual C++-Projekt legen. Dann sollte die Vorlage im Auswahlmenü beim Erstellen eines neuen Projekts erscheinen. Ich füge außerdem noch meine Testdatei bei, die ein bisschen was in eine Datei myfile.txt im LOTUS-Hauptverzeichnis schreibt. Achtung: "Ein bisschen was" heißt hier 6 Zeilen in jedem Frame! Daher bitte nur zum kurzen Testen nutzen.



    Ich habe allerdings noch mehrere Probleme mit der Plugin-Schnittstelle, für die ich keine Lösung gefunden habe. Feder  Marcel Kuhnt


    LOTUS stürzt ab, wenn ich SetButton nutze, PluginFinalize wird nicht aufgerufen, eventindex hat bei SetFloat immer den Wert 64984 und *value enthält in ReceiveVarString nur sinnlose Zeichen (getestet mit der Variable Hint - war die einzige Stringvariable, die ich im GT6N-Script finden konnte - und Hints waren natürlich eingeschaltet). Da die Stringübermittelung und Ausgabe bei OnConnectingVehicle funktioniert und der Code identisch ist, scheint da etwas anderes nicht zu stimmen. ?/

  • Konnte das Plugin noch nicht ausführlich testen, aber es wird auf jeden Fall ausgeführt.

    Danke Dir/Euch vielmals:-)


    PS: Feder Mit dem Dependency Walker habe ich festgestellt, dass die Funktionsnamen ein "_" an den Anfang gesetzt bekommen haben. Vmtl. wurde das Plugin deshalb nicht ausgeführt, weil die Funktionen nicht gefunden wurden. Das Problem löst dann bestimmt die *.def-Datei von Captain Vimes.