Autor Beitrag
user32
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 55
Erhaltene Danke: 5



BeitragVerfasst: Sa 28.02.15 16:31 
Gibt es eine Möglichkeit, den DWORD den man mittels GetTickCount abrufen kann, manuell wieder auf 0 zu setzen?
Ohne Windows neuzustarten natürlich.
Wo wird dieser Wert gespeichert?
Nach 49 Tagen landet er automatisch wieder auf 0 wenn der DWORD überläuft,
aber so lange möchte ich eigentlich nicht warten :P
Nersgatt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1565
Erhaltene Danke: 270


Delphi 10 Seattle Prof.
BeitragVerfasst: Sa 28.02.15 18:58 
Nein, das ist wohl nicht wirklich möglich.
Aber Du könntest es ja mit einem Offset rausrechnen und dafür eine eingene GetTickCount-Funktion machen.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var 
  TickCountOffset : DWord;

function MyGetTickCount : DWord;
begin
  result := GetTickCount - TickCountOffset;
end;

procedure ResetTickCount;
begin
  TickCountOffset := GetTickCount;
end;


So lange TickCountOffset 0 ist, gibt MyGetTickCount den selben Wert zurück, wie GetTickCount. Wenn Du ResetTickCount aufruft, wird der Offset auf den aktuellen Wert gesetzt. In dem Moment würde also MyGetTickCount (wenn theoretisch zwischen den Aufrufen keine Zeit verginge) wieder 0 zurückgeben.

_________________
Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Sa 28.02.15 20:59 
Falls du eine neuere Delphi-Version hast, solltest du vielleicht besser TStopwatch aus der Unit System.Diagnostics benutzen. Benutzt Timer mit höherer Präzision wenn möglich (Stichwort QueryPerformanceCounter). Damit könntest du einfach ein TStopwatch.Start machen und die Eigenschaften Elapsed/ElapsedTicks/ElapsedMilliseconds benutzen, für 0-basierte Werte.
user32 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 55
Erhaltene Danke: 5



BeitragVerfasst: So 01.03.15 11:58 
Mhhja, die Sache ist, ich brauche das nicht selbst im Programm sondern schon global in Windows...
Es geht um ein altes Spiel, welches GetTickCount halt oft verwendet. Deswegen muss ich schon irgendwie an den Zähler selbst rankommen.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 18717
Erhaltene Danke: 1624

W10 x64 (Chrome, IE11)
Delphi 10.2 Ent, Oxygene, C# (VS 2015), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 01.03.15 12:30 
Dann kannst du GetTickCount in dem Programm hooken.

// EDIT:
Na gut, das ist nicht ganz so einfach, aber hier findest du weitere Informationen:
code.google.com/p/de.../issues/detail?id=10
stackoverflow.com/qu...-gettickcount-with-c
user32 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 55
Erhaltene Danke: 5



BeitragVerfasst: So 01.03.15 12:50 
Oje. Hab eigentlich gedacht, dass ist einfach nur irgendeine Adresse im RAM, die man vielleicht überschreiben kann.
Aber ich seh schon, das war von Microsoft wohl nicht so vorgesehen :/

Dann bleibe ich wohl lieber beim Neustarten..
Trotzdem danke euch allen :!:
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: So 01.03.15 15:04 
Ach so ist das! Es geht also gar nicht um Zeitmessung, sondern darum, einem anderen Programm etwas vorzugaukeln.
Das ist auch nur eine Adresse im RAM. :)

Wenn du GetTickCount nicht global für ALLE Windows-Programme ändern willst, sondern nur für ein bestimmtes Programm, dann habe ich eine Lösung für dich.

Das ganze ist nicht soooo schwierig. Im Prinzip musst du eine kleine DLL programmieren, die von deinem Zielprogramm (dem Spiel) geladen wird. Deine DLL kann dann den Import-Eintrag von GetTickCount auf deine eigene Version umbiegen. Wie bekommst du aber das Spiel dazu, deine DLL zu laden? Da gibt es zwei Möglichkeiten:

