Jump to content

KaFu

MVPs
  • Posts

    3,944
  • Joined

  • Last visited

  • Days Won

    15

KaFu last won the day on October 26 2024

KaFu had the most liked content!

About KaFu

Profile Information

  • Member Title
    Hey, it's just me, KhaFoo...
  • Location
    Germany, Hamburg
  • WWW
    https://funk.eu

Recent Profile Visitors

6,845 profile views

KaFu's Achievements

  1. For x64: There's a conversion error, is the Int datatype hardcoded in the x64 opcode? The best template from the AHK guys for variable parameters in the opcode seems to be in this post: https://www.autohotkey.com/boards/viewtopic.php?p=594011#p594011 Edit: Would it be a quick (and dirty) solution to set both parameters as ptr in the opcode? By default 4-byte in x86 and 8-byte in x64?
  2. This seems to work for two parameters. Are there callbacks with more than two parameters? I guess this hack is limited to two parameters anyhow, because it relies on sendmessage (with only two parameters)? Though the clean-up after the main loop is still messed, but that's for tomorrow. Although I broke it again for x64 🙄 with another hack, that currently works for x86 (without this hack it worked under x64 too). In the CreateTimerQueueTimer call I pass a ptr for $t_Data_Send_to_Callback, which is fed to to callback via $lParam. Under x86 I can read the buffer in the callback, under x64 it currently crashes. #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_UseX64=n #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** ; based on ; https://www.autoitscript.com/forum/topic/212917-createtimerqueuetimer/page/2/#findComment-1543584 ; by nine #include <Memory.au3> #include <WindowsConstants.au3> #include <WinAPIError.au3> #include <WinAPIHObj.au3> #include <WinAPISys.au3> Global Const $WT_EXECUTELONGFUNCTION = 0x00000010 ; The callback function can perform a long wait. This flag helps the system to decide if it should create a new thread. Global Const $WT_EXECUTEINTIMERTHREAD = 0x00000020 ; The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations. Global Const $WT_EXECUTEINPERSISTENTTHREAD = 0x00000080 ; The callback function is queued to a thread that never terminates. Local $a_h_CreateTimerQueue = DllCall("kernel32.dll", "handle", "CreateTimerQueue") ConsoleWrite("CreateTimerQueue = " & $a_h_CreateTimerQueue[0] & @CRLF & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) Local $i_TimerQueue_Start_after = 100 Local $i_TimerQueue_Repeat_after = 100 Local $pCallback = _RegisterSyncCallback("_CallBackFunction", "none", "ptr;bool") ; WaitOrTimerCallback callback function > https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms687066(v=vs.85) Func _CallBackFunction($lParam, $TimerOrWaitFired) ConsoleWrite("_CallBackFunction fired - 0 = " & TimerInit() & @CRLF) ConsoleWrite("_CallBackFunction fired - 1 = " & $lParam & @TAB & $TimerOrWaitFired & @CRLF) ConsoleWrite("_CallBackFunction fired - 2 = " & VarGetType($lParam) & @TAB & VarGetType($TimerOrWaitFired) & @CRLF) Local $t_Data_Received = DllStructCreate("wchar[256]", $lParam) ConsoleWrite(DllStructGetData($t_Data_Received, 1) & @CRLF) EndFunc ;==>_CallBackFunction Local $t_Data_Send_to_Callback = DllStructCreate("wchar[256]") DllStructSetData($t_Data_Send_to_Callback, 1, "Update from main @ " & TimerInit()) ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx Local $a_hCall = DllCall("kernel32.dll", "bool", "CreateTimerQueueTimer", _ "handle*", 0, _ "handle", $a_h_CreateTimerQueue[0], _ "ptr", $pCallback, _ "ptr", DllStructGetPtr($t_Data_Send_to_Callback), _ "dword", $i_TimerQueue_Start_after, _ "dword", $i_TimerQueue_Repeat_after, _ "ulong", $WT_EXECUTEINTIMERTHREAD) Local $phNewTimer = $a_hCall[1] ConsoleWrite("CreateTimerQueueTimer = " & $a_hCall[0] & @TAB & $phNewTimer & @CRLF & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) Local $timer = TimerInit() While Sleep(10) ConsoleWrite("+ " & TimerDiff($timer) & @CRLF) DllStructSetData($t_Data_Send_to_Callback, 1, "Update from main @ " & TimerInit()) If TimerDiff($timer) > 2000 Then ConsoleWrite("! fire Exitloop event" & @CRLF) ExitLoop EndIf WEnd Local $a_hCall = DllCall("kernel32.dll", "bool", "DeleteTimerQueueTimer", _ "handle", $a_h_CreateTimerQueue[0], _ "handle", $phNewTimer, _ "handle", 0) ConsoleWrite("DeleteTimerQueueTimer = " & $a_hCall[0] & @CRLF & @CRLF) If _WinAPI_GetLastError() = 997 Then ; ERROR_IO_PENDING = 997 ConsoleWrite("If there are outstanding callback functions and CompletionEvent is NULL, the function will fail and set the error code to ERROR_IO_PENDING (997). " & @CRLF _ & "This indicates that there are outstanding callback functions. Those callbacks either will execute or are in the middle of executing. " & @CRLF _ & "The timer is cleaned up when the callback function is finished executing." & @CRLF & @CRLF) EndIf ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) ConsoleWrite("_WinAPI_CloseHandle($phNewTimer) = " & _WinAPI_CloseHandle($phNewTimer) & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) Local $a_hCall = DllCall("kernel32.dll", "bool", "DeleteTimerQueueEx", "handle", $a_h_CreateTimerQueue[0], "handle", 0) ConsoleWrite("DeleteTimerQueueEx = " & $a_hCall[0] & @CRLF & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) Exit Func _RegisterSyncCallback($sFunction, $sReturnType, $sParams) Local Static $hGUI Local Static $pSendMessage Local Static $iMsg If Not $hGUI Then $hGUI = GUICreate("RegisterSyncCallback_Msg", 100, 100) $iMsg = $WM_APP GUIRegisterMsg($iMsg, RegisterSyncCallback_Msg) $pSendMessage = _WinAPI_GetProcAddress(_WinAPI_GetModuleHandle("user32.dll"), "SendMessageW") ConsoleWrite("$hGUI: " & Hex($hGUI) & @CRLF) ConsoleWrite("$pSendMessage: " & Hex($pSendMessage) & @CRLF) ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF) EndIf Local $hHandle = DllCallbackRegister($sFunction, $sReturnType, $sParams) ConsoleWrite("$hHandle = " & $hHandle & @CRLF) If @AutoItX64 Then Local $sOPCode = "0x" _ & "48894C2408488954" _ & "24104C894424184C" _ & "894C24204883EC28" _ & "4989C8" _ & "48C7C1" _ & SwapEndian($hGUI, False) _ & "48C7C2" _ & SwapEndian($iMsg, False) _ & "49B9" _ & SwapEndian(DllCallbackGetPtr($hHandle)) _ & "48B8" _ & SwapEndian($pSendMessage) _ & "FFD04883C428C3" Else Local $sOPCode = "0x" _ & "68" _ & SwapEndian(DllCallbackGetPtr($hHandle)) _ & "FF74240868" _ & SwapEndian($iMsg) _ & "68" _ & SwapEndian($hGUI) _ & "B8" _ & SwapEndian($pSendMessage) _ & "FFD0C20800" EndIf Local $pRemoteCode = _MemVirtualAlloc(0, BinaryLen($sOPCode), $MEM_COMMIT, $PAGE_EXECUTE_READWRITE) If Not $pRemoteCode Then MsgBox(0, "Error", "_MemVirtualAlloc :(") Local $tCodeBuffer = DllStructCreate("byte[" & BinaryLen($sOPCode) & "]", $pRemoteCode) DllStructSetData($tCodeBuffer, 1, $sOPCode) ConsoleWrite("$tCodeBuffer: " & DllStructGetData($tCodeBuffer, 1) & @CRLF) Return $pRemoteCode EndFunc ;==>_RegisterSyncCallback Func RegisterSyncCallback_Msg($hWnd, $iMsg, $wParam, $lParam) ConsoleWrite(">RegisterSyncCallback_Msg Called" & @CRLF) ; ConsoleWrite("$hWnd: " & Hex($hWnd) & @CRLF) ; ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF) ; ConsoleWrite("$wParam: " & Hex($wParam) & @CRLF) ; ConsoleWrite("$lParam: " & Hex($lParam) & @CRLF) DllCallAddress("none", $lParam, "int", $wParam, "int", 1) EndFunc ;==>RegisterSyncCallback_Msg Func SwapEndian($uInt, $b64 = True) Local $iLen = (@AutoItX64 And $b64) ? 16 : 8, $sHex = Hex($uInt, $iLen), $sRes For $i = $iLen - 2 To 0 Step -2 $sRes &= StringMid($sHex, $i + 1, 2) Next Return $sRes EndFunc ;==>SwapEndian
  3. Or this way. #include <WinAPISys.au3> Local $tInfo = _WinAPI_GetStartupInfo() ConsoleWrite(_WinAPI_GetString($tInfo.title) & @CRLF)
  4. This is true for this specific CreateTimerQueue callback, but for other callbacks the return parameters will change. It would be great to make _RegisterSyncCallback() as generic and flexible as possible.
  5. Looks good on my side for x64 🙂👍. You've removed the parameters from this example, I would think they are required for the callbacks to fully work. I would also assume that the parameter type is relevant for the distinction between x86 and x64, so an explicit variable for the callback parameter structure should be part of the _RegisterSyncCallback() function call (they need to be passed to the DllCallbackRegister() call for $hHandle too?). Local $t_Callback_Parameters = "ptr lpParameter; bool TimerOrWaitFired" ; WaitOrTimerCallback callback function > https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms687066(v=vs.85) Local $pCallback = _RegisterSyncCallback(_CallBackFunction, $t_Callback_Parameters) I also propose to create the opcode memory only after the definition of the opcode itself, with something like this. Local $pRemoteCode = _MemVirtualAlloc(0, BinaryLen($sOPCode), $MEM_COMMIT, $PAGE_EXECUTE_READWRITE) If Not $pRemoteCode Then MsgBox(0, "Error", "_MemVirtualAlloc :(") Local $tCodeBuffer = DllStructCreate("byte[" & BinaryLen($sOPCode) & "]", $pRemoteCode)
  6. Hi jugador, thanks for the feedback. The x86 code worked without the volatile for me, but I've added it to be on the safe side 👍. The only open topic for me would be to make the opcode x64 compatible. Just ported SMF to x64, and I made it a habit to try to make all my code x86 and x64 compatible.
  7. Hi UEZ, initially my request was about CreateTimerQueueTimer, triggered by this comment. By jugadors comment I learned of the opcode hack with RegisterSyncCallback, as described by the AHK guys. Also jugador tested it with _WinHttpSetStatusCallback and EnumWindows here. So now it's only about making RegisterSyncCallback x64 compatible, to have a hack in the quiver for any functions using callbacks, which will crash with DllCallbackRegister. Besides the mentioned ones I think there are more functions out there, where this can be used. I think the callback in CopyFileEx also crashed with DllCallbackRegister, maybe it works with RegisterSyncCallback?
  8. There are cases with work-arounds using opcode execution in separate threads, e.g. transex gif animation UDF. I think this RegisterSyncCallback hack might be a good workaround at least for simple callbacks in separate threads.
  9. Great 🙂, works for me too 👍! The team over at the AHK forum also posted the opcode for x64 mode. https://www.autohotkey.com/boards/viewtopic.php?p=538378#p538378 p := pcb if A_PtrSize = 8 { /* 48 89 4c 24 08 ; mov [rsp+8], rcx 48 89 54'24 10 ; mov [rsp+16], rdx 4c 89 44 24 18 ; mov [rsp+24], r8 4c'89 4c 24 20 ; mov [rsp+32], r9 48 83 ec 28' ; sub rsp, 40 4c 8d 44 24 30 ; lea r8, [rsp+48] (arg 3, &params) 49 b9 .. ; mov r9, .. (arg 4, operand to follow) */ p := NumPut('Ptr' , 0x54894808244c8948, 'Ptr' , 0x4c182444894c1024, 'Ptr' , 0x28ec834820244c89, 'Ptr' , 0x00b9493024448d4c, p) - 1 lParamPtr := p, p += 8 p := NumPut('Char' , 0xba, ; mov edx, nmsg 'Int' , msg, 'Char' , 0xb9, ; mov ecx, hwnd 'Int' , wnd.hwnd, 'Short', 0xb848, ; mov rax, SendMessageW 'Ptr' , SendMessageW, /* ff d0 ; call rax 48 83 c4 28 ; add rsp, 40 c3 ; ret */ 'Ptr' , 0x00c328c48348d0ff, p) } else { p := NumPut('Char' , 0x68, p) ; push ... (lParam data) lParamPtr := p, p += 4 p := NumPut('Int' , 0x0824448d, ; lea eax, [esp+8] 'Char' , 0x50, ; push eax 'Char' , 0x68, ; push nmsg 'Int' , msg, 'Char' , 0x68, ; push hwnd 'Int' , wnd.hwnd, 'Char' , 0xb8, ; mov eax, &SendMessageW 'Int' , SendMessageW, 'Short', 0xd0ff, ; call eax 'Char' , 0xc2, ; ret argsize 'Short', InStr(Options, 'C') ? 0 : ParamCount * 4, p) } I'll give it a try, although I have to admit I'm not good in this assembly stuff. Edit: Found this for SwapEndian under x64. uint64_t swapLong(void *X) { uint64_t x = (uint64_t) X; x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32; x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16; x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8; return x; }
  10. What did you change?
  11. Thanks for pointing that out 🙂! I'll test @Danyfirex's function from here later the day. Edit: No luck yet, the callback fires one time and on the second call the parameters are gone and the process crashes. #include <WinAPI.au3> #include <Memory.au3> Global $dll_kernel32 = DllOpen("kernel32.dll") Func _CallBackFunction($arg1, $arg2) ; MsgBox(0, "_MyFn - Child Thread", "GetCurrentThreadId: " & DllCall("kernel32.dll", "handle", "GetCurrentThread")[0] & @CRLF & "$arg1: " & Int($arg1) & @CRLF & "$arg2: " & Int($arg2)) ConsoleWrite("_CallBackFunction" & @tab & TimerInit() & @crlf) EndFunc ;==>_CallBackFunction ; https://www.autoitscript.com/forum/topic/211447-registersynccallback/#findComment-1529893 ; by Danyfirex Local $hCallback = _RegisterSyncCallback("_CallBackFunction", 2) ConsoleWrite("$hCallback = " & $hCallback & @crlf) Local $tParameters = DllStructCreate("int Value1;int Value2") $tParameters.Value1 = 10 $tParameters.Value2 = 30 ;~ DllCallAddress("int", $pCB, "ptr", DllStructGetPtr($tParameters)) ConsoleWrite("DllStructGetPtr($tParameters): " & DllStructGetPtr($tParameters) & @CRLF) DllCall($dll_kernel32, "hwnd", "CreateThread", "ptr", 0, "dword", 0, "long", $hCallback, "ptr", DllStructGetPtr($tParameters), "long", 0, "int*", 0) Local Const $WT_EXECUTELONGFUNCTION = 0x00000010 ; The callback function can perform a long wait. This flag helps the system to decide if it should create a new thread. Local Const $WT_EXECUTEINTIMERTHREAD = 0x00000020 ; The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations. Local Const $WT_EXECUTEINPERSISTENTTHREAD = 0x00000080 ; The callback function is queued to a thread that never terminates. Local $i_TimerQueue_Start_after = 100 Local $i_TimerQueue_Repeat_after = 100 Local $a_hCall = DllCall($dll_kernel32, "bool", "CreateTimerQueueTimer", _ "handle*", 0, _ "handle", Null, _ "ptr", $hCallback, _ "ptr", Null, _ "dword", 100, _ "dword", $i_TimerQueue_Repeat_after, _ "ulong", BitOR($WT_EXECUTELONGFUNCTION, $WT_EXECUTEINTIMERTHREAD)) Local $phNewTimer = $a_hCall[1] ConsoleWrite("CreateTimerQueueTimer = " & $a_hCall[0] & @TAB & $phNewTimer & @CRLF & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) Local $timer = TimerInit() ConsoleWrite("! Main loop - START" & @crlf) While 1 ConsoleWrite("+ " & TimerDiff($timer) & @CRLF) If TimerDiff($timer) > 5000 Then ConsoleWrite("! fire Exitloop event" & @CRLF) ExitLoop EndIf Sleep(10) WEnd ConsoleWrite("! Main loop - END" & @crlf) Local $a_hCall = DllCall($dll_kernel32, "bool", "DeleteTimerQueueTimer", _ "handle", 0, _ "handle", $phNewTimer, _ "handle", 0) ConsoleWrite("DeleteTimerQueueTimer = " & $a_hCall[0] & @CRLF & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) ConsoleWrite("_WinAPI_CloseHandle($phNewTimer) = " & _WinAPI_CloseHandle($phNewTimer) & @CRLF) Exit Func _RegisterSyncCallback($Function, $iParamCount = 0, $iOptions = 0) Local Static $hGUI Local Static $pSendMessage Local Static $iMsg If Not $hGUI Then $hGUI = GUICreate("RegisterSyncCallback_Msg", 300, 200) $iMsg = 0x8000 GUIRegisterMsg($iMsg, RegisterSyncCallback_Msg) $pSendMessage = DllCall($dll_kernel32, "ptr", "GetProcAddress", "ptr", _WinAPI_GetModuleHandle("user32.dll"), "str", "SendMessageW")[0] ConsoleWrite("$hGUI: " & Hex($hGUI) & @CRLF) ConsoleWrite("$pSendMessage: " & Hex($pSendMessage) & @CRLF) ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF) EndIf If @AutoItX64 Then Return MsgBox(0, "Error", "This is a x86 Sample :(") ; add x64 code yourself Local $pRemoteCode = _MemVirtualAlloc(0, 96, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE) If Not $pRemoteCode Then MsgBox(0, "Error", "_MemVirtualAlloc :(") Local $tCodeBuffer = DllStructCreate("byte[96]", $pRemoteCode) Local $hHandle = DllCallbackRegister($Function, "int", "int;int") ; hardcode one parameter ; Local $tExtradata = DllStructCreate("wchar[128]") ; DllStructSetData($tExtradata,1,"TestData") Local $sOPCode = "0x68" & SwapEndian($pRemoteCode + 30) & _ "8D4424085068" & SwapEndian($iMsg) & "68" & SwapEndian($hGUI) & _ "B8" & SwapEndian($pSendMessage) & "FFD0C2" & StringLeft(SwapEndian($iParamCount * 4), 4) & _ SwapEndian(DllCallbackGetPtr($hHandle)) & SwapEndian($iParamCount) ; & SwapEndian(DllStructGetPtr($tExtradata)) ;<<== added this line and it worked > by jugador DllStructSetData($tCodeBuffer, 1, $sOPCode) ConsoleWrite("$tCodeBuffer: " & DllStructGetData($tCodeBuffer, 1) & @CRLF) Return $pRemoteCode EndFunc ;==>_RegisterSyncCallback Func RegisterSyncCallback_Msg($hWnd, $iMsg, $wParam, $lParam) ConsoleWrite(">RegisterSyncCallback_Msg Called" & @CRLF) ConsoleWrite("$hWnd: " & Hex($hWnd) & @CRLF) ConsoleWrite("$iMsg: " & Hex($iMsg) & @CRLF) ConsoleWrite("$wParam: " & Hex($wParam) & @CRLF) ConsoleWrite("$lParam: " & Hex($lParam) & @CRLF) Local $tStruct = DllStructCreate("ptr pFunction", $lParam) Local $tNParameters = DllStructCreate("ptr NParameters", $lParam + 4) Local $tStructParameters = DllStructCreate("ptr", $wParam) ConsoleWrite("$tStruct.pFunction: " & $tStruct.pFunction & @CRLF) ConsoleWrite("$tNParameters.NParameters: " & $tNParameters.NParameters & @CRLF) Local $aValues[$tNParameters.NParameters] For $i = 0 To $tNParameters.NParameters - 1 $aValues[$i] = DllStructGetData(DllStructCreate("int", DllStructGetData($tStructParameters, 1) + ($i * 4)), 1) ConsoleWrite($aValues[$i] & @CRLF) Next DllCallAddress("int", $tStruct.pFunction, "int", $aValues[0], "int", $aValues[1]) Return 1 EndFunc ;==>RegisterSyncCallback_Msg Func SwapEndian($hex) Return Hex(BitOR(BitOR(BitOR(BitShift($hex, 24), _ BitAND(BitShift($hex, -8), 0x00FF0000)), _ BitAND(BitShift($hex, 8), 0x0000FF00)), _ BitShift($hex, -24)), 8) EndFunc ;==>SwapEndian
  12. I guess these two describe the same problem, sadly with no solution.
  13. Thanks for all the feedback 🙂👍! UEZ, it's not stable for me. I think you've set the initial delay too high with 5000, this way the callback is called just before the timeout. When you reduce it to 100, I think it will fail for you too again.
  14. Hello Forum, I'm trying to create a working example for CreateTimerQueueTimer, but in my example the script exits without error before reaching the timeout of 5 seconds. I appreciate any input on this. CreateTimerQueueTimer > https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx ; https://www.codeproject.com/KB/system/timers_intro.aspx ; > If you want a high-precision, low-overhead, non-blocking timer that will work on Windows 2000 and later, use the queue timer. ; Example by trancexx > https://www.autoitscript.com/forum/topic/87702-myip/#comment-631759 #include <WinAPIError.au3> #include <WinAPIHObj.au3> Local $hCallback = DllCallbackRegister("_CallBackFunction", "none", "lparam;bool") Func _CallBackFunction($lParam, $TimerOrWaitFired) ConsoleWrite("_CallBackFunction fired = " & TimerInit() & @TAB & $lParam & @TAB & $TimerOrWaitFired & @CRLF) EndFunc ;==>_CallBackFunction Local Const $WT_EXECUTELONGFUNCTION = 0x00000010 ; The callback function can perform a long wait. This flag helps the system to decide if it should create a new thread. Local Const $WT_EXECUTEINTIMERTHREAD = 0x00000020 ; The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations. Local Const $WT_EXECUTEINPERSISTENTTHREAD = 0x00000080 ; The callback function is queued to a thread that never terminates. Local $a_h_CreateTimerQueue = DllCall("kernel32.dll", "handle", "CreateTimerQueue") ConsoleWrite("CreateTimerQueue = " & $a_h_CreateTimerQueue[0] & @CRLF & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) Local $i_TimerQueue_Start_after = 100 Local $i_TimerQueue_Repeat_after = 100 ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx Local $a_hCall = DllCall("kernel32.dll", "bool", "CreateTimerQueueTimer", _ "handle*", 0, _ "handle", $a_h_CreateTimerQueue[0], _ "ptr", DllCallbackGetPtr($hCallback), _ "ptr", 678, _ "dword", $i_TimerQueue_Start_after, _ "dword", $i_TimerQueue_Repeat_after, _ "ulong", BitOR($WT_EXECUTELONGFUNCTION, $WT_EXECUTEINTIMERTHREAD)) Local $phNewTimer = $a_hCall[1] ConsoleWrite("CreateTimerQueueTimer = " & $a_hCall[0] & @TAB & $phNewTimer & @CRLF & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) Local $timer = TimerInit() While 1 ConsoleWrite("+ " & TimerDiff($timer) & @CRLF) If TimerDiff($timer) > 5000 Then ConsoleWrite("! fire Exitloop event" & @CRLF) ExitLoop EndIf Sleep(10) WEnd Local $a_hCall = DllCall("kernel32.dll", "bool", "DeleteTimerQueueTimer", _ "handle", $a_h_CreateTimerQueue[0], _ "handle", $phNewTimer, _ "handle", 0) ConsoleWrite("DeleteTimerQueueTimer = " & $a_hCall[0] & @CRLF & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) ConsoleWrite("_WinAPI_CloseHandle($phNewTimer) = " & _WinAPI_CloseHandle($phNewTimer) & @CRLF) Local $a_hCall = DllCall("kernel32.dll", "bool", "DeleteTimerQueueEx", "handle", $a_h_CreateTimerQueue[0], "handle", 0) ConsoleWrite("DeleteTimerQueueEx = " & $a_hCall[0] & @CRLF & @CRLF) ConsoleWrite(_WinAPI_GetLastError() & @TAB & _WinAPI_GetLastErrorMessage() & @CRLF & @CRLF) DllCallbackFree($hCallback) Exit #cs ; https://www.autohotkey.com/boards/viewtopic.php?t=24997 #SingleInstance force qt := new QueueTimer(5, Func("Test")).Start() Sleep 10000 qt.Stop() SoundBeep Sleep 2000 qt.Start() Sleep 2000 return ; Destructor auto-fires, de-registering timer on exit Test(){ ToolTip % A_TickCount } ; Implements CreateTimerQueueTimer ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx class QueueTimer { TimerFunc := 0 ; Pointer to the function object to be called on timer tick Period := 0 ; Time (in ms) that the timer fires at Delay := 0 ; Amount of time before first fire Running := 0 ; Whether or not the timer is running RegisteredCallback := 0 ; Pointer to the callback registered with RegisterCallback hTimer := 0 ; handle to the timer-queue timer hQueue := 0 ; handle to the timer queue Freq := 0 ; Number of ticks per second, or 0 if high-resolution performance counters not supported __New(period, TimerFunc := 0, delay := 0){ DllCall("QueryPerformanceFrequency", "Int64*", freq) this.freq := freq if (this.freq == 0 || TimerFunc == 0 || period == 0) return 0 this.TimerFunc := TimerFunc this.period := period this.delay := delay } __Delete(){ this.Stop() } Start(){ if (this.Running){ this.Stop() } this.Running := 1 this.hQueue := DllCall("kernel32.dll\CreateTimerQueue") hTimer := 0 this.RegisteredCallback := RegisterCallback(this.TimerFunc,"F") DllCall("kernel32.dll\CreateTimerQueueTimer", "Ptr", &hTimer, "Ptr", this.hQueue, "UInt" , this.RegisteredCallback, "UInt", 0, "UInt", this.delay, "UInt", this.period, "Int", 0x00000000) this.hTimer := hTimer return this ; Allow chaining } Stop(){ if (this.Running){ DllCall("kernel32.dll\DeleteTimerQueueTimer", "Ptr", this.hQueue, "Ptr", this.hTimer, "Ptr", 0) DllCall("kernel32.dll\DeleteTimerQueueEx", "Ptr", this.hQueue, "Ptr", 0) DllCall("GlobalFree", "Ptr", this.RegisteredCallback, "Ptr") this.Running := 0 } return this } } #ce Best Regards
  15. Try Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam) instead. The NMHDR structure is not "int;int;int", but "struct; hwnd hWndFrom;uint_ptr IDFrom;INT Code; endstruct". Also don't use InputBox in the event, it must return as soon as possible. Try to relocate the complex part to your main loop, e.g. only set a trigger variable to true. Something like this: Global $b_Trigger_Event_Listview_NM_DBLCLK = False Global $hListView = GUICtrlGetHandle($todoList) GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY_HANDLER") While Sleep(10) If $b_Trigger_Event_Listview_NM_DBLCLK = True Then _Edit_Task() $b_Trigger_Event_Listview_NM_DBLCLK = False EndIf WEnd Func WM_NOTIFY_HANDLER($hWnd, $iMsg, $wParam, $lParam) Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam) Local $hwFrom = DllStructGetData($tNMHDR, 1) Local $code = DllStructGetData($tNMHDR, 3) If $code = $NM_DBLCLK Then ; THIS PART DOES NOT EXECUTE ON 64-BIT EXE. WHY??? If $hwFrom = $hListView Then $b_Trigger_Event_Listview_NM_DBLCLK = True EndIf EndIf Return $GUI_RUNDEFMSG EndFunc ;==>WM_NOTIFY_HANDLER Func _Edit_Task() Local $index = _GUICtrlListView_GetSelectionMark($hListView) If $index <> -1 Then Local $oldText = _GUICtrlListView_GetItemText($hListView, $index) Local $newText = InputBox("Edit Task", "Double-click edit:", $oldText) If Not @error And StringStripWS($newText, 3) <> "" Then _GUICtrlListView_SetItemText($hListView, $index, $newText) _GUICtrlListView_SetColumnWidth($hListView, 0, $LVSCW_AUTOSIZE_USEHEADER) EndIf EndIf EndFunc ;==>_Edit_Task
×
×
  • Create New...