0

I have a simple program, single threaded, with a main form that has the controls, and another form with a bitmap and some TLabels on top of the bitmap that have the score, clock time, penalty time teams, and period.

In my main form, I have a TTimer with a 500ms interval that, when started, computes the clock and any penalty times as it counts down and updates the labels on the other form. The basic code in the timer event is:

  // decrement main clock
  if ClockEndTime > Now then
    begin
      MainClockTime := ClockEndTime - Now;
...
  else
    // clock time is expired
    begin
      MainClockTime := ZeroTime;
      tmrMainClock.Enabled := False;
      ClockRunning := False;
...
    end;
    // update the timer values and overlay
    lblClock.Caption := FormatDateTime('n:ss',MainClockTime);
    fOverlay.lblClock.Caption := lblClock.Caption;
...

After 1 to 2 hours, the main from will stop responding, and I get a "Canvas does not allow" exception. The form with the labels and the bitmap has a blank spot where the time clock should be.

Exception error

I have to close and restart the program for it to work again. None of the controls on the main form will respond even after I acknowledge the exception. It seems like the TTimer event might still be firing and having the same draw issues. I have a hotkey for another function that still works while the program appears to be frozen.

I tried an exception handler in the timer event around where I update the label captions. It never shows my message box, but appears that it is going to the exception handler as I added code to close the other form, wait some time, and then reopen it. The other form goes away and returns, but then nothing appears to draw on it. It shows what is in the background behind it.

Do you think it is an issue because my main form code is updating labels on the other form?

It is hard to test, as it involves waiting 1 to 2 hours for the issue to re-appear. I am wondering, has anyone experienced something like this?

I am using Delphi 10.4 Community Edition, and the program runs on Windows 11.

One more interesting thing I noted the last time I tested is that, while my program was trying to update the labels, it also caused a GIF image on a completely unrelated Delphi 7 program that I have running to move positions. I have no idea why one program would impact the other. The only thing they have in common is the use of a TTimer.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Craig
  • 11
  • 2
  • 2
    You're most likely leaking handles. Let your program run, [open the task manager's "details" tab and watch your process allocating more and more handles and/or GDI objects](https://superuser.com/a/1206362/650342). – AmigoJack Dec 08 '22 at 01:20
  • OK, I see the Handles remains steady at 220. The GDI Objects do appear to constantly increase. So they must reach some maximum number I am guessing and that is what causes it to hang up. Ok, I have to learn about GDI Objects apparently and try to figure out what I'm doing here to cause them. Thanks. – Craig Dec 08 '22 at 04:04
  • Please show your whole timer code. Do you do manual drawing somewhere in the timer event? Have you enabled range and overflow checking in the compiler settings? – Delphi Coder Dec 08 '22 at 06:55

1 Answers1

1

The issue was a function I was using to get a pixel color from the screen. I called this function in my TTimer event.

function TfConfigureMute.GetPixelColor(X,Y: integer): TColor;
var
  dc: HDC;

begin
  // Get Device Context of windows desktop
  dc := GetDC(0);
  // Read the color of the pixel at the given coordinates
  Result := GetPixel(dc,X,Y);
  ReleaseDC(0,dc);
end;

I was originally missing the ReleaseDC(0,dc); line. So that was the leak.

Thanks for your time.

Craig
  • 11
  • 2