AutoHotkey Community

It is currently May 25th, 2012, 5:46 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 50 posts ]  Go to page 1, 2, 3, 4  Next
Author Message
PostPosted: May 20th, 2007, 3:15 pm 
Offline

Joined: June 15th, 2006, 6:29 am
Posts: 59
Location: Oriel College
The link archive contains a small DLL and a sample AHK script which calls EnumWindows, using a callback stub produced by the DLL.
Any number of callbacks stubs can be produced.
There is an overhead of 84+4*num of params bytes of heap memory allocated using GlobalAlloc (which can be manually freed after use using GlobalFree) per added callback, and one call to SendMessage to relay the message to AHK per call to that callback.

The callback handler assumes the STDCALL/Pascal calling convention (ie. not C's).

http://www.autohotkey.net/~JGR/callback.rar

JGR


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2007, 3:27 pm 
If this script (archive) contains something usefull for noobs too, would you/someone mind to explain (using a noobish example) what it's for ?

Btw. thanks for sharing it. 8)


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2007, 3:35 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Extraordinary work. Thx.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2007, 3:37 pm 
Offline

Joined: June 15th, 2006, 6:29 am
Posts: 59
Location: Oriel College
The DLL makes it feasible to implement a callback routine in AHK.
A callback is a function we define which somebody else (ie. Windows, etc.) calls, instead of us calling somebody else. There are quite a lot of windows api functions which expect to be passed pointers to callback routines.

The noobish example is in the archive, really. You call windows, and it calls your function multiple times with the data you asked it for.

The DLL is used because AHK does not natively support pointers to functions.

This is not something that will be useful in all cases, only specific ones.

If you didn't get that, then you probably won't be needing to use it :)

JGR


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2007, 5:08 pm 
Offline
User avatar

Joined: December 29th, 2004, 1:28 pm
Posts: 2540
Thanks :)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2007, 6:07 pm 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
Superb! Now we have callback.
BTW, you seem to forget to set "DetectHiddenWindows On" in EnumWindowsPrc.
So, I added it.

Code:
DetectHiddenWindows, On
Process, Exist
hAHK := WinExist("ahk_pid " . ErrorLevel)

WM_AHK_Callback := DllCall("RegisterWindowMessage", "str", "WM_AHK_Callback")
OnMessage(WM_AHK_Callback, "EnumWindows")

hModule := DllCall("LoadLibrary", "str", "callback")
hProc := DllCall("callback\callbackit", "Uint", 2, "Uint", hAHK, "Uint", WM_AHK_Callback, "Uint", 0)
DllCall("EnumWindows", "Uint", hProc, "Uint", 0)
MsgBox, % Out

DllCall("FreeLibrary", "Uint", hModule)
ExitApp
   
EnumWindows(wParam, lParam)
{
   Global Out
   DetectHiddenWindows, On
   hWnd := DecodeInteger(lParam)
   WinGetTitle, sTitle, ahk_id %hWnd%
   WinGetClass, sClass, ahk_id %hWnd%
   Out .= "hWnd: ". hWnd . "`tTitle: " . sTitle . "`tClass: " . sClass . "`n"
   Return 1
}

DecodeInteger(ref, nSize = 4)
{
   DllCall("RtlMoveMemory", "int64P", val, "Uint", ref, "Uint", nSize)
   Return val
}

EncodeInteger(ref, val = 0, nSize = 4)
{
   DllCall("RtlMoveMemory", "Uint", ref, "int64P", val, "Uint", nSize)
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2007, 6:25 pm 
Offline

Joined: June 15th, 2006, 6:29 am
Posts: 59
Location: Oriel College
I've updated the linked archive to include that and the FreeLibrary call (and GlobalFree of the callback stub).
Thanks for spotting that.

I will say though that you really don't need to use RegisterWindowMessage, as the only window which will be receiving the message is the AHK one. RegisterWindowMessage realistically is only used by message broadcasters.
All you need to do is pick a number between 0x400 and 0x7FFF, which you haven't already used in your script (for other callbacks etc.), and use that.

JGR


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2007, 9:39 pm 
Offline
User avatar

Joined: December 29th, 2004, 1:28 pm
Posts: 2540
JGR wrote:
I will say though that you really don't need to use RegisterWindowMessage, as the only window which will be receiving the message is the AHK one.
One of the reasons for using it might be if you are creating a function to be included in other scripts. This way you can avoid collisions with other message handlers.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 21st, 2007, 12:14 am 
Offline
User avatar

Joined: December 29th, 2004, 1:28 pm
Posts: 2540
Thanks again JGR :) . Btw, I didn't notice a license in the download. Is this available for commercial use, distribution, etc... ?

