我在编写程序时偶然碰到了一个奇怪的问题,最终发现问题出在MFC的CDatabase类上。-〉程序的基本情况:
我编写了一个简单的多线程程序,其中,界面包含了一个非模态对话框,在单独的线程中,我通过MFC的CDatabase类访问SqlServer数据库。int result = data.OpenEx(strODBC, CDatabase::noOdbcDialog);-〉问题的基本情况:
在线程中使用CDatabase类访问Sqlserver数据库时,如果数据库服务没有运行,数据库连接操作OpenEx()将阻塞一段时间(一般是几十秒)。在OpenEx()函数阻塞期间,如果界面非模态对话框处于显示状态,界面也将被阻塞,直到OpenEx()函数返回。
此外,当把非模态对话框改为模态对话框时,界面将不会被阻塞。
问题:
OpenEx()函数与非模态对话框处于完全不同的两个线程,为什么会这样呢?如何解决?请教高手!!!
在分析问题的过程中,发现MFC的CDatabase源码中存在以下代码:以下是MFC代码片断:
BOOL CDatabase::Connect(DWORD dwOptions)
{
USES_CONVERSION; HWND hWndTop;
HWND hWnd = CWnd::GetSafeOwner_(NULL, &hWndTop);
if (hWnd == NULL)
hWnd = ::GetDesktopWindow(); UCHAR szConnectOutput[MAX_CONNECT_LEN];
RETCODE nRetCode;
SWORD nResult;
UWORD wConnectOption = SQL_DRIVER_COMPLETE;
if (dwOptions & noOdbcDialog)
wConnectOption = SQL_DRIVER_NOPROMPT;
else if (dwOptions & forceOdbcDialog)
wConnectOption = SQL_DRIVER_PROMPT;
AFX_SQL_SYNC(::SQLDriverConnect(m_hdbc, hWnd,
(UCHAR*)T2A((LPTSTR)(LPCTSTR)m_strConnect), SQL_NTS,
szConnectOutput, _countof(szConnectOutput),
&nResult, wConnectOption));
if (hWndTop != NULL)
::EnableWindow(hWndTop, TRUE);
……CDatabase变量的OpenEx()操作在执行AFX_SQL_SYNC()时阻塞,该函数hWnd参数的值即为非模态对话框的句柄。

解决方案 »

  1.   

    是啊,就是因为问题的现象太奇怪了才是问题啊。
    基本上与我贴出的那部分代码有关,你可以仔细看一下,CDatabase::Connect()函数中使用到了两个窗口句柄hWndTop和hWnd,一般来说,这两个句柄在访问参数dwOptions 不是 noOdbcDialog时使用的(你可能记得有时访问数据库时弹出一个对话框提示你设置访问参数吧),但是,在我的程序中,直接采用的是noOdbcDialog,结果确是界面被阻塞。我暂时怀疑这是微软MFC的一个漏洞。该线程是在软件初始化完成之后通过定时器延时启动的,定时器超时值为10秒。//启动工作线程
    m_pThread = AfxBeginThread( ThreadProc, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);   
    m_pThread->m_bAutoDelete = TRUE;
    m_pThread->ResumeThread();线程中的代码是这样的:
    UINT ThreadProc( LPVOID pParam)
    {
    CMyDatabase data; while(1)
    {
    CString strODBCA
    strODBCA.Format( "DSN=testdata;UID=caddatabase;PWD=caddatabase");
    BOOL result = data->OpenEx( strODBCA, CDatabase::noOdbcDialog);
    ……
    }
    }
    //代码较多,仅节选相关代码非模态对话框则没有任何特别之处。
      

  2.   

    不好意思,上面贴出的代码有点问题:CMyDatabase data应为CDatabase data;data->OpenEx()应为data.OpenEx()。请不要怀疑你的眼睛,实际上我的示例程序就是这么简单:
    1、 程序没有从数据库中读取任何记录信息;
    2、 界面代码中除了启动该线程之外,没有任何与线程相关的代码,即不存在任何交互;
    3、 非模态对话框模版完全采用默认模版,非模态对话框对应的类也是ClassWizard自动生成的;
    4、 你完全可以把本程序看作是“经典的Hello程序” + “线程启动代码” + “非模态对话框”,即界面代码中也没有其他特殊处理。