
HOWTO: Enhancing AUTOEXP.DAT - Creating Custom Type Evaluator Routines
Hello,
Several weeks ago, one my good friend has played with eecxx.dll and he discovered technique, how to write Custom Type Evaluator Routines. Due to number of people posting questions about using autoexp.dat I asked him to publish these [totally undocumented] informations.
The type evaluator is bound with custom type evaluating routine through autoexp.dat pseudomacro $ADDIN in the following form:
<type>=$ADDIN(<dll_name>,<exported_function>)
where
dll_name is the name of custom evaluator dll that exports exported_function. One library can export multiple functions for different evaluated types.
We have tested only relative names (dll was placed in the same directory as autoexp.dat - %MSDevDir%\bin). Maybe, double-quoted absolute path would work too.
Example:
MyRecord=$ADDIN(my.dll,Evaluate_MyRecord)
MyPointer=$ADDIN(my.dll,Eval_MyPointer)
MyNS::MyType=$ADDIN(my2.dll,Evaluate_MyType)
Note: The expression $ADDIN(<dll_name>,<exported_function>) [probably] should not contain spaces.
When Visual C++ de{*filter*} is asked to evaluate custom type (e.g. QuickWatch or displaying tool tip in source editor), is calls extension dll and runs exported function
int _cdecl EvaluateXXX(
LPVOID lpEvalObject // pointer to evaluated object
DWORD dwSignature // signature
DWORD dwRadix // radix to use
DWORD dwReserver // reserved, always 0
LPSTR pszText // displayed text
DWORD cchMax // text size
);
Parameters
lpEvalObject
[in] Pointer to object being evaluated in debugged process memory space (see Remarks)
dwSignature
[in] 0x52526c00 = "RRI" - used to get evaluated object (see Remarks)
dwRadix
[in] Radix to use (to format numbers in the returned string) - usually decimal (10) or hexadecimal (16), set by checking Hexadecimal display option in Tools/Options/Debug/General or Watch/Variables window context menu
dwReserved
[in] Reserved - always 0 (for future use ?)
pszText
[out] Address of the buffer to receive the null-terminated string of object being evaluated
cchMax
[in] Size of the buffer to receive the null-terminated string
Return Values
Returns NOERROR (0) if successful, other value is considered as an error and displayed text is "???".
Remarks
When the size of evaluated object is <= sizeof(DWORD), lpEvalObject is not pointer, but it does contain the object immediately (char, int, DWORD). For larger objects (structures.) the lpEvalObject points to memory in debugged process memory space and the object has to be obtained by calling function GetDebuggedProcessMemory - the address of this function follows dwSignature:
typedef int (_stdcall *GETMEM_FUNC)(DWORD dwSignature, void *pProcessMemory, DWORD dwBufferSize, void *pBuffer, DWORD *pdwBytesReceived);
inline int GetDebuggedProcessMemory(DWORD dwSignature, void *pProcessMemory, DWORD dwBufferSize, void *pBuffer)
{
DWORD dwBytesReceived;
GETMEMFUNC _GetMem = (GETMEM_FUNC)*(((int *)dwSignature) + 1);
int nRetVal = _GetMem(dwSignature, pProcessMemory, dwBufferSize, pBuffer, &dwBytesReceived);
if(nRetVal != 0)
return nRetVal;
if(dwBytesReceived != dwBufferSize)
return 0x80004005;
return 0;
Quote:
}
Example, how to write custom type evaluator function:
MyType=$ADDIN(my.dll,EvaluateMyType)
int _cdecl EvaluateMyType(LPVOID lpEvalObject, DWORD dwSignature, DWORD dwRadix, DWORD dwReserver, LPSTR pszText, DWORD cchMax)
{
int nRetVal;
BYTE buffer[1024];
MyType *pMyVar = (MyType *)buffer; // Important note: if(sizeof(MyType) <= sizeof(DWORD)) // pMyVar = lpEvalObject if((nRetVal = GetDebuggedProcessMemory(dwSignature, pEvalObject, sizeof(MyType), buffer)) != 0)
return nRetVal; // Here you can examine variable and fill string
// lstrcpyn(pszText, pMyVar->ToString(), cchMax); // pszText[cchMax] = '\0';
return 0;}
Note: IMHO (maybe) dwSignature is a part of more complex structure that contains dwSignature followed with pointer to GetDebuggedProcessMemory function. By the way, in this time it is not so important...Viktor "Viki" VolmutStanislav "ChainsaW" Simicek---DGroup Team