EternalWindows
ホスト API / タスクの管理

今回は、IHostTaskManagerを実装する方法について説明します。 このインターフェースは、マネージコードでスレッドが作成されようとする瞬間を検出することができ、 CLRの代わりにホストがスレッドを作成することが可能になっています。 つまり、マネージコードを実行するスレッドのハンドルをホストが維持できるということであり、 状況に応じてスレッドを操作できます。 たとえば、無限ループでCPUを消費しているスレッドがいるならば、それを強制終了するなどの使い方があります。

まず、マネージDLLのコードを示します。

using System;
using System.IO;
using System.Threading;

public class Class1
{
    public static int Method1(String arg)
    {
        Thread thread = new Thread(new ThreadStart(Run));
        thread.Start();

        return 0;
    }

    public static void Run()
    {
        Thread.Sleep(5000);

        string str = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
        FileStream hStream = File.Create(str + "\\gh.txt");
        hStream.Close();
    }
}

続いて、task.hを示します。

class CHostTaskManager;

class CHostControl : public IHostControl
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();

	STDMETHODIMP GetHostManager(REFIID riid, void **ppObject);
	STDMETHODIMP SetAppDomainManager(DWORD dwAppDomainID, IUnknown *pUnkAppDomainManager);

	CHostControl();
	~CHostControl();

private:
	LONG             m_cRef;
	CHostTaskManager *m_pTaskManager;
};

class CHostTaskManager : public IHostTaskManager 
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();

	STDMETHODIMP GetCurrentTask(IHostTask **pTask);
	STDMETHODIMP CreateTask(DWORD dwStackSize, LPTHREAD_START_ROUTINE pStartAddress, PVOID pParameter, IHostTask **ppTask);
	STDMETHODIMP Sleep(DWORD dwMilliseconds, DWORD option);
	STDMETHODIMP SwitchToTask(DWORD option);
	STDMETHODIMP SetUILocale(LCID lcid);
	STDMETHODIMP SetLocale(LCID lcid);
	STDMETHODIMP CallNeedsHostHook(SIZE_T target, BOOL *pbCallNeedsHostHook);
	STDMETHODIMP LeaveRuntime(SIZE_T target);
	STDMETHODIMP EnterRuntime();
	STDMETHODIMP ReverseLeaveRuntime();
	STDMETHODIMP ReverseEnterRuntime();
	STDMETHODIMP BeginDelayAbort();
	STDMETHODIMP EndDelayAbort();
	STDMETHODIMP BeginThreadAffinity();
	STDMETHODIMP EndThreadAffinity();
	STDMETHODIMP SetStackGuarantee(ULONG guarantee);
	STDMETHODIMP GetStackGuarantee(ULONG *pGuarantee);
	STDMETHODIMP SetCLRTaskManager(ICLRTaskManager *pManager);

	CHostTaskManager();
	~CHostTaskManager();

private:
	LONG            m_cRef;
	ICLRTaskManager *m_pTaskManager;
};

class CHostTask : public IHostTask
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();

	STDMETHODIMP Start();
	STDMETHODIMP Alert();
	STDMETHODIMP Join(DWORD dwMilliseconds, DWORD option);
	STDMETHODIMP SetPriority(int newPriority);
	STDMETHODIMP GetPriority(int *pPriority);
	STDMETHODIMP SetCLRTask(ICLRTask *pCLRTask);

	CHostTask(HANDLE hThread);
	~CHostTask();
	
	void Suspend();
	void Resume();
	void Abort();
	void GetMemStats(ULONGLONG *puSize);
	BOOL IsExit();

private:
	LONG     m_cRef;
	HANDLE   m_hThread;
	ICLRTask *m_pTask;
};

続いて、ホストのコードを示します。

#include <windows.h>
#include <mscoree.h>
#include <corerror.h>
#include "task.h"

#define ID_LISTBOX 100
#define ID_METHOD 200
#define ID_THREAD 201
#define ID_SUSPEND 300
#define ID_RESUME 301
#define ID_ABORT 302
#define ID_MEMORY 303

#pragma comment (lib, "mscoree.lib")

HWND      g_hwndListBox = NULL;
CHostTask *g_aTask[100];
int       g_nThreadCount = 0;

