可跨进程使用的栈

来源:互联网 发布:太原淘宝电动三轮价 编辑:程序博客网 时间:2024/05/24 05:01

写调试器时遇到跨进程通信问题。因为部分代码需要注入到目标进程,又不能破坏目标进程运行环境,COM和消息以及SOCKET等方法都不便使用,所以写下了这个。测试了一下,性能还过得去,也没发现BUG。分享一下知识。


注意一些瑕疵:

在CPBStack::Push和CPBStack::Pop两函数中使用了shared_ptr且暗含shared_ptr构造函数不会抛出异常的假设。若对robustness要求苛刻,请将shared_ptr替换为自定义的AUTOLOCK,并在栈中构造AUTOLOCK对象以阻止shared_ptr构造函数中使用new操作失败时抛出异常。

对POD类型支持不好;若要支持,请自行实现,我将在项目完成后修正为完整版。


PBMemory.h

/*PBMemory.h进程间共享的内存。< .fuhao >< .2016年11月27日14时28分 >*/#pragma once#include <sal.h>#include "stdex.h"#include "fhmacro.h"__pragma(pack( 8 ));class __declspec(align(8)) CPBMemory{public:STATIC VOID ThrowRuntimeError( __in LPCSTR lpDescript ){stdex::StringA s;s.Format( "%s;错误:%u,描述:%s", lpDescript, GetLastError(),(LPCSTR)stdex::StringA::FormatMessage( GetLastError() ) );#if defined( _DEBUG )OutputDebugStringA( (LPCSTR)s );#endifstd::_Xruntime_error( (LPCSTR)s );}CPBMemory( __in LPCTSTR lpMemoryName, DWORD dwMemoryOfBytesSize ): m_bMemoryOwner( FALSE ), m_hMapHandle( NULL ), m_lpMapAddress( NULL ){ASSERT_MSG( !IsEmptyString( lpMemoryName ), _T( "必须为共享栈命名" ) );ASSERT_MSG( dwMemoryOfBytesSize != 0, _T( "内存长度为零?" ) );stdex::StringA sErrorDescript;do{SECURITY_DESCRIPTOR sd;if( !InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION ) ){sErrorDescript = __FUNCTION__"无法初始化安全描述符";break;}if( !SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE ) ){sErrorDescript = __FUNCTION__"无法修攺安全描述符属性";break;}SECURITY_ATTRIBUTES sa = { sizeof( SECURITY_ATTRIBUTES ), &sd, TRUE };// STEP1、创建(或打开)文件映射,如果 lpMemoryName 已被创建,则打开它。stdex::String &sFileMappingName( stdex::String::FastFormat( _T( "Global\\%s" ), lpMemoryName ) );m_hMapHandle = CreateFileMapping( INVALID_HANDLE_VALUE,&sa, PAGE_READWRITE, ZERO,dwMemoryOfBytesSize,(LPCTSTR)sFileMappingName );if( m_hMapHandle == NULL ){sErrorDescript = __FUNCTION__"无法打开映射文件";break;}m_bMemoryOwner = GetLastError() != ERROR_ALREADY_EXISTS;// STEP4、映射到内存m_lpMapAddress = MapViewOfFile( m_hMapHandle,FILE_MAP_WRITE | FILE_MAP_READ,ZERO, ZERO, ZERO );if( m_lpMapAddress == NULL ){sErrorDescript = __FUNCTION__"无法映射文件到内存";break;}} while( FALSE );if( !sErrorDescript.IsEmpty() ){if( m_lpMapAddress != NULL ){VERIFY_MSG( UnmapViewOfFile( m_lpMapAddress ) );m_lpMapAddress = NULL;}if( m_hMapHandle != NULL ){VERIFY_MSG( CloseHandle( m_hMapHandle ) );m_hMapHandle = NULL;}ThrowRuntimeError( (LPCSTR)sErrorDescript );} while( FALSE );}virtual ~CPBMemory(){if( m_lpMapAddress != NULL ){VERIFY_MSG( UnmapViewOfFile( m_lpMapAddress ) );m_lpMapAddress = NULL;}if( m_hMapHandle != NULL ){VERIFY_MSG( CloseHandle( m_hMapHandle ) );m_hMapHandle = NULL;}m_sMemoryName.clear();}LPCTSTR MemoryName(){ return (LPCTSTR)m_sMemoryName; }LPVOID MemoryAddress(){ return m_lpMapAddress; }DWORD MemorySize(){ return m_dwMemoryOfBytesSize; }BOOL IsOwner(){ return m_bMemoryOwner; }protected:stdex::String m_sMemoryName;HANDLE m_hMapHandle;LPVOID m_lpMapAddress;// MapViewOfFile返回整页内存基址,所以此地址一定是对齐的。DWORD m_dwMemoryOfBytesSize;// m_lpMapAddress指向的内存长度。BOOL m_bMemoryOwner;// 本对象是栈的拥有者;本对象创建了栈。};__pragma(pack());