I decided to try and put together a small function to make #Including in generic scripts a bit easier. Here's a rough, initial beta version for fun :)
Code:
SetCallbackFunction("EnumWindows")
Sleep, 200
DllCall("EnumWindows","UInt",EnumWindows_PS,"UInt",0)
MsgBox Output1:`n%out%

out=
SetCallbackFunction("EnumWindows", "Off")
Sleep, 200
DllCall("EnumWindows","UInt",EnumWindows_PS,"UInt",0)
MsgBox Output2:`n%out%
Return




; *****************************************************************************
SetCallbackFunction(FunctionName, _Remove=0) {
Global
Static _CallbackDllLoaded := false
Local _pid, _hwnd, _hHookModule, _MsgNmb
If (_Remove) or (_Remove = "Off") {
  If %FunctionName% is integer
  {
    OnMessage(%FunctionName%, "")
    %FunctionName% := ""
    %FunctionName%_PS := ""
    Return "Removed Callback"
  }
  Else
    Return "ERROR: Invalid Message Number"
}
_pid:=DllCall("GetCurrentProcessId","Uint")
DetectHiddenWindows, On
_hwnd:=WinExist("ahk_pid " . _pid)

If !(_CallbackLoaded) {
  _hHookModule := DllCall("LoadLibrary", "str", "callback\callback.dll")
  _CallbackLoaded := true
  }
_MsgNmb := DllCall("RegisterWindowMessage", "Str", FunctionName)
%FunctionName%_PS:=DllCall("callback\callback.dll\callbackit", "UInt", 2,"UInt", _hwnd, "UInt", _MsgNmb, "UInt", 0)
OnMessage(_MsgNmb, FunctionName)
%FunctionName% := _MsgNmb
Return _MsgNmb
}

; *****************************************************************************
EnumWindows(wParam, lParam, msg, hwnd) {
  global out
  DetectHiddenWindows, On
  winid:=GetDeRefInteger(lParam)
  otherparam:=GetDeRefInteger(lParam+4)
  WinGetTitle, text, ahk_id %winid%
  WinGetClass, class, ahk_id %winid%
  out.="HWND: ". winid . "`tTitle: " . text . "`tClass: " . class . "`n"
  return 1
}

