在自己的线程上的MFC对话框和OpenGL控件之间传递消息

卡西

我正在尝试将OpenGL控件添加到MFC对话框中。该对话框是从也运行OpenGL的窗口启动的,这使它变得复杂。由于一次只能有一个OpenGL上下文可以在一个线程上运行,因此我要么必须拥有整个对话框,要么仅是其自己线程上的控件。我选择尝试后者,因为这样会使我们项目中的其他对话框也易于使用。

我所学的关于MFC的大部分知识都是参差不齐的,因为我一直在学习,所以如果我错过了很多东西,深表歉意。实际上,我可以使用一些MFC帮助来确定传达消息的最佳方法,或者从对话框中为控件设置属性。幸运的是,我似乎有些工作。

我仅以OnSize为例,我在控件包装器类中创建了这个函数,试图将消息从对话框推送到控件。

void AddOpenGLControl::PostMessageToControl(UINT Msg, WPARAM wParam, LPARAM lParam)
{
  if (dialogHandle != NULL)
    PostMessage(dialogHandle, Msg, wParam, lParam);
}

但这让我试图在对话框中模拟可能已经存在的假Windows消息,以发送给控件。

WPARAM wp = nType;
LPARAM lp = MAKELPARAM(ctrlRect.Width(), ctrlRect.Height());

openGlControlOnThread.PostMessageToControl(WM_SIZE, wp, lp);

我知道我将要像普通控件一样希望访问来触发更多消息,并且此方法将很快变得麻烦。

有没有一种方法可以简单地传递消息?很难问到我不知道的东西,但是我正在考虑继承,或者包装类中的消息映射,或者预翻译消息,或者从隐藏的常规控件中窃取消息以发送或...好吧,我d采取所有适用于线程的方法。

这是所有代码,以填补空白。我将代码尽可能地配对,但是我确实必须使用我们的消息循环(对不起!)。这是我从窗口启动的标准窗口:

//Main thread (with existing openGL window)
class TCADTLSandbox2
{
public:
  virtual bool DoCmd(); // Main Entry Point

};

bool TCADTLSandbox2::DoCmd()
{

  TCADTLSandbox2Dialog dialog;

  dialog.Create(TCADTLSandbox2Dialog::IDD);
  dialog.ShowWindow(SW_SHOW);

  // We have to use the main window's accessors to get any messages from the window
  WPARAM dialogMessage;
  while (!GetUITools()->WaitForCommandMessage("", dialogMessage) || (dialogMessage != TIMsgIDCancel))
  {
    if (dialogMessage == TIMsgIDOK)
    {
      break;
    }
  }

  dialog.ShowWindow(SW_HIDE);
  dialog.DestroyWindow();

  return true;
}

这是对话框类。类成员openGlControlOnThreadOnSize()是令人感兴趣的两件事。

//Dialog
class TCADTLSandbox2Dialog : public CDialog
{
  DECLARE_DYNAMIC(TCADTLSandbox2Dialog)

public:
  TCADTLSandbox2Dialog(CWnd* pParent = NULL);
  virtual ~TCADTLSandbox2Dialog();

  enum { IDD = IDD_SANDBOX2 };

protected:
  virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
  DECLARE_MESSAGE_MAP()

private:
  AddOpenGLControl openGlControlOnThread;

  virtual BOOL OnInitDialog();
  afx_msg void OnBnClickedOk();
  afx_msg void OnBnClickedCancel();
  afx_msg void OnSize(UINT nType, int cx, int cy);
};

IMPLEMENT_DYNAMIC(TCADTLSandbox2Dialog, CDialog)

TCADTLSandbox2Dialog::TCADTLSandbox2Dialog(CWnd* pParent /*=NULL*/): CDialog(TCADTLSandbox2Dialog::IDD, pParent){}

TCADTLSandbox2Dialog::~TCADTLSandbox2Dialog(){}