1. Finde heraus, welche DLLs das Spiel importiert. Wenn du Glück hast, importiert es eine oder wenige Funktionen irgendeiner nicht-Standard-DLL. Dann köntest du deine DLL genau so nennen und neben der EXE des Spiels platzieren. Somit würde Windows bei jedem Start der EXE deine DLL in das Spiel laden. Nachteil: du musst alle Funktionen der echten DLL, die du imitierst, nachbauen oder "durchschleifen". Und das funktioniert nicht mit "Known DLLs" (Standard DLLs), wie z.B. kernel32, user32, usw., denn Windows lädt die immer aus dem Systemordner, selbst wenn es beim startenden Programm eine lokale Kopie gibt.
Es gibt durchaus Fälle, in denen ein Programm nur eine einzige Funktion einer nicht-Standard-DLL importiert (und dann vielleicht noch nicht mal im normalen Programmverlauf benutzt). So etwas wäre ideal, wird aber recht selten sein.

2. Injiziere deine DLL in den Prozess des Spiels. Dazu programmierst du dir ein kleines "Launcher"-Programm, das Windows anweist, das Spiel in den Speicher zu laden aber noch nicht zu starten. Dann startet der Launcher einen neuen Thread im Prozess des Spiels, der deine DLL lädt. Schließlich wird der Hauptthread des Spiels gestartet und deine DLL verweilt dort automatisch bis das Spiel wieder beendet wird.

Zu deinem Glück habe ich sowas schon öfter programmiert. Hier ist Code von mir, der funktionieren sollte:

Launcher, speichern als "DllInjector.dpr"
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
program DllInjector;
{
Dieses Delphi-Programm ist ein kleiner Launcher für das Programm ExeFileName und injiziert
dabei die DLL DllFileName in den Speicher des Programms. Die DLL bekommt damit die Möglichkeit
allerlei Dinge anzustellen, z.B. Windows-Funktionsaufrufe des Programms abzufangen / umzuleiten.
Autor: SMO
}


{$R *.res}

uses
  Winapi.Windows;


const
  ExeFileName = 'GAME.EXE';
  DllFileName = 'mydll.dll';


// existiert die angegebene Datei?
function FileExists(FileName: string): Boolean;
var
  Flags: Cardinal;
begin
  Flags := GetFileAttributes(PChar(FileName));
  Result := (Flags <> INVALID_FILE_ATTRIBUTES) and (Flags and FILE_ATTRIBUTE_DIRECTORY = 0);
end;


procedure ShowMessage(const Msg: string);
begin
  MessageBox(0, PChar(Msg), nil, MB_ICONERROR or MB_OK);
end;


// lade Programm AppName in den Speicher, aber starte es noch nicht
function MyCreateProcess(AppName, CmdLine: stringout PI: TProcessInformation): Boolean;
var
  SI: TStartupInfo;
begin
  FillChar(PI, SizeOf(PI), 0);
  FillChar(SI, SizeOf(SI), 0);
  Result := CreateProcess(PChar(AppName), PChar(CmdLine), nilnil, False, CREATE_SUSPENDED,
    nilnil, SI, PI);
end;


function InjectDll(const PI: TProcessInformation; LibName: string): Boolean;
const
  ProcName = {$IFDEF UNICODE} 'LoadLibraryW' {$ELSE} 'LoadLibraryA' {$ENDIF};
var
  TID: DWORD;
  hProcess, hThread: THandle;
  ByteSize: SIZE_T;
  Parameters: Pointer;
  PThreadStartAddr: Pointer;
  MustCloseHandle: Boolean;
