// aplAggr.h
#pragma once

#define APL_AGGR_INCLUDED

#define ENABLE_APL_STRING
//#define USE_APL_ARRAY

#ifndef _MFC_VER

	#define ENABLE_APL_STRING		  // Компилировать CaplString
	#define USE_APL_STRING_AS_CSTRING // Использовать CaplString вместо CString
	#define USE_APL_ARRAY             // Использовать CaplArray  вместо CArray

	#ifndef  UNICODE
		#define  UNICODE   // Так в файлах Windows
	#endif
	#ifndef _UNICODE
		#define _UNICODE   // Так в стандарте С
	#endif
#endif
//*************************************************************************
//*************************************************************************
#ifdef _MFC_VER
	#ifndef APL_AGGR_NOAUTOLIB
		#ifdef _DEBUG
			#ifdef UNICODE
			#pragma comment (lib,"aplAggr_ud.lib")
			#else
			#pragma comment (lib,"aplAggrD.lib")
			#endif
		#else
			#ifdef UNICODE
			#pragma comment (lib,"aplAggr_u.lib")
			#else
			#pragma comment (lib,"aplAggr.lib")
			#endif
		#endif
	#endif

//  Microsoft
	#ifdef APL_AGGR_DLL
		#define APL_AGGR_API __declspec(dllexport)
	#else
		#define APL_AGGR_API __declspec(dllimport)
	#endif

	#ifdef _DEBUG
		#define DEBUG_BEEP(p1,p2) Beep(p1,p2)
	#else
		#define DEBUG_BEEP(p1,p2)
	#endif

	#define __APL_FUNC__ _T(__FUNCTION__)

#else
//  GCC
	#ifdef APL_AGGR_DLL
		#define APL_AGGR_API __attribute__((visibility("default")))
	#else
		#define APL_AGGR_API
	#endif

	#define DEBUG_BEEP(p1,p2)
	#define __APL_FUNC__  ((LPCTSTR)CaplStringAdapter(__func__))

/*#else
// неизвестная среда
	#define APL_AGGR_API
	#pragma warning Неизвестная среда для динамической линковки
	#error*/
#endif
//*************************************************************************

//*************************************************************************
#ifndef _MFC_VER
    #include <set>  // set string и wctype нужны для сборки под GCC 11 и 12 версий
    #include <string>
    #include <wctype.h>
	#include "aplWinDefineForGCC.h"
#endif
//*************************************************************************

// Макрос APL_STOP_IF_DEBUG останавливает работу (если true==bAplAggrStopInDebugEnable)

extern bool bAplAggrStopInDebugEnable;  //Включает или выключает APL_STOP_IF_DEBUG

#ifdef _DEBUG
	#ifdef _MFC_VER
		#define APL_STOP_IF_DEBUG {if(bAplAggrStopInDebugEnable) ASSERT(FALSE);}
	#else
		#ifdef __linux__
			#define APL_STOP_IF_DEBUG {if(bAplAggrStopInDebugEnable) {__asm__("int3");}}
		#else
			#define APL_STOP_IF_DEBUG {if(bAplAggrStopInDebugEnable) {__debugbreak();}}  // {__asm__("int3");}}
		#endif
		#define ASSERT(p) {if(!(p)) APL_STOP_IF_DEBUG}
	#endif
#else
	#define APL_STOP_IF_DEBUG
	
	#ifndef _MFC_VER
		#define ASSERT(p)
	#endif
#endif


#define APL_MAX_STRING_LENGTH 4000

// 5<100 - разные приложения, код ошибки завив
// 101 - не определено (значение по умолчанию)
// В конкретных приложениях этот макрос будет переопределяться чтобы показывать, из какого 
// приложения вылетело исключение и какого типа значение лежит в m_err_code и остальных полях
#define APL_THROW_LEVEL 0 
// обозначения приложений
// 1 - транспорт, код ошибки представляет собой ASC_ERROR_CODES
#define SaplErrorDescriptionLevel_Transport 1
// 2 - CaplData, код ошибки - число из дефайнов APLAPIERR_xxx из этого файла
#define SaplErrorDescriptionLevel_CaplData 2
// 3 - NetStepData, код ошибки - число из дефайнов APLAPIERR_xxx из этого файла и APL_NET_CLI_xxx из aplNetStepData.h
#define SaplErrorDescriptionLevel_NetStepData 3
// 4 - Сервер, код ошибки - enum AplNetErrors из MiddleWare.h
#define SaplErrorDescriptionLevel_Server 4
// 5 - Сервер, код ошибки - http код из стандартных
#define SaplErrorDescriptionLevel_ServerHttp 5

#define APL_WRITE_TO_LOG(p) 



//******************************************************

#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <set>

#ifndef USE_APL_ARRAY
	
	#include <afxtempl.h> // подключаем CArray для MFC
#endif


#ifdef __linux__
#define aplDirRazd _T('/')
#else
	#define aplDirRazd _T('\\')
#endif
// Неправильный разделитель путей из другой ОС
#ifdef __linux__
	#define aplMissedDirRazd _T('\\')
#else
	#define aplMissedDirRazd _T('/')
#endif

#ifdef UNICODE
	typedef std::wstring _std_string;
#else
	typedef std::string _std_string;
#endif


#ifdef UNICODE
	#define _strcpy wcscpy
	#define _strncpy wcsncpy
	#define _strcmp wcscmp
	#define _strncmp wcsncmp
	#define _strlen wcslen
	//#define _sprintf swprintf
	//#define _vsprintf vswprintf
	#define _fprintf fwprintf
	#define _fopen _wfopen
	#define _remove _wremove
	#define _rename _wrename
	#define __itoa _itow
	#define __atof _wtof
	#define __atol _wtol
	#define _atoi _wtoi
	#define _isdigit iswdigit
	#define _strtok wcstok
	#define _strstr wcsstr
	#define __access _waccess
	#define _printf wprintf
	#define _fgets fgetws
	#define _fputs fputws
	#define _EOF WEOF
	#define __finddata_t _wfinddata_t
	#define __findfirst _wfindfirst
	#define __findnext _wfindnext
	#define _getenv _wgetenv
	#define _setlocale _wsetlocale
	#define __splitpath _wsplitpath
	#define __open _wopen
	#define _isalnum iswalnum
	#define _fputc fputwc
	#define __fsopen _wfsopen
	#define _strtol wcstol
	#define _strtoul wcstoul
#else
	#define _strcpy strcpy
	#define _strncpy strncpy
	#define _strcmp strcmp
	#define _strncmp strncmp
	#define _strlen strlen
	//#define _sprintf sprintf
	//#define _vsprintf vsprintf
	#define _fprintf fprintf
	#define _fopen fopen
	#define _remove remove
	#define _rename rename
	#define __itoa _itoa
	#define __atof atof
	#define __atol atol
	#define _atoi atoi
	#define _isdigit isdigit
	#define _strtok strtok
	#define _strstr strstr
	#define __access _access
	#define _printf printf
	#define _fgets fgets
	#define _fputs fputs
	#define _EOF EOF
	#define __finddata_t _finddata_t
	#define __findfirst _findfirst
	#define __findnext _findnext
	#define _getenv getenv
	#define _setlocale setlocale
	#define __splitpath _splitpath
	#define __open _open
	#define _isalnum isalnum
	#define _fputc fputc
	#define __fsopen _fsopen
	#define _strtol strtol
	#define _strtoul strtoul
#endif

#ifdef _UNICODE
	#define _sscanf swscanf
	#define __strdate _wstrdate
	#define __strtime _wstrtime
	#define __mkdir _wmkdir
#else // #ifdef _UNICODE
	#define _sscanf sscanf
	#define __strdate _strdate
	#define __strtime _strtime
	#define __mkdir _mkdir
#endif // #ifdef _UNICODE


#ifdef __linux__
	// в линуксе x64 обычный long восьмибайтный
	#define __fseek64 fseek
	#define __ftell64 ftell
#else
	#define __fseek64 _fseeki64
	#define __ftell64 _ftelli64
#endif
//******************************************************
//******************************************************
//******************************************************

inline LPCTSTR aplTranslate(LPCTSTR str); // Чтобы header обрабатывался в 1 проход (под QTCreator)

	#define APL_T(str) aplTranslate(_T(str))
	#define APL_NO_T(str) _T(str)
#ifndef _MFC_VER
	// Макрос для перевода на другие языки строк QString
	#define APL_Q(stringA) QString(aplTranslate(stringA))
#endif

// декларируем класс CaplStringAdapter для нормальной работы __APL_FUNC__
class APL_AGGR_API CaplStringAdapter;

//******************************************************
//******************************************************
//******************************************************


// Фунции стандартной библиотеки, отличающиеся по семантике в ANSI и Unicode версиях
int APL_AGGR_API _sprintf(LPTSTR string, LPCTSTR format, ...);

#ifdef USE_APL_ARRAY
	#include "aplArray.h"
	#define CArray CaplArray
#endif

#ifdef _MFC_VER
	typedef CString CMFCString; // Чтобы можно было использовать орининальный MFC-шный клас
#endif 

#ifdef ENABLE_APL_STRING

	#include "aplString.h"
	#include "aplStringW.h"

	#ifdef UNICODE
		typedef CaplStringW CaplStringT;
	#else
		typedef CaplStringA CaplStringT;
	#endif

	typedef CaplStringT CaplString;
	typedef CArray <CaplString,CaplString&> CaplStringArrayTemplate;

class CaplStringArray : public CaplStringArrayTemplate
{
public:
	inline int Add(LPCTSTR newElement)
	{
		CaplString newStr(newElement);
		return CaplStringArrayTemplate::Add(newStr);
	}
};

	#ifdef USE_APL_STRING_AS_CSTRING
		#define CString CaplString
		#define CStringA CaplStringA
		#define CStringW CaplStringW
		#define CStringArray CaplStringArray
	#endif

#else
	typedef CString CaplString;
#endif


//******************************************************
//******************************************************
// файл на основе стандартного файла CRT
//******************************************************
//******************************************************
class APL_AGGR_API CaplFile
{
public:
	enum OpenFlags {
		modeRead = (int)0x00000,
		modeWrite = (int)0x00001,
		modeReadWrite = (int)0x00002,
		//shareCompat = (int)0x00000,		// этот флаг отсутствует в последних реализациях Microsoft _fsopen
		shareExclusive = (int)0x00010,
		shareDenyWrite = (int)0x00020,
		shareDenyRead = (int)0x00030,
		shareDenyNone = (int)0x00040,
		modeNoInherit = (int)0x00080,
#ifdef _UNICODE
		typeUnicode = (int)0x00400, // used in derived classes (e.g. CStdioFile) only
#endif
		modeCreate = (int)0x01000,
		modeNoTruncate = (int)0x02000,
		typeText = (int)0x04000, // used in derived classes (e.g. CStdioFile) only
		typeBinary = (int)0x08000, // used in derived classes (e.g. CStdioFile) only
		//osNoBuffer = (int)0x10000,
		//osWriteThrough = (int)0x20000,
		//osRandomAccess = (int)0x40000,
		//osSequentialScan = (int)0x80000,
	};

	enum SeekPosition { begin = 0x0, current = 0x1, end = 0x2 };

	enum
	{
		APLFILEERR_FILE_NULL = 0		// пустой файл
		, APLFILEERR_READ				// ошибка чтения файла
		, APLFILEERR_WRITE				// ошибка записи файла
		, APLFILEERR_DISK_FULL			// ошибка закрытия файла (переполнение диска)
		, APLFILEERR_SEEK				// Ошибка позиционирования файла
	};

	static const HANDLE hFileNull;

	CaplFile();
	virtual ~CaplFile(); // виртуальный - чтобы QT Creator не ругался

	// открытие файла

	BOOL Open(LPCTSTR lpszFileName, unsigned int nOpenFlags,
		CFileException* pException = NULL);

	bool IsFileOpen(){ return 0!=m_hFile; };

	// чтение строки из файла

	//  Внимание!!! работа функции может быть неоднозначна для файлов в кодировке отличной от однобайтовой 
	//  есть нюансы с флагами открытия файла в linux
	//  Для чтения строк из файла лучше использовать СaplStringFile

	LPTSTR ReadString(LPTSTR lpsz, unsigned int nMax);

#ifdef USE_APL_STRING_AS_CSTRING
	bool ReadString(CaplStringT& sStr);
#else
	bool ReadString(CString& sStr);
#endif

	// запись строки в файл
	bool WriteString(LPCTSTR lpsz);

	// сохранить файл на диск
	virtual bool Flush();

	// закрыть файл
	virtual bool Close();

	// перемещение по файлу
	virtual unsigned long long Seek(long long lOff, unsigned int nFrom);

	// получить текущую позицию. При ошибке возвращает -1 (CFile::GetPosition при ошибке вызывает исключение)
    virtual long long GetPosition( ) const;

	// перемещение по файлу в конец/начало
	unsigned long long SeekToEnd();
	void SeekToBegin();

	virtual bool SetLength(unsigned long long dwNewLen); //Меняет размер файл. После выполенения текущая позиция - конец файла.  Если файл 	увеличивается, к нему добавляются нулевые символы '\0'
	virtual __int64 GetLength() const;
    virtual bool Write(const void* lpBuf, unsigned int nCount);
	virtual UINT Read(void* lpBuf, UINT nCount);

