> I can't see where the dialogs unit is getting the TMsgDlgButtons method
> or function or procedure or whatever it is called in Pascal from.

Short story: the patch is attached, it should help:)

Longer explanation:

1. TMsgDlgButtons is a "type". It's a set (which is like a type-safe
bitfield in Pascal). Writing "Dialogs.TMsgDlgButtons" means "take
TMsgDlgButtons type from Dialogs unit, not from any other unit that
may define the same name" --- that's how you deal with multiple used
units having the same identifier in Pascal.

2. What happens in this code is a little dirty, as mricron code
defines it's own type "TMsgDlgButtons", with the *exact* same memory
layout as standard "TMsgDlgButtons" type in "Dialogs" unit. "Dialogs"
unit is part of the Lazarus library (LCL). Then the line

  lButtons:= Dialogs.TMsgDlgButtons(Buttons);

converts one type to another. The dirtyness here is that such typecast
avoids any type checks, it just assumes that memory layout of both
"TMsgDlgButtons" types matches, and that the programmer knows what
(s)he is doing:)

3. To make it a little confusing, type names are the same, so you need
to be aware how compiler resolves them, and some error messages become
unclear. This message from FPC:

  Illegal type conversion: "TMsgDlgButtons" to "TMsgDlgButtons"

actually means that we cannot convert "TMsgDlgButtons as defined in
unit dialogsx" to "TMsgDlgButtons as defined in unit Dialogs".
Possibly FPC checks got stricter? Which would be a good thing here ---
this code is indeed dangerous, it's good that it's prohibited, IMHO.

In fact, Lazarus Dialogs.TMsgDlgButtons type did change (there's a new
enum mbClose), it only accidentally didn't change the memory layout of
TMsgDlgButtons (as mbClose was added at the end).

4. The attached patch just does the type conversion the long (but safe) way.

Regards,
Michalis
--- common/dialogsx.pas.orig	2016-02-06 15:20:36.000000000 +0100
+++ common/dialogsx.pas	2016-02-06 15:32:45.000000000 +0100
@@ -66,6 +66,36 @@
 end;
 {$ENDIF}
 
+{ Convert our TMsgDlgButtons type to Dialogs.TMsgDlgButtons type
+  in a type-safe manner. Do not assume that memory layout matches between
+  - TMsgDlgButtons and Dialogs.TMsgDlgButtons, or
+  - TMsgDlgBtn and Dialogs.TMsgDlgBtn. 
+}
+function MsgDlgButtonsConvertToStandard(
+  const Buttons: TMsgDlgButtons): Dialogs.TMsgDlgButtons;
+var
+  B: TMsgDlgBtn;
+begin
+  Result := [];
+  for B := Low(B) to High(B) do
+    if B in Buttons then
+      { convert our TMsgDlgBtn to Dialogs.TMsgDlgBtn type }
+      case B of
+        mbYes	  : Include(Result, Dialogs.mbYes     );
+	mbNo	  : Include(Result, Dialogs.mbNo      );
+	mbOK	  : Include(Result, Dialogs.mbOK      );
+	mbCancel  : Include(Result, Dialogs.mbCancel  );
+	mbAbort	  : Include(Result, Dialogs.mbAbort   );
+	mbRetry	  : Include(Result, Dialogs.mbRetry   );
+	mbIgnore  : Include(Result, Dialogs.mbIgnore  );
+	mbAll	  : Include(Result, Dialogs.mbAll     );
+	mbNoToAll : Include(Result, Dialogs.mbNoToAll );
+	mbYesToAll: Include(Result, Dialogs.mbYesToAll);
+	mbHelp	  : Include(Result, Dialogs.mbHelp    );
+        else raise Exception.Create('Unsupported TMsgDlgBtn value');
+      end;
+end;
+
 function MsgDlg(const Msg: string; DlgType: TMsgDlgType; Buttons: TMsgDlgButtons; HelpCtx: Longint): Word;
 {$IFDEF GUI}
 var
@@ -74,7 +104,7 @@
 
 begin
   lDlgType :=  Dialogs.TMsgDlgType(DlgType);
-  lButtons:= Dialogs.TMsgDlgButtons(Buttons);
+  lButtons:= MsgDlgButtonsConvertToStandard(Buttons);
   result := MessageDlg(Msg, lDlgType, lButtons,HelpCtx);
 {$ELSE}
 begin

Reply via email to