Delphi IDE freezes on code completion and jumping to declaration - delphi

Okay, so this is not the best practice situation, but luckily I have an answer, so please bear with me.
I'm working on a rather large Delphi project with lots of external APIs used. Those APIs are plugged in different ways, including units wrapped into IFDEFs in the DPR itself, when they are platform-dependent. Having IFDEFs in DPR is a bad practice - sure, but it is handy to have all the units listed in the project tree to switch between them (rather than switch between projects).
So I was adding another alternative API implementation to the project and as usual - wrapped the existing solution units into the {$IFDEF SOME_API_DLL} clause and a new solution units into another {$IFDEF SOME_API_EXE}.
Anyways, I noticed soon after, that the IDE started to freeze semi-randomly after just 3-5min of coding, which is unbearable toll.. The IDE would hog one CPU (e.g. 13% on 8-core PC). I was able to isolate the cause to be invocation of "autocomplete", "code completion" (Ctrl+Space) and "jump to declaration (Ctrl+Click).
The main question is - what could cause the IDE to freeze on how to resolve this?
I'm using Delphi XE8, but switching to other versions didn't help. Unfortunately newer 10.3+ versions (with claimed rewritten Code Insight) could not be tested/used due to third-party components.

Thanks to the bug being well connected to the new API implementation, I was able to track down the cause. So it appears that IDE does not like DPR files with IFDEFs (old news), but the real bummer seems to be that IDE handles IFDEFs okay only if they wrap only one unit.
The hint to the solution was the Project Tree, which gets auto-updated with units in DPR, but it auto-included only the first units after the IFDEF. Apparently, thats what also throws off the track the auto-completion.
Bad DPR structure that causes IDE to freeze on code-completion:
{$IFDEF EXTAI_API_DLL}
KM_ExtAI_DLL in 'src\KM_ExtAI_DLL.pas',
KM_ExtAI_DLLs in 'src\KM_ExtAI_DLLs.pas',
KM_ExtAI_SharedTypes in 'src\KM_ExtAI_SharedTypes.pas',
KM_ExtAI_SharedInterfaces in 'src\KM_ExtAI_SharedInterfaces.pas',
KM_ExtAIActions in 'src\KM_ExtAIActions.pas',
KM_ExtAIMaster in 'src\KM_ExtAIMaster.pas',
KM_ExtAIStates in 'src\KM_ExtAIStates.pas',
KM_ExtAIUtils in 'src\KM_ExtAIUtils.pas',
{$ENDIF}
Good DPR structure that lets IDE work well:
{$IFDEF EXTAI_API_DLL}KM_ExtAI_DLL in 'src\KM_ExtAI_DLL.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAI_DLLs in 'src\KM_ExtAI_DLLs.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAI_SharedTypes in 'src\KM_ExtAI_SharedTypes.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAI_SharedInterfaces in 'src\KM_ExtAI_SharedInterfaces.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAIActions in 'src\KM_ExtAIActions.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAIMaster in 'src\KM_ExtAIMaster.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAIStates in 'src\KM_ExtAIStates.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAIUtils in 'src\KM_ExtAIUtils.pas',{$ENDIF}

Related

Using VCL Styles in a DLL causes System Exception in 10.2 Tokyo

An application originally written in XE2 that uses styles within DLL's so that forms that popup from DLL's are the same style as the EXE, when updated to build in 10.2 Tokyo, now causes System Exceptions when opening certain forms from the EXE, or when closing certain forms in the EXE.
I don't need to include the minimal reproducible example in this question, because I have an answer, that someone else may have been able to add to my original question had it not been closed so quickly, and then not re-opened even after making it on-topic.
Turns out it's a behavioural issue in VCL:
Exception if using comboboxes in a form that resides in a DLL and that uses VCLStyles.
Embarcadero won't fix it as it's not a "problem",
R&D writes that the style manager has to be enabled in the application
and there can be only one one TStyleManager with enabled system hooks
(TStyleManager.SystemHooks property), because it process all windows
from application. The current system doesn't support the scenario you
are suggesting, and there is currently no plan to rework it
but there is a workaround which is to add the following line of code immediately before calling SetStyle or TrySetStyle in the DLL code:
TStyleManager.SystemHooks := [];
Hopefully this will be of assistance to Delphi developers who run into this annoying problem after upgrading to the newer versions.
In my case, I add a conditional define to the project for the libraries, then add this code to the places where it setting styles, as the same unit is included in both EXE and DLL:
{$IFDEF DLL}
TStyleManager.SystemHooks := [];
{$ENDIF}

