Quantcast
Channel: Delphi – The Wiert Corner – irregular stream of stuff
Viewing all 1440 articles
Browse latest View live

How to properly cleanup/shutdown a Delphi ISAPI (which uses Threads) | Mathias Pannier programmiert

$
0
0

For my link archive:

If you use Threads or global Objects (which are created in the initialization section for example) You have to cleanup/shutdown them in the OnTerminate event of the ISAPI Application. If You destroy/shutdown them in the finalization section in a unit it could end up in a hanging application pool in IIS on shutdown/reuse and some windows event log entries.

//added
procedure DoTerminate;
begin
  //free global objects and wait/terminate threads here
end;
exports
  GetExtensionVersion,
  HttpExtensionProc,
  TerminateExtension;
begin
  ReportMemoryLeaksOnShutdown := true;
  CoInitFlags := COINIT_MULTITHREADED;
  Application.Initialize;
  Application.WebModuleClass := WebModuleClass;
  TISAPIApplication(Application).OnTerminate := DoTerminate; //added
  Application.Run;
end.

Source [WayBack] How to properly cleanup/shutdown a Delphi ISAPI (which uses Threads) | Mathias Pannier programmiert

Related:

–jeroen


“Use TXSxxxx classes for simple nillable types”

$
0
0

Some Delphi settings require you to run with an Administrative UAC token

$
0
0

Though user-defined Code Templates live under %UserProfile%\Documents\Embarcadero\Studio\code_templates\Delphi, the stock ones (standard ones shipping with Delphi) ones do not: they live under %ProgramFiles(x86)%\x\Studio\y.0\ObjRepos\en\Code_Templates.

Above, x usually is Embarcadero, and y is your Galileo version number; see for instance Update to List-Delphi-Installed-Packages.ps1 shows HKCU/HKLM keys and doesn’t truncated fields any more.

Unlike user registry settings, which are copied from HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER, the stock Code Templates are not.

This means that if you want to change the stock Live Templates (for instance disable them) from the IDE, or from the Windows Explorer, you need to run them with an UAC token to elevate them to Administrator level permissions.

Oh, did you notice the consistency between code_templates and Code_Templates? Delphi is full of these consistency surprises**

via: [WayBack] EDIT…..ANSWER: Right-click and run Delphi IDE as “Run as Admin”.. remove the template. Start IDE normally. .. IMHO, you shouldn’t have to do this… – Vin Colgin – Google+ who commented:

Vin Colgin:
+Uwe Raabe yes. Imho. Delphi should use a UAC helper app to change things that need elevated permissions… just like we all have been doing with shipping software since Windows Vista. But nobody asked me. Imho.

as a response to

Uwe Raabe:
Usually you don’t have access rights to Program Files where the standard code templates live. Therefore you cannot remove any of these from inside the IDE. Either adjust the behavior to your needs (that will make a copy) or remove it from $(BDS) with admin rights.

** Some more consistency examples:

Just pick a few functions from these or more recent Delphi versions:

For example (there are many more):

Of the above methods:

  • 9 have  a starting const AText: ; parameter
  • 3 have a secondary const AText: ; parameter

