有人在 C++/WinAPI 中有类似 FileSystemWatcher 的类吗?

发布于 2024-08-18 06:00:23 字数 470 浏览 4 评论 0原文

我需要原始 C++/WinAPI 中的 .Net 的 FileSystemWatcher 模拟。 我几乎开始使用 FindFirstChangeNotification/FindNextChangeNotification 自己编写一个代码,但后来我想到我可能不是第一个需要这个的人,也许有人愿意分享。

理想情况下,我需要的是一个可以按如下方式使用的类:

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

或者如果它使用像 boost::signal 这样的东西,那就更好了。 但请注意,除了标准库、boost 和原始 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

何以畏孤独 2024-08-25 06:00:23

ReadDirectoryChangesW 函数怎么样?

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

它将通知存储在缓冲区中,这样您就不会错过任何更改(除非缓冲区溢出)

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)

柠檬心 2024-08-25 06:00:23

2021 答案:

下面列出的存储库的分叉版本正在积极维护:https://github.com/SpartanJ/ efsw

旧答案:

这是一个跨平台解决方案,但可以很好地包装 Win32 内容:
https://github.com/jameswynn/simplefilewatcher

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

故事还在继续 2024-08-25 06:00:23

此处有一些公共域代码。我当前的项目使用这个(继承自以前的开发人员)。它工作得很好,但我们确实由于不清楚的原因而错过了通知(并且可能不是由此代码引起的)。

请注意,此处的 Win32 API 有一些限制,这使得很难/不可能避免丢失通知。 API 的背景和所谓的解决方法是 这里

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

皇甫轩 2024-08-25 06:00:23

http://msdn .microsoft.com/en-us/library/system.io.filesystemwatcher.created%28v=vs.71%29.aspx 上面是通过C#实现的,我们总是可以编写一个COM Wrapper

沉睡月亮 2024-08-25 06:00:23

这是 ReadDirectoryChangesW 的示例,用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)))
    }
}

可以到这里获取完整代码。

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文