	// переименовывает файл
	static bool Rename(LPCTSTR lpszOldName, LPCTSTR lpszNewName	);

	// Удаляет файл
	static bool Remove(LPCTSTR lpszFileName);

	// Создает папку
	static bool CreateFolder(LPCTSTR path);
	
	// проверяет существование файла
	static bool IsFileExist(LPCTSTR lpszFileName); 

	// Проверяет атрибуты файла на доступность для записи
	static bool IsFileAttrAllowWrite(LPCTSTR lpszFileName);

	// Копирует файл
	// bFailIfExists если true, то в случае существования lpNewFileName вернет false, в противном случае перезапишет
	// если true, то копирует и атрибуты доступа (только для windows)
	static bool CopyFile(LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName, bool bFailIfExists, bool bCopyAccessInfo=false);

	// Создает резервную копию файла
	// Копия создается в той-же папке, но если нет доступа то в папке %TEMP%
	// iNumBackupCopies определяет кол-во резервных копий которые надо хранить
	// В имена копий добавляется номер. При создании копии самая старая удаляется
	// остальные циклически переименовываются
	// если iNumBackupCopies<1, то считается, что равен 1
	static bool CreateBackupFile(LPCTSTR lpExistingFileName, int iNumBackupCopies=1);


	// Нормализует строку чтобы её можно было использовать как имя файла
	// (Заменяет все недопустимые символы на '~'
	static void NormalizeFileName(CString &fname);


//protected:  // У MFC-шного CFile это public
	FILE* m_hFile;
};
//***********************************************************
//***********************************************************
//***********************************************************
class CaplFileException
{
public:
	enum {
		none,
		genericException,
		fileNotFound,
		badPath,
		tooManyOpenFiles,
		accessDenied,
		invalidFile,
		removeCurrentDir,
		directoryFull,
		badSeek,
		hardIO,
		sharingViolation,
		lockViolation,
		diskFull,
		endOfFile
	};

	CaplFileException(int cause = CFileException::none, LONG lOsError = -1, LPCTSTR lpszArchiveName = NULL);

	// Attributes
	int     m_cause;
	LONG    m_lOsError;
	CString m_strFileName;

	// Operations
	// convert a OS dependent error code to a Cause
	static int OsErrorToException(LONG lOsError);
	static int ErrnoToException(int nErrno);

	static LPCTSTR Error2String(int iErr);

/*	// helper functions to throw exception after converting to a Cause
	static void PASCAL ThrowOsError(LONG lOsError, LPCTSTR lpszFileName = NULL);
	static void PASCAL ThrowErrno(int nErrno, LPCTSTR lpszFileName = NULL);

	// Implementation
public:
	virtual ~CFileException();
#ifdef _DEBUG
	virtual void Dump(CDumpContext&) const;
#endif
	virtual BOOL GetErrorMessage(_Out_z_cap_(nMaxError) LPTSTR lpszError, _In_ UINT nMaxError,
		_Out_opt_ PUINT pnHelpContext = NULL) const;*/
};



//***********************************************************
//***********************************************************
//***********************************************************

class APL_AGGR_API SaplErrorDescription
{
private:
	const SaplErrorDescription& operator =(const SaplErrorDescription &){return *this;}
public:
	SaplErrorDescription();
    SaplErrorDescription(int _err_code, LPCTSTR _filename, LPCTSTR _datecompile, int _err_line,
        LPCTSTR _err_descr=_T(""), int _core_level=APL_THROW_LEVEL, LPCTSTR _funct_name=0
        ,LPCTSTR _cls_info=0 , LPCTSTR  _err_descr2=0);
    SaplErrorDescription(const SaplErrorDescription& src);

    SaplErrorDescription(const SaplErrorDescription& src, LPCTSTR _filename, LPCTSTR _datecompile, int _err_line,
		LPCTSTR _err_descr=_T(""), LPCTSTR _funct_name=0, LPCTSTR _cls_info=0 );
	~SaplErrorDescription();

    void Set(const SaplErrorDescription& src);
    void Set(const SaplErrorDescription& src, LPCTSTR _filename, LPCTSTR _datecompile, int _err_line,
		LPCTSTR _err_descr=_T(""), LPCTSTR _funct_name=0, LPCTSTR _cls_info=0 );
	CString Print(LPCTSTR add_info = 0, bool for_mb = true);


	int m_err_code;
	CString m_filename;
	CString m_datecompile;
	int m_err_line;

	CString m_funct_name; // название функции (если определено)
	CString m_cls_info;	// описание класса в котором произошла ошибка (если определено)

    CArray<SaplErrorDescription*,SaplErrorDescription*> m_stack;

	CString m_err_descr; // описание ошибки, контекста ошибки
	CString m_err_descr2; // расширенное описание ошибки в зависимости от уровня. Например, описание ошибки оракла (уровень 3) или ошибки механизма сокетов (уровень 0)

	// уровень приложения в котором возникло исключение и тип ошибки
	int m_core_level;

	// по умолчанию - этот флаг установлен в false и при возникновении исключения по памяти - будет выдан MessageBox с сообщением об ошибке
	// в сервере и других службах MessageBox приведет к ошибке и падению; этот флаг надо становить в true, тогда при
	// возникновении исключения по памяти исключение будет отретранслировано на уровень вызывающей программы
	static void __stdcall SetRetranslateMemoryExeption(bool is_retranslate=true);
	static bool __stdcall GetRetranslateMemoryExeption();
};

// Обработка исключений SaplErrorDescription: получение описание ошибок уровня Transport
void APL_AGGR_API SetGetTransportErrorDescription(CString (__cdecl *_GetTransportErrorDescription)(int));

// Обработка исключений SaplErrorDescription: получение описание ошибок уровня StepData
void APL_AGGR_API SetGetStepDataErrorDescription(CString (__cdecl *_GetStepDataErrorDescription)(int));

// Обработка исключений SaplErrorDescription: получение описание ошибок уровня сервера
void APL_AGGR_API SetGetServerErrorDescription(CString (__cdecl *_GetServerErrorDescription)(int));

// Порождает исключение SaplErrorDescription с одним кодом ошибки
#define APL_THROW(err_code) throw(SaplErrorDescription(err_code,_T(__FILE__),_T(__DATE__),__LINE__,_T(""),APL_THROW_LEVEL, __APL_FUNC__))
// Порождает исключение SaplErrorDescription с кодом ошибки и описанием
#define APL_THROW_EX(err_code,m_err_descr) throw(SaplErrorDescription(err_code,_T(__FILE__),_T(__DATE__),__LINE__,(LPCTSTR)m_err_descr,APL_THROW_LEVEL, __APL_FUNC__))




//***********************************************************************************
//***********************************************************************************
//***********************************************************************************

// Возможные кодировки  aplUCS2 == aplUTF16LE
typedef enum {aplUnknownEncoding=0, aplANSI=1, aplUTF8=2, aplUCS2=3, aplUTF16BE=4, aplUTF32LE=5, aplUTF32BE=6, aplCP866=7  } aplTextEncoding;

extern const aplTextEncoding aplCurCharCode;  // Код текущей кодировки multiBYTE
extern const aplTextEncoding aplCurWcharCode; // Код текущей кодировки unicode  

#define aplTextEncoding_defined   // чтобы избежать конфликтов с определением в других проектах

// функция возвращает кодировку по BOM в буффере
// если pBomLen!=0, то в нем возвращается длинна BOM
APL_AGGR_API aplTextEncoding aplGetBufferEncodeByBOM(const BYTE *pSrcBuf, int pSrcBufLen, int *pBomLen=0);

// Функция возвращает в pBom данные BOM для кодировки. В pBomLen возвращается длина строки к BOM в байтах
APL_AGGR_API void GetBomData(aplTextEncoding code, BYTE *&pBom, int &pBomLen);

//Возвращает максимальное кол-во байт для символа по кодироке (только для рус. яз.)
APL_AGGR_API size_t aplGetMaxBYTEInCharForEncoding(aplTextEncoding coding);

//Возвращает минимальное кол-во байт для символа по кодироке 
APL_AGGR_API size_t aplGetMinBYTEInCharForEncoding(aplTextEncoding coding);

// Возвращает строку с именем кодировки для вызова iconv
APL_AGGR_API const char* aplGetCodingNameForIcov(aplTextEncoding coding);

// Проверяет данные на соответствие принципам UTF-8
APL_AGGR_API bool IsBufUtf8( const BYTE *pBuf, int iBufLen);

// Проверяет данные на соответствие принципам UCS2
APL_AGGR_API bool IsBufUCS2( const BYTE *pBuf, int iBufLen);

// Класс для работы файлами содержащими строки
// Сделан взамен СStdioFile для работы с файлами в заранее неизвестной кодировке
// Должен унифицировать работу с текстовыми файлами в ANSI и UNICODE конфигурациях
// Считается, что все не ANSI файлы в начале имеют BOM
// При чтении игнорирует пустые строки
// Нулевой символ считает переводом строки

// Класс модержит много Windows специфики и под linux не нужен, т.к. маловероятно, что в linux будут файлы в 1251
// Вместо использования GetBufferEncode класса надо использовать aplGetBufferEncodeByBOM

class APL_AGGR_API CaplStringFile
{
private:
	const CaplStringFile& operator =(const CaplStringFile& ){return *this;}
public:
	CaplStringFile(void);
	CaplStringFile(LPCTSTR lpszFileName, UINT nOpenFlags);

	~CaplStringFile(void);

	BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags, aplTextEncoding prev_FileCoding = aplUnknownEncoding);
	BOOL Flush();  // Скидывает изменения на диск. Пишет все данные целиком.
	void Close();  // Если есть изменеия, то скидывает их на диск

	BOOL Load2Memory(); // Зачитывает целиком в память.  Автоматически вызывается из ReadString

	BOOL WriteString(LPCTSTR lpsz);  // Записывает в буффер, но на диск не пишется
	BOOL ReadString(CString& rString); // Читает очередную строку из буффера (см. описание класса)
	BOOL ReadAllDataToString(CString& rString); // Заносит все данне файл в строку

	void SeekToBegin( ); // Помещаем позицию считывания в начало и проматываем BOM, если он есть

	UINT GetPosition(); // Возвращает текущую позицию в данных

	UINT SetPosition(UINT pos); // Устанавливает текущую позицию в данных. Возвращает получившееся значение

	// Устанавливает кодировку в которой будет записан файл
	// По умолчанию ANSI для конфигурации ANSI и UCS2 для конфигурации Unicode
	void SetFileEncoding(aplTextEncoding NewCoding){ m_FileCoding=NewCoding; }

	// Возвращает кодировку текущего файла
	aplTextEncoding GetFileEncoding(){return m_FileCoding;}

	// Устанавливае кодироку используемую вместо распознавания, если нет BOM
	void SetEncodingIfNotBOM(aplTextEncoding NewCoding){ m_UseEncodingIfNotBOM=NewCoding; }

	// Проверяет есть ли BOM в начале буфера. Если есть возвращает кодировку этого BOM
	// Если BOM-а нет, то возвращает aplANSI. При ощибке возвращает  aplUnknownCode
	static aplTextEncoding GetBufferEncode( BYTE *pSrcBuf, int pSrcBufLen);

	// Преобразует данние из UCS2 (pSrcBuf) в кодировку  newEncoding
	// Создает новый буффер и возвращает его в pDstBuf
	// pSrcBufLen и piDstBufSize в байтах
	static bool ConverUCS2Encoding( BYTE *pSrcBuf, int pSrcBufLen, aplTextEncoding DstEncoding, BYTE *&pDstBuf, int &iDstBufSize);

	// Преобразует данние из SrcEncoding (pSrcBuf) в кодировку  UCS2
	// Создает новый буффер и возвращает его в pDstBuf
	// pSrcBufLen и piDstBufSize в байтах	
	static bool ConvertEncoding2UCS2(aplTextEncoding SrcEncoding, BYTE *pSrcBuf, int pSrcBufLen, BYTE *&pDstBuf, int &iDstBufSize); 

	// Преобразует данние из SrcEncoding (pSrcBuf) в кодировку  DstEncoding
	// Создает новый буффер и возвращает его в pDstBuf
	// pSrcBufLen и iDstBufSize в байтах	
	static bool ConvertEncoding(aplTextEncoding SrcEncoding, BYTE *pSrcBuf, int pSrcBufLen, aplTextEncoding DstEncoding, BYTE *&pDstBuf, int &iDstBufSize); 

	// Преобразование буффер BE<->LE
	static bool BufConvert_Utf16_BE2LE(BYTE *pBuf, UINT pBufLen); 
	static bool BufConvert_Utf32_BE2LE(BYTE *pBuf, UINT pBufLen); 

	// Преобразование Utf32->UCS2 выкидыванием лишних байт (только для русского)
	// Создает буфер и возвращает его в pBuf16, а его длинну (в байтах) в pBuf16Len
	static bool BufConvert_Utf32le_to_UCS2(BYTE *pBuf32, UINT pBuf32Len, BYTE *&pBuf16, UINT &pBuf16Len);

	// Преобразование UCS2->Utf32le добавлением нулей (только для русского)
	// Создает буфер и возвращает его в pBuf32, а его длинну (в байтах) в pBuf32Len
	static bool BufConvert_UCS2_to_Utf32le(BYTE *pBufUcs2, UINT pBufUcs2Len, BYTE *&pBuf32, UINT &pBuf32Len);

	// Освобождает буфферв выделенный функциями Converd
	// Сделан на случай если в другой dll не будет работать удаление буффера выделенного в этой dll 
	static void FreeBuffer(BYTE *pBuf) {if(0!=pBuf) delete pBuf; };

	CString GetErrorMessage(){return m_ErrorMessage;}

	bool IsFileOpen(){ return 0!=m_file.m_hFile;};

	// Функции не рекомендуемые к использованию
	const BYTE *_GetBuf(){return m_pBuf;};
	UINT _GetDataSize(){return m_iDataSize;};
	UINT _GetCurPos(){return m_iCurPosRead;};

