Solved

Canvas does not allow drawing...

Posted on 2004-04-02
36
14,114 Views
Last Modified: 2012-05-04
Dear experts:

I'm back with more of my lack of knowledge in Delphi ;)

For those of you that remember my RAS application, well it's working very well thanks to the EE experts, although I'm having another problem:

Every now and then I get a "canvas does not allow drawing" error (this is what the Delphi debuger sends). I've done some testing and finally spotted a common place where it happens.

I don't know if this info is relevant but I'm giving it anyway in case it helps:
The error occurs when the user tries to dial the phone number under a phone-central without the -  9,  - prefix to get a line. So the central returns a beeping similar to the "line bussy" and the application returns a user friendly error (coded by me) telling the line is bussy. Then the application attemps to dial again (since it's default config is to make 3 redial attempts) with the common close-line/dial-again that works like a charm when the phone number was entered correctly. When it tries to dial again, my application crashes with the "canvas does not allow drawing" error.

I read a bit and the explanations for this error that I found (lack of system resources) don't seem to apply to me. I read about a possible solution Canvas.Lock or Canvas.Trylock but I don't know where to place that.

Any ideas or suggestions are very welcome.

Thank you,

Esopo.
0
Comment
Question by:Esopo
  • 14
  • 11
  • 9
  • +2
36 Comments
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10746265
Common issues:

1.) you may have run out of resources, which means a new DC cannot be
allocated. Usually this is a result of not freeing unused DCs or other
objects

2.) you are attempting to draw to an device context that is not yet valid.
Such as in the create constructor of a component.

3) you are trying to draw on a canvas while your last draw-operation has
not finished yet. In this case you can use Canvas.Lock or Canvas.TryLock to fix it.

This can also be a problem if attempting to draw on a control's canvas before Loaded has been called for the component. A little bit of code would help to further see where the problem might be. But anyways, if you have some drawing code, check the component first to make sure that the ComponentState does not contain the csLoading flag. If it does, the component should not be drawn upon until after this flag is removed (which is when the component is fully loaded). You can also try the Canvas.Lock before drawing/painting, and unlock when done

if Canvas.TryLock then
begin
 try
   // .. do painting
 finally
  Canvas.Unlock;
 end;
end;
 
--------------

Regards,
Russell
0
 
LVL 10

Assisted Solution

by:Jacco
Jacco earned 50 total points
ID: 10746860
I had this error once when I was using Canvas.TextWidth and Canvas.TextHeight to measure things. The solution was to create a separate Canvas myself like this:

MyCanvas := TCanvas.Create;
MyCanvas.Handle := GetDC(0);
MyCanvas.Font := Font;
MeasuredWidth := MyCanvas.TextWidth('the text to measure');
MyCanvas.Free;

Regards Jacco
0
 
LVL 14

Author Comment

by:Esopo
ID: 10750655
Thanks for posting guys,

The thing is, I can't seem to spot which line of my code is causing the problem.
I'd love to post some code, but my guess is if I knew which line of code was the one, I could probably fix it myself with what I've read so far, very similar to rllibby's comment.

I haven't hand coded anything related to canvas. All I've done so far is inserting common objects (buttons, images, etc). Any ideas?

Esopo.
0
 
LVL 7

Expert Comment

by:sftweng
ID: 10751267
If you are using multi-threading, you may be trying to do a GUI update from a thread other than the main one. In those threads, you must use "Synchronize".
0
 
LVL 1

Assisted Solution

by:ricswika
ricswika earned 50 total points
ID: 10779154
I've gotten this error with poorly designed 3rd party components, by setting a property before Parent has been assigned. Tracing their code revealed an attempt to access the canvas, which won't work when is no parent control has been assigned. The lame workaround I found was to set Visible false when Parent isn't assigned, which prevented entering the branch of code that accesses the canvas.

This doens't sound like your probelm, but I thought I'd post this to possible help others.
0
 
LVL 14

Author Comment

by:Esopo
ID: 10779581
ricswika,
Thanks for posting, anything counts. I think the Internet lacks enough info on this issue.

I don't know how to trace back to my problem. I get the CPU window, but don't know hot to use it, could anyone post a link to some sort of tutorial or article so I can learn?

sftweng,
Where do I put the Synchronize?
0
 
LVL 7

Expert Comment

by:sftweng
ID: 10779635
Esopo, any procedure call that interfaces with the GUI needs to take place within a Synchronize procedure.

You can see an example here, created within Delphi by going to "File -> New -> Other -> Thread Object":

unit Unit2;

interface

uses
  Classes;

type
  MyThread = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;

implementation

{ Important: Methods and properties of objects in VCL or CLX can only be used
  in a method called using Synchronize, for example,

      Synchronize(UpdateCaption);

  and UpdateCaption could look like,

    procedure MyThread.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end; }

{ MyThread }

procedure MyThread.Execute;
begin
  { Place thread code here }
end;

end.
0
 
LVL 14

Author Comment

