Sample Code to invoke managed code inside profiling API
ProfilerCallback.cpp
// This is the function that will invoke the managed code through COM interop on another thread
// this function creates the CCW object
// [in] this pointer
DWORD WINAPI CreateManagedStub(LPVOID lpParam)
{
_ManagedStub * pIManagedStub = NULL;
HRESULT hr = CoCreateInstance(CLSID_ManagedStub, NULL, CLSCTX_INPROC_SERVER, IID__ManagedStub, (void **)&pIManagedStub);
if (FAILED(hr))
{
printf("Fail to CoCreateInstance on ManagedStub class 0x%x\n", hr);
return 1;
}
if (pIManagedStub == NULL)
{
printf("pIManagedStub is null 0x%x\n", hr);
return 1;
}
// we have the managed instance now.
((CProfilerCallback*)lpParam)->m_pIManagedStub = pIManagedStub;
return 0;
}
// this function is the actual caller to the managed world.
// this function can be used to get a managed dynamic method token back
// [in] this pointer; method token; assembly name
// [out] delegate type name
DWORD WINAPI ManagedPreStub(LPVOID lpParam)
{
PMYDATA pData = (PMYDATA)lpParam;
LPWSTR delegateName = NULL;
if (pData->thisObj->m_pIManagedStub == NULL)
{
printf("pData->thisObj->m_pIManagedStub is null");
return 1;
}
HRESULT hr = pData->thisObj->m_pIManagedStub->PreStub(pData->assemblyName, pData->methodTk, &delegateName);
if (FAILED(hr))
{
printf("Fail to Call PreStub 0x%x\n", hr);
}
wcscpy_s(pData->delegateName, (wchar_t*)delegateName);
return 0;
}
......
// Profiler callback function
HRESULT CProfilerCallback::JITCompilationStarted(UINT functionId,
BOOL fIsSafeToBlock)
{
HANDLE hThread = NULL;
DWORD dwThreadId;
/////////////////////////////////////////////////////////
// We dont want to continue on JITCompilationStarted if the thread is for the "managed code" we are to call from this callback.
// We store these thread ids in our skip thread hashtable
// 1. if the JITCompilationStarted is on one of the skipping thread, avoid following steps.
DWORD currentThreadId = GetCurrentThreadId();
if (m_SkipJCHashTable->PLookup(currentThreadId))
return S_OK;
// create the ManagedStub object
if (m_pIManagedStub==NULL) // if it is already created, no need to create twice
{
hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateManagedStub, this, 0, &dwThreadId);
// add the thread Id to skip pool so that JITCompilationStarted will do nothing on that thread.
m_SkipJCHashTable->SetAt(dwThreadId, 0);
if (hThread != NULL)
{
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
m_SkipJCHashTable->RemoveKey(dwThreadId);
}
....
///////////////////////////////////////////////////////
// try to define a managed dynamic method and get the dynamic method method token
// prepare the parameters to be passed in
PMYDATA pData = (PMYDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYDATA));
if( pData == NULL )
goto exit;
pData->assemblyName = wszAssemblyName;
pData->methodTk = tkMethod;
pData->thisObj = this;
ProfilerPrintf("Working on asm %ws func %ws.", wszAssemblyName, wszFunctionName);
hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ManagedPreStub, pData, 0, &dwThreadId);
m_SkipJCHashTable->SetAt(dwThreadId, 0);
if (hThread != NULL)
{
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
m_SkipJCHashTable->RemoveKey(dwThreadId);
// we free up the memory here instead of in the thread because we want some return value back.
wchar_t wszDelegateName[512];
if (pData->delegateName == NULL)
return S_OK;
// copy the return value out
wcsncpy_s(wszDelegateName, _countof(pData->delegateName), pData->delegateName, _countof(pData->delegateName));
HeapFree(GetProcessHeap(), 0, pData);
...
We have these definitions in ProfilerCallback.h:
private:
CMap<DWORD, DWORD, int, int> * m_SkipJCHashTable;
...
Enjoy!