protected:	
	CFile m_file;
	bool m_bDataInMemory;	// файл считан в память
	bool m_bDataChanged;	// данные были изменены
	BYTE *m_pBuf;			// буффер в памяти	
	UINT m_iBufSize;		// размер буффера (в байтах)
	UINT m_iDataSize;		// кол-во данных в буффере (в байтах)
	UINT m_iCurPosRead;		// текущая позиция в буффере для ReadString (в байтах)

	aplTextEncoding m_FileCoding;   // Кодировка файла
	aplTextEncoding m_BufferCoding; // Кодировка в памяти

	aplTextEncoding m_UseEncodingIfNotBOM; // кодировка используемая вместо автораспознавания, если нет BOM (

	void Reset(); // Обнуляет всё переменные (чтобы было в одном месте)
	void FreeBuffer(); // Освобождает буффер
	bool SetBufSize(UINT iNewBufSize); // Увеличивает размер текущего буффера

	CString m_ErrorMessage; // текст ошибки при открытии файла
};


//***********************************************************************************
//***********************************************************************************
//***********************************************************************************

class CaplAggr;
//***********************************************************
//***********************************************************
//*** Класс для работы с буффером данных переменной длинны ***
//***********************************************************
//***********************************************************
class APL_AGGR_API CaplDataBuf
{
private:
	CaplDataBuf(const CaplDataBuf& ){}
	const CaplDataBuf& operator =(const CaplDataBuf& ){return *this;}
public:
	// Attributes
	char *m_data;  /**<Указатель на текущий буффер (может меняться при добавлении данных или при выделении нового буффера*/
	int m_Size;    /**<Текущий размер данных*/
	bool m_ansi_string; /**<Если true - в любой версии строки пишутся/читаются в буфер как однобайтовые. Если false и версия уникодная - то как wchar_t* */

protected:
	int m_MaxSize;
	int m_Iterator;
	bool m_IsExternalData;

	
	// Metods
public:
	CaplDataBuf();						/**<Конструктор*/
	CaplDataBuf(int max_size);			/**<Конструктор резервирующий max_size байт*/
	CaplDataBuf(char* data,int size);
	virtual ~CaplDataBuf();
	void SetExternalData(void* data,int size);
	void AttachExternalData(void* data,int size);
	bool IsExternalData(){return m_IsExternalData;}

	void Clear();						/**<Очистка набора данных*/
	void *GetBuffer();
	void *GetCurBuffer();
	void *GetCurBuffer(const int size);		/**<Возвращает указатель на текущий буфер и передвигает итератор на size.*/
	int Add(const void *buf, const int size);	/**<Добавляет \c size байт из \c buf в конец буффера. Возвращает новый размер.*/
	int AddStr(LPCSTR str);		/** Добавляет строку (без нуля на конце)*/
	int AddStr(LPCWSTR str);		/** Добавляет UNICODE строку (без нуля на конце)*/
	int AddStrBuf(LPCTSTR str);		/** Добавляет размер строки в символах и строку (в зависимости от переменной m_ansi_string) */
	bool SetSize(int max_size);			/**<Резервирует память размером \c max_size байт.*/
	int GetCurPos(){return m_Iterator;} /**<Возвращает текущее положение курсора.*/
	void SetCurPos(int pos){ m_Iterator=pos;} /**<Устанавливает текущее положение курсора.*/
	void Reset(){m_Iterator=0;}			/**<Сбрасывает в 0 текущее положение курсора.*/

	//void Begin();						/**<Установка курсора в начало буффера данных*/ - вместо нее надо использовать функцию Reset
	bool Read(void *buffer,int size);	/**<Чтение size  байт в \с buffer по текщей позиции */
	bool Read(INT32 *buf);				/**<Чтение size целого значения \с buf по текщей позиции*/
	bool Read(UINT32 *buf);
	bool ReadStrBuf(CString &str);		/** Читаем строку (в зависимости от переменной m_ansi_string и согласно длине строки перед телом) */

	// Добавления DIV - функции нужны серверу чтобы обеспечить сквозное использование памяти в транспорте-сервере
	int m_Pos_Sizer;
	bool BeginPosSizer();		// выделить место под переменную, хранящую размер суббуфера
	void *GetNewBuffer(int size);// выделяет буфер, увеличивает размер поля данных, забивает буфер нулями, возвращает указатель на новый буфер
	void EndSiezer(int size=-1);	// устанавливает ранее зарезервированную переменную исходя из текущего размера буфера или из переданного значения
	// добавил Дещере 05_03_10 
	// читает из буфера строчку до перевода строки или до нуля или до конца буфера
    bool ReadString(CString &rString);
	int GetMaxSize(){return m_MaxSize;}

};

//********************************************************
//********************************************************
// Класс для работы с внешним буффером данных 
//********************************************************
class APL_AGGR_API CaplDataBufEx : public CaplDataBuf
{
public:
	// собственный буфер очищается и заменяется на указатель на внешний буфер
	// нужна для уменьшения количества копирований буферов при их чтении и разборе
	void SetExternalData(void* data, int size, int MaxSize);
	/*	Нужна для работы с внешним буфером.
		Добавляет \c size байт из \c buf начиная с позиции итератора и передвигает итератор. Если выделенной 
					памяти не хватает (и работа с собственным буфером) -  увеличивает собственный буфер. Возвращает новую позицию итератора.*/
	int Add2Iterator(void *buf, int size);		
	int Add2Iterator(const char* buf, int size){return Add2Iterator((void*)buf, size);}

	// запись в буфер строки, для передачи внутри процесса или процессу сделанному таким же копилятором
	// добавляет к итератору 4 байта  с длиной строки (длина в символах!) и потом строку в TCHAR
	int AddStrBuf_TCHAR_2Iterator(CString &str);

	// чтение из буфера строки, принятой внутри процесса или от процесса сделанного таким же копилятором!!
	// читает из итератора 4 байта с длиной строки (длина в символах!) и потом строку в TCHAR
	// При ошибке возвращает -1
	int ReadStrBuf_TCHAR_r(CString &str);

	// чтение из буфера строки, принятой внутри процесса или от процесса сделанного таким же копилятором!!
	// читает из итератора 4 байта с длиной строки (длина в символах!) и потом строку в TCHAR
	// При ошибке кидает исключение SaplErrorDescription
	int ReadStrBuf_TCHAR_(CString &str);

	// устанавливает итератор и размер данных в нулевое положение
	void Set2Begin();

	// Возвращает максимальный размер буфера
	int GetMaxSize(){return m_MaxSize;}

	// Копирует внешние данные из buf размером size в ранее выделенный или назначенный внешний буфер начиная со смещения offset
	bool CopyDataTo(int offset, void *buf, int size);

	// Отличие от Read: если данных в буфере меньше чем size, то читает сколько есть и возвращает количество прочитанного.
	int ReadFromIterator(void *buffer, int size);

	// Ищет в буфере последовательность BYTEs размером size байт начиная с позиции start.
	// если найдено - возвращает позицию начала последоватлеьности.
	// если не найдено - возвращает -1
	int FindBytes(int start, void* BYTEs, int size);

	// Устанавливает буферу buf в качестве внешних данных последовательность байт начиная со смещения start размером size
	bool GetBytes(int start, int size, CaplDataBufEx &buf);

	// Записывает в строку str последовательность байт начиная со смещения start длиной size
	// строка str уже сконвертирована из UTF8
	bool GetBytes(int start, int size, CString &str, bool convertFromUTF8 = true);

	// конвертирует строку в utf-8 и записывает в буфер длину строки (4 байта) и саму строку
	bool Utf8StringAdd2Iterator(CString str);
	// читает из буфера 4 байта длину строки, строку в utf-8 и конвертирует ее в текущую кодировку
	bool Utf8StringReadFromIterator(CString &str);

	// Перемещает свой буфер в dest. Свои указатели на буфер, размер и т.п. обнуляет.
	bool MoveData2DBuf(CaplDataBuf &dest);

	bool EnMaskTail(int start, const char* mask, int size);
};



//********************************************************
//*** CaplTAggr ***
//********************************************************
//***********************************************************
#define APLAGGR_DEFAULT			0x00000		/**<Параметр MODE для шаблона CaplTAggr (установить все флаги режимов в false)*/
#define APLAGGR_UNIQUE			0x00001		/**<Параметр MODE для шаблона CaplTAggr (установить Unique в true)*/
#define APLAGGR_LIST			0x00002		/**<Параметр MODE для шаблона CaplTAggr (установить ListMode в true)*/
#define APLAGGR_AUTOKILLREF		0x00004		/**<Параметр MODE для шаблона CaplTAggr (установить AutoKillReference в true)*/

#define APLAGGR_LIST_OR_AUTOKILLREF			APLAGGR_LIST|APLAGGR_AUTOKILLREF
#define APLAGGR_UNIQUE_LIST_AUTOKILLREF		APLAGGR_UNIQUE|APLAGGR_LIST|APLAGGR_AUTOKILLREF


/**Шаблон для работы со списками (аналог CArray)*/
template<class TYPE, class REF_TYPE, int MODE> 
class CaplTAggr   
{
public:
	/** Конструктор
		@param mode  режим работы - комбинация следующих флагов:<br>
			<b>APLAGGR_DEFAULT</b>			все флаги режимов установить в false<br>
			<b>APLAGGR_UNIQUE</b>			установить Unique в true<br>
			<b>APLAGGR_LIST</b>				установить ListMode в true<br>
			<b>APLAGGR_AUTOKILLREF</b>		установить AutoKillReference в true
		@param size количество элементов под которые надо зараезервировать память
		*/
	CaplTAggr(long size=8);
#if __cplusplus >= 201103L
	// Для конструктора вида CaplTAggr<CaplInstance*,X,X> testAggr({inst1, inst2, inst3});
	CaplTAggr(std::initializer_list<TYPE> items):CaplTAggr(items.size())
		{	for (auto item : items) Add(item);}
#endif
	virtual ~CaplTAggr();

	// Attributes
public:
	TYPE *Data;             /**<Указатель на данные (только чтение)*/
	long Size;              /**<Текущий размер (количество элементов)*/
	bool Unique;            /**<Элементы должны быть уникальны*/
	bool ListMode;          /**<Если true CaplTAggr работает как список (при удалении элемента последующие сдвигаются)*/
	bool AutoKillReference; /**<При удалении ссылки на элемент CaplTAggr элемент должен удалиться из памяти*/
	bool Sorted_id;			/**<Флаг отсортированности массива */
	bool Sorted_inst;		/**<Флаг отсортированности массива */

	const CaplTAggr& operator =(const CaplTAggr& src);
	CaplTAggr(const CaplTAggr& src);

protected:
	long MaxSize;
		
	// Metods
public:

	const TYPE* GetData() const;
	TYPE* GetData();

	// [3/3/2008 ZiloT] для небольшого упрощения работы с STL ////////////////
	const TYPE* begin() const	{ return GetData(); }
	TYPE*		begin()			{ return GetData(); }

	const TYPE* end() const		{ return GetData() + GetSize(); }
	TYPE*		end()			{ return GetData() + GetSize(); }
	//////////////////////////////////////////////////////////////////////////

	inline long GetSize() const { return Size; }
	int Add(TYPE  data);				/**<Добавить элемент в конец списка*/
	int AddNonZero(TYPE  data);				/**<Добавить элемент в конец списка, если он не равен нулю*/
	int Append(const CaplTAggr &aggr);		/**<Дополнить другим списком*/
	bool SetAt(int index, TYPE  data);	/**<Установить значение*/
	bool Insert(int index, TYPE  data);	/**<Вставить значение*/
	bool Remove(int index);				/**<Установить значение элемента по индексу*/
	inline TYPE operator [](int index) const
		{if(index<0) return 0; if(index>=Size) return 0;return Data[index];};
	inline TYPE GetAt(int index) const
		{if(index<0) return 0; if(index>=Size) return 0;return Data[index];};
	CaplTAggr& operator<<(const TYPE& item)
		{ Add(item); return *this; }

	int Find(TYPE data) const;			/**<Найти элемент по значению. Возвращает индекс.*/

	void Clear();						/**<Очистить список*/
	bool SetSize(int max_size);			/**<Зарезервировать память под max_size элементов*/

	void Sort();						/**<Отсортировать список по возрастанию*/
	void SortWithFunct(int (__cdecl *comp)(const void *, const void *));
				/**<Отсортировать список с помощью функции. Формат функции - как у qsort*/
	void Normalize();						/**<Отсортировать список и выкинуть повтряющиеся элементы*/

