Toon posts:

[Delphi] problemen met while

Pagina: 1
Acties:
  • 153 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Hallo allemaal,

Ik heb maar even een nieuw topic geopent, omdat mijn volgende probleem niet zo zeer met scanline te maken heeft, maar met de while loop daarom heen. De onderstaande code werkt perfect zonder de while loop. Maar als ik de while loop hieraan toevoeg, dan werkt het niet meer.

Ik had deze code eerst achter een TTimer hangen. Dat werkte perfect. De reden dat ik deze aan een buttonclick aan een while-loop wil hangen, is dat deze code zo snel mogelijk achterelkaar uitgevoerd dient te worden. Een timer kan ik niet lager zetten dan 1 ms.

Zodra ik while toevoeg aan de code, werkt de code helemaal niet meer! Weet iemand misschien wat ik hier fout doe?

code:
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
const
  RectWidth = 290;
  RectHeight = 1;
  RectLeft = 866;
  RectTop = 660;
var
  Bitmap: TBitmap;
  DC: HDC;
  w, h, L, T, x, y, color: Integer;
  PixelPtr: PRGBQuad;
  I: Integer;
 begin
 while B = False do begin
   I := 0;
  Bitmap := TBitmap.Create;
  try
    w := RectWidth;
    h := RectHeight;
    L := RectLeft;
    T := RectTop;
    Bitmap.Width := w;
    Bitmap.Height := h;
    DC := GetDC(0);
    try
      BitBlt(Bitmap.Canvas.Handle, 0, 0, w, h,
             DC, L, T, SRCCOPY);
    finally
      ReleaseDC(0, DC);
    end;
    Bitmap.PixelFormat := pf32Bit;
    for y := 0 to h - 1 do
    begin
      PixelPtr := PRGBQuad(Bitmap.ScanLine[y]);
      for x := 0 to w - 1 do
      begin
        I := I +1;
        Inc(PixelPtr);
        Color := PixelPtr^.rgbRed;
        if Color = 0 then begin Exit; end;
        if Color = 227 then begin SetCursorPos (L+I, T); end;
        if Color < 50  then begin SetCursorPos (L+I, T); end;
        if Color = 208 then begin SetCursorPos (L+I+50, T); end;
      end;
    end;
  finally
    Bitmap.Free;
    end;
  end;
end;

  • martijn_brinkers
  • Registratie: November 2001
  • Laatst online: 31-10-2025
Wat is B? (copy paste foutje?)

Verwijderd

Waar definieer je B voordat je de loop ingaat?
Ik zie in ieder geval nergens een definitie staan.
verder zie ik ook niet waar je die B in de loop zelf aanpast.

Het is lang geleden dat ik met Delphi heb gewerkt, maar is het niet zo dat als je in een loop zit je geen waardes kunt aanpassen van buiten de loop? (dus in dit geval die B wat het ook moge zijn)

Verwijderd

B is voor zover ik kan zien een variabele..
While 'B' = false
zo duidelijk ( notatie is denk ik fout ;) )

  • Kevinp
  • Registratie: Juni 2001
  • Laatst online: 18-08 13:05
die zal wel lekker loopen als b niet true wordt zolang b false is(dus altijd want wordt niet true) gaat hij door.

d'r is maar één ding in het leven wat moet, en dat is dood gaan.


  • klinz
  • Registratie: Maart 2002
  • Laatst online: 10-08 15:44

klinz

weet van NIETS

Je moet binnen de loop (als je dit al zo wilt doen) een Application.ProcessMessages zetten. Wat je nu doet is echter niet verstandig. Je blockt je applicatie door op een event een oneindige lus te creeeren. Of wordt de variabele b nog ergens gewijzigd?

Hoe dan ook, je kunt beter een thread in combinatie met de multimedia timers gebruiken. De multimedia timers hebben een hogere resolutie (tot 1ms volgens mij). Als dat nog niet genoeg is kun je over stappen op Performance Counters.

BTW: de resolutie van TTimer is nog slechter dan die 1ms. Je kunt 'm wel op 1ms zetten, maar de werkelijke resolutie is ~55 ms.

