В общем, я использую исключения для работы с ошибками, однако проблема, с которой я столкнулся, заключается в том, что ошибка относится к другому потоку, чем тот, который ее вызвал.
В основном у окна есть собственный поток, и устройство direct3d должно быть создано и сброшено тем же потоком, который создал окно. Однако создание устройства может завершиться неудачно, поэтому мне нужно вызвать исключение в потоке, который пытался создать экземпляр, а не в коде окна.
Функция обратного вызова:
void Callback(HWND hwnd, boost::function<void(HWND,LPARAM)> call, LPARAM lParam)
{
//Make our stack allocated function object into a heap allocated one
boost::function<void(HWND,LPARAM)> *callH = new boost::function<void(HWND,LPARAM)>(call);
//send a message with a pointer to our function object in the WPARAM
PostMessage(hwnd, WM_CALLBACK, (unsigned)callH, lParam);
}
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
//check for our custom message
if (msg == WM_CALLBACK)
{
//retreive the function pointer from the WPARAM
boost::function<void(HWND,LPARAM)> *callH = (boost::function<void(HWND,LPARAM)>*)wParam;
//call it
(*callH)(hwnd,lParam);
//delete our heap allocated function object
delete callH;
return 0;
}
else
//if there was nothing relevant to us, call the old message procedure
return CallWindowProc(hooked[hwnd], hwnd, msg, wParam, lParam);
}
//std::map<HWND, WNDPROC> hooked;
Код, который запрашивает оконный поток для создания устройства.
Graphics::Graphics(IWindow *_window, Size2<unsigned> _size)
:lost(false), reset(false), refCnt(0), backCol(0xFF000000),
started(false), exited(false),
window(_window), size(_size)
{
window->AddRef();
HWND hwnd = *((HWND*)window->GetHandle());
CallbackHook(hwnd);
Callback(hwnd, boost::bind(&Graphics::create, this), 0);
while(!started)Sleep(100);
}
void Graphics::create()
{
...code that may throw various exceptions
started = true;
}
Поэтому в основном мне нужно, чтобы исключения, генерируемые методом create (), перехватывались обработчиками исключений, где был создан объект Graphics, который является другим потоком.





Что ж, ответ в том, что вы не можете. Жизнеспособной альтернативой было бы выполнение только необходимой работы, которая не может потерпеть неудачу, например, извлечения значения g из параметров в вашей функции обратного вызова и вызова функции create () в вашем основном потоке.
Кроме того, вам не нужно ждать создания, использовать семафор, мьютекс и событие и т. д.
Graphics::Graphics(IWindow *_window, Size2<unsigned> _size)
:lost(false), reset(false), refCnt(0), backCol(0xFF000000),
started(false), exited(false),
window(_window), size(_size)
{
window->AddRef();
HWND hwnd = *((HWND*)window->GetHandle());
semaphore = CreateSemaphore(NULL, 0, 1, NULL);
if (semaphore == NULL) throw whatever;
CallbackHook(hwnd);
Callback(hwnd, boost::bind(&Graphics::create, this), 0);
WaitForSingleObject(semaphore);
create();
CloseHandle(semaphore); /* you won't need it */
}
void Graphics::create()
{
...code that may throw various exceptions
}
void Graphics::create_callback()
{
ReleaseSemaphore(semaphore, 1, NULL);
}
Ах так. Вы должны сохранить свое исключение в какой-либо переменной и затем проверить переменную в другом потоке. Посмотреть ответ далле
Простым решением было бы перехватить исключение в потоке, который его вызвал (или проверить параметры перед вызовом функции, чтобы решить, будет ли выбрано исключение), а затем передать исключение через статическую переменную в основной поток, где основной поток может бросить.
Я бы добавил проверку после WaitForSingleObject, чтобы определить, ожидает ли исключение, чтобы быть сгенерировано, и в случае его возникновения - выбросить.
Я вижу, вы вызываете PostMessage из Callback, а затем активно ждете, используя петлю и Sleep. Замените PostMessage на SendMessage, который будет ждать завершения обработки сообщения. Таким образом, цикл, требующий интенсивной работы процессора, исчезнет. Более того, вы можете использовать код возврата вызова SendMessage (из ветви WM_CALLBACKHookProc) для передачи информации об ошибке. Либо используйте простые числовые коды (например, ненулевое значение означает ошибку), либо передайте объекты исключений (в этом случае вам придется выделить затем в куче из вашего обработчика исключений, вы не можете передать параметр напрямую, поскольку он выделен стеком ).
Наконец, использование синхронного SendMessage вместо асинхронного PostMessage означает, что вам больше не нужно создавать промежуточный функциональный объект callH из HookProc, поскольку вместо этого вы можете передать выделенный стеком параметр call. Это приводит к гораздо более простому коду.
Например:
void Callback(HWND hwnd, boost::function<void(HWND,LPARAM)> call, LPARAM lParam)
{
//send a message with a pointer to our function object in the WPARAM
if (SendMessage(hwnd, WM_CALLBACK, &call, lParam))
{
// error handling.
}
}
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
//check for our custom message
if (msg == WM_CALLBACK)
{
//retreive the function pointer from the WPARAM
boost::function<void(HWND,LPARAM)> *callH = (boost::function<void(HWND,LPARAM)>*)wParam;
//call it, catching exceptions in the process.
try
{
(*callH)(hwnd,lParam);
return 0;
}
catch (...)
{
return 1;
}
}
else
//if there was nothing relevant to us, call the old message procedure
return CallWindowProc(hooked[hwnd], hwnd, msg, wParam, lParam);
}
//std::map<HWND, WNDPROC> hooked;
А также:
Graphics::Graphics(IWindow *_window, Size2<unsigned> _size)
:lost(false), reset(false), refCnt(0), backCol(0xFF000000),
started(false), exited(false),
window(_window), size(_size)
{
window->AddRef();
HWND hwnd = *((HWND*)window->GetHandle());
CallbackHook(hwnd);
Callback(hwnd, boost::bind(&Graphics::create, this), 0);
// No need to wait, as SendMessage is synchronous.
}
void Graphics::create()
{
...code that may throw various exceptions
started = true;
}
Возможно, вы можете заключить вызов в другую функцию, используя Boost.Exception.
Следующий код не работает, но вы, вероятно, поняли идею.
class Context
{
public:
Context(HWND hwnd, const boost::function<void(HWND,LPARAM)>& f, LPARAM lParam)
{
// TODO: reroute call through Wrapper function.
}
void wait()
{
mutex::scoped_lock l(m);
while (!finished)
{
c.wait(l);
}
if (ex)
rethrow_exception(ex);
}
private:
void Wrapper()
{
try
{
f(/*params*/);
}
catch (...)
{
ex = current_exception();
}
mutex::scoped_lock l(m);
finished = true;
c.notify_all();
}
boost::function<void(HWND,LPARAM)> f;
exception_ptr ex;
bool finished;
mutex m;
condition c;
};
void Callback(HWND hwnd, const boost::function<void(HWND,LPARAM)>& f, LPARAM lParam)
{
Context ctx(hwnd, f, lParam);
ctx.wait();
}
См. http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html.
Ответы должны быть написаны таким образом, чтобы сделать их невосприимчивыми к гниению ссылок, т.е.они должны содержать фактические ответы, а не перенаправления.
Это невозможно, создание повторного запуска устройства Direct3D ДОЛЖНО выполняться с тем же потоком, который создал окно, и в 99% случаев это те, которые терпят неудачу.