begin
  // Öffne ein Handle zum Prozess, damit wir seinen Speicher manipulieren können
  MustCloseHandle := False;
  hProcess := PI.hProcess;
  if hProcess = 0 then
  begin
    hProcess := OpenProcess(PROCESS_ALL_ACCESS, False, PI.dwProcessId);
    MustCloseHandle := True;
  end;
  // Reserviere neuen Speicher im Prozess und kopiere den String LibName dort rein
  // Windows erwartet 0-terminierte Strings, Delphi-Strings haben praktischerweise immer diesen
  // 0-Char implizit am Ende, deshalb kopieren wir Length(LibName) + 1 Chars.
  ByteSize := (Length(LibName) + 1) * SizeOf(Char);
  Parameters := VirtualAllocEx(hProcess, nil, ByteSize, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE);
  Result := WriteProcessMemory(hProcess, Parameters, PChar(LibName), ByteSize, ByteSize);
  // Jetzt starten wir einen neuen Thread im geöffneten Prozess. Der Thread startet mit der
  // Ausführung direkt am Beginn der Funktion "LoadLibrary" von kernel32.dll
  // kernel32.dll als Kernkomponente von Windows existiert in JEDEM Prozess.
  // Der Thread führt also LoadLibrary(LibName) aus und lädt/injiziert damit die DLL
  PThreadStartAddr := GetProcAddress(GetModuleHandle('kernel32.dll'), ProcName);
  hThread := CreateRemoteThread(hProcess, nil0, PThreadStartAddr, Parameters, 0, TID);
  // Warten, bis der Thread seine Arbeit gemacht hat
  WaitForSingleObject(hThread, INFINITE);
  // Aufräumen: reservierten Speicher wieder freigeben, Thread schließen
  Result := VirtualFreeEx(hProcess, Parameters, 0, MEM_RELEASE) and Result;
  Result := CloseHandle(hThread) and Result;
  // Prozess-Handle schließen, falls wir es geöffnet haben
  if MustCloseHandle then Result := CloseHandle(hProcess) and Result;
end;


function CloseHandles(var PI: TProcessInformation): Boolean;
begin
  Result := CloseHandle(PI.hProcess);
  Result := CloseHandle(PI.hThread) and Result;
  PI.hProcess := 0;
  PI.hThread := 0;
end;


var
  PI: TProcessInformation;

