Forum: PC-Programmierung Problem mit Visual Studio 2015 bei einem CLR Projekt


von Tobias B. (horschtx)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,

hier ist bestimmt jemand der mir helfen kann.
Ich entschuldige mich gleich mal, da die kenntnisse von mir mit C++ und 
PC-Programmierung nicht so toll ist. Aber vielleicht bekomme ich es mit 
hilfe trotzdem hin.

Ich versuche mal mein Problem so gut wie möglich zu erklären.

Prinzipell geht es um eine Verbindung zwischen ein PC-Programm und dem 
Microsoft Flugsimulator X.
Es gibt die Software FSUIPC mit der man direkt in den Simulator kommt. 
Ich habe nun versucht auf diese Software zuzugreifen. Es gibt noch eine 
API mit fertigem Code den man direkt einbinden kann.
Also habe ich mir Visual Studio 2015 besorgt und das mal Probiert.
In einem Allgemeinen Projekt hat das auch funktioniert.

Nun wollte ich das ganze aber gern mit einer Benutzeroberfläche haben. 
Also ein leeres CLR Projekt erstellt. Da ein kleines Tutorial gemacht um 
die Funktion ein bisschen kenner zu lernen. (Hab ich schon erwähnt, ist 
mein erstes mal mit Visual Studio 2015, bzw. generell Visual Studio). 
Dann habe ich das Programm und da den Code aus dem Allgemeinen Projekt 
erweitert.

Nun der Versuch zu Kompilieren hat nicht mehr funktioniert. Nun kommt 
die Fehlermeldung D8045 Die Datei "IPCuser.c" kann nicht mit der 
/clr-Option kompiliert werden.

Hmm... doof. Nun ein bisschen gegoogelt und mal die Kompilieroptionen 
probiert.
Wenn ich nun die IPCuser.c als C++ Code mit der Option /TP Kompiliere 
ist der erste Fehler weg dafür eine menge neue ander da.

Prinzipell schein es immer der gleich zu sein:
Fehler  C2664  "int wsprintfW(LPWSTR,LPCWSTR,...)" : Konvertierung von 
Argument 1 von "char [260]" in "LPWSTR" nicht möglich

Und hier komme ich jetzt nicht mehr weiter.
Wenn sich jemand erbarmen würde und sich das mal anschauen könnte wäre 
das echt Spitze und ich könnte vielleicht bis zum nächsten Problem 
weitermachen.

Ich habe das Programm mal mit angehängt wenn jemand reinschauen mag. 
(Nicht wundern, der Name ist noch von dem ersten Tesprogramm)

Vielen Dank für eure Hilfe
Schöner Gruß Tobias

von Arc N. (arc)


Lesenswert?

In den Projekteigenschaften unter Configuration Properties / General von 
Unicode auf Multibyte umstellen. Wenn es daran liegen sollte...

Andere Frage: Warum muss das unbedingt CLR sein?

von Tobias B. (horschtx)


Lesenswert?

Hallo

die Umstellung auf Multibyte hat jetzt wieder andere Fehler gebracht.
Schaut dann ungefähr so aus:
S
Fehler  LNK2019  Verweis auf nicht aufgel÷stes externes Symbol ""extern 
"C" unsigned int __stdcall RegisterWindowMessageA(char const *)" 
(?RegisterWindowMessageA@@$$J14YGIPBD@Z)" in Funktion ""extern "C" int 
__cdecl FSUIPC_Open(unsigned long,unsigned long *)" 
(?FSUIPC_Open@@$$J0YAHKPAK@Z)".

Was hab ich für alternativen zu CLR? (Was bedeutet das CLR?)

Ich bin für alles offen..

von Bitte ein Bit (Gast)


Lesenswert?

Du nutzt, lax gesagt, die Microsoft Variante von Java. Du benutzt die 
Sprache C++/CLR und nicht C++. Das sind generell zwei unterschiedliche 
Sprachen! "Javascript to Java is like Ham to Hamster!"

