TheGood, you've got my vote

, I was trying to do that trough Lexikos lowlevel functions, but there I had the problem whenever syntax was wrong it does not work and functions that are never called are not listed.
Yours looks to work really great, so I stop to develop mine.
A few suggestion:
- when I type in a value to search, it should find the label or function even if my entry is in the middle of its name
- - this way you can enter : to get all labels or () all functions as well
- list hotkeys?
- update position of GUI when SciTE is resized/moved
@ fincs: please include in SciTE4AutoHotkey
here is what I have so far using LowLevel:
- Require AutoHotkey 1.47.06 because of LowLevel
- Requre Remote Buffer (included in script)
There is one script that will get the text from Scintilla1 and second that will run trough pipe containing the text from scintilla1 and it will get all functions, labels and hotkeys using lowlevel functions.
First script:
Code:
DetectHiddenWindows, On
FileRead,script,%A_ScriptDir%\LnF_.ahk
IfWinExist, ahk_class SciTEWindow
{
ControlGet, hwnd,hwnd,,Scintilla1,ahk_class SciTEWindow
clipboard:="Goto, #__CREATE_WINDOW`n" . ScintillaGetText(hwnd) . "`n" . script
pid := RuntempScript("Goto, #__CREATE_WINDOW`n" . ScintillaGetText(hwnd) . "`n" . script,"Functions and Labels 4 Sci4AutoHotkey")
WinWait, ahk_id "pid"
hwnd:= WinExist("ahk_pid " . pid)
}
Else
ExitApp
WinWaitClose,ahk_id %hwnd%
MsgBox
Process, Exist,%pid%
If !ErrorLevel
Process, Close,%pid%
ExitApp
ScintillaGetLength(hwnd){
static SCI_GETLENGTH:=2006
SendMessage, SCI_GETLENGTH, 0, 0,, ahk_id %hwnd%
Return ErrorLevel+1
}
ScintillaGetText(hwnd){
static SCI_GETLENGTH:=2006, SCI_GETTEXT:=2182
SendMessage, SCI_GETLENGTH, 0, 0,, ahk_id %hwnd%
length := ErrorLevel
RemoteBuf_Open(hSBuf, hwnd, length + 2)
SendMessage, SCI_GETTEXT, length + 1, RemoteBuf_Get(hSBuf),, ahk_id %hwnd%
;test SendMessage, 2024, 309, RemoteBuf_Get(hSBuf),, ahk_id %hwnd%
VarSetCapacity(sText, length + 2)
RemoteBuf_Read(hSBuf, sText, length + 2)
RemoteBuf_Close(hSBuf)
Return sText
}
; Title: Remote Buffer
; *Read and write process memory*
;
/*-------------------------------------------------------------------------------
Function: Open
Open remote buffer
Parameters:
H - Reference to variable to receive remote buffer handle
hwnd - HWND of the window that belongs to the process
size - Size of the buffer
Returns:
Error message on failure
*/
RemoteBuf_Open(ByRef H, hwnd, size) {
static MEM_COMMIT=0x1000, PAGE_READWRITE=4
WinGet, pid, PID, ahk_id %hwnd%
hProc := DllCall( "OpenProcess", "uint", 0x38, "int", 0, "uint", pid) ;0x38 = PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE
IfEqual, hProc,0, return A_ThisFunc "> Unable to open process (" A_LastError ")"
bufAdr := DllCall( "VirtualAllocEx", "uint", hProc, "uint", 0, "uint", size, "uint", MEM_COMMIT, "uint", PAGE_READWRITE)
IfEqual, bufAdr,0, return A_ThisFunc "> Unable to allocate memory (" A_LastError ")"
; Buffer handle structure:
; @0: hProc
; @4: size
; @8: bufAdr
VarSetCapacity(H, 12, 0 )
NumPut( hProc, H, 0)
NumPut( size, H, 4)
NumPut( bufAdr, H, 8)
}
/*----------------------------------------------------
Function: Close
Close the remote buffer
Parameters:
H - Remote buffer handle
*/
RemoteBuf_Close(ByRef H) {
static MEM_RELEASE = 0x8000
handle := NumGet(H, 0)
IfEqual, handle, 0, return A_ThisFunc "> Invalid remote buffer handle"
adr := NumGet(H, 8)
r := DllCall( "VirtualFreeEx", "uint", handle, "uint", adr, "uint", 0, "uint", MEM_RELEASE)
ifEqual, r, 0, return A_ThisFunc "> Unable to free memory (" A_LastError ")"
DllCall( "CloseHandle", "uint", handle )
VarSetCapacity(H, 0 )
}
/*----------------------------------------------------
Function: Read
Read from the remote buffer into local buffer
Parameters:
H - Remote buffer handle
pLocal - Reference to the local buffer
pSize - Size of the local buffer
pOffset - Optional reading offset, by default 0
Returns:
TRUE on success or FALSE on failure. ErrorMessage on bad remote buffer handle
*/
RemoteBuf_Read(ByRef H, ByRef pLocal, pSize, pOffset = 0){
handle := NumGet( H, 0), size:= NumGet( H, 4), adr := NumGet( H, 8)
IfEqual, handle, 0, return A_ThisFunc "> Invalid remote buffer handle"
IfGreaterOrEqual, offset, %size%, return A_ThisFunc "> Offset is bigger then size"
VarSetCapacity( pLocal, pSize )
return DllCall( "ReadProcessMemory", "uint", handle, "uint", adr + pOffset, "uint", &pLocal, "uint", size, "uint", 0 ), VarSetCapacity(pLocal, -1)
}
/*----------------------------------------------------
Function: Write
Write local buffer into remote buffer
Parameters:
H - Remote buffer handle
pLocal - Reference to the local buffer
pSize - Size of the local buffer
pOffset - Optional writting offset, by default 0
Returns:
TRUE on success or FALSE on failure. ErrorMessage on bad remote buffer handle
*/
RemoteBuf_Write(Byref H, byref pLocal, pSize, pOffset=0) {
handle:= NumGet( H, 0), size := NumGet( H, 4), adr := NumGet( H, 8)
IfEqual, handle, 0, return A_ThisFunc "> Invalid remote buffer handle"
IfGreaterOrEqual, offset, %size%, return A_ThisFunc "> Offset is bigger then size"
return DllCall( "WriteProcessMemory", "uint", handle,"uint", adr + pOffset,"uint", &pLocal,"uint", pSize, "uint", 0 )
}
/*----------------------------------------------------
Function: Get
Get address or size of the remote buffer
Parameters:
H - Remote buffer handle
pQ - Query parameter: set to "adr" to get address (default), to "size" to get the size or to "handle" to get Windows API handle of the remote buffer.
Returns:
Address or size of the remote buffer
*/
RemoteBuf_Get(ByRef H, pQ="adr") {
return pQ = "adr" ? NumGet(H, 8) : pQ = "size" ? NumGet(H, 4) : NumGet(H)
}
/*---------------------------------------------------------------------------------------
Group: Example
(start code)
;get the handle of the Explorer window
WinGet, hw, ID, ahk_class ExploreWClass
;open two buffers
RemoteBuf_Open( hBuf1, hw, 128 )
RemoteBuf_Open( hBuf2, hw, 16 )
;write something
str := "1234"
RemoteBuf_Write( hBuf1, str, strlen(str) )
str := "_5678"
RemoteBuf_Write( hBuf1, str, strlen(str), 4)
str := "_testing"
RemoteBuf_Write( hBuf2, str, strlen(str))
;read
RemoteBuf_Read( hBuf1, str, 10 )
out = %str%
RemoteBuf_Read( hBuf2, str, 10 )
out = %out%%str%
MsgBox %out%
;close
RemoteBuf_Close( hBuf1 )
RemoteBuf_Close( hBuf2 )
(end code)
*/
/*-------------------------------------------------------------------------------------------------------------------
Group: About
o Ver 2.0 by majkinetor. See http://www.autohotkey.com/forum/topic12251.html
o Code updates by infogulch
o Licenced under Creative Commons Attribution-Noncommercial <http://creativecommons.org/licenses/by-nc/3.0/>.
*/
/*
Many thanks to Lexikos@ahk
*/
RunTempScript(TempScript, name="")
{
If name =
pipe_name := A_TickCount
Else
pipe_name := name
; Before reading the file, AutoHotkey calls GetFileAttributes(). This causes
; the pipe to close, so we must create a second pipe for the actual file contents.
; Open them both before starting AutoHotkey, or the second attempt to open the
; "file" will be very likely to fail. The first created instance of the pipe
; seems to reliably be "opened" first. Otherwise, WriteFile would fail.
pipe_ga := CreateNamedPipe(pipe_name, 2)
pipe := CreateNamedPipe(pipe_name, 2)
if (pipe=-1 or pipe_ga=-1) {
MsgBox, 0,Error,Error, please try again
ExitApp
}
Run, %A_AhkPath% "\\.\pipe\%pipe_name%",,,PID
; Wait for AutoHotkey to connect to pipe_ga via GetFileAttributes().
DllCall("ConnectNamedPipe","uint",pipe_ga,"uint",0)
; This pipe is not needed, so close it now. (The pipe instance will not be fully
; destroyed until AutoHotkey also closes its handle.)
DllCall("CloseHandle","uint",pipe_ga)
; Wait for AutoHotkey to connect to open the "file".
DllCall("ConnectNamedPipe","uint",pipe,"uint",0)
; AutoHotkey reads the first 3 bytes to check for the UTF-8 BOM "". If it is
; NOT present, AutoHotkey then attempts to "rewind", thus breaking the pipe.
Script := chr(239) chr(187) chr(191) TempScript
if !DllCall("WriteFile","uint",pipe,"str",Script,"uint",StrLen(Script)+1,"uint*",0,"uint",0)
MsgBox, 0,Error,Check Syntax, 4 ;WriteFile failed: %ErrorLevel%/%A_LastError%
DllCall("CloseHandle","uint",pipe)
Return PID
}
CreateNamedPipe(Name, OpenMode=3, PipeMode=0, MaxInstances=255) {
return DllCall("CreateNamedPipe","str","\\.\pipe\" Name,"uint",OpenMode
,"uint",PipeMode,"uint",MaxInstances,"uint",0,"uint",0,"uint",0,"uint",0)
}
Second Script (Execuded trough Pipe):
Code:
Suspend, On
#SingleInstance, force
;#ErrorStdOut
#__CREATE_WINDOW:
IfWinExist, Functions and Labels 4 Sci4AutoHotkey ahk_class AutoHotkeyGUI
WinClose, Functions and Labels 4 Sci4AutoHotkey ahk_class AutoHotkeyGUI
#_#_Get_ALL_#_#(function,label)
Gui,98:+ToolWindow +LastFound -Caption +Resize
Gui, 98:Margin,0,0
Gui,98:Default
mainhwnd:= WinExist(),todo:="Function|Label"
sciTEhwnd:=WinExist("ahk_class SciTEWindow")
WinGetPos,x,y,w,h,ahk_id %sciTEhwnd%
ControlGetPos,sx,sy,sw,sh,Scintilla1,ahk_id %sciTEhwnd%
ControlGet,scintilla1hwnd,hwnd,,Scintilla1,ahk_id %sciTEhwnd%
Gui, 98:Add,Button,w300 Center g98GuiClose, &Hide fuctions and labels list
Gui,98:Add,ListView,% "r" . Round((sh-90)/13) . " w300 xs y+5 g#JumpTo v#Functions",Line#|Count|Type|Name
Loop,Parse,todo,|
If (todo:=A_LoopField)
Loop,Parse,%A_LoopField%,`n
If A_LoopField
LV_Add("",SubStr(A_LoopField,1,InStr(A_LoopField,".")-1),A_Index,todo,SubStr(A_LoopField,InStr(A_LoopField,".")+1))
LV_ModifyCol(3), LV_ModifyCol(4), LV_ModifyCol(1, "Integer"), LV_ModifyCol(2, "Integer")
;Parent_Handle := DllCall( "FindWindowEx", "uint",0, "uint",0, "str", sciTEhwnd, "uint",0)
Gui,98:Show,% "x" . x+sx+sw-322 . " y" . y+sy . " h" . sh
;DllCall( "SetParent", "uint", mainhwnd, "uint", Parent_Handle ) ; success = handle to previous parent, failure =null
WinActivate,ahk_id %sciTEhwnd%
SetTimer, #WinMove,100
Return ;WinWaitClose, ahk_id `%mainhwnd`%
98GuiClose:
ExitApp
Return
#WinMove:
IfWinNotExist,ahk_id %sciTEhwnd%
ExitApp
IfWinActive,ahk_id %sciTEhwnd%
WinSet,AlwaysOnTop, On,ahk_id %mainhwnd%
IfWinNotActive,ahk_id %sciTEhwnd%
IfWinNotActive,ahk_id %mainhwnd%
WinSet,AlwaysOnTop, Off,ahk_id %mainhwnd%
WinGetPos,x,y,w,h,ahk_id %sciTEhwnd%
;MsgBox % x "." y
ControlGetPos,sx,sy,sw,sh,Scintilla1,ahk_id %sciTEhwnd%
;MsgBox % sx "." sy "." sw "`n" "x" . x+sx+sw-323
WinMove,ahk_id %mainhwnd%,,x+sx+sw-323,y+sy,sw<400 ? sw/2,sh
SendMessage, 0x214,1,0,,ahk_id %mainhwnd%
Return
98GuiSize:
#__Anchor_("button1","w")
#__Anchor_("SysListView321","wh")
Return
#JumpTo:
If !(A_GuiEvent="DoubleClick")
Return
LV_GetText(pos,A_EventInfo)
SendMessage,2024,pos,0,,ahk_id %scintilla1hwnd%
WinActivate,ahk_id %sciTEhwnd%
Return
/*
Function: Anchor
Defines how controls should be automatically positioned relative to the new dimensions of a GUI when resized.
Parameters:
cl - a control HWND, associated variable name or ClassNN to operate on
a - (optional) one or more of the anchors: 'x', 'y', 'w' (width) and 'h' (height),
optionally followed by a relative factor, e.g. "x h0.5"
r - (optional) true to redraw controls, recommended for GroupBox and Button types
Examples:
> "xy" ; bounds a control to the bottom-left edge of the window
> "w0.5" ; any change in the width of the window will resize the width of the control on a 2:1 ratio
> "h" ; similar to above but directrly proportional to height
Remarks:
Anchor must always be called within a GuiSize label where AutoHotkey assigns a real value to A_Gui.
The only exception is when the second and third parameters are omitted to reset the stored positions for a control.
For a complete example see anchor-example.ahk.
License:
- Version 4.56 by Titan <http://www.autohotkey.net/~Titan/#anchor>
- GNU General Public License 3.0 or higher <http://www.gnu.org/licenses/gpl-3.0.txt>
*/
/*
Function: Anchor
Defines how controls should be automatically positioned relative to the new dimensions of a GUI when resized.
Parameters:
cl - a control HWND, associated variable name or ClassNN to operate on
a - (optional) one or more of the anchors: 'x', 'y', 'w' (width) and 'h' (height),
optionally followed by a relative factor, e.g. "x h0.5"
r - (optional) true to redraw controls, recommended for GroupBox and Button types
Examples:
> "xy" ; bounds a control to the bottom-left edge of the window
> "w0.5" ; any change in the width of the window will resize the width of the control on a 2:1 ratio
> "h" ; similar to above but directrly proportional to height
Remarks:
Anchor must always be called within a GuiSize label where AutoHotkey assigns a real value to A_Gui.
The only exception is when the second and third parameters are omitted to reset the stored positions for a control.
For a complete example see anchor-example.ahk.
License:
- Version 4.56 by Titan <http://www.autohotkey.net/~Titan/#anchor>
- GNU General Public License 3.0 or higher <http://www.gnu.org/licenses/gpl-3.0.txt>
*/
#__Anchor_(i, a = "", r = false) {
static c, cs = 12, cx = 255, cl = 0, g, gs = 8, z = 0, k = 0xffff, gx = 1
If z = 0
VarSetCapacity(g, gs * 99, 0), VarSetCapacity(c, cs * cx, 0), z := true
If a =
{
StringLeft, gn, i, 2
If gn contains :
{
StringTrimRight, gn, gn, 1
t = 2
}
StringTrimLeft, i, i, t ? t : 3
If gn is not digit
gn := gx
}
Else gn := A_Gui
If i is not xdigit
{
GuiControlGet, t, Hwnd, %i%
If ErrorLevel = 0
i := t
Else ControlGet, i, Hwnd, , %i%
}
gb := (gn - 1) * gs
Loop, %cx%
If (NumGet(c, cb := cs * (A_Index - 1)) == i) {
If a =
{
cf = 1
Break
}
Else gx := A_Gui
d := NumGet(g, gb), gw := A_GuiWidth - (d >> 16 & k), gh := A_GuiHeight - (d & k), as := 1
, dx := NumGet(c, cb + 4, "Short"), dy := NumGet(c, cb + 6, "Short")
, dw := NumGet(c, cb + 8, "Short"), dh := NumGet(c, cb + 10, "Short")
Loop, Parse, a, xywh
If A_Index > 1
av := SubStr(a, as, 1), as += 1 + StrLen(A_LoopField)
, d%av% += (InStr("yh", av) ? gh : gw) * (A_LoopField + 0 ? A_LoopField : 1)
DllCall("SetWindowPos", "UInt", i, "Int", 0, "Int", dx, "Int", dy, "Int", dw, "Int", dh, "Int", 4)
If r != 0
DllCall("RedrawWindow", "UInt", i, "UInt", 0, "UInt", 0, "UInt", 0x0101) ; RDW_UPDATENOW | RDW_INVALIDATE
Return
}
If cf != 1
cb := cl, cl += cs
If (!NumGet(g, gb)) {
Gui, %gn%:+LastFound
WinGetPos, , , , gh
VarSetCapacity(pwi, 68, 0), DllCall("GetWindowInfo", "UInt", WinExist(), "UInt", &pwi)
, NumPut(((bx := NumGet(pwi, 48)) << 16 | by := gh - A_GuiHeight - NumGet(pwi, 52)), g, gb + 4)
, NumPut(A_GuiWidth << 16 | A_GuiHeight, g, gb)
}
Else d := NumGet(g, gb + 4), bx := d >> 16, by := d & k
ControlGetPos, dx, dy, dw, dh, , ahk_id %i%
If cf = 1
{
Gui, %gn%:+LastFound
WinGetPos, , , gw, gh
d := NumGet(g, gb), dw -= gw - bx * 2 - (d >> 16), dh -= gh - by - bx - (d & k)
}
NumPut(i, c, cb), NumPut(dx - bx, c, cb + 4, "Short"), NumPut(dy - by, c, cb + 6, "Short")
, NumPut(dw, c, cb + 8, "Short"), NumPut(dh, c, cb + 10, "Short")
Return, true
}
#_#_Get_ALL_#_#(ByRef functions,ByRef labels){
__init()
pfunc := __getFirstFunc()
Loop
{
If (NumGet(pfunc+49,0,"CHR")="1")
{
pfunc := NumGet(pfunc+44, 0, "UInt")
Continue
}
pName := NumGet(pfunc+0, 0, "UInt")
pLine := NumGet(pfunc+4, 0, "UInt")
If !pFunc
break
pfunc := NumGet(pfunc+44, 0, "UInt")
var .= NumGet(pLine+8,0,"UInt")-2
Loop
{
if !NumGet(pName+A_Index-1,0,"Uchr")
break
varlabel .=Chr(NumGet(pName+A_Index-1,0,"CHR"))
}
If varlabel not in __init,__getVar,__findFunc,__getFirstFunc,__GetFirstLabel,#__Anchor_,#_#_Get_ALL_#_#,__str
,__getFuncUDF,__getFirstLine,__findLabel,__mcode,__mcode__getVar,__getFirstLabelDllCall,__getFirstLabelDllCallchr
,__getFirstLabelDllCallchrStrLen,__getFirstLabelDllCallchrStrLenWinExist,__getFirstLabelDllCallchrStrLenWinExistLV_Add
,__getFirstLabelDllCallchrStrLenWinExistLV_AddSubStr,__getFirstLabelDllCallchrStrLenWinExistLV_AddSubStrInStr
,__getFirstLabelDllCallchrStrLenWinExistLV_AddSubStrInStrLV_ModifyCol
,__getFirstLabelDllCallchrStrLenWinExistLV_AddSubStrInStrLV_ModifyColLV_GetText
,__getFirstLabelDllCallchrStrLenWinExistLV_AddSubStrInStrLV_ModifyColLV_GetTextNumGet
,__getFirstLabelDllCallchrStrLenWinExistLV_AddSubStrInStrLV_ModifyColLV_GetTextNumGetNumPut
,__getFirstLabelDllCallchrStrLenWinExistLV_AddSubStrInStrLV_ModifyColLV_GetTextNumGetNumPutRegisterCallback
,__getFirstLabelDllCallchrStrLenWinExistLV_AddSubStrInStrLV_ModifyColLV_GetTextNumGetNumPutRegisterCallbackVarsetCapacity
functions .= var . "." . varlabel . (!NumGet(pfunc+44, 0, "UInt") ? "" : "`n")
var=
varlabel=
}
pFunc := __getFirstLabel()
Loop
{
pName := NumGet(pfunc+0, 0, "UInt")
pLine := NumGet(pfunc+4, 0, "UInt")
var .= NumGet(pLine+8,0,"UInt")-2
Loop
{
if !NumGet(pName+A_Index-1,0,"Uchr")
break
varlabel .=Chr(NumGet(pName+A_Index-1,0,"CHR"))
}
If varlabel not in #JumpTo,#__CREATE_WINDOW,__getFirstLabel_label,98GuiSize,98GuiClose,#WinMove
labels .= var . "." . varlabel . "`n"
var=
varlabel=
pfunc := NumGet(pfunc+12, 0, "UInt")
If !pFunc
break
}
}
__init() {
; __getFirstFunc must be called at least once before (or by) __mcode, or it won't work properly later on.
;__getFirstFunc()
__mcode("__getVar","8B4C24088B0933C08379080375028B018B4C2404998901895104C3")
;__mcode("__init","C3")
}
__mcode(FuncName, Hex)
{
if !(pFunc := __findFunc(FuncName)) or !(pbin := DllCall("GlobalAlloc","uint",0,"uint",StrLen(Hex)//2))
return 0
Loop % StrLen(Hex)//2
NumPut("0x" . SubStr(Hex,2*A_Index-1,2), pbin-1, A_Index, "char")
NumPut(pbin,pFunc+4), NumPut(1,pFunc+49,0,"char")
return pbin
}
__getVar(var) {
; This function is implemented in machine code. See __init().
}
__findFunc(FuncName, FirstFunc=0)
{
if !FirstFunc {
; __getFuncUDF, in-line:
if pCb := RegisterCallback(FuncName) {
pFunc := NumGet(pCb+28)
DllCall("GlobalFree","uint",pCb)
if pFunc
return pFunc
} ; end __getFuncUDF.
if !(FirstFunc := __getFirstFunc())
return 0
}
pFunc := FirstFunc
Loop {
if __str(NumGet(pFunc+0)) = FuncName ; pFunc->mName
return pFunc
if ! pFunc := NumGet(pFunc+44) ; pFunc->mNextFunc
return 0
}
}
; How __findFunc works:
; - If we weren't given a list to search (via FirstFunc), try RegisterCallback.
; RegisterCallback fails if the function is built-in or has ByRef parameters.
; - Built-in functions do not exist in the linked list until they are either
; referenced in script or "searched for" by calling RegisterCallback.
; - To access the linked list of functions, __getFirstFunc searches through all
; function derefs in the script. We then search the linked list.
__getFirstFunc()
{
static pFirstFunc
if !pFirstFunc {
if !(pLine := __getFirstLine())
return 0
Loop {
Loop % NumGet(pLine+1,0,"uchar") { ; pLine->mArgc
pArg := NumGet(pLine+4) + (A_Index-1)*12 ; pLine->mArg[A_Index-1]
if (NumGet(pArg+0,0,"uchar") != 0) ; pArg->type != ARG_TYPE_NORMAL
continue ; arg has no derefs (only a Var*)
Loop {
pDeref := NumGet(pArg+8) + (A_Index-1)*12 ; pArg->deref[A_Index-1]
if (!NumGet(pDeref+0)) ; pDeref->marker (NULL terminates list)
break
if (NumGet(pDeref+8,0,"uchar")) ; pDeref->is_function
{
; The first function is either the first defined function,
; or if no explicitly #included UDFs exist, the first
; built-in function referenced in code.
pFunc := NumGet(pDeref+4)
if (NumGet(pFunc+49,0,"uchar")) { ; pFunc->mIsBuiltIn
if !pFirstBIF
pFirstBIF := pFunc
} else { ; UDF
pFuncLine := NumGet(pFunc+4)
FuncLine := NumGet(pFuncLine+8)
FuncFile := NumGet(pFuncLine+2,0,"ushort")
if !pFirstFunc or (FuncFile < FirstFuncFile || (FuncFile = FirstFuncFile && FuncLine < FirstFuncLine))
pFirstFunc:=pFunc, FirstFuncLine:=FuncLine, FirstFuncFile:=FuncFile
}
}
}
}
if !(pLine:=NumGet(pLine+20)) ; pLine->mNextLine
break
}
if pFirstBIF
{ ; Usually the first UDF will be before the first BIF, but not if
; only auto-included/stdlib UDFs exist *AND* the first BIF is
; referenced in code before the first UDF.
if pFirstFunc
{ ; Look for the BIF using the UDF as a starting point.
pFunc := pFirstFunc
Loop {
if !(pFunc := NumGet(pFunc+44)) ; pFunc->mNextFunc
break
; If the BIF is found, the UDF must precede it in the list.
if (pFunc = pFirstBIF)
return pFirstFunc
}
; If we got here, the BIF was not found, so is probably the first Func.
}
pFirstFunc := pFirstBIF
return pFirstFunc
}
}
return pFirstFunc
}
__str(addr,len=-1) {
if len<0
return DllCall("MulDiv","uint",addr,"int",1,"int",1,"str")
VarSetCapacity(str,len), DllCall("lstrcpyn","str",str,"uint",addr,"int",len+1)
return str
}
__getFuncUDF(FuncName) {
if pCb := RegisterCallback(FuncName) {
func := NumGet(pCb+28)
DllCall("GlobalFree","uint",pCb)
}
return func
}
__getFirstLine()
{
static pFirstLine
if (pFirstLine = "") {
if pThisFunc := __getFuncUDF(A_ThisFunc) {
if pFirstLine := NumGet(pThisFunc+4) ; mJumpToLine
Loop {
if !(pLine:=NumGet(pFirstLine+16)) ; mPrevLine
break
pFirstLine := pLine
}
}
}
return pFirstLine
}
__findLabel(LabelName, FirstLabel=0)
{
if !FirstLabel && !(FirstLabel := __getFirstLabel())
return 0
pLabel := FirstLabel
Loop {
if __str(NumGet(pLabel+0)) = LabelName
return pLabel
if ! pLabel := NumGet(pLabel+12)
return 0
}
}
__getFirstLabel()
{
static pFirstLabel
if !pFirstLabel {
if !(pLine := NumGet(__getFuncUDF(A_ThisFunc)+4))
return 0
Loop {
act := NumGet(pLine+0,0,"char")
if (act = 96 || act = 95) ; ACT_GOSUB || ACT_GOTO
break
if !(pLine:=NumGet(pLine+20)) ; pLine->mNextLine
return 0
}
pFirstLabel := NumGet(pLine+24)
Loop {
if ! pPrevLabel:=NumGet(pFirstLabel+8)
break
pFirstLabel := pPrevLabel
}
}
return pFirstLabel
; Since Labels are in a doubly-linked list, we can find the first label
; by getting the Label associated with the goto line below.
__getFirstLabel_label:
goto __getFirstLabel_label
}