Brandon Staggs I guess your SO question got closed or removed because it was of the category "why does this code not work". I was just going to comment when it disappeared.

Brandon Staggs  I guess your SO question got closed or removed because it was of the category "why does this code not work". I was just going to comment when it disappeared.

Anyway:
"Diffing the generated asm is telling me that this is a codegen bug (already present in XE5 afais) where it switched the parameter addresses on the stack in the nested procedure - you should report it."

Comments

  1. Do you have a link to the deleted question? Hard to tell, but perhaps it should be nominated for undeletion.

    ReplyDelete
  2. It's here: http://stackoverflow.com/questions/33127136/

    Brandon deleted it. Presumably because it is a duplicate and he decided that there was no need for another question kept for posterity.

    And no, it's not a compiler bug. It was an anomoly that the code ever worked at all on the 32 bit compiler. The documentation has always outlawed this. The documentation says:

    Nested procedures and functions (routines declared within other routines) cannot be used as procedural values.

    See: http://stackoverflow.com/questions/10162749/why-cannot-take-address-to-a-nested-local-function-in-64-bit-delphi

    ReplyDelete
  3. I did not know that - but now suddenly FixInsight W526 makes sense :)

    ReplyDelete
  4. Yuep, I deleted it because of the duplicate, although the title of the previous question is more like and answer to mine, which is why I did not find it in my initial search-- I didn't know the answer so didn't know to look for it, LOL.

    ReplyDelete
  5. Brandon Staggs You could edit the title of the other question to make it easier to find.

    ReplyDelete
  6. The thing is, there is no indication of an error, you just have to "know" you can't do it. My callback got the right number of calls but with no changes in the parameters.  I agree it's not a compiler bug that it didn't work, but there should be some indication that it shouldn't work, other than mysterious parameters.

    ReplyDelete
  7. Some indication, like a compiler warning or error?  Calling Roman Yankovsky...

    ReplyDelete
  8. I don't think we need to change any of the questions. There must be at least 10 dupes! I did know where to look, I'm sure I've answered the question more than once!

    As I said in the answer that I linked to, the real problem is the RTL header translation using an untyped Pointer instead of a procedural type for the callback procedure. The compiler simply cannot perform type checking if you have to use @Func to get an untyped pointer to pass to the Winapi function.

    If Emba would grasp the nettle and fix these bogus header translations then we'd not have to keep on discovering the same problem again and again. The compiler could tell us.

    Personally I follow a rule to never use @ on a procedure. If I have to call a function like EnumWindows and the Emba header translation is bogus, I replace it with a typesafe version. Then I don't need to use @ with procedures.

    ReplyDelete
  9. Do you have a list of these mistranslated headers anywhere? I'd like to file them in QP (or vote if there's already an item.)

    ReplyDelete
  10. David Millington I think it is probably futile. I believe that there are reported issues already. I think Emba won't change because it will break lots of code. I can understand the point of view. But it would be nice to be able to opt in to a proper type safe header translation, where possible.

    I don't have a list. There are a shed load of them though. Search for FARPROC. And then the various aliases of FARPROC. It will blow your mind.

    ReplyDelete
  11. David Millington FixInsight's warning W526 - "Pointer to a nested method" :)

    ReplyDelete
  12. David Heffernan but it could/should work to have two overloads for these APIs, one with the old pointer (marked as deprecated) and one with the type-safe procedure type, I think?

    ReplyDelete
  13. +Hallvard Maybe. I'm not sure whether or not that overloading would be sufficiently unambiguous.

    ReplyDelete
  14. This seems to work:

    type
      TEnumWindowsProc = function (hwnd: HWND; lParam: LPARAM): BOOL; stdcall;

    function EnumWindows(lpEnumFunc: TEnumWindowsProc; lParam: LPARAM): BOOL; stdcall; overload; external user32 name 'EnumWindows';
    function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL; stdcall; overload; external user32 name 'EnumWindows';

    function GlobalProc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
    begin
      Result := False;
    end;

    procedure Test;
      function NestedProc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
      begin
        Result := False;
      end;
    begin
      EnumWindows(@GlobalProc, 0);
      EnumWindows(@NestedProc, 0);
      EnumWindows(GlobalProc, 0);
      EnumWindows(NestedProc, 0); // E2094 Local procedure/function 'NestedProc' assigned to procedure variable
    end;

    ReplyDelete

Post a Comment