Seite 3 von 3

Re: ListViewGadget leeren und neu befüllen

Verfasst: 14.04.2010 15:55
von ts-soft
@AndyMars
Dein Code hat ein Problem, es wird ein Event im Callback gestohlen! Es
können ja ausser Cancel auch andere Ereignisse auftreten!

Re: ListViewGadget leeren und neu befüllen

Verfasst: 14.04.2010 16:14
von AndyMars
ts-soft hat geschrieben:@AndyMars
Dein Code hat ein Problem, es wird ein Event im Callback gestohlen! Es
können ja ausser Cancel auch andere Ereignisse auftreten!
Da hast du recht! Aber ich habe es ehrlich gesagt lieber sequentiell - bei mir müssen User warten, bis eine Datei kopiert ist, oder sie müssen abbrechen. Das ist nicht sehr nett, aber sicherer - bei solchen Backgroundsachen muss der Programmierer verdammt gut aufpassen, dass sein Programm keinen Scheiss macht, wenn der User es darauf anlegt...

Re: ListViewGadget leeren und neu befüllen

Verfasst: 14.04.2010 20:09
von frankmannb
AndyMars hat geschrieben:Och Thomas... ich war wieder mal langsamer... /:->

Getesteter Code :D :

Code: Alles auswählen

;CopyFileEx1004.pb
;Beispiel für WinAPI CopyFileEx
;PB 4,4

Procedure.l _CopyFileExCallback(TotalFileSize.q, TotalBytesTransferred.q, StreamSize.q, StreamBytesTransferred.q, dwStreamNumber.l, dwCallbackReason.l, hSourceFile.l, hDestinationFile.l, lpData.l)
  TotalMB=TotalFileSize/1048576
  MBTrans=TotalBytesTransferred/1048576
  SetGadgetText(0,Str(MBTrans)+"/"+Str(TotalMB)+" MB")
  ev=1
  While ev
  	ev=WindowEvent()
  	If ev=#PB_Event_Gadget
  		If EventGadget()=1 ;STOP-Button
  			ProcedureReturn 1 ;1=PROGRESS_CANCEL
  		EndIf
  	EndIf
  Wend
  ProcedureReturn 0 ;0=PROGRESS_CONTINUE
EndProcedure

Procedure.l _CopyFileEx(pSource.s,pTarget.s)
  Erg = CopyFileEx_(@pSource.s, @pTarget.s, @_CopyFileExCallback(), 0, 0, 0)
  ProcedureReturn Erg
EndProcedure

If OpenWindow(0,200,200,80,50,"CopyFileEx Test")
	TextGadget(0,5,5,60,20,"")
	ButtonGadget(1,5,25,60,20,"STOP")
	;hier natürlich bei euch existierende Pfade angeben (mit ner groooossen Datei)
	If _CopyFileEx("D:\_Downloads\3DMark06_v110_installer.exe","D:\_temp\3DMark06_v110_installer.exe")
		Debug "Kopieren erfolgreich"
	Else
		Debug "Fehler beim Kopieren"
	EndIf
	Repeat
		ev=WaitWindowEvent()
	Until ev=#PB_Event_CloseWindow 
	CloseWindow(0)
EndIf
Edit2: Evtl. müsste man einen Abbruch noch besser behandeln, damit nicht einfach ein Fehler auftritt - aber ihr dürft ruhig auch noch ein wenig hirnen...

Edit: Hach sind wir wieder produktiv! Gleich zwei Methoden im Angebot. Jetzt kann sich aber niemand mehr beschweren... ;)

Gruss Andy
Hallo Andy vielen Dank für den Beispielcode. Ich denke davon werde ich schon was für mein Programm verwenden können, werde mal schauen. Eine Frage hätte ich allerdings. Was bedeuten die @ vor den Stringvariablen und warum ist einmal hinter der procedure ein "_" (CopyFileEx_) und das nächste mal _CopyFileExCallback() so wie in diesem beispiel.

Procedure.l _CopyFileEx(pSource.s,pTarget.s)
Erg = CopyFileEx_(@pSource.s, @pTarget.s, @_CopyFileExCallback(), 0, 0, 0)
ProcedureReturn Erg
EndProcedure

kann man irgendwo erlesen was sich hinter den weiteren Parametern von _CopyFileExCallback verbirgt?

Danke und Grüße aus Potsdam
frankmannb

Re: ListViewGadget leeren und neu befüllen

Verfasst: 14.04.2010 21:05
von ts-soft
Naja, meinen Code hab ich zwischendurch mal getestet, ist Fehlerfrei.
Crossplattform, keine API, unterstützt auch x64 usw.
Und um meinen Code zu verstehen genügt die Hilfe, die mit PB mitgeliefert wird,
suchen in MSDN entfällt also.