; *****************************************************************************
GetDeRefInteger(pSource, pIsSigned = false, pSize = 4)
; pSource is an integer pointer to a raw/binary integer
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
{
   Loop %pSize%  ; Build the integer by adding up its bytes.
      result += *(pSource + A_Index-1) << 8*(A_Index-1)
   if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
      return result  ; Signed vs. unsigned doesn't matter in these cases.
   ; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
   return -(0xFFFFFFFF - result + 1)
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 21st, 2007, 1:30 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7501
Location: Australia
I don't get it... why decode lParam? wParam and lParam are already numbers in AHK (string) format...
Quote:
Code:
hWnd := DecodeInteger(lParam)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 21st, 2007, 4:02 am 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
lexikos wrote:
I don't get it... why decode lParam? wParam and lParam are already numbers in AHK (string) format...


Notice that it's lParam, not &lParam.
The first argument of DecodeInteger() is a pointer.
Maybe my usage of the term ref as the variable name confused you.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 21st, 2007, 5:04 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7501
Location: Australia
Sean wrote:
Notice that it's lParam, not &lParam.
The first argument of DecodeInteger() is a pointer.
Maybe my usage of the term ref as the variable name confused you.

Ah, thanks, I get it now. I subconsciously assumed "DecodeInteger" would accept an integer, hence my confusion. Also, I didn't quite understand how DecodeInteger was working. Now I see you aren't actually decoding the integer - AutoHotkey is automatically converting it because the parameter is declared as "int64P." (Right?)

So it seems the name of the function comes from the usage of DllCall itself, not the dll function called. :?

Would DecodeInteger work for 32-bit (or less) signed integers? I see it accepts a "size" parameter, but the output is always intepreted as a 64-bit integer.


The "ref" variable didn't confuse me, though I would have understood at a glance had it been named "ptr". :)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 21st, 2007, 6:51 am 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
lexikos wrote:
Would DecodeInteger work for 32-bit (or less) signed integers? I see it accepts a "size" parameter, but the output is always intepreted as a 64-bit integer.


That is at the heart of this function, as all types of integers are part of 64bit integer.

Code:
nSize = 1 -> decoded as unsigned  8bit integer
nSize = 2 -> decoded as unsigned 16bit integer
nSize = 4 -> decoded as unsigned 32bit integer
nSize = 8 -> decoded as "signed" 64bit integer

Similar with EncodeInteger().
BTW, there will be no problems encoding negative 8/16/32-bit integers.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 21st, 2007, 6:51 am 
Offline

Joined: June 15th, 2006, 6:29 am
Posts: 59
Location: Oriel College
The value in lParam is a numeric pointer to the offset where the first parameter of the callback is in memory.
In C terms that is *int[], I believe.
As for how the DecodeInteger function works, it is more to do with the quirkiness of AHK on pointers than any fancy operation, but it does take an integer.
What you are really doing is:
hWnd = *lParam;

As for license, treat it as you would anything else on the forum. Use it as you see fit.
My advice however, is not to modify the DLL source code unless you really know what you are doing and understand the existing code (ie. you write assembly code).

JGR

Edit:
Re-uploaded archive, as DLL changed to reduce memory overhead per callback to 60 and alleviate the need to allocate a buffer to params and copy them (superfluous). lParam is now a stack pointer as in the hook dll.
This does not affect the AHK scripts at all.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 21st, 2007, 1:57 pm 
Offline
User avatar

Joined: December 29th, 2004, 1:28 pm
Posts: 2540
JGR wrote:
As for license, treat it as you would anything else on the forum. Use it as you see fit.
Thanks :) . If you get a chance, please have a look at the SetCallbackFunction function I posted above and consider using it in sample scripts (or something similar if you'd prefer a different method). The reason I'm mentioning this is that a Standard Library is planned and something like this would be much better if we could include it in a script easily. Although the code I posted is a bit rough, it only requires a user to enter one line of code to get callback fuctionality when using your dll. Here's a slightly modified version that will work if the dll is placed in an /Include subfolder in the AutoHotkey directory.
Code:
SetCallbackFunction(FunctionName, _Remove=0) {
Global
Static _CallbackDllLoaded := false
Local _pid, _hwnd, _hHookModule, _MsgNmb
If (_Remove) or (_Remove = "Off") {
  If %FunctionName% is integer
  {
    OnMessage(%FunctionName%, "")
    %FunctionName% := ""
    %FunctionName%_PS := ""
    Return "Removed Callback"
  }
  Else
    Return "ERROR: Invalid Message Number"
}
_pid:=DllCall("GetCurrentProcessId","Uint")
DetectHiddenWindows, On
_hwnd:=WinExist("ahk_pid " . _pid)
If !(_CallbackLoaded) {
   SplitPath, A_AhkPath,,x_LibPath
   x_LibPath .= "\Include\callback.dll"
   Loop, %x_LibPath%
     x_LibPath = %A_LoopFileShortPath%
  _hHookModule := DllCall("LoadLibrary", "str", x_LibPath)
  _CallbackLoaded := true
  }
_MsgNmb := DllCall("RegisterWindowMessage", "Str", FunctionName)
%FunctionName%_PS:=DllCall(x_LibPath . "\callbackit", "UInt", 2,"UInt", _hwnd, "UInt", _MsgNmb, "UInt", 0)
OnMessage(_MsgNmb, FunctionName)
%FunctionName% := _MsgNmb
Return _MsgNmb
}


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 50 posts ]  Go to page 1, 2, 3, 4  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: No registered users and 8 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group