Thanks for the reply Carl which I have included as it seems I have been
blessed in being the sole user able to post to the DUG.

The approach you mention does sound daunting, it reminds me of writing self
modifying assembler code for 6800's !

I have found another fix
(http://x64.deja.com/[ST_rn=ps]/threadmsg_ct.xp?AN=714365698.1&mhitnum=1)
which forces sendmessage on the MDI activate event. Probably not as
efficient as the fix for menu.pas but at least we can implement it.


Thanks again 
Neil



Carl's reply post .....

I too am currently unable to mail to the list or even to visit the
delphi.org.nz website.  I've tried to send the following message for the
last two days...

Cheers,
Carl

---------------------------------------------------------------------------

Neil, sorry I didn't get back to you earlier - I've been busy.

I had a brief look at patching the fuctions you were talking about, but it's
far from easy.  Maybe I can tell you what I tried and if it's really
critical to you you might investigate hiring an asm wizz (which I'm not BTW)
to investigate further.

It's possible to modify compiled code that's private in the VCL by using
VirtualProtect to unprotect it, overwriting it with what you want, and using
VirtualProtect to protect it again.  The downsides are that (a) you have to
get the address of the function that you want to modify somehow and (b) you
need to know your asm.  The method I've previously used is to overwrite the
initial code with a JMP to some code of your own, and when you've finished,
JMP back to either the end of the function or (if you wanted to run the
original function when you were done) add the instructions that you wrote
over with the JMP to the end of your function, and then JMP back to the line
that followed them.

There's an example of this patching at
http://x63.deja.com/getdoc.xp?AN=707162422.  Note that the patch works
whether or not you're using runtime packages, by checking for indirection.

Unfortunately however, changing the code in the way you need is a whole lot
harder.  Just looking at RebuildHandle is difficult, because the address of
the function is hard to find.  There may be better methods you can use to
find it (perhaps you could ask about this in the basm newsgroup), but I was
driven to finding the address of the function by looking for something that
I knew was there (Destroy) and looking a _fixed offset_ away for the
function.  The extreme downside of this is that *every time this module is
modified the offset will change*!  Given that you're using packages though,
and assuming no more patches come out until D6 (or if they do, they fix the
problem), you could do things in this way (here's my code):

  DestroyAddress := PInteger(Integer(TMenuItem) + vmtDestroy)^;
  RebuildHandleAddress := DestroyAddress + $570;
  while PInt64(RebuildHandleAddress)^ <> $CCFC458951EC8B55 do begin
    // Not the RebuildHandle procedure we expected - check for indirection
    if PJumpDWord(RebuildHandleAddress)^.OpCode <> CJmpCode then Exit;
    RebuildHandleAddress :=
PInteger(PJumpDWord(RebuildHandleAddress)^.Distance)^;
  end;
  if PInt64(RebuildHandleAddress + $55)^ <> $50000000D4E8FC45 then Exit; //
Code may already have been fixed

Notice the two hideous constants I use to check that the code is unchanged:
$CCFC458951EC8B55 is the code at the beginning of the function (obtained by
reversing the bytes in the CPU window while stepping through the real
RebuildHandle) and $50000000D4E8FC45 is the start of the section of code
that I want to modify (at an offset of $55, again, all obtained from the CPU
window).  Don't be fooled - I just use one Int64's worth of instructions for
comparison in each case because it's convenient, is likely to be good
enough, and means only one comparison rather than, if you're determined to
check thoroughly, several hundred byte comparisons.  Note also that unless
you and I have identical menus.pas units, that these offsets will be
different, and the code will exit, and in that case you will have to
determine what these values should be yourself in order to get it to work.

Ok, so with what you know so far you might be able to insert your own code.
But how do you generate appropriate asm code?  Well, you can't rewrite it
verbatim as many of the variables used within it are private anyway!  The
best I came up with during my brief foray into this was to do the following:

type
    TNewMenuItem = class(TMenuItem)
    public
        procedure NewRebuildHandle;
    end;

procedure TNewMenuItem.NewRebuildHandle;
const cFAF = $04;
var I: Integer;
    LRepopulate: Boolean;
begin
// Point "A"
    I := GetMenuItemCount(Handle);
    LRepopulate := I = 0;
    while I > 0 do begin
        if (WordRec(LongRec(GetMenuState(Handle, I - 1,
MF_BYPOSITION)).Lo).Lo and cFAF) = 0 then begin
             RemoveMenu(Handle, I - 1, MF_BYPOSITION);
             LRepopulate := True;
        end;
        Dec(I);
    end;
    if LRepopulate then begin
        // JMP to point "B"
    end
    else // JMP to point "C"
end;

That's half the patch of RebuildHandle you were given, made public.  Then
you would have to use the techniques I first mentioned, with the stuff on
the URL as a base on how to do it, to modify the real RebuildHandle at
runtime:

procedure TMenuItem.RebuildHandle;
begin
  if csDestroying in ComponentState then Exit;
  if csReading in ComponentState then
    FStreamedRebuild := True
  else
  begin
    if FMergedWith <> nil then
      FMergedWith.RebuildHandle
    else
    begin
// Overwrite HERE with a JMP to point "A" (in TNewMenuItem.NewRebuildHandle)
      while GetMenuItemCount(Handle) > 0 do
        RemoveMenu(Handle, 0, MF_BYPOSITION);
// Point "B"
      if (FParent = nil) and (FMenu is TMainMenu) then
      //if (Owner = nil) or (Owner is TMainMenu) then
      begin
        DestroyMenu(FHandle);
        FHandle := 0;
      end
      else
        PopulateMenu;
      MenuChanged(False);
    end;
  end;
// Point "C"
end;

Anyway, I never got around to actually trying to implement this, and I never
even looked at the other function.  My theory is that the code in
TNewMenuItem should execute successfully, as it's not too different to a
cast, but there may be other factors, and there may also be CPU registers
that need to be conserved between JMPs (although I don't think so).  Of
course translating the second function may be even harder!  Whether you
attempt to take this further yourself will depend on how desperate you
really are.

Sorry this is such an incomplete hack.

Cheers,
Carl

---------------------------------------------------------------------------
    New Zealand Delphi Users group - Delphi List - [EMAIL PROTECTED]
                  Website: http://www.delphi.org.nz
To UnSub, send email to: [EMAIL PROTECTED] 
with body of "unsubscribe delphi"

Reply via email to