Deine "IPCuser.c" ist aber ein C Projekt (wgegen Dateiendung). Deswegen 
musst du am besten ein C Projekt anlegen. Unter "Neues Projekt" musst du 
bei Vorlagen Visual C++, Win32, Win32 Konsolenanwendung wählen. Und 
danach musst du die Hauptdatei die Dateiendung .c verpassen, damit dein 
Projekt ein C Projekt wird.

von Tobias B. (horschtx)


Lesenswert?

mhh ok..

jetzt stellt sich mir nur die Frage wie bekomme ich dann Buttons und 
Fenster da rein. Mit der CLR Variante ging das ja recht simpel.

Sry, aber das alles ist irgendwie so verwirrend und es gibt so viele 
verschieden möglichkeiten. Da fehlt mir absolut der Überblick.

von Knirps (Gast)


Lesenswert?

Ich würde nicht völlig ausschließen, dass es mit den richtigen 
Einstellungen funktionieren könnte, denn C++/CLI kann mit nativem und 
managed Code umgehen (darum lassen sich damit ja Wrapper schreiben - was 
auch so ziemlich die einzige sinnvolle Anwendung von C++/CLI ist).
Aber hier würde ich doch eher "normales" C oder C++ und eine 
entsprechende GUI-Bibliothek empfehlen.

Tobias B. schrieb:
> jetzt stellt sich mir nur die Frage wie bekomme ich dann Buttons und
> Fenster da rein.

MFC, WTL, Win32++, wxWidgets, Qt, ...

von Klaus (Gast)


Lesenswert?

Wenn es C++ sein soll, dann musst Du eine WIN32 Anwendung erstellen, wie 
ja bereits jemand sagte. Es muss aber keine Konsole sein, sondern ein 
WIN32-Projekt. Dann kannst Du mit der guten alten WIN32 API auch Deine 
Fenster und Buttons einbauen.

Nur mal als Beispiel, wie man das so machen kann:
http://ahidlib.com/index.php/free-download

Wähle die C++ Demo / WIN32 API Anwendung aus. Diese ist zwar für VS2010, 
sollte aber 'per Knopfdruck' konvertierbar sein.

Gruß Klaus

von Carsten P. (r2pi)


Lesenswert?

Ich frage mich ja, lieber @OP, im Ernst, wieso du (oder warst du das gar 
nicht) uns einen Anhang namens "RandomNumberGenerator" geschickt hast. 
Ist hier was wohl nicht virensicher? Ough!

Ansonsten ist dein Post so ein Konglomerat von "ich habe echt wenig 
Ahnung, aber morgen muss das gehen!" und Freistoßvorlagen für sehr viele 
"wieso willst du überhaupt was von Microsoft, du musst total kaputt und 
dumm sein, wir wissen das doch alle besser"-Antworter.

Mein Tipp wäre: Lies die Doku zu dem FSUIPC Dings. Wenn dir das ned 
hilft, schreib den Entwickler an. In der Regel sind Microsoft-Entwickler 
recht entspannte Menschen mit einem großen Willen zum Teilen. Lass es 
zwei Tage länger dauern, bis es "Klick!" bei dir macht, so what?

