C++ Faults when a TChartListBox is used in a DLL.

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Wed Nov 20, 2013 10:39 pm

This is a continuation of a previous stream that was never really resolved. I have decided to start another stream and I will explain why later. The previous stream was "Having TDGIPlus Within a DLL Causes Fault in XE4". I had sent in a test case and you said you could not reproduce the problem on your systems or within your environment. I will take you for your word on this. However, the final post said that an attempt was made in "an environment as similar as possible" as mine. It also said that my problem might be related to "a case of the mentioned problem". The link for the "mentioned problem" was related to "Getting "F1027 Unit not found" compiling release mode for iosDevice". Since I am currently running only C++ XE4 and this release never had (and C++ still does not have) iosDevice, I figured that we had progressed as far as we could at the time. I was at a point that I could back up and run the previous version as there was nothing I needed in the update.

However, you have now created a new major release with a lot of features that I wish to use. I downloaded and installed your update (November 19th) today and I have encountered the same problems as I had before. Needless to say, I was quited disappointed, but I was determined to try and advance this to a correction. In my testing I have determined that the faulty code is in VCLTee::TeeGDIPlus::TeeGDIPlusCanvas::RecreateGraphics. I cannot tell you why it is faulting (as I do not have source code).

I used my previous test case, and I found that it failed quite readily. After much trial and error, I was able to narrow this down to a very simple situation. The revised test case has two projects within the project group. The first project is a DLL and has a single form, and on the form is a single component (TChartListBox). The second project is an EXE that has a single form, and on the form is a single component (TChart). In the EXE form, the processing creates a new form (with the TChartListBox) from the DLL so the DLL and EXE are linked. When you execute the program (either in the IDE or outside of it), it will fault when it is attempting to create the TChart. (I noticed that in this case, the GDIPlus.dll gets started when the TChart is being created). I have included a picture of the exception along with a picture of the call stack (they are included in the test case file).

I did a couple of other tests to see the scope of the issue. If I remove the TChartListBox from the form in the DLL and put it in the EXE, it will work (and the GDIPlus.dll is not started). If I put things back and instead remove the TChart from the EXE form and put it on the DLL form, the processing will fault as before. The faulting situation is that if a DLL contains a TChartListBox and a subsequent TChart is created, the processing will fault. Whenever the fault occurs, the processing is always starting the GDIPlus.DLL system DLL when the TChart is being created. When there is success, this DLL is never started.

I am including the revised and simplified test case. I hope that you have all the information that you need to fix this.

I would like to add that I am really stuck here. I cannot use this November 19th update at all. This is the same problem I encountered before and I will now have to back up to the April release again. I really need this new release quite badly and hope there is something you can do for me. I consider this problem quite urgent and need some form of resolution as quickly as possible.

Should you need any more information, please let me know.

rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Wed Nov 20, 2013 10:41 pm

Here is the test case. Sorry about that.
Attachments
C++ TChartListBox Problem.zip
(135.1 KiB) Downloaded 1914 times

rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Wed Nov 20, 2013 10:50 pm

I just discovered that I had not saved everything to the test case project. I have uploaded another copy of the test case with all the files in it.
Attachments
C++ TChartListBox Problem.zip
(137.91 KiB) Downloaded 2036 times

rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Wed Nov 20, 2013 11:49 pm

Some additional information for you. I checked my latest version of my application. This version does not use the TChartListBox in any of its processing (the older version does), but the application still faults like I stated in the earlier post (from within the RecreateGraphic method). I would have to conclude that other processing like what is done for the TChartListBox is causing the issue, so the problem may be in more than one place.

Should you need any other information, please let me know.

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by Narcís » Thu Nov 21, 2013 12:15 pm

Hello,

Thanks for your feedback. Just to keep track of the complete issue, its discussion started here.

Back to your inquiry, a better approach to your needs is using .bpl packages instead of DLLs. Packages already solve that sort of problems and nothing becomes duplicated. Using DLLs all TeeChart code is encapsulated into the DLL and also duplicated into the .exe.

RecreateGraphics does nothing important but it's necessary that the GDI+ DLL has been loaded. It might be that your DLL is not loading it. That load is done automatically when creating a TChart and GDI canvas. However, if you have a TChartListBox instead this doesn't occur. Having that in mind, a possible solution is that you do the following in your DLL:

Code: Select all

uses TeeGDIPlusObj;

initialization
  TeeGDIPlusStartup
end
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by Narcís » Thu Nov 21, 2013 1:14 pm

