Текст в ListView и проч.
1. Это только у меня ListView в режиме LVS_REPORT отображает только примерно 100 символов в ячейке, или у всех так? хранит ведь полностью строчку
2. я так и не понял, в каких случаях ReadDirectoryChanges возвращает несколько записей и почему они все одинаковые?
дальше приведён кусочек кода с комментариями. Он опирается на STL и самописную библиотеку напоминающую упрощённую смесь QT и Swing
Код:
class DirectoryChanges // класс для ReadDirectoryChangesW
{
HANDLE fdirectory;
OVERLAPPED foverlapped;
vector<unsigned char> fbuffer; // буфер 4К для чтения
StringEvent* fevent; // класс с одной пустой виртуальной функцией virtual void put(const wstring&) = 0;
void restart();
static void __stdcall _competed(unsigned long, unsigned long, OVERLAPPED*); // FileIoCompletionRoutine
public:
DirectoryChanges(const wstring& dir);
~DirectoryChanges();
void setEvent(StringEvent* event) {fevent=event;}
};
// каждый раз, при появлении данных, в ListView добавляется строчка
// вследтсвие вызова fevent->put()
DirectoryChanges::DirectoryChanges(const wstring& dir) :
fdirectory(CreateFile ( // кусок прямо из хелпа
dir.c_str(),
FILE_LIST_DIRECTORY, // access (read-write) mode
FILE_SHARE_READ|FILE_SHARE_DELETE, // share mode
0, // security descriptor
OPEN_EXISTING, // how to create
FILE_FLAG_OVERLAPPED|FILE_FLAG_BACKUP_SEMANTICS, // file attributes
0)), // file with attributes to copy
foverlapped (),
fbuffer (4096)
{
if (fdirectory==(HANDLE)-1)
{
ostringstream ss;
ss << "DirectoryChanges::DirectoryChanges error=" << GetLastError();
throw runtime_error(ss.str());
}
foverlapped.hEvent = reinterpret_cast<HANDLE>(this); // т.к. не используется в FileIoCompletionRoutine, микрософт не против
}
DirectoryChanges::~DirectoryChanges()
{
// stopListening();
CloseHandle(fdirectory);
}
void __stdcall DirectoryChanges::_competed(unsigned long code, unsigned long bytes_transfered, OVERLAPPED* ovlp)
{
// это место выполняется "асинхронным вызовом процедуры", в главном цикле, который обрабатывает сообщения,
// функцией MsgWaitForMultipleObjectsEx.
if (code==0)
{
try
{
// разглядываем что прочитали
DirectoryChanges* this1 = reinterpret_cast<DirectoryChanges*>(ovlp->hEvent);
FILE_NOTIFY_INFORMATION* info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&this1->fbuffer[0]);
if (this1->fevent)
{
// 1. для начала посмотрим в виде HEX
wostringstream ss;
ss.fill('0');
ss.flags(ios::hex|ios::fixed);
for (unsigned n=0; n<bytes_transfered; ++n)
{
ss << ' ';
ss.width(2);
ss << unsigned(this1->fbuffer[n]);
}
// выводим полученную строчку в ListView
this1->fevent->put(ss.str());
// теперь разбираем результат, как написано в хелпе
for (;;) // назовём его "цикл №2"
{
// закидываем строчку в ListView
// опытным путём нашёл, что info->FileNameLength измеряется в байтах
this1->fevent->put(wstring(info->FileName, info->FileNameLength>>1));
if (!info->NextEntryOffset) break; // типа если дошли до конца
info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>( // продвигаем указатель
reinterpret_cast<char*>(info) + info->NextEntryOffset);
}
this1->restart(); // когда закончили с буфером, отправляем читать дальше
}
}
// подстраховка, чтобы не порушить стек MsgWaitForMultipleObjectsEx
// потому что у моего компилятора не сильно совместимая система откатов от исключений
catch (exception& e)
{
MessageBoxA(0, e.what(), 0, MB_OK);
}
catch (...)
{
}
}
}
void DirectoryChanges::restart()
{
// читаем всё сподряд
if (!ReadDirectoryChangesW(fdirectory, &fbuffer[0], fbuffer.size(), true,
FILE_NOTIFY_CHANGE_DIR_NAME|
FILE_NOTIFY_CHANGE_FILE_NAME|
FILE_NOTIFY_CHANGE_ATTRIBUTES|
FILE_NOTIFY_CHANGE_SIZE|
FILE_NOTIFY_CHANGE_LAST_WRITE|
FILE_NOTIFY_CHANGE_LAST_ACCESS|
FILE_NOTIFY_CHANGE_CREATION|
FILE_NOTIFY_CHANGE_SECURITY, 0, &foverlapped, _competed))
{
ostringstream ss;
ss << "DirectoryChanges::start error=" << GetLastError();
throw runtime_error(ss.str());
}
}
class Form1 : public Form
{
struct event1 : StringEvent {
Form1* form;
event1(Form1* f) : form(f) {}
void put(const wstring&);
};
ListView view1;
DirectoryChanges directory_changes;
struct event1 event1;
public:
Form1();
//void perform(Message&);
};
void Form1::event1::put(const wstring& s)
{
// void ListView::insertItem(/*позиция*/ control_Id, /*то,что я пожелаю*/ application_Id, /*строчка*/ text, /*ну ещё там кой-чего, инициализируется по умолчанию*/);
form->view1.insertItem(0, 0, s);
// чистый API: читаю, что записалось в ListView (может он урезал строчку?)
vector<wchar_t> buf(4096);
LV_ITEM lvi = LV_ITEM();
lvi.mask = LVIF_TEXT;
lvi.pszText = &buf[0];
lvi.cchTextMax = buf.size();
unsigned size1 = SendMessage(form->view1.handle(), LVM_GETITEMTEXT, 0, long(&lvi));
// дублирую
lvi.iSubItem = 2;
SendMessage(form->view1.handle(), LVM_SETITEMTEXT, 0, long(&lvi));
// сколько же информации я прочитал?
wostringstream ss;
ss << size1;
// ListView::setItemText(unsigned control_id, unsigned control_col_Id, const wstring& str);
form->view1.setItemText(0, 1, ss.str());
// когда это место было раскомментировано, я убедился, что строчка хранится в ListView полностью
// но тогда начинал "глючить" ReadDirectoryChangesW. Он каждый следующий раз говорил, что
// изменится мой project1 Debug.exe, причём количество одинаковых записей увеличивалось на 1.
// то есть первый раз выдалась одна запись
// второй раз 2 одинаковые ("цикл №2" сработал 2 раза)
// и так далее, в бесконечном цикле
//MessageBox(0, &buf[0], 0, MB_OK);
}
Form1::Form1() :
view1 (*this, ListView::style_report),
directory_changes(wstr("d:\\")),
event1 (this)
{
directory_changes.setEvent(&event1);
directory_changes.start();
// ListView::insertColumn(unsigned column_Id, const wstring& str, unsigned width, ...)
view1.insertColumn(0, wstr("Directory"), 5000);
view1.insertColumn(1, wstr("Size"), 100);
view1.insertColumn(2, wstr("Directory 2"), 5000);
setCenter(&view1); // ну типа растягивается теперь на всю форму
}
|