Hallo, ich arbeite gerade an einem Projekt wo ich Daten an ein per USB angeschlossenem Gerät schicken soll bisjetzt habe ich nur über Serial ports Informationen erhlaten! scheint aber nicht mit USB ports zu funktionieren oder doch? ich arbeite mit VB .NET und benutze die Klasse SerialPort hier mein Code Private Sub Connect() Dim port As SerialPort = New SerialPort("COM3", 11500, Parity.None, 8, StopBits.One) port.Open() Dim sendWptsFromGps = New Byte() {&H10, &HA, &H2, &H7, &H0, &HED, &H10, &H3} port.Write(sendWptsFromGps, 0, sendWptsFromGps.length) 'port.Write("test") MsgBox(port.ReadTo("test").Length) AddHandler port.DataReceived, AddressOf OnDataReceived port.Close() End Sub Funktioniert aber nicht! bitte helft mir Danke
Wenn ich das richtig verstehe, versucht der OP ein USB-Gerät über die Programmfunktionen einer seriellen Schnittstelle anzusprechen. Die wird ein USB-seriell Adapter auch nicht helfen. Möchtest du ein USB-Gerät ansprechen, so müssen auch die entsprechenden Funktionen zum ansprechen dieser Schnittstelle verwendet werden.
es gibt nur einen USB Kabel der die beiden Geräte verbindet und jetzt ist meine Frage wie ich von meinem Computer aus via VB .NET mein anderes Gerät ansprechen kann das oben genannte Beispiel funktioniert nicht! scheint nur für serielle ports zu klappen! ich suche halt eine Lösung für mein USB ohne einen Adapter kaufen zu müssen ist denn sowas möglich? wenn ja welche funktionen genau soll ich denn benutzen?
Für ein nicht von dir selbst erstelltes USB-Gerät dürfste das ansprechen schwierig, wenn nicht sogar unmöglich werden. Denn dazu müsstest du ja einmal wissen, wie du mit diesem Gerät kommunizieren musst. Bei einem seriellen Gerät kann man das Protokoll mitschneiden (hab ich mal bei einem Multimeter gemacht, da war die Datenmenge die hin- und hergeschoben wurde aber auch gering). Ob das bei USB auch so einfach geht weiß ich nicht.
Schreib doch evtl. mal, um was für ein konkretes Gerät es sich dabei handelt, dann kann man dir sicher besser helfen.
es geht um dieses Gerät Garmin Streetpilot 2720 es ist ein Navigationsgerät und es geht darum Routen oder Wegpunkte via USB zu übertragen Mehr Informationen auf dieser Seite: http://www.garmin.de/Produktbeschreibungen/StreetPilot2720.php Danke
Meldet sich das Teil nicht eh als virtueller SerialPort am System? Dann kannst ganz einfach die SerialPort Methoden nutzen (mit den entsprechenden Einstellungen halt - die solltest im Handbuch finden) ... Ich tippe mal auf 9600Baud 8N1 (ist aber nur geraten)
ja so habe ich auch zuerst gedacht und das ist mein jetziger code Private Sub Connect() Dim port As SerialPort = New SerialPort("COM3", 11500, Parity.None, 8, StopBits.One) port.Open() Dim sendWptsFromGps = New Byte() {&H10, &HA, &H2, &H7, &H0, &HED, &H10, &H3} port.Write(sendWptsFromGps, 0, sendWptsFromGps.length) 'port.Write("test") MsgBox(port.ReadTo("test").Length) AddHandler port.DataReceived, AddressOf OnDataReceived port.Close() End Sub scheint aber nicht zu funktionieren wie weiß ich dass mein gerät sich als virtueller serialport anmeldet? bzw. woher soll ich wissen dass das gerät die daten empfangen bzw. wie bringe ich das gerät zu antworten?
Schau in die Systemsteuerung -> Verwaltung -> Computerverwaltung -> Geräte Manager -> Was ist dort unter "Anschlüsse" aufgelistet. Bzw. siehst du irgendwo sonst dein Device ? Was soll das Gerät denn überhaupt senden? GPS Geräte senden deren aktuellen Koordinaten normalerweise automatisch ohne dass man einen Transfer anstossen muss. Wenns um andere Daten geht, wirst du erst einen bestimmten Befehl (den du hoffentlich im Handbuch findest) hinsenden müssen.
also mein gerät kann ich schon finden als anhang schicke ich das bild mehr Informationen über mein Gerät findest du auf dieser Seite http://vancouver-webpages.com/peter/grmnprot.html das gerät soll routen, wegpunkte, tracks, datum usw ... senden können irgendeine Idee?
Gut ... sieht so aus aus würds komplizierter werden. Garmin stellt jedoch auf deren Webseite ein SDK zur Verfügung, welches auch einen kleinen C Code beinhaltet. Was ich gesehen habe läuft es darauf hinaus dass du mittels der Winapi - Funktionen WriteFile und ReadFile auf das Device schreibst bzw. davon liest. Ich habe sowas zwar mal gemacht (ebenfalls mit einem USB Device) aber das ist schon eine Weile her und war eine recht mühsame Sache. Nichts desto trotz - machbar ist es :) Das SDK findest du hier: http://www.garmin.com/support/commProtocol.html
ja ich habe schon das SDK nur es ist in C geschrieben worden und ich bin schon mit meinem lateien am ende ich kann es einfach in VB .NET umschreiben! versucht habe ich es auf jeden fall deswegen frage ich dich ob du mir irgendwie helfen kannst bitte :)
Ohne das Gerät zum Probieren, wird dir hier keiner so wirklich weiterhelfen können. Den Code den ich damals geschrieben habe, darf ich nicht rausrücken, habe eben mit meiner Firma gepsrochen. Daran werde ich mich auch halten. Im Prinzip läufts auf folgendes hinaus: Du rufst die entsprechenden Funktionen der WINAPI (SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, SetupDiGetDeviceInterfaceDetail) auf. Die Strukturen und deren Parameter sind in der MSDN aufgelistet. Der Aufruf CreateFile liefert dir dann ein Handle auf dein Device. Auf dieses Handle kannst du dann mittels ReadFile und WriteFile schreiben bzw. davon lesen. Wenn du dir das C - File ansiehst, wirst du merken dass genau das hier passiert. Erst wird Initialize aufgerufen, das dir am Ende das Handle für genau dein Device liefert (nach Aufruf der entsprechenden Funktionen). Dann liest bzw. schreibst du Packets mittels ReadFile/WriteFile. Was du jedoch machen müssen wirst, ist folgendes: * Die Strukturen für die Winapi Calls in VB.NET nachbauen (LayoutKind). Der Methodenaufruf erwartet meist ein paar Strukturen. * Dich damit beschäftigen, wie du in VB.NET Winapi Funktionen aufrufst Das Ganze wird dich schon einige Tage kosten.
unter VB6 konnte man das MSComm-Objekt benutzen. (Hab lang nicht mehr mit VB gearbeitet...)
das Ding meldet sich nicht als Virtueller COM Port, daher kannst das MSComm Objekt vergessen.
Hallo, ich war also schon auf dem richtigen Weg! nur ich habe ein Problem mit SetupDiEnumDeviceInterfaces und SetupDiGetDeviceInterfaceDetail beide liefern immer false CreateFile dagegen liefert mir einen Handle aber WriteFile funktioniert anscheinend nicht DeviceIoControl klappt auch nicht so richtig was ist denn falsch mit meinem code? (methode init() und sendpacket()) hier mein code: //Deklarationen <DllImport("setupapi.dll", CharSet:=CharSet.Auto)> _ Public Function SetupDiEnumDeviceInfo( _ ByVal DeviceInfoSet As Long, _ ByVal MemberIndex As Long, _ ByRef DeviceInfoData As SP_DEVINFO_DATA) As Long End Function <DllImport("setupapi.dll", CharSet:=CharSet.Auto)> _ Function SetupDiEnumDeviceInterfaces( _ ByVal DeviceInfoSet As Integer, _ ByVal DeviceInfoData As Integer, _ ByRef InterfaceClassGuid As System.Guid, _ ByVal MemberIndex As Integer, _ <MarshalAs(UnmanagedType.Struct)> ByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA) As Boolean End Function Function DeviceIoControl(ByVal hDevice As IntPtr, ByVal dwIoControlCode As UInt32, _ ByVal lpInBuffer As IntPtr, ByVal nInBufferSize As UInt32, _ ByVal lpOutBuffer As IntPtr, _ ByVal nOutBufferSize As UInt32, _ ByRef lpBytesReturned As UInt32, _ ByVal lpOverlapped As IntPtr) As Boolean End Function <DllImport("setupapi.dll", CharSet:=CharSet.Auto)> _ Public Function SetupDiGetClassDevs(ByRef ClassGuid As System.Guid, _ ByVal Enumerator As String, _ ByVal hwndParent As Integer, _ ByVal flags As Integer) As IntPtr End Function <DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _ Function WriteFile(ByVal hFile As Integer, _ ByVal Buffer As Byte(), _ ByVal nNumberOfBytesToWrite As Integer, _ ByRef lpNumberOfBytesWritten As Integer, _ ByVal lpOverlapped As Integer) As Integer End Function <DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _ Function CreateFile(ByVal lpFileName As String, _ ByVal dwDesiredAccess As Integer, _ ByVal dwShareMode As Integer, _ ByRef lpSecurityAttributes As SECURITY_ATTRIBUTES, _ ByVal dwCreationDisposition As Integer, _ ByVal dwFlagsAndAttributes As Integer, _ ByVal hTemplateFile As Integer) As Integer End Function <DllImport("setupapi.dll", CharSet:=CharSet.Auto)> _ Function SetupDiGetDeviceInterfaceDetail(ByVal DeviceInfoSet As IntPtr, _ ByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA, _ ByVal DeviceInterfaceDetailData As IntPtr, _ ByVal DeviceInterfaceDetailDataSize As Integer, _ ByRef RequiredSize As Integer, ByVal DeviceInfoData As IntPtr) As Boolean End Function Public Sub init() Dim GUID_DEVINTERFACE_GRMNUSB As System.Guid Dim DIDI As SP_DEVICE_INTERFACE_DATA Dim DIDC As SP_DEVINFO_DATA DIDI.cbSize = Len(DIDI) DIDC.cbSize = Len(DIDC) Dim size As IntPtr = 0 GUID_DEVINTERFACE_GRMNUSB = New System.Guid("{0x2C9C45C2, 0x8E7D, 0x4C08, {0xA1, 0x2D, 0x81, 0x6B, 0xBA, 0xE7, 0x22, 0xC0}}") Dim theDevInfo As Long Dim BufferLen As Integer //klappt theDevInfo = SetupDiGetClassDevs(GUID_DEVINTERFACE_GRMNUSB, vbNullString, 0, DIGCF_DEVICEINTERFACE Or DIGCF_PRESENT) //klappt nicht If Not SetupDiEnumDeviceInterfaces(theDevInfo, 0, GUID_DEVINTERFACE_GRMNUSB, BufferLen, DIDI) Then Handle = 0 Return End If //klappt nicht SetupDiGetDeviceInterfaceDetail(theDevInfo, DIDI, IntPtr.Zero, 0, BufferLen, IntPtr.Zero) SetupDiGetDeviceInterfaceDetail(theDevInfo, DIDI, 0&, 0&, BufferLen, 0&) Dim Security As SECURITY_ATTRIBUTES Security.lpSecurityDescriptor = 0 Security.bInheritHandle = CInt(True) Security.nLength = Len(Security) //Handle klappt Handle = CreateFile("\\?\usb#vid_091e&pid_0003#5&295c686&0&2#{2c9c45c2-8e7d-4c08- a12d-816bbae722c0}", 0, FILE_SHARE_READ Or FILE_SHARE_WRITE, Security, OPEN_EXISTING, 0, 0) 'Get the USB packet size, which we need for sending packets 'Dim tes As Api.OVERLAPPED Dim IOCTL_USB_PACKET_SIZE As IntPtr IOCTL_USB_PACKET_SIZE = CTL_CODE(FILE_DEVICE_UNKNOWN, &H851, METHOD_BUFFERED, FILE_ANY_ACCESS) //klappt nicht DeviceIoControl(Handle, IOCTL_USB_PACKET_SIZE, vbNull, 0, 1024, 28, BufferLen, 0) Dim testB5 = New Byte() {&H0, &HFD, &H5, &H10, &H3} SendPacket(testB10) CloseHandle(Handle) End Sub Public Sub SendPacket(ByVal aPacket As Byte()) Dim theBytesReturned As Integer PurgeComm(Handle, PURGE_RXCLEAR Or PURGE_TXCLEAR) 'löschen der Puffer WriteFile(Handle, aPacket, aPacket.Length, theBytesReturned, 0) End Sub
Also wenn du ein false zurückbekommst (oder einen fehler) solltest du mittels der API - Funktion GetLastError den Grund für den Fehler abfragen können. Evtl. solltest das mal versuchen - vielleicht steht was drin, das dir weiterhelfen könnte. Irgendeine der Setup - Funktionen musst du zweimal aufrufen. Das ist auch in der MSDN beschrieben. Das erste mal mit einem Parameter (ich glaub null), das zweite mal dann mit dem Wert, den du beim ersten Aufruf in einem Parameter mitbekommen hast. Konkret einen Fehler sehe ich jedoch nicht - dafür reicht mein (Detail-)Wissen nicht aus. Wie hast du die entsprechenden Strukturen definiert? Sind diese korrekt?
ja die strukturen sind anscheinend korrekt weil die keine PInvoke Funktion Fehlermeldung bekomme das mit dem 2 mal aufrufen trifft auf die methode SetupDiGetDeviceInterfaceDetail zu! habe ich auch ungefähr gemacht und na ja scheint nicht zu gehen ist es eigentlich relevant dass die oberen funktionen nicht klappen solange ich einen handle zu meinem device bekomme? oder warum brauche ich sie eigentlich?
Wenn du dein Handle fix fertig schon kennst, sollten die Setup - Funktionen egal sein. Die Setup Aufrufe brauchst du ja um den Device - String zu bekommen. Was mir jedoch aufgefallen ist:
1 | Handle = |
2 | CreateFile("\\?\usb#vid_091e&pid_0003#5&295c686&0&2#{2c9c45c2-8e7d-4c08-a12d-816bbae722c0}", |
3 | 0, FILE_SHARE_READ Or FILE_SHARE_WRITE, Security, OPEN_EXISTING, 0, 0) |
a) Passt VID und PID zu deinem Device? (solltest du im Gerätemanager irgendwo sehen können) b) Wird der String schon korrekt escaped? In C# würde ich bei CreateFile(@"..."); noch das @ hinbasteln ... ansonsten könnten die Backslash falsch interpretiert werden. c) Was ist bei dir security? Im Aufruf von dem SDK steht dort 0. d) FileAttributeNormal ist 0 ? Es könnte sein, dass hier was nicht stimmt. Der Aufruf zwar funktioniert, aber irgendwelche Parameter falsch sind und dass deshalb ein späteres Read/Write nicht funktioniert. Ein Handle bekommst du ja wie du sagst.
ja VID und PID sind auf jeden Fall richtig und wie gesagt da ich Handle habe muß der String "\\.\usb..." korrekt sein oder? Dim Security As SECURITY_ATTRIBUTES Security.lpSecurityDescriptor = 0 Security.bInheritHandle = CInt(True) Security.nLength = Len(Security) hier die struktur: Public Structure SECURITY_ATTRIBUTES Dim nLength As Integer Dim lpSecurityDescriptor As Integer Dim bInheritHandle As Integer End Structure
259 steht für "No more items". In der Winerror.h (oder so ähnlich) sollten alle Fehlercodes aufgelistet sein. Wann rufst du GetLastError denn auf? Bei der enumeration? Evtl. mal mittels SetLastError vor dem Methodenaufruf den Fehlercode zurücksetzen. Nach CreateFile müsste jedoch der aktuelle Fehlercode 0 sein ..
vorher habe ich getlasterror() gleich nach SetupDiEnumDeviceInterfaces gesetzt was 259 geliefert hat nach CreateFile ist getlasterror()=0 nach WriteFile ist getlasterror()=5 woran liegt es denn?
5 steht für "ERROR_ACCESS_DENIED" getlasterror liefert immer den fehler der zuletzt aufgerufenen funktion. Also bei der Enumeration ist es kein Fehler, ist ja auch korrekt (No more items). Nach CreteFile ist es 0, d.h. das Handle wurde erfolgreich zurückgeliefert. WriteFile meint "Error, Access denied". Das lässt zwei Schlüsse zu: a) Du darfst nicht auf das Handle schreiben, was mich aber wundern würde, schliesslich wird das ja auch in deren SDK so beschrieben. b) Du gibst CreateFile die falschen Permissions. File_Share_Read bzw. File_share_Write sind hoffentlich korrekt? Im Notfall in der MSDN wegen deren Werte nachsehen. Ausserdem stört mich immer noch das "Security". Wirf dort mal einfach eine 0 hin. So wie es auch in der Beispiel Applikation des SDK gemacht wird.
hier ist also createfile und anstatt die ganzen konstanten habe ich die richtigen werte geschrieben Handle = Api.CreateFile("\\?\usb#vid_091e&pid_0003#5&295c686&0&2#{2c9c45c2-8e7d-4 c08-a12d-816bbae722c0}", 0, &H1 Or &H2, 0, 3, 0, 0) security habe ich weggemacht und mit 0 ersetzt getlasterror ist immer noch 0 bei CreateFile und 5 bei WriteFile klappt wohl immer noch nicht oder was mache ich falsch?
Du programmierst doch in VB.Net? Da gabs mal einen ct-Artikel zu VB.Net Sytemprogrammierung mit WinApi Aufrufen. Wesentliches Fazit: Wenn du da eine Variable erstellst ist das eine Variable im verwalteten Bereich die jederzeit verschoben werden kann. Eine solche Variable kannst du nicht einfach benutzen. Wie es richtig geht siehe ct-Artikel 2.Public Sub SendPacket(ByVal aPacket As Byte()) Dim theBytesReturned As Integer .. WriteFile(Handle, aPacket, aPacket.Length, theBytesReturned,0) End Sub aPacket,theBytesReturned müssen Zeiger sein In deinem obigen Code sind noch ein paar solche Sachen, wo ich mir nicht sicher bin ob VB die Umwandlung richtig macht.
hmmm .. gehen wirs mal gemeinsam durch: HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ); * lpFileName sagen wir mal dass das passt. * dwDesiredAccess: -- das SDK sagt Generic_Read und Generic_Write -- ich hab die als: -- Private Const GENERIC_READ As Integer = &H80000000 -- Private Const GENERIC_WRITE As Integer = &H40000000 -- Wie kommst du auf H1 / H2 ? * dwShareMode: SDK sagt 0, wir also auch * lpSecurityAttributes: SDK sagt NULL, du sagst 3? Wieso das? * dwCreationDisposition: OPEN_EXISTING (das müsste Private Const OPEN_EXISTING As Long = &H3& sein) * dwFlagsAndAttributes: FILE_ATTRIBUTE_NORMAL (das müsste Const FILE_ATTRIBUTE_NORMAL = &H80 sein) * hTemplateFile: SDK sagt NULL, du sagst 0 - warum ? und mach sicherheitshalber noch einen Verbatim String aus dem "\\?\..." (@ davor). Kanns sein dass du Share und Access vertauscht hast? Der zweite Parameter sollte DesiredAccess sein .. der dritte ShareMode
jetzt seh ichs erst ... Bei dir ist ja für den Access kein einziges Recht erlaubt (0). Das kann nicht funktionieren. So darfst du auf das Handle weder lesen noch schreiben. Deshalb erhälst du bei WriteFile auch einen Fehler. Änder das mal ...
was mache ich denn jetzt wohl wenn VB .NET keine zeiger unterstützt?
ich muß gestehen den alten code hatte ich einfach übernommen ohne großartig zu verstehen ich habe also den code an deinen anweisungen angepasst und so sieht es jetzt aus: Const GENERIC_READ As Integer = &H80000000 Const GENERIC_WRITE As Integer = &H40000000 Const OPEN_EXISTING As Long = &H3& Const FILE_ATTRIBUTE_NORMAL = &H80 Handle = Api.CreateFile("\\?\usb#vid_091e&pid_0003#5&295c686&0&2#{2c9c45c2-8e7d-4 c08-a12d-816bbae722c0}", GENERIC_READ Or GENERIC_WRITE, 0, vbNull, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, vbNull) wobei die konstanten genauso deklariert sind wie oben bekomme dann eine AccessViolationException mit folgender Fehlermeldung: "Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist." was stimmt denn nicht?
die Exception is neu in .NET 2.0 Probier mal: unsafe { Api.CreateFile("\\?\...", ....); } Ausserdem spreche ich leider kein Vb.NET sondern nur C# ... aber eines davon ist falsch: Const OPEN_EXISTING As Long = &H3& Const FILE_ATTRIBUTE_NORMAL = &H80 ich tippe mal dass es Const OPEN_EXISTING As Long = &H3 heissen muss ... aber vielleicht ist das & auch irgendwas VB-spezifisches...
@Wolfram: Du hast schon recht. Aber beim CreateFile Statement welches wir hier haben, ist das ja nicht der Fall. Wenn wir folgendes hätten, bräuchten wir das fixed keyword: House house = new House(); fixed ( long* pStrasse = &house.strasse )
"\\?\usb#vid_091e&pid_0003#5&295c686&0&2#{2c9c45c2-8e7d-4c08-a12d-816bba e722c0}" ist ein String im verwalteten Speicher und noch dazu ein VB-String! WinAPI Calls wollen Nullterminierte Strings! Schau dir mal http://www.vbarchiv.net/workshop/workshop02.php an.
es gibt kein unsafe in VB .NET außerdem klappt &H3 anstatt &H3& auch nicht ich weiß echt nicht mehr weiter liegt es vielleicht an VB? soll ich mit C# arbeiten?
Sorry, hast natürlich recht .. den String habe ich wohl übersehen. Beim Rest sollts jedoch egal sein. Daher würde ich auch den Weg vorziehen, mir das entsprechende Device über die passenden Winapi Funktionen zu suchen. Da hat man dann auch gleich den korrekten String .. und das auch Null-terminiert.
ja aber das blöde ist dass weder SetupDiEnumDeviceInterfaces noch SetupDiGetDeviceInterfaceDetail klappen oder habe ich das falsch deklariert?
könnte evtl. an dem von wolfram genannten problem mit dem string liegen. Wenn er nett ist, hilft er dir vielleicht weiter. Ich jedoch steige an dieser Stelle aus. Das übersteigt dann mein Wissen. Erst recht da ich in VB überhaupt keine Erfahrung habe. Sagen wirs so. Wenn dus in VB.NET nicht schaffst, dann wahrscheinlich auch in C# nicht.
ich habe nur einen teil des Codes von dir. Wie soll ich da sagen können, ob es an der deklaration liegt. Schon möglich. Du solltest halt einfach mal ganz vorne anfangen. Einen Schritt nach dem anderen machen, dann führt auch das Eine zum Anderen. Laufend mittels GetLastError die Fehler abfragen und und und ..
weiter oben kann du die ganzen deklarationen anschauen deswegen frage ich ja ob es irgendwie falsch ist! ich bekomme ja keine Pinvoke Fehlermeldung
@Thomas: Also ich bin zwar nett, aber da geht es mir ähnlich wie Dir,für VB6 hätte ich es noch gekonnt. Vielleicht solltest du nicht so schnell aussteigen, die freie .Net Entwicklungsumgebung SharpDevelop hat einen Übersetzer von C# auf VB und umgekehrt. Für normalen Programmcode funktioniert es sehr gut. Mit DLL Importen habe ich's aber noch nicht probiert.
kannst mir das gesamte projekt mal mailen ? Bis zu dem Punkt mit dem CreateFile kann ichs mir mal ansehen ... Mail: somnaticuser@hotmail.com
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.