If you use the ChildRDM architecture available in Delphi 6 in %DELPHI%\Demos\Midas\SharedConn, there is a possibility of the server hanging in memory after all clients have exited. This really only applies if you are using a COM object in another DLL and using that COM object as a ChildRDM in your main server EXE.
Debugging the problem, I found that there were still references to the external DLL, and that the CPU view was showing me that the EXE was repeatedly trying to enter a critical section, but not being able to do so. If you look at the destruction code for TDataModule, you'll see that it tries to lock access to the DataModule. Well, if you rely on Delphi to get rid of internally stored interface references when they go out of scope, it will be too late to reclaim the external DLL. The solution is as follows:
Create an overridden BeforeDestruction method in the main RDM. In that RDM, set all of your ChildRDM interface references to nil in order to explicitly release the COM objects. Then, make a call to CoFreeUnusedLibraries (found in ActiveX.pas). This will release the DLL, if the COM subsystem says it's OK to do so. However, I found that even that was enough in all cases, so add another call to CoFreeUnusedLibraries in the main RDM's finalization section. See the code snippet here:
procedure TMainRDM.BeforeDestruction;
begin
fSharedDataRdm := nil;
fChildRdm := nil;
CoFreeUnusedLibraries;
inherited;
end;
initialization
TComponentFactory.Create(ComServer, TMainRDM,
Class_MainRDM, ciMultiInstance, tmApartment);
finalization
CoFreeUnusedLibraries;
end.
After doing this, I was not able to get the app server to hang in memory again.
Thanks go out to Steve Trefethen for providing this unit. We just got done updating httpsrvr.dll to use the unit, and performance has just sky-rocketed. After running stress testing on some MIDAS servers on a dual-CPU machine with hyper-threading, it is absolutely incredible how fast and stable things are. We didn't measure at the API level, but our perception and wall-clock timing tells us there is at least a 300% increase in speed, if not more.
Thanks again, for a job well done!
Quality Insight is Borland's re-branding of the JCL OTAPI exception, combined with an interface to submit bug reports directly to QC. In a nutshell, it takes any unhandled exceptions that occur in the Delphi 2005 IDE, report a dialog - complete with stack trace - to you, and allows you to submit the bug report to QC. It's similar in concept to Windows' online error reporting mechanism. It's a very handy tool, and I believe it will help increase the stability of the IDE, since it gives R&D more information about bugs that pop up in the IDE.
Unfortunately, it looks like it is disabled by default during install (at least it was here on all 8 machines). To turn this useful feature on, run regedit.exe and set the following key/value pair in HKCU\Software\Borland\BDS\3.0\Known IDE Packages
$(BDS)\Bin\exceptiondiag90.bpl = (Untitled)
If the value is blank, the package will not be loaded in the IDE. Therefore, set it to some arbitrary value, like "(Untitled)", restart Delphi, and you should be greeted with the exception dialog the next time you encounter an unhandled exception in the IDE.
Note: Be careful of using other 3rd party exception tools (e.g. madExcept and Eureka). You'll have to test for sure, but these other packages may end up consuming the exception before it gets to Quality Insight. If that's the case, simply disable the IDE package of the 3rd party exception tool.
OK, I admit it. I'm a Distributed Computing junkie. Every time I go to a new platform, language, or environment, the first thing I try to do is figure out everything I can about how to get applications and computers talking to each other. I've been that way ever since college when I got FINGER working on our VAX system without admin privileges. The resultant meeting in the admin offices the next Monday morning wasn't exactly my idea of fun, however.
Rocky Lhotka wrote an exceptional post on what Indigo could mean to distributed computing. It is a great summary of why Indigo could be The Next Big Thing in this space. I also watched the Introduction to Indigo MSDN TV episode. Very well done. Lastly, the reference to Indigo on MSDN contains some valuable information, too.
I've been spending waaay too much time on this D2005 VCL change. The intent of adding this property was certainly a noble one. You can read more about the reasons why this property was added in Allen Bauer's blog.
In practice, however, this change has caused me much grief. For example, I have some components that override the original WndProc to do some custom drawing. Since the PopupMode property now can cause forms to be recreated, that means that all the controls on the form will be recreated. I'll now need to trap recreate events on the component's form and figure out a way to remove and re-add all of the custom WndProcs to the current list of components that have their WndProcs overridden. It also caused some problems by causing a WinRunner form to briefly display before our splash screen. Setting the PopupMode to pmAuto solves a lot of the problems, but by then, I grew to loathe this new property.
I'm all for progress, but I would have preferred that any VCL changes made that cause forms to be recreate would preserve the legacy behavior as the default value for this property (at the very least).
In my last entry, I discussed a simple work-around for overcoming a new Delphi 2005 bug. After further review, the Bears still suck. Sorry, that probably only makes sense if you knew the story of the Packers/Bears instant replay game. See the November 5, 1989 game on this page for more details - and yes, the replay official got it right.
Back to our PageControl story... The work-around I provided works well, but the problem is that countless other things in the VCL also fire off a request to do a RecreateWnd (e.g. setting a form's Parent). This means that you would need to do all of the things that cause RecreateWnd to be done before setting ActivePage. That's not entirely possible. Here is my new list of possible work-arounds:
- Patch the VCL to fix the new TPageControl.SetTabIndex method. Either comment out everything after inherited, or add code to take invisible tabs into consideration (a la TPageControl.GetImageIndex). This will only work if you don't use run-time packages, though.
- Create a descendant TPageControl, override the TPageControl.SetTabIndex method, and either fix the method or restore to D6 functionality (only call inherited)
- Wait for the fix in the VCL code
Unlike my last entry, I would classify this one as a bug.
If you use a TPageControl, and you set certain Tabsheets to invisible, and then you set the OwnerDraw property to true on the PageControl (like the code listed below), you will not get the proper page selected. This is due to a new method, TPageControl.SetTabIndex, that doesn't take visible tabs/pages into account before trying to set the PageControl.ActivePage property. In Delphi 6, this method didn't exist, and everything worked fine. However, in Delphi 2005, things are different. When you set OwnerDraw, the underlying window is recreated. During the destroy of the window, the current tab index is saved off so that it can be restored when the window is later recreated. It is at this point that the new SetTabIndex method is called, and in that method, notice that no effort is made to determine that any tabs have been set invisible, therefore, the wrong page is selected.
The simple workaround is to set OwnerDraw in the DFM, or at the very least, set OwnerDraw prior to setting any tabs to invisible.
Tabsheet2.TabVisible := false;
Pagecontrol1.ActivePage := Tabsheet4;
Pagecontrol1.OwnerDraw := true;
I also submitted this to Quality Central (QC) as issue 11978. You will find a test case in that entry, too.
Lastly, thanks to both Chris Hesik (Borland) for helping me out with some debugger issues, and Rich Werning for helping debug this to the point where we could come up with a valid test case.
Here's an interesting change between Delphi 6 and Delphi 2005 in the VCL. Take the following code snippet where SQLQuery1.SQLConnection = nil:
SQLQuery1.SQL.Text := 'select * from MyTable where ID = :ID';
SQLQuery1.Params.ParamByName('ID').DataType := ftInteger;
In Delphi 6, everything works fine. In Delphi 2005, TSQLQuery.QueryChanged has been updated to use TSQLQuery.SetParamsFromSQL internally. This means that if you don't have a SQLConnection assigned when updating the query text, then the params will be cleared. This means that an exception will be thrown when trying to do the ParamByName call.
I'm not sure that this would qualify as a bug, per se, but it definitely changes your application's behavior when all of these circumstances are present.
Michael Slinn is the new Sr. Product Manager for Delphi, C++, C# and .NET at Borland (wow, that's a mouthful for a title!). I have enjoyed reading his blog posts, his replies to comments, and his posts in the newsgroups. He asked about the top 10 things that can be done to market Delphi, so I came up with this one idea that I thought was worth sharing.
Thesis: I believe the IDE and personalities should be broken out into separate SKUs.
Since the Galileo IDE can support multiple languages, it might make more sense to break up the SKUs better. With VS.NET, you buy VS.NET and get VB.NET, C#, and C++ - whether you want them all or not. My thought is that you could have the IDE be sold as the base item. It would contain things like source code integration, StarTeam integration, Caliber/RM integration, Refactoring, and maybe some more core bits. The personalities could then be sold as add-on packages. Delphi would contain Win32 and .NET. The C# personality would be separate from that, as would the C++ personality. Each personality would be sold as Pro, Enterprise, or Architect. The add-on packages would plugin to the main IDE and contain all of the bits that pertain to that personality (e.g. VCL, VCL.NET, debugger, designers, etc.).
While this would be more work initially for Borland, I think the benefits would be huge over the long-run. If someone reports a bug in the IDE that is truly an IDE bug, the IDE can be fixed and a patch released and you would get that fix for all personalities. If there is a bug in a personality, just update the personality. If the fix touches both, then both parts can be updated. This independence would be ideal for the consumer. For example, if there is a bug that is causing IDE performance bottlenecks, the consumer may be forced to wait for an entirely new release of Borland Developer Studio (BDS), since fixing those problems may require breaking changes to the interface compatibility initially released with Delphi 2005. By adopting this model, Borland could start to release more frequent patches, and also dedicate resources to the appropriate codebase based on SKU sales and profitability.
I could also see a tool used in the Borland R&D department where you would have dependancy graphs used to tell the team which files would be affected by a specific change. For example, pretend there is a unit named "CoreIDEServices.pas" that needs a patch. The developer would fix the patch, run the tool, and it would say that the only file that needs to be updated is coreide90.bpl. Perhaps a VCL for Win32 bug fix would result in only the PAS file, DCU file, and corresponding BPL file needing deployment.
People have been asking for the VCL to be more of an "a la carte" model forever. This could be a step in that direction. I'm not advocating that this be done today. Iron out some of the current problems, but keep an eye on this model. I think it would serve everyone well.
|