CColorDialog的非模态实现

早上看到论坛上有人发问CColorDialog怎么实现非模态,仔细追踪了一下,CColorDialog继承自CCommonDialog,而CCommonDialog在构造时给CDialog类的对话框资源ID是0, 

//CColorDialog构造函数
CColorDialog::CColorDialog(COLORREF clrInit, DWORD dwFlags,
	CWnd* pParentWnd) : CCommonDialog(pParentWnd)
{
 //……省略细节
}

// CCommonDialog函数
_AFXDLGS_INLINE CCommonDialog::CCommonDialog(CWnd* pParentWnd)
	: CDialog((UINT)0, pParentWnd) { }

试着Create(0, NULL)则直接弹出ASSERT, 

BOOL AFXAPI _AfxCheckDialogTemplate(LPCTSTR lpszResource, BOOL bInvisibleChild)
{
	ASSERT(lpszResource != NULL);
	//……省略细节
}	

错误也就是资源 不能为空。

用资源编辑器打开comdlg32.dll,可以看到对话框资源中存在"CHOOSECOLOR",正好是颜色对话框的资源,于是试着从comdlg32.dll创建

  BOOL CreateColorDialog(CWnd *pParent = NULL)
  {
    HINSTANCE hInst = LoadLibrary(_T("Comdlg32.dll"));
    HRSRC hResource = ::FindResource(hInst, "CHOOSECOLOR", RT_DIALOG);
    HGLOBAL hTemplate = LoadResource(hInst, hResource);
    BOOL bResult = CreateIndirect(hTemplate, pParent, hInst);
    ShowWindow(SW_SHOW);
    FreeLibrary(hInst);
    return bResult;
  }

结果对话框倒是正确显示了,但是没有颜色,也不能操作,这是因为此处仅显示了对话框,但是没有相关的WindowProc来处理这个过程。而comdlg32.dll中的导出函数中也看不出这样的接口, 因此还得从ChooseColor入手,而ChooseColor是个阻塞式的函数,虽然提供一个Hook接口,通过Hook这个函数设置断点,可以找出ChooseColor调用了user32.dll!_DialogBoxIndirectParamAorW@24,但是传递的参数DLGPROC却直接是7632DC1Fh,也就是comdlg32.dll的内部的一个函数,这个地址会随着不同的版本将有差异,因此如果使用绝对地址会对dll的版本依赖性很大。

 

   那么就换个思路吧,新开一个线程,把阻塞式的操作放到新线程中,这样更加容易解决问题,此种方法同样也适用于其他阻塞式操作的非阻塞式实现。废话就不多说了,见具体的实现代码。

//继承自CColorDialog的类
//可模态或非模态运行
class CModelessColorDialog : public CColorDialog
{
public:
  INT_PTR iReturn; //记录DoModal的返回值

protected:
  HANDLE m_ThreadEx; //辅助线程
  DWORD m_ThreadID; //线程ID
  HWND hParentWnd; //接收非模态消息的父窗口指针

protected:
  //静态的线程函数
  //回调函数传入的参数是本类的指针
  static DWORD CALLBACK BackDoThread(LPVOID lParam)
  {
    CModelessColorDialog *pThis = (CModelessColorDialog *)lParam; 

    //pThis->iReturn = pThis->DoModal(); 
    //不用原始的DoModal,
    //因为pThis->m_cc.hwndOwner = PreModal();会使ChooseColor仍然变成模态的

    //执行阻塞的DoModal
    pThis->m_cc.hwndOwner = NULL;//PreModal();
    pThis->iReturn = (ChooseColor(&pThis->m_cc)==IDOK) ? IDOK : IDCANCEL;
    //PostModal();

    //接收消息的父窗口指针有效,
    //发送消息(这里模拟发送一个WM_COMMAND消息)
    //这里也可以使用其他消息或回调函数等
    if(pThis->hParentWnd && IsWindow(pThis->hParentWnd))
    {
      ::PostMessage(pThis->hParentWnd, WM_COMMAND, 
        MAKEWPARAM(0x8888U, pThis->iReturn),
        (LPARAM)pThis->GetColor() );
    }

    pThis->m_ThreadID = 0;
    CloseHandle(pThis->m_ThreadEx);
    pThis->m_ThreadEx = NULL;

    return pThis->iReturn;
  }

public:
  //构造函数 参数和CColorDialog一样
  CModelessColorDialog(COLORREF clrInit=0, DWORD dwFlags=0, CWnd* pParentWnd=NULL)
    : CColorDialog(clrInit, dwFlags, NULL)
  {
    m_ThreadEx = NULL;
    m_ThreadID = 0;
    hParentWnd = NULL;
    if(pParentWnd)
      hParentWnd = pParentWnd->GetSafeHwnd(); //记录父窗口指针
    //如果父窗口指针为空,将不发送消息
  }

  //解析函数
  ~CModelessColorDialog()
  {
    //退出线程
    if(m_ThreadEx && m_ThreadID)
    {
      PostThreadMessage(m_ThreadID, WM_QUIT, 0, 0);
    }
  }

public:
  //模态执行仍可用 DoModal

  //非模态执行
  BOOL DoModeless(CWnd *pParent)
  {
    hParentWnd = pParent->GetSafeHwnd();

    //已经在运行中,把窗口提前
    if(m_ThreadEx)
    {
      return TRUE;
    }
    //启动辅助线程
    m_ThreadEx = CreateThread(NULL, 0, 
      (LPTHREAD_START_ROUTINE)BackDoThread, this, 
      0, &m_ThreadID);
    
    return m_ThreadEx ? TRUE:FALSE;
  }

};

 

 //测试代码 

// TestDlg1Dlg.h : header file
class CTestDlg1Dlg : public CDialog
{
  //类中的声明
  CModelessColorDialog colorSelDlg;
  //……其它细节省略
};

//TestDlg1Dlg.cpp : implementation file
//按下ID_CANCEL2按钮消息
void CTestDlg1Dlg::OnCancel2() 
{
	// TODO: Add your control notification handler code here
  //非模态运行
  colorSelDlg.DoModeless(this);
}

//接收非模态发送到消息
BOOL CTestDlg1Dlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
  // TODO: 在此添加专用代码和/或调用基类
  UINT wNotifyCdoe = HIWORD(wParam);
  UINT wID = LOWORD(wParam);
  if(wID == 0x8888U)
  {
    COLORREF cr = (COLORREF)lParam;
    CString szMsg;
    szMsg.Format(_T("return = %d color=(R=%u G=%u B=%u)"),
      wNotifyCdoe, 
      GetRValue(cr), GetGValue(cr), GetBValue(cr));
    AfxMessageBox(szMsg);
    return TRUE;
  }

  return CDialog::OnCommand(wParam, lParam);
}


效果图

你可能感兴趣的:(对话框/属性页)