Jump to content
PeterPanettone

Add a system-menu item to all applications?

Recommended Posts

You probably need to hook at the other processes. Not easy, and likely to be extremely brittle. Also won't work for any programs that do system menus in a non standard way. 

 

Share this post


Link to post

Until now I achieved what I wanted to achieve by intercepting the right-button-mouse-click on the title-bar of any application window. But now I have a system program which also does exactly that, so it overrides my action.

Share this post


Link to post

I used the system menu to allow users to change the VCL style of my application. The code looked like this:

Procedure TMyApplicationMainForm.FormShow(Sender: TObject);
Var
 sysmenu: THandle;
 a: Integer;
 flags: Cardinal;
Begin
 sysmenu := GetSystemMenu(Self.Handle, False);
 AppendMenu(sysmenu, MF_SEPARATOR, Word(-1), '');
 For a := Low(_validstyles) To High(_validstyles) Do
  Begin
   flags := MF_STRING;
   If TStyleManager.ActiveStyle.Name = _validstyles[a] Then flags := flags Or MF_CHECKED;
   AppendMenu(sysmenu, flags, WM_USER + 1 + a, PWideChar(_validstyles[a]));
  End;
End;

Instead of Self.Handle I suppose you can supply a different form handle returned by FindWindow. This solution will send a WM_USER message to the form when the appended item is clicked but the foreign one has to process that correctly...

 

I guess you can append to the system menu of an other application, but I doubt that it will ever work correctly. At least not this way 😞

Share this post


Link to post

I created a test project (see attachment) which can add a system menu item to my own application or to any other application (tested with notepad: get the notepad window handle).

 

However, reacting to the click event of the added system menu item works only when added to the own application.

 

How can I make it sending a message to my application's window?

 

AddSystemMenuItemToAnyApp.zip

Edited by PeterPanettone

Share this post


Link to post
4 hours ago, David Heffernan said:

Inject code into every process

I don't know any official Delphi library which does process injection. Is there any?

Edited by PeterPanettone

Share this post


Link to post
16 hours ago, PeterPanettone said:

How can I make it sending a message to my application's window?

I don't think 'injecting code' is the best way to go.

 

I think you need to catch the WM_SYSCOMMAND which is sent to the other application(s).

You should be able to do that with SetWindowsHookEx().

https://social.msdn.microsoft.com/Forums/windows/en-US/f87193b7-ccc2-4c30-9b1e-195addd375c8/how-to-intercept-a-wmsyscommand-sent-to-another-app?forum=winforms

 

An example in C++ http://forums.codeguru.com/showthread.php?161054-System-wide-hook-for-WM_SYSCOMMAND

 

Share this post


Link to post
25 minutes ago, rvk said:

I don't think 'injecting code' is the best way to go.

SetWindowsHookEx is one of the ways of 'injecting code' on Windows.

 

Documentation: " SetWindowsHookEx can be used to inject a DLL into another process."

 

Share this post


Link to post
1 hour ago, Ondrej Kelle said:

SetWindowsHookEx is one of the ways of 'injecting code' on Windows.

 

Documentation: " SetWindowsHookEx can be used to inject a DLL into another process."

 

The example here is of WH_MSGFILTER which I don't think does injection. 

Share this post


Link to post
4 hours ago, David Heffernan said:

The example here is of WH_MSGFILTER which I don't think does injection. 

Yes, WH_(SYS)MSGFILTER hooks are injected into every process that is being hooked, if hooking other processes than the one that is installing the hook.

Share this post


Link to post

I have found a code example that works perfectly and gets a callback when clicking a system menu item in ANY application! It adds a system-wide hook to WM_SYSCOMMAND messages by using a DLL. (Unfortunately, it works only with 32-bit programs):

 

AddSystemMenuItemToAnyApp2.zip

 

I have tried to make it work with 64-bit applications by compiling the projects (EXE and DLL) as Windows 64-bit platform. I did not succeed. I made these changes in the DLL code:

 

THandle is different on the 64-bit platform: System.UInt64

 