[ Voor 12% gewijzigd door klinz op 30-03-2003 22:46 ]


Verwijderd

Topicstarter
B is een public variabel (Boolean) die altijd false is. Het is de bedoeling dat deze procedure inderdaad eeuwig door blijf gaan, maar ipv dat werkt de hele procedure met de toevoeging van de while loop niet

  • klinz
  • Registratie: Maart 2002
  • Laatst online: 10-08 15:44

klinz

weet van NIETS

De procedure werkt wel, maar je ziet geen output omdat er geen messages meer geprocessed worden. Daarom moet je dus de Application.ProcessMessages toevoegen.

Verwijderd

Topicstarter
klinz schreef op 30 March 2003 @ 22:42:
Je moet binnen de loop (als je dit al zo wilt doen) een Application.ProcessMessages zetten. Wat je nu doet is echter niet verstandig. Je blockt je applicatie door op een event een oneindige lus te creeeren. Of wordt de variabele b nog ergens gewijzigd?

Hoe dan ook, je kunt beter een thread in combinatie met de multimedia timers gebruiken. De multimedia timers hebben een hogere resolutie (tot 1ms volgens mij). Als dat nog niet genoeg is kun je over stappen op Performance Counters.

BTW: de resolutie van TTimer is nog slechter dan die 1ms. Je kunt 'm wel op 1ms zetten, maar de werkelijke resolutie is ~55 ms.
Het is de bedoeling dat deze procedure zo snel mogelijk achter elkaar doorgaat tot het programma wordt gesloten. Het liefst zou ik er helemaal geen timer tussen willen hebben. Als de procedure eindigt moet deze gewoon weer van voorn af aan beginnen.

  • klinz
  • Registratie: Maart 2002
  • Laatst online: 10-08 15:44

klinz

weet van NIETS

Verwijderd schreef op 30 March 2003 @ 22:54:
[...]


Het is de bedoeling dat deze procedure zo snel mogelijk achter elkaar doorgaat tot het programma wordt gesloten. Het liefst zou ik er helemaal geen timer tussen willen hebben. Als de procedure eindigt moet deze gewoon weer van voorn af aan beginnen.
Ja, maar daar ga je dus het tegen het principe van events in. Je blokkeert namelijk alle events. Dus ook paintevents. Gevolg: je ziets niets meer. Je ontkomt er waarschijnlijk niet aan om een thread icm de multimedia timers te gebruiken.

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 22:26

Tomatoman

Fulltime prutser

Verwijderd schreef op 30 March 2003 @ 22:45:
B is een public variabel (Boolean) die altijd false is. Het is de bedoeling dat deze procedure inderdaad eeuwig door blijf gaan, maar ipv dat werkt de hele procedure met de toevoeging van de while loop niet
Je kunt de regel
Delphi:
1
while B = False do begin
beter vervangen door
Delphi:
1
while True do begin
want dan heb je die variabele helemaal niet nodig.

Een goede grap mag vrienden kosten.


Verwijderd

Topicstarter
Verwijderd schreef op 30 maart 2003 @ 22:45:
B is een public variabel (Boolean) die altijd false is. Het is de bedoeling dat deze procedure inderdaad eeuwig door blijf gaan, maar ipv dat werkt de hele procedure met de toevoeging van de while loop niet
dus daarom verplaatst mijn cursor ook niet.... Waar zou ik die processmessage neer moeten zetten in mijn procedure?

  • klinz
  • Registratie: Maart 2002
  • Laatst online: 10-08 15:44

klinz

weet van NIETS

Verwijderd schreef op 30 March 2003 @ 23:04:
[...]


dus daarom verplaatst mijn cursor ook niet.... Waar zou ik die processmessage neer moeten zetten in mijn procedure?
Maakt niet zoveel uit. Bijvoorbeeld voor het einde van de buitenste for-loop. Maar zoals ik al meldde: je kunt bent beter af met een seperate thread.

Verwijderd

Topicstarter
klinz schreef op 30 March 2003 @ 23:06:
[...]

