EternalWindows
プロファイル API / プロファイラ サンプル

プロファイラ(DLL)を作成したら、そのプロファイラを目的のプロセスにロードさせなければなりません。 まず、プロファイラはCOMサーバーのDLLであるため、DLLの情報を事前にレジストリへ登録しておきます。 そして次に、プロファイリング対象のプロセスが起動時にプロファイラをロードするために、 そのプロセスに対して適切な環境変数を設定します。 マネージアプリケーションをエクスプローラから起動しても、適切な環境変数の設定は行われませんから、 プロファイラを開発する開発者は、マネージアプリケーションを起動するためのアプリケーションも作成することになります。 そのようなアプリケーションには、次のようなコードが必要になるでしょう。

#include <windows.h>

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	STARTUPINFO         startupInfo;
	PROCESS_INFORMATION processInformation;

	SetEnvironmentVariable(TEXT("COR_ENABLE_PROFILING"), TEXT("1"));
	SetEnvironmentVariable(TEXT("COR_PROFILER"), TEXT("{89F29503-C239-497c-9790-D80187D196EA}"));

	startupInfo.dwFlags = 0;
	GetStartupInfo(&startupInfo);
	CreateProcess(TEXT("C:\\WindowsFormsApplication1.exe"), NULL, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation);

	CloseHandle(processInformation.hThread);
	CloseHandle(processInformation.hProcess);

	return 0;
}

CreateProcessを呼び出しているのは、マネージアプリケーションを起動するためです。 SetEnvironmentVariableは、現在のプロセスに引数の環境変数を設定する関数であり、特定のプロセスに環境変数を設定するわけではありませんが、これは問題ありません。 なぜなら、CreateProcessは現在のプロセスの環境変数を子プロセスの環境変数に継承させることになっているため、 SetEnvironmentVariableに指定した環境変数は子プロセスに反映されるのです。 CLRはプロセスの起動時にCOR_ENABLE_PROFILINGが1である場合は、プロファイラをアドレス空間にロードすべきこと認識します。 そして、COR_PROFILERからプロファイラのCLSIDを取得し、これを基にCoCreateInstanceを呼び出してDLLをロードして、 ICorProfilerCallback2::Initializeを呼び出します。 上記のアプリケーションはランチャーとしてのみ機能していますが、GUIアプリケーションのようにウインドウを作成すれば、 プロファイラが取得したデータをウインドウに表示する役割も果たせるでしょう。 つまり、プロファイラはデータをファイルに保存するのではなく、GUIアプリケーションに送信するようにするわけです。

今回作成したプロファイラのコードを見ていきます。 まず、DLLのdefファイルを示します。

LIBRARY	"mydll"

EXPORTS
	DllCanUnloadNow PRIVATE
	DllGetClassObject PRIVATE
	DllRegisterServer PRIVATE
	DllUnregisterServer PRIVATE

次に、sample.hを示します。