void TCADTLSandbox2Dialog::DoDataExchange(CDataExchange* pDX)
{
  CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(TCADTLSandbox2Dialog, CDialog)
  ON_BN_CLICKED(IDOK, &TCADTLSandbox2Dialog::OnBnClickedOk)
  ON_BN_CLICKED(IDCANCEL, &TCADTLSandbox2Dialog::OnBnClickedCancel)
  ON_WM_SIZE()
END_MESSAGE_MAP()


BOOL TCADTLSandbox2Dialog::OnInitDialog()
{
  CDialog::OnInitDialog();

  CRect rect;
  GetDlgItem(IDC_CADTL_SANDBOX_OPENGLTEST)->GetWindowRect(rect);
  ScreenToClient(rect);
  openGlControlOnThread.Create(rect, this);

  initialized = true;
  return TRUE;
}

void TCADTLSandbox2Dialog::OnSize(UINT nType, int cx, int cy)
{
  CDialog::OnSize(nType, cx, cy);

  if (initialized)
  {

    CRect ctrlRect(0, 0, 0, 0);
    GetDlgItem(IDC_CADTL_SANDBOX_OPENGLTEST)->GetWindowRect(ctrlRect);
    ScreenToClient(ctrlRect);

    //int buttonWidth = ctrlRect.Width();
    ctrlRect.left = 20;
    ctrlRect.right = cx - 20;
    ctrlRect.bottom = cy - 50;

    WPARAM wp = nType;
    LPARAM lp = MAKELPARAM(ctrlRect.Width(), ctrlRect.Height());

    openGlControlOnThread.PostMessageToControl(WM_SIZE, wp, lp);
  }
}

void TCADTLSandbox2Dialog::OnBnClickedOk()
{
  PostQuitMessage(0);
}

void TCADTLSandbox2Dialog::OnBnClickedCancel()
{
  PostQuitMessage(0);
  CDialog::OnCancel();
}

这就是乐趣所在。这是我将OpenGL控件包装到新线程上的工作。我希望用更简单的东西代替PostMessageToControl

// Class to kickoff the control thread
class AddOpenGLControl
{
public:
  AddOpenGLControl();
  ~AddOpenGLControl();

  void Create(CRect rect, CWnd *parent);

  // You can send something along the lines of (WM_DESTROY, NULL, NULL)
  void PostMessageToControl(UINT Msg, WPARAM wParam, LPARAM lParam);

private:
  void StartThread(CRect rect, CWnd *parent);
  std::thread controlThread;
};

// *** The thread class ***

AddOpenGLControl::AddOpenGLControl(){}

AddOpenGLControl::~AddOpenGLControl()
{
  WaitForSingleObject(controlThread.native_handle(), INFINITE); // Okay?
  controlThread.join(); // Also okay?
}

void AddOpenGLControl::PostMessageToControl(UINT Msg, WPARAM wParam, LPARAM lParam)
{
  if (dialogHandle != NULL)
    PostMessage(dialogHandle, Msg, wParam, lParam);
}

void AddOpenGLControl::Create(CRect rect, CWnd *parent)
{
  std::thread initThread(&AddOpenGLControl::StartThread, this, rect, std::ref(parent));
  controlThread = std::move(initThread);
}

void AddOpenGLControl::StartThread(CRect rect, CWnd *parent)
{
  COpenGLControl *openGLControl = new COpenGLControl;

  openGLControl->Create(rect, parent);
  openGLControl->m_unpTimer = openGLControl->SetTimer(1, 1000, 0);

  HWND threadHandle = (HWND)openGLControl->GetSafeHwnd();

// This is where I start getting lost.  Taken from MSDN
  MSG msg;
  BOOL bRet;
  while ((bRet = GetMessage(&msg, threadHandle, 0, 0)) != 0)
  {
    if (bRet == -1)
    {
      // handle the error and possibly exit
    }
    else
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }

}

这是控件本身:

// OpenGL Dialog Control thread (now it can have it's own openGL context)
class COpenGLControl : public CWnd
{
public:
  UINT_PTR m_unpTimer;

  COpenGLControl();
  virtual ~COpenGLControl();

  void Create(CRect rect, CWnd *parent);
  void UpdateOpenGLDispay();

  afx_msg void OnSize(UINT nType, int cx, int cy);
  void ParentDialogResize(UINT nType, int cx, int cy);

private:
  CWnd    *hWnd;
  HDC     hDC;
  HGLRC   openGLRC;
  CString className;

  void Initialize();

  void DrawCRD(double centerX, double centerY, double centerZ, double crdScale);

  afx_msg void OnPaint();
  afx_msg void OnDraw(CDC *pDC);
  afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
  afx_msg void OnTimer(UINT_PTR nIDEvent);

  DECLARE_MESSAGE_MAP()
};

// *** The actual control ***

COpenGLControl::COpenGLControl()
{
  initialized = false;
  display = new Display2d;
}

COpenGLControl::~COpenGLControl()
{
  CleanUp();
}


BEGIN_MESSAGE_MAP(COpenGLControl, CWnd)
  ON_WM_PAINT()
  ON_WM_CREATE()
  ON_WM_TIMER()
  ON_WM_SIZE()
  ON_WM_DESTROY()
END_MESSAGE_MAP()

int COpenGLControl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

  Initialize();

  return 0;
}