(Und ja, ich schreibe hier mit größter Absicht keine angeblichen 
Hinweise der Art "Lade dir mal das und das herunter, das könnte helfen, 
aber eigentlich weiß ich es auch nicht" hin ;) )

LG
Carsten

von Tobias B. (horschtx)


Lesenswert?

So nach viel lesen und probieren und auch nicht verstehen bin ich 
eigentlich der Meinung das der einfachste weg wäre wenn ich das 
bestehende Programm welches der FSUIPC bereitstellt zu nehmen und in das 
CLR Projekt einbinden würde.

Vielleicht gibt es ja jemand der mir verständlich zeigen kann wie man 
sowas macht. Mein Kenntnisse reichen so auf jeden fall nicht aus und ich 
möchte und kann nicht Wochenlang dafür weiter investieren.

Wenn sich jemand findet freut mich das natürlich und wenn nicht das 
schau ich mal weiter ob es noch wo anderst jemand gibt oder lasse es 
einfach sein.
Danke

von Carsten P. (r2pi)


Lesenswert?

Also, Tobias,

hier wurde (sorry, wenn das von den Autoren nicht so geplant war) mal 
wieder viel Microsoft- und CLR-Bashing betrieben in den Antworten.

Wichtig zwischen all den anderen Aussagen war der Hinweis darauf, dass 
C++/CLR nicht dasselbe ist wie C++. Die CLR ("common language runtime") 
ist eine Sammlung von Bibliotheken im Rahmen dessen, was man in 
Microsoft-Kreisen "managed code" nennt. Egal, welche Programmiersprache 
du aus der "managed" Familie nutzt (es gibt u.a. VisualBasic.NET, C#, 
C++/CLR, F#, Perl und, ich glaube, sogar Python...), der jeweilige 
Compiler baut daraus (mehr oder weniger) denselben "intermediate 
language" (IL) Code, der erst bei der Ausführung "just in time" (JIT) 
wirklich in Maschinencode übersetzt wird.

Deswegen ist es z.B. möglich, eine Klassenbibliothek auf einem 
Windows-PC zu entwickeln mit all den Nettigkeiten, die Visual Studio, 
Team Foundation Server und die 100.000 Add-ons dazu bieten bis hin zu 
Scrumban-basiertem Projektmanagement und Integration in MS Project und 
automatisierten Test- und Deployment-Factories und und und (ja, das gibt 
es alles auch für andere Umgebungen...), das ganze einfach per 
Dateikopie auf einen Raspberry zu schubsen und laufen zu lassen. Nix 
Neukompilieren, läuft einfach. So in etwa funzt das ganze ja auch mit 
Java.

Jedenfalls: es gibt ein paar Stoplerfallen bei der Sache.

Zuerst: Alles, was nicht "managed", also CLR ist, musst du einpacken, 
sprich: Wrapper dazu schreiben. Wenn du einen POCT ("Plain Old C Type") 
einpackst, ist das easy, weil es keine Abhängigkeiten gibt. Fowler hatte 
schon Recht. Es gibt das Attribut "DllImportAttribute" in der .NET-Welt. 
Einfach mal im Studio danach suchen oder bei msdn.microsoft.com (dein 
bester Freund in der .NET-Welt). Da findest du auch Beispiele, wie du 
den Wrapper aufbauen musst.

Dann: Pfade. .NET ist ziemlich autistisch, was Pfade angeht. Wenn du 
dich nicht stur an die (sich von Version zu Version gerne mal ändernden) 
Vorgaben der jeweiligen Windows-Version hältst, findet die Laufzeit 
keine Bibliotheken, außer mit ganz viel Hexerei in der Registry, und auf 
anderen Betriebssystemen mit der Mono-Umgebung (eine freie, von 
Microsoft durchaus gepushte Implementierung für Linux, OSX, Android und 
Co.) wird es bloß noch schlimmer. Wenn es nämlich keine POxTs sind, 
sondern die Bibliotheken wiederum Abhängigkeiten mit sich rum schleppen 
(was die meisten Bibliotheken tun), bist du schnell in der berüchtigten 
"DLL Hell" und bei "Plug & Pray".

Daher: Mache dich mit den Eigenschaften-Seiten von Projekten und 
Solutions im Visual Studio vertraut. Es gibt dort (sorry, ich hab ne 
englische Umgebung) sogenannte "Build Events". Dich interessieren an 
dieser Stelle die "Post-build Events". Dort kannst du z.B. Dateien hin 
und her kopieren. Das jetzt in aller Ausführlichkeit zu beschreiben, 
würde ewig dauern -- und ist auch unnötig, weil wie immer 
msdn.microsoft.net ;)

Wenn du schon in den Eigenschaften deiner Projekte rumstocherst, dann 
sieh zu, dass du alle (!!) Bibliotheken, die du für deine Testläufe 
brauchst, in den Post-build Events in das Verzeichnis deiner Exe 
kopierst. Für ein späteres Deployment kannst du das dem Build-Server 
oder deinem Installer deiner Wahl (MSI, WinRAR, apt, make, ...) 
aufbürden.

So, jetzt nochmal zu den Wrappern. Auch da gehört ein bisschen Lesen 
dazu, wie man Prozeduren, Funktionen, Methoden, Eigenschaften aus 
Bibliotheken welcher Herkunft einpackt. Grundsätzlich ist aus dem 
Blickwinkel der Software-Architektur die beste Vorgehensweise 3-stufig:

Der erste Wrapper ist eine eigenständige Bibliothek, die 1:1 alle 
angebotenen Sachen (Prozeduren, Funktionen, Methoden, Eigenschaften, 
Konstanten...) in die CLR überführt (und in eine Klasse schiebt). Welche 
Sprache aus der Familie der, die es für .NET gibt, nimmst, ist deine 
Entscheidung. Als jemand, der ursprünglich aus der C-Ecke kommt, gibt es 
für mich nur C# und je nach Fall auch F#. Falls du mehr der 
Python-Mensch bist, fühlst du dich bei VisualBasic.NET vielleicht 
wohler. Aber wie gesagt, soweit ich weiß, gibt es auch eine 
Python.NET-Version. Hat mich halt nie interessiert. Es steht eben nicht 
jeder auf Rothaarige ;) Jedenfalls übersetzt du alles 1:1 von der 
ursprünglichen Bibliothek in etwas CLR-Konformes. Wie gesagt, suche im 
MSDN nach dem "DllImportAttribute", da gibt es mehr. Ein weiteres 
wichtiges Thema ist das "Marshalling". Einfach Google oder MSDN danach 
fragen und lesen.

