Firstly I'm not familiar with using Async, so the below approach could be completely wrong.
I'm currently decoding multiple images and displaying them in a dialog window.
This works fine, as long as I don't attempt to close the dialog while the decodes are still running.
If I do close the dialog, I get a Debug Assertion Error at return hh;
In my class header I have:
class CmytestDlg: public CDialog
{
protected:
// Generated message map functions
BOOL OnInitDialog() override;
std::future<HBITMAP> futurebmps[12];
};
In the .cpp:
BOOL CmytestDlg::OnInitDialog()
{
int totalJobs = 12;
// Queue up all the items,
for (int i = 0; i < totalJobs; i++)
{
futurebmps[i] = std::async(std::launch::async,
[this](const int i, const std::string& name) {
// This line is just here to simulate a long running image decode
std::this_thread::sleep_for(std::chrono::seconds(10));
HBITMAP hh = nullptr;
PostMessage(8888, i, 0); // Send a message to display the image
return hh; // <<< Debug Assertion Error Here (If I close dialog to soon)
}, i, std::to_string(0));
}
The message to draw the image is handled by OnRefreshAsyncView:
LRESULT CmytestDlg::OnRefreshAsyncView(WPARAM wParam, LPARAM lParam)
{
// For the purpose of a minimal demo, even if this is empty It still casuses the issue.
return 0;
}
I believe that the issue is triggered by the call to PostMessage, if I comment out this line, I no longer get the crash.
How do I safely handle the scenario where the dialog is closing? So I can prevent this crash.
My psychic powers suggest that your ATL/MFC program is asserting on the PostMessage call because the window handle became invalid after after you closed/destroyed it. Somewhere in the middle of that PostMessage call is an ASSERT that the m_hWnd instance is non-null. Hence, the assert is calling attention to a real bug in your code.
PostMessage is is a wrapper for the Win32 API: PostMessage And when you invoke the wrapper function on a different thread, it's likely asserts that m_hWnd (or similar HWND member) is non-null.
Consider writing additional (thread safe) code (with a mutex) that prevents PostMessage from getting called at the same time or after WM_CLOSE/WM_DESTROY is getting processed on the handle.
Something like this:
void CmytestDlg::SafePostMessage(UINT uMsg, WPARAM wp, LPARAM lp)
{
std::unique_lock<std::mutex> lck(_mutex);
if (_isHwndValid)
{
PostMessage(uMsg, wp, lp);
}
}
void CmytestDlg::OnClose(...)
{
std::unique_lock<std::mutex> lck(_mutex);
_isHwndValid = false;
}
Where OnClose is your handler for a WM_CLOSE message and isHwndValid is a bool member of your class that gets set to true when the window is created. And _mutex is a std::mutex.
To extend selbie's answer:
If you want your threads to stop early, you can turn the flag into a std::atomic and check it regularly during your computations, like:
std::mutex _mutex;
std::atomic<bool> _shutdown = false;
void CmytestDlg::LongRunningThing()
{
do_something();
if (_shutdown) return;
do_something_else()
if (_shutdown) return;
...
}
void CmytestDlg::SafePostMessage(UINT uMsg, WPARAM wp, LPARAM lp)
{
std::unique_lock<std::mutex> lck(_mutex);
if (!_shutdown)
{
PostMessage(uMsg, wp, lp);
}
}
void CmytestDlg::OnClose(...)
{
std::unique_lock<std::mutex> lck(_mutex);
_shutdown = false;
}
Related
My application has a separate thread that repeatably performs some check. If the check fails, the UI thread is notified (a MessageBox is displayed that requires user action what to do next).
Unfortunately, I have to use C++03 compiler (Visual Studio 2010 SP1) and boost library usage is prohibited. Therefore, I cannot use <thread>, <atomic>, <chrono>, etc. Thats why I have to use CreateThread, PostMessage and other WinAPI functions.
Here is my UI thread code (simplified). My main window is CMDIFrameWnd (from MFC):
//a struct with all parameters that is needed for a repeatable check
struct RepeatFunctionParameters
{
unsigned int repeatDelayInMilliseconds;
HWND checkIsFailedPostMessageWindowHandler;
UINT checkIsFailedPostMessageMessageId;
HANDLE checkIsPausedMutexHandle;
RepeatFunctionParameters(unsigned int _repeatDelayInMilliseconds, HWND _checkIsFailedPostMessageWindowHandler,
UINT _checkIsFailedPostMessageMessageId, HANDLE _haspSerialCheckIsPausedMutexHandle)
: repeatDelayInMilliseconds(_repeatDelayInMilliseconds), checkIsFailedPostMessageWindowHandler(_checkIsFailedPostMessageWindowHandler),
checkIsFailedPostMessageMessageId(_checkIsFailedPostMessageMessageId), haspSerialCheckIsPausedMutexHandle(_haspSerialCheckIsPausedMutexHandle)
{}
};
----------------------------
//creating a mutex to pause repeatable checks (whe Messagebox is displayed in UI thread)
HANDLE haspSerialCheckIsPausedMutexHandle = CreateMutex(NULL, FALSE, NULL);
//starting a separate thread with a check that repeats every 5000 milliseconds
auto params = new RepeatFunctionParameters(5000, myApp_hWnd, WM_USER_HASP_CHECK_FAILED, haspSerialCheckIsPausedMutexHandle);
CreateThread(NULL, 0, RepeatFunction, params, 0, NULL);
----------------------------
//special message that is sended when check is failed
#define WM_USER_HASP_CHECK_FAILED (WM_USER+0x150)
//mapping message handling function to that message
ON_MESSAGE( WM_USER_HASP_CHECK_FAILED, OnUserHaspCheckFailed)
//message handling function definition
afx_msg LRESULT OnUserHaspCheckFailed(WPARAM wParam, LPARAM lParam);
//message handling function body
LRESULT CMainWnd::OnUserHaspCheckFailed(WPARAM wParam, LPARAM lParam)
{
//capturing a mutex that signals to pause repeatable checks
WaitForSingleObject(haspSerialCheckIsPausedMutexHandle, INFINITE);
//show a messagebox that requires user action what to do next
if (::MessageBox(myApp_hWnd, ("Check is failed! Retry or cancel?").c_str(),
myApp_name, MB_RETRYCANCEL | MB_ICONERROR | MB_SYSTEMMODAL) == IDCANCEL)
//closing main windows if user clicks Cancel
pWnd->SendMessage(WM_CLOSE, 0x00010000, 0);
//releasing a mutex that signals to pause repeatable checks
ReleaseMutex(haspSerialCheckIsPausedMutexHandle);
return 0;
}
//WM_CLOSE handling function body
LRESULT CMainWnd::OnClose( WPARAM wParam, LPARAM lParam)
{
----------------------------
if( haspSerialCheckIsPausedMutexHandle != NULL)
CloseHandle( haspSerialCheckIsPausedMutexHandle);
----------------------------
CMDIFrameWnd::OnClose();
return NULL;
}
Here is my separate thread with repeatable check code (simplified):
DWORD WINAPI RepeatFunction(LPVOID parameters)
{
//getting parameters struct from a pointer
auto temp = static_cast<RepeatFunctionParameters*>(parameters);
//make a struct local copy (Further, all work goes only with it, regardless of the
state of the object, the pointer to which came as a function parameter)
auto params = *temp;
//deleting the structure, the pointer to which came as a function parameter
delete temp;
//repeatable check
while (true)
{
//checking a mutex that signals to pause repeatable checks. if it is free
//then there is no messagebox in UI thread and we can perform a check.
//if it is captured - wait until user clicks some button in that messagebox
WaitForSingleObject(params.haspSerialCheckIsPausedMutexHandle, INFINITE);
//and releasing it immediately
ReleaseMutex(params.haspSerialCheckIsPausedMutexHandle);
auto startMilliseconds = GetTickCount();
//performing a check
BOOL success = PerformACheck();
unsigned long defaultSleepDelay = 1000;
//if PerformACheck() will last longer than params.repeatDelayInMilliseconds,
//then check will be repeated after 1000 milliseconds, otherwise -
//after params.repeatDelayInMilliseconds minus PerformACheck() call time
auto endMilliseconds = GetTickCount();
if ((endMilliseconds - startMilliseconds) < params.repeatDelayInMilliseconds)
sleepDelay = params.repeatDelayInMilliseconds - (endMilliseconds - startMilliseconds);
//if check is failed
if (!success)
{
//sending a message with an identifier params.checkIsFailedPostMessageMessageId
//to a HWND params.checkIsFailedPostMessageWindowHandler so in it's
//handling function a messagebox with will be displayed and a mutex
//params.haspSerialCheckCanRunMutexHandle will be captured until
//user click some button in that messagebox
PostMessage(params.checkIsFailedPostMessageWindowHandler, params.checkIsFailedPostMessageMessageId, 0, 0);
//if check is failed then next check always repeats after 1000 milliseconds
sleepDelay = 1000;
}
Sleep(sleepDelay);
}
}
The result is that the main window becomes unresponsive after some time. It looks like my code has some logical mistake, or a memory leak.
I'm a newbie to C++ (and especially to outdated standards).
I have a CEdit derived control that displays the string "N/A" when the undelying data is null. I recently added code to empty the control(SetWindowText("");) when it gains focus and set if back to "N/A"(SetWindowText("N/A")) when the focus is lost if the user left the control empty.
The only problem is that setting the window text to "" or "N/A" triggers EN_CHANGE, so my dialog thinks that the data has changed.
How can I avoid EN_CHANGE from being fired when calling SetWindowText (WM_SETTEXT)?
NOTES
-I know I can set the edit control to Multiline=TRUE but that's not accpectable for me.
-My application is MBCS so I can't use SetCueBanner
-I want an elegant solution. Setting the parent window to NULL temporarily is not an elegant solution.
EDIT:
-I want the solution to be in my custom control, not in each dialog
Thanks
The way I've done it before (last time, like 20 minutes ago; in fact I was thinking about asking the same question), is by setting a flag. When I'm about to set the text programatically, I set the flag, and I check it in the EN_CHANGE handler:
void CMyDialog::MyFunction()
{
setEditTextProgramatically = true;
c_Edit.SetWindowText(_T("Whatever"));
setEditTextProgramatically = false;
}
void CMyDialog::OnEnChangeEdit()
{
if (!setEditTextProgramatically)
{
// Do whatever you need to do
}
}
I know it's not the most elegant solution, but it works, at least for me.
I've always wondered why MFC doesn't provide a way to distinguish user input from changes from code, but that's the way it is.
I finally found a suitable solution to my problem.
First, I added a flag to my derived control's header file and I initialized it to false in the constructor
bool m_bNoEnChange;
I overrode the OnChildNotify in my derived control's header file and in the implementation, I checked for the WM_COMMAND message with the EN_CHANGE parameter. I then returned TRUE to prevent the message from being sent to the parent(dialog/page)
virtual BOOL OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult);
BOOL CADEdit::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult)
{
if(message == WM_COMMAND && HIWORD(wParam) == EN_CHANGE)
{
//If the flag is set, don't send the message to the parent window
if(m_bNoEnChange)
return TRUE;
}
return CEdit::OnChildNotify(message, wParam, lParam, pLResult);
}
Finally, when the control gains and loses focus, I wrapped the problematic SetWindowText with my flag
m_bNoEnChange = true;
SetWindowText(_T(""));
m_bNoEnChange = false;
This solution is the best in my case because I don't have to modify each dialog.
You could disable (EnableWindow(FALSE) or send WM_ENABLE with param FALSE) the control prior to sending WM_SETTEXT then enabling it afterwards. That should prevent the EN_CHANGE
There is probably some more elegant method :p
The below code uses a C++ 11 feature, but that can easily be changed.
HEADER
// CEditOptionalNotify.h
//
// CEdit derived class allowing the control's text value to be
// set without (optionally) causing EN_CHANGE processing.
//
#pragma once
class CEditOptionalNotify : public CEdit
{
//DECLARE_DYNAMIC(CEditOptionalNotify)
// Enable use of RUNTIME_CLASS macro and CObject::IsKindOf()
public:
CEditOptionalNotify();
virtual ~CEditOptionalNotify();
enum class PerformOnChangeProcessing { No, Yes };
void vSetText(const TCHAR* pText, PerformOnChangeProcessing e);
protected:
afx_msg BOOL bConsiderEnChangeAsHandled();
bool m_bChangeNotificationsEnabled;
DECLARE_MESSAGE_MAP()
};
IMPLEMENTATION
// EditOptionalNotify.cpp : implementation file
//
#include "stdafx.h"
#include <EditOptionalNotify.h>
//IMPLEMENT_DYNAMIC(CEditOptionalNotify, CEdit)
CEditOptionalNotify::CEditOptionalNotify() :
m_bChangeNotificationsEnabled(true)
{
}
CEditOptionalNotify::~CEditOptionalNotify()
{
}
BEGIN_MESSAGE_MAP(CEditOptionalNotify, CEdit)
ON_CONTROL_REFLECT_EX(EN_CHANGE, bConsiderEnChangeAsHandled)
END_MESSAGE_MAP()
BOOL CEditOptionalNotify::bConsiderEnChangeAsHandled()
{
return (m_bChangeNotificationsEnabled ? FALSE : TRUE);
}
void CEditOptionalNotify::vSetText(const TCHAR* pText, PerformOnChangeProcessing e)
{
bool bChangeNotificationsDesired = (PerformOnChangeProcessing::No == e ? false : true);
if (bChangeNotificationsDesired != m_bChangeNotificationsEnabled)
{
m_bChangeNotificationsEnabled = bChangeNotificationsDesired;
CEdit::SetWindowText(pText);
m_bChangeNotificationsEnabled = (bChangeNotificationsDesired ? false : true);
}
else
CEdit::SetWindowText(pText);
}
LRESULT CMainDlg::OnEnUpdateEditID(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
//using static variable
static bool isCodeChangeText = false;
if(isCodeChangeText)
return 0;
……//Deal Window Text
if(old == new)
return 0;
int nSel = m_editPID.GetSel();//record cursor pos
isCodeChangeText = true;
m_editID.SetWindowText(new);
m_editID.SetSel(nSel);
isCodeChangeText = false;
return 0;
}
In case somebody else finds this discussion...
As Steven wrote UpdateData does not cause an EN_CHANGE being sent.
Under the hood MFC calls AfxSetWindowText with which one can specify one hwnd.
I am wondering how to catch the fact that a view (CView in a CMDIChildWnd frame) has been resized, and that the user just released the left mouse button.
None of OnSize, OnSizing and OnLButtonUp work.
I have tried the solution in http://www.codeguru.com/forum/showthread.php?t=59476 and it doesn't work.
I am working on VC2010 with W7.
Thanks in advance.
Try WM_NCLBUTTONUP. I don't know if a view can be resized other than by the mouse, but if it can you probably also want to respond to WM_EXITSIZEMOVE as in the link you gave.
I recently needed to do this. I used a combination of OnSysMessage to capture the SC_SIZE and SC_MOVE event and WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE within the CMDIChildWnd derived class. Within the OnSysMessage handler I set a public variable for IsSizing. I can then check the variable within the OnEnterSizeMove function and act accordingly. Within the OnExitSizeMove function, it resets the variable to FALSE.
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CChildFrame)
ON_WM_SYSCOMMAND()
ON_MESSAGE(WM_ENTERSIZEMOVE, OnEnterSizeMove)
ON_MESSAGE(WM_EXITSIZEMOVE, OnExitSizeMove)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
And for the handlers:
void CChildFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
switch(nID&0xfff0)
{
case SC_SIZE:
m_bIsSizing = TRUE;
break;
case SC_MOVE:
m_bIsSizing = FALSE;
break;
}
CMDIChildWnd::OnSysCommand(nID, lParam);
}
LRESULT CChildFrame::OnEnterSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnEnterSizeMove\n");
}
return 0; //don't flag we processed the message
}
LRESULT CChildFrame::OnExitSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnExitSizeMove\n");
m_bIsSizing = FALSE; // set to false before calling OnSizing
CRect dlgRect;
pView->GetClientRect(dlgRect);
pView->InvalidateRect(NULL, FALSE);
pView->SendMessage(WM_SIZE, WPARAM(SIZE_RESTORED), MAKELONG(dlgRect.Width(), dlgRect.Height()));
}
return 0; //don't flag we processed the message
}
Then within your View code, you can check if the Frame is sizing within your OnSize handler.
void CMyView::OnSize(UINT nType, int cx, int cy)
{
CChildFrame* pFrame=(CChildFrame*)GetParentFrame();
if(!pFrame->m_bIsSizing) {
CWaitCursor cur;
//DO UPDATE CODE
}
}
I have a little console application that has an embedded v8 engine, and I would like to add a hook to register key events. This all worked before when I was using Qt and QtScript, but I am porting it all over to straight C++ in VC++ 2008. The application compiles and runs, but the hook is never called, here is the relevant code:
In main()
HWND hwndC = GetConsoleWindow() ;
HINSTANCE hInst = (HINSTANCE)GetWindowLong( hwndC, GWL_HINSTANCE );
if (SetWindowsHookEx(WH_KEYBOARD_LL, HookProc, hInst, NULL) == 0) {
printf("Failed to set hook\n");
} else {
printf("Hook established\n");
}
g->RunScript(argc,argv);
And the proc:
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
printf("HookProc called\n");
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) (lParam);
if (wParam == WM_KEYDOWN) {
keyDown(p,g);
} else if (wParam == WM_KEYUP) {
keyUp(p,g);
}
fflush(stdout);
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
This is essentially an expansion on shell.cc from the v8 sample code. I wonder if it is somehow blocking? I admit to not really knowing what I am doing here, just playing around and learning but this one has me stumped.
Inside of keyDown say, I have something like this:
v8::Handle<v8::String> callback_name = v8::String::New("onKeyDown");
v8::Handle<v8::Value> callback_val = g->_context->Global()->Get(callback_name);
if (!callback_val->IsFunction()) {
printf("No onKeyDown handler found\n");
return;
}
v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(callback_val);
const int argc = 1;
v8::Handle<v8::Value> argv[argc] = { v8::Int32::New(char(p->vkCode)) };
printf("Calling onKeyDown\n");
v8::Handle<v8::Value> result = callback->Call(g->_context->Global(), argc, argv);
Some of this may actually not work in the end, but it just never gets called, when I run the program, and define: onKeyDown = function(key) {...}; I can see that onKeyDown is working just fine, I can use all of my bound c++ method etc from JS, so this thing is just driving me batty.
Any help, maybe pointers to some educational materials would be much appreciated.
Just to be clear, this function in c: LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam) is never getting called, or never seeing a printf, and the output at the start says: Hook established, so windows is reporting the hook is established.
/Jason
A low-level hook, like WH_KEYBOARD_LL requires that your application pumps a message loop. That's the only way that Windows can break into your thread and make the call to the HookProc callback you registered.
A console mode app doesn't pump a message loop like regular Windows GUI apps do. Judging from your snippet, it isn't going to be easy to add one either. You'll need to create a thread.
Maybe this function will be of help to you?
GetAsyncKeyState
In general I use exceptions to deal with errors, however the problem I have here is that the error applies to a different thread than the one that caused it.
Basicly the window has its own thread, and the direct3d device must be created and reset by the same thread that created the window. However creating the device may fail, so I need to throw an exception in the thread that was trying to create the instance, not the window code
The callback function:
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;
The code that requests the window thread to create the device
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;
}
So basically I need the exceptions being thrown by the create() method to be caught by the exception handelers where the Graphics object was created which is another thread.
Perhaps you can wrap the call inside another function, using Boost.Exception.
The following code does not work though, but you'll probably get the idea.
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();
}
I see you call PostMessage from Callback then actively wait using a loop and Sleep. Replace PostMessage by SendMessage, which will wait for the message processing to complete. That way the processor-intensive loop is gone. Moreover, you can use the return code of the SendMessage call (from the WM_CALLBACK arm of HookProc) to pass error information. Either use simple numeric codes (e.g. non-zero means error) or pass exception objects (in this case you'll have to allocate then on the heap from your exception handler, you can't pass the parameter directly as it is stack-allocated).
Last, using the synchronous SendMessage instead of the async PostMessage means you no longer have to build an intermediary callH function object from the HookProc, as you can pass the stack allocated call parameter instead. This results in much simpler code.
For example:
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;
And:
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;
}
A simple solution would be to catch the exception in the thread that thrown it (or check the parameters before calling the function to decide if an exception would be thrown) and then pass the exception via a static variable tot the main thread where the main thread can throw it.
I would add a check after the WaitForSingleObject to discover if an exception is waiting to be thrown and in case it does - throw it.
Well, the answer is that you can't. A viable alternative would be to do only necessary work that can't fail, like extractiong value from the parameters, in your callback function, and call the create() function in your main thread.
Also, you shouldn't busy wait for the creation, use a Semaphore, or a Mutex & Event etc.
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);
}
See http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html.