begin
  if not FileExists(ExeFileName) then
  begin
    ShowMessage('Kann das Programm "' + ExeFileName + '" nicht finden!'#10
      + 'Dieser Launcher sollte im selben Verzeichnis wie "' + ExeFileName + ' gestartet werden".'#10);
    Exit;
  end;
  if not FileExists(DllFileName) then
  begin
    ShowMessage('Kann die DLL "' + DllFileName + '" nicht finden!'#10
      + 'Diese DLL sollte sich im selben Verzeichnis wie dieser Launcher und "' + ExeFileName + ' befinden".'#10);
    Exit;
  end;

  // Zielprogramm laden
  if MyCreateProcess(ExeFileName, '', PI) then
  begin
    // DLL injizieren und Hauptthread des Programms starten
    if InjectDll(PI, DllFileName) then
      ResumeThread(PI.hThread)
    else
      ShowMessage('Konnte DLL "' + DllFileName + '" nicht in "' + ExeFileName + '" injizieren!');
    CloseHandles(PI);
  end
  else
    ShowMessage('Konnte "' + ExeFileName + '" nicht starten!');
end.


Und die DLL (mydll.dpr):
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
library mydll;
{
Delphi Windows-DLL, die Winapi-Hooking demonstriert.
Als Beispiel wird GetTickCount (kernel32.dll) auf eine eigene Version umgeleitet, die den
TickCount ab Programmstart bei 0 beginnen lässt.
Autor: SMO
}


uses
  System.AnsiStrings,  // für SameText
  Winapi.Windows;

{$R *.res}

// Diese Funktion liefert einen Pointer in die Import-Tabelle des angegebenen Moduls.
// Der Pointer zeigt auf die Funktion "ProcName", die aus der DLL "LibName" importiert wird.
// Kann also benutzt werden, um beliebige DLL-Funktionen zu hooken, die im aktuellen
// Prozess geladen sind, sofern die DLL implizit ("statisch") geladen wurde und nicht
// explizit per LoadLibrary (denn dann gibt es in der Import-Tabelle keinen Eintrag dafür).
function GetImportTablePointer(Module: HMODULE; const LibName, ProcName: string): Pointer;
var
  LibNameA, ProcNameA: AnsiString;
  DosHeader: PImageDosHeader;
  NtHeaders: PImageNtHeaders;
  ImportDesc: PImageImportDescriptor;
  PName: PAnsiChar;
  Thunk, OrigThunk: PImageThunkData32;
  ImportName: PImageImportByName;
begin
  // Der folgende Code ist etwas kompliziert, wenn man die Datenstrukturen nicht kennt.
  // Details siehe z.B.:
  // http://www.ntcore.com/files/inject2it.htm
  // http://sandsprite.com/CodeStuff/Understanding_imports.html

  // ein HMODULE ist die Start-Speicheradresse eines Programms, dort stehen Header
  DosHeader := PImageDosHeader(Module);
  // Navigiere durch die diversen PE-Header (EXE,DLL...) und prüfe die Signaturen
  if DosHeader.e_magic <> IMAGE_DOS_SIGNATURE then Exit(nil);
  NtHeaders := PImageNtHeaders(IntPtr(Module) + DosHeader._lfanew);
  if (NtHeaders.Signature <> IMAGE_NT_SIGNATURE) or
    (NtHeaders.OptionalHeader.Magic <> IMAGE_NT_OPTIONAL_HDR32_MAGIC) then Exit(nil);

  with NtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] do
    UIntPtr(ImportDesc) := VirtualAddress + UIntPtr(Module);

  // Die Import-Tabelle benutzt AnsiString, nicht Unicode
  LibNameA := AnsiString(LibName);
  ProcNameA := AnsiString(ProcName);
  // Jetzt Schleife über alle Import-Descriptoren, jede DLL hat einen.
  // Ende der Liste wird durch OriginalFirstThunk = 0 angezeigt
  while ImportDesc.OriginalFirstThunk > 0 do
  begin
    PName := PAnsiChar(ImportDesc.Name + UIntPtr(Module));
    // OriginalFirstThunk zeigt auf eine Liste von Pointern auf ImportByName Records
    // FirstThunk zeigt auf eine Liste von Pointern auf die tatsächlichen Funktionen
    UIntPtr(OrigThunk) := ImportDesc.OriginalFirstThunk + UIntPtr(Module);
    UIntPtr(Thunk) := ImportDesc.FirstThunk + UIntPtr(Module);
    // SameText: Stringvergleich, ignoriert Unterschiede in Groß/Kleinschreibung
    if SameText(PName, LibNameA) then
    // Jetzt Schleife über die Namen aller Prozeduren, die aus der aktuellen DLL importiert werden
    // Ende der Liste wird durch AddressOfData = 0 angezeigt
    while OrigThunk.AddressOfData > 0 do
    begin
      // Wenn ungerade (=Bit 0 gesetzt), dann ist es ein "ordinaler Import" ohne Name -> überspringen
      if not Odd(OrigThunk.Ordinal) then
      begin
        UIntPtr(ImportName) := OrigThunk.AddressOfData + UIntPtr(Module);
        PName := PAnsiChar(UIntPtr(ImportName) + SizeOf(ImportName.Hint));
        if SameText(PName, ProcNameA) then
          Exit(Thunk);
      end;
      Inc(OrigThunk);
      Inc(Thunk);
    end;
    Inc(ImportDesc);
  end;
  Result := nil;
end;


// Diese Funktion "hookt" einen Import im angegebenen Modul
function InstallImportHook(Module: HMODULE; const LibName, ProcName: string; NewProc: Pointer;
  out OrigProc: Pointer): Boolean;
var
  P: PPointer;
  OldProtect: DWORD;
begin
  P := GetImportTablePointer(Module, LibName, ProcName);
  if Assigned(P) and Assigned(NewProc) then
  begin
    // Speicherbereich der Import-Tabelle beschreibbar machen, in dem sich der Pointer befindet
    OldProtect := PAGE_EXECUTE_WRITECOPY;
    Result := VirtualProtect(P, SizeOf(P^), OldProtect, OldProtect);
    if Result then
    begin
      // alten Import-Pointer sichern und durch neuen ersetzen
      OrigProc := P^;
      P^ := NewProc;
    end;
    // vorige Zugriffseinstellung des Speicherbereichs wiederherstellen
    VirtualProtect(P, SizeOf(P^), OldProtect, OldProtect);
  end
  else Result := False;
end;


var
  // HMODULE des Hauptprogramms (der EXE, die diese DLL lädt)
  HMainModule: HMODULE;
  PreviousDLLProcEx: TDLLProcEx;
  OrigGetTickCount: function: DWORD; stdcall;
  InitialTickCountValue: DWORD;


function MyGetTickCount: DWORD; stdcall;
begin
  Result := OrigGetTickCount - InitialTickCountValue
end;

procedure InstallGetTickCountHook;
begin
  // Jetzt biegen wir für HMainModule den GetTickCount Importeintrag auf unsere eigene
  // Funktion um.
  // Konstante kernel32 = 'kernel32.dll', definiert in Winapi.Windows
  if InstallImportHook(HMainModule, kernel32, 'GetTickCount', @MyGetTickCount, @OrigGetTickCount) then
    // Hook wurde installiert! Jetzt den initialen TickCount merken.
    InitialTickCountValue := OrigGetTickCount;
end;

procedure UninstallGetTickCountHook;
var
  Dummy: Pointer;
begin
  // Ursprünglichen Pointer wiederherstellen.
  InstallImportHook(HMainModule, kernel32, 'GetTickCount', @OrigGetTickCount, Dummy);
end;


procedure DllMainEx(Reason: Integer; Reserved: Pointer);
begin
  case Reason of
    // DLL wurde geladen, Initialisierungsaufgaben können jetzt durchgeführt werden
    DLL_PROCESS_ATTACH:
      InstallGetTickCountHook;
    // DLL wird entladen, Aufräumarbeiten können jetzt durchgeführt werden
    DLL_PROCESS_DETACH:
      UninstallGetTickCountHook;
  end;
  // die Meldung an den vorigen Handler weiterleiten, falls er existiert
  if Assigned(PreviousDLLProcEx) then PreviousDLLProcEx(Reason, Reserved);
end;


// Hauptprogrammblock:
begin
  HMainModule := GetModuleHandle(nil);
  DisableThreadLibraryCalls(HInstance);
  // DllProc initialisieren
  PreviousDLLProcEx := DllProcEx;
  DllProcEx := @DllMainEx;
  DllProcEx(DLL_PROCESS_ATTACH, nil);
end.



Eine kleine Erklärung zum DLL-Grundgerüst habe ich hier geschrieben.
In diesem einfach Fall könnte man sich das Hantieren mit DllProc natürlich sparen und "InstallGetTickCountHook" direkt aus dem Hauptprogrammblock aufrufen. Das Ausführen von "UninstallGetTickCountHook" ist nicht unbedingt nötig, da die DLL erst wieder entladen wird, wenn das Programm selbst beendet wurde. Aber so wie im Code gezeigt ist es wahrscheinlich formell korrekter.
Falls es Fragen zum Code gibt, lass es mich wissen!


Zuletzt bearbeitet von SMO am Mo 02.03.15 17:07, insgesamt 6-mal bearbeitet

Für diesen Beitrag haben gedankt: jaenicke
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 458
Erhaltene Danke: 90

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Mo 02.03.15 10:48 
Da ist wohl der Testzeitraum abgelaufen und es wird versucht, diesen "irgendwie" zu verlängern.

Gibt es eine Möglichkeit, eine solche Manipulation zu erkennen ?

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Mo 02.03.15 13:22 
Ob das der Anwendungszweck ist, habe ich auch kurz überlegt. Das wäre allerdings eine äußerst dumme Methode, einen Testzeitraum zu implementieren, da der TickCount ja sowieso bei jedem Neustart von Windows zurückgesetzt wird. Und wenn die Prüfung so plump ist, muss man nicht extra eine DLL programmieren, sondern könnte das Programm auch disassemblieren und die Abfrage direkt durch Ändern von ein paar Bytes aushebeln.

Klar gibt es Möglichkeiten sowas zu erkennen. Und natürlich auch wieder Möglichkeiten, der Erkennung zu entgehen. Das Programm könnte z.B. GetProcAddress(GetModuleHandle('kernel32.dll'), 'GetTickCount') aufrufen, um die Adresse vom echten GetTickCount zu bekommen. Aber dann biegt man eben auch GetProcAddress auf eine eigene Variante um...

Ich habe DLL-Injektion und Api-Hooking für recht legitime Zwecke benutzt. Nämlich um ein paar alte Windows 9x Spiele wieder zum Laufen zu bringen, in Windows 7 und neuer. Im Prinzip macht Microsofts eigene Kompatibilitätsschicht nichts anderes, funktioniert aber oft nicht zufriedenstellend, weil sie eben allgemein gehalten ist und nicht maßgeschneidert wurde auf die Anforderungen spezieller Programme.


Zuletzt bearbeitet von SMO am Di 03.03.15 12:46, insgesamt 1-mal bearbeitet
user32 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 55
Erhaltene Danke: 5



BeitragVerfasst: Mo 02.03.15 15:07 
Nein nein, keine Sorge. Habe das Spiel gekauft und kann das auch beweisen (sogar 2x weil ich die CD zerkratzt hatte), aber das interessiert hier auch gar nicht nicht.
GetTickCount wird hier in irgendeiner Rendering Funktion benutzt, so wie es viele alte OpenGL Programme damals taten. Und wenn die Zahl zu groß wird scheint es irgendwelche Glitches zu geben. Und umso länger der PC an ist, desto schlimmer.

Werde mir deine Methode mal später genauer angucken, Danke SMO.
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Mo 02.03.15 16:37 
Na dann hoffe ich mal, dass dieses Problem auch wirklich an GetTickCount liegt.
Ich habe übrigens gerade noch ein paar Kommentare zum Code hinzugefügt und auch einen kleinen Fehler korrigiert.
user32 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 55
Erhaltene Danke: 5



BeitragVerfasst: Mo 02.03.15 17:14 
Doch ich denke schon, weil es tritt wirklich nur auf, wenn ich den PC länger anhabe
(wir reden hier übrigens von Tagen, nicht ein paar stunden oder so) .
Der Verdacht kam, als ich vor einiger Zeit ein Programm machen wollte, das anzeigt wie lange der PC schon läuft - eben mit GetTickCount. Und irgendwann stellte ich mit erschrecken fest, dass der Zähler rückwärts läuft :lol:
Wenn dieses altes Uptime-Programm anfängt zu "spinnen", dann fangen zufällig auch die Glitches in dem Spiel an. Eine klare Korrelation. Aber ich hab das jetzt nicht alles zu 100% erforscht :P
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 18717
Erhaltene Danke: 1624

W10 x64 (Chrome, IE11)
Delphi 10.2 Ent, Oxygene, C# (VS 2015), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 02.03.15 20:45 
Der Timer funktioniert eigentlich bis zu knapp 50 Tagen normal. In der Zeit startest du doch wohl den PC ein paarmal neu?!? :shock:
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1182
Erhaltene Danke: 96

Win7
DXE2 Prof, Lazarus
BeitragVerfasst: Di 03.03.15 07:19 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Der Timer funktioniert eigentlich bis zu knapp 50 Tagen normal. In der Zeit startest du doch wohl den PC ein paarmal neu?!? :shock:

Ob Du es glaubst oder nicht, es gibt Rechner die 24/7 laufen und das über Monate hinweg. Einzig und allein Windows Updates zwingen einen immer mal zum Neustart des Rechners. Denke zum Beispiel mal an Industrie PCs die als Maschienen Interface im 3 Schicht Betrieb laufen. Da ist nicht ersthaft Zeit für eine Unterbrechung.

Wenn ein Rechner nie ins Internet geht oder nur mit einem bestimmten Server redet sind Neustarts genauso mehr als nervige Ausfallzeiten.
Mir ist aufgefallen das ein Win Rechner nach ca. einem Monat Laufzeit noch ein paar mehr Macken bekommt, Je nachdem was M$ gerade malwieder rumgemurkst (auf hohem Niveau natürlich) hat. Auf einem Rechner mit FTP Server drauf gehen mir die TCP/IP Ports aus. Die Übersicht der offenen Ports zeigt aber keine verlorenen Ports an. Das habe ich auf einem anderen Rechner auch schon erlebt. Eine Zeit lang gab es mal nen Fehler mit einem Integer überlauf beim Zähler für gesendete oder empfangene Daten über eine bestimmte Netzwerkkarte. Schön wenn man zwei hat!
Interessant ist jedenfalls das selbst M$ Server Systeme Windowsupdates bekommen und durch die sie zwangsweise neu gestartet werden müssen. Gerade bei Servern halte ich das aber für ziemlich dämlich.

_________________
Solange keine Zeile Code geschrieben ist, läuft ein Programm immer fehlerfrei.
Ich teste nicht, weil ich Angst habe Fehler zu finden.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 18717
Erhaltene Danke: 1624

W10 x64 (Chrome, IE11)
Delphi 10.2 Ent, Oxygene, C# (VS 2015), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 03.03.15 08:16 
Industrie-PCs nutzen aber dann erstens eher GetTickCount64 und außerdem meinte ich natürlich den vorliegenden Fall und somit einen einfachen privaten PC. Oder meinst du, dass das Spiel auf einem solchen Industrie-PC laufen soll? ;-)
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1182
Erhaltene Danke: 96