PBStack.h

/*PBStack.h// Process between stack进程间共享的栈。 示例:__pragma(pack( 8 ));typedef struct _PROGRAM_ITEM{DWORD Signature;LPVOID lpReserved;}PROGRAM_ITEM;__pragma(pack());LPCTSTR STACK_NAME = _T( "StackName" );CONST DWORD STACK_MAX_ELEMCOUNT = 100;// 进程A:int main( int argc, char ** argv ){CPBStack < PROGRAM_ITEM, PushNotifyLockTraits< MutexLockTraits > > *pStack = NULL;try{pStack = new CPBStack< PROGRAM_ITEM >( STACK_NAME, STACK_MAX_ELEMCOUNT );}catch( std::runtime_error &re ){printf( "%s\n Stop.\n", re.what() );return ERROR_UNIDENTIFIED_ERROR;}PROGRAM_ITEM PrgItem;for( int i = 0; i < 100; ++i ){PrgItem.Signature = i;// ...pStack->Push( PrgItem );}return ERROR_SUCCESS;}// 进程B:int main( int argc, char ** argv ){CPBStackPushNotify< PROGRAM_ITEM > *pStack = NULL;struct PBStackSink: public IPBStackPushEventSink{VOID OnPBStackPushEvent(){PROGRAM_ITEM PrgItem;(*ppStack)->Pop( &PrgItem );printf( "%u, ", PrgItem.Signature );}CPBStackPushNotify< PROGRAM_ITEM > **ppStack;};PBStackSink Sink;*Sink.ppStack = pStack;try{pStack = new CPBStackPushNotify< PROGRAM_ITEM >( STACK_NAME, STACK_MAX_ELEMCOUNT, &Sink );}catch( std::runtime_error &re ){printf( "%s\nStop.\n", re.what() );return ERROR_UNIDENTIFIED_ERROR;}printf( "任意键退出...\n" );while( !_kbhit() );delete pStack;return ERROR_SUCCESS;}以上示例代码,进程B会打印:0, 1, 2, 3, 4, 5, 6, ..... 99, 缺陷:若“进程A”或“进程B”两者有其中之一个调用Push或Pop过程中因操作系统调度线程或调试器暂停线程导致执行被中断,则另一线程对Push或Pop的调用也会随之进入等待状态直到被中断的执行重新继续…………:那么,如果把CPBStack应用到CGDIDebugger中可能导致目标进程和调试进程中看到的GDI对象数量不一,所以必须保证目标进程向调试进程发送一次数据完成前,下一次发送过程不会开始。既:调试进程一定能处理完除最后一条数据外的所有数据。用CPBStack,设置栈长度为 1 能保证调试进程一定能处理完除最后一条数据外的所有数据。2016年11月9日20时29分,为支持栈长度为1时可以不锁的情况,为CPBStack增加一个模板参数:LockTraitsCPBStack会在其认为需要对资源访问同步时调用LockTraits类型中的WaitForSingleObject或Release来锁定或解锁对栈的访问,如果栈中只有一条数据,而且使用了CPBStackPushNotify,那么操作栈时可以不锁(将CPBStack的LockTraits设置为NothingLockTraits既可);此次修攺不影响上方的示例代码正常运行。< .fuhao >< .2016年10月30日17时56分 >*/#pragma once#include <sal.h>#include "stdex.h"#include <xutility>#include <memory>#include "fhmacro.h"#include <stdexcept>#include <type_traits>#include "PBMemory.h"#if defined( _WIN64 )#error "此文件中代码尚未通过64位环境测试"#endif__pragma(pack( 8 ));typedef struct __declspec(align(8)) _SHRAED_STACK_HEADER{__readonly DWORD dwMajorProcessId;// 主进程(创建栈的进程)__readonly DWORD dwMinorProcessId;// 从进程(打开栈的进程)__readonly DWORD dwElemLength;// 单个元素长度__readonly DWORD dwElemMaxCount;// 可容纳的最大元素数量DWORD dwElemSize;// 当前栈中元素数量__reserved DWORD dwReserved;// 保留(可能作原子锁或被填充为当前获取锁的线程ID)BYTE Data[1];// 栈...}SHARED_STACK_HEADER, *PSHARED_STACK_HEADER;static_assert(__alignof(SHARED_STACK_HEADER) == 8, "SHARED_STACK_HEADER结构必须以64位边界对齐");struct NothingLockTraits{BOOL Create( SECURITY_ATTRIBUTES *, DWORD, BOOL, LPCTSTR ){return TRUE;}VOID Destroy(){}DWORD WaitForSingleObject( DWORD, BOOL ){return WAIT_OBJECT_0;}BOOL Release( BOOL ){return TRUE;}};struct MutexLockTraits{MutexLockTraits():m_hStackMutex( NULL ){}BOOL Create( __in_opt SECURITY_ATTRIBUTES *sa, __in DWORD dwStackSize, __in BOOL bOpenLock, __in LPCTSTR lpLockName ){if( bOpenLock ){m_hStackMutex = OpenMutex( MUTEX_ALL_ACCESS, FALSE, lpLockName );}else{m_hStackMutex = CreateMutex( sa, FALSE, lpLockName );}return !!m_hStackMutex;}VOID Destroy(){if( m_hStackMutex != NULL ){VERIFY_MSG( CloseHandle( m_hStackMutex ) );}m_hStackMutex = NULL;}DWORD WaitForSingleObject( DWORD dwMilliseconds, BOOL ){CONST DWORD dwWaitResult = ::WaitForSingleObject( m_hStackMutex, dwMilliseconds );if( dwWaitResult == WAIT_OBJECT_0 || dwWaitResult == WAIT_ABANDONED_0 ){return WAIT_OBJECT_0;}return dwWaitResult;}BOOL Release( BOOL ){return ReleaseMutex( m_hStackMutex );}HANDLE m_hStackMutex;};typedef MutexLockTraits DefaultLockTraits;template< class _Bty >struct PushNotifyLockTraits : private _Bty{PushNotifyLockTraits(): _Bty(), m_hStackResourceSemaphore( NULL ){ }BOOL Create( __in_opt SECURITY_ATTRIBUTES *sa, __in DWORD dwStackSize, __in BOOL bOpenLock, __in LPCTSTR lpLockName ){if( !__super::Create( sa, dwStackSize, bOpenLock, lpLockName ) ){return FALSE;}stdex::String &sStackResourceName( stdex::String::FastFormat( _T( "%s_StackResourceName" ), lpLockName ) );if( bOpenLock ){m_hStackResourceSemaphore = OpenSemaphore( SEMAPHORE_ALL_ACCESS, FALSE, (LPCTSTR)sStackResourceName );}else{m_hStackResourceSemaphore = CreateSemaphore( sa, 0, dwStackSize, (LPCTSTR)sStackResourceName );}if( m_hStackResourceSemaphore == NULL ){__super::Destroy();return FALSE;}return TRUE;}VOID Destroy(){__super::Destroy();if( m_hStackResourceSemaphore != NULL ){VERIFY_MSG( CloseHandle( m_hStackResourceSemaphore ) );}m_hStackResourceSemaphore = NULL;}DWORD WaitForSingleObject( DWORD dwMilliseconds, BOOL bPush ){return __super::WaitForSingleObject( dwMilliseconds, bPush );}BOOL Release( BOOL bPush ){if( !__super::Release( bPush ) ){return FALSE;}if( bPush ){return ReleaseSemaphore( m_hStackResourceSemaphore, 1, NULL );}return TRUE;}HANDLE m_hStackResourceSemaphore;};template< class _Elem, class LockTraits = DefaultLockTraits > class __declspec(align(8)) CPBStack : private CPBMemory{static_assert(std::is_pod< _Elem >::value, "CPBStack的元素必须是POD类型");public:CPBStack( __in LPCTSTR lpStackName, __in DWORD dwMaxElemCount )try : CPBMemory( lpStackName, dwMaxElemCount * sizeof( _Elem ) + sizeof( SHARED_STACK_HEADER ) ), m_pStackHeader( (PSHARED_STACK_HEADER)MemoryAddress()), m_pElem( NULL ), m_hStackSemaphore( NULL ){ASSERT_MSG( !IsEmptyString( lpStackName ), _T( "必须为共享栈命名" ) );ASSERT_MSG( dwMaxElemCount != 0, _T( "栈长度为零?" ) );stdex::StringA sErrorDescript;do{SECURITY_DESCRIPTOR sd;if( !InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION ) ){sErrorDescript = __FUNCTION__"无法初始化安全描述符";break;}if( !SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE ) ){sErrorDescript = __FUNCTION__"无法修攺安全描述符属性";break;}SECURITY_ATTRIBUTES sa = { sizeof( SECURITY_ATTRIBUTES ), &sd, TRUE };// STEP1、创建(或打开)栈资源信号stdex::String &sStackSemaphoreName( stdex::String::FastFormat( _T( "Global\\%s_StackSemaphore" ), lpStackName ) );if( IsOwner() ){m_hStackSemaphore = CreateSemaphore( &sa, (LONG)dwMaxElemCount,(LONG)dwMaxElemCount, (LPCTSTR)sStackSemaphoreName );}else{m_hStackSemaphore = OpenSemaphore( SEMAPHORE_ALL_ACCESS,FALSE, (LPCTSTR)sStackSemaphoreName );}if( m_hStackSemaphore == NULL ){sErrorDescript = __FUNCTION__"无法创建(或打开)栈资源信号";break;}// STEP2、创建(或打开)互斥锁。stdex::String &sStackLock( stdex::String::FastFormat( _T( "Global\\%s_StackLock" ), lpStackName ) );if( !m_hStackLock.Create( &sa, dwMaxElemCount, !IsOwner(), (LPCTSTR)sStackLock ) ){sErrorDescript = __FUNCTION__"无法创建(或打开)栈锁对象";break;}if( IsOwner() ){// 初始化栈头m_pStackHeader->dwElemLength = sizeof( _Elem );m_pStackHeader->dwElemMaxCount = dwMaxElemCount;m_pStackHeader->dwElemSize = ZERO;m_pStackHeader->dwReserved = ZERO;m_pStackHeader->dwMajorProcessId = GetCurrentProcessId();}// 检查栈头中描述的元素长度和_Elem长度是否一致。else{if( sizeof( _Elem ) != m_pStackHeader->dwElemLength ){sErrorDescript = __FUNCTION__"栈头中描述的元素长度和_Elem不一致";}m_pStackHeader->dwMinorProcessId = GetCurrentProcessId();}m_pElem = reinterpret_cast<_Elem*>(m_pStackHeader->Data);} while( FALSE );if( !sErrorDescript.IsEmpty() ){m_hStackLock.Destroy();if( m_hStackSemaphore != NULL ){VERIFY_MSG( CloseHandle( m_hStackSemaphore ) );m_hStackSemaphore = NULL;}m_pStackHeader = NULL;m_pElem = NULL;ThrowRuntimeError( (LPCSTR)sErrorDescript );}}catch( std::runtime_error &re ){throw re;// 不执行构造函数。}virtual ~CPBStack(){m_hStackLock.Destroy();if( m_hStackSemaphore != NULL ){VERIFY_MSG( CloseHandle( m_hStackSemaphore ) );}m_pStackHeader = NULL;m_hStackSemaphore = NULL;m_pElem = NULL;}private:INLINE VOID InternalPush( __readonly _Elem *pElem ){CONST DWORD dwIndex = InterlockedIncrement( (volatile ULONG*)&m_pStackHeader->dwElemSize );CopyMemory( m_pElem + dwIndex - 1, pElem, m_pStackHeader->dwElemLength );}INLINE VOID InternalPop( __writableTo( sizeof( _Elem ) ) _Elem *pElem ){CONST DWORD dwIndex = InterlockedDecrement( (volatile ULONG*)&m_pStackHeader->dwElemSize );CopyMemory( pElem, m_pElem + dwIndex, m_pStackHeader->dwElemLength );}public:BOOL Push( CONST _Elem &Elem, __in DWORD dwMilliseconds = INFINITE ){ return Push( const_cast< _Elem*>( &Elem ), dwMilliseconds ); }__success( return != FALSE ) virtual BOOL Push( _In_bytecount_( sizeof( _Elem ) ) _Elem *pElem,__in DWORD dwMilliseconds = INFINITE ){if( WaitForSingleObject( m_hStackSemaphore, dwMilliseconds ) != WAIT_OBJECT_0 ){return FALSE;}// m_hStackSemaphore上的资源计数已经减少。必须保证锁成功。不成功便成仁;VERIFY_MSG( m_hStackLock.WaitForSingleObject( INFINITE, TRUE ) == WAIT_OBJECT_0 );ANONYMOUS_VAR( std::shared_ptr< VOID >, &m_hStackLock,[this]( VOID* )->VOID{ VERIFY_MSG( m_hStackLock.Release( TRUE ), _T( "Release锁失败,错误:%u" ), GetLastError() ); } );InternalPush( pElem );return TRUE;}__success( return != FALSE ) virtual BOOL Pop(__writableTo( sizeof( _Elem ) ) _Elem *pElem ){if( m_hStackLock.WaitForSingleObject( INFINITE, FALSE ) != WAIT_OBJECT_0 ){return FALSE;}if( IsEmpty() ){return FALSE;// 空栈}ANONYMOUS_VAR( std::shared_ptr< VOID >, m_hStackSemaphore,[this]( HANDLE )->VOID{VERIFY_MSG( ReleaseSemaphore( m_hStackSemaphore, 1, NULL ),_T( "ReleeaseSempahore失败,错误:%u" ), GetLastError() );VERIFY_MSG( m_hStackLock.Release( FALSE ),_T( "Release锁失败,错误:%u" ), GetLastError() );} );InternalPop( pElem ); return TRUE;}_Check_return_ BOOL IsEmpty(){return !InterlockedCompareExchange( (volatile ULONG*)&m_pStackHeader->dwElemSize,m_pStackHeader->dwElemSize, ZERO );}_Ret_notnull_ LockTraits *GetLockObject(){ return &m_hStackLock; }DWORD MajorProcess(){ return m_pStackHeader->dwMajorProcessId; }DWORD MinorProcess(){ return m_pStackHeader->dwMinorProcessId; }private:PSHARED_STACK_HEADER m_pStackHeader;_Elem *m_pElem;HANDLE m_hStackSemaphore;// 如果栈满,Wait函数无法返回。LockTraits m_hStackLock;// 调用Push或Pop函数时使用该互斥对象。};struct IPBStackPushEventSink{virtual VOID OnPBStackPushEvent() = 0;};template< class _Elem, class LockTraits = DefaultLockTraits >class __declspec(align(8)) CPBStackPushNotify : public CPBStack< _Elem, PushNotifyLockTraits< LockTraits > >{public:CPBStackPushNotify( __in LPCTSTR lpStackName,__in DWORD dwMaxElemCount,__in IPBStackPushEventSink *pPushEventSink,__in DWORD dwRegisterWaitFlags = WT_EXECUTEDEFAULT )try : CPBStack( lpStackName, dwMaxElemCount ), m_hWaitObject( NULL ), m_pPushEventSink( pPushEventSink ){ASSERT_MSG( pPushEventSink );if( m_pPushEventSink == NULL ){ThrowRuntimeError( __FUNCTION__"参数错误,pPushEventSink不能为空" );}PushNotifyLockTraits< LockTraits > *pLock = GetLockObject();VERIFY_MSG( RegisterWaitForSingleObject( &m_hWaitObject,pLock->m_hStackResourceSemaphore,(WAITORTIMERCALLBACKFUNC)[]( LPVOID lpArgs, BOOLEAN )->VOID{CPBStackPushNotify *pSink = reinterpret_cast<CPBStackPushNotify*>(lpArgs);ASSERT_MSG( !IsBadWritePtr( pSink, sizeof( CPBStackPushNotify ) ) &&!IsBadReadPtr( pSink, sizeof( CPBStackPushNotify ) ) );pSink->OnPushNotify();},reinterpret_cast<LPVOID>(this),INFINITE,dwRegisterWaitFlags ) );}catch( std::runtime_error &re ){throw re;// 不要执行函数体};virtual ~CPBStackPushNotify(){UnregisterWaitEx( m_hWaitObject, INVALID_HANDLE_VALUE );m_hWaitObject = NULL;}public:VOID OnPushNotify(){ m_pPushEventSink->OnPBStackPushEvent(); }private:HANDLE m_hWaitObject;IPBStackPushEventSink *m_pPushEventSink;};__pragma(pack());