Der zweite Wrapper ist ebenfalls eine eigenständige Bibliothek, in der 
du das Interface der ursprünglichen Bibliothek entgültig CLR-konferm 
machst. Ein Beispiel ist dein Problem "char[260] zu LPWSTR". In der CLR 
ist das einfach ein String (plus ein bisschen Konfiguration), und die 
CLR stellt die passenden Werkzeuge bereit, um das hin und her zu 
schubsen. "char[260]" ist eigentlich ein 260 Einträge großes Feld aus 
vorzeichenlosen 8-Bit-Werten, während ein LPWSTR ein "Lang-Zeiger" auf 
eine "wort-basierte Zeichenkette" ist. Eine "wort-basierte Zeichenkette" 
wiederum ist eine UTF16-Zeichenkette, wobei jedes Zeichen durch einen 
16-Bit-Wert dargestellt wird. Wie gesagt, die .NET-Laufzeit bietet für 
den ganzen Kram passende (und einfache) Werkzeuge an, die dir die 
Bit-Fummelei abnehmen. Dein Ziel in diesem Wrapper sollte sein, alle 
Parameter und Rückgabewerte so hinzubiegen, dass sie dem Typen-System 
der .NET-Laufzeit entsprechen.

Weiterhin solltest du die Signaturen der Methoden (C#-Duktus) so 
verändern, dass es keine "ref" und "out"-Parameter mehr gibt. Auch wenn 
die .NET-Laufzeit selbst solche hässlichen Entlein enthält, ist es guter 
Stil (in Sachen Lesbarkeit, Wartbarkeit, Entkopplung), sich ihrer so 
weit es geht zu entledigen (google mal nach "Open Close Principle"). 
Wann immer du in den Parametern einer Signatur einer C++-Methode etwas 
wie "*CMyClass" siehst, wird es ganz schrecklich, und du musst da mal 
ran! Wenn eine Methode mehr als einen einfachen Rückgabewert liefern 
will, schreibe eine neue Klasse und packe die Ergebnisse da rein.

Der dritte Wrapper gehört bereits zu deiner Anwendung, ist aber trotzdem 
eine eigene Bibliothek. Hier zimmerst du dir die eigentliche Bibliothek 
so zurecht, wie deine Anwendung sie benötigt. Bis zum zweiten Wrapper 
ist es okay, wenn du bei neuen Versionen der eigentlichen DLL 
nachfummeln musst. Der dritte Wrapper sollte (außer bei ganz großen 
Änderungen natürlich) davon nicht betroffen sein. Die größere 
Abhängigkeit besteht zu deiner Anwendung. Du musst also dafür sorgen, 
dass dieser Wrapper vom zweiten so weit wie möglich entkoppelt ist. Dazu 
kannst du z.B. Interfaces oder abstrakte Klassen benutzen oder am besten 
gleich einen sogenannten "Inversion of Control container".

Zum Lesen, starte mal bei 
"https://en.wikipedia.org/wiki/Castle_Project#Features"; und folge den 
Links dort.

Zusammenfassend:

- Der erste Wrapper packt die eigentliche Bibliothek ein und kümmert 
sich um das Hin und Her zwischen "managed" und "unmanaged" Code. Dazu 
gehört noch mehr, etwa die Ausnahmen-Behandlung, aber jetzt muss auch 
mal gut sein.

- Der zweite Wrapper packt den ersten ein und kümmert sich vor allem 
darum, dass das "unmanaged" komplett verschwindet. Dazu gehört wieder 
ein bisschen mehr, etwa dass du sicherstellst, dass der Code in der 
Domäne eines normalen Anwenders lauffähig ist.

- Der dritte Wrapper packt den zweiten ein und ist quasi eine "Facade" 
(googlen!) für deine Anwendung. Dort biegst du dir die eigentliche 
Bibliothek so zurecht, wie du sie brauchst, aber eben auf der Basis des 
zweiten, managed Wrappers.

- Im VS in den Build Events kopierst du dir alles so hin und her, dass 
das, was du brauchst, im selben Verzeichnis endet.

Mal ein Beispiel zur Veranschaulichung: Du hast einen speziellen 
Spiele-Controller, der mit einem Treiber geliefert wird, der sich zum 
Glück einigermaßen offenbart. Der Treiber wurde in C geschrieben und 
schiebt zig Konstanten und Funktionen hin und her, samt "Hooks", also 
hässlichen Funktionszeigern, die was tun, wenn ein Knopf auf dem 
Controller gedrückt wird.

Im ersten Wrapper übersetzt du dann bloß 1:1 alles, was der Treiber 
liefert, in managed Code mit dem .NET-Typensystem.

Im zweiten Wrapper kümmerst du dich darum, dass die Signaturen 
CLR-kompatibel werden und darum, dass du den ersten Wrapper ohne 
besondere Benutzer-Rechte aufrufen kannst. Hier kannst du auch 
Timing-Probleme und einfache Werte-Umwandlungen (im Treiber: 
"fire_button_pressed = false", in deinem Wrapper: "FireButtonPressed = 
!(fire_button_pressed)" (damit "true" rauskommt, wenn der Knopf gedrückt 
wird), oder du machst aus dem Drückeberger-Ding ein Ereignis und 
definierst dafür einen Event) vornehmen.

