|
Das deutsche QBasic- und FreeBASIC-Forum Für euch erreichbar unter qb-forum.de, fb-forum.de und freebasic-forum.de!
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
BD
Anmeldungsdatum: 15.01.2014 Beiträge: 38
|
Verfasst am: 19.01.2014, 22:18 Titel: Probleme mit dem Dialog(ProgressBar) bei Verlust des Fokus |
|
|
Und schon gibt es ein neues Problem, welches es zu meistern gilt. Leider führten bis jetzt alle Versuche nur zu einem unbefriedigenden Ergebnis.
Ziel eines Programmes ist es mehr oder weniger, einfach nur einen Dateiinhalt von einer Datei in eine andere zu kopieren. Dazu soll in einem Dialog ein Fortschrittsbalken ProgressBar, je nachdem wie weit der Vorgang abgeschlossen ist, upgedated werden. Gestartet wird der Vorgang mit einem Buttonklick.
Das Ganze funktioniert auch soweit, indem ich den Kopiervorgang zunächst in dem Select-Bereich der Tastenaktion eingebunden habe.
Das Problem ist: Verliert der Dialog den Fokus, wird das Dialogfenster nicht mehr upgedated und der Balken bleibt einfach stehen. Selbst wenn man dem Dialogfenster durch Anklicken mit der Maus den Fokus zurückgibt. Der Kopiervorgang an sich läuft allerdings korrekt weiter.
Wie kann man das verhindern, dass das Dialogfenster "hängen" bleibt?
Sinnigerweise müsste man mit dem Kopiervorgang einen Thread starten und innerhalb dieses Threads den ProgressBar aktualisieren. Doch wie mache ich das am sinnvollsten und wohin mit dem Code, sodass der Anzeigevorgang auch bei Verlust des Fokus weiter aktualisiert wird? |
|
Nach oben |
|
|
BD
Anmeldungsdatum: 15.01.2014 Beiträge: 38
|
Verfasst am: 21.01.2014, 03:45 Titel: |
|
|
Nachtrag:
Inzwischen habe ich das Ganze in VB 2012 nachgebaut und dort habe ich kein Fensterrefreshproblem. (Dafür muss man sich mit der ungewöhnlichen .NET-Programmierung bei binären Datei-Lese-/Schreibvorgängen herumschlagen, da es dort kein OPEN, CLOSE, GET und PUT mehr gibt)
in FreeBASIC habe ich nun dies probiert:
Code: | Declare Function Refr(ByVal hwnd As Long) as Integer
.
.
.
Function Refr(ByVal hwnd As Long)
If PeekMessage(pMSG, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE) Then
TranslateMessage pMSG
DispatchMessage pMSG
End If
Return TRUE
End Function |
und in der Funktion des Dialogs dann den Aufruf
zum Zwischendurchrefresh des Dialogfensters, doch sobald dieses den Fokus verliert läuft es noch kurz weiter und dann friert es ein, während der Code an sich weiter abgearbeitet wird. Also brachte dieser Versuch auch keinen Erfolg.
Hat vielleicht jemand noch Ideen oder Vorschläge, dies in den Griff zu bekommen? |
|
Nach oben |
|
|
Jojo alter Rang
Anmeldungsdatum: 12.02.2005 Beiträge: 9736 Wohnort: Neben der Festplatte
|
Verfasst am: 21.01.2014, 10:29 Titel: |
|
|
BD hat Folgendes geschrieben: | (Dafür muss man sich mit der ungewöhnlichen .NET-Programmierung bei binären Datei-Lese-/Schreibvorgängen herumschlagen, da es dort kein OPEN, CLOSE, GET und PUT mehr gibt) |
Hast du mal System.IO.BinaryReader verwendet? Das ist wesentlich angenehmer als GET/PUT, da man auch direkt am Funktionsaufruf sehen kann, was da überhaupt für eine Variable aus der Datei gelesen wird (ReadUInt32() z.B.) _________________ » Die Mathematik wurde geschaffen, um Probleme zu lösen, die es nicht gäbe, wenn die Mathematik nicht erschaffen worden wäre.
|
|
Nach oben |
|
|
BD
Anmeldungsdatum: 15.01.2014 Beiträge: 38
|
Verfasst am: 21.01.2014, 15:44 Titel: |
|
|
Ich verwende hier FileSystem.WriteAllBytes, was aber ein Problem darstellt, wie man in nachfolgendem Beispiel sieht, da der letzte Rest der Datei, meist nicht der Puffergröße entspricht und so mehr Byte geschrieben werden, als gelesen werden. Das habe ich hier sehr unelegant gelöst, indem ich für den letzten Block an Bytes einen eigenen Array erstelle, der nur dessen Länge und nicht die des Puffers hat und diesen dann als Puffer nutze, aber so funktioniert es zumindest in VB.
Code: | Sub StarteProzedur_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StarteProzedur.Click
Dim vonDatei As String
Dim nachDatei As String
Dim Pfade As String
Dim D(1) As String
Dim f As ULong
Dim Puffer As ULong
Dim Erledigt As ULong
Dim Gross As ULong
Dim Txt$
Dim vD, nD As String
Dim overwrite As Boolean
Dim rest As ULong
StarteProzedur.Visible = False
Fortschritt.Visible = True
Information.Visible = True
Prozent.Visible = True
overwrite = True
Pfade = "c:\bin"
vonDatei = "vondatei.bin"
nachDatei = "nachdatei.bin"
Erledigt = 0
Puffer = 1000000
vD = Pfade + "\" + vonDatei
nD = Pfade + "\" + nachDatei
Dim inputFile = IO.File.Open(vD, IO.FileMode.Open)
Gross = My.Computer.FileSystem.GetFileInfo(vD).Length
If overwrite AndAlso My.Computer.FileSystem.FileExists(nD) Then
My.Computer.FileSystem.DeleteFile(nD)
End If
Gross = My.Computer.FileSystem.GetFileInfo(vD).Length
Dim bytes = New Byte(Puffer - 1) {}
For f = 0 To Gross - 1 Step Puffer
If Gross - f < Puffer Then
rest = Gross - f
Dim rbytes = New Byte(rest - 1) {}
inputFile.Read(rbytes, 0, rest)
My.Computer.FileSystem.WriteAllBytes(nD, rbytes, True)
Else
inputFile.Read(bytes, 0, Puffer)
My.Computer.FileSystem.WriteAllBytes(nD, bytes, True)
End If
Erledigt = f / Gross * 100
Txt$ = "Kopiere... (" + Trim(Str$(Int(f / 1024 / 1024))) + " MB von " + Trim(Str$(Int(Gross / 1024 / 1024))) + " MB)"
Information.Text = Txt$
Txt$ = Trim(Str$(Erledigt) + "%")
Prozent.Text = Txt$
Fortschritt.Value = Erledigt
Application.DoEvents()
Next
inputFile.Close()
Information.Text = "Fertig."
Prozent.Text = "100%"
Fortschritt.Value = Erledigt
Application.DoEvents()
Me.Close()
End Sub
|
Mit BinaryReader war ich mir nicht sicher, wie ich größere Blöcke einlesen kann, damit diese beim Schreiben auch unverfälscht in der Zieldatei landen.
Am liebsten würde ich das Ganze mit FreeBASIC realisieren, doch da klemmt es mit dem Fortschrittsbalken bei Fokusverlust. |
|
Nach oben |
|
|
BD
Anmeldungsdatum: 15.01.2014 Beiträge: 38
|
Verfasst am: 23.01.2014, 01:05 Titel: |
|
|
Nach langem Suchen habe ich endlich die Lösung für ein DoEvents für Freebasic gefunden:
http://www.freebasic.net/forum/viewtopic.php?p=106402
Einfacher ausgedrückt eine Sub mit folgendem Inhalt:
Code: | #include "inc\windows.bi"
Declare Sub DoEvents
Sub DoEvents()
Static wMsg As MSG
PeekMessage VarPtr(wMsg), 0, 0, 0, 1
If wMsg.message = WM_QUIT Then End
TranslateMessage VarPtr(wMsg)
DispatchMessage VarPtr(wMsg)
End Sub |
Der Aufruf dann in der Select Case-Schleife nach dem Refresh des Progressbars und schon läuft alles.
Das DoEvents sorgt dann dafür, dass Windows die Gelegenheit bekommt, das Dialogfenster zu refreshen, sodass es nicht einfrieren kann. |
|
Nach oben |
|
|
St_W
Anmeldungsdatum: 22.07.2007 Beiträge: 949 Wohnort: Austria
|
Verfasst am: 23.01.2014, 12:29 Titel: |
|
|
Wäre wohl besser gewesen, du hättest das nicht gefunden.
Zeitintensive Operationen gehören ganz einfach nicht in den UI-Thread. _________________ Aktuelle FreeBasic Builds, Projekte, Code-Snippets unter http://users.freebasic-portal.de/stw/
http://www.mv-lacken.at Musikverein Lacken (MV Lacken) |
|
Nach oben |
|
|
BD
Anmeldungsdatum: 15.01.2014 Beiträge: 38
|
Verfasst am: 23.01.2014, 14:40 Titel: |
|
|
@St_W: Welchen Lösungsvorschlag gibt es dann, um wie in dem Beispiel einen Kopierfortschritt per Progressbar zu dokumentieren?
Ruhig mal mit Codebeispiel, weil ich denke, das geht Richtung Multithread und da gibt es auch eine Menge Stolperfallen. |
|
Nach oben |
|
|
BD
Anmeldungsdatum: 15.01.2014 Beiträge: 38
|
Verfasst am: 25.01.2014, 16:05 Titel: |
|
|
Schade, keine Antwort. Nur zu sagen, dass es so keine gute Lösung ist, ohne eine Alternative anzubieten und aufzuzeigen, wie diese realisierbar wäre, ist leider nicht sehr konstruktv.
Wenigstens läuft es mit dem DoEvents-Nachbau tadellos. |
|
Nach oben |
|
|
St_W
Anmeldungsdatum: 22.07.2007 Beiträge: 949 Wohnort: Austria
|
Verfasst am: 25.01.2014, 17:40 Titel: |
|
|
Ich hab mich inzwischen schon lange nicht mehr mit der WinAPI beschäftigt und muss zugeben, dass ich mit Sicherheit in meinen WinAPI Programmen (die großteils nur Spielereien waren und es nicht in die Öffentlichkeit geschafft haben) auch Fehler wie diese gemacht habe. Der Grund weshalb ich dir keine WinAPI Lösung sagen kann ist, dass mir diese UI-Thread / Worker-Thread Do's and Don'ts erst später als ich in C# programmierte wichtig wurden. Mit meinem Rat oberhalb wollte ich dich davor bewahren die gleichen Fehler zu machen, nachdem ich inzwischen weiß, dass man dies nicht so machen soll - obwohl ich dir nicht konkret sagen kann wie man es am Besten richtig macht.
Im allgemeinen gibt es die beiden Möglichkeiten:- Synchrone API in anderem Thread verwenden
- Asynchrone API verwenden (kein anderer Thread aus Sicht des Programmierers notwendig)
Für ersteres müsstest du also die Dateioperationen in einem eigenen Thread abwickeln und von dort aus Statusmeldungen übermitteln; für letzteres müsstest du asynchrone API-Funktionen wie etwa ReadFileEx oder WriteFileEx verwenden. _________________ Aktuelle FreeBasic Builds, Projekte, Code-Snippets unter http://users.freebasic-portal.de/stw/
http://www.mv-lacken.at Musikverein Lacken (MV Lacken) |
|
Nach oben |
|
|
BD
Anmeldungsdatum: 15.01.2014 Beiträge: 38
|
Verfasst am: 26.01.2014, 16:24 Titel: |
|
|
Da ich Threadprogrammierung in FreeBASIC insbesondere mit WinAPI-Elementen bis heute nicht gemacht habe, stellt sich mir die Frage der Kommunikation zwischen den beiden Threads (GUI und eigentlichem Kopiervorgang). Hier fehlt mir zur Zeit jeglicher Ansatz.
Die Programmidee ist eigentlich vollkommen simpel. Eine sehr große Datei soll kopiert werden (dauert mehrere Minuten), wobei der Kopierfortschritt in einem Progressbar angezeigt werden soll.
Sicher ist es nicht wirklich empfehlenswert, die eigentliche Programmaufgabe im GUI-Code zu behandeln, aber wie gesagt, da fehlt mr in FreeBASIC der Ansatz und es soll ja auch "nur" eine lauffähige Lösung sein und nicht später in andere Programmiersprachen portiert werden. Demzufolge ist mir hier auch eine "unsaubere" aber funktionierende Lösung recht.
Außerdem kämpfe ich noch gegen das Flackern des Dialogfeldes beim Update, welches eigentlich durch Doublebuffering behebbar wäre, was ich aber bei verschiedenen Versuchen noch nicht abstellen konnte.
Die Dialogeigenschaft WS_EX_COMPOSITED sollte dort helfen, doch ist diese in meinen Versuchen sie einzusetzen, ohne Wirkung geblieben. Das Flackern bleibt.
Falls noch jemand Ideen hat, bitte posten. Irgendwie muss es ja schließlich auch mit FreeBASIC und WinAPI funktionieren. So schnell gebe ich nicht auf. |
|
Nach oben |
|
|
Jojo alter Rang
Anmeldungsdatum: 12.02.2005 Beiträge: 9736 Wohnort: Neben der Festplatte
|
Verfasst am: 26.01.2014, 17:27 Titel: |
|
|
BD hat Folgendes geschrieben: | Da ich Threadprogrammierung in FreeBASIC insbesondere mit WinAPI-Elementen bis heute nicht gemacht habe, stellt sich mir die Frage der Kommunikation zwischen den beiden Threads (GUI und eigentlichem Kopiervorgang). Hier fehlt mir zur Zeit jeglicher Ansatz.
|
Man kann an einen Thread ja beliebige Variablen zu Beginn mitgeben. Alle zur Kommunikation benötigten Variablen können z.B. in einem TYPE abgelegt werden, der beim Start des Threads übergeben wird. _________________ » Die Mathematik wurde geschaffen, um Probleme zu lösen, die es nicht gäbe, wenn die Mathematik nicht erschaffen worden wäre.
|
|
Nach oben |
|
|
BD
Anmeldungsdatum: 15.01.2014 Beiträge: 38
|
Verfasst am: 29.01.2014, 23:14 Titel: |
|
|
Eine Sub in einem neuen Thread zu starten ist in FreeBASIC tatsächlich nicht sonderlich schwer. Nachdem ich die Tutorials durchforstet habe, bin ich schnell fündig geworden. Dennoch bleibt mein Problem. Ob ich den Kopiervorgang in einen separaten Thread abhandeln lasse oder nicht, sobald es um den Update des Progressbars geht, bleibt der Dialog bei Fokusverlust stehen. Ich kann aber den Kopierthread sinnigerweise nur im Select Case-Zweig des "Button-Drückens" starten und muss ihn auch im "Case Else"-Zweig abfragen, wie weit er ist. Doch das führt zu den bekannten Problemen. Bei Fokusverlust hängt der Dialog sofort. Bleibt das Dialogfenster im Fokus läuft es (flackernd) und zeigt den Fortschritt an.
Das Ganze ist doch im Prinzip eine ganz gewöhnliche Geschichte, nur langsam gehen mir die Ideen aus. Sobald es den Fokus verliert, wird es von Windows nach kürzester Zeit sogar komplett mit "keine Rückmeldung" abgeschrieben. Obendrein bleibt trotz Beendigung der Prozess im Taskmanager bestehen und beendet sich nicht.
Wo ist da mein Denkfehler? |
|
Nach oben |
|
|
St_W
Anmeldungsdatum: 22.07.2007 Beiträge: 949 Wohnort: Austria
|
Verfasst am: 30.01.2014, 18:32 Titel: |
|
|
Da der Prüfungsstress derzeit kurzzeitig unterbrochen ist, hab ich ein kleines Beispiel geschrieben, wie man Threads in WinAPI Programmen verwenden könnte für einen länger andauernden Vorgang.
Wie bereits erwähnt, kenne ich jedoch die "best practices" hierzu nicht und habs einfach einen Aufbau gewählt, der mir spontan am Besten zugesagt hat. Leicht möglich, dass sich hierbei Fehler eingeschlichen haben oder generell ein anderer Aufbau besser wäre.
Hier das Beispiel (braucht ca. 2GB freien Speicherplatz):
http://users.freebasic-portal.de/stw/files/prog/fb/misc/WinThreads-demo.zip _________________ Aktuelle FreeBasic Builds, Projekte, Code-Snippets unter http://users.freebasic-portal.de/stw/
http://www.mv-lacken.at Musikverein Lacken (MV Lacken) |
|
Nach oben |
|
|
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1220 Wohnort: Ruhrpott
|
Verfasst am: 31.01.2014, 11:16 Titel: |
|
|
Ich habe zwar keine fertige Lösung für dein Problem, aber vielleicht kann dieses Codebeispiel dir einen Denkanstoß liefern. Es zeigt, wie zwei "wildfremde" Programme auch ohne Pipe Daten austauschen können. Die Elemente innerhalb einer GUI sind ja im Grunde auch nichts anderes als einzelne Fenster.
Beide Codes werden als Konsolenprogramm kompiliert. Ich hoffe, daß es dir weiterhilft.
Code: | 'SENDER
#Include "windows.bi"
Dim As HWND diesesFenster, empfaenger
Dim As Integer x, threadid
Dim As String text
diesesFenster = GetForegroundWindow()
SetWindowText(diesesFenster,"SENDER")
empfaenger = FindWindow(0,"EMPFÄNGER")
threadid = GetWindowThreadProcessId(empfaenger,0)
SetForegroundWindow(empfaenger)
PostThreadMessage(threadid, WM_USER+1, 0, 2) 'bildschirm löschen
Do
empfaenger = FindWindow(0,"EMPFÄNGER")
threadid = GetWindowThreadProcessId(empfaenger,0)
text = Right(Time,5)
PostThreadMessage(threadid, WM_USER+1, 256 * 10 + 0, 4) 'farben setzen
PostThreadMessage(threadid, WM_USER+1, 1 * 256 + 1, 2) 'seite löschen
For x = 0 To Len(text)-1 'text übertragen
PostThreadMessage(threadid, WM_USER+1, text[x], 0)
Next
PostThreadMessage(threadid, WM_USER+1, 0, 1) 'drucken
Print text;" ";threadid
Sleep 100
Loop
|
Code: | 'EMPFÄNGER
#Include "windows.bi"
#Include "fbgfx.bi"
Dim As HWND diesesFenster, sender
Dim As WPARAM wParam
Dim As LPARAM lparam
Dim Shared As MSG nachricht
Dim As String text, g
Dim As Integer threadid, breit, hoch, gzeile, zeilenhoehe, x, fensterhoehe, fensterbreite
fensterhoehe = 20
fensterbreite = 40
SCREENRES fensterbreite, fensterhoehe, 4,, FB.GFX_NO_FRAME Or FB.GFX_ALWAYS_ON_TOP
ScreenInfo breit, hoch
Width breit\8, hoch\16
diesesFenster = GetConsoleWindow()
SetWindowText(diesesFenster,"EMPFÄNGER")
ShowWindow(diesesFenster,SW_HIDE)
sender = FindWindow(0,"SENDER")
Do
sender = FindWindow(0,"SENDER")
GetMessage(@nachricht,NULL,0,0)
Select Case nachricht.message
Case WM_USER+1
Select Case nachricht.lparam
Case 0 'text übertragen
text += Chr(nachricht.wparam)
Case 1 'text drucken
zeilenhoehe = 17
Draw String (0,gzeile * zeilenhoehe), text
gzeile += 1
text = ""
Case 2 'bildschirm löschen
Cls 1
gzeile = 0
Case 3 'cursorposition
Locate HiByte(nachricht.wparam), LoByte(nachricht.wparam)
gzeile = 0
Case 4 'farben
Color HiByte(nachricht.wparam), LoByte(nachricht.wparam)
End Select
Case Else
End Select
Loop
|
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
|
BD
Anmeldungsdatum: 15.01.2014 Beiträge: 38
|
Verfasst am: 03.02.2014, 00:35 Titel: |
|
|
@St_W:
Dein Kopierbeispiel mit der Progressbar sieht sehr interessant und vielversprechend aus, da es auch einen Fokusverlust "übersteht". Du erzeugst das Dialogfenster auch direkt im Programmcode und nicht mittels des Dialogeditors von FBEdit.
Wie ich sehe läuft der Progressbar unterschiedlich bei der Dateierstellung der testfile.tmp und beim wirklichen Kopiervorgang mittels CopyFileEx über die Funktion CopyProgressHandler. Das muss ich mir noch genauer ansehen doch der Denkanstoß müsste mir bereits reichen, um das auch für den eigentlichen Kopiervorgang in meinem Programm zu nutzen.
Der fehlende EnableWindow() war übrigens der entscheidende Befehl, der in meinem Programm das Aufhängen bewirkte, da Meldungen an die Dialogbox geschickt wurden, ohne dass diese sie verarbeiten konnte, da sie beschäftigt war.
Vielen Dank für den konstruktiven Denkanstoß, die Umsetzung in mein Programm müsste mein Problem mit dem Fokusverlust lösen.
@grindstone:
Dein Programmcode ist zwar nicht unbedingt für mein Kopierproblem geeignet, aber trotzdem sehr interessant, da es mir einige Möglichkeiten über die Kommunikation zwischen Fenstern verrät und was mit FreeBASIC da möglich ist. Das kann ich für ein anderes Problem als Denkanstoß nehmen. Da ich in Excel-VBA einen "Word-Dokument zu E-Book-Converter" erstellt habe, weil Excel-VBA recht simpel mit Word kommunizieren kann, schwebt mir da eine Excel-VBA-freie Fassung mittels FreeBASIC vor. Da ist dein Programmbeispiel recht hilfreich.
Zunächst einmal vielen Dank an alle! |
|
Nach oben |
|
|
St_W
Anmeldungsdatum: 22.07.2007 Beiträge: 949 Wohnort: Austria
|
Verfasst am: 03.02.2014, 02:22 Titel: |
|
|
BD hat Folgendes geschrieben: | Wie ich sehe läuft der Progressbar unterschiedlich bei der Dateierstellung der testfile.tmp und beim wirklichen Kopiervorgang mittels CopyFileEx über die Funktion CopyProgressHandler. Das muss ich mir noch genauer ansehen doch der Denkanstoß müsste mir bereits reichen, um das auch für den eigentlichen Kopiervorgang in meinem Programm zu nutzen. |
Grundsätzlich läuft es immer gleich ab: per PostMessage wird dem UI-Thread eine WM mit den neuen Fortschrittsdaten in die Queue gelegt. Das mit dem Datei erstellen und kopieren ist nur ein simples Beispiel zur Demonstration. Für CopyFileEx brauch ich da eine Callback-Funktion "CopyProgressHandler", die regelmäßig aufgerufen wird (jedoch nicht bei jedem der sehr vielen Aufrufe eine WM posten soll, sondern nur wenn sich wirklich etwas signifikant geändert hat; wenn man da zu viele WM postet, wird das System ziemlich lahm - zumindest mein Win7 hier), da CopyFileEx nicht asynchron ausgeführt wird.
In meinem Programm verwende ich EnableWindow nur, um den Button zum Starten zu deaktivieren bzw. wieder aktivieren. Das ganze Fenster zu Disablen habe ich auch probiert, schien mir aber keine gute Idee, da hierbei auch Aktionen auf den Fensterrahmen nicht mehr möglich waren. Deine genaues Problem mit dem Fokus verstehe ich übrigens nicht wirklich - die Aktualisierung des Fensterinhalts ist eigentlich unabhängig davon, ob es gerade den Fokus hat oder nicht. Wie das Dialogfenster erzeugt wird sollte ebenso nicht wirklich eine Rolle spielen. _________________ Aktuelle FreeBasic Builds, Projekte, Code-Snippets unter http://users.freebasic-portal.de/stw/
http://www.mv-lacken.at Musikverein Lacken (MV Lacken) |
|
Nach oben |
|
|
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
|