Timer
-
Hallo,
ich bin gerade dabei WinAPI zu lernen.
Ich hatte vorher schon mal ein Konsolenprogramm, was eine benutzerdefinierte Zeit wartet und dann Alarm schlägt.Jetzt wollte ich es mit WinAPI als Gui schreiben. Das Warten und Alarm schlagen funktionier einwandfrei. Nur wollte ich in drei Edit-Controls, jeweils eine für Stunden, Minuten und Sekunden, einen Timer ablaufen lassen. Und da liegt mein Problem:
Die Edit-Controls werden nur von dem letzten SetWindowText beschrieben. Während der ganzen Wartezeit passiert nichts, blos am Ende werden überall Nullen reingeschrieben.Mein Programm:
hours_number = GetWindowTextNum (hours_insert, 10); minutes_number = GetWindowTextNum (minutes_insert, 10); seconds_number = GetWindowTextNum (seconds_insert, 10); for (int i = hours_number * 60 * 60 + minutes_number * 60 + seconds_number; i > 0; i --) { wait (0,0,1,0); seconds_number --; if (seconds_number == -1) { minutes_number --; seconds_number = 59; } if (minutes_number == -1) { hours_number --; minutes_number = 59; } SetWindowTextNum (hours_view, hours_number, 10); SetWindowTextNum (minutes_view, minutes_number, 10); SetWindowTextNum (seconds_view, seconds_number, 10); }Ist natürlich nur ein Ausschnitt.
Die Set- und GetWindowText-Funktionen mit eingebauter Konvertierung:
void SetWindowTextNum (HWND _hwnd, int number, int size) { char string [size]; sprintf (string, "%d", number); SetWindowText (_hwnd, string); } int GetWindowTextNum (HWND _hwnd, int size) { TCHAR string[size]; int number; GetWindowText (_hwnd, string, 10); return number = atoi (string); }Es wäre shön wenn ihr mir helfen könntet.
mww
-
Du musst die Timer Funktion auf einem eigenen Thread laufen lassen und jeweils updates aus der Timer Funktion an die Controls posten (mit PostMessage(..)).
-
Oder Windows-Timer mithilfe von SetTimer nutzen. Dürfte in dem Fall ausreichend sein, wenn du die zeitliche Differenzen zwischen zwei WM_TIMER-Nachrichten selber mit GetTickCount ermittelst (und dann natürlich die Zeit, die vergehen soll, in ms speicherst).
-
Danke für die Tipps!
Bei der PostMessage-Lösung:
Muss ich da den neuen Text einfach an die Edit-Control schicken?
-
Ich würde mir es dennoch überlegen, für deine Anwendung reicht ein Thread aus.
LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static DWORD lastTick; static DWORD timeInMs; switch (msg) { case WM_COMMAND: switch(LOWORD(wParam)) { case ID_START: // könnte ein Button mit der ID sein, hier soll der Countdown begonnen werden timeInMs=/*initialisieren*/; lastTick = GetTickCount(); // momentanen Wert speichern SetTimer(hwnd,1,1000,NULL); // Windows-Timer anfordern break; } break; case WM_TIMER: { DWORD curTick = GetTickCount(); // momentaner Tick DWORD diff = curTick-lastTick; // Differenz seit letzter Nachricht lastTick = curTick; // wichtig: alten Wert speichern timeInMs-=diff; // Differenz abziehen DWORD time = timeInMs; // Kopie time/=1000; // ms kürzen int second=time%60; time/=60; int minute=time%60; int hour=time/60; // Editfelder mit SetWindowtextNum füllen, in SetWindowtextNum wahrscheinlich besser ein sprintf (string, "%2d", number); // wenn die Zeit abgelaufen ist, KillTimer(hwnd,1); aufrufen break; } ... }
-
Mein Timer funktioniert ja, nur wird SetWindowTextNum nicht aufgerufen bzw. schreibt nichts. Erst, wenn der Timer abgelaufen ist, wird geschrieben.
http://img819.imageshack.us/img819/2783/83619360.png
http://img3.imageshack.us/img3/9339/45016079.pngDazwischen bekomme ich keine Anzeigen.
-
Dazwischen bietest du Windows ja auch keine Gelegenheit, das Fenster neu zu zeichnen.
-
Ich kenne deine wait-Funktion nicht, vermutlich blockiert sie den Thread. Du könntest es mit
UpdateWindowmit dem Handle auf dein Fenster probieren und vor allem einfach Sleep verwenden.Edit: Aber wie schon geschrieben, Windows-Timer können wegen des Schedulers ungenau sein, die Zeitspannen solltest du dir immer selber ausrechnen.
-
Danke.
UpdateWindow hat geholfen.
-
Ok. Das hat geklappt, jetzt habe ich ein neues Problem.
Solange ich das Fenster aktiv behalte, funktioniert alles wunderbar. Jedoch wenn ich es minimiere oder auf ein anderes Fenster klicke, frisst sich das Prog fest.
-
Wie hoch ist denn deine CPU-Auslastung des Prozesses?
Wie schon geschrieben, das Beste wären entweder ein eigener Thread mit Sleep-Aufrufen oder Windows-Timer.
-
Ich habs mit Sleep un eigenem Thread.
Da hab ich knapp 1500KB RAM die es braucht.#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <windowsx.h> #include <time.h> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <Edit_Numbers.h> #include "resource.h" #include <mmsystem.h> void wait(int h, int m, int s, int ms) { //Wartefunktion, es wird die angegebene Zeit gewartet, die Funktion beendet sich wenn die zu wartende zeit vorüber ist. //create by: Tom Lambert //Originaldateiname: wait2.exe, wait2.pdb //Originalprojekt: wait2.sln, VS2010 //Erstelldatum: 11.06.2011, Debugdatum: 11.06.2011 clock_t zeit1, zeit2; float intervall = 0.001*CLOCKS_PER_SEC; bool flag = true; zeit1 = clock(); zeit2 = clock()+h*3600000+m*60000+s*1000+ms; while(flag) { zeit1 = clock(); //cout<<"Zeit1: "<<zeit1<<"\tZeit2: "<<zeit2<<endl; if(zeit1 >= zeit2) flag = false; } } BOOL play(char *name) { PlaySoundA(name, NULL, SND_FILENAME | SND_ASYNC); return 0; } BOOL pl_sound, waiting; int minutes_number, hours_number, seconds_number; HWND edit1, edit2, edit3; DWORD WINAPI SoundThreadFunction (LPVOID param) { pl_sound = TRUE; while(pl_sound == TRUE) { play("alarm.wav"); wait(0,0,3,0); } return 0; } DWORD WINAPI WaitThreadFunction (LPVOID param) { waiting=TRUE; for (int i = hours_number * 60 * 60 + minutes_number * 60 + seconds_number; i > 0; i --) { wait (0,0,1,0); seconds_number --; if (seconds_number == -1) { minutes_number --; seconds_number = 59; } if (minutes_number == -1) { hours_number --; minutes_number = 59; } SetWindowTextNum (edit1, hours_number, 10); SetWindowTextNum (edit2, minutes_number, 10); SetWindowTextNum (edit3, seconds_number, 10); } waiting==FALSE; } HINSTANCE hInst; BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_INITDIALOG: /* * TODO: Add code to initialize the dialog. */ return TRUE; case WM_CLOSE: EndDialog(hwndDlg, 0); return TRUE; case WM_COMMAND: switch(LOWORD(wParam)) { /* * TODO: Add more control ID's, when needed. */ case IDC_BUTTON_QUIT: EndDialog(hwndDlg, 0); return TRUE; case IDC_BUTTON_START: //Handles erstellen HWND hours_insert = GetDlgItem (hwndDlg, IDC_EDIT_HOURS_INSERT); HWND hours_view = GetDlgItem (hwndDlg, IDC_EDIT_HOURS_VIEW); HWND minutes_insert = GetDlgItem (hwndDlg, IDC_EDIT_MINUTES_INSERT); HWND minutes_view = GetDlgItem (hwndDlg, IDC_EDIT_MINUTES_VIEW); HWND seconds_insert = GetDlgItem (hwndDlg, IDC_EDIT_SECONDS_INSERT); HWND seconds_view = GetDlgItem (hwndDlg, IDC_EDIT_SECONDS_VIEW); edit1 = hours_view; edit2 = minutes_view; edit3 = seconds_view; hours_number = GetWindowTextNum (hours_insert, 10); minutes_number = GetWindowTextNum (minutes_insert, 10); seconds_number = GetWindowTextNum (seconds_insert, 10); for (int i = hours_number * 60 * 60 + minutes_number * 60 + seconds_number; i > 0; i --) { //wait (0,0,1,0); Sleep (1000); seconds_number --; if (seconds_number == -1) { minutes_number --; seconds_number = 59; } if (minutes_number == -1) { hours_number --; minutes_number = 59; } SetWindowTextNum (hours_view, hours_number, 10); SetWindowTextNum (minutes_view, minutes_number, 10); SetWindowTextNum (seconds_view, seconds_number, 10); UpdateWindow (hwndDlg); } HANDLE WaitThreadHandle = CreateThread(0, 0, WaitThreadFunction, 0, 0, 0); while (waiting==TRUE) { UpdateWindow (hwndDlg); } HANDLE SoundThreadHandle = CreateThread(0, 0, SoundThreadFunction, 0, 0, 0); MessageBox(hwndDlg, "Die Zeit ist abgelaufen", "Time Alarm", MB_ICONWARNING); pl_sound = FALSE; return TRUE; } } return FALSE; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { hInst = hInstance; // The user interface is a modal dialog box return DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DialogProc); }
-
Jetzt hast Du die Sachen aber gründlich vermischt.
Du ruftst auf dem UI Thread Sleep() auf UND machst einen Thread der auch noch herunterzählt.
1. Benutze kein Update(..)
2. Benutze Sleep() in der Thread Funktion
3. Mache das Timer Zeugs im der DialogProc() weg - starte dort nur den Thread!
4. Updates via PostMessage() (oder PostThreadMessage()) an den UI Thread!
-
Ok. Wir dumm von mir, dass ich dass vergessen habe aus dem Hauptthread rauszunehmen.
Gut, doch wie funktioniert das mit dem PostMessage? Ich hab das noch nicht so durchschaut. Auch in der msdn binn ich nicht schlau geworden.
-
Also wenn das ein Lernprojekt ist: OK
Sonst benutze SetTimer..) KillTimer(..) und die WM_TIMER, wäre irgendwie einfacher und besser:
http://msdn.microsoft.com/en-us/library/ms644902(v=vs.85).aspxDieser Vorschlag kam auch schon von anderen Postern.