Maakt niet zoveel uit. Bijvoorbeeld voor het einde van de buitenste for-loop. Maar zoals ik al meldde: je kunt bent beter af met een seperate thread.
Dus een while loop die op zijn beurt weer een thread aanroept die de bovenstaande procedure uitvoerd.

Verwijderd

Topicstarter
by the way: Is dit de stelst mogelijke procedure om een bepaald gedeelte van het scherm te analyseren op veranderingen, of is er nog ies snellers?

  • Delphi32
  • Registratie: Juli 2001
  • Laatst online: 22-08 10:56

Delphi32

Heading for the gates of Eden

Als je dit al wilt oplossen met de while loop, dan moet de Application.ProcessMessages direct na de
code:
1
while True do
komen te staan. Dan geef je je scherm de kans te updaten voordat je de analyse van het scherm doet.
Los je het op met een thread (heeft ook mijn voorkeur trouwens), dan plak je gewoon alle code die je hierboven hebt staan, naar de TThread.Execute. Dus NIET de while loop bij je button houden en dan steeds die thread starten vanuit de button handler, maar de thread al het werk laten doen.
Ik ben geen thread expert, ik zou niet weten of je dan ook een Application.ProcessMessages nodig hebt. Probeer het even, of wie weet heeft iemand anders in deze thread hierover een mening.
Bij elkaar heb je dan ongeveer de snelste manier te pakken volgens mij.

  • klinz
  • Registratie: Maart 2002
  • Laatst online: 10-08 15:44

klinz

weet van NIETS

Verwijderd schreef op 30 maart 2003 @ 23:45:
[...]


Dus een while loop die op zijn beurt weer een thread aanroept die de bovenstaande procedure uitvoerd.
Nee, de while loop in een aparte thread zetten.
Verwijderd schreef op 30 maart 2003 @ 23:46:
by the way: Is dit de stelst mogelijke procedure om een bepaald gedeelte van het scherm te analyseren op veranderingen, of is er nog ies snellers?
Nee, pollen is performancetechnisch bijna nooit handig. Het belast het systeem onnodig en je hebt kans dat je dingen mist.

Je kunt beter gebruik de WndProc hooken door gebruik te maken van SetWindowLong(Image1.Handle, GWL_WNDPROC, NewWndProc).

NewWndProc is dan het adres van de nieuwe wndproc. In deze procedure reageer je op de message WM_PAINT zodat je precies weet wanneer er iets getekend is. Je kunt zelfs opvragen welk gedeelte van het scherm gewijzigd is met BeginPaint(). Als het geen WM_PAINT message is roep je CallWindowProc() aan.

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

In de thread is Application.ProcessMessages juist niet nodig, aangezien je juist de thread maakt om deze aanroep te voorkomen. Zorg er wel voor dat je vanuit je thread niet zomaar je main-thread aanspreekt. Dit kun je doen dmv Synchronize (of een andere synchronisatie methode).

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Topicstarter
klinz schreef op 31 March 2003 @ 00:12:
[...]

Nee, de while loop in een aparte thread zetten.

[...]

Nee, pollen is performancetechnisch bijna nooit handig. Het belast het systeem onnodig en je hebt kans dat je dingen mist.

Je kunt beter gebruik de WndProc hooken door gebruik te maken van SetWindowLong(Image1.Handle, GWL_WNDPROC, NewWndProc).

NewWndProc is dan het adres van de nieuwe wndproc. In deze procedure reageer je op de message WM_PAINT zodat je precies weet wanneer er iets getekend is. Je kunt zelfs opvragen welk gedeelte van het scherm gewijzigd is met BeginPaint(). Als het geen WM_PAINT message is roep je CallWindowProc() aan.
wow, dit gaat me iets te snel. Ik ben niet echt heel erg bekent met de windows API.

SetWindowLong, Daarmee kun je een attribuut van een window veranderen toch? Ik heb ffe in de Windows SDK gekeken, maar daar werd ik niet echt veel wijzer van, wat doet deze functie precies?

