Я хотел добиться чего-то похожего, за исключением того, что хотел превратить подобную функцию в один .dll - но тогда вы не можете использовать __ImageBase, так как это специфично для того .dll, где находится функция. Я даже пытался переопределить, используя подход
GetDllPath( HMODULE hDll = (HMODULE) __ImageBase)
Но это тоже не сработало. (По какой-то причине после этого возвращает путь к приложению.)
Тогда я понял - почему я не использую VirtualQuery, а использую указатель на функцию и получаю оттуда HMODULE. Но опять же - как получить указатель на функцию вызывающего абонента?
А теперь вернемся к определению стека вызовов - я не буду беспокоить вас всеми грязными деталями, просто перейдите по ссылкам указанных ссылок.
Вот полный снимок кода:
//
// Originated from: https://sourceforge.net/projects/diagnostic/
//
// Similar to windows API function, captures N frames of current call stack.
// Unlike windows API function, works with managed and native functions.
//
int CaptureStackBackTrace2(
int FramesToSkip, //[in] frames to skip, 0 - capture everything.
int nFrames, //[in] frames to capture.
PVOID* BackTrace //[out] filled callstack with total size nFrames - FramesToSkip
)
{
#ifdef _WIN64
CONTEXT ContextRecord;
RtlCaptureContext(&ContextRecord);
UINT iFrame;
for (iFrame = 0; iFrame < (UINT)nFrames; iFrame++)
{
DWORD64 ImageBase;
PRUNTIME_FUNCTION pFunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip, &ImageBase, NULL);
if (pFunctionEntry == NULL)
{
if (iFrame != -1)
iFrame--; // Eat last as it's not valid.
break;
}
PVOID HandlerData;
DWORD64 EstablisherFrame;
RtlVirtualUnwind(0 /*UNW_FLAG_NHANDLER*/,
ImageBase,
ContextRecord.Rip,
pFunctionEntry,
&ContextRecord,
&HandlerData,
&EstablisherFrame,
NULL);
if(FramesToSkip > (int)iFrame)
continue;
BackTrace[iFrame - FramesToSkip] = (PVOID)ContextRecord.Rip;
}
#else
//
// This approach was taken from StackInfoManager.cpp / FillStackInfo
// http://www.codeproject.com/Articles/11221/Easy-Detection-of-Memory-Leaks
// - slightly simplified the function itself.
//
int regEBP;
__asm mov regEBP, ebp;
long *pFrame = (long*)regEBP; // pointer to current function frame
void* pNextInstruction;
int iFrame = 0;
//
// Using __try/_catch is faster than using ReadProcessMemory or VirtualProtect.
// We return whatever frames we have collected so far after exception was encountered.
//
__try {
for (; iFrame < nFrames; iFrame++)
{
pNextInstruction = (void*)(*(pFrame + 1));
if (!pNextInstruction) // Last frame
break;
if (FramesToSkip > iFrame)
continue;
BackTrace[iFrame - FramesToSkip] = pNextInstruction;
pFrame = (long*)(*pFrame);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
#endif //_WIN64
iFrame -= FramesToSkip;
if(iFrame < 0)
iFrame = 0;
return iFrame;
} //CaptureStackBackTrace2
//
// Gets .dll full path or only directory.
//
CStringW GetDllPath( bool bPathOnly /* = false */ )
{
void* pfunc = &GetDllPath;
wchar_t path[MAX_PATH] = { 0 };
MEMORY_BASIC_INFORMATION info;
HMODULE hdll;
CaptureStackBackTrace2(1, 2, &pfunc);
// Get the base address of the module that holds the current function
VirtualQuery(pfunc, &info, sizeof(MEMORY_BASIC_INFORMATION));
// MEMORY_BASIC_INFORMATION::AllocationBase corresponds to HMODULE
hdll = (HMODULE)info.AllocationBase;
// Get the dll filename
if ( !GetModuleFileName( hdll, path, MAX_PATH ) )
return L"";
if ( bPathOnly )
{
wchar_t* p = wcsrchr( path, '\\' );
if ( p )
*p = 0;
}
return path;
} //GetDllPath