MFCユーザースレッドの作成と終了

同社の生産テストツールは長年使用されてきました。N年は何年ですか?現在の学生の何人かがまだVC6.0を知らないかどうかはわかりません(私はそれが間違っているかもしれないことを知りません、ただ嘲笑し、攻撃はありません、私はそれを使ったことがないと言われるべきです)、プロダクション当時はまだVC6.0でテストツールが開発されていたので、今戦っている清王朝の「無敵の将軍」と同じだと思います。そのため、VC6.0から始めましたが、途中で僧侶になったプログラマーが残したものを補ってくれます。

ツールのUIと機能は実際には非常に単純ですが、最適化する場所はたくさんあります。

今日は、実際のテストツールにはより多くの機能があるため、インターフェイススレッドの一部であるUIスレッドについてのみ説明します。ここでは、デモを使用して、作成したものの一部を記録します。

コードダウンロードリンク:https//download.csdn.net/download/yangkunhenry/14110419

基本的な内容

まず、メインインターフェイスはダイアログボックスです。MFCを使用してダイアログボックスベースのプログラムを簡単に作成できます。最近はアフターサービスツールであるため、ツールの名前はアフターセールスツールと呼ばれています。粘着性があります、ごめんなさい。

ダイアログボックスはこのように見え、かなり単純で、中央の編集ボックスの役割は言及されていません(AfxInitRichEdit2();がアプリのInitInstanceに追加されなかったため、RichEdit2です。最初にエラーが発生しました)。 、プログラムの実行開始時にエラーが発生しました:「警告:ダイアログでMFCコントロールを使用する場合、ポイントではなく#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。」)。これは主に、以下に示すように、マウスが[設定]をクリックしたときに別の[設定]ダイアログボックスを生成するためのものです。

ここでの設定ダイアログボックスは、もともとメインプロセスで作成されたものですが、設定が終了するとメインプロセスが終了しますが、これは良くありません。

したがって、ここでは、ASL設定インターフェイスはユーザースレッドの方法で実行されます

インターフェイススレッドを作成する

インターフェーススレッドとワーカースレッドは、2つの基本的なタイプのスレッドです。最近、CSDNフォーラムで多くの大物に質問しました。インターフェーススレッドはインターフェースに関連しているだけで、他には何もありません。

前述のように、[OK]を押すとインターフェイススレッドが作成されます。

インターフェイススレッド作成関数:AfxBeginThread

MSDN:

CWinThread* AfxBeginThread(
   AFX_THREADPROC pfnThreadProc,
   LPVOID pParam,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);
CWinThread* AfxBeginThread(
   CRuntimeClass* pThreadClass,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);

pThreadClass 
从 CWinThread 派生的对象的 RUNTIME_CLASS。

MSDNでスレッドを作成するための2つの関数があります。インターフェイススレッドを作成するときは、後者を使用します。最初のパラメーターは、CRuntimeClassポインターのこの関数です。

明らかに、このパラメーターを準備する必要があります。次のタイプのパラメーターを提供する必要があります。CRuntimeClass 

pThreadClassは、CWinThreadから派生したオブジェクトのRUNTIME_CLASSです。

ここでCSettingクラスを作成しました。もちろん、基本クラスはCWinThreadです。

#pragma once
#include <afxwin.h>
#include "stdafx.h"
#include "CSettingDlg.h"
class CSetting :
	public CWinThread
{
	DECLARE_DYNCREATE(CSetting)
protected:
	CSetting();
	~CSetting();
public:
	BOOL InitInstance();
	virtual int ExitInstance();
	CSettingDlg *m_psetting_dlg;
	afx_msg void OnThreadInfo(WPARAM wParam, LPARAM lParam);
protected:
	DECLARE_MESSAGE_MAP()	/* 本身宏中已经有protected:但是为了防止直接在后面追加成员变量,所以这里还是再把protected写上*/
public:
	BOOL m_b_dev_id;
};

