Jump to content

Making CALLWNDPROCRET hook


Amate
 Share

Recommended Posts

I made a hook to an application and this application crashes for some reason. I do not understand why. Cannot find what am I doing wrong.

Declaration.

Global Const $tagCWPRETSTRUCT = "lresult lResult;lparam lParam;wparam wParam;uint message;hwnd hwnd"
Global Const $WM_MEASUREITEM = 0x002C
Global $hHook

Here i registered a hook.

MessageCaptured = 0
$MyNULL = 0
$hStub_GetMenuTextProc = DllCallbackRegister("_GetMenuTextProc", "lresult", "int;wparam;lparam")
$hmod = _WinAPI_GetModuleHandle(0)
$hHook = _WinAPI_SetWindowsHookEx($WH_CALLWNDPROCRET, DllCallbackGetPtr($hStub_GetMenuTextProc), $hmod, _WinAPI_GetWindowThreadProcessId(WinGetHandle($WT_Main), $MyNULL))

Callback and freeing.

Func _GetMenuTextProc($nCode, $wParam, $lParam)
    Local $tRETSTRUCT
    $tRETSTRUCT = DllStructCreate($tagCWPRETSTRUCT, $lParam)
    If $nCode < 0 Then
        Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam)
    EndIf

    If DllStructGetData($tRETSTRUCT, "message") = $WM_MEASUREITEM Then
        $MessageCaptured = 1
    EndIf

    Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam)
EndFunc   ;==>_GetMenuTextProc

Func OnAutoItExit()
    _WinAPI_UnhookWindowsHookEx($hHook)
    DllCallbackFree($hStub_GetMenuTextProc)
EndFunc   ;==>OnAutoItExit
Edited by Amate
Link to comment
Share on other sites

Is the thread of the $WT_Main window belongs to a different process, other than yours? If so, the hook procedure must reside in a DLL file. Also, the structure is not correct, it should be:

Global Const $tagCWPRETSTRUCT = "int_ptr lResult;int_ptr lParam;uint_ptr wParam;uint message;hwnd hwnd"

You could check if the dll struct is correct by inspecting the @error value or using DllStructGetSize(). Refer to this topic about the hooking process, just scroll down to the accepted solution. You can ignore the shared data segment as it's not a system-wide procedure, nonetheless, the hook procedure must reside in a DLL (if I'm not wrong)

Edited by Authenticity
Link to comment
Share on other sites

Is the thread of the $WT_Main window belongs to a different process, other than yours?

Yes, That is right.

If so, the hook procedure must reside in a DLL file.

That is not the only solution. It is enough that all the handles are in shared section. I have no idea how to do it with AutoIT. Please advice.

Also, the structure is not correct, it should be:

Global Const $tagCWPRETSTRUCT = "int_ptr lResult;int_ptr lParam;uint_ptr wParam;uint message;hwnd hwnd"

You could check if the dll struct is correct by inspecting the @error value or using DllStructGetSize().

Thank you. I've corrected my code. But this is not the reason why $WT_Main application crashes.

Refer to this topic about the hooking process, just scroll down to the accepted solution. You can ignore the shared data segment as it's not a system-wide procedure, nonetheless, the hook procedure must reside in a DLL (if I'm not wrong)

I cannot view the solution. I cannot use trial period because they demand credit card and do not accept my Visa Electron. Please copy\paste the solution here.
Link to comment
Share on other sites

I reviewed the solution.

How can I make DLL with AutoIT? How can I manage shared memory with AutoIT?

Please advice.

Is there any way I could perform my task with AutoIT only?

My task is as follows. I need to get main menu item names from Delphi written application. Main menu control items set as owner drawn. So I need to intercept WM_MEASUREITEM message on return from owner so I can get long pointer to MEASUREITEMSTRUCT.

/----------
// Main.cpp
//----------
#include <windows.h>
#include <iostream.h>
#include <stdlib.h>
#include <conio.h>

bool setHook(DWORD threadId);
bool removeHook();

void main(int argc,char* argv[])
{
   if(setHook(0)) {
      cout<<"Press Enter to quit"<<endl;
      _getch();
      cout<<removeHook()<<endl;
   }  
}