I get it that these won’t be changed because they have been this way for more than a decade, but in my book, designing that way back then in the first place would show lack of code review or seniority in the team, no matter if part of these functions look like [WayBackStrPos and [WayBackAnsiPos in the [WayBack] SysUtils unit.

A while ago, [WayBack] Paul TOTH Found out that the below two functions have their parameters in reverse order:

var
  Comp : function(const s1, s2: string): Boolean;
  begin
  if CheckBox1.Checked then
    Comp := ContainsText
  else
  Comp := AnsiStartsText;

why are the parameters in a reverse order !!!

There was an issue filed on some of the above functions in 2016, which was only getting a feedback from the internal bug tracking service almost a YEAR LATER:

Marco Cantù added a comment – 

Changing the sequence of parameters for EXISTING functions seems a very bad idea, as this changes the semantic on existing code that compiles. In general I’d agree tat consistency is important. But I disagree parameterless calls are such a smart move in terms of readability.

I’m inclined to close this request as “won’t do”

Jira-Quality Sync Service added a comment – 

Sync status from internal system, internal issue closed on Dec 22, 2016 by Marco Cantù with comment:
Even if having similar functions with different sequence in params was a bad idea on our side, changing the sequence of parameters for EXISTING functions and breaking the semantics of existing code seems even worse. So we are not going to do this.

–jeroen

delphi – How to set form height larger than 1096 pixels? (or width > 1620 pixels)- Stack Overflow

$
0
0

Delphi will limit [WayBackHeight or [WayBack] Width of a form in the designer based on the current Windows limitations, not on what your target users might.

See [WayBack] delphi – How to set form height larger than 1096 pixels? – Stack Overflow:

The reason for this behavior is that when you do not set constraints for the form size, Delphi will automatically get constraints at system level via the [WayBackWM_GETMINMAXINFO message, which is fired when…

Solution is to configure the [WayBack] Constraints property by setting either [WayBackConstraints.MaxHeight or [WayBackConstraints.MaxWidth.

Via: [WayBack] Since I put up 10.2, I cannot get a form width >1620. Whenever I enter a greater value, the system changes it back! As I have some programmes with comp… – erik wilson – Google+

–jeroen

The Delphi System.Exit “Function”

$
0
0

Still wrongly documented as System.Exit Function [WayBack], most people think it is a statement.

However, it is a compiler intrinsic procedure in the System unit that – when called inside a real function – optionally accepts a parameter with the same type as the encompassing function because it is a compiler intrinsic. It kind of acts as an overloaded procedure, but in fact translate to machine code via an intermediate parse tree.

The parameterless version has been there since at least Turbo Pascal 3.0, but the parameterised version is more recent: I think it was introduced around Delphi 7.

It then stops executing that function after first executing any explicit or implicit finally blocks.

I’ve seen various projects that used their own Exit procedure. This is a very bad habit: Since the System unit is always further away in scope, the introduced one is called which can severely confuse programmers not being aware of this.

The code generation for the parameterless and parameterised  “overloads” of System.Exit is slightly different:

  • The parameterless one can often be optimised away, for instance folding multiple calls to them into one, or rearranging code execution so a jump isn’t needed any more. This means you cannot always put a breakpoint on them.
  • The parameterised one always needs code to load the function result, so you can always put a breakpoint on them.

Stefan Glienke explained the above in [WayBack] The advantage of using Exit() instead of a plain Exit? You can place a breakpoint! – Uwe Raabe – Google+

–jeroen

DelphiSpringTrees – Trees in Spring4D

Reminder to self: check progress on Delphi helper requests

$
0
0

One of the things the Delphi language really could use some updates in is helpers.

A semi-random example is in [WayBack… It nags me that I can’t create a record helper for a generic type… – Lars Fosdal – Google+.

So I wonder if there has been any progress, and this is a post to remind me checking it out.

Example feature requests (no more QC, so these have to do) by Horácio Filho:

–jeroen

Spring4D and factory – for my link archive

$
0
0

When the Delphi IDE gets slow…

$
0
0

A few tips for when the Delphi IDE gets slow, try these step by step:

  1. Install Delphi Fix-Pack by Andreas Hausladen (a.k.a. Andy)
  2. Disable “Error Insight”
  3. Disable or install LiveBindings related packages

The first can be hard to find as his site disallows search engine indexing, but I’ve written about it before site:wiert.me delphi fixpack andy.

The second step is not a package, but an IDE setting:

The last step involves packages like these (package path followed by package description in the order of the registry):

$(BDSBIN)\dclBindCompFireDAC220.bpl            LiveBinding Expression Components FireDac
$(BDSBIN)\dclbindcomp220.bpl                   Embarcadero LiveBindings Components
$(BDSBIN)\dclbindcompvcl220.bpl                Embarcadero LiveBindings Components VCL
$(BDSBIN)\dclbindcompdbx220.bpl                LiveBindings Expression Components DbExpress
$(BDSBIN)\dclbindcompfmx220.bpl                Embarcadero LiveBindings Components FireMonkey

In those, $(BDSBIN) and $(BDS)\Bin point to the same directory.

Parts via: [WayBack] EDIT: Pretty sure Fix-Pack solved the speed issue, as I installed it and things started flying. I was trying to be as stock as possible, in order to und… – Vin Colgin – Google+

–jeroen

 

use the JVCL’s TJvXxxAppStorage to store float values as strings while controlling the decimal separator…

$
0
0

For my link archive: [WayBack] Is there a way to use the JVCL’s TJvXxxAppStorage to store float values as strings (e.g. “10.152”) rather than hex dumps and also control the decimal separator… – Thomas Mueller (dummzeuch) – Google+ with a solution by Achim Kalwa:

+Thomas Mueller You may try this:

type
  TMyStorage = class(TJvAppRegistryStorage)
  protected
    procedure DoWriteFloat(const Path: string; Value: Extended); override;
  end;
  TJvAppRegistryStorage = class(TMyStorage);
...
Implementation

{ TMyStorage }

procedure TMyStorage.DoWriteFloat(const Path: String; Value: Extended);
var
  SValue : string;
  FS     : TFormatSettings;
begin
  FS := TFormatSettings.Create;
  FS.DecimalSeparator := '.';
  FS.ThousandSeparator := #0;
  SValue := FloatToStr(Value, FS);
  DoWriteString(Path, SValue);
end;

And you need to set
StorageOptions.FloatAsString := False;

–jeroen

 

Delphi code completion fail with anonymous methods – Stack Overflow

$
0
0

[WayBack] Delphi code completion fail with anonymous methods – Stack Overflow.

It also often fails by inserting end; inside if/then/else statements having a proper begin/end blocks.

The reason is that code completion uses a different parser than the regular Delphi compiler.

Chances of these issues to get fixed are close to zero, because another part of the problem is that there is no formalised Delphi language grammar, not even at Embarcadero itself.

I wish there was an Embarcadero sanctioned grammar (see [WayBack] “The arguments that won the day for us here were about strengthening the ecosystem and becoming the best tooled language on the planet. They were about … – Jeroen Wiert Pluimers – Google+). Actually I had that wish like in the end of the 1990s, but I cannot find my forum post archive back.

The best reverse engineered grammar is DelphiAST: a well maintained open source project implementing an Abstract Syntax Tree for Delphi.

Via: [WayBack] SO…/delphi-code-completion-fail-with-anonymous-methods I have seen this question and I am curious. There is an… – Alberto Miola – Google+

–jeroen

Spring4d Marshmallow ORM – ensuring the right identifier quoting is used

$
0
0

When an ORM generates the SQL for a certain database back-end, it needs to use the right quoting.

Normally, the Spring4D Marshmallow project derives that from the back-end specific adapter used.

But if you use a generic TDriverConnectionAdapter, then you need to manually set the QueryLanguage property in your IDBConnection using a valid value from Spring.Persistence.SQL.Interfaces. This then uses the correct SQLGenerator from the TSQLGeneratorRegister to generate the SQL including correct quotes.

To quote identifiers, most back-ends use a double quote, but MySQL uses a backtick.

via:

You obviously already modified that code as the commented out code is the correct one.
I guess you already got that mistake when you started using MySQL via ADO as MySQL needs the backtick.

Set QueryLanguage of your IDBConnection properly (the valid values are in Spring.Persistence.SQL.Interfaces). Then the correct SQLGenerator will be used.
If you are using an adapter that is specific to a certain database then it sets the QueryLanguage properly but if you are using an adapter for components that can connect to multiple databases (like ADO or FireDAC) it does not, hence the need you specify it.

 

–jeroen

E2398 Class methods in record types must be static (Delphi) – RAD Studio

$
0
0

[WayBack] E2398 Class methods in record types must be static (Delphi) – RAD Studio present since at least Delphi 2007 ([WayBack] E2398: Class methods in record types must be static).

This is a Delphi compiler limitation for records, that is not present for classes.

Maybe one day when record inheritance kicks in, this will be resolved (as Stefan Glienke hinted in the below tread), because currently Self (which is present in non-static class methods on classes) then points to the current type. In static class methods, there is no Self, which is OK as that would point to the only available type anyway.

Via: [WayBack] why a class method of a record without the static keyword raise E2998 error ? AFAIK it can not be anything else then static ?! – Paul TOTH – Google+

Note that [WayBack] Error and Warning Messages (Delphi) – RAD Studio shows various other errors around the use of static:

All are marked “No further information is available for this error or warning.“, though they would at least deserve some examples.

–jeroen

Before using the PPL, I need to check to see if a few things have been fixed yet

$
0
0

Before using the PPL ever, I need to check out at least a few posts like these:

Most of the PPL code was written by Allen Bauer when he was Chief Scientist at Embarcadero in the 2007-2010 time frame and it was still called DPL (Delphi Parallel Library). The code was released in Delphi XE7 (see [Archive.isUsing the Parallel Programming Library – RAD Studio) but seems hardly maintained after he left.

You can find some of his notes at [Archive.isThe Oracle at Delphi: Parallel Programming

–jeroen

When whas the global FormatSettings variable deprecated?


Next up: TPDFPrinter and TPDFCanvas Expect a high paced (and easy) update cy…

Changing the order of TabSheets in a PageControl in Delphi – twm’s blog

How to convert a Delphi enum or set to a JSON value, with different specific values…

$
0
0

As I will probably need this one day: [WayBack] How does one convert a Delphi enum to a JSON value, with different specific values? – CHUA Chee Wee – Google+:

eg, TEnum1 = (test1, test2, test3)

TSomeClass.FEnum := test1;

When converted to JSON, I’d like to see maybe
{"Enum": "Value1"} instead of test1

and test2 to "Godzilla", test3 to "Tiburon"

The solution is in his repository: github/chuacw/EnumJson:

After initially suggesting to look into [Archive.is] Serializing User Objects – RAD Studio, he based his solution on a set of clever tricks circumventing Delphi compiler limitations and bugs:

Later he extended the solution to include sets in additions to enums: [Archive.is] Persisting enumeration and sets to JSON – Chee Wee’s blog: IT solutions for Singapore and companies worldwide (via [Archive.isMade my JSON interceptor demo public. Now you can save your enum and sets to JSON, with customized output to boot! – CHUA Chee Wee – Google+)

Clever!

Hopefully this got Lars Fosdal some ideas to solve [WayBackJSON in Berlin – Can I persuade TJSon to treat “params” as it was a string and not an object for the TJsonRPC class? – Lars Fosdal – Google+

Enums with Delphi

Enums with Delphi and their mapping is a repeating topic, see for instance [WayBack] What’s the easiest (ie., least coding) way to map an enum to a const string and vice versa? (Can attributes be used on enum values yet?) eg., type TMyColor… – David Schwartz – Google+

It shows how much the Delphi language is in need of language enhancements as right now there are way too many open source libraries struggling with the same issues each working around them or providing solutions in slightly different way.

Three things immediately come to mind:

  • the NameOf that – analogous to TypeOf – gets the name of an identifier as a string
  • expose all RTTI for all enum types (especially the ones with non-contiguous values or starting at another value than ordinal zero)
  • allow for attributes on enum values

Some examples of libraries providing enumeration support:

Some more thoughts from a different perspective: [WayBackWhat’s New in Edge Rails: Active Record enums

–jeroen

 

Working in a team? DprojSplitter might be helpful! | The Art of Delphi Programming

$
0
0

Reminder to self: split off the user settings from a Delphi .dproj so only the non-user settings get into your version control system.

Some of you are probably already using my DprojNormalizer plugin for Delphi, which tries to reduce the unwanted differences disturbing your version control system. It does this by forcing a dedicat…

So what about them?
DprojSplitter handles the current build configuration and current platform. In addition these settings are handled, too (found in CommonOptionStrs):

sDebugger_RunParams,
sDebugger_RemoteRunParams,
sDebugger_HostApplication,
sDebugger_RemotePath,
sDebugger_RemoteHost,
sDebugger_EnvVars,
sDebugger_SymTabs,
sDebugger_Launcher,
sDebugger_RemoteLauncher,
sDebugger_IncludeSystemVars,
sDebugger_UseLauncher,
sDebugger_UseRemoteLauncher,
sDebugger_CWD,
sDebugger_RemoteCWD,
sDebugger_RemoteDebug,
sDebugger_DebugSourcePath,
sDebugger_LoadAllSymbols,
sDebugger_LoadUnspecifiedSymbols,
sDebugger_SymbolSourcePath

Source: [WayBackWorking in a team? DprojSplitter might be helpful! | The Art of Delphi Programming

via: [WayBack] Oops! I did it again! This is my new IDE plugin – again targeted on dproj files. – Uwe Raabe – Google+

–jeroen

Links to debugging COM stuff for Office Automation in Delphi

$
0
0

From a long time ago, but recently I needed them again.

Note the .NET side is much much harder Revisited from the .NET side: Why doesn’t WINWORD.EXE quit after Closing the document from Delphi? (via: Stack Overflow).

[WayBack] Why doesn’t WINWORD.EXE quit after Closing the document from Delphi? – Stack Overflow

Q

I managed to distill one of the underlying issues rooted in my question How to trace _AddRef / _Release calls for OLE Automation objects in the unit below.

I’ll answer this answer too, just in case anyone else bumps into this.

The question: with the below code, why doesn’t WINWORD.EXE always quit (sometimes it does quit).

The unit can probably be trimmed down even more.

unit Unit2;

interface

uses
  Winapi.Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls,
  WordXP;

type
  TForm2 = class(TForm)
    WordXPFailsToQuitButton: TButton;
    procedure WordXPFailsToQuitButtonClick(Sender: TObject);
  private
    FWordApplication: TWordApplication;
  strict protected
    function GetWordApplication: TWordApplication; virtual;
    function GetWordApplication_Documents: Documents; virtual;
    procedure WordApplication_DocumentBeforeClose(ASender: TObject; const Doc: _Document; var Cancel: WordBool); virtual;
    procedure WordApplication_Quit(Sender: TObject); virtual;
    property WordApplication: TWordApplication read GetWordApplication;
    property WordApplication_Documents: Documents read GetWordApplication_Documents;
  end;

var
  Form2: TForm2;

implementation

uses
  Vcl.OleServer;

{$R *.dfm}

function TForm2.GetWordApplication: TWordApplication;
begin
  if not Assigned(FWordApplication) then
  begin
    FWordApplication := TWordApplication.Create(nil);

    FWordApplication.AutoConnect := False;
    FWordApplication.AutoQuit := False;
    FWordApplication.ConnectKind := ckNewInstance;
    FWordApplication.OnDocumentBeforeClose := WordApplication_DocumentBeforeClose;
    FWordApplication.OnQuit := WordApplication_Quit;
    FWordApplication.Connect;
  end;
  Result := FWordApplication;
end;

function TForm2.GetWordApplication_Documents: Documents;
begin
  Result := WordApplication.Documents;
  if not Assigned(Result) then
    raise EAccessViolation.Create('WordApplication.Documents');
end;

procedure TForm2.WordXPFailsToQuitButtonClick(Sender: TObject);
begin
  try
    WordApplication_Documents.Add(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
    WordApplication.Visible := True;
    WordApplication.ActiveDocument.Close(False, EmptyParam, EmptyParam);
  finally
    WordApplication.OnQuit := nil;
    WordApplication.OnDocumentBeforeClose := nil;
    WordApplication.AutoQuit := True;
    WordApplication.Disconnect;
    WordApplication.Free;
    FWordApplication := nil;
  end;
end;

procedure TForm2.WordApplication_DocumentBeforeClose(ASender: TObject; const Doc: _Document; var Cancel: WordBool);
begin
  FWordApplication.Disconnect;
end;

procedure TForm2.WordApplication_Quit(Sender: TObject);
begin
  FWordApplication.Disconnect;
end;

end.

Answer part 1:

Comment out the disconnect in the below event:

procedure TForm2.WordApplication_DocumentBeforeClose(ASender: TObject; const Doc: _Document; var Cancel: WordBool);
begin
//  FWordApplication.Disconnect;
end;

The event will be called during the DocumentClose(…) method, then disconnect and delete the OLE interface from the FWordApplication instance.

I have not yet figured out which reference is dangling, but this effectively keeps WINWORD.EXE alive most of the times.

Answer part 2:

Sometimes WINWORD.EXE does quit because toe WordApplication_DocumentBeforeClose event is not called. The reason is that the code runs so fast that Word is not fully initialized yet to perform the event.

[WayBack] delphi – How to trace _AddRef / _Release calls for OLE Automation objects – Stack Overflow

Q

(Delphi XE2 update 4)

I’m trying to get a big Microsoft Word OLE automation unit I inherited (based on the early bindingTWordApplication and interfaces from the WordXP/Word2010 units) to close WINWORD.EXE when all references have been released.

So far, it looks like I did catch a couple of reference leaks: most references are properties or local variables.

Some usage scenario’s however still keep WINWORD.EXE open.

A few of the fixes indicate I should favour local variables in stead of chains from

procedure TOffice_10_XP_WordInterface.AddDocument;
var
  WordApplicationDocuments: Documents;
begin
  WordApplication_Documents.Add(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
end;

to

procedure TOffice_10_XP_WordInterface.AddDocument;
var
  WordApplicationDocuments: Documents;
begin
  WordApplicationDocuments := WordApplication_Documents;
  WordApplicationDocuments.Add(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
end;

based on a WordApplication_Documents property that calls this function:

function TOffice_10_XP_WordInterface.GetWordApplication_Documents: Documents;
begin
  Result := WordApplicationReference.Documents;
  if not Assigned(Result) then
    raise EAccessViolation.Create('Documents');
end;

The properties are there to make the EAccessViolation messages more readable than the $C0000005 errors you get in the debugger.

I’m wondering about generic (since I’ll probably need this for other automation projects as well) ways to monitor _AddRef and _Release calls.

I did take a look at these links:

A

A tedious way that gets you going is this:

Put breakpoints of all _AddRef and _Release calls in the Delphi System unit that are not inside TInterfacedObject specific methods.

Now eliminate (using conditional expressions) all the interfaces that are not part of Delphi (EAXcontains the vTable pointer for each interface).

  1. Start debugging your application with a simple run/exit without doing much actual functionality:
  2. Before tracing in each call, make a note of the EAX value.
  3. If you end up in any of these methods: NopReleaseNopAddrefTInterfacedObject._AddRefTInterfacedObject._ReleaseMemReleaseMemAddRef then add the EAX value to a conditional breakpoint instruction for all of the breakpoints like below.

Example conditional breakpoint expression for my application:

(EAX <> $401E58) and (EAX <> $54AD14) and (EAX <> $4A7C88) ...

This method has a lot of drawbacks, but it gets you going.

Drawbacks:

  • There are limitations on the length of the conditional breakpoint expression. Which means that if you continue to add and (EXA <> $xxxx) portions, the debugger will ignore those without indicating a warning.
  • You loose the setting if you exit Delphi without saving your desktop
  • It takes a lot of time to setup

–jeroen

Viewing all 1440 articles
Browse latest View live