class CProfilerCallback : public ICorProfilerCallback2
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP Initialize(IUnknown *pICorProfilerInfoUnk);
	STDMETHODIMP Shutdown();
	STDMETHODIMP AppDomainCreationStarted(AppDomainID appDomainId);
	STDMETHODIMP AppDomainCreationFinished(AppDomainID appDomainId, HRESULT hrStatus);
	STDMETHODIMP AppDomainShutdownStarted(AppDomainID appDomainId);
	STDMETHODIMP AppDomainShutdownFinished(AppDomainID appDomainId, HRESULT hrStatus);
	STDMETHODIMP AssemblyLoadStarted(AssemblyID assemblyId);
	STDMETHODIMP AssemblyLoadFinished(AssemblyID assemblyId, HRESULT hrStatus);
	STDMETHODIMP AssemblyUnloadStarted(AssemblyID assemblyId);
	STDMETHODIMP AssemblyUnloadFinished(AssemblyID assemblyId, HRESULT hrStatus);
	STDMETHODIMP ModuleLoadStarted(ModuleID moduleId);
	STDMETHODIMP ModuleLoadFinished(ModuleID moduleId, HRESULT hrStatus);
	STDMETHODIMP ModuleUnloadStarted(ModuleID moduleId);
	STDMETHODIMP ModuleUnloadFinished(ModuleID moduleId, HRESULT hrStatus);
	STDMETHODIMP ModuleAttachedToAssembly(ModuleID moduleId, AssemblyID AssemblyId);
	STDMETHODIMP ClassLoadStarted(ClassID classId);
	STDMETHODIMP ClassLoadFinished(ClassID classId, HRESULT hrStatus);
	STDMETHODIMP ClassUnloadStarted(ClassID classId);
	STDMETHODIMP ClassUnloadFinished(ClassID classId, HRESULT hrStatus);
	STDMETHODIMP FunctionUnloadStarted(FunctionID functionId);
	STDMETHODIMP JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock);
	STDMETHODIMP JITCompilationFinished(FunctionID functionId, HRESULT hrStatus, BOOL fIsSafeToBlock);
	STDMETHODIMP JITCachedFunctionSearchStarted(FunctionID functionId, BOOL *pbUseCachedFunction);
	STDMETHODIMP JITCachedFunctionSearchFinished(FunctionID functionId, COR_PRF_JIT_CACHE result);
	STDMETHODIMP JITFunctionPitched(FunctionID functionId);
	STDMETHODIMP JITInlining(FunctionID callerId, FunctionID calleeId, BOOL *pfShouldInline);
	STDMETHODIMP ThreadCreated(ThreadID threadId);
	STDMETHODIMP ThreadDestroyed(ThreadID threadId);
	STDMETHODIMP ThreadAssignedToOSThread(ThreadID managedThreadId, DWORD osThreadId);
	STDMETHODIMP RemotingClientInvocationStarted();
	STDMETHODIMP RemotingClientSendingMessage(GUID *pCookie, BOOL fIsAsync);
	STDMETHODIMP RemotingClientReceivingReply(GUID *pCookie, BOOL fIsAsync);
	STDMETHODIMP RemotingClientInvocationFinished();
	STDMETHODIMP RemotingServerReceivingMessage(GUID *pCookie, BOOL fIsAsync);
	STDMETHODIMP RemotingServerInvocationStarted();
	STDMETHODIMP RemotingServerInvocationReturned();
	STDMETHODIMP RemotingServerSendingReply(GUID *pCookie, BOOL fIsAsync);
	STDMETHODIMP UnmanagedToManagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason);
	STDMETHODIMP ManagedToUnmanagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason);
	STDMETHODIMP RuntimeSuspendStarted(COR_PRF_SUSPEND_REASON suspendReason);
	STDMETHODIMP RuntimeSuspendFinished();
	STDMETHODIMP RuntimeSuspendAborted();
	STDMETHODIMP RuntimeResumeStarted();
	STDMETHODIMP RuntimeResumeFinished();
	STDMETHODIMP RuntimeThreadSuspended(ThreadID threadId);
	STDMETHODIMP RuntimeThreadResumed(ThreadID threadId);
	STDMETHODIMP MovedReferences(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[  ], ObjectID newObjectIDRangeStart[  ], ULONG cObjectIDRangeLength[  ]);
	STDMETHODIMP ObjectAllocated(ObjectID objectId, ClassID classId);
	STDMETHODIMP ObjectsAllocatedByClass(ULONG cClassCount, ClassID classIds[  ], ULONG cObjects[  ]);
	STDMETHODIMP ObjectReferences(ObjectID objectId, ClassID classId, ULONG cObjectRefs, ObjectID objectRefIds[  ]);
	STDMETHODIMP RootReferences(ULONG cRootRefs, ObjectID rootRefIds[  ]);
	STDMETHODIMP ExceptionThrown(ObjectID thrownObjectId);
	STDMETHODIMP ExceptionSearchFunctionEnter(FunctionID functionId);
	STDMETHODIMP ExceptionSearchFunctionLeave();
	STDMETHODIMP ExceptionSearchFilterEnter(FunctionID functionId);
	STDMETHODIMP ExceptionSearchFilterLeave();
	STDMETHODIMP ExceptionSearchCatcherFound(FunctionID functionId);
	STDMETHODIMP ExceptionOSHandlerEnter(UINT_PTR __unused);
	STDMETHODIMP ExceptionOSHandlerLeave(UINT_PTR __unused);
	STDMETHODIMP ExceptionUnwindFunctionEnter(FunctionID functionId);
	STDMETHODIMP ExceptionUnwindFunctionLeave();
	STDMETHODIMP ExceptionUnwindFinallyEnter(FunctionID functionId);
	STDMETHODIMP ExceptionUnwindFinallyLeave();
	STDMETHODIMP ExceptionCatcherEnter(FunctionID functionId, ObjectID objectId);
	STDMETHODIMP ExceptionCatcherLeave();
	STDMETHODIMP COMClassicVTableCreated(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable, ULONG cSlots);
	STDMETHODIMP COMClassicVTableDestroyed(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable);
	STDMETHODIMP ExceptionCLRCatcherFound();
	STDMETHODIMP ExceptionCLRCatcherExecute();

	STDMETHODIMP ThreadNameChanged(ThreadID threadId, ULONG cchName, WCHAR name[  ]);
	STDMETHODIMP GarbageCollectionStarted(int cGenerations, BOOL generationCollected[  ], COR_PRF_GC_REASON reason);
	STDMETHODIMP SurvivingReferences(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[  ], ULONG cObjectIDRangeLength[  ]);
	STDMETHODIMP GarbageCollectionFinished();
	STDMETHODIMP FinalizeableObjectQueued(DWORD finalizerFlags, ObjectID objectID);
	STDMETHODIMP RootReferences2(ULONG cRootRefs, ObjectID rootRefIds[  ], COR_PRF_GC_ROOT_KIND rootKinds[  ], COR_PRF_GC_ROOT_FLAGS rootFlags[  ], UINT_PTR rootIds[  ]);
	STDMETHODIMP HandleCreated(GCHandleID handleId, ObjectID initialObjectId);
	STDMETHODIMP HandleDestroyed(GCHandleID handleId);

	CProfilerCallback();
	~CProfilerCallback();
	
	void WriteMethodInfo(FunctionID funcID, COR_PRF_FUNCTION_ARGUMENT_INFO *argInfo);
	CorElementType GetElementType(PCCOR_SIGNATURE *ppSig, mdTypeDef *pTypeDef, LPBOOL lpbRef, LPBOOL lpbArray);
	void GetElementTypeName(IMetaDataImport *pMetaDataImport, CorElementType type, mdTypeDef typeDef, BOOL bRef, BOOL bArray, LPWSTR lpszBuf);
	void GetValue(LPVOID lpValue, CorElementType type, LPWSTR lpszBuf);
	LPWSTR GetString(LPBYTE lp);
	void GetArray(LPVOID lpValue, CorElementType type, LPWSTR lpszBuf);
	void GetClass(IMetaDataImport *pMetaDataImport, ClassID classId, LPWSTR lpszClassName, LPVOID lpValue);
	void GetObject(LPVOID lpValue, CorElementType type, LPWSTR lpszBuf);
	CorElementType GetElementTypeFromClassName(LPWSTR lpszClassName);
	BOOL FilterClass(FunctionID functionId, LPWSTR lpszFilterClassName);
	void GetFullMethodName(FunctionID functionId, LPWSTR lpszFunctionName);
	
private:
	LONG              m_cRef;
	ICorProfilerInfo2 *m_pProfilerInfo2;	
};

class CClassFactory : public IClassFactory
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
	STDMETHODIMP LockServer(BOOL fLock);
};

次に、ソースファイル(ICorProfilerCallback2の実装)を示します。

#include <windows.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <cor.h>
#include <corprof.h>
#include "sample.h"

#pragma comment (lib, "shlwapi.lib")
#pragma comment (lib, "corguids.lib")

const CLSID CLSID_ProfilerCallback = {0x89f29503, 0xc239, 0x497c, {0x97, 0x90, 0xd8, 0x1, 0x87, 0xd1, 0x96, 0xea}};
const TCHAR g_szClsid[] = TEXT("{89F29503-C239-497c-9790-D80187D196EA}");

LONG              g_lLocks = 0;
HINSTANCE         g_hinstDll = NULL;
CProfilerCallback *g_pProfilerCallback;
CRITICAL_SECTION  g_cs;