Hoe kan ik reageren op zo'n WM_PAINT message?

Verder vond ik dit over CallWindowProc in de Windows SDK: The CallWindowProc function passes message information to the specified window procedure. :?

Kan je me globaal uitleggen hoe deze functies werken en wat ze precies doen?

Verwijderd

offtopic:
In plaats van de functie in een timer te zetten, kun je beter de procedure Sleep() in de while loop zetten

  • VisionMaster
  • Registratie: Juni 2001
  • Laatst online: 18-07 20:32

VisionMaster

Security!

Timers zijn zowisso tricky :X
Als je procedure binnen de Timer gestelde tijd (nu 1ms) niet klaar is met z'n executie, dan volgt al weer een nieuw Event van uit de Timer tenzij je de Timer na het starten van je Timer Event eventjes op Enabled = False zet.
Aan het eind van je Timer event pleur je het ding weer aan en begint het weer binnen je gestelde Timer tijd (1 ms) weer een Event te triggeren.
Je heb momenteel niet eens een Application.ProcesMessages er tussen dus de Events staan klaar, maar moeten wachten tot je While loop is uit geraast om Uberhaupt op de EventStack te komen van je applicatie.

Er zijn dus meerdere oplossingen voor je probleem, maar ongeacht je keuze is het gebruik van een Application.ProcesMessages op een gunstig plekje ZEER aanteraden.

Het is afhankelijk van je keuze:
-Je kan je while lus gebruiken, misschien opschonen en Application.ProcesMessages toevoegen aan een kritiek punt in de lus (begin, eind of specifieke voorwaarde -> pas hier mee op).
-Je kan je while lus aan passen, zodat het in iedere loop een Thread maakt die je handeling van je lust uitvoerd. In je while-lus heb je nog een Application.ProcesMessages nodig, want je systeem raast maar door met Threads maken en runnen en gunt je alleen tijd om iets te doen tussen het bouwen van je Thread en het werkelijk uitvoeren van je Thread (niet noemens waardig = fout).
-Je kan een Thread starten die je hele while lus herbergt. In die lus moet je dezelfde constructies bouwen als mijn eerste punt. Het staat nu in een aparte Thread die jou zou kunnen stoppen, maar als je while zonder Application.ProcesMessages draait heb je er nog niets aan want dan heb je hetzelfde als dat je nu heb alleen in een Applicatie en een Thread ervan gesplitst. (voordeel 0=NULL=NIL)

suc6 :Y)

I've visited the Mothership @ Cupertino


  • klinz
  • Registratie: Maart 2002
  • Laatst online: 10-08 15:44

klinz

weet van NIETS

Verwijderd schreef op 31 maart 2003 @ 00:37:
[...]


wow, dit gaat me iets te snel. Ik ben niet echt heel erg bekent met de windows API.

SetWindowLong, Daarmee kun je een attribuut van een window veranderen toch?
Inderdaad. Maar je kunt er ook de WndProc van een window mee zetten (met GWL_WNDPROC als parameter van SetWindowLong).
code:
1
2
3
4
5
var
   OldWndProc: Pointer;

   OldWndProc:=Pointer(SetWindowLong(Image1.Handle,
       GWL_WNDPROC, LongInt(@NewWndProc)));

De oude waarde van de WndProc moet je bewaren omdat je deze bij beeindiging van je programma weer terug moet zetten.
Ik heb ffe in de Windows SDK gekeken, maar daar werd ik niet echt veel wijzer van, wat doet deze functie precies?
Hoe kan ik reageren op zo'n WM_PAINT message?
In je nieuwe messagehandler kun je WM_PAINT opvangen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
function NewWndProc(hWnd: HWND; Msg: longint;
     wParam: longint; lParam: longint): longint stdcall;
var
    Rect: TRect;    
begin
    if Msg = WM_PAINT then
    begin
        GetUpdateRect(Image1.Handle, @Rect, false); 
        // *** voer hier je bewerkingen op Rect uit ***
    end;
    Result :=  CallWindowProc(OldWndProc, hWnd,
          Msg, wParam, lParam);