by:Esopo
ID: 10779777
sftweng,
How can I identify when something is outside the main thread?
I'm used to VB and am in the process of switching to Delphi so there are this things I still don't get.
This is an example of my code:

**********************
procedure MostrarCiudadesProv(Provincia:integer);
var
ContCiudad:integer;

begin
ContCiudad:=1;

FrmPrincipal.CB1.Clear;
while Telefonos[Provincia,ContCiudad,0]<>'' do
begin
  FrmPrincipal.CB1.Items.Add(Telefonos[Provincia,ContCiudad,0]);
  ContCiudad:=ContCiudad+1;
end;

  FrmPrincipal.CB1.Items.Add('Otro');
end;
**********************

It basically loads a list into a combo box. I call it like this:
MostrarCiudadesProv(CBProv.ItemIndex+1);

Within an OnChange event fired by a combo box.

My question is, is this the kind of things that need to be called with Synchronize?
0
 
LVL 7

Expert Comment

by:sftweng
ID: 10779807
If you are triggering the procedure call from an OnChange event then it's being done in the main thread so no Synchronize should be necessary.

If you are not explicitly creating TThread objects then this is probably not the problem.

However, all of the ComboBox access in MostrarCiudadesProv would need to be the subject of a Synchronize call if done from any other thread than the main one.

I am assuming that FrmPrincipal is created at design time rather than from within a thread.
0
 
LVL 14

Author Comment

by:Esopo
ID: 10779845
Yes FrmPrincipal is the main form created at design time.
I'm not explicitly creating TThread objects.
Would a thread, to be considered other than the main one, have to be created using a TThread object, or are there other ways to have a thread not be the main one?
0
 
LVL 7

Expert Comment

by:sftweng
ID: 10779865
Some components create threads (e.g., the Indy Internet components) but it should be obvious from their documentation that they are doing so. I notice in your original posting that you mentioned "RAS", by which I interpret "Remote Access Services", so there is some possibility that you do have threads. What I cannot tell without looking at the code is whether any of those threads invoke GUI operations.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10779876
The main form runs on the main thread (no way around that in the VCL). But there may be other threads to contend with.

Easiest way to check is through the use of the IDE's debug windows (or NT Task manager where applicable) to determine the number of threads in the app. If only one thread, then you need to look at other areas, as the canvas sync'ing won't apply.

Regards,
Russell
0
 
LVL 14

Author Comment

by:Esopo
ID: 10779929
I'd love to post my whole code but it's very long (about 1500 lines) besides, I don't want you spending that much time browsing through it. I'm not using any thirparty components, my -Remote Access Services- connection is handled completely by my code.
You say two ways to look for extra threads, one is to run the app and check the TaskManager, I'll do that.
The other one is to look in the code for Threads (TThread objects), I'll do that too.
0
 
LVL 7

Expert Comment

by:sftweng
ID: 10779943
If you run the program in debug mode, you can open a Threads debug window.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10779967

Alan, nice to see you again ;-). (haven't seen you around recently.)

Kind Regards,
Russell
0
 
LVL 14

Author Comment

by:Esopo
ID: 10779980
I did the first to things and got no other process related to my app and also found no Tthread call in my code.
I ran the program in debug mode with the threads debug window and eventually got the error, this is what the threads debug window listed:

Thread ID                        State          Status
Principal1.exe (1920)    
2180                                 S                 U
1904                                 S                 U
2212                                 S                Init

S: Stopped
U:Unknown

Any ideas?
0
 
LVL 7

Expert Comment

by:sftweng
ID: 10780015
All I can suggest at this point is that you post your "Uses" statements so that we can see whether any of the imported modules might contain multithreaded code.
0
 
LVL 7

Expert Comment

by:sftweng
ID: 10780026
Thanks, Russell, I've been pre-occupied with some personal issues, most of which are resolved.

Alan
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 26

Expert Comment

by:Russell Libby
ID: 10780076
Also, if an option (you have full source for vcl)...

There is only ONE location that will raise this exception, and if you have the vcl source (depending on delphi package std/pro/enterprise/etc), you can set the "Use debug DCU's" in the compiler options.

What you would be looking for is this:

procedure TCanvas.RequiredState(ReqState: TCanvasState);
var
  NeededState: TCanvasState;
begin
  NeededState := ReqState - State;
  if NeededState <> [] then
  begin
    if csHandleValid in NeededState then
    begin
      CreateHandle;
      if FHandle = 0 then
        raise EInvalidOperation.CreateRes(@SNoCanvasHandle);
    end;
    if csFontValid in NeededState then CreateFont;
    if csPenValid in NeededState then CreatePen;
    if csBrushValid in NeededState then CreateBrush;
    State := State + NeededState;
  end;
end;

By setting a breakpoint on the raise statement, you should be able to check the call stack window to find out where things went wrong.

Hope this helps,
Russell

0
 
LVL 14

Author Comment

by:Esopo
ID: 10780082
unit Principal;
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Ras, Forms,  Dialogs, StdCtrls, ExtCtrls, jpeg, Buttons, FileUtilities;

uses ConnProp, Unit1, Aydua;
                    > Unit1 and Aydua are forms in my proyect.


unit unit1;
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,   Dialogs, StdCtrls, jpeg, ExtCtrls;


unit Aydua;
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, jpeg, ExtCtrls, StdCtrls, ComCtrls;


unit FileUtilities;
uses
windows, messages, sysutils, classes, graphics, controls, forms, dialogs, stdctrls;


unit Ras;
uses Windows, Messages, SysUtils;


These are the files in my proyect.
0
 
LVL 14

Author Comment

by:Esopo
ID: 10780119
Russell,

I'm there,
I checked the "Use debug DCU's" and managed to get the exception, the debugger is paused at the line
raise EInvalidOperation.CreateRes(@SNoCanvasHandle);
 what should I do now?
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10780126

Should have also mentioned that the clipped block of code comes from graphics.pas, my bad on that ;-)