void COpenGLControl::Initialize()
{
  // Initial Setup:
  //
  //Set up the pixal descriptor for using openGL on the graphics card
  PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),  // Size of this structure
    1,                              // Version of this structure
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    24,                             // Want 24bit color
    0, 0, 0, 0, 0, 0,                    // Not used to select mode
    0, 0,                            // Not used to select mode
    0, 0, 0, 0, 0,                      // Not used to select mode
    32,                             // Size of depth buffer
    0,                              // Not used to select mode
    0,                              // Not used to select mode
    PFD_MAIN_PLANE,                 // Draw in main plane
    0,                              // Not used to select mode
    0, 0, 0 };                        // Not used to select mode


  // Store the device context   
  if (!(hDC = GetDC()->m_hDC)) // From the dialog?
  {
    AfxMessageBox("Can't Create A GL Device Context.");
    //May not be able to unregister class??  May not need to?
    return;
  }

  // Choose a pixel format that best matches that described in pfd
  int nPixelFormat;                        // Pixel format index
  nPixelFormat = ChoosePixelFormat(hDC, &pfd);

  // Set the pixel format for the device context
  if (!SetPixelFormat(hDC, nPixelFormat, &pfd))
    AfxMessageBox("Setting pixel format failed");

  // Create the rendering context and make it current
  if (!(openGLRC = wglCreateContext(hDC)))
  {
    AfxMessageBox("Can't Create A GL Rendering Context.");
    //May not be able to unregister class??  May not need to?
    return;
  }

  if (!wglMakeCurrent(hDC, openGLRC))
    AfxMessageBox("WGLFailed");


  // Set color to use when clearing the background.
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  glClearDepth(1.0f);

  // Turn on backface culling
  glFrontFace(GL_CCW);
  glCullFace(GL_BACK);

  // Turn on depth testing
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);

  // Enable alpha blending
  glEnable(GL_BLEND); 
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 

  UpdateOpenGLDispay();

  initialized = true;
}

void COpenGLControl::Create(CRect rect, CWnd *parent)
{
  className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_OWNDC, NULL, (HBRUSH)GetStockObject(BLACK_BRUSH), NULL);

  CreateEx(0, className, "OpenGL", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, rect, parent, 0);

  hWnd = parent;
}

void COpenGLControl::OnPaint()
{
  // Attempt to help with flickering
  UpdateOpenGLDispay();
  CPaintDC dc(this);
  ValidateRect(NULL);
}

void COpenGLControl::OnDraw(CDC *pDC)
{
  UpdateOpenGLDispay();
}

void COpenGLControl::UpdateOpenGLDispay()
{
  DrawCRD(); // ... just draw something
  SwapBuffers(hDC);
}

void COpenGLControl::DrawCRD(double centerX, double centerY, double centerZ, double crdScale)
{
  // X CRD              
  glColor4f(1.0f, 0.0f, 0.0f, 1);//R
  glBegin(GL_LINES);
  glVertex3f(centerX, centerY, centerZ);
  glVertex3f(centerX + 1 * crdScale, centerY, centerZ);
  glEnd();

  // Y CRD
  glColor4f(0.0f, 0.0f, 1.0f, 1);//B
  glBegin(GL_LINES);
  glVertex3f(centerX, centerY, centerZ);
  glVertex3f(centerX, centerY + 1 * crdScale, centerZ);
  glEnd();

  // Z CRD
  glColor4f(0.0f, 1.0f, 0.0f, 1);//G
  glBegin(GL_LINES);
  glVertex3f(centerX, centerY, centerZ);
  glVertex3f(centerX, centerY, centerZ + 1 * crdScale);
  glEnd();
}

void COpenGLControl::OnTimer(UINT_PTR nIDEvent)
{
  switch (nIDEvent)
  {
  case 1:
    UpdateOpenGLDispay();
    break;
  }

  CWnd::OnTimer(nIDEvent);
}

void COpenGLControl::OnSize(UINT nType, int cx, int cy)
{
  CWnd::OnSize(nType, cx, cy);

  if (0 >= cx || 0 >= cy || nType == SIZE_MINIMIZED) return;

  if (nType == SIZE_RESTORED)
    ParentDialogResize(nType, cx, cy);
}