void LockModule(BOOL bLock);
BOOL CreateRegistryKey(HKEY hKeyRoot, LPTSTR lpszKey, LPTSTR lpszValue, LPTSTR lpszData);
void FunctionEnterNaked2(FunctionID funcId, UINT_PTR clientData, COR_PRF_FRAME_INFO func, COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo);
void __stdcall FunctionEnterGlobal(FunctionID funcId, UINT_PTR clientData, COR_PRF_FRAME_INFO func, COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo);
void WriteLogFile(LPWSTR lpszData);


// global


void _declspec(naked) FunctionEnterNaked2(FunctionID funcId, UINT_PTR clientData, COR_PRF_FRAME_INFO func, COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo)
{
    __asm
    {
        push    ebp                 // Create a standard frame
        mov     ebp,esp
        pushad                      // Preserve all registers

        mov     eax,[ebp+0x14]      // argumentInfo
        push    eax
        mov     ecx,[ebp+0x10]      // func
        push    ecx
        mov     edx,[ebp+0x0C]      // clientData
        push    edx
        mov     eax,[ebp+0x08]      // funcId
        push    eax
        call    FunctionEnterGlobal

        popad                       // Restore all registers
        pop     ebp                 // Restore EBP
        ret     16
    }
}

void __stdcall FunctionEnterGlobal(FunctionID funcId, UINT_PTR clientData, COR_PRF_FRAME_INFO func, COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo)
{
	if (g_pProfilerCallback != NULL) {
		WCHAR szName[256], szBuf[1024];
		
		if (!g_pProfilerCallback->FilterClass(funcId, L"Program"))
			return;

		g_pProfilerCallback->GetFullMethodName(funcId, szName);
		wsprintfW(szBuf, L"--------Enter %s--------", szName);
		WriteLogFile(szBuf);
		
		g_pProfilerCallback->WriteMethodInfo(funcId, argumentInfo);
	}
}

void WriteLogFile(LPWSTR lpszData)
{
	TCHAR  szFileName[] = TEXT("\\profile.txt"); 
	TCHAR  szDesktopPath[MAX_PATH];
	WCHAR  szBuf[1024];
	HANDLE hFile;
	DWORD  dwResult;
	
	SHGetSpecialFolderPath(NULL, szDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
	lstrcat(szDesktopPath, szFileName);

	EnterCriticalSection(&g_cs);

	hFile = CreateFile(szDesktopPath, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		LeaveCriticalSection(&g_cs);
		return;
	}
	
	SetFilePointer(hFile, 0, NULL, FILE_END);
	
	wsprintfW(szBuf, L"%s\r\n", lpszData);
	WriteFile(hFile, szBuf, lstrlenW(szBuf) * sizeof(WCHAR), &dwResult, NULL);

	CloseHandle(hFile);

	LeaveCriticalSection(&g_cs);
}


// CProfilerCallback


CProfilerCallback::CProfilerCallback()
{
	m_cRef = 1;
	m_pProfilerInfo2 = NULL;

	LockModule(TRUE);
}

CProfilerCallback::~CProfilerCallback()
{
	LockModule(FALSE);
}

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

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ICorProfilerCallback) || IsEqualIID(riid, IID_ICorProfilerCallback2))
		*ppvObject = static_cast<ICorProfilerCallback2 *>(this);
	else
		return E_NOINTERFACE;

	AddRef();

	return S_OK;
}

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

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

	return m_cRef;
}

STDMETHODIMP CProfilerCallback::Initialize(IUnknown *pICorProfilerInfoUnk)
{
	pICorProfilerInfoUnk->QueryInterface(IID_PPV_ARGS(&m_pProfilerInfo2));

	m_pProfilerInfo2->SetEventMask(COR_PRF_MONITOR_ENTERLEAVE | COR_PRF_ENABLE_FUNCTION_RETVAL | COR_PRF_ENABLE_FUNCTION_ARGS);

	m_pProfilerInfo2->SetEnterLeaveFunctionHooks2(FunctionEnterNaked2, NULL, NULL);
	
	InitializeCriticalSection(&g_cs);

	WriteLogFile(L"Initialize");
	
	g_pProfilerCallback = this;

	return S_OK;
}