	static int __cdecl CompareElements(const void *p1, const void *p2); // Сравнивает элементы массива, используется в Sort()
};

//********************************************************
template<class TYPE, class REF_TYPE, int MODE>
CaplTAggr <TYPE, REF_TYPE, MODE>::CaplTAggr(long size)
{
	Data=0;
	Size=0;
	Sorted_inst=false;
	Sorted_id=false;
	Unique=(MODE & APLAGGR_UNIQUE) ? true : false;
	ListMode=(MODE & APLAGGR_LIST) ? true :false;
	AutoKillReference=(MODE & APLAGGR_AUTOKILLREF) ? true :false;
	if(size<1) SetSize(16);else SetSize(size);
}
template<class TYPE, class REF_TYPE, int MODE>
CaplTAggr <TYPE, REF_TYPE, MODE>::CaplTAggr(const CaplTAggr& src)
{
	// Этот конструктор скорее всего вызван при неправильно передаче массива параметром в функцию
	// Массив типа CaplTAggr можно передавать в функцию ТОЛЬКО по ссылке или по указателю!
	// Поправьте свой код!
	VERIFY(0);
	// неявное копирование может привести к повреждению данных вызывающей функции!
	//this->operator = (src);
	
	if(src.GetSize()) {} // Тупо чтобы не было warning-a
}

template<class TYPE, class REF_TYPE, int MODE>
CaplTAggr<TYPE, REF_TYPE, MODE>::~CaplTAggr()
{
	Clear();
	if(Data!=0){ delete []Data;Data=0;}
	Size=0;
}

//********************************************************
template<class TYPE, class REF_TYPE, int MODE>
void  CaplTAggr<TYPE, REF_TYPE, MODE>::Clear()
{
	Sorted_inst=false;
	Sorted_id=false;
	if(Size==0) return;
	if(Data==0) return;
	if(AutoKillReference){
		for(int i=0; i<Size;i++){
			if(Data[i]!=0){ 
				delete (REF_TYPE)(INT_PTR)Data[i];
				Data[i]=0;
			}
		}
	}
	Size=0;
}

template<class TYPE, class REF_TYPE, int MODE>
bool CaplTAggr<TYPE, REF_TYPE, MODE>::SetSize(int max_size) // число элементов
{
	if(max_size<Size) return false;
	if(max_size==Size) return true;
	int i;
	TYPE *data1;
	try{
		data1=new TYPE[max_size];
		if(Data!=0)
		{
			for(i=0;i<Size;i++)	data1[i]=Data[i];
			delete []Data;
		}
	}
#ifdef _MFC_VER
	catch(CMemoryException* e)
	{
		TCHAR   szCause[255];  szCause[0]=_T('\0');
		e->GetErrorMessage(szCause, 255);
		szCause[254] = 0;
		CString err_mess;
		err_mess.Format(APL_T("Ошибка при выделении памяти в CaplTAggr<...>::SetSize: %s\r\n Size: %i  max_size: %i"),szCause,Size,max_size);

		if(SaplErrorDescription::GetRetranslateMemoryExeption()){APL_THROW_EX(27, err_mess);}
		else{ MessageBox(NULL, (LPCTSTR)err_mess, APL_T("Ошибка"), MB_OK|MB_ICONERROR);}

		return false;
	}	
#else
    catch(const exception& e)
    {
        CString err_mess;
        err_mess.Format(APL_T("Ошибка при выделении памяти в CaplTAggr<...>::SetSize: %s\r\n Size: %i  max_size: %i"),e.what(),Size,max_size);

        if(SaplErrorDescription::GetRetranslateMemoryExeption()){APL_THROW_EX(27,err_mess);}

        return false;
    }

#endif

	Data=data1;
	MaxSize=max_size;
	return true;
}

//********************************************************
template<class TYPE, class REF_TYPE, int MODE>
int CaplTAggr<TYPE, REF_TYPE, MODE>::Add(TYPE data)
{
	if(Unique)
	{
		for(int i=0;i<Size;i++)
			if((Data)[i]==data) return i;
	}
	if(Size+1>MaxSize) { if (!SetSize((Size+1)*2)) return -1; }
	Data[Size]=data;
	Size++;
	Sorted_inst=false;
	Sorted_id=false;
	return Size-1;
}
//********************************************************
template<class TYPE, class REF_TYPE, int MODE>
int CaplTAggr<TYPE, REF_TYPE, MODE>::AddNonZero(TYPE data)
{
	if(data==0)return -1;
	return Add(data);
}
//********************************************************
template<class TYPE, class REF_TYPE, int MODE>
int CaplTAggr<TYPE, REF_TYPE, MODE>::Append(const CaplTAggr &aggr)
{
	int k=Size-1;
	SetSize(Size+aggr.Size);
	for(int i=0;i<aggr.Size;i++){
		if(aggr.AutoKillReference){
			// Внимание! Если надо копировать массив динамически создаваемых данных -
			// надо переопределять оператор "=" с учетом свойств хранимых объектов
			// этим оператором пользоваться нельзя!
			ASSERT(true);
		}else{
			Add(aggr[i]);
		}
	}
	Sorted_inst=false;
	Sorted_id=false;
	return k-1;
}

template<class TYPE, class REF_TYPE, int MODE>
const CaplTAggr<TYPE, REF_TYPE, MODE>&  CaplTAggr<TYPE, REF_TYPE, MODE>::operator =(const CaplTAggr& src)
{
	if(this==&src)return *this;
	Clear();
	Append(src);

	Unique=src.Unique;            
	ListMode=src.ListMode;          
	AutoKillReference=src.AutoKillReference; 
	Sorted_id=src.Sorted_id;				
	Sorted_inst=src.Sorted_inst;			

	return *this;
}

//********************************************************
template<class TYPE, class REF_TYPE, int MODE>
bool CaplTAggr<TYPE, REF_TYPE, MODE>::Insert(int index, TYPE  data)
{
	int i;
	if(index<0) return false;
	if(index>MaxSize-1) SetSize(index+1);
	else if(Size+1>MaxSize) { if (!SetSize((Size+1)*2)) return false; }
	if(Unique)
	{
		for(i=0;i<Size;i++)
		{
			if(i!=index)
				if(Data[i]==data) return false;
		}
	}

	if(index>=Size)
	{
		for(i=Size;i<index+1;i++) Data[i]=0;
		Size=index+1;
	}
	else
	{
		for(i=Size-1;i>=index;i--)
			Data[i+1]=Data[i];
		Size++;
	}
	Sorted_inst=false;
	Sorted_id=false;
	Data[index]=data;
	
	return true;
}
//********************************************************
template<class TYPE, class REF_TYPE, int MODE>
bool CaplTAggr<TYPE, REF_TYPE, MODE>::SetAt(int index,TYPE data)
{
	int i;
	if(Unique)
	{
		for(i=0;i<Size;i++)
		{
			if(i!=index)
				if(Data[i]==data) return false;
		}
	}
	if(index<0) return false;
	if(index>MaxSize-1) SetSize(index+1);
	else if(Size+1>MaxSize) { if (!SetSize((Size+1)*2)) return false; }

	if(index>=Size)
	{
		for(i=Size;i<index+1;i++) Data[i]=0;
		Size=index+1;
	}
	
	if(AutoKillReference) {
		if(Data[index]!=0) delete (REF_TYPE)Data[index];
	}
	Data[index]=data;
	Sorted_inst=false;
	Sorted_id=false;
	return true;
}
//********************************************************
template<class TYPE, class REF_TYPE, int MODE>
bool CaplTAggr<TYPE, REF_TYPE, MODE>::Remove(int index)
{
	if(ListMode==false) return false;
	if(index<0)return false;
	if(index>=Size)return false;
	
	if(AutoKillReference) 
		if(Data[index]!=0) delete (REF_TYPE)Data[index];

	Size--;
	for(int i=index;i<Size;i++)
		Data[i]=Data[i+1];
	return true;
}
//********************************************************
template<class TYPE, class REF_TYPE, int MODE>
int CaplTAggr<TYPE, REF_TYPE, MODE>::Find(TYPE data) const
{
	for (int i = 0; i < Size; i++)
		if (Data[i] == data) return i;

	return -1;
}

template<class TYPE, class REF_TYPE, int MODE>
int __cdecl CaplTAggr<TYPE, REF_TYPE, MODE>::CompareElements(const void *p1, const void *p2)
{
	if( (*((TYPE *)p1))>(*((TYPE *)p2)) ) return  1;
	if( (*((TYPE *)p1))<(*((TYPE *)p2)) ) return -1;
	return 0;
}



template<class TYPE, class REF_TYPE, int MODE>
void CaplTAggr<TYPE, REF_TYPE, MODE>::Sort()
{
	if(Data==0) return;
	if(Size<2) return;

	qsort(Data,Size,sizeof(TYPE),CompareElements);

	/*int i,j,k;
	TYPE n;
	for(i=1;i<Size;i++)
	{
		if(Data[i]<Data[i-1])
		{
			for(j=i-1;j>0;j--)if(Data[j-1]<Data[i]) break;
			n=Data[i];
			for(k=i;k>j;k--)Data[k]=Data[k-1];
			Data[j]=n;
		}
	}*/
	Sorted_inst=true;
	Sorted_id=false;
}
template<class TYPE, class REF_TYPE, int MODE>
void CaplTAggr<TYPE, REF_TYPE, MODE>::SortWithFunct(int (__cdecl *comp)(const void *, const void *))
{
	if(Data==0) return;
	if(Size<2) return;
	qsort(Data,Size,sizeof(TYPE),comp);
	Sorted_id=true;
}

template<class TYPE, class REF_TYPE, int MODE>
void CaplTAggr<TYPE, REF_TYPE, MODE>::Normalize()
{
	if(Data==0) return;
	if(Size<2) return;
	qsort(Data,Size,sizeof(TYPE),CompareElements);
	Sorted_inst=true;
	Sorted_id=false;

	int i;
	for(i=1;i<Size;i++)
	{
		if(Data[i]==Data[i-1])
		{
			Remove(i);
			i--;
		}
	}
}

template<class TYPE, class REF_TYPE, int MODE>
TYPE* CaplTAggr<TYPE, REF_TYPE, MODE>::GetData()
{
	return (TYPE*)Data;
}

template<class TYPE, class REF_TYPE, int MODE>
const TYPE* CaplTAggr<TYPE, REF_TYPE, MODE>::GetData() const
{
	return (const TYPE*)Data;
}

//***********************************************************
//***********************************************************
//***********************************************************
//*** CaplMap  ***
//***********************************************************
//***********************************************************
struct aplMap_element // Элемент карты четырехбайтовых значений
{
	INT_PTR in;  ///<Входной ключ
	INT_PTR out; ///<Выходной ключ
};

/** Карта четырехбайтовых значений*/
class APL_AGGR_API CaplMap
{
private:
	CaplMap(const CaplMap& ){}
	CaplMap& operator=(CaplMap& ){return *this;}
public:
	CaplMap();					/**< Конструктор*/
	CaplMap(int max_size);		/**< Конструктор резервирующий память под max_size элементов*/

	virtual ~CaplMap();

	// Attributes
public:
	aplMap_element *Data;		/**< Данные*/
	int Size;					/**< Текущий размер*/
	bool bAutoSort;				/**< Если true - функция Add автоматически сортирует, иначе добавляет в конец*/
//	bool UniqueIn;				/**< Уникальность входных значений (используется при Add)*/
	bool NotFullFoudInQuick;	/**< Запрет перебора при быстром поиске*/

protected:
	int MaxSize;
	bool m_sorted;
		
	// Metods
public:
	
	int Add(INT_PTR in, INT_PTR out);			/**< Добавление комбинации. Возвращает индекс добавленного элемента*/
		
	bool Remove(int index);				/**< Удаление комбинации по индексу*/
	
	aplMap_element operator [](int index){return Data[index];}/**< Возврат комбинации по индексу*/
	
	void Clear();							/**< Очистка */
	void RemoveAll();						/**< Очистка c освобождением памяти */

	int GetSize() const { return Size;}	/**< Текущее кол-во элементов*/
	bool SetSize(int max_size);				/**< Резервирование памяти для max_size элементов*/
	
	inline int FindByIn(INT_PTR data){return QFindByIn(data);}	/**< Поиск индекса  по входному значению*/
	inline int FindByIn(void *data){return QFindByIn((INT_PTR)data);}
	int FindByOut(INT_PTR data) const;			/**< Поиск индекса перебором по выходному значению */
	
	INT_PTR GetByIn(INT_PTR data);				/**< Поиск выходного значения (числа) по входному перебором*/
	void *GetByInP(INT_PTR data);				/**< Поиск выходного значения (указателя) по входному перебором*/
	INT_PTR GetByOut(INT_PTR data) const;			/**< Поиск  входного значения по выходному перебором*/
	 
	void SortIn();						    	/**< Сортировка входных значений*/
	int QFindByIn(INT_PTR data);				/**< Быстрый поиск индекса по входному значению*/
	INT_PTR QGetByIn(INT_PTR data);				/**< Быстрый поиск выходного значения по входному */
	void *QGetPointerByIn(INT_PTR data);		/**< Быстрый поиск выходного значения по входному */
	inline void *QGetByInP(INT_PTR data){return QGetPointerByIn(data);}