Hello,
rgsolutions wrote:I just discovered that I had not saved everything to the test case project. I have uploaded another copy of the test case with all the files in it.
My apologies, I missed the correct project. I could reproduce the issue here now. The solution we suggested is for IDEs prior to XE2. In XE2, and later on we imagine, the solution would be: http://stackoverflow.com/questions/8563 ... pplication
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by Narcís » Thu Nov 21, 2013 2:23 pm

Hello,

Continuing with the investigation, the problem might be beyond GDI+. You can not do new TForm from an .exe to a .dll. You need to do packages or create a MyShowForm unit inside the DLL and call it from the .exe for this to work. MyShowForm unit can make: new TForm, ShowModal, etc. More info at http://docwiki.embarcadero.com/RADStudi ... _(C%2B%2B)
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Thu Nov 21, 2013 4:04 pm

Thanks for the information. Let me comment on your ideas.

First of all about using packages. There are no packages in C++ for 64bit code, so this would limit my processing to 32bit only. Also, my application has 17 DLLs that are being used as part of the executable (the product has evolved over many years), so conversion to packages would be prohibitive. Also, I am not sure it will work as we are dealing with C++ and not Delphi. There are differences in the two platforms and I would have to set up a test environment before even thinking about going down this path. Until there are 64bit packages in C++, I will not even consider this as an option.

As for the GDI+ DLL being loaded. When the TChart processing is totally contained in the EXE, it is never loaded and the processing works correctly. When the TChart processing is contained in the DLL, the DLL is loaded as part of the TChart create processing. (I saw this by dynamically creating the TChart in the test dll and saw it loaded after the constructor was entered and before the fault).

You say that RecreateGraphics does nothing important, but with my experience with program faults (and I have plenty of this), it appears that RecreateGraphics is passing a null pointer value to System::Object::InitInstance. I would suggest that you might wish to look into this method a bit more to see if there is any possibility that this situation is occurring. If it is possible to pass a null pointer, under what conditions it does so would be helpful to me as how to get around this situation. I would have to add that since this fault is occurring three levels deep in TGDIPlusCanvas, it would be hard to see what I can do to prevent this.

As for the MyShowForm option. The problem with this is that I use MDI processing, so have a form in ShowModal would be an issue. I also have forms embedded in other forms. In some cases I have several forms that are tabs on a single form, and these forms are sometimes in different DLLs. Not sure what I can do about this as we are talking about a major redesign that might not resolve the issue.

I will close this post with a statement and my next actions. I installed XE4 on my system in early May. At that time I used your current Beta release for XE4 and it worked just fine for me. In latter May, you released the official release for XE4, and this one did not work for me and thus the previous posts. The current release (November) behaves for me just like the May release did. Something had changed between the Beta and May release. I believe all of this will work once we find out what change is causing the problem. I hate to say this, but all my instincts keep coming back to RecreateGraphics. If we can find out what is causing the fault in this method, then I think we will have a good idea on how to solve this. If you could investigate this fault a bit more, I will continue with some of your suggestions. Some of them do not involve major changes and thus can be tried.

I will let you know of any progress.

rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Thu Nov 21, 2013 6:16 pm

As an update, I tried a couple of things. First I tried your DLL initialization code:

uses TeeGDIPlusObj;

initialization
TeeGDIPlusStartup
end

I have no idea how to do this. I cannot find any reference to TeeGDIPlusObj or TeeGDIPlusStartup in any of the Steema files. (I do not have source).