//----------
// Hook.dll
//----------

// Cannot use WH_GETMESSAGE, because WM_NCPAINT is *sent*, but not *posted*.

#include <windows.h>

#pragma data_seg(".shared")
HHOOK hHook=0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:.shared,RWS")

HINSTANCE hDll;

void onNCPaint(CWPRETSTRUCT& msg)
{
  // Do whatever you want.
}


LRESULT CALLBACK hookProc(int code,WPARAM wParam,LPARAM lParam)
{
   if(code==HC_ACTION) {
      CWPRETSTRUCT* msg=(CWPRETSTRUCT*)(lParam);
      if(msg->message==WM_NCPAINT) {
         onNCPaint(*msg);
      }
   }
   return CallNextHookEx(hHook,code,wParam,lParam);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID)
{
   if(fdwReason==DLL_PROCESS_ATTACH)
      hDll=hinstDLL;
   return TRUE;
}

_declspec(dllexport) bool setHook(DWORD threadId)
{
   if(hHook==0)
      hHook=SetWindowsHookEx(WH_CALLWNDPROCRET,hookProc,hDll,threadId);
   return hHook!=0;
}

_declspec(dllexport) bool removeHook()
{
   bool r=true;
   if(hHook) {
      r=UnhookWindowsHookEx(hHook);
      hHook=0;
   }
   return r;
}
Link to comment
Share on other sites

Currently, you can't write dlls in AutoIt (and maybe not so currently). You can use the free ones though, including BloodShed, Code::Blocks, or VS2008 or VS2005 the express edition. Do you want to intercept the message to be able to get the data (string) because it's an owner-drawn and otherwise you can't?

Link to comment
Share on other sites

Do you want to intercept the message to be able to get the data (string) because it's an owner-drawn and otherwise you can't?

Seems I cannot do it bacause the structure contains mainly size of the owner drawn item but not the string, but anyway i can get itemID and custom data from itemData field and this allows me to identify the menu item unambiguously. I need this bacause for some reason the menu items may be ordered different ways on different computers so i cannot use zero position id and be sure I execute the same menu item on different computers. My current code for menu follows.

$hMenu = _GUICtrlMenu_GetMenu(WinGetHandle($WT_Main))
$hDoc = _GUICtrlMenu_GetItemSubMenu($hMenu, $MM_Doc)
$hCash = _GUICtrlMenu_GetItemSubMenu($hDoc, $MM_Cash)

_GUICtrlMenu_SetItemText($hMenu, $MM_Doc, "Doc")
_GUICtrlMenu_SetItemText($hDoc, $MM_Cash, "Cash")
_GUICtrlMenu_SetItemText($hCash, $MM_Credit, "Credit")

WinMenuSelectItem($WT_Main, "", "Doc", "Cash", "Credit")

This code works in different ways on different computers because it uses zero bazed index ($MM_Credit). I need to make it work the same way on any computer.

Link to comment
Share on other sites

You can get the menu and sub-menus items and items ids into a two-dimensional array and use the ID instead, assuming the items strings don't change.

#include <GUIMenu.au3>

Local $hWnd = WinGetHandle("")
Local $hMenu = _GUICtrlMenu_GetMenu($hWnd)
Local $hSubMenu

For $i = 0 To _GUICtrlMenu_GetItemCount($hMenu)-1
    
    ConsoleWrite(_GUICtrlMenu_GetItemText($hMenu, $i) & @CRLF)
    $hSubMenu = _GUICtrlMenu_GetItemSubMenu($hMenu, $i)
    
    For $j = 0 To _GUICtrlMenu_GetItemCount($hSubMenu)-1
        ConsoleWrite(@TAB &  _GUICtrlMenu_GetItemText($hSubMenu, $j) & " " & _GUICtrlMenu_GetItemID($hSubMenu, $j) & @CRLF)
    Next
Next
Link to comment
Share on other sites

As I mentioned above this is owner drawn menu, so _GUICtrlMenu_GetItemText returns "". That is the reason I must name menu items myself to select menu item. Anyway _GUICtrlMenu_GetItemID returns valid ID so I'll try using it. Thank you for you time.

Link to comment
Share on other sites

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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...