Multithreading 在64位windows上重复34个周期后,从工作线程内创建进度控制时,CWnd::DefWindowProc中的Mfc应用程序崩溃

Multithreading 在64位windows上重复34个周期后,从工作线程内创建进度控制时,CWnd::DefWindowProc中的Mfc应用程序崩溃,multithreading,visual-c++,mfc,cwnd,Multithreading,Visual C++,Mfc,Cwnd,我试图找出在64位系统上运行的32位MFC应用程序中发生崩溃的确切原因。 实际上,这是一个多线程MFCSDI应用程序,可以执行循环执行,包括检查和将检查结果输出为报告 检查完成后,将显示一个带有进度控件的自定义警报窗口,直到生成报告。警报窗口是从工作线程创建的,主线程将等待窗口创建 以下是显示带有进度条的警报窗口的一个周期的编码表示: static const __int64 POPUPWND_POLLPERIOD = 10 * 10000LL; static const __int64

我试图找出在64位系统上运行的32位MFC应用程序中发生崩溃的确切原因。 实际上,这是一个多线程MFCSDI应用程序,可以执行循环执行,包括检查和将检查结果输出为报告

检查完成后,将显示一个带有进度控件的自定义警报窗口,直到生成报告。警报窗口是从工作线程创建的,主线程将等待窗口创建

以下是显示带有进度条的警报窗口的一个周期的编码表示:

static const __int64     POPUPWND_POLLPERIOD = 10 * 10000LL;
static const __int64     POPUPWND_POLLTIMEOUT = 1000 * POPUPWND_POLLPERIOD;

class CCallFunc
{
public:
    class Queue;

public:
    typedef int(*Call)(const CCallFunc &cf);

public:
    CCallFunc(Call call, LPVOID lpData) :
        m_call(call),
        m_lpData(lpData)
    {}

    int Run() { m_call(*this); }

    LPVOID GetData() const { return m_lpData; }

private:
    Call    m_call;
    LPVOID  m_lpData;
};

class CCallFunc::Queue
{
public:
    int SetQueue(const CCallFunc &cf, const __int64 &timeout = INFINITE)
    {
        m_pcf = &cf;
        m_timeout = timeout;
    }

public:
    int Run(const __int64 &timeout = 0)
    {
        CCallFunc   cf(*m_pcf);

        cf.Run();
    }

private:
    const CCallFunc*    m_pcf;
    __int64         m_timeout;
};


class CWorkThread
{
private:
    static DWORD WINAPI SystemThread(LPVOID lpData)
    {
        CWorkThread*    pThread = (CWorkThread*)lpData;
        __int64         timeout = pThread->m_timeout;

        try {
            pThread->m_queue.Run(timeout);
        }
        catch (const CCallFunc &cf) {
            pThread->m_queue.SetQueue(cf, timeout);
        }
    }

public:
    static int Aquire(CWorkThread *pThread)
    {
        pThread = &thisThread;

        return S_OK;
    }

    static void Sleep(const __int64 &period)
    {
        __int64 current;
        __int64 final = period;

        switch (final) {
        case INFINITE:
            while (true)
                ::SleepEx(INFINITE, TRUE);
            throw;

        case 0:
            ::SleepEx(DWORD(0), TRUE);
            return;

        default:
            ::GetSystemTimeAsFileTime(reinterpret_cast<FILETIME*>(&current));
            if ((final += current) < 0)
                final = current;

            while (current < final) {
                if (::SleepEx(DWORD((final - current) / __int64(10000)), TRUE) == 0)
                    return;

                ::GetSystemTimeAsFileTime((FILETIME*)&current);
            }
        }
    }

    int Start(CCallFunc::Call call, LPVOID lpData)
    {
        return Start(CCallFunc(call, lpData));
    }

    int Start(const CCallFunc &fc)
    {
        DWORD dwID = 0;

        ::CreateThread(0, 0, &SystemThread, this, 0, &dwID);
    }

public:
    CCallFunc::Queue m_queue;

private:
    __int64 m_timeout;

    static CWorkThread thisThread;
};

class CPopupWindow;

struct PopupWndCreateContext : public CCreateContext {
    CPopupWindow*   popup;
    CString         clsname;
    CString         wndname;
    DWORD           style;
    DWORD           exstyle;
    CRect           rc;
    HWND            parent;
    UINT            id;
};


