开发者

Does anyone have a FileSystemWatcher-like class in C++/WinAPI?

I need a .Net's FileSystemWatcher analog in raw C++/WinAPI. I almost started to code one myself using FindFirstChangeNotification/FindNextChangeNotification, but then it开发者_如何转开发 occurred to me that I am probably not the first one who needs this and maybe someone will be willing to share.

Ideally what I need is a class which can be used as follows:

FileWatcher fw;
fw.startWatching("C:\MYDIR", "filename.dat", 
     FileWatcher::SIZE | FileWatcher::LAST_WRITE,
     &myChangeHandler);
...
fw.stopWatching();

Or if it would use somehting like boost::signal it would be even better. But please, no dependencies other than the Standard Library, boost and raw WinAPI. Thanks!


What about the ReadDirectoryChangesW function?

http://msdn.microsoft.com/en-us/library/aa365465(VS.85).aspx

It stores notifications in a buffer so you don't miss any changes (unless the buffer overflows)


2021 answer:

A forked version of the repo listed below that is actively maintained: https://github.com/SpartanJ/efsw

Old answer:

This is a cross-platform solution, but does the job wrapping the Win32 stuff nicely: https://github.com/jameswynn/simplefilewatcher


There is some public-domain code here. My current project uses this (inherited from previous developers). It works pretty well but we do miss notifications for reasons that are unclear (and possibly not caused by this code).

Note that the Win32 API here has some limitations which make it difficult/impossible to avoid missing notifications. Background and alleged work-round for the API are here


http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.created%28v=vs.71%29.aspx the above does throgh C#, we can always write a COM Wrapper


This is an example of ReadDirectoryChangesW, written in go

kernel32dll := w32.NewKernel32DLL()

dirPath := "C://test_dir"
// Get the HANDLE of the target directory
hDir, _ := kernel32dll.CreateFile(dirPath,
    w32.FILE_LIST_DIRECTORY,
    w32.FILE_SHARE_READ|w32.FILE_SHARE_WRITE|w32.FILE_SHARE_DELETE,
    0,
    w32.OPEN_EXISTING,
    w32.FILE_FLAG_BACKUP_SEMANTICS|w32.FILE_FLAG_OVERLAPPED,
    0,
)
defer kernel32dll.CloseHandle(hDir) // close the handle when the program exit

var maxBufferSize uint32 = 96 // depend on you.
buffer := make([]uint8, maxBufferSize)

// a function for reset the data.
memset := func(a []uint8, v uint8) {
    for i := range a {
        a[i] = v
    }
}

// a function for get the filename
getName := func(offset, fileNameLength uint32) string {
    size := fileNameLength / 2
    filename := make([]uint16, size)
    var i uint32 = 0
    for i = 0; i < size; i++ {
        filename[i] = binary.LittleEndian.Uint16([]byte{buffer[offset+2*i], buffer[offset+2*i+1]})
    }
    return syscall.UTF16ToString(filename)
}

var record w32.FILE_NOTIFY_INFORMATION
for {
    var dwBytes uint32 = 0
    memset(buffer, 0) // clear the buffer for use again.

    kernel32dll.ReadDirectoryChanges(hDir,
        uintptr(unsafe.Pointer(&buffer[0])),
        maxBufferSize,
        true, // bWatchSubtree
        w32.FILE_NOTIFY_CHANGE_LAST_WRITE|w32.FILE_NOTIFY_CHANGE_CREATION|w32.FILE_NOTIFY_CHANGE_FILE_NAME,
        &dwBytes,
        nil,
        0,
    )

    if dwBytes == 0 { // if successful dwBytes is the number bytes used, or zero for Failed.
        fmt.Printf("Buffer overflow! max-size:%d\n", maxBufferSize)
        return
    }

    record = *(*w32.FILE_NOTIFY_INFORMATION)(unsafe.Pointer(&buffer[0]))
    // There may be many FILE_NOTIFY_INFORMATION. For example, if you rename the file, it will trigger the FILE_ACTION_RENAMED_OLD_NAME and FILE_ACTION_RENAMED_NEW_NAM
    var offsetFilename uint32 = 12 // The 12 is calculated from FILE_NOTIFY_INFORMATION.{NextEntryOffset, Action, FileName Length} => they are uint32 => 4*3=12
    for {
        switch record.Action {
        case w32.FILE_ACTION_ADDED:
            fmt.Println("FILE_ACTION_ADDED")
        case w32.FILE_ACTION_REMOVED:
            fmt.Println("FILE_ACTION_REMOVED")
            return
        case w32.FILE_ACTION_MODIFIED:
            fmt.Println("FILE_ACTION_MODIFIED")
        case w32.FILE_ACTION_RENAMED_OLD_NAME:
            fmt.Println("FILE_ACTION_RENAMED_OLD_NAME")
        case w32.FILE_ACTION_RENAMED_NEW_NAME:
            fmt.Println("FILE_ACTION_RENAMED_NEW_NAME")
        default:
            break
        }

        fmt.Println(getName(offsetFilename, record.FileNameLength))

        if record.NextEntryOffset == 0 {
            break
        }
        offsetFilename = record.NextEntryOffset + 12
        record = *(*w32.FILE_NOTIFY_INFORMATION)(unsafe.Pointer(uintptr(unsafe.Pointer(&buffer[0])) + uintptr(record.NextEntryOffset)))
    }
}

You can go here to get the complete code.

https://github.com/CarsonSlovoka/go-pkg/blob/cf4a28372b05458d715ab118d4ce888b2727ac4d/v2/w32/kernel32_func_test.go#L465-L597

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