Connected - Internetverbindung überprüfen (ohne Verzögerung mit Serverabfrage) v.1.1

    • Dll

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    • Connected - Internetverbindung überprüfen (ohne Verzögerung mit Serverabfrage) v.1.1

      Guten Tag, community,

      vor etwa 2 Wochen hatte ich mein erstes Rendezvous mit C++ und hiermit will ich euch mein erstes publizierbares Ergebnis nicht vorenthalten.

      An dieser Stelle möchte ich mich auch bei den Mitgliedern des GM-Teams im Chat hier bedanken die mir bei einigen Anfänger-Fragen beigestanden haben :)

      So, nun zur DLL.
      Als ich mal ein einem Spiel mit online-Anbidnung gearbeitet habe, ist mir aufgefallen dass, wenn man dafür Funktionen aus anderen DLLs benutzt die z.B. eine Seite abrufen, die time-out Zeit bei fehlender Verbindung relativ lang ist, d.h. wenn ein Spiel Internetverbindung automatisch benutzt, führt das zu einem kurzzeitigen Blockieren.
      Es gibt schon ein paar DLLs die behaupten einem zu sagen ob Inet Verbindung besteht oder nicht. Diese geben aber nur an ob der PC per Lan an ein Netzwerk angeschlossen ist und nicht ob tatsächlich eine Internetverbindung besteht.

      Dieses Problem soll nun diese DLL lösen.

      Im Grunde benutzt sie dafür die PING Funktion des Systems. Es wird jedoch nicht bei Aufruf einer Funktion erst 'gepingt' und erst dann das Ergebnis zurückgegeben (was ebenfalls ein Blockieren des Spiels zur Folge hätte), sondern dies geschieht im Hintergrund in regelmäßigen Zeitabständen (die bestimmt werden können). Die Funktion zum Abrufen der Verbindung ist somit sehr schnell und gibt das Ergebnis der letzten Verbindungsversuchs zurück.

      Die Benutzung ist relativ einfach. Das Beispielprojekt sowie die Komentare/Namen der Funktionionen sollten alles erklären.

      Zusatzfunktion: Zusätzlich kann die DLL eine Komandozeile versteckt aufrufen! D.h. ohne dass dieses typische, schwarze cmd-Fenster aufpoppt. Dabei kann gewählt werden ob das Spiel warten soll bis die Ausführung beendet wurde oder ob auch dies im Hintergrund geschehen soll.


      Hiermit veröffentliche ich auch den Quellcode dieser DLL.
      Spoiler anzeigen

      Ich habe ihn (allerdings auf Englisch) vollständig kommentiert. Eventuell kann jemand etwas daraus lernen.. auch wenn ich Anfänger dieser Sprache bin..
      Würde mich auch freuen wenn jemand zum Beispiel Memoryleaks, schlecht Programierte Dinge oder Bugs in der DLL findet und mitteilt!
      Hier zudem zwei Tutorials zum Thema DLLs mit C++: gmc.yoyogames.com/index.php?showtopic=458187 und gmc.yoyogames.com/index.php?showtopic=405812

      Zur Sprache selbst kann man durch Google sehr viel Information (viel, viel mehr als zu GML) finden. Etwas wirklich herrausragendes konnte ich aber leider nicht ausfindig machen und mir wurde auch empfohlen eher eins der bekannteren C++ Bücher (ja, Bücher gibt es anscheinend noch :D ) zu kaufen wenn man es wirklich ernst meint (grade billig sind die nicht, dafür gibt es aber gratis Umgebungen/Compiler für C++ wie das Visual Studio Express oder Code::Blocks).

      Hier der Code. Einen header benötigt das Projekt nicht.

      C-Quellcode

      1. //-//
      2. #include <windows.h>
      3. #include <process.h>
      4. #include <string>
      5. using namespace std;
      6. //-//
      7. // those lines include other files and name spaces which enable a variety of functions
      8. #define DLLEXPORT extern "C" __declspec(dllexport) // an important line which simplifies your work when writting a DLL for GM
      9. // whenever a fucntion shall be acessible from GM, write DLLEXPORT before it from now on
      10. bool check;
      11. volatile bool connected;
      12. float check_interval;
      13. char* cmdln;
      14. char* cmdlne;
      15. PROCESS_INFORMATION pI_global;
      16. // initializes a bunch of _global_variables of diffrent data types.
      17. // the word "volatile" enables in this case secure editing of the variable even from a diffrent process
      18. void showError(LPCSTR capt)
      19. {
      20. LPTSTR Error = 0;
      21. FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
      22. NULL,
      23. GetLastError(),
      24. 0,
      25. (LPTSTR)&Error,
      26. 0,
      27. NULL);
      28. MessageBox( 0, Error,capt, MB_OK );
      29. }
      30. BOOL __stdcall DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved) // This function is _automatically_ called by the system when the DLL is laoded into GM or when it's freed from it
      31. {
      32. switch (dwReason) // with this variable you can check what exactly happened
      33. {
      34. case DLL_PROCESS_ATTACH: // start
      35. {
      36. check = false;
      37. connected = false;
      38. check_interval = 0;
      39. cmdln = "";
      40. cmdlne = "";
      41. // Gives all those variables a start-value
      42. } break;
      43. case DLL_PROCESS_DETACH: // end
      44. {
      45. delete[] cmdln; // deletes this variable since it was created with "new" (that always requires a "manual" delete)
      46. delete[] cmdlne;
      47. } break;
      48. }
      49. return TRUE; // return whetehr initialisation has been sucessfull
      50. }
      51. void backgroundProcess(void*);
      52. // This line just prepares the function backgroundProcess.
      53. // This needs to happen to be free in what order we can place the functions in the DLL.
      54. // Delete this line and you will notice that you cannot compile the next function because
      55. // it does not see that there is a function "backgroundProcess" below
      56. DLLEXPORT double Start(char* input, double time) { // This initializes the first function for GM. The data type for passing a string has therefore to be a char* while numbers from GM need a double.
      57. // Don't forget to write DLLEXPORT !
      58. string strInput(input); // turns the content of the passed string into the C++ string-variable called strInput
      59. if (strInput == "")
      60. {
      61. cmdln = "ping www.google.com -n 2"; // pinging a standart, hopefully always online site
      62. }
      63. else
      64. {
      65. strInput = "ping "+strInput+" -n 2"; // pinging the given adress
      66. cmdln = new char[strInput.length()]; // assigns a new array of char(acters) to the existing pointer called cmdln
      67. strcpy_s(cmdln,sizeof(strInput),strInput.c_str()); // copies the string into the this array
      68. }
      69. check = true;
      70. check_interval = (float)(time*1000); // sets the time between two ping-checks.
      71. _beginthread( backgroundProcess, 0, 0); // Runs the function "backgroundProcess" in a new Thread (!)
      72. // That means it will run parallel to the current thread which is also the thread of Game Maker.
      73. // Therefore everything called there is not blocking the game.
      74. Sleep(10); // the system apperently needs time to initialize the secondary process before the calling "event" ends - not really sure whether this is needed.
      75. return 1;
      76. }
      77. void backgroundProcess(void*) // this is the prozess which is called in the background in another Thread
      78. {
      79. while(check) // this causes this thread to never end until check is == false
      80. {
      81. //-//
      82. STARTUPINFO startInf;
      83. ZeroMemory( &startInf, sizeof(startInf) );
      84. startInf.cb = sizeof(startInf);
      85. PROCESS_INFORMATION procInf;
      86. ZeroMemory( &procInf, sizeof(procInf) );
      87. //-//
      88. //Initializes two local, required data structures and fills them with 0's but without information (no information).
      89. if (!CreateProcess(NULL,cmdln,NULL,NULL,false,NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,NULL,NULL,&startInf,&procInf))
      90. MessageBox(0, "The Ping failed for some reason. Is the adress correct?", cmdln , MB_OK);
      91. // The first of thsoe two lines eventually starts the Process. That process only contains the
      92. // command-line code from "cmdln" which we aranged before.
      93. // The CREATE_NO_WINDOW attribute prevents the window from showing up. Remove this (including the "| " part)
      94. // and the usual cmd-window will show up.
      95. // Waits until the created process finishes (so that emans until the pings have completed
      96. WaitForSingleObject( procInf.hProcess, INFINITE );
      97. // Because we are in a separate thread, this waiting time does not block the progrmm in Game Maker.
      98. DWORD result; // creates a special variable
      99. if (!GetExitCodeProcess(procInf.hProcess, &result )) // the Ping-Function of Windows returns a special data type which is set here into the variable "result". this function also returns false if something went wrong before.
      100. MessageBox(0, "Retrieving a result from the ping failed for some reason.", "" , MB_OK);
      101. else
      102. connected = (result == 0);
      103. // For Ping, a result of "0" means that everything worked fine and that the pingw as delivered and recieved right. Everythinge else is a failure.
      104. CloseHandle(procInf.hProcess );
      105. CloseHandle(procInf.hThread ); // Closes the process
      106. Sleep(check_interval); // sleeps so long until it should ping again.
      107. }
      108. }
      109. DLLEXPORT double getStatus() { // initializes the function for recieving information.
      110. return connected;
      111. }
      112. DLLEXPORT double Stop() { // initializes the Stopping function.
      113. if (check == true)
      114. {
      115. check = false; // this will stop the background Thread.
      116. delete[] cmdln; // this will free the space reserved for cmdln
      117. return 1;
      118. }
      119. return 0;
      120. }
      121. void endProcess(void*);
      122. // Again a fucntion has to be initialised before it gets it's own code (down there). So you can mentain a logical order of your functions.
      123. DLLEXPORT double runCmd(char* input, double wait) { // Calls the run function. It's main built is the same as below.
      124. string strInput(input); // turns the content of the passed string into the C++ string-variable called strInput
      125. strInput = "cmd.exe /c "+strInput; // adds that string to the beginning sicne the line you provided has to be give to the cmd.exe
      126. cmdlne = new char[strInput.length()];
      127. strcpy(cmdlne,strInput.c_str()); // copies the string into this array
      128. STARTUPINFO startInf;
      129. ZeroMemory( &startInf, sizeof(startInf) );
      130. startInf.cb = sizeof(startInf);
      131. PROCESS_INFORMATION procInf;
      132. ZeroMemory( &procInf, sizeof(procInf) );
      133. if (!CreateProcess(NULL,cmdlne,NULL,NULL,false,NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,NULL,NULL,&startInf,&procInf))
      134. MessageBox(0, "The command line which was meant to be executed, failed!", input , MB_OK);
      135. if (wait == 1)
      136. {
      137. WaitForSingleObject( procInf.hProcess, INFINITE );
      138. bool ret = false;
      139. DWORD result;
      140. if (GetExitCodeProcess(procInf.hProcess, &result))
      141. ret = true;
      142. CloseHandle(procInf.hProcess );
      143. CloseHandle(procInf.hThread ); // Closes the process
      144. if (ret) return (double)(int)result; // don't ask me why the heck this sort of conversion is needed.
      145. // It doesn't seem to be able to convert the result directly
      146. // into the required format (double) to be returned
      147. }
      148. else
      149. {
      150. pI_global = procInf; // this variable is global
      151. _beginthread( endProcess, 0 , 0); // runs the function down there in another background thread.
      152. // This secures that the process is closed properly.
      153. Sleep(10);
      154. }
      155. return 1; // return 1 to show that calling has been sucessfull
      156. }
      157. void endProcess(void*)
      158. {
      159. WaitForSingleObject( pI_global.hProcess, INFINITE ); // waits for the process to end
      160. CloseHandle( pI_global.hProcess );
      161. CloseHandle( pI_global.hThread ); // closes it
      162. }
      Alles anzeigen

      Ihr dürft mit dem Code machen was ihr wollt.

      EDIT: Die in den folgenden Posts erwähnten Fehler wurden behoben.


      Hoffe mal dass dies jemandem nützlich ist.

      EDIT: 2 UPDATE: Die Funktion um die Komandozeile aufzurufen wurde korigiert. Jetzt ist es tatsächlich möglich CMD-Zeilen auszufrühren, nicht nur Programme mit Argumenten zu starten!
      Basierend darauf habe ich ein Skript geschrieben namens directory_delete um GM's fehlende Funktion auszugleichen. Aus Sicherheitsgründen kann man mit dem Skript aber nur Unterordner eines Spiels löschen, also nicht für Schadsoftware geignet.
      Dateien
      • Connected 1.1.zip

        (23,9 kB, 451 mal heruntergeladen, zuletzt: )

      Willst du auf diese Drachen und -eier klicken?
      Sie werden sich freuen ;)

      Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von DragonGamer ()

    • Also, ich will jetzt deine Arbeit nicht verunglimpfen, aber ich hab schon beim ersten Blick ein paar Bugs gefunden. Bei C/C++ Neulingen sind vor allem memory leaks eine große Gefahr:

      Quellcode

      1. delete &strInput;


      Das ist nicht nötig, da string eine Klasse ist und das Objekt wird auf dem Stack erstellt. D.h. der Destruktor wird automatisch aufgerufen, außerdem wird delete nur für Objekte auf dem Heap gebraucht.

      Quellcode

      1. delete cmdln;


      Hier brauchst du delete[], da es sich um ein Array handelt.

      Noch ein Tipp: Es lohnt sich sehr, eine DllMain Funktion hinzuzufügen, da diese automatisch gecalled wird, wenn du die DLL im GM lädst oder beendest.

      © 2008 by Teamgrill Productions
    • Erstmal vielen Dank für das Feedback!

      Soul Reaver schrieb:

      Noch ein Tipp: Es lohnt sich sehr, eine DllMain Funktion hinzuzufügen, da diese automatisch gecalled wird, wenn du die DLL im GM lädst oder beendest.
      Das hatte ich zunächst versucht, es aber dann gelassen als das example aus einem der Tuts nicht genau funktioneirte und ich dachte man braucht das nicht. Die Variablen werden ja trotzdem initialisiert.
      Und ich weiss dass cmdln eigentlich ein delete braucht, allerdings ist diese Variable ja solange in Benutzung wie die DLL geladen bleibt. Wenn die DLL nun aus dem GM mittels external_free gelöscht wird, wird auch der gesamte assozieierte Speicher freigegeben, oder nicht?

      Was das andere delete angeht hast du natürlich Recht, werde ich korigieren auch wenn da wohl kein Schaden entsteht, nehme ich an. Oder wird daraus auch ein memory leak?

      Willst du auf diese Drachen und -eier klicken?
      Sie werden sich freuen ;)
    • DragonGamer schrieb:


      Und ich weiss dass cmdln eigentlich ein delete braucht, allerdings ist diese Variable ja solange in Benutzung wie die DLL geladen bleibt. Wenn die DLL nun aus dem GM mittels external_free gelöscht wird, wird auch der gesamte assozieierte Speicher freigegeben, oder nicht?

      Nein wird er nicht, generell gilt: Alle Objekte die du auf dem Heap erstellst (new, new[], malloc, ...) musst du selber auch wieder freigeben. Außerdem meinte ich, dass du ein delete[] für Arrays brauchst und kein einfaches delete.

      DragonGamer schrieb:


      Was das andere delete angeht hast du natürlich Recht, werde ich korigieren auch wenn da wohl kein Schaden entsteht, nehme ich an. Oder wird daraus auch ein memory leak?

      Schaden? Kann ich nicht sagen. Es kann natürlich zu ungewöhnlichen Problemen kommen, wenn du etwas aus dem Stack mittels delete löscht.

      © 2008 by Teamgrill Productions
    • Ahh, okey, danke für die Belehrungen!
      Sonst noch irgendwas auffälliges?


      EDIT: So, korigiert!

      Kurze Frage: Kann man irgendwo den Speicherverbrauch einer DLL feststellen?

      Willst du auf diese Drachen und -eier klicken?
      Sie werden sich freuen ;)

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von DragonGamer ()

    • Du kannst ein Programm schreiben, das alle Funktionen der DLL einmal durchtestet, bzw. den Lebenszyklus deiner DLL in einem Programm simuliert und am Ende auf eine Nutzereingabe wartet (also pausiert), dann kannst du nachschauen, wieviel Speicher das Programm braucht (Taskmanager). Und zum Vergleich gibst du dem Programm dann noch eine Möglichkeit mit gestartet zu werden, ohne die DLL zu nutzen und kannst da dann ablesen, wieviel das Programm an sich braucht. Die Differenz ist dann, was deine DLL frisst.
    • @CAS
      Danke, das ist keine schlechte idee. Eine umständliche aber wohl die einzige Möglichkeit..

      Hab diese DLL nun etwas geupdatet.
      In erster Linie kann die con_execute Funktion jetzt wirklich eine normale Komando-Zeile bzw. Batch Befehl ausführen! Bisher führte es nur Programme aus (wie z.B. das Programm "ping" aus dem Beispiel.). Hätte die Funktion mehr testen sollen.. :whistling:

      Außerdem habe ich ein Skript hinzugefügt namens directory_delete um GM's fehlende Funktion auszugleichen. Aus Sicherheitsgründen kann man mit dem Skript aber nur Unterordner eines Spiels löschen, ist also nicht für Schadsoftware geignet.

      Neue Version ist im obersten Post.

      Willst du auf diese Drachen und -eier klicken?
      Sie werden sich freuen ;)