void InitializeMenuItem(HMENU hmenu, LPTSTR lpszItemName, int nId, HMENU hmenuSub);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static ICLRRuntimeHost *pRuntimeHost = NULL;
	static IHostControl    *pHostControl = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		HRESULT hr;
		HMENU   hmenu;
		HMENU   hmenuPopup;

		g_hwndListBox = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)ID_LISTBOX, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		hr = CorBindToRuntimeEx(L"v2.0.50727", L"wks", 0, CLSID_CLRRuntimeHost, IID_PPV_ARGS(&pRuntimeHost));
		if (FAILED(hr))
			return -1;

		pHostControl = new CHostControl;
		pRuntimeHost->SetHostControl(pHostControl);
		
		pRuntimeHost->Start();

		hmenu  = CreateMenu();
		hmenuPopup = CreatePopupMenu();

		InitializeMenuItem(hmenuPopup, TEXT("一時停止"), ID_SUSPEND, NULL);
		InitializeMenuItem(hmenuPopup, TEXT("再開"), ID_RESUME, NULL);
		InitializeMenuItem(hmenuPopup, TEXT("中止"), ID_ABORT, NULL);
		InitializeMenuItem(hmenuPopup, TEXT("メモリ情報取得"), ID_MEMORY, NULL);
		InitializeMenuItem(hmenu, TEXT("メソッドの実行(&M)"), ID_METHOD, NULL);
		InitializeMenuItem(hmenu, TEXT("スレッド操作(&T)"), ID_THREAD, hmenuPopup);

		SetMenu(hwnd, hmenu);

		return 0;
	}

	case WM_COMMAND: {
		int nId = LOWORD(wParam);

		if (nId == ID_METHOD) {
			HRESULT hr;

			hr = pRuntimeHost->ExecuteInDefaultAppDomain(L"ClassLibrary1.dll", L"Class1", L"Method1", NULL, NULL);
			if (FAILED(hr))
				MessageBox(NULL, TEXT("メソッドの呼び出しに失敗しました。"), NULL, MB_ICONWARNING);
		}
		else if (nId >= ID_SUSPEND && nId <= ID_MEMORY) {
			int       nIndex;
			CHostTask *pTask;

			nIndex = (int)SendMessage(g_hwndListBox, LB_GETCURSEL, 0, 0);
			if (nIndex == -1) {
				MessageBox(NULL, TEXT("スレッドが作成されていません。"), NULL, MB_ICONWARNING);
				return 0;
			}
			
			pTask = g_aTask[nIndex];
			if (pTask->IsExit()) {
				MessageBox(NULL, TEXT("このスレッドは終了しています。"), NULL, MB_ICONWARNING);
				return 0;
			}
			
			if (nId == ID_SUSPEND)
				pTask->Suspend();
			else if (nId == ID_RESUME)
				pTask->Resume();
			else if (nId == ID_ABORT)
				pTask->Abort();
			else if (nId == ID_MEMORY) {
				TCHAR     szBuf[256];
				ULONGLONG uSize;

				pTask->GetMemStats(&uSize);
				wsprintf(szBuf, TEXT("%d"), uSize);
				MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
			}
		}

		return 0;
	}
	
	case WM_SIZE:
		MoveWindow(g_hwndListBox, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		if (pRuntimeHost != NULL) {
			pRuntimeHost->Stop();
			pRuntimeHost->Release();
		}
		if (pHostControl != NULL)
			pHostControl->Release();
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void InitializeMenuItem(HMENU hmenu, LPTSTR lpszItemName, int nId, HMENU hmenuSub)
{
	MENUITEMINFO mii;
	
	mii.cbSize = sizeof(MENUITEMINFO);
	mii.fMask  = MIIM_ID | MIIM_TYPE;
	mii.wID    = nId;

	if (lpszItemName != NULL) {
		mii.fType      = MFT_STRING;
		mii.dwTypeData = lpszItemName;
	}
	else
		mii.fType = MFT_SEPARATOR;

	if (hmenuSub != NULL) {
		mii.fMask   |= MIIM_SUBMENU;
		mii.hSubMenu = hmenuSub;
	}

	InsertMenuItem(hmenu, nId, FALSE, &mii);
}

続いて、タスク関連のコードを示します。

#include <windows.h>
#include <mscoree.h>
#include <corerror.h>
#include "task.h"

extern HWND      g_hwndListBox;
extern CHostTask *g_aTask[100];
extern int       g_nThreadCount;

CHostControl::CHostControl()
{
	m_cRef = 1;
	m_pTaskManager = new CHostTaskManager;
}

CHostControl::~CHostControl()
{
	m_pTaskManager->Release();
}

STDMETHODIMP CHostControl::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IHostControl))
		*ppvObject = static_cast<IHostControl *>(this);
	else
		return E_NOINTERFACE;

	AddRef();
	
	return S_OK;
}