As for the second suggestion (http://stackoverflow.com/questions/8563 ... pplication). I looked at the example and cannot find any reference to TGDIPlusStartupInput in the Embarcadero files, so I could not do this. I suspect this was removed in XE3 or XE4. I am currently using XE4.

As for your third one suggestion. You are absolutely correct about creating a DLL form from the exe. ("You can not do new TForm from an .exe to a .dll."). In my processing, the form is always created from the module containing it. However, I do embed DLL forms into forms that are created in the exe (or another DLL). (I did look at the DocWiki entry you suggested, and there is nothing in there that I do not know or have not used). In the test case I gave you, the "new TFormDLL1 (this);" was only intended to "link" the exe and dll. If you get to this code (and it aborts on the new), then all is working. To avoid confusion on this matter, I have changed the test case (and included it in this post). I have included a button on the main form that will (illegally) create the new form. It is never called, but the linkage to the DLL is maintained. Now, if the test case works, it will show the form without aborting.

Finally, I tried a couple of things. If I only have TChart controls on the dll form that do not use a canvas, the processing will work. If I have TChart controls that have a canvas (such as TChartListBox, TChartGrid, TTeeInspector, etc.) then the processing will fault. (Note that the revised test case has a TChartGrid on the form).

I have exhausted all your suggestions, and I have no further ideas. As I said before, this all used to work and now it doesn't. I haven't done anything new, in fact, if I use the Beta release from April, it does work.

What's next?
Attachments
C++ Controls in DLL.zip
(138.26 KiB) Downloaded 2041 times

rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Thu Nov 21, 2013 10:16 pm

One more update. I put in code to load the GDIPlus.dll at the time the DLL is initialized. It did not make any difference, the processing still aborted in RecreateGraphics.

I think the key to moving this forward is to find out why RecreateGraphics is causing a fault.

David Berneda
Site Admin
Site Admin
Posts: 83
Joined: Wed Nov 12, 2003 5:00 am
Location: Girona, Catalonia
Contact:

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by David Berneda » Fri Nov 22, 2013 2:55 pm

There's one thing you can try just to see if the exception is not raised.
First, set any chart to *not* use GDI+ canvas (chart editor dialog -> 3D -> Render -> GDI)

Then, change this registry value to be an empty blank string:

HKEY_CURRENT_USER\Software\Steema Software\TeeChart Pro\Editor -> DefaultCanvas

With these settings, the chart will never create a TGDIPlusCanvas, so it will never call RecreateGraphics.

Make sure any project is being compiled "Using Runtime packages".

The code RecreateGraphics is below.
I think the exception happens at first line (TGPGraphics.Create), inside the object creation InitInstance.
TGPGraphics is a class inside XE4 source WinAPI.GDIPOBJ.pas.

procedure TGDIPlusCanvas.RecreateGraphics(const AHandle:HDC);
var Status : TStatus;
begin
FGraphics:=TGPGraphics.Create(AHandle);

Status:=FGraphics.GetLastStatus;
if Status<>Ok then
Raise Exception.Create(IntToStr(Ord(Status)));

DoSetAntiAlias(FAnti);
DoSetAntialiasText;

FGraphics.SetPageUnit(UnitPixel);
end;

I really wonder why this happens.
When using a TChartListBox, this control does not use at all the TeeGDIPlus unit or any GDI+ code.

rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Fri Nov 22, 2013 4:12 pm

I tried your suggestions with my test case. I did the changes in steps to allow for more information to be gathered. Here are the results.

1. Changed the setting in the TChart to render the 3D with GDI and NOT GDI+. Result: Processing still fails with exception from RecreateGraphics.

2. Leaving TChart rendering at GDI and NOT GDI+, and then changed the registry setting for the DefaultCanvas to blank. Result: Works!!!!!

3. Leave registry setting blank for Default Canvas and change TChart rendering back to GDI+. Result: fails with exception from RecreateGraphics.

It appears to be as you stated about using GDI+ in the DLL. However, I would have to disagree with your last statement ("When using a TChartListBox, this control does not use at all the TeeGDIPlus unit or any GDI+ code."). While this statement may be true for an EXE, it is not true for a DLL.

In the IDE I can see the DLLs that are loaded. The last DLL that is loaded before the exception occurs is the GDIPlus DLL. However, if I were to just run the EXE (no DLL linkage) with a TChart and TChartListBox on a form, the application will work and the GDIPlus DLL is NEVER LOADED.

It appears that you have some type of code that is either new or left over that deals specifically with a DLL file.

I have done all this processing with the test case I have supplied. Have you been able to duplicate this problem there using my C++ test case? I will take a bit more time today to refine the test case so that it will truly be isolated to the DLL.

David Berneda
Site Admin
Site Admin
Posts: 83
Joined: Wed Nov 12, 2003 5:00 am
Location: Girona, Catalonia
Contact:

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by David Berneda » Fri Nov 22, 2013 4:52 pm

>>It appears that you have some type of code that is either new or left over that deals specifically with a DLL file.

You're right. I was wrong. It happens the TChartListBox unit uses other units that in the end they are using the GDIPAPI.

Yes, I can reproduce the exception with your test case.

I'm not still sure if the problem is when using the GDI+ lib, or something else that is breaking at the time a GDI+ object is accessed or created.
Looks like its a GDI+ issue, or something in the dll that is forcing the load of GDI+ (which should be delay-loaded by the exe instead).

In this unit, at the end of the file:

C:\Program Files (x86)\Embarcadero\RAD Studio\11.0\source\rtl\win\Winapi.GDIPObj.pas

The dll is only loaded in the case that the running process is not a library (dll) :

initialization
if not IsLibrary then
begin
.....

(that's why its necessary to startup GDI+ in the exe instead of the dll)

I've tried to do this at Project2.cpp, at the very beginning with the code below, with no sucess, and no more ideas.

#include <tchar.h>
#include <gdiplus.h>
//---------------------------------------------------------------------------
USEFORM("UnitEXE1.cpp", FormEXE1);
//---------------------------------------------------------------------------
Gdiplus::GdiplusStartupInput GDIPlusInput;
unsigned long gdiplusToken;

int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
try
{

GDIPlusInput.GdiplusVersion= 1;
GDIPlusInput.DebugEventCallback=NULL;
GDIPlusInput.SuppressBackgroundThread=TRUE;
GDIPlusInput.SuppressExternalCodecs=FALSE;
Gdiplus::GdiplusStartup(&gdiplusToken,&GDIPlusInput,NULL);

Application->Initialize();
Application->MainFormOnTaskBar = true;
Application->CreateForm(__classid(TFormEXE1), &FormEXE1);
Application->Run();

......


regards !
david

rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Fri Nov 22, 2013 6:43 pm

First of all regarding your last post. I too had trouble with the code so I reverted to my C++ code. The following can be added to the same place you were trying to load the GDIPlus.DLL in the EXE. (The include needs to be in the appropriate place but the rest can be anywhere in the try.

#include <stdio.h>
char *env_value,
name[200];
env_value = getenv ("SystemRoot");
sprintf (name, "%s\\system32\\GDIPlus.dll", env_value);
LoadLibraryA (name);

However, I can save you some time. This code does load the GDIPlus.DLL, but it does no good. The fault still occurs. I have done some other analysis, and I do not believe it is when the dll is loaded that is causing the issue. Whether the DLL is loaded early or late, it still is loaded and the program faults. I think the real issue is how it is being used. The following is some additional analysis that might help you.

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

Let me try to describe to you what is going on. Some of my information is not as descriptive as it should be, but I am looking at cpu code to give you this information. Since I do not have source, it is difficult to be more precise.

The processing is trying to create the main form for the processing. To do this it does the following calls (based on the stack):
1. TApplication::CreateForm
2. TFormEXE1:TFormEXE1 (the main form)
3. VCL::Forms::TForm::TForm
4. VCL::Forms::TCustomForm

From this last call, the processing calls System::Classes::NotifyGlobalLoading. I am not sure what this processing does, but I would guess that it is used to load all the necessary classes that are needed for processing.

From NotifyGlobalLoading, the processing calls Vcltee::Teeprocs::TCustomTeePanel::Loaded. This processng makes an initial call to get a value. It compares the returned value to see if it is null. In this case it is not null so it makes a call to Vcltee::Teeprocs::TCustomTeePanel::CreateDefaultCanvas.

The CreateDefaultCanvas processing performs some checks and then loops through checking several objects which looks likes controls. I cannot follow all the processing here in that it is doing nested calls within the function. Eventually, the processing ends up in Vcltee::Teegdiplus::TGDIPlusCanvas. This could be the actual call from TCustomTeePanel::Loaded, but I am not sure).

The TGDIPlusCanvas processing does some checking and then finally at the end of the function, it calls Vcltee::Teegdiplus::TGDIPlusCanvas::GetGraphics.

The GetGraphics processing does some checking and then calls Vcltee::Teegdiplus::TGDIPlusCanvas::RecreateGraphics. This processing enters the function and makes the first call in the function. This call causes the GDIPlus.DLL to be loaded and then the exception occurs.

As I have said before, the GDIPlus.DLL is being loaded and is being loaded (in the test case) as a resulted of a call from RecreateGraphics (delayed Load). It is a delayed load, but the load is successful.

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

I hope this information helps. I believe that it is necessary for you to register (or something similar to that) the classes on the load of the DLL. I suspect this is where it is happening. It is possible the "Loaded" processing (of TCustomTeePanel) is expecting an environment that might not be correctly established at this early stage of the DLL processing. Please remember that at this stage of the processing, the processing environment is not fully formed and to have processing assuming that it is could be a problem. It is possible that if this code is called in both a package and DLL, the processing environment is more fully formed at the time for the package than for the DLL.

One question you might wish to get answered is why are you using the GDIPlus.DLL at this stage, if the DLL is even part of the problem. It is possible that the DLL has nothing to do with the problem.

Please let me know of any further help that I might give you on this. I am holding off on reverting back to XE4 Beta while we are actively pursuing this.

rgsolutions
Newbie
Newbie
Posts: 46
Joined: Tue Jun 04, 2013 12:00 am

Re: C++ Faults when a TChartListBox is used in a DLL.

Post by rgsolutions » Tue Nov 26, 2013 9:45 pm

How do you wish to proceed on this?

Post Reply