Как и в заголовке, я хочу добавить/удалить элементы класса, полученного из класса WTL CListViewCtrl, из рабочих потоков, но всегда получаю «Необработанное исключение: нарушение прав доступа к чтению».Обновление CListViewCtrl из рабочих потоков в WTL и C++
Я попытался Win32 API PostMessage и SendMessage но когда рабочий поток коснется HWND из CListViewCtrl я получаю такое же исключение.
// CListCtrl member function, calling from worker thread
HWND GetHwnd()
{
return hwndListCtrl; // exception here
}
Я это SafeQueue, но когда рабочий поток затрагивает мьютекс или очередь, то исключение попробовал еще раз.
// SafeQueue is member variable in CListViewCtrl, created in GUI thread
SafeQueue<T> m_SafeQueue;
. . .
// member function in SafeQueue class, calling from worker thread
void enqueue(T t)
{
std::lock_guard<std::mutex> lock(m); // exception here
q->push(t);
}
Я попытался создать мьютекс и очередь с новый и HeapAlloc/LocalAlloc но тем же исключением снова.
Я пробовал Win32 API CreateMutex, но не повезло, это же исключение при доступе к дескриптору мьютекса из рабочего потока.
Он отлично работает, когда я добавляю элементы из потока графического интерфейса.
Единственный способ это работает из рабочих потоков, если я объявляю HWND или мьютекс и очередь в статической/глобальной, но я хотел бы избежать этого, так как я хочу использовать более одного экземпляра из этого ListControl и я предпочитаю любой более изящный способ, чем глобальная переменная.
Я хочу сделать этот класс многоразовым, так как я хочу использовать его много раз с несколькими модификациями (больше столбцов, разных цветов).
Я ценю любую помощь и идею, как я могу это сделать.
Окружающая среда: VS2015 сообщество, WTL/C++ и Win10 Pro 64bit
Я нашел проблему, которая вызывает нарушение прав доступа: Я объявил ThreadProc функции обратного вызова в качестве функциистатического члена в классе CListViewCtrl.
// DO NOT USE
// in CListViewCtrl
**static** DWORD WINAPI ThreadProc(LPVOID lp)
{
. . .
}
LRESULT OnStartWorkerThread(WORD /*wNotifyCode*/, WORD /*wID*/, HWND . ..)
{
DWORD dw;
::CreateThread(NULL, 0, this->ThreadProc, NULL, 0, &dw);
}
рабочий раствор:
class CListViewCtrl ...
{
// thread-safe queue to store listctrl items to be added later in GUI thread
SafeQueue<CListCtrlItem<nCols> > m_SafeQueue;
// thread ID of the thread in which listctrl was created, saved in OnCreate
DWORD m_dwGuiTid;
// . . .
Проверить, если SafeAddItem функция вызывается из GUI или любых других потоки
BOOL InvokeRequired()
{
if (m_GuiTid == ::GetCurrentThreadId())
return false;
return true;
}
// ...
SafeAddItem функции-члена может быть вызвана из графического интерфейса и рабочего нити
void SafeAddItem(CListCtrlItem<nCols> item)
{
if (!InvokeRequired())
{
// we are in GUI thread so just add listctrl item "normal" way
AddItem(item);
return;
}
// we are in other thread so enqueue listctrl item and post a message to GUI
m_SafeQueue.Enqueue(item);
::PostMessage(m_hWnd, WM_ADD_ITEM, 0, 0);
}
// . . .
Обработчик сообщений из PostMessage, мы в GUI потоке
LRESULT OnAddItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
{
CListCtrlItem<nCols> item;
while (!m_SafeQueue.Empty())
{
item = m_SafeQueue.Dequeue();
// we are in GUI thread so we can add list ctrl items normal way
AddItem(item);
}
return 1;
}
// ...
}
И теперь мы можем добавлять элементы ListCtrl из любых потоков таким образом.Я прохожу этот указатель ThreadProc в _beginthreadex
m_ListCtrl.SafeAddItem(item);
Благодарим вас за ответ.Я знаю, что мне не нужно обращаться к объектам GUI из другого потока, но я не мог получить доступ даже к дескриптору мьютекса, что важно для синхронизации между потоками. И доступ к элементам данных из другого потока не должен приводить к исключению нарушения прав доступа только по странному поведению. У меня возникло ощущение, что это странная проблема, и похоже, что я нашел проблему: я объявлял функцию обратного вызова рабочего потока как статическую функцию-член в CListViewCtrl. А статическая функция-член может получить доступ только к статическому элементу данных. – hkhk
Спасибо за разъяснение. Из вашего описания - «Я хочу добавить/удалить элементы в класс, полученный из класса WTL CListViewCtrl из рабочих потоков ... Я пробовал Win32 API PostMessage и SendMessage, но как только рабочий поток касается HWND CListViewCtrl, я получаю то же исключение. Это нормально работает, когда я добавляю элементы из потока графического интерфейса. " Похоже, вы пытались перейти к ListView непосредственно из рабочего потока. –
Извините, если это было непонимание. Сначала я попытался использовать PostMessage, но его первым параметром является целевое окно HWND, но я не смог прочитать/получить этот HWND из рабочего потока, поскольку это вызвало исключение нарушения прав доступа. Это звучало очень странно для меня, поскольку это не должно вызывать исключения. Теперь он работает так, как должен, с нечленой нестатической функцией ThreadProc. Спасибо – hkhk