/*!
 * \file $RCSfile$
 * \version $Revision: 228662 $
 * \date $Date:: 2021-06-08 22:32:15 +0300#$
 * \author $Author: maxdm $
 *
 * \brief Замена для модуля <atltrace.h> из библиотеки ATL Microsoft.
 *
 * Этот файл определяет свою версию функций трассировки в журнал отладки.
 * Эта версия похожа на стандарную ATL версию, но имеет следующие особенности:
 *  1) Она умеет протоколировать не только имя файла и строку, но и имя функции.
 *  2) Она умеет протоколировать и для Release сборок.
 *
 * Макросы ATLTRACE и ATLTRACE2 перенаправляются на нашу реализацию для
 * отладочных _DEBUG сборок и сам ATL переопределяет их в (void)0 для
 * Release сборок, т.е. их поведение в целом сохраняется, но появляется
 * возможность вывода имени функции.
 *
 * Здесь определяются новые макросы CPTRACE и CPTRACE2, которые выводят
 * информацию не только для отладочных, но и для Release сборок.
 *
 * К сожалению, из этого файла нельзя включить <atltrace.h>, как это принято
 * в других заголовках ATL2, т.к. он косвенно включит файл <windows.h>.
 * Но в проектах MFC файл <afx.h> желает сам
 * включать <windows.h>. Поэтому порядок включения такой:
 * "atltrace2.h" затем <afx.h> и уже только потом "atltrace2.inl"
 * Для проектов ATL таких ограничений вроде нет, поэтому можно включить
 * "atltrace2.inl" непосредственно после "atltrace2.h", при этом 
 * <atltrace.h> тоже будет включен.
 */

#ifndef _ATLTRACE2_H_INCLUDED
#define _ATLTRACE2_H_INCLUDED

#ifdef USE_CPTRACEB
#error cptraceb.lib is obsolete, use cptrace.lib instead
#endif //USE_CPTRACEB


// Макрос USE_CPTRACE показывает, надо ли вообще использовать наш замечательный
// CPTraceFileAndLineInfo(__FUNCTION__, __FILE__, __LINE__) и заместить им
// макросы ATLTRACE и ATLTRACE2.

// Макрос USE_CPTRACE_IMPL показывает, нужно ли нашему замечательному
// CPTraceFileAndLineInfo(__FUNCTION__, __FILE__, __LINE__)
// использовать собственную реализацию фунций в cptrace.lib, иначе 
// будет использоваться реализация из ATL библиотеки atls.lib.

// Макрос NOT_USE_ATLTRACE_H говорит о том, что нужно полностью заменить 
// файл <atltrace.h> этим файлом и своей библиотекой и направить
// даже макросы ATLTRACE и ATLTRACE2 в нашу реализацию.

#define USE_CPTRACE

#ifdef USE_COMMON_MAPFILE_32_64
#define XDWORD_PTR DWORD
#define XDWORD_PTR_UINT unsigned int
#else //USE_COMMON_MAPFILE_32_64
#define XDWORD_PTR DWORD_PTR
#define XDWORD_PTR_UINT DWORD_PTR
#endif //USE_COMMON_MAPFILE_32_64

#ifndef USE_CPTRACE
// Если просили не замещать ATLTRACE и ATLTRACE2, оставим их в покое,
// но макросы CPTRACE и CPTRACE2 все-таки определим.
#define CPTRACE ATLTRACE
#define CPTRACE2 ATLTRACE2
#define DECLARE_CPTRACE_CATEGORY(name) DECLARE_TRACE_CATEGORY(name)
#define CPTraceGeneral	ATL::atlTraceGeneral
#define CPTraceSnapin	ATL::atlTraceSnapin
#else //USE_CPTRACE

#ifdef _DEBUG

// Для _WIN64 родная реализация ATLTRACE ущербная (поплыли размеры структур),
// поэтому на _WIN64 сделан вариант использования своей реализации 
// CPTraceModuleManager как для Release, так и для Debug конфигураций.
// Однако использование этого варианта приводит к проблемам со старым
// системным atl.dll, от которого зависят некоторые другие системные
// DLL, например activeds.dll. Поэтому здесь всё закоментировано.
#ifdef USE_COMMON_MAPFILE_32_64
//#define NOT_USE_ATLTRACE_H
#endif //USE_COMMON_MAPFILE_32_64