Im dritten Wrapper reichst du entweder den Wert von "FireButtonPressed" 
durch oder schreibst einen Ereignis-Behandler und verbindest ihn mit dem 
definierten Ereignis, sodass du noch weiter in den empfohlenen Mustern 
in der CLR-Welt bist. Wieso das sinnvoll ist? Nehmen wir an, das Drücken 
von zwei Knöpfen gleichzeitig hat eine spezielle Bedeutung für dein 
Spiel, aber der Treiber liefert nur die einzelnen Knöpfchendrück-Werte. 
Dann kannst du in diesem dritten, deiner Anwendung angepassten Wrapper 
daraus einen neuen Event "KillThemAllEvent" erstellen. Es ist dann also 
nicht mehr nötig, dass du in deiner Anwendung durch alle Schichten 
tauchst und abfragst, ob beide Knöpfe innerhalb einer gewissen 
Verzögerungszeit gedrückt wurden. Du hängst dich einfach an den Event im 
Wrapper, und wenn dieses Ereignis passiert, wird dein Code in der 
Anwendung von ganz alleine aufgerufen.

So, und was ist jetzt "eine gewisse Verzögerungszeit"? Legst du die 
selbst fest? Nein, tust du natürlich nicht. Das überlässt du der 
Konfiguration. Für eSports Bundesliga-Daddler mögen für einen Move 50 ms 
passend sein, fürs Altersheim zur Mobilisierung der älteren Herrschaften 
eher 1 s. Dafür gibt es "System.Configuration". Auch da mal lesen ;)

LG
Carsten

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.