STDMETHODIMP_(ULONG) CHostControl::AddRef()
{
	return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CHostControl::Release()
{
	if (InterlockedDecrement(&m_cRef) == 0) {
		delete this;
		return 0;
	}

	return m_cRef;
}

STDMETHODIMP CHostControl::GetHostManager(REFIID riid, void **ppObject)
{
	*ppObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IHostTaskManager)) {
		*ppObject = static_cast<IHostTaskManager *>(m_pTaskManager);
		return S_OK;
	}
	
	return E_NOINTERFACE;
}

STDMETHODIMP CHostControl::SetAppDomainManager(DWORD dwAppDomainID, IUnknown *pUnkAppDomainManager)
{
	return E_NOTIMPL;
}


// CHostTaskManager


CHostTaskManager::CHostTaskManager()
{
	m_cRef = 1;
}

CHostTaskManager::~CHostTaskManager()
{
}

STDMETHODIMP CHostTaskManager::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IHostTaskManager))
		*ppvObject = static_cast<IHostTaskManager *>(this);
	else
		return E_NOINTERFACE;

	AddRef();
	
	return S_OK;
}

STDMETHODIMP_(ULONG) CHostTaskManager::AddRef()
{
	return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CHostTaskManager::Release()
{
	if (InterlockedDecrement(&m_cRef) == 0) {
		delete this;
		return 0;
	}

	return m_cRef;
}

STDMETHODIMP CHostTaskManager::GetCurrentTask(IHostTask **pTask)
{
	*pTask = new CHostTask(GetCurrentThread());

	return S_OK;
}

STDMETHODIMP CHostTaskManager::CreateTask(DWORD dwStackSize, LPTHREAD_START_ROUTINE pStartAddress, PVOID pParameter, IHostTask **ppTask)
{
	HANDLE    hThread;
	CHostTask *p;
	TCHAR     szBuf[256];

	hThread = CreateThread(NULL, dwStackSize, pStartAddress, pParameter, CREATE_SUSPENDED, 0);
	p = new CHostTask(hThread);
	*ppTask = p;

	g_aTask[g_nThreadCount++] = p;

	wsprintf(szBuf, TEXT("Thread ID %d"), GetThreadId(hThread));
	SendMessage(g_hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);

	return S_OK;
}

STDMETHODIMP CHostTaskManager::Sleep(DWORD dwMilliseconds, DWORD option)
{
	::Sleep(dwMilliseconds);

	return S_OK;
}

STDMETHODIMP CHostTaskManager::SwitchToTask(DWORD option)
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::SetUILocale(LCID lcid)
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::SetLocale(LCID lcid)
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::CallNeedsHostHook(SIZE_T target, BOOL *pbCallNeedsHostHook)
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::LeaveRuntime(SIZE_T target)
{
	return S_OK;
}

STDMETHODIMP CHostTaskManager::EnterRuntime()
{
	return S_OK;
}

STDMETHODIMP CHostTaskManager::ReverseLeaveRuntime()
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::ReverseEnterRuntime()
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::BeginDelayAbort()
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::EndDelayAbort()
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::BeginThreadAffinity()
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::EndThreadAffinity()
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::SetStackGuarantee(ULONG guarantee)
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::GetStackGuarantee(ULONG *pGuarantee)
{
	return E_NOTIMPL;
}

STDMETHODIMP CHostTaskManager::SetCLRTaskManager(ICLRTaskManager *pManager)
{
	m_pTaskManager = pManager;

	return S_OK;
}


// CHostTask


CHostTask::CHostTask(HANDLE hThread)
{
	m_cRef = 1;
	m_hThread = hThread;
	m_pTask = NULL;
}

CHostTask::~CHostTask()
{
	if (m_hThread != NULL)
		CloseHandle(m_hThread);
}

STDMETHODIMP CHostTask::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IHostTask))
		*ppvObject = static_cast(this);
	else
		return E_NOINTERFACE;

	AddRef();
	
	return S_OK;
}