	// обертки для более простого использования указателей
	inline int Add(void *in, void *out) {return Add((INT_PTR)in, (INT_PTR)out);}
	inline int Add(INT_PTR in, void *out) {return Add(in, (INT_PTR)out);}
	inline int Add(void *in, INT_PTR out) {return Add((INT_PTR)in, out);}

	inline INT_PTR GetByIn(void *data){return GetByIn((INT_PTR)data);}
	inline void *GetByInP(void *data){return GetByInP((INT_PTR)data);}

	inline int QFindByIn(void *data){return QFindByIn((INT_PTR)data);}
	inline INT_PTR QGetByIn(void *data){return QGetByIn((INT_PTR)data);}
	inline void *QGetPointerByIn(void *data){return QGetPointerByIn((INT_PTR)data);}
	inline void *QGetByInP(void *data){return QGetPointerByIn((INT_PTR)data);}
};

//***********************************************************
//***********************************************************
//*** Карта строка - число **********************************
//***********************************************************
//***********************************************************
class APL_AGGR_API CaplStrMap
{
public:

	class CaplStrMapItem
	{
	public:
		CaplStrMapItem() {val=0;};
		CaplStrMapItem(LPCTSTR str_init, INT_PTR val_init) {str=str_init; val=val_init;};

		CString str;
		INT_PTR val;
	};
	
	CaplStrMap(bool bCompareCase = true) 
	{
		bNotUseApiInFind = false; m_bCompareCase = bCompareCase; m_bCompareAsInt = false;
	};

	int  Add(LPCTSTR str, INT_PTR val);	/**< Добавление комбинации. Возвращает индекс добавленного элемента*/
	inline int  Add(LPCTSTR str, void* val){return Add(str,(INT_PTR)val);};	/**< Добавление комбинации. Возвращает индекс добавленного элемента*/
	void Clear();					/**< Очистка */
	
	void SetSize(int nSize);

	int GetSize() const { return items.GetSize();} /**< Размер */
	CaplStrMapItem* GetAt(int index) const;		   /**< Чтение элемента по индексу*/
	bool SetAt(int index, INT_PTR val);/**< Изменение значения по индексу*/

    int  Find(LPCTSTR str) const;	/**< Поиск индекса по строке*/
	int  Find(INT_PTR val) const;	/**< Поиск индекса по значению (долгий поиск перебором!)*/
	bool Remove(int index);			/**< Удаление элемента по индексу*/
	
    long Get(LPCTSTR str) const;			/**< Чтение значения по строке*/
    void *GetP(LPCTSTR str) const;			/**< Чтение элемента по строке*/

	void Sort();					/**< Сортировка входных значений*/

	bool bNotUseApiInFind;			/**< Не использовать функцию bsearch при поиске*/

	bool m_bCompareCase;
	bool m_bCompareAsInt;			/**< Сравнивать как числа*/
	
protected:
	CaplTAggr <CaplStrMapItem*,CaplStrMapItem*,APLAGGR_LIST|APLAGGR_AUTOKILLREF> items;

	static int compare_CaplStrMapItem( const void *arg1, const void *arg2 );
	static int compare_CaplStrMapItem_NoCase( const void *arg1, const void *arg2 );
	static int compare_CaplStrMapItem_AsInt(const void *arg1, const void *arg2);
};

//****************************************************************************************
//****************************************************************************************
//*** Карта строка - строка ***
//****************************************************************************************
//****************************************************************************************
class APL_AGGR_API CaplStrStrMap
{
public:

	class CaplStrStrMapItem
	{
	public:
		CaplStrStrMapItem() {}
		CaplStrStrMapItem(LPCTSTR str_init, LPCTSTR val_init) {str=str_init; val=val_init;};

		CString str;
		CString val;
	};
	
	CaplStrStrMap(){bNotUseApiInFind=false;}

	int  Add(LPCTSTR str, LPCTSTR val);	/**< Добавление комбинации. Возвращает индекс добавленного элемента*/
	void Clear();						/**< Очистка */
	
	int GetSize() const { return items.GetSize(); }	/**< Размер */
	CaplStrStrMapItem* GetAt(int index) const;			/**< Чтение элемента по индексу*/
	bool SetAt(int index, LPCTSTR val);					/**< Изменение значения по индексу*/

	int  FindByIn(LPCTSTR str) const;	/**< Поиск индекса по строке*/
	int  FindByOut(LPCTSTR val) const;	/**< Поиск индекса по значению (долгий поиск перебором!)*/
	bool Remove(int index);				/**< Удаление элемента по индексу*/
	
	CString Get(LPCTSTR str, bool bNocase=false) const;		/**< Чтение значения по строке*/

	void Sort();					/**< Сортировка входных значений*/

	bool bNotUseApiInFind;			/**< Не использовать функцию bsearch при поиске*/
	
protected:
	CaplTAggr <CaplStrStrMapItem*,CaplStrStrMapItem*,APLAGGR_LIST|APLAGGR_AUTOKILLREF> items;

	static int compare_CaplStrStrMapItem( const void *arg1, const void *arg2 );
	static int compareNoCase_CaplStrStrMapItem( const void *arg1, const void *arg2 );
};

//****************************************************************************************
//****************************************************************************************
//***  Классы и функции перевода строк ***
//****************************************************************************************
//****************************************************************************************
// В сборке под MFC: *) испольуется числовое обозначение языков, принятое в операционных системах Microsoft
//		*) значение текущего языка хранится в реестре
// В сборке под Qt: *) используется обозначение языков в виде строк с нижними подчеркиваниями "ru_Ru", "en_US" и т.п., принятое в Linux
//		*) значение текущего языка хранится в ini в параметре [Options]DefLang
// Имена файлов с переводами должны быть с тире (translate#en-US.xml, translate#ru-RU.xml и т.п)

#define APL_ALP_LANG_CODE_RUSSIAN 0x419
#define APL_ALP_LANG_CODE_ENGLISH 0x409

struct APL_AGGR_API SAPLanguage
{
	int id;					// 16-битный код локали
	CString name;			// буквенное обозначение (например, ru-RU)
	CString description;	// описание языка (Russian (Russia))
	CString trans;			// идентификатор языка для автоперевода

	SAPLanguage(int new_id, const CString& new_name, const CString& new_descr, const CString& new_trans) : id(new_id), name(new_name), description(new_descr), trans(new_trans) {}
};

typedef std::set<int> TIntSet;
typedef std::set<int>::iterator TIntSet_it;
typedef std::set<std::wstring> TWStrSet;

#define APL_TRANSLATE_ENABLED
#define APL_LANG_COUNT 207

// все поддерживаемые языки
extern APL_AGGR_API SAPLanguage apl_langs[];

class APL_AGGR_API CaplTranslate
{
public:
	CaplTranslate();
	virtual ~CaplTranslate();
    static LPCTSTR Translate(LPCTSTR str); // Преобразует русскую строку в строку текущего языка
	static TCHAR* ConvertSpecSymbols(TCHAR *buf); //преобразует внутри исз. буффера из записи строки C++ в обычную строку. Возвращает входящий буффер.
#ifndef _MFC_VER
	static bool SetIniPath(LPCTSTR iniPath);
	static LPCWSTR GetLangTitul(LPCWSTR name);
	static LPCWSTR GetLangShort(LPCWSTR name);
#endif

	static bool Init(bool hide_mode=false, CString *errMess=0); // Загружает словарь для текущей локали (GetSystemLocale)

#ifdef _MFC_VER
	static unsigned long GetGurLocale(); // Возвращает текущий язык для которого зачитан xml с переводомя (глобальная переменная CaplTranslate_m_cur_lang)
	static unsigned long GetSystemLocale(); // Возвращает текущий язык в Thread (вызывает GetThreadLocale())
#else
	static LPCTSTR GetGurLocale(); // Возвращает текущий язык для которого зачитан xml с переводом (глобальная переменная CaplTranslate_m_cur_lang)
	static LPCTSTR GetSystemLocale(); // Возвращает текущий язык в Thread (вызывает GetThreadLocale())
#endif

	static bool IsNeedTranslate() { return m_bTranslate; } // возвращает true, если что-то надо переводить (по сути, текущий язык русский или нет)

#ifdef _MFC_VER
	/// Ищет в папке pszPath файлы перевода и возвращает в lang_nums  список кодов доступных языков
	/// Возвращает кол-во элементов в lang_nums
	/// Используется в aplFillLanguageMenu()
	static int GetTranslateFiles(LPCTSTR pszPath, TIntSet& lang_nums);
#else
	/// Ищет в папке pszPath файлы перевода и возвращает в langs  список строк с именами доступных языков
	/// Используется в aplFillLanguageMenu()
	static int GetTranslateFiles(LPCTSTR pszPath, TWStrSet &langs);
#endif

	// Устанавливает необходимость перевода (нужна, если в процессе понадобится отключить перевод оставив нерусскую локаль)
	static void SetShouldTranslate(bool bTranslate = true) { m_bTranslate = bTranslate; };

	static void AddStringTranslationPair(const CString& sStr, const CString& sTranslation);

#ifdef _MFC_VER
	// получить глобальное значение выбранного языка
	static unsigned long GetGlobalUILang();

	// задать глобальное значение выбранного языка
	static void SetGlobalUILang(unsigned long ulLang, bool bUpdateLocalUILang = true);
#else
	// Читает текущий язык из файла ini из параметра [Options]DefLang
	// Файл ini может быть задан функцией SetIniPath; если файл не был задан - используется стандартный AplTransport.ini
	// Возвращается строчка в формате Qt с подчеркиванием (ru_RU, en_US)
	static LPCTSTR GetGlobalUILang();

	// Задать глобальное значение выбранного языка
	// Cтрочка может быть как с подчеркиванием (ru_RU, en_US), так и с тире. В результате она приводится
	//	к формату с подчеркиванием
	// Значение сохраняется в файле ini в параметре [Options]DefLang
	// Файл ini может быть задан функцией SetIniPath; если файл не был задан - используется стандратный AplTransport.ini
	static void SetGlobalUILang(LPCTSTR uiLang, bool bUpdateLocalUILang = true);
#endif


	// 2-е поколение функций (котороые должны быть общие для всех модулей

	// Устанавливает текущий язык согласно настройкам в реестре (MFC) или в файле ini (Qt). Если настройки
	//		не заданы, берет текущий язык потока (MFC) или системной локали QLocale::system (Qt), и для этого
	//		языка зачитывает xml с переводами (функция Init)
	// Должна запускаться в каждом модуле при запуске. По идее, только она и должна запускаться
	static bool SetCurLocaleFromReg(bool hide_mode=false, CString *errMess = 0);

#ifdef _MFC_VER
	// Читает код языка из общих настроек в реестре.  Если его нет, то из настроек модуля PDM.
	// Если и там нет, то из текущих настроек приложения
	// Если и там нет,, то возвращает 0
	// (ЯАИ: функция взамен SetGlobalUILang чтобы не менять существующее)
	static DWORD ReadUILangFromReg();

	// Записывает в реестр значение выбранного языка. (ЯАИ: сделал ещё одну чтобы не менять существующее)
	static void SaveUILangToReg(DWORD ulLang);
#else
	/* !!! Qt должен работать в разных ОС, и весьма вероятно портабельное использование, поэтому текущий
			язык хранится в ini файле. Для чтения/установки языка надо использовать GetGlobalUILang()/SetGlobalUILang()
			в паре с CaplTranslate::SetIniPath
	// Читает код языка из общих настроек в реестре.  Если его нет, то из настроек модуля PDM.
	static LPCTSTR ReadUILangFromReg(){return GetGlobalUILang();}
	// Записывает в реестр значение выбранного языка. (ЯАИ: сделал ещё одну чтобы не менять существующее)
	static void SaveUILangToReg(LPCTSTR uiLang){SetGlobalUILang(uiLang);}
	*/
#endif

	static void SaveShowStringId(bool bShowStringId); // Сохраняет в реестр галочку показывать ID.  Текущее занчение не меняется
	static bool GetShowStringId(){return m_bShowStringId;}; // Возвращает текущее значение галочки

	static bool m_Suppress; // если true, то механизма перевода будет отключен

protected:
	static bool m_bTranslate;	// флаг нужно ли вообще переводить
	static bool m_bShowStringId; // Показывать Id строки

    static bool LoadXml(LPCTSTR filename, CStringArray &strarray,bool hide_mode);
};

	inline LPCTSTR aplTranslate(LPCTSTR str){return CaplTranslate::Translate(str);};
#ifndef _MFC_VER
#ifdef _UNICODE
	// Функция для перевода QString
	CStringA aplTranslate(LPCSTR stringA);
#endif
#endif

//*********************************************************************************
//*********************************************************************************
/** Класс для чтения файлов csv */
//*********************************************************************************
//****************************************************************************************

class APL_AGGR_API CaplCSV
{
public:
	CaplCSV(char razd=_T(','));					/**< Конструктор указывающий используемый разделитель полей*/
	
	bool OpenFile(LPCTSTR file_name);	/**< Открыть файл с именем file_name*/
	void CloseFile(void);					/**< Закрыть файл*/
	void SeekToBegin(void);					/**< Перейти в начало файла */
	bool ReadString(CStringArray &strings); /**< Прочитать строку в strings*/

