// aplNetStepData.cpp : implementation file
//
///Ser  WIN32,_DEBUG,_WINDOWS,_MBCS,_AFXDLL
///Cli  WIN32,_DEBUG,_WINDOWS,_WINDLL,_AFXDLL,_MBCS,_AFXEXT
///Ser	/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_AFXDLL"                           /FR"Debug/" /Fp"Debug/AplNetTransportServ.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c 
///Cli  /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_AFXDLL" /D "_WINDLL" /D "_AFXEXT" /FR"Debug/" /Fp"Debug/AplTransportClient.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
///
#include "stdafx.h"
#include <io.h>

#include <aplBaseLib.h>
#include <ExternalTools.h>
#include <aplSocketTransport.h>
#include <sl_struct.h>
#include "aplNetStepData.h"
#include "TransportSocketClient.h"

#include "DlgLogin.h"
#include "DlgTuneListConnections.h"
#include "DlgSetInstAccess.h"
#include "DlgQlLang.h"
#include "DlgCopyBLOB.h"
#include "ErrMessCl.h" 
#include "InstProperties.h"
#include "Servises.h"
#include "DlgAttrsHistory.h"
#include "Dlg4Log.h"
#include "ReplaceInstDlg.h"

// #include <gost_3411_2012_calc.h>

#ifdef _OPENMP
#include <omp.h>
#endif

//#define LOGGING
#ifdef LOGGING
CString tmp_buf;
#endif


extern CaplLog m_MainLog;
extern bool prev_ping;
extern int num_prev_ping;
extern bool AutoReconnect;
bool forced_AutoReconnect = false;

// При установке в false - во внутренних циклах и т.п. перестает обрабатывать
// сообщения WM_PAINT. Флаг добавлен для ILS.
bool global_process_wm_paint = true;

//#define LOG_BASE_POINT

CString m_VerCli(_T(""));
CString m_AppName(_T(""));

CString m_PathSetting(_T(""));
int m_NeedDictVer = 44;

CString globalRequestedMainLicense(_T("PSS"));
CStringArray globalRequestedAddLicenses;

#define ERR_MESS_MISSED_LIC_SERVER_FIRST 	APL_T("Сервер приложений не поддерживает работу с новыми типами лицензий! ")
#define ERR_MESS_MISSED_LIC_SERVER_RENEW 	APL_T("Обновите сервер приложений.");


CString globalIniName(_T(""));

CAplAtomicLong globalLoadForceMode(0);

bool global_first_dlg_login = true;

#ifdef _DEBUG
//#define COUNT_TIMES
#endif

// Вывод в лог времени операций
//#define WRITE_TICK_LOG

#ifdef WRITE_TICK_LOG

// макрос начала измерения. Передается имя операции. Ставится перед началом операции
#define WriteTickLog(mess) \
	if(tmpp_name != _T("")){CString tmpp; DWORD tmpp_t2=GetTickCount()-tmpp_t1; tmpp.Format(_T("%04i мс: "),tmpp_t2); tmpp+=tmpp_name; list_times.Add(tmpp);}else{}\
	tmpp_name = mess;\
	if(tmpp_name != _T("")){tmpp_t1=GetTickCount();}

// Макрос для вывода измеренного в лог. Ставится после выполнения всех операций 
#define SaveTickLog {CString mess; GetClearTickLog(mess); WriteLocalLog(mess);}

CStringArray list_times;

CString tmpp_name = _T("");
DWORD tmpp_t1 = 0;

void GetClearTickLog(CString &mess)
{
	WriteTickLog(_T(""));
	mess = _T("");
	for(int i=0; i <list_times.GetSize(); i++)
	{
		mess+=list_times.GetAt(i);
		mess+=_T("\n");
	}
	list_times.RemoveAll();
}

#else // WRITE_TICK_LOG

#define WriteTickLog(mess)
#define SaveTickLog

#endif // WRITE_TICK_LOG


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


// Установка кода ошибки (и ее описания) в операциях на уровне клиента
#define SetAPLErrorWithFileInfo(ErrCode) SetAPLError((AplNetErrors)ErrCode, (m_AplTransport==0?_T(""):m_AplTransport->m_description_serv_error), _T(__FILE__), __LINE__, __APL_FUNC__);
#undef SetAPLErrorWithFileInfoEx
#define SetAPLErrorWithFileInfoEx(ErrCode, add_text_err) \
{CString mixed_err_mess(_T("")); if(m_AplTransport!=0){mixed_err_mess+=m_AplTransport->m_description_serv_error; mixed_err_mess+=_T(" ");}\
	mixed_err_mess += add_text_err;\
	SetAPLError((AplNetErrors)ErrCode, mixed_err_mess, _T(__FILE__), __LINE__, __APL_FUNC__);}

#define SetAPLErrorWithFileInfoExtent(ErrCode, p_instances_extent) \
{ \
	CString inst_list = APL_T("Проверяемые инстансы: "), tmp_str; \
		for(int i=0; i<p_instances_extent->GetSize();i++){tmp_str.Format(_T("#%i,"), p_instances_extent->GetAt(i)->GetId());inst_list+=tmp_str;} \
	SetAPLError((AplNetErrors)ErrCode, inst_list, _T(__FILE__), __LINE__, __APL_FUNC__); \
}

// если работаем с файлом - выходим из функций, работающих с сервером
#define CHECK_BD(ret_val) if(m_CurDataFile != _T("")) return ret_val;

// проверяет состояние глобального флага завершения работы, если установлен - выходим из функции
// проверяет (в зависимости от флагов check_connect, check_processed) есть ли соединение и не выполняется ли операция в фоне. При неправильнос состоянии - выходим
#define CHECK_STATE(check_connect, check_processed, retval) \
	if(InterlockedExchangeAdd(&m_external_exit,0) > 0){SetAPLErrorWithFileInfoEx(APL_NET_CLI_BREAK, _T("m_external_exit > 0")); return retval;}\
	if(check_connect || check_processed){if(!CanProcessed(check_connect, check_processed, _T(""),_T(__FILE__),__LINE__,__APL_FUNC__)){return retval;}} 

#define CHECK_STATE_S(check_connect, check_processed, retval) \
	if(InterlockedExchangeAdd(&m_external_exit,0) > 0){SetAPLErrorWithFileInfoEx(APL_NET_CLI_BREAK, _T("m_external_exit > 0")); return retval;}\
	if(check_connect || check_processed){if(!CanProcessed(check_connect, check_processed, source,_T(__FILE__),__LINE__,__APL_FUNC__)){return retval;}} 

// если соединение не разорвано - пытается восстановить его. Если не удалось - установливает переменные ошибок и выходит
// если соединение есть или восстанвлено - устанавливает служебные переменные в начальное состояние
#define TEST_RECONNECT(retval) \
	if(!m_AplTransport->IsConnected()){if(!m_AplTransport->Reconnect(true,_T(__FILE__),__LINE__,_T(__DATE__))) \
		{SetAPLError(APL_NET_CLI_NOT_CONNECT,_T(""),_T(__FILE__),__LINE__,__APL_FUNC__); return retval;}} \
	apl_CheckConnection((LPVOID)this); \
	if(!m_IsWaitDlg){m_AplTransport->SetManageMode(MANAGE_ABSENT); } m_LastAplErrorCode = APL_NET_OK;



// проверка входных параметров функций. Если параметры не соответсвуют - выходим из функции и во втором варианте перед выходом чистим буфера
#define CHECK_PARAM(condition) 	if(condition){SetAPLErrorWithFileInfo(APL_NET_CLI_MISSED_DATA); return false;}
#define CHECK_PARAM_EX(condition, descr) 	if(condition){SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, descr); return false;}
#define CHECK_PARAM_WITH_CLEAR(condition) 	if(condition){SetAPLErrorWithFileInfo(APL_NET_CLI_MISSED_DATA); m_AplTransport->ClearInternalData(); return false;}

// проверка результатов вызова функций  сервера. При плохом ответе - установливает переменные ошибок и выходит
#define RETURN_IF_OR_CHECK_ERR(condition) RETURN_IF_OR_CHECK_ERR_EX(condition, _T(""));

#define RETURN_IF_OR_CHECK_ERR_EX(condition, add_text_err) if(condition || SPEC_ERR_CODES(aplError)) \
	{SetAPLErrorWithFileInfoEx(aplError, add_text_err); \
	if(aplError == APL_NET_SRV_APLAPIERR_FILE_BD_READ_ONLY){m_is_readonly = true;} \
	if(aplError != APL_SOCK_OPERATION_IN_PROGRESS){m_AplTransport->ClearInternalData();} \
	LogDebugMessageIfDef( CString(APL_T("Ошибка в %s: %s")), __APL_FUNC__, add_text_err); \
	return false;}

// проверка результатов вызова функций  сервера. При плохом ответе - установливает переменные ошибок и вызывает исключение SaplErrorDescription
#define THROW_IF_OR_CHECK_ERR(condition) if(condition || SPEC_ERR_CODES(aplError)) \
	{/*SetAPLErrorWithFileInfo(aplError);*/ \
	if(aplError == APL_NET_SRV_APLAPIERR_FILE_BD_READ_ONLY){m_is_readonly = true; } \
	MY_THROW(aplError);}

// запись в буфер служебных параметров
#define WriteExtBufData(dbuf_in) \
	if(code1==-1){double* p_data; p_data = (double*)((BYTE*)m_AplTransport->m_tmp_buf.m_data+4); dbuf_in.Add(p_data,8);} \
	else{dbuf_in.Add(&(code1),4); dbuf_in.Add(&(code2), 4); }


// обработка исключений по памяти и прочих исключений. 
// !! Должен использоваться в коде в паре с блоком try{} или try{}catch(SaplErrorDescription error) 
#define CATH_MEMORY_ANY_EXEPTION catch(CMemoryException* e) \
	{TCHAR   szCause[255];\
	e->GetErrorMessage(szCause,255); TCHAR err_buf[1516];\
	_sprintf(err_buf,_T("Memory exeption: %s"), szCause); m_MainLog.LogMessage(err_buf);\
	if(!m_hide_mode)AfxMessageBox(err_buf);	TRACE(err_buf);}catch(...){TRACE( APL_T("Неизвестное исключение внутри клиента..."));}


#ifdef _DEBUG
void inline DebugStopper()
{
	int a = 0;
}
#endif

// счетчик клиентских программ на данном компе
#pragma data_seg("SHARED")  // Begin the shared data segment.
int global_computer_id = 0;
#pragma data_seg()          
#pragma comment(linker, "/section:SHARED,RWS")

// Максимально возможное количество подключений
#define MAX_CON_NUM 100

#define DEF_INI_FILENAME _T("AplTransport.ini")


#define CallTimerId 1005
UINT TimerId = 0;
LPVOID lpGlobParameter = 0;

UINT RenewClientStateOnServer(LPVOID lpParameter)
{
	CaplStackLogger stack_logger(__APL_FUNC__,true);

	LogDebugMessageIfDefZero(_T("___________ RenewClientStateOnServer _____________"));

	AplNetErrors aplError = APL_NET_OK;
	CaplDataBuf buf1, buf2;
	CTransportSocketClient* local_transport = (CTransportSocketClient*)lpParameter;
	if(local_transport == 0)return 0;
	bool m_hide_mode = false;
	
	try{

		if(!local_transport->IsConnected())
			return 0;

		//Запрос на обновление счетчика на сервере
		if(!local_transport->IsOperated())
		{
			local_transport->SetWaitDlgText( APL_T("Обновление регистрационных данных"));

			LogDebugMessageIfDefZero( APL_T("___________ RenewClientStateOnServer   вызываем..."));

			local_transport->AT_DCSaveChanges(aplError, buf1, buf2);
			if(aplError!=APL_SOCK_OPERATION_IN_PROGRESS)
			{
				LogDebugMessageIfDefZero( APL_T("___________ RenewClientStateOnServer   очищаем данные...")); 
				local_transport->ClearInternalData();
			}
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	LogDebugMessageIfDefZero( APL_T("___________ RenewClientStateOnServer   выходим!"));
	return 0;
}

CString MakeSourceDescr(CString file, int line, CString date)
{
	CString str;
	str.Format( APL_T("%s (%i) от %s"), file, line, date);
	return str;
}


// количество энтитей в массиве
#define NUM_ENTITY_ADMIN_ACCESS 3
int ENTITY_ADMIN_ACCESS_ARRAY[] = {129, 130, 131};

// для некоторыйх энтитей для создания/удаления/изменения параметров надо быть администратором
// функция проверяет, входит ли ентити в этот список
bool CheckEntityAdminAccess(int ent_id)
{
	int i;
	for(i=0; i <NUM_ENTITY_ADMIN_ACCESS; i++)
	{
		if(ENTITY_ADMIN_ACCESS_ARRAY[i] == ent_id) return true;
	}
	return false;
}


//функция по таймеру лезет к серверу и обновляет список подключенных компьютеров
void CALLBACK CallTimer(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
	if(TimerId==idEvent)
	{
		//	MessageBox(m_hwnd,_T("Connect"),_T("TimerFUnct"),MB_OK);
		RenewClientStateOnServer(lpGlobParameter);
	}
	return;
}




// 0 - удалить и создать
// 1 - только создать
// 2 - только удалить
void ResetTimer(int mode = 0, LPVOID lpParameter = 0)
{
	// Установим таймер на обновление списка подключений
	DWORD uElapse = 0;
	//	отладка
	//	uElapse=1*20*1000;// 20 секунд
	//	рабочий вариант
	uElapse = 1 * 30 * 1000;// полминуты, не больше - так как http сессия на сервере при работе поверх http проверяет состояние раз в минуту

	if(mode == 1){KillTimer(NULL, TimerId);}
	if(mode == 2)
	{
		TimerId = SetTimer(NULL, CallTimerId, uElapse, &CallTimer);
	}
	lpGlobParameter = lpParameter;
}

void PumpTimerPaint()
{
	MSG msg; 
	if(global_process_wm_paint)
	{
		while (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT,
			PM_REMOVE|  PM_NOYIELD)) 
			DispatchMessage(&msg);
	}
	while (::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER,
		PM_REMOVE|  PM_NOYIELD)) 
		DispatchMessage(&msg);
	while (::PeekMessage(&msg, NULL, WM_SOCKET_NOTIFY, WM_SOCKET_NOTIFY,
		PM_REMOVE|  PM_NOYIELD)) 
		DispatchMessage(&msg);
}

bool servFileExist(CString &file)
{	
	bool res = false;
	//Критическая по быстродействию функция
	if(file == _T("")) return false;
	__finddata_t fileinfo;
	int i=__findfirst(file, &fileinfo );
	if (i != -1)
	{
		_findclose(i);
		res = true;
	}
	return res;
}//bool CAplNetTransportServTCPApp::servFileExist(char *file)


//Происходит ли в данный момент поиск имени файла
extern HANDLE IsEventFileCreateProcessed;

int servGetFreeFileName(CString dir, CString orig, CString ext, CString &filename)
{
	//Критическое место
	WaitForSingleObject(IsEventFileCreateProcessed, 5000);
	int i=0;
	do
	{
		i = global_computer_id++;
		filename.Format(_T("%s%s%i.%s"), dir, orig, i, ext);

	} while(servFileExist(filename));

	SetEvent(IsEventFileCreateProcessed);
	//Конец критического места
	return i;

}//char *CAplNetTransportServTCPApp::servGetFreeFileName()

////////////////////////////////////////////
//определение ip машины клиента
bool CaplNetStepData::GetCompName(CString &name)
{
	name = _T("");
	if( m_AplTransport != 0)
	{
		// первый вариант - только имя компьютера
		return m_AplTransport->GetCompName(name);
		// второй вариант - имя компьютера с доменом. Это обратный DNS запрос, очень сильно тормозит в больших сетях
		// не рекомендуется раскомментаривать
		//DWORD ip_num;
		//ip_num=m_AplTransport->GetCompIp();
		//Ip2String(ip_num,ip_str);
		//return Ip2Name(ip_num,name) == true;
	}
	return false;
}

////////////////////////////////////////////
//определение ip машины клиента
UINT32 CaplNetStepData::GetCompIp()
{
	if(m_AplTransport != 0)
	{
		return m_AplTransport->GetCompIp();
	}
	return 0;
	/* устарело
	// структура для определения id
	typedef struct _ASTAT_
	{
	ADAPTER_STATUS adapt;
	NAME_BUFFER    NameBuff [30];
	}ASTAT, * PASTAT;

	// Определение по номеру сетевой карты
	int id = 0;
	ASTAT Adapter;
	NCB ncb;
	UCHAR uRetCode;

	memset( &ncb, 0, sizeof(ncb) );
	ncb.ncb_command = NCBRESET;
	ncb.ncb_lana_num = 0;
	uRetCode = Netbios( &ncb );
	//printf( "The NCBRESET return code is: 0x%x \n", uRetCode );

	memset( &ncb, 0, sizeof(ncb) );
	ncb.ncb_command = NCBASTAT;
	ncb.ncb_lana_num = 0;

	char *aaa="*               ";
	strcpy( (char*)ncb.ncb_callname,  aaa);
	ncb.ncb_buffer = (unsigned char *) &Adapter;
	ncb.ncb_length = sizeof(Adapter);

	uRetCode = Netbios( &ncb );
	//printf( "The NCBASTAT return code is: 0x%x \n", uRetCode );
	if ( uRetCode == 0 )
	{
	/*printf( "The Ethernet Number is: %02x%02x%02x%02x%02x%02x\n",
	Adapter.adapt.adapter_address[0],
	Adapter.adapt.adapter_address[1],
	Adapter.adapt.adapter_address[2],
	Adapter.adapt.adapter_address[3],
	Adapter.adapt.adapter_address[4],
	Adapter.adapt.adapter_address[5] );* /
	id=*(int*)(&Adapter.adapt.adapter_address[2]);
	}
	return id;
	/* старые варианты
	#define VERSION_98
	#ifdef VERSION_98
	if(global_computer_id== 0){
	CTime t = CTime::GetCurrentTime();
	// example for CTime::GetTime
	time_t osBinaryTime = t.GetTime(); // time_t defined in <time.h>
	//printf( "time_t = %ld\n", osBinaryTime );
	global_computer_id=(int)osBinaryTime;
	}
	return global_computer_id;
	#else //#ifdef VERSION_98
	SYSTEMTIME SystemTime; 
	GetSystemTime(&SystemTime);
	long ms_sys_time=GetTickCount();
	long iid = 0;

	ms_sys_time=SystemTime.wMilliseconds+1000*SystemTime.wSecond+
	1000*60*SystemTime.wMinute + 1000*60*60*SystemTime.wHour-ms_sys_time;
	iid=SystemTime.wMilliseconds+1000*SystemTime.wSecond+1000*60*SystemTime.wMinute + 1000*60*60*SystemTime.wHour;
	ms_sys_time=iid-ms_sys_time;
	ms_sys_time=ms_sys_time>>2; 
	return ms_sys_time;
	#endif //#ifdef VERSION_98
	*/
}//UINT32 CaplNetStepData::GetCompId(){



// Структура для передачи данных о измененных атрибутах-агграх
struct SChangedAggrVal
{
	SChangedAggrVal(){inst = 0; attrval = 0; attr = 0;}
	CaplInstance*	inst;
	CaplInstance::CaplValueDefinition*		attrval;
	CaplAttr* attr;
};

CStringArray test_strings;

void GlobalEmergencyLogMess(LPCTSTR mess)
{
	// для контроля при отладке - останавливал нить сообщением и дублировал вывод в основной лог
	//if(a==1)AfxMessageBox("Stop!");
	//theApp.m_MainLog.LogMessage(mess,true,true,true,true,0,0); - если раскомментарить, в лог буду сыпаться сообщения о всех сменах функций, и сервер будет сильно тормозить
	test_strings.Add(mess);
}

void SaveTestString()
{
	int i;
	m_MainLog.LogMessage(APL_T("Массив тестовых строк:"));
	for(i=0; i <test_strings.GetSize(); i++)
	{
		m_MainLog.LogMessage(test_strings.GetAt(i), true, false, true, true, 0,0,0,0,0,0);
	}
	test_strings.RemoveAll();
}


/////////////////////////////////////////////////////////////////////////////
// CaplNetStepData

CaplNetStepData::CaplNetStepData():CaplStepData()
{
	m_class_info.Format(_T("CaplNetStepData " POINTER_FORMAT),this);
	LogDebugMessageIfDef(__APL_FUNC__);

#ifdef _DEBUG
	// Если раскомментарить две нижележащие функции, информация о входе-выходе в большинство функций NET_ будет
	// писаться в строковый буфер test_strings, что может привести к торможению и проблемам с памятью.
	// Только для отладки!
// 	SetStackLoggerMode(true); 
// 	aplSetStackEmergencyLogFunct(GlobalEmergencyLogMess);

	m_csTestFileName = _T("");
#endif


#ifdef LOG_BASE_POINT
	CString class_info;class_info.Format("this %x",this);
	m_MainLog.LogMessage("CaplNetStepData::CaplNetStepData(): ",false,true,true,false,0,class_info);
#endif //LOG_BASE_POINT

	m_ErrorMessageMode = true;
	m_AutoDownloadAttr = false;
	m_NetErrorMessageMode = true;
	m_SizePortionSavedInst = 0;

	ZeroBDParams();

	m_is_readonly = false;
	m_non_admin_readonly = false;
	m_processed_set_error = false;
	m_IsWaitDlg = true;
	m_hide_mode = false;
	m_check_access_from_server = true;
	m_can_find_in_blob = false;
	m_count_cur_users = 0;
	m_def_curr_user = 0;
	m_def_curr_user_group = 0;
	m_SingleAlias.Empty();
	m_Protocol = 0;
	m_AutoStartSrv = true;
	m_NameDomain = _T("");
	m_changed_thread = 0;
	m_external_exit = 0;
	m_MaxBlobSize = MAXLONG;

	m_IsLoadedTable4Entity = false;

	// по умолчанию считаем сервер ansiшным. ПРи коненекте если надо - поменяем
	m_global_ansi_string = true;

	m_VerSrv = _T("");
	m_ServerAUSessionId = 0;
	m_LastAplErrorCode = APL_NET_OK;
	m_AplErrorDescription = _T("");
	m_loadcount_all = 0;
	m_loadcount_unique = 0;

	m_parent_wnd = 0;

	m_LoadTimeOut = 5;
	m_last_load_queries = 0;
	m_last_load_queries_count = 0;
	m_load_access_agent_from_serv = true;

	m_ShowLastUser = true;
	m_NumLastUsers = 10;
	m_NumLastDomains = 2;

	CWnd* main_cwnd = AfxGetMainWnd();
	m_hwnd = (main_cwnd == 0) ? 0 : main_cwnd->m_hWnd;

	m_type_lic_current = _T("");
	m_SetAccessWithSave = false;

	m_max_connection_num = MAX_CON_NUM;
	m_ServerHashMethod = _T("");
	m_is_correct_d_branch = false;

	m_AplTransport = new CTransportSocketClient(this);
	m_AplTransport->m_class_info.Format( APL_T("CTransportSocketClient 0x%08X; Клиент: "), this);
	m_AplTransport->m_class_info += this->GetClassDescription();

	m_check_cur_access_befor_set_access = true;
	m_check_cur_access_after_set_access = true;



	// m_lang_england = false; - убрано по просьбе Лобанова. Перевод будет выполняться новым механизмом через CaplTranslate.
	m_IsActualizeChangedAggr = true;

	m_num_load = 0;
	m_num_save = 0;
	m_num_find = 0;
	m_num_query = 0;
	m_num_find_local_cache = 0;
	m_num_run_sql = 0;
	m_num_run_sql_re = 0;

	m_last_load_time_meter.Zero();
	m_last_read_users_timer.Zero();

	m_bCanLoadInstUserDataTimeInfo = false;

	// Заполнение глобальных переменных (имя и путь к exe, путь к ini  и т.п.)
	InitAppName();
	// определяем версию приложения (PSS, ILS, ПМ ИЛП) по файлу XYZ.ver
	GetVersionClient();

	CaplNetStepData::g_CurLogMode = (aplLogMode)m_Ini.GetInt(_T("Options"), _T("defLogInfoLevel"), LL_error);
	
	//MessageBox(m_hwnd,_T("Конструктор"),_T("Informationnn"),MB_OK);
}//CaplNetStepData::CaplNetStepData():CaplStepData()


CaplNetStepData::~CaplNetStepData()
{
	LogDebugMessageIfDef(__APL_FUNC__);

	if(m_query != 0)
	{
		delete m_query;
		m_query = 0;
	}

	NET_Disconnect();

	if(m_AplTransport != 0)
		delete m_AplTransport;
	m_AplTransport = 0;
	
	LogDebugMessageIfDef(_T("~CaplNetStepData end"));


}//CaplNetStepData::~CaplNetStepData()

bool CaplNetStepData::SetHideMode(bool mode)
{
	bool old_mode = m_hide_mode;
	SetNetErrorMessageMode(!mode);
	// флаг m_ErrorMessageMode для вывода низкоуровневых сообщений, изначально он false. Тут его только сбрасываем, если надо его обратно поставить - пусть делают ручками
	if(m_hide_mode)SetErrorMessageMode(false); 
	m_hide_mode = mode;
	m_AplTransport->SetHideMode(mode);

	g_bShow_aplMessageBox_Error = false;
	g_bShow_aplMessageBox_Warning = false;
	g_bShow_aplMessageBox_Information = false;

	return old_mode;
}

CString CaplNetStepData::GetClassDescription()
{
	CString descr, tmpbuf, old_err_descr;

	descr+= APL_T("Параметры сеанса:");
	if(GetNameCurrConnection() == _T("") || 0 == m_CurrUser)
	{
		descr+= APL_T(" соединение не установлено");
	}
	else
	{
		descr+= APL_T("  БД: ") + GetNameCurrBase();
		descr+= APL_T("  Соединение \'") + GetNameCurrConnection() + _T("\'");
		descr+= APL_T("  Пользователь: ") + GetNameCurrUser();

	}
	if(m_CurrAdminUser != 0)
	{
		old_err_descr = m_ErrorDescription;
		GetAttr(m_CurrAdminUser, m_attr_apl_user__name, tmpbuf);
		descr+= APL_T(" (Режим админа: Админ: ") + tmpbuf + CString(_T(")"));
		m_ErrorDescription = old_err_descr;
	}

	descr+= APL_T(" Схема: "); descr+=GetDictionaryDescription();

	DWORD pid = GetCurrentProcessId();
	tmpbuf.Format(_T(" pid %i"), pid);
	descr += tmpbuf;
	if(m_AplTransport != 0)
	{
		tmpbuf.Format(_T(" (client_ip %s  client_id %u(%u/%u)"), 
			Ip2String(m_AplTransport->m_login_data.client_ip), m_AplTransport->m_login_data.client_id, 
			m_AplTransport->m_login_data.s1, m_AplTransport->m_login_data.s2);
		descr += tmpbuf;
	}
	return descr;
}

bool CaplNetStepData::SetNetErrorMessageMode(bool mode)
{
	bool old_mode = m_NetErrorMessageMode;
	m_NetErrorMessageMode = mode;
	return old_mode;
}

void CaplNetStepData::SetAPLError(SaplErrorDescription &err, LPCTSTR context)
{
	SetAPLError((AplNetErrors)err.m_err_code, err.m_err_descr, err.m_filename, err.m_err_line, err.m_funct_name, context);
}

void CaplNetStepData::SetUserAPLError(LPCTSTR user_error_description)
{
	CString err_mess = _T("\n");
	err_mess += user_error_description;
	SetAPLError(APL_NET_CLI_ERROR_PROCESSED, err_mess);
}

bool CaplNetStepData::WriteLocalLog(LPCTSTR message)
{
	if(0==message) return false;
	if(message[0]==_T('0')) return false;
	CString class_info;
	class_info.Format(_T("this %x"), this);
	return m_MainLog.LogMessage(message, true, true, true, false, 0, class_info);
}

aplLogMode CaplNetStepData::g_CurLogMode=aplLogModeError;

bool CaplNetStepData::WriteLocalLog(aplLogMode minLogMode, LPCTSTR message) // Записывает в лог только если текущий уровень больше или равен minLogMode
{
	if(minLogMode<g_CurLogMode) return false;
	return (WriteLocalLog(message));

}

void CaplNetStepData::SetAPLError(AplNetErrors res, CString add_text, LPCTSTR file, int line, LPCTSTR funct_name, LPCTSTR context)
{
	CString class_info;
	class_info.Format(_T("this %x"),this);

	if(m_processed_set_error)
	{
		LogDebugMessageIfDef( APL_T("SetAPLError - m_processed_set_error==true, не обрабатываем"));
		m_MainLog.LogMessage( APL_T("Во время обработки предыдущей ошибки возникла следующая: ")+
			APL_NET_ERROR2char(m_LastAplErrorCode, add_text), false, true, true, false, 0, class_info);
		return;
	}
	m_processed_set_error = true;
	m_LastAplErrorCode = res;
	CString int_add_text = _T("");
	CString tmpbuf;
	// из этой переменной текст выводится только в лог!
	CString buf_ext = _T("");
	CString buf_ext_file = _T("");

	if(add_text != _T(""))
	{
		int_add_text = _T("\r\n") + add_text;
	}
	if(m_AplTransport->m_description_serv_error  != _T(""))
	{
		int_add_text += _T("\r\n");
		int_add_text += m_AplTransport->m_description_serv_error;
	}

	m_ErrorDescription = _T("");

	if(m_LastAplErrorCode == APL_NET_CLI_NET_ERROR)
	{
		if(m_AplTransport->m_wsaError == 10035)
		{
			m_ErrorDescription = APL_T("Сервер приложений не ответил на запрос. Возможно, он не запущен или перегружен (код ошибки 10035).");
		}
		else if(m_AplTransport->m_wsaError == 10022 ||
				m_AplTransport->m_wsaError == 10061 )
		{
			m_ErrorDescription = APL_T("Невозможно установить соединение с сервером приложений. Возможно, указан неверный адрес сервера или сервер не запущен.");
		}
		else
		{
			m_ErrorDescription += m_AplTransport->GetDescriptionLastError() + APL_T("(код ошибки клиента APL_NET_CLI_NET_ERROR)");
		}
		if(add_text != _T(""))
		{
			m_ErrorDescription += int_add_text;
		}
	}
	else if(m_LastAplErrorCode == APL_NET_CLI_SERVER_DN_SUPPORT_FUNCTION && !add_text.IsEmpty() && context != 0)
	{
		m_ErrorDescription += add_text;
	}
	else
	{
		m_ErrorDescription += APL_NET_ERROR2char(m_LastAplErrorCode, int_add_text);
		if(m_LastAplErrorCode>APL_NET_SRV_APLAPIERR_NODATA && m_LastAplErrorCode<APL_NET_SRV_APLAPIERR_UNKNOWN_EXEPTION)
			aplTranslateDictErrorMessages(m_ErrorDescription);
	}
	if(file != 0 && line  != 0 )
	{
		buf_ext.Format(_T("%s(%i) "), file, line);
		buf_ext_file = _T("Src: ");
	}
	if(funct_name != 0)
	{
		buf_ext += CString(_T("\n\tFun: ")) + funct_name + _T("  ");
	}

	buf_ext += CString(_T("\n\tConn: ")) + GetClassDescription();

	if(context != 0)
	{
		m_ErrorDescription += APL_T("\nДополнительная информация: \n");
		m_ErrorDescription += context;
	}

	if(res != APL_NET_OK)
	{
		OutputDebugString(m_ErrorDescription + _T("\n") + buf_ext + _T("\n"));
		if(m_ErrorPrintMode)aplMessagePrint(m_ErrorDescription,MB_ICONERROR);
		m_MainLog.LogMessage(m_ErrorDescription + _T("\n") + buf_ext_file + buf_ext, false, true, true, false, 0, class_info);
	}

	m_AplErrorDescription = m_ErrorDescription;

	LogDebugMessageIfDef( APL_T("SetAPLError -Сообщение об ошибке PDM Step Suite: %s"), m_ErrorDescription);
	if(m_NetErrorMessageMode)
	{
		if(m_LastAplErrorCode == APL_NET_OK || m_LastAplErrorCode == APL_SOCK_OPERATION_IN_PROGRESS)
		{
			//LogDebugMessageIfDef("SetAPLError - APL_NET_OK || APL_SOCK_OPERATION_IN_PROGRESS; description : "+m_ErrorDescription);
			// ничего не выводим
			// вообще это только на время отладки!!!!
			/*
			if(m_AplTransport->GetLastAscError()!=ASC_NO_ERROR &&
			m_AplTransport->GetLastAscError()!=ASC_OPERATION_IN_PROGRESS &&
			m_AplTransport->GetLastAscError()!=ASC_BREAK_CALL &&
			m_AplTransport->GetLastAscError()!=ASC_ERROR_SOCKET_CLOSE)
			{
			// вообще это только на время отладки!!!!
			MessageBox(m_hwnd,"AAA"+m_ErrorDescription,
			"Служба RPC PDM Step Suite",MB_ICONEXCLAMATION|MB_OK|MB_TASKMODAL);

			}
			*/
		}
		else if(m_LastAplErrorCode == APL_NET_CLI_NET_ERROR && m_AplTransport->GetLastAscError() == ASC_NO_ERROR)
		{
			//LogDebugMessageIfDef("SetAPLError - APL_NET_CLI_NET_ERROR || ASC_NO_ERROR: description : "+m_ErrorDescription);
			// тоже ничего не выводим
			// вообще это только на время отладки!!!!
		}
		else
		{
			if(!m_hide_mode)
			{
				MessageBox(m_hwnd, m_ErrorDescription,
					 APL_T("Сообщение об ошибке PDM Step Suite"), MB_ICONERROR|MB_OK|MB_TASKMODAL);
			}
			if(m_bufer_for_err_message != 0){ m_bufer_for_err_message->Append(_T("\n")); m_bufer_for_err_message->Append(m_ErrorDescription); }
		}
	}
	m_processed_set_error = false;
}//void CaplNetStepData::SetAPLError(int res){


void CaplNetStepData::SetLoadTimeOut(int time_out){ m_LoadTimeOut = time_out; }

void CaplNetStepData::SetWaitTimeOutWithoutDlg(int time_out)
{
	if(m_AplTransport== 0)return;
	if(m_AplTransport->m_aplSocket== 0)return;
	m_AplTransport->m_aplSocket->SetTimeOut(time_out*1000);
}

AplNetErrors CaplNetStepData::GetLastAplError(){return m_LastAplErrorCode;}//

CString CaplNetStepData::GetLastErrorDescription(){return m_ErrorDescription;}

CString CaplNetStepData::GetLastAplErrorDescription(){return m_AplErrorDescription;}

bool CaplNetStepData::GetHideMode(){return m_hide_mode;}

bool CaplNetStepData::CanFindInBLOB(){return m_can_find_in_blob;}

// void CaplNetStepData::SetConnectDlgLang(bool lang_england){m_lang_england=lang_england;} - убрано по просьбе Лобанова. Перевод будет выполняться новым механизмом через CaplTranslate.

bool CaplNetStepData::IsConnected()
{
	if(!m_AplTransport->IsConnected())return false;
	return m_IsConnected;
}

CString CaplNetStepData::GetNameCurrBase(){return m_NameCurrBase;}

CString CaplNetStepData::GetNameCurrConnection(){return m_NameCurrConnection;}

bool CaplNetStepData::IsCurrConnectionLocal()
{
	if(!IsConnected()) return false;
	if(m_AplTransport==0) return false;
	return m_AplTransport->IsServerLocal();
}

CString CaplNetStepData::GetNameCurrUser(){return m_NameCurrUser;}

CString CaplNetStepData::GetNameCurrDomain(){return m_NameDomain;}

CaplInstance* CaplNetStepData::GetCurrUser(){return m_CurrUser;}

int CaplNetStepData::GetBlobPortionDefSize(){return SIZE_DOSE_BLOB;}

int CaplNetStepData::GetAttrId(CaplAttr *attr)
{
	if(attr!=NULL){return attr->id;}else{return 0;}
}
/** Возвращает идентификатор атрибута типа <b>entity</b> c именем <b>attr_name</b> 
@return идентификатор атрибута*/
int CaplNetStepData::GetAttrIdBN(CaplEntity *entity, LPCTSTR attr_name)
{
	CaplAttr* attr = GetAttrDefinition(entity, attr_name);
	return GetAttrId(attr);
}
/** Возвращает идентификатор <b>entity</b>, заданной параметром <b>ent</b>
@return идентификатор <b>entity</b>*/
int CaplNetStepData::GetEntId(CaplEntity *ent)
{
	if(ent!=NULL){return ent->id;}else{return 0;}
}
/** Возвращает идентификатор <b>entity</b> по имени, заданном параметром <b>ent_name</b> 
@return идентификатор <b>entity</b>*/
int CaplNetStepData::GetEntIdBN(LPCTSTR ent_name)
{
	CaplEntity* ent = GetEntityBN(ent_name);
	return GetEntId(ent);
}
/** Возвращает идентификатор <b>instance</b>, заданной параметром <b>inst</b>
@return идентификатор <b>instance</b>*/
int CaplNetStepData::GetInstId(CaplInstance *inst)
{
	if(inst!=NULL){return inst->GetId();}else{return 0;}
}

void CaplNetStepData::SetAutoDownloadMode(bool mode){m_AutoDownloadAttr = mode;}

bool CaplNetStepData::GetAutoDownloadMode(){return m_AutoDownloadAttr;}

void CaplNetStepData::AppendNotComparedAggr(CaplInstance *inst, CaplAttr *attr)
{
	m_not_compared_aggr.Add((long)inst, (long)attr);
}



/************************************************************************/
/* 
/************************************************************************/
void CaplNetStepData::ClearData()
{
	LogDebugMessageIfDef(_T("begin"));
	int curr_user_id = 0;
	int curr_admin_user_id = 0;
	if(m_CurrUser != 0)
	{
		curr_user_id = m_CurrUser->GetId();
	}
	if(m_CurrAdminUser != 0)
	{
		curr_admin_user_id = m_CurrAdminUser->GetId();
	}
	CString last_err_descr = m_ErrorDescription;

	m_loaded_in_admin.Clear();

	CaplStepData::ClearData();

	m_ErrorDescription=last_err_descr;

	if(m_ent_apl_user != NULL && curr_user_id != 0)
	{
		bool is_readonly = m_is_readonly;
		m_is_readonly = false;
		m_CurrUser = CaplStepData::CreateInstance(m_ent_apl_user);
		m_CurrUser->SetId(curr_user_id);
		m_is_readonly = is_readonly;
		if(m_is_readonly)
		{
			m_CurrUser->SetAccessmode(aplRO);
		}
		if(curr_admin_user_id != 0)
		{
			m_CurrAdminUser = CaplStepData::CreateInstance(m_ent_apl_user);
			m_CurrAdminUser->SetId(curr_admin_user_id);
			if(m_is_readonly)
			{
				m_CurrAdminUser->SetAccessmode(aplRO);
			}
		}
	}
	else
	{
		m_CurrUser = 0;	
		m_CurrAdminUser = 0;
	}
	LogDebugMessageIfDef(_T("CaplNetStepData::ClearData end"));
}
/************************************************************************
 *
 ************************************************************************/
void CaplNetStepData::ClearDict()
{
	//Эта функция вызывается при очистке словаря из функций CaplStepData, ну и после нее работа с сервером требует нового подключения
	// Проверка entities.Size!=0 нужна, так как виртуальная CaplStepData::ClearDict() вызываестя при подключении к БД и не должна мешать
	if(entities.Size>0)
		NET_Disconnect();

	CaplStepData::ClearDict();
}


/************************************************************************/
/* 
/************************************************************************/
#define BUF_LEN 255

bool CaplNetStepData::ReadLastActive(CStringArray &reg_user_list, CString &reg_base, CStringArray &reg_domain_list)
{
	// Чтение последних активных имени пользователя и имени базы из реестра
	LONG l = 0;
	HKEY key = 0;
	reg_user_list.RemoveAll();
	reg_domain_list.RemoveAll();
	if(!m_ShowLastUser)return true;

	l = RegCreateKey(HKEY_CURRENT_USER, REG_KEY, &key);
	if(l== 0)
	{
		unsigned long format = 0, size = BUF_LEN;
		TCHAR *buf = new TCHAR[BUF_LEN];
		CString sbuf, sbuf1;
		int pos1 = 0, pos2, pos3;

		// новый механизм
		if(RegQueryValueEx(key, _T("LastUsersList"), 0, &format, (BYTE*) buf, &size) == 0 && size>0)
		{
			sbuf=buf;
			while(true)
			{
				pos2 = sbuf.Find(_T(','), pos1);
				if(pos2==-1 &&  pos1<sbuf.GetLength() )pos2 = sbuf.GetLength();
				sbuf1 = sbuf.Mid(pos1,pos2-pos1);
				do
				{
					pos3 = sbuf1.Find(_T("\"\""));
					if(pos3!=-1)sbuf1.Delete(pos3, 1);

				}while(pos3!=-1);

				sbuf1.Delete(0);
				sbuf1.Delete(sbuf1.GetLength()-1);
				if(!sbuf1.IsEmpty())
					reg_user_list.Add(sbuf1);
				if(reg_user_list.GetSize() > m_NumLastUsers)
					break;
				pos1 = pos2 + 1;
				if(pos1 >= sbuf.GetLength())
					break;
			}
		}
		format = 0; size=BUF_LEN;

		// старый механизм - для порядку
		if(RegQueryValueEx(key, _T("LastUser"), 0, &format, (BYTE*) buf, &size) == 0 && size>0)
		{
			reg_user_list.Add(buf);
		}
		format = 0; size=BUF_LEN;
		if(RegQueryValueEx(key, _T("LastUser2"), 0, &format, (BYTE*) buf, &size) == 0 && size>0)
		{
			reg_user_list.Add(buf);
		}

		format = 0; size=BUF_LEN;
		if(RegQueryValueEx(key, _T("LastUser2"), 0, &format, (BYTE*) buf, &size) == 0 && size>0)
		{
			reg_user_list.Add(buf);
		}
		format = 0; size=BUF_LEN;
		if(RegQueryValueEx(key, _T("LastUser3"), 0, &format, (BYTE*) buf, &size) == 0 && size>0)
		{
			reg_user_list.Add(buf);
		}
		format = 0; size=BUF_LEN;
		if(RegQueryValueEx(key, _T("LastUser4"), 0, &format, (BYTE*) buf, &size) == 0 && size>0)
		{
			reg_user_list.Add(buf);
		}
		format = 0; size = BUF_LEN;
		if(RegQueryValueEx(key, _T("LastUser5"), 0, &format, (BYTE*) buf, &size) == 0 && size>0)
		{
			reg_user_list.Add(buf);
		}

		format = 0; size = BUF_LEN;
		l=RegQueryValueEx(key, _T("LastDataBase"), 0, &format, (BYTE*) buf, &size);
		if(l== 0)
		{
			if(size>0)reg_base = buf;
		}
		format = 0;size = BUF_LEN;
		if(RegQueryValueEx(key, _T("LastDomainList"), 0, &format, (BYTE*) buf, &size)== 0 && size>0)
		{
			sbuf = buf;
			pos1 = 0;
			bool empt = false;
			while(true)
			{
				pos2 = sbuf.Find( _T(','), pos1);
				if(pos2 == -1 && pos1 < sbuf.GetLength()) pos2 = sbuf.GetLength();
				sbuf1 = sbuf.Mid(pos1, pos2-pos1);
				do
				{
					pos3 = sbuf1.Find( _T("\"\""));
					if(pos3 != -1)sbuf1.Delete(pos3,1);
				}while(pos3 != -1);
				sbuf1.Delete(0);
				sbuf1.Delete(sbuf1.GetLength()-1);
				reg_domain_list.Add(sbuf1);
				if(sbuf1 == _T(""))empt = true;
				if(reg_domain_list.GetSize() > (empt ? m_NumLastDomains+1 : m_NumLastDomains) )break;
				pos1 = pos2 + 1;
				if(pos1 >= sbuf.GetLength() ) break;
			}
		}
		l = RegCloseKey(key);
		delete[] buf;
	}
	else
	{
		return false;
	}
	return true;
}

// Добавляет строку в начало списка, и удаляет, если она она была в середине
void CaplNetStepData::AppendString2List(CStringArray &list, CString &new_string)
{
	for(int i = 0;i<list.GetSize();i++)
	{
		if(list.GetAt(i)==new_string)
		{
			list.RemoveAt(i);
			i--;
		}
	}
	list.InsertAt(0, new_string);
}

bool CaplNetStepData::WriteLastActive(CStringArray &reg_user_list, CString name_base, CStringArray &reg_domain_list)
{
	LogDebugMessageIfDef( _T("begin"));
	// Сохранение в реестре
	LONG l = 0;
	HKEY key = 0;
	int i,j;
	//CString *str;
	CString sbuf, sbuf1;
	CString str_zero = _T("");
	l=RegCreateKey(HKEY_CURRENT_USER, REG_KEY, &key);
	if(l == 0)
	{
		unsigned long format = 0, size=255;
		//TCHAR *buf = new TCHAR[size]; //UUUU???
		sbuf.Empty();

		// новый механизм
		if(m_ShowLastUser)
		{
			// выкидываем дубли
			for(i=0; i <reg_user_list.GetSize(); i++)
			{
				sbuf1 = reg_user_list.GetAt(i);
				for(j=i+1; j<reg_user_list.GetSize(); j++)
				{
					if(reg_user_list.GetAt(j) == sbuf1) { reg_user_list.RemoveAt(j);}
				}
				sbuf1.Replace( _T("\""), _T("\"\""));
			}
			//записываем
			for(i=0; i <reg_user_list.GetSize(); i++)
			{
				if(i > m_NumLastUsers) break;
				sbuf1 = reg_user_list.GetAt(i);
				sbuf1.Replace( _T("\""), _T("\"\""));
				sbuf+= _T("\"") + sbuf1 + _T("\",");
			}
		}
		RegSetValueEx(key, _T("LastUsersList"), 0, REG_SZ, (BYTE*)(LPCTSTR)sbuf, sbuf.GetLength()*sizeof(TCHAR));

		// удаляем ключи старого механизма
		RegDeleteValue(key, _T("LastUser"));
		RegDeleteValue(key, _T("LastUser2"));
		RegDeleteValue(key, _T("LastUser3"));
		RegDeleteValue(key, _T("LastUser4"));
		RegDeleteValue(key, _T("LastUser5"));

// 		if(reg_user_list.Size>0 ){
// 			str=reg_user_list.GetAt(0);
// 		}else{
// 			str=&str_zero;
// 		}
// 
// 		l=RegSetValueEx(key,"LastUser",0,REG_SZ,(const unsigned char *)LPCTSTR(*str),str->GetLength());
// 		if(reg_user_list.Size>1){
// 			str=reg_user_list.GetAt(1);
// 		}else{
// 			str=&str_zero;
// 		}
// 		l=RegSetValueEx(key,"LastUser2",0,REG_SZ,(const unsigned char *)LPCTSTR(*str),str->GetLength());
// 		if(reg_user_list.Size>2){
// 			str=reg_user_list.GetAt(2);
// 		}else{
// 			str=&str_zero;
// 		}
// 		l=RegSetValueEx(key,"LastUser3",0,REG_SZ,(const unsigned char *)LPCTSTR(*str),str->GetLength());
// 		if(reg_user_list.Size>3){
// 			str=reg_user_list.GetAt(3);
// 		}else{
// 			str=&str_zero;
// 		}
// 		l=RegSetValueEx(key,"LastUser4",0,REG_SZ,(const unsigned char *)LPCTSTR(*str),str->GetLength());
// 		if(reg_user_list.Size>4){
// 			str=reg_user_list.GetAt(4);
// 		}else{
// 			str=&str_zero;
// 		}
// 		l=RegSetValueEx(key,"LastUser5",0,REG_SZ,(const unsigned char *)LPCTSTR(*str),str->GetLength());

		l = RegSetValueEx(key, _T("LastDataBase"), 0, REG_SZ, (BYTE*)LPCTSTR(name_base), name_base.GetLength()*sizeof(TCHAR));

		// выкидываем дубли
		for(i=0; i <reg_domain_list.GetSize(); i++)
		{
			sbuf1=reg_domain_list.GetAt(i);
			for(j=i+1;j<reg_domain_list.GetSize(); j++)
			{
				if(reg_domain_list.GetAt(j)==sbuf1){reg_domain_list.RemoveAt(j);}
			}
			sbuf1.Replace(_T("\""),_T("\"\""));
		}
		//записываем
		sbuf = _T("");
		for(i=0; i < reg_domain_list.GetSize(); i++)
		{
			//if(i>=m_NumLastDomains)break;
			sbuf1 = reg_domain_list.GetAt(i);
			sbuf1.Replace( _T("\""), _T("\"\""));
			sbuf += _T("\"") + sbuf1 + _T("\",");
		}

		l = RegSetValueEx(key, _T("LastDomainList"), 0, REG_SZ, (BYTE*)(LPCTSTR)sbuf, sbuf.GetLength()*sizeof(TCHAR));
		l = RegCloseKey(key);
		//delete[] buf;
	}
	LogDebugMessageIfDef( _T("CaplNetStepData::WriteLastActive end"));
	return true;
}


bool CaplNetStepData::ReadLastProxyList(CStringArray &proxy_list)
{
	// Чтение списка использованных прокси для протокола http из реестра
	LONG l = 0;
	HKEY key = 0;
	proxy_list.RemoveAll();
	if(!m_ShowLastUser)return true;

	l=RegCreateKey(HKEY_CURRENT_USER, REG_KEY, &key);
	if(l== 0)
	{
		unsigned long format = 0, size=255;
		TCHAR *buf = new TCHAR[size];
		CString sbuf, sbuf1;
		int pos1 = 0, pos2, pos3;

		// чтение списка использованных прокси для протокола http
		if(RegQueryValueEx(key, _T("LastProxyList"), 0, &format, (BYTE*) buf, &size)== 0 && size>0)
		{
			sbuf = buf;
			while(true)
			{
				pos2=sbuf.Find( _T(','), pos1);
				if(pos2 == -1 && pos1 < sbuf.GetLength() ) pos2 = sbuf.GetLength();
				sbuf1 = sbuf.Mid(pos1, pos2-pos1);
				do
				{
					pos3 = sbuf1.Find( _T("\"\""));
					if(pos3 != -1) sbuf1.Delete(pos3, 1);
				}while(pos3 != -1);

				sbuf1.Delete(0);
				sbuf1.Delete(sbuf1.GetLength()-1);
				proxy_list.Add(sbuf1);
				if(proxy_list.GetSize()>m_NumLastUsers)break;
				pos1=pos2+1;
				if(pos1>=sbuf.GetLength())break;
			}
		}
		format = 0; size = 255;

		l=RegCloseKey(key);
		delete[] buf;
	}
	else
	{
		return false;
	}
	return true;
}


bool CaplNetStepData::WriteLastProxyList(CStringArray &proxy_list, bool clear)
{
	LogDebugMessageIfDef( _T("begin"));
	// Сохранение в реестре списка использованных прокси для протокола http из реестра
	LONG l = 0;
	HKEY key = 0;
	int i,j;
	//CString *str;
	CString sbuf, sbuf1;
	CString str_zero = _T("");
	l=RegCreateKey(HKEY_CURRENT_USER, REG_KEY, &key);
	if(l == 0)
	{
		unsigned long format = 0, size=255;
		//TCHAR *buf = new TCHAR[size];
		sbuf.Empty();

		// новый механизм
		if(m_ShowLastUser)
		{
			// выкидываем дубли
			for(i=0; i <proxy_list.GetSize(); i++)
			{
				sbuf1=proxy_list.GetAt(i);
				for(j=i+1; j<proxy_list.GetSize(); j++)
				{
					if(proxy_list.GetAt(j) == sbuf1) { proxy_list.RemoveAt(j);}
				}
				sbuf1.Replace( _T("\""), _T("\"\""));
			}
			//записываем
			for(i=0; i <proxy_list.GetSize(); i++)
			{
				if(i > m_NumLastUsers)break;
				sbuf1 = proxy_list.GetAt(i);
				sbuf1.Replace( _T("\""), _T("\"\""));
				sbuf += _T("\"") + sbuf1 +  _T("\",");
			}
		}
		l=RegSetValueEx(key, _T("LastProxyList"), 0, REG_SZ, (BYTE*)(LPCTSTR)sbuf, sbuf.GetLength()*sizeof(TCHAR));


		l = RegCloseKey(key); //-V519
		//delete[] buf;
	}
	LogDebugMessageIfDef( _T("CaplNetStepData::WriteLastActive end"));
	return true;
}



bool CaplNetStepData::ReadConnectionsInfo()
{
	LogDebugMessageIfDef( _T("CaplNetStepData::ReadConnectionsInfo begin"));
	int i = 0, con_id  = 0;
	m_list_connection.Clear();
	m_Ini.SetNeedReload();
	CConnectInfo* cif = 0;
	CString num, dest;
	int pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0, pos5 = 0, pos6 = 0;
	//TCHAR *buf = new TCHAR[1024];

	CStringArray list_keys;
	m_Ini.GetListKeys( _T("ConnectionsList"), list_keys);

	for(i=0; i <list_keys.GetSize(); i++)
	{
		num = list_keys.GetAt(i);
		dest = m_Ini.GetString( _T("ConnectionsList"), num, _T(""));
		if(dest != _T(""))
		{
			// разбираем строчку
			pos1=dest.Find(_T('='));
			pos2=dest.Find(_T(':'));
			pos3=dest.Find(_T('/'));
			pos4=dest.Find(_T('/'),pos3+1);
			pos5=dest.Find(_T('/'),pos4+1);
			pos6=dest.Find(_T('/'),pos5+1);
			if(pos1==-1 || pos2==-1 || pos3==-1)
			{
				// эти поля обязательно должны быть - запись некорректна
				SetAPLErrorWithFileInfo(APL_NET_CLI_MISSED_CONNECTION_REC);
				continue;
			}
			con_id = _atoi(num);
			if(con_id > m_max_connection_num) m_max_connection_num = con_id;
			cif = new CConnectInfo();
			cif->id = i;
			cif->name = dest.Left(pos1);
			cif->comp_name = dest.Mid(pos1+1, pos2-pos1-1);
			cif->port = _atoi(dest.Mid(pos2+1, pos3-pos2-1));
			if(cif->port == 0) cif->port = DEF_PORT_NUM;
			if(pos4 == -1)
			{
				cif->base_name = dest.Mid(pos3+1);
			}
			else
			{
				cif->base_name = dest.Mid(pos3+1, pos4-pos3-1);
				if(pos5 == -1)
				{
					cif->def_user_name = dest.Mid(pos4+1);
				}
				else
				{
					cif->def_user_name = dest.Mid(pos4+1, pos5-pos4-1);
					if(pos6 == -1)
					{
						cif->protocol = _atoi(dest.Mid(pos5+1));
					}
					else
					{
						cif->protocol = _atoi(dest.Mid(pos5+1, pos6-pos5-1));
						cif->proxy = dest.Mid(pos6+1);
					}
					if(cif->protocol > 1) cif->protocol = 1;
				}

			}
			// 
			m_list_connection.Add(cif);
		}
	}


	// Информация о локальном сервера (если ЕСТЬ локальный по умолчанию)
	m_LocalServerPath= m_Ini.GetString( _T("LocalServer"), _T("ServerPath"), _T(""));

	// Имя файла трассировки
	/* временно не используется
	CString path;
	TCHAR* lpFilename = new TCHAR[1024];
	DWORD r = GetModuleFileName(0, lpFilename, 1024);
	path = lpFilename;
	delete []lpFilename;


	// Извлекаем из имени файла его путь
	for(i = path.GetLength()-1; i>0; i--)
	{
		if(path[i] == _T('.'))
		{
			path.Delete(i+1, path.GetLength()-i-1);// чтобы остался завершающий точк
			break;
		}
	}
	path += _T("log");
	path = m_Ini.GetString(_T("Options"), _T("TransportLog"), path);
	*/

	prev_ping = (m_Ini.GetInt( _T("Options"), _T("NoPrevPing"), 0) != 1);
	num_prev_ping = m_Ini.GetInt( _T("Options"), _T("NumPrevPing"), 3);

	// Глобальные параметры, определяющие автоматическое восстановление соединения
	if(forced_AutoReconnect) 
		AutoReconnect = true;
	else 
		AutoReconnect = (m_Ini.GetInt( _T("Options"), _T("AutoReconnect"), 0) == 1);

	// 	AutoReconnectCount=m_Ini.GetInt("Options","AutoReconnect_Count",5);
	// 	AutoReconnectInterval=m_Ini.GetInt("Options","AutoReconnect_Interval",5);

	m_ShowLastUser = (m_Ini.GetInt( _T("Options"), _T("ShowLastUser"), 1) == 1);
	m_NumLastUsers = m_Ini.GetInt( _T("Options"), _T("NumLastUsers"), 10);

	// Если 1 то функции назначения доступа сохраняют данные о доступе в кеше и передают их на сервер только при вызове NET_SaveChanges
	// если -1 - то параметр в ini не установлен, и текущее значение флага мы не меняем
	i = m_Ini.GetInt( _T("Options"), _T("SetAccessWithSave"), -1);
	if(i != -1)
	{
		m_SetAccessWithSave = (i == 1);
	}

	//SetModuleLogFileName(path);
	//delete[] buf;

	m_SetAccessShowInst = (m_Ini.GetInt(_T("Options"), _T("SetAccessShowInst"), 0) == 1);

	LogDebugMessageIfDef( _T("CaplNetStepData::ReadConnectionsInfo end"));

	return true;
}//bool CaplNetStepData::ReadConnectionsInfo(){


bool CaplNetStepData::WriteConnectionsInfo()
{
	int i=0;
	CString buf, num;
	CConnectInfo* cif = 0;

	// при сохранении перенумеровываем - чтобы не было дырок в списке
	int real_num = 0;
	// для ускорения - сначала сохраняем только в память
	m_Ini.SetWriteMode(false);
	// А теперь все с номерами, сохраняем
	for(i=0; i <m_list_connection.GetSize(); i++)
	{
		cif = m_list_connection.GetAt(i);
		if(cif == 0)continue;
		cif->id = real_num++;

		if(cif->def_user_name == _T("") && cif->protocol == 0 && cif->proxy == _T(""))
		{
			buf.Format( _T("%s=%s:%i/%s"), cif->name, cif->comp_name, cif->port, cif->base_name);
		}
		else if(cif->protocol == 0 && cif->proxy == _T(""))
		{
			buf.Format( _T("%s=%s:%i/%s/%s"), cif->name, cif->comp_name, cif->port,
				cif->base_name, cif->def_user_name);
		}
		else
		{
			buf.Format( _T("%s=%s:%i/%s/%s/%i/%s"), cif->name, cif->comp_name, cif->port,
				cif->base_name, cif->def_user_name, cif->protocol, cif->proxy);

		}
		num.Format( _T("%i"), cif->id);
		if(!m_Ini.WriteString( _T("ConnectionsList"), num, buf)){return false;}
	}
	// а пустое пространство очищаем
	for(i = m_list_connection.GetSize(); i <m_max_connection_num + 1; i++)
	{
		num.Format( _T("%i"), i);
		if(!m_Ini.WriteString( _T("ConnectionsList"), num, (LPCTSTR)NULL)){ return false;}
	}
	// при смене режима все из памяти уйдет в файл
	m_Ini.SetWriteMode(true);

	return true;
}//bool CaplNetStepData::WriteConnectionsInfo(){


void CaplNetStepData::SetParentWnd(CWnd* parent_wnd)
{
	m_parent_wnd = parent_wnd;
	if(m_AplTransport != 0){m_AplTransport->m_parent_wnd = m_parent_wnd;}
	if(m_parent_wnd != 0)
	{
		BOOL res = IsWindowVisible(m_parent_wnd->m_hWnd);
		int a = 0;
	}

}

/// Возвращает список доступных подключений
void CaplNetStepData::GetListConnections(CStrArr &list)
{
	CConnectInfo* el;
	CString str;
	list.Clear();
	for(int i=0; i <m_list_connection.GetSize(); i++)
	{
		el = m_list_connection.GetAt(i);
		if(el != 0) list.Add(new CString(el->name));
	}
}

// Внимание! При ошибке генерит исключение!
bool CaplNetStepData::ConnectWithParam(int dialog_mode)
{
	LogDebugMessageIfDef( _T("begin"));
	if(!m_AplTransport->m_need_relogin)
		NET_Disconnect();

	//	bool is_dialog = false;
	//	CString reg_user,reg_base;
	CString reg_last_conn;
	CConnectInfo* cif = 0;
	CaplDataBuf dBuf;
	CString name_connect_up, str2;
	int i=0;
	bool ret_code = true;
	bool is_dialog = false;

	// Читаем из ini список баз
	ReadConnectionsInfo();	
	// Читаем из реестра предыдущие логины-подключения
	THROW_IF((!ReadLastActive(m_reg_user_list, reg_last_conn, m_reg_domain_list)),APL_NET_CLI_ERROR_REESTR);

	// Проверка наличия указанного подключения в списке
	int num_connect = -1;
	if(!m_AplTransport->m_login_data.name_connect.IsEmpty())
	{
		name_connect_up = m_AplTransport->m_login_data.name_connect;
		name_connect_up.MakeUpper();

		for(i=0; i <m_list_connection.GetSize(); i++)
		{
			// Имя подключения
			cif = m_list_connection.GetAt(i);
			if(cif != 0)
			{
				str2 = cif->name;
				str2.MakeUpper();
				if(str2 == name_connect_up)
				{
					num_connect = i;
					break;
				}
			}
		}
	}
	if(m_AplTransport->m_need_relogin)
	{
		THROW_IF(m_hide_mode, APL_NET_CLI_MISSED_DLG_IN_HIDE_MODE);
		m_AplTransport->m_login_data.username = m_NameCurrUser;
		m_AplTransport->m_login_data.name_connect = m_NameCurrConnection;
		is_dialog = true;
	}
	// проверяем корректность входных параметров
	else 
	{
		if(dialog_mode==CONNECT_DLG_MODE__SHOW_ALVAYS)
		{
			THROW_IF(m_hide_mode, APL_NET_CLI_MISSED_DLG_IN_HIDE_MODE);
			is_dialog = true;
		}
		if(m_AplTransport->m_login_data.username.IsEmpty())
		{
			THROW_IF((dialog_mode == CONNECT_DLG_MODE__SUPRESS || m_hide_mode),APL_NET_CLI_MISSED_CONNECT_PARAM_USERNAME);
		}
		if(m_AplTransport->m_login_data.name_connect.IsEmpty() || num_connect==-1)
		{
			THROW_IF((dialog_mode == CONNECT_DLG_MODE__SUPRESS || m_hide_mode),APL_NET_CLI_MISSED_CONNECT_PARAM_CONNECTION);
			m_AplTransport->m_login_data.name_connect = reg_last_conn;
			is_dialog = true;
		}
	}

	CWnd* wnd = 0;
	//	wnd=AfxGetMainWnd();
	// 	BOOL is_vis = false;
	// 	if(wnd != 0){
	// 		is_vis= wnd->IsWindowVisible();
	// 		if(!is_vis){
	// 			wnd->ShowWindow(SW_SHOWMINIMIZED);
	// 		}
	// 	}

	// 	CString stttr;
	// 	stttr.Format("wnd 0x%08x",wnd);
	// 	LogDebugMessageIfDef(stttr);


	try
	{
		CString sOsUser, sOsDomain;
		aplGetOSUser(sOsUser, sOsDomain);

		if(sOsDomain.Left(1) == _T("."))
			sOsDomain.Delete(0, 1);

		if(m_AplTransport->m_login_data.isCurrDomainUser)
		{
			if(sOsDomain.IsEmpty())
			{
				CString errmess = APL_T("Программа запущена от учетной записи локального пользователя, а параметры функции требуют доменной аутентификации");
#ifdef _DEBUG
				m_MainLog.LogMessage(errmess);
#endif
				THROW_IF_EX((dialog_mode == APL_NET_CLI_MISSED_DLG_IN_HIDE_MODE || m_hide_mode),
					APL_NET_CLI_MISSED_DLG_IN_HIDE_MODE, errmess);

				m_AplTransport->m_login_data.isCurrDomainUser = false;
				is_dialog = true;
			}
			else if(m_AplTransport->m_login_data.isCurrDomainUser)
			{
				m_AplTransport->m_login_data.domain = sOsDomain;
				m_AplTransport->m_login_data.username = sOsUser;
			}
		}

		if(is_dialog)
		{
			// Переназначаем: ресурсы брать из DLL	
			CaplSetResourceHandle res_handle(module_inst);

			CDlgLogin dlg(wnd);
			int indx_c;
			bool login_readed = false;
			bool show_dlg = true;
			//Готовим данные для формы
			dlg.m_cur_select_connection = num_connect;
			dlg.m_Username = m_AplTransport->m_login_data.username;
			dlg.m_Pwd = _T("");// Пароль обнуляем перед выводом на экран
			dlg.m_Domain = m_NameDomain;
			dlg.m_data = this;
			dlg.m_cur_name = m_AplTransport->m_login_data.name_connect;// при перенастройке подключений будет использовано имя
			dlg.m_type_license = m_type_lic_current;
			dlg.m_sOsUser = sOsUser;
			dlg.m_sOsDomain = sOsDomain;
			dlg.m_bIsCurrDomainUser = m_AplTransport->m_login_data.isCurrDomainUser;

			int nResponse = 0;

			if(global_first_dlg_login)
			{
				global_first_dlg_login = false;
				login_readed = aplParamsR1(m_AplTransport->m_login_data.username, m_AplTransport->m_login_data.pwd, m_AplTransport->m_login_data.name_connect, m_AplTransport->m_login_data.domain);
				if(m_AplTransport->m_login_data.username == _T(""))
					login_readed = false;
				else
				{
					for(indx_c = 0; indx_c < m_list_connection.GetSize(); indx_c++)
					{
						if(m_list_connection.GetAt(indx_c)->name == m_AplTransport->m_login_data.name_connect)
						{
							num_connect = indx_c;
							break;
						}
					}
					if(indx_c == m_list_connection.GetSize())
						login_readed = false;
				}
				show_dlg = !login_readed;
				m_AplTransport->m_is_saved_login = login_readed;
			}
			if(show_dlg)
			{
				if(login_readed) 
					dlg.m_IsSaveLoginData = 1;

				LogDebugMessageIfDef( _T("CaplNetStepData::ConnectWithParam DoModal"));
				nResponse = dlg.DoModal();

				if(dlg.m_bExitAfterClose)
				{
					//PostQuitMessage(0);
					return false;
				}

				THROW_IF_EX(IDCANCEL == nResponse, APL_NET_OK, APL_T("Пользователь отменил операцию. "));

				m_AplTransport->m_login_data.username = dlg.m_Username;
				m_AplTransport->m_login_data.domain = dlg.m_Domain;
				m_AplTransport->m_login_data.pwd = dlg.m_Pwd;
				num_connect = dlg.m_cur_select_connection;
				if(!sOsDomain.IsEmpty())
					m_AplTransport->m_login_data.isCurrDomainUser = dlg.m_bIsCurrDomainUser==TRUE;

			}
			else
			{
				AppendString2List(m_reg_user_list, m_AplTransport->m_login_data.username);
			}
			// m_type_lic_current = dlg.m_type_license; - нонсенс
			cif = m_list_connection.GetAt(num_connect);

			if(show_dlg && cif != 0)
			{
				if(dlg.m_IsSaveLoginData == 1)
				{
					login_readed = aplParamsW1(dlg.m_Username, dlg.m_Pwd, cif->name, dlg.m_Domain, dlg.m_NumDays);
				}
				else
				{
					CString tmp = _T("");
					aplParamsW1(tmp, tmp, tmp, tmp, 1);
				}
			}
		}

		THROW_IF_EX(cif == 0,APL_NET_CLI_ERROR_PROCESSED, APL_T("Не найден описатель соединения"));
		m_AplTransport->m_login_data.name_connect = cif->name;

		m_AplTransport->m_login_data.sOsUser = sOsUser;
		if(sOsDomain != _T("")){m_AplTransport->m_login_data.sOsUser += _T("@");m_AplTransport->m_login_data.sOsUser += sOsDomain; }

		// Сохранение в реестре
		WriteLastActive(m_reg_user_list, cif->name, m_reg_domain_list);

		THROW_IF(!ConnectWithTestLocal(cif, true, is_dialog),APL_SOCK_ERROR_PROCESSED);
		m_AplTransport->m_login_data.name_base = cif->base_name;
		m_Protocol = cif->protocol;
	}
	catch(SaplErrorDescription error)
	{
		ret_code = false;
		m_AplTransport->m_login_data.name_base = _T("");
		// ради этого SetResource этот обработчик исключения и есть. 
		// Установка ошибки в вышестоящем обработчике
		//SetAPLErrorWithFileInfo(error.m_err_code); // это делается вызывающей функцией
		throw(error);
	} 
	CATH_MEMORY_ANY_EXEPTION;

	if(m_AplTransport != 0){m_AplTransport->ClearInternalData();}
	// 
	LogDebugMessageIfDef( _T("CaplNetStepData::ConnectWithParam end"));

	return ret_code;
}//bool CaplNetStepData::ConnectWithParam(CString &username, CString &pwd,CString &name_connect,CString &name_base,int dialog_mode){


bool CaplNetStepData::ConnectWithTestLocal(CConnectInfo* cif, bool foreground_main_wnd, bool bDlgMode /*= true*/)
{
	if(m_AutoStartSrv)
		return m_AplTransport->ConnectWithTestLocal(cif, m_LocalServerPath, foreground_main_wnd, bDlgMode);
	return m_AplTransport->ConnectWithTestLocal(cif, _T(""), foreground_main_wnd);
}

int CaplNetStepData::NET_TuningConnections(int last_selected)
{
	CaplStackLogger logger(__APL_FUNC__);


	ResetTimer(0);
	CaplSetResourceHandle res_handle(module_inst);
	int iSelConnection=-1;
	CDlgTuneListConnections dlg;

	CHECK_STATE(false, false, -1);
	try{
		dlg.m_data = this;
		dlg.m_last_selected = last_selected;
		int res = dlg.DoModal();

		if(dlg.m_bExitAfterClose) return -2; // Программу запустили от имени фдминистратора. Эту копию надо закрыть

		if(res == IDOK)
		{
			iSelConnection=dlg.m_last_selected;

			// Читаем заново лист контактов
			ReadConnectionsInfo();
		
			if(dlg.m_ClearUserList)
			{
				m_reg_user_list.RemoveAll();
				m_reg_domain_list.RemoveAll();
			}
		}
	}
	CATH_MEMORY_ANY_EXEPTION;
	return iSelConnection;
}

bool CaplNetStepData::InitAppName()
{
	// определяем имя для ini файла (если оно будет без путя - 
	// система возьмет его из каталога Windows)
	if(m_Ini.GetRealIniName().IsEmpty())
		m_Ini.SetINI(DEF_INI_FILENAME);
	if(globalIniName.IsEmpty())
		globalIniName = m_Ini.GetRealIniName();

	if(m_AppName == _T(""))
	{
		CString exe_name, client_info;
		CString tmp;
		bool goto_d_s = false;

		// определяем путь к исполняемому файлу, его имя, текущий каталог
		int pos_sep;

		aplGetModuleFileName(m_AppName);

		pos_sep = m_AppName.ReverseFind(_T('\\'));
		m_PathSetting = m_AppName.Left(pos_sep + 1);
		exe_name = m_AppName.Mid(pos_sep + 1);

		// читаем из ini путь к клиентским настройкам. Если он не задан - берем каталог откуда запущен exe
		m_PathSetting = m_Ini.GetString( _T("Paths"), _T("ClientSettingsPath"), m_PathSetting);
		if(m_PathSetting.Right(1) != _T("\\"))m_PathSetting += _T("\\");

		client_info= APL_T("Запущено приложение: ") + m_AppName;

		// пытаемся создать указанный путь - если его конечно еще нет
		int err_code = SHCreateDirectoryEx(NULL, m_PathSetting, NULL);
		if(err_code != ERROR_SUCCESS && err_code != ERROR_FILE_EXISTS && err_code != ERROR_ALREADY_EXISTS)
		{
			// каталог создать не могем... Скорее всего, нет доступа. Переходим в Documents and Settings
			goto_d_s = true;
		}
		else
		{
			// проверяем, можно ли создать лог в указанном каталоге
			tmp.Format(m_PathSetting + _T("~%i.tmp"), GetTickCount());

			HANDLE tmp_handle = CreateFile(tmp, 0, 0, 0, CREATE_ALWAYS,FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, 0);
			
			if(tmp_handle==INVALID_HANDLE_VALUE)
			{
				// файл создать не могем... Скорее всего, нет доступа. Переходим в Documents and Settings
				goto_d_s = true;
			}
			else
			{
				CloseHandle(tmp_handle);
			}
		}
		

		if(goto_d_s)
		{
			// лог надо выносить в каталог "c:\Documents and Settings\[username]\Application Data\" 
			// так как в катлоге, определенном ранее мы не можем создать файл
			TCHAR buffer[MAX_PATH];

			HRESULT hr = ::SHGetFolderPath(NULL, CSIDL_APPDATA,  NULL, SHGFP_TYPE_CURRENT, &buffer[0] );

			if(SUCCEEDED(hr))
			{
				m_PathSetting = buffer;
				m_PathSetting += _T("\\PSS\\");
			}
			else
			{
				hr = ::SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, &buffer[0] );
				if(SUCCEEDED(hr))
				{
					m_PathSetting = buffer;
					m_PathSetting += _T("\\PSS\\");
				}
// 				else
// 				{
// 					CString buf;
// 					GetTempPath(256,buffer);
// 					hr=(HRESULT)1;
// 				}
			}
		}

		//m_MainLog.m_isAlwaysLogStart = true;
		m_MainLog.SetFileName(m_PathSetting + exe_name + _T(".log"), client_info);
		m_MainLog.m_MaxLogSize = m_Ini.GetInt( _T("Options"), _T("ClientLogMaxSize"), 5);
		m_MainLog.m_MaxLogSize *=1024*1024; 
		//m_MainLog.m_isLogTick = true;
		SetModuleLog(&m_MainLog);
	}
	return true;
}

bool GetVersionClient(CString *version)
{
	LogDebugMessageIfDefZero( _T("GetVersionClient"));

	CString ininame;
	CString buf, buf1, ver_sys = _T("?");

	if(m_VerCli == _T(""))
	{
		int num_product = 3; // 0 - pss, 1 - ils, 2 - lss, 3 - pm_ils
		CaplINI ini;

		do{

			CString ininame = _T("");
			// Чтение версии
			//?? //NSIS aplGetModuleFilePath(ininame);

			if(num_product==0)
			{	ininame = _T("pss.ver");}
			else if(num_product==1)
			{	ininame += _T("ils.ver");}
			else if(num_product==2)
			{	ininame += _T("lsa.ver");}
			else
			{	ininame += _T("pm_ils.ver");}

			ini.SetINI(ininame);

			if(ini.isFileExist())
			{
				if(num_product==0)
				{
					ver_sys = ini.GetString(_T("version"), _T("pssver"), _T("?"));
				}
				else if(num_product==1 || num_product==2 || num_product==3)
				{
					ver_sys = ini.GetString(_T("version"), _T("lsaver"), _T("?"));
				}
			}
			if(ver_sys == _T("?") && num_product > 0)
			{
				num_product--;
				continue;
			}
			else
			{
				break;
			}

		}while(true);

		// Внимание! При изменении формата версии согласовывать с сервером: CAplTransport::ProcessedConnectDBCheckSelectCreate
		CString str;
		str = ini.GetString(_T("version"), _T("client_app_name"), _T(""));

		if(num_product==0)
			m_VerCli = _T("PSS ");
		else if(num_product==1)
			m_VerCli = _T("ILS ");
		else if(num_product==2)
			m_VerCli = _T("LSA ");
		else if(num_product==3)
			m_VerCli = APL_T("ПМ ИЛП ");

		if(str != _T(""))
		{
			m_VerCli += _T("(") + str + _T(")");
		}
		
		if(ver_sys != _T("?"))
			m_VerCli += _T("(") + ver_sys + _T(")");

		// Чтение версии из инсталятора
		if(ver_sys == _T("?"))
		{
			HKEY inst_key = 0;

			LSTATUS status;

			if(num_product==0)
			{
				status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
					_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\PDM STEP Suite"), 0, KEY_QUERY_VALUE, &inst_key);

				if(ERROR_SUCCESS != status)
				{
					status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
						APL_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\PDM STEP Suite ver. ГПИХ"), 0, KEY_QUERY_VALUE, &inst_key);
				}
			}
			else if(num_product==1)
			{
				status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
					_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ILS Suite"), 0, KEY_QUERY_VALUE, &inst_key);
			}
			else if(num_product==2)
			{
				status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
					_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\LSA Suite"), 0, KEY_QUERY_VALUE, &inst_key);
			}

			if(ERROR_SUCCESS == status)
			{
				TCHAR *ver_buf = new TCHAR[1024];
				ver_buf[0] = _T('\0');
				DWORD type_ver_buf = REG_SZ;
				DWORD len_ver_buf = 1023;
				if(ERROR_SUCCESS == RegQueryValueEx(inst_key, _T("DisplayVersion"), 0,
					&type_ver_buf, (LPBYTE)ver_buf, &len_ver_buf))
				{
					if((ver_sys == _T("?")) || ( ver_sys != ver_buf))
					{
						m_VerCli += _T("  ("); m_VerCli += (TCHAR*)ver_buf; m_VerCli += _T(")");
					}
				}
				delete[] ver_buf;
				RegCloseKey(inst_key);
			}
		}

		// Добавление даты компиляции
		m_VerCli += _T(" [");
		m_VerCli += _T(__DATE__);
		m_VerCli += _T("]");

#ifdef _UNICODE
#ifdef _DEBUG
		m_VerCli += _T(" UD");
#else
		m_VerCli += _T(" U");
#endif
#else // #ifdef _UNICODE
#ifdef _DEBUG
		m_VerCli += _T(" D");
#endif
#endif // #ifdef _UNICODE

	}
	if(version != 0){*version = m_VerCli;}

	return true;
}

/// имя приложения, из которого создан объект CaplNetStepData
CString GetAppName(){return m_AppName;}

/// Имя ini файла (с путем), из которого читаются настройки.
/// Заполняется при первом создании объекта CaplNetStepData
CString GetIniFullName(){return globalIniName;}

/// имя каталога, в котором хранятся настройки приложений (путь включает завершающюю косую черту)
/// путь к каталогу хранится в ini (группа Paths параметр ClientSettingsPath), по 
/// умолчанию - каталог из которого был запущен exe
CString GetPathSetting(){return m_PathSetting;}

/// читает параметр из ini файла, из которого читаются настройки
bool CaplNetStepData::GetIniParameter(LPCTSTR group_name, LPCTSTR key_name, CString &value, bool isExpandRelatedPath)
{
	bool retval = m_Ini.GetString(group_name, key_name, value, _T(""));
	if(isExpandRelatedPath){ value = m_Ini.ExpandPath(value);}
	return retval;
}

/// читает параметр из ini файла, из которого читаются настройки
int CaplNetStepData::GetIniParameter(LPCTSTR group_name, LPCTSTR key_name)
{
	CString value;
	int ival;
	m_Ini.GetString(group_name, key_name, value, _T(""));
	ival = _atoi(value);
	return ival;
}

/// записывает параметр в ini файл, из которого читаются настройки
bool CaplNetStepData::WriteIniParameter(LPCTSTR group_name, LPCTSTR key_name, LPCTSTR value)
{
	return m_Ini.WriteString(group_name, key_name, value);
}

bool CaplNetStepData::GetVersionServer(CString &version, CString &type, long *ServerAUSessionId, CString *ClassSrv)
{
	version = m_VerSrv;
	type = m_TypeSrv;
	if(ServerAUSessionId != 0){*ServerAUSessionId = m_ServerAUSessionId;}
	if(ClassSrv!=0){*ClassSrv = m_ClassSrv;}
	return true;
}

/** Если соединение с сервером разорвано -  востанавливает соединение. В режиме HideMode==true возвращает ошибку
@param asked если true - выводит запрос о необходимости восстановленимя соединения. 
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::Reconnect(bool /*asked*/)
{
	if(!m_AplTransport->IsConnected())
	{
		return m_AplTransport->Reconnect(true,_T(__FILE__),__LINE__,_T(__DATE__));
	}
	return true;
}

void CaplNetStepData::NET_LIC_SetSeansParams(UINT32 s1, UINT32 s2)
{
	if(s1==0 && s2 != 0){s1 = s2; s2 = 0;}
	m_AplTransport->m_login_data.s1 = s1;
	m_AplTransport->m_login_data.s2 = s2;
}




bool CaplNetStepData::NET_Connect(LPCTSTR _username, LPCTSTR _pwd, LPCTSTR _name_connect, DWORD connection_type,
				 LPCTSTR wait_text, bool supress_dlg, bool load_titul_bd, LPCTSTR _domain, bool bIsCurrDomainUser)
{
	CString license =_T("");
	if(connection_type == apl_LT_DEF_TYPE) license	= _T("PSS");
	else if(connection_type == apl_LT_TYPE_1) license	= _T("ILS");
	else if(connection_type == apl_LT_TYPE_X) license	= SPEC_LIC_TYPE;
	else
	{
		SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, _T("Указан неверный числовой тип лицензии."));
	}
	return NET_Connect(_username, _pwd, _name_connect, license, wait_text, supress_dlg, false, _domain, bIsCurrDomainUser);
}

bool CaplNetStepData::NET_Connect(LPCTSTR _username, LPCTSTR _pwd, LPCTSTR _name_connect,
				LPCTSTR license, LPCTSTR wait_text, bool supress_dlg, bool /*load_titul_bd*/, LPCTSTR _domain, bool bIsCurrDomainUser)
{
	CaplStackLogger stack_logger(__APL_FUNC__);

	LogDebugMessageIfDef( APL_T("Вход в NET_Connect"));
	//	AfxMessageBox("NET_Connect");
	CWnd* main_cwnd = 0;
	bool is_wc = false;
	bool ret_val = false;

	CHECK_STATE(false, false, false);


	CString buf;
	int command;

	AplNetErrors aplError = APL_NET_OK;
	bool prev_NetErrorMessageMode = m_NetErrorMessageMode;

	m_num_load = 0;
	m_num_save = 0;
	m_num_find = 0;
	m_num_query = 0;
	m_num_find_local_cache = 0;
	m_num_run_sql = 0;
	m_num_run_sql_re = 0;
	m_count_admin = 0;

	try
	{
		if(m_hwnd== 0)
		{
			main_cwnd = AfxGetMainWnd();
			m_hwnd = (main_cwnd == 0 ? 0 : main_cwnd->m_hWnd);
		}
		/**/
		if(m_AplTransport == 0)return false;
		if(m_IsConnected && !m_AplTransport->m_need_relogin)
		{
			if(m_AplTransport->IsConnected())
			{
				if(m_hide_mode)
				{
					return true;
					//command=IDYES;
				}
				else
				{
					command = MessageBox(m_hwnd, APL_T("Соединение уже установлено. Разорвать соединение?"),
						 APL_T("Служба RPC PDM Step Suite"), MB_YESNO | MB_ICONQUESTION);
				}
				if(command == IDYES)
				{
					NET_Disconnect();
				}
				else
				{
					//SetAPLErrorWithFileInfo(APL_NET_CLI_ALREADY_CONNECT);
					return true;		
				}
			}
			else
			{
				// все равно физической связи с сервером нет. Разрываем и логическую
				NET_Disconnect();
			}

		}

		m_AplTransport->m_login_data.username = _username;
		m_AplTransport->m_login_data.pwd = _pwd;
		m_AplTransport->m_login_data.name_connect = _name_connect;
		m_AplTransport->m_login_data.domain = _domain;
		m_AplTransport->m_login_data.isCurrDomainUser = bIsCurrDomainUser;

		/**/
		int len_usrn = m_AplTransport->m_login_data.username.GetLength();
		int len_con	= m_AplTransport->m_login_data.name_connect.GetLength();
		int len_pwd	= m_AplTransport->m_login_data.pwd.GetLength();
		THROW_IF_EX((len_usrn > MAX_STR_LEN ) || (len_pwd > MAX_STR_LEN ) || (len_con > MAX_STR_LEN),
			APL_NET_CLI_MISSED_DATA, APL_T("Слишком длинные входные параметры!"));

		DWORD res;
		UINT32 idCurrUser;

		if(!m_AplTransport->m_need_relogin)
			ClearData();

		m_IsConnected = false;
		m_admin_mode = false;
		int dialog_mode = CONNECT_DLG_MODE__ONLY_ERROR;
		if(supress_dlg)
		{
			dialog_mode = CONNECT_DLG_MODE__SUPRESS;
		}
		m_type_lic_current = license;
		if(m_type_lic_current.IsEmpty())
		{
 			if(globalRequestedMainLicense.IsEmpty())
				m_type_lic_current = _T("PSS");
 			else
 				m_type_lic_current = globalRequestedMainLicense;
		}

		do
		{
			// Обрабатываем входные параметры и уточняем их, если надо
			// так же читаем из списка соединений адрес и порт сервера и устанавливаем соединение TCP
			if(!ConnectWithParam(dialog_mode))
			{
				// Диалог ввода имени/пароля пользователя завершился неудачно
				THROW_IF(true, GetLastAplError());
			}

			if(!m_AplTransport->AT_DCQueryServerVersion(aplError, &m_VerSrv, &m_TypeSrv, &m_ClassSrv, &m_BuildSrv))
			{
				// сервер не знает такой функции. Тип определим по кодировке
				if(m_AplTransport->m_aplSocket != 0)
				{
					if(m_AplTransport->m_aplSocket->m_ServerAnsi)m_ClassSrv = _T("MSVCx32");
					else m_ClassSrv = _T("QT_"); // Больше по кодировке ничео сказать нельзя

					// До подключения сервер после неизвестных функций разрывает сокет. Играем на опережение
					m_AplTransport->DisconnectFull();

					// так же читаем из списка соединений адрес и порт сервера и устанавливаем соединение TCP
					if(!ConnectWithParam(CONNECT_DLG_MODE__ONLY_ERROR))
					{
						// Диалог ввода имени/пароля пользователя завершился неудачно
						THROW_IF(true, GetLastAplError());
					}
				}
				aplError = APL_NET_OK; // чтобы не ломать дальнейшую обработку
			}
			else
			{
				m_AplTransport->ClearInternalData();
				if(m_BuildSrv.IsEmpty())
					m_is_correct_d_branch = false;
				else
					m_is_correct_d_branch = (m_BuildSrv > _T("20250804000000"));

			}

			THROW_IF_EX(m_AplTransport->m_login_data.name_base.IsEmpty(),
					APL_NET_CLI_MISSED_DATA, APL_T("В подключении не задано имя БД!"));

			WriteTickLog(_T("Connect - start"));
			if(main_cwnd != 0){main_cwnd->BeginWaitCursor(); is_wc = true;}

			aplError = APL_NET_OK;
			if(!m_hide_mode && !supress_dlg  && m_IsWaitDlg)
			{
				if(wait_text == 0){m_AplTransport->SetWaitDlgText( APL_T("Идет регистрация в БД"));}
				else{m_AplTransport->SetWaitDlgText(wait_text);}
			}

			WriteTickLog(_T("Test Versions 1"));
			CaplDataBuf test_ver_buf;
			int code, size;
			CStringA cs_buf;

			//code=std_databuf;test_ver_buf.Add(&code,4);cs_buf=std_databuf_header;size=cs_buf.GetLength()+1;test_ver_buf.Add(&size,4);test_ver_buf.Add(cs_buf.GetBufferSetLength(size),size);
			FILL_SERVICE_TEST(std_databuf);
			FILL_SERVICE_TEST(std_databuf_ret);
			FILL_SERVICE_TEST(std_access_list);
			//FILL_SERVICE_TEST(std_test_find);
			FILL_SERVICE_TEST(std_LoadInstUserDataTimeInfoQuery);
			FILL_SERVICE_TEST(std_LoadInstUserDataTimeInfoData);
			//FILL_SERVICE_TEST(std_LoadUserLogInfo); - буфер проверяется в самой функции
			FILL_SERVICE_TEST(std_LoadBDParam);
			FILL_SERVICE_TEST(std_ClientSQLQuery);
			FILL_SERVICE_TEST(std_ClientSQLQueryEx);
			FILL_SERVICE_TEST(std_ClientSQLResult);
			FILL_SERVICE_TEST(std_ExtendedBuf);
			//FILL_SERVICE_TEST(std_QueryUsersLogFilter); - буфер проверяется в самой функции
			FILL_SERVICE_TEST(std_DCServisAccessPatterns);
			FILL_SERVICE_TEST(ACCESS_BUF_SET);
			FILL_SERVICE_TEST(ACCESS_BUF_GET);
			FILL_SERVICE_TEST(LOAD_QUERY_BUF);
			FILL_SERVICE_TEST(CLIENT2SERVER_LOG_BUF);
			FILL_SERVICE_TEST(ACCESS_BUF_BI);
			FILL_SERVICE_TEST(std_DCAttrsHistory);
			FILL_SERVICE_TEST(std_DCAttrs4Blob);
			FILL_SERVICE_TEST(ACCESS_BUF_BI);

			if(!m_AplTransport->AT_DCServiceTestVersions(aplError,test_ver_buf) && aplError==APL_NET_SRV_MISSED_BUF_FORMAT)
			{
				if(main_cwnd != 0 && is_wc){main_cwnd->EndWaitCursor(); is_wc = false;}

				SetAPLErrorWithFileInfoEx(aplError, APL_T("Ошибка при проверки версии"));
				if(!m_hide_mode && !supress_dlg)
				{
					m_AplTransport->DisconnectFull(); // вызываем перед диалогом, иначе рассоединение tcp по тайм-ауту нас зациклит

					MessageBox(m_hwnd,
						 APL_T("Версия сервера устарела. Обновите сервер приложений \n или обратитесь к системному администратору"),
						 APL_T("Служба RPC PDM Step Suite"),
						MB_OK | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
				}
				aplError = APL_NET_SRV_NOT_CONNECT;
				THROW_CHECK(aplError);
			}
			THROW_CHECK(aplError);
			m_AplTransport->ClearInternalData();

			// Отдельная проверка - поддерживает ли сервер приложений дополнительнные лицензии.
			// Без них работать будем, но с ограниченным функционалом
			m_AplTransport->m_IsServer4AddLic = false;

			/* Неожиданно оказалось, что AT_DCServiceTestVersions с 2011 года работала некорректно :-(
				Придется использовать костыль. См ниже вызов AT_DCLicOperate. 
				Этот код закомментарен чтобы в лог сервера не сыпались ненужные записи. 
			test_ver_buf.Clear();
			FILL_SERVICE_TEST(std_LicOperate);
			if(!m_AplTransport->AT_DCServiceTestVersions(aplError,test_ver_buf))
			{
				if(main_cwnd != 0 && is_wc){main_cwnd->EndWaitCursor(); is_wc = false;}
				if(aplError==APL_NET_SRV_MISSED_BUF_FORMAT)
					m_AplTransport->m_IsServer4AddLic = false;
				else
					THROW_CHECK(aplError);
			}
			else if(aplError == APL_NET_OK)
				m_AplTransport->m_IsServer4AddLic = true;
			m_AplTransport->ClearInternalData();
			 */

			CString base_pwd(m_AplTransport->m_login_data.pwd);
			WriteTickLog(_T("m_AplTransport->AT_DCConnectEx"));
			
			if(m_AplTransport->m_login_data.isCurrDomainUser)//if(bIsKerberos)
			{
				if(!m_AplTransport->DC_AuthExternal(m_AplTransport->m_login_data.username, 
								m_AplTransport->m_login_data.pwd, m_AplTransport->m_login_data.domain))
				{
					aplError = APL_NET_SRV_NOT_CONNECT;
					MY_THROW((AplNetErrors) aplError);
				}
				m_AplTransport->m_login_data.pwd = _T(""); // при работе с керберосом пароль, пришедший в функцию, смысла не имеет
			}
			else
			{
				// Если первый и последний символ галочки и длина больше 35 - это явно временный пароль, его шифровать не надо
				if(!(m_AplTransport->m_login_data.pwd[0]==_T('^') && 
					m_AplTransport->m_login_data.pwd.GetLength()>35 && 
					m_AplTransport->m_login_data.pwd.Right(1)==_T("^")))
				{
					// Шифруем пароль
#ifdef _UNICODE
					if(m_global_ansi_string)
					{
						if(!m_hide_mode)
							AfxMessageBox( ERR_MESS_SERVER_A_CLIENT_U_DEBUG , MB_OK | MB_ICONINFORMATION);
// 						else
// 							APL_THROW_EX(APLAPIERR_INCOMPATIBLDATAFORMAT,CString( ERR_MESS_SERVER_A_CLIENT_U));

					}
					if(m_Ini.GetInt(_T("Options"), _T("EncodeUnicodePassword"), 0)==1)
					{
						aplTranslateK(m_AplTransport->m_login_data.pwd, buf);
						m_AplTransport->m_login_data.pwd = buf;
					}

#else // #ifdef _UNICODE
					if(m_global_ansi_string || m_Ini.GetInt(_T("Options"), _T("EncodeUnicodePassword"), 0)==1)
					{
						aplTranslateK(m_AplTransport->m_login_data.pwd, buf);
						m_AplTransport->m_login_data.pwd = buf;
					}
#endif // #ifdef _UNICODE				
				}

			}

			m_AplTransport->m_login_data.type_license = m_type_lic_current;

			m_AplTransport->AT_DCConnectEx(aplError, &idCurrUser, 
					&m_VerSrv, &m_TypeSrv, &m_ServerAUSessionId, &m_can_find_in_blob, &m_ClassSrv);

			if(aplError==APL_NET_SRV_MISSED_DATA && (m_VerSrv.Find(_T("(Qt "))!=-1 || m_VerSrv.Find(_T(" Qt "))!=-1) && 
				m_AplTransport->m_description_serv_error.Find(APL_T("Закодированные")) != -1)
			{
				// Qtшный сервер. Запрашиваем его метод хэширования паролей
				m_AplTransport->AT_DCServiceGetAuthParam(aplError, APC_GetHashMethod, m_ServerHashMethod);
				if(aplError==APL_NET_OK)
				{
					if(m_ServerHashMethod.IsEmpty())
						m_AplTransport->m_login_data.pwd = base_pwd;
					else if(!internalCryptGetHash(m_ServerHashMethod, base_pwd, m_AplTransport->m_login_data.pwd, buf))
					{
						aplError = APL_NET_CLI_ERROR_PROCESSED;
						MY_THROW_EX((AplNetErrors) aplError, CString(APL_T("При вычислении хэша пароля возникла ошибка: ")) + buf);
					}
					m_AplTransport->AT_DCConnectEx(aplError, &idCurrUser, 
						&m_VerSrv, &m_TypeSrv, &m_ServerAUSessionId, &m_can_find_in_blob, &m_ClassSrv);
				}
			}
			// сервер не прислал свой класс. Определим его по кодировке
			if(m_ClassSrv.IsEmpty() && m_AplTransport->m_aplSocket != 0)
			{
				if(m_AplTransport->m_aplSocket->m_ServerAnsi)m_ClassSrv = _T("MSVCx32");
				else m_ClassSrv = _T("QT_"); // Больше по кодировке ничео сказать нельзя
			}

			if(aplError==APL_NET_OK)
			{
				break;
			}
			if(main_cwnd != 0 && is_wc){ main_cwnd->EndWaitCursor(); is_wc = false;}
			//			if(m_hide_mode  /*&& !m_NetErrorMessageMode*/){
			//				THROW_IF(true,apl_error);
			//			}

			if(aplError == APL_NET_CLI_SERVER_DN_SUPPORT_FUNCTION)
			{
				m_AplErrorDescription = ERR_MESS_MISSED_LIC_SERVER_FIRST;
				m_AplErrorDescription += ERR_MESS_MISSED_LIC_SERVER_RENEW;
				MY_THROW_EX(aplError, m_AplErrorDescription);
			}
			if(aplError == APL_NET_SRV_USER_READ_ONLY)
			{
				if(m_hide_mode || supress_dlg)
				{
					m_is_readonly = true;
					m_LastAplErrorCode = aplError;
					m_ErrorDescription += m_AplTransport->GetDescriptionLastError();
					if(m_WarningPrintMode)aplMessagePrint(m_ErrorDescription, MB_ICONWARNING);
					break;
				}
				res = MessageBox(m_hwnd,
					 APL_T("Ваш доступ к БД ограничен чтением.\n Перейти в режим \"Только чтение\"?"),
					 APL_T("Служба RPC PDM Step Suite"),
					MB_YESNO | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
				if(res == IDYES)
				{
					m_is_readonly = true;
					m_LastAplErrorCode = aplError;
					m_ErrorDescription += m_AplTransport->GetDescriptionLastError();
					break;
				}
				else
				{
					aplError = APL_NET_SRV_NOT_CONNECT;
				}
			}
			if(aplError == APL_NET_SRV_SINGLE_CONNECT_DB)
			{
				buf = APL_T("Не найден сервер лицензий или нет свободных \n лицензий. Работа возможна только в режиме \n эксклюзивного доступа.");
				if(m_hide_mode || supress_dlg)
				{
					if(m_WarningPrintMode)aplMessagePrint(buf, MB_ICONWARNING);
					break;
				}
				else
				{
					res=MessageBox(m_hwnd,
						buf + APL_T("\n Перейти в режим эксклюзивного доступа?"),
						APL_T("Служба RPC PDM Step Suite"),
						MB_YESNO | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
					if(res == IDYES)break;
					aplError = APL_NET_SRV_LIMIT_CONNECT_EXESS;
				}
			}
			if(aplError == APL_NET_SRV_MISSED_KEY_IN_SRV_LIC)
			{
				if(m_VerSrv.Find(_T("(Qt "))!=-1 || m_VerSrv.Find(_T(" Qt "))!=-1)
				{
					buf = APL_T(" Лицензии указанного типа не заданы. Работа \n возможна только в демонстрационном режиме.");
				}
				else
				{
					buf = APL_T("Сервер лицензий не нашел аппаратный ключ.\nРабота возможна только в режиме эксклюзивного\nдоступа.");
				}
				if(m_hide_mode || supress_dlg)
				{
					if(m_WarningPrintMode)aplMessagePrint(buf, MB_ICONWARNING);
					break;
				}
				else
				{
					if(m_VerSrv.Find(_T("(Qt "))!=-1 || m_VerSrv.Find(_T(" Qt "))!=-1)
					{
						res = AfxMessageBox(
							buf + APL_T("\n Перейти в демонстрационный режим?"),
							MB_YESNO | MB_ICONEXCLAMATION);
					}
					else
					{
						res = AfxMessageBox(
							buf + APL_T("Перейти в режим эксклюзивного доступа?"),
							MB_YESNO | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
					}
					if(res == IDYES)break;
					aplError = APL_NET_SRV_LIMIT_CONNECT_EXESS;
				}
			}
			if(aplError == APL_NET_SRV_SINGLE_CONNECT_LITE_D)
			{
				m_NetErrorMessageMode = false;
				buf = APL_T("Сервер приложений не нашел аппаратный ключ.\n Работа идет в режиме эксклюзивного доступа. \n После установки ключа перезапустите сервер.");
				if(m_hide_mode || supress_dlg)
				{
					if(m_WarningPrintMode)aplMessagePrint(buf, MB_ICONWARNING);
					break;
				}
				else
				{
					MessageBox(m_hwnd,
						buf,
						APL_T("Служба RPC PDM Step Suite"),
						MB_OK | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
					break;
				}
			}
			if(aplError == APL_NET_SRV_APPROACH_EXPIRE_SRC_LIC_SRV)
			{
				m_NetErrorMessageMode = false;
				buf = APL_T("Сообщение с сервера лицензий: до окончания \n срока действия лицензии осталось менее 30 дней! \n Срочно обновите лицензию!\n");
				if(m_hide_mode || supress_dlg)
				{
					if(m_WarningPrintMode)aplMessagePrint(buf, MB_ICONWARNING);
					break;
				}
				else
				{
					MessageBox(m_hwnd,
						buf,
						APL_T("Служба RPC PDM Step Suite"),
						MB_OK | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
				}
				break;
			}
			if(aplError == APL_NET_SRV_EXPIRE_KEY_IN_SRV_LIC_EXCLUSIVE)
			{
				m_NetErrorMessageMode = false;
				res=IDYES;
				buf = APL_T("Сообщение с сервера лицензий: нет лицензий \n запрошенного типа. Проверьте сроки действия \n ключей и ключевых файлов.");
				if(m_hide_mode || supress_dlg)
				{
					if(m_WarningPrintMode)aplMessagePrint(buf, MB_ICONWARNING);
					break;
				}
				else
				{
					res=MessageBox(m_hwnd,
						buf + APL_T("\n Перейти в режим эксклюзивного доступа?"),
						APL_T("Служба RPC PDM Step Suite"),
						MB_YESNO | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
				}
				if(res==IDYES)break;
			}
			if(aplError==APL_NET_SRV_EXPIRE_SRC_LIC_D)
			{
				m_NetErrorMessageMode = false;
				buf = APL_T("Работа идет в режиме эксклюзивного доступа. \n Некоторые функции могут быть недоступны!");
				if(m_hide_mode || supress_dlg)
				{
					if(m_WarningPrintMode)aplMessagePrint(buf, MB_ICONWARNING);
					break;
				}
				else
				{
					MessageBox(m_hwnd,
						buf,
						APL_T("Служба RPC PDM Step Suite"),
						MB_OK | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
				}
				break;
			}

			THROW_IF(m_hide_mode || supress_dlg,aplError);

			if(aplError == APL_NET_SRV_MISSED_NAME_BASE)
			{
				buf.Format(APL_T("Указанная в подключении %s База Данных с\n именем %s недоступна через текущий сервер \n приложений."),
					m_AplTransport->m_login_data.name_connect, m_AplTransport->m_login_data.name_base);
				buf += APL_T("  Проверьте тип сервера приложений \n или настройте БД в параметрах подключения\n");
				if(supress_dlg)
				{
					m_NetErrorMessageMode = false;
					MY_THROW_EX(aplError, buf);
				}
				m_AplTransport->DisconnectFull(); // вызываем перед диалогом, иначе рассоединение tcp по тайм-ауту нас зациклит

				MessageBox(m_hwnd, buf, APL_T("Служба RPC PDM Step Suite"),
					MB_OK | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONERROR);
				dialog_mode = CONNECT_DLG_MODE__SHOW_ALVAYS;
				continue;
			}
			if(aplError == APL_NET_SRV_UNKNOWN_USER)
			{
				if(supress_dlg)
				{
					m_NetErrorMessageMode = false;
					MY_THROW(aplError);
				}
				// Сохраняем текст серверной ошибки
				CString sServerErrMess = m_AplTransport->m_description_serv_error;

				m_AplTransport->DisconnectFull(); // вызываем перед диалогом, иначе рассоединение tcp по тайм-ауту нас зациклит

				MessageBox(m_hwnd, APL_NET_ERROR2char(aplError, sServerErrMess), APL_T("Служба RPC PDM Step Suite"),
					MB_OK | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONERROR);
				dialog_mode = CONNECT_DLG_MODE__SHOW_ALVAYS;
				continue;
			}

			if(aplError == APL_NET_SRV_LIMIT_CONNECT_EXESS)
			{
				m_NetErrorMessageMode = false;
				if(m_hide_mode || supress_dlg)
				{
					MY_THROW(aplError);
				}
				else
				{
					m_AplTransport->DisconnectFull(); // вызываем перед диалогом, иначе рассоединение tcp по тайм-ауту нас зациклит

					MessageBox(m_hwnd,
						 APL_T("Все лицензии исчерпаны. Подключение невозможно.\n Попробуйте установить соединение позднее \n или обратитесь к системному администратору"),
						 APL_T("Служба RPC PDM Step Suite"),
						MB_OK | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
				}
				MY_THROW(aplError);
			}

			if(aplError == APL_NET_SRV_APLAPIERR_FILE_BD_READ_ONLY)
			{
				m_NetErrorMessageMode = false;
				if(m_hide_mode || supress_dlg)
				{
					MY_THROW(aplError);
				}
				else
				{
					res=MessageBox(m_hwnd,
						 APL_T("Один или несколько файлов БД защищены для записи.\n Работа возможна только в режиме \"Только чтение\".\n Перейти в режим \"Только чтение\"?"),
						 APL_T("Служба RPC PDM Step Suite"),
						MB_YESNO | MB_TOPMOST | MB_TASKMODAL | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
				}
				if(res==IDYES)
				{
					m_is_readonly = true;
					break;
				}
			}
			THROW_CHECK(aplError);

		}while(true);
		//local_transport=&(this->m_AplTransport);
		//дебаговое сообщение MessageBox(m_hwnd,"Соединились успешно","Служба RPC PDM Step Suite",MB_OK);

		m_NetErrorMessageMode = prev_NetErrorMessageMode;
		WriteTickLog(_T("SetRenew"));
		if(m_TypeSrv.Find(_T("ORA"))!=-1)// Postgre
		{
			SetMaxDoubleVal(9E125);
		}

		/**/
		m_MakeCopyAggr = true;// работаем через сеть - надо кешировать аггры
		m_IsConnected = true;

		if(m_AplTransport->m_aplSocket != 0)
		{
			m_AplTransport->m_aplSocket->SetRenewFunction(::RenewClientStateOnServer, (LPVOID)m_AplTransport);
		}

		if((m_type_lic_current != _T("PSS") && m_type_lic_current != _T("ILS")) || globalRequestedAddLicenses.GetSize() > 0)
		{
			// Костыль для определения старого сервера со старой системой лицензий
			CStringArray listIn, listOut;
			m_AplTransport->m_IsServer4AddLic = false;
			if(m_AplTransport->AT_DCLicOperate(aplError, LO_TestTake, listIn, listOut))
				if(aplError == APL_NET_OK)
					m_AplTransport->m_IsServer4AddLic = true;
			m_AplTransport->ClearInternalData();

			if(!m_AplTransport->m_IsServer4AddLic)
			{
				m_AplErrorDescription = ERR_MESS_MISSED_LIC_SERVER_FIRST;
				m_AplErrorDescription += APL_T("Часть функций будет недоступна.");
				if(!m_hide_mode )
				{
					res = MessageBox(m_hwnd, m_AplErrorDescription, APL_T("Служба RPC PDM Step Suite"), MB_OK | MB_ICONWARNING);
				}
				m_LastAplErrorCode = APL_NET_CLI_SERVER_DN_SUPPORT_FUNCTION;

// 				CString str = ERR_MESS_MISSED_LIC_SERVER_FIRST;
// 				str += ERR_MESS_MISSED_LIC_SERVER_RENEW;
// 				MY_THROW_EX(APL_NET_CLI_SERVER_DN_SUPPORT_FUNCTION, str);
			}
			else if(globalRequestedAddLicenses.GetSize() > 0)
			{
				if(!m_AplTransport->AT_DCLicOperate(aplError, LO_TestTake, globalRequestedAddLicenses, m_types_lic_adt) || aplError != APL_NET_OK)
				{
					if(aplError == APL_NET_OK)
						aplError = APL_NET_CLI_ERROR_PROCESSED;
					if(aplError != APL_NET_SRV_LIMIT_CONNECT_EXESS)
						SetAPLError(aplError, APL_T("Ошибка при получении дополнительных лицензий"));
				}
				m_AplTransport->ClearInternalData();
			}
		}

		// Исключаем из отображаемого имени базы пароль оракла (если он есть)
		int pos1 = 0,pos2 = 0;
		CString ounm = _T(""),opwd = _T(""), oals = _T(""), name_base(m_AplTransport->m_login_data.name_base);
		pos1 = name_base.Find(_T('/'), 0);
		if(pos1 != -1)
		{
			ounm = name_base.Left(pos1);
			pos2 = name_base.Find(_T('@'), pos1);
			if(pos2 == -1)
			{
				opwd = name_base.Right(name_base.GetLength()-pos1-1);
			}
			else
			{
				opwd = name_base.Mid(pos1+1, pos2-pos1-1);
				oals = name_base.Right(name_base.GetLength()-pos2-1);
			}
			m_NameCurrBase = ounm + _T("/*****");
			if(oals!=_T(""))m_NameCurrBase += _T("@")+oals;
		}
		else
		{
			m_NameCurrBase = name_base;
		}
		m_NameCurrUser = m_AplTransport->m_login_data.username;
		m_NameDomain = m_AplTransport->m_login_data.domain;
		m_NameCurrConnection = m_AplTransport->m_login_data.name_connect;
		if(m_AplTransport->m_need_relogin)
		{
			ret_val= true;
		}
		else
		{
			WriteTickLog(_T("NET_LoadDictionaryFromServer"));
			ret_val= NET_LoadDictionaryFromServer();

			WriteTickLog(_T("Fill attrs and entities"));

			m_ClearDataAfterError = false;
			m_IsLoadedTable4Entity = false;

			m_ent_apl_access_agent = GetEntityBN(_T("apl_access_agent"));
			m_ent_apl_user		= GetEntityBN(_T("apl_user"));
			m_ent_apl_workgroup	= GetEntityBN(_T("apl_workgroup"));

			m_attr_apl_user__name = GetAttrDefinition(m_ent_apl_user,_T("name"));
			m_attr_apl_user__pass = GetAttrDefinition(m_ent_apl_user,_T("password"));
			m_attr_apl_user__grop = GetAttrDefinition(m_ent_apl_user,_T("groups"));
			m_attr_apl_user__pers = GetAttrDefinition(m_ent_apl_user,_T("personality"));

			m_ent_apl_access_pattern = GetEntityBN(_T("apl_access_pattern"));
			m_attr_apl_acc_pat__name = GetAttrDefinition(m_ent_apl_access_pattern,_T("name"));
			m_attr_apl_acc_pat__pattern = GetAttrDefinition(m_ent_apl_access_pattern,_T("pattern"));


			m_ent_person_and_org = GetEntityBN(_T("person_and_organization"));

			m_attr_person_and_org__pers = GetAttrDefinition(m_ent_person_and_org,_T("the_person"));

			m_ent_person = GetEntityBN(_T("person"));
			m_attr_person__last_name = GetAttrDefinition(m_ent_person,_T("last_name"));
			m_attr_person__first_name = GetAttrDefinition(m_ent_person,_T("first_name"));
			m_attr_person__middle_names = GetAttrDefinition(m_ent_person,_T("middle_names"));

			if(m_ent_apl_user!=NULL)
			{
				WriteTickLog(_T("GetTitulCurrBD"));
				bool is_readonly = m_is_readonly;
				m_is_readonly = false;
				m_CurrUser = CaplStepData::CreateInstance(m_ent_apl_user);
				m_CurrUser->SetId((apl_id)idCurrUser);
				m_is_readonly = is_readonly;
				if(m_is_readonly)
				{
					m_CurrUser->SetAccessmode(aplRO);
				}
				// атрибуты текущего юзера c обвязкой
				buf = _T("\nConnected to: ");
				buf += GetTitulCurrBD(true,true,true,true);
				m_MainLog.AddFirstInfo(buf);
			}

			WriteTickLog(_T("NET_GetAttrs4Blobs"));
			NET_GetAttrs4Blobs(m_list_attrs4blobs);
		}

		if(main_cwnd != 0 && is_wc){main_cwnd->EndWaitCursor();is_wc = false;}
#ifdef LOG_BASE_POINT
		CString str=GetClassDescription();
		CString class_info;class_info.Format("this %x (m_local_data %x)",this,m_local_data);
		m_MainLog.LogMessage("NET_COnnect: "+str,false,true,true,false,0,class_info);
#endif //LOG_BASE_POINT

		WriteTickLog(_T("NET_GetServerDateTime"));
		COleDateTime odt_s,odt_c;
		NET_GetServerDateTime(odt_s);
		odt_c = COleDateTime::GetCurrentTime();
		buf = _T("Server time: ") + odt_s.Format(_T("%d.%m.%Y %H:%M:%S")) + _T("  Client time: ") + odt_c.Format(_T("%d.%m.%Y %H:%M:%S"));
		WriteTickLog(_T(""));
		if(m_AplTransport->m_need_relogin)
		{
			buf += APL_T(" !! Повторная авторизация по запросу сервера!!");
		}
		m_MainLog.AddFirstInfo(buf);
		m_MainLog.AddFirstInfo(m_VerCli);

		NET_LoadBDInfo();

		m_AplTransport->m_need_reconnect = true;
		m_AplTransport->m_need_relogin = false;
	}
	catch(SaplErrorDescription error)
	{
		if(main_cwnd != 0 && is_wc){main_cwnd->EndWaitCursor();is_wc = false;}
		// if(m_AplTransport->GetDescriptionServError() != _T("")){error.m_err_descr += m_AplTransport->GetDescriptionServError();} - эта строчка добавляется внутри SetAPLError
		
		CString mess = APL_T("Неудачная попытка соединения с БД.");

		if(m_AplTransport->m_login_data.username != _T(""))
		{
			mess += APL_T(" Логин: \"");
			mess += m_AplTransport->m_login_data.username;
			mess += _T("\"");
		}
		if(m_AplTransport->m_login_data.name_connect != _T(""))
		{
			mess += APL_T("  Подключение: \"");
			mess += m_AplTransport->m_login_data.name_connect;
			mess += _T("\"");
		}
		if(m_AplTransport->m_login_data.domain != _T(""))
		{
			mess += APL_T("  Домен: \"");
			mess += m_AplTransport->m_login_data.domain;
			mess += _T("\"");
		}
		if(m_type_lic_current != _T(""))
		{
			mess += APL_T("  Тип лицензии: \"");
			if(m_type_lic_current == SPEC_LIC_TYPE)
				mess += SPEC_LIC_MESS;
			else
				mess += m_type_lic_current;
			mess += _T("\"");
		}
		
		if(!m_AplTransport->m_need_relogin)
			NET_Disconnect();

		//Устанавливаем после дисконнекта, чтобы дисконнект не сбросил код ошибки
		SetAPLError(error, mess);

		m_NetErrorMessageMode = prev_NetErrorMessageMode;

		return false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();
	SaveTickLog;

	return ret_val;

}//bool CaplNetStepData::Connect(LPCTSTR username, LPCTSTR pwd, LPCTSTR name_base)


/** @brief: static. Устанавливает список запрашиваемых типов лицензий, которые будут заниматься модулем при подключении к серверу приложений.
	Если в функции NET_Connect параметр license равен пустой строке, то для подключения будет использован 
		тип лицензий, указанный параметром licenseTypeMain; при отсутствии лицензий указанного типа подключение будет невозможно.
	Если в функции NET_Connect параметр license содержит строку с именеи типа, то для подключения будет использован 
		тип лицензий указанный в параметре license, а заданный параметро licenseTypeMain будет проигнорирован. 
	После успешного подключения клиент попытается захвать лицензии из списка licenseTypesAdd.
	@param[in] licenseTypeMain главный тип лицензий
	@param[in] licenseTypesAdd список запрашиваемых дополнительных типов лицензий. Строки из списка заменяют собой предыдущий список
		запрашиваемых лицензий.
	@return true если параметр licenseTypeMain является строкой и false если параметр пустой. */
bool CaplNetStepData::NET_LIC_SetRequestedLicenseTypes(LPCTSTR licenseTypeMain, CStringArray &licenseTypesAdd)
{
	if(CString(licenseTypeMain).IsEmpty())
		return false;
	
	globalRequestedMainLicense = licenseTypeMain;
	globalRequestedAddLicenses.RemoveAll();
	globalRequestedAddLicenses.Append(licenseTypesAdd);
	return true;
}

bool CaplNetStepData::NET_LIC_SetRequestedLicenseTypes(LPCTSTR licenseTypeMain)
{
	if(CString(licenseTypeMain).IsEmpty())
		return false;

	globalRequestedMainLicense = licenseTypeMain;
	globalRequestedAddLicenses.RemoveAll();
	return true;
}


/** @brief: static. Возвращает основной запрашиваемый тип лицензии, и список дополнительных запрашиваемых типов
	@param[out] licenseTypeMain
	@param[out] licenseTypesAdd список типов лицензии */
void CaplNetStepData::NET_LIC_GetRequestedLicenseTypes(CString &licenseTypeMain, CStringArray &licenseTypesAdd)
{
	licenseTypeMain = globalRequestedMainLicense;
	licenseTypesAdd.RemoveAll();
	licenseTypesAdd.Append(globalRequestedAddLicenses);
}

/** Возвращает описание основной и дополнительных запрашиваемых типов лицензии */
CString CaplNetStepData::NET_LIC_GetRequestedLicenseTypes()
{
	CString str; str.Format(APL_T("Основная: '%s'"),  (LPCTSTR)globalRequestedMainLicense);
	if(globalRequestedAddLicenses.GetSize() > 0)
	{
		str += APL_T(" Дополнительные: ");
		for(int i=0; i< globalRequestedAddLicenses.GetSize(); i++)
		{
			if(i>0) str += _T(", ");
			str += _T("'") + globalRequestedAddLicenses.GetAt(i) + _T("'");
		}
	}
	return str;
}


/** Возвращает список типов лицензий, которые заняты в настоящий момент экземпляром класса, работающим
	с БД. Первым элементом в списке идет основной тип лицензии, последующими - дополнительные типы.
	До соединения с БД возвращается пустой список.
	@param[out] licenseTypes список типов лицензии */
void CaplNetStepData::NET_LIC_GetUsedLicenseTypes(CStringArray &licenseTypes)
{
	licenseTypes.RemoveAll();
	if(m_AplTransport == 0  || !m_AplTransport->IsConnected()){SetAPLError(APL_NET_CLI_NOT_CONNECT); return;}

	licenseTypes.Add(m_type_lic_current);
	licenseTypes.Append(m_types_lic_adt);

	return;
}

/** Возвращает описание основной и дополнительных типов лицензии, захваченных в текущий момент */
CString CaplNetStepData::NET_LIC_GetUsedLicenseTypes()
{
	CString str; str.Format(APL_T("Основная: '%s'"),  (LPCTSTR)m_type_lic_current);
	if(m_types_lic_adt.GetSize() > 0)
	{
		str += APL_T(" Дополнительные: ");
		for(int i=0; i< m_types_lic_adt.GetSize(); i++)
		{
			if(i>0) str += _T(", ");
			str += _T("'") + m_types_lic_adt.GetAt(i) + _T("'");
		}
	}
	return str;
}


/** Вызывает  на сервере приложений проверку наличия лицензии указанного типа и ее захват, если есть свободные лицензии.
	@param[in] licenseType имя типа лицензий
	@return true если лицензия захвачена или была захвачена ранее, false если свободных лицензий нет.
	*/
bool CaplNetStepData::NET_LIC_TestTakeLicense(LPCTSTR licenseType)
{
	int i;
	if(m_AplTransport == 0  || !m_AplTransport->IsConnected()){ SetAPLError(APL_NET_CLI_NOT_CONNECT); return false;}
	if(CString(licenseType).IsEmpty()){ SetAPLError(APL_NET_CLI_MISSED_DATA, APL_T("Не задан тип лицензии")); return false;}

	if(m_type_lic_current == licenseType) return true;
// Всегда запрашиваем сервер!
// 	for(i=0; i<m_types_lic_add.GetSize(); i++)
// 		if(m_types_lic_add.GetAt(i) == licenseType) return true;

	CStringArray arr_in, arr_out;
	arr_in.Add(licenseType);
	AplNetErrors aplError;

	if(!m_AplTransport->AT_DCLicOperate(aplError, LO_TestTake, arr_in, arr_out))
	{	
		// Выводим сообщение только если ошибка отличается от типичных ошибок.
		// Если ошибки типичные для этой функции - пускай ругается вызывающая программа
		if(aplError == APL_SOCK_MISSED_FUNCTION)
		{
			m_AplErrorDescription = ERR_MESS_MISSED_LIC_SERVER_FIRST;
			m_AplErrorDescription += ERR_MESS_MISSED_LIC_SERVER_RENEW;
			m_LastAplErrorCode = APL_NET_CLI_SERVER_DN_SUPPORT_FUNCTION;
			//SetAPLError(APL_NET_CLI_SERVER_DN_SUPPORT_FUNCTION, str);

		}
		else if(aplError != APL_NET_SRV_LIMIT_CONNECT_EXESS)
			SetAPLError(aplError);
		m_AplTransport->ClearInternalData();
		return false;
	}

	bool res = false;
	for(i=0; i<arr_out.GetSize(); i++)
	{
		bool is_new = true;
		for(int j=0; j<m_types_lic_adt.GetSize(); j++)
		{
			if(arr_out[i] == m_types_lic_adt[j]){is_new = false; break;}
		}
		if(is_new) m_types_lic_adt.Add(arr_out.GetAt(i));
		if(arr_out.GetAt(i) == licenseType) res = true;
	}
	m_AplTransport->ClearInternalData();
	return res;
}


/** Освобождает лицензию указанного типа. Освободить можно только дополнительный тип лицензий
	@param[in] licenseType имя типа лицензий
	@return true если лицензия указанного типа была захвачена ранее как дополнительная и была успешно освобождена;
		false если лицензий указанного типа была занята как основная или не была занята ранее. */
bool CaplNetStepData::NET_LIC_FreeLicense(LPCTSTR licenseType)
{
	int i;
	if(m_AplTransport == 0  || !m_AplTransport->IsConnected()){ SetAPLError(APL_NET_CLI_NOT_CONNECT); return false;}
	if(CString(licenseType).IsEmpty()){ SetAPLError(APL_NET_CLI_MISSED_DATA, APL_T("Не задан тип лицензии")); return false;}

	if(m_type_lic_current == licenseType){ SetAPLError(APL_NET_CLI_MISSED_DATA, APL_T("Нельзя освободить основной тип лицензии")); return false;}

	bool flag = true;
	for(i=0; i<m_types_lic_adt.GetSize(); i++)
	{	
		if(m_types_lic_adt.GetAt(i) == licenseType){ flag = false; break;}
	}
	if(flag)
		return true;

	CStringArray arr_in, arr_out;
	arr_in.Add(licenseType);
	AplNetErrors aplError;

	if(!m_AplTransport->AT_DCLicOperate(aplError, LO_Remove, arr_in, arr_out))
	{	
		if(aplError == APL_NET_SRV_LIMIT_CONNECT_EXESS)
		{
			if(!m_hide_mode)AfxMessageBox(APL_T("Ошибка при освобождении лицензии."));
		}
		else
			SetAPLError(aplError); 

		m_AplTransport->ClearInternalData();
		return false;
	}
	m_AplTransport->ClearInternalData();

	for(i=0; i<m_types_lic_adt.GetSize(); i++)
	{	
		if(m_types_lic_adt.GetAt(i) == licenseType){ m_types_lic_adt.RemoveAt(i); break;}
	}

	return true;
}



/** Задает время, в течении которого клиент будет не в состоянии отвечать на статусные запросы сервера
@param time_in_minuts время в минутах, после которого клиент гарантировано должен ответить на статусный запрос сервера
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::NET_BeginLongOperation(int time_in_minuts)
{
	LogDebugMessageIfDef(_T("CaplNetStepData::NET_BeginLongOperation"));
	bool retval = true;
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	try{

		//Подготавливаем данные для передачи серверу
		AplNetErrors aplError = APL_NET_OK; 

		TEST_RECONNECT(false);

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCBeginLongOperation(aplError,time_in_minuts));

	}
	CATH_MEMORY_ANY_EXEPTION;

	LogDebugMessageIfDef( APL_T("Выходим из NET_BeginLongOperation"));
	return retval;

}

/** Переводит соединение в режим администратора 
@param _username имя пользователя находящегося в группе администраторов. 
@param _pwd пароль пользователя
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::NET_StartAdminMode2(LPCTSTR _username, LPCTSTR _pwd, LPCTSTR source)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_StartAdminMode"));

	bool retval = true;
	CHECK_BD(false);
	CHECK_STATE_S(true, true, false);

	//Подготавливаем данные для передачи серверу
	AplNetErrors aplError = APL_NET_OK; 
	CString username = _username;
	CString pwd = _pwd, pwd_crypt = _T("");

	UINT32 idCurrAdminUser = 0;

	m_count_admin++;
	if(m_count_admin > 1) return true;


	try
	{
		TEST_RECONNECT(false);
		
		if(!m_hide_mode && m_IsWaitDlg)
		{
			m_AplTransport->SetWaitDlgText( APL_T("Перевод соединения в режим администратора"));
		}

		// Шифруем пароль
		aplTranslateK(pwd, pwd_crypt);
		pwd = pwd_crypt;

		int len_usrn = _strlen(username);
		int len_pwd	= _strlen(pwd);

		THROW_IF_EX((len_usrn > MAX_STR_LEN) || (len_pwd > MAX_STR_LEN),
			APL_NET_CLI_MISSED_DATA, APL_T("Слишком длинные входные параметры!"));

		aplError = APL_NET_OK;

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCStartAdminMode(aplError, username, pwd, &idCurrAdminUser, 0, 0, source));

		if(m_ent_apl_user != NULL)
		{
			m_non_admin_readonly = m_is_readonly;
			m_is_readonly = false;
			aplQFindInstIdInExtent(*((aplExtent*)(&instances)), idCurrAdminUser, instances.Size, false, &m_CurrAdminUser);
			if(m_CurrAdminUser == 0)
			{
				m_CurrAdminUser = CaplStepData::CreateInstance(m_ent_apl_user);
				m_CurrAdminUser->SetId(idCurrAdminUser);
			}
			SetAdminMode(true);
			m_local_data->SetAdminMode(true);

			// атрибуты текущего юзера
			CaplLoadData ld(this, DEF_SOURCE);
			ld.AddQuery(0 ,m_CurrAdminUser, true);
			ld.LoadEx();
		}

	}
	catch(SaplErrorDescription error)
	{
		LogDebugMessageIfDef( APL_T("Ошибка в NET_StartAdminMode!!!"));
		SetAPLError(error);
		retval = false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("Выходим из NET_StartAdminMode"));
	return retval;
}

bool CaplNetStepData::NET_StartAdminMode2(int code1, int code2, LPCTSTR source)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef("CaplNetStepData::NET_StartAdminMode (code)");

	bool retval = true;
	CHECK_BD(false);
	CHECK_STATE_S(true, true, false);

	//Подготавливаем данные для передачи серверу
	AplNetErrors aplError = APL_NET_OK; 

	UINT32 idCurrAdminUser = 0;

	m_count_admin++;
	if(m_count_admin > 1) return true;

	try
	{
		TEST_RECONNECT(false);
		
		if(!m_hide_mode && m_IsWaitDlg)
		{
			m_AplTransport->SetWaitDlgText( APL_T("Перевод соединения в режим администратора"));
		}

		aplError = APL_NET_OK;

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCStartAdminMode(aplError, 0, 0, &idCurrAdminUser, code1, code2, source));

		m_non_admin_readonly = m_is_readonly;
		m_is_readonly = false;
		int pos=aplQFindInstIdInExtent(*((aplExtent*)(&instances)), idCurrAdminUser, instances.Size, false, &m_CurrAdminUser);
		if(m_CurrAdminUser== 0)
		{
			m_CurrAdminUser = CaplStepData::CreateInstance(m_ent_apl_user);
			m_CurrAdminUser->SetId(idCurrAdminUser);
		}
		SetAdminMode(true);
		m_local_data->SetAdminMode(true);

		// атрибуты текущего юзера
		CaplLoadData ld(this, DEF_SOURCE);
		ld.AddQuery(0, m_CurrAdminUser, true);
		ld.LoadEx();

	}
	catch(SaplErrorDescription error)
	{
		m_count_admin--;
		LogDebugMessageIfDef( APL_T("Ошибка в NET_StartAdminMode!!!"));
		SetAPLError(error);
		retval = false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("Выходим из NET_StartAdminMode"));
	return retval;
}

/** Переводит соединение из режима администратора в нормальный режим
*/
bool CaplNetStepData::NET_ExitAdminMode()
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_ExitAdminMode"));

	bool retval = true;
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	//Подготавливаем данные для передачи серверу
	AplNetErrors aplError = APL_NET_OK; 

	UINT32 idCurrUser = 0;
	int i;

	m_count_admin--;
	if(m_count_admin > 0) return true;

	try
	{
		TEST_RECONNECT(false);
		
		if(!m_hide_mode && m_IsWaitDlg)
		{
			m_AplTransport->SetWaitDlgText( APL_T("Вывод соединения из режима администратора"));
		}
		// для начала сохранимся = чтобы не было проблем при следующих, не админовых сохранениях
		if(!NET_SaveChanges( APL_T("Сохранение при выходе из режима администратора")))
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка сохранения изменений при выходе из режима администратора!"));
			return false;
		}

		aplError = APL_NET_OK;

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCExitAdminMode(aplError, &idCurrUser));

		m_CurrAdminUser = 0;
		SetAdminMode(false);
		m_local_data->SetAdminMode(false);
		m_is_readonly = m_non_admin_readonly;

		if(m_CurrUser->GetId() != idCurrUser)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Поменялся идентификатор текущего пользователя!"));
		}
		// обновляем доступ у загруженных с сервера - уже для обычного пользователя
		if(m_loaded_in_admin.GetSize()>0)
		{
			m_loaded_in_admin.Normalize();	
			CaplLoadData ld(this, DEF_SOURCE);
			i=0;
			do 
			{
				if(m_loaded_in_admin[i] == 0){i++;continue;}
				if(m_loaded_in_admin[i]->GetType() == 0){i++;continue;}

				ld.AddQuery(-1,m_loaded_in_admin[i]);
				i++;

				if(ld.m_queries.GetSize()>500 || i == m_loaded_in_admin.GetSize())
				{
					ld.LoadEx();
				}
			} while (i <m_loaded_in_admin.GetSize());

			m_loaded_in_admin.Clear();
		}
	}
	catch(SaplErrorDescription error)
	{
		LogDebugMessageIfDef( APL_T("Ошибка в NET_ExitAdminMode!!!"));
		SetAPLError(error);
		retval = false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("Выходим из NET_ExitAdminMode"));
	return retval;
}


bool CaplNetStepData::NET_SessionPartTest()
{
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	//Подготавливаем данные для передачи серверу
	AplNetErrors aplError = APL_NET_OK; 
	CaplDataBuf dbuf_in, dbuf_out;

	// заголовки буферов - однобайтовые! 
	int k = strlen(std_SessionPartsOperate_header) + 1;
	const char* version = std_SessionPartsOperate_header; // заголовки буферов - однобайтовые! 

	dbuf_in.Add(version, k);
	dbuf_in.m_ansi_string = m_global_ansi_string;
	UINT32 i_val = 0; // проверка наличия структур и таблиц для управления именованной части сессии
	dbuf_in.Add(&i_val, sizeof(i_val));

	m_AplTransport->SetWaitDlgText(_T(""));

	bool res = m_AplTransport->AT_DCSessionPartsOperate(aplError, dbuf_in, dbuf_out);
	if(aplError==APL_NET_SRV_DICTIONARY_NEEDS_TO_BE_UPDATED)
	{
		m_LastAplErrorCode = aplError;
		m_ErrorDescription = APL_NET_ERROR2char(m_LastAplErrorCode);
		m_AplErrorDescription = m_ErrorDescription;
		m_AplTransport->ClearInternalData();
		return false;
	}
	RETURN_IF_OR_CHECK_ERR(!res);

	/*
	char* header = 0; // заголовки буферов - однобайтовые! 
	// Заголовок
	k = strlen(std_SessionPartsOperate_header) + 1;
	header = new char[k];
	dbuf_out.Read(header, k);
	if(CStringA(std_SessionPartsOperate_header) != header)
	{
		delete header;
		SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел буфер неизвестного формата"));
		m_AplTransport->ClearInternalData();
		return false;
	}
	delete[] header; header = 0;
	*/
	m_AplTransport->ClearInternalData();

	return true;

}

bool CaplNetStepData::NET_SessionPartStart(LPCTSTR PartName, UINT32 *SessionPartId)
{
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	//Подготавливаем данные для передачи серверу
	AplNetErrors aplError = APL_NET_OK; 
	CaplDataBuf dbuf_in, dbuf_out;

	// заголовки буферов - однобайтовые! 
	int k = strlen(std_SessionPartsOperate_header) + 1;
	const char* version = std_SessionPartsOperate_header; // заголовки буферов - однобайтовые! 

	dbuf_in.Add(version, k);
	dbuf_in.m_ansi_string = m_global_ansi_string;
	UINT32 i_val = 1; // начало именованной части сессии
	dbuf_in.Add(&i_val, sizeof(i_val));
	dbuf_in.AddStrBuf(PartName);

	m_AplTransport->SetWaitDlgText(_T(""));

	RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCSessionPartsOperate(aplError, dbuf_in, dbuf_out));

	char* header = 0; // заголовки буферов - однобайтовые! 
	// Заголовок
	k = strlen(std_SessionPartsOperate_header) + 1;
	header = new char[k];
	dbuf_out.Read(header, k);
	if(CStringA(std_SessionPartsOperate_header) != header)
	{
		delete header;
		SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел буфер неизвестного формата"));
		m_AplTransport->ClearInternalData();
		return false;
	}
	delete[] header; header = 0;

	// идентификатор сессии
	dbuf_out.Read(&(i_val), 4);
	if(SessionPartId!=0)*SessionPartId = i_val;
	m_AplTransport->ClearInternalData();
	return true;
}

bool CaplNetStepData::NET_SessionPartEnd(UINT32 *SessionPartId)
{
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	//Подготавливаем данные для передачи серверу
	AplNetErrors aplError = APL_NET_OK; 
	CaplDataBuf dbuf_in, dbuf_out;

	// заголовки буферов - однобайтовые! 
	int k = strlen(std_SessionPartsOperate_header) + 1;
	const char* version = std_SessionPartsOperate_header; // заголовки буферов - однобайтовые! 

	dbuf_in.Add(version, k);
	dbuf_in.m_ansi_string = m_global_ansi_string;
	UINT32 i_val = 2; // завершение именованной части сессии
	dbuf_in.Add(&i_val, sizeof(i_val));

	m_AplTransport->SetWaitDlgText(_T(""));

	RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCSessionPartsOperate(aplError, dbuf_in, dbuf_out));

	char* header = 0; // заголовки буферов - однобайтовые! 
	// Заголовок
	k = strlen(std_SessionPartsOperate_header) + 1;
	header = new char[k];
	dbuf_out.Read(header, k);
	if(CStringA(std_SessionPartsOperate_header) != header)
	{
		delete header;
		SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел буфер неизвестного формата"));
		m_AplTransport->ClearInternalData();
		return false;
	}
	delete[] header; header = 0;

	// идентификатор сессии
	dbuf_out.Read(&(i_val), 4);
	if(SessionPartId!=0)*SessionPartId = i_val;
	m_AplTransport->ClearInternalData();

	return true;
}

bool CaplNetStepData::NET_GetOraAlias(int code1, int code2, CString &alias, CString &username, CString &password)
{
	LogDebugMessageIfDef(_T("CaplNetStepData::NET_GetOraAlias (code)"));

	bool retval = true;
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	//Подготавливаем данные для передачи серверу
	AplNetErrors aplError = APL_NET_OK; 
	CaplDataBuf dbuf;

	alias = _T("");
	username = _T("");
	password = _T("");
	TCHAR* c_buf;
	int c_buf_size;

	try
	{
		TEST_RECONNECT(false);
		
		if(!m_hide_mode && m_IsWaitDlg)
		{
			m_AplTransport->SetWaitDlgText( APL_T("Получение настроек Oracle"));
		}

		aplError = APL_NET_OK;

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCGetOraAlias(aplError,0,0,code1,code2,dbuf));
		
		dbuf.Read(&c_buf_size,4);
		c_buf = new TCHAR[c_buf_size+1];

		dbuf.Read(c_buf, c_buf_size * sizeof(TCHAR));
		c_buf[c_buf_size] = 0;
		alias = c_buf;
		delete[] c_buf;

		if(alias == _T("ERROR"))
		{
			alias = _T("");
			LogDebugMessageIfDef( APL_T("На сервере произошла ошибка при определении алиаса!"));
			retval = false;
		}
		else
		{
			dbuf.Read(&c_buf_size,4);
			c_buf = new TCHAR[c_buf_size+1];
			dbuf.Read(c_buf,c_buf_size * sizeof(TCHAR));
			c_buf[c_buf_size] = 0;
			username = c_buf;
			delete[] c_buf;

			dbuf.Read(&c_buf_size, 4);
			if(c_buf_size > 0)
			{
				c_buf = new TCHAR[c_buf_size+1];
				dbuf.Read(c_buf, c_buf_size);
				c_buf[c_buf_size] = 0;
				password = c_buf;
				delete[] c_buf;
			}
			else
			{
				password = _T("");
			}

		}
	}
	catch(SaplErrorDescription error)
	{
		LogDebugMessageIfDef( APL_T("Ошибка в NET_GetOraAlias!!!"));
		SetAPLError(error);
		retval = false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("Выходим из NET_GetOraAlias"));
	return retval;
}


int CaplNetStepData::GetTable4Entity(CaplEntity* entity)
{
	LogDebugMessageIfDef(_T("CaplNetStepData::GetTable4Entity"));

	int retval=-1;
	CHECK_BD(-1);
	CHECK_STATE(true, true, -1);

	if(entity==NULL)
	{
		SetAPLError((AplNetErrors) APL_NET_CLI_MISSED_DATA,_T(""),_T(__FILE__),__LINE__,__APL_FUNC__);
		return -1;
	}

	try
	{
		if(!m_IsLoadedTable4Entity)
		{
			// надо загружать таблицы с сервера
			CaplMap Entity2Tabl;
			int i;
			//Подготавливаем данные для передачи серверу
			AplNetErrors aplError = APL_NET_OK; 
			TEST_RECONNECT(-1);
			
			if(!m_hide_mode && m_IsWaitDlg)
			{
				m_AplTransport->SetWaitDlgText( APL_T("Загружаем распределение entity по таблицам"));
			}
			aplError = APL_NET_OK;

			m_AplTransport->AT_DCLoadEntityTables(aplError,Entity2Tabl);
			if(aplError!=APL_NET_OK){ MY_THROW(aplError); }
			
			for(i=0; i <entities.GetSize(); i++)
			{
				entities[i]->m_table_id=Entity2Tabl.QGetByIn(entities[i]->id);
			}
			m_IsLoadedTable4Entity = true;
		}
		retval=entity->m_table_id;

	}
	catch(SaplErrorDescription error)
	{
		LogDebugMessageIfDef( APL_T("Ошибка в GetTable4Entity!!!"));
		bool old_mode = m_NetErrorMessageMode;
		m_NetErrorMessageMode = false;
		SetAPLError(error);
		m_NetErrorMessageMode = old_mode;
	}
	CATH_MEMORY_ANY_EXEPTION;	

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("Выходим из NET_StartAdminMode"));
	return retval;

}


/** Возвращает строку, описывающую текущее подключение к БД и предназначенную для 
помещения в заголовок приложения.
@param name_bd включать в строку имя активной БД
@param name_connect включать в строку название подключения
@param name_user включать в строку имя пользователя
@param name_person включать в строку имя сотрудника 
@param pid включать в строку идентификатор процесса
@return строка для заголовка*/
CString CaplNetStepData::GetTitulCurrBD(bool name_bd, bool name_connect,
										bool name_user, bool name_person, bool pid)
{
	LogDebugMessageIfDef(_T("CaplNetStepData::GetTitulCurrBD"));
	CString titul = _T("");
	CString buf, buf1;
	CaplAggr aggr0;
	CaplInstance *po, *pers;

	if(!m_IsConnected)
	{
		if(m_CurDataFile != _T(""))
		{
			titul =_T("File ");
#ifdef _UNICODE
			titul +=_T("(file ");
			if(m_global_ansi_string)titul += _T("ansi)");else titul += _T("unicode)");
#endif // #ifdef _UNICODE
			titul +=_T(": ");
			titul += m_CurDataFile;
		}
		else
		{
			titul= APL_T("Не соединены с БД");
		}
		return titul;
	}
	else
	{
#ifdef _UNICODE
		if(m_global_ansi_string) titul += _T("A)");else titul += _T("U)");
#endif // #ifdef _UNICODE
	}


	if(name_bd)
	{
		titul = APL_T("БД \'") + GetNameCurrBase() + _T("\' ");
#ifdef _UNICODE
		if(m_global_ansi_string) titul += _T("(ansi) ");else titul += _T("(unicode) ");
#endif // #ifdef _UNICODE
	}
	if(name_connect)
	{
		titul += APL_T("Соединение \'") + GetNameCurrConnection() + _T("\' ");
	}
	if(name_user)
	{
		CString domain = GetNameCurrDomain();
		titul += APL_T("Пользователь \'" )+ GetNameCurrUser();
		if(domain!= _T(""))
		{
			titul += _T("@");
			titul += domain;
		}
		titul += _T("\' ");
	}


	if(name_person && m_attr_apl_user__pers!=0)
	{
		CaplLoadData ld(this, DEF_SOURCE);
		ld.AddQuery(0, m_CurrUser, true);
		ld.AddQuery(1, _T('d'), 0, 0, m_attr_apl_user__pers, true, true);
		if(0!=m_attr_person_and_org__pers) // Для TGB не надо
		{
			ld.AddQuery(2, _T('d'), 1, 0, m_attr_person_and_org__pers, true, true);
		}
		ld.LoadEx();

		if(0!=m_attr_person_and_org__pers)
		{
			titul+= APL_T("Сотрудник ");
			GetAttr(m_CurrUser,m_attr_apl_user__pers,po);
			if(po != 0)
			{
				if(IsKindOf(po,m_ent_person_and_org))
				{
					GetAttr(po,m_attr_person_and_org__pers,pers);
				}
				else
				{
					pers=po;
				}
				titul += _T("\'");
				
				GetAttr(pers, m_attr_person__last_name, buf);
				titul += buf + _T(" ");
				
				GetAttr(pers, m_attr_person__first_name, buf);
				titul += buf + _T(" ");

				GetAttr(pers, m_attr_person__middle_names, aggr0);
				aggr0.GetByIndex(0, buf);

				titul += buf + _T(" ");
				titul += _T("\' ");
			}
			else
			{
				titul += APL_T("Не определен ");
			}
		}
	}
	if(pid)
	{
		DWORD pid = GetCurrentProcessId();
		buf.Format(_T("pid %i"), pid);
		titul += buf + _T(" ");
		if(m_AplTransport != 0)
		{
			buf.Format(_T("/*client_ip %i  client_id %u"), m_AplTransport->m_login_data.client_ip, m_AplTransport->m_login_data.client_id);
			if(m_AplTransport->m_login_data.s1 != 0){buf1.Format(_T("/%u"), m_AplTransport->m_login_data.s1); buf+=buf1;}
			if(m_AplTransport->m_login_data.s2 != 0){buf1.Format(_T(":%u"), m_AplTransport->m_login_data.s2); buf+=buf1;}
			titul += buf + _T(" ");
		}
	}
	return titul;
}

void CaplNetStepData::ZeroBDParams()
{
	m_is_readonly = false;
	m_count_admin = 0;

	m_NameCurrBase = _T("");
	m_NameCurrConnection = _T("");
	m_NameCurrUser = _T("");
	m_FullDictName = _T("");
	m_IsConnected = false;
	m_admin_mode = false;
	m_CurrUser = 0;
	m_CurrAdminUser = 0;

	m_PercentSCh = 0;
	m_LocalServerPath = _T("");
	m_local_data = 0;
	m_query = 0;
	m_ServerHashMethod = _T("");


	m_ent_apl_workgroup = 0;
	m_ent_apl_access_agent = 0;
	m_ent_apl_user = 0;

	m_attr_apl_user__name = 0;
	m_attr_apl_user__pass = 0;
	m_attr_apl_user__grop = 0;
	m_attr_apl_user__pers = 0;

	m_ent_apl_access_pattern = 0;

	m_attr_apl_acc_pat__name = 0;
	m_attr_apl_acc_pat__pattern = 0;

	m_ent_person_and_org = 0;
	m_attr_person_and_org__pers = 0;

	m_ent_person = 0;
	m_attr_person__last_name = 0;
	m_attr_person__first_name = 0;
	m_attr_person__middle_names = 0;

	m_ent_apl_GUID_access = 0;
	m_attr_apl_GUID_access__GUID = 0;
	m_attr_apl_GUID_access__access_def = 0;
	m_attr_apl_GUID_access__access_ind = 0;
	m_attr_apl_GUID_access__pattern = 0;

}


/************************************************************************/
/* 
/************************************************************************/
bool CaplNetStepData::NET_Disconnect()
{
	CaplStackLogger stack_logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_Disconnect begin"));

	CHECK_BD(true);
	CHECK_STATE(false, false, false);

	try{

#ifdef LOG_BASE_POINT
		CString str=GetClassDescription();
		CString class_info;class_info.Format("this %x",this);
		m_MainLog.LogMessage("NET_Disconnect: "+str,false,true,true,false,0,class_info);
#endif //LOG_BASE_POINT

		ResetTimer(1,0);

		m_list_attrs4blobs.RemoveAll();

		if(m_FullDictName != _T(""))DeleteFile(LPCTSTR(m_FullDictName));

		if(m_AplTransport != 0)
		{
			m_AplTransport->DisconnectFull();
			m_AplTransport->m_IsServer4AddLic = false;
		}
		m_ClearDataAfterError = true;
		
		ClearData();
		// важно, так как CaplNetStepData::ClearDict() вызывает NET_Disconnect и приведет к зацикливанию
		CaplStepData::ClearDict();
		ClearLastQuery();

		if(m_local_data != 0)
		{
			delete m_local_data;
			m_local_data = 0;
		}
		ZeroBDParams();

		LogDebugMessageIfDef(_T("CaplNetStepData::NET_Disconnect end"));
		//	AfxMessageBox("NET_Disconnect end");
	}
	CATH_MEMORY_ANY_EXEPTION;
	return true;
}//bool CaplNetStepData::Disconnect()

/*
bool CaplNetStepData::SetCurrUser(CaplInstance* user)
{
	if(m_IsConnected) return false;
	m_CurrUser=user;
	return true;
}
*/


// Определяет таблицу для измененных аггров (inst - attr - aggr)
// Запрашивает на сервере новые значения измененных аггров 
// и корректирует значения для сохранения 
bool CaplNetStepData::ActualizeChangedAggr(CaplLoadData &ld, aplExtent &ext)
{
	LogDebugMessageIfDef(_T("CaplNetStepData::ActualizeChangedAggr"));

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	//	SChangedAggrValList list;
	CaplTAggr<SChangedAggrVal*,SChangedAggrVal*,APLAGGR_UNIQUE|APLAGGR_LIST|APLAGGR_AUTOKILLREF> list;
	int i = 0, j = 0,k = 0;

	CaplInstance *inst_b = 0;
	SChangedAggrVal* cav = 0;

	try{

		list.Clear();
		SetLastError(0);

		if(!m_IsActualizeChangedAggr)
		{
			m_not_compared_aggr.Clear();
			return true;
		}

		if(m_not_compared_aggr.Size>1)
		{
			m_not_compared_aggr.SortIn();
		}
		int position = -1;

		// получение списка старых Instance c изменненными аграми
		for(i=0; i <ext.Size; i++)
		{
			inst_b=ext[i];
			if(inst_b->GetId() == 0) continue;
			if(inst_b->GetType() == 0) continue;
			if(inst_b->attrs == 0) continue;
			if(inst_b->GetTemporary()) continue;

			position = m_not_compared_aggr.QFindByIn((long)inst_b);

			for(j = 0; j<inst_b->GetType()->all_attrs.Size; j++)
			{
				if(inst_b->GetType()->all_attrs[j]->type != aplAGGR) continue;
				if(inst_b->attrs[j].changed == false) continue;
				if(inst_b->attrs[j].value.type != aplNOTYPE)
				{
					if(inst_b->attrs[j].value.type != aplAGGR ) 
					{
						SetAPLErrorWithFileInfo(APL_NET_CLI_ERROR_PROCESSED);
						return false;
					}
					if(inst_b->attrs[j].value.aggrval == 0)
					{
						SetAPLErrorWithFileInfo(APL_NET_CLI_ERROR_PROCESSED);
						return false;
					}
				}
				if(position != -1)
				{
					if(m_not_compared_aggr[position].out == (long)inst_b->GetType()->all_attrs[j])
					{
						continue;
					}
				}
				cav = new SChangedAggrVal();
				cav->inst = inst_b;
				cav->attrval = &(inst_b->attrs[j]);
				cav->attr = inst_b->GetType()->all_attrs[j];
				list.Add(cav);
			}
		}
		m_not_compared_aggr.Clear();

		if(list.GetSize() == 0) 
			return true;

		if(m_local_data == 0)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Не выделен буфер m_local_data"));
			return false;
		}
		m_local_data->ClearData();

		// организуем запрос на свеженькое аггро
		ld.m_placement = m_local_data;
		int ind;
		for(ind = 0; ind<list.GetSize(); ind++)
		{
			cav = list.GetAt(ind);
			ld.AddQuery( _T('i'), cav->inst->GetId(), 0, cav->attr, true);
			
			if(ld.m_queries.GetSize() > 500 || ind == list.GetSize()-1)
			{
				ld.LoadEx(true, 0, APL_T("Сравнение изменившихся массивов"));
				CaplAggr *current_aggr = 0, new_el, del_el;// 
				CaplAggr new_stored_aggr, old_stored_aggr;// 
				CaplInstance* inst_stored = 0, *new_inst = 0;
				CaplValue val_old, val_new;
				bool found = true;
				// сравниваем списки инстансов
				for(i=0; i <list.GetSize(); i++)
				{
					cav = list.GetAt(i);
					// текущее значение аггра в кеше
					current_aggr = cav->attrval->value.aggrval; 
					// старое значение измененного аггра
					old_stored_aggr.Clear();
					if(cav->attrval->ArhiveAggr != 0)
						old_stored_aggr.Append(*(cav->attrval->ArhiveAggr));
					// очистим массивы добавленных и удаленных другими сеансами
					new_el.Clear();
					del_el.Clear();

					aplQFindInstIdInExtent(*((aplExtent*)(&(m_local_data->instances))), cav->inst->GetId(), -1, true, &inst_stored);

					if(inst_stored == 0)
						continue;

					// получаем новое значение измененного аггра из базы
					m_local_data->GetAttrBN(inst_stored, cav->attr->name, new_stored_aggr);

					// найдем отличия двух аггров прямым перебором сверху вниз - так быстрей
					for(j=old_stored_aggr.GetSize()-1; j>= 0; j--)
					{
						old_stored_aggr.GetByIndex(j, val_old);
						found = false;
						for(k = new_stored_aggr.GetSize()-1; k >= 0; k--)
						{
							new_stored_aggr.GetByIndex(k, val_new);
							if(new_stored_aggr.type == aplINSTANCE)
							{
								if(val_old.instval == 0 || val_new.instval == 0)
								{
									SetAPLErrorWithFileInfo(APL_NET_CLI_ERROR_PROCESSED);
									return false;
								}
								if(val_old.instval->GetId() == val_new.instval->GetId())
								{
									found = true;
									break;
								}
							}
							else if(val_old == val_new)
							{
								found = true;
								break;
							}
						}
						if(found)
						{
							// этот элемент не изменен другими сеансами. Удаляем его из обоих списков, благо оба копии
							old_stored_aggr.Remove(j);
							new_stored_aggr.Remove(k);
						}
					}
					// Удаляем из кешового значения удаленные внешними сеансами
					for(j = 0; j<old_stored_aggr.GetSize(); j++)
					{
						old_stored_aggr.GetByIndex(j, val_old);
						for(k = 0; k < current_aggr->GetSize(); k++)
						{
							current_aggr->GetByIndex(k, val_new);
							if(val_old == val_new)
							{
								current_aggr->Remove(k);
								break;
							}
						}
					}
					// Добавляем в кешовое значение добавленные внешними сеансами
					for(j = 0; j < new_stored_aggr.GetSize(); j++)
					{
						new_stored_aggr.GetByIndex(j, inst_stored);// надо помнить, это ДРУГАЯ цапельная переменная
						if(inst_stored != 0)
						{
							aplQFindInstIdInExtent(*((aplExtent*)(&instances)), inst_stored->GetId(), -1, true, &new_inst);
							if(new_inst == 0)
							{
								// а мы его еще не загрузили...
								new_inst = CaplStepData::CreateInstanceBN(inst_stored->GetType()->name);
								new_inst->SetId(inst_stored->GetId());
							}
							current_aggr->Add(new_inst);
						}
					}
				}
			}

		}

	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}


CaplInstance* CaplNetStepData::GetInstById(int id, bool auto_load, bool load_attr)
{
	LogDebugMessageIfDef(_T("CaplNetStepData::GetInstById"));
	CaplInstance* inst = 0;
	CaplLoadData ld(this, DEF_SOURCE);
	aplExtent ext;
	// поиск в отсортированном списке инстансов
	aplQFindInstIdInExtent(*((aplExtent*)(&instances)), id, -1, true, &inst);
	if(inst == 0 && id > 3900 && auto_load)
	{
		// пытаемся загрузить с сервера
		ld.AddQuery( _T('a'), id, (CaplEntity*)0, (CaplAttr*)0, true, load_attr);
		ld.LoadEx(true, &ext);
		if(ext.GetSize() != 0)
		{
			inst = ext.GetAt(0);
		}
	}
	return inst;
}

bool Compareapl_NET_LoadQuery(apl_NET_LoadQuery &q1, apl_NET_LoadQuery &q2)
{
	return q1.id == q2.id && q1.type == q2.type && q1.inst_or_id == q2.inst_or_id && 
		q1.ent_id == q2.ent_id && q1.attr_id == q2.attr_id && q1.in_result == q2.in_result && 
		q1.load_all_attr == q2.load_all_attr;
}

void CaplNetStepData::ClearLastQuery()
{
	if(m_last_load_queries != 0)
	{
		delete[] m_last_load_queries;
	}
	m_last_load_queries = 0;
	m_last_load_queries_count = 0;
	m_last_load_result.Clear();
}

/************************************************************************/
/* Напрямую вызывает Load, передает ему буфер Queries. В случае успеха 
/* создает массив для результата и загружает его с помошью Appload...
/* По окончании использования клиент должен удалить Queries
/************************************************************************/
bool CaplNetStepData::NET_Load(apl_NET_LoadQuery *queries, int count,
							   aplExtent* ext_out, CaplNetStepData* placement,
							   LPCTSTR wait_text, CString source, bool forced)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_Load"));

	CHECK_BD(false);
	CHECK_STATE_S(true, false, false);

	logger.SetFunctionInfo(_T("\t\t") + source);

	if(count== 0 || queries == NULL )
	{
		SetAPLError((AplNetErrors) APL_NET_CLI_MISSED_DATA, APL_T("Пустой массив запросов"),_T(__FILE__),__LINE__,__APL_FUNC__);
		return false;
	}

	m_num_load++;

	try
	{

		//TEST_RECONNECT(false); -- код этого макроса здесь слегка видоизменен
		if(!m_AplTransport->IsConnected())
		{
			if(m_hide_mode)
			{
				SetAPLError((AplNetErrors) APL_NET_CLI_NOT_CONNECT);
				return false;
			}
			if(!m_AplTransport->Reconnect(true,_T(__FILE__),__LINE__,_T(__DATE__)))
			{
				SetAPLError((AplNetErrors) APL_NET_CLI_NOT_CONNECT);
				return false;
			}
		}
		else
		{
			SetAPLError((AplNetErrors) APL_NET_OK);
		}
		if(!m_IsWaitDlg){m_AplTransport->SetManageMode(MANAGE_ABSENT);}

		int index_q;
		bool clear_last = false;
		m_loadcount_all++;
		if(m_last_load_queries != 0)
		{
			if(forced || globalLoadForceMode.Get() > 0)
			{
				clear_last = true;
			}
			else if(placement!=this && placement != 0)
			{
				clear_last = true;
			}
			else if(m_last_load_queries_count== 0)
			{
				clear_last = true;
			}
			else if( !m_last_load_time_meter.CheckTimeOut(m_LoadTimeOut*1000))
			{
				// тайм аут еще не прошел. Проверяем запрос на соответствие кешированному
				if(m_last_load_queries_count != count)
				{
					clear_last = true;
				}
				for(index_q = 0; index_q < count; index_q++)
				{
					if(!Compareapl_NET_LoadQuery(m_last_load_queries[index_q], queries[index_q]))
					{
						// запрос другой
						clear_last = true;
						break;
					}
				}
			}
			else
			{
				// времени прошло больше чем можно
				clear_last = true;
			}
			if(clear_last)
			{
				// старый запрос неактуален. Очищаем хвосты
				ClearLastQuery();
			}
			else
			{
				// такой же запрос был выполнен очень недавно. Незачем повторяться
#ifdef _DEBUG
				m_MainLog.LogMessage( APL_T("Запрос дублирует предыдущий. Возвращаем кешированное значение."));
#endif //#ifdef _DEBUG

				if(ext_out != 0)
				{
					ext_out->Clear();
					bool last_unique = ext_out->Unique;
					ext_out->Unique = false;
					ext_out->Append(m_last_load_result);
					// проверЯть на уникальность не надо - это буфер с сервера, он по умолячанию уникален
					ext_out->Unique = last_unique;
				}
				// время последнего запроса ставим текущее - чтобы если следующий запрос 
				// будет опять такой же - не гонять на сервер
				m_last_load_time_meter.Start();
				return true;

			}
		}
		m_loadcount_unique++;

		CaplDataBuf dbuf_in, dbuf_out;

		if(source == _T(""))
		{
			dbuf_in.Add(LOAD_QUERY_BUF_header,22);
		}
		else
		{
			dbuf_in.Add(LOAD_QUERY_BUF_header_2,22);
			dbuf_in.m_ansi_string = m_global_ansi_string;
			dbuf_in.AddStrBuf(source);
		}
		dbuf_in.Add(&count, 4);
		dbuf_in.Add(queries, count*sizeof(apl_NET_LoadQuery));


		LogDebugMessageIfDef( APL_T("Вызов Load на сервере"));

		m_AplTransport->SetWaitDlgText(wait_text);
		AplNetErrors aplError = APL_NET_OK;

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCLoad(aplError, dbuf_in, dbuf_out));

		LogDebugMessageIfDef( APL_T("Загрузка буфера в кеш"));

		if(placement== 0)placement = this;
		long wd_trhead = 0;
		if(!m_hide_mode && m_IsWaitDlg && dbuf_out.m_Size > 500000)
		{
			wd_trhead=StartTransportWaitDlg( APL_T("Загрузка буфера в кеш"));
#ifdef _DEBUG
//			AfxMessageBox("PC");
#endif
		}
		placement->ApploadFromDataBuf(dbuf_out, &m_last_load_result);
		if(wd_trhead != 0)
		{
			EndTransportWaitDlg(wd_trhead);
			wd_trhead = 0;
		}

		LogDebugMessageIfDef( APL_T("Сохранение копии последнего запроса"));
		m_last_load_queries = new apl_NET_LoadQuery[count];
		m_last_load_queries_count = count;
		for(index_q = 0; index_q < count; index_q++)
		{
			m_last_load_queries[index_q].Set(queries[index_q]);
		}
		LogDebugMessageIfDef( APL_T("Формирование экстента пришедших инстансов"));

		if(!m_hide_mode && m_IsWaitDlg && dbuf_out.m_Size > 500000)
		{
			wd_trhead = StartTransportWaitDlg( APL_T("Формирование экстента пришедших инстансов"));
		}
		//placement->MakeExtFromDbuf(dbuf_out,m_last_load_result);

		if(ext_out != 0)
		{
			ext_out->Clear();
			bool last_unique = ext_out->Unique;
			ext_out->Unique = false;
			ext_out->Append(m_last_load_result);
			// проверЯть на уникальность не надо - это буфер с сервера, он по умолячанию уникален
			ext_out->Unique = last_unique;
		}
		if(wd_trhead != 0)
		{
			EndTransportWaitDlg(wd_trhead);
			wd_trhead = 0;
		}

		m_last_load_time_meter.Start();
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("Выход из NET_Load"));

	return true;
}//bool CaplNetStepData::NET_Load


/** Загружает с сервера информацию об атрибутах инстансов из экстента ext. Если в списке указаны
     *  атрибуты, то загружает значения только указанных атрибутов. Если список
     *  атрибутов пустой - загружает значения всех атрибутов.*/
bool CaplNetStepData::NET_LoadExtAttrPaths(aplExtent &ext, std::list<AttrPath> attr_paths, aplExtent *ext_out)
{
    CHECK_BD(false);
    CHECK_STATE(true, false, false);
    if(ext.GetSize()==0) return true;
	if(!m_is_correct_d_branch)
	{
		SetAPLError(APL_NET_CLI_SERVER_DN_SUPPORT_FUNCTION, APL_T("Сервер приложений не поддерживает функцию NET_LoadExtAttrPaths. Обновите сервер приложений!"));
		return false;
	}

    CaplLoadData ld(this);
    int portion=0, portion_size=1000;
    bool load_all=attr_paths.size()==0 || attr_paths.begin()->attr==0;
    for(int i=0; i<ext.GetSize(); i++)
    {
        portion++;
        ld.AddQuery(0, ext.GetAt(i), load_all);
        if(portion >= portion_size || i==ext.GetSize()-1)
        {
			for ( std::list<AttrPath>::iterator it=attr_paths.begin(); it!= attr_paths.end(); ++it )
			{
				if(it->attr==0)continue;
				ld.AddQuery(0, *it);
			}

            ld.LoadEx(true, ext_out);
            portion=0;
        }
    }
    return true;
}


/** Загружает с сервера информацию об атрибутах инстансов из экстента ext. Если в списке указаны
     *  атрибуты, то загружает значения только указанных атриюутов. Если список
     *  атрибутов пустой - загружает значения всех атрибутов.*/
bool CaplNetStepData::NET_LoadExtAttrs(aplExtent &ext, std::list<CaplAttr *> attrs, aplExtent *ext_out)
{
    CHECK_BD(false);
    CHECK_STATE(true, false, false);

    if(ext.GetSize()==0) return true;

    CaplLoadData ld(this);
    int portion=0, portion_size=1000;
    bool load_all=attrs.size()==0;
    for(int i=0; i<ext.GetSize(); i++)
    {
        portion++;
        ld.AddQuery(0, ext.GetAt(i), load_all);
        if(portion >= portion_size || i==ext.GetSize()-1)
        {
			for ( std::list<CaplAttr *>::iterator it=attrs.begin(); it!= attrs.end(); ++it )
				ld.AddQuery('d', 0, 0,  *it, true, true);

            portion=0;
        }
    }
	ld.LoadEx();
    return true;
}

bool CaplNetStepData::NET_LoadInstAttrs(CaplInstance *inst, std::list<CaplAttr*> attrs, aplExtent *ext_out)
{
	if(inst==0) return false;
	aplExtent ext;
	ext.Add(inst);
	return NET_LoadExtAttrs(ext, attrs, ext_out);
}


/************************************************************************
 * 	Устанавливает или отключает формированный режим чтения данных из БД (см	параметр forced в функции NET_Load).
 ************************************************************************/
bool CaplNetStepData::SetLoadForced(bool forced)
{
	bool prev = globalLoadForceMode.Get() > 0;
	globalLoadForceMode = forced;
	return prev;
}//bool CaplNetStepData::NET_Load


/************************************************************************/
/* 	Окончательная функция загрузки словаря с сервера
/************************************************************************/
bool CaplNetStepData::NET_LoadDictionaryFromServer(LPCTSTR wait_text)
{
	CaplStackLogger stack_logger(__APL_FUNC__);

	LogDebugMessageIfDef(__APL_FUNC__);

	bool res = true;
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	long blob_size;
	__int64 blob_size64;
	BYTE *dict_array = 0;
	bool dict_alternate_load = true;
	CaplDataBuf dictDbuf;

	try
	{
		//Грузим имя словаря и путь к нему на сервере
		UINT32 BufNameSize = MAX_STR_LEN;
		UINT32 BufFilePathSize = MAX_STR_LEN*8;
		BYTE BufName[MAX_STR_LEN];
		BYTE BufFilePath[MAX_STR_LEN];

		AplNetErrors aplError = APL_NET_OK;
		m_AplTransport->SetWaitDlgText(wait_text);

WriteTickLog(_T("AT_DCGetDictName"));

		// размер буферов в байтах
		THROW_IF(!m_AplTransport->AT_DCGetDictName(aplError, &BufNameSize, BufName, &BufFilePathSize, BufFilePath), aplError);

		THROW_CHECK(aplError);

		m_local_data = new CaplNetStepData();
		m_local_data->m_WarningPrintMode = this->m_WarningPrintMode;

		BYTE temporary_buf[MAX_STR_LEN];
		
		// здесь имя словаря и его БЛОБа приходят с юникодного сервера - в юникоде
		CString dict_name = ConvertStringFromServerEnc(BufName, temporary_buf);
		CString dict_path = ConvertStringFromServerEnc(BufFilePath, temporary_buf);

WriteTickLog(_T("NET_LoadFileFromSrv"));
		// передаваем с сервера сам файл словаря
		if(NET_FileSrvSize((LPCTSTR)dict_path, blob_size64))
		{
			if(blob_size64 > MAXLONG)
			{
				CString tmp;
				tmp.Format(APL_T("Сервер вернул размер словаря %I64d. Это явно неверный размер; перезапустите сервер приложений."), blob_size64);
				MY_THROW_EX(APL_NET_CLI_FILE_BIG, tmp);
			}
			blob_size = (long) blob_size64;
			dict_array = new BYTE[blob_size];
			if(NET_LoadFileFromSrv2Memory((LPCTSTR)dict_path, dict_array, blob_size))
			{
				dictDbuf.SetExternalData(dict_array, blob_size);
				WriteTickLog(_T("LoadDictionaryFromMemory"));
				res = LoadDictionaryFromMemory(dictDbuf);
				WriteTickLog(_T("m_local_data->LoadDictionaryFromMemory"));
				m_local_data->LoadDictionaryFromMemory(dictDbuf);
				dict_alternate_load = false;
			}
		}
		// Если сервер новый - вернет реальный размер. Если старый - то останется MAXLONG
		m_AplTransport->AT_GetMaxBlobSize64(aplError, m_MaxBlobSize);


		if(dict_alternate_load)
		{
			m_ListAddSchemes = m_Ini.GetString(_T("Options"), _T("DefSchemaExtended"), _T(""));
			m_local_data->m_ListAddSchemes = m_ListAddSchemes;
			res = LoadDictionary((LPCTSTR)dict_name);
			m_local_data->LoadDictionary((LPCTSTR)dict_name);
		}
	}
	catch(SaplErrorDescription error)
	{
		//SetAPLError(error.m_err_code);
		SetAPLError(error);
		res = false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	if(dict_array != NULL) delete dict_array;

	WriteTickLog(_T(""));
	LogDebugMessageIfDef( APL_T("CaplNetStepData::NET_LoadDictionaryFromServer: Выход"));

#ifdef COUNT_TIMES
	tmpp.Format(_T("Всего: %i\n"), tmpp_sum);
	list_times.Add(tmpp);
	GetClearDebufStrArr(tmpp);
	AfxMessageBox(tmpp);
#endif // #ifdef COUNT_TIMES

	return res;
}//bool CaplNetStepData::NET_LoadDictionaryFromServer(LPCTSTR schema){


/************************************************************************/
/* 
/************************************************************************/
bool CaplNetStepData::NET_SaveChanges(LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_SaveChanges"));

	bool ret_val = true;
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	LogDebugMessageIfDef( APL_T("Вход в NET_SaveChanges"));
	if(m_is_readonly)
	{
		if(!m_hide_mode)AfxMessageBox( APL_T("БД доступна только для чтения. Сохранение невозможно"), MB_ICONERROR);
		return false;
	}
	aplExtent* saved_extents = 0;
	int num_portions, portion;
	CaplDataBuf dbuf_in, dbuf_out;
	//	aplExtent inSavInst;
	aplExtent outSavInst;
	aplExtent outSavAttrInst;
	int i=0;
	//	bool is_last = false;

	m_num_save++;

	try
	{
		TEST_RECONNECT(false);
		
		// старый запрос может быть неактуален- мы что-то создали, что-то изменили. Очищаем хвосты
		ClearLastQuery();

		m_AplTransport->SetWaitDlgText(wait_text== 0 ? APL_T("Сохранение изменений") : wait_text);

		CaplLoadData ld(this, DEF_SOURCE);
		if(!ActualizeChangedAggr(ld, (aplExtent&)instances))
		{
			return false;
		}
		//Подготавливаем данные для передачи серверу

		//	inSavInst.Unique = false;
		outSavInst.Unique = false;
		outSavAttrInst.Unique = false;
		// пачинаем сохранять с нулевой инстансы
		//	int i0 = 0,i1 = 0;
#ifdef _DEBUG
		//	SaveToTextFile("d:\\_swh\\2.txt");
#endif
		m_PercentSCh = 0;
		if(m_SizePortionSavedInst == 0)
		{

			num_portions = 1;
			saved_extents = (aplExtent*)&instances;
		}
		else
		{
			//ldiv_t tt = div(global_size, SizeDose);
			ldiv_t rr = div(instances.Size, (long)m_SizePortionSavedInst);

			num_portions = rr.quot;
			if(rr.rem > 0) num_portions++;

			saved_extents = new aplExtent[num_portions];
			portion = 0;
			for(i=0; i <instances.Size; i++)
			{
				saved_extents[portion].Unique = false;
				saved_extents[portion].Add(instances[i]);
				if(saved_extents[portion].GetSize() >= m_SizePortionSavedInst)
				{
					portion++;
					if(portion >= num_portions)
					{
						MY_THROW_EX(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при разделении данных на порции!!!!"));
					}
				}
			}
		}

		for(portion = 0; portion<num_portions; portion++)
		{
			outSavInst.Clear();
			m_PercentSCh = div(portion*100, num_portions).quot;
			dbuf_in.Clear();
#ifdef LOGGING
			int it;
			tmp_buf.Format( APL_T("\n\n\n========================= Порция сохранения %i ==============================="),portion);
			APL_NET_LogMessage(tmp_buf);
			for(it = 0; it<saved_extents[portion].GetSize(); it++)
			{
				tmp_buf.Format(_T("portion %i -> inst 0x%08X"), portion, saved_extents[portion].GetAt(it));
				APL_NET_LogMessage(tmp_buf);
			}
#endif
			if(!_SaveExtToDataBuf(dbuf_in, saved_extents[portion], outSavInst, outSavAttrInst))
			{
				MY_THROW_EX(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при упаковке данных в стандартный буфер!!!"));
			}
			//Отладочный кусок
#ifdef _DEBUG
			if(!m_csTestFileName.IsEmpty())
			{	
				CFileException pError;
				CFile file;
				if(file.Open(m_csTestFileName,CFile::modeCreate|CFile::modeWrite|CFile::typeBinary,&pError))
				{
					bool err=false;
					file.Write(dbuf_in.m_data,dbuf_in.m_Size);
					file.Flush();
					file.Close();
				}
			}
#endif

			//			CaplNetStepData t_data;
			//			t_data.ApploadFromDataBuf(dbuf);
			//			t_data.SaveToFile("d:\\_swh\\_server_data\\D3.std");

			if((outSavInst.GetSize()) == 0)
			{
				// Ничего сохранять не надо
				continue;
			}
			AplNetErrors aplError = APL_NET_OK; 

			LogDebugMessageIfDefFormat1( APL_T("Вызываем SaveChanges (размер буфера in %i)"), dbuf_in.m_Size);

			THROW_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCSaveChanges(aplError, dbuf_in, dbuf_out))

			//теперь надо вернуть идентификаторы
			//но сначала проверим правильность буфера
			char version[22]; // заголовки буферов - однобайтовые! 
			CHECK_PARAM_WITH_CLEAR( dbuf_out.m_Size==0);

			dbuf_out.Read(version, 22);

			CHECK_PARAM_WITH_CLEAR(CStringA(std_databuf_ret_header) != version);

			// Запись id в Instance
			CaplInstance *inst_b = 0;
			int id_b = 0;
			USHORT type_id_b = 0;

			LogDebugMessageIfDef( APL_T("Сервер вернул управление. Обновляем id"));

			for(i=0; i <outSavInst.Size; i++)
			{
				inst_b = outSavInst[i];
				dbuf_out.Read(&id_b, sizeof(id_b));
				dbuf_out.Read(&type_id_b, sizeof(type_id_b));

				if(inst_b->GetType() != 0)
				{
					if(inst_b->GetType()->id == type_id_b)
					{
						inst_b->SetId(id_b);
						instances.Sorted_id = false;
					}
					else
					{
						MY_THROW(APL_NET_CLI_MISSED_DATA);
					}
				}
				else
				{
					m_SavedDeleted.Add(inst_b);
				}
			}
			CSortClass::SortExtentByInst(m_SavedDeleted);

			// блядство! Перенесли сбрасывание флага изменгившихся атрибутов опосля вызова сервера - теперь приходится передавать
			// наружу из функции формировнаия буфера дополнительный экстент - и устраивать цикл по нему - потому что в выходном (outSavInst) есть инстансы
			// у которых атрибуты не были сохранены! (т.е. те инстансы, на которые ссылаются инстансы из saved_extents[portion])
			for(i=0; i <outSavAttrInst.Size; i++)
			{
				inst_b = outSavAttrInst[i];
				if(inst_b->GetType() != 0 && !(inst_b->GetTemporary()) && inst_b->attrs != 0)
				{
					int j;
					for(j = 0; j<inst_b->GetType()->all_attrs.Size; j++)
					{
						CaplInstance::CaplValueDefinition *attrval = &(inst_b->attrs[j]);
						if(attrval != 0) attrval->changed = false; // - раньше флаг сбрасывался при заполнении буфера, а теперь - здесь
					}
				}
			}
			m_AplTransport->ClearInternalData();
			// новые инстансы тоже должны быть отсортированы по возрастанию - перед любыми операциями!
			CSortClass::SortInstById(instances);
			// Читаем текущее состояние измененных агров
			ld.m_placement = 0;
			ld.LoadEx();
#ifdef _DEBUG
			//			SaveToTextFile("d:\\_swh\\3.txt");
#endif
		}	
		
		aplUsers *table;
		for(i=0; i <m_ListDelaedAccess.Size; i++)
		{
			table = m_ListDelaedAccess[i];
			if(table == 0)continue;
			NET_SetGroupeAccessInternal(*table);
		}

		m_ListDelaedAccess.Clear();

		aplListAccess2Inst *ListAccess2Inst;
		for(i=0; i <m_ListDelaedAccessBI.Size; i++)
		{
			ListAccess2Inst = m_ListDelaedAccessBI[i];
			if(ListAccess2Inst == 0)continue;
			NET_SetGroupeAccessBIInternal(*ListAccess2Inst);
		}

		m_ListDelaedAccessBI.Clear();

	}
	catch(SaplErrorDescription error)
	{
		LogDebugMessageIfDef( APL_T("Ошибка в NET_SaveChanges!!!"));
		SetAPLError(error);
		ret_val = false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	if(m_SizePortionSavedInst != 0 && saved_extents != 0)
	{
		delete[] saved_extents;
		saved_extents = 0;
	}

	m_PercentSCh = 100;

	LogDebugMessageIfDef( APL_T("Выходим из NET_SaveChanges"));

	return ret_val;
}//bool CaplNetStepData::NET_SaveChanges(){

/** Сохраняет в БД изменения указанных инстансов и тех, на кого ссылаются указанные
@param ext - список сохраняемых инстасов
@param wait_text сообщение, которое будет выводиться в диалоге ожидания ответа сервера. При пустом значении выводится сообщение по умолчанию
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */

bool CaplNetStepData::NET_SaveExtent(CaplInstance* inst_saved, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);


	aplExtent ext_saved; ext_saved.Add(inst_saved);
	return NET_SaveExtent(ext_saved, wait_text);
}

bool CaplNetStepData::NET_SaveExtent(aplExtent &ext_saved, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_SaveExtent"));

	bool ret_val = true;
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	LogDebugMessageIfDef( APL_T("Вход в NET_SaveExtent"));
	if(m_is_readonly)
	{
		if(!m_hide_mode) AfxMessageBox( APL_T("БД доступна только для чтения. Сохранение невозможно"), MB_ICONERROR);
		return false;
	}
	//Подготавливаем данные для передачи серверу
	CaplDataBuf dbuf_in, dbuf_out;
	aplExtent outSavInst;
	aplExtent outSavAttrInst;
	int i=0;
	aplExtent* saved_extents = 0;
	ldiv_t rr;
	AplNetErrors aplError = APL_NET_OK; 
	// Запись id в Instance
	CaplInstance *inst_b = 0;
	int id_b = 0;
	USHORT type_id_b = 0;
	int num_portions, portion;

	try
	{
		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text == 0 ? APL_T("Сохранение изменений") : wait_text);

		CaplLoadData ld(this,DEF_SOURCE);
		if(!ActualizeChangedAggr(ld, ext_saved))
		{
			return false;
		}

		outSavInst.Unique = false;
		outSavAttrInst.Unique = false;
		// пачинаем сохранять с нулевой инстансы
		m_PercentSCh = 0;
		if(m_SizePortionSavedInst == 0)
		{
			num_portions = 1;
			saved_extents = (aplExtent*)&ext_saved;
		}
		else
		{
			rr = div(ext_saved.Size, (long)m_SizePortionSavedInst);
			num_portions = rr.quot;
			if(rr.rem>0) num_portions++;
			saved_extents = new aplExtent[num_portions];
			portion = 0;
			for(i=0; i <ext_saved.Size; i++)
			{
				saved_extents[portion].Add(ext_saved[i]);
				if(saved_extents[portion].GetSize() >= m_SizePortionSavedInst)
				{
					portion++;
					if(portion >= num_portions)
					{
						MY_THROW_EX(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при разделении данных на порции!!!!"));
					}
				}
			}
		}


		for(portion = 0;portion<num_portions;portion++)
		{
			outSavInst.Clear();

			m_PercentSCh = div(portion*100, num_portions).quot;
			dbuf_in.Clear();
			if(!_SaveExtToDataBuf(dbuf_in, saved_extents[portion], outSavInst, outSavAttrInst))
			{
				MY_THROW_EX(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при упаковке данных в стандартный буфер!!!"));
			}

			if((outSavInst.GetSize()) == 0)
			{
				// Ничего сохранять не надо
				continue;
			}


			LogDebugMessageIfDefFormat1( APL_T("Вызываем функцию сохранения с сервера (размер буфера in %i)"), dbuf_in.m_Size);

			THROW_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCSaveChanges(aplError, dbuf_in, dbuf_out))

			//теперь надо вернуть идентификаторы
			//но сначала проверим правильность буфера
			char version[22]; // заголовки буферов - однобайтовые! 

			CHECK_PARAM_WITH_CLEAR(dbuf_out.m_Size == 0);

			dbuf_out.Read(version,22);
			CHECK_PARAM_WITH_CLEAR(CStringA(std_databuf_ret_header) != version);

			LogDebugMessageIfDef( APL_T("Сервер вернул управление. Обновляем id"));

			for(i=0; i <outSavInst.Size; i++)
			{
				inst_b = outSavInst[i];
				dbuf_out.Read(&id_b, sizeof(id_b));
				dbuf_out.Read(&type_id_b, sizeof(type_id_b));

				if(inst_b->GetType() != 0)
				{
					if(inst_b->GetType()->id == type_id_b)
					{
						inst_b->SetId(id_b);
						instances.Sorted_id = false;
					}
					else
					{
						MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Тип данных пришедших с сервера не совпадает с типом на клиенте"));
					}
				}
			}
			for(i=0; i <outSavAttrInst.Size; i++)
			{
				inst_b = outSavAttrInst[i];
				if(inst_b->GetType() != 0 && !(inst_b->GetTemporary()) && inst_b->attrs != 0)
				{
					int j;
					for(j = 0; j<inst_b->GetType()->all_attrs.Size; j++)
					{
						CaplInstance::CaplValueDefinition *attrval = &(inst_b->attrs[j]);
						if(attrval != 0)attrval->changed = false; // - раньше флаг сбрасывался при заполнении буфера, а теперь - здесь
					}
				}
			}

			// новые инстансы тоже должны быть отсортированы по возрастанию id - перед любыми операциями!
			CSortClass::SortInstById(instances);
			// Читаем текущее состояние измененных агров
			ld.m_placement = 0;
			ld.LoadEx();
#ifdef _DEBUG
				//			SaveToTextFile("d:\\_swh\\3.txt");
#endif
		}	
	}
	catch(SaplErrorDescription error)
	{
		LogDebugMessageIfDef( APL_T("Ошибка в NET_SaveExtent!!!"));
		SetAPLError(error);
		ret_val = false;
	}
	CATH_MEMORY_ANY_EXEPTION;
	
	m_AplTransport->ClearInternalData();

	if(m_SizePortionSavedInst != 0 && saved_extents != 0)
	{
		delete[] saved_extents;
		saved_extents = 0;
	}

	m_PercentSCh=100;

	LogDebugMessageIfDef( APL_T("Выходим из NET_SaveExtent"));

	return ret_val;
}

/************************************************************************/
/* Помещает экстент в буфер без всяких проверок (и пока что без атрибутов)
/************************************************************************/
bool CaplNetStepData::PutExtToDataBuf(aplExtent &ext, CaplDataBuf &dbuf)
{
	int i = 0, k = 0, id_b = 0;
	USHORT type_id_b = 0;
	CaplInstance *inst_b = 0;

#ifdef _UNICODE
	if(m_global_ansi_string)
		dbuf.Add(std_databuf_header, 20); 
	else
		dbuf.Add(std_databuf_header_u, 20);
#else // #ifdef _UNICODE
	dbuf.Add(std_databuf_header, 20);
#endif // #ifdef _UNICODE
	dbuf.m_ansi_string = m_global_ansi_string;


	dbuf.AddStrBuf(m_CurSchema);

	dbuf.Add(&m_MaxInstNum, sizeof(m_MaxInstNum));
	k = ext.Size; // 5
	dbuf.SetSize(50*k);
	dbuf.Add(&k, sizeof(k));

	for(i=0; i <ext.Size; i++)
	{
		inst_b = ext[i];
		if(inst_b != 0)
		{
			id_b = inst_b->GetId();
			if(inst_b->GetTemporary())continue;
			if(inst_b->GetType() != 0)
			{
				type_id_b = (short)inst_b->GetType()->id;
			}
			else
			{
				type_id_b = 0;
			}
			dbuf.Add(&id_b, sizeof(int));
			dbuf.Add(&type_id_b, sizeof(short));
			BYTE access_mode = inst_b->GetAccessmode();
			dbuf.Add(&access_mode, sizeof(BYTE));
		}
		else
		{
#ifdef _DEBUG
			inst_b = 0;
#endif
		}
		PumpTimerPaint();
	}
	return true;
}

//перегруженные

/************************************************************************/
/* Эта перегрузка возвращает экстент с со списком сохраняемых инстансов
/************************************************************************/
bool CaplNetStepData::_SaveExtToDataBuf(CaplDataBuf &dbuf, aplExtent &inSavInst, aplExtent &outSavInst, aplExtent &ext4attr)
{
	LogDebugMessageIfDef(_T("CaplNetStepData::_SaveExtToDataBuf"));
	int i = 0, j = 0, k = 0, id_b = 0;
	USHORT type_id_b = 0, attr_id_b = 0;
	CaplInstance *inst_b = 0;
	//pCaplInstance *instmap = 0;
	CaplMap instmap; //instmap.UniqueIn = false;
	outSavInst.Clear();
	ext4attr.Clear();
	// Для предотвращения тормозов при добавлении
	bool flag_uq = outSavInst.Unique; 
	outSavInst.Unique = false;
	ext4attr.Unique = false;

	// получение списка сохраняемых Instance
	LogDebugMessageIfDefFormat1( APL_T("Сохранение в буфер: получение списка сохраняемых Instance; inSavInst size: %i"), inSavInst.Size);
	bool added_out,added_attr;

	for(i=0; i <inSavInst.Size; i++)
	{
		added_attr = false;
		added_out = false;
		inst_b = inSavInst[i];
		if(inst_b == 0)
		{
			SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE);
			continue;
		}
		if(inst_b->GetTemporary())
		{
			continue;
		}
		if(inst_b->GetAccessmode() > aplRW)
		{
			continue;
		}
		if(inst_b->GetType() == 0)
		{
			if(inst_b->GetId() == 0)
			{
				// эту инстанс еще не сохранили на сервере - и уже удалили на клиенте. Ничего с ней делать не надо
				continue;
			}
			if(!inst_b->IsDeleted())
			{
				// эта инстанс была удалена не нашим клиентом (ну или баг - с сервера пришла с пустым типом). В буфер ее писать нельзя
				continue;
			}
			// проверяем, сохраняли ли мы эту удаленную инстанс на сервере
			if(aplQFindInstInExtent(m_SavedDeleted, inst_b) == -1)
			{
				outSavInst.Add(inst_b);
#ifdef LOGGING
				tmp_buf.Format( APL_T("Будем сохранять inst (del) 0x%08X"), inst_b);
				APL_NET_LogMessage(tmp_buf);
#endif
			}
			continue;
		}
		if(inst_b->GetId() == 0)
		{
			outSavInst.Add(inst_b);
			added_out = true;
#ifdef LOGGING
			tmp_buf.Format( APL_T("Будем сохранять inst 0x%08X"), inst_b);
			APL_NET_LogMessage(tmp_buf);
#endif
		}
		if(inst_b->attrs != 0)
		{
			for(j = 0; j<inst_b->GetType()->all_attrs.Size; j++)
			{
				// сохраняем измененные атрибуты 
				if(inst_b->attrs[j].changed == true)
				{
					if(!added_out)
					{
						outSavInst.Add(inst_b);
						added_out = true;
#ifdef LOGGING
						tmp_buf.Format( APL_T("Будем сохранять inst 0x%08X"),inst_b);
						APL_NET_LogMessage(tmp_buf);
#endif
					}
					if(!added_attr)
					{
						ext4attr.Add(inst_b);
						added_attr = true;
#ifdef LOGGING
						tmp_buf.Format( APL_T("Будем сохранять inst 0x%08X"),inst_b);
						APL_NET_LogMessage(tmp_buf);
#endif
					}
					// добавляем в outSavInst те инстансы, на кого ссылается атрибут
					aplAddInstValToExtent(outSavInst,&(inst_b->attrs[j].value));
				}
			}
		}
		PumpTimerPaint();
	}
	// Чтобы реально сократить время на поиски
	LogDebugMessageIfDefFormat2(_T("1 outSavInst size: %i; ext4attr size %i"), outSavInst.Size, ext4attr.Size);

	CSortClass::SortExtentByInst(outSavInst);
	CSortClass::MakeExtUnique(outSavInst,true);

	LogDebugMessageIfDefFormat1(_T("2 outSavInst size: %i"), outSavInst.Size);

	dbuf.m_ansi_string = m_global_ansi_string;
	if(m_global_ansi_string)
	{
		dbuf.Add(std_databuf_header, 20);
	}
	else
	{
		dbuf.Add(std_databuf_header_u, 20);
	}

	dbuf.AddStrBuf(m_CurSchema);

	dbuf.Add(&m_MaxInstNum, sizeof(m_MaxInstNum));
	k = outSavInst.Size; // 5
	dbuf.SetSize(50*k);
	dbuf.Add(&k, sizeof(k));
	instmap.SetSize(outSavInst.Size*8);
	LogDebugMessageIfDef( APL_T("Сохранение в буфер: Запись Instance"));

	// Запись Instance
	// Запись удаленных
	UINT32 i_inst=0;
	for(i=0; i <outSavInst.Size; i++)
	{
		inst_b = outSavInst[i];
		if(inst_b != 0)
		{
			id_b = inst_b->GetId();
			if(inst_b->GetTemporary()) continue;
			if(inst_b->GetType() != 0)
			{
				type_id_b = (short)inst_b->GetType()->id;
			}
			else
			{
				type_id_b = 0;
			}
			dbuf.Add(&id_b, sizeof(int));
			dbuf.Add(&type_id_b, sizeof(short));
			BYTE access_mode = inst_b->GetAccessmode();
			dbuf.Add(&access_mode, sizeof(BYTE));

			instmap.Add((long)inst_b, i_inst);
			i_inst++;
#ifdef LOGGING
			tmp_buf.Format( APL_T("Записали в буфер inst 0x%08X"), inst_b);
			APL_NET_LogMessage(tmp_buf);
#endif

		}
		else
		{
#ifdef _DEBUG
			inst_b = 0;
#endif
		}
		PumpTimerPaint();
	}
	instmap.SortIn();

	LogDebugMessageIfDef( APL_T("Сохранение в буфер: Запись атрибутов"));

	// Запись атрибутов
	for(i=0; i <ext4attr.Size; i++)
	{
		inst_b = ext4attr[i];
#ifdef LOGGING
		tmp_buf.Format( APL_T("Входим в атрибуты inst 0x%08X"),inst_b);
		APL_NET_LogMessage(tmp_buf);
#endif
		if(inst_b == 0)
			continue;
		if(inst_b->GetTemporary())
			continue;
		if(inst_b->GetAccessmode()>aplRW) 
			continue;//Эти атрибуты сервер примет за ошибку
		if(inst_b->GetType() == 0)
			continue;//Это удаленные инстансы
		if(inst_b->attrs == 0)
			continue;//Здесь нет атрибутов
		id_b = instmap.QGetByIn((long)inst_b);
		if(id_b==-1)
		{
			SetLastErrorEx(APLAPIERR_UNKNOWN_EXEPTION,false,_T(__FILE__),__LINE__,(CaplInstance*)inst_b,(CaplAttr *)-2,
			(CaplEntity *)-2,(CaplEntity *)-2,APL_T("Инстанс отсутствует в списке записанных в буфер."));
			return false;
		}
		dbuf.Add(&id_b, sizeof(id_b));
#ifdef LOGGING
		tmp_buf.Format( APL_T("Будем сохранять атрибуты inst 0x%08X (позиция %i)"), inst_b, id_b);
		APL_NET_LogMessage(tmp_buf);
#endif
		k = 0;
		for(j = 0; j<inst_b->GetType()->all_attrs.Size; j++)
		{
			if(inst_b->attrs[j].changed == true || inst_b->GetId() == 0) k++;
		}
		dbuf.Add(&k, sizeof(k));

		for(j = 0; j<inst_b->GetType()->all_attrs.Size; j++)
		{
			CaplInstance::CaplValueDefinition *attrval = &(inst_b->attrs[j]);
			if(attrval->changed == true || inst_b->GetId() == 0)
			{
				if(inst_b->GetType()->all_attrs[j]->type == aplBINARY && attrval->value.type == aplBINARY 
					&& attrval->value.binval !=0 && attrval->value.binval->m_Size > 0)
				{
					//SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE);
					SetLastErrorEx(APLAPIERR_BADTYPE,false,_T(__FILE__),__LINE__,(CaplInstance*)inst_b,(CaplAttr *)inst_b->GetType()->all_attrs[j],
										(CaplEntity *)-2,(CaplEntity *)-2,APL_T("Операции с атрибутами с типом aplBINARY запрещены."));
					return false;
				}
				attr_id_b = (short)inst_b->GetType()->all_attrs[j]->id;
				dbuf.Add(&attr_id_b, sizeof(attr_id_b));
				if(attr_id_b == m_attr_apl_user__pass->id && !m_ServerHashMethod.IsEmpty())
				{
					// Сохраняется пароль, и на сервере требуются хешированные пароли.
					// Проверяем, и если пароль открытый - заменяем на хэш.
					CString pwd, pwd_crypt, method, errMess;
					attrval->value.Get(pwd);
					if(!internalHashGetMethod(pwd, method))
					{
						if(internalCryptGetHash(m_ServerHashMethod, pwd, pwd_crypt, errMess))
						{
							attrval->value.Set(pwd_crypt);
						}
						else
						{
							m_MainLog.LogMessage(errMess, false, true, true, false, __APL_FUNC__, 0, 0, _T(__FILE__), _T(__DATE__), __LINE__);
						}
					}
				}
				aplAppendBufValue(dbuf, instmap, &(attrval->value));
				// attrval->changed = false; - флаг теперь сбрасывается после успешного ответа сервера
				// Сохранение архивной копии
				if(attrval->value.type == aplAGGR)
				{
					if(attrval->ArhiveAggr == 0) 
						attrval->ArhiveAggr = new CaplAggr;
					else 
						attrval->ArhiveAggr->Clear();
					attrval->ArhiveAggr->Append(*(attrval->value.aggrval));

				}
			}
		}
		PumpTimerPaint();
	}
	//if (instmap != 0) delete instmap;
	outSavInst.Unique = flag_uq;

	LogDebugMessageIfDef( APL_T("_SaveExtToDataBuf Выход"));

	return true;
}//bool CaplNetStepData::_SaveExtToDataBuf(CaplDataBuf &dbuf, aplExtent &inSavInst, aplExtent &outSavInst)


/************************************************************************/
/* Служебная функция.  Возвращает буфер со строкой и размер строки для передачи на сервер
/* Если кодировка сервера сопадает с кодировкой клиента - возвращает ту же строку, что и на входе
/* Если кодировка сервера ansi а клиента unicode - конвертирует строку в буфер temporary_buf и
/* возвращает ссылку на него. В size_buf возвращается размер буфера в байтах
/************************************************************************/
BYTE* CaplNetStepData::ConvertString2ServerEnc(LPCTSTR source, BYTE* temporary_buf, UINT32 &size_buf)
{
	size_buf = _strlen(source) + 1;
#ifdef _UNICODE
	if(m_global_ansi_string)
	{
		CaplStringAdapter str_ad(source);
		memcpy(temporary_buf, (const char*)str_ad, size_buf);
	}
	else
	{
#ifndef __linux__
		ASSERT(2==sizeof(TCHAR));
		size_buf *= sizeof(TCHAR);
		memcpy(temporary_buf, source, size_buf);
#else
		ASSERT(4==sizeof(TCHAR));

		BYTE *strUcs2=0;
		int strUcs2Len=0;
		if(bConvertTextWithIconv(aplUTF32LE,(BYTE*)source,size_buf*sizeof(TCHAR),aplUCS2,strUcs2,strUcs2Len))
		{
			if(0!=strUcs2)
			{
				memcpy(temporary_buf, strUcs2, strUcs2Len);
				size_buf = strUcs2Len;
				delete strUcs2;
			}
		}
#endif
	}
#else // #ifdef _UNICODE
	if(m_global_ansi_string)
	{
		memcpy(temporary_buf, source, size_buf);
	}
	else
	{
		CaplStringAdapter str_ad(source);
		CStringW str_w = (const wchar_t*)str_ad;
		size_buf = (str_w.GetLength()+1)*2;
		memcpy(temporary_buf, str_w.GetBuffer(), size_buf);
	}
#endif // #ifdef _UNICODE
	return temporary_buf;
}

/************************************************************************/
/* Служебная функция.  Декодирует строку пришедшую с сервера
/* Если кодировка сервера сопадает с кодировкой клиента - возвращает ту же строку, что и на входе с правильным приведением типа
// Если нужна перекодировка - копирует строку в правильной кодировке в temporary_buf и возвращает ссылку на него
/************************************************************************/
LPCTSTR CaplNetStepData::ConvertStringFromServerEnc(BYTE* source, BYTE* temporary_buf)
{
#ifdef _UNICODE
	if(m_global_ansi_string)
	{	
		if(temporary_buf==0)
		{
			ASSERT(false);
			return 0;
		}
		size_t size = strlen((const char*)source) + 1;
		CaplStringAdapter str_ad((const char*)source);
// Как будет конвертироваться строка с ANSI сервера а линуксовом клиенте?
// #ifndef _MSC_VER
// 		str_ad.m_bIsAnsiUtf8=false; // корреспондент ждет в буфере 1251
// #endif
		memcpy(temporary_buf, (wchar_t*)str_ad, size*sizeof(TCHAR));
		return (wchar_t*)temporary_buf;
	}
	else
	{
#ifndef __linux__
		ASSERT(2==sizeof(TCHAR));
		return (wchar_t*)source;
#else
		ASSERT(4==sizeof(TCHAR));
		BYTE *strUcs2=0; 
		int strUcs2Len=0;
		if(bConvertTextWithIconv(aplUTF32LE,(BYTE*)str,k*sizeof(TCHAR),aplUCS2,strUcs2,strUcs2Len))
		{
			if(0!=strUcs2)
			{
				Add((void*)strUcs2,strUcs2Len);
				delete strUcs2;
			}
		}
#endif
		return (wchar_t*)source;
	}
#else // #ifdef _UNICODE
	if(m_global_ansi_string)
	{	
		return (const char*)source;
	}
	else
	{
		if(temporary_buf==0)
		{
			ASSERT(false);
			return 0;
		}
		CaplStringAdapter str_ad((const wchar_t*)source);
		CString str = (LPCTSTR)str_ad;
		memcpy(temporary_buf, str.GetBuffer(), str.GetLength()+1);

		return (LPCTSTR)temporary_buf;
	}
#endif // #ifdef _UNICODE
	return 0;

}


/************************************************************************/
/* Записывает файл на сервер в каталог с логами сервера
/* 
/************************************************************************/
/* 4del - убрано, так как никому не нужно много лет
bool CaplNetStepData::NET_SaveFile2Spetial(LPCTSTR file_source, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, false, false);

	CHECK_PARAM(file_source==NULL);

	FILE* file = NULL;
	__int64 filelen;
	CString full_path, name;

	try{

		TEST_RECONNECT(false);

		m_AplTransport->SetWaitDlgText(wait_text);

		// новое определение длины файла
		file = _fopen(file_source, _T("rb"));
		if(NULL != file)
		{
			_fseeki64(file, 0, SEEK_END);
			filelen = _ftelli64(file);
			fclose(file);
		}
		else
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNF, CString(APL_T("Ошибка при сохранении файла ")) + file_source);
			return false;
		}

		if(filelen > m_MaxBlobSize)
		{
			CString tmp;
			tmp.Format(APL_T("Сервер приложений не поддерживает файлы размером больше %i. Обновите версию сервера чтобы сохранить файл "), m_MaxBlobSize);
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_FILE_BIG, tmp + file_source);
			return false;
		}

		AplNetErrors aplError = APL_NET_OK;
		UINT32 SizeNameSourceFile;
		BYTE *NameSourceFile;
		//Создаем переменные
		UINT32 SizeNameBLOB = MAX_STR_LEN;
		BYTE NameBLOB [MAX_STR_LEN];

		full_path = file_source;
		int pos = full_path.ReverseFind(_T('\\'));
		if(pos == -1)
		{
			name = full_path;
		}
		else
		{
			name = full_path.Mid(pos+1);
		}
		
		BYTE temporary_buf[MAX_STR_LEN];
		NameSourceFile = ConvertString2ServerEnc(file_source, temporary_buf, SizeNameSourceFile);

		NameBLOB[0] = -1;
		UINT32 SizeDose;
		if(filelen > SIZE_DOSE_BLOB)
		{
			SizeDose = SIZE_DOSE_BLOB;
		}
		else
		{
			SizeDose = (UINT32) filelen;
		}
		if(m_Protocol == 1 && SizeDose > SIZE_DOSE_BLOB_HTTP)
		{
			SizeDose = SIZE_DOSE_BLOB_HTTP;
		}
		if(SizeDose == 0)
			SizeDose = 1;

		CaplDataBuf DoseB(SizeDose);
		if(DoseB.GetMaxSize() < (int)SizeDose)
		{
			SetAPLErrorWithFileInfo(APL_NET_CLI_ERROR_PROCESSED);
			return false;
		}
		BYTE *Dose = (BYTE*)DoseB.GetBuffer();

		UINT32 SizeBLOB = 0;
		__int64 SizeBLOB64 = 0;

		//Создаем итератор
		if(!m_AplTransport->AT_DCCreateIterator64(aplError, &SizeNameBLOB, NameBLOB, SizeNameSourceFile, NameSourceFile, &SizeDose, &SizeBLOB64) || SPEC_ERR_CODES(aplError))
		{
			if(aplError == APL_SOCK_MISSED_FUNCTION)
			{
				if(filelen > MAXLONG)
				{
					CString tmp;
					tmp.Format(APL_T("Сервер приложений не поддерживает файлы размером больше %i. Обновите версию сервера чтобы сохранить файл "), MAXLONG);
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FILE_BIG, tmp + file_source);
					return false;
				}
				RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, NameBLOB, SizeNameSourceFile, NameSourceFile, &SizeDose, &SizeBLOB));
			}
			else
			{
				SetAPLErrorWithFileInfoEx(aplError, CString(APL_T("Ошибка при сохранении файла ")) + file_source);
				return false;
			}
		}

		NameBLOB[SizeNameBLOB] = 0;
		//Порциями читаем данные из файла и пишем на сервер
		file = _fopen(file_source, _T("rb"));
		if(file == NULL)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNF, CString(APL_T("Ошибка при чтении файла ")) + file_source);
			return false;
		}
		int size_curr = 0;
		__int64 portions = 0, indx = 0;
		UINT32 LastDose = 0;

		portions = filelen / SizeDose;
		__int64 quot = filelen - ((__int64)portions)*SizeDose;
		if(quot > 0)portions++;

		CDlgCopyBLOB* dlg = 0;
		if(portions>2)
		{
			dlg = new CDlgCopyBLOB();
		}
		if(dlg != 0)
		{
			dlg->m_titul= APL_T("Сохранение файла в БД");
			dlg->m_AplTransport = m_AplTransport;
			m_AplTransport->SetManageMode(MANAGE_ABSENT);
			dlg->StartThread();
		}

		do
		{
			if(dlg != 0)
			{
				if(dlg->SetProgress((int)((indx*100)/portions)))
				{
					aplError = (AplNetErrors)APL_NET_CLI_BREAK;
					break;
				}
			}
			size_curr = fread(Dose, 1, SizeDose, file);
			if(feof(file))
			{
				LastDose = 1;
			}
			else
			{
				LastDose = 0;
			}

			if(!m_AplTransport->AT_DCWriteToIterator(aplError, size_curr, Dose, LastDose) || SPEC_ERR_CODES(aplError))
			{
				if(aplError == APL_NET_OK){aplError = (AplNetErrors)APL_NET_CLI_ERROR_PROCESSED;}
				break;
			}
			indx++;
		}while(feof(file) == 0); 

		fclose(file);

		if(dlg != 0)
		{
			dlg->StopThread();
			m_AplTransport->SetManageMode(MANAGE_WAITDLG);
			delete dlg;
		}

		if(SPEC_ERR_CODES(aplError))
		{
			SetAPLErrorWithFileInfo(aplError);
			AplNetErrors err; 
			m_AplTransport->AT_DCDeleteIterator(err, 0, 0);
			return false;
		}

	}
	CATH_MEMORY_ANY_EXEPTION;
	
	// Сохранять (SaveChange) не надо - файл уже на сервере в нужном каталоге 

	return true;
}
*/

/************************************************************************/
/* Читает указанный файл, сохраняет его на сервер под своим именем 
/* и записывает его серверное имя в указанный атрибут
/************************************************************************/
bool CaplNetStepData::NET_SaveBlobBN(CaplInstance *inst, LPCTSTR attr_name, LPCTSTR file_source, long &code, LPCTSTR file_name, LPCTSTR wait_text, int crc32)
{
	CaplStackLogger logger(__APL_FUNC__);

	CaplAttr *attr = 0;
	if(inst != NULL)
	{
		attr = GetAttrDefinition(inst->GetType(), attr_name);
	}
	else
	{
		return false;
	}
	return NET_SaveBlob(inst, attr, file_source, code, file_name, wait_text, crc32);
}

bool CaplNetStepData::NET_SaveBlob(CaplInstance *inst, CaplAttr *attr, LPCTSTR file_source, long &code, LPCTSTR file_name, LPCTSTR wait_text, int crc32)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(inst==NULL || attr==NULL || file_source==NULL);

	CaplValue val;
	FILE* file=NULL;
	__int64 filelen;

	try{
	
		TEST_RECONNECT(false);
	
		m_AplTransport->SetWaitDlgText(wait_text);

		//Проверка на существование атрибута
		if(attr->type != aplSTRING)
		{
			return false;
		}
		CaplStepData::GetAttr(inst, attr, val);
		if(val.type == aplNOTYPE)
		{
			val.Set(_T(""));
		}
		// Проверяем что у нас есть доступ к инстансу inst
		if(!PutAttr(inst, attr, val))
		{
			return false;
		}
		// запоминаем старый идентификатор БЛОБа
		CString NameOldBLOB;
		val.Get(NameOldBLOB);

		if(file_source == NULL)
		{
			SetAPLErrorWithFileInfo(APL_NET_CLI_MISSED_DATA);
			return false;
		}
		// новое определение длины файла
		file = _fopen(file_source, _T("rb"));
		if(NULL != file)
		{
			_fseeki64(file, 0, SEEK_END);
			filelen= _ftelli64(file);
			fclose(file);
		}
		if(NULL == file /*|| 0 == filelen*/) // как выяснилось, пустые файлы тоже должны обрабатываться
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNF, CString(APL_T("Ошибка при сохранении в БД файла ")) + file_source);
			return false;
		}
		if(filelen > m_MaxBlobSize)
		{
			CString tmp;
			tmp.Format(APL_T("Сервер приложений не поддерживает файлы размером больше %i. Обновите версию сервера чтобы сохранить файл "),m_MaxBlobSize);
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_FILE_BIG, tmp + file_source);
			return false;
		}


		AplNetErrors aplError = APL_NET_OK;
		//Создаем переменные
		UINT32 SizeNameSourceFile;
		BYTE *NameSourceFile, *NameOldBlob;
		UINT32 SizeNameBLOB = MAX_STR_LEN;
		BYTE NameBLOB[MAX_STR_LEN];
		memset(NameBLOB, 0, MAX_STR_LEN);

		BYTE temporary_buf[MAX_STR_LEN];
		if(file_name == 0)
			NameSourceFile = ConvertString2ServerEnc(file_source, temporary_buf, SizeNameSourceFile);
		else
			NameSourceFile = ConvertString2ServerEnc(file_name, temporary_buf, SizeNameSourceFile);

		UINT32 SizeDose;
		UINT32 LastDose = 0;

		if(filelen > SIZE_DOSE_BLOB)
		{
			SizeDose = SIZE_DOSE_BLOB;
		}
		else
		{
			SizeDose = (UINT32)filelen;
			LastDose = 1;
		}
		if(m_Protocol==1 && SizeDose > SIZE_DOSE_BLOB_HTTP)
		{
			SizeDose = SIZE_DOSE_BLOB_HTTP;
		}
		if(SizeDose == 0)
			SizeDose = 1;

		CaplDataBuf DoseB(SizeDose);
		if(DoseB.GetMaxSize() < (int)SizeDose)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при перекодировании строк в формат сервера"));
			return false;
		}
		BYTE *Dose = (BYTE*)DoseB.GetBuffer();

		// delete[] NameSourceFile; !!!! нельзя удалять !!!

		UINT32 SizeBLOB = 0;
		__int64 SizeBLOB64 = 0;

		//Создаем итератор
		if(!m_AplTransport->AT_DCCreateIterator64(aplError, &SizeNameBLOB, NameBLOB, SizeNameSourceFile, NameSourceFile,
													&SizeDose, &SizeBLOB64, inst->GetId(), crc32) || SPEC_ERR_CODES(aplError))
		{
			if(aplError == APL_SOCK_MISSED_FUNCTION)
			{
				if(filelen > MAXLONG)
				{
					CString tmp;
					tmp.Format(APL_T("Сервер приложений не поддерживает файлы размером больше %i. Обновите версию сервера чтобы сохранить файл "),MAXLONG);
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FILE_BIG, tmp + file_source);
					return false;
				}
				RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, NameBLOB, SizeNameSourceFile, NameSourceFile,
																			&SizeDose, &SizeBLOB, inst->GetId(), crc32));

			}
			else
			{
				SetAPLErrorWithFileInfoEx(aplError, CString(APL_T("Ошибка при сохранении в БД файла ")) + file_source);
				return false;
			}
		}

		// Этот текст нам уже не нужен
		NameSourceFile = 0;

		//Порциями читаем данные из файла и пишем на сервер
		file = _fopen(file_source, _T("rb"));
		if(file == NULL)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNF, CString(APL_T("Ошибка при чтении файла ")) + file_source);
			return false;
		}
		int ch = 0, indx = 0;
		__int64 portions = filelen / (__int64)SizeDose;
		if(filelen - portions*SizeDose > 0)portions++;

		CDlgCopyBLOB* dlg = 0;
		if(portions > 2)
		{
			dlg=new CDlgCopyBLOB();
		}
		if(dlg != 0)
		{
			dlg->m_titul = APL_T("Сохранение файла в БД");
			dlg->m_AplTransport = m_AplTransport;
			m_AplTransport->SetManageMode(MANAGE_ABSENT);
			dlg->StartThread();
		}

		do
		{
			if(dlg != 0)
			{
				if(dlg->SetProgress((int)((indx*100)/portions)))
				{
					aplError=(AplNetErrors)APL_NET_CLI_BREAK;
					break;
				}
			}
			ch = fread(Dose, 1, SizeDose, file);
			if(feof(file)>0)
				LastDose = 1;

			//Считаем простейшую чексумму
			for(int ji=0; ji <ch; ji++)
			{
				code += Dose[ji];
			}

			if(!m_AplTransport->AT_DCWriteToIterator(aplError, ch, Dose, LastDose)|| SPEC_ERR_CODES(aplError))
			{
				if(aplError == APL_NET_OK){aplError = (AplNetErrors)APL_NET_CLI_ERROR_PROCESSED;}
				break;
			}
			indx++;
		}while(LastDose == 0);

		fclose(file);
		
		if(dlg != 0)
		{
			dlg->StopThread();
			m_AplTransport->SetManageMode(MANAGE_WAITDLG);
			delete dlg;
		}

		if(SPEC_ERR_CODES(aplError))
		{
			SetAPLErrorWithFileInfo(aplError);
			AplNetErrors err; 
			m_AplTransport->AT_DCDeleteIterator(err, 0, 0);
			return false;
		}


		//	if(SPEC_ERR_CODES(aplError)){
		//		SPEC_SET_ERR(aplError);
		//	AplNetErrors err; 	
		//	m_AplTransport->AT_DCDeleteIterator(err);	
		//	;return false;}

		//Файл записался успешно - пишем в инстанс значение нового идентификатора БЛОБа
		// здесь имя БЛОБа приходят с юникодного сервера - в юникоде, надо перекодировать
		CString str = ConvertStringFromServerEnc(NameBLOB, temporary_buf);

		PutAttr(inst, attr, str);


		////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//					Удаляем старый файл на сервере (если он такой есть)
		////////////////////////////////////////////////////////

		if(NameOldBLOB == _T(""))
			return true;

		NameOldBlob = ConvertString2ServerEnc(NameOldBLOB, temporary_buf, SizeNameBLOB);

		if((SizeNameBLOB-1) == 0)
		{
			return true;
		}

		UINT32 sizeNameFiles = 0;

		if(!m_AplTransport->AT_DCCreateIterator64(aplError, &SizeNameBLOB, NameOldBlob, sizeNameFiles, NameSourceFile,
													&SizeDose, &SizeBLOB64) || SPEC_ERR_CODES(aplError))
		{
			if(aplError == APL_SOCK_MISSED_FUNCTION)
			{
				RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, NameOldBlob, sizeNameFiles, NameSourceFile, &SizeDose, &SizeBLOB));
			}
			else if(aplError == APL_NET_SRV_BLOB_NOT_FOUND)
			{
				// Скорей всего, старого БЛОБа нет. Незачем нервировать пользователя.
				return true;
			}
			else
			{
				SetAPLErrorWithFileInfoEx(aplError, CString(APL_T("Ошибка при сохранении в БД файла ")) + file_source);
				return false;
			}
		}

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCDeleteIterator(aplError, 0, 0));

	}
	CATH_MEMORY_ANY_EXEPTION;

	// Сохранять не надо - SaveChange должен быть вызван из приложения после заполнения всех нужных атрибутов

	return true;
}//bool CaplNetStepData::NET_SaveBlob(CaplInstance *inst,LPCTSTR attr_name,char* file_source)


///////////////////////////////////////////////////////
bool CaplNetStepData::NET_SaveFileFromMemory(CaplInstance *inst, CaplAttr *attr, BYTE* source, long size_source, long &code, LPCTSTR wait_text, int crc32)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	if(inst==NULL || attr==NULL || source == NULL || size_source == 0)
	{
		SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Некорректные параметры функции NET_SaveFileFromMemory"));
		return false;
	}

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		CaplValue val;
		//Проверка на существование атрибута
		if(attr->type != aplSTRING)
		{
			return false;
		}
		CaplStepData::GetAttr(inst, attr, val);
		if(val.type==aplNOTYPE)
		{
			val.Set(_T(""));
		}
		if(!PutAttr(inst, attr, val))
		{
			return false;
		}

		AplNetErrors aplError = APL_NET_OK;

		//Создаем переменные
		TCHAR* name_blob_from_memory = _T("blob_from_memory");

		UINT32 SizeNameBLOB = MAX_STR_LEN;
		BYTE NameBLOB[MAX_STR_LEN];
		NameBLOB[0]=0;
		NameBLOB[1] = 0;

		UINT32 SizeNameSourceFile;
		BYTE temporary_buf[MAX_STR_LEN];
		BYTE* NameSourceFile = ConvertString2ServerEnc(name_blob_from_memory, temporary_buf, SizeNameSourceFile);

		UINT32 SizeDose = size_source < SIZE_DOSE_BLOB ? size_source : SIZE_DOSE_BLOB;
		
		if(SizeDose == 0)
			SizeDose = 1;

		CaplDataBuf DoseB(SizeDose);
		if(DoseB.GetMaxSize() < (int)SizeDose)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при перекодировании строк в формат сервера"));
			return false;
		}
		BYTE *Dose = (BYTE*)DoseB.GetBuffer();

		UINT32 SizeBLOB = 0;
		//Создаем итератор
		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, NameBLOB, SizeNameSourceFile, NameSourceFile,
																	&SizeDose, &SizeBLOB, inst->GetId(), crc32));
		NameBLOB[SizeNameBLOB] = 0;
		NameBLOB[SizeNameBLOB+1] = 0;

		//Порциями читаем данные из буфера и пишем на сервер
		int count = 0, indx = 0, position = 0, size_cur;
		UINT32 LastDose = 0;
		ldiv_t tt = div(size_source, (long)SizeDose);
		count=tt.quot;
		if(tt.rem > 0)count++;

		CDlgCopyBLOB* dlg = 0;
		if(count>2)
		{
			dlg = new CDlgCopyBLOB();
		}
		if(dlg != 0)
		{
			dlg->m_titul= APL_T("Сохранение файла в БД");
			dlg->m_AplTransport = m_AplTransport;
			m_AplTransport->SetManageMode(MANAGE_ABSENT);
			dlg->StartThread();
		}
		LastDose = 0;

		do
		{
			if(dlg != 0)
			{
				if(dlg->SetProgress((int)((indx*100)/count)))
				{
					aplError=(AplNetErrors)APL_NET_CLI_BREAK;
					break;
				}
			}
			size_cur = size_source-position;
			if(size_cur>(int)SizeDose){size_cur = SizeDose;}else{LastDose = 1;}

			memcpy(Dose, source+position, size_cur);
			position += size_cur;
			//Считаем простейшую чексумму
			for(int ji=0; ji <size_cur; ji++)
			{
				code += Dose[ji];
			}

			if(!m_AplTransport->AT_DCWriteToIterator(aplError, size_cur, Dose, LastDose)|| aplError!=APL_NET_OK)
			{
				break;
			}
			indx++;

		}while(LastDose == 0); 

		if(dlg != 0)
		{
			dlg->StopThread();
			m_AplTransport->SetManageMode(MANAGE_WAITDLG);
			delete dlg;
		}

		if(SPEC_ERR_CODES(aplError))
		{
			SetAPLErrorWithFileInfo(aplError);
			AplNetErrors err; 
			m_AplTransport->AT_DCDeleteIterator(err, 0, 0);
			return false;
		}


		//Файл записался успешно - пишем атрибут инстансы
		PutAttr(inst, attr, ConvertStringFromServerEnc(NameBLOB, temporary_buf));

		//Удаляем старый файл на сервере (если он такой есть)
		CString s_NameOldBLOB;
		val.Get(s_NameOldBLOB);

		BYTE* b_NameOldBLOB = ConvertString2ServerEnc(s_NameOldBLOB, temporary_buf, SizeNameBLOB);

		if((SizeNameBLOB-1) == 0)
		{
			return true;
		}

		//	SizeNameBLOB++;
		UINT32 sizeNameFiles = 0;

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, b_NameOldBLOB, sizeNameFiles, NameSourceFile, &SizeDose, &SizeBLOB));

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCDeleteIterator(aplError,0,0));

		// Сохранять не надо - SaveChange должен быть вызван из приложения после заполнения всех нужных атрибутов

	}
	CATH_MEMORY_ANY_EXEPTION;
	return true;
}//bool CaplNetStepData::NET_SaveFileFromMemory


bool CaplNetStepData::NET_DeleteBlobBN(CaplInstance *inst, LPCTSTR attr_name, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);


	CaplAttr *attr = 0;
	if(inst != NULL)
	{
		attr = GetAttrDefinition(inst->GetType(), attr_name);
	}
	else
	{
		return false;
	}
	return NET_DeleteBlob(inst, attr, wait_text);
}

bool CaplNetStepData::NET_DeleteBlob(LPCTSTR _ServNameBLOB, LPCTSTR wait_text, int inst_id, int attr_id)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(_ServNameBLOB == NULL);

	try{
		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;
		UINT32 SizeNameBLOB;
		BYTE temporary_buf[MAX_STR_LEN];
		BYTE* ServNameBLOB = ConvertString2ServerEnc(_ServNameBLOB, temporary_buf, SizeNameBLOB);

		UINT32 SizeDose = SIZE_DOSE_BLOB;
		UINT32 sizeNameFiles = 0;
		UINT32 SizeBLOB = 0;
		__int64 SizeBLOB64 = 0;

		//Создаем итератор
		if(!m_AplTransport->AT_DCCreateIterator64(aplError, &SizeNameBLOB, (BYTE*)ServNameBLOB, sizeNameFiles, (BYTE*)ServNameBLOB, &SizeDose, &SizeBLOB64) ||
			SPEC_ERR_CODES(aplError))
		{
			if(aplError==APL_SOCK_MISSED_FUNCTION)
			{
				if(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, (BYTE*)ServNameBLOB, sizeNameFiles, (BYTE*)ServNameBLOB, &SizeDose, &SizeBLOB) || 
						SPEC_ERR_CODES(aplError))
				{
					if(aplError != APL_NET_SRV_BLOB_NOT_FOUND)
					{
						// если удаляем блоб, которого нет - ругаться не надо, типа все в порядке
						SetAPLError((AplNetErrors) aplError);
					}
					else
					{
						aplError = APL_NET_OK;
						return true;
					}
					if(aplError == APL_NET_SRV_APLAPIERR_FILE_BD_READ_ONLY)
					{
						m_is_readonly = true;
					}
					return false;
				}
			}
			else if(aplError != APL_NET_SRV_BLOB_NOT_FOUND)
			{
				// если удаляем блоб, которого нет - ругаться не надо, типа все в порядке
				SetAPLError((AplNetErrors) aplError);
				return false;
			}
			else
			{
				aplError = APL_NET_OK;
				return true;
			}
		}

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCDeleteIterator(aplError, inst_id, attr_id));

	}
	CATH_MEMORY_ANY_EXEPTION;
	return true;
}



bool CaplNetStepData::NET_DeleteBlob(CaplInstance *inst, CaplAttr *attr, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	bool retval = false;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(inst == NULL || attr==NULL);
	CHECK_PARAM(inst->GetType() == NULL);
	
	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		CaplValue val;
		//Проверка на существование атрибута
		if(attr->type != aplSTRING)
		{
			return false;
		}
		CaplStepData::GetAttr(inst, attr, val);
		//Удаляем старый файл на сервере (если он такой есть)
		CString NameOldFile;
		val.Get(NameOldFile);
		if(NameOldFile == _T("")) return true;

		retval = NET_DeleteBlob((LPCTSTR)NameOldFile, wait_text, inst->GetId(), attr->id);

		if(retval)CaplStepData::PutAttr(inst, attr, _T(""));

	}
	CATH_MEMORY_ANY_EXEPTION;

	return retval;
}//bool CaplNetStepData::NET_DeleteBlob(CaplInstance *inst,CaplAttr *attr){



/************************************************************************/
/* Читает указанный атрибут, сохраняет его на сервер под своим именем 
/* и записывает его серверное имя в указанный атрибут
/************************************************************************/
bool CaplNetStepData::NET_LoadBlobBN(CaplInstance *inst, LPCTSTR attr_name, LPCTSTR file_dest, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);


	CaplAttr *attr = 0;
	if(inst!=NULL)
	{
		attr = GetAttrDefinition(inst->GetType(), attr_name);
	}
	else
	{
		return false;
	}
	return NET_LoadBlob(inst, attr, file_dest, wait_text);

}
bool CaplNetStepData::NET_LoadBlob(CaplInstance *inst, CaplAttr *attr, LPCTSTR file_dest, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(inst==NULL || attr==NULL || file_dest==NULL);

	TEST_RECONNECT(false);

	try{

		CString ServNameFile;
		CaplStepData::GetAttr(inst,attr,ServNameFile);
		if(ServNameFile == _T(""))return true;
		CString ext_info; ext_info.Format(_T("inst_id #%i, attr %i"), inst->GetId(), attr->id);

		if(NET_LoadFileFromSrv((LPCTSTR)ServNameFile, file_dest, wait_text, ext_info))
		{
			return true;
		}
		
		CString add_text_err;
		add_text_err.Format( APL_T("Ошибка при загрузке БЛОБа \'") + ServNameFile + _T("\' inst_id %i  attr_id %i"), inst->GetId(), attr->id);
		m_MainLog.LogMessage(add_text_err, false);

	}
	CATH_MEMORY_ANY_EXEPTION;

	return false;

}//bool CaplNetStepData::NET_LoadBlob(CaplInstance *inst,CaplAttr *attr,char* file_dest)


bool CaplNetStepData::NET_SaveBlob2Srv(LPCTSTR _ServNameBLOB, LPCTSTR _ServNameFileDest, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(_ServNameBLOB==0||_ServNameFileDest==NULL);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;


		UINT32 SizeNameBLOB;
		UINT32 SizeNameFile; 

		BYTE *ServNameBLOB, *ServNameFileDest;
		//Создаем переменные
		BYTE temporary_buf1[MAX_STR_LEN];
		BYTE temporary_buf2[MAX_STR_LEN];
		ServNameBLOB = ConvertString2ServerEnc(_ServNameBLOB, temporary_buf1, SizeNameBLOB);
		ServNameFileDest = ConvertString2ServerEnc(_ServNameFileDest, temporary_buf2, SizeNameFile);

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCSaveBlob2Srv(aplError, SizeNameBLOB, (BYTE*)ServNameBLOB,SizeNameFile, (BYTE*)ServNameFileDest));

	}
	CATH_MEMORY_ANY_EXEPTION;
	return true;
}

bool CaplNetStepData::NET_SaveBlob2Srv(CaplInstance *inst, CaplAttr *attr, LPCTSTR _ServNameFileDest, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	// CHECK_STATE_AND_TRY_ANY_EXEPTION(true, false, false); - проверка будет в вызываемой функции

	CHECK_PARAM((inst==NULL || attr==NULL || _ServNameFileDest==NULL));

	//TEST_RECONNECT(false); - реконнект будет в вызываемой функции
	
	m_AplTransport->SetWaitDlgText(wait_text);

	CaplValue val;
	CString name_blob;
	//Проверка на существование атрибута
	if(attr->type!=aplSTRING)
	{
		return false;
	}
	CaplStepData::GetAttr(inst, attr, val);
	
	if(val.type==aplNOTYPE)
	{
		return false;
	}
	else if(val.type==aplSTRING)
	{
		val.Get(name_blob);
	}
	else if(val.type==aplINTEGER)
	{
		val.Print(name_blob);
	}
	else
	{
		return false;
	}
	return NET_SaveBlob2Srv((LPCTSTR)name_blob, _ServNameFileDest, wait_text);
}

bool CaplNetStepData::NET_CreateBlobFromSrv(LPCTSTR _global_file_src, CString &name_blob, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(_global_file_src==NULL);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;

		//Создаем переменные
		BYTE *global_file_src;
		UINT32 SizeNameFile;
		UINT32 SizeBLOB = 0;
		__int64 SizeBLOB64 = 0;
		UINT32 SizeNameBLOB = MAX_STR_LEN;
		BYTE NameBLOB[MAX_STR_LEN];

		BYTE temporary_buf[MAX_STR_LEN];
		global_file_src = ConvertString2ServerEnc(_global_file_src, temporary_buf, SizeNameFile);

		if(!m_AplTransport->AT_DCReadBlobFromSrv64(aplError, &SizeNameBLOB, (BYTE*)NameBLOB,
														SizeNameFile, (BYTE*)global_file_src, &SizeBLOB64) || SPEC_ERR_CODES(aplError))
		{
			if(aplError==APL_SOCK_MISSED_FUNCTION)
			{
				RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCReadBlobFromSrv(aplError, &SizeNameBLOB, (BYTE*)NameBLOB,
																			SizeNameFile, (BYTE*)global_file_src, &SizeBLOB));
			}
			else
			{
				SetAPLErrorWithFileInfoEx(aplError, CString(APL_T("Ошибка при сохранении в БД файла с сервера ")) + _global_file_src);
				return false;
			}
		}
		
		name_blob = ConvertStringFromServerEnc(NameBLOB, temporary_buf);

	}
	CATH_MEMORY_ANY_EXEPTION;
	return true;
}

bool CaplNetStepData::NET_CreateBlobFromSrv(CaplInstance *inst, CaplAttr *attr, LPCTSTR _global_file_src, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(inst==NULL || attr==NULL || _global_file_src == NULL);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		CaplValue val;
		//Проверка на существование атрибута
		if(attr->type!=aplSTRING)
		{
			return false;
		}
		CString name_blob, old_name_blob = _T("");
		CaplStepData::GetAttr(inst, attr, val);
		if(val.type == aplNOTYPE)
		{
			val.Set(_T(""));
		}
		else
		{
			val.Get(old_name_blob);
		}
		if(!PutAttr(inst, attr, val))
		{
			return false;
		}

		if(!NET_CreateBlobFromSrv(_global_file_src, name_blob, wait_text))
		{
			return false;
		}
		CaplStepData::PutAttr(inst, attr, name_blob);

		NET_DeleteBlob((LPCTSTR)old_name_blob);

	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}

bool CaplNetStepData::NET_LoadFileFromSrv(LPCTSTR _ServNameBLOB, LPCTSTR file_dest, LPCTSTR wait_text, LPCTSTR ext_info)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(_ServNameBLOB== 0 || file_dest==NULL);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		WIN32_FIND_DATA file_data;
		HANDLE res_find;
		CString outFileName=file_dest;

		// ЯАИ:  Обработка для очень длинных путей
		if((outFileName.GetLength()+8) > MAX_PATH)
		{
			if(outFileName.Left(4)!=_T("\\\\?\\"))	outFileName.Insert(0,_T("\\\\?\\"));
		}

		CString outFileName_tmp;
		outFileName_tmp=outFileName + _T(".tmp");
		//CFile file;
		//CFileStatus rStatus;

		res_find = FindFirstFile(outFileName, &file_data);

		if(res_find != INVALID_HANDLE_VALUE)
		{
			FindClose(res_find);
			if(!m_hide_mode)
			{
				if(AfxMessageBox( APL_T("Файл ") + outFileName + APL_T(" существует. Заменить?"), MB_YESNO) == IDNO)
				{
					return false;
				}
			}

			/* ЯАИ не работает с длинными именами (> MAX_PATH)
			CFile::GetStatus( outFileName, rStatus );
			rStatus.m_attribute = 0;
			CFile::SetStatus( outFileName, rStatus );
			*/
			DWORD dwFileAttributes=GetFileAttributes(outFileName);
			if (INVALID_FILE_ATTRIBUTES!=dwFileAttributes) ::SetFileAttributes(outFileName, FILE_ATTRIBUTE_NORMAL);
		}

		/* ЯАИ не работает с длинными именами (> MAX_PATH)
		if(CFile::GetStatus( outFileName_tmp, rStatus ) == TRUE)
		{
			rStatus.m_attribute = 0;
			CFile::SetStatus( outFileName_tmp, rStatus );
		}*/

		DWORD dwFileAttributes=GetFileAttributes(outFileName_tmp);
		if (INVALID_FILE_ATTRIBUTES!=dwFileAttributes) ::SetFileAttributes(outFileName_tmp, FILE_ATTRIBUTE_NORMAL);

		FILE* file = NULL;
		file = _fopen(outFileName, _T("rb"));
		if(file != NULL)
		{
			//Учесть загрузку словаря в начале работы!
			fclose(file);
		}
		file = _fopen(outFileName_tmp, _T("wb"));
		if(file == NULL)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNCREATE, CString(APL_T("Ошибка при сохранении файла ")) + outFileName_tmp);
			return false;
		}

		AplNetErrors aplError = APL_NET_OK;
		//Создаем переменные
		BYTE *ServNameBLOB;
		UINT32 SizeNameBLOB = MAX_STR_LEN;
		UINT32 SizeNameFile = 0;

		BYTE temporary_buf[MAX_STR_LEN];
		ServNameBLOB = ConvertString2ServerEnc(_ServNameBLOB, temporary_buf, SizeNameBLOB);

		UINT32 SizeDose = SIZE_DOSE_BLOB;
		UINT32 SizeBLOB = 0;
		__int64 SizeBLOB64 = 0;

		CaplDataBuf DoseB(SizeDose);
		if(DoseB.GetMaxSize() < (int)SizeDose)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при перекодировании строк в формат сервера"));
			return false;
		}
		BYTE *Dose = (BYTE*)DoseB.GetBuffer();

		//Создаем итератор
		if(!m_AplTransport->AT_DCCreateIterator64(aplError, &SizeNameBLOB, (BYTE*)ServNameBLOB,
														SizeNameFile, (BYTE*)ServNameBLOB ,&SizeDose, &SizeBLOB64) || SPEC_ERR_CODES(aplError))
		{
			if(aplError==APL_SOCK_MISSED_FUNCTION)
			{
				if(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, (BYTE*)_ServNameBLOB, SizeNameFile, (BYTE*)_ServNameBLOB ,&SizeDose, &SizeBLOB) || 
						SPEC_ERR_CODES(aplError))
				{
					fclose(file);
					_remove(outFileName_tmp);
					return false;
				}
				SizeBLOB64 = SizeBLOB;
			}
			else
			{
				CString str_err = CString(APL_T("Ошибка при чтении БЛОБа с id ")) + _ServNameBLOB;
				if(ext_info != 0){ str_err += APL_T(" ("); str_err+= ext_info; str_err += APL_T(")");}
				else if(wait_text != 0){ str_err += APL_T(" ("); str_err+= wait_text; str_err += APL_T(")");}
				SetAPLErrorWithFileInfoEx(aplError, str_err );

				{
					fclose(file);
					_remove(outFileName_tmp);
				}
				return false;
			}
		}
		
		//*** Добавил ЯАИ, т.к. возникала ощибка, хотя ситуация штатная
		if(0==SizeBLOB64)
		{
			AplNetErrors err; 
			m_AplTransport->AT_DCCloseIterator(err);
			fclose(file);
		}
		else
		{
			int ch = 0,indx = 0;
			UINT32 LastDose = 0;

			__int64 portions = SizeBLOB64 / (__int64)SizeDose;
			if(SizeBLOB64 - portions*SizeDose > 0) portions++;

			CDlgCopyBLOB* dlg = 0;
			if(portions>2 && !m_hide_mode)
			{
				dlg = new CDlgCopyBLOB();
			}
			if(dlg != 0)
			{
				dlg->m_titul = APL_T("Чтение файла из БД");
				dlg->m_AplTransport = m_AplTransport;
				m_AplTransport->SetManageMode(MANAGE_ABSENT);
				dlg->StartThread();
			}
			int stopper_indx = 0;
			bool err = false;

			//Идем по итератору
			do
			{
				if(dlg != 0)
				{
					if(dlg->SetProgress((int)((indx*100)/portions)))
					{
						aplError = (AplNetErrors)APL_NET_CLI_BREAK;
						break;
					}
				}
				if(!m_AplTransport->AT_DCReadFromIterator(aplError, &SizeDose, Dose, &LastDose) || aplError!=APL_NET_OK)
				{
					CString str_err = CString(APL_T("Ошибка при чтении БЛОБа с id ")) + _ServNameBLOB;
					if(ext_info != 0){ str_err += APL_T(" ("); str_err+= ext_info; str_err += APL_T(")");}
					else if(wait_text != 0){ str_err += APL_T(" ("); str_err+= wait_text; str_err += APL_T(")");}
					SetAPLErrorWithFileInfoEx(aplError, str_err );
					break;
				}
				ch = fwrite(Dose, SizeDose, 1, file);
				if(ch == 0)
				{ 
					err = true;
					break;
				}
				ch = fflush(file);
				if(ch != 0)
				{
					err = true;
					break;
				}

				m_AplTransport->ClearInternalData();
				indx++;
				stopper_indx++;
				if(stopper_indx>300)
				{
					stopper_indx = 0;
					//AfxMessageBox("Подожди...");
				}

			}while(LastDose == 0); 

			if(err && !m_hide_mode) 
				MessageBox(m_hwnd, APL_T("Ошибка при записи файла '") + outFileName + _T("'!"), APL_T("Служба RPC PDM Step Suite"), MB_OK|MB_ICONERROR);

			fclose(file);
			if(dlg != 0)
			{
				m_AplTransport->SetManageMode(MANAGE_WAITDLG);
				dlg->StopThread();
				delete dlg;
			}
			if(aplError != APL_NET_OK)
			{
				_remove(outFileName_tmp);
				AplNetErrors err; 
				m_AplTransport->AT_DCCloseIterator(err);
				SetAPLError(aplError);
				return false;
			}
		}
		SetFileAttributes(outFileName, FILE_ATTRIBUTE_NORMAL);
		_remove(outFileName);
		_rename(outFileName_tmp, outFileName);

	}
	CATH_MEMORY_ANY_EXEPTION;
	return true;
}//bool CaplNetStepData::NET_LoadFileFromSrv(CString ServNameBLOB){

struct SBLOBDescr 
{
	SBLOBDescr(){inst = 0; blob_id = 0; blob_size = 0; offset = 0; filename = _T(""); fstatus = 0;}
	~SBLOBDescr(){if(fstatus != 0)delete fstatus; fstatus = 0;}
	CaplInstance *inst;
	int blob_id;
	__int64 blob_size;
	__int64 offset;
	CString filename;
	CFileStatus *fstatus;
};


bool CaplNetStepData::NET_LoadMultiBlobsFromSrv(CUIntArray &blob_ids, LPCTSTR folder_dest,CStringArray &blob_names, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(blob_ids.GetSize() == 0 || folder_dest==NULL);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		CaplTAggr<SBLOBDescr*,SBLOBDescr*,APLAGGR_AUTOKILLREF> list_blob;
		SBLOBDescr* blob_descr;

		CString outFolderName = folder_dest;
		CString outFileName_tmp, tmp;
		int i, j, blob_id, blob_size, count_blobs;
		__int64 global_size, blob_size64;
		CFileStatus rStatus;

		AplNetErrors aplError = APL_NET_OK;
		UINT32 SizeDose = SIZE_DOSE_BLOB;
		int stopper_indx = 0;

		CaplDataBuf dbuf1, dbuf2;
		FILE* file = NULL;
		void *pbuff = 0;

		if(CreateDirectory(outFolderName, NULL)!=S_OK)
		{
			GetDescriptionSystemError(GetLastError(), tmp);
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Не удалось создать папку ") + outFolderName + _T(": ") + tmp); return false;
		}
		if(outFolderName.Right(1) != _T("\\")){outFolderName += _T("\\");}

		// генерим структурки-описатели БЛОБов, настраиваем файлы
		for(i=0; i <blob_ids.GetSize(); i++)
		{
			blob_descr = new SBLOBDescr();
			list_blob.Add(blob_descr);
			blob_descr->blob_id = blob_ids[i];
			tmp.Format(_T("blob_%i"), blob_ids[i]);
			blob_descr->filename = outFolderName + tmp + _T(".tmp");

			// ищем предыдущие и снимает атрибуты
			if(CFile::GetStatus( blob_descr->filename, rStatus ) == TRUE)
			{
				rStatus.m_attribute = 0;
				CFile::SetStatus( blob_descr->filename, rStatus );
			}
		}

		//Создаем итератор
		count_blobs = blob_ids.GetSize();
		dbuf1.Add(&count_blobs, 4);
		for(i=0; i <blob_ids.GetSize(); i++)
		{
			blob_id = blob_ids.GetAt(i);
			dbuf1.Add(&blob_id, 4);
		}

		bool is64 = true;

		if(!m_AplTransport->AT_DCCreateMultiIterator64(aplError, dbuf1, &SizeDose, dbuf2) || SPEC_ERR_CODES(aplError))
		{
			if(aplError == APL_SOCK_MISSED_FUNCTION)
			{
				is64 = false;
				RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCreateMultiIterator(aplError, dbuf1, &SizeDose, dbuf2));
			}
			else
			{
				SetAPLErrorWithFileInfo(aplError);
				m_AplTransport->ClearInternalData();
				return false;
			}
		}

		// читаем размеры 
		// считаем общий размер для вывода в диалог, ну и проверяем корректность
		dbuf2.Read(&count_blobs, 4);
		if(count_blobs != list_blob.GetSize())
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера пришло меньше блобов, чем запрашивалось!"));
			m_AplTransport->ClearInternalData();
			return false;
		}
		global_size = 0;

		for(i=0; i <list_blob.GetSize(); i++)
		{
			blob_descr = list_blob.GetAt(i);
			dbuf2.Read(&blob_id, 4);
			if(blob_id != blob_descr->blob_id)
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера БЛОБы идут в неверном порядке!"));
				m_AplTransport->ClearInternalData();
				return false;
			}
			if(is64)
			{
				dbuf2.Read(&blob_size64, 8);
				blob_descr->blob_size = blob_size64;
			}
			else
			{
				dbuf2.Read(&blob_size, 4);
				blob_descr->blob_size = blob_size;
			}
			global_size += blob_descr->blob_size;
		}
		m_AplTransport->ClearInternalData();
		
		// если порций больше двух - запускаем диалог
		int ch = 0;
		UINT32 LastDose = 0;
		__int64 portions = 0, indx = 0;
		portions = global_size / SizeDose;
		__int64 quot = global_size - portions*SizeDose;
		if(quot > 0)portions++;

		CDlgCopyBLOB* dlg = 0;

		if(portions>2 && !m_hide_mode)
		{
			dlg=new CDlgCopyBLOB();
		}
		if(dlg != 0)
		{
			dlg->m_titul = APL_T("Чтение файла из БД");
			dlg->m_AplTransport = m_AplTransport;
			m_AplTransport->SetManageMode(MANAGE_ABSENT);
			dlg->StartThread();
		}

		// выделяем память
		CaplDataBuf DoseB(SizeDose);
		if(DoseB.GetMaxSize() < (int)SizeDose)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при перекодировании строк в формат сервера"));
			return false;
		}
		BYTE *Dose = (BYTE*)DoseB.GetBuffer();

		//Идем по итератору
		do
		{
			if(dlg != 0)
			{
				if(dlg->SetProgress((int)((indx*100)/portions)))
				{
					aplError = (AplNetErrors)APL_NET_CLI_BREAK;
					break;
				}
			}
			if(!m_AplTransport->AT_DCReadFromMultiIterator(aplError, &SizeDose, dbuf1, dbuf2) || aplError!=APL_NET_OK)
			{
				break;
			}
			// разбираем пришедший буфер 
			dbuf2.Read(&count_blobs ,4);
			for(i=0; i <count_blobs; i++)
			{
				dbuf2.Read(&blob_id ,4);
				dbuf2.Read(&blob_size, 4);
				blob_descr = 0;
				for(j = 0; j<list_blob.GetSize(); j++)
				{
					if(list_blob.GetAt(j)->blob_id == blob_id)
					{
						blob_descr = list_blob.GetAt(j);
						break;
					}
				}
				if(blob_descr == 0)
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера пришел неопознанный БЛОБ!"));
					m_AplTransport->ClearInternalData();
					return false;
				}
				
				if(blob_descr->offset == 0)
				{
					file = _fopen(blob_descr->filename,_T("wb"));
				}
				else
				{
					file = _fopen(blob_descr->filename,_T("ab"));
				}
				if(file == NULL)
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNCREATE, CString(APL_T("Ошибка при попытке открыть файл: "))+ blob_descr->filename); 
					return false;
				}
		
				pbuff = dbuf1.GetCurBuffer(blob_size);
				if(pbuff == 0)
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNCREATE, APL_T("Ошибка при чтении из буфера: буфер пустой!"));
					m_AplTransport->ClearInternalData();
					fclose(file);
					return false;
				}			
				ch = fwrite(pbuff, blob_size, 1, file);
				if(ch == 0)
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNCREATE, APL_T("Ошибка при записи файла '") + blob_descr->filename + _T("'!"));
					m_AplTransport->ClearInternalData();
					fclose(file);
					return false;
				}			
				ch = fflush(file);
				if(ch != 0)
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNCREATE, APL_T("Ошибка при записи файла '") + blob_descr->filename + _T("'!"));
					m_AplTransport->ClearInternalData();
					fclose(file);
					return false;
				}			
				// Фиксируем, сколько байт пришло
				blob_descr->offset += blob_size;
				fclose(file);
		
			}

			dbuf2.Read(&LastDose, 4);

			m_AplTransport->ClearInternalData();
			indx++;

#ifdef _DEBUG
			// если надо время от времени прерываться....
			stopper_indx++;
			if(stopper_indx>300)
			{
				stopper_indx = 0;
				//AfxMessageBox("Подожди...");
			}
#endif
		}while(LastDose == 0); 

		if(dlg != 0)
		{
			m_AplTransport->SetManageMode(MANAGE_WAITDLG);
			dlg->StopThread();
			delete dlg;
		}

		if(aplError != APL_NET_OK)
		{
			AplNetErrors err; 
			m_AplTransport->AT_DCCloseIterator(err);
			SetAPLError(aplError);
			return false;
		}

		// проверяем, что прочитались все БЛОБы целиком
		for(j = 0; j<list_blob.GetSize(); j++)
		{
			blob_descr = list_blob.GetAt(j);
			if(blob_descr->blob_size != blob_descr->offset)
			{
				tmp.Format( APL_T("БЛОБ %i (файл '") + blob_descr->filename + APL_T("') прочитан не полностью!"), blob_descr->blob_id);
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED,tmp);
				return false;
			}
		}

		// заполняем выходной массив имен
		for(j = 0; j<list_blob.GetSize(); j++)
		{
			blob_descr = list_blob.GetAt(j);
			blob_names.Add(blob_descr->filename);
		}

	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}//bool CaplNetStepData::NET_LoadMultiBlobsFromSrv(){

bool CaplNetStepData::NET_Autoupdate_LoadListNamesFiles(LPCTSTR system, CStringArray &list_masks,
														CaplTAggr<SaplFileDescr*,SaplFileDescr*,APLAGGR_LIST_OR_AUTOKILLREF> &list_files, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(system==NULL || list_masks.GetCount() == 0);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;
		CaplDataBuf dbuf_in, dbuf_out;
		int len = 0, count = 0;
		int i;
		SaplFileDescr* file_descr;
		CString mask = _T("");

		list_files.Clear();

		// заголовки в буферах - однобайтные
		len = strlen(LoadListNamesFiles_header);
		dbuf_in.Add(&len,4);
		dbuf_in.Add(LoadListNamesFiles_header, strlen(LoadListNamesFiles_header)+1);

		dbuf_in.m_ansi_string = m_global_ansi_string;
		dbuf_in.AddStrBuf(system);

		for(i=0; i <list_masks.GetCount(); i++)
		{
			if(i>0) mask += _T("\n");
			mask += list_masks.GetAt(i);
		}

		dbuf_in.AddStrBuf(mask);

		if(!m_AplTransport->AT_DCAutoupdate_LoadListNamesFilesEx(aplError, dbuf_in, dbuf_out) || SPEC_ERR_CODES(aplError))
		{
			if(aplError==APL_SOCK_MISSED_FUNCTION)
			{
				dbuf_in.Clear();

				dbuf_in.AddStrBuf(system);

				mask = _T("");
				for(i=0; i <list_masks.GetCount(); i++)
				{
					if(i>0)mask += _T(" ");
					mask += list_masks.GetAt(i);
				}

				dbuf_in.AddStrBuf(mask);

				if(!m_AplTransport->AT_DCAutoupdate_LoadListNamesFiles(aplError, dbuf_in, dbuf_out) || SPEC_ERR_CODES(aplError))
				{
					if(aplError == APL_SOCK_MISSED_FUNCTION)
					{
						AfxMessageBox( APL_T("Сервер приложений не поддерживает автоматическое обновление.\r\n\r\nПоставьте новую версию сервера приложений или настройте обновление через файловый сервер!\r\n\r\nДля продолжения работы в текущей версии нажмите Ok"));
					}
					else if(aplError == APL_SOCK_MISSED_FUNCT_PARAM)
					{
						AfxMessageBox( APL_T("Сервер приложений не поддерживает новую версию автоматического обновления.\r\n\r\nПоставьте новую версию сервера приложений или настройте обновление через файловый сервер!\r\n\r\nДля продолжения работы в текущей версии нажмите Ok"));
					}
					else if(aplError == APL_NET_SRV_ERROR_PROCESSED)
					{
						AfxMessageBox( APL_T("При выполнении автоматического обновления возникла ошибка:\n\r\n\r") + m_AplTransport->GetDescriptionServError() +
							 APL_T("\n\r\n\r Для продолжения работы в старой версии нажмите Ok"));
					}
					else
					{
						SetAPLError(aplError, m_AplTransport->GetDescriptionServError(),_T(__FILE__),__LINE__,__APL_FUNC__);
					}
					m_AplTransport->ClearInternalData();
					return false;
				}
			}
			else
			{
				SetAPLError(aplError, m_AplTransport->GetDescriptionServError(), _T(__FILE__), __LINE__, __APL_FUNC__);
				m_AplTransport->ClearInternalData();
				return false;
			}
		}

		dbuf_out.m_ansi_string = m_global_ansi_string;
		dbuf_out.Read(&count);

		for(i=0; i <count; i++)
		{
			file_descr = new SaplFileDescr();
			list_files.Add(file_descr);

			dbuf_out.ReadStrBuf(file_descr->name);
			if(file_descr->name == _T(""))
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера пришла пустая строка!"));
				m_AplTransport->ClearInternalData();
				return false;
			}

			dbuf_out.Read(&len);
			dbuf_out.Read(&(file_descr->fstatus),len);
			dbuf_out.Read(&(file_descr->crc32),4);
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

bool CheckAndCreateFolder(LPCTSTR filename)
{
	if(filename == 0) return false;

	int i=0;
	CFileFind ff;
	CString fname = filename, dir, mask;

	// Проверка наличия пути
	i = fname.ReverseFind(_T('\\'));
	if(i <0) return true;
	i++;
	mask = fname.Left(i);
	mask += _T("*.*");
	if(ff.FindFile(mask, 0)) return true;

	// Если пути нет
	i=0;
	while(true)
	{
		i=fname.Find(_T('\\'), i);
		if(i <0) break;
		i++;
		dir = fname.Left(i);
		mask = dir;
		mask += _T("*.*");

		if(ff.FindFile(mask, 0)) continue;

		if(CreateDirectory(LPCTSTR(dir), 0)) continue;
		return false;
	}
	return true;
}

bool CaplNetStepData::NET_Autoupdate_LoadListFiles(LPCTSTR system, LPCTSTR folder_dest, CStringArray &list_files, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(list_files.GetSize() == 0 || folder_dest==NULL || system== 0);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		CString outFolderName = folder_dest;
		CString outFileName_tmp, tmp;
		int i, j, blob_size, count_files;
		int file_indx;
		int len_system_name;
		__int64 global_size;

		AplNetErrors aplError = APL_NET_OK;
		UINT32 SizeDose = SIZE_DOSE_BLOB;
		int stopper_indx = 0;
		int len;
		CFileStatus rStatus;


		CaplDataBuf dbuf1, dbuf2;
		FILE* file = NULL;
		void *pbuff = 0;

		SBLOBDescr* blob_descr;
		CaplTAggr<SBLOBDescr*,SBLOBDescr*,APLAGGR_AUTOKILLREF> list_blob;

		len_system_name = _strlen(system);
		if(len_system_name == 0)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Не задана система для автообновления"));
			return false;
		}


		// проверка на самокопирование
		if(m_AplTransport->IsServerLocal())
		{
			CString name_folder_src;
			CString curpath = _T("\\");
			CWinApp *app=AfxGetApp(); // При вызове из ActiveX возвращает 0
			if(0!=app)
			{
				tmp = app->m_pszHelpFilePath;
				i = tmp.ReverseFind(_T('\\'));
				if(i > 0) curpath = tmp.Left(i+1);
			}

			// сервер на этом же компе.
			// Читаем путь-источник для данной системы
			name_folder_src = m_Ini.GetString(_T("AutoUpdatePath"), system, _T("") );
			name_folder_src.TrimLeft();
			name_folder_src.TrimRight();
			name_folder_src.MakeLower();
			curpath.MakeLower();
			if(name_folder_src == curpath)
			{
				if(!m_hide_mode) AfxMessageBox(APL_T("Автообновление настроено некорректно - папка-источник \r\nна сервере и папка назначения на клиенте совпадают!\r\nОбновление не выполнено."),MB_ICONEXCLAMATION);
				return false;
			}
		}


		if(CreateDirectory(outFolderName, NULL)!=S_OK)
		{
			GetDescriptionSystemError(GetLastError(), tmp);
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Не удалось создать папку ") + outFolderName + _T(": ") + tmp); return false;
		}
		if(outFolderName.Right(1) != _T("\\")){outFolderName += _T("\\");}

		dbuf1.m_ansi_string = m_global_ansi_string;

		// заполняем буфер - заносим имя системы
		dbuf1.AddStrBuf(system);
		
		//заносим в буфер имена файлов
		count_files = list_files.GetSize();
		dbuf1.Add(&count_files, 4);

		for(i=0; i <list_files.GetSize(); i++)
		{
			blob_descr = new SBLOBDescr();
			list_blob.Add(blob_descr);
			blob_descr->filename = list_files.GetAt(i);

			dbuf1.AddStrBuf(blob_descr->filename);

			// ищем предыдущие и снимает атрибуты - чтобы файлы перезаписались без проблем
			if(CFile::GetStatus( outFolderName + blob_descr->filename, rStatus ) == TRUE)
			{
				rStatus.m_attribute = 0;
				CFile::SetStatus( outFolderName + blob_descr->filename, rStatus );
			}
		}
		// создаем итератор
		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCAutoupdate_CreateMultiIterator(aplError, dbuf1, &SizeDose, dbuf2));

		dbuf2.m_ansi_string = m_global_ansi_string;

		// считаем общий размер для вывода в диалог, ну и проверяем корректность
		dbuf2.Read(&count_files, 4);
		if(count_files != list_files.GetSize())
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера пришло меньше файлов, чем запрашивалось!"));
			m_AplTransport->ClearInternalData();
			return false;
		}
		global_size = 0;
		for(j = 0; j<list_files.GetSize(); j++)
		{
			dbuf2.ReadStrBuf(tmp);

			if(tmp != list_blob[j]->filename)
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера пришла неверная информация о файлах!"));
				m_AplTransport->ClearInternalData();
				return false;
			}
			dbuf2.Read(&len);
			if(len != 0)
			{
				list_blob[j]->fstatus = new CFileStatus();
				dbuf2.Read((list_blob[j]->fstatus), len);
			}

			dbuf2.Read(&blob_size, 4);
			list_blob[j]->blob_size = blob_size;
			global_size += blob_size;
		}
		m_AplTransport->ClearInternalData();

		// если порций больше двух - запускаем диалог
		int ch = 0, portions = 0, indx = 0;
		UINT32 LastDose = 0;
		
		__int64 portions64 = global_size/SizeDose;
		if(portions64 > MAXLONG)
		{
			CString err_str;
			err_str.Format(APL_T("Сервер пытается передать слишком много данных (%I64d байт). Операция не выполнена."), global_size);
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, err_str);
			m_AplTransport->ClearInternalData();
			return false;
		}
		portions = (int)(portions64);
		if(global_size > portions64*SizeDose)
			portions++;

		CDlgCopyBLOB* dlg = 0;

		if(portions > 2 && !m_hide_mode)
		{
			dlg = new CDlgCopyBLOB();
		}
		if(dlg != 0)
		{
			dlg->m_titul = APL_T("Загрузка файлов с сервера");
			dlg->m_AplTransport = m_AplTransport;
			m_AplTransport->SetManageMode(MANAGE_ABSENT);
			dlg->StartThread();
		}

		//Идем по итератору
		do
		{
			if(dlg != 0)
			{
				if(dlg->SetProgress((int)((indx*100)/portions)))
				{
					aplError = (AplNetErrors)APL_NET_CLI_BREAK;
					break;
				}
			}
			if(!m_AplTransport->AT_DCAutoupdate_ReadFromMultiIterator(aplError, &SizeDose, dbuf1, dbuf2) || aplError != APL_NET_OK)
			{
				break;
			}
			// разбираем пришедший буфер - по информации из служебного буфера
			dbuf2.Read(&count_files, 4);
			for(i=0; i <count_files; i++)
			{
				dbuf2.Read(&file_indx, 4);
				dbuf2.Read(&blob_size, 4);
				if(file_indx < 0 || file_indx > list_blob.GetSize())
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера пришел неправильный индекс файла!"));
					m_AplTransport->ClearInternalData();
					return false;
				}
				blob_descr = list_blob.GetAt(file_indx);

				if(blob_descr->offset == 0)
				{
					if(!CheckAndCreateFolder(outFolderName + blob_descr->filename))
					{
						SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при создании пути к файлу \'") + blob_descr->filename + _T("\'"));
						m_AplTransport->ClearInternalData();
						return false;

					}
					file = _fopen(outFolderName + blob_descr->filename, _T("wb"));
				}
				else
				{
					file = _fopen(outFolderName + blob_descr->filename, _T("ab"));
				}
				if(file == NULL)
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNCREATE, CString(APL_T("Ошибка при попытке открыть файл "))+outFolderName + blob_descr->filename); 
					return false;
				}

				pbuff = dbuf1.GetCurBuffer(blob_size);
				if(pbuff == 0)
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNCREATE, APL_T("Ошибка при чтении из буфера: буфер пустой!"));
					m_AplTransport->ClearInternalData();
					fclose(file);
					return false;
				}			
				ch = fwrite(pbuff, blob_size, 1, file);
				if(ch == 0 && blob_size != 0)
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNCREATE, APL_T("Ошибка при записи файла '") + outFolderName + blob_descr->filename + _T("'!"));
					m_AplTransport->ClearInternalData();
					fclose(file);
					return false;
				}			
				ch=fflush(file);
				if(ch != 0)
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_FNCREATE, APL_T("Ошибка при записи файла '") + outFolderName + blob_descr->filename + _T("'!"));
					m_AplTransport->ClearInternalData();
					fclose(file);
					return false;
				}			
				// Фиксируем, сколько байт пришло
				blob_descr->offset += blob_size;
				fclose(file);

			}

			dbuf2.Read(&LastDose, 4);

			m_AplTransport->ClearInternalData();
			indx++;

			// если надо время от времени прерываться....
			stopper_indx++;
			if(stopper_indx > 300)
			{
				stopper_indx = 0;
				//AfxMessageBox("Подожди...");
			}
		}while(LastDose == 0); 

		if(dlg != 0)
		{
			m_AplTransport->SetManageMode(MANAGE_WAITDLG);
			dlg->StopThread();
			delete dlg;
		}

		if(aplError!=APL_NET_OK)
		{
			AplNetErrors err; 
			m_AplTransport->AT_DCCloseIterator(err);
			SetAPLError(aplError);
			return false;
		}

		// проверяем, что прочитались все БЛОБы целиком
		for(j = 0; j<list_blob.GetSize(); j++)
		{
			blob_descr = list_blob.GetAt(j);
			if(blob_descr->blob_size != blob_descr->offset)
			{
				tmp.Format( APL_T("БЛОБ %i (файл '") + outFolderName + blob_descr->filename + APL_T("') прочитан не полностью!"), blob_descr->blob_id);
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, tmp);
				return false;
			}
		}

		// устанавливаем дату/время скопированных файлов
		for(j = 0; j<list_blob.GetSize(); j++)
		{
			blob_descr = list_blob.GetAt(j);
			if(blob_descr->fstatus != 0)
			{
				CFile::GetStatus(outFolderName + blob_descr->filename, rStatus);
				rStatus.m_mtime = blob_descr->fstatus->m_mtime;
				rStatus.m_ctime = blob_descr->fstatus->m_ctime;
				CFile::SetStatus(outFolderName + blob_descr->filename, rStatus);
			}
		}

	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}//bool NET_Autoupdate_LoadListFiles(LPCTSTR system, LPCTSTR folder_dest,CStringArray &list_files, LPCTSTR wait_text)


bool CaplNetStepData::NET_GetBlobSize(CaplInstance *inst, CaplAttr *attr, __int64 &blob_size, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	blob_size = 0;

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_GetBlobSize"));
	CString ServNameBLOB;
	// CHECK_CONN_PROCESSED(true, false); - проверки на подключение - в вызываемой функции

	CaplStepData::GetAttr(inst, attr, ServNameBLOB);
	if(ServNameBLOB == _T(""))return true;
	
	bool old_mode = m_NetErrorMessageMode; m_NetErrorMessageMode = false;

	bool result = NET_FileSrvSize((LPCTSTR)ServNameBLOB, blob_size, wait_text);
	m_NetErrorMessageMode = old_mode;
	if(result) return true;
	
	CString add_text_err;
	add_text_err.Format(_T("inst_id %i  attr_id %i"), inst->GetId(), attr->id);
	SetAPLErrorWithFileInfoEx(GetLastAplError(), add_text_err);
	return false;
}


bool CaplNetStepData::NET_GetBlobSize(CaplInstance *inst, CaplAttr *attr, long &blob_size, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	blob_size = 0;

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_GetBlobSize"));
	CString ServNameBLOB;

	// CHECK_CONN_PROCESSED(true, false);  - проверки на подключение - в вызываемой функции

	CaplStepData::GetAttr(inst, attr, ServNameBLOB);
	if(ServNameBLOB == _T(""))return true;
	
	bool old_mode = m_NetErrorMessageMode; m_NetErrorMessageMode = false;
	bool result = NET_FileSrvSize((LPCTSTR)ServNameBLOB, blob_size, wait_text);
	m_NetErrorMessageMode = old_mode;
	if(result) return true;
	
	CString add_text_err;
	add_text_err.Format(_T("inst_id %i  attr_id %i"), inst->GetId(), attr->id);
	SetAPLErrorWithFileInfoEx(GetLastAplError(), add_text_err);
	return false;
}

bool CaplNetStepData::NET_FileSrvSize(LPCTSTR _ServNameBLOB, __int64 &blob_size, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);	
	blob_size = 0;

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_FileSrvSize"));

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM_EX((CString(_ServNameBLOB) == _T("") || _ServNameBLOB == 0), APL_T("Неверный идентификатор БЛОБа"));

	try{

		TEST_RECONNECT(false);

		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;
		//Создаем переменные
		BYTE *ServNameBLOB;
		UINT32 SizeNameBLOB;
		UINT32 SizeNameFile = 0;

		BYTE temporary_buf[MAX_STR_LEN];
		ServNameBLOB = ConvertString2ServerEnc(_ServNameBLOB, temporary_buf, SizeNameBLOB);


		UINT32 SizeDose = SIZE_DOSE_BLOB;

		UINT32 SizeBLOB = 0;
		__int64 SizeBLOB64 = 0;

		//Создаем итератор
		if(!m_AplTransport->AT_DCCreateIterator64(aplError, &SizeNameBLOB, (BYTE*)ServNameBLOB,
									SizeNameFile, (BYTE*)ServNameBLOB, &SizeDose, &SizeBLOB64) || SPEC_ERR_CODES(aplError))
		{
			if(aplError == APL_SOCK_MISSED_FUNCTION)
			{
				if(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, (BYTE*)ServNameBLOB,
									SizeNameFile, (BYTE*)ServNameBLOB, &SizeDose, &SizeBLOB) || SPEC_ERR_CODES(aplError))
				{
					CString add_text_err = _T("_ServNameBLOB: "); 
					add_text_err += _ServNameBLOB;
					SetAPLErrorWithFileInfoEx(aplError, add_text_err);
					if(aplError == APL_NET_SRV_APLAPIERR_FILE_BD_READ_ONLY){m_is_readonly = true;}
					return false;
				}
				blob_size = SizeBLOB;
			}
			else
			{
				SetAPLErrorWithFileInfoEx(aplError, CString(APL_T("Ошибка при получении размера БЛОБа с id ")) + _ServNameBLOB);
				return false;
			}
		}
		else
		{
			blob_size = SizeBLOB64;
		}

		m_AplTransport->AT_DCCloseIterator(aplError);

	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;

}

bool CaplNetStepData::NET_FileSrvSize(LPCTSTR _ServNameBLOB, long &blob_size, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_FileSrvSize"));
	blob_size = -1;
	__int64 blob_size64;
	bool ret_val = NET_FileSrvSize(_ServNameBLOB, blob_size64, wait_text);
	if(ret_val)
	{
		if(blob_size64 >= MAXLONG)
		{
			CString tmp;
			tmp.Format(APL_T("Размер БЛОБа превышает 2 гигабайта. Дальнейшая обработка может выполняться некорректно."));
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_FILE_BIG, tmp );
			ret_val = false;
		}
		else
		{
			blob_size = (long) blob_size64;
		}
	}

	return ret_val;
}

bool CaplNetStepData::NET_GetBlobSize(aplExtent &extent, CaplAttr *attr, C__int64Array &blob_sizes, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_GetBlobSize 3"));
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);
		blob_sizes.RemoveAll();

		CString ServNameBLOB;
		AplNetErrors aplError = APL_NET_OK;
		int i;
		CaplInstance *inst;
		CaplTAggr<SBLOBDescr*,SBLOBDescr*,APLAGGR_AUTOKILLREF> list_blob;
		SBLOBDescr* blob_descr;
		int count_blobs;
		CaplDataBuf dbuf1,dbuf2;
		int blob_id;
		int blob_size;
		__int64 blob_size64;
		UINT32 SizeDose=SIZE_DOSE_BLOB;

		// генерим структурки-описатели БЛОБов, настраиваем файлы
		for(i=0; i <extent.GetSize(); i++)
		{
			inst = extent.GetAt(i);
			CaplStepData::GetAttr(inst, attr, ServNameBLOB);

			blob_descr = new SBLOBDescr();
			list_blob.Add(blob_descr);
			blob_descr->inst = inst;
			blob_descr->blob_id = _atoi(ServNameBLOB);
		}

		//Создаем итератор
		count_blobs = list_blob.GetSize();
		dbuf1.Add(&count_blobs, 4);
		for(i=0; i <list_blob.GetSize(); i++)
		{
			blob_id = list_blob.GetAt(i)->blob_id;
			dbuf1.Add(&blob_id, 4);
		}

		bool is64 = true;

		if(!m_AplTransport->AT_DCCreateMultiIterator64(aplError, dbuf1, &SizeDose, dbuf2) || SPEC_ERR_CODES(aplError))
		{
			if(aplError==APL_SOCK_MISSED_FUNCTION)
			{
				is64 = false;
				RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCreateMultiIterator(aplError, dbuf1, &SizeDose, dbuf2));
			}
			else
			{
				SetAPLErrorWithFileInfo(aplError);
				m_AplTransport->ClearInternalData();
				return false;
			}
		}


		// читаем размеры 
		dbuf2.Read(&count_blobs, 4);
		if(count_blobs != list_blob.GetSize())
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера пришло меньше блобов, чем запрашивалось!"));
			m_AplTransport->ClearInternalData();
			return false;
		}
		for(i=0; i <list_blob.GetSize(); i++)
		{
			blob_descr = list_blob.GetAt(i);
			dbuf2.Read(&blob_id, 4);
			if(blob_id != blob_descr->blob_id)
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера БЛОБы идут в неверном порядке!"));
				m_AplTransport->ClearInternalData();
				return false;
			}
			if(is64)
			{
				dbuf2.Read(&blob_size64, 8);
				blob_descr->blob_size = blob_size64;
			}
			else
			{
				dbuf2.Read(&blob_size, 4);
				blob_descr->blob_size = blob_size;
			}
			blob_sizes.Add(blob_descr->blob_size);
		}
		m_AplTransport->AT_DCCloseMultiIterator(aplError);
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}


bool CaplNetStepData::NET_GetBlobSize(aplExtent &extent, CaplAttr *attr, CUIntArray &blob_sizes, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_GetBlobSize 2"));
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);
		blob_sizes.RemoveAll();

		CString ServNameBLOB;
		AplNetErrors aplError = APL_NET_OK;
		int i;
		CaplInstance *inst;
		CaplTAggr<SBLOBDescr*,SBLOBDescr*,APLAGGR_AUTOKILLREF> list_blob;
		SBLOBDescr* blob_descr;
		int count_blobs;
		CaplDataBuf dbuf1, dbuf2;
		int blob_id, blob_size;
		UINT32 SizeDose = SIZE_DOSE_BLOB;

		// генерим структурки-описатели БЛОБов, настраиваем файлы
		for(i=0; i <extent.GetSize(); i++)
		{
			inst = extent.GetAt(i);
			CaplStepData::GetAttr(inst,attr,ServNameBLOB);

			blob_descr = new SBLOBDescr();
			list_blob.Add(blob_descr);
			blob_descr->inst = inst;
			blob_descr->blob_id = _atoi(ServNameBLOB);
		}

		//Создаем итератор
		count_blobs = list_blob.GetSize();
		dbuf1.Add(&count_blobs, 4);
		for(i=0; i <list_blob.GetSize(); i++)
		{
			blob_id = list_blob.GetAt(i)->blob_id;
			dbuf1.Add(&blob_id, 4);
		}

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCreateMultiIterator(aplError, dbuf1, &SizeDose, dbuf2));

		// читаем размеры 
		dbuf2.Read(&count_blobs, 4);
		if(count_blobs != list_blob.GetSize())
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера пришло меньше блобов, чем запрашивалось!"));
			m_AplTransport->ClearInternalData();
			return false;
		}
		for(i=0; i <list_blob.GetSize(); i++)
		{
			blob_descr = list_blob.GetAt(i);
			dbuf2.Read(&blob_id, 4);
			if(blob_id!=blob_descr->blob_id)
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("С сервера БЛОБы идут в неверном порядке!"));
				m_AplTransport->ClearInternalData();
				return false;
			}
			dbuf2.Read(&blob_size, 4);
			inst = extent.GetAt(i);
			if(blob_id == 0)
			{
				CaplStepData::GetAttr(inst, attr, ServNameBLOB);
				if(ServNameBLOB == _T("0"))blob_descr->blob_size = blob_size;
			}
			else
			{
				blob_descr->blob_size = blob_size;
			}
			blob_sizes.Add((int)blob_descr->blob_size);
		}
		m_AplTransport->AT_DCCloseMultiIterator(aplError);
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}


bool CaplNetStepData::NET_LoadBlob2MemoryBN(CaplInstance *inst, LPCTSTR attr_name, BYTE* blob_dest, long &size_buf, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);


	CaplAttr *attr = 0;
	if(inst!=NULL)
	{
		attr = GetAttrDefinition(inst->GetType(), attr_name);
	}
	else
	{
		return false;
	}
	return NET_LoadBlob2Memory(inst, attr, blob_dest, size_buf, wait_text);

}

bool CaplNetStepData::NET_LoadBlob2Memory(CaplInstance *inst, CaplAttr *attr, BYTE* blob_dest, long &size_buf, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	// CHECK_CONN_PROCESSED(true, false);  - проверки на подключение - в вызываемой функции

	CHECK_PARAM(inst==NULL || attr == NULL || blob_dest==NULL);

	CString ServNameFile;
	CaplStepData::GetAttr(inst, attr, ServNameFile);
	if(ServNameFile == _T(""))return true;

	CString ext_info; ext_info.Format(_T("inst_id #%i, attr %i"), inst->GetId(), attr->id);
	if(NET_LoadFileFromSrv2Memory((LPCTSTR)ServNameFile, blob_dest, size_buf, wait_text, ext_info)) return true;

	CString add_text_err;
	add_text_err.Format( APL_T("Ошибка при загрузке в память БЛОБа \'") + ServNameFile + _T("\' inst_id %i  attr_id %i"), inst->GetId(), attr->id);
	m_MainLog.LogMessage(add_text_err, false);

	return false;
}

bool CaplNetStepData::NET_LoadFileFromSrv2Memory(LPCTSTR _ServNameBLOB, BYTE* blob_dest, long &size_buf, LPCTSTR wait_text, LPCTSTR _ext_info)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(CString(_ServNameBLOB)==_T("") || _ServNameBLOB == 0 || blob_dest == 0 || size_buf == 0);

	try{

		TEST_RECONNECT(false);
		CString ext_info(_T(""));
		if(_ext_info != 0 ) ext_info = _ext_info;
		else if(wait_text != 0 ) ext_info = wait_text;
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;
		//Создаем переменные
		BYTE *ServNameBLOB;
		UINT32 SizeNameBLOB;
		UINT32 SizeNameFile = 0;

		BYTE temporary_buf[MAX_STR_LEN];
		ServNameBLOB = ConvertString2ServerEnc(_ServNameBLOB, temporary_buf, SizeNameBLOB);

		UINT32 SizeDose = SIZE_DOSE_BLOB;
		UINT32 SizeBLOB = 0;
		CaplDataBuf DoseB;
		DoseB.SetSize(SizeDose);
		if(DoseB.GetMaxSize() < (int)SizeDose)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка при перекодировании строк в формат сервера"));
			return false;
		}

		BYTE *Dose = (BYTE*)DoseB.GetBuffer();


		//Создаем итератор
		RETURN_IF_OR_CHECK_ERR_EX(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, (BYTE*)ServNameBLOB, SizeNameFile, 
																						(BYTE*)ServNameBLOB, &SizeDose, &SizeBLOB), ext_info);

		if(SizeBLOB > (UINT32)size_buf)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Размер буфера меньше, чем размер тела читаемого документа"));
			m_AplTransport->AT_DCCloseIterator(aplError);
			return false;
		}

		//Идем по итератору
		int iterator = 0;
		UINT32 LastDose = 0;
		do
		{
			RETURN_IF_OR_CHECK_ERR_EX(!m_AplTransport->AT_DCReadFromIterator(aplError, &SizeDose, Dose, &LastDose), ext_info);

			memcpy(blob_dest + iterator, Dose, SizeDose);
			iterator += SizeDose;
		}while(LastDose == 0); 

		size_buf = SizeBLOB;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

bool CaplNetStepData::NET_ReadFirstPortionFileFromSrv2Memory(LPCTSTR _ServNameBLOB, BYTE* blob_dest, long &size_buf, bool &last, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);

	CHECK_PARAM(blob_dest == 0 || size_buf == 0);

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;
		//Создаем переменные
		UINT32 SizeDose = size_buf < SIZE_DOSE_BLOB ? size_buf : SIZE_DOSE_BLOB;
		UINT32 SizeBLOB = 0;

		//Создаем переменные
		BYTE *ServNameBLOB;
		UINT32 SizeNameBLOB;
		UINT32 SizeNameFile = 0;

		BYTE temporary_buf[MAX_STR_LEN];
		ServNameBLOB = ConvertString2ServerEnc(_ServNameBLOB, temporary_buf, SizeNameBLOB);

		//Создаем итератор
		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCreateIterator(aplError, &SizeNameBLOB, (BYTE*)ServNameBLOB,
																SizeNameFile, (BYTE*)ServNameBLOB, &SizeDose, &SizeBLOB));

		return NET_ReadNextPortionFileFromSrv2Memory(blob_dest, size_buf, last, wait_text);

	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return false;
}

bool CaplNetStepData::NET_ReadNextPortionFileFromSrv2Memory(BYTE* blob_dest, long &SizeDose, bool &last, LPCTSTR /*wait_text*/)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, false, false);

	try{

		//Идем по итератору
		UINT32 LastDose = 0;

		if(SizeDose > SIZE_DOSE_BLOB){ SizeDose = SIZE_DOSE_BLOB;}
		
		AplNetErrors aplError = APL_NET_OK;

		if(SizeDose > MAXUINT32)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Слишком большой размер порции данных!"));
			return false;
		}

		CaplDataBuf DoseB;
		DoseB.SetSize(SizeDose);
		if(DoseB.GetMaxSize() < (int)SizeDose)
		{
			SetAPLErrorWithFileInfo(APL_NET_CLI_ERROR_PROCESSED);
			return false;
		}

		BYTE *Dose = (BYTE*)DoseB.GetBuffer();
		UINT32 _SizeDose=SizeDose, _LastDose=0;

		if(!m_AplTransport->AT_DCReadFromIterator(aplError, &_SizeDose, Dose, &_LastDose) || SPEC_ERR_CODES(aplError))
		{
			SetAPLErrorWithFileInfo(aplError);
			m_AplTransport->AT_DCCloseIterator(aplError);
			last = true;
			return false;
		}
		SizeDose = _SizeDose;
		LastDose = _LastDose;
			
		memcpy(blob_dest, Dose, SizeDose);
		last = (LastDose != 0);

	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}


bool CaplNetStepData::TestUniqueAttrValuesBN(LPCTSTR entity, CaplInstance *tested_inst,
											 int count, CaplAttrValue *values)
{
	LogDebugMessageIfDef(__APL_FUNC__);
	return TestUniqueAttrValues(GetEntityBN(entity), tested_inst, count,values);
}


// Функция перегружает виртуальную функцию из StepData и вызывается из его BN вместо родной
bool CaplNetStepData::TestUniqueAttrValues(CaplEntity *entity, CaplInstance *tested_inst,
										   int count, CaplAttrValue *values, bool bIgnoreSpace)
{
	LogDebugMessageIfDef(__APL_FUNC__);
	bool is_substring = false;
	if(entity == 0) return false;
	if(count < 1)  return false;
	if(values == 0) return false;

	CString val_str_in,val_str_base;

	// Ноль в tested_inst означает что надо искать среди всех inst без исключения
	//	if(tested_inst== 0) return false;
	aplExtent extent_out;
	CaplValue val;

	if(FindInstancesWithAttrValuesInLocalCache(entity, tested_inst, count, values, extent_out, 
		false, false, true, aplSBMiddle, false, bIgnoreSpace) > 0)
		return false;
	//
	if(!m_IsConnected) return true;
	//Вызов ее в базе
	return NET_TestUniqueAttrValues(entity, tested_inst, count, values, is_substring, 0, bIgnoreSpace);

}//bool CaplNetStepData::TestUniqueAttrValues(CaplEntity *entity, int count, CaplAttrValue *values, bool is_substring)


int CaplNetStepData::NET_FindInstancesWithAttrValues2(CaplEntity *entity,
													 int count, CaplAttrValue *values, aplExtent &extent_out, 
													 bool is_substring, bool match_case, LPCTSTR wait_text,
													 aplStringBindMode string_bind_mode, bool bIgnoreSpace, LPCTSTR source)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(_T("CaplNetStepData::NET_FindInstancesWithAttrValues"));
	if(entity == 0) return -1;
	if(count < 1)  return -1;
	if(values == 0) return -1;
	extent_out.Clear();

	CString val_str_in, val_str_base;
	int i=0;
	aplExtent ext;
	CaplValue val;

	try{

		m_num_find++;

		FindInstancesWithAttrValuesInLocalCache(entity, 0, count, values, extent_out, 
			is_substring, match_case, false, string_bind_mode, true, bIgnoreSpace);

		CHECK_BD(extent_out.GetSize());
		CHECK_STATE_S(true, true, extent_out.GetSize());

		TEST_RECONNECT(-1);

		//Вызов ее в базе
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;

		CaplDataBuf dbuf_in, dbuf_out;

		if(!FeelTestFindBuf(dbuf_in, entity, 0, count, values, is_substring, match_case, string_bind_mode, &bIgnoreSpace))
		{
			return extent_out.GetSize();
		}

		SetAPLError(APL_NET_OK);

		m_AplTransport->m_source = source;
		bool ret = m_AplTransport->AT_DCFindInstancesWithAttrValuesQuery(aplError, dbuf_in, dbuf_out);

		if(aplError == APL_NET_SRV_MISSED_BUF_FORMAT)
		{
			m_AplTransport->ClearInternalData();
			dbuf_in.Clear();
			if(!FeelTestFindBuf(dbuf_in, entity, 0, count, values, is_substring, true, string_bind_mode))
			{
				return -1;
			}
			SetAPLError(APL_NET_OK);

			ret = m_AplTransport->AT_DCFindInstancesWithAttrValuesQuery(aplError, dbuf_in, dbuf_out);
		}

		if(!ret || SPEC_ERR_CODES(aplError))
		{
			SetAPLErrorWithFileInfo(aplError);
			if(aplError == APL_NET_SRV_APLAPIERR_FILE_BD_READ_ONLY){m_is_readonly = true; }
			return -1;
		}

		ext.Clear();
		if(!ApploadFromDataBuf(dbuf_out, &ext))
		{
			// Очищаем буфер в трансопроте
			m_AplTransport->ClearInternalData();
			return 0;
		}
		for(i=0; i < ext.Size; i++)
		{
			if(ext.GetAt(i)->GetType() == 0)continue;
			extent_out.Add(ext.GetAt(i));
		}

	}
	CATH_MEMORY_ANY_EXEPTION;

	// Очищаем буфер в транспорте
	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("NET_FindInstancesWithAttrValues Выход"));
	return extent_out.GetSize();

}//int CaplNetStepData::NET_FindInstancesWithAttrValues(CaplEntity *entity, CaplInstance *tested_inst,
//										int count, CaplAttrValue *values,aplExtent &extent_out, bool is_substring)



bool CaplNetStepData::NET_TestUniqueAttrValues(CaplEntity *entity, CaplInstance *tested_inst,
											   int count, CaplAttrValue *values, bool is_substring, LPCTSTR wait_text, bool bIgnoreSpace)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{
	
		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);


		AplNetErrors aplError = APL_NET_OK;
		CaplDataBuf dbuf;
		int i;

		// проверка строковых атрибюутов на длинну строки
		for(i=0; i < count; i++)
		{
			if(values[i].value.type == aplSTRING)
			{
				if(_strlen(values[i].value.sval) > 3000)
				{
					if(!m_hide_mode)
					{
						AfxMessageBox(APL_T("Введенная строка слишком длинная и не может быть проверена на уникальность"),MB_OK | MB_ICONERROR);
					}
					return false;
				}
			}
		}

		if(!FeelTestFindBuf(dbuf, entity, tested_inst, count, values, is_substring, true, aplSBMiddle, &bIgnoreSpace))
		{
			return false;
		}

		SetAPLError(APL_NET_OK);

		bool ret = m_AplTransport->AT_DCTestUniqueAttrValues(aplError, dbuf);

		if(aplError == APL_NET_SRV_MISSED_BUF_FORMAT)
		{
			m_AplTransport->ClearInternalData();
			dbuf.Clear();
			if(!FeelTestFindBuf(dbuf, entity, tested_inst, count, values, is_substring, true, aplSBMiddle))
			{
				return false;
			}
			SetAPLError(APL_NET_OK);

			ret = m_AplTransport->AT_DCTestUniqueAttrValues(aplError, dbuf);
		}
		if(aplError != APL_SOCK_OPERATION_IN_PROGRESS)m_AplTransport->ClearInternalData();

		if(ret)
		{
			if(aplError == APL_NET_OK)
			{
				return true;
			}
			else if(aplError == APL_NET_SRV_NOT_UNIQUE)
			{
				return false;
			}
			else
			{
				SetAPLError(aplError);
			}
		}
		else
		{
			SetAPLError(aplError);
			return false;
		}

	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("NET_TestUniqueAttrValues выход"));
	return false;
}//bool CaplNetStepData::NET_TestUniqueAttrValues(CaplEntity *entity, int count, CaplAttrValue *values, bool is_substring)


CString CaplNetStepData::NET_GetServerDateTime()
{
	COleDateTime time;
	CString currentDate;

	if(true == NET_GetServerDateTime(time))
		aplDate2String(time, currentDate);
		
	return currentDate;
}

//возвращает дату время на сервере (lite или ORACLE)
bool CaplNetStepData::NET_GetServerDateTime(COleDateTime &odt)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);
	AplNetErrors aplError = APL_NET_OK;
	BYTE net_buf[8];

	CHECK_BD(false);
	if(m_AplTransport == NULL)
		return false;

	try{

		if(!m_IsWaitDlg){m_AplTransport->SetManageMode(MANAGE_ABSENT); }
		m_LastAplErrorCode = APL_NET_OK;
		
		//Загружаем экстент с сервера
		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCQuerSysTime(aplError,net_buf));

		//копирование
		DATE s_date;
		memcpy((void*)&s_date,(void*)net_buf, 8);
		odt=s_date;

	}
	CATH_MEMORY_ANY_EXEPTION;

	LogDebugMessageIfDef( APL_T("NET_GetServerDateTime выход"));
	return true;
}


//возвращает список активных (приконнекченых) юзеров
bool CaplNetStepData::NET_GetActiveUserList(CaplTAggr<SUserLogInfo*,SUserLogInfo*,APLAGGR_AUTOKILLREF> &ListConnects, bool only_me, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);
	ListConnects.Clear();

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;
		CaplDataBuf dbuf_in, dbuf_out;
		SUserLogInfo *el;
		bool old_funct = false;
		int size, user_id, ip, i;

		CaplInstance *user;
		aplExtent users;
		CString error = _T(""), tmp;
		int i_ver_buf = 4;
		if(only_me) i_ver_buf = 5;
		int len_ver = 0;
		const char* version = 0; // заголовки буферов - однобайтовые! 

		LoadAllUsers(users);
		do 
		{
			switch(i_ver_buf)
			{
			case 5:
				// заголовки буферов - однобайтовые!
				len_ver = strlen(ACTIVE_LIST_header_05) + 1;
				version = ACTIVE_LIST_header_05;
				break;
			case 4:
				// заголовки буферов - однобайтовые!
				len_ver = strlen(ACTIVE_LIST_header_04) + 1;
				version = ACTIVE_LIST_header_04;
				break;
			case 2:
				len_ver = strlen(ACTIVE_LIST_header_02) + 1;
				version = ACTIVE_LIST_header_02;
				break;
			}
			dbuf_in.Add(version, len_ver);

			if(m_AplTransport->AT_DCQuerActiveUserListEx(aplError, dbuf_in, dbuf_out))
			{
				char version[100]; // заголовки буферов - однобайтовые! 

				dbuf_out.Reset();
				dbuf_out.Read(version, strlen(ACTIVE_LIST_header_01) + 1);
				if(CStringA(ACTIVE_LIST_header_01) == version)
				{
					if(5 == i_ver_buf)
					{
						// видать, сервер старый, его надо запрашивать предыдущей версией буфера
						i_ver_buf = 4;
						dbuf_in.Clear();
						m_AplTransport->ClearInternalData();
						continue;
					}
					else if(4 == i_ver_buf)
					{
						// видать, сервер старый, его надо запрашивать второй версией буфера
						i_ver_buf = 2;
						dbuf_in.Clear();
						m_AplTransport->ClearInternalData();
						continue;
					}
					old_funct = true;
				}
				else if(CStringA(ACTIVE_LIST_header_02) == version)
				{
					i_ver_buf = 2;
				}
				else if(CStringA(ACTIVE_LIST_header_04) == version)
				{
					i_ver_buf = 4;
				}
				else if(CStringA(ACTIVE_LIST_header_05) == version)
				{
					i_ver_buf = 5;
				}
				else
				{
					m_AplTransport->ClearInternalData();
					SetAPLErrorWithFileInfoEx(APLAPIERR_INVALIDDATAFORMAT, APL_T("Ошибка при запросе активных пользователей"));
					return false;
				}
				dbuf_out.Read(&size,4);
				for(i=0; i <size; i++)
				{
					if(old_funct)
					{
						dbuf_out.Read(&user_id, 4);
						dbuf_out.Read(&ip, 4);
						user = GetInstById(user_id, false);
						if(user == 0)
						{
							tmp.Format(_T("%i"), user_id);
							if(error != _T(""))error += _T(", ");
							error += tmp;
							continue;
						}
						el = new SUserLogInfo();
						el->user = user;
						el->ip_cli = Ip2String(ip);
						ListConnects.Add(el);
					}
					else
					{
						el = ReadUserLogInfoFromDataBuf(dbuf_out, i_ver_buf);
			
						if(0 == el) continue;
						if(el->user == 0)
						{
							tmp.Format(_T("%i"), el->user_id);
							if(error != _T("")) error += _T(", ");
							error += tmp;
						}
						ListConnects.Add(el);
					}
				}
				m_AplTransport->ClearInternalData();
				if(error != _T("") && !m_hide_mode)
				{
					AfxMessageBox(CString( APL_T("Сервер прислал идентификаторы пользователей, которых нет в БД: ")) + error, MB_ICONERROR);
				}
			}
			else
			{
				if(APL_NET_SRV_APLAPIERR_INVALIDDATAFORMAT == aplError)
				{
					if(5 == i_ver_buf)
					{
						i_ver_buf = 4;
						dbuf_in.Clear();
						m_AplTransport->ClearInternalData();
						continue;
					}
					else if(4 == i_ver_buf)
					{
						i_ver_buf = 2;
						dbuf_in.Clear();
						m_AplTransport->ClearInternalData();
						continue;
					}
				}
			}
			if(SPEC_ERR_CODES(aplError))
			{
				SetAPLErrorWithFileInfo(aplError);
				dbuf_out.Clear();
			}
			break;

		}while (true);

	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

//возвращает список активных (приконнекченых) юзеров
bool CaplNetStepData::NET_GetActiveUserList(CaplTAggr <CaplInstance*, CaplInstance*, APLAGGR_LIST> &ext, LPCTSTR wait_text, CUIntArray *array_ip)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef(__APL_FUNC__);
	ext.Clear();
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;
		CaplDataBuf dbuf_in, dbuf_out;
		bool old_funct = true;
		int size,user_id,ip,i;
		CaplInstance *user;
		aplExtent users;
		CString error = _T(""), tmp;

		if(array_ip != 0)
		{
			LoadAllUsers(users);
			if(m_AplTransport->AT_DCQuerActiveUserListEx(aplError, dbuf_in, dbuf_out))
			{
				old_funct = false;
				ext.Clear();
				char version[100]; // заголовки буферов - однобайтовые! 

				dbuf_out.Reset();
				dbuf_out.Read(version, strlen(ACTIVE_LIST_header_01) + 1);
				if(CStringA(ACTIVE_LIST_header_01) != version)
				{
					m_AplTransport->ClearInternalData();
					SetAPLErrorWithFileInfoEx(APLAPIERR_INVALIDDATAFORMAT, APL_T("Ошибка при запросе списка активных пользователей"));
					return false;
				}

				dbuf_out.Read(&size, 4);

				for(i=0; i <size; i++)
				{
					dbuf_out.Read(&user_id, 4);
					dbuf_out.Read(&ip, 4);
					user = GetInstById(user_id ,false);
					if(user == 0)
					{
						tmp.Format(_T("%i"), user_id);
						if(error != _T(""))error += _T(", ");
						error += tmp;
						continue;
					}
					ext.Add(user);
					array_ip->Add(ip);
				}
				m_AplTransport->ClearInternalData();
				if(error != _T("") && !m_hide_mode)
				{
					AfxMessageBox(CString( APL_T("Сервер прислал идентификаторы пользователей, которых нет в БД: ")) + error, MB_ICONERROR);
				}
			}
		}
		if(old_funct)
		{
			aplExtent extt;
			RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCQuerActiveUserList(aplError, dbuf_out));
			
			ApploadFromDataBuf(dbuf_out,&extt);
			for(i=0; i <extt.GetSize(); i++)
			{
				ext.Add(extt[i]);
			}
		}

	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

/// отображает диалог с историей изменения атрибутов и поиском по параметрам
bool CaplNetStepData::ShowAttrsHistoryDlg()
{
	CDlgAttrsHistory dlg;
	dlg.m_data = this;
	dlg.DoModal();
	
	return true;
}

// Возвращает в строковом массиве все значения одного атрибута всех инстансов
bool CaplNetStepData::NET_GetAllAttrValues(CaplAttr* attr, DWORD count_in_result, CaplTAggr <CaplValue*,CaplValue*,APLAGGR_UNIQUE_LIST_AUTOKILLREF> &values,
											DWORD &real_size, LPCTSTR source, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(_T("CaplNetStepData::NET_GetActiveUserList"));

	bool retval = true;
	pCaplInstance *instmap = 0;

	CHECK_BD(false);
	CHECK_STATE_S(true, true, false);	

	try{

		TEST_RECONNECT(false);

		m_AplTransport->SetWaitDlgText(wait_text);

		CaplDataBuf dbuf_in;
		CaplDataBuf dbuf_out;
		CaplDataBuf dbuf_tmp;
		AplNetErrors aplError = APL_NET_OK;

		int i_val;
		int k, i;
		CaplValue value, *p_value;

		values.Clear();
		real_size = 0;
		if(attr == 0) return false;
		if(count_in_result == 0) return true;

		switch(attr->type)
		{
		case aplSTRING:
		case aplINTEGER:
		case aplINSTANCE:
		case aplSELECT:
		case aplREAL:
			break;
		default:
			SetAPLError(APL_NET_CLI_MISSED_DATA, APL_T("Допустимые типы атрибутов: строка, целое число, дробное число"));
			return false;
			break;
		}

		// заголовки буферов - однобайтовые! 
		int len_str = strlen(std_GetAllAttrValues_in_header) + 1;
		const char* version = std_GetAllAttrValues_in_header; // заголовки буферов - однобайтовые! 

		dbuf_in.Add(version, len_str);
		dbuf_in.m_ansi_string = m_global_ansi_string;
		i_val = attr->id;
		dbuf_in.Add(&i_val, sizeof(i_val));
		dbuf_in.Add(&count_in_result, sizeof(count_in_result));

		dbuf_in.AddStrBuf(source);

		m_AplTransport->SetWaitDlgText(wait_text);


		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCGetAllAttrValues(aplError, dbuf_in, dbuf_out));

		char* header = 0; // заголовки буферов - однобайтовые! 
		// Заголовок
		k = strlen(std_GetAllAttrValues_out_header) + 1;
		header = new char[k];
		dbuf_out.Read(header, k);
		if(CStringA(std_GetAllAttrValues_out_header) != header)
		{
			delete header;
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел буфер неизвестного формата"));
			m_AplTransport->ClearInternalData();
			return false;
		}
		delete[] header; header = 0;

		// полное количество значений для этого атрибута
		dbuf_out.Read(&(real_size), 4);

		// количество инстансов в буфере
		dbuf_out.Read(&(k), 4);

		if(k > 0)
		{
			CUIntArray id_s;
			int id_b;
			CaplLoadData ld(this, DEF_SOURCE);
			int last_size_inst;
			CaplInstance *inst;

			for(i=0; i<k; i++)
			{
				dbuf_out.Read(&id_b); 
				id_s.Add(id_b);
				ld.AddQuery( _T('a'), id_b, (CaplEntity*)0, (CaplAttr*)0, true);
			}
			dbuf_tmp.Add(dbuf_out.GetCurBuffer(), dbuf_out.m_Size - dbuf_out.GetCurPos());
			m_AplTransport->ClearInternalData();

			ld.LoadEx();

			last_size_inst = instances.Size;

			instmap = new pCaplInstance[k];

			for(i=0; i<k; i++)
			{
				id_b = id_s[i];
				aplQFindInstIdInExtent(*((aplExtent*)(&instances)), id_b, last_size_inst, false, &inst);
				instmap[i] = inst;
			}
		}
		else
		{
			dbuf_tmp.SetExternalData(dbuf_out.GetCurBuffer(), dbuf_out.m_Size - dbuf_out.GetCurPos());
		}

		// количество строк в массиве
		k = 0;
		dbuf_tmp.m_ansi_string = m_global_ansi_string;
		dbuf_tmp.Read(&(k),4);
		for(i=0; i <k; i++)
		{
			// чтение значения
			aplReadValueFromBuf(dbuf_tmp, instmap, &value);
			p_value = new CaplValue();
			p_value->Set(value);
			values.Add(p_value);
		}

	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		retval = false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	if (instmap != 0) delete []instmap;

	m_AplTransport->ClearInternalData();	
	
	return retval;
}


/// возвращает списки ентитей, которые сейчас отслеживаются, и инстансы которых храняться в истории изменения атрибутов
bool CaplNetStepData::NET_GetAttrsHistoryEntities(CArray<CaplEntity*,CaplEntity*> &list_active, CArray<CaplEntity*,CaplEntity*> &list_all, 
													aplExtent &users, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	TEST_RECONNECT(false);

	try{

		m_AplTransport->SetWaitDlgText(wait_text);
		
		CaplDataBuf dbuf_in;
		CaplDataBuf dbuf_out;
		AplNetErrors aplError = APL_NET_OK;
		CaplEntity* entity;
		CaplInstance* user;

		int code1 = 0, code2 = 0;
		int command = 0;
		int size, k, i;

		bool ver_buf_0001 = false;

point_gah_old_buf1:
		int len_header_in = strlen(std_DCAttrsHistory_header) + 1;
		const char* header_in;  // заголовки буферов - однобайтовые! 

		if(ver_buf_0001)
			header_in = std_DCAttrsHistory_header_0001;
		else
			header_in = std_DCAttrsHistory_header;

		list_active.RemoveAll();
		list_all.RemoveAll();
		users.Clear();

		aplExtent users_tmp;
		LoadAllUsers(users_tmp);

		dbuf_in.Add(header_in, len_header_in);

		dbuf_in.Add(&code1, sizeof(code1));
		dbuf_in.Add(&code2, sizeof(code2));

		dbuf_in.Add(&command, sizeof(command));

		// RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCGetAttrsHistory(aplError, dbuf_in, dbuf_out));

		if(!m_AplTransport->AT_DCGetAttrsHistory(aplError, dbuf_in, dbuf_out))
		{
			if(APL_NET_SRV_APLAPIERR_INVALIDDATAFORMAT == aplError || APL_NET_SRV_MISSED_BUF_FORMAT == aplError)
			{
				if(!ver_buf_0001)
				{
					ver_buf_0001 = true;
					dbuf_in.Clear();
					goto point_gah_old_buf1;
				}
			}
		}
		if(SPEC_ERR_CODES(aplError))
		{
			RETURN_IF_OR_CHECK_ERR(true);
		}

		char* header_out = 0; // заголовки буферов - однобайтовые! 
		// Заголовок
		k = strlen(std_DCAttrsHistory_header) + 1;
		header_out = new char[k];

		dbuf_out.Read(header_out,k);

		if(CStringA(std_DCAttrsHistory_header_0001) == header_out)
		{
			if(!ver_buf_0001)
			{
				delete header_out;
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел буфер неизвестного формата"));
				m_AplTransport->ClearInternalData();
				return false;
			}
		}
		else if(CStringA(std_DCAttrsHistory_header) != header_out)
		{
			delete header_out;
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел буфер неизвестного формата"));
			m_AplTransport->ClearInternalData();
			return false;
		}
		delete[] header_out; header_out = 0;

		// флаг операции
		k = 0;
		dbuf_out.Read(&(k), 4);
		if(k != 0)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел неверный флаг операции"));
			m_AplTransport->ClearInternalData();
			return false;
		}
		// количество отслеживаемых в настоящее время
		dbuf_out.Read(&(size), 4);
		for(i=0; i <size; i++)
		{
			dbuf_out.Read(&(k), 4);
			entity = GetEntityById(k);
			if(entity == 0)
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел неверный идентификатор entity"));
				m_AplTransport->ClearInternalData();
				return false;
			}
			list_active.Add(entity);
		}
		// количество находящихся в таблице
		dbuf_out.Read(&(size), 4);
		for(i=0; i <size; i++)
		{
			dbuf_out.Read(&(k), 4);
			entity = GetEntityById(k);
			if(entity == 0)
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел неверный идентификатор entity"));
				m_AplTransport->ClearInternalData();
				return false;
			}
			list_all.Add(entity);
		}
		// количество юзеров
		dbuf_out.Read(&(size), 4);
		for(i=0; i <size; i++)
		{
			dbuf_out.Read(&(k), 4);
			user = GetInstById(k);
			if(user == 0)
			{
				user = CreateInstance(m_ent_apl_user, true);
				user->SetId(k);
	// 			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел неверный идентификатор пользователя"));
	// 			m_AplTransport->ClearInternalData();
	// 			return false;
			}
			users.Add(user);
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

bool CaplNetStepData::IsAttrStoreBLOB(CaplAttr* attr)
{
	if(attr == 0) return false;
	if(m_list_attrs4blobs.QGetPointerByIn(attr->id) == 0 ) return false;
	return true;
}

/// Получить список атрибутов, в которых живут идентификаторы БЛОБов
/// in - идентификатор атрибута, out - указатель на CaplAttr
bool CaplNetStepData::NET_GetAttrs4Blobs(CaplMap &list_attrs, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		CaplDataBuf dbuf_out;

		AplNetErrors aplError = APL_NET_OK;

		CaplAttr* attr;
		int attr_id;
		int size, k;

		list_attrs.RemoveAll();

	//	ДляLite теперь тоже поддерживается
	// 	if(m_TypeSrv.Find("ORA") == -1)
	// 		return false;

		m_AplTransport->SetWaitDlgText(wait_text);

		if(!m_AplTransport->AT_DCGetAttrs4Blobs(aplError, dbuf_out) || SPEC_ERR_CODES(aplError))
		{
			//SPEC_SET_ERR(aplResult);
			if(aplError == APL_NET_SRV_APLAPIERR_FILE_BD_READ_ONLY)
			{
				m_is_readonly = true;
			}
			dbuf_out.Clear();
			return false;
		}

		char* header = 0; // заголовки буферов - однобайтовые! 
		// Заголовок
		k = strlen(std_DCAttrs4Blob_header) + 1;
		header = new char[k];
		dbuf_out.Read(header, k);

		if(CStringA(std_DCAttrs4Blob_header) != header)
		{
			delete header;
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел буфер неизвестного формата"));
			m_AplTransport->ClearInternalData();
			return false;
		}
		delete[] header; header = 0;

		dbuf_out.Read(&size, 4);
		for(k=0; k<size; k++)
		{
			if(!dbuf_out.Read(&attr_id, 4)){ break;}
			attr = GetAttrDefinitionById(attr_id);
			if(attr != 0)
			{
				list_attrs.Add(attr->id, (long)attr);
			}
		}
		list_attrs.SortIn();
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

/// запрашивает историю изменения значений атрибутов
bool CaplNetStepData::NET_GetAttrsHistory(SQAH_Filter &filter, CaplTAggr<SQAH_Info*,SQAH_Info*,APLAGGR_LIST_OR_AUTOKILLREF> &list_rez, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		m_AplTransport->SetWaitDlgText(wait_text);
		CaplDataBuf dbuf_in;
		CaplDataBuf dbuf_out;

		AplNetErrors aplError = APL_NET_OK;
		SQAH_Info* el;

		int code1 = 0, code2 = 0;
		int i_command = 1;
		BYTE b_command = 0;
		int size, k;
		bool ver_buf_0001 = false;

point_gah_old_buf2:
		int len_header_in = strlen(std_DCAttrsHistory_header) + 1;
		const char* header_in;  // заголовки буферов - однобайтовые! 
		
		if(ver_buf_0001)
			header_in = std_DCAttrsHistory_header_0001;
		else
			header_in = std_DCAttrsHistory_header;

		list_rez.Clear();

		if(filter.ent_id == 0)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("В фильтре должно быть задано значение entity"));
			return false;
		}

		dbuf_in.Add(header_in, len_header_in);

		dbuf_in.Add(&code1, sizeof(code1));
		dbuf_in.Add(&code2, sizeof(code2));

		dbuf_in.Add(&i_command, sizeof(i_command));
		dbuf_in.m_ansi_string = m_global_ansi_string;

		b_command = QUL_F_ent;
		dbuf_in.Add(&b_command, 1);
		dbuf_in.Add(&(filter.ent_id), 4);

		if(filter.user_id != 0)
		{
			b_command = QUL_F_user;
			dbuf_in.Add(&b_command, 1);
			dbuf_in.Add(&(filter.user_id), 4);
		}
		if(filter.date_beg_1 != _T(""))
		{
			b_command = QUL_F_date_beg_1;
			dbuf_in.Add(&b_command, 1);
			dbuf_in.AddStrBuf(filter.date_beg_1);
		}
		if(filter.date_end_1 != _T(""))
		{
			b_command = QUL_F_date_end_1;
			dbuf_in.Add(&b_command, 1);
			dbuf_in.AddStrBuf(filter.date_end_1);
		}

		// RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCGetAttrsHistory(aplError, dbuf_in, dbuf_out)); - оставлен для примера

		if(!m_AplTransport->AT_DCGetAttrsHistory(aplError, dbuf_in, dbuf_out))
		{
			if(APL_NET_SRV_APLAPIERR_INVALIDDATAFORMAT == aplError || APL_NET_SRV_MISSED_BUF_FORMAT == aplError)
			{
				if(!ver_buf_0001)
				{
					ver_buf_0001 = true;
					dbuf_in.Clear();
					goto point_gah_old_buf2;
				}
			}
		}
		if(SPEC_ERR_CODES(aplError))
		{
			RETURN_IF_OR_CHECK_ERR(true);
		}
		char* header_out = 0; // заголовки буферов - однобайтовые! 
		// Заголовок
		k = strlen(std_DCAttrsHistory_header) + 1;
		header_out = new char[k];
		dbuf_out.Read(header_out, k);

		if(CStringA(std_DCAttrsHistory_header_0001) == header_out)
		{
			if(!ver_buf_0001)
			{
				delete header_out;
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел буфер неизвестного формата"));
				m_AplTransport->ClearInternalData();
				return false;
			}
		}
		else if(CStringA(std_DCAttrsHistory_header) != header_out)
		{
			delete header_out;
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел буфер неизвестного формата"));
			m_AplTransport->ClearInternalData();
			return false;
		}
		delete[] header_out; header_out = 0;

		// флаг операции
		k = 0;
		dbuf_out.Read(&(k), 4);
		if(k != 1)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел неверный флаг операции"));
			m_AplTransport->ClearInternalData();
			return false;
		}

		dbuf_out.Read(&size, 4);
		for(k=0; k<size; k++)
		{
			el = new SQAH_Info();
			list_rez.Add(el);

			if(!dbuf_out.Read(&(el->AUSId),4)){break;}
			if(!dbuf_out.Read(&(el->inst_id),4)){break;}
			if(!dbuf_out.Read(&(el->user_id),4)){break;}
			if(!dbuf_out.Read(&(el->attr_id),4)){break;}
			if(!dbuf_out.Read(&(el->update_date), sizeof(DATE))){break;}
			if(ver_buf_0001)
			{
				if(!dbuf_out.Read(&(el->int_val_old),4)){break;}
				if(!dbuf_out.Read(&(el->int_val_new),4)){break;}

				dbuf_out.ReadStrBuf(el->str_val_old);
				dbuf_out.ReadStrBuf(el->str_val_new);

				if(!dbuf_out.Read(&(el->rea_val_old), sizeof(double))){break;}
				if(!dbuf_out.Read(&(el->rea_val_new), sizeof(double))){break;}
				el->single_val = false;
			}
			else
			{
				aplReadValueFromBuf(dbuf_out, 0, &(el->val_old));
				aplReadValueFromBuf(dbuf_out, 0, &(el->val_new));
			}
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

/// запрашивает историю коннекта пользователей
bool CaplNetStepData::NET_QueryUsersLog(CaplTAggr<SUserLogInfo*,SUserLogInfo*,APLAGGR_AUTOKILLREF> &array, LPCTSTR wait_text, QUL_Filter* filter)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);
	array.Clear();
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;
		CaplDataBuf dbuf_in;
		CaplDataBuf dbuf_out;
		SUserLogInfo* el;
		int num,i;
		int i_ver_buf = 4;
		if(filter== 0)
		{
			RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCQueryUsersLog(aplError, dbuf_out));
		}
		else
		{
			BYTE command;
			int len_ver;
			const char* version; // заголовки буферов - однобайтовые! 

			do 
			{
				switch(i_ver_buf)
				{
				case 4:
					len_ver = strlen(std_QueryUsersLogFilter_004_h) + 1;
					version = std_QueryUsersLogFilter_004_h;
					break;
				case 2:
					len_ver = strlen(std_QueryUsersLogFilter_002_h) + 1;
					version = std_QueryUsersLogFilter_002_h;
					break;
				default:
					len_ver = strlen(std_QueryUsersLogFilter_001_h) + 1;
					version = std_QueryUsersLogFilter_001_h;
				}

				dbuf_in.Clear();
				dbuf_in.Add(version, len_ver);
				dbuf_in.m_ansi_string = m_global_ansi_string;

				if(filter->AUSId != 0)
				{
					command=QUL_F_AUSId;
					dbuf_in.Add(&command, 1);
					dbuf_in.Add(&(filter->AUSId),4);
				}
				if(filter->user_id != 0)
				{
					command=QUL_F_user;
					dbuf_in.Add(&command, 1);
					dbuf_in.Add(&(filter->user_id),4);
				}
				if(filter->admin_user_id != 0)
				{
					command=QUL_F_admin_user;
					dbuf_in.Add(&command, 1);
					dbuf_in.Add(&(filter->admin_user_id),4);
				}
				if(filter->date_beg_1 != _T(""))
				{
					command=QUL_F_date_beg_1;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->date_beg_1);
				}
				if(filter->date_beg_2 != _T(""))
				{
					command=QUL_F_date_beg_2;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->date_beg_2);
				}
				if(filter->date_end_1 !=  _T(""))
				{
					command=QUL_F_date_end_1;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->date_end_1);
				}
				if(filter->date_end_2 !=  _T(""))
				{
					command=QUL_F_date_end_2;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->date_end_2);
				}
				if(filter->status != 100)
				{
					command=QUL_F_status;
					dbuf_in.Add(&command, 1);
					dbuf_in.Add(&(filter->status),4);
				}

				if(filter->ip_cli !=  _T(""))
				{
					command=QUL_F_ip_cli;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->ip_cli);
				}
				if(filter->ip_srv !=  _T(""))
				{
					command=QUL_F_ip_srv;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->ip_srv);
				}
				if(filter->ver_cli != _T(""))
				{
					command=QUL_F_ver_cli;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->ver_cli);
				}
				if(filter->ver_srv != _T(""))
				{
					command=QUL_F_ver_srv;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->ver_srv);
				}
				if(filter->time_connect != _T(""))
				{
					command=QUL_F_time_connect;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->time_connect);
				}
				if(filter->module_name != _T(""))
				{
					command=QUL_F_module_name;
					dbuf_in.Add(&command, 1);
					dbuf_in.AddStrBuf(filter->module_name);
				}

				if(!m_AplTransport->AT_DCQueryUsersLogEx(aplError, dbuf_in, dbuf_out))
				{
					if(APL_NET_SRV_APLAPIERR_INVALIDDATAFORMAT == aplError)
					{
						if(4 == i_ver_buf)
						{
							i_ver_buf = 2;
							continue;
						}
						else if(2 == i_ver_buf)
						{
							i_ver_buf = 1;
							continue;
						}
					}
				}
				if(SPEC_ERR_CODES(aplError))
				{
					SetAPLErrorWithFileInfo(aplError);
					dbuf_out.Clear();
				}
				break;

			}while (true);
		}

		// читаем из буфера	
		char version[28]; // заголовки буферов - однобайтовые! 

		dbuf_out.Reset();
		dbuf_out.Read(version,28);
		if(CStringA(std_LoadUserLogInfo_header104) == version)
		{
			i_ver_buf = 4;
		}
		else if(CStringA(std_LoadUserLogInfo_header103) == version)
		{
			i_ver_buf = 3;
		}
		else if(CStringA(std_LoadUserLogInfo_header102) == version)
		{
			i_ver_buf = 2;
		}
		else if(CStringA(std_LoadUserLogInfo_header101) == version)
		{
			i_ver_buf = 1;
		}
		else
		{
			//MessageBox(m_hwnd,APL_T("Не тот формат загружаемого буфера"),APL_T("Ошибка"),MB_ICONERROR);
			SetAPLErrorWithFileInfoEx(APLAPIERR_INVALIDDATAFORMAT, APL_T("Ошибка при запросе истории подключений"));
			return false;
		}

		dbuf_out.Read(&num);
		CUIntArray for_load;

		for(i=0; i <num; i++)
		{
			el = ReadUserLogInfoFromDataBuf(dbuf_out, i_ver_buf);
		
			if(el->user != 0 || el->user_id != 0 || el->exit_status == 10)
			{
				array.Add(el);
				if(0 == el->user && 0 != el->user_id) for_load.Add(el->user_id);
			}
			else
			{
				delete el;
			}
		}
		if(for_load.GetSize()>0)
		{
			CaplLoadData ld(this,DEF_SOURCE);
			for(i=0; i <for_load.GetSize(); i++)
			{
				ld.AddQuery( _T('a'), for_load.GetAt(i), (CaplEntity*)0, (CaplAttr*)0, true);
			}
			ld.LoadEx();
			int last_size_inst = instances.GetSize();
			for(i=0; i <array.GetSize(); i++)
			{
				el = array.GetAt(i);
				if(el->user == 0)
				{
					aplQFindInstIdInExtent(*((aplExtent*)(&instances)), el->user_id, last_size_inst, false, &(el->user));
				}
				if(el->user == 0)
				{
					// Ошибка!!
					//				SetAPLErrorWithFileInfo(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
					//				array.Clear();
					//				return false;
					// не выходим - юзера просто удалили из БД. Лог-то показывать все равно надо
				}
				if(el->user_base4admin == 0)
				{
					aplQFindInstIdInExtent(*((aplExtent*)(&instances)), el->user_id, last_size_inst, false, &(el->user_base4admin));
				}
			}
		}
		// Упорядочиваем для всяких целей...
		CSortClass::SortInstById(this->instances);

	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

/** чистит историю коннекта пользователей
@param mode режим очистки:
0 - удаляются все записи (массив array может быть пустым)
1 - удаляются записи, перечистенный в массиве array (фильтрация идет по дате-времени подключения 
и дате-времени отключения; этого достаточно для определения уникальной записи; остальные 
поля структуры SUserLogInfo можно не заполнять)
2 - удаляются записи, предшевствующие записи с индексом 0 в массиве array (фильтрация идет по 
дате-времени подключения пользователя; остальные поля структуры SUserLogInfo можно не заполнять)
*/
bool CaplNetStepData::NET_ClearUsersLog(CaplTAggr<SUserLogInfo*,SUserLogInfo*,APLAGGR_AUTOKILLREF> &array, int mode, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		AplNetErrors aplError = APL_NET_OK;
		CaplDataBuf dbuf;
		SUserLogInfo* el;
		int i_val, num,i;

		DATE date;
		// заголовки буферов - однобайтовые! 
		dbuf.Add(std_LoadUserLogInfo_header102, strlen(std_LoadUserLogInfo_header102) + 1);
		dbuf.m_ansi_string = m_global_ansi_string;
		
		num = array.GetSize();
		dbuf.Add(&num, 4);

		for(i=0; i<array.GetSize(); i++)
		{
			el = array.GetAt(i);
			i_val = el->user_id;
			dbuf.Add(&i_val, 4);
			dbuf.AddStrBuf(el->ip_cli);
			dbuf.AddStrBuf(el->ip_srv);

			date=el->date_login;
			dbuf.Add(&date, sizeof(DATE));
			date=el->date_logout;
			dbuf.Add(&date, sizeof(DATE));

			i_val = el->exit_status;
			dbuf.Add(&i_val,4);
		}
		dbuf.Add(&mode,4);

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCClearUsersLog(aplError, dbuf));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}


bool CaplNetStepData::NET_GetLicenseStatus(CString &status, long &code)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);
	status = _T("--");
	CHECK_BD(true);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);
		
		status = _T("?");
		AplNetErrors aplError = APL_NET_OK;

		UINT32 _code = 0;

		if(!m_AplTransport->ABO_DCGetLicenseStatus(aplError, status, &_code) || aplError!=APL_NET_OK)
		{
			SetAPLError(aplError);

			return false;
		}
		code = _code;

	}
	CATH_MEMORY_ANY_EXEPTION;

	LogDebugMessageIfDef( APL_T("NET_GetLicenseStatus выход"));

	return false;

}//void CaplNetStepData::NET_GetLicenseStatus(CString &status)

CaplInstance* CaplNetStepData::IsUserCorrect(LPCTSTR username, LPCTSTR password)
{
	aplExtent users;
	int i;
	CString tmpbuf, tmpbuf2;

	CaplLoadData LoadExData(this,DEF_SOURCE);
	LoadExData.ClearQuery();
	LoadExData.AddQuery( _T('e'), 0, GetEntId(m_ent_apl_user), 0, true, true);
	if(!LoadExData.LoadEx(true,&users))
	{
		return 0;
	}

	for(i=0; i <users.GetSize(); i++)
	{
		if(users[i] != 0)
		{
			GetAttr(users[i], m_attr_apl_user__name, tmpbuf);
			GetAttr(users[i], m_attr_apl_user__pass, tmpbuf2);
			if(tmpbuf.CompareNoCase(username) == 0)
			{
				if(aplCheckPassword(password, tmpbuf2))
				{
					return users[i];
				}
				else
				{
					return 0;
				}
			}
		}
	}
	return 0;
}


/// загружает список переменных сервера
bool CaplNetStepData::NET_LoadBDInfo()
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);
		
		AplNetErrors aplError = APL_NET_OK;
		CaplDataBuf dbuf_out;

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCLoadBDInfo(aplError, dbuf_out));

		m_bd_parameters.Clear();

		CString name_param = _T(""), val_param_s = _T("");
		// заголовки буферов - однобайтовые! 
		char *sbuf;
		int num;

		int k = strlen(std_LoadBDParam_header)+1;
		sbuf = new char[k];
		dbuf_out.Read(sbuf, k);

		if(CStringA(std_LoadBDParam_header) != sbuf)
		{
			//MessageBox(m_hwnd,APL_T("Не тот формат загружаемого буфера"),APL_T("Ошибка"),MB_ICONERROR);
			SetAPLErrorWithFileInfoEx(APLAPIERR_INVALIDDATAFORMAT, APL_T("Ошибка при запросе информации о БД"));
			delete sbuf;
			m_AplTransport->ClearInternalData();
			return false;
		}
		delete sbuf;
		dbuf_out.Read(&num);
		dbuf_out.m_ansi_string = m_global_ansi_string;
		
		for(int i=0; i < num; i++)
		{
			dbuf_out.ReadStrBuf(name_param);
			dbuf_out.ReadStrBuf(val_param_s);

			m_bd_parameters.Add(name_param, val_param_s);
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

/// По имени переменной возвращает предыдущее полученное с сервера значение. Если значения нет - пустую строку
bool CaplNetStepData::NET_GetBDStringInfo(LPCTSTR param_name, CString &param_value)
{
	param_value = m_bd_parameters.Get(param_name);

	return true;
}

CString CaplNetStepData::NET_GetBDStringInfo(LPCTSTR param_name)
{
	return m_bd_parameters.Get(param_name);
}


// ПРоверки на сохранение инстансов экстента
bool CaplNetStepData::CheckSaved(aplExtent* extent)
{
	LogDebugMessageIfDef(__APL_FUNC__);
	if(extent->GetSize() == 0)
		return true;

	bool flag_save = false;
	bool flag_temporary = false;
	bool flag_del = false;
	for(int i=0; i <extent->GetSize(); i++)
	{
		if(extent->GetAt(i)== 0)
		{
			flag_del = true;
		}
		else
		{
			if(extent->GetAt(i)->GetType() == 0)
			{
				flag_del = true;
			}
			if(extent->GetAt(i)->GetId() == 0)
			{
				flag_save = true;
			}
			if(extent->GetAt(i)->GetTemporary())
			{
				flag_temporary = true;
			}
		}
	}
	if(flag_del)
	{
		if(!m_hide_mode) MessageBox(m_hwnd,
			 APL_T("В списке элементов  на установку доступа  есть пустые или \n удаленные элементы данных. Для этих элементов невозможно выполнить \n         сохранение в БД и установку прав доступа."),
			 APL_T("Служба RPC PDM Step Suite"),
			MB_OK);
		return false;
	}
	if(flag_temporary)
	{
		if(!m_hide_mode) MessageBox(m_hwnd,
			 APL_T("В списке элементов  на установку доступа  есть временные \n элементы данных. Для этих элементов невозможно выполнить \n         сохранение в БД и установку прав доступа."),
			 APL_T("Служба RPC PDM Step Suite"),
			MB_OK);
		return false;
	}
	if(flag_save && !m_SetAccessWithSave)
	{
		int res;
		if(m_hide_mode)
		{
			res = IDYES;
		}
		else
		{
			res = MessageBox(m_hwnd,
				 APL_T("В списке элементов на установку доступа есть элементы данных,\n не сохраненных в БД. Необходимо или сохранить информацию,\n или прервать установку прав доступа. Сохранить информацию в БД?"),
				 APL_T("Служба RPC PDM Step Suite"),
				MB_YESNO |  MB_TASKMODAL | MB_ICONEXCLAMATION);
		}

		if(res == IDYES)
		{
			NET_SaveChanges();
		}
		else
		{
			return false;
		}
	}
	return true;
}



// ПРоверка на степень доступа текущего пользователя
// Если в экстенте не у всех есть aplOWN и параметр is_RO не нулевой, проверяется на RO
// Если у всех как минимум RO, то *is_RO=true иначе false и ругательное сообщение
bool CaplNetStepData::CheckOwner(aplExtent* extent, bool *is_RO)
{
	if(is_RO != 0)*is_RO = true;
	if(extent->GetSize() == 0) return true;

	// админ имеет доступ на все!
	if(IsAdmin()) return true;

	// Обновим состояние доступа
	if(!CheckInstState(extent)) return false;

	bool flag_access = false, flag_RO = true;

	// ПРоверки на степень доступа текущего пользователя
	for(int i=0; i<extent->GetSize(); i++)
	{
		if(extent->GetAt(i)->GetAccessmode() != aplOWN)
		{
			flag_access = true;
			break;// При таком раскладе дальнейшая проверка бессмыслена
		}
	}
	if(flag_access && is_RO != 0)
	{
		// надо проверить на aplRO,если нет полного доступа
		for(int i=0; i <extent->GetSize(); i++)
		{
			if(extent->GetAt(i)->GetAccessmode() >aplRO)
			{
				flag_RO = false;
				break;// При таком раскладе дальнейшая проверка бессмыслена
			}
		}
		*is_RO = flag_RO;
	}
	if(flag_access && (is_RO == 0 || !flag_RO))
	{
		if(!m_hide_mode)
		{
			MessageBox(m_hwnd,
				 APL_T("У Вас не хватает прав чтобы изменить степень\n доступа для указанных объектов\n"),
				 APL_T("Служба RPC PDM Step Suite"),
				MB_OK |  MB_TASKMODAL | MB_ICONEXCLAMATION);
		}
		return false;
	}
	return !flag_access;
}

//Запрашиваем список юзеров
bool CaplNetStepData::LoadAllUsers(aplExtent &users)
{
	LogDebugMessageIfDef(__APL_FUNC__);
	if(m_ent_apl_access_agent==0)
		return false;

	// запрашиваем сервер только если есть подключение
	if(m_CurDataFile == _T("") && CanProcessed(true, true, _T(""),_T(__FILE__),__LINE__,__APL_FUNC__) &&
					m_load_access_agent_from_serv && m_last_read_users_timer.CheckTimeOut(5000))
	{
		// Переменная для маленького местного массива запросиков
		CaplLoadData LoadExData(this, DEF_SOURCE);

		LoadExData.ClearQuery();

		int i0 = LoadExData.AddQuery( _T('e'), 0, GetEntId(m_ent_apl_access_agent), 0, true, true);
		if(m_ent_apl_access_pattern != 0)
			LoadExData.AddQuery( _T('e'), 0, GetEntId(m_ent_apl_access_pattern), 0, true, true);
		if(m_attr_apl_user__pers != 0)
			LoadExData.AddQuery(_T('d'), i0, 0, m_attr_apl_user__pers, true, true);

		if(!LoadExData.LoadExNoInstUserDataTimeInfo())
		{
			return false;
		}
	}
	//Выделим экстент из юзеров
	GetEntityExtent(m_ent_apl_access_agent, users);
	CSortClass::SortExtentById(users);

	LogDebugMessageIfDef( APL_T("LoadAllUsers выход"));

	return true;
}

/** Новая реализация на сервере! Копирует карту доступа инстансы str_pattern для всех инстансов входящих в 
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::NET_CopyAccessFromInst2Inst(aplExtent &patterns, aplExtent &destinations,
												  LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);

	AplNetErrors aplError = APL_NET_OK;
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		aplExtent patterns_internal;
		aplExtent destinations_internal;

		CaplDataBuf dbuf1, dbuf2;

		int i = 0;

		if(m_is_readonly)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ACCESS_DENY, APL_T("В режиме \"Только чтение\" назначение доступа запрещено!"));
			return false;
		}

		if(!m_hide_mode && m_IsWaitDlg)
		{
			m_AplTransport->SetWaitDlgText(wait_text);
		}

		if(patterns.GetSize() != destinations.GetSize())
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Размеры экстентов должны совпадать!"));
			return false;
		}

		// ПРоверки на сохранение инстансов экстента
		if(!CheckSaved(&destinations))
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Перед назначением доступа необходимо сохранить изменения"));
			return false;
		}
		// Если не заданы коды - надо проверить на степень доступа текущего пользователя 
		if(code1== 0 && code2== 0 && !IsAdmin() )
		{
			if(!CheckOwner(&destinations))
			{
				bool old_flag = m_NetErrorMessageMode; m_NetErrorMessageMode = false;
				// при user==-1 нас устроит только полный доступ
				aplExtent *p_destinations = &destinations;
				SetAPLErrorWithFileInfoExtent(APL_NET_CLI_ACCESS_DENY, p_destinations);
				m_NetErrorMessageMode = old_flag;
				return false;
			}
		}
		for(i=0; i < patterns.GetSize(); i++)
		{
			if(patterns[i] != destinations[i])
			{
				patterns_internal.Add(patterns[i]);
				destinations_internal.Add(destinations[i]);
			}
		}

		if(destinations_internal.GetSize() == 0)return true;

		PutExtToDataBuf(patterns_internal, dbuf1);
		PutExtToDataBuf(destinations_internal, dbuf2);

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCopyAccessFromExt2Ext(aplError, dbuf1, dbuf2, code1, code2));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("NET_CopyAccessFromInst2Inst выход"));
	return aplError == APL_NET_OK;

}


/** Новая реализация на сервере! Копирует карту доступа инстансы str_pattern для всех инстансов входящих в 
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::NET_CopyGroupeAccess(CaplInstance* pattern, aplExtent &destinations,
										   LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);

	AplNetErrors aplError = APL_NET_OK;
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		CaplDataBuf dbuf1;
		aplExtent ext_work;

		if(m_is_readonly)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ACCESS_DENY, APL_T("В режиме \"Только чтение\" назначение доступа запрещено!"));
			return false;
		}
		if(!m_hide_mode && m_IsWaitDlg)
		{
			m_AplTransport->SetWaitDlgText(wait_text);
		}

		if(pattern== 0) return false;
		if(pattern->GetId() == 0 || pattern->GetType() == 0)return false;
		if(destinations.GetSize() == 0)return true;

		// ПРоверки на сохранение инстансов экстента
		if(!CheckSaved(&destinations))
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Перед назначением доступа необходимо сохранить изменения"));
			return false;
		}
		// Если не заданы коды - надо проверить на степень доступа текущего пользователя 
		if(code1 == 0 && code2 == 0 && !IsAdmin() )
		{
			if(!CheckOwner(&destinations))
			{
				bool old_flag = m_NetErrorMessageMode; m_NetErrorMessageMode = false;
				// при user==-1 нас устроит только полный доступ
				aplExtent *p_destinations = &destinations;
				SetAPLErrorWithFileInfoExtent(APL_NET_CLI_ACCESS_DENY, p_destinations);
				m_NetErrorMessageMode = old_flag;
				return false;
			}
		}
		ext_work.Add(pattern);
		ext_work.Append(destinations);
		PutExtToDataBuf(ext_work, dbuf1);

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCCopyAccessFromInst2Ext(aplError, dbuf1, code1, code2));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("NET_CopyGroupeAccess выход"));
	return aplError == APL_NET_OK;

}

//////////




/** Формирует строку, описывающую карту доступа для указанного instance 
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::NET_GetExtentAccess2String(CaplInstance* inst, CString &destination, bool for_user,
												 LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);

	aplExtent extent;
	CStringArray destination_sa;
	if(inst== 0) return false;
	extent.Add(inst);
	if(NET_GetExtentAccess2String(extent, destination_sa, for_user, wait_text))
	{
		destination = destination_sa.GetAt(0);
	}
	else
	{
		return false;
	}
	return true;
}

/** Формирует массив строк, описывающих карту доступа для каждого instance из экстента 
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::NET_GetExtentAccess2String(aplExtent &extent, CStringArray &destination, bool for_user,
												 LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	// см описание у закоментаренного флага m_IsAccessBI
	return NET_GetExtentAccess2StringBI(extent, destination, for_user, wait_text);
}


/** Формирует массив строк, описывающих карту доступа для каждого instance из экстента 
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::NET_GetExtentAccess2StringBI(aplExtent &extent, CStringArray &destination, bool for_user,
												 LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		m_AplTransport->SetWaitDlgText(wait_text);

		CString buf_old;
		CString patt;

		int i = 0, j = 0,indx;

		// экстент из юзеров
		aplExtent ext_usr;

		CaplInstance *user;
		aplAccessModeType access;

		// новые труевые объекты доступа
		aplListAccess2Inst ListAccess;
		aplAccess2Inst* inst_access; 


		destination.RemoveAll();
		if(extent.GetSize() == 0)return true;

		// Проверки на сохранение инстансов экстента
		if(!CheckSaved(&extent))
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Перед назначением доступа необходимо сохранить изменения"));
			return false;
		}
		CaplMap in_ext_map;
		for(i=0; i<extent.GetSize(); i++)
		{
			in_ext_map.Add((long)extent.GetAt(i),i);
			destination.Add(_T(""));
		}
		in_ext_map.SortIn();

		//Запрашиваем список юзеров
		LoadAllUsers(ext_usr);

		// читаем с сервера информацию о доступе
		// заодно обновляем информацию о доступе текущего пользователя к обрабатываемым инстансам
		// для каждой из запрашиваемых инстансов
		for(j = 0; j<extent.GetSize(); j++)
		{
			// идентификаторы инстанс
			inst_access = new aplAccess2Inst();
			inst_access->inst = extent.GetAt(j);
			ListAccess.Add(inst_access);
		}

		if(!NET_GetGroupeAccessBI(ListAccess))
		{
			return false;
		}

		CString delimit(_T(""));
		if(for_user)delimit = _T(" ");

		for(i=0; i <ListAccess.Size; i++)
		{
			inst_access = ListAccess.GetAt(i);
			if(inst_access == 0) continue;
			indx = in_ext_map.QGetByIn((long)inst_access->inst);
			if(indx == -1){Beep(1000,100); continue;}
			buf_old = destination.GetAt(indx);
			// доступ по умолчанию
			buf_old += UserNameDecode(0, for_user) + _T(":") + delimit + GetNameAccess((aplAccessModeType)inst_access->access_def, for_user, true, false) + _T(";") + delimit;
			// шаблон
			if(for_user)
			{
				if(inst_access->pattern != 0)
				{
					buf_old += APL_T("<шаблон доступа>: '");
					GetAttr(inst_access->pattern, m_attr_apl_acc_pat__name, patt);
					buf_old += patt + _T("'; ");
				}
			}
			else
			{
				patt.Format(_T("#%i"), inst_access->pattern == 0 ? 0 : inst_access->pattern->GetId());
				buf_old += _T("pat:") + patt + _T(";");
			}

			for(j = 0; j<inst_access->GetSize(); j++)
			{
				inst_access->Get(j, &user, access);
				buf_old += UserNameDecode(user, for_user, false) + _T(":")  + delimit + GetNameAccess(access, for_user, true, false) + _T(";") + delimit;
			}
			// убираем запятые из конца строчек
			buf_old.TrimRight();
			buf_old.TrimRight(_T(';'));
			destination.SetAt(indx, buf_old);
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("NET_GetExtentAccess2String выход"));
	return true;
}

// работа с назначением доступа для LSS-объектов по GUID 
// проверяет наличие и при отсутствии - инициализируент переменные ентитей и атриюутов
bool CaplNetStepData::CheckGUIDAccessDict()
{
	if(m_ent_apl_GUID_access != 0) return true;

	m_ent_apl_GUID_access = GetEntityBN(_T("apl_guid_access"));

	if(m_ent_apl_GUID_access == 0) return false;

	m_attr_apl_GUID_access__GUID = GetAttrDefinition(m_ent_apl_GUID_access, _T("guid"));
	m_attr_apl_GUID_access__access_def = GetAttrDefinition(m_ent_apl_GUID_access, _T("access_def"));
	m_attr_apl_GUID_access__access_ind = GetAttrDefinition(m_ent_apl_GUID_access, _T("access_individual"));
	m_attr_apl_GUID_access__pattern = GetAttrDefinition(m_ent_apl_GUID_access, _T("pattern"));

	if(m_attr_apl_GUID_access__GUID == 0 || m_attr_apl_GUID_access__access_def == 0 || 
		m_attr_apl_GUID_access__access_ind == 0 || m_attr_apl_GUID_access__pattern == 0) return false;

	return true;
}

bool CaplNetStepData::LoadGUIDAccess(CStringArray &GUIDs, CaplStrMap &GUID2inst)
{
	try{

		if(!CheckGUIDAccessDict()) return false;

		CString stql, errmsg, tmp;
		aplExtent ext, ext_users;
		CaplInstance *access_inst, *inst_tmp;
		int i, indx;

		LoadAllUsers(ext_users);

		if(GUIDs.GetSize() == 0) return true;

		// читаем из БД инстансы с записями доступа для GUID-ов
		stql = _T("SELECT_LOAD_ALL Ext FROM Ext {apl_GUID_access (");
		for(i=0; i <GUIDs.GetSize(); i++)
		{
			stql += _T(".GUID = '") + GUIDs.GetAt(i) + _T("' ");
			if(i < GUIDs.GetSize()-1) stql += _T(" OR \n");
		}
		stql += _T(")} END_SELECT ");

		if(NET_QueryEditParse(stql, false, &errmsg))
		{
			NET_QueryExecute(ext);
		}
		else
		{
			if(!m_hide_mode) AfxMessageBox( APL_T("Ошибка при получении данных: ") + errmsg);
			return false;
		}
		errmsg = _T("");

		// Ищем в локальном кеше их же
		CaplAttrValue values[1];
		CaplEntity* e_apl_guid_access = GetEntityBN(_T("apl_guid_access"));
		CaplAttr* a_aga__guid = GetAttrDefinition(e_apl_guid_access, _T("guid"));
		aplExtent ext_1;

		if(e_apl_guid_access == 0 || a_aga__guid == 0) return false;

		values[0].attr = a_aga__guid;

		for(i=0; i < GUIDs.GetSize(); i++)
		{
			values[0].value.Set(GUIDs.GetAt(i));
			FindInstancesWithAttrValuesInLocalCache(e_apl_guid_access, 0, 1, &values[0], ext_1, false, false, false);
			ext.Append(ext_1);
		}

		// проверяем на униклаьность, выкидываем и удаляем дубли (удаляем те, у кого instance_id меньше), заполняем выходной массив
		for(i=0; i <ext.GetSize(); i++)
		{
			access_inst = ext[i];
			if(access_inst == 0)continue;
			if(access_inst->GetType() == 0)continue;

			GetAttr(access_inst, m_attr_apl_GUID_access__GUID, tmp);
			indx = GUID2inst.Find(tmp);

			if(indx!=-1)
			{
				inst_tmp = (CaplInstance*) GUID2inst.GetAt(indx)->val;
				if(inst_tmp == access_inst) continue;
				if(inst_tmp != 0)
				{
					if(inst_tmp->GetId() == 0 && access_inst->GetId() == 0)
					{
						DeleteInstance(access_inst);
						continue;
					}
					else if(inst_tmp->GetId() == 0 && access_inst->GetId() != 0)
					{
						DeleteInstance(inst_tmp);
					}
					else if(inst_tmp->GetId() != 0 && access_inst->GetId() == 0)
					{
						DeleteInstance(access_inst);
						continue;
					}
					else if(inst_tmp->GetId() < access_inst->GetId())
					{
						DeleteInstance(access_inst);
						continue;
					}
					else if(inst_tmp->GetId() > access_inst->GetId())
					{
						DeleteInstance(inst_tmp);
					}
				}
			}
			GUID2inst.Add(tmp, (long)access_inst);
		}
		
		// сортируем для удобства пользования
		GUID2inst.Sort();
	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}

// работа с назначением доступа для LSS-объектов по GUID 
// вызывает диалог назначения доступа. Результаты заносит в массив объектов apl_GUID_access и сохраняет в БД
bool CaplNetStepData::SetGUIDAccess(CStringArray &GUIDs, bool read_only)
{
	aplUsers cur_table, tmp_table;
	int nResponse;
	aplExtent ext;
	CString error;
	CString str_pattern, access_ind;
	CaplInstance *inst_assess, *pattern;
	int access_def;
	int i, j;
	aplAccessList *list_def = 0, *list;
	aplAccessEl *el;
	bool is_RO = read_only;

	CaplStrMap GUID2inst;

	try{

		// читаем из БД инстансы с записями доступа наших GUID-ов
		if(!LoadGUIDAccess(GUIDs, GUID2inst)) return false;

		if(!is_RO)
		{
			if(!CheckGUIDOwner(GUIDs, &is_RO))
			{
				if(m_hide_mode)
				{
					return false;
				}
				else
				{
					nResponse=AfxMessageBox( APL_T("У Вас не хватает прав, чтобы изменить \n права доступа указанных объектов.\n Показать текущие права доступа?"),MB_YESNO);
				}
				if(nResponse == IDNO)
				{
					// Не надо - так не надо
					return false;
				}
			}
			else
			{
				is_RO  = false;
			}
		}

		list_def = cur_table.Find(0);
		if(list_def == 0)
		{
			list_def = new aplAccessList();
			list_def->user = 0;
			cur_table.Add(list_def);
		}
		
		// переводим доступ из строк и атрибутов в мега-таблицу
		for(i=0; i <GUID2inst.GetSize(); i++)
		{
			inst_assess = (CaplInstance *) (GUID2inst.GetAt(i)->val);
			if(inst_assess== 0) continue;
			GetAttr(inst_assess ,m_attr_apl_GUID_access__access_ind, access_ind);
			GetAttr(inst_assess, m_attr_apl_GUID_access__access_def, access_def);
			GetAttr(inst_assess, m_attr_apl_GUID_access__pattern, pattern);
			
			el = new aplAccessEl();
			el->access = (aplAccessModeType)access_def;
			el->pattern = pattern;
			list_def->Add(el);

			if(access_ind != _T(""))
			{
				el->is_individual = TRUE;
				ParseAccessStringPattern(access_ind, tmp_table, &error, true);
				for(j = 0; j<tmp_table.GetSize(); j++)
				{
					if(tmp_table.GetAt(j)->user != 0 && tmp_table.GetAt(j)->GetSize()>0)
					{
						list = cur_table.Find(tmp_table.GetAt(j)->user);
						if(list == 0)
						{
							list = new aplAccessList();
							list->user = tmp_table.GetAt(j)->user;
							cur_table.Add(list);
						}

						el = new aplAccessEl();
						el->Set(tmp_table.GetAt(j)->GetAt(0));
						list->Add(el);
					}
				}
			}
		}

		if(list_def->GetSize() == 0)
		{
			el = new aplAccessEl();
			list_def->Add(el);
			el->access = aplOWN;
			el->is_individual = FALSE;

		}

		CaplSetResourceHandle res_handle(module_inst);
		CDlgSetInstAccess dlg;

		cur_table.m_auto_add = false;
		dlg.m_main_table = &cur_table;
		dlg.m_data = this;
		dlg.m_only_RO = is_RO;

		nResponse = dlg.DoModal();

		if(nResponse==IDOK)
		{
			list_def = cur_table.Find(0);

			if(list_def == 0 || list_def ->GetSize() == 0 ||  list_def ->GetAt(0) == 0)
			{
				if(!m_hide_mode)
				{
					AfxMessageBox( APL_T("Ошибка при определении доступа по умолчанию"),MB_ICONERROR);
				}
				return false;
			}

			access_def = list_def ->GetAt(0)->access;
			pattern = list_def ->GetAt(0)->pattern;

			// получаем строчку индивидуального доступа
			PrintAccessStringPattern(cur_table, str_pattern, false, 0, false);
			for(i=0; i <GUIDs.GetSize(); i++)
			{
				inst_assess = (CaplInstance*) GUID2inst.GetP(GUIDs.GetAt(i));
				if(inst_assess == 0)
				{
					// а для тех GUID, для кого записей до сих пор не было - создаем их
					inst_assess = CreateInstance(m_ent_apl_GUID_access);
					PutAttr(inst_assess, m_attr_apl_GUID_access__GUID, GUIDs.GetAt(i));
				}
				PutAttr(inst_assess, m_attr_apl_GUID_access__access_def, access_def);
				PutAttr(inst_assess, m_attr_apl_GUID_access__pattern, pattern);
				PutAttr(inst_assess, m_attr_apl_GUID_access__access_ind, str_pattern);
			}
			
			return true;
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	return false;
}

// работа с назначением доступа для LSS-объектов по GUID 
// вызывает диалог назначения доступа. Результаты заносит в массив объектов apl_GUID_access и сохраняет в БД
// пока функция писалась, необходимость в ней слегка отпала, поэтому функция закомментарена.
bool CaplNetStepData::SetGUIDAccess(CStringArray &GUIDs, aplAccessModeType access_def, CaplInstance* access_pattern, CaplMap &individual_access)
{

	return false;

// 	aplUsers cur_table, tmp_table;
// 	int nResponse;
// 	aplExtent ext;
// 	CString error;
// 	CString str_pattern, access_ind;
// 	CaplInstance *inst_assess, *pattern;
// 	int access_def;
// 	int i,j;
// 	aplAccessList *list_def = 0, *list;
// 	aplAccessEl *el;
// 	bool is_RO = false;
// 
// 	CaplStrMap GUID2inst;
// 	// читаем из БД инстансы с записями доступа наших GUID-ов
// 	if(!LoadGUIDAccess(GUIDs, GUID2inst)) return false;
// 
// 	if(!is_RO)
// 	{
// 		if(!CheckGUIDOwner(GUIDs, &is_RO))
// 		{
// 			if(m_hide_mode)
// 			{
// 				return false;
// 			}
// 			else
// 			{
// 				nResponse=AfxMessageBox( APL_T("У Вас не хватает прав, чтобы изменить \n права доступа указанных объектов.\n Показать текущие права доступа?"),MB_YESNO);
// 			}
// 			if(nResponse==IDNO)
// 			{
// 				// Не надо - так не надо
// 				return false;
// 			}
// 			SetGUIDAccess(GUIDs, true);
// 			return false;
// 		}
// 		else
// 		{
// 			is_RO  = false;
// 		}
// 	}
// 
// 	list_def = cur_table.Find(0);
// 	if(list_def == 0)
// 	{
// 		list_def = new aplAccessList();
// 		list_def->user = 0;
// 		cur_table.Add(list_def);
// 		el = new aplAccessEl();
// 		list_def->Add(el);
// 		el->access = aplOWN;
// 		el->is_individual = FALSE;
// 
// 	}
// 
// 	// переводим доступ из строк и атрибутов в мега-таблицу
// 	for(i=0; i <GUID2inst.GetSize(); i++)
// 	{
// 		inst_assess = (CaplInstance *)(GUID2inst.GetAt(i)->val);
// 		if(inst_assess== 0) continue;
// 		GetAttr(inst_assess, m_attr_apl_GUID_access__access_ind, access_ind);
// 		GetAttr(inst_assess, m_attr_apl_GUID_access__access_def, access_def);
// 		GetAttr(inst_assess, m_attr_apl_GUID_access__pattern, pattern);
// 
// 		el = new aplAccessEl();
// 		el->access = access_def;
// 		el->pattern = pattern;
// 		list_def->Add(el);
// 
// 		if(access_ind != _T(""))
// 		{
// 			el->is_individual = true;
// 			ParseAccessStringPattern(access_ind, tmp_table, &error);
// 			for(j = 0; j<tmp_table.GetSize(); j++)
// 			{
// 				if(tmp_table.GetAt(j)->user != 0 && tmp_table.GetAt(j)->GetSize()>0)
// 				{
// 					list = cur_table.Find(tmp_table.GetAt(j)->user);
// 					if(list == 0)
// 					{
// 						list = new aplAccessList();
// 						list->user = tmp_table.GetAt(j)->user;
// 						cur_table.Add(list);
// 					}
// 
// 					el = new aplAccessEl();
// 					el->Set(tmp_table.GetAt(j)->GetAt(0));
// 					list->Add(el);
// 				}
// 			}
// 		}
// 	}
// 
// 
// 	CaplSetResourceHandle res_handle(module_inst);
// 	CDlgSetInstAccess dlg;
// 
// 	cur_table.m_auto_add = false;
// 	dlg.m_main_table = &cur_table;
// 	dlg.m_data = this;
// 	dlg.m_only_RO = is_RO;
// 
// 	nResponse = dlg.DoModal();
// 
// 	if(nResponse == IDOK)
// 	{
// 		list_def = cur_table.Find(0);
// 
// 		if(list_def == 0 || list_def ->GetSize() == 0 ||  list_def ->GetAt(0) == 0)
// 		{
// 			if(!m_hide_mode)
// 			{
// 				AfxMessageBox( APL_T("Ошибка при определении доступа по умолчанию"),MB_ICONERROR);
// 			}
// 			return false;
// 		}
// 
// 		access_def = list_def ->GetAt(0)->access;
// 		pattern = list_def ->GetAt(0)->pattern;
// 
// 		// получаем строчку индивидуального доступа
// 		PrintAccessStringPattern(cur_table, str_pattern, false, true, false);
// 		for(i=0; i <GUIDs.GetSize(); i++)
// 		{
// 			inst_assess = (CaplInstance*) GUID2inst.GetP(GUIDs.GetAt(i));
// 			if(inst_assess == 0)
// 			{
// 				// а для тех GUID, для кого записей до сих пор не было - создаем их
// 				inst_assess=CreateInstance(m_ent_apl_GUID_access);
// 				PutAttr(inst_assess, m_attr_apl_GUID_access__GUID, GUIDs.GetAt(i));
// 			}
// 			PutAttr(inst_assess, m_attr_apl_GUID_access__access_def, access_def);
// 			PutAttr(inst_assess, m_attr_apl_GUID_access__pattern, pattern);
// 			PutAttr(inst_assess, m_attr_apl_GUID_access__access_ind, str_pattern);
// 		}
// 	}

	return true;
}

// ПРоверка на степень доступа текущего пользователя
// Если в экстенте не у всех есть aplOWN и параметр is_RO не нулевой, проверяется на RO
// Если у всех как минимум RO, то *is_RO=true иначе false и ругательное сообщение
bool CaplNetStepData::CheckGUIDOwner(CStringArray &GUIDs, bool *is_RO)
{
	CaplStrMap GUIDsMap;
	int i;

	if(is_RO != 0)*is_RO = true;
	if(GUIDs.GetSize() == 0)
		return true;
	if(IsAdmin())
	{
		// админ имеет доступ на все!
		return true;
	}

	for(i=0; i <GUIDs.GetSize(); i++)
	{
		GUIDsMap.Add(GUIDs.GetAt(i), (long)0);
	}

	// Обновим состояние доступа
	if(!GetGUIDAccess(GUIDsMap))
	{
		return false;
	}
	bool flag_access = false, flag_RO = true;
	// ПРоверки на степень доступа текущего пользователя
	for(i=0; i <GUIDsMap.GetSize(); i++)
	{
		aplAccessModeType a = (aplAccessModeType)GUIDsMap.GetAt(i)->val;
		if(a != aplOWN && a != aplNOT_DEFINED)
		{
			flag_access = true;
			break;// При таком раскладе дальнейшая проверка бессмыслена
		}
	}
	if(flag_access && is_RO != 0)
	{
		// надо проверить на aplRO,если нет полного доступа
		for(int i=0; i <GUIDsMap.GetSize(); i++)
		{
			if(GUIDsMap.GetAt(i)->val >aplRO)
			{
				flag_RO = false;
				break;// При таком раскладе дальнейшая проверка бессмыслена
			}
		}
		*is_RO = flag_RO;
	}
	if(flag_access && (is_RO == 0 || !flag_RO))
	{
		if(!m_hide_mode)
		{
			MessageBox(m_hwnd,
				APL_T("У Вас не хватает прав чтобы изменить степень\n доступа для выбранных объектов\n"),
				APL_T("Служба RPC PDM Step Suite"),
				MB_OK |  MB_TASKMODAL | MB_ICONEXCLAMATION);
		}
		return false;
	}
	return !flag_access;
}


// работа с назначением доступа для LSS-объектов по GUID 
// вызывает диалог назначения доступа. Результаты заносит в массив объектов apl_GUID_access и сохраняет в БД
bool CaplNetStepData::DelGUIDAccess(CStringArray &GUIDs)
{
	CaplStrMap GUID2inst;
	int i;

	if(!LoadGUIDAccess(GUIDs, GUID2inst)) return false;

	for(i=0; i <GUID2inst.GetSize(); i++)
	{
		DeleteInstance( (CaplInstance*)GUID2inst.GetAt(i)->val);
	}

	return true;
}
// для переданных в параметрах in GUID зачитывает из БД соответствующие apl_GUID_access; применяет 
// доступ к текущему юзеру и возвращает в параметре out собственно доступ текущего юзера
bool CaplNetStepData::GetGUIDAccess(CaplStrMap &GUIDs)
{
	CString stql, errmsg, access_string, tmp;
	aplExtent ext;
	aplExtent ext_users;
	CaplInstance *access_inst, *pattern;
	CaplStrMap GUID2inst;
	CStringArray GUIDs_Arr;
	int i, j, indx_u;
	aplUsers access_table;
	aplAccessList *list;
	
	try{
	
		for(i=0; i <GUIDs.GetSize(); i++) GUIDs_Arr.Add(GUIDs.GetAt(i)->str);
		
		if(!LoadGUIDAccess(GUIDs_Arr, GUID2inst)) return false;

		// читаем в

		ext_users.Add(GetCurrUser());
		GetAttr(GetCurrUser() ,m_attr_apl_user__grop, ext);
		ext_users.Append(ext);


		for(i=0; i <GUIDs.GetSize(); i++)
		{
			int access= aplNOT_DEFINED;
			access_inst = (CaplInstance *) GUID2inst.GetP(GUIDs.GetAt(i)->str);
			if(access_inst != 0)
			{
				// проверяем наличие индивидуального доступа
				GetAttr(access_inst, m_attr_apl_GUID_access__access_ind, tmp);
				if(tmp != _T(""))
				{
					// строчка есть. Разбираем
					if(!ParseAccessStringPattern(tmp, access_table, &errmsg, true))
					{
						if(!m_hide_mode) AfxMessageBox( APL_T("Ошибка при получении данных: ") + errmsg);
						return false;
					}
					// идем по разобранному шаблону
					for(j = 0; j<access_table.GetSize(); j++)
					{
						list = access_table.GetAt(j);
						if(list == 0) continue;
						for(indx_u = 0; indx_u<ext_users.GetSize(); indx_u++)
						{
							if(list->user == ext_users.GetAt(indx_u))
							{
								// в шаблоне есть запись для текущего юзера или его группы
								if(list->GetSize()>0)
								{
									if(access > list->GetAt(0)->access) access = list->GetAt(0)->access;
								}
							}
						}
					}
				}
				if(access == aplNOT_DEFINED)
				{
					// индивидуальный доступ не задан, или в шаблоне нет записей, подходящих для текущего юзера
					// проверяем наличие шаблона
					GetAttr(access_inst, m_attr_apl_GUID_access__pattern, pattern);
					if(pattern != 0)
					{
						GetAttr(pattern, m_attr_apl_acc_pat__pattern, tmp);
						if(tmp != _T(""))
						{
							// строчка есть. Разбираем
							if(!ParseAccessStringPattern(tmp, access_table, &errmsg, true))
							{
								if(!m_hide_mode) AfxMessageBox( APL_T("Ошибка при получении данных: ") + errmsg);
								return false;
							}
							// идем по разобранному шаблону
							for(j = 0; j<access_table.GetSize(); j++)
							{
								list = access_table.GetAt(j);
								if(list == 0) continue;
								for(indx_u = 0; indx_u<ext_users.GetSize(); indx_u++)
								{
									if(list->user == ext_users.GetAt(indx_u))
									{
										// в шаблоне есть запись для текущего юзера или его группы
										if(list->GetSize() > 0)
										{
											if(access > list->GetAt(0)->access) access = list->GetAt(0)->access;
										}
									}
								}
							}
						}				
					}
				}
				if(access == aplNOT_DEFINED)
				{
					// индивидуальный доступ и доступ по шаблону не задан, или нет записей, подходящих для текущего юзера
					// берем доступ по умолчанию
					GetAttr(access_inst, m_attr_apl_GUID_access__access_def, access);
				}
				if(access == aplNOT_DEFINED)
				{
					// доступ по умолчанию тоже не задан. Ну что ж, тогда доступ полный
					access = aplOWN;
				}
			}
			GUIDs.GetAt(i)->val = access;
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;

}

// копирует значения атрибутов от одного инстанса в другой
bool CaplNetStepData::CopyInstAttrs(CaplInstance *from, CaplInstance *to)
{
	if(0 == from) return false;
	if(0 == to) return false;
	if(to->GetType() != from->GetType()) return false;
	if(to->attrs == 0 || from->attrs == 0) return false;

	CaplEntity *entity = from->GetType();
	if(entity != 0)
	{
		for(int i=0; i <entity->all_attrs.GetSize(); ++i)
		{
			CaplValue value;

			GetAttr(from, entity->all_attrs[i], value);
			PutAttr(to, entity->all_attrs[i], value);
		}
	}

	return true;
}

// копирует доступ с инстанса на массив GUID-ов
bool CaplNetStepData::CopyAccessFromInst2GUIDs(CaplInstance* source, CStringArray &GUIDs)
{
	if(source== 0) return false;

	aplListAccess2Inst ListAccess;
	aplAccess2Inst* inst_access; 
	CaplStrMap GUID2inst;
	CString individual_access;
	aplUsers cur_table;
	int i;
	CaplInstance* inst_assess;

	try{

		inst_access = new aplAccess2Inst();
		inst_access->inst = source;
		ListAccess.Add(inst_access);

		// определяем доступ инстанса
		if(!NET_GetGroupeAccessBI(ListAccess))
		{
			return false;
		}

		// читаем из БД инстансы с записями доступа наших GUID-ов
		if(!LoadGUIDAccess(GUIDs, GUID2inst)) return false;
		
		// проверяем уровень доступа текущего юзера к GUIDам
		if(!CheckGUIDOwner(GUIDs))
		{
			if(!m_hide_mode)
			{
				AfxMessageBox( APL_T("У Вас не хватает прав, чтобы изменить \n права доступа указанных объектов."),MB_OK | MB_ICONERROR);
			}
			return false;
		}
		
		cur_table.Set(&ListAccess);

		if(!PrintAccessStringPattern(cur_table, individual_access, false, 0, false)) return false;

		for(i=0; i <GUIDs.GetSize(); i++)
		{
			inst_assess = (CaplInstance*) GUID2inst.GetP(GUIDs.GetAt(i));
			if(inst_assess == 0)
			{
				// а для тех GUID, для кого записей до сих пор не было - создаем их
				inst_assess = CreateInstance(m_ent_apl_GUID_access);
				PutAttr(inst_assess, m_attr_apl_GUID_access__GUID, GUIDs.GetAt(i));
			}
			PutAttr(inst_assess, m_attr_apl_GUID_access__access_def, inst_access->access_def);
			PutAttr(inst_assess, m_attr_apl_GUID_access__pattern, inst_access->pattern);
			PutAttr(inst_assess, m_attr_apl_GUID_access__access_ind, individual_access);
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}



// Вызывает диалог, в котором можно здать доступ к екстенту
bool CaplNetStepData::NET_SetInstancesAccess(aplExtent* extent, LPCTSTR wait_text)
{
	return NET_SetInstancesAccess(extent, NULL, (aplAccessModeType)-1, wait_text);
}


// Возможные параметры
// user==-1 -- Установка доступа по умолчанию и сброс индивидуального флага (типа "заморозить для всех")
// должен быть корректен параметр access
// user== 0 или access== -1 -- надо выводить диалог с уточнением
// поведение диалога:

// Если пользователь не трогает поле доступа по умолчанию (оставляет его пустым),
// но меняет индивижуальные настройки какого-нибудь пользователя - все Ок., в базе
// меняются только настройки для этого юзера и ставится галочка индивидуального доступа.
// Если меняются только доступ по умолчанию, а галочка индивидуального доступа 
// остается по умолчанию (нет индивидуального доступа), то меняется доступ 
// инстансы по умолчанию, а все индивидуальные настройки убиваются.
// если меняется и то, и то, то в базе меняется оба значения, и галочка индивидуального
// доступа ставится в положение "есть"
bool CaplNetStepData::NET_SetInstancesAccess(aplExtent* extent,
							CaplInstance* user, aplAccessModeType access_def, LPCTSTR wait_text, CaplInstance *access_pattern_inst, bool only_show)
{
	CaplStackLogger logger(__APL_FUNC__);

	// см описание у закоментаренного флага m_IsAccessBI
	return NET_SetInstancesAccessBI(extent, user, access_def, wait_text, access_pattern_inst, only_show);

}//bool CaplNetStepData::NET_SetInstancesAccess(aplExtent* extent,CaplInstance* user,aplAccessModeType access){

bool CaplNetStepData::NET_SetInstancesAccessBI(aplExtent* extent,
							CaplInstance* user, aplAccessModeType access_def, LPCTSTR wait_text, 
							CaplInstance *access_pattern_inst, bool only_show, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		/*  Это кусочек для отладки - искусственное создание многих инстансов на входе
		extent->Clear();
		GetEntityExtentBN("product",*extent);
		/**/

		// новые труевые объекты доступа
		aplListAccess2Inst ListAccess;
		aplAccess2Inst* inst_access; 


		int j = 0;
		bool only_RO = false;
		int nResponse = 0;

		// экстент из юзеров
		aplExtent ext_usr;

		bool is_RO = true;
		bool need_dlg = (user == NULL || access_def == aplNOT_DEFINED);
		bool changed = false;

		if(extent == 0)return false;
		if(extent->GetSize() == 0)return true;
		
		// ПРоверки на сохранение инстансов экстента
		if(!CheckSaved(extent))
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Перед назначением доступа необходимо сохранить изменения"));
			return false;
		}
		if(m_SetAccessShowInst)
		{
			CString cstr(_T("Доступ назначается на инстансы: ")), cstr1;
			for (j = 0; j < extent->Size; j++)
			{
				if (extent->GetAt(j) == 0)cstr1 = _T("#0, ");
				cstr1.Format(_T("#%i, "), extent->GetAt(j)->GetId());
				cstr += cstr1;
			}
			AfxMessageBox(cstr);
		}

		bool bNotCodes = true;
		if(code1 != 0 && code2 != 0)
		{
			bNotCodes = false;
		}
		
		// читаем с сервера информацию о доступе
		// заодно обновляем информацию о доступе текущего пользователя к обрабатываемым инстансам
		// для каждой из запрашиваемых инстансов
		for(j = 0; j<extent->Size; j++)
		{
			// идентификаторы инстанс
			inst_access = new aplAccess2Inst();
			inst_access->inst = extent->GetAt(j);
			ListAccess.Add(inst_access);
		}

		if(!NET_GetGroupeAccessBI(ListAccess))
		{
			return false;
		}

		// ПРоверки на степень доступа текущего пользователя
		if(m_is_readonly)
		{
			only_RO = true;
		}
		else if(m_check_cur_access_befor_set_access && bNotCodes)
		{
			bool prev_check = m_check_access_from_server;
			m_check_access_from_server = false;
			bool state = CheckOwner(extent, &is_RO);
			m_check_access_from_server = prev_check;
			if(!state)
			{
				if(!is_RO || (int)user == -1)
				{
					// при user==-1 нет смысла показывать текущее состояние доступа
					// при доступе aplNA на какую-либо инстанс - тоже
					bool old_flag = m_NetErrorMessageMode;
					m_NetErrorMessageMode = false;
					SetAPLErrorWithFileInfoExtent(APL_NET_CLI_ACCESS_DENY, extent);
					m_NetErrorMessageMode = old_flag;
					return false;
				}
				only_RO = true;
			}
		}
		if(only_RO)
		{
			if(m_hide_mode)
			{
				nResponse = IDNO;
			}
			else
			{
				nResponse=AfxMessageBox( APL_T("У Вас не хватает прав, чтобы изменить\n права доступа указанных объектов.\n Показать текущие права доступа?"),MB_YESNO);
			}
			if(nResponse == IDNO)
			{
				// Не надо - так не надо
				return false;
			}
			need_dlg = true;
		}
		if(only_show)
		{
			only_RO = true;
			need_dlg = true;
		}

		if((int)user == -1 && !need_dlg)
		{
			// Установка доступа по умолчанию и сброс индивидуального флага (типа "заморозить для всех")
			// Если не прошли проверку на RO - то
			if(access_def < 0 || access_def > aplNOT_DEFINED)
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Задано некорректное значение доступа"));
				return false;
			}
			ListAccess.Clear();

			// для каждой из запрашиваемых инстансов
			for(j = 0; j<extent->Size; j++)
			{
				// идентификаторы инстанс
				inst_access = new aplAccess2Inst();

				inst_access->inst = extent->GetAt(j);
				inst_access->access_def = access_def;
				inst_access->is_individual = false;
				inst_access->pattern = access_pattern_inst;

				ListAccess.Add(inst_access);
			}
		}
		else if(!need_dlg)
		{
			// это установка параметров только для одного юзера
			// т.е. старый доступ - не удаляем!
			ListAccess.Clear();
			ListAccess.clear_old_ind_access = false;

			//Запрашиваем список юзеров
			LoadAllUsers(ext_usr);

			//Определяем номер переданного юзера
			int num_user = -1;
			if(user != NULL)
			{
				num_user = ext_usr.Find(user);
				if(num_user == -1)
				{
					//Нам передали неправильного юзера
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Задан некорректный объект пользователя"));
					return false;
				}
			}

			// для каждой из запрашиваемых инстансов
			for(j = 0; j< extent->Size; j++)
			{
				// идентификаторы инстанс
				inst_access = new aplAccess2Inst();

				inst_access->inst = extent->GetAt(j);
				inst_access->access_def = aplNOT_DEFINED;
				inst_access->is_individual = true;
				inst_access->pattern = access_pattern_inst;
				inst_access->Add(user, access_def);

				ListAccess.Add(inst_access);
			}
		}
		else
		{
			//Неясны некоторые параметры. Ну или явно задано, что надоть выводить диалог

			//Запрашиваем список юзеров
			LoadAllUsers(ext_usr);

			// Поиск ресурса диалога в dll
			CaplSetResourceHandle res_handle(module_inst);
			CDlgSetInstAccess dlg;
			changed = false;

			dlg.m_ListAccess2Inst = &ListAccess;
			dlg.m_data = this;
			dlg.m_only_RO = only_RO;

			nResponse = dlg.DoModal();

			if(IDCANCEL == nResponse)
			{
				// Не надо - так не надо
				return false;
			}
			// ОпределяемЮ что же изменилось
			if(!ListAccess.changed && bNotCodes)
			{
				AfxMessageBox( APL_T("Параметры доступа не изменились."), MB_OK);
				return true;
			}
		}
		if(only_RO) return true;
		if(need_dlg)
		{
			if(!CheckOwner(extent))
			{
				AfxMessageBox( APL_T("Доступ к объектам на сервере был только что изменен из другого сеанса.\r\n Недостаточно прав для назначения доступа"),MB_ICONERROR | MB_OK);
				return false;
			}
		}
		return NET_SetGroupeAccessBI(ListAccess, wait_text, code1, code2);

	}
	CATH_MEMORY_ANY_EXEPTION;

	return false;

}//bool CaplNetStepData::NET_SetInstancesAccess(aplExtent* extent,CaplInstance* user,aplAccessModeType access)



bool CaplNetStepData::PrepareAccessStringPattern(CString &str_pattern, CString *definition)
{
	aplUsers cur_table;
	int nResponse;
	aplExtent ext;
	CString error;

	try{

		if(!TemporaryCurrUsersCreate()) return false;

		// создаем временные инстансы для группы Текущего пользователя и для текущего пользователя
		LoadAllUsers(ext);

		cur_table.m_auto_add = true;
		// Готовим таблицу-пытаемся разобрать строчку
		if(!ParseAccessStringPattern(str_pattern, cur_table, &error))
		{
			AfxMessageBox(error + APL_T("\r\n\r\nДля исправления ошибок измените шаблон и сохраните его"), MB_ICONERROR);
		}

		CaplSetResourceHandle res_handle(module_inst);
		CDlgSetInstAccess dlg;

		dlg.m_main_table = &cur_table;
		dlg.m_data = this;
		dlg.m_only_RO = false;
		dlg.m_edit_access_pattern = true;

		nResponse = dlg.DoModal();

		if(nResponse == IDOK)
		{
			// СохранЯем в буфер
			PrintAccessStringPattern(cur_table, str_pattern);
			if(definition != 0)
			{
				PrintAccessStringPattern(cur_table, *definition, true);
			}
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	TemporaryCurrUsersDelete();

	if(IDCANCEL == nResponse)
	{
		// Не надо - так не надо
		return false;
	}

	return true;
}

bool CaplNetStepData::SelectAplUsers(aplExtent &users, aplAccessModeType &access)
{
	//Запрашиваем список юзеров
	aplExtent ext_usr;
	LoadAllUsers(ext_usr);

	CSelectAccessAgent dlg;
	CaplInstance *inst;
	int i;
	users.Clear();
	dlg.Attach(this);
	if(dlg.DoModal() != IDOK)
	{
		return false;
	}
	access = dlg.m_res_access;

	for(i=0; i <dlg.m_ext_out.GetSize(); i++)
	{
		inst = dlg.m_ext_out.GetAt(i);
		users.Add(inst);
	}
	return true;
}


bool CaplNetStepData::NET_SetAccessFromPatternInst(aplExtent* extent, CaplInstance *access_pattern_inst,
												   LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);

	CString pattern_str;
	if(access_pattern_inst == 0) return false;
	if(access_pattern_inst->GetType() == 0) return false;

	CaplLoadData ld(this);
	ld.AddQuery(0, access_pattern_inst, true);
	ld.LoadEx();
	
	GetAttr(access_pattern_inst, m_attr_apl_acc_pat__pattern, pattern_str);

	return NET_SetAccessFromPattern(extent, pattern_str, wait_text, code1, code2);

}
bool CaplNetStepData::NET_SetAccessFromPatternName(aplExtent* extent, CString &pattern_name,
												   LPCTSTR wait_text, int code1, int code2)
{
	CString pattern_str;
	CaplInstance *pattern_inst;
	CaplAttrValue tst_values[1];     
	aplExtent ext_pat;

	tst_values[0].attr=m_attr_apl_acc_pat__name;  
	tst_values[0].value.Set((LPCTSTR)pattern_name); 

	FindInstancesWithAttrValuesInLocalCache(m_ent_apl_access_pattern, 0, 1, tst_values, ext_pat, false, false, false, aplSBMiddle, false);

	if(ext_pat.GetSize() == 0) return false;
	if(ext_pat.GetSize() > 1) return false;
	
	pattern_inst = ext_pat[0];
	
	if(pattern_inst == 0) return false;
	if(pattern_inst->GetType() == 0) return false;

	GetAttr(pattern_inst, m_attr_apl_acc_pat__pattern, pattern_str);

	return NET_SetAccessFromPattern(extent, pattern_str ,wait_text, code1, code2);
}

bool CaplNetStepData::NET_SetAccessFromPattern(aplExtent* extent, CString &str_pattern,
											   LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);

	// см описание у закоментаренного флага m_IsAccessBI
	return NET_SetAccessFromPatternBI(extent, str_pattern, wait_text, code1, code2);

}

bool CaplNetStepData::NET_SetAccessFromPatternBI(aplExtent* extent, CString &str_pattern, LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	// новые труевые объекты доступа
	aplListAccess2Inst ListAccess;
	aplAccess2Inst* inst_access; 

	aplUsers pattern_table;
	aplAccessEl *el;

	aplAccessList *list_def = 0, *list_curr_u = 0, *list_curr_g = 0;
	aplAccessModeType access_curr_u = aplNOT_DEFINED, access_curr_g = aplNOT_DEFINED, access_def = aplNOT_DEFINED;
	CaplMap list_groups_cu;

	CaplInstance* pattern = (CaplInstance*)-1;
	BOOL is_individual = false;
	aplAccessModeType access;

	aplAccessList *list = 0;
	CaplInstance* user;

	aplExtent ext_u;

	int i, j, indx_extent;

	try{

		TEST_RECONNECT(false);

		if(extent == 0 ) return false;
		if(extent->GetSize() == 0) return true;

		if(m_is_readonly)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ACCESS_DENY, APL_T("В режиме \"Только чтение\" назначение доступа запрещено!"));
			return false;
		}

		if(str_pattern == _T(""))
		{
			return false;
		}
		// ПРоверки на сохранение инстансов экстента
		if(!CheckSaved(extent))
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("Перед назначением доступа необходимо сохранить изменения"));
			return false;
		}
		// ПРоверки на степень доступа текущего пользователя
		bool bCheck = true;
		if(code1 != 0 && code2 != 0)
		{
			bCheck = false;
		}

		if(bCheck && !CheckOwner(extent))
		{
			bool old_flag = m_NetErrorMessageMode;
			m_NetErrorMessageMode = false;
			SetAPLErrorWithFileInfo(APL_NET_SRV_ACCESS_DENY);
			m_NetErrorMessageMode = old_flag;
			return false;
		}
		// создаем временные инстансы для группы Текущего пользователя и для текущего пользователя
		if(!TemporaryCurrUsersCreate()) return false;

		// загружаем с сервера всех пользователей
		LoadAllUsers(ext_u);

		// Готовим таблицу-пытаемся разобрать строчку
		if(!ParseAccessStringPattern(str_pattern, pattern_table))
		{
			TemporaryCurrUsersDelete();
			return false;
		}

		// Старый доступ идет нафиг - для гарантии ставим флаг
		ListAccess.clear_old_ind_access = true;
		// для всех иснтансов доступ будет общим
		ListAccess.identical_access4all = true;

		// находим записи для доступа по умолчанию, текщего пользователя и текущей группы
		i=0;
		while(i < pattern_table.GetSize() && (list_def == 0 || list_curr_u == 0 || list_curr_g == 0))
		{
			list = pattern_table.GetAt(i);
			if(list->user == 0)
			{
				list_def = list;
				pattern_table.RemoveAt(i);
			}
			else if(list->user == m_def_curr_user)
			{
				list_curr_u = list;
				pattern_table.RemoveAt(i);
			}
			else if(list->user == m_def_curr_user_group)
			{
				list_curr_g = list;
				pattern_table.RemoveAt(i);
			}
			else
			{
				i++;
			}
		}

		// обрабатываем записи для текущего пользователя
		if(list_curr_u != 0)
		{
			if(m_CurrUser->GetId() == DEFAILT_ID_ADMIN_USER)
			{
				// админу доступ назначать бессмысленно
				delete list_curr_u;
				list_curr_u = 0;
			}
			else
			{
				is_individual = true;

				el = list_curr_u->GetAt(0);
				if(el != 0)
				{
					access_curr_u = el->access;
				}
				else
				{
					access_curr_u = aplNOT_DEFINED;
				}
			}
		}
		// если задан доступ для текущей группы - формируем и сортируем экстент групп текущего пользователя
		if(list_curr_g != 0)
		{
			aplExtent ext;
			list_groups_cu.Clear();
			
			GetAttr(m_CurrUser, m_attr_apl_user__grop, ext);
			
			for(i=0; i < ext.GetSize(); i++)
			{
				user = ext.GetAt(i);
				if(user->GetId() == DEFAILT_ID_ADMIN_GROUP )
				{
					continue;
				}
				list_groups_cu.Add((long)user, (long)aplNOT_DEFINED);
				is_individual = true;
			}
			if(list_groups_cu.Size > 0)
			{
				list_groups_cu.SortIn();
				is_individual = true;
				el=list_curr_g->GetAt(0);
				if(el != 0)
				{
					access_curr_g = el->access;
				}
				else
				{
					access_curr_g = aplNOT_DEFINED;
				}
			}
			else
			{
				// текущий пользователь только в группе админов
				delete list_curr_g; 
				list_curr_g = 0;
			}
		}

		// доступ одинаковый
		ListAccess.identical_access4all = true;

		// заполняем таблицу доступа по инстансам 
		for(indx_extent = 0; indx_extent < extent->Size; indx_extent++)
		{
			inst_access = new aplAccess2Inst();
			if(inst_access == 0){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Пустой указатель в списке доступа!"));}

			inst_access->inst = extent->GetAt(indx_extent);
			ListAccess.Add(inst_access);

			// поскольку доступ одинаковый, индивидуальный доступ пишется только в нулевой эелемент массива
			if(indx_extent == 0) 
			{
				for(i=0; i < pattern_table.GetSize(); i++)
				{
					list = pattern_table.GetAt(i);
					if(list == 0) continue;
					el = list->GetAt(0);
					if(el== 0) continue;
					access = el->access;

					if(list_curr_u != 0)
					{
						// а не задан ли явно доступ для текущий пользователя?
						if(list->user == m_CurrUser)
						{
							// назначаем наивысший возможный доступ
							if(access_curr_u < access) access = access_curr_u;
							// список не нужен, удаляем (в таблице его нет, автоматически не удалится!)
							delete list_curr_u;
							list_curr_u = 0;
						}
					}
					if(list_curr_g != 0)
					{
						// а не задан ли явно доступ для групп текущего пользователя?
						j = list_groups_cu.FindByIn((long)list->user);
						if(j > -1)
						{
							// назначаем наивысший возможный доступ
							if(access_curr_g < access) access = access_curr_g;
							if(list_groups_cu.Data[i].out != (long)aplNOT_DEFINED)
							{
								aplAccessModeType a_o = (aplAccessModeType)list_groups_cu.Data[i].out;
								if(a_o < access) access = a_o;
							}
							// доступ к этой группе уже задан, дублировать в таблице не надо
							list_groups_cu.Data[i].out = (long)access;
						}
					}
					if(access == aplNOT_DEFINED) continue;
					// это явно некий пользователь или группа - нужна галочка доступа по умолчанию
					is_individual = TRUE;

					inst_access->Add(list->user, access);
				}
				// те группы текущего юзера, которые не были заданы явно - надо задать
				for(i=0; i <list_groups_cu.Size; i++)
				{
					if(list_groups_cu[i].out != (long)aplNOT_DEFINED) continue;

					inst_access->Add((CaplInstance*)list_groups_cu[i].in, access_curr_g);
				}
				if(list_curr_g != 0)
				{
					// список не нужен, удаляем (в таблице его нет, автоматически не удалится!)
					delete list_curr_g;
					list_curr_g = 0;
				}
				if(list_curr_u != 0)
				{
					inst_access->Add(GetCurrUser(), access_curr_u);
					delete list_curr_u; 
					list_curr_u = 0;
				}
			}

			// доступ по умолчанию добавляем в самом конце - чтобы учесть флаг индивидуального доступа
			if(list_def != 0)
			{
				el = list_def->GetAt(0);
				if(el != 0)
				{
					access_def = el->access;
					pattern = el->pattern;
				}
				delete list_def;
				list_def = 0;
			}

			inst_access->access_def = access_def;
			inst_access->is_individual = is_individual;;
			inst_access->pattern = pattern;
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	TemporaryCurrUsersDelete();

	try{
		return NET_SetGroupeAccessBI(ListAccess, wait_text, code1, code2);
	}
	CATH_MEMORY_ANY_EXEPTION;

	return false;		
}


bool CaplNetStepData::NET_SetSpecialInstancesAccess(aplExtent* extent, CaplInstance* user,
													aplAccessModeType access_def, LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);

	// см описание у закоментаренного флага m_IsAccessBI
	return NET_SetInstancesAccessBI(extent, user, access_def, wait_text, (CaplInstance *)-1, false, code1, code2);

}

// Для каждой инстансы из extent устанавливает доступ: 
// текущему юзеру - полный
// доступ по умолчанию - ReadOnly
// индивидуальный доступ других юзеров не меняется
bool CaplNetStepData::NET_BecomeInstancesOwner(aplExtent* extent, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	/*  Это кусочек для отладки - искусственное создание многих инстансов на входе
	extent->Clear();
	GetEntityExtentBN("product",*extent);
	/**/
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	aplAccessEl *el = 0;
	aplAccessList *list = 0;
	aplUsers new_table;

	int j = 0;

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		if(extent->GetSize() == 0)
			return true;

		// ПРоверки на сохранение инстансов экстента
		if(!CheckSaved(extent))
		{	
			NET_SaveChanges(); //Вставил ayatsk
			//return false;
		}
		// ПРоверки на степень доступа текущего пользователя
		if(!CheckOwner(extent))
		{
			return false;
		}

		list = new aplAccessList();
		// Генерим запрос на изменение доступа по умолчанию
		list->user = 0;// символ дефайновости

		// и для каждой из запрашиваемых инстансов
		// доступ только чтение (для других юзерей)
		for(j = 0; j<extent->Size; j++)
		{
			// идентификаторы инстанс
			el = new aplAccessEl();

			el->inst = extent->GetAt(j);
			el->access = aplRO;
			el->is_individual = true;

			list->Add(el);
		}
		new_table.Add(list);

		// Доcтуп для текущего
		list = new aplAccessList();
		list->user = m_CurrUser;

		// и для каждой из запрашиваемых инстансов
		// доступ собственника
		for(j = 0; j<extent->Size; j++)
		{
			// идентификаторы инстанс
			el = new aplAccessEl();

			el->inst = extent->GetAt(j);
			el->access = aplOWN;
			el->is_individual = true;

			list->Add(el);
		}
		new_table.Add(list);

		return NET_SetGroupeAccess(new_table);
	}
	CATH_MEMORY_ANY_EXEPTION;

	return false;

}//bool CaplNetStepData::NET_SetInstancesOwner(aplExtent* extent)


bool CaplNetStepData::AppendInBufUserArray(CaplDataBuf &buf_in, aplAccessList *list, int start_in_portion, int size_in_user, aplExtent* ext, bool setaccess)
{
	if(list== 0){MY_THROW(APL_NET_CLI_MISSED_DATA);}
	aplAccessEl	*el = 0;
	int user_id;
	UINT32 inst_id = 0;
	BYTE access;
	UINT32 ent_id = 0;
	int pattern_id = 0;
	// галочка индивидуального доступа
	BOOL is_individual = false;
	int j;

	// идентификатор очередного юзера
	if(list->user == 0){user_id = 0;}else{user_id = list->user->GetId();}
	buf_in.Add(&user_id, sizeof(user_id));
	// количество инстансов для него
	buf_in.Add(&size_in_user, sizeof(size_in_user));

	int end_in_portion = start_in_portion + size_in_user;

	for(j = start_in_portion; j<end_in_portion; j++)
	{
		el = list->GetAt(j);
		// Ну и для каждой инстансы ее ид и предполагаемый доступ
		if(el == NULL) // ошибка!
			MY_THROW(APL_NET_CLI_MISSED_DATA);

		if(el->inst == NULL) // ошибка!
			MY_THROW(APL_NET_CLI_MISSED_DATA);

		if(ext != 0){ext->Add(el->inst);}
		inst_id = el->inst->GetId();
		buf_in.Add(&inst_id, sizeof(inst_id));

		// тип инстансы
		ent_id = el->inst->GetType()->id;
		buf_in.Add(&ent_id, sizeof(ent_id));

		// Значение доступа инстансы
		access = el->access;
		buf_in.Add(&access, sizeof(access));

		// галочка индивидуального доступа
		if(setaccess && user_id == 0)
		{
			// она пишется только при установке доступа по умолчанию
			is_individual = el->is_individual;
			buf_in.Add(&is_individual, sizeof(is_individual));

			// шаблон пишется тоже только при установке доступа по умолчанию
			pattern_id = (el->pattern==(CaplInstance*)-1) ? -1 : ( (el->pattern==(CaplInstance*)0) ? 0 : el->pattern->GetId() );
			buf_in.Add(&pattern_id, sizeof(pattern_id));
		}
	}
	return true;
}

bool CaplNetStepData::ReadFromBuferUserArray(CaplDataBuf &buf_out, aplAccessList *list, int start_in_portion, int size_in_user)
{
	if(list== 0){MY_THROW(APL_NET_CLI_MISSED_DATA);}
	int user_id;
	aplAccessEl	*el = 0;
	UINT32 inst_id = 0;
	BYTE access;
	UINT32 ent_id = 0;
	// галочка индивидуального доступа
	BOOL is_individual = false;
	int size_in_user_in;
	int j;
	int pattern_id;

	// Идентификатор юзеря
	buf_out.Read(&user_id, sizeof(user_id));
	// сравниваем со входным (если надо)
	if(list->user!=NULL && user_id != list->user->GetId())
	{
		MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
	}

	// Количество инстансов
	buf_out.Read(&size_in_user_in, sizeof(size_in_user_in));
	if(size_in_user_in != size_in_user)
	{
		MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
	}
	int end_in_portion = start_in_portion + size_in_user;

	for(j=start_in_portion; j<end_in_portion; j++)
	{
		el = list->GetAt(j);
		// Ну и для каждой инстансы ее ид
		buf_out.Read(&inst_id, sizeof(inst_id));
		if(el == 0)
		{
			MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
		}
		if(el->inst == 0)
		{
			MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
		}
		if(el->inst->GetId() != inst_id)
		{
			MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
		}
		// тип
		buf_out.Read(&ent_id, sizeof(ent_id));
		if(el->inst->GetType() == 0)
		{
			MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
		}
		if(el->inst->GetType()->id != ent_id)
		{
			MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
		}
		// Значение доступа инстансы
		buf_out.Read(&access, sizeof(access));
		el->access = (aplAccessModeType)access;

		// галочка доступа
		buf_out.Read(&is_individual, sizeof(is_individual));
		el->is_individual = is_individual;

		// шаблон
		buf_out.Read(&pattern_id, sizeof(pattern_id));
		el->pattern = (pattern_id == 0 ? 0 : GetInstById(pattern_id, false));

	}
	return true;
}


bool CaplNetStepData::NET_SetGroupeAccess(aplUsers &new_table, LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try
	{
		TEST_RECONNECT(false);

		if(m_is_readonly)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ACCESS_DENY, APL_T("В режиме \"Только чтение\" назначение доступа запрещено!"));
			return false;
		}

		if(m_SetAccessWithSave)
		{
			aplUsers* table = new aplUsers();
			table->Set(new_table);
			if(code1 != 0 || code2 != 0)
			{
				table->code1 = code1;
				table->code2 = code2;
			}
			m_ListDelaedAccess.Add(table);
			return true;
		}
		else
		{
			return NET_SetGroupeAccessInternal(new_table, wait_text, code1, code2);
		}
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		m_AplTransport->ClearInternalData();
		return false;	
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}//bool CaplNetStepData::NET_SetGroupeAccess(aplUsers &main_table)


bool CaplNetStepData::NET_SetGroupeAccessInternal(aplUsers &new_table, LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	// Буфер передачи данных
	CaplDataBuf buf;
	int num_user = 0, num_inst = 0;
	int i = 0, j = 0;
	aplAccessList *list = 0;
	aplExtent extent; extent.Unique = false;
	aplExtent *ext = 0;

	bool flag_curr_user = false;

	int size_in_user = 0;
	int start_in_portion = 0;
	bool all_users = true;
	aplAccessEl* el;

	try
	{
		TEST_RECONNECT(false);

		m_AplTransport->SetWaitDlgText(wait_text);

		if(m_is_readonly)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ACCESS_DENY, APL_T("В режиме \"Только чтение\" назначение доступа запрещено!"));
			return false;
		}
		if(new_table.GetSize() == 0)
		{
			// ничего не изменилось
			return true;
		}
		if(code1 == 0 && code2 == 0)
		{
			code1 = new_table.code1;
			code2 = new_table.code2;
		}

		for(i=0; i <new_table.GetSize(); i++)
		{
			list = new_table.GetAt(i);
			if(list == 0){MY_THROW(APL_NET_CLI_MISSED_DATA);}
			if(list->user != 0)
			{
				if(list->user->GetType() == 0)
				{
					// юзер был удален, пока клиент назначал доступ и чесался. Эту запись игнорируем
					new_table.RemoveAtWithDel(i);
					i--;
					continue;
				}
			}
			if(list->user == m_CurrUser || list->user == 0)
			{
				// да это же текущий юзер! Или доступ по умолчанию, тоже может повлиять на текущего
				flag_curr_user = true;
			}
			// проверка на инстансы который могли быть удалены
			for(j = 0; j<list->GetSize(); j++)
			{
				el = list->GetAt(j);
				if(el == 0)
				{
					list->RemoveAt(j);
					j--;
					continue;
				}
				if(el->inst == 0)
				{
					list->RemoveAt(j);
					j--;
					continue;
				}
				if(el->inst->GetType() == 0)
				{
					list->RemoveAt(j);
					j--;
				}
			}

			size_in_user += list->GetSize();
		}
		// Количество (записей) юзеров
		num_user = new_table.GetSize();

		if(m_SizePortionSavedInst > 0)
		{
			// решаем, как нам послать на сервер
			// одной порцией или по юзерам да по маленьким порциям
			if(size_in_user > m_SizePortionSavedInst)
			{
				all_users = false;
				num_user = 1;
			}
		}
		if(flag_curr_user)
		{
			ext = &extent;
		}
		if(all_users)
		{
			// Заполняем буфер
			// заголовки буферов - однобайтовые! 
			const char* header = ACCESS_BUF_SET_header;
			buf.Clear();
			buf.Add(header, 22);

			// расширенный заголовок
			WriteExtBufData(buf);

			// и подсчет количества записей для контроля
			buf.Add(&size_in_user, sizeof(size_in_user));

			buf.Add(&num_user, sizeof(num_user));
			for(i=0; i <num_user; i++)
			{
				list = new_table.GetAt(i);
				AppendInBufUserArray(buf, list, 0, list->GetSize(), ext, true);
			}

			m_AplTransport->SetWaitDlgText(wait_text);
			AplNetErrors aplError = APL_NET_OK;

			RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCSetUsersAccess(aplError, buf));
		}
		else
		{
			num_user = 1;// отсылаются по одному юзеру
			for(i=0; i <new_table.GetSize(); i++)
			{
				list = new_table.GetAt(i);
				start_in_portion = 0;
				do
				{
					// Заполняем буфер
					// заголовки буферов - однобайтовые! 
					const char* header = ACCESS_BUF_SET_header;
					buf.Clear();
					buf.Add(header, 22);

					// расширенный заголовок
					WriteExtBufData(buf);

					// количество инстансов у юзеря в текущей посылке
					size_in_user = list->GetSize() - start_in_portion;
					if(size_in_user == 0)
					{
						break;
					}
					if(m_SizePortionSavedInst != 0 && (start_in_portion + m_SizePortionSavedInst) < size_in_user)
					{
						size_in_user = m_SizePortionSavedInst;
					}
					// в буфер общее количество инстансов
					buf.Add(&size_in_user, sizeof(num_inst));
					buf.Add(&num_user, sizeof(num_user));
					AppendInBufUserArray(buf, list, start_in_portion, size_in_user, ext, true);

					
					m_AplTransport->SetWaitDlgText(wait_text);
					AplNetErrors aplError = APL_NET_OK;

					RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCSetUsersAccess(aplError, buf));
					start_in_portion += size_in_user;

				}while(size_in_user != 0);
			}	
		}

		if(flag_curr_user && m_check_cur_access_after_set_access)
		{
			// надо проверить доступ текущего юзера, коли его было задействовано
			// для всех инстансов экстента
			// тока вначале помсотрим, не админы ли мы
			if(IsAdmin()) return true;

			m_AplTransport->ClearInternalData();

			return CheckInstState(&extent);
		}	

	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		return false;	
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

bool CaplNetStepData::CheckInstState(aplExtent *extent)
{
	if(extent->GetSize() == 0)
		return true;

	if(!m_check_access_from_server)
	{
		return true;
	}

	int i = 0;
	// Переменная для маленького местного массива запросиков
	CaplLoadData LoadExData(this, DEF_SOURCE);
	LoadExData.ClearQuery();

	// проверка состояния должна всегда лезть на сервер. Даже если она была вызвана прошлый раз секунду назад
	ClearLastQuery();

	// убираем дубли из экстента
	CSortClass::SortExtentByInst(*extent);

	for(i=1; i <extent->GetSize(); i++)
	{
		if(extent->GetAt(i-1) == extent->GetAt(i))
		{
			extent->Remove(i);
			i--;
		}
	}

	for(i=0; i <extent->GetSize(); i++)
	{
		if(extent->GetAt(i)->GetTemporary()) continue;
		if(LoadExData.AddQuery( _T('a'), (long)(extent->GetAt(i)->GetId()), (long)0, (long)0, true) == -1)
			return false;
	}
	return LoadExData.LoadEx();
}//bool CaplNetStepData::CheckInstStata(aplExtent ext)



bool CaplNetStepData::NET_GetGroupeAccess(aplUsers &main_table, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	int i = 0;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	// Буфер передачи данных
	CaplDataBuf dbuf_in, dbuf_out;
	aplAccessList *list = 0;

	int num_user = 0;
	int size_in_user = 0;

	// заголовки буферов - однобайтовые! 
	const char* header = ACCESS_BUF_GET_header;

	int curr_size_portion = 0;
	int start_in_portion = 0;
	bool all_users = true;

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		// Количество юзеров
		num_user = main_table.GetSize();
		if(num_user == 0) return true;

		if(m_SizePortionSavedInst>0)
		{
			// решаем, как нам послать на сервер
			// одной порцией или по юзерам да по маленьким порциям
			for(i=0; i <num_user; i++)
			{
				list = main_table.GetAt(i);
				if(list == 0){MY_THROW(APL_NET_CLI_MISSED_DATA);}
				curr_size_portion += list->GetSize();
			}
			if(curr_size_portion > m_SizePortionSavedInst)
			{
				all_users = false;
				num_user=1;
			}
		}

		if(all_users)
		{
			dbuf_in.Clear();
			dbuf_in.Add(header, 22);

			dbuf_in.Add(&num_user, sizeof(num_user));

			for(i=0; i < num_user; i++)
			{
				list = main_table.GetAt(i);
				AppendInBufUserArray(dbuf_in, list, 0, list->GetSize());
			}
			
			m_AplTransport->SetWaitDlgText(wait_text);
			AplNetErrors aplError = APL_NET_OK;

			RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCGetUsersAccess(aplError, dbuf_in, dbuf_out));

			// Расшифровка
			// заголовки буферов - однобайтовые! 
			char header_new[22];
			dbuf_out.Read(header_new, 22);
			if(CStringA(ACCESS_BUF_GET_header) != header_new)
			{
				// не тот формат у буфера!
				MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
			}

			// Количество юзеров
			dbuf_out.Read(&num_user, sizeof(num_user));
			if(num_user != main_table.GetSize())
			{
				MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("Не совпадает количество отправленных и принятых пользователей"));
			}
			for(i=0; i <num_user; i++)
			{
				list = main_table.GetAt(i);
				ReadFromBuferUserArray(dbuf_out, list, 0, list->GetSize());
			}
		}
		else
		{
			num_user = 1;// отсылаются по одному юзеру
			for(i=0; i <main_table.GetSize(); i++)
			{
				list = main_table.GetAt(i);
				start_in_portion = 0;
				do
				{
					// количество инстансов у юзеря в текущей посылке
					size_in_user = list->GetSize() - start_in_portion;
					if(size_in_user == 0)
					{
						break;
					}

					dbuf_in.Clear();
					// заголовки буферов - однобайтовые! 
					dbuf_in.Add(header, 22);
					dbuf_in.Add(&num_user, sizeof(num_user));

					if(m_SizePortionSavedInst != 0 && (start_in_portion + m_SizePortionSavedInst) < size_in_user)
					{
						size_in_user = m_SizePortionSavedInst;
					}

					AppendInBufUserArray(dbuf_in, list, start_in_portion, size_in_user);
					
					m_AplTransport->SetWaitDlgText(wait_text);
					AplNetErrors aplError = APL_NET_OK;

					RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCGetUsersAccess(aplError, dbuf_in, dbuf_out));


					// Расшифровка
					// заголовки буферов - однобайтовые! 
					char header_new[22];
					dbuf_out.Read(header_new, 22);

					if(CStringA(ACCESS_BUF_GET_header) != header_new)
					{
						// не тот формат у буфера!
						MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
					}

					// Количество юзеров
					dbuf_out.Read(&num_user, sizeof(num_user));
					if(num_user != 1)
					{
						MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("Не совпадает количество отправленных и принятых пользователей"));
					}

					ReadFromBuferUserArray(dbuf_out, list, start_in_portion, size_in_user);
					start_in_portion += size_in_user;

				}while(size_in_user != 0);
			}
		}
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		m_AplTransport->ClearInternalData();
		return false;	
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();
	return true;
}//bool CaplNetStepData::NET_GetGroupeAccess(...)

bool CaplNetStepData::NET_GetGroupeAccessBI(aplListAccess2Inst &ListAccess, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	int i = 0, j = 0;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	// Буфер передачи данных
	CaplDataBuf dbuf_in, dbuf_out;

	UINT32 inst_id = 0, ent_id = 0, user_id = 0, pattern_id;
	int inst_count, user_count;

	// заголовки буферов - однобайтовые! 
	const char* header = ACCESS_BUF_BI_header;

	aplAccess2Inst* inst_access;
	CaplInstance* user;
	CaplInstance* inst;
	BYTE access_bt;
	aplAccessModeType access;
	BOOL is_individual;

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		//Актуализируем список юзеров
		aplExtent ext_usr;
		LoadAllUsers(ext_usr);

		// Количество инстансов
		inst_count = ListAccess.Size;
		if(inst_count == 0) return true;

		dbuf_in.Clear();
		// заголовки буферов - однобайтовые! 
		dbuf_in.Add(header, 21);
		// Количество инстансов		
		dbuf_in.Add(&inst_count, sizeof(inst_count));

		for(i=0; i <inst_count; i++)
		{
			inst_access = ListAccess.GetAt(i);
			if(inst_access == 0){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Пустой указатель в списке доступа!"));}
			if(inst_access->inst == 0){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Пустая инстанс в списке доступа!"));}
			if(inst_access->inst->GetType() == 0){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Удаленная инстанс в списке доступа!"));}
			inst_id = inst_access->inst->GetId();
			ent_id = inst_access->inst->GetType()->id; 
			dbuf_in.Add(&inst_id, sizeof(inst_id));
			dbuf_in.Add(&ent_id, sizeof(ent_id));

			user_count = inst_access->GetSize();
			dbuf_in.Add(&user_count, sizeof(user_count));

			for(j = 0; j<user_count; j++)
			{
				if(!inst_access->Get(j, &user, access)){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Ошибка при чтении индивидуального доступа!"));}
				user_id = user->GetId();
				dbuf_in.Add(&user_id, sizeof(user_id));
			}

		}
			
		m_AplTransport->SetWaitDlgText(wait_text);
		AplNetErrors aplError = APL_NET_OK;

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCGetUsersAccessBI(aplError, dbuf_in, dbuf_out));


		// Расшифровка
		// заголовки буферов - однобайтовые! 
		char header_new[21];
		dbuf_out.Read(header_new, 21);
		if(CStringA(ACCESS_BUF_BI_header) != header_new)
		{
			// не тот формат у буфера!
			MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
		}

		// Количество инстансов		
		dbuf_out.Read(&inst_count, sizeof(inst_count));
		if(inst_count != ListAccess.Size) MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("Не совпадает количество отправленных и принятых инстансов"));

		for(i=0; i <inst_count; i++)
		{
			
			dbuf_out.Read(&inst_id, sizeof(inst_id));
			
			aplQFindInstIdInExtent(*((aplExtent*)(&instances)), inst_id, instances.Size, false, &inst);
			if(inst == 0) MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, _T("inst == 0"));

			inst_access = ListAccess.Find(inst);
			if(inst_access == 0 ) MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("Для пришедшего с сервера инстанса не задана структура доступа"));

			// сброс значения доступа в NOT_DEFINED
			inst_access->ResetAccess();

			// данные по доступу по умолчанию
			dbuf_out.Read(&access_bt, sizeof(access_bt));
			dbuf_out.Read(&is_individual, sizeof(is_individual));
			dbuf_out.Read(&pattern_id, sizeof(pattern_id));

			inst_access->access_def = access_bt;
			inst_access->is_individual = is_individual;
			aplQFindInstIdInExtent(*((aplExtent*)(&instances)), pattern_id, instances.Size, false, &(inst_access->pattern));

			// данные по доступу к текущему юзеру
			dbuf_out.Read(&access_bt, sizeof(access_bt));
			inst_access->inst->SetAccessmode((aplAccessModeType)access_bt);

			dbuf_out.Read(&user_count, sizeof(user_count));
			for(j = 0; j<user_count; j++)
			{
				dbuf_out.Read(&user_id, sizeof(user_id));
				dbuf_out.Read(&access_bt, sizeof(access_bt));

				aplQFindInstIdInExtent(*((aplExtent*)(&instances)), user_id, instances.Size, false, &user);

				if(user == 0) MY_THROW_EX( APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("С сервера пришел идентификатор неизвестного юзера"));

				inst_access->Add(user, (aplAccessModeType)access_bt);
			}
		}
		m_AplTransport->ClearInternalData();
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		m_AplTransport->ClearInternalData();
		return false;	
	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}//bool CaplNetStepData::NET_GetGroupeAccessBI(...)

bool CaplNetStepData::NET_RemoveAccessAgent(aplExtent &extent, CaplInstance* access_agent, LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);

	int i;
	aplListAccess2Inst ListAccess;
	aplAccess2Inst *inst_access;
	aplAccessModeType access = aplNOT_DEFINED;

	// читаем с сервера информацию о доступе
	// заодно обновляем информацию о доступе текущего пользователя к обрабатываемым инстансам
	// для каждой из запрашиваемых инстансов
	for(i=0; i <extent.GetSize(); i++)
	{
		// идентификаторы инстанс
		inst_access = new aplAccess2Inst();
		inst_access->inst = extent.GetAt(i);
		ListAccess.Add(inst_access);
	}

	if(!NET_GetGroupeAccessBI(ListAccess, wait_text))
	{
		return false;
	}

	for(i=0; i <ListAccess.Size; i++)
	{
		inst_access = ListAccess.GetAt(i);
		if(inst_access == 0) continue;
		if(inst_access->is_individual == false) continue;
		inst_access->Add(access_agent, access);
	}
	if(!NET_SetGroupeAccessBI(ListAccess, wait_text, code1, code2))
	{
		return false;
	}

	return true;
}

bool CaplNetStepData::NET_SetGroupeAccessBI(aplListAccess2Inst &ListAccess, LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try
	{
		TEST_RECONNECT(false);

		if(m_is_readonly)
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ACCESS_DENY, APL_T("В режиме \"Только чтение\" назначение доступа запрещено!"));
			return false;
		}

		if(m_SetAccessWithSave)
		{
			aplListAccess2Inst* ListAccessI = new aplListAccess2Inst();
			ListAccessI->Set(ListAccess);
			if(code1 != 0 || code2 != 0)
			{
				ListAccessI->code1 = code1;
				ListAccessI->code2 = code2;
			}
			m_ListDelaedAccessBI.Add(ListAccessI);
			return true;
		}
		else
		{
			return NET_SetGroupeAccessBIInternal(ListAccess, wait_text, code1, code2);
		}
		m_AplTransport->ClearInternalData();

	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		m_AplTransport->ClearInternalData();
		return false;	
	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}

bool CaplNetStepData::NET_SetGroupeAccessBIInternal(aplListAccess2Inst &ListAccess, LPCTSTR wait_text, int code1, int code2)
{
	CaplStackLogger logger(__APL_FUNC__);

	int i = 0, j = 0;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	


	// Буфер передачи данных
	CaplDataBuf dbuf_in, dbuf_out;

	UINT32 inst_id = 0,ent_id = 0,user_id = 0, pattern_id;
	int inst_count, user_count;

	// заголовки буферов - однобайтовые! 
	const char* header = ACCESS_BUF_BI_header;

	aplAccess2Inst* inst_access;
	CaplInstance* user;
	BYTE access_bt;
	aplAccessModeType access;
	BOOL is_individual;

	// Количество инстансов
	inst_count = ListAccess.Size;
	if(inst_count == 0) return true;

	try
	{
		TEST_RECONNECT(false);

		m_AplTransport->SetWaitDlgText(wait_text);

		dbuf_in.Clear();
		dbuf_in.Add(header, 21);

		// Читаем спецчисла для работы workFlow
		dbuf_in.Add(&code1, sizeof(code1));
		dbuf_in.Add(&code2, sizeof(code2));

		// флаг списка доступа. Если TRUE - индивидуальный доступ на все инстансы одинаковый, список доступа идет один раз после списка
		// инстансов. Если FALSE - список доступа свой к каждому инстансу
		is_individual = ListAccess.identical_access4all ? TRUE : FALSE;
		dbuf_in.Add(&is_individual, sizeof(is_individual));

		// как поступать с предыдущими записями индивидуального доступа. Если true- сносить, если false- оставлять
		is_individual = ListAccess.clear_old_ind_access ? TRUE: FALSE;
		dbuf_in.Add(&is_individual, sizeof(is_individual));

		// Количество инстансов		
		dbuf_in.Add(&inst_count, sizeof(inst_count));

		for(i=0; i <inst_count; i++)
		{
			inst_access = ListAccess.GetAt(i);
			if(inst_access == 0){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Пустой указатель в списке доступа!"));}
			if(inst_access->inst == 0){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Пустая инстанс в списке доступа!"));}
			if(inst_access->inst->GetType() == 0){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Удаленная инстанс в списке доступа!"));}
			inst_id = inst_access->inst->GetId();
			ent_id = inst_access->inst->GetType()->id; 
			dbuf_in.Add(&inst_id, sizeof(inst_id));
			dbuf_in.Add(&ent_id, sizeof(ent_id));

			// пишем в буфер данные по доступу по умолчанию
			dbuf_in.Add(&(inst_access->access_def), sizeof(inst_access->access_def));

			is_individual = inst_access->is_individual;
			dbuf_in.Add(&is_individual, sizeof(is_individual));

			pattern_id = (inst_access->pattern == (CaplInstance*)-1) ? -1 : (inst_access->pattern == (CaplInstance*)0) ? 0 : inst_access->pattern->GetId();
			dbuf_in.Add(&pattern_id, sizeof(pattern_id));
		
			if(!ListAccess.identical_access4all)
			{
				user_count = inst_access->GetSize();
				dbuf_in.Add(&user_count, sizeof(user_count));

				for(j = 0; j<user_count; j++)
				{
					if(!inst_access->Get(j, &user, access)){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Ошибка при чтении индивидуального доступа!"));}
					user_id = user->GetId();
					dbuf_in.Add(&user_id, sizeof(user_id));
					access_bt = access;
					dbuf_in.Add(&access_bt, sizeof(access_bt));
				}
			}
		}
		if(ListAccess.identical_access4all)
		{
			inst_access = ListAccess.GetAt(0);
			user_count = inst_access->GetSize();
			dbuf_in.Add(&user_count, sizeof(user_count));

			for(j = 0; j<user_count; j++)
			{
				if(!inst_access->Get(j, &user, access)){ MY_THROW_EX(APL_NET_CLI_MISSED_DATA, APL_T("Ошибка при чтении индивидуального доступа!"));}
				user_id = user->GetId();
				dbuf_in.Add(&user_id, sizeof(user_id));
				access_bt = access;
				dbuf_in.Add(&access_bt, sizeof(access_bt));
			}
		}
			
		m_AplTransport->SetWaitDlgText(wait_text);
		AplNetErrors aplError = APL_NET_OK;

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCSetUsersAccessBI(aplError, dbuf_in, dbuf_out));

		ApploadFromDataBuf(dbuf_out);

		m_AplTransport->ClearInternalData();
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		m_AplTransport->ClearInternalData();
		return false;	
	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}//bool CaplNetStepData::NET_SetGroupeAccessBI(...)


//*********************************************************************************
bool CaplNetStepData::ApploadFromDataBuf(CaplDataBuf &dbuf, aplExtent *ext_inst)
{
	CaplStackLogger logger(__APL_FUNC__);

	aplExtent ext_internal;
	if(ext_inst == 0){ ext_inst = &ext_internal;}
	bool ret_val = CaplStepData::ApploadFromDataBuf(dbuf, ext_inst);
	if(m_admin_mode)
	{
		m_loaded_in_admin.Unique = false;
		m_loaded_in_admin.Append(*ext_inst);
	}
	return ret_val;
}

//************************************************************************
CaplInstance  *CaplNetStepData::CreateInstance(CaplEntity *entity, bool is_temporary, bool create_attrs)
{
	if (entity == 0) {SetLastErrorWithFileInfo(APLAPIERR_BADENT); return 0;}
	if (CheckEntityAdminAccess(entity->id))
	{
		if(!IsAdmin())
		{
			if(!m_hide_mode)AfxMessageBox( APL_T("Создавать инстанс с типом ") + entity->name + APL_T(" может только администратор"));
			SetLastErrorWithEnttFileInfo(APLAPIERR_NOACCESSRIGHT, entity); return 0;
		}
	}
	return CaplStepData::CreateInstance(entity, is_temporary, create_attrs);
}

CaplInstance  *CaplNetStepData::CreateInstanceBN(LPCTSTR type_name, bool is_temporary, bool create_attrs)
{
	if (type_name == 0) {SetLastErrorWithFileInfo(APLAPIERR_BADENT); return 0;}
	SetLastError(0);
	CaplEntity *ent = GetEntityBN(type_name);
	if (ent == 0) {SetLastErrorWithFileInfoExtText(APLAPIERR_BADENT, type_name); return 0;}
	if (CheckEntityAdminAccess(ent->id))
	{
		if(!IsAdmin())
		{
			if(!m_hide_mode) AfxMessageBox( APL_T("Создавать инстанс с типом ") + ent->name + APL_T(" может только администратор"));
			SetLastErrorWithEnttFileInfo(APLAPIERR_NOACCESSRIGHT, ent); return 0;
		}
	}
	return CaplStepData::CreateInstance(ent, is_temporary, create_attrs);
}

//************************************************************************
bool CaplNetStepData::DeleteInstanceForced(CaplInstance *inst)
{
	if (inst == 0) {SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE); return false;}
	if (inst->GetType() == 0) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE, inst); return false;}
	if (CheckEntityAdminAccess(inst->GetType()->id))
	{
		if(!IsAdmin())
		{
			if(!m_hide_mode)AfxMessageBox( APL_T("Удалять инстанс с типом ") + inst->GetType()->name + APL_T(" может только администратор"));
			SetLastErrorWithInstFileInfo(APLAPIERR_NOACCESSRIGHT, inst); return 0;
		}
	}
	return CaplStepData::DeleteInstanceForced(inst);
}
//************************************************************************
bool CaplNetStepData::DeleteInstance(CaplInstance *inst, bool bChekReference, bool bNoSetChanged)
{
	if (inst == 0) {SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE); return false;}
	if (inst->GetType() == 0) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE, inst); return false;}
	if (CheckEntityAdminAccess(inst->GetType()->id))
	{
		if(!IsAdmin())
		{
			if(!m_hide_mode)AfxMessageBox( APL_T("Удалять инстанс с типом ")+inst->GetType()->name+ APL_T(" может только администратор"));
			SetLastErrorWithInstFileInfo(APLAPIERR_NOACCESSRIGHT,inst); return 0;
		}
	}
	return CaplStepData::DeleteInstance(inst, bChekReference, bNoSetChanged);
}

//*********************************************************************************
bool CaplNetStepData::GetAttr(CaplInstance *inst, CaplAttr *attr, CaplValue **value)
{

	*value = 0;
	if(inst == 0) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADINSTANCE, inst, attr); return false;}
	if(inst->GetType() == 0) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADINSTANCE, inst, attr); return false;}
	if(attr == 0) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADATTR, inst, attr); return false;}
	if(m_TestAccessMode)
	{
		if(inst->GetAccessmode() > aplRO) 
		{
			SetLastErrorWithInstAttrFileInfo(APLAPIERR_NOACCESSRIGHT, inst, attr); 
			return false;
		}
	}
	SetLastError(0);

	CaplInstance::CaplValueDefinition *av = GetAttrValue(inst, attr);
	if(av != 0) 
	{
		*value = &(av->value);
		return true;
	}
	if(m_AutoDownloadAttr && !inst->GetTemporary())
	{
		if(IsConnected())
		{
			// проверка типа
			if(inst->GetType()->all_attrs.Find(attr) != -1 && inst->GetId() != 0)
			{
				// чтение из базы
				apl_NET_LoadQuery q(0,  'i', inst->GetId(), 0, attr->id, true);
				if(!NET_Load(&q, 1))
				{
					SetLastWarningWithFileInfo(APLAPIERR_ATTRNSET);
					return false;
				}
				av = GetAttrValue(inst, attr);
				if(av != 0) 
				{
					*value = &(av->value);
					return true;
				}
			}
		}
	}
	SetLastWarningWithFileInfo(APLAPIERR_ATTRNSET);
	return false;
}//bool CaplNetStepData::GetAttr(CaplInstance *inst,CaplAttr *attr,CaplValue **value)


bool CaplNetStepData::NET_QueryEditParseNoModal(CString &query_lng,
												int (__cdecl *end_of_edit)(DWORD *), DWORD id_calling_object,
												CaplQLQueryParse **query, CString *errmsg,
												int* is_dlg_ro, CString *name, bool *save2bd, bool *make_table_result
												)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef("CaplNetStepData::NET_QueryEditParseEx");

	return false;

}

bool CaplNetStepData::NET_QueryEditParse(CString &query_lng, bool is_dlg,
										 CString *errmsg, int* is_dlg_ro, CString *name, bool *save2bd,
										 bool *make_table_result)
{
	CaplStackLogger logger(__APL_FUNC__);

	LogDebugMessageIfDef("CaplNetStepData::NET_QueryEditParse");

#ifdef _DEBUG
	//m_MainLog.LogMessage(query_lng);
#endif //#ifdef _DEBUG

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		if(m_query != 0)
		{
			delete m_query;
			m_query = 0;
		}
		int str_num = 0, pos = 0;
		CString str, tmp;

		if(is_dlg)
		{
			CaplSetResourceHandle res_handle(module_inst);
			CDlgQlLang dlg;
			dlg.m_hStepData = this;
			dlg.m_lng_query = query_lng;

			if(is_dlg_ro != 0)
			{
				dlg.m_is_ro = *is_dlg_ro>=1;
				dlg.m_only_ro = *is_dlg_ro==2;
			}
			if(name != 0)
			{
				dlg.m_NameQuery = *name;
			}
			if(save2bd != 0)
			{
				dlg.m_can_save2bd = true;
				dlg.m_IsSave2BD = *save2bd;
			}
			if(make_table_result != 0)
			{
				dlg.m_IsMakeTable = *make_table_result;
			}
			int res = dlg.DoModal();
			if(res != IDOK)
			{
				if(errmsg != 0) *errmsg = APL_T("Отмена ввода запроса");
				return false;
			}
			if(is_dlg_ro != 0)
			{
				*is_dlg_ro = dlg.m_is_ro;
			}
			if(!dlg.m_only_ro && name != 0)
			{
				*name = dlg.m_NameQuery;
			}
			if(make_table_result != 0)
			{
				*make_table_result = (dlg.m_IsMakeTable==TRUE);
			}
			if(save2bd != 0)
			{
				*save2bd = (dlg.m_IsSave2BD==TRUE);
			}
			query_lng = dlg.m_lng_query;
			if(!dlg.m_is_parsed)
			{
				if(errmsg != 0) *errmsg = APL_T("Неверный ввод текста запроса");
				return false;
			}
			m_query = new CaplQLQueryParse(this);
			m_query->SetFromOlder(&dlg.query);
		}
		else
		{
			if(query_lng == _T(""))
			{
				if(errmsg != 0) *errmsg = APL_T("Отсутствует текст запроса");
				return false;
			}
			str = query_lng;
			m_query = new CaplQLQueryParse(this);
			m_query->parse(str);
#ifdef _DEBUG
	// 		str.Format( APL_T("Размер m_extents %i"),m_query->m_extents.GetSize());
	// 		m_MainLog.LogMessage(str);
#endif //#ifdef _DEBUG

			if(!m_query->m_is_parse)
			{
				str = m_query->GetLastError(str_num, pos);
				if(errmsg != 0)
				{
					errmsg->Format(APL_T("Ошибка в строке %i позиция %i :%s; Текст запроса:\n"), str_num, pos, (LPCTSTR)str);
					errmsg->Append(query_lng);
					m_AplErrorDescription = *errmsg;
				}
				else
				{
					m_AplErrorDescription.Format(APL_T("Ошибка в строке %i позиция %i :%s; Текст запроса:\n"), str_num, pos, (LPCTSTR)str);
					m_AplErrorDescription.Append(query_lng);
				}
				m_MainLog.LogMessage(m_AplErrorDescription);
				m_LastAplErrorCode = APL_NET_SRV_MISSED_DATA;
				return false;
			}
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	return true;
}//bool CaplNetStepData::NET_QueryLang(aplExtent &ext,CString query_lng, bool is_dlg,CString *errmsg)


// Выполнение запроса по списку объектов
bool CaplNetStepData::NET_QueryExecute(aplExtent &ext, LPCTSTR wait_text)
{
	
	return NET_QueryExecute(ext, wait_text, _T(""));
}

bool CaplNetStepData::NET_QueryExecute(aplExtent &ext, LPCTSTR wait_text, LPCTSTR source)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(_T("CaplNetStepData::NET_QueryExecute "));

	// CHECK_CONN_PROCESSED(true, true); - проверки на подключение - в вызываемой функции

	CHECK_PARAM_EX(m_query == 0, APL_T("Системный объект запроса не инициализирован"));

	m_num_query++;

	bool bval = NET_QueryExecuteEx(m_query, ext, wait_text, source);

	delete m_query;
	m_query = 0;

	LogDebugMessageIfDef( APL_T("NET_QueryExecute выход"));
	return bval;
}

bool CaplNetStepData::NET_QueryExecute(DWORD &count_res, LPCTSTR wait_text, LPCTSTR source)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(_T("CaplNetStepData::NET_QueryExecute "));

	// CHECK_CONN_PROCESSED(true, true); - проверки на подключение - в вызываемой функции

	CHECK_PARAM_EX(m_query == 0, APL_T("Системный объект запроса не инициализирован"));

	m_num_query++;

	bool bval = NET_QueryExecuteEx(m_query, count_res, wait_text, source);

	LogDebugMessageIfDef( APL_T("NET_QueryExecute выход"));
	return bval;
}

// Выполнение запроса по списку объектов
bool CaplNetStepData::NET_QueryExecuteEx(CaplQLQueryParse* query, aplExtent &ext, LPCTSTR wait_text)
{

	return NET_QueryExecuteEx(query, ext, wait_text, _T(""));
}

// Выполнение запроса по списку объектов
bool CaplNetStepData::NET_QueryExecuteEx(CaplQLQueryParse* query, aplExtent &ext, LPCTSTR wait_text, LPCTSTR source)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);

	bool ret_code = false;
	CHECK_BD(false);
	CHECK_STATE_S(true, true, false);	

	CaplDataBuf dbuf_in, dbuf_out, dbuf_out2;
	ext.Clear();
	AplNetErrors aplError = APL_NET_OK;

	if(!m_hide_mode)
	{
		if(wait_text == 0){ m_AplTransport->SetWaitDlgText( APL_T("Выполняется поиск"));}
		else{ m_AplTransport->SetWaitDlgText(wait_text);}
	}

	CHECK_PARAM_EX(query == 0, APL_T("В функцию выполнения запроса передан пустой объект запроса"));

	try{

		TEST_RECONNECT(false);

		m_num_query++;	

		// Результаты разбора в буфер
		query->SaveToDataBuf(dbuf_in);

		if(m_AplTransport->AT_DCQueryEx(aplError, dbuf_in, dbuf_out, dbuf_out2, source))
		{
			ret_code = ApploadFromDataBuf(dbuf_out2);
		}
		else
		{
			SetAPLErrorWithFileInfo(aplError);
			m_AplTransport->ClearInternalData();
			return false;
		}

		LogDebugMessageIfDef( APL_T("NET_QueryExecuteEx чтение данных"));

		ret_code = ApploadFromDataBuf(dbuf_out, &ext);
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("NET_QueryExecuteEx выход"));

	return ret_code;
}//bool CaplNetStepData::NET_QueryObj(CaplQLQuery* query,aplExtent &ext)

// Выполнение запроса по списку объектов
bool CaplNetStepData::NET_QueryExecuteEx(CaplQLQueryParse* query, DWORD &count_res, LPCTSTR wait_text, LPCTSTR source)
{
	CaplStackLogger logger(__APL_FUNC__);
	LogDebugMessageIfDef(__APL_FUNC__);

	bool ret_code = false;
	CHECK_BD(false);
	CHECK_STATE_S(true, true, false);	

	CaplDataBuf dbuf_in, dbuf_out, dbuf_out2;
	AplNetErrors aplError = APL_NET_OK;
	count_res = 0;
	CHECK_PARAM_EX(query == 0, APL_T("В функцию выполнения запроса передан пустой объект запроса"));

	try{

		TEST_RECONNECT(false);

		if(!m_hide_mode)
		{
			if(wait_text == 0){m_AplTransport->SetWaitDlgText( APL_T("Выполняется поиск"));}
			else{m_AplTransport->SetWaitDlgText(wait_text);}
		}
		m_num_query++;	
		// Результаты разбора в буфер
		query->SaveToDataBuf(dbuf_in, false);

		if(m_AplTransport->AT_DCQueryEx(aplError, dbuf_in, dbuf_out, dbuf_out2, source))
		{
			ret_code = true;
			dbuf_out.Read(&count_res, sizeof(count_res));
		}
		else
		{
			SetAPLErrorWithFileInfo(aplError);
			m_AplTransport->ClearInternalData();
			return false;
		}

		LogDebugMessageIfDef( APL_T("NET_QueryExecuteEx чтение данных"));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef( APL_T("NET_QueryExecuteEx выход"));

	return ret_code;
}//bool CaplNetStepData::NET_QueryObj(CaplQLQuery* query,aplExtent &ext)


// Загрузить с сервера списко удаленных инстансов
bool CaplNetStepData::NET_LoadDelInst(aplExtent &ext, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	AplNetErrors aplError = APL_NET_OK;

	bool res = false;
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	CaplDataBuf dbuf;

	try{

		TEST_RECONNECT(false);
		
		m_AplTransport->SetWaitDlgText(wait_text);

		LogDebugMessageIfDef( APL_T("Вызов DCQuerDelIst на сервере"));

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCQuerDelInst(aplError, dbuf));

		LogDebugMessageIfDef( APL_T("Передача буфера с сервера"));

		res = ApploadFromDataBuf(dbuf, &ext);
		// 	if(res){ // для отладки
		// 		res=MakeExtFromDbuf(dbuf,ext);
		// 	}

		if(res)
		{
			for(int i=0; i <ext.GetSize(); i++)
			{
				if(ext.GetAt(i)->GetType() != 0)
				{	
					SetAPLErrorWithFileInfoEx(APLAPIERR_BADDATA, APL_T("В списке инстансов, удаленных другими сеансами, есть не удаленные инстансы."));
					return false;
				}
			}
		}
		LogDebugMessageIfDef( APL_T("Выход из NET_LoadDelInst"));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return res;
}//bool CaplNetStepData::NET_LoadDelInst(aplExtent &ext)



// Заполнить список для передачи на сервер запроса поиска
bool CaplNetStepData::FeelTestFindBuf(CaplDataBuf &dbuf, CaplEntity *ent,
									  CaplInstance *test_inst, int count, CaplAttrValue *values,
									  bool is_substring, bool match_case, aplStringBindMode string_bind_mode, bool *bIgnoreSpace)
{
	int i=0;
	CaplValue val;
	BYTE btv;
	// Заголовок
	// заголовки буферов - однобайтовые! 
	const char* header;
	if(bIgnoreSpace == 0)header = std_test_find_header3;
	else header = std_test_find_header;

	dbuf.Add(header,(strlen(header)+1));
	dbuf.m_ansi_string = m_global_ansi_string;

	//Пишем ентити
	UINT32 id_c = 0;
	if(ent == 0)
		return false;
	id_c = ent->id;
	dbuf.Add(&id_c, sizeof(id_c));
	if(test_inst == 0)
	{
		id_c = 0;
	}
	else
	{
		id_c = test_inst->GetId();
	}
	dbuf.Add(&id_c, sizeof(id_c));

	// Количество атрибутов
	dbuf.Add(&count, sizeof(count));

	//pCaplInstance *instmap=new pCaplInstance[count];
	CaplMap instmap; CaplInstance* inst = 0; aplExtent ext;
	// Добавляем номера инстансов - если есть - для стандартной расшифровки на сервере
	for(i=0; i <count; i++)
	{
		if(values[i].attr == 0)
		{
			if(!m_hide_mode) AfxMessageBox( APL_T("Атрибут не может быть пустым"));
			return false;
		}
		if(!IsEntityAttr(ent,values[i].attr))
		{
			if(!m_hide_mode) AfxMessageBox( APL_T("Атрибут ") + values[i].attr->name + APL_T(" отсутствует у entity ") + ent->name + APL_T(" Обновите словарь БД!\n"));
			return false;
		}

		if(values[i].value.type == aplINSTANCE || values[i].value.type == aplSELECT)
		{
			ext.Add(values[i].value.instval);
		}
		else if(values[i].value.type == aplAGGR)
		{
			CaplAggr *aggr = values[i].value.aggrval;
			if(aggr == 0) continue;
			if(aggr->type != aplINSTANCE) continue;
			for(int k = 0; k<aggr->GetSize(); k++)
			{
				aggr->GetByIndex(k, inst);
				ext.Add(inst);
			}
		}
	}

	// Количество передаваемых инстансов
	int k = ext.Size;
	dbuf.Add(&k, sizeof(k));

	// Сами инстансы
	for(i=0; i <k; i++)
	{
		if(ext[i] == 0)
		{
			return false;
		}
		id_c = ext[i]->GetId(); 
		if(id_c == 0)
		{
			return false;
		}
		dbuf.Add(&id_c, sizeof(id_c));
		//instmap[i]=values[i].value.instval;
		instmap.Add((long)ext[i], i);
	}

	// Заносим значение в буфер
	for(i=0; i <count; i++)
	{
		id_c = values[i].attr->id; 
		dbuf.Add(&id_c, sizeof(id_c));
		//val.Set(values[i].value);
		aplAppendBufValue(dbuf, instmap, &(values[i].value));
	}
	if(is_substring){ k=1; }else{ k = 0;}
	dbuf.Add(&k, 4);
	if(match_case){ k=1; }else{ k = 0;}
	dbuf.Add(&k, 4);
	dbuf.Add(&string_bind_mode, 4);
	if(bIgnoreSpace != 0)
	{
		btv = (*bIgnoreSpace) ? 1 : 0;
		dbuf.Add(&btv, 1);
	}
	//	delete instmap;
	return true;

}//bool CaplNetStepData::FeelTestFindBuf(CaplDataBuf &dbuf,CaplEntity *ent,CaplInstance *test_inst,
//					 int count,CaplAttrValue *values, bool is_substring, bool match_case)


int CaplNetStepData::FindInstancesWithAttrValuesInLocalCache(CaplEntity *entity, CaplInstance *tested_inst, 
															 int count, CaplAttrValue *values, aplExtent &extent_out, 
															 bool is_substring, bool match_case, bool test_only,
															 aplStringBindMode string_bind_mode, bool only_new, 
															 bool bIgnoreSpace)
{
	if(entity == 0) return -1;
	if(count < 1)  return -1;
	if(values == 0) return -1;
	extent_out.Clear();
	int i = 0, j = 0;
	aplExtent ext;
	CaplValue val;
	CString val_str_in, val_str_base;
	bool is_connected = IsConnected();

	m_num_find_local_cache++;

	GetEntityExtent(entity, ext);

	if(test_only)
	{
		for(i=0; i <ext.Size; i++)
		{
			bool equ = true;
			if(ext[i] != tested_inst && ext[i] != 0)
			{
				if(ext[i]->GetType() == 0) continue;
				if(ext[i]->GetAccessmode() > aplRO) continue;
				for(j = 0; j<count; j++)
				{
					CaplAttr *attr = values[j].attr;
					if(attr->type == aplSTRING || attr->type == aplENUMERATION)
					{
						if(values[j].value.Get(val_str_in) && ext[i]->GetAccessmode() < aplNO && GetAttr(ext[i], attr, val))
						{
							val.Get(val_str_base);
							if(!match_case)
							{
								// разницу не учитываем
								// для сравнения используем Upper-ованные строки
								val_str_in.MakeUpper();
								val_str_base.MakeUpper();
							}
							if(bIgnoreSpace)
							{
								val_str_base.Replace(_T(" "), _T(""));
								val_str_in.Replace(_T(" "), _T(""));
							}
							if(is_substring)// а не проверить ли нам подстроки?
							{
								// ищем подстроку
								// ищем подстроку
								int pos;
								if(string_bind_mode == aplSBMiddle)
								{
									pos=val_str_base.Find(val_str_in);
									//типа не нашли
									if(pos==-1){equ = false; break;}
								}
								else if(string_bind_mode == aplSBLeft)
								{
									//типа не нашли
									if(val_str_base.Left(val_str_in.GetLength()) != val_str_in){ equ = false; break;}
								}
								else if(string_bind_mode == aplSBRight )
								{
									if(val_str_base.Right(val_str_in.GetLength()) != val_str_in){ equ = false; break;}
								}
							}
							else
							{
								if (values[j].is_substring)
								{
									//  [8/1/2012 lobanov]
									// если не установлен флаг is_substring в функции, то проверяется флаг is_substring для
									// каждого CaplAttrValue из values
									if(val_str_base.Left(val_str_in.GetLength()) != val_str_in){ equ = false; break;}
								}
								else if(val_str_in != val_str_base)
								{
									equ = false;
									break;
								}
							}
						}
						else
						{
							equ = false; 
							break;
						}
					}
					else
					{
						if(GetAttr(ext[i], attr, val))
						{
							if(!val.IsEquWithAggr(values[j].value))
							{
								equ = false;
								break;
							}
						}
						else
						{
							if(values[j].value.type != aplNOTYPE)
							{
								equ = false; 
								break;
							}
						}
					}
				}
				if(equ)
				{
					// если соединения нет, нас устраивают все объекты локального кеша.
					// если соединение есть - из кеша берем только новые, остальные с сервера
					if(!is_connected || ext[i]->GetId() == 0 || !only_new)
					{
						// Работаем как TestUniqueAttrValues
						extent_out.Add(ext[i]);
						return 1;
					}
				}
			}
		}
	}
 	else if(ext.Size > 0)
	{
		int pos;
		bool equ = true, bOk;
		CaplAttr *attr;
		{
			for(i=0; i <ext.Size; i++)
			{
				if(ext[i] != 0)
				{
					if(ext[i]->GetType() == 0) continue;
					if(ext[i]->GetAccessmode() > aplRO) continue;

					equ = true;
					for(j = 0; j<count; j++)
					{
						attr = values[j].attr;
						if(equ)
						{
							if(attr->type == aplSTRING || attr->type == aplENUMERATION)
							{
								if(values[j].value.Get(val_str_in) && ext[i]->attrs != NULL)
								{
									bOk = GetAttr(ext[i], attr, val);
									if(bOk)
									{
										val.Get(val_str_base);
										if(!match_case)
										{
											// разницу не учитываем
											// для сравнения используем Upper-ованные строки
											val_str_in.MakeUpper();
											val_str_base.MakeUpper();
										}
										if(bIgnoreSpace)
										{
											val_str_in.Replace(_T(" "), _T(""));
											val_str_base.Replace(_T(" "), _T(""));
										}
										if(is_substring)
										{// а не проверить ли нам подстроки?
											// ищем подстроку
											// ищем подстроку
											pos = -1;
											if(string_bind_mode == aplSBMiddle)
											{
												pos=val_str_base.Find(val_str_in);
												//типа не нашли
												if(pos == -1)
													equ = false;
											}
											else if(string_bind_mode == aplSBLeft)
											{
												//типа не нашли
												if(val_str_base.Left(val_str_in.GetLength()) != val_str_in)
													equ = false;
											}
											else if(string_bind_mode==aplSBRight )
											{
												if(val_str_base.Right(val_str_in.GetLength()) != val_str_in)
													equ = false;
											}
										}
										else
										{
											if (values[j].is_substring)
											{
												//  [8/1/2012 lobanov]
												// если не установлен флаг is_substring в функции, то проверяется флаг is_substring для
												// каждого CaplAttrValue из values
												if(val_str_base.Left(val_str_in.GetLength()) != val_str_in)
													equ = false;
											}
											else if(val_str_in != val_str_base)
												equ = false;
										}
									}
								}
								else
									equ = false;
							}
							else
							{
								bOk = false;
								if(ext[i]->attrs != NULL)
								{
									bOk = GetAttr(ext[i], attr, val);
								}
								if(bOk)
								{
									if(!val.IsEquWithAggr(values[j].value))
										equ = false;
								}
								else
								{
									if(values[j].value.type != aplNOTYPE)
										equ = false;
								}
							}
						}
					}
					if(equ)
					{
						// если соединения нет, нас устраивают все объекты локального кеша.
						// если соединение есть - из кеша берем только новые, остальные с сервера
						{
							if(!is_connected || ext[i]->GetId() == 0 || !only_new)
							{
								extent_out.Add(ext[i]);
							}
						}
					}
				}
			}
		}
	}

	return extent_out.GetSize();
}

/**/

int CaplNetStepData::GetPercentSCh()
{
	return m_PercentSCh;
}

// размер порции для сохранения
bool CaplNetStepData::SetPortionSavedInst(int size)
{
	if(m_PercentSCh == 0 || m_PercentSCh == 100)
	{
		m_SizePortionSavedInst = size;
		return true;
	}
	return false;
}



/*
CaplSQLResult myValues[5];
myValues[0].field="sss";
myValues[0].type=aplString;
aplRunSql("", myValues ,5)
*/
bool CaplNetStepData::NET_RunSql(LPCTSTR sql, SaplSQLResult *result, int count,
								 SaplSQLError* oracle_error, int code1, int code2,
								 LPCTSTR wait_text, LPCTSTR sql_prev, LPCTSTR source)
{
	CaplStackLogger logger(__APL_FUNC__);

	bool ret_val = true;
	CHECK_BD(false);
	CHECK_STATE_S(true, true, false);	

	m_num_run_sql++;

	try
	{
		LogDebugMessageIfDef(_T("NET_RunSql begin"));

		TEST_RECONNECT(false);

		CaplDataBuf dbuf_in, dbuf_out;
		AplNetErrors aplError = APL_NET_OK;
		int i, k, row_size = -1;
		char microtype;
		SaplSQLError internal_error, *error_p;
		if(oracle_error == 0)
		{
			error_p = &internal_error;
		}
		else
		{
			error_p = oracle_error;
		}
		error_p->Clear();
		//	CaplMap inst_map;// пустое значение - нужно для функции чтения из буфера
		CaplValue* value;

		// заполняем буфер
		// заголовки буферов - однобайтовые! 
		char* header = std_ClientSQLQuery_header;
		dbuf_in.Add(header, (strlen(header) + 1));
		dbuf_in.m_ansi_string = m_global_ansi_string;
		// расширенный заголовок
		WriteExtBufData(dbuf_in);

		// пишем sql
		dbuf_in.AddStrBuf(sql);

		// пишем поля данных
		dbuf_in.Add(&count, sizeof(count));
		for(i=0; i <count; i++)
		{
			// имя поля
			dbuf_in.AddStrBuf(result[i].field);
			// тип поля
			microtype = (char)result[i].type;
			dbuf_in.Add(&microtype, sizeof(microtype));
			// и очищаем список 
			result[i].values.Clear();
		}
		if(sql_prev != 0)
		{
			// пишем sql
			dbuf_in.AddStrBuf(sql_prev);
		}
		
		m_AplTransport->SetWaitDlgText(wait_text);

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCExecuteSQL(aplError, dbuf_in, dbuf_out, source));
		
		header = 0;
		// читаем из буфера
		// Заголовок
		// заголовки буферов - однобайтовые! 
		i = strlen(std_ClientSQLResult_header) + 1;
		header = new char[i];
		dbuf_out.Read(header,i);

		if(CStringA(std_ClientSQLResult_header) != header)
		{
			delete[] header;
			error_p->m_LastErrDescr = APL_T("Не верная версия буфера");
			MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, error_p->m_LastErrDescr);
		}
		delete[] header;
		
		dbuf_out.m_ansi_string = m_global_ansi_string;

		// код ошибки
		dbuf_out.Read(&(error_p->m_err_code), 4);
		if(error_p->m_err_code == 0)
		{
			// читаем данные
			dbuf_out.Read(&k);
			if(k != count)
			{
				error_p->m_LastErrDescr = APL_T("Не совпадает количество полей данных ушедших на сервер и пришедших с него!");
				MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, error_p->m_LastErrDescr);
			}
			for(i=0; i <count; i++)
			{
				CString str1;
				dbuf_out.ReadStrBuf(str1);
				if(result[i].field != str1)
				{
					error_p->m_LastErrDescr.Format( APL_T("Не совпадает имя %i-го поля данных ушедшее на сервер и пришедшее с него!"), i);
					MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, error_p->m_LastErrDescr);
				}
				dbuf_out.Read(&microtype, sizeof(microtype));
				if(result[i].type != (aplValueType)microtype)
				{
					error_p->m_LastErrDescr.Format( APL_T("Не совпадает тип %i-го поля данных ушедшее на сервер и пришедшее с него!"), i);
					MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, error_p->m_LastErrDescr);
				}
				dbuf_out.Read(&k, sizeof(k));
				if(row_size == -1)
				{
					row_size = k;
				}
				else
				{
					if(row_size != k)
					{
						error_p->m_LastErrDescr.Format( APL_T("Количество результатов у %i-го поля не совпадает с количеством результатов предыдущих полей!"), i);
						MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, error_p->m_LastErrDescr);
					}
				}
				for(int j = 0; j<k; j++)
				{
					value = new CaplValue();
					aplReadValueFromBuf(dbuf_out, 0, value);
					result[i].values.Add(value);
				}
			}		
		}
		else
		{
			// читаем описание ошибки
			dbuf_out.ReadStrBuf(error_p->m_LastErrDescr);

			dbuf_out.Read(&(error_p->m_err_offset), 4);
			dbuf_out.Read(&(error_p->m_missed_field), 4);
			ret_val = false;
		}
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		m_AplTransport->ClearInternalData();
		return false;	
	}
	CATH_MEMORY_ANY_EXEPTION;

	// Очищаем буфер в транспорте
	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef(_T("NET_RunSql return true"));
	return ret_val;

}


/// Выполняет SQL (только для версии под оракл!) и возвращает экстент инстансов
bool CaplNetStepData::NET_RunSqlReturnExtent(LPCTSTR base_sql, aplExtent &result, SaplSQLError* oracle_error,
											 LPCTSTR sql_get_result, int code1, int code2, LPCTSTR wait_text, bool load_all_attrs, LPCTSTR source)
{
	CaplStackLogger logger(__APL_FUNC__);

	bool retval = true;
	CHECK_BD(false);
	CHECK_STATE_S(true, true, false);	

// 	if(base_sql== 0){
// 		return false;
// 	}
	m_num_run_sql_re++;

	try
	{
		LogDebugMessageIfDef(_T("NET_RunSqlReturnExtent begin"));

		TEST_RECONNECT(false);

		CaplDataBuf dbuf_in, dbuf_out, dbuf_out2;
		AplNetErrors aplError = APL_NET_OK;

		int i, k;
		SaplSQLError internal_error, *error_p;
		if(oracle_error == 0)
		{
			error_p = &internal_error;
		}
		else
		{
			error_p = oracle_error;
		}
		// заполняем буфер
		// заголовки буферов - однобайтовые! 
		char* header;
		bool ver2buf = true;

		do 
		{
			error_p->Clear();
			dbuf_in.Clear();
			dbuf_out.Clear();

			if(load_all_attrs)
			{
				if(ver2buf) header = std_ClientSQLQueryEx_header;
				else header = std_ClientSQLQueryEx_header_02;
			}
			else
			{
				header = std_ClientSQLQueryEx_header_01;
				ver2buf = false;
			}
			dbuf_in.Add(header, strlen(header) + 1);
			dbuf_in.m_ansi_string = m_global_ansi_string;

			// расширенный заголовок
			WriteExtBufData(dbuf_in);

			// пишем sql
			if(base_sql == 0)
			{
				k=1; int zero = 0;
				dbuf_in.Add(&k, sizeof(k));
				dbuf_in.Add(&zero, k);
			}
			else
			{
				dbuf_in.AddStrBuf(base_sql);
			}

			// пишем sql
			dbuf_in.AddStrBuf(sql_get_result);

			if(load_all_attrs)
			{
				k = 1;
				dbuf_in.Add(&k, sizeof(k));
			}
			
			m_AplTransport->SetWaitDlgText(wait_text);

			if(ver2buf)
			{
				if(!m_AplTransport->AT_DCExecuteSQLEx(aplError, dbuf_in, dbuf_out, false, dbuf_out2, source))
				{
					if(aplError!=APL_SOCK_MISSED_FUNCTION)
					{
						SetAPLErrorWithFileInfo(aplError);
						return false;
					}
					ver2buf = false;
					continue;
				}
			}
			else
			{
				RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCExecuteSQLEx(aplError, dbuf_in, dbuf_out, true, dbuf_out2, source));
			}
			break;
		}while(true);

		header = 0;
		// читаем из буфера
		// заголовки буферов - однобайтовые!
		i = max(strlen(std_ClientSQLResult_header) + 1, strlen(std_databuf_header)+1);
		header = new char[i];
		dbuf_out.Read(header,i);
		dbuf_out.m_ansi_string = m_global_ansi_string;

		if(CStringA(std_ClientSQLResult_header) == header)
		{
			// так, это код ошибки оракла
			retval = false;
			delete header;
			// код ошибки
			dbuf_out.Read(&(error_p->m_err_code), 4);
			if(error_p->m_err_code == 0)
			{
				// чушь какая-то...
				error_p->m_LastErrDescr = APL_T("В буфере должен быть код ошибки, а его нет!");
				MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, error_p->m_LastErrDescr);
			}
			// читаем описание ошибки
			dbuf_out.ReadStrBuf(error_p->m_LastErrDescr);

			dbuf_out.Read(&(error_p->m_err_offset),4);
			dbuf_out.Read(&(error_p->m_missed_field),4);

		}
		else if(CStringA(std_databuf_header) == header)
		{
			// ага, это результат - екстент
			delete[] header;
			dbuf_out.Reset();
			if(ver2buf) ApploadFromDataBuf(dbuf_out2);
			ApploadFromDataBuf(dbuf_out, &result);
		}
		else if(CStringA(std_databuf_header_u) == header)
		{
			// ага, это результат - екстент
			delete[] header;
			dbuf_out.Reset();
			dbuf_out.m_ansi_string = false;
			dbuf_out2.m_ansi_string = false;
			if(ver2buf) ApploadFromDataBuf(dbuf_out2);
			ApploadFromDataBuf(dbuf_out, &result);
		}
		else
		{
			error_p->m_LastErrDescr.Format(APL_T("Не верная версия буфера: '%s'"), (LPCTSTR) header);
			delete[] header;
			MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, error_p->m_LastErrDescr);
		}
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		retval = false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	// Очищаем буфер в транспорте
	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef(_T("NET_RunSqlReturnExtent return true"));

	return retval;
}


bool CaplNetStepData::NET_Test(LPCTSTR wait_text, int id, int id2)
{

// 	SaveTestString();
// 
// 	return true;
	
	m_AplTransport->SetWaitDlgText(wait_text);

 	AplNetErrors aplError = APL_NET_OK;
	CaplDataBuf buf1, buf2;
// 	DWORD s_tick;
// 	int i,num_c=1000;

	/*
	// проверка времени выполнения пустой транзакции - сервер при приеме такого буфера сразу вернет ответ
	APL_NET_LogMessage("Start test");
	m_AplTransport->ClearTickOp();
	for(i=0; i <num_c; i++)
	{
		m_AplTransport->AT_DCSaveChanges(aplError,buf1,buf2);
		m_AplTransport->ClearInternalData();
	}
	s_tick=m_AplTransport->GetTickOp();
	if(out != 0)
	{
		*out=s_tick;
	}
	*/

	CaplDataBuf dbuf_in, dbuf_out;
	dbuf_in.Add(&id,4);
	dbuf_in.Add(&id2,4);
	//?? int m_long = 0;

	m_AplTransport->AT_DCTest(aplError, dbuf_in, dbuf_out);

	// Очищаем буфер в транспорте
	m_AplTransport->ClearInternalData();

	return aplError == APL_NET_OK;
}


bool CaplNetStepData::CheckModified()
{

	int i;
	bool modified = false;
	for(i=0; i <instances.Size; i++)
	{
		CaplInstance *inst = instances[i];
		modified = inst->IsChanged();
		if(modified) break;
	}
	return modified;
}

bool CaplNetStepData::IsAdmin()
{
	if(m_CurDataFile != _T("")) return true;
	if(m_CurrUser == 0) return false;
	// А не администратор ли наш юзер?
	if(m_CurrUser->GetId() == DEFAILT_ID_ADMIN_USER)
	{
		// админ!
		return true;
	}
	// А может, в группе админов?
	aplExtent ext;
	CaplInstance* user;

	GetAttr(m_CurrUser, m_attr_apl_user__grop, ext);

	for(int i=0; i <ext.GetSize(); i++)
	{
		user = ext.GetAt(i);
		if(user->GetId() == DEFAILT_ID_ADMIN_GROUP )
		{
			return true;
		}
	}
	// А может, запущен режим администратора?
	if(GetAdminMode()) return true;

	return false;
}



bool CaplNetStepData::TestNeedRestore()
{
	bool ret = false;
	int result;
	CHECK_BD(false);

	if(IsConnected() && m_AplTransport != 0)
	{
		// Судя по верхнему уровню - соединены
		if(!m_AplTransport->IsConnected())
		{
			CaplStackLogger stack_logger(__APL_FUNC__);
			// а на нижнем уровне связи нет. Связь была разорвана некорректно. А как данные?
			if(CheckModified())
			{
				if(m_hide_mode)
				{
					result = IDNO;
				}
				else
				{
					result = MessageBox(m_hwnd, 
						APL_T("Предыдущее соединение с сервером было прервано некорректно.\n При обрыве соединения остались несохраненные данные.\n Попытаться восстановить предыдущее соединение?"),
						APL_T("Служба RPC PDM Step Suite"),MB_YESNO);
				}
				if(result == IDYES)
				{
					ret = m_AplTransport->Reconnect(false,_T(__FILE__),__LINE__,_T(__DATE__));
					if(ret)
					{
						return true;
					}
					if(m_hide_mode)
					{
						result = IDNO;
					}
					else
					{
						result = MessageBox(m_hwnd,
							 APL_T("Восстановить предыдущее соединение не удалось. При новом соединении\n несохраненные данные будут утеряны. Для установления нового соединения\n Нажмите \"Да\". Для отказа от нового соединения нажмите \"Нет\""),
							 APL_T("Служба RPC PDM Step Suite"),MB_YESNO);
					}
					if(result == IDNO)
					{
						return true;
					}
				}
			}
		}
	}
	return ret;
}




/** Загружает с сервера и отображает информацию о дате создания/изменения и пользователе, 
создавшем/изменившем указанные <B>instance</B>
@param ext_in список <B>instance</B>, для которых запрашивается информация
@param comments массив комментариев (любая текстовая информация) к каждому instance. 
Массив может быть меньше размера экстента или параметр может быть равен 0
@param wait_text сообщение, которое будет выводиться в диалоге ожидания ответа сервера. При пустом значении выводится сообщение по умолчанию
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::NET_ViewInstUserDataTimeInfo(aplExtent &ext_in, CArray<CString,CString> *comments, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	int i;
	CaplTAggr<SInstInfo*,SInstInfo*,APLAGGR_AUTOKILLREF> data;
	SInstInfo* el;
	bool res;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{
		TEST_RECONNECT(false);

		if(ext_in.GetSize() == 0)
		{
			return false;
		}
		for(i=0; i < ext_in.GetSize(); i++)
		{
			if(ext_in.GetAt(i) == 0) continue;
			if(ext_in.GetAt(i)->GetId() == 0) continue;
			el = new SInstInfo(ext_in.GetAt(i));
			data.Add(el);
		}
		if(data.GetSize() == 0) return false;
		res = NET_LoadInstUserDataTimeInfo(data, wait_text);
		if(res)
		{
			// выводим диалог с табличкой
			CInstProperties dlg;
			dlg.m_inst_data = &data;
			dlg.m_comments = comments;
			dlg.m_data = this;
			dlg.DoModal();
		}
	}
	CATH_MEMORY_ANY_EXEPTION;
	return true;
}

/** Загружает с сервера информацию о дате создания/изменения и пользователе, 
создавшем/изменившем указанные <B>instance</B>
@param data массив структур, содержащий инстансы для которых запрашивается информация, и в
в который помещаются результаты
@param wait_text сообщение, которое будет выводиться в диалоге ожидания ответа сервера. При пустом значении выводится сообщение по умолчанию
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки */
bool CaplNetStepData::NET_LoadInstUserDataTimeInfo(CaplTAggr<SInstInfo*,SInstInfo*,APLAGGR_AUTOKILLREF> &data, LPCTSTR wait_text)
{
	CaplStackLogger logger(__APL_FUNC__);

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	CaplDataBuf dbuf_in, dbuf_out;
	AplNetErrors aplError = APL_NET_OK;
	CaplInstance* inst;
	int i;
	UINT32 id_b;
	USHORT type_id_b;
	DATE date;
	SInstInfo* el;
	bool res;

	aplExtent ext_users;

	try{

		TEST_RECONNECT(false);

		LogDebugMessageIfDef(_T("NET_LoadInstUserDataTimeInfo "));

		//Запрашиваем список юзеров
		LoadAllUsers(ext_users);

		// заполняем буфер
		// заголовки буферов - однобайтовые! 
		char* header = std_LoadInstUserDataTimeInfoQuery_header;
		dbuf_in.Add(header, (strlen(header) + 1));

		i = data.GetSize();
		dbuf_in.Add(&i, sizeof(i));

		for(i=0; i < data.GetSize(); i++)
		{
			el = data.GetAt(i);
			if(el == 0)
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("В списке пустой указатель на объект"));
				LogDebugMessageIfDef("NET_LoadInstUserDataTimeInfo return false");
				return false;
			}
			inst = el->inst;
			if(inst == 0)
			{
				SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA, APL_T("В списке пустой указатель на объект"));
				LogDebugMessageIfDef("NET_LoadInstUserDataTimeInfo return false");
				return false;
			}
			id_b = inst->GetId();
			if(inst->GetType() == 0)
			{
				//SetAPLError(APL_NET_CLI_MISSED_DATA);
				//return false;
				type_id_b = 0;
			}
			else
			{
				type_id_b = (short)inst->GetType()->id;
			}
			dbuf_in.Add(&id_b, sizeof(id_b));
			dbuf_in.Add(&type_id_b, sizeof(type_id_b));
			char access_mode = inst->GetAccessmode();
			dbuf_in.Add(&access_mode, sizeof(access_mode));
		}
		
		m_AplTransport->SetWaitDlgText(wait_text);

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCLoadInstUserDataTimeInfo(aplError, dbuf_in, dbuf_out));

		// читаем из буфера
		// Заголовок
		// заголовки буферов - однобайтовые! 
		i = strlen(std_LoadInstUserDataTimeInfoData_header)+1;
		header = new char[i];
		dbuf_out.Read(header, i);
		if(CStringA(std_LoadInstUserDataTimeInfoData_header) != header)
		{
			delete header;
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("Неверный заголовок буфера данных"));
			LogDebugMessageIfDef(_T("NET_LoadInstUserDataTimeInfo return false"));
			return false;
		}
		delete header;
		// количество
		dbuf_out.Read(&i);
		if(i != data.GetSize())
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("Неверный формат буфера"));
			LogDebugMessageIfDef(_T("NET_LoadInstUserDataTimeInfo return false"));
			return false;
		}
		bool flag_read = true;
		// в цикле по входному ехктенту
		for(i=0; i <data.GetSize(); i++)
		{
			el = data.GetAt(i);
			inst = el->inst;
			if(flag_read)
				res = dbuf_out.Read(&id_b);
			if(!res)
			{
				m_AplTransport->ClearInternalData();
				return true;			
			}
			if(id_b != inst->GetId())
			{
				if(inst->GetType() == 0)
				{
					flag_read = false;
					LogDebugMessageIfDef(_T("NET_LoadInstUserDataTimeInfo return false"));
					continue;
				}
				else
				{
					SetAPLErrorWithFileInfoEx(APL_NET_CLI_MISSED_DATA_FROM_SERVER, APL_T("В буфере запроса и в выходном буфере не совпадает порядок объектов"));
					LogDebugMessageIfDef(_T("NET_LoadInstUserDataTimeInfo return false"));
					m_AplTransport->ClearInternalData();
					return false;
				}
			}
			flag_read = true;
			// юзер создавший
			dbuf_out.Read(&id_b);
			// поиск соответствующего
			if(id_b != 0)
			{
				aplQFindInstIdInExtent(ext_users, id_b, -1, true, &(el->create_user));
			}
			else
			{
				el->create_user = 0;
			}
			// дата создания-изменения
			dbuf_out.Read(&date, sizeof(DATE));
			el->create_date = date;
			// юзер изменивший
			dbuf_out.Read(&id_b);
			// поиск соответствующего
			if(id_b != 0)
			{
				aplQFindInstIdInExtent(ext_users, id_b, -1, true, &(el->update_user));
			}
			else
			{
				el->update_user = 0;
			}
			// дата создания-изменения
			dbuf_out.Read(&date, sizeof(DATE));
			el->update_date = date;
		}
	}
	CATH_MEMORY_ANY_EXEPTION;

	// Очищаем буфер в транспорте
	m_AplTransport->ClearInternalData();

	LogDebugMessageIfDef(_T("NET_LoadInstUserDataTimeInfo return true"));
	return true;

}//int CaplNetStepData::NET_LoadViewInstUserDataTimeInfo(aplExtent ext_in,CArray<CaplInstance*,CaplInstance*> *ext_user,CArray<DATE,DATE> *dates, bool is_view,CArray<CString*,CString*> *comments, LPCTSTR wait_text)


void CaplNetStepData::Detach()
{
	if(m_AplTransport != 0) m_AplTransport->Detach();
	InterlockedIncrement(&m_changed_thread);
}

void CaplNetStepData::Attach()
{
	if(m_AplTransport != 0) m_AplTransport->Attach();
	InterlockedIncrement(&m_changed_thread);
}

void CaplNetStepData::SetHalt()
{
	if(m_AplTransport != 0) m_AplTransport->SetHalt();
	if(InterlockedExchangeAdd(&m_changed_thread, 0) > 0)
		InterlockedIncrement(&m_external_exit);
}

/** Записывает в лог на сервер сообщение от клиента
@param log_name имя файла клиентского лога (располагается на сервере в каталоге с логом сервера)
@param message сообщение
@param is_oracle если true и сервер ORACLE - дублирует сообщение в таблицу Clients_log (сообщение обрезается до 3000 символов)
@return <b>true</b> при удачном выполнении и <b>false</b> в случае ошибки 
*/
bool CaplNetStepData::NET_WriteLog(CString log_name, CString message, bool is_oracle)
{
	CaplStackLogger logger(__APL_FUNC__);

	if(log_name == _T("") && !is_oracle)return false;
	if(message == _T("")) return false;
	
	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	CaplDataBuf dbuf;
	AplNetErrors aplError = APL_NET_OK;

	try{

		TEST_RECONNECT(false);

		// заголовки буферов - однобайтовые! 
		const char* header = CLIENT2SERVER_LOG_BUF_header;
		dbuf.Add(header,29);
		dbuf.m_ansi_string = m_global_ansi_string;

		dbuf.AddStrBuf(log_name);
		dbuf.AddStrBuf(message);

		int len = is_oracle ? 1 : 0;
		dbuf.Add(&len, 4);

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCWriteToLog(aplError, dbuf));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}


bool CaplNetStepData::NET_ControlAccessDocAndWriteLog(CaplInstance *doc, long &num_readed, CString log_name, CString message, bool is_oracle)
{
	CaplStackLogger logger(__APL_FUNC__);
	int doc_id;
	num_readed = 0;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	CaplDataBuf dbuf;
	AplNetErrors aplError = APL_NET_OK;

	try{
		TEST_RECONNECT(false);

		// заголовки буферов - однобайтовые! 
		const char* header = CLIENT2SERVER_DOC_CONTR_header;
		int len = strlen(CLIENT2SERVER_DOC_CONTR_header) + 1;
		dbuf.Add(header, len);

		dbuf.m_ansi_string = m_global_ansi_string;

		if(doc == 0)
		{
			doc_id = 0;
		}
		else
		{
			doc_id = doc->GetId();
		}
		dbuf.Add(&doc_id, 4);
		dbuf.AddStrBuf(log_name);
		dbuf.AddStrBuf(message);
		len = is_oracle ? 1 : 0;
		dbuf.Add(&len, 4);

		UINT32 _num_readed = 0;

		if(!m_AplTransport->AT_DCControlAccessDoc(aplError, dbuf, _num_readed) || SPEC_ERR_CODES(aplError))
		{
			if(aplError == APL_SOCK_MISSED_FUNCTION || aplError == APL_SOCK_MISSED_FUNCT_PARAM)
			{
				// сервер приложений старый, в нем нет нужного функционала
				m_LastAplErrorCode = aplError;
			}
			else if(aplError == APL_NET_SRV_MISSED_BASE_VERSION)
			{
				// в оракле нет нужной таблицы, скорее всего не обновлены словари
				m_LastAplErrorCode = aplError;
			}
			else if(aplError == APL_NET_SRV_FUNCTION_ONLY_DB)
			{
				// в оракле нет нужной таблицы, скорее всего не обновлены словари
				m_LastAplErrorCode = aplError;
			}
			else
			{
				// еще какая-то ошибка
				SetAPLError(aplError, m_AplTransport->GetDescriptionServError(), _T(__FILE__), __LINE__, __APL_FUNC__);
			}
			m_AplTransport->ClearInternalData();
			return false;
		}
		num_readed = _num_readed;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}


static int staticStdOutThread(CDlg4Log *dlg)
{
	return dlg->DoModal(); 
}



// Принять с клиента новый словарь и запустить обновление словарей посредством запуска БДАдмина
bool CaplNetStepData::NET_RunUpdateDictionary(CString new_dict_path, bool autostart)
{
	CaplStackLogger logger(__APL_FUNC__);

	CString str;
	DWORD dwThreadID;
	HANDLE DlgThread;
	CDlg4Log dlg;
	DWORD res;

	CaplTAggr <CaplInstance*, CaplInstance*, APLAGGR_LIST> ext_users;
	CUIntArray array_ip;
	
	///// здесь надо проверить что юзер - админ!
	if(!IsAdmin())
	{
		AfxMessageBox(APL_T("Для выполнения этой функции необходимо быть администратором БД!"));
		return false;
	}


	///// здесь надо проверить, что к БД подключены только мы
	if(!NET_GetActiveUserList(ext_users, _T(""), &array_ip))
	{
		return false;
	}
	if(ext_users.Size > 1) 
	{
		AfxMessageBox(APL_T("Для выполнения операции отключите от БД всех пользователей!"));
		return false;
	}

	dlg.m_data = this;
	dlg.m_Path2NewDict = new_dict_path;
	dlg.m_autostart = autostart;

	DlgThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)staticStdOutThread, &dlg, 0, &dwThreadID);

	HANDLE h_arr[2];
	h_arr[0] = dlg.m_event_run;
	h_arr[1] = DlgThread;

	do 
	{
		res = WaitForMultipleObjects(2, h_arr, FALSE, 100);
		if(WAIT_OBJECT_0 == res )
		{
			// нажали кнопочку "run"
			NET_RunUpdateDictionaryInt(dlg.m_Path2NewDict);
			InterlockedExchange(&dict_update_runned, 0);

			m_AplTransport->Reconnect(false);
		}
		else if(WAIT_OBJECT_0 + 1 == res )
		{
			// нить завершилась, диалог закрылся. Нам тоже делать нечего, выходим
			break;
		}
		else if(WAIT_ABANDONED_0 == res )
		{
			// Среди event-ов есть некорректныt/ Скорее всего, нить завершилась. Нам тоже делать нечего, выходим
			break;
		}
		else if(res == WAIT_TIMEOUT)
		{
			// Продолжаем ждать
		}
		else if(res == WAIT_FAILED)
		{
			GetDescriptionSystemError(GetLastError(), str);
			AfxMessageBox( APL_T("Ожидание прервано из-за ошибки: ") + str);
			break;
		}
		else if(m_AplTransport->IsConnected())
		{
		}

	} while (true);

	return true;

}

bool _ApplicationLevelLogMessageDlg(LPCTSTR message);

bool _ApplicationLevelLogMessage2Log(LPCTSTR message)
{
	return m_MainLog.LogMessage(message);
}

// Принять с клиента новый словарь и запустить обновление словарей посредством запуска БДАдмина
bool CaplNetStepData::NET_RunUpdateDictionaryInt(CString new_dict_path)
{
	CaplStackLogger logger(__APL_FUNC__);

	CaplDataBuf dbuf_in;
	AplNetErrors aplError = APL_NET_OK;
	CFile file;
	int len;
	char* cbuf = 0;

	if(new_dict_path == _T("")) return false;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		// заголовки буферов - однобайтовые! 
		const char* header = std_DCRunUpdateDictionary_header;
		dbuf_in.Add(header, strlen(std_DCRunUpdateDictionary_header) + 1);

		if(file.Open(new_dict_path, CFile::modeRead) == 0)
		{
			return false;
		}
		// словарь тоже идет в однобайтовой кодировке
		len = (int) file.GetLength();
		cbuf = new char[len];
		file.Read(cbuf,len);

		dbuf_in.Add(&len,4);
		if(len>0)
		{
			dbuf_in.Add(cbuf,len);
		}
		delete[] cbuf;cbuf = 0;
		
		m_AplTransport->SetManageMode(MANAGE_ABSENT);

		/////// Отладка окошка лога
	/*
		CString str;
		for(int i=0; i <3000; i++)
		{
			str.Format("AAAAA111\r\nAAAAA222\r\nAAAAA333\r\nAAAAA444\r\nAAAAA555\r\n абвгдежз %i\r\n",i);
			_ApplicationLevelLogMessage(str);
			Sleep(200);
		}
	*/
		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCRunUpdateDictionary(aplError, dbuf_in));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}

// Запустить на сервере указанное приложение
bool CaplNetStepData::NET_RunApp(CString app_name, CString cmd_line, bool print2log)
{
	CaplStackLogger logger(__APL_FUNC__);

	CaplDataBuf dbuf_in;
	AplNetErrors aplError = APL_NET_OK;
	CFile file;
	int len;

	if(app_name == _T("")) return false;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		// заголовки буферов - однобайтовые! 
		const char* header = std_DCRunUpdateDictionary_header;
		dbuf_in.Add(header, strlen(std_DCRunUpdateDictionary_header) + 1);
		dbuf_in.m_ansi_string = m_global_ansi_string;

		dbuf_in.AddStrBuf(app_name);
		dbuf_in.AddStrBuf(cmd_line);

		// пытался передать этот флаг на сервер и управлять видимостью запущенного приложения - но плюнул
		// так как сервер работает службой, и все запущенное им все равно не будет видно
		bool show_window = false;
		len = show_window ? 1 : 0;
		dbuf_in.Add(&len, 4);
		
		m_AplTransport->SetManageMode(MANAGE_ABSENT);

		if(print2log)
		{
			m_AplTransport->SetApplicationLevelLogMessage(_ApplicationLevelLogMessage2Log);
		}
		
		/////// Отладка окошка лога
		/*
		CString str;
		for(int i=0; i <3000; i++)
		{
			str.Format("AAAAA111\r\nAAAAA222\r\nAAAAA333\r\nAAAAA444\r\nAAAAA555\r\n абвгдежз %i\r\n",i);
			_ApplicationLevelLogMessage(str);
			Sleep(200);
		}
		*/
		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCRunApp(aplError, dbuf_in));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}



BOOL AFX_EXT_API ClientPing(LPCTSTR strHost)
{
	return TransportPing(strHost);
}

void CaplNetStepData::SetCheckCurAccess(bool befor_set_access, bool after_set_access)
{
	m_check_cur_access_befor_set_access = befor_set_access;
	m_check_cur_access_after_set_access = after_set_access;
}

void CaplNetStepData::FillConnectionList(CComboBox &SelectConnection, LPCTSTR def_value, int &cur_select_connection)
{
	int i = 0, j = 0;
	CConnectInfo *cif = 0;

	ReadConnectionsInfo();

	SelectConnection.ResetContent();
	for(i=0; i <m_list_connection.GetSize(); i++)
	{
		cif=m_list_connection.GetAt(i);
		if(cif != 0)
		{
			SelectConnection.AddString(cif->name);
			if(cur_select_connection == -1)
			{
				if(cif->name == def_value)
				{
					cur_select_connection = j;
				}
			}
			j++;
		}
	}
	if(cur_select_connection >= m_list_connection.GetSize())
		cur_select_connection = 0;

	if(cur_select_connection != -1)
	{
		SelectConnection.SetCurSel(cur_select_connection);
	}
	else 
	{
		//SelectConnection.SetWindowText(def_value); /ЯАИ: Это не работает в list mode
		SelectConnection.SelectString(0, def_value);
	}
}

void CaplNetStepData::SetSingleAlias(LPCTSTR SingleAlias)
{
	m_SingleAlias = SingleAlias;
}

bool CaplNetStepData::IsVersionOk()
{
	return m_CurSchemaVersion >= (int)m_NeedDictVer;
}

void APL_NET_LogMessage(LPCTSTR message)
{
	m_MainLog.LogMessage(message);
}


// Велит серверу перечитать список шаблонов доступа
bool CaplNetStepData::NET_RefreshAccessPatterns()
{
	CaplStackLogger logger(__APL_FUNC__);

	CaplDataBuf dbuf_in;
	CaplDataBuf dbuf_out;
	int command;
	AplNetErrors aplError = APL_NET_OK;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		// заголовки буферов - однобайтовые! 
		dbuf_in.Add(std_DCServisAccessPatterns_header, strlen(std_DCServisAccessPatterns_header) + 1);
		command = 1;
		dbuf_in.Add(&command, sizeof(command));

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCServiceAccessPatterns(aplError, dbuf_in, dbuf_out));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}


/** Проверяет, не заблокированы ли заданные инстансы другими клиентами. Если нее заблокированы - устанавливает для них блокировку на запись и возвращает <b>true</b>.
Если заблокированы - ждет в течение <b>iMilliseconds</b> миллисекунд, периодически пытаясь повторить блокировку. Если за это время инстанс(ы) не осовбодились - возвращает <b>false</b>
	@param instance - инстанс для блокировки
	@param extent - массив инстансов для блокировки
	@param iMilliseconds - время в милисекундах, которое функция пытается повторить блокировку.
	@return <b>true</b> если инстанс(ы) удачно заблокированы и <b>false</b> если инстанс(ы) уже заблокированы другим клиентским сеансом */
bool CaplNetStepData::NET_LockInstanceForChange(CaplInstance *instance, int iMilliseconds)
{
	CaplStackLogger logger(__APL_FUNC__);

	aplExtent extent;
	if(instance == 0) return false;
	if(instance->GetType() == 0) return false;
	extent.Add(instance);
	return NET_LockInstancesForChange(extent, iMilliseconds);
}

bool CaplNetStepData::NET_LockInstancesForChange(aplExtent &extent, int iMilliseconds)
{
	CaplStackLogger logger(__APL_FUNC__);

	CaplDataBuf dbuf_in;
	AplNetErrors aplError = APL_NET_OK;
	int ival,i;
	int result = 0;
	CaplInstance* instance;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{
	
		TEST_RECONNECT(false);

		if(!CheckSaved(&extent))
		{
			return false;
		}
		// заголовки буферов - однобайтовые! 
		dbuf_in.Add(std_DCLockInstance_header, strlen(std_DCLockInstance_header) + 1);

		ival = extent.GetSize();

		if(iMilliseconds < 50) iMilliseconds = 50;
		dbuf_in.Add(&iMilliseconds, sizeof(iMilliseconds));
		dbuf_in.Add(&ival, sizeof(ival));

		for(i=0; i <extent.GetSize(); i++)
		{
			instance = extent.GetAt(i);
			if(instance == 0) return false;
			if(instance->GetId() == 0)
			{
				return false;
			}
			if(instance->GetType() == 0)
			{
				return false;
			}
			if(instance->GetAccessmode() > aplRW)
			{
				SetLastErrorWithInstFileInfo(APLAPIERR_NOACCESSRIGHT, instance);
				return false;
			}

			ival = instance->GetId();
			dbuf_in.Add(&ival, sizeof(ival));
		}

		if(!m_hide_mode && m_IsWaitDlg)
		{
			m_AplTransport->SetWaitDlgText(APL_T("Выполняется блокировка..."));
		}

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCLockInstance(aplError, dbuf_in, result));
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return (result == 1);
}

// Служебные операции сервера по запросу клиента
bool CaplNetStepData::NET_ServiceOp(BYTE command, CStringArray &list_strings, CaplDataBuf *dbuf_test)
{
	CaplStackLogger logger(__APL_FUNC__);

	CaplDataBuf dbuf_in;
	CaplDataBuf dbuf_out;
	AplNetErrors aplError = APL_NET_OK;
	char* header = 0;
	int i, size_list, size_str;
	CString str;

	list_strings.RemoveAll();

	// заголовки буферов - однобайтовые! 
	dbuf_in.Add(std_DCServiceOp_header, strlen(std_DCServiceOp_header)+1);

	dbuf_in.Add(&command, sizeof(command));

	if(command == 101 && dbuf_test != 0) dbuf_in.Add(dbuf_test->GetBuffer(), dbuf_test->m_Size);

	RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCServiceOp(aplError, dbuf_in, dbuf_out));
	
	if(command == 2)
	{
		size_str = strlen(std_DCServiceOp_header)+1;
		header = new char[size_str];
		dbuf_out.Read(header,size_str);
		dbuf_out.m_ansi_string = m_global_ansi_string;

		if(CStringA(std_DCServiceOp_header) != header)
		{
			CaplStringAdapter str_ad((const char*)header);
			str = CString(APL_T("Заголовок пришедшего буфера: ")) + (LPCTSTR)str_ad;
			delete header;
			m_AplTransport->ClearInternalData();
			return false;
		}
		delete header;header = 0;

		dbuf_out.Read(&size_list,4);
		for(i=0; i <size_list; i++)
		{
			dbuf_out.ReadStrBuf(str);
			list_strings.Add(str);
		}
	}
	if(command == 101 && dbuf_test != 0)
	{
		int i1,i2;
		while(dbuf_test->Read(&i1))
		{
			dbuf_out.Read(&i2);
			if(i1 != i2)
			{
				m_AplTransport->ClearInternalData();
				return false;
			}
		}
	}

	m_AplTransport->ClearInternalData();
	return true;
}

// определяет, используется ли на сервере шаблон доступа
bool CaplNetStepData::NET_IsUsedAccessPattern(CaplInstance* access_pattern_inst)
{
	CaplStackLogger logger(__APL_FUNC__);

	CaplDataBuf dbuf_in;
	CaplDataBuf dbuf_out;
	int command, k;
	UINT32 pattern_id;
	AplNetErrors aplError = APL_NET_OK;
	char* header = 0;
	int i;

	if(access_pattern_inst == 0) return false;
	if(access_pattern_inst->GetType() == 0) return false;
	if(!IsKindOf(access_pattern_inst, m_ent_apl_access_pattern)) return false;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);
		
		// заголовки буферов - однобайтовые! 
		dbuf_in.Add(std_DCServisAccessPatterns_header,strlen(std_DCServisAccessPatterns_header)+1);
		command = 2;
		dbuf_in.Add(&command, sizeof(command));
		
		k = 1;
		dbuf_in.Add(&k, sizeof(k));

		pattern_id = access_pattern_inst->GetId();
		dbuf_in.Add(&pattern_id, sizeof(pattern_id));

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCServiceAccessPatterns(aplError, dbuf_in, dbuf_out));

		// Заголовок
		// заголовки буферов - однобайтовые! 
		k = strlen(std_DCServisAccessPatterns_header)+1;
		header = new char[k];
		dbuf_out.Read(header, k);
		if(CStringA(std_DCServisAccessPatterns_header) != header)
		{
			delete[] header;header = 0;
			MY_THROW(APL_NET_SRV_MISSED_BUF_FORMAT);
		}
		delete[] header;header = 0;

		// Читаем команду
		dbuf_out.Read(&command, sizeof(command));
		dbuf_out.Read(&k, sizeof(k));

		if(k!=1)
		{
			MY_THROW(APL_NET_SRV_MISSED_DATA);
		}

		for(i=0; i <k; i++)
		{
			dbuf_out.Read(&pattern_id, sizeof(pattern_id));
			if(pattern_id != access_pattern_inst->GetId())
			{
				MY_THROW(APL_NET_SRV_MISSED_DATA);
			}
			dbuf_out.Read(&pattern_id, sizeof(pattern_id));
		}
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		return false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return pattern_id == 1;

}

// определяет, используется ли на сервере шаблоны доступа из списка
// в массиве used возвращается? 1 если шаблон с таким индексом используется, 0 - если не используется
bool CaplNetStepData::NET_IsUsedAccessPatterns(aplExtent &ext_access_pattern_inst, CUIntArray &used)
{
	CaplStackLogger logger(__APL_FUNC__);

	CaplDataBuf dbuf_in;
	CaplDataBuf dbuf_out;
	int command, k;
	UINT32 pattern_id;

	AplNetErrors aplError = APL_NET_OK;
	char* header = 0;
	int i;
	CaplInstance* inst;

	used.RemoveAll();
	if(ext_access_pattern_inst.GetSize() == 0)return false;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		// заголовки буферов - однобайтовые! 
		dbuf_in.Add(std_DCServisAccessPatterns_header, strlen(std_DCServisAccessPatterns_header)+1);
		
		command = 2;
		dbuf_in.Add(&command, sizeof(command));

		k = ext_access_pattern_inst.GetSize();
		dbuf_in.Add(&k, sizeof(k));
		
		for(i=0; i <k; i++)
		{
			inst = ext_access_pattern_inst[i];
			if(inst == 0) return false;
			if(inst->GetType() == 0) return false;
			if(!IsKindOf(inst, m_ent_apl_access_pattern)) return false;

			pattern_id = inst->GetId();
			dbuf_in.Add(&pattern_id, sizeof(pattern_id));

		}

		RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCServiceAccessPatterns(aplError, dbuf_in, dbuf_out));

		// Заголовок
		// заголовки буферов - однобайтовые! 
		k = strlen(std_DCServisAccessPatterns_header)+1;
		header = new char[k];
		dbuf_out.Read(header, k);
		if(CStringA(std_DCServisAccessPatterns_header) != header)
		{
			delete[] header;header = 0;
			MY_THROW(APL_NET_SRV_MISSED_BUF_FORMAT);
		}
		delete[] header;header = 0;

		// Читаем команду
		dbuf_out.Read(&command, sizeof(command));
		dbuf_out.Read(&k, sizeof(k));

		if(k != ext_access_pattern_inst.GetSize())
		{
			MY_THROW(APL_NET_SRV_MISSED_DATA);
		}

		for(i=0; i <k; i++)
		{
			dbuf_out.Read(&pattern_id, sizeof(pattern_id));
			if(pattern_id!=ext_access_pattern_inst[i]->GetId())
			{
				MY_THROW(APL_NET_SRV_MISSED_DATA);
			}
			dbuf_out.Read(&pattern_id, sizeof(pattern_id));
			used.Add(pattern_id);
		}
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		return false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;

}

// определяет, используется ли на сервере шаблоны доступа из списка
// в массиве used возвращается? 1 если шаблон с таким индексом используется, 0 - если не используется
bool CaplNetStepData::NET_WherePatternUsed(CaplInstance* access_pattern_inst, CUIntArray &users)
{
	CaplStackLogger logger(__APL_FUNC__);

	CaplDataBuf dbuf_in;
	CaplDataBuf dbuf_out;
	int command, k;
	UINT32 pattern_id;
	apl_id instance_id;

	AplNetErrors aplError = APL_NET_OK;
	char* header = 0;
	int i;

	users.RemoveAll();
	if(access_pattern_inst == 0)return false;
	if(access_pattern_inst->GetId() == 0)return false;
	if(access_pattern_inst->GetType() == 0)return false;

	CHECK_BD(false);
	CHECK_STATE(true, true, false);	

	try{

		TEST_RECONNECT(false);

		// заголовки буферов - однобайтовые! 
		dbuf_in.Add(std_DCServisAccessPatterns_header, strlen(std_DCServisAccessPatterns_header)+1);

		command = 3;
		dbuf_in.Add(&command, sizeof(command));

		k = 1;
		dbuf_in.Add(&k, sizeof(k));
		pattern_id = access_pattern_inst->GetId();
		dbuf_in.Add(&pattern_id, sizeof(pattern_id));

		//RETURN_IF_OR_CHECK_ERR(!m_AplTransport->AT_DCServiceAccessPatterns(aplError, dbuf_in, dbuf_out));
		if(!m_AplTransport->AT_DCServiceAccessPatterns(aplError, dbuf_in, dbuf_out))
		{
			// старый сервер не знает кода 3 и возвращает APL_NET_SRV_MISSED_DATA.
			if(aplError == APL_NET_SRV_MISSED_DATA)aplError = APL_SOCK_MISSED_FUNCTION;
			if(aplError == APL_NET_SRV_MISSED_DATA)aplError = APL_NET_SRV_DICTIONARY_NEEDS_TO_BE_UPDATED;

			SetAPLErrorWithFileInfo(aplError);
			if(aplError == APL_NET_SRV_APLAPIERR_FILE_BD_READ_ONLY){m_is_readonly = true;}
			if(aplError != APL_SOCK_OPERATION_IN_PROGRESS){m_AplTransport->ClearInternalData();}
			LogDebugMessageIfDef( CString(APL_T("Ошибка в %s")), __APL_FUNC__);
			return false;
		}

		// Заголовок
		// заголовки буферов - однобайтовые! 
		k = strlen(std_DCServisAccessPatterns_header)+1;
		header = new char[k];
		dbuf_out.Read(header, k);
		if(CStringA(std_DCServisAccessPatterns_header) != header)
		{
			delete[] header;header = 0;
			MY_THROW(APL_NET_SRV_MISSED_BUF_FORMAT);
		}
		delete[] header;header = 0;

		// Читаем команду
		dbuf_out.Read(&command, sizeof(command));
		// Количество шаблонов
		dbuf_out.Read(&k, sizeof(k));

		if(k != 1)
		{
			MY_THROW(APL_NET_SRV_MISSED_DATA);
		}
		// Минимальная проверка
		dbuf_out.Read(&pattern_id, sizeof(pattern_id));
		if(pattern_id!=access_pattern_inst->GetId())
		{
			MY_THROW(APL_NET_SRV_MISSED_DATA);
		}
		// Количество инстансов
		dbuf_out.Read(&k, sizeof(k));

		for(i=0; i <k; i++)
		{
			dbuf_out.Read(&instance_id, sizeof(instance_id));
			users.Add(instance_id);
		}
	}
	catch(SaplErrorDescription error)
	{
		SetAPLError(error);
		return false;
	}
	CATH_MEMORY_ANY_EXEPTION;

	m_AplTransport->ClearInternalData();

	return true;
}



// Возвращает строку временного пароля для дублицирования сессии
bool CaplNetStepData::NET_GetString4DupSession(CString &spetial_string)
{
	CaplStackLogger logger(__APL_FUNC__);

	return m_AplTransport->AT_DCGetString4DupSession(spetial_string);
}



bool CaplNetStepData::SetModeSetAccessWithSave(bool mode)
{
	bool old_flag = m_SetAccessWithSave;
	m_SetAccessWithSave = mode;
	return old_flag;
}

UINT CaplNetStepData::RenewClientStateOnServer()
{
	return ::RenewClientStateOnServer((LPVOID)m_AplTransport);
}

bool CaplNetStepData::SetApplicationCloseFunction(long (__cdecl *_application_close_funct)(LPVOID), LPCTSTR message)
{
	if(m_AplTransport == 0) return false;
	m_AplTransport->m_application_close_funct = _application_close_funct;
	if(message != 0)m_AplTransport->m_application_reconnect_text = message;
	return true;
}

//***************************************************************************************************
bool CaplNetStepData::TestCycleInRelation(CaplEntity *entity, CaplAttr *in_attr, CaplAttr *out_attr,
									   CaplInstance *in_val, CaplInstance *out_val)
{
	if(entity == 0) return false;
	if(in_attr == 0) return false;
	if(out_attr == 0) return false;
	if(in_val == 0) return false;
	if(in_val->GetType() == 0) return false;
	if(out_val== 0) return false;
	if(out_val->GetType() == 0) return false;

	if(in_val == out_val) return false;

	if(!CaplStepData::TestCycleInRelation(entity, in_attr, out_attr, in_val, out_val)) return false; // сначала по кэшу

	aplExtent ext0, ext1;
	int i;
	CaplInstance *inst0, *inst1;

	ext0.Unique = false;
	ext0.Add(in_val);

	CaplMap map; // для контроля зацикливания

	CaplLoadData ld(this);
	while (ext0.GetSize() > 0)
	{
		ld.ClearQuery();
		for(i=0; i <ext0.GetSize(); i++)
		{
			if(ext0[i]->GetId() == 0) continue;
			ld.AddQuery(0, ext0[i]);
		}
		if(ld.m_queries.GetSize() == 0) return true;
		ld.AddQuery( _T('r'), 0, entity, out_attr, true, true);
		ld.LoadEx(true, &ext1);
		ext0.Clear();
		for(i=0; i <ext1.GetSize(); i++)
		{
			inst0 = ext1[i];
			if(!IsKindOf(inst0, entity)) continue;
			GetAttr(inst0, in_attr, inst1);
			if(inst1 == out_val) 
				return false; // нашли цикл

			if(0 == inst1) continue;
			if(0 == inst1->GetId()) continue;

			if(map.QFindByIn((long)inst1) < 0) //такой еще не проверяли
			{
				ext0.Add(inst1);
				map.Add((long)inst1, 0);
			}
		}
	}
	return true;
}



bool AFX_EXT_API aplReplaceInstance(CaplNetStepData *data, CaplInstance *inst1, CaplInstance *inst2, bool bUseSelect, bool bAutoRun, bool bAutoHide)
{
	if(0 == data || 0 == inst1 || 0 == inst2) return false;
	
	if(!data->IsConnected()) {AfxMessageBox(APL_T("Соединение с БД не установлено!"), MB_OK|MB_ICONSTOP); return false;}
	if(!data->IsAdmin()) {AfxMessageBox(APL_T("Нет прав администратора!"), MB_OK|MB_ICONSTOP); return false;}

	CaplSetResourceHandle res_handle(module_inst);
	CReplaceInstDlg dlg;
	dlg.m_inst1 = inst1;
	dlg.m_inst2 = inst2;
	dlg.m_data = data;
	dlg.m_bCheckSelect = bUseSelect;
	dlg.m_bAutoRun = bAutoRun;
	dlg.m_bAutoHide = bAutoHide;

	dlg.DoModal();

	return dlg.m_bResult;
}

bool AFX_EXT_API aplFindAllInstanceRef(CaplNetStepData *data, CaplInstance *inst1, bool bUseSelect, aplExtent *ext_with_ref, CString *sReport)
{
	if(0 != ext_with_ref) ext_with_ref->Clear();
	if(0 != sReport) *sReport = _T("");
	if(0 == data) return false;
	if(0 == inst1) return false;
	if(0 == inst1->GetId()) return false;
	if(0 == inst1->GetType()) return false;
	if((0 == ext_with_ref) && (0 == sReport)) return false;

	long waitdlg = aplStartWaitDlg(APL_T("Поиск ссылок"),APL_T("Поиск ссылок"));

	bool bOldUnique = true;
	if(0 != ext_with_ref) {bOldUnique = ext_with_ref->Unique; ext_with_ref->Unique = false;}

	int i,j,k;
	int iCountAttr = 0, iCountInstWithRef = 0;

	CString buf,sAllRef;
	CaplEntity *ent_inst1 = inst1->GetType();

	buf.Format(APL_T("Ссылки на #%i (%s)\r\n\r\n"), inst1->GetId(), inst1->GetType()->name);
	*sReport += buf;

	for(i=0; i <data->entities.GetSize(); i++)
	{
		CaplEntity *ent = data->entities[i];
		for(j = 0; j < ent->attrs.GetSize(); j++)
		{
			CaplAttr *attr = ent->attrs[j];

			bool bCheck = false;

			if(attr->type == aplINSTANCE)
			{
				//проверяем типы
				CaplEntity *ent_t = attr->inst_type;
				if(0 == ent_t)bCheck = true; // на всякий случай
				else 
				{
					if(ent_t == ent_inst1) bCheck = true;
					else
					{
						for(k = 0;k<ent_t->all_subtypes.GetSize();k++)
						{
							if(ent_inst1 == ent_t->all_subtypes[k])
							{ 
								bCheck = true;
								break;
							}
						}
					}
				}
			}
			else if(attr->type == aplSELECT && bUseSelect ) bCheck = true;
			else if(attr->type == aplAGGR && bUseSelect) { bCheck = true;}

			if( !bCheck) continue;
			
			iCountAttr++;
			buf.Format(_T("Entity: %i/%i  %s.%s\nНайдено %i"), i, data->entities.GetSize(), LPCTSTR(ent->name), LPCTSTR(attr->name), iCountInstWithRef);
			aplSetTextWaitDlg(waitdlg,buf);

			aplExtent ext;
			CaplLoadData ld(data, DEF_SOURCE);
			ld.AddQuery(0, _T('b'), inst1->GetId(), ent, attr, true, false);
			ld.LoadEx(true, &ext);
			//buf.Format(" Найдено %i.", ext.GetSize());

			if(ext.GetSize()>0)
			{
				iCountInstWithRef += ext.GetSize();
				if(0 != ext_with_ref) ext_with_ref->Append(ext);
				if(0 != sReport)
				{
					buf.Format(_T("%s:%s {"), LPCTSTR(ent->name), LPCTSTR(attr->name));
					*sReport += buf;
					for(k = 0; k<ext.GetSize(); k++)
					{
						if(k != 0) *sReport += _T(", ");
						buf.Format(_T("#%i"), ext[k]->GetId());
						 *sReport += buf;

						 if(sAllRef != _T("")) sAllRef += _T(", ");
						 sAllRef += buf;
					}
					*sReport += _T("}\r\n");
				}
			}
		}
	}
	if(0!=sReport)
	{
		if(0==iCountInstWithRef) 
			*sReport = APL_T("Ссылок не найдено");
		else
		{
			buf.Format(APL_T("\r\n Итого обработано %i атрибутов.  Найдено %i ссылок\r\n\r\nВсе ссылки: {"), iCountAttr, iCountInstWithRef);
			*sReport += buf;
			*sReport += sAllRef;
			*sReport += _T("}\r\n");
		}
	}
	if(0 != ext_with_ref) { ext_with_ref->Unique = bOldUnique;}
	aplEndWaitDlg(waitdlg);
	return true;
}


void CaplNetStepData::ShowInstancesDebugInfo(CaplInstance *inst1, CaplInstance *inst2, CaplInstance *inst3, CaplInstance *inst4, CaplInstance *inst5)
{
	// ЯАИ Переменное число аргументов было делать лениво, да и пусть типы при компиляции проверяет.
	CString str, buf; 
	PrintInstanceDebugInfo(inst1, str);
	if(0 != inst2) {PrintInstanceDebugInfo(inst2,buf); str+=_T("\n\n"); str+=buf;}
	if(0 != inst3) {PrintInstanceDebugInfo(inst3,buf); str+=_T("\n\n"); str+=buf;}
	if(0 != inst4) {PrintInstanceDebugInfo(inst4,buf); str+=_T("\n\n"); str+=buf;}
	if(0 != inst5) {PrintInstanceDebugInfo(inst5,buf); str+=_T("\n\n"); str+=buf;}
	AfxMessageBox(str,MB_OK|MB_ICONINFORMATION);
}


void CaplNetStepData::ShowExtentDebugInfo(aplExtent  *ext)
{
	CString str, str1;
	if(0 == ext) str = _T("ext = 0");
	else
	{
		str.Format(_T("Ext  %i элементов"), ext->GetSize());
		for(int i=0; i <ext->GetSize(); i++)
		{
			str += _T("\n\n");
			PrintInstanceDebugInfo((*ext)[i],str1);
			str += str1;
		}
	}
	AfxMessageBox(str,MB_OK|MB_ICONINFORMATION);
}

void CaplNetStepData::PrintInstanceDebugInfo(CaplInstance *inst, CString &str)
{
	str = _T("");
	CString buf;
	if(0==inst) {str= _T("Inst  = 0"); return;}

	str.Format(_T("0x%p   id=%i "), inst, inst->GetId());

	str += _T("   type=");
	CaplEntity *ent = inst->GetType();
	if (0 == ent) str+= _T("0"); else str+=ent->name;

	str += _T("   access=");
	aplAccessModeType access = inst->GetAccessmode();
	switch(access)
	{
	case aplOWN: str +=_T("OWN"); break;
	case aplRW:  str +=_T("RW"); break;
	case aplRO:  str +=_T("RO"); break;
	default: str +=_T("NOT_DEF"); break;
	}

	CaplTAggr<SInstInfo*,SInstInfo*,APLAGGR_AUTOKILLREF> data;
	SInstInfo *el = new SInstInfo(inst);
	data.Add(el);
	if(NET_LoadInstUserDataTimeInfo(data))
	{
		COleDateTime odt;
		str += APL_T("\n   Создан:  ");

		if(el->create_date == 0) buf += APL_T("(неизвестно когда)");
		else
		{
			odt = el->create_date;
			buf = odt.Format( APL_T(" %d.%m.%Y  %H:%M:%S "));
		}
		str += buf;

		str += APL_T(" Пользователь: ");
		if(el->create_user == 0) buf = APL_T("возможно удален");
		else 	GetAttrBN(el->create_user, _T("name"), buf);
		str += buf;


		str += APL_T("\n   Изменен: ");
		
		if(el->update_date == 0) buf += APL_T("(неизвестно когда)");
		else
		{
			odt = el->update_date;
			buf = odt.Format( APL_T(" %d.%m.%Y  %H:%M:%S "));
		}
		str += buf;

		str += APL_T(" Пользователь: ");
		if(el->update_user == 0) buf = APL_T("возможно удален");
		else 	GetAttrBN(el->update_user, _T("name"), buf);
		str += buf;

		str += _T("\n");
	}

	if(0 == ent) return;
	if(0 == inst->attrs) {str += APL_T("\n   Атрибуты не зачитаны"); return;}

	int i;
	for(i=0; i <ent->all_attrs.GetSize(); i++)
	{
		str += _T("\n   ");
		str += ent->all_attrs[i]->name;
		str += _T("=");
		inst->attrs[i].value.PrintDebugInfo(buf);
		str += buf;
	}
}

bool CaplNetStepData::PrintQuery(CString &query_lng_out)
{
	if(m_query == 0)
	{
		query_lng_out = _T("");
	}
	else
	{
		return m_query->PrintQuery(query_lng_out);
	}
	return false;
}

/* Устарело. Надо использовать пару функций SaveDictionaryToMemory(dBuf) - LoadDictionaryFromMemory(dBuf);
void CaplNetStepData::GetFullDictName(CString &FullDictName)
{
	if(m_FullDictName == _T(""))
	{
		TCHAR* TempDir = NULL;
		CString dict_path_name;
		DWORD result = 0;
		DWORD nSize = MAX_STR_LEN;

		TempDir = new TCHAR[nSize];
		result = GetTempPath(nSize, TempDir);
		if(result > nSize)
		{
			delete TempDir;
			nSize = result;
			TempDir = new TCHAR[nSize];
			result = GetTempPath(nSize,TempDir);
		}
		// Исключаем из имени базы кривые символы
		m_NameCurrBase.Remove(_T('/'));
		m_NameCurrBase.Remove(_T('@'));
		m_NameCurrBase.Remove(_T('*'));

		//		dict_path_name=_T(TempDir);
		//		dict_path_name+=m_NameCurrBase;
		//		dict_path_name+=".dict";
		servGetFreeFileName(TempDir, m_NameCurrBase, _T("dict"), dict_path_name);
		m_FullDictName = dict_path_name;

		if(!SaveDictionaryToFile(m_FullDictName))
		{
			SetAPLErrorWithFileInfoEx(APL_NET_CLI_ERROR_PROCESSED, APL_T("Ошибка сохранения словаря!"));
			m_FullDictName = _T("");
		}

		if(TempDir != NULL) delete TempDir;			
	}
	FullDictName = m_FullDictName;
}*/

bool CaplNetStepData::RecoveryInst(CaplInstance* ciInst)
{
	if (ciInst == NULL)
		return false;

	int iFind = m_SavedDeleted.Find(ciInst);
	if (iFind > -1)
	{
		return m_SavedDeleted.Remove(iFind);
	}

	return false;
}

void CaplNetStepData::ForceAutoReconnect()
{
	forced_AutoReconnect = true;
}

bool CaplNetStepData::SetGlobalProcessWmPaint(bool is_process)
{
	bool prev_val = global_process_wm_paint;
	global_process_wm_paint = is_process;
	CaplSocketClient::SetGlobalProcessWmPaint(global_process_wm_paint);
	CaplStepData::SetGlobalProcessWmPaint(global_process_wm_paint);
	guiSetGlobalProcessWmPaint(global_process_wm_paint);
	return prev_val;
}




bool SendSimpleEMail(CString &err_mess, LPCTSTR server, LPCTSTR recipient_email, 
						LPCTSTR sender_email, LPCTSTR subject, LPCTSTR message)
{
	CString Serv = _T("HELO ");
	Serv += server;
	Serv += _T("\r\n");
	CString From = _T("MAIL FROM:<");
	From += sender_email;
	From += _T(">\r\n");
	CString To = _T("RCPT TO:<");
	To += recipient_email;
	To += _T(">\r\n");
	CString Data ;

	Data = _T("Subject: ");
	Data += subject;
	Data += _T("\r\n");
	Data += _T("Message-ID: <");
	CString guid = _T("");

	err_mess = _T("");

	::CoInitialize(NULL);
	GUID pGuid = GUID_NULL;
	::CoCreateGuid(&pGuid);
	if(pGuid!=GUID_NULL)
	{
		guid.Format(_T("%08lX-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X"),
			pGuid.Data1, pGuid.Data2, pGuid.Data3,
			pGuid.Data4[0], pGuid.Data4[1], pGuid.Data4[2], pGuid.Data4[3],
			pGuid.Data4[4], pGuid.Data4[5], pGuid.Data4[6], pGuid.Data4[7]);
	}
	else
	{
		guid.Format(_T("%i_%i_%i"), GetCurrentProcessId(), GetCurrentThreadId(), GetTickCount());
	}

	::CoUninitialize();

	Data += guid + _T(">\r\n");

	Data += _T("To: <");
	Data += recipient_email;
	Data += _T(">\r\n");

	Data += _T("From: <");
	Data += sender_email;
	Data += _T(">\r\n");
	
	Data += _T("User-Agent: PSS|3.x\r\n");
	Data += _T("MIME-Version: 1.0\r\n");
	Data += _T("Content-Type: text/plain; charset=windows-1251; format=flowed\r\n");
	Data += _T("Content-Transfer-Encoding: 8bit\r\n");

	Data += _T("\r\n");
	Data += message;
	Data += _T("\r\n\r\n.\r\n");

	
	CStringArray MailMessage; 
	
	MailMessage.Add(Serv);
	MailMessage.Add(From);
	MailMessage.Add(To);
	MailMessage.Add(_T("DATA\r\n"));
	MailMessage.Add(Data);
	MailMessage.Add(_T("QUIT\r\n"));

	
	WSADATA	Wsa;
	
	int err_code = WSAStartup(0x0101,&Wsa);
	if(err_code != 0)
	{
		GetDescriptionSystemError(err_code, err_mess);
		return false;
	}
// 	SOCKET s = socket(AF_INET,SOCK_STREAM,0);
// 
// 	SOCKADDR_IN	sin;
// 	sin.sin_addr.s_addr = inet_addr(server);
// 	sin.sin_family = AF_INET;
// 	sin.sin_port = htons(25);
	CSocket socket;

//	err_code = connect(s,(LPSOCKADDR)&sin, sizeof(sin));
	BOOL retval = socket.Create();
	if(retval == FALSE)
	{
		GetDescriptionSystemError(GetLastError(), err_mess);
		return false;
	}

	retval = socket.Connect(server, 25);
	if(retval == FALSE /*err_code  == SOCKET_ERROR*/)
	{
		GetDescriptionSystemError(GetLastError(), err_mess);
		return false;
	}

	int iLength = 0;
	int iEnd = 0;
	// почтовые серверы общаются в однобайтных кодировках
	char sBuff[255] = "";
	int iMsg = 0;

	err_mess = _T("");
	do
	{
		//iLength = recv(s,(LPTSTR)sBuff+iEnd, sizeof(sBuff)-iEnd,0);
		iLength = socket.Receive((char*)sBuff + iEnd, sizeof(sBuff)-iEnd, 0);
		if(iLength > 0)
		{
			iEnd += iLength;
			sBuff[iEnd] = '\0';
		}
		//send(s,(LPTSTR)MailMessage[iMsg],strlen(MailMessage[iMsg]),0);
		CaplStringAdapter str_ad((LPCTSTR)MailMessage[iMsg]);
		socket.Send((const char*)str_ad, MailMessage[iMsg].GetLength(), 0);
		iMsg++;
		err_mess += sBuff;
		err_mess += _T("\r\n");

	}while(MailMessage[iMsg]);

	//closesocket(s);
	socket.Close();
	WSACleanup();
	return true;
}

SUserLogInfo* CaplNetStepData::ReadUserLogInfoFromDataBuf(CaplDataBuf &dbuf_out, int i_ver_buf)
{
	int i_val = 0;
	INT64 i64_val = 0;
	DATE date;

	SUserLogInfo* el = new SUserLogInfo();
	
	dbuf_out.m_ansi_string = m_global_ansi_string;

	dbuf_out.Read(&i_val);
	if(i_val != 0)
	{
		el->user = GetInstById(i_val, false);
		el->user_id = i_val;
	}
	if(i_ver_buf >= 3)
	{
		dbuf_out.ReadStrBuf(el->user_name);
	}

	dbuf_out.ReadStrBuf(el->ip_cli);
	dbuf_out.ReadStrBuf(el->ip_srv);

	dbuf_out.Read(&date, sizeof(DATE));
	el->date_login = date;
	dbuf_out.Read(&date, sizeof(DATE));
	el->date_logout = date;

	dbuf_out.Read(&i_val);
	el->exit_status = i_val;

	if(1 == i_ver_buf)
		return el;

	dbuf_out.Read(&i_val);
	el->AUSId = i_val;

	dbuf_out.ReadStrBuf(el->ver_cli);
	dbuf_out.ReadStrBuf(el->ver_srv);
	dbuf_out.ReadStrBuf(el->exe_name);

	dbuf_out.Read(&i_val);
	el->user_id_base4admin = i_val;
	if(i_val != 0)
	{
		el->user_base4admin = GetInstById(i_val,false);
	}
	
	if(i_ver_buf <= 3)
		return el;

	dbuf_out.Read(&i64_val, 8);
	el->count_load = i64_val;

	return el;
}



bool CaplNetStepData::CanProcessed(bool check_connect, bool check_processed, LPCTSTR add_text, LPCTSTR file, int line, LPCTSTR funct_name)
{
	if(check_connect)
	{
		if(m_AplTransport == NULL || !m_IsConnected)
		{
			SetAPLError( APL_NET_CLI_NOT_CONNECT, add_text, file, line, funct_name);
			return false;
		}
	}
	if(check_processed)
	{
		if(m_AplTransport->IsOperated())
		{
			CString mess(_T(""));
			if(!m_AplTransport->m_last_calling.IsEmpty())
			{
				mess = APL_T("Выполняется предыдущий вызов из функции '") + m_AplTransport->m_last_calling + _T("': ");
			}
			mess +=  add_text;
			SetAPLError(APL_SOCK_OPERATION_IN_PROGRESS, mess, file, line, funct_name);
			return false;
		}

	}
	return true;
}

bool CaplNetStepData::NET_GetListDb(LPCTSTR comp_name, int port, int protocol, LPCTSTR proxy, CStringArray &BaseList)
{
	CaplDataBuf dBuf_tmp,dBuf;
	AplNetErrors aplError;
	CString err_header = APL_T("Ошибка данных");
	/*  - убрано по просьбе Лобанова. Перевод будет выполняться новым механизмом через CaplTranslate.
	if(m_lang_england)
	{
		err_header = _T("Error");
	}*/
	
	BaseList.RemoveAll();

	CConnectInfo cif;
	cif.comp_name = comp_name;
	cif.port = port;
	cif.protocol = protocol;
	cif.proxy = proxy;

	if(cif.comp_name == _T("") || cif.port<1 || cif.port>65536)
	{
		MessageBox(m_hwnd, APL_T("Неверные параметры доступа к серверу"), err_header, MB_OK);
		/*  - убрано по просьбе Лобанова. Перевод будет выполняться новым механизмом через CaplTranslate.
		if(m_lang_england)
		{
			MessageBox(m_hwnd, _T("Server not awailable. Check server parametres"), err_header, MB_OK);
		}
		else
		{
			MessageBox(m_hwnd, APL_T("Неверные параметры доступа к серверу"), err_header, MB_OK);
		}*/
		return false;
	}
	CString lex = _T("");
	CTransportSocketClient* sock=m_AplTransport;
	m_AplTransport = new CTransportSocketClient(this);

	try{
		if(!ConnectWithTestLocal(&cif,false))
		{
			MY_THROW(0);
		}
		m_AplTransport->ABO_DCQueryListBase(aplError,dBuf_tmp);
		dBuf.Clear();
		dBuf.Add(dBuf_tmp.GetBuffer(),dBuf_tmp.m_Size);
		NET_Disconnect();
		if(aplError!=APL_NET_OK)
		{
			MY_THROW(aplError);
		}
		//А теперь читаем. Сначала проверка
		// заголовки буферов - однобайтовые!
		char version[18];
		dBuf.Read(version,18);
		int count_of_base = 0;
		dBuf.m_ansi_string = m_global_ansi_string;

#ifdef _UNICODE
		if(CStringA("BaseListBuf v1.0u")==version) 
		{
			dBuf.m_ansi_string = false;
		}
		else
		{
			if(CStringA("BaseListBuf v1.00")==version) 
			{
				if(!m_hide_mode)
					MessageBox(m_hwnd, ERR_MESS_SERVER_A_CLIENT_U_DEBUG, err_header, MB_OK | MB_ICONINFORMATION);
				dBuf.m_ansi_string = true;
			}
			else
			{
				MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
			}
		}
#else // #ifdef _UNICODE
		if(CStringA("BaseListBuf v1.00")!=version) 
		{
			if(CStringA("BaseListBuf v1.0u")==version) 
			{
				if(!m_hide_mode)
					MessageBox(m_hwnd, ERR_MESS_SERVER_U_CLIENT_A_DEBUG, err_header, MB_OK | MB_ICONINFORMATION);
				dBuf.m_ansi_string = false;

// 				if(m_Ini.GetInt(_T("Options"), _T("AllowUnicodeServer"), 0)!=1)
// 				{
// #ifdef _DEBUG
// 					MessageBox(m_hwnd, ERR_MESS_SERVER_U_CLIENT_A_DEBUG, err_header, MB_OK | MB_ICONINFORMATION);
// 					dBuf.m_ansi_string = false;
// #else
// 					MY_THROW_EX(APL_NET_CLI_MISSED_DATA_FROM_SERVER, ERR_MESS_SERVER_U_CLIENT_A);
// #endif
// 				}
			}
			else
			{
				MY_THROW(APL_NET_CLI_MISSED_DATA_FROM_SERVER);
			}
		}

#endif // #ifdef _UNICODE

		dBuf.Read(&count_of_base, sizeof(count_of_base));
		if(count_of_base== 0)
		{
			MY_THROW(APL_NET_CLI_LIST_OF_BASE_EMPTY);
		}

		//Читаем из буфера список баз
		for(int i = 0;i<count_of_base;i++)
		{
			// Имя базы
			dBuf.ReadStrBuf(lex);
			BaseList.Add(lex);
		}

	}
	catch(SaplErrorDescription err)
	{
		bool old_hide=m_hide_mode;
		m_hide_mode = true;
		SetAPLError(err);
		m_hide_mode=old_hide;
		if(err.m_err_code!=APL_NET_OK)
		{
			MessageBox(m_hwnd, GetLastErrorDescription(), err_header ,MB_OK);
		}
		delete m_AplTransport;
		m_AplTransport=sock;
		return false;
	}
	delete m_AplTransport;
	m_AplTransport=sock;

	return true;
}

bool CaplNetStepData::GetAplUserName(CaplInstance *user, CString &buf)
{
	buf = _T("");
	if(user== 0) return false;
	CaplInstance *inst = 0, *pers = 0;
	if(!IsKindOf(user, m_ent_apl_access_agent))
		return false;

	GetAttr(user, m_attr_apl_user__name, buf);

	if(IsKindOf(user, m_ent_apl_user))
	{
		GetAttr(user, m_attr_apl_user__pers, inst);
		if(inst != 0)
		{
			if(IsKindOf(inst, m_ent_person_and_org))
			{
				GetAttr(inst, m_attr_person_and_org__pers, pers);
			}
			else pers = inst;
		}
		CaplAggr aggr0;
		if(pers != 0)
		{
			CString buf1;
			buf += _T(" (");
			GetAttr(pers, m_attr_person__last_name, buf1);
			buf += buf1;
			GetAttr(pers, m_attr_person__first_name, buf1);
			if(buf1.GetLength()>0) {buf+=_T(" "); buf+=buf1[0]; buf+=_T(".");}
			GetAttr(pers, m_attr_person__middle_names, aggr0);
			aggr0.GetByIndex(0, buf1);
			if(buf1.GetLength()>0) {buf+=_T(" "); buf+=buf1[0]; buf+=_T(".");}
			buf+=_T(")");
		}
	}

	return true;
}