11
votes

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

3
I don't have a Word97.pas file in my Delphi XE2, nor in my Delphi 2010. Is that because I didn't install it (perfectly possible) or is it because it's no longer available? Can you ctrl+enter on the file name to make sure the file actually comes from your Delphi XE2 installation and you didn't accidentally bring it from your D6 install?Cosmin Prund
I don't recall any problems with EmptyParam when I upgraded Delphi. Rather than describing your code, could you show it. Rather than describing the error message could you quote it exactly. Copying D6 source files, especially Variants.pas is not going to be the solution.David Heffernan
@DavidHeffernan Try passing EmptyParam to any of the parameters in function Open(var FileName: OleVariant; var ConfirmConversions: OleVariant; var ReadOnly: OleVariant; var AddToRecentFiles: OleVariant; var PasswordDocument: OleVariant; var PasswordTemplate: OleVariant; var Revert: OleVariant; var WritePasswordDocument: OleVariant; var WritePasswordTemplate: OleVariant; var Format: OleVariant; var Encoding: OleVariant; var Visible: OleVariant; var OpenAndRepair: OleVariant; var DocumentDirection: OleVariant; var NoEncodingDialog: OleVariant): WordDocument; safecall;user743382
@RudyVelthuis, as scary as it looks, that's how Delphi imports the Type Library for Word.Cosmin Prund
@RudyVelthuis I copied it from Delphi XE's WordXP.pas. And that's exactly the problem: they shouldn't be var, but they are. And now that EmptyParam is a function, it's no longer a valid function argument for a var parameter.user743382

3 Answers

16
votes

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.

7
votes

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).

1
votes

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.

You are probably legally allowed to copy the Word97.pas from your much older Delphi to the new one, but then you'll need to "upgrade" it to work with the new compiler. You're likely going to face a lot more problems, mostly because Delphi XE2 is Unicode enabled and Delphi 6 is not. If that unit is what I think it is (as mentioned I don't have it on the 3 versions of Delphi I've got handy and checked) then it's a wrapper for OLE Automating Word - ie, it's full of hard to understand system code and lots of method declarations with no implementation. This last bit, method declarations with no implementation, makes it very hard to change: if you change something and it compiles it doesn't mean it's right - you'll only find at runtime if it works or not.

What you should probably do:

  • Re-import the type library for Word 97 (I assume that's what the Word97.pas is) and re-write your export code to generate the word document using this new library. Edit: Apparently re-importing the TypeLibrary produces unusable code.
  • Write a DLL in Delphi6 that uses the old Word97.pas and use that from Delphi XE2. Not very elegant but will work.
  • Re-think your strategy around whatever the current offer for Word automation is.