Delphi XE memory leak in TWSDLLookup.Destroy method

I am using Delphi XE. I have come across a memory leak problem using Delphi Soap. It turns out to be due to a missing .Free call in TWSDLLookup.Destroy, as described in QC 91160
The problem that I have is the described work-around, which is simply to add FLookup.Free to the TWSDLLookup.Destroy method.
I don't want to change the Delphi source, so I tried copying the unit to my project folder, making the change and recompiling, as described here in Tom's answer. The problem with this technique is that it apparently only works if you also recompile all the dependent units. I have tried copying just WSDLLookup.pas to my project directory and I get a Stackoverflow error. I'm not familiar with Web Services / SOAP so I don't know what other units I should copy over if I do use this technique.
Rob Kennedy's answer on the same page describes a different technique involving code hooking - but it doesn't seem to apply to object methods. I have done as he suggests and downloaded the free code for the TNT Unicode controls and located the relevant procedures, but I have been unable to find info on how to hook an object's methods - if indeed this is possible. If I could do this, I would then hook TWSDLLookup.Destroy and add the FLookup.Free call.
Any ideas for how to fix this will be much appreciated. I'm a bit of a newbie programmer so I'm hoping that I've missed something obvious?
What you are trying to do does in fact work fine. I tested it out myself. Here's the project file I used:
program WSDLLookupTest;
{$APPTYPE CONSOLE}
uses
WSDLLookup in 'WSDLLookup.pas';
var
intf: IInterface;
begin
intf := GetWSDLLookup as IInterface;
end.
I made a copy of the WSDLLookup.pas file and placed it in the same directory as the .dpr file. Then, in the copy rather than the original, I modified TWSDLLookup.Destroy.
destructor TWSDLLookup.Destroy;
begin
Beep;
ClearWSDLLookup;
FLookup.Free;
inherited;
end;
I added the Beep to prove to myself that this code was indeed being executed.
In your position I would definitely use this solution instead of attempting code hooks. And of course the other simple solution is to upgrade to a later Delphi version.
One thing to be careful of is to remember to remove the modified unit when you do upgrade. The leak was fixed in XE2.

Single-source unit tests for Free Pascal and Delphi

Is there a way to write unit tests so that they can be compiled and run both with Delphi and Free Pascal?
There are different unit test frameworks for Delphi and Free Pascal, which causes duplicate work for developers who target both compilers (for example, library and framework developers).
So maybe there is a way, using either the DUnit or the FPCUnit framework and tweak the test case source code (or the framework itself) so that it also works with the other compiler.
So essentially the question is:
which framework (DUnit or FPCUnit) can be compiled with both compilers (Delphi and Free Pascal) with as little modifications as possible?
or
is there a third framework (Thanks to Arnaud for mentioning TSynTest) which works with Delphi and FPC?
See this very nice blog article - just fresh meat about FPCUnit testing.
In short, as far as I know, and if you compare to DUnit:
Most Check*() methods were renamed Assert*();
SetUp / TearDown methods are called per-function in both framework;
Some other thinks may vary.
So, I think it could be easy to let FPCUnit "mimics" DUnit, by creating a small wrapper class over FPCUnit implementation, to have the same exact methods than with DUnit. So you may be able to share code between the two targets, and even re-use existing DUnit tests. Such a wrapper class is IMHO much more convenient that using {$ifdef FPC} as other suggested here. Conditional compilation tends to make code hard to debug, verbose, redundant and should be used only if necessary.
Another potential solution could be to use other tests frameworks. Our small TSynTest classes are lighter, but I'm currently converting the framework to FPC. So the same exact code could be used with both compilers. It has some features (like optional logging with fine profiling, and full stack strace on failure) which I would miss from DUnit / FPCUnit. It does not have a GUI nor a Wizard but honestly, as I programmer I prefer plain text that I can include in my technical release documentation easily to testify that no regression occurred.
Default unit test framework for Free Pascal is FPCUnit, it has the same design as DUnit but different from it in minor details. You can write common unit tests for FPCUnit and DUnit by circumventing the differences by {$IFDEF FPC}. I just tested FPCUnit, it is a usable framework, and blogged about it.
I just whipped up a sample that works in both DUnit (delphi) and FPCUnit (Freepascal equivalent nearest to DUnit, that happens to ship already "in the box" in lazarus 1.0, which includes freepascal 2.6 ):
A trivial bit of IFDEF and you're there.
unit TestUnit1;
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}
interface
uses
Classes,
{$ifdef FPC}
fpcunit, testutils, testregistry,
{$else}
TestFramework,
{$endif}
SysUtils;
type
TTestCase1= class(TTestCase)
published
procedure TestHookUp;
end;
implementation
procedure TTestCase1.TestHookUp;
begin
Self.Check(false,'value');
end;
initialization
RegisterTest(TTestCase1{$ifndef FPC}.Suite{$endif});
end.