String和StringA的定义见:http://blog.csdn.net/passfuhao/article/details/52926264

以下是依赖的几个宏的实现:


ASSERT_MSG、VERIFY_MSG

/*ASSERT_EXPRW、ASSERT_MSGW,ASSERT_EXPRA、ASSERT_MSGA断言(ASSERT),并在断言窗口中显示自定义消息;ASSERT_EXPRW、ASSERT_MSGW-使用UNICODE字符集的版本ASSERT_EXPRA、ASSERT_MSGA-使用ASCII字符集的版本ASSERT_EXPR、ASSERT_MSG-根据当前项目字符集自适应的版本示例:ASSERT_MSG( sizeof( void* ) > 4, _T("此代码片段不支持64位") );备注:这个宏来自crtdbg.h中的_ASSERT_EXPR,因为_ASSERT_EXPR不支持ASCII字符集,所以重写一个;另外,crtdbg.h中还包含一些其它用来调试的宏,比如:_RPT0和_RPTF0(带__FILE__,__LINE__信息的_RPT0),用来直接显示一个报告窗口(或在rptno设置为_CRT_WARN在输出窗口打印警告),并在报告(或输出)窗口中显示调试消息;_RPT0和_RPTF0使用示例:if( sizeof( void * ) > 4 ){_RPT0( _CRT_ERROR, _T( "错误:此代码片段不能运行在64位环境下" ) );}if( sizeof( void * ) > 4 ){_RPTF0( _CRT_WARN, _T( "警告:此代码片段未在64位环境下测试" ) );}< .fuhao >< .2016年6月8日10时39分 >*/#if defined( _DEBUG )#if _MSC_VER > 1800/* VS2013 或更高 */#define ASSERT_EXPRW( expr, ... )(VOID) ((!!(expr)) || \(1 != _CrtDbgReportW(_CRT_ASSERT, _CRT_WIDE(__FILE__), __LINE__, NULL, L#expr L"\r\nMessage: " _CRT_UNPARENTHESIZE_(##__VA_ARGS__) )) || \(_CrtDbgBreak(), 0))#else#define ASSERT_EXPRW( expr, ... )(VOID) ((!!(expr)) || \(1 != _CrtDbgReportW(_CRT_ASSERT, _CRT_WIDE(__FILE__), __LINE__, NULL, L#expr L"\r\nMessage: " ##__VA_ARGS__ )) || \(_CrtDbgBreak(), 0))#endif#else#define ASSERT_EXPRW( expr, ... )__noop#endif#define ASSERT_MSGWASSERT_EXPRW#if defined( _DEBUG )#define ASSERT_EXPRA( expr, ... ) (VOID) ((!!(expr)) || \                (1 != _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, #expr "\r\nMessage: " ##__VA_ARGS__)) || \                (_CrtDbgBreak(), 0))#else#define ASSERT_EXPRA__noop#endif#define ASSERT_MSGAASSERT_EXPRA#if defined( _UNICODE )#define ASSERT_EXPRASSERT_EXPRW#define ASSERT_MSGASSERT_MSGW#else#define ASSERT_EXPRASSERT_EXPRA#define ASSERT_MSGASSERT_MSGA#endif/** 2016年11月22日18时20分,此处的_CrtDbgReport 函数在 Release版中可能不存在。原因是只有 MTd 或MDd编译时 _CrtDbgReport函数才存在。所以,此处有误区,需要重新VERIFY_MSG宏。< .fuhao >< .2016年11月22日18时21分 >*//** VERIFY_EXPR、VERIFY_MSG 无论 Debug还是Release都有效的 ASSERT_EXPR *//*#define VERIFY_EXPRW( expr, ... ) (VOID) ((!!(expr)) || \                (1 != _CrtDbgReportW(_CRT_ERROR, _CRT_WIDE(__FILE__), __LINE__, NULL,  L"Expression: "L#expr L"\r\nMessage: "##__VA_ARGS__)) || \                (DebugBreak(), 0))#define VERIFY_MSGWVERIFY_EXPRW#define VERIFY_EXPRA( expr, ... ) (VOID) ((!!(expr)) || \                (1 != _CrtDbgReport(_CRT_ERROR, __FILE__, __LINE__, NULL, "Expression: "#expr"\r\nMessage: "##__VA_ARGS__)) || \                (DebugBreak(), 0))#define VERIFY_MSGAVERIFY_EXPRA*/#if defined ( _DEBUG )#define VERIFY_EXPRWASSERT_EXPRW#define VERIFY_MSGWASSERT_MSGW#define VERIFY_EXPRAASSERT_EXPRA#define VERIFY_MSGAASSERT_MSGA#else#define VERIFY_EXPRW( expr, ... ) ((VOID)(expr))#define VERIFY_MSGWVERIFY_EXPRW#define VERIFY_EXPRA( expr, ... ) ((VOID)(expr))#define VERIFY_MSGAVERIFY_EXPRA#endif#if defined( _UNICODE )#define VERIFY_EXPRVERIFY_EXPRW#define VERIFY_MSGVERIFY_EXPRW#else#define VERIFY_EXPRVERIFY_EXPRA#define VERIFY_MSGVERIFY_EXPRA#endif


ANONYMOUS_VAR
/*ANONYMOUS_VAR用来定义匿名变量并向构造函数传递参数。*/#if !defined( ANONYMOUS_VAR )#define __ANONYMOUS_VAR( type, var, line, ... ) type var##line(##__VA_ARGS__)#define _ANONYMOUS_VAR( type, line, ... ) __ANONYMOUS_VAR( type, var, line, ##__VA_ARGS__ )#define ANONYMOUS_VAR( type, ... )_ANONYMOUS_VAR( type, __LINE__, ##__VA_ARGS__ )#endif


0 0