STDMETHODIMP_(ULONG) CHostTask::AddRef()
{
	return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CHostTask::Release()
{
	if (InterlockedDecrement(&m_cRef) == 0) {
		delete this;
		return 0;
	}

	return m_cRef;
}

STDMETHODIMP CHostTask::Start()
{
	ResumeThread(m_hThread);
	
	return S_OK;
}

STDMETHODIMP CHostTask::Alert()
{
	return S_OK;
}

STDMETHODIMP CHostTask::Join(DWORD dwMilliseconds, DWORD option)
{
	DWORD dwResult;

	dwResult = WaitForSingleObject(m_hThread, dwMilliseconds);
	if (dwResult == WAIT_OBJECT_0)
		return S_OK;
	else if (dwResult == WAIT_TIMEOUT)
		return HOST_E_TIMEOUT;
	else if (dwResult == WAIT_IO_COMPLETION)
		return HOST_E_INTERRUPTED;
	else
		;

	return E_FAIL;
}

STDMETHODIMP CHostTask::SetPriority(int newPriority)
{
	SetThreadPriority(m_hThread, newPriority);

	return S_OK;
}

STDMETHODIMP CHostTask::GetPriority(int *pPriority)
{
	*pPriority = GetThreadPriority(m_hThread);

	return S_OK;
}

STDMETHODIMP CHostTask::SetCLRTask(ICLRTask *pCLRTask)
{
	m_pTask = pCLRTask;

	return S_OK;
}

void CHostTask::Suspend()
{
	SuspendThread(m_hThread);
	m_pTask->SwitchOut();
}

void CHostTask::Resume()
{
	m_pTask->SwitchIn(m_hThread);
	ResumeThread(m_hThread);
}

void CHostTask::Abort()
{
	m_pTask->Abort();
}

void CHostTask::GetMemStats(ULONGLONG *puSize)
{
	COR_GC_THREAD_STATS status;

	m_pTask->GetMemStats(&status);
	*puSize = status.PerThreadAllocation;
}

BOOL CHostTask::IsExit()
{
	DWORD dwStatus;

	GetExitCodeThread(m_hThread, &dwStatus);
	
	return dwStatus != STILL_ACTIVE;
}

このプログラムは、ホストが作成したスレッドのIDをリストボックスに列挙します。 スレッドに対して何らかの操作を行いたい場合はその項目を選択し、 メニューの「スレッド操作」から任意の項目を選択します。 リストボックスには、CLRが既定で作成するスレッドが列挙されているはずですが、 これに対して操作を行っても特に面白いことはありません。 メニューから「メソッドの呼び出し」を選択すれば、 マネージコードでSystem.Threading.Threadが呼ばれてスレッドが作成されるため、 そのスレッドに対して操作を行うことになります。

IHostTaskManagerの中で、実装が分かりやすいメソッドを順に示します。

STDMETHODIMP CHostTaskManager::SetCLRTaskManager(ICLRTaskManager *pManager)
{
	m_pTaskManager = pManager;

	return S_OK;
}

SetCLRTaskManagerは、ICLRTaskManagerをホストに渡す目的で呼ばれます。 このインターフェースは、CLRを通じてタスクを管理したい場合に使用できます。 ホストはこれを無視することもできますが、メンバとして保存することもできます。

STDMETHODIMP CHostTaskManager::CreateTask(DWORD dwStackSize, LPTHREAD_START_ROUTINE pStartAddress, PVOID pParameter, IHostTask **ppTask)
{
	HANDLE    hThread;
	CHostTask *p;
	TCHAR     szBuf[256];

	hThread = CreateThread(NULL, dwStackSize, pStartAddress, pParameter, CREATE_SUSPENDED, 0);
	p = new CHostTask(hThread);
	*ppTask = p;

	g_aTask[g_nThreadCount++] = p;

	wsprintf(szBuf, TEXT("Thread ID %d"), GetThreadId(hThread));
	SendMessage(g_hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);

	return S_OK;
}

CreateTaskは、マネージコードでスレッドが作成されようとする際に呼ばれます。 ここではpStartAddressを開始アドレスとしたスレッド(またはファイバ)を作成することになります。 CREATE_SUSPENDEDを指定しているのは、スレッドを実行させるためのメソッドが別個用意されているからであり、 この時点では実行しないようにしておきます。 CLRは作成されたスレッドをIHostTaskで識別したがっているので、 これを実装したCHostTaskというクラスを作成しています。 このクラスは1つのスレッドを識別しますから、スレッドのハンドルを渡しておきます。 g_aTaskはグローバルに定義されたCHostTaskの配列であり、これはリストボックスで項目を選択する際に必要です。 SendMessageによって、リストボックスにスレッドのIDを追加されます。

STDMETHODIMP CHostTaskManager::Sleep(DWORD dwMilliseconds, DWORD option)
{
	::Sleep(dwMilliseconds);

	return S_OK;
}

Sleepは、マネージコードでThread.Sleepが呼ばれた際に呼ばれます。 ここでは、dwMillisecondsの値だけ待機するべきであるため、Win32のSleep関数を呼び出しています。

続いて、IHostTaskの実装について見ていきます。

STDMETHODIMP CHostTask::SetCLRTask(ICLRTask *pCLRTask)
{
	m_pTask = pCLRTask;

	return S_OK;
}

SetCLRTaskは、ICLRTaskをホストに渡す目的で呼ばれます。 IHostTaskはCLRがホストを通じてタスクを操作する場合に使用されますが、 ICLRTaskはホストがCLRを通じてタスクを操作する場合に使用します。 つまり、1つのタスクを表現するインターフェースがホストとCLRで異なります。

STDMETHODIMP CHostTask::Start()
{
	ResumeThread(m_hThread);
	
	return S_OK;
}

Startは、スレッドが動作を開始すべきタイミングになった際に呼ばれます。 スレッドは一時停止状態で作成されていたため、ResumeThreadで動作を開始することになります。 Startは、IHostTaskManager::CreateTaskによって作成されたタスクに対して呼ばれますが、 IHostTaskManager::GetCurrentTaskによって取得されたタスクに対しては呼ばれません。

STDMETHODIMP CHostTask::Alert()
{
	return S_OK;
}

Alertは、マネージコードでSystem.Threading.Thread.Abortが呼ばれた場合や、 ホストがICLRTask::Abortを呼び出した場合に呼ばれます。 System.Threading.Thread.Abortをスレッドが自分自身で呼び出した場合は、 呼ばれないことに注意してください。 Alertが呼ばれた時点ではスレッドは生存していますが、 メソッドから制御を返した後には終了します。

STDMETHODIMP CHostTask::Join(DWORD dwMilliseconds, DWORD option)
{
	DWORD dwResult;

	dwResult = WaitForSingleObject(m_hThread, dwMilliseconds);
	if (dwResult == WAIT_OBJECT_0)
		return S_OK;
	else if (dwResult == WAIT_TIMEOUT)
		return HOST_E_TIMEOUT;
	else if (dwResult == WAIT_IO_COMPLETION)
		return HOST_E_INTERRUPTED;
	else
		;

	return E_FAIL;
}

Joinは、マネージコードでSystem.Threading.Thread.Joinが呼ばれた場合や、 ホストがIRuntimeHost::Stopを呼び出した場合に呼ばれます。 System.Threading.Thread.Joinが呼ばれるということは、スレッドが終了するまでdwMillisecondsだけ待機するということであるため、 WaitForSingleObjectにdwMillisecondsを指定します。 待機しているスレッドが制御を返すようにして、終了処理を促したい場合は、 ホストがイベントオブジェクトを作成すればよいでしょう。 これにより、joinではWaitForMultipleObjectsを呼び出せるようになり、 ホストはスレッドを終了させる場合にSetEventを呼び出すことができます。 このように、スレッドが終了することを分かっている場合は、 ICLR::Exitを呼び出してCLRに通知しておくとよいでしょう。

WM_COMMANDのelse if文では、「スレッド操作」で選択された項目に関連する処理を行います。 処理を行うにあたって、スレッドハンドルやICLRTaskが必要になるため、 それらを持っているCHostTaskに処理を任せます。

void CHostTask::Suspend()
{
	SuspendThread(m_hThread);
	m_pTask->SwitchOut();
}

Suspendは、「一時停止」という項目が選択された場合に呼ばれます。 この場合は、SuspendThreadでスレッドを一時停止させ、 スレッドがタスクを実行できなくなったことをCLRに通知するために、 ICLRTask::SwitchOutを呼び出します。

void CHostTask::Resume()
{
	m_pTask->SwitchIn(m_hThread);
	ResumeThread(m_hThread);
}

Resumeは、「再開」という項目が選択された場合に呼ばれます。 この場合は、タスクを実行できるようになったことをCLRに通知するために、 ICLRTask::SwitchInを呼び出します。 ResumeThreadによってスレッドの動作が再開されます。

void CHostTask::Abort()
{
	m_pTask->Abort();
}

Abortは、「中止」という項目が選択された場合に呼ばれます。 この場合は、ICLRTask::Abortによってスレッドに例外を発生させます。 これによって、IHostTask::Alertが呼ばれ、制御を返した頃にはスレッドは終了しています。

void CHostTask::GetMemStats(ULONGLONG *puSize)
{
	COR_GC_THREAD_STATS status;

	m_pTask->GetMemStats(&status);
	*puSize = status.PerThreadAllocation;
}

GetMemStatsは、「メモリ情報取得」という項目が選択された場合に呼ばれます。 この場合は、ICLRTask::GetMemStatsによってCOR_GC_THREAD_STATS構造体を初期化します。 この構造体のPerThreadAllocationには、スレッドに割り当てられているメモリのバイト数が格納されています。


戻る