Win7
DXE2 Prof, Lazarus
BeitragVerfasst: Di 03.03.15 15:44 
Eher unwahrscheinlich. Industrie PCs waren nur ein Beispiel für Rechner die 24/7 laufen. Aber auch meine Privatrechner laufen gerne mal als einen Monat am Stück. Ich hatte den GetTickCount Fehler selber auch schon. Bin dann auf QueryPerformanceCounter umgestiegen.

_________________
Solange keine Zeile Code geschrieben ist, läuft ein Programm immer fehlerfrei.
Ich teste nicht, weil ich Angst habe Fehler zu finden.
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 165
Erhaltene Danke: 42



BeitragVerfasst: Mi 04.03.15 11:59 
Der Fehler, das ein Counter rückwärts läuft, ist aber eindeutig dem Programmierer der Anwendung anzulasten.
GetTickCount liefert eine Ganzzahl ohne Vorzeichen.
Wenn man das höchste Bit aber als Vorzeichen interpretiert, wird daraus irgendwann eine negative Zahl, deren Absolutbetrag stetig kleiner wird.
Falls die Anwendung auch mehrere Tage läuft, währe es wahrscheinliche sinnvoll, auch das oberste Bit bei GetTickCount per Maske auszublenden.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
function MyGetTickCount: DWORD; stdcall;
begin
  Result := (OrigGetTickCount - InitialTickCountValue) and $7FFFFFFF;
end;

Das Problem mit dem Überlauf dieses Zählers ist schon so lange bekannt, wie diese Funktion existiert.
Es gibt verscheidene Lösungen, der Programmierer musste sich nur die passende auswählen.
GetTickCount64 gibt es auch schon eine ganze Weile.
user32 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 55
Erhaltene Danke: 5



BeitragVerfasst: Do 05.03.15 15:48 
Moderiert von user profile iconNarses: Komplettzitat des letzten Beitrags entfernt.

Ja wie gesagt, es war ein altes Programm von mir. Aber genau dann wenn er anfängt rückwärts zu laufen, gingen die besagten Glitches im Spiel los. Das heißt wohl, die Entwickler von dem Spiel haben den Fehler wohl auch gemacht 8)