end;

Terugzetten van de WndProc:
code:
1
2
   SetWindowLong(Image1.Handle, GWL_WNDPROC,
             LongInt(OldWndProc));
Verder vond ik dit over CallWindowProc in de Windows SDK: The CallWindowProc function passes message information to the specified window procedure. :?
Die moet je dus als laatste aanroepen zodat de andere messages (en WM_PAINT!) gewoon afgehandeld worden.

Nog een vraagje: wat wil je hier eigenlijk mee bereiken? Als je zelf op de TImage (waarom geen TPaintBox?) tekent, weet je toch wanneer en waar je tekent? Of wil je iets anders, zoals de desktop, capturen voor een remote admin (of een trojan :))?

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 22:26

Tomatoman

Fulltime prutser

Ik begrijp niet zo goed wat je aan het doen bent. Je controleert zo vaak mogelijk of de pixels in een bitmap niet zijn veranderd door alle pixels stuk voor stuk af te lopen. Dat betekent dus dat je processor continu voor 100% belast wordt, terwijl het heel goed mogelijk is dat de gebruiker de krant zit te lezen en de computer niet aanraakt.

Laat ik er een metafoor bij pakken, waarbij je programma op iets moet doen zodra er een brief wordt bezorgd (in jouw geval: een pixel een bepaalde kleur krijgt). Volgens de huidige opzet is je programma zo vaak mogelijk naar de brievenbus aan het rennen om te kijken of de post er al is. En jij bent bezig om je programma nog harder te laten rennen. Waarom pak je het probleem niet bij de bron aan? Luister bijvoorbeeld naar het klepperen van de brievenbus. Dat scheelt tig keer rennen en het maakt alles veel eenvoudiger. Het probleem dat je zou moeten oplossen is dus: hoe luister ik naar het klepperen van de brievenbus?

Terug naar je applicatie. Waardoor veranderen die pixels van kleur? Doordat er iets op een canvas getekend wordt? Dan is het klepperen van de brievenbus TCanvas.OnChange. Doordat je programma zelf de bitmap tekent? Dan moet je een event aan dat tekenwerk hangen.

Een goede grap mag vrienden kosten.


  • VisionMaster
  • Registratie: Juni 2001
  • Laatst online: 18-07 20:32

VisionMaster

Security!

tomatoman schreef op 31 March 2003 @ 01:53:
Ik begrijp niet zo goed wat je aan het doen bent. Je controleert zo vaak mogelijk of de pixels in een bitmap niet zijn veranderd door alle pixels stuk voor stuk af te lopen. Dat betekent dus dat je processor continu voor 100% belast wordt, terwijl het heel goed mogelijk is dat de gebruiker de krant zit te lezen en de computer niet aanraakt.

Laat ik er een metafoor bij pakken, waarbij je programma op iets moet doen zodra er een brief wordt bezorgd (in jouw geval: een pixel een bepaalde kleur krijgt). Volgens de huidige opzet is je programma zo vaak mogelijk naar de brievenbus aan het rennen om te kijken of de post er al is. En jij bent bezig om je programma nog harder te laten rennen. Waarom pak je het probleem niet bij de bron aan? Luister bijvoorbeeld naar het klepperen van de brievenbus. Dat scheelt tig keer rennen en het maakt alles veel eenvoudiger. Het probleem dat je zou moeten oplossen is dus: hoe luister ik naar het klepperen van de brievenbus?

Terug naar je applicatie. Waardoor veranderen die pixels van kleur? Doordat er iets op een canvas getekend wordt? Dan is het klepperen van de brievenbus TCanvas.OnChange. Doordat je programma zelf de bitmap tekent? Dan moet je een event aan dat tekenwerk hangen.
Oei... :X
ik heb helemaal over het hoofd gezien wat de TopicStarter nu eigenlijk wou doen.... You've got a point Tomatoman. Op deze manier is de topic starter een gebeurtenis (lees: event) in het programma aan het opsporen door eigen zoek methoden terwijl de applicatie zelf al een mooi event maakt, waar op je kan handelen. |:(
Dus je while-lus-code kan je uitvoeren in het getriggerde event . 8)7