	bool ReadFile(LPCTSTR filePath, std::vector<std::vector<CString>> &fileData);
    static bool StrToArray(LPCTSTR str, CStringArray &strings, char razd=_T(','), bool bUseBreakStr = false); /**< Преобразует строку в CStringArray*/

	char m_razd;							/**< Разделитель полей*/
	int m_cur_line;
	bool	m_bUseBreakStr;
protected:
	CaplFile m_file;
};
//****************************************************************************************
//****************************************************************************************
//****************************************************************************************

// если строка равна нулю или пустой строке - возвращает true
inline bool APL_AGGR_API aplIsStringEmpty(LPCTSTR mess){ if(0==mess) return true; if(_T('\0')==mess[0]) return true; return false;}

// Заменяет в строке символы так, чтобы строку можно было использовать в качестве имени файла
void APL_AGGR_API aplCorrectFileName(CString& sFilename, TCHAR cReplaceWith=_T('_'));

// Проверяет, можно ли использовать строку в имени Lite БД или в имени подключения
bool APL_AGGR_API IsNameCorrect(CString &tested, CString &err_mess, bool is_db_name);


//****************************************************************************************
// Класс и массив структур, которые в полуавтоматическмо режиме хранят журнал стека вызовов
// В журнал попадает информация для тех функций, в которых определен объект CaplStackLogger
// при выходе из функции - объект автоматом удаляется и информация в журнале обновляется
// В процессе выполнения функции можно задавать комментарии, которые будут записываться в журнал с привязкой к текущей функции.
// Можно определить фунцию для вывода сообщений об ошибках
// Если глубина стека достигнет 999 - генерится наше исключение с объектом SaplErrorDescription
//****************************************************************************************

// Создает объект со структурами для ведения журнала стека вызовов и задает функции для выводасообщений в лог и и в аварийный лог
//		LogFunct - функция вывода сообщений в лог приложения. С помощью этой функции журнал стека вызовов
// передает вызывающему модулю информацию об возникших ошибках
//		_StackEmergencyLogFunct - функция вывода сообщений в аварийный лог приложения. Если она задана, то при выполнении
// любых операций (создание объектов журнала стека, назначение имен точек) в нее выводится вся информация о выполняемых
// операциях. Т.о. задание этой функции с выводом на диск может отрицательно сказаться на быстродействии приложения!
// В сервере приложений эта функция выводит информацию в циклический аварийный лог в оперативке; эта область памяти так же доступна
// главному серверу. При крахе рабочего сервера - главный сохраняет аварийный лог упавшего сервера на диск для разбора полетов.
bool APL_AGGR_API GlobalStackLogInit(void (*LogFunct)(LPCTSTR mess, LPCTSTR funct, LPCTSTR class_info),
									 void (*_StackEmergencyLogFunct)(LPCTSTR mess));
// Уничтожает объект со структурами для ведения журнала стека вызовов
void APL_AGGR_API GlobalStackLogDestroy();

// Включает и выключает режим ведения журнала стека.
// Возвращает предыдущее значение флага
bool APL_AGGR_API SetStackLoggerMode(bool mode);

//***********************************************************************************
//***********************************************************************************
// В экземплярах этой структуры  хранится информация о текущем стеке вызовов (количество уровней+названия функций+комментарии к ним, если они заданы) одной нити
// Названия функций и комментарии хоранятся в строковой карте CaplStrStrMap
//***********************************************************************************
//***********************************************************************************
struct APL_AGGR_API StackLoggerThreadEl
{
	StackLoggerThreadEl();
	~StackLoggerThreadEl();
	// занести в журнал информацию о входе в функцию. На вход - строчка с именем функции. Строчку меняет и возвращает новое имя, включая идентификтаор нити и уровень стека в этой нити
	void Enter(CString &funct_name,bool force_write);
	// убрать из журнала информацию о нити
	void Leave(CString &funct_name);
	// добавить в журнал комментарий к функции
	void SetMess(CString &funct_name, LPCTSTR message);
	// вывести строчку с текущим стеком нити
	CString GetCurrStackList();

	DWORD m_thread_id;
#ifndef __linux__
	HANDLE m_thread_h;
#endif

	DWORD m_level;
	CaplStrStrMap m_stack_log;
	CString m_PrevFunctName;
	int m_count_duplicate;
	DWORD m_level_dup;
};

//***********************************************************************************
// Класс, который при создании добавляет информацию в журнал стека функций, а 
// при разрушении - автоматом убирает информацию из журнала
// Дополнительно можно задать строчку, описывающую параметры/контекст текущей функции
//***********************************************************************************
class CGlobalStackLog;
class APL_AGGR_API CaplStackLogger
{
private:
	CaplStackLogger(const CaplStackLogger& ){}
	CaplStackLogger& operator=(CaplStackLogger& ){return *this;}
public:
	CaplStackLogger(LPCTSTR function_name, bool force_write=false);
	~CaplStackLogger();

	void Leave();

	// задает комментарий к функции в журнале стека
	void SetFunctionInfo(LPCTSTR function_info);

	void static ExternalEnter(CString &function_name,bool force_write=false);
	void static ExternalLeave(CString &function_name);
	void static ExternalSetFunctionInfo(CString &function_name, LPCTSTR function_info);

	CString GetCurrStackList();

	// строчка - идентификатор этой функции в журнале (включая нить, уровень стека в этой нити и имя функции)
	CString m_id;
	// идентификатор нити, из которой была вызвана функция

	DWORD m_thread_id;
protected:
	// если при инициализации класса журнал был выключен (SetStackLoggerMode(false)), то
	// слудющие две переменный будут равны нулю
	// указатель на журнал стека текущей нити
	StackLoggerThreadEl* m_cur_el;
	// указатель на глобальный журнал стека - чтобы при отладке было легко до него достучаться из любого места
	CGlobalStackLog* m_p_global_stack;
};




//***************************************************
//***************************************************
//*** excel 
//***************************************************
//***************************************************

#ifdef _MFC_VER
	int APL_AGGR_API InitializeExcell(IDispatch *&pXlApp, bool bShowMessge = true); 
	bool APL_AGGR_API aplCheckExcel(bool bShowMessge = true);
	APL_AGGR_API char* aplGetStringFromMsg( DWORD dwMessage, bool = true );
#endif


//***************************************************
//***************************************************
//*** перевод строк
//***************************************************
//***************************************************

class APL_AGGR_API CaplStringAdapter
{
private:
	CaplStringAdapter(const CaplStringAdapter& ){}
	CaplStringAdapter& operator=(CaplStringAdapter& ){return *this;}
protected:
	char* m_buffer_ansi;
	wchar_t* m_buffer_utf;

	size_t m_size_ANSI;			// размер памяти под буфер ANSI (это обычно кол-во симовлов + 1 завершающий символ '\0')
	size_t m_size_UTF;			// размер памяти под буфер UTF (это обычно кол-во симовлов + 1 завершающий символ '\0')

	// выделить память под буфер
	size_t SetSizeANSI(size_t new_size);
	size_t SetSizeUTF(size_t new_size);

public:

	CaplStringAdapter();
	CaplStringAdapter(const char* c_str);
	CaplStringAdapter(const wchar_t* w_str);

	~CaplStringAdapter();

	// оператор преобразования в ANSI-строку
	operator LPCSTR() throw();
	operator LPSTR() throw();

	// оператор преобразования в UNICODE-строку
	operator LPCWSTR() throw();
	operator LPWSTR() throw();

	CaplStringAdapter& operator=(const char* c_str);
	CaplStringAdapter& operator=(const wchar_t* w_str);

	bool m_bIsAnsiUtf8; // переключалка для чтения строк из windows
	bool m_bIsError; // устанавливается в true при ошибке перекодировки
	CString m_sError; // переменная для сообщения об ошибке

	static UINT m_st_encoding; // Кодировка ANSI строк
};

//***************************************************
//***************************************************
//*** работа с датами
//***************************************************
//***************************************************


class APL_AGGR_API CaplDateTimeSpan
{
	// Constructors
public:
	CaplDateTimeSpan();

	CaplDateTimeSpan(double dblSpanSrc);
	CaplDateTimeSpan(LONG lDays, int nHours, int nMins, int nSecs);

	// Attributes
	enum DateTimeSpanStatus
	{
		valid = 0,
		invalid = 1,    // Invalid span (out of range, etc.)
		null = 2,       // Literally has no value
	};

	double m_span;
	DateTimeSpanStatus m_status;

	void SetStatus(DateTimeSpanStatus status);
	DateTimeSpanStatus GetStatus() const;

	double GetTotalDays() const;    // span in days (about -3.65e6 to 3.65e6)
	double GetTotalHours() const;   // span in hours (about -8.77e7 to 8.77e6)
	double GetTotalMinutes() const; // span in minutes (about -5.26e9 to 5.26e9)
	double GetTotalSeconds() const; // span in seconds (about -3.16e11 to 3.16e11)

	LONG GetDays() const;       // component days in span
	LONG GetHours() const;      // component hours in span (-23 to 23)
	LONG GetMinutes() const;    // component minutes in span (-59 to 59)
	LONG GetSeconds() const;    // component seconds in span (-59 to 59)

	// Operations
	CaplDateTimeSpan& operator=(double dblSpanSrc);

	bool operator==(const CaplDateTimeSpan& dateSpan) const;
	bool operator!=(const CaplDateTimeSpan& dateSpan) const;
	bool operator<(const CaplDateTimeSpan& dateSpan) const;
	bool operator>(const CaplDateTimeSpan& dateSpan) const;
	bool operator<=(const CaplDateTimeSpan& dateSpan) const;
	bool operator>=(const CaplDateTimeSpan& dateSpan) const;

	// DateTimeSpan math
	CaplDateTimeSpan operator+(const CaplDateTimeSpan& dateSpan) const;
	CaplDateTimeSpan operator-(const CaplDateTimeSpan& dateSpan) const;
	CaplDateTimeSpan& operator+=(const CaplDateTimeSpan dateSpan);
	CaplDateTimeSpan& operator-=(const CaplDateTimeSpan dateSpan);
	CaplDateTimeSpan operator-() const;

	operator double() const;

	void SetDateTimeSpan(LONG lDays, int nHours, int nMins, int nSecs);

	// formatting
	CString Format(LPCTSTR pFormat) const;
	//CString Format(UINT nID) const;

	// Implementation
	void CheckRange();

private:
	static const double OLE_DATETIME_HALFSECOND;
};


class APL_AGGR_API CaplDateTime
{
	// Constructors
public:
	static CaplDateTime GetCurrentTime();

	CaplDateTime();
	~CaplDateTime();
	//	CaplDateTime(const VARIANT& varSrc);
	CaplDateTime(DATE dtSrc);
	CaplDateTime(const time_t timeSrc);
	// 	CaplDateTime(__time32_t timeSrc);
	// 	CaplDateTime(__time64_t timeSrc);
	CaplDateTime(const SYSTEMTIME& systimeSrc);
	// 	CaplDateTime(const FILETIME& filetimeSrc);
	CaplDateTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec);
	//	CaplDateTime(WORD wDosDate, WORD wDosTime);

	// Attributes
	enum DateTimeStatus
	{
		error = -1,
		valid = 0,
		invalid = 1,    // Invalid date (out of range, etc.)
		null = 2,       // Literally has no value
	};

	DATE m_dt;
	DateTimeStatus m_status;


	void SetStatus(DateTimeStatus status);
	CaplDateTime::DateTimeStatus GetStatus() const;

	bool GetAsSystemTime(SYSTEMTIME& sysTime) const;
	bool GetAsUDATE( UDATE& udate ) const;

	int GetYear(); 
	// Month of year (1 = January)
	int GetMonth(); 
	// Day of month (1-31)
	int GetDay();
	// Hour in day (0-23)
	int GetHour();
	// Minute in hour (0-59)
	int GetMinute();
	// Second in minute (0-59)
	int GetSecond();
	// Day of week (1 = Sunday, 2 = Monday, ..., 7 = Saturday)
	int GetDayOfWeek();
	// Days since start of year (1 = January 1)
	int GetDayOfYear() const;

	// 	// Operations

	// 	CaplDateTime& operator=(const VARIANT& varSrc);
	CaplDateTime& operator=(DATE dtSrc);
	CaplDateTime& operator=(time_t timeSrc);
	 
	// 	CaplDateTime& operator=(const __time32_t& timeSrc);
	// 	CaplDateTime& operator=(const __time64_t& timeSrc);
	 
	CaplDateTime& operator=(const SYSTEMTIME& systimeSrc);
	// 	CaplDateTime& operator=(const FILETIME& filetimeSrc);
	CaplDateTime& operator=(const UDATE& udate);
	 
	bool operator==(const CaplDateTime& date) const;
	bool operator!=(const CaplDateTime& date) const;
	bool operator<(const CaplDateTime& date) const;
	bool operator>(const CaplDateTime& date) const;
	bool operator<=(const CaplDateTime& date) const;
	bool operator>=(const CaplDateTime& date) const;
	 
	// DateTime math
	CaplDateTime operator+(CaplDateTimeSpan dateSpan) const;
	CaplDateTime operator-(CaplDateTimeSpan dateSpan) const;
	CaplDateTime& operator+=(CaplDateTimeSpan dateSpan);
	CaplDateTime& operator-=(CaplDateTimeSpan dateSpan);
	 
	// DateTimeSpan math
	CaplDateTimeSpan operator-(const CaplDateTime& date) const;

	operator DATE() const;

	int SetDateTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec);
	int SetDate(int nYear, int nMonth, int nDay);
	int SetTime(int nHour, int nMin, int nSec);

	//bool ParseDateTime(LPCTSTR lpszDate, DWORD dwFlags = 0, LCID lcid = LANG_USER_DEFAULT);

	// formatting
	//CString Format(DWORD dwFlags = 0, LCID lcid = LANG_USER_DEFAULT) const;
	CString Format(LPCTSTR lpszFormat) const;
	//CString Format(UINT nFormatID) const;

	static bool CheckSystemTime(const SYSTEMTIME& systime); // Проверяет нахождение дней в разумном диапазоне