class CPopupWindow : public CWnd
{
public:
    int Show()
    {
        HWND        hParent = 0;
        CWinApp*    pApp = NULL;
        CWnd*       pMain;

        if ((pApp = ::AfxGetApp()) != 0 && (pMain = pApp->GetMainWnd()) != 0) {
            hParent = pMain->m_hWnd;
        }

        Create(800, 600, hParent);
    }

private:
    int Create(int iWidth, int iHeight, HWND parent)
    {
        PopupWndCreateContext ctxt;

        ctxt.popup = this;
        ctxt.clsname = "AlertCtrl";
        ctxt.wndname = "Alert Control";
        ctxt.style = WS_VISIBLE | WS_POPUP;
        ctxt.exstyle = 0;
        ctxt.rc = CRect(0, 0, iWidth, iHeight);

        ctxt.parent = parent;
        ctxt.id = 10000;

        CWorkThread* pThread;
        int         e;

        if (SUCCEEDED(e = CWorkThread::Aquire(pThread)) && SUCCEEDED(e = pThread->Start(&Run, &ctxt))) {

            for (__int64 t = 0; t < POPUPWND_POLLTIMEOUT; t += POPUPWND_POLLPERIOD) {
                if (::IsWindow(*this))
                    return 0;
                CWorkThread::Sleep(POPUPWND_POLLPERIOD);
            }
        }
    }

    static int Run(const CCallFunc &cf)
    {
        int                     e = 0;
        PopupWndCreateContext&  ctxt = *(static_cast<PopupWndCreateContext*>(cf.GetData()));

        ASSERT(&ctxt != 0);

        CPopupWindow    &wnd = *ctxt.popup;

        static const DWORD   clsstyle = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW;
        static const HCURSOR clscursor = ::LoadCursor(0, IDC_WAIT);
        static const HICON   clsicon = 0;
        static LPCTSTR       clsname = ::AfxRegisterWndClass(clsstyle, clscursor, NULL, clsicon);

        if (wnd.CreateEx(DWORD(ctxt.exstyle), ctxt.clsname, ctxt.wndname, DWORD(ctxt.style), ctxt.rc.left, ctxt.rc.top, ctxt.rc.Width(), ctxt.rc.Height(), ctxt.parent, HMENU(ctxt.id), 0) != 0) {
            HWND              hwnd = wnd.GetSafeHwnd();

            ::UpdateWindow(hwnd);

            MSG               msg;
            while ((::GetMessage(&msg, 0, 0, 0))) {

                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }

            wnd.DestroyWindow();

        }
        return e;

    }
};


class CAlertCtrl : CPopupWindow
{
    CProgressCtrl m_progctrl;

    DECLARE_MESSAGE_MAP();

    int OnCreate(LPCREATESTRUCT cs)
    {
        int e = 0;                     //< error code / return value
        if ((e = __super::OnCreate(cs)) != 0)
            return e;

        if (!::IsWindow(m_progctrl))
        {
            CRect rc;

            GetClientRect(rc);
            if (m_progctrl.Create(WS_CHILD | WS_VISIBLE | PBS_SMOOTH, rc, this, 100000))
                m_progctrl.SetRange(0, 10000);
        }

        return e;
    }
};


BEGIN_MESSAGE_MAP(CAlertCtrl, CPopupWindow)
    ON_WM_CREATE()
END_MESSAGE_MAP()

这里有很多代码需要关注,但我注意到的一点是,您使用的是CreateThread而不是AfxBeginThread,这不会初始化MFC的多线程支持。如果工作线程不使用MFC对象,但没有更多的时间查看,那就可以了….您好@RogerRowland谢谢,但请注意,这并不是每次都发生,而是第35次,因为在同一过程中,这是一次不确定的循环检查,会反复发生,因此在一个检查周期结束后,在将报告写入磁盘时,还会创建带有进度条的警报窗口。另外,由于Inspection应用程序是一个常规的Dll MFC,它最终创建CAlertCtrl类型的对象,然后调用Show方法,因此我可以使用::AfxGetApp->m_pMainWnd获取主窗口,并向主窗口发布消息以创建警报控件。不过,如果在后台线程中使用MFC对象,您必须使用AfxBeginThread。问题可能是间歇性的和微妙的,因为空闲处理被用来做一些事情,比如释放临时句柄映射,所以在崩溃之前,它可以正常工作一段时间。在任何情况下,这都是一个很小的变化,可能值得进行测试,以消除它作为一个可能的错误源。
/////////////////////////////////////////////////////////////////////////////
// Default CWnd implementation

LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    if (m_pfnSuper != NULL)
        return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);