#endif //_DEBUG

#define USE_CPTRACE_IMPL

#ifdef NOT_USE_ATLTRACE_H
#define __ATLTRACE_H__ // Чтоб atltrace.h реально не включился.
#endif //NOT_USE_ATLTRACE_H

#ifdef _MSC_VER

#pragma comment(linker, "/include:?g_bCPTraceSettingsInited@@3_NA")

#endif //_MSC_VER

#ifdef NOT_USE_ATLTRACE_H
#pragma comment(lib, "atlsd_nobug.lib")
#pragma comment(linker, "/nodefaultlib:atlsd")
#endif //NOT_USE_ATLTRACE_H

#ifdef _DEBUG
// Для передачи трассы и ATLASSERT-ов в программу WebDbg.exe нужен DECLARE_DEBUGREPORTHOOK
// однако, если WebDbg.exe не запущен, то в этом случае ASSERT-ы пропадают совсем (везде)
// и никакой DECLARE_NOUIASSERT не помогает их вернуть
#define DECLARE_DEBUGREPORTHOOK() ATL::CDebugReportHook _g_DebugReportHook;
#else //_DEBUG
#define DECLARE_DEBUGREPORTHOOK()
#endif //_DEBUG

// Для любых конфигурации CPTRACE указывает на CPTraceFileAndLineInfo
#define CPTRACE2 ATL::CPTraceFileAndLineInfo(__FUNCTION__, __FILE__, __LINE__)
#define CPTRACE CPTRACE2

#ifdef USE_CPTRACE_IMPL
#define DECLARE_CPTRACE_CATEGORY(name) extern ATL::CPTraceCategory name;
#else //USE_CPTRACE_IMPL
#define DECLARE_CPTRACE_CATEGORY(name) DECLARE_TRACE_CATEGORY(name)
#define CPTraceGeneral	ATL::atlTraceGeneral
#define CPTraceSnapin	ATL::atlTraceSnapin
#endif //USE_CPTRACE_IMPL

#ifdef NOT_USE_ATLTRACE_H
//#define CTraceCategory CPTraceCategory
#define CPTraceCategory CTraceCategory
#define CCPTrace CTrace
#endif // NOT_USE_ATLTRACE_H

#if !defined UNIX
#include <basetsd.h>
#else
#include <wchar.h>
#endif	/* UNIX */

namespace ATL {

#ifdef USE_CPTRACE_IMPL

typedef enum CPTRACESTATUS
{
    CPTRACESTATUS_INHERIT, CPTRACESTATUS_ENABLED, CPTRACESTATUS_DISABLED
} CPTRACESTATUS;

// Declare a global instance of this class to automatically register a custom trace category at startup
class CPTraceCategory
{
public:
// К сожалению, в этом файле еще нельзя пользоваться типами LPCTSTR и пр.,
// Т.к. к этому моменту они еще не определены (а это сделано из-за MFC).

#ifdef UNICODE
    explicit CPTraceCategory( const wchar_t *pszCategoryName, unsigned int nStartingLevel = 0 ) throw();
#else //UNICODE
    explicit CPTraceCategory( const char *pszCategoryName, unsigned int nStartingLevel = 0 ) throw();
#endif //UNICODE

    unsigned int GetLevel() const throw();
    void SetLevel( unsigned int nLevel ) throw();
    CPTRACESTATUS GetStatus() const throw();
    void SetStatus( CPTRACESTATUS eStatus) throw();

    operator XDWORD_PTR_UINT () const throw();

public:
    XDWORD_PTR_UINT m_dwCategory;
};

#endif //USE_CPTRACE_IMPL

class CPTraceFileAndLineInfo
{
public:
    CPTraceFileAndLineInfo(const char *pszTypeName, const char *pszFileName, int nLineNo);
    CPTraceFileAndLineInfo(const char *pszFileName, int nLineNo);
    void __cdecl operator()(DWORD_PTR dwCategory, UINT32 nLevel, const char *pszFmt, ...) const;
    void __cdecl operator()(DWORD_PTR dwCategory, UINT32 nLevel, const wchar_t *pszFmt, ...) const;
    void __cdecl operator()(const char *pszFmt, ...) const;
    void __cdecl operator()(const wchar_t *pszFmt, ...) const;
private:
    const char *cut_filename_add_typeinfo(const char *pszTypeName, const char *pszFileName);
    const char *cut_filename(const char *pszFileName);

    /* unimplemented */
    CPTraceFileAndLineInfo &__cdecl operator=(const CPTraceFileAndLineInfo &right);
    CPTraceFileAndLineInfo(const CPTraceFileAndLineInfo &source);

    const char *m_pszFileName;
    int m_nLineNo;
    char m_filepart[260];
};

#ifdef USE_CPTRACE_IMPL
// Declare a global instance of this class to automatically register a custom trace category at startup
DECLARE_CPTRACE_CATEGORY(CPTraceGeneral);
DECLARE_CPTRACE_CATEGORY(CPTraceSnapin);
#endif //USE_CPTRACE_IMPL

} // namespace ATL

#endif //USE_CPTRACE

#ifdef NOT_USE_ATLTRACE_H
namespace ATL {
// Определим здесь все, что должен был определить atltrace.h

DECLARE_CPTRACE_CATEGORY( atlTraceGeneral )
DECLARE_CPTRACE_CATEGORY( atlTraceCOM )
DECLARE_CPTRACE_CATEGORY( atlTraceQI )
DECLARE_CPTRACE_CATEGORY( atlTraceRegistrar )
DECLARE_CPTRACE_CATEGORY( atlTraceRefcount )
DECLARE_CPTRACE_CATEGORY( atlTraceWindowing )
DECLARE_CPTRACE_CATEGORY( atlTraceControls )
DECLARE_CPTRACE_CATEGORY( atlTraceHosting )
DECLARE_CPTRACE_CATEGORY( atlTraceDBClient )
DECLARE_CPTRACE_CATEGORY( atlTraceDBProvider )
DECLARE_CPTRACE_CATEGORY( atlTraceSnapin )
DECLARE_CPTRACE_CATEGORY( atlTraceNotImpl )
DECLARE_CPTRACE_CATEGORY( atlTraceAllocation )
DECLARE_CPTRACE_CATEGORY( atlTraceException )
DECLARE_CPTRACE_CATEGORY( atlTraceTime )
DECLARE_CPTRACE_CATEGORY( atlTraceCache )
DECLARE_CPTRACE_CATEGORY( atlTraceStencil )
DECLARE_CPTRACE_CATEGORY( atlTraceString )
DECLARE_CPTRACE_CATEGORY( atlTraceMap )
DECLARE_CPTRACE_CATEGORY( atlTraceUtil )
DECLARE_CPTRACE_CATEGORY( atlTraceSecurity )
DECLARE_CPTRACE_CATEGORY( atlTraceSync )
DECLARE_CPTRACE_CATEGORY( atlTraceISAPI )

// atlTraceUser categories are no longer needed.  Just declare your own trace category using CTraceCategory.
DECLARE_CPTRACE_CATEGORY( atlTraceUser )
DECLARE_CPTRACE_CATEGORY( atlTraceUser2 )
DECLARE_CPTRACE_CATEGORY( atlTraceUser3 )
DECLARE_CPTRACE_CATEGORY( atlTraceUser4 )

#pragma deprecated( atlTraceUser )
#pragma deprecated( atlTraceUser2 )
#pragma deprecated( atlTraceUser3 )
#pragma deprecated( atlTraceUser4 )
} // namespace ATL

#define ATLTRACENOTIMPL(funcname) __pragma(warning(push)) \
    __pragma(warning(disable: 4127)) \
    do { ATLTRACE(ATL::atlTraceNotImpl, 0, \
    _TEXT("ATL: %s not implemented.\n"), funcname); \
    return E_NOTIMPL; \
    } while(0) \
    __pragma(warning(pop))

#endif //NOT_USE_ATLTRACE_H

#include "atldef2.h"

// К сожалению, здесь нельзя включить <atltrace.h>, т.к. он косвенно
// включит файл <windows.h>. Но в проектах MFC файл <afx.h> желает сам
// включать <windows.h>. Поэтому порядок включения такой:
// "atltrace2.h" затем <afx.h> и уже только потом "atltrace2.inl"
// но для макоси он нужен всегда иначе в дебаге неопределенный символ 
// __ZN3ATL22CPTraceFileAndLineInfoC1EPKcS2_i

#if defined DEBUG && defined DARWIN
#include "tchar.h"
#include "atltrace2.inl"
#endif

#endif // _ATLTRACE2_H_INCLUDED
