这里讲解的是针对vs2010之前的版本的(即vs2005,vs2008。因为vs2010对于这方面有了一些改动),并以CEGUI 0.7.9版本(因为这个版本的CEGUI的String对象采用统一utf32编码,调试时很难查看字符串信息)中的CEGUI::String类型为例讲解,
首先介绍一点此版本的CEGUI::String类需要注意的地方。
有一个很重要的地方需要注意,0.7.9的版本中CEGUI::String对于const char*,以及对于const utf8*(即const unsigned char*)的构造函数有区别。
很多时候我们想通过CEGUI::String::c_str()函数,让CEGUI::String返回c风格字符串,但是我要告诉你,CEGUI::String::c_str()是个文不达意的函数,其真正功能是将保存的utf32字符串转换成utf8编码的字符串。这对于ascii字符集中的字符没有什么问题,但是对非ASCII字符集的字符,你调用CEGUI::String::c_str()将会返回乱码。
CEGUI::String strTest = "中国";
std::cout << strTest.c_str() << std::endl;
这是为什么呢?这正是前面第一点提到的,因为CEGUI::String::String( const char* )构造函数,对于非ASCII字符集字符串的构造根本就是错误的。这点在CEGUI::String::Assign(const utf8*)中的注释中CEGUI已经考虑到了。但是未做过多处理。
然后我们来看一下如果让vs调试器帮你格式化显示CEGUI::String类型。
用过CEGUI.0.7.9的开发人员都知道,CEGUI::String类中直接将字符串全部保存到utf32(即一个字符为4个字节)的缓冲区中!这将意味着vs调试器不能直接查看CEGUI::String里面的字符,因为这个缓冲区里面到处都有c风格字符串的结尾符(即字节的值为0)。所以你很难查看到一个CEGUI::String对象的字符含义。当然如果你的CEGUI::String里面只保存的是ascii字符,那么有个简陋的方法是可以看到字符串。那就是使用VS的Memory查看器,我们将字符串头地址传给Memory查看器,Memory查看器会自动将能显示的ascii字符显示出来。这样能勉强能满足你的愿望。
但是,如果你的CEGUI::String对象,保存的是中文,那么没有任何简单的方法能让你再次看到其字符含义。要想让其格式化显示中文,我们必须给vs调试器写一个小插件(听着插件,似乎很麻烦,但实际上很简单,主要就牵扯到几个函数)。以下是具体的步骤:
HRESULT WINAPI CustomViewer(
DWORD dwAddress, // low 32-bits of address
DEBUGHELPER *pHelper, // callback pointer to access helper functions
int nBase, // decimal or hex
BOOL bIgnore, // not used
char *pResult, // where the result needs to go
size_t max, // how large the above buffer is
DWORD dwReserved // always pass zero
)
只要函数类型符合就可以,函数名字随便。只要我们完成这个函数,然后调试器每次显示你的数据类型的对象的时候,就会调用这个接口,你所需要做的就是将想要显示的信息填充到pResult所指向的字符缓冲区中。这是我们的中心思想,但是为了完成这个任务,我们有不少困难需要克服。后面会一一列举。typedef struct tagDEBUGHELPER
{
DWORD dwVersion;
HRESULT (WINAPI *ReadDebuggeeMemory)(
struct tagDEBUGHELPER *pThis, //DEBUGHELPER pointer
DWORD dwAddr,//the address of object you want to show formatted prompt information
DWORD nWant, //the object size in byte.
VOID* pWhere, //the dest buffer for storing the object
DWORD *nGot );//number bytes are transferred.
// from here only when dwVersion >= 0x20000
DWORDLONG (WINAPI *GetRealAddress)( struct tagDEBUGHELPER *pThis );
//use for 64-bit system.
HRESULT (WINAPI *ReadDebuggeeMemoryEx)( struct tagDEBUGHELPER *pThis, DWORDLONG qwAddr,
DWORD nWant, VOID* pWhere, DWORD *nGot );
int (WINAPI *GetProcessorType)( struct tagDEBUGHELPER *pThis );
} DEBUGHELPER;
// CEGUIDbg.cpp : Defines the exported functions for the DLL application.
//#include "stdafx.h"
#include
#include "tchar.h"
#include
#include
#include
#include "ceguistring.h"
#define ADDIN_API __declspec(dllexport)
typedef struct tagDEBUGHELPER
{
DWORD dwVersion;
HRESULT (WINAPI *ReadDebuggeeMemory)(
struct tagDEBUGHELPER *pThis, //DEBUGHELPER pointer
DWORD dwAddr,//the address of object you want to show formatted prompt information
DWORD nWant, //the object size in byte.
VOID* pWhere, //the dest buffer for storing the object
DWORD *nGot );//number bytes are transferred.
// from here only when dwVersion >= 0x20000
DWORDLONG (WINAPI *GetRealAddress)( struct tagDEBUGHELPER *pThis );
//use for 64-bit system.
HRESULT (WINAPI *ReadDebuggeeMemoryEx)( struct tagDEBUGHELPER *pThis, DWORDLONG qwAddr,
DWORD nWant, VOID* pWhere, DWORD *nGot );
int (WINAPI *GetProcessorType)( struct tagDEBUGHELPER *pThis );
} DEBUGHELPER;
// 多字节编码转为UTF8编码
bool MultiByteToUtf8( char* pszDestUtf8, int iDestUtf8Size, const char* pszMultiByte, int iMultiByteSize = -1 )
{
if( NULL == pszDestUtf8 || NULL == pszMultiByte )
{
return false;
}
// convert an MBCS string to widechar
int iWideCharSize = MultiByteToWideChar( CP_ACP, 0, pszMultiByte, iMultiByteSize, NULL, 0 );
std::vector< WCHAR > vctWideChar( iWideCharSize );
int iNumWritten = MultiByteToWideChar( CP_ACP, 0, pszMultiByte, iMultiByteSize, &vctWideChar.front(), iWideCharSize );
if( iNumWritten != iWideCharSize )
{
return false;
}
// convert an widechar string to utf8
int iUtf8Size = WideCharToMultiByte(CP_UTF8, 0, &vctWideChar.front(), -1, NULL, 0, NULL, NULL);
if ( iUtf8Size <= 0)
{
return false;
}
if( iUtf8Size > iDestUtf8Size )
{
iUtf8Size = iDestUtf8Size;
}
iNumWritten = WideCharToMultiByte( CP_UTF8, 0, &vctWideChar.front(), -1, pszDestUtf8, iUtf8Size, NULL, NULL );
if ( iNumWritten != iUtf8Size )
{
return false;
}
return true;
}
ADDIN_API HRESULT WINAPI CEGUIDbg_String(DWORD dwAddress, DEBUGHELPER *pHelper,
int nBase, BOOL bUniStrings, char *pResult,
size_t max, DWORD reserved )
{
CEGUI::String strDebug;
DWORD nGot;
//get CEGUI::String data member.
if (pHelper->ReadDebuggeeMemory(pHelper,dwAddress,sizeof( strDebug),&strDebug,&nGot) != S_OK)
{
return E_FAIL;
}
if( nGot != sizeof( strDebug ) )
{
return E_FAIL;
}
const CEGUI::utf32* pszUtf32 = strDebug.ptr();
int iLength = strDebug.length();
std::vector< CEGUI::utf32 > vctBuffer;
//if the string data is stored in a memory allocated by new(), we have to copy the data to out memory block.
if( iLength > STR_QUICKBUFF_SIZE )
{
vctBuffer.resize( iLength );
if( S_OK != pHelper->ReadDebuggeeMemory( pHelper, ( DWORD )pszUtf32, iLength * sizeof( CEGUI::utf32 ), &vctBuffer.front(), &nGot ) )
{
return E_FAIL;
}
if( nGot != vctBuffer.size() * sizeof( CEGUI::utf32 ) )
{
return E_FAIL;
}
pszUtf32 = &vctBuffer.front();
}
//get ascii character.
//although the data pointer is utf32*, but the data isn't encoded by utf32 if you pass const char* to CEGUI::String constructor. In contrary, it only store each ascii character
//in a utf32-type element.
int iSize = iLength + 1;
if( iSize > max )
{
iSize = max;
iLength = iSize - 1;
}
std::vector< char > vctAscii( iSize );
for( int i = 0; i < iLength; ++i )
{
vctAscii[ i ] = ( char )( unsigned char )pszUtf32[ i ];
}
vctAscii[ iLength ] = 0;
//convert ascii character set to utf8 character set.
//Because debugger accepts utf8 character set.
//If you pass ascii string to pResult, chinese character can't be shown.
if( false == MultiByteToUtf8( pResult, max, &vctAscii.front() ) )
{
return E_FAIL;
}
//set all data to 0, then CEGUI::String::~String won't delete anything should't be deleted.
memset( &strDebug, 0, sizeof( strDebug ) );
return S_OK;
}
[AutoExpand]
CEGUI::String=$ADDIN(ceguidbg.dll,?CEGUIDbg_String@@YGJKPAUtagDEBUGHELPER@@HHPADIK@Z)
; $ADDIN allows external DLLs to be added to display even more complex
; types via the EE Add-in API. The first argument is the DLL name, the
; second argument is the name of the export from the DLL to use. For
; further information on this API see the sample called EEAddIn.
CEGUI::String strDebug;
DWORD nGot;
//get CEGUI::String data member.
if (pHelper->ReadDebuggeeMemory(pHelper,dwAddress,sizeof( strDebug),&strDebug,&nGot) != S_OK)
{
return E_FAIL;
}
if( nGot != sizeof( strDebug ) )
{
return E_FAIL;
}
const CEGUI::utf32* pszUtf32 = strDebug.ptr();
int iLength = strDebug.length();
std::vector< CEGUI::utf32 > vctBuffer;
//if the string data is stored in a memory allocated by new(), we have to copy the data to out memory block.
if( iLength > STR_QUICKBUFF_SIZE )
{
vctBuffer.resize( iLength );
if( S_OK != pHelper->ReadDebuggeeMemory( pHelper, ( DWORD )pszUtf32, iLength * sizeof( CEGUI::utf32 ), &vctBuffer.front(), &nGot ) )
{
return E_FAIL;
}
if( nGot != vctBuffer.size() * sizeof( CEGUI::utf32 ) )
{
return E_FAIL;
}
pszUtf32 = &vctBuffer.front();
}
//get ascii character.
//although the data pointer is utf32*, but the data isn't encoded by utf32 if you pass const char* to CEGUI::String constructor. In contrary, it only store each ascii character
//in a utf32-type element.
int iSize = iLength + 1;
if( iSize > max )
{
iSize = max;
iLength = iSize - 1;
}
std::vector< char > vctAscii( iSize );
for( int i = 0; i < iLength; ++i )
{
vctAscii[ i ] = ( char )( unsigned char )pszUtf32[ i ];
}
vctAscii[ iLength ] = 0;
//convert ascii character set to utf8 character set.
//Because debugger accepts utf8 character set.
//If you pass ascii string to pResult, chinese character can't be shown.
if( false == MultiByteToUtf8( pResult, max, &vctAscii.front() ) )
{
return E_FAIL;
}
//set all data to 0, then CEGUI::String::~String won't delete anything should't be deleted.
memset( &strDebug, 0, sizeof( strDebug ) );
终于搞定了,如果还有不明白的地方,请留言!
不想自己动手写的,可以直接下载我上传到csdn的资源:http://download.csdn.net/detail/xujiezhige/5740411