6

To help us modularize a monolithic application, we are in the process of setting up packages for use in debug builds, while still compiling to a single exe for release builds.

One of our packages (EAUtils) contains a unit that is now producing [DCC Error] E2201 Need imported data reference ($G) to access 'SMsgDlgWarning' from unit 'SystemUtils'.

This happens when building the EAUtils package itself. I am not into building packages that depend on EAUtils yet. EAUtils only depends on rtl/vcl packages and a package I created for the Jedi WinApi units.

This is a result of the lines:

// This is a TaskDialog override, with the same args as the old MessageDlg.
function TaskDialog(const aContent: string; const Icon: HICON = 0; 
  const Buttons: TTaskDialogCommonButtonFlags = TDCBF_OK_BUTTON): Integer;
const
  Captions: array[TMsgDlgType] of Pointer = (@SMsgDlgWarning, @SMsgDlgError, @SMsgDlgInformation, @SMsgDlgConfirm, nil);
var
  aMsgDlgType: TMsgDlgType;
  aTitle: string;
begin
  aMsgDlgType := TaskDialogIconToMsgDlgType(Icon);
  if aMsgDlgType <> mtCustom then
    aTitle := LoadResString(Captions[aMsgDlgType])
  else
    aTitle := Application.Title;

More specifically this is a result of referencing SMsgDlgWarning, SMsgDlgError, SMsgDlgInformation and SMsgDlgConfirm, which are all declared in Vcl.Const.

Please note that this code compiles without error when we are building a single executable.

As a means of optimization, our include file does contain {$IMPORTEDDATA OFF} as this allows for faster access to (global) variables and constants. See http://hallvards.blogspot.com/2006/09/hack13-access-globals-faster.html.

According to the documentation on the error ( http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/cm_package_varref_xml.html ) this is the cause and it says "To alleviate the problem, it is generally easiest to turn on the $IMPORTEDDATA switch and recompile the unit that produces the error."

So, I have set {$IMPORTEDDATA ON} in our include file and made doubly sure by setting the Use imported data references to true in the Delphi Compiler | Compiling | Debugging section of the project options.

Unfortunately, contrary to the documentation, this did not alleviate the problem. Even setting this compiler directive directly above the offending code and rebuilding the package did not remove the errors.

What else do I need to do to solve this E2201 error? Not sure, but it may be significant that SMsgDlgWarning and its friends are resource strings?

Marjan Venema
  • 19,136
  • 6
  • 65
  • 79
  • 1
    I get this error sometimes when compiling packages, but building always works. – jpfollenius May 09 '12 at 08:37
  • @Smasher: thanks, but I did build. Shft-F9 is in my fingers as I have had too many hassles with conditional defines and "just" compiling :-) – Marjan Venema May 09 '12 at 08:39
  • You shouldn't get the error if you rebuild the original package which contains the unit with `$IMPORTEDDATA` on and make sure you are using the resulting .dcu and .dcp when building the dependant package(s). – Ondrej Kelle May 09 '12 at 09:05
  • @TOndrej: what original package? I get the error when I am building the package (EAUtils) that contains the unit with the code mentioned. EAUtils only depends on rtl/vcl packages and a package I created for the JediWinApi. I am not even into building packages that depend on EAUtils yet. – Marjan Venema May 09 '12 at 09:13
  • The original package which contains the unit, as I said (in the `contains` clause). Also make sure you're not using an old .dcu. – Ondrej Kelle May 09 '12 at 09:18
  • @TOndrej: As said I am building the package that contains the unit. No old dcu's lying around either. All of our projects have their own dcu output folders. – Marjan Venema May 09 '12 at 09:20
  • @TOndrej: LOL. Did just that before I pushed "Add Comment" ;-) – Marjan Venema May 09 '12 at 09:22
  • Sorry, the error message was a bit misleading, I was able to reproduce the problem, added an answer. – Ondrej Kelle May 09 '12 at 09:54

1 Answers1

10

The error message is, IMHO, misleading, it's Vcl.Consts which has been compiled with $G- and that's causing the problem. As a workaround, you can use something like this:

function Captions(AType: TMsgDlgType): Pointer;
begin
  Result := nil;

  case AType of
    TMsgDlgType.mtWarning:
      Result := @SMsgDlgWarning;
    TMsgDlgType.mtError:
      Result := @SMsgDlgError;
    TMsgDlgType.mtInformation:
      Result := @SMsgDlgInformation;
    TMsgDlgType.mtConfirmation:
      Result := @SMsgDlgConfirm;
  end;
end;

Using a const array of string compiles, too (although it breaks localization):

const
  Captions: array[TMsgDlgType] of string = (SMsgDlgWarning, SMsgDlgError, SMsgDlgInformation, SMsgDlgConfirm, '');

or you could build your own package containing Vcl.* units, with {$G+} and use that instead of the standard vcl package. I prefer the first solution; the latter can potentially create more problems later with deployment (so-called "DLL hell").

Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128
  • Me thank you! I had tried with a local var and some casting, but didn't think of using a function. – Marjan Venema May 09 '12 at 10:11
  • Welcome and thanks! I've also added the case of `const array of string` which works, too. – Ondrej Kelle May 09 '12 at 10:29
  • Ah interesting and good to know as it may come in handy in other places. I wonder though what the effect would be with regard to localization of the original resourcestrings. My guess is that the const array will be initialized with the values of the resourcestrings at compile time and the items, now being normal strings, won't be replaced by translated strings from a resource dll and neither can they be passed to LoadResString as we are doing with the addresses of the original resourcestring records. – Marjan Venema May 09 '12 at 11:48
  • I think you're right; for localization to work correctly you need to use `LoadResString` to resolve the strings at runtime. I've edited the answer. – Ondrej Kelle May 09 '12 at 11:55