Link to home
Start Free TrialLog in
Avatar of RoyKendall
RoyKendall

asked on

Need help with SetWindowsHookEx/WH_CBT

Hi experts i need to set a system hook to get notified when a window is created or destroyed and when it is created i will need to use SetWindowPos and change the window dimensions before the window becomes visible, if not before immediately after. This question will be worth 500 points if done to as i described, with grade of "A" meaing it is 2 thousand points more to your score. I don't think what i ask is too difficult for experts to handle but i do not mind giving maximum points since the help is needed and i respect that experts are often busy.

What i've thought up is to use a CBT hook and then call SetWindowPos api in the hook callback when the window of interest is created. I don't need the extra overhead of a global system hook however so when i set the hook i need the last parameter to be non-zero, i.e* so that window thread i'm interested in only gets hooked.

var Hook: HHOOK;
begin
Hook := SetWindowsHookEx(WH_CBT, CBT_CB, hInstance, GetWindowThreadProcessID(Wnd, nil));
                                                                                         { only care about notepad's main thread }
...

I need example, it can be any window really but i've used notepad in my unsuccessful tests. I need to obtain the old window dimensions everytime the window is created, maybe use GetSystemMetrics() or something and store the original values so that i can easily change the position offsets without causing my window to appear half way off screen or something like this

 case nCode of
   HCBT_CREATEWND:
    SetWindowPos(FindWindow('notepad', nil), HWND_BOTTOM, OLD_HORZ-5, OLD_VERT-5, OLD_W-5, OLD_H-5, 0);

If i stated anything unclearly please ask and i can try to clarify, people say my english is pretty good but its not my native language so i tried to explain in the best way i know how.

Thanks R.K



ASKER CERTIFIED SOLUTION
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of RoyKendall
RoyKendall

ASKER

Slick812 thanks for reply

In laymens term i need to set a computer based training hook so that whenever notepad gets created I can assign the window custom window dimensions via SetWindowPos.

(Example given)
CBT Hook is set, notepad is opened, HCBT_CREATEWND fires in my hook's callback function and I call SetWindowPos giving the notepad window new custom dimensions, but remembering the old dimensions so i can base the new dimension offsets from them within the call to SetWindowPos. I don't see what isn't very clear, maybe it was the way i phrase it in first question.

MSDN excerpt:

"At the time of the HCBT_CREATEWND notification, the window has been created, but its final size and position may not have been determined and its parent window may not have been established. It is possible to send messages to the newly created window, although it has not yet received WM_NCCREATE or WM_CREATE messages. It is also possible to change the position in the z-order of the newly created window by modifying the hwndInsertAfter member of the CBT_CREATEWND structure." -MSDN

So I believe that it is possible to give the window position dimensions before it actually receives the create or nccreate messages. Correct me if I'm way off. I need help with this and the link you showed me was for disallowing a window to be created which isn't what i need. I already know how to hook, i just need SetWindowPos to work properly in my hook callback is all.

Thanks, R.K

 
Slick812,
 here's a quick example i wrote for you, maybe this will give you a better idea of what im needing done.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//32-bit DLL code
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library CBT_HOOK;

uses
 Windows,
 Messages;

var
 h_HOOK: HHOOK;    // handle to hook procedure

function CBT_FUNC(nCode: Integer; wP: wParam; lP: lParam): LRESULT; stdcall;
var
 hTemp: hWnd;                                                                    // serve as temporary window handle var
 szClass: array [0..MAX_PATH] of char;                                  // stores the class name
begin  
 try
   if (nCode >= HC_ACTION) then                                           //  >= 0
   begin
     case nCode of
       HCBT_CREATEWND:                                                       // ( if ncode = 3) then a window is about to be created
       begin
         hTemp := HWND(wp);                                                  // word parameter stores window handle
         FillChar(szClass, MAX_PATH, 0);                                    // wipe out static array elements
         ZeroMemory(@szClass, SizeOf(szClass));                       // not really needed but cant hurt
         GetClassName(hTemp, szClass, MAX_PATH);                  // get class name from window handle for cmp
         if (szClass = 'Notepad') then                                          // cmp is good...      
         begin
         SetWindowPos(FindWindow('Notepad', nil), 0, 10, 535, 490, 65, 0);
       // MessageBoxA(0, 'Notepad Window Created', '', MB_OK);
         end;
       end;
       HCBT_DESTROYWND:                                                     // nCode = 4                                                          
       begin                                                                             // a window is about to be destroyed
         hTemp := HWND(wp);                                                  
         FillChar(szClass, MAX_PATH, 0);
         ZeroMemory(@szClass, SizeOf(szClass));
         GetClassName(hTemp, szClass, MAX_PATH);
         if (szClass = 'Notepad') then
         begin
      //  MessageBoxA(0, 'Notepad Window Destroyed', '', MB_OK);
         end;
       end;
     end;
   end;
   result := CallNextHookEx(h_HOOK, nCode, wp, lp); // pass on hook chain so other installed hooks get called in system
 except
   result := 0;
 end;
end;

Procedure InitHook();
begin
try
h_HOOK := SetWindowsHookEx(WH_CBT, @CBT_FUNC, hInstance, 0); // this example will hook all threads in system
except                                                   // I'd like to use GetWindowThreadProcessID as last param on the set hook
end;                                                      // to keep less overhead and only affect 1 thread
end;

Procedure KillHook();
begin
if (h_HOOK <> 0) then                  // handle is non-zero, hook must still be active
try
UnhookWindowsHookEx(h_HOOK);  // attempt to unhook
except

end;
end;

procedure DllEntry(dwReason: DWORD);
begin
 case dwReason of                        
   DLL_PROCESS_ATTACH:             //1
begin

end;
   DLL_PROCESS_DETACH:             //0
begin

     end;
  end;
end;

exports
 InitHook,
 KillHook;

begin
 DisableThreadLibraryCalls(hInstance);       // no need for thread notifications
 DllProc := @DLLEntry;                              // assign pointer to entrypoint
 DllEntry(DLL_PROCESS_ATTACH);             // manually call our entrypoint for attachment
end.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Executable code
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Procedure InitHook; external 'CBT_HOOK.dll';      // extrn decl
Procedure KillHook; external 'CBT_HOOK.dll';      // extrn decl

InitHook();                                                       // install the hook system wide

KillHook();                                                       // removes installed hook from system

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Sorry if the comments bleed through into the code blocks i just woke up and i wrote this is a hurry before work.

If you look at the hcbt_createwnd case you'll see that after i determine that the window handle matches the classname of
interest i call SetWindowPos hoping to give the window new size dimensions before it is created. I would like to get the current dimensions of the window if possible so that when i apply the new ones i have a general idea for my math calculations. Like i said maybe one could dabble with GetSystemMetrics to get them. I don't know how to get this to work, if it will work at all but i'm seriously in need of solution. Maybe i'm casing the wrong event and i shouldn't be looking for createwnd but maybe HCBT_ACTIVATE rather, i don't know. I've only been coding in delphi for 6 months so i'm no guru ;) My only guess is i'm calling SetWindowPos prematurely and I need another way.

Thanks, R.K
Slick812,
I solved it myself. I just glanced at MSDN again and switched the cbt to a shell hook, replaced hcbt_createwnd with hshell_windowcreated and hcbt_destroywnd with hshell_windowdestroyed and viola. Seems to be working as i needed. I'll still award you points though since you were the only person who responded in the last 24 hrs. If i run into anything else involving this hook i'll be sure to ask for you.

Thanks, R.K
by the way slick812, if you want to help me out with something i'd like to see able to get the windows dimensions verbatim before i attempt to change them. I still havent done that yet.

R.K