Поток win32 неожиданно заканчивается в ActiveX (C++)
Я занимаюсь разработкой видео проигрывателя ActiveX. Это внутрипроцессный компонент в DLL. Я использую Visual Studio 2010.
Мне нужно, чтобы он имел отдельный поток, который запускался бы один раз, когда компонент загружался, создавал объект Direct3D9 и устройство Direct3D9, затем останавливался на выгрузке компонента и уничтожал эти объекты. Пока компонент работает, я бы хотел, чтобы этот поток периодически вызывал TestCooperativeLevel
и перезагрузите устройство D3D, если необходимо.
Я делаю это, поскольку клиентское приложение может создать несколько экземпляров моего проигрывателя, но настоятельно рекомендуется иметь только один экземпляр объекта и устройства D3D9.
Я объявил класс со статическими методами и членами, чей конструктор вызывает _beginthreadex()
и начинает поток.
Вот выдержки из кода (С ОШИБКАМИ).
// .h
class D3DManager {
static mutex d3; // mutex is my own class, just a wrapper around CriticalSection
static LPDIRECT3D9 g_d3d;
static LPDIRECT3DDEVICE9 g_d3ddev;
static D3DPRESENT_PARAMETERS g_d3dpp;
static int g_d3d_counter;
static HANDLE hthread;
static HANDLE exitev;
static bool exit_flag;
static mutex exit_mutex;
public:
D3DManager();
~D3DManager();
static unsigned int __stdcall thread(void *);
static void stop(void) {
exit_mutex.lock();
exit_flag = true;
SetEvent(exitev);
exit_mutex.unlock(); }
static bool exit_signal(void) {
exit_mutex.lock();
bool result = exit_flag;
exit_mutex.unlock();
return exit_flag; }
static void CreateD3DDevice(LPDIRECT3D9& d3dobj, LPDIRECT3DDEVICE9& d3ddev);
static void DestroyD3DDevice(void);
static void GetSwapChain(HWND hwnd, LPDIRECT3DSWAPCHAIN9& chain);
static void release_d3d(void);
static void LockDevice(void) { d3.lock(); };
static void UnlockDevice(void) { d3.unlock(); };
};
//.cpp
mutex D3DManager::d3;
LPDIRECT3D9 D3DManager::g_d3d = NULL;
LPDIRECT3DDEVICE9 D3DManager::g_d3ddev = NULL;
D3DPRESENT_PARAMETERS D3DManager::g_d3dpp;
int D3DManager::g_d3d_counter = 0;
HANDLE D3DManager::hthread;
HANDLE D3DManager::exitev;
bool D3DManager::exit_flag = false;
mutex D3DManager::exit_mutex;
// this variable will be single and shared by all activeX instances
static D3DManager d3dm;
D3DManager::D3DManager()
{
exitev = CreateEvent(NULL, true, false, NULL);
hthread = (HANDLE)_beginthreadex(NULL, 0, thread, NULL, 0, NULL);
_OutputDebugString("D3DManager: thread created handle %x\n", hthread); // my wrapper around OutputDebugString
}
D3DManager::~D3DManager()
{
stop();
HRESULT hr = WaitForSingleObject(hthread, 1000);
if (hr == WAIT_ABANDONED) {
TerminateThread(hthread, 0);
release_d3d();
}
CloseHandle(exitev);
}
unsigned int __stdcall D3DManager::thread(void *)
{
create_d3d9();
while(!exit_signal()) {
WaitForSignleObject(exitev, 500);
d3.lock();
HRESULT hr = g_d3ddev->TestCooperativeLevel();
switch(hr) {
case S_OK:
break;
case D3DERR_DEVICENOTRESET :
// Fill DISPLAYPARAMETERS
g_d3ddev->Reset();
break;
default:
break;
}
d3.unlock();
}
///////// This text is never seen
OutputDebugString("D3dManagert exit from while loop\n");
////////
release_d3d();
_endthreadex(0);
return 0;
}
Мой компонент встроен в форму WindowsForms, написанную на C#.
Проблема в том, что когда я закрываю форму, поток прерывается внутри цикла while и никогда не попадает в код после него. Я никогда не видел текст из OutputDebugString
, release_d3d()
также никогда не вызывается, и я вижу много сообщений от d3d отладки об утечке памяти. Если я установлю точку останова, она никогда не будет достигнута.
Все, что я вижу, это сообщение:
The thread 'Win32 Thread' (0x1044) has exited with code 0 (0x0)
Когда я устанавливаю точку останова в деструкторе, я получаю его, но после сообщений об утечке видеопамяти.
Я также включил разрыв отладки в C++ Exceptions и Win32 Exception в Studio, но ни один из них не сработал.
Обновить. Прочитал в MSDN, что все потоки завершаются, когда любой из них вызывает exit
, _exit
, или же abort
или же ExitProcess
и попробовал сттинг в конструкторе atexit
обработчик:
D3DManager::D3DManager()
{
exitev = CreateEvent(NULL, true, false, NULL);
hthread = (HANDLE)_beginthreadex(NULL, 0, thread, NULL, 0, NULL);
_OutputDebugString("D3DManager: thread created handle %x\n", hthread);
atexit(&release_d3d);
}
Все еще не повезло. release_d3d
вызывается после получения сообщений об утечке видеопамяти. Кроме того, у меня есть ошибка исключения.
Обновление 2.
Вот отредактированный код
// .h
class D3DManager {
static mutex d3; // mutex is my own class, just a wrapper around CriticalSection
static LPDIRECT3D9 g_d3d;
static LPDIRECT3DDEVICE9 g_d3ddev;
static D3DPRESENT_PARAMETERS g_d3dpp;
static int g_d3d_counter;
static HANDLE hthread;
static HANDLE exitev;
public:
D3DManager();
~D3DManager();
static unsigned int __stdcall thread(void *);
static void stop(void) { SetEvent(exitev); }
static void CreateD3DDevice(LPDIRECT3D9& d3dobj, LPDIRECT3DDEVICE9& d3ddev);
static void DestroyD3DDevice(void);
static void GetSwapChain(HWND hwnd, LPDIRECT3DSWAPCHAIN9& chain);
static void release_d3d(void);
static void LockDevice(void) { d3.lock(); };
static void UnlockDevice(void) { d3.unlock(); };
};
//.cpp
mutex D3DManager::d3;
LPDIRECT3D9 D3DManager::g_d3d = NULL;
LPDIRECT3DDEVICE9 D3DManager::g_d3ddev = NULL;
D3DPRESENT_PARAMETERS D3DManager::g_d3dpp;
int D3DManager::g_d3d_counter = 0;
HANDLE D3DManager::hthread;
HANDLE D3DManager::exitev;
// this variable will be single and shared by all activeX instances
static D3DManager d3dm;
D3DManager::D3DManager()
{
exitev = CreateEvent(NULL, true, false, NULL);
hthread = (HANDLE)_beginthreadex(NULL, 0, thread, NULL, 0, NULL);
_OutputDebugString("D3DManager: thread created handle %x\n", hthread); // my wrapper around OutputDebugString
}
D3DManager::~D3DManager()
{
stop();
HRESULT hr = WaitForSingleObject(hthread, 1000);
if (hr == WAIT_TIMEOUT) {
TerminateThread(hthread, 0);
release_d3d();
}
CloseHandle(exitev);
}
unsigned int __stdcall D3DManager::thread(void *)
{
create_d3d9();
while(WAIT_TIMEOUT == WaitForSingleObject(exitev, 500)) {
d3.lock();
HRESULT hr = g_d3ddev->TestCooperativeLevel();
switch(hr) {
case S_OK:
break;
case D3DERR_DEVICENOTRESET :
// Fill DISPLAYPARAMETERS
g_d3ddev->Reset();
break;
default:
break;
}
d3.unlock();
}
///////// This text is never seen
OutputDebugString("D3dManagert exit from while loop\n");
////////
release_d3d();
_endthreadex(0);
return 0;
}
2 ответа
Зачем ждать объект остановки, а затем, если получен сигнал, все еще выполнять тело кода? Пытаться
while(WAIT_TIMEOUT==WaitForSingleObject(exitev, 500){
..
}
Кроме того, я не уверен, для чего все это!exit_signal() и exit_mutex для? Зачем вам нужен мьютекс или, если на то пошло, логическое значение выхода, когда у вас уже есть событие для подачи сигнала? Есть ли еще код, который сигнализирует о событии по какой-то другой причине, кроме остановки? Я заметил, что вы опечатали WFSO - "WaitForSignleObject", поэтому вы не опубликовали свой реальный код.
Не подавать в суд на 'if (hr == WAIT_ABANDONED)'. Я почти никогда не жду, когда завершатся потоки, и поэтому я не знаю наверняка, если / почему этот возврат сделан при ожидании на дескрипторе потока.
Я вижу, что ваш exit_signal() копирует значение, но не возвращает его. Могут быть шансы, что переменная exit_flag будет изменена после того, как вы выйдете из синхронизированного блока кода, и ваш exit_signal() вернет false.