The plan is to open a file for reading
Read the file and extract the last occurrence of a specific string using regexp
Save the position of the file pointer
Since it is the log file of another application, it will be changed by the other program.
When that happens, I want to be notified that it has happened, then read from the file pointer to the current EOF
In the olden days, I could use a callback function
I see AHK has RegisterCallback()
I presume that it would involve the FindFirstChangeNotification API
https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstchangenotificationa
Has anyone done this before?
As an alternative, I could set up a timer and monitor the file size ever few seconds, but I'd like to get to know call back functions, and this seems like an ideal place to use one.
Detect file change Topic is solved
Re: Detect file change
only if ure ok with the whole script being left stuck waiting in a nonsignaled state the majority of the time. see https://docs.microsoft.com/en-us/windows/win32/fileio/obtaining-directory-change-notificationsseems like an ideal place to use one.
just check for changes with SetTimer + ahk functions. this api is meant for multithreaded applications, which ahk isnt
-
- Posts: 4391
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: Detect file change Topic is solved
This could be done with WMI:
Code: Select all
#Persistent
FolderPath := "D:\Downloads" ; set the folder to watch
interval := 0.5 ; set the interval
SetFileMonitoring(FolderPath, interval)
SetFileMonitoring(FolderPath, interval) {
static winmgmts := ComObjGet("winmgmts:"), createSink
SplitPath, FolderPath,,,,, Drive
Folder := RegExReplace(FolderPath, "[A-Z]:\\|((?<!\\)\\(?!\\)|(?<!\\)$)", "\\")
ComObjConnect(createSink := ComObjCreate("WbemScripting.SWbemSink"), "FileEvent_")
winmgmts.ExecNotificationQueryAsync(createSink
, "Select * From __InstanceOperationEvent"
. " within " interval
. " Where Targetinstance Isa 'CIM_DataFile'"
. " And TargetInstance.Drive='" Drive "'"
. " And TargetInstance.Path='" Folder "'")
}
FileEvent_OnObjectReady(objEvent)
{
if (objEvent.Path_.Class = "__InstanceModificationEvent")
TrayTip, The file has been changed:, % RegExReplace(objEvent.TargetInstance.Name, ".*\\")
}
Return
Re: Detect file change
Boom!
That is what I had been remembering...
That is what I had been remembering...
- flyingDman
- Posts: 2840
- Joined: 29 Sep 2013, 19:01
Re: Detect file change
This gives me a "quota violation error". On a smaller folder it works fine. What is the file count limit?
14.3 & 1.3.7
-
- Posts: 4391
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: Detect file change
@flyingDman
This seems to be your local issue. WMI Error: 0x8004106C Description: Quota violation, while running WMI queries
This seems to be your local issue. WMI Error: 0x8004106C Description: Quota violation, while running WMI queries
-
- Posts: 4391
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: Detect file change
An implementation based on ReadDirectoryChanges:
Code: Select all
#NoEnv
SetBatchLines -1
folderPath1 := A_Desktop
folderPath2 := A_ScriptDir
FILE_NOTIFY_CHANGE_FILE_NAME := 0x1
FILE_NOTIFY_CHANGE_DIR_NAME := 0x2
FILE_NOTIFY_CHANGE_ATTRIBUTES := 0x4
FILE_NOTIFY_CHANGE_SIZE := 0x8
notifyFilter := FILE_NOTIFY_CHANGE_FILE_NAME
| FILE_NOTIFY_CHANGE_DIR_NAME
| FILE_NOTIFY_CHANGE_ATTRIBUTES
| FILE_NOTIFY_CHANGE_SIZE
OnDirectoryChanges(folderPath1, "UserFunc", notifyFilter)
OnDirectoryChanges(folderPath2, "UserFunc", notifyFilter)
UserFunc(filePath, action) {
MsgBox % A_ThisFunc . "`n" . ["Added", "Removed", "Modified", "Renamed, old name", "Renamed, new name"][action] ": " filePath
}
OnDirectoryChanges(dirPath, procName := -1, notifyFilter := 3) {
static DirList := {}, EventList, WM_ReadDirectoryChanges := 0xFFF
IsObject(EventList) || EventList := DirectoryChangesEventListener(-1, 0, 0)
if (procName = -1) {
return EventList[DirList[dirPath]].UserDefinedProc.Name
}
else if (procName = "") {
if DirList.HasKey(dirPath) && eventHandle := DirList.Delete(dirPath)
return EventList.Delete(eventHandle).UserDefinedProc.Name
}
else if IsFunc(procName) {
if DirList.HasKey(dirPath) && eventHandle := DirList.Delete(dirPath)
priorProcName := EventList.Delete(eventHandle).UserDefinedProc.Name
else priorProcName := procName
if OnMessage(WM_ReadDirectoryChanges) != "DirectoryChangesEventListener"
OnMessage(WM_ReadDirectoryChanges, "DirectoryChangesEventListener")
Event := new _Event
Event.Proc := new _AsyncReadDirectoryChanges(dirPath, notifyFilter)
Event.Wait := new _AsyncWaitFunc(Event.handle, A_ScriptHWND, WM_ReadDirectoryChanges)
Event.UserDefinedProc := Func(procName)
Event.dirPath := dirPath
DirList[dirPath] := Event.handle
EventList[Event.handle] := Event
Event.Proc()
Event.Wait()
return priorProcName
}
else return false
}
DirectoryChangesEventListener(wParam, lParam, msg) {
static EventList := {}, WM_ReadDirectoryChanges := 0xFFF
Critical
if (msg = WM_ReadDirectoryChanges && Event := EventList[wParam]) {
action := Event.Proc.GetEventType()
name := Event.Proc.GetObjectName()
path := Event.dirPath . "\" . name
Proc := Event.UserDefinedProc
%Proc%(path, action)
Event.Proc()
Event.Wait()
}
else if (wParam = -1)
return EventList
}
class _Event
{
__New() {
this.handle := DllCall("CreateEvent", "Int", 0, "Int", 0, "Int", 1, "Int", 0, "Ptr")
this.SetCapacity("overlapped", 32)
NumPut(this.handle, this.GetAddress("overlapped"), A_PtrSize*2 + 8, "Ptr")
}
__Delete() {
DllCall("CloseHandle", "Ptr", this.handle)
}
}
class _AsyncWaitFunc
{
__New(eventObjHandle, hWnd, msg, timeout := -1) {
if !this.startAddress := CreateWaitFunc(eventObjHandle, hWnd, msg, timeout)
throw Exception("Failed to create wait function.`n`nError code:`t" . A_LastError)
}
__Call(EventObj) {
if IsObject(EventObj) {
if !threadHandle := DllCall("CreateThread", "Int", 0, "Int", 0, "Ptr", this.startAddress, "Int", 0, "UInt", 0, "Int", 0, "Ptr")
throw Exception("Failed to create thread.`n`nError code:`t" . A_LastError)
return threadHandle
}
}
}
class _AsyncReadDirectoryChanges
{
__New(dirPath, notifyFilter) {
static FILE_SHARE_READ := 1
, FILE_SHARE_WRITE := 2
, OPEN_EXISTING := 3
, FILE_FLAG_OVERLAPPED := 0x40000000
, FILE_FLAG_BACKUP_SEMANTICS := 0x2000000
this.SetCapacity("buffer", 1024)
this.notifyFilter := notifyFilter
this.handle := DllCall("CreateFile", "Str", dirPath, "UInt", 1, "UInt", FILE_SHARE_READ|FILE_SHARE_WRITE, "Int", 0
, "UInt", OPEN_EXISTING
, "UInt", FILE_FLAG_OVERLAPPED|FILE_FLAG_BACKUP_SEMANTICS, "Int", 0, "Ptr")
}
GetEventType() {
return NumGet(this.GetAddress("buffer") + 4, "UInt")
}
GetObjectName() {
bufferRef := this.GetAddress("buffer")
return StrGet(bufferRef + 12, NumGet(bufferRef + 8, "UInt")//2)
}
__Call(EventObj) {
if IsObject(EventObj) {
return DllCall("ReadDirectoryChanges", "Ptr", this.handle, "Ptr", this.GetAddress("buffer"), "UInt", 1024, "Int", 1
, "UInt", this.notifyFilter, "UInt", 0, "Ptr", EventObj.GetAddress("overlapped"), "Int", 0)
}
}
__Delete() {
DllCall("CloseHandle", "Ptr", this.handle)
}
}
CreateWaitFunc(Handle, hWnd, Msg, Timeout=-1)
{
static MEM_COMMIT := 0x1000, PAGE_EXECUTE_READWRITE := 0x40
ptr := DllCall("VirtualAlloc", "Ptr", 0, "Ptr", A_PtrSize = 4 ? 49 : 85, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "kernel32.dll", "Ptr")
pWaitForSingleObject := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "WaitForSingleObject", "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "user32.dll", "Ptr")
pSendMessageW := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "SendMessageW", "Ptr")
NumPut(pWaitForSingleObject, ptr*1)
NumPut(pSendMessageW, ptr + A_PtrSize)
if (A_PtrSize = 4) {
NumPut(0x68, ptr + 8, "UChar")
NumPut(Timeout, ptr + 9, "UInt"), NumPut(0x68, ptr + 13, "UChar")
NumPut(Handle, ptr + 14), NumPut(0x15FF, ptr + 18, "UShort")
NumPut(ptr, ptr + 20), NumPut(0x6850, ptr + 24, "UShort")
NumPut(Handle, ptr + 26), NumPut(0x68, ptr + 30, "UChar")
NumPut(Msg, ptr + 31, "UInt"), NumPut(0x68, ptr + 35, "UChar")
NumPut(hWnd, ptr + 36), NumPut(0x15FF, ptr + 40, "UShort")
NumPut(ptr+4, ptr + 42), NumPut(0xC2, ptr + 46, "UChar"), NumPut(4, ptr + 47, "UShort")
}
else {
NumPut(0x53, ptr + 16, "UChar")
NumPut(0x20EC8348, ptr + 17, "UInt"), NumPut(0xBACB8948, ptr + 21, "UInt")
NumPut(Timeout, ptr + 25, "UInt"), NumPut(0xB948, ptr + 29, "UShort")
NumPut(Handle, ptr + 31), NumPut(0x15FF, ptr + 39, "UShort")
NumPut(-45, ptr + 41, "UInt"), NumPut(0xB849, ptr + 45, "UShort")
NumPut(Handle, ptr + 47), NumPut(0xBA, ptr + 55, "UChar")
NumPut(Msg, ptr + 56, "UInt"), NumPut(0xB948, ptr + 60, "UShort")
NumPut(hWnd, ptr + 62), NumPut(0xC18941, ptr + 70, "UInt")
NumPut(0x15FF, ptr + 73, "UShort"), NumPut(-71, ptr + 75, "UInt")
NumPut(0x20C48348, ptr + 79, "UInt"), NumPut(0xC35B, ptr + 83, "UShort")
}
Return ptr + A_PtrSize*2
}
Re: Detect file change
@teadrinker
what does this do? post the source
and
is this safe in ahk now? last time i tried it my script kept crashing occasionally
Code: Select all
CreateWaitFunc(Handle, hWnd, Msg, Timeout=-1)
{
static MEM_COMMIT := 0x1000, PAGE_EXECUTE_READWRITE := 0x40
ptr := DllCall("VirtualAlloc", "Ptr", 0, "Ptr", A_PtrSize = 4 ? 49 : 85, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "kernel32.dll", "Ptr")
pWaitForSingleObject := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "WaitForSingleObject", "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "user32.dll", "Ptr")
pSendMessageW := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "SendMessageW", "Ptr")
NumPut(pWaitForSingleObject, ptr*1)
NumPut(pSendMessageW, ptr + A_PtrSize)
if (A_PtrSize = 4) {
NumPut(0x68, ptr + 8, "UChar")
NumPut(Timeout, ptr + 9, "UInt"), NumPut(0x68, ptr + 13, "UChar")
NumPut(Handle, ptr + 14), NumPut(0x15FF, ptr + 18, "UShort")
NumPut(ptr, ptr + 20), NumPut(0x6850, ptr + 24, "UShort")
NumPut(Handle, ptr + 26), NumPut(0x68, ptr + 30, "UChar")
NumPut(Msg, ptr + 31, "UInt"), NumPut(0x68, ptr + 35, "UChar")
NumPut(hWnd, ptr + 36), NumPut(0x15FF, ptr + 40, "UShort")
NumPut(ptr+4, ptr + 42), NumPut(0xC2, ptr + 46, "UChar"), NumPut(4, ptr + 47, "UShort")
}
else {
NumPut(0x53, ptr + 16, "UChar")
NumPut(0x20EC8348, ptr + 17, "UInt"), NumPut(0xBACB8948, ptr + 21, "UInt")
NumPut(Timeout, ptr + 25, "UInt"), NumPut(0xB948, ptr + 29, "UShort")
NumPut(Handle, ptr + 31), NumPut(0x15FF, ptr + 39, "UShort")
NumPut(-45, ptr + 41, "UInt"), NumPut(0xB849, ptr + 45, "UShort")
NumPut(Handle, ptr + 47), NumPut(0xBA, ptr + 55, "UChar")
NumPut(Msg, ptr + 56, "UInt"), NumPut(0xB948, ptr + 60, "UShort")
NumPut(hWnd, ptr + 62), NumPut(0xC18941, ptr + 70, "UInt")
NumPut(0x15FF, ptr + 73, "UShort"), NumPut(-71, ptr + 75, "UInt")
NumPut(0x20C48348, ptr + 79, "UInt"), NumPut(0xC35B, ptr + 83, "UShort")
}
Return ptr + A_PtrSize*2
}
and
Code: Select all
DllCall("CreateThread"
-
- Posts: 4391
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: Detect file change
This function creates the machine code for WaitForSingleObject and SendMessageW to launch them in a separated thread.
Yes, for me this code works without issues.
I can't, since it was not written by me.
Re: Detect file change
This is perfectly safe to do, but that doesn't mean the code is safe,This function creates the machine code for WaitForSingleObject and SendMessageW to launch them in a separated thread.
It is not very clear if this is handled, I guess it is not.WaitForSingleObject - hHandle wrote: If this handle is closed while the wait is still pending, the function's behavior is undefined.
It seems you create a new thread for every time an event occurs, and not closing the old thread handle.CreateThread wrote: The thread object remains in the system until the thread has terminated and all handles to it have been closed through a call to CloseHandle
This does not zero initialise the memory, and,Code: Select all
this.SetCapacity("overlapped", 32)
OVERLAPPED structure wrote: Any unused members of this structure should always be initialized to zero before the structure is used in a function call. Otherwise, the function may fail and return ERROR_INVALID_PARAMETER.
In your case, it is filled asynchronously, and I cannot see any code which syncs the reading of the result, this is not safe.ReadDirectoryChangesW - lpBuffer wrote: This buffer is filled either synchronously or asynchronously
Also, since you make an explicit call to the msg monitor callback, DirectoryChangesEventListener(-1, 0, 0), you should avoid using critical in the message monitor callback, else you set this as the default for all threads (since you make this call in the auto-exec section).
Cheers.
Re: Detect file change
so its just calling
though i cant quite make out what its passing in lParam. 0? 0x50?
and what/if it returns
@Helgef are u saying that starting threads with CreateThread is safe or that WaitForSingleObject is safe?
Code: Select all
WaitForSingleObject(Handle, Timeout);
SendMessageW(hWnd, Msg, Handle, ???);
and what/if it returns
@Helgef are u saying that starting threads with CreateThread is safe or that WaitForSingleObject is safe?
Re: Detect file change
The message monitor function doesn't doesn't use lParam. It doesn't have a fixed return value, it will return whatever sendmessage returns.
Both are safe unless you do anything unsafe with them.are u saying that starting threads with CreateThread is safe or that WaitForSingleObject is safe?
-
- Posts: 4391
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: Detect file change
@Helgef
Thanks for clarifications, I've added some improvements (RtlZeroMemory, TerminateThread and CloseHandle), is it enough?
Thanks for clarifications, I've added some improvements (RtlZeroMemory, TerminateThread and CloseHandle), is it enough?
Re: Detect file change
Hard to say without seeing the codeI've added some improvements (RtlZeroMemory, TerminateThread and CloseHandle), is it enough?
Did you handle this? You are reading from the buffer which is filled asynch.Code: Select all
action := Event.Proc.GetEventType() name := Event.Proc.GetObjectName()
Cheers.ReadDirectoryChangesW wrote: For asynchronous completion, you can receive notification in one of three ways:
-
- Posts: 4391
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: Detect file change
Not completely understand. Now reading occurs when a message from the thread is recieved. I can implement a completion routine, in that case messaging is not needed? What the issues may the current way cause?
Sorry, forgot to post the code:
Code: Select all
#NoEnv
SetBatchLines -1
folderPath1 := A_Desktop
folderPath2 := A_ScriptDir
FILE_NOTIFY_CHANGE_FILE_NAME := 0x1
FILE_NOTIFY_CHANGE_DIR_NAME := 0x2
FILE_NOTIFY_CHANGE_ATTRIBUTES := 0x4
FILE_NOTIFY_CHANGE_SIZE := 0x8
notifyFilter := FILE_NOTIFY_CHANGE_FILE_NAME
| FILE_NOTIFY_CHANGE_DIR_NAME
| FILE_NOTIFY_CHANGE_ATTRIBUTES
| FILE_NOTIFY_CHANGE_SIZE
OnDirectoryChanges(folderPath1, "UserFunc", notifyFilter)
OnDirectoryChanges(folderPath2, "UserFunc", notifyFilter)
UserFunc(filePath, action) {
MsgBox % A_ThisFunc . "`n" . ["Added", "Removed", "Modified", "Renamed, old name", "Renamed, new name"][action] ": " filePath
}
OnDirectoryChanges(dirPath, procName := -1, notifyFilter := 3) {
static DirList := {}, EventList, WM_ReadDirectoryChanges := 0xFFF
IsObject(EventList) || EventList := DirectoryChangesEventListener(-1, 0, 0)
if (procName = -1) {
return EventList[DirList[dirPath]].UserDefinedProc.Name
}
else if (procName = "") {
if DirList.HasKey(dirPath) && eventHandle := DirList.Delete(dirPath)
return EventList.Delete(eventHandle).UserDefinedProc.Name
}
else if IsFunc(procName) {
if DirList.HasKey(dirPath) && eventHandle := DirList.Delete(dirPath)
priorProcName := EventList.Delete(eventHandle).UserDefinedProc.Name
else priorProcName := procName
if OnMessage(WM_ReadDirectoryChanges) != "DirectoryChangesEventListener"
OnMessage(WM_ReadDirectoryChanges, "DirectoryChangesEventListener")
Event := new _Event
Event.Proc := new _AsyncReadDirectoryChanges(dirPath, notifyFilter)
Event.Wait := new _AsyncWaitFunc(Event.handle, A_ScriptHWND, WM_ReadDirectoryChanges)
Event.UserDefinedProc := Func(procName)
Event.dirPath := dirPath
DirList[dirPath] := Event.handle
EventList[Event.handle] := Event
Event.Proc()
Event.Wait()
return priorProcName
}
else return false
}
DirectoryChangesEventListener(wParam, lParam, msg) {
static EventList := {}, WM_ReadDirectoryChanges := 0xFFF
if (msg = WM_ReadDirectoryChanges && Event := EventList[wParam]) {
action := Event.Proc.GetEventType()
name := Event.Proc.GetObjectName()
path := Event.dirPath . "\" . name
Proc := Event.UserDefinedProc
%Proc%(path, action)
Event.Proc()
Event.Wait()
}
else if (wParam = -1)
return EventList
}
class _Event
{
__New() {
this.handle := DllCall("CreateEvent", "Int", 0, "Int", 0, "Int", 1, "Int", 0, "Ptr")
this.SetCapacity("overlapped", A_PtrSize*3 + 8)
addr := this.GetAddress("overlapped")
DllCall("RtlZeroMemory", "Ptr", addr, "Ptr", A_PtrSize*3 + 8)
NumPut(this.handle, addr + A_PtrSize*2 + 8, "Ptr")
}
__Delete() {
DllCall("CloseHandle", "Ptr", this.handle)
}
}
class _AsyncWaitFunc
{
__New(eventObjHandle, hWnd, msg, timeout := -1) {
if !this.startAddress := CreateWaitFunc(eventObjHandle, hWnd, msg, timeout)
throw Exception("Failed to create wait function.`n`nError code:`t" . A_LastError)
}
__Call(EventObj) {
if IsObject(EventObj) {
if this.threadHandle {
DllCall("TerminateThread", "Ptr", this.threadHandle, "UInt", 0)
DllCall("CloseHandle", "Ptr", this.threadHandle)
}
if !this.threadHandle := DllCall("CreateThread", "Int", 0, "Int", 0, "Ptr", this.startAddress, "Int", 0, "UInt", 0, "Int", 0, "Ptr")
throw Exception("Failed to create thread.`n`nError code:`t" . A_LastError)
return this.threadHandle
}
}
__Delete() {
if this.threadHandle {
DllCall("TerminateThread", "Ptr", this.threadHandle, "UInt", 0)
DllCall("CloseHandle", "Ptr", this.threadHandle)
}
}
}
class _AsyncReadDirectoryChanges
{
__New(dirPath, notifyFilter) {
static FILE_SHARE_READ := 1
, FILE_SHARE_WRITE := 2
, OPEN_EXISTING := 3
, FILE_FLAG_OVERLAPPED := 0x40000000
, FILE_FLAG_BACKUP_SEMANTICS := 0x02000000
this.SetCapacity("buffer", 1024)
this.notifyFilter := notifyFilter
this.handle := DllCall("CreateFile", "Str", dirPath, "UInt", 1, "UInt", FILE_SHARE_READ|FILE_SHARE_WRITE, "Int", 0
, "UInt", OPEN_EXISTING
, "UInt", FILE_FLAG_OVERLAPPED|FILE_FLAG_BACKUP_SEMANTICS, "Int", 0, "Ptr")
}
GetEventType() {
return NumGet(this.GetAddress("buffer") + 4, "UInt")
}
GetObjectName() {
bufferRef := this.GetAddress("buffer")
return StrGet(bufferRef + 12, NumGet(bufferRef + 8, "UInt")//2)
}
__Call(EventObj) {
if IsObject(EventObj) {
return DllCall("ReadDirectoryChanges", "Ptr", this.handle, "Ptr", this.GetAddress("buffer"), "UInt", 1024, "Int", 1
, "UInt", this.notifyFilter, "UInt", 0, "Ptr", EventObj.GetAddress("overlapped"), "Int", 0)
}
}
__Delete() {
DllCall("CloseHandle", "Ptr", this.handle)
}
}
CreateWaitFunc(Handle, hWnd, Msg, Timeout=-1)
{
static MEM_COMMIT := 0x1000, PAGE_EXECUTE_READWRITE := 0x40
ptr := DllCall("VirtualAlloc", "Ptr", 0, "Ptr", A_PtrSize = 4 ? 49 : 85, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "kernel32.dll", "Ptr")
pWaitForSingleObject := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "WaitForSingleObject", "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "user32.dll", "Ptr")
pSendMessageW := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "SendMessageW", "Ptr")
NumPut(pWaitForSingleObject, ptr*1)
NumPut(pSendMessageW, ptr + A_PtrSize)
if (A_PtrSize = 4) {
NumPut(0x68, ptr + 8, "UChar")
NumPut(Timeout, ptr + 9, "UInt"), NumPut(0x68, ptr + 13, "UChar")
NumPut(Handle, ptr + 14), NumPut(0x15FF, ptr + 18, "UShort")
NumPut(ptr, ptr + 20), NumPut(0x6850, ptr + 24, "UShort")
NumPut(Handle, ptr + 26), NumPut(0x68, ptr + 30, "UChar")
NumPut(Msg, ptr + 31, "UInt"), NumPut(0x68, ptr + 35, "UChar")
NumPut(hWnd, ptr + 36), NumPut(0x15FF, ptr + 40, "UShort")
NumPut(ptr+4, ptr + 42), NumPut(0xC2, ptr + 46, "UChar"), NumPut(4, ptr + 47, "UShort")
}
else {
NumPut(0x53, ptr + 16, "UChar")
NumPut(0x20EC8348, ptr + 17, "UInt"), NumPut(0xBACB8948, ptr + 21, "UInt")
NumPut(Timeout, ptr + 25, "UInt"), NumPut(0xB948, ptr + 29, "UShort")
NumPut(Handle, ptr + 31), NumPut(0x15FF, ptr + 39, "UShort")
NumPut(-45, ptr + 41, "UInt"), NumPut(0xB849, ptr + 45, "UShort")
NumPut(Handle, ptr + 47), NumPut(0xBA, ptr + 55, "UChar")
NumPut(Msg, ptr + 56, "UInt"), NumPut(0xB948, ptr + 60, "UShort")
NumPut(hWnd, ptr + 62), NumPut(0xC18941, ptr + 70, "UInt")
NumPut(0x15FF, ptr + 73, "UShort"), NumPut(-71, ptr + 75, "UInt")
NumPut(0x20C48348, ptr + 79, "UInt"), NumPut(0xC35B, ptr + 83, "UShort")
}
Return ptr + A_PtrSize*2
}
Re: Detect file change
Hi.
Cheers.
Xp users should not use it at all.TerminateThread wrote: TerminateThread is a dangerous function that should only be used in the most extreme cases.
For example, TerminateThread can result in the following problems:
If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.
I don't see that the documentation guarantees that the buffer has been formatted at this stage, although it evidently can be. It seems like it would be trivial to call GetOverlappedResult in the message callback, then you can also verify that buffer really has been written to.Now reading occurs when a message from the thread is recieved.
This would also be a possibility.I can implement a completion routine, in that case messaging is not needed?
The result of concurrent read and write operations is undefined, anything can happen.What the issues may the current way cause?
Cheers.
Re: Detect file change
ive tried running this code with ahk x64
Code: Select all
#NoEnv
; SetBatchLines -1
DllCall("CreateThread", "Ptr", 0, "Ptr", 0, "Ptr", RegisterCallback("ThreadProc"), "Ptr", 0, "UInt", 0, "UInt*", lpThreadId, "Ptr")
ThreadProc(lpParameter) {
i := 0
Loop 5
ToolTip % ++i
MsgBox % i
}
Esc::ExitApp
with SetBatchLines -1 sometimes:
- the same thing happens: a ToolTip "1" is shown, the loop never exits and it never gets to the msgbox even
- it counts up to 5 very fast(presumably also showing incrementing tooltips in between), a ToolTip "5" is shown and a MsgBox "5" is shown
- it counts up to 6 very fast(presumably also showing incrementing tooltips in between), a ToolTip "6" is shown and a MsgBox "6" is shown
and if i add a Sleep inside the loop, it gets stuck on ToolTip "1" all the time in every case
Re: Detect file change
That would be an example where you do something unsafe with createthread.
Re: Detect file change
so "its safe until its not"
ill take that as "regular ahk functions + RegisterCallback = unsafe"
@teadrinker
i think the source is
except with all parameters(save for WaitEvent) already hardcoded in
and still not sure whether the return type is correct
ill take that as "regular ahk functions + RegisterCallback = unsafe"
@teadrinker
i think the source is
Code: Select all
void __fastcall ThreadProc(LPVOID lpParameter) {
DWORD WaitEvent = WaitForSingleObject(Handle, Timeout);
SendMessageW(hWnd, Msg, (WPARAM)Handle, WaitEvent);
}
and still not sure whether the return type is correct
Re: Detect file change
Exactly . Rule of thumbs, shared mutable state, bad , everything else, fine .
Right again, think about what you discovered in Confirmed bug with #if and Hotkey, If , command parameters ending up in a static variable, causing issues with ahk's psuedo threads sharing states, clearly you cannot run multiple threads with such a design.ill take that as "regular ahk functions + RegisterCallback = unsafe"
That is possible, I didn't disassemble it, but I verified (with dllcall) that it returns the result from SendMessage, which I suppose can be simply because its return value is left in the return register. Note,i think the source isCode: Select all
void __fastcall ThreadProc(LPVOID lpParameter) { DWORD WaitEvent = WaitForSingleObject(Handle, Timeout); SendMessageW(hWnd, Msg, (WPARAM)Handle, WaitEvent); }
Cheers.ThreadProc callback function wrote: Do not declare this callback function with a void return type and cast the function pointer to LPTHREAD_START_ROUTINE when creating the thread. Code that does this is common, but it can crash on 64-bit Windows.
Who is online
Users browsing this forum: Google [Bot], MrHue and 53 guests