void COpenGLControl::ParentDialogResize(UINT nType, int cx, int cy)
{
  if (initialized)
  {
    if (0 >= cx || 0 >= cy || nType == SIZE_MINIMIZED) return;
    this->SetWindowPos(NULL, 20, 20, cx, cy, SWP_SHOWWINDOW); // fixed for this example
  }
}

void COpenGLControl::OnDestroy()
{
  CWnd::OnDestroy();
  PostQuitMessage(0);
}

void COpenGLControl::CleanUp()
{

  if (!wglDeleteContext(openGLRC))
    AfxMessageBox("Deleting OpenGL Rendering Context failed");

  if (!wglMakeCurrent(hDC, NULL))
    AfxMessageBox("Removing current DC Failed");

  //ReleaseDC(hDC); // The internets say the device context should automatically be deleted since it's CS_OWNDC

}

// Sorry for the heap of code.  Or is it stack?  

谢谢!

卡西

似乎我错过了MFC的一些重要方面。如果有人遵循类似的方法,最好先尝试将常规控件子类化,然后再尝试创建opengl控件。

有几种方法可以做到这一点。如果我使用OnWndMsg或类似的WindowProc,则可以在消息仍处于wParam / lParam状态时获取该消息,并将其传递。

BOOL TCADTLSandbox2Dialog::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
{  
  //This is one way to just pass the message along.  For some cases, we may need to check the mouse position as well. 
  if (message == WM_MOUSEWHEEL) 
  { 
    openGlControlOnThread.PostMessageToControl(message, wParam, lParam); 
    return true; 
  } 
  if (wParam == WM_LBUTTONDOWN) 
  { 
    openGlControlOnThread.PostMessageToControl(message, wParam, lParam); 
    return true; 
  } 
  return CDialog::OnWndMsg(message, wParam, lParam, pResult); 
}

而且,事实证明,PreTranslateMessage是我可以使用的选项,尽管当时我还没有看到。

BOOL TCADTLSandbox2Dialog::PreTranslateMessage(MSG* pMsg)
{
  // Send the message to the control (the dialog gets it by default)
  if (pMsg->message == WM_MOUSEWHEEL)
  {
      openGlControlOnThread.PostMessageToControl(pMsg->message, pMsg->wParam, pMsg->lParam);
      return true;
  }

  return MCDialog::PreTranslateMessage(pMsg);
}

希望这对其他人有所帮助!

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

带线程的对话框消息

来自分类Dev

在多个对话框中显示和使用相同的MFC CList控件

来自分类Dev

对话框可以拦截传递到其控件的拖放消息吗?

来自分类Dev

在对话框之间传递数据

来自分类Dev

MFC对话框错误中的C ++多线程

来自分类Dev

C ++获取对话框控件的位置和大小

来自分类Dev

IntelliJ 标准对话框控件和中文符号

来自分类Dev

在活动与对话框片段之间传递数据

来自分类Dev

在活动与对话框片段之间传递数据

来自分类Dev

显示消息对话框

来自分类Dev

Haskell OpenGL 中的对话框和按钮回调

来自分类Dev

MFC:如何在一个函数中捕获对话框的每个控件的设置焦点

来自分类Dev

在MFC中调整对话框大小时如何移动控件?

来自分类Dev

如何在MFC对话框中替换/更新ActiveX控件

来自分类Dev

超高分辨率的MFC对话框控件

来自分类Dev

在MFC中调整对话框大小时如何移动控件?

来自分类Dev

警报对话框标题和消息未显示

来自分类Dev

解除和取消警报对话框Android之间的交互

来自分类Dev

Android在片段和对话框片段之间共享ViewModel?

来自分类Dev

MFC从DLL导入对话框

来自分类Dev

防止双击MFC对话框按钮

来自分类Dev

设置MFC对话框的位置

来自分类Dev

MFC C ++中的对话框

来自分类Dev

MFC:在基于对话框的vs SDI vs MDI之间进行选择

来自分类Dev

UI对话框按钮隐藏控件

来自分类Dev

xPages扩展库对话框控件

来自分类Dev

Ubuntu(Mini)-如何用我自己的版本替换系统消息对话框的外壳?

来自分类Dev

如何在基于对话框的MFC应用程序上启用滚动?

来自分类Dev

C ++ MFC对话框-如何在列表控件中显示MySQL数据库中的项目?

Related 相关文章

热门标签

归档