#3
by きたぐに » 5年前
すみませんでした。すべて乗っけておきます。
//エラーが起こらないコード
コード:
//ヘッダファイル
#ifndef WATCHER
#define WATCHER
namespace file
{
//ディレクトリの変更を監視する
class watcher
: private singleton<watcher>
{
public:
enum class actionType
{
file_invalid_flag = -1,
file_added = FILE_ACTION_ADDED,
file_removed = FILE_ACTION_REMOVED,
file_modified = FILE_ACTION_MODIFIED,
file_renamed_old = FILE_ACTION_RENAMED_OLD_NAME,
file_renamed_new = FILE_ACTION_RENAMED_NEW_NAME
};
enum class status
{
run,
ready_terminate,
terminate,
};
//変更されたファイルの情報
struct fileInfo
{
WCHAR FileName[1<<8]; //ファイル名
actionType Type; //その情報
//コンストラクタ
fileInfo( const WCHAR* Name, const size_t& Length, const DWORD& dwRawFileAction ) noexcept :
FileName(), Type( static_cast<actionType>( dwRawFileAction ) )
{
if (Length >= std::size( FileName )) {
fatal << L"File Name is too long";
return;
}
memcpy( FileName, Name, sizeof( WCHAR )*( Length + 1 ) );
}
//デフォルトコンストラクタ
fileInfo() :
fileInfo( L"", 0, -1 )
{
}
//コピーコンストラクタ
fileInfo( const fileInfo& Left ) :
Type( Left.Type )
{
memcpy( FileName, Left.FileName, sizeof( FileName ) );
}
//ムーブコンストラクタ
fileInfo( fileInfo&& Right ) noexcept :
Type( Right.Type )
{
memcpy( FileName, Right.FileName, sizeof( FileName ) );
ZeroMemory( Right.FileName, sizeof( FileName ) );
Right.Type = actionType::file_invalid_flag;
}
fileInfo& operator=( const fileInfo& Left )
{
if (this == std::addressof( Left )) {
return *this; //自己代入は禁止
}
memcpy( FileName, Left.FileName, sizeof( FileName ) );
this->Type = Left.Type;
return *this;
}
fileInfo& operator=( fileInfo&& Right )
{
if (this == std::addressof( Right )) {
return *this;
}
memcpy( FileName, Right.FileName, sizeof( FileName ) );
ZeroMemory( Right.FileName, sizeof( FileName ) );
this->Type = Right.Type;
Right.Type = actionType::file_invalid_flag;
return *this;
}
};
private:
struct thread_info
{
std::wstring Path;
DWORD Filter;
status Status;
std::vector<fileInfo> Modified; //編集されたリスト
std::thread Thread;
//デフォルトコンストラクタ
thread_info() noexcept :
Path(), Filter(), Status( status::terminate ), Modified(), Thread()
{
}
//コンストラクタ
template< class _Fn,class... _Args>
thread_info( const WCHAR* lpszPath, const DWORD& dwFilter, _Fn&& Func, _Args&&... Args ) :
Path( lpszPath ), Filter( dwFilter ), Status( status::run ), Modified(),
Thread( std::forward<_Fn>( Func ), std::forward<_Args>( Args )... )
{
}
//コピーは禁止
thread_info( const thread_info& ) = delete;
thread_info& operator=( const thread_info& ) = delete;
//ムーブのみ
thread_info( thread_info&& Right ) noexcept :
Path( std::move( Right.Path ) ), Filter( Right.Filter ),Status(Right.Status),
Modified( std::move( Right.Modified ) ), Thread( std::move( Right.Thread ) )
{
Right.Filter = 0;
Right.Status = status::terminate;
}
thread_info& operator=( thread_info&& Right ) noexcept
{
if (this == std::addressof( Right )) {
return *this; //何もしない
}
this->Path = std::move( Right.Path );
this->Filter = Right.Filter;
this->Status = Right.Status;
this->Modified = std::move( Right.Modified );
this->Thread = std::move( Right.Thread );
Right.Filter = 0;
Right.Status = status::terminate;
return *this;
}
~thread_info()
{
if (Thread.joinable()) {
warn << L"Thread is alive!";
Thread.detach();
}
}
};
static void _main( const WPARAM& Code );
public:
//デフォルトのフィルタ
constexpr static DWORD dwDefaultFilter =
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE;
size_t terminate( const WPARAM& Code );
void assign( const WCHAR* Path, const WPARAM& Param, const DWORD& dwFilter = dwDefaultFilter );
const fileInfo getModified( const WPARAM& Code,size_t& Where ) const; //★、こいつがエラーを起こしていた
void sethWnd( HWND hWnd ) noexcept;
void detach_all();
private:
std::map<WPARAM, thread_info> _thInfo;
std::mutex _mtx;
HWND _hWnd;
};
}
#endif
ソースファイル
コード:
#include "stdafx.h"
#include "watcher.h"
#include "define.h"
namespace file
{
void watcher::_main( const WPARAM& Code )
{
OVERLAPPED _OverLapped;
std::vector<UCHAR> _Buffer( 1 << 14, 0 );
DWORD _Size;
FILE_NOTIFY_INFORMATION* _Info;
//初期化
CHANDLE _hDict = CreateFileW(_get()._thInfo[Code].Path.data(), FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr );
if (_hDict == INVALID_HANDLE_VALUE) {
error << L"Invalid Handle Value detected " << _get()._thInfo[Code].Path.data()
<< L"Code = " << Code;
std::lock_guard<std::mutex> _Lock( _get()._mtx );
_get()._thInfo[Code].Status = status::terminate;
return;
}
CHANDLE _hEvent = CreateEventW( nullptr, TRUE, FALSE, nullptr );
auto _Initialize = [&]()
{
ResetEvent( _hEvent );
_OverLapped = { 0 };
_OverLapped.hEvent = _hEvent;
//read only
return ReadDirectoryChangesW( _hDict, _Buffer.data(), _Buffer.size(),
TRUE, _get()._thInfo[Code].Filter, &_Size, &_OverLapped, nullptr ) == TRUE;
};
_Initialize();
while (_get()._thInfo[Code].Status == status::run) {
if (WaitForSingleObject( _hEvent, 500 ) == WAIT_TIMEOUT) {
continue;
}
if (!GetOverlappedResult( _hDict, &_OverLapped, &_Size, FALSE )) {
_Initialize();
continue;
}
if (_Size == 0) {
_Initialize();
continue;
}
_Info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>( _Buffer.data() );
_get()._mtx.lock();
_get()._thInfo[Code].Modified.clear();
while(true) {
_get()._thInfo[Code].Modified.emplace_back( _Info->FileName,
_Info->FileNameLength / sizeof( WCHAR ), _Info->Action );
if (_Info->NextEntryOffset == 0) {
break;
}
_Info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(
reinterpret_cast<unsigned char*>( _Info ) + _Info->NextEntryOffset );
}
_Buffer.clear();
_Initialize();
_get()._mtx.unlock();
SendMessageW( _get()._hWnd, WM_MODIFIED_FILE, Code, 0 );
}
_get()._thInfo[Code].Status = status::terminate;
}
size_t watcher::terminate( const WPARAM& Code )
{
if (_get()._thInfo[Code].Status == status::run) {
_get()._thInfo[Code].Status = status::ready_terminate;
while (_get()._thInfo[Code].Status == status::ready_terminate) {
std::this_thread::sleep_for( std::chrono::microseconds( 400 ) );
}
_get()._thInfo[Code].Thread.detach();
}
_get()._thInfo.erase( Code );
return _get()._thInfo.size();
}
void watcher::assign( const WCHAR* Path, const WPARAM& Param, const DWORD& dwFilter )
{
std::lock_guard<std::mutex> _Lock( _get()._mtx );
if (_get()._thInfo.find( Param ) != _get()._thInfo.cend()) {
//今動いているスレッドを止める
terminate( Param );
}
info << L"新しいパス:" << Path << L" Code: " << Param;
_get()._thInfo.emplace(
std::piecewise_construct, std::forward_as_tuple( Param ),
std::forward_as_tuple( Path, dwFilter, _main, Param ) );
}
const watcher::fileInfo watcher::getModified( const WPARAM& Code, size_t& Where ) const
{
std::lock_guard<std::mutex> _Lock( _get()._mtx );
const auto _OldWhere = Where;
++Where;
if (Where >= _get()._thInfo[Code].Modified.size()) {
Where = -1;
}
if (_OldWhere < _get()._thInfo[Code].Modified.size()) {
return _get()._thInfo[Code].Modified[_OldWhere];
}
//データはすべて返した
return fileInfo();
}
void watcher::sethWnd( HWND hWnd ) noexcept
{
_get()._hWnd = hWnd;
}
void watcher::detach_all()
{
std::lock_guard<std::mutex> _Lock( _get()._mtx );
size_t i = 0;
while (terminate( _get()._thInfo.begin()->first ) > 0) {}
}
}
古い実装だと、変更されたファイルリストはwatcher::thread_info::ModifiedのものをCodeが一致していればそのまま返していました。(しかも内部で動的にメモリを確保するwstringを使っていました)
今の実装では少し手間がかかりますが、指定された要素を一つずつ返すようにしたところ
**_Pnext** が 0xFFFFFFFFFFFFFFFF でした。
HEAP CORRUPTION DETECTED: after Normal block (#1176) at 0x00000043142539C0. CRT detected that the application wrote to memory after end of heap buffer.
のようなエラーが起こらなくなったのですが、その理由がわからない(上のエラーは確かwstringのデストラクタで起きていました)ので質問した次第です。
すみませんでした。すべて乗っけておきます。
//エラーが起こらないコード
[code]
//ヘッダファイル
#ifndef WATCHER
#define WATCHER
namespace file
{
//ディレクトリの変更を監視する
class watcher
: private singleton<watcher>
{
public:
enum class actionType
{
file_invalid_flag = -1,
file_added = FILE_ACTION_ADDED,
file_removed = FILE_ACTION_REMOVED,
file_modified = FILE_ACTION_MODIFIED,
file_renamed_old = FILE_ACTION_RENAMED_OLD_NAME,
file_renamed_new = FILE_ACTION_RENAMED_NEW_NAME
};
enum class status
{
run,
ready_terminate,
terminate,
};
//変更されたファイルの情報
struct fileInfo
{
WCHAR FileName[1<<8]; //ファイル名
actionType Type; //その情報
//コンストラクタ
fileInfo( const WCHAR* Name, const size_t& Length, const DWORD& dwRawFileAction ) noexcept :
FileName(), Type( static_cast<actionType>( dwRawFileAction ) )
{
if (Length >= std::size( FileName )) {
fatal << L"File Name is too long";
return;
}
memcpy( FileName, Name, sizeof( WCHAR )*( Length + 1 ) );
}
//デフォルトコンストラクタ
fileInfo() :
fileInfo( L"", 0, -1 )
{
}
//コピーコンストラクタ
fileInfo( const fileInfo& Left ) :
Type( Left.Type )
{
memcpy( FileName, Left.FileName, sizeof( FileName ) );
}
//ムーブコンストラクタ
fileInfo( fileInfo&& Right ) noexcept :
Type( Right.Type )
{
memcpy( FileName, Right.FileName, sizeof( FileName ) );
ZeroMemory( Right.FileName, sizeof( FileName ) );
Right.Type = actionType::file_invalid_flag;
}
fileInfo& operator=( const fileInfo& Left )
{
if (this == std::addressof( Left )) {
return *this; //自己代入は禁止
}
memcpy( FileName, Left.FileName, sizeof( FileName ) );
this->Type = Left.Type;
return *this;
}
fileInfo& operator=( fileInfo&& Right )
{
if (this == std::addressof( Right )) {
return *this;
}
memcpy( FileName, Right.FileName, sizeof( FileName ) );
ZeroMemory( Right.FileName, sizeof( FileName ) );
this->Type = Right.Type;
Right.Type = actionType::file_invalid_flag;
return *this;
}
};
private:
struct thread_info
{
std::wstring Path;
DWORD Filter;
status Status;
std::vector<fileInfo> Modified; //編集されたリスト
std::thread Thread;
//デフォルトコンストラクタ
thread_info() noexcept :
Path(), Filter(), Status( status::terminate ), Modified(), Thread()
{
}
//コンストラクタ
template< class _Fn,class... _Args>
thread_info( const WCHAR* lpszPath, const DWORD& dwFilter, _Fn&& Func, _Args&&... Args ) :
Path( lpszPath ), Filter( dwFilter ), Status( status::run ), Modified(),
Thread( std::forward<_Fn>( Func ), std::forward<_Args>( Args )... )
{
}
//コピーは禁止
thread_info( const thread_info& ) = delete;
thread_info& operator=( const thread_info& ) = delete;
//ムーブのみ
thread_info( thread_info&& Right ) noexcept :
Path( std::move( Right.Path ) ), Filter( Right.Filter ),Status(Right.Status),
Modified( std::move( Right.Modified ) ), Thread( std::move( Right.Thread ) )
{
Right.Filter = 0;
Right.Status = status::terminate;
}
thread_info& operator=( thread_info&& Right ) noexcept
{
if (this == std::addressof( Right )) {
return *this; //何もしない
}
this->Path = std::move( Right.Path );
this->Filter = Right.Filter;
this->Status = Right.Status;
this->Modified = std::move( Right.Modified );
this->Thread = std::move( Right.Thread );
Right.Filter = 0;
Right.Status = status::terminate;
return *this;
}
~thread_info()
{
if (Thread.joinable()) {
warn << L"Thread is alive!";
Thread.detach();
}
}
};
static void _main( const WPARAM& Code );
public:
//デフォルトのフィルタ
constexpr static DWORD dwDefaultFilter =
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE;
size_t terminate( const WPARAM& Code );
void assign( const WCHAR* Path, const WPARAM& Param, const DWORD& dwFilter = dwDefaultFilter );
const fileInfo getModified( const WPARAM& Code,size_t& Where ) const; //★、こいつがエラーを起こしていた
void sethWnd( HWND hWnd ) noexcept;
void detach_all();
private:
std::map<WPARAM, thread_info> _thInfo;
std::mutex _mtx;
HWND _hWnd;
};
}
#endif
[/code]
ソースファイル
[code]
#include "stdafx.h"
#include "watcher.h"
#include "define.h"
namespace file
{
void watcher::_main( const WPARAM& Code )
{
OVERLAPPED _OverLapped;
std::vector<UCHAR> _Buffer( 1 << 14, 0 );
DWORD _Size;
FILE_NOTIFY_INFORMATION* _Info;
//初期化
CHANDLE _hDict = CreateFileW(_get()._thInfo[Code].Path.data(), FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr );
if (_hDict == INVALID_HANDLE_VALUE) {
error << L"Invalid Handle Value detected " << _get()._thInfo[Code].Path.data()
<< L"Code = " << Code;
std::lock_guard<std::mutex> _Lock( _get()._mtx );
_get()._thInfo[Code].Status = status::terminate;
return;
}
CHANDLE _hEvent = CreateEventW( nullptr, TRUE, FALSE, nullptr );
auto _Initialize = [&]()
{
ResetEvent( _hEvent );
_OverLapped = { 0 };
_OverLapped.hEvent = _hEvent;
//read only
return ReadDirectoryChangesW( _hDict, _Buffer.data(), _Buffer.size(),
TRUE, _get()._thInfo[Code].Filter, &_Size, &_OverLapped, nullptr ) == TRUE;
};
_Initialize();
while (_get()._thInfo[Code].Status == status::run) {
if (WaitForSingleObject( _hEvent, 500 ) == WAIT_TIMEOUT) {
continue;
}
if (!GetOverlappedResult( _hDict, &_OverLapped, &_Size, FALSE )) {
_Initialize();
continue;
}
if (_Size == 0) {
_Initialize();
continue;
}
_Info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>( _Buffer.data() );
_get()._mtx.lock();
_get()._thInfo[Code].Modified.clear();
while(true) {
_get()._thInfo[Code].Modified.emplace_back( _Info->FileName,
_Info->FileNameLength / sizeof( WCHAR ), _Info->Action );
if (_Info->NextEntryOffset == 0) {
break;
}
_Info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(
reinterpret_cast<unsigned char*>( _Info ) + _Info->NextEntryOffset );
}
_Buffer.clear();
_Initialize();
_get()._mtx.unlock();
SendMessageW( _get()._hWnd, WM_MODIFIED_FILE, Code, 0 );
}
_get()._thInfo[Code].Status = status::terminate;
}
size_t watcher::terminate( const WPARAM& Code )
{
if (_get()._thInfo[Code].Status == status::run) {
_get()._thInfo[Code].Status = status::ready_terminate;
while (_get()._thInfo[Code].Status == status::ready_terminate) {
std::this_thread::sleep_for( std::chrono::microseconds( 400 ) );
}
_get()._thInfo[Code].Thread.detach();
}
_get()._thInfo.erase( Code );
return _get()._thInfo.size();
}
void watcher::assign( const WCHAR* Path, const WPARAM& Param, const DWORD& dwFilter )
{
std::lock_guard<std::mutex> _Lock( _get()._mtx );
if (_get()._thInfo.find( Param ) != _get()._thInfo.cend()) {
//今動いているスレッドを止める
terminate( Param );
}
info << L"新しいパス:" << Path << L" Code: " << Param;
_get()._thInfo.emplace(
std::piecewise_construct, std::forward_as_tuple( Param ),
std::forward_as_tuple( Path, dwFilter, _main, Param ) );
}
const watcher::fileInfo watcher::getModified( const WPARAM& Code, size_t& Where ) const
{
std::lock_guard<std::mutex> _Lock( _get()._mtx );
const auto _OldWhere = Where;
++Where;
if (Where >= _get()._thInfo[Code].Modified.size()) {
Where = -1;
}
if (_OldWhere < _get()._thInfo[Code].Modified.size()) {
return _get()._thInfo[Code].Modified[_OldWhere];
}
//データはすべて返した
return fileInfo();
}
void watcher::sethWnd( HWND hWnd ) noexcept
{
_get()._hWnd = hWnd;
}
void watcher::detach_all()
{
std::lock_guard<std::mutex> _Lock( _get()._mtx );
size_t i = 0;
while (terminate( _get()._thInfo.begin()->first ) > 0) {}
}
}
[/code]
[url=https://dixq.net/forum/viewtopic.php?f=3&t=20304]古い実装[/url]だと、変更されたファイルリストはwatcher::thread_info::ModifiedのものをCodeが一致していればそのまま返していました。(しかも内部で動的にメモリを確保するwstringを使っていました)
今の実装では少し手間がかかりますが、指定された要素を一つずつ返すようにしたところ
**_Pnext** が 0xFFFFFFFFFFFFFFFF でした。
HEAP CORRUPTION DETECTED: after Normal block (#1176) at 0x00000043142539C0. CRT detected that the application wrote to memory after end of heap buffer.
のようなエラーが起こらなくなったのですが、その理由がわからない(上のエラーは確かwstringのデストラクタで起きていました)ので質問した次第です。