• The error seems to be in the DLL code in the function Hookup in the line GetMsgProcRec^.Hwnd := Hwnd:

   Access violation at address 0000000003585C9F in Modul 'fwGetMsgProc.dll'. Reading from address 0000000000000000.

 

• Also, in the EXE code, in the Button5Click handler I changed:

@Hookup := GetProcAddress(hGetMsgProc, Pchar(LongInt(1)));

to:

@Hookup := GetProcAddress(hGetMsgProc, PAnsichar(LongInt(1)));

 

But, as I said, I did not succeed: There is still the above-mentioned access violation in the DLL code.

 

Could anybody with more experience have a look at the code and successfully compile it to 64-bit?

 

The only code portions you would need to look at are:

• The Button5Click handler in the EXE code

• The DLL code which is just a few lines

Edited by PeterPanettone

Share this post


Link to post
22 minutes ago, PeterPanettone said:

The error seems to be in the DLL code in the function Hookup in the line GetMsgProcRec^.Hwnd := Hwnd:

   Access violation at address 0000000003585C9F in Modul 'fwGetMsgProc.dll'. Reading from address 0000000000000000.

The reason for this is because GetMsgProcRec seems to be nil in the DLL.

It seems that the call to CreateFileMapping fails in 64 bit.

 

For testing this you can add your fwGetMsgProc dll project to the projectgroup of TESTHks and debug the dll-calls.

Set a breakpoint in InitializeDll and you'll see it stops at the first MappedMem line.

Next (on the second try) trace into the procedure with F7 and you'll see the exception for cErrorCreating,['TMappedMem'] is called.

 

You'll see a $FFFFFFFF as parameter, meaning an invalid handle (see https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createfilemappinga)

But INVALID_HANDLE_VALUE is THandle(-1) which is something different then $FFFFFFFF in 64 bit. (you'll need $FFFFFFFFFFFFFFFF)

 

So change it to

Quote

    fHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0,  Size, PChar(Name));

Now the hook can successfully be installed.

 

(I hadn't much luck with catching anything but now you're a step closer :classic_blink:)

 

Share this post


Link to post

Unfortunately, when I try to start a debug session for any Win64 project in my Delphi 10.3.1, I get the following error (debugging Win32 projects does work):

 

image.png.447daffa25390aac0c84c74ed366d31b.png

 

Why is there an error in a CPP file if I try to start a debug session for a DELPHI project?

 

Then I click any button, then this error message pops up:

 

image.thumb.png.e61a34d7498d028b67048fd115545a67.png

 

So, unfortunately, I cannot debug the Win64 projects.

Share this post


Link to post
3 hours ago, rvk said:

fHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0,  Size, PChar(Name));

When I use this call instead of the previous call in \fwGetMsgProc\MapFile.pas then there is no more error in the DLL code in the function Hookup in the line GetMsgProcRec^.Hwnd := Hwnd

 

However, I also do not get a callback when clicking on any system menu item in any application.

Edited by PeterPanettone

Share this post


Link to post
1 minute ago, PeterPanettone said:

However, I also do not get a callback when clicking on any system menu item in any application.

Yep. That was my conclusion too. There still must be something wrong.

 

Using WH_CBT you do get some messages. There supposed to be a HCBT_SYSCOMMAND message containing the information but I haven't looked at it further.

 

Share this post


Link to post
3 minutes ago, PeterPanettone said:

Does anybody know him

 

Never heard of him 🙂

 

  • Haha 2

Share this post


Link to post
22 minutes ago, PeterPanettone said:

The package

 

That was a joke.

That code is from last century, any warranty has expired..

Share this post


Link to post
3 minutes ago, FredS said:

 

That was a joke.

That code is from last century, any warranty has expired..

But it works well with 32-bit programs. How can it make to work with BOTH 64-bit programs and 32-bit programs?

Share this post


Link to post
4 minutes ago, PeterPanettone said:

How can it make to work with BOTH

You need DLLs for both.

Share this post


Link to post
28 minutes ago, FredS said:

You need DLLs for both.

That's what I am doing. But it does not work with 64-bit DLLs. See my attached code example above.

Share this post


Link to post
9 hours ago, FredS said:

 

Never heard of him 🙂

 

Nice, I actually laughed on this 🙂

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×