スレッドクラスを作成する方法:

  1. CWinThreadのサブクラスを作成します。ここではCSettingです。
  2. クラス宣言にDECLARE_DYNCREATE(CSetting)を追加します。h。DECLARE_DYNCREATEの関数は実際には非常に単純です。静的メンバーを追加することです。staticconstCRuntimeClassclassSetting;このメンバー変数へのポインターは後で使用されます。
  3. クラスの定義にIMPLEMENT_DYNCREATE(CSetting、CWinThread)を追加します。もちろん、これは前の.hの宣言部分の特定の定義である必要があります。これは何ですか。これは、CRunTimeClass *がどのように発生するかの特定のプロセスに関するものです。別のブログを開いてこれを説明することができます。時間があるときにこれについて詳しく説明します。ここにマークを付けてください。
  4. CWinThradのInitInstanceとExitInstanceをオーバーロードします。もちろん、オーバーロード時にそれらを入力する方法についていくつか質問がありますか?次に、InitInstanceでTRUEを返すように指示します。なぜですか。CWinThreadのInitInstanceの実現を見てください。
    /
    // CWinThread default implementation
    
    BOOL CWinThread::InitInstance()
    {
    	ASSERT_VALID(this);
    
    	return FALSE;   // by default don't enter run loop
    }

    注を見てください。デフォルトでは実行ループに入らないでください。つまり、FALSEを返すと、CWinThreadの実行ループは実行されません。したがって、一部の学生がこの関数をリロードすると、TRUEを返す代わりに、CWinThreadを返します。 :InitInstanceを定義し、メッセージ応答関数を定義すると、PostThreadMessageを呼び出してもメッセージ応答はありません。thrdcore.cppで確認できます(自分で検索するか、Cドライブでシングルステップ追跡できます)。 CWinThread関数の場合、_AfxThreadEntryのコードを確認できます。

    	// else -- check for thread with message loop
    	else if (!pThread->InitInstance())
    	{
    		ASSERT_VALID(pThread);
    		nResult = pThread->ExitInstance();
    	}
    	else
    	{
    		// will stop after PostQuitMessage called
    		ASSERT_VALID(pThread);
    		nResult = pThread->Run();
    	}

    ご覧のとおり、InitInstanceがFALSEを返した場合、以下のelseは間違いなく入りません。

  5. 作成するDlgのポインタを追加します(ダイアログボックスクラスを作成する方法は?マーク)m_psetting_dlg
  6. メインプロセスからインターフェイススレッドにメッセージを送信する必要があるため、メッセージ応答関数afx_msg void OnThreadInfo(WPARAM wParam、LPARAM lParam);を作成する必要があります。
  7. もちろん、DECLARE_MESSAGE_MAPも不可欠です。通常、単純なダイアログボックスにメッセージを追加すると、MFCクラスウィザードを使用してメッセージ応答関数が自動的に追加されます。コードにそのようなコードがあることがわかりましたが、現在、多くのウィンドウタイプがあります。 、MFCクラスウィザードは非ウィンドウタイプのメッセージ応答関数の追加をサポートしていないため、自分で記述する必要があります。これはクラス宣言に記述する必要のある文であり、もう1つは次のとおりです。
  8. クラス定義を追加する
    BEGIN_MESSAGE_MAP(CSetting, CWinThread)
    	ON_THREAD_MESSAGE(WM_THREADINFO, &CSetting::OnThreadInfo)
    END_MESSAGE_MAP()

     

  9. もちろん、対応するWM_THREADINFOを事前に定義する必要があります。ここで定義したのは、#define WM_THREADINFO WM_USER +50です。
  10. 上記を実行した後、スレッドクラスを確認して作成し、m_setting_ui_thread = AfxBeginThread(RUNTIME_CLASS(CSetting));を使用してスレッドを作成できます。m_setting_ui_threadのタイプはCWinThread *タイプです。AfxBeginThreadの実装はもちろん私たちのプロセスで行われます。スレッドm_setting_ui_threadを作成した後、ダイアログを作成するようにスレッドに通知するメッセージを送信できます。もちろん、スレッドクラスのインスタンスでダイアログダイアログボックスを直接作成することもできます。 CSetting。送信メッセージの方法
    m_setting_ui_thread->PostThreadMessage(WM_THREADINFO, 2, 0);

     

  11. この投稿メッセージの後、スレッドクラスのメッセージ応答関数で
    //显示消息处理函数
    void CSetting::OnThreadInfo(WPARAM wParam, LPARAM lParam)
    {
    	if (wParam == 2)
    	{//启动
    		m_psetting_dlg = new CSettingDlg;
    		m_psetting_dlg->Create(IDD_DLG_SETTING);
    		m_psetting_dlg->UpdateData(FALSE);
    		m_psetting_dlg->ShowWindow(TRUE);
    	}
    	//return 0;
    }

    Dlgクラスを新規作成し、ウィンドウを作成し、ウィンドウを更新し、ウィンドウを表示して、インターフェイスウィンドウを作成します。