Hier nochmal eine verfeinerte Version:

Code: Alles auswählen

Procedure CopyFileBuffer(source.s, dest.s, buffersize = 4096)
  Static FD, FS
  Protected *mem, result
  
  If Not FD
    FD = CreateFile(#PB_Any, dest)
    If FD And buffersize >= 1028
      FileBuffersSize(FD, buffersize)
    EndIf
  EndIf
  
  If Not FS
    FS = ReadFile(#PB_Any, source)
    If FS And buffersize >= 1028
      FileBuffersSize(FS, buffersize)
    EndIf
  EndIf
  
  *mem = AllocateMemory(buffersize)
  
  If *mem And FS And FD
    If Loc(FS) + buffersize < Lof(FS)
      ReadData(FS, *mem, buffersize)
      WriteData(FD, *mem, buffersize)
    Else
      buffersize = Lof(FS) - Loc(FD)
      If buffersize
        ReadData(FS, *mem, buffersize)
        WriteData(FD, *mem, buffersize)
      EndIf
      CloseFile(FS)
      CloseFile(FD)
      FD = 0
      FS = 0
      result = #True
    EndIf
    FreeMemory(*mem)
  EndIf
  ProcedureReturn result
EndProcedure

While Not CopyFileBuffer("D:\_Downloads\3DMark06_v110_installer.exe", "D:\_temp\3DMark06_v110_installer.exe")
Wend
wobei die While Wend schleife nur der Demo dient. Tatsächlich soll die Procedure nur bei Timerereignis aufgerufen werden, so kann das Programm normal weiterlaufen.

Re: ListViewGadget leeren und neu befüllen

Verfasst: 14.04.2010 22:19
von frankmannb
@CodeCommander
Auch dir vielen Dank für deine Hilfe mein Problem zu lösen. Ich werde auch versuchen deinen code zu verstehen und ihn in mein Programm einzubauen. Nur ist es erstmal bei mir so das ich eben noch anfänger bin und noch nicht so professional programmieren kann wie Ihr. Ich finde es aber super wie viele von euch hier Ihr bestes geben um anderen zu helfen. Sobald ich eine Version für mein Programm verwendet habe werde ich dies hier kundtun.

grüße aus Potsdam und nochmals vielen Dank
frankmannb

Re: ListViewGadget l&b - bzw. Kopieren mit Fortschrittsanze

Verfasst: 14.04.2010 23:04
von AndyMars
ts-soft hat geschrieben:Crossplattform, keine API, unterstützt auch x64 usw.
Also langsam beginnt mir dein Code zu gefallen. :)

Einzig anmerken müsste man, das keine Datei-Attribute, -Datum und NTFS-DataStreams mit kopiert werden - was vielleicht auch gar nicht so tragisch ist... (die ersten beiden wären auch einfach noch hinzu zu fügen - Get-,Set-,-FileAttributes,-FileDate). (Macht CopyFileEx intern noch einen CRC Test?...)

Antworten muss ich nun dennoch:
frankmannb hat geschrieben:Hallo Andy vielen Dank für den Beispielcode. Ich denke davon werde ich schon was für mein Programm verwenden können, werde mal schauen. Eine Frage hätte ich allerdings. Was bedeuten die @ vor den Stringvariablen und warum ist einmal hinter der procedure ein "_" (CopyFileEx_) und das nächste mal _CopyFileExCallback() so wie in diesem Beispiel.

Procedure.l _CopyFileEx(pSource.s,pTarget.s)
Erg = CopyFileEx_(@pSource.s, @pTarget.s, @_CopyFileExCallback(), 0, 0, 0)
ProcedureReturn Erg
EndProcedure

kann man irgendwo erlesen was sich hinter den weiteren Parametern von _CopyFileExCallback verbirgt?

Danke und Grüße aus Potsdam
frankmannb
Ach. Ja, das ist etwas verwirrend im Beispiel - weil ich sehr ähnliche Namen verwendet hab...

Ich stelle meinen Prozeduren gerne ein _ voran, weil sie dann schneller in der Codevervollständigung erscheinen und sich zudem offensichtlich von internen PB Funktionen unterscheiden. Bei nachfolgendem _ wie bei CopyFileEx_, bedeutet das für PB, dass es sich um eine API-Funktion handelt: CopyFileEx Function

Dieser API-Funktion kann man eine Adresse einer eigenen Funktion angeben, die wiederum von von der API-Funktion aufgerufen wird - deshalb heisst das auch Callback: "zurück rufen". Mit dem @ macht man genau das, man gibt die Adresse einer eigenen Prozedur an - in unserem Fall die von _CopyFileExCallback(). Dieser Prozedur werden einige Daten übergeben - das sind genau diese Parameter, nach denen du fragst: CopyProgressRoutine Callback Function - benutzen muss man diese Parameter nicht - aber die Prozedur muss die Parameter haben.

Ich hab den Code nochmal geändert, mit anderen Prozedurnamen und noch einer kleinen Unterscheidung, ob vom User unterbrochen wurde.

TS-Soft hat recht, dass mein Eventhandling nicht vom Feinsten ist... So ist das Schliessen des Fensters während des Kopierens zBsp nicht möglich... Wobei man sich darüber noch streiten könnte, ob das möglich sein soll... ^^
Was ich noch gut fände, wäre ein separater Dialog fürs Kopieren und/oder das Deaktivieren von anderen Dialogen - so dass es für den User unzweifelhaft klar ist, dass nichts anderes geht, weil jetzt gerade kopiert wird. Ich behaupte jetzt einfach mal, dass dies der separate Dialog für's Kopieren IST... ;)

Code: Alles auswählen

;CopyFileEx1004b.pb
;Beispiel für WinAPI CopyFileEx
;PB 4.4

Procedure.l WinCopyFileCallBack(TotalFileSize.q, TotalBytesTransferred.q, StreamSize.q, StreamBytesTransferred.q, dwStreamNumber.l, dwCallbackReason.l, hSourceFile.i, hDestinationFile.i, lpData.l)
  Shared SWinCopyFileBreak.b
  If TotalFileSize
  	Proz=100*TotalBytesTransferred/TotalFileSize
	  SetGadgetState(2,Proz)
  EndIf
  ev=1
  While ev
  	ev=WindowEvent()  ;hier werden alle Events gefressen - ausser die folgenden...
  	If ev=#PB_Event_Gadget
  		If EventGadget()=3 ;Abbrechen-Button
  			SWinCopyFileBreak=1
				ProcedureReturn 1 ;1=PROGRESS_CANCEL -> sagt CopyFileEx_, dass es abbrechen soll
  		EndIf
  	ElseIf ev=#PB_Event_CloseWindow
			SWinCopyFileBreak=2
			ProcedureReturn 1 ;1=PROGRESS_CANCEL
		EndIf
  Wend
  ProcedureReturn 0 ;0=PROGRESS_CONTINUE
EndProcedure

Procedure.l WinCopyFile(pSource.s,pTarget.s)
	Shared SWinCopyFileBreak.b
	If OpenWindow(0,200,200,400,105,"Datei Kopieren")
		TextGadget(0,5,5,390,20,"Kopiere von: "+pSource,#PB_Text_Center)
		TextGadget(1,5,25,390,20,"nach: "+pTarget,#PB_Text_Center)
		TextGadget(2,160,50,60,20,"")
		ProgressBarGadget(2,20,50,360,20,0,100)
		ButtonGadget(3,160,80,80,20,"Abbrechen")
		
		SWinCopyFileBreak=0
  	Erg = CopyFileEx_(@pSource.s, @pTarget.s, @WinCopyFileCallBack(), 0, 0, 0) ;Windows API-Befehl
  	
		If Erg=0
			Erg=-SWinCopyFileBreak
		EndIf
		
		CloseWindow(0)
	EndIf	
  ProcedureReturn Erg
EndProcedure

;hier natürlich bei euch existierende Pfade angeben (mit ner groooossen Datei)
Erg=WinCopyFile("D:\_Downloads\3DMark06_v110_installer.exe","D:\_temp\3DMark06_v110_installer.exe")

If Erg=1 ;hier könnte man selber auf das Fehler-/Abbruch-/Ergebnis reagieren
	Debug "Kopieren erfolgreich"
Else
	If Erg=-1
		Debug "Kopieren abgebrochen"
	ElseIf Erg=-2
		Debug "Kopieren abgebrochen durch Fenster schliessen"
	Else
		Debug "Fehler beim Kopieren"
	EndIf
EndIf

Re: ListViewGadget leeren und neu befüllen

Verfasst: 18.04.2010 13:54
von frankmannb
ts-soft hat geschrieben:wobei die While Wend schleife nur der Demo dient. Tatsächlich soll die Procedure nur bei Timerereignis aufgerufen werden, so kann das Programm normal weiterlaufen.
Hallo TS-Soft,

ich versuche gerade deinen Code bei mir im Programm unterzubringen. Das Klappt sofern man nicht in das Fenster klickt prima. Aber ich bekomme es nicht hin das das Programm "normal" weiterläuft (ohne keine Rückmeldung ) oder das ich das Programm abbrechen kann. Irgendwie stehe ich hier auf dem Schlauch. Könntest du mir hier nochmal helfen und mir ein kleines Beispiel mit einem Abbrechen bzw. fortsetzten Button coden?.

Vielen Dank
frankmannb