Compiler Directives - Delphi Versions

I have a unit I wrote in Delphi 7 some time ago, and have just had the fun (pain) of converting to Delphi XE (Unicode).
The Unit works fine after some trouble, I am now trying to make this unit compatible with different Delphi Versions should I ever need to switch IDE back to Delphi 7 whilst updating some other code.
I only have Delphi 7 and Delphi XE, but from what I gather code written in Delphi 1 to Delphi 2007 will compile, but code from Delphi 2009 and above will be Unicode.
...Anyway, in the Unit I am separating the non-unicode and unicode like so:
{$IFDEF VER150} //Delphi 7
// code
{$ELSE IFDEF VER220} //Delphi XE
// code
{$ENDIF}
How do I modify the compiler directive so the rules apply to multi versions? For example something like:
{$IFDEF VER80, //D1
VER90, //D2
VER100, //D3
VER120, //D4
VER130, //D5
VER140, //D6
VER150, //D7}
This would then cover all Delphi versions should I distribute the source or .dcu unit.
Thanks.
I wonder if the simplest approach in this instance is to switch behaviour on the UNICODE conditional. This conditional is defined if and only if you are using a Unicode version of Delphi, i.e. in Delphi 2009 and later. The big advantage of this is that it is future proof—you don't need to update your code every time a new Delphi is released. What's more, the conditional switch will be far more readable since it will clearly express the intent.
The Delphi documentation has an excellent topic listing all the pre-defined conditionals. The full list of version conditionals is also linked from there.
Your best bet is actually to look at one of the many JEDI projects, http://sourceforge.net/projects/jedi-apilib/ for example and look at how they do it. They have common include files that contain exactly the details you are interested in. JVCL is another good choice ...

ShareMem/ string-exchanging with Delphi DLL

Quick one I hope - I'm just about to delve into a Delphi 5 legacy app that makes calls to a DLL (also written in D5), passing a string which the DLL can modify if required.
I have the code to both the DLL and the app. Pasted right at the top of the DLL source is a remark about using ShareMem, and it needing to be the first line in the uses clause of the project etc.
If I was porting this whole thing to D2007, is there a better (or more modern) way of getting a Delphi app to share string data with a Delphi DLL? Does the D5 ShareMem stuff still apply to Delphi 2007 applications (with FastMM etc)? I haven't even had a bash at recompiling the whole thing yet - just wondered if this bit was going to be a problem and if there was an alternative/recommended way of doing this?
FWIW, the DLL is totally first party (it's only used by this particular app - so recompiling it under D2007 as well wouldn't be a problem).
To use the FastMM included with D2007, use SimpleShareMem as the 1st unit in both your application and the DLL projects.
Or download the full FastMM4 from SourceForge, set the Flags in FastMM4Options.Inc (ShareMM, ShareMMIfLibrary, AttemptToUseSharedMM) and put FastMM4 as the 1st unit in both the application and the DLL projects.
Use only a FastMM4. FastMM4 is a great memory manager and automatically includes a ShareMem like solution! FastMM4 is compatible with D5 and up!
The reason you need sharemem is that the reference counting on ansistrings breaks when passed to a dll. One solution is :-
If you are able to restrict your strings to shortstring then you can dispense with sharemem. I have written some two dozen dlls, mostly drivers for hardware and I have not had to use sharemem once.

Resources