スレッド出口:

ここで、このDlgはメッセージ応答関数で新しいことがわかったので、スレッドが終了するときにこのオブジェクトも削除する必要があります。スレッドexitはスレッドクラスexitであり、スレッドクラスexitはスレッドデストラクタ呼び出しです。ここで、デストラクタで削除操作を実行します。

CSetting::~CSetting()
{
	if (m_psetting_dlg != NULL) {
		delete m_psetting_dlg;
		m_psetting_dlg = NULL;
	}
}

もちろん、スレッドが終了し、スレッドの終了が自動的にスレッドの実行を終了することが前提です。ここではスレッドの実行を実装していませんが、CWinThreadでの実行は引き続きメッセージループを実行しています。実行がWM_QUITメッセージを受信すると、終了します。CWinThreadのrunメソッド:

int CWinThread::Run()
{
	ASSERT_VALID(this);
	_AFX_THREAD_STATE* pState = AfxGetThreadState();

	// for tracking the idle time state
	BOOL bIdle = TRUE;
	LONG lIdleCount = 0;

	// acquire and dispatch messages until a WM_QUIT message is received.
	for (;;)
	{
		// phase1: check to see if we can do idle work
		while (bIdle &&
			!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
		{
			// call OnIdle while in bIdle state
			if (!OnIdle(lIdleCount++))
				bIdle = FALSE; // assume "no idle" state
		}

		// phase2: pump messages while available
		do
		{
			// pump message, but quit on WM_QUIT
			if (!PumpMessage())
				return ExitInstance();

			// reset "no idle" state after pumping "normal" message
			//if (IsIdleMessage(&m_msgCur))
			if (IsIdleMessage(&(pState->m_msgCur)))
			{
				bIdle = TRUE;
				lIdleCount = 0;
			}

		} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
	}
}

それを参照してください:

// pump message, but quit on WM_QUIT
			if (!PumpMessage())
				return ExitInstance();

コメントは非常に明確なpmupメッセージですが、WM_QUITで終了します。また、実行が終了すると、ExitInstanceアクションが実行されることも確認しました。

要約すると、スレッドを終了させる必要があるプロセスの特定の場所で、WM_QUITメッセージをスレッドに送信する必要があります。

メインプロセスダイアログボックスで[OK]をクリックすると、スレッドが終了します。

void CdwaftersalestoolDlg::OnBnClickedOk()
{
	// TODO: 在此添加控件通知处理程序代码
	/*
		发送WM_QUIT线程给UI线程
	*/
	if (m_setting_ui_thread) {
		m_setting_ui_thread->PostThreadMessage(WM_QUIT, 0, 0);
		if (WAIT_OBJECT_0 == WaitForSingleObject(m_setting_ui_thread->m_hThread, INFINITE)) {
			CDialogEx::OnOK();
		}
	}else
		CDialogEx::OnOK();
}

PostThreadMessageを呼び出した後、WM_QUITメッセージをインターフェイススレッドのメッセージループに送信するだけですが、実行関数が出口点に移動するかどうかは別の問題です。一般に、ほぼ高速に移動できます。ただし、それでも使用する必要があります。 WaitForSingleObjectは、スレッドのシグナルが送信されるのを待機し、スレッドの一部の操作が完全に実行されないようにするために、他のアクションを実行します。たとえば、スレッドクラスの作成時にm_bAutoDelete = FALSEを指定した場合、次のようになります。 m_setting_ui_threadを手動で削除するには、スレッドの実行が終了するまで待機せず、ExitInistanceが終了するまで待機しなかった場合、m_setting_ui_threadを削除すると不明なエラーが発生します。

 

もう1つの注意点として、インターフェイススレッドでAfxmessageboxなどの関数を呼び出さないでください。デバッグ中にAfxmessageboxを呼び出すことによってプログラムエラーが発生しました。

おすすめ

転載: blog.csdn.net/yangkunhenry/article/details/112433106