Russell


0
 
LVL 26

Assisted Solution

by:Russell Libby
Russell Libby earned 200 total points
ID: 10780130
Check the call stack to see where the call originated from...

Russell
0
 
LVL 14

Author Comment

by:Esopo
ID: 10780177
I think I spotted something. The exception seems to be caused by any line of code after a showmessage call:

showmessage( StatusString(state, error) + chr(10) + 'Consulte la ayuda en las opciones avanzadas');

I say any line of code, cause if comment the line that caused it and when ran again the following line caused it.
0
 
LVL 14

Author Comment

by:Esopo
ID: 10780193
>I say any line of code, cause if comment the line that caused it and when ran again the following line caused it.
Does that make sense? hehe

I say any line of code after the showmessage one, cause the line that followed was just a call to a method that adds a info to a log file. I just commented it ( // ) and then, when ran the program again, the following line caused it (the line that came right after the comment one):

      If IntentoActual=TotalIntentos then
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10780227
Can you try converting the ShowMessage call to a MessageBox function?

MessageBox(Handle, PChar(Message information), PChar(Title), MB_OK+MB_ICONINFORMATION);

Perhaps something is interfering with the handle allocation. (Could also check GetLastError when in the breakpoint, and verify the canvas is either TControlCanvas/TBitmapCanvas by checking Self.ClassName)

Russell


0
 
LVL 14

Author Comment

by:Esopo
ID: 10780248
>check GetLastError
How do I do that?
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10780262

Also thinking out loud...
Are you sure the ShowMessage call is made within the main thread? If it isn't (it uses vcl code), then this would also cause the error you are getting. Try the message box, which is straight api stuff to see if this makes a difference.

Russell

----------

Offtopic:

Alan, good to hear, hope things are "straight" now. Glad to see your expertise  back around here.

0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10780268
To check any of the stuff I mentioned...

at the breakpoint.

Right click / "Debug" / "Evaluate/Modify"
enter in the expression, eg: GetLastError
press enter
check the result value

----------------

Russell


0
 
LVL 14

Author Comment

by:Esopo
ID: 10780349
I'm having trouble repliclating the error. As stated before it happens almost at will ;)

I'll keep trying and playing around with the showmessage until I get it straight.

Thanks guys, it's been very useful. I'll check this up and come back later.
0
 
LVL 7

Expert Comment

by:sftweng
ID: 10780513
The fact that the error seems to have a mind of its own (i.e., "happens almost at will") implies to me that there is a threading problem of some kind.
0
 
LVL 7

Expert Comment

by:sftweng
ID: 10780519
What is unit ConnProp?
0
 
LVL 14

Author Comment

by:Esopo
ID: 11043486
I'm sorry to have been gone for too long. I wasn't able to get anywhere with this and had to work on more important stuff.
Never got around the canvas error so I fixed my application to work without the code that was generating the error. It now works but without the options it was suppoused to have.

I'm very worried about this canvas thing cause it isn't the first application I get this with, I'll see into this problem some more and then come back to tell you my findings.

Thank you for your time,

Esopo.
0
 
LVL 7

Accepted Solution

by:
sftweng earned 200 total points
ID: 11043679
Esopo, sometimes the best alternative is a redesign and rewrite. If that solves the problem, then move on. After having worked in this business for about 40 years, I'm convinced I'd have twice as many grey hairs if I had tried to solve every lurking problem. Hmm. not a bad idea actually - then I'd have almost twice as much hair. ;-)
0
 
LVL 14

Author Comment

by:Esopo
ID: 11580744
Thank you all for your help.

I never really found a solution for this, gave it many long hours and eventually had to move on to other stuff. Maybe as Alan says sometimes is best to light the code on fire with a keg + bunch of friends + couple of guitars and go for a second round.

Nevertheless your help is most appreciated, and even when I found no conclution for this problem I did got many possibilities I had no idea about.

Sorry to have kept this Q open for so long.

Esopo.
0
 
LVL 7

Expert Comment

by:sftweng
ID: 11581348
Thanks. Personally, I'd prefer a keg + bunch of critics (probably also friends) + some bagpipes + a whiteboard. Occasionally blowing it all away feels good and breaks fixations.
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now