protected:

	SYSTEMTIME *m_pSysTime; // Используется как внутр. кэш для убыстрения работы GetYear() и прочих. Если не 0, то в нём актуальное значение.

	void Init_m_pSysTime(); // Конвертирует значение во внутр. кэш
	void Free_m_pSysTime(); // Удаляет внутр кэш

	static double DoubleFromDate( DATE date );
	static DATE DateFromDouble( double f );

	inline void CheckRange();	
	bool ConvertSystemTimeToVariantTime(const SYSTEMTIME& systimeSrc);
};


//***************************************************
//***************************************************
//*** Критические секции
//***************************************************
//***************************************************


#ifdef __linux__
	#include <pthread.h>
#else
	typedef void *HANDLE;
#endif


class APL_AGGR_API CAplCriticalSection
{
public:
	CAplCriticalSection();
	~CAplCriticalSection();
	void EnterAplCriticalSection();
	void LeaveAplCriticalSection();
protected:
	void Init();
#ifdef __linux__
	// В линуксе используем мутекс
	pthread_mutex_t m_mutex;
	// Используем мутекс, который не разрешает себя захватывать рекурсивно. Для определения
	// момента освобождения используем счетчик
	int m_recurs_level;
#else
	// В винде используем оригинальную критическую секцию
	CRITICAL_SECTION m_sect;
#endif
	bool m_inited;
};


class APL_AGGR_API CAplAtomicLong
{
public:
	CAplAtomicLong(long new_val = 0);
	~CAplAtomicLong();
	long Set(long new_val);
	long Get() const;
	long Increment();
	long Decrement();
	long Add(long add_val);
	long operator = (CAplAtomicLong &val){return Set(val.Get());}
	long operator = (const int &val){return Set(val);}
	long operator = (const long &val){return Set(val);}
	long operator = (const bool &val){return Set(val?1:0);}
	bool operator != (CAplAtomicLong &val) const {return Get() != val.Get();}
	bool operator != (const int &val) const {return Get() != val;}
	bool operator == (CAplAtomicLong &val) const {return Get() == val.Get();}
	bool operator == (const int &val) const {return Get() == val;}
	bool operator >  (const int &val) const {return Get()>val;}
	bool operator >= (const int &val) const {return Get()>=val;}
	bool operator <  (const int &val) const {return Get()<val;}
	bool operator <= (const int &val) const {return Get()<=val;}
	operator long() const { return Get();}
	operator bool() const { return Get()>0;}

protected:
#ifdef __linux__
	// В линуксе используем мутекс внутри критической секции
	mutable CAplCriticalSection m_cr_sect;
	// А в винде - оригинальные функции Interlocked...
#endif
	mutable long m_val;
};


class APL_AGGR_API CAplAtomicLongReseter
{
public:
	CAplAtomicLongReseter(CAplAtomicLong *p_val):m_val(p_val){if(m_val != 0)m_val->Set(1);}
	~CAplAtomicLongReseter(){if(m_val != 0)m_val->Set(0);}
protected:
	CAplAtomicLong *m_val;
};


class APL_AGGR_API CaplEnterCriticalSection
{
public:
#ifdef _MFC_VER
	CaplEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
	void Set(LPCRITICAL_SECTION lpAplCriticalSection);
#endif
	CaplEnterCriticalSection(CAplCriticalSection* lpAplCriticalSection);
	void Set(CAplCriticalSection* lpAplCriticalSection);

	~CaplEnterCriticalSection();

	void Leave(); // На случай прекращения блокировки до вызова деструктора

protected:
	int m_iCountEnter; // Кол-во блокировок, на случай многократного вызова Leave;
#ifdef _MFC_VER
	LPCRITICAL_SECTION m_lpCriticalSection;
#endif
	CAplCriticalSection* m_lpAplCriticalSection;

};


// Класс для быстрого измерения тайм-аутов, с защитой от переполнения DWORDа
class APL_AGGR_API CaplTimeMeter
{
protected:
	DWORD m_TickStart;
	DWORD m_TickOffset;
public:
	CaplTimeMeter();
	~CaplTimeMeter();
	// устанавливает счетчик
	DWORD Start();
	void Zero();
	// проверяет, прошло ли с момента старта TickDelta милисекунд. Если прошло - true, иначе - false
	bool CheckTimeOut(DWORD TickDelta);
	DWORD GetTickDelta();

	static DWORD GetHalfDWORD();

	CAplCriticalSection m_DataThreadProtect;

};



/////////////////////////////////////////////////////////////////////////////
// CaplINI 
/** Класс для работы с ini-файлами через функции файлового api
	При изменении опций может работать в режиме режим работы с файлом (по умолчанию) или с памятью (устанавливается функцией SetWriteMode)

	В режиме работы с памятью файл ini читается и парсится один раз при установке режима, после этого изменение опций ведется в оперативной памяти.
	Для сохранения изменений на диск необходимо вызвать функцию Flush, при этом так же устанавливается режим работы с файлом

	В режиме работы с файлом после каждого изменения опций данные сохраняются на диск

	При чтении опций чтение файла и его парсинг производится не чаще одного раза в секунду, более частые вызовы читают данные из структур 
в оперативной памяти
*/
class CaplTimeMeter;

struct SKeyVal{
	SKeyVal(LPCTSTR _key, LPCTSTR _val){key=_key; val=_val;};
	SKeyVal(SKeyVal *base){if(base==0){key=_T(""); val=_T("");}else{key=base->key; val=base->val;}};
	CString key; CString val;
};

class AFX_EXT_CLASS CaplINI  
{
protected:
	struct SSection;
	struct AFX_EXT_CLASS SOption 
	{
		SOption(LPCTSTR name=0);
		CString m_name;		// имя опции
		CString m_value;	// значение опции
		bool m_need_equal;	// нужен чтобы сохранить в памяти и нормально записать в новую инишку строки без имени опции 
	};
	
	struct AFX_EXT_CLASS SSection 
	{
		SSection(CaplTAggr<SSection*,SSection*,APLAGGR_AUTOKILLREF> &sections, LPCTSTR name=0);
		CString m_name;		// имя секции
		CString m_after;	// строчка, идущая после заголовка секции, например комментарий
		CString m_prev;		// строчка, которая будет выводиться перед секцией. Работает только один раз, после сохранения очищается; из файла не читается

		CaplTAggr<SOption*,SOption*, APLAGGR_LIST_OR_AUTOKILLREF> options;	// список опций
		int Add(SOption* option); // добавление опции в конец списка. Используется при парсинге файла
		int Insert(SOption* option, int pos_insert=-1);		// вставка опции в конец списка непустых опций секции или в позиции pos
		int m_last_option;		// первая пустая опция. Такие опции используются для разделения опций одной секции от заголовка следующей секции
		BYTE m_flags;	// есть ли в имени секкции правая/левая скобка
	};

public:
	CaplINI(LPCTSTR ini_name=0, bool find_current=true);
	virtual ~CaplINI();

	/** Устанавливает имя ini-файла с параметрами.
		@param ini_name - имя ini
		@param find_current - искать ли в текущем каталоге
		@param levels - если больше нуля, то ini ищется в каталогах выше. levels определяет количество уровней поиска.
		Если имя ini-файла указано без пути и find_current==true, то функция ищет указанный ini по очереди: сначала в каталоге с приложением,
	потом для windows в папке c:\windows; для линукс - в домашней папке пользователя.
		Если имя ini-файла указано без пути и find_current==false, то сразу ищет для windows в папке c:\windows; для 
	линукс - в домашней папке пользователя.
		Если имя ini-файла указано с абсолютным путем, то ищет только по этому пути
		@return true если файл ini найден и false если не найден*/
	virtual bool SetINI(LPCTSTR ini_name, bool find_current=true, int levels = 0);
	
	// Возвращает полный путь к рабочему ini
	void GetRealIniName(CString &name); // Устаревший вариант
	CString GetRealIniName();
	// Каталог с ini
	CString GetRealIniPath();

	bool isFileExist(){return m_file_exist;}
	
	// если параметр задан в ini возвращает true. Если не задан - false
	bool isParamSet(LPCTSTR lpAppName, LPCTSTR lpKeyName);
	// если группа задана в ini возвращает true. Если не задана - false
	bool isGroupeSet(LPCTSTR lpAppName);

	// Получение значений опций
	// Если опции lpKeyName в группе lpAppName нет, то возвращается nDefault и
	//  если isCreate=true то в ini создается группа и опция со значением nDefault
	virtual int GetInt(LPCTSTR lpAppName, LPCTSTR lpKeyName, int nDefault, bool isCreate=false);
	virtual bool GetString(LPCTSTR lpAppName, LPCTSTR lpKeyName, CString &value, LPCTSTR lpDefault, bool isCreate=false);
	virtual CString GetString(LPCTSTR lpAppName, LPCTSTR lpKeyName, LPCTSTR lpDefault, bool isCreate=false);
	
	// Установка значений опций. Если параметр в ini не задан и указан параметр comment, то
	// перед строчкой с параметром добавляется строка комментария из comment
	virtual bool WriteInt(LPCTSTR lpAppName, LPCTSTR lpKeyName, int value, LPCTSTR comment = 0);
	virtual bool WriteString(LPCTSTR lpAppName, LPCTSTR lpKeyName, LPCTSTR value, LPCTSTR comment = 0);

	// удаление опции
	virtual bool Delete(LPCTSTR lpAppName, LPCTSTR lpKeyName);

	// переименование группы опций
	virtual void RenameGroupe(LPCTSTR lpAppNameOld, LPCTSTR lpAppNameNew);
	// переименование опции
	virtual void RenameParam(LPCTSTR lpAppName, LPCTSTR lpKeyNameOld, LPCTSTR lpKeyNameNew);

	// получение списка имен опций указанной секции
	bool GetListKeys(LPCTSTR lpAppName, CStringArray &keys);
	// получение списка имен и опций указанной секции (включая опции с одинаковыми ключами)
	bool GetListKeysVals(LPCTSTR lpAppName, CaplTAggr<SKeyVal*, SKeyVal*, APLAGGR_AUTOKILLREF> &values);

	// Устанавливает режим сохранения опций. true - каждая установка опции вызывает сохранение в 
	//		файл (по умолчанию), false - изменения выполняются только в памяти, после изменений необходимо 
	//		принудительно сохранить данные функцией Flush() или SetWriteMode(true)
	// если write_to_file = false, а до этого был режим работы с файлом (true), то читает/парсит файл; дальнейшие операции идут с памятью
	// если write_to_file = true, а до этого был режим работы с памятью (false), то сохраняет данные из памяти в файл
	void SetWriteMode(bool write_to_file);
	
	CString GetErrDescription();

	// сбрасывает таймер, задающий 1 сек между перечитыванием данных из файла
	void SetNeedReload();

	// Проверяет доступность файла ini на запись.
	// возвращает true если файл доступен для записи; если запись недоступна - возвращает false и устанавливает переменную класса, блокирующую изменение опций
	bool CheckWrite(); 

	// Если был установлен режим работы с памятью (см SetWriteMode), то сохраняет  данные в файл и устанавливает режим работы с файлом 
	// Если был установлен режим работы с файлом, то не делает ничего
	bool Flush();

	// Преобразует короткий путь типа ./db/ и ../AAA/ в полный путь относительно расположения ini
	// Если в начале пути точка - то относительный каталог прибавляется к каталогу в котором находится ini
	// Если в начале пути две точки - то относительный каталог прибавляется к каталогу уровнем выше каталога с ini
	// В остальны случаях путь не меняется
	// Если каталог начинается с символа ~  то в консоль выводится предупреждение
	CString ExpandPath(LPCTSTR lpSourcePath);

	bool IsHomeDir()
	{
		return m_is_home;
	}
#ifdef __linux__
	// Имя подпаки в папке etc, которая будетт использована для поиска ini
	void SetEtcSubFolder(LPCTSTR _etc_folder){m_etc_folder = _etc_folder;}
#endif
	void SetEnvForFind(LPCTSTR _env){m_env_for_find = _env;}


protected:
#ifdef __linux__
	CString m_etc_folder;
#endif
	CString m_env_for_find;

	virtual bool Parse();
	virtual bool InternalSave();