STDMETHODIMP CProfilerCallback::Shutdown()
{
	DeleteCriticalSection(&g_cs);

	WriteLogFile(L"Shutdown");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::AppDomainCreationStarted(AppDomainID appDomainId)
{
	WriteLogFile(L"AppDomainCreationStarted");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::AppDomainCreationFinished(AppDomainID appDomainId, HRESULT hrStatus)
{
	WriteLogFile(L"AppDomainCreationFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::AppDomainShutdownStarted(AppDomainID appDomainId)
{
	WriteLogFile(L"AppDomainShutdownStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::AppDomainShutdownFinished(AppDomainID appDomainId, HRESULT hrStatus)
{
	WriteLogFile(L"AppDomainShutdownFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::AssemblyLoadStarted(AssemblyID assemblyId)
{
	WriteLogFile(L"AssemblyLoadStarted");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::AssemblyLoadFinished(AssemblyID assemblyId, HRESULT hrStatus)
{
	WriteLogFile(L"AssemblyLoadFinished");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::AssemblyUnloadStarted(AssemblyID assemblyId)
{
	WriteLogFile(L"AssemblyUnloadStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::AssemblyUnloadFinished(AssemblyID assemblyId, HRESULT hrStatus)
{
	WriteLogFile(L"AssemblyUnloadFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ModuleLoadStarted(ModuleID moduleId)
{
	WriteLogFile(L"ModuleLoadStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ModuleLoadFinished(ModuleID moduleId, HRESULT hrStatus)
{
	WriteLogFile(L"ModuleLoadFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ModuleUnloadStarted(ModuleID moduleId)
{
	WriteLogFile(L"ModuleUnloadStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ModuleUnloadFinished(ModuleID moduleId, HRESULT hrStatus)
{
	WriteLogFile(L"ModuleUnloadFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ModuleAttachedToAssembly(ModuleID moduleId, AssemblyID AssemblyId)
{
	WriteLogFile(L"ModuleAttachedToAssembly");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ClassLoadStarted(ClassID classId)
{
	WriteLogFile(L"ClassLoadStarted");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::ClassLoadFinished(ClassID classId, HRESULT hrStatus)
{
	WriteLogFile(L"ClassLoadFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ClassUnloadStarted(ClassID classId)
{
	WriteLogFile(L"ClassUnloadStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ClassUnloadFinished(ClassID classId, HRESULT hrStatus)
{
	WriteLogFile(L"ClassUnloadFinished");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::FunctionUnloadStarted(FunctionID functionId)
{
	WriteLogFile(L"FunctionUnloadStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::JITCompilationStarted(FunctionID functionId, BOOL fIsSafeToBlock)
{
	WriteLogFile(L"JITCompilationStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::JITCompilationFinished(FunctionID functionId, HRESULT hrStatus, BOOL fIsSafeToBlock)
{
	WriteLogFile(L"JITCompilationFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::JITCachedFunctionSearchStarted(FunctionID functionId, BOOL *pbUseCachedFunction)
{
	WriteLogFile(L"JITCachedFunctionSearchStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::JITCachedFunctionSearchFinished(FunctionID functionId, COR_PRF_JIT_CACHE result)
{
	WriteLogFile(L"JITCachedFunctionSearchFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::JITFunctionPitched(FunctionID functionId)
{
	WriteLogFile(L"JITFunctionPitched");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::JITInlining(FunctionID callerId, FunctionID calleeId, BOOL *pfShouldInline)
{
	WriteLogFile(L"JITInlining");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ThreadCreated(ThreadID threadId)
{
	WriteLogFile(L"ThreadCreated");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::ThreadDestroyed(ThreadID threadId)
{
	WriteLogFile(L"ThreadDestroyed");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::ThreadAssignedToOSThread(ThreadID managedThreadId, DWORD osThreadId)
{
	WriteLogFile(L"ThreadAssignedToOSThread");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RemotingClientInvocationStarted()
{
	WriteLogFile(L"RemotingClientInvocationStarted");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::RemotingClientSendingMessage(GUID *pCookie, BOOL fIsAsync)
{
	WriteLogFile(L"RemotingClientSendingMessage");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RemotingClientReceivingReply(GUID *pCookie, BOOL fIsAsync)
{
	WriteLogFile(L"RemotingClientReceivingReply");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RemotingClientInvocationFinished()
{
	WriteLogFile(L"RemotingClientInvocationFinished");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::RemotingServerReceivingMessage(GUID *pCookie, BOOL fIsAsync)
{
	WriteLogFile(L"RemotingServerReceivingMessage");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::RemotingServerInvocationStarted()
{
	WriteLogFile(L"RemotingServerInvocationStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RemotingServerInvocationReturned()
{
	WriteLogFile(L"RemotingServerInvocationReturned");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::RemotingServerSendingReply(GUID *pCookie, BOOL fIsAsync)
{
	WriteLogFile(L"RemotingServerSendingReply");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::UnmanagedToManagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason)
{
	WriteLogFile(L"UnmanagedToManagedTransition");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ManagedToUnmanagedTransition(FunctionID functionId, COR_PRF_TRANSITION_REASON reason)
{
	WriteLogFile(L"UnmanagedToManagedTransition");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RuntimeSuspendStarted(COR_PRF_SUSPEND_REASON suspendReason)
{
	WriteLogFile(L"RuntimeSuspendStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RuntimeSuspendFinished()
{
	WriteLogFile(L"RuntimeSuspendFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RuntimeSuspendAborted()
{
	WriteLogFile(L"RuntimeSuspendAborted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RuntimeResumeStarted()
{
	WriteLogFile(L"RuntimeResumeStarted");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RuntimeResumeFinished()
{
	WriteLogFile(L"RuntimeResumeFinished");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RuntimeThreadSuspended(ThreadID threadId)
{
	WriteLogFile(L"RuntimeThreadSuspended");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RuntimeThreadResumed(ThreadID threadId)
{
	WriteLogFile(L"RuntimeThreadResumed");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::MovedReferences(ULONG cMovedObjectIDRanges, ObjectID oldObjectIDRangeStart[  ], ObjectID newObjectIDRangeStart[  ], ULONG cObjectIDRangeLength[  ])
{
	WriteLogFile(L"MovedReferences");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ObjectAllocated(ObjectID objectId, ClassID classId)
{
	WriteLogFile(L"ObjectAllocated");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ObjectsAllocatedByClass(ULONG cClassCount, ClassID classIds[  ], ULONG cObjects[  ])
{
	WriteLogFile(L"ObjectsAllocatedByClass");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ObjectReferences(ObjectID objectId, ClassID classId, ULONG cObjectRefs, ObjectID objectRefIds[  ])
{
	WriteLogFile(L"ObjectReferences");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::RootReferences(ULONG cRootRefs, ObjectID rootRefIds[  ])
{
	WriteLogFile(L"RootReferences");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionThrown(ObjectID thrownObjectId)
{
	WriteLogFile(L"ExceptionThrown");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionSearchFunctionEnter(FunctionID functionId)
{
	WriteLogFile(L"ExceptionSearchFunctionEnter");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionSearchFunctionLeave()
{
	WriteLogFile(L"ExceptionSearchFunctionLeave");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionSearchFilterEnter(FunctionID functionId)
{
	WriteLogFile(L"ExceptionSearchFilterEnter");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionSearchFilterLeave()
{
	WriteLogFile(L"ExceptionSearchFilterLeave");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionSearchCatcherFound(FunctionID functionId)
{
	WriteLogFile(L"ExceptionSearchCatcherFound");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionOSHandlerEnter(UINT_PTR __unused)
{
	WriteLogFile(L"ExceptionOSHandlerEnter");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionOSHandlerLeave(UINT_PTR __unused)
{
	WriteLogFile(L"ExceptionOSHandlerLeave");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionUnwindFunctionEnter(FunctionID functionId)
{
	WriteLogFile(L"ExceptionUnwindFunctionEnter");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionUnwindFunctionLeave()
{
	WriteLogFile(L"ExceptionUnwindFunctionLeave");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionUnwindFinallyEnter(FunctionID functionId)
{
	WriteLogFile(L"ExceptionUnwindFinallyEnter");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionUnwindFinallyLeave()
{
	WriteLogFile(L"ExceptionUnwindFinallyLeave");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionCatcherEnter(FunctionID functionId, ObjectID objectId)
{
	WriteLogFile(L"ExceptionCatcherEnter");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionCatcherLeave()
{
	WriteLogFile(L"ExceptionCatcherLeave");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::COMClassicVTableCreated(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable, ULONG cSlots)
{
	WriteLogFile(L"COMClassicVTableCreated");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::COMClassicVTableDestroyed(ClassID wrappedClassId, REFGUID implementedIID, void *pVTable)
{
	WriteLogFile(L"COMClassicVTableDestroyed");

	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionCLRCatcherFound()
{
	WriteLogFile(L"ExceptionCLRCatcherFound");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::ExceptionCLRCatcherExecute()
{
	WriteLogFile(L"ExceptionCLRCatcherExecute");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::ThreadNameChanged(ThreadID threadId, ULONG cchName, WCHAR name[  ])
{
	WriteLogFile(L"ThreadNameChanged");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::GarbageCollectionStarted(int cGenerations, BOOL generationCollected[  ], COR_PRF_GC_REASON reason)
{
	WriteLogFile(L"GarbageCollectionStarted");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::SurvivingReferences(ULONG cSurvivingObjectIDRanges, ObjectID objectIDRangeStart[  ], ULONG cObjectIDRangeLength[  ])
{
	WriteLogFile(L"SurvivingReferences");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::GarbageCollectionFinished()
{
	WriteLogFile(L"GarbageCollectionFinished");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::FinalizeableObjectQueued(DWORD finalizerFlags, ObjectID objectID)
{
	WriteLogFile(L"FinalizeableObjectQueued");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::RootReferences2(ULONG cRootRefs, ObjectID rootRefIds[  ], COR_PRF_GC_ROOT_KIND rootKinds[  ], COR_PRF_GC_ROOT_FLAGS rootFlags[  ], UINT_PTR rootIds[  ])
{
	WriteLogFile(L"RootReferences2");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::HandleCreated(GCHandleID handleId, ObjectID initialObjectId)
{
	WriteLogFile(L"HandleCreated");
	
	return S_OK;
}

STDMETHODIMP CProfilerCallback::HandleDestroyed(GCHandleID handleId)
{
	WriteLogFile(L"HandleDestroyed");
	
	return S_OK;
}


// CClassFactory


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

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

	AddRef();
	
	return S_OK;
}

STDMETHODIMP_(ULONG) CClassFactory::AddRef()
{
	LockModule(TRUE);

	return 2;
}

STDMETHODIMP_(ULONG) CClassFactory::Release()
{
	LockModule(FALSE);

	return 1;
}

STDMETHODIMP CClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
{
	CProfilerCallback *p;
	HRESULT           hr;
	
	*ppvObject = NULL;

	if (pUnkOuter != NULL)
		return CLASS_E_NOAGGREGATION;

	p = new CProfilerCallback();
	if (p == NULL)
		return E_OUTOFMEMORY;

	hr = p->QueryInterface(riid, ppvObject);
	p->Release();

	return hr;
}

STDMETHODIMP CClassFactory::LockServer(BOOL fLock)
{
	LockModule(fLock);

	return S_OK;
}


// DLL Export


STDAPI DllCanUnloadNow(void)
{
	return g_lLocks == 0 ? S_OK : S_FALSE;
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
	static CClassFactory serverFactory;
	HRESULT hr;
	
	*ppv = NULL;

	if (IsEqualCLSID(rclsid, CLSID_ProfilerCallback))
		hr = serverFactory.QueryInterface(riid, ppv);
	else
		hr = CLASS_E_CLASSNOTAVAILABLE;

	return hr;
}

STDAPI DllRegisterServer(void)
{
	TCHAR szModulePath[256];
	TCHAR szKey[256];

	wsprintf(szKey, TEXT("CLSID\\%s"), g_szClsid);
	if (!CreateRegistryKey(HKEY_CLASSES_ROOT, szKey, NULL, TEXT("Profiler Sample")))
		return E_FAIL;

	GetModuleFileName(g_hinstDll, szModulePath, sizeof(szModulePath) / sizeof(TCHAR));
	wsprintf(szKey, TEXT("CLSID\\%s\\InprocServer32"), g_szClsid);
	if (!CreateRegistryKey(HKEY_CLASSES_ROOT, szKey, NULL, szModulePath))
		return E_FAIL;
	
	wsprintf(szKey, TEXT("CLSID\\%s\\InprocServer32"), g_szClsid);
	if (!CreateRegistryKey(HKEY_CLASSES_ROOT, szKey, TEXT("ThreadingModel"), TEXT("Both")))
		return E_FAIL;

	return S_OK;
}

STDAPI DllUnregisterServer(void)
{
	TCHAR szKey[256];

	wsprintf(szKey, TEXT("CLSID\\%s"), g_szClsid);
	SHDeleteKey(HKEY_CLASSES_ROOT, szKey);

	return S_OK;
}

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
{
	switch (dwReason) {

	case DLL_PROCESS_ATTACH:
		g_hinstDll = hinstDll;
		DisableThreadLibraryCalls(hinstDll);
		return TRUE;

	}

	return TRUE;
}


// Function


void LockModule(BOOL bLock)
{
	if (bLock)
		InterlockedIncrement(&g_lLocks);
	else
		InterlockedDecrement(&g_lLocks);
}

BOOL CreateRegistryKey(HKEY hKeyRoot, LPTSTR lpszKey, LPTSTR lpszValue, LPTSTR lpszData)
{
	HKEY hKey;
	LONG lResult;

	lResult = RegCreateKeyEx(hKeyRoot, lpszKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
	if (lResult != ERROR_SUCCESS)
		return FALSE;

	RegSetValueEx(hKey, lpszValue, 0, REG_SZ, (LPBYTE)lpszData, lstrlen(lpszData) * sizeof(TCHAR));
	RegCloseKey(hKey);
	
	return TRUE;
}

次に、ソースファイル(独自メソッドの実装)を示します。

#include <windows.h>
#include <cor.h>
#include <corprof.h>
#include "sample.h"

extern void WriteLogFile(LPWSTR lpszData);

void CProfilerCallback::WriteMethodInfo(FunctionID functionId, COR_PRF_FUNCTION_ARGUMENT_INFO *pArgInfo)
{
	IMetaDataImport *pMetaDataImport;
	PCCOR_SIGNATURE pSig, p, pParamStart;
	WCHAR           szName[256], szType[256], szValue[256], szBuf[1024];
	ULONG           i;
	CorElementType  type;
	mdMethodDef     methodDef;
	mdTypeDef       typeDef;
	BOOL            bArray, bRef;
	LPVOID          *lplpValue;

	m_pProfilerInfo2->GetTokenAndMetaDataFromFunction(functionId, IID_IMetaDataImport, (IUnknown **)&pMetaDataImport, &methodDef);
	pMetaDataImport->GetMethodProps(methodDef, NULL, szName, 256, NULL, NULL, &pSig, NULL, NULL, NULL);

	p = &pSig[2];
	type = GetElementType(&p, &typeDef, &bRef, &bArray);
	pParamStart = p;
	GetElementTypeName(pMetaDataImport, type, typeDef, bRef, bArray, szType);

	wsprintfW(szBuf, L"%s %s(", szType, szName);

	for (i = 0; i < pArgInfo->numRanges; i++) {
		type = GetElementType(&p, &typeDef, &bRef, &bArray);
		GetElementTypeName(pMetaDataImport, type, typeDef, bRef, bArray, szType);
		lstrcatW(szBuf, szType);
		if (i + 1 != pArgInfo->numRanges)
			lstrcatW(szBuf, L", ");
	}
	lstrcatW(szBuf, L")");
	WriteLogFile(szBuf);

	p = pParamStart;
	for (i = 0; i < pArgInfo->numRanges; i++) {
		lplpValue = (LPVOID *)pArgInfo->ranges[i].startAddress;

		type = GetElementType(&p, &typeDef, &bRef, &bArray);
		GetElementTypeName(pMetaDataImport, type, typeDef, bRef, bArray, szType);

		if (bArray) {
			GetArray(lplpValue, type, szValue);
			wsprintfW(szBuf, L"%s = %s", szType, szValue);
			WriteLogFile(szBuf);
		}
		else if (type == ELEMENT_TYPE_CLASS) {
			ClassID  classId;
			ObjectID oid = *(ObjectID *)(lplpValue);

			m_pProfilerInfo2->GetClassFromObject(oid, &classId);
			GetClass(pMetaDataImport, classId, szType, *lplpValue);
		}
		else if (type == ELEMENT_TYPE_VALUETYPE) {
			ClassID  classId;
			ModuleID moduleId;

			m_pProfilerInfo2->GetFunctionInfo(functionId, 0, &moduleId, 0);
			m_pProfilerInfo2->GetClassFromToken(moduleId, typeDef, &classId);
			
			GetClass(pMetaDataImport, classId, szType, lplpValue);
		}
		else {
			GetValue(bRef ? *lplpValue : lplpValue, type, szValue);
			wsprintfW(szBuf, L"%s = %s", szType, szValue);
			WriteLogFile(szBuf);
		}
	}

	pMetaDataImport->Release();
}

CorElementType CProfilerCallback::GetElementType(PCCOR_SIGNATURE *ppSig, mdTypeDef *pTypeDef, LPBOOL lpbRef, LPBOOL lpbArray)
{
	CorElementType type = (CorElementType)**ppSig;

	*ppSig += 1;

	if (type == ELEMENT_TYPE_SZARRAY) {
		type = (CorElementType)**ppSig;
		*ppSig += 1;
		*lpbArray = TRUE;
	}
	else
		*lpbArray = FALSE;
	
	if (type == ELEMENT_TYPE_BYREF) {
		type = (CorElementType)**ppSig;
		*ppSig += 1;
		*lpbRef = TRUE;
	}
	else
		*lpbRef = FALSE;
	
	if (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_CLASS)
		*ppSig += CorSigUncompressToken(*ppSig, pTypeDef);
	else
		*pTypeDef = mdTypeDefNil;

	return type;
}

void CProfilerCallback::GetElementTypeName(IMetaDataImport *pMetaDataImport, CorElementType type, mdTypeDef typeDef, BOOL bRef, BOOL bArray, LPWSTR lpszBuf)
{
	WCHAR szType[256];

	if (bArray) {	
		GetElementTypeName(pMetaDataImport, type, typeDef, FALSE, FALSE, szType);
		wsprintfW(lpszBuf, L"%s[]", szType);
		return;
	}

	if (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_CLASS) {
		pMetaDataImport->GetTypeDefProps(typeDef, lpszBuf, 256, NULL, NULL, NULL);
		return;
	}
	
	if (type == ELEMENT_TYPE_VOID)
		lstrcpyW(szType, L"void");
	else if (type == ELEMENT_TYPE_BOOLEAN)
		lstrcpyW(szType, L"bool");
	else if (type == ELEMENT_TYPE_CHAR)
		lstrcpyW(szType, L"char");
	else if (type == ELEMENT_TYPE_I1)
		lstrcpyW(szType, L"sbyte");
	else if (type == ELEMENT_TYPE_U1)
		lstrcpyW(szType, L"byte");
	else if (type == ELEMENT_TYPE_I2)
		lstrcpyW(szType, L"short");
	else if (type == ELEMENT_TYPE_U2)
		lstrcpyW(szType, L"ushort");
	else if (type == ELEMENT_TYPE_I4)
		lstrcpyW(szType, L"int");
	else if (type == ELEMENT_TYPE_U4)
		lstrcpyW(szType, L"uint");
	else if (type == ELEMENT_TYPE_I8)
		lstrcpyW(szType, L"long");
	else if (type == ELEMENT_TYPE_U8)
		lstrcpyW(szType, L"ulong");
	else if (type == ELEMENT_TYPE_STRING)
		lstrcpyW(szType, L"string");
	else if (type == ELEMENT_TYPE_OBJECT)
		lstrcpyW(szType, L"object");
	else
		wsprintfW(szType, L"unknown-type %x", type);

	if (bRef)
		wsprintfW(lpszBuf, L"ref %s", szType);
	else
		lstrcpyW(lpszBuf, szType);
}

void CProfilerCallback::GetValue(LPVOID lpValue, CorElementType type, LPWSTR lpszBuf)
{
	if (type == ELEMENT_TYPE_VOID)
		lstrcpyW(lpszBuf, L"void");
	else if (type == ELEMENT_TYPE_BOOLEAN) {
		if (*(bool *)lpValue == true)
			lstrcpyW(lpszBuf, L"true");
		else
			lstrcpyW(lpszBuf, L"false");
	}
	else if (type == ELEMENT_TYPE_CHAR)
		wsprintfW(lpszBuf, L"%c", *(char *)lpValue);	
	else if (type == ELEMENT_TYPE_I1)
		wsprintfW(lpszBuf, L"%d", *(char *)lpValue);
	else if (type == ELEMENT_TYPE_U1)
		wsprintfW(lpszBuf, L"%u", *(unsigned char *)lpValue);
	else if (type == ELEMENT_TYPE_I2)
		wsprintfW(lpszBuf, L"%d", *(short *)lpValue);
	else if (type == ELEMENT_TYPE_U2)
		wsprintfW(lpszBuf, L"%u", *(unsigned short *)lpValue);
	else if (type == ELEMENT_TYPE_I4)
		wsprintfW(lpszBuf, L"%d", *(int *)lpValue);
	else if (type == ELEMENT_TYPE_U4)
		wsprintfW(lpszBuf, L"%u", *(unsigned int *)lpValue);
	else if (type == ELEMENT_TYPE_I8)
		wsprintfW(lpszBuf, L"%d", *(LONGLONG *)lpValue);
	else if (type == ELEMENT_TYPE_U8)
		wsprintfW(lpszBuf, L"%u", *(ULONGLONG *)lpValue);
	else if (type == ELEMENT_TYPE_STRING)
		lstrcpyW(lpszBuf, GetString(*((LPBYTE *)lpValue)));
	else if (type == ELEMENT_TYPE_CLASS)
		lstrcpyW(lpszBuf, L"---");
	else if (type == ELEMENT_TYPE_VALUETYPE)
		lstrcpyW(lpszBuf, L"---");
	else if (type == ELEMENT_TYPE_OBJECT)
		GetObject(lpValue, type, lpszBuf);
	else
		lstrcpyW(lpszBuf, L"?");
}

LPWSTR CProfilerCallback::GetString(LPBYTE lp)
{
	HRESULT hr;
	LPWSTR  lpString;
	ULONG   uBufferOffset;
	
	hr = m_pProfilerInfo2->GetStringLayout(NULL, NULL, &uBufferOffset);
	if (FAILED(hr))
		return NULL;
	
	lpString = (LPWSTR)(lp + uBufferOffset);
	
	return lpString;
}

void CProfilerCallback::GetArray(LPVOID lpValue, CorElementType type, LPWSTR lpszBuf)
{
	ObjectID oid = *(ObjectID *)(lpValue);
	ULONG32  i, uDimSize;
	int      nDimLowerBound;
	PBYTE    pData;
	WCHAR    szValue[256], szBuf[1024];

	m_pProfilerInfo2->GetArrayObjectInfo(oid, 1, &uDimSize, &nDimLowerBound, &pData);

	lstrcpyW(lpszBuf, L"{");

	for (i = nDimLowerBound; i < uDimSize; i++) {
		GetValue(pData, type, szValue);
		wsprintfW(szBuf, L"%s", szValue);
		if (i + 1 != uDimSize)
			lstrcatW(szBuf, L", ");

		lstrcatW(lpszBuf, szBuf);

		if (type == ELEMENT_TYPE_I4)
			pData += sizeof(int);
		else if (type == ELEMENT_TYPE_CHAR)
			pData += sizeof(char);
		else if (type == ELEMENT_TYPE_I1 || type == ELEMENT_TYPE_U1)
			pData += sizeof(BYTE);
		else if (type == ELEMENT_TYPE_I2 || type == ELEMENT_TYPE_U2)
			pData += sizeof(short);
		else if (type == ELEMENT_TYPE_I4 || type == ELEMENT_TYPE_U4)
			pData += sizeof(int);
		else if (type == ELEMENT_TYPE_I8 || type == ELEMENT_TYPE_U8)
			pData += sizeof(LONGLONG);
		else if (type == ELEMENT_TYPE_BOOLEAN)
			pData += sizeof(bool);
		else if (type == ELEMENT_TYPE_STRING)
			pData += sizeof(ObjectID);
		else if (type == ELEMENT_TYPE_OBJECT)
			pData += sizeof(ObjectID);
		else if (type == ELEMENT_TYPE_CLASS)
			pData += sizeof(ObjectID);
		else
			;
	}

	lstrcatW(lpszBuf, L"}");
}

void CProfilerCallback::GetClass(IMetaDataImport *pMetaDataImport, ClassID classId, LPWSTR lpszClassName, LPVOID lpValue)
{
	WCHAR            szBuf[1024];
	LPBYTE           lpAddress  = (LPBYTE)lpValue;
	ULONG            i, uFieldOffsetCount = 0;
	COR_FIELD_OFFSET *pFieldOffset;
	
	m_pProfilerInfo2->GetClassLayout(classId, NULL, 0, &uFieldOffsetCount, NULL);
	if (uFieldOffsetCount == 0)
		return;
	
	pFieldOffset = (COR_FIELD_OFFSET *)HeapAlloc(GetProcessHeap(), 0, sizeof(COR_FIELD_OFFSET) * uFieldOffsetCount);
	m_pProfilerInfo2->GetClassLayout(classId, pFieldOffset, uFieldOffsetCount, &uFieldOffsetCount, NULL);

	for (i = 0; i < uFieldOffsetCount; i++) {
		PCCOR_SIGNATURE pSig, p;
		WCHAR           szName[256], szType[256], szValue[256];
		CorElementType  type;
		BOOL            bArray, bRef;
		mdTypeDef       typeDef;

		pMetaDataImport->GetFieldProps(pFieldOffset[i].ridOfField, NULL, szName, 256, NULL, NULL, &pSig, NULL, NULL, NULL, NULL);

		p = &pSig[1];
		type = GetElementType(&p, &typeDef, &bRef, &bArray);
		GetElementTypeName(pMetaDataImport, type, typeDef, bRef, bArray, szType);

		if (bArray)
			GetArray((LPVOID)(lpAddress + pFieldOffset[i].ulOffset), type, szValue);
		else
			GetValue((LPVOID)(lpAddress + pFieldOffset[i].ulOffset), type, szValue);

		wsprintfW(szBuf, L"%s.%s %s = %s", lpszClassName, szType, szName, szValue);
		WriteLogFile(szBuf);
	}

	HeapFree(GetProcessHeap(), 0, pFieldOffset);
}

void CProfilerCallback::GetObject(LPVOID lpValue, CorElementType type, LPWSTR lpszBuf)
{
	HRESULT         hr;
	ClassID         classId;
	ObjectID        oid = *(ObjectID *)(lpValue);
	ModuleID        moduleId;
	mdTypeDef       typeDef;
	ULONG32         uBufferOffset;
	LPVOID          lpAddress;
	WCHAR           szType[256];
	IMetaDataImport *pMetaDataImport;
	
	m_pProfilerInfo2->GetClassFromObject(oid, &classId);
	hr = m_pProfilerInfo2->GetBoxClassLayout(classId, &uBufferOffset);
	if (SUCCEEDED(hr))
		lpAddress = (LPVOID)(oid + uBufferOffset);
	else
		lpAddress = lpValue;
	
	m_pProfilerInfo2->GetClassIDInfo(classId, &moduleId, &typeDef);
	m_pProfilerInfo2->GetModuleMetaData(moduleId, ofRead, IID_IMetaDataImport, (IUnknown **)&pMetaDataImport);
	pMetaDataImport->GetTypeDefProps(typeDef, szType, 256, NULL, NULL, NULL);
	pMetaDataImport->Release();
	
	type = GetElementTypeFromClassName(szType);
	
	GetValue(lpAddress, type, lpszBuf);
}

CorElementType CProfilerCallback::GetElementTypeFromClassName(LPWSTR lpszClassName)
{
	CorElementType type;

	if (lstrcmpW(lpszClassName, L"System.Boolean") == 0)
		type = ELEMENT_TYPE_BOOLEAN;
	else if (lstrcmpW(lpszClassName, L"System.Char") == 0)
		type = ELEMENT_TYPE_CHAR;
	else if (lstrcmpW(lpszClassName, L"System.SByte") == 0)
		type = ELEMENT_TYPE_I1;
	else if (lstrcmpW(lpszClassName, L"System.Byte") == 0)
		type = ELEMENT_TYPE_U1;
	else if (lstrcmpW(lpszClassName, L"System.Int16") == 0)
		type = ELEMENT_TYPE_I2;
	else if (lstrcmpW(lpszClassName, L"System.UInt16") == 0)
		type = ELEMENT_TYPE_U2;
	else if (lstrcmpW(lpszClassName, L"System.Int32") == 0)
		type = ELEMENT_TYPE_I4;
	else if (lstrcmpW(lpszClassName, L"System.UInt32") == 0)
		type = ELEMENT_TYPE_U4;
	else if (lstrcmpW(lpszClassName, L"System.Int64") == 0)
		type = ELEMENT_TYPE_I8;
	else if (lstrcmpW(lpszClassName, L"System.UInt64") == 0)
		type = ELEMENT_TYPE_U8;
	else if(lstrcmpW(lpszClassName, L"System.String") == 0)
		type = ELEMENT_TYPE_STRING;
	else
		type = ELEMENT_TYPE_END;

	return type;
}

BOOL CProfilerCallback::FilterClass(FunctionID functionId, LPWSTR lpszFilterClassName)
{
	IMetaDataImport *pMetaDataImport;
	mdMethodDef     methodDef;
	mdTypeDef       typeDef;
	WCHAR           szClassName[256];

	m_pProfilerInfo2->GetTokenAndMetaDataFromFunction(functionId, IID_IMetaDataImport, (IUnknown **)&pMetaDataImport, &methodDef);
	pMetaDataImport->GetMethodProps(methodDef, &typeDef, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
	pMetaDataImport->GetTypeDefProps(typeDef, szClassName, 256, NULL, NULL, NULL);
	pMetaDataImport->Release();

	return lstrcmpW(szClassName, lpszFilterClassName) == 0;
}

void CProfilerCallback::GetFullMethodName(FunctionID functionId, LPWSTR lpszFunctionName)
{
	IMetaDataImport *pMetaDataImport;
	mdMethodDef     methodDef;
	mdTypeDef       typeDef;
	WCHAR           szName[256], szClassName[256];
	
	m_pProfilerInfo2->GetTokenAndMetaDataFromFunction(functionId, IID_IMetaDataImport, (IUnknown **)&pMetaDataImport, &methodDef);
	pMetaDataImport->GetMethodProps(methodDef, &typeDef, szName, 256, NULL, NULL, NULL, NULL, NULL, NULL);
	pMetaDataImport->GetTypeDefProps(typeDef, szClassName, 256, NULL, NULL, NULL);
	pMetaDataImport->Release();

	wsprintfW(lpszFunctionName, L"%s.%s", szClassName, szName);
}

ICorProfilerCallback2の様々なメソッドでは、そのメソッドが呼ばれたことを示す文字列を書き込んでいますが、 既定のままではこれらがファイルに保存されることはありません。 CLRはSetEventMaskに指定したイベントに関するメソッドだけを呼び出すため、 たとえばアセンブリに関するメソッドが呼ばれてほしいならば、 SetEventMaskにCOR_PRF_MONITOR_ASSEMBLY_LOADSを指定しなければなりません。 しかし、今回のように関数のフックを目的にした場合は、関数以外の情報は書き込まれない方が分かりやすいでしょう。

DLLを作成したら、コマンドプロント上で次の文字列を入力します。 ファイルパスの部分は、DLLのフルパスに置き換えます。 DLLが存在するパスに置き換えてください。

regsvr32 C:\sample.dll (登録の解除時は regsvr32 /u C:\sample.dll のようにする)

regsvr32にDLLのパスを指定すれば、DLLのDllRegisterServerを呼ばれます。 これよって、プロファイラの情報がレジストリに登録されます。


戻る