I've visited the Mothership @ Cupertino


  • OZ-Gump
  • Registratie: November 2002
  • Laatst online: 26-06 10:37

OZ-Gump

terug van weggeweest

_/-\o_ @ tomatoman.
Geniale metafoor ;)

En je hebt natuurlijk helemaal gelijk. Ik denk dat dit er eentje is die valt onder het kopje [alg] slechtste prog voorbeelden.

My personal website


Verwijderd

Topicstarter
klinz schreef op 31 March 2003 @ 01:26

Nog een vraagje: wat wil je hier eigenlijk mee bereiken? Als je zelf op de TImage (waarom geen TPaintBox?) tekent, weet je toch wanneer en waar je tekent? Of wil je iets anders, zoals de desktop, capturen voor een remote admin (of een trojan :))?
Het is de bedoeling dat bepaalde pixels op mijn scherm gecontroleerd worden op veranderingen.

  • klinz
  • Registratie: Maart 2002
  • Laatst online: 10-08 15:44

klinz

weet van NIETS

Van het huidige programma of van andere programma's? In het eerste geval kun je, zoals anderen al beschreven, gebruik maken van het Canvas.OnChange event. Je hebt immers gewoon toegang tot het canvas van je programma.

In het tweede geval ontkom je niet aan het omleiden van de WndProc zoals ik boven beschreef.

Verwijderd

Topicstarter
Dus eigenlijk moet ik de onderstaande code in een thread zetten. En vervolgens deze als een global hook installeren met SetWindowHookEx
klinz schreef op 31 March 2003 @ 01:26:
[...]

Inderdaad. Maar je kunt er ook de WndProc van een window mee zetten (met GWL_WNDPROC als parameter van SetWindowLong).
code:
1
2
3
4
5
var
   OldWndProc: Pointer;

   OldWndProc:=Pointer(SetWindowLong(Image1.Handle,
       GWL_WNDPROC, LongInt(@NewWndProc)));

De oude waarde van de WndProc moet je bewaren omdat je deze bij beeindiging van je programma weer terug moet zetten.

[...]

In je nieuwe messagehandler kun je WM_PAINT opvangen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
function NewWndProc(hWnd: HWND; Msg: longint;
     wParam: longint; lParam: longint): longint stdcall;
var
    Rect: TRect;    
begin
    if Msg = WM_PAINT then
    begin
        GetUpdateRect(Image1.Handle, @Rect, false); 
        // *** voer hier je bewerkingen op Rect uit ***
    end;
    Result :=  CallWindowProc(OldWndProc, hWnd,
          Msg, wParam, lParam);
end;

Terugzetten van de WndProc:
code:
1
2
   SetWindowLong(Image1.Handle, GWL_WNDPROC,
             LongInt(OldWndProc));


[...]

Die moet je dus als laatste aanroepen zodat de andere messages (en WM_PAINT!) gewoon afgehandeld worden.

Nog een vraagje: wat wil je hier eigenlijk mee bereiken? Als je zelf op de TImage (waarom geen TPaintBox?) tekent, weet je toch wanneer en waar je tekent? Of wil je iets anders, zoals de desktop, capturen voor een remote admin (of een trojan :))?

  • Paul
  • Registratie: September 2000
  • Laatst online: 19-08 15:39
Verwijderd schreef op 31 maart 2003 @ 22:15:
Dus eigenlijk moet ik de onderstaande code in een thread zetten. En vervolgens deze als een global hook installeren met SetWindowHookEx
[...]
Nee, je moest of je while-loop in een thread zetten, of een hook setten :) Die hook vangt alle messages af waardoor je in je eventhandler kunt kijken of het de paint-message was :) Die laatste is veel processor-vriendlijker :)

Het deel van het scherm dat je wilt "bewaken", is dat onderdeel van je eigen prog?

"Your life is yours alone. Rise up and live it." - Richard Rahl
Rhàshan - Aditu Sunlock

Pagina: 1