I have been going round for a few days on an issue here... Basically, I
have a 3rd party app that allows the user to exit even if the app is not in
a safe place to do so. This results in data corruption and a host of other
issues. The developer of this app is not willing to do anything to prevent
this, so I am doing it myself.
I now control where or not the user can use the File/Exit , System Menu, or
Shutdown/Restart to exit the application based on the apps state. When it
is safe to close I re-enable those exit methods. The two methods I have not
been able to stop yet are: Alt-F4 and End Process in task manager.
I was referred to madshi's madCodeHook to handle this, but have not had a
lot of luck getting it to run as expected when injected into an app. madshi
actually recommended that I use SetWindowsHookEx instead of madCH. My
problem there is that I cannot seem to find a way to monitor for WM_CLOSE on
a single process. It seems wasteful to set a global hook when only a couple
of the processes might be affected.
Can anyone think of any alternatives? Maybe I am just missing something
obvious?
I would love to see a demo that would intercept and block the WM_CLOSE for
notepad.exe. If it is possible, then it should be fairly simple.
Anyone want to help me before my head explodes?
THANKS!
Dave
> My problem there is that I cannot seem to find a way to monitor for
> WM_CLOSE on a single process. It seems wasteful to set a global
> hook when only a couple of the processes might be affected.
SetWindowsHookEx() lets you specify the specific thread ID that you want to
hook. All you need to do is determine the thread ID of the external
application's main thread. Easiest way to do that is to find a HWND that
belongs to that application and then use GetWindowThreadProcessId(). You
still need to put your hook code into a DLL in order to inject the hook into
an external process, but you can hook a single thread of an external process
instead of all threads in all processes.
Gambit
Thanks Gambit. That is what I was hoping to hear. I just did not want to
waste too much more time on it if I was chasing something impossible.
I will give it another go tomorrow. If I can intercept WM_CLOSE in notepad
then I am sure I will be able to make my app work as well.
Wish me luck,
Dave
Great news:
1) I got the SetWindowsHookEx working properly and can now stop WM_CLOSE as
needed.
2) I also figured out how to determine if the System Close button is grayed
or not.
Problem now is that I am having trouble in the callback function of my DLL.
I need to check the status of the System Close button of the hooked window,
but cannot get it to work. This is the code that is giving me trouble:
function GrayX:integer;
var
AppSysMenu: THandle;
MenuItemInfo: TMenuItemInfo;
Buffer: array[0..79] of Char;
begin
Result := 0;
// Setup MenuItemInfo
MenuItemInfo.cbSize := SizeOf(MenuItemInfo);
MenuItemInfo.fMask := MIIM_STATE;
MenuItemInfo.dwTypeData := Buffer;
MenuItemInfo.cch := SizeOf(Buffer);
// Get System Menu and Enable/Disable X
AppSysMenu:=GetSystemMenu(hOrigWdw,False);
GetMenuItemInfo(AppSysMenu,SC_CLOSE,false,MenuItemInfo);
if (MenuItemInfo.fState = MF_GRAYED) then
Result := 1;
end;
This function is called from my callback function in the DLL. The exact
same code works fine in a regular app, but not in the DLL. My guess is that
the handle passed into the Callback Function might not be the handle of the
hooked window? If this is true, how can I figure that out?
Any ideas why this would not work in a DLL? No errors, but it will never
return a 1 (MF_GRAYED).
Thanks,
David
I just tried adding this line before the GetSysMenu call and it works now:
hOrigWdw := FindWindowEx(0,0,'Notepad',nil);
Of course, I need the DLL to be able to figure out it's host's handle on
it's own. In my final app there couple be many 'notepads' running, each
with their own handles.
I had tried to store the Handle from the window in my SetHook function, but
it appears the handle changes after somehow after the SetWindowsHookEx?
> I just tried adding this line before the GetSysMenu call and it works now:
> hOrigWdw := FindWindowEx(0,0,'Notepad',nil);
Did you compare that HWND value to the original HWND to see if they are the
same or not?
> Of course, I need the DLL to be able to figure out it's host's handle on
> it's own.
That would be very difficult, if you take into account that there is no
concept of a "main" window in the Win32 API. A thread can have multiple
windows running in it. If you can't determine the exact HWND that you want
to access, then you will just have to loop through all of the available HWND
for the thread that you have hooked, such as with EnumThreadWindows().
Another thing to consider trying to is implement a WH_CBT hook for each
thread, so that you can detect the creation/deletion of new HWNDs after your
main hook has been installed.
> In my final app there couple be many 'notepads' running, each with their
own handles.
Since you are trying to hook 1 thread at a time, it is your responsibility
to identify the separate threads so that you hook them accordingly.
Otherwise you are back to the scenerio when you need to install a global
hook instead.
> I had tried to store the Handle from the window in my SetHook function,
but
> it appears the handle changes after somehow after the SetWindowsHookEx?
Please elaborate.
Gambit