Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EmptyParam was variable now a function - How to resolve legacy code?

Tags:

com

delphi

I am just upgrading some old code written in Delphi 6 to Delphi XE2. Unfortunately the code references a Word97 COM object for generating some .doc documents. There is a direct uses clause of Word97 in the code.

I have to keep the document generated in the same Word format as it is used by an old Crystal Report and another 3rd party app which requries that format of the document.

So, to the question. Because I am using the Word97 in the uses clause, the compiler complains about Types of actual and formal var parameters must be identical whenever an EmptyParam variable is used. This is coming straight out of the Word97.pas source file. This is because EmptyParam is now declared as a function and not a variable.

what is the best way to deal with this? Should I copy the Delphi 6 source files (Word97.pas et al) say into my local directory, directly add them to my project, together with System.Variants.pas and change the Compiler Directive of my app to include EMPTYPARAM_VAR? I haven't tried that, but hopefully it would declare EmptyParam as a variable then. Or perhaps there's an easier solution.

Thanks

EDIT

Here's a little more background info, even though I have accepted an answer, for future reference. Here's an example of the code (AddClaimsLetter is the "Application" COM object - ie TWordApplication):

AddClaimsLetter.Documents.Open(Wordfile, EmptyParam, EmptyParam,
                        EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                        EmptyParam, EmptyParam, EmptyParam, EmptyParam);

Without changing anything, the EmptyParam arguments here failed at compile time stating "E2033 Types of actual and formal var parameters must be identical".

However, because I wanted to keep Word97 (which is in OCX/Server in Delphi 6 Ent. installation folder), I did need to copy the .pas files into my local project file and declare a variable that was used in place of EmptyParam (because these files attempted to compile too and I got the same compiler error as above).

So all working now, but I might discuss with management upgrading to a later version of Office for this App!

Thanks

like image 601
Jason Avatar asked Feb 21 '12 00:02

Jason


2 Answers

I quick workaround for the problem might be to declare a local variable of type OleVariant and assign it the result of the function EmptyParam.

like image 102
Uwe Raabe Avatar answered Sep 18 '22 16:09

Uwe Raabe


For every var parameter, you should add a local variable that you fill with the EmptyParam call.

The reason is that all var parameters are passed by reference. If you pass the same variable at multiple references, you can encounter the following issue (simplified using Integers).

Before: VarParameter=-1
Before: A=-1
Before: B=-1
After: A=2
After: B=2
After: VarParameter=2
EAssertionFailed: Assertion failure

This is the code that shows the risk:

program TheVarParameterRisk;

procedure AssignParameters(var A: Integer; var B: Integer);
begin
  Writeln('Before: A=', A);
  Writeln('Before: B=', B);
  A := 1;
  B := 2;
  Writeln('After: A=', A);
  Writeln('After: B=', B);
  Assert(A = 1);
end;

var
  VarParameter: Integer;

begin
  try
    VarParameter := -1;
    try
      Writeln('Before: VarParameter=', VarParameter);
      AssignParameters(VarParameter, VarParameter);
    finally
      Writeln('After: VarParameter=', VarParameter);
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

You see that in the AssignParameters the Assert fails, because the caller passed the same reference into the two var parameters.

This is exactly the reason that EmptyParam became a function: it should never be passed as a var parameter.

People were complaining about bugs in Delphi (for instance here) that caused overwrites of the EmptyParam value.

That problem is solved now that EmptyParam is a function. When you need to pass it as a var parameter, use a local intermediate.

Notes:

  • If you (because your imported API indicates you should) have to pass EmptyParam to a var parameter, then essentially you have problem in itself: a var parameter means that something is going to be changed, which means that passing EmptyParam (or a local variable having the same content) is preventing the change.
  • If you think the Type Library Importer generates the wrong code (that has var parameters in stead of const parameters), then either the imported type library has the wrong parameter flags, or (if the parameter flags are OK) it is buggy (and you should file a bug report at https://quality.embarcadero.com (this used to be the search engine indexed http://qc.embarcadero.com but that has been shut down; https://quality.embarcadero.com requires a free account to search).

Edit:

The fact that you have to pass EmptyParam in to a var parameter is a very strong hint into my point that that the function that is called might acually use those parameters and will fail. Just like my example function fails. (Yes, the function documentation might lure you into using multiple EmptyParam parameters that in fact are not meant to be that empty at all; I've often seen documentation say A and actual functions perform B).

like image 39
Jeroen Wiert Pluimers Avatar answered Sep 18 '22 16:09

Jeroen Wiert Pluimers