	// Ищет опцию lpKeyName в секции lpAppName; сравнение идет без учета строчных/заглавных
	// Если is_create==true и
	//		опции или секции нет  - то создает секцию и/или опцию
	//		опция/секция есть - то проверяет наличие дублей и дубли переименовывает в [оригинальное имя]__copy
	// Так же в режиме is_create==true обновляет имя секций/опций с точки зрения строчных/заглавных
	// Если опции нет в ini нет и указан параметр comment, то перед строчкой с параметром добавляется строка комментария из comment
	SOption *FindCreateOption(LPCTSTR lpAppName, LPCTSTR lpKeyName, bool is_create = false, LPCTSTR comment = 0);

	// вывод предупреждений при чтении ini
	void PrintWarningWithSaveFlags(LPCTSTR message);
	// вывод сообщений перекодирования путей в консоль или в переменнную m_ErrDescription
	void PrintInfoWithSaveFlags(LPCTSTR message);
	// При true (значение по умолчанию) - выводит сообщения перекодирования в консоль. ПРи false - записывает в m_ErrDescription
	bool m_isPrintExpandMessages;

	CString m_IniName; // полное имя файла ini
	CString m_IniPath; // путь к файлу ini
	aplTextEncoding m_FileCoding; // кодировка оригинального ini
	CString m_ErrDescription; 
	bool m_write_to_file; // режим работы с файлом (true) или с памятью (false)
	bool m_readonly; // файл ini доступен только для чтения
	bool m_file_exist;
	// *) для линукса: находится ли файл настроек в /home/user/ или как его текущего пользователя
	// *) для винды: находится ли файл настроек в папке c:\Users\[user]\ или что там задано в переменной окружения USERPROFILE
	bool m_is_home;
	
	CaplTAggr<SSection*,SSection*,APLAGGR_AUTOKILLREF> m_sections;

	CaplTimeMeter m_LastParseTimer; // таймер, отсчитывающий 1 секунду с предыдущего чтения файла в режиме работы с файлом

	CAplCriticalSection m_DataProtect;

};

//******************************************************
/** Функции определяют, является ли строка или символ числом  */
bool AFX_EXT_API IsNumber(LPCTSTR _str, bool is_hex = false);
bool AFX_EXT_API IsNumber(CString &str, bool is_hex = false);
bool AFX_EXT_API IsNumber(TCHAR smb, bool is_hex = false);


//******************************************************
//******************************************************
// Вывод диагонстических сообщений
//******************************************************
//******************************************************

// Флаги позволяющие отключить некоторые типы сообщений
extern bool APL_AGGR_API g_bShow_aplMessageBox_Error;
extern bool APL_AGGR_API g_bShow_aplMessageBox_Warning;
extern bool APL_AGGR_API g_bShow_aplMessageBox_Information;
extern bool APL_AGGR_API g_bShow_aplMessageBox_WaitEnter;
extern bool APL_AGGR_API g_bShow_aplMessageBox_InRussian;

// Переключение функций aplMessage* в скрытый "CGI" режим
// После вызова этой функции все сообщения из aplMessage* буду накапливаться во
//	внутренних переменных и не будут выводиться в std::out.
bool AFX_EXT_API aplInitMessageWrappers();
// Возвращает нормальный режим вывода сообщений из aplMessage*
//	Сообщения из внутренних переменных будут выведены в std::out.
bool AFX_EXT_API aplClearMessageWrappers();
// Возвращает строчки, которые были сохранены во внутренних переменных функциями aplMessage*
//	В csError записываются сообщения об ошибках, в csMessage остальные сообщения
//	После вывода внутренние переменные очищаются.
bool AFX_EXT_API aplGetMessageWrappers(CString &csError, CString &csMessage);
// Возвращает true, если во внутренних переменных есть какие-то значения
bool AFX_EXT_API aplTestMessageWrappers(bool testWarn = true);

// Универсальная функция вывода диагностических сообщений в консоль с копированием в log, если надо
int AFX_EXT_API aplMessagePrint(LPCTSTR lpszText, UINT nType = MB_OK);

// Выводит AfxMessageBox для MSVC или aplMessagePrint для QtCreator
// Сделана для унификации с linux и обеспечения возможности отключения сообщений определенного типа (warning, error)
int AFX_EXT_API aplMessageBoxConsole(LPCTSTR lpszText, UINT nType = MB_OK);  

#ifdef _MFC_VER

#define aplMessageBox aplMessageBoxConsole  // В студии выводит стандартный afxMessageBox с некоторыми проверками

#else
// Под Qt два варианта:
// *) по умолчанию выводит текст в консоль и ждет нажатия enter
// *) если приложение GUI и оно определило диалог для MessageBox - вызывает переопределенную фунцию
int AFX_EXT_API aplMessageBox(LPCTSTR lpszText, UINT nType = MB_OK);

// Задает функцию вывода GUIевого MessageBox вместо консольного
void AFX_EXT_API SetMessageBoxInAggr(int (*MessageBoxGuiExt)(LPCTSTR lpszText, UINT nType));

// Возвращает true, если уже была определена функция вывода GUIевого MessageBox вместо консольного
bool AFX_EXT_API CheckMessageBoxInAggr();

// Возвращает критическую секцию, защищающую вывод aplMessageBox* в std::out
CAplCriticalSection* AFX_EXT_API aplGetProtectMess();
// Возвращает указатели на строчки для хранения сообщений в "CGI" режиме
CString* AFX_EXT_API aplGetInternalMessWrapper();
CString* AFX_EXT_API aplGetInternalErrWrapper();


#endif

//***************************************************
//***************************************************
//*** Всякие функции
//***************************************************
//***************************************************

// помешает в строку buf описание системной ошибки error_id. Если если full_mess==true то в строчку попадает код ошибки и ее описание; если false то только описание ошибки
void AFX_EXT_API aplGetDescriptionSystemError(int error_id,CString &buf, bool full_mess = true);

//Возвращает путь к папке TEMP с символом '\\' на конце
void AFX_EXT_API aplGetTempPath(CString &tmp_path);


//Возвращает доступное имя для временного файла в папке TEMP
/// prefix - начальная часть файла
//  ext расширение файла
// fname Найденное имя файла fname
bool AFX_EXT_API aplGetTempFileName(LPCTSTR prefix, LPCTSTR ext, CString &fname);


//Возвращает имя текущего выполняемого файла
bool AFX_EXT_API aplGetModuleFileName(CString &fname);

//Возвращает путь к папке из которой запущен текущий выполняемый файл
bool AFX_EXT_API aplGetModuleFilePath(CString &fpath);

// Возвращает оптимальный размер буфера для файловых операций (windows)
// iPercent - % памяти разрешенной  к использованию. (Всегда автоматически уменьшается до 40)
unsigned int AFX_EXT_API aplGetOptimalBufSize(unsigned int iPercent=15);

// Разбивает строчку на подстроки по символу sep
void AFX_EXT_API CStringSplit(CString &str, TCHAR sep, CStringArray &sa);
// Объединяет массив строк в строку с разделителем sep
CString AFX_EXT_API CStringJoin(CStringArray &sa, LPCTSTR sep);


#ifdef __linux__
	inline int _wtoi(LPCWSTR str){return atoi(LPCSTR(CaplStringAdapter(str)));}
	inline long _wtol(LPCWSTR str){return atol(LPCSTR(CaplStringAdapter(str)));}
	inline double _wtof(LPCWSTR str){return atof(LPCSTR(CaplStringAdapter(str)));}
#endif


#ifdef __GNUC__

	// Функция предназначена для встаивания в другие, поэтому все проверки на разумность вх данных должны быть снаружи
	// Сама подбирает нужный буфер и возвращает его в pDstBuf и DstBufSize
	// iDstDataSize - количество байт полученных при конвертации
	// iDstBufSize - размер выделенного буфера в байтах
	// Т.к. нужный размер буффера не всегда очевиден, то функция итерацилнно подбирает нужный
	// Если в pOutErrMess передается указатель на CString, то сообщения об ошибках выводся в него, а aplMessageBox не вызываются

	bool  bConvertTextWithIconv(aplTextEncoding SrcEncoding, BYTE *pSrcBuf,  int pSrcBufLen,
		aplTextEncoding DstEncoding, BYTE *&pDstBuf, int &iDstDataSize, int *iDstBufSize=0, CString *pOutErrMess=0);
#endif



#ifndef _MFC_VER

//Копия из MFC
class CUIntArray
{
public:

	// Construction
	CUIntArray();

	// Attributes
	INT_PTR GetSize() const;
	INT_PTR GetCount() const;
	BOOL IsEmpty() const;
	INT_PTR GetUpperBound() const;
	void SetSize(INT_PTR nNewSize, INT_PTR nGrowBy = -1);

	// Operations
	// Clean up
	void FreeExtra();
	void RemoveAll();

	// Accessing elements
	UINT GetAt(INT_PTR nIndex) const;
	void SetAt(INT_PTR nIndex, UINT newElement);

	//?? непонятно, что возвращать при ошибке  UINT& ElementAt(INT_PTR nIndex);

	// Direct Access to the element data (may return NULL)
	const UINT* GetData() const;
	UINT* GetData();

	// Potentially growing the array
	void SetAtGrow(INT_PTR nIndex, UINT newElement);

	INT_PTR Add(UINT newElement);

	INT_PTR Append(const CUIntArray& src);
	void Copy(const CUIntArray& src);

	// overloaded operator helpers
	UINT operator[](INT_PTR nIndex) const;
	//?? непонятно, что возвращать при ошибке  UINT& operator[](INT_PTR nIndex);

	// Operations that move elements around
	void InsertAt(INT_PTR nIndex, UINT newElement, INT_PTR nCount = 1);

	void RemoveAt(INT_PTR nIndex, INT_PTR nCount = 1);
	void InsertAt(INT_PTR nStartIndex, CUIntArray* pNewArray);

	// Implementation
protected:
	UINT* m_pData;   // the actual array of data
	INT_PTR m_nSize;     // # of elements (upperBound - 1)
	INT_PTR m_nMaxSize;  // max allocated
	INT_PTR m_nGrowBy;   // grow amount


public:
	~CUIntArray();
	/*//??    у меня в коде не используется
	#ifdef _DEBUG
	void Dump(CDumpContext&) const;
	void AssertValid() const;
	#endif
	*/

protected:
	// local typedefs for class templates
	typedef UINT BASE_TYPE;
	typedef UINT BASE_ARG_TYPE;
};

//****************************************************************************************
//****************************************************************************************
//*** CaplFileFind - Поиск файла(ов) по маске
//****************************************************************************************
//****************************************************************************************

class APL_AGGR_API CaplFileFind
{
public:
	// описание найденного файла
	typedef struct SaplFindFileInfo
	{
		CString name;    // имя файла
		CString pathname;    // имя файла c путем
		uint32_t dwFileAttributes; // атрибуты файла
	} SaplFindFileInfo;

protected:
	CString m_sPath;         // путь к каталогу поиска

	CaplTAggr<SaplFindFileInfo*, SaplFindFileInfo*, APLAGGR_LIST_OR_AUTOKILLREF> m_files;   // массив описаний всех найденных файлов

	int m_current_file;     // индекс текущего найденного файла

public:
	CaplFileFind();
	~CaplFileFind();

	// инициировать поиск
	virtual BOOL FindFile(LPCTSTR pstrName = NULL);

	virtual BOOL FindNextFile(){return FindNextFileEx();} // найти очередной файл (эта функция должна быть вызвана хотя бы один раз)
	virtual BOOL FindNextFileEx(); // найти очередной файл (эта функция должна быть вызвана хотя бы один раз)

	virtual CString GetFilePath() const; // получить полный путь текущего найденного файла

	virtual CString GetFileName() const; // Получить имя файла без пути

	bool IsDots(); // Является ли файл каталогом текущего или верхнего уровня
	bool IsDirectory(); // Является ли найденный файл подкаталогом
	int GetSize(){return m_files.GetSize();}

	virtual CString GetFileTitle( ) const{return GetFileTitleEx( );} // Получить имя файла без расширения и пути
	virtual CString GetFileTitleEx( ) const; // Получить имя файла без расширения и пути
};

// В разных ОС разный порядок вызова конструкторов глобальных объектов - экземпляров классов
// В windows сначала вызываются конструкторы из dll в порядке образования зависимостей
//		при сборке, и только потом конструкторы из exe
// В linux наоборот, сначала вызываются конструкторы из исполняемого файла, потом из dll,
//		причем в собираемых первыми dll конструкторы будут вызваны последними
// Но есть большое исключение: значение простых типов типа bool присваивается на этапе компиляции. Поэтому
//		можно использовать класс aplTestInit для определения, были ли вызваны конструкторы глобальных
//		переменных из текущего модуля
struct aplTestInit
{
	// varInited адрес глобальной переменной типа bool, которая при инициализации глобальных переменных-классов устанавливается в true
	// isUnInit надо ли запоминать адрес переменной, чтобы при деинициализации глобальных переменных сбросить ее в false
	aplTestInit(bool *pVarInited, bool isUnInit=false)
	{
		if(pVarInited!=0)*pVarInited=true;
		if(isUnInit)m_pVarInited=pVarInited;
	}
	~aplTestInit(){if(m_pVarInited!=0)*m_pVarInited=false;}
	bool* m_pVarInited{0};
};


#endif // #ifndef _MFC_VER
