Esempio n. 1
0
bool CSetDlgFonts::StartEnumFontsThread()
{
	_ASSERTE(mh_EnumThread == NULL);
	mb_EnumThreadFinished = false;
	mh_EnumThread = apiCreateThread(EnumFontsThread, NULL, &mn_EnumThreadId, "EnumFontsThread"); // хэндл закроет сама нить
	return (mh_EnumThread != NULL);
}
Esempio n. 2
0
void WindowHandler::Check()
{
	if(!m_Thread || WaitForSingleObject(m_Thread, 0)!=WAIT_TIMEOUT)
	{
		m_Thread = apiCreateThread(nullptr, 0, this, &WindowHandler::WindowThreadRoutine, nullptr, 0, nullptr);
	}
}
Esempio n. 3
0
	void Close()
	{
		if (bTokenInitialized)
		{
			GdiplusShutdown(gdiplusToken);
			bTokenInitialized = false;
		}

		if (hGDIPlus)
		{
			//FreeLibrary(hGDIPlus);
			DWORD nFreeTID = 0, nWait = 0, nErr = 0;
			HANDLE hFree = apiCreateThread(FreeThreadProc, this, &nFreeTID, "gdip::FreeThreadProc");

			if (!hFree)
			{
				nErr = GetLastError();
				FreeLibrary(hGDIPlus);
			}
			else
			{
				nWait = WaitForSingleObject(hFree, 5000);

				if (nWait != WAIT_OBJECT_0)
					apiTerminateThread(hFree, 100);

				CloseHandle(hFree);
			}

			hGDIPlus = NULL;
		}

		if (bCoInitialized)
		{
			bCoInitialized = FALSE;
			CoUninitialize();
		}

		FREE(this);
	};
Esempio n. 4
0
CAttachDlg::AttachMacroRet CAttachDlg::AttachFromMacro(DWORD anPID, bool abAlternative)
{
	MArray<AttachParm> Parms;

	HWND hFind = NULL;
	CProcessData ProcessData;

	while ((hFind = FindWindowEx(NULL, hFind, NULL, NULL)) != NULL)
	{
		if (!IsWindowVisible(hFind))
			continue;

		AttachWndInfo Info = {};
		if (!GetWindowThreadProcessId(hFind, &Info.nPID) || (Info.nPID != anPID))
			continue;

		if (!CanAttachWindow(hFind, 0, &ProcessData, Info))
			continue;

		AttachParm p = {};
		p.hAttachWnd = hFind;
		p.nPID = Info.nPID;
		p.nBits = Info.nImageBits;
		if (lstrcmp(Info.szType, szTypeCon) == 0)
			p.nType = apt_Console;
		else if (lstrcmp(Info.szType, szTypeGui) == 0)
			p.nType = apt_Gui;
		else
			continue;
		p.bAlternativeMode = abAlternative;
		Parms.push_back(p);
	}

	if (Parms.empty())
		return amr_WindowNotFound;
	if (Parms.size() > 1)
		return amr_Ambiguous;

	AttachParm Null = {};
	Parms.push_back(Null);

	// Работу делаем в фоновом потоке, чтобы не блокировать главный
	// (к окну ConEmu должна подцепиться новая вкладка)
	AttachParm* pParm = Parms.detach();
	if (!pParm)
		return amr_Unexpected;

	DWORD nTID = 0;
	HANDLE hThread = apiCreateThread((LPTHREAD_START_ROUTINE)StartAttachThread, pParm, &nTID, "CAttachDlg::StartAttachThread#2");
	if (!hThread)
	{
		//DWORD dwErr = GetLastError();
		//_wsprintf(szItem, SKIPLEN(countof(szItem)) L"ConEmu Attach, PID=%u, TID=%u", GetCurrentProcessId(), GetCurrentThreadId());
		//DisplayLastError(L"Can't start attach thread", dwErr, 0, szItem);
		return amr_Unexpected;
	}
	// We don't need this handle
	CloseHandle(hThread);

	return amr_Success;
}
Esempio n. 5
0
bool CAttachDlg::OnStartAttach()
{
	bool lbRc = false;
	// Тут нужно получить инфу из списка и дернуть собственно аттач
	wchar_t szItem[128] = {};
	//DWORD nPID = 0, nBits = WIN3264TEST(32,64);
	//AttachProcessType nType = apt_Unknown;
	wchar_t *psz;
	int iSel, iCur;
	DWORD nTID;
	HANDLE hThread = NULL;
	AttachParm *pParm = NULL;
	MArray<AttachParm> Parms;
	//HWND hAttachWnd = NULL;

	ShowWindow(mh_Dlg, SW_HIDE);

	BOOL bAlternativeMode = (IsDlgButtonChecked(mh_Dlg, IDC_ATTACH_ALT) != 0);

	iSel = ListView_GetNextItem(mh_List, -1, LVNI_SELECTED);
	while (iSel >= 0)
	{
		iCur = iSel;
		iSel = ListView_GetNextItem(mh_List, iCur, LVNI_SELECTED);

		AttachParm L = {NULL, 0, WIN3264TEST(32,64), apt_Unknown, bAlternativeMode};

		ListView_GetItemText(mh_List, iCur, alc_PID, szItem, countof(szItem)-1);
		L.nPID = wcstoul(szItem, &psz, 10);
		if (L.nPID)
		{
			psz = wcschr(szItem, L'[');
			if (!psz)
			{
				_ASSERTE(FALSE && "Process bitness was not detected?");
			}
			else
			{
				L.nBits = wcstoul(psz+1, &psz, 10);
			}
		}
		ListView_GetItemText(mh_List, iCur, alc_Type, szItem, countof(szItem));
		if (lstrcmp(szItem, szTypeCon) == 0)
			L.nType = apt_Console;
		else if (lstrcmp(szItem, szTypeGui) == 0)
			L.nType = apt_Gui;

		ListView_GetItemText(mh_List, iCur, alc_HWND, szItem, countof(szItem));
		L.hAttachWnd = (szItem[0]==L'0' && szItem[1]==L'x') ? (HWND)(DWORD_PTR)wcstoul(szItem+2, &psz, 16) : NULL;

		if (!L.nPID || !L.nBits || !L.nType || !L.hAttachWnd)
		{
			MBoxAssert(L.nPID && L.nBits && L.nType && L.hAttachWnd);
			goto wrap;
		}

		Parms.push_back(L);
	}

	if (Parms.empty())
	{
		goto wrap;
	}
	else
	{
		AttachParm N = {NULL};
		Parms.push_back(N);
	}

	//// Чтобы клик от мышки в консоль не провалился
	//WARNING("Клик от мышки в консоль проваливается");
	//gpConEmu->mouse.nSkipEvents[0] = WM_LBUTTONUP;
	//gpConEmu->mouse.nSkipEvents[1] = 0;
	//gpConEmu->mouse.nReplaceDblClk = 0;

	// Все, диалог закрываем, чтобы не мешался
	Close();

	// Работу делаем в фоновом потоке, чтобы не блокировать главный
	// (к окну ConEmu должна подцепиться новая вкладка)
	pParm = Parms.detach();
	if (!pParm)
	{
		_wsprintf(szItem, SKIPLEN(countof(szItem)) L"ConEmu Attach, PID=%u, TID=%u", GetCurrentProcessId(), GetCurrentThreadId());
		DisplayLastError(L"Parms.detach() failed", -1, 0, szItem);
		goto wrap;
	}
	else
	{
		hThread = apiCreateThread((LPTHREAD_START_ROUTINE)StartAttachThread, pParm, &nTID, "CAttachDlg::StartAttachThread#1");
		if (!hThread)
		{
			DWORD dwErr = GetLastError();
			_wsprintf(szItem, SKIPLEN(countof(szItem)) L"ConEmu Attach, PID=%u, TID=%u", GetCurrentProcessId(), GetCurrentThreadId());
			DisplayLastError(L"Can't start attach thread", dwErr, 0, szItem);
		}
		else
			lbRc = true;
	}
wrap:
	// We don't need this handle
	if (hThread)
		CloseHandle(hThread);

	return lbRc;
}
// Warning, напрямую НЕ вызывать. Пользоваться "общей" PostMacro
void CPluginW2800::PostMacroApi(const wchar_t* asMacro, INPUT_RECORD* apRec, bool abShowParseErrors)
{
	if (!InfoW2800 || !InfoW2800->AdvControl)
		return;

	MacroSendMacroText mcr = {sizeof(MacroSendMacroText)};
	//mcr.Flags = 0; // По умолчанию - вывод на экран разрешен
	bool bEnableOutput = true;

	while ((asMacro[0] == L'@' || asMacro[0] == L'^') && asMacro[1] && asMacro[1] != L' ')
	{
		switch (*asMacro)
		{
		case L'@':
			bEnableOutput = false;
			break;
		case L'^':
			mcr.Flags |= KMFLAGS_NOSENDKEYSTOPLUGINS;
			break;
		}
		asMacro++;
	}

	if (bEnableOutput)
		mcr.Flags |= KMFLAGS_ENABLEOUTPUT;

	// This macro was not adopted to Lua?
	_ASSERTE(*asMacro && *asMacro != L'$');

	// Вообще говоря, если тут попадается макрос в старом формате - то мы уже ничего не сделаем...
	// Начиная с Far 3 build 2851 - все макросы переведены на Lua

	mcr.SequenceText = asMacro;
	if (apRec)
		mcr.AKey = *apRec;

	mcr.Flags |= KMFLAGS_SILENTCHECK;

	if (!InfoW2800->MacroControl(&guid_ConEmu, MCTL_SENDSTRING, MSSC_CHECK, &mcr))
	{
		if (abShowParseErrors)
		{
			wchar_t* pszErrText = NULL;
			size_t iRcSize = InfoW2800->MacroControl(&guid_ConEmu, MCTL_GETLASTERROR, 0, NULL);
			MacroParseResult* Result = iRcSize ? (MacroParseResult*)calloc(iRcSize,1) : NULL;
			if (Result)
			{
				Result->StructSize = sizeof(*Result);
				_ASSERTE(FALSE && "Check MCTL_GETLASTERROR");
				InfoW2800->MacroControl(&guid_ConEmu, MCTL_GETLASTERROR, iRcSize, Result);

				size_t cchMax = (Result->ErrSrc ? lstrlen(Result->ErrSrc) : 0) + lstrlen(asMacro) + 255;
				pszErrText = (wchar_t*)malloc(cchMax*sizeof(wchar_t));
				_wsprintf(pszErrText, SKIPLEN(cchMax)
					L"Error in Macro. Far %u.%u build %u r%u\n"
					L"ConEmu plugin %02u%02u%02u%s[%u] {2800}\n"
					L"Code: %u, Line: %u, Col: %u%s%s\n"
					L"----------------------------------\n"
					L"%s",
					gFarVersion.dwVerMajor, gFarVersion.dwVerMinor, gFarVersion.dwBuild, gFarVersion.Bis ? 1 : 0,
					MVV_1, MVV_2, MVV_3, _CRT_WIDE(MVV_4a), WIN3264TEST(32,64),
					Result->ErrCode, (UINT)(int)Result->ErrPos.Y+1, (UINT)(int)Result->ErrPos.X+1,
					Result->ErrSrc ? L", Hint: " : L"", Result->ErrSrc ? Result->ErrSrc : L"",
					asMacro);

				SafeFree(Result);
			}
			else
			{
				size_t cchMax = lstrlen(asMacro) + 255;
				pszErrText = (wchar_t*)malloc(cchMax*sizeof(wchar_t));
				_wsprintf(pszErrText, SKIPLEN(cchMax)
					L"Error in Macro. Far %u.%u build %u r%u\n"
					L"ConEmu plugin %02u%02u%02u%s[%u] {2800}\n"
					L"----------------------------------\n"
					L"%s",
					gFarVersion.dwVerMajor, gFarVersion.dwVerMinor, gFarVersion.dwBuild, gFarVersion.Bis ? 1 : 0,
					MVV_1, MVV_2, MVV_3, _CRT_WIDE(MVV_4a), WIN3264TEST(32,64),
					asMacro);
			}

			if (pszErrText)
			{
				DWORD nTID;
				HANDLE h = apiCreateThread(BackgroundMacroError, pszErrText, &nTID, "BackgroundMacroError");
				SafeCloseHandle(h);
			}
		}
	}
	else
	{
		//gFarVersion.dwBuild
		InfoW2800->MacroControl(&guid_ConEmu, MCTL_SENDSTRING, 0, &mcr);
	}
}
Esempio n. 7
0
BOOL WINAPI OnSetConsoleOutputCP(UINT wCodePageID)
{
	//typedef BOOL (WINAPI* OnSetConsoleOutputCP_t)(UINT wCodePageID);
	ORIGINALFAST(SetConsoleOutputCP);
	_ASSERTE(OnSetConsoleOutputCP!=SetConsoleOutputCP);
	BOOL lbRc = FALSE;
	SCOCP sco = {wCodePageID, (OnSetConsoleCP_t)F(SetConsoleOutputCP), CreateEvent(NULL,FALSE,FALSE,NULL)};
	DWORD nTID = 0, nWait = -1, nErr;
	/*
	wchar_t szErrText[255], szTitle[64];
	msprintf(szTitle, SKIPLEN(countof(szTitle)) L"PID=%u, TID=%u", GetCurrentProcessId(), GetCurrentThreadId());
	*/
	#ifdef _DEBUG
	DWORD nPrevCP = GetConsoleOutputCP();
	#endif
	DWORD nCurCP = 0;
	HANDLE hThread = apiCreateThread(SetConsoleCPThread, &sco, &nTID, "OnSetConsoleOutputCP(%u)", wCodePageID);

	if (!hThread)
	{
		nErr = GetLastError();
		_ASSERTE(hThread!=NULL);
		/*
		msprintf(szErrText, SKIPLEN(countof(szErrText)) L"chcp(out) failed, ErrCode=0x%08X\nConEmuHooks.cpp:OnSetConsoleOutputCP", nErr);
		Message BoxW(NULL, szErrText, szTitle, MB_ICONSTOP|MB_SYSTEMMODAL);
		lbRc = FALSE;
		*/
	}
	else
	{
		HANDLE hEvents[2] = {hThread, sco.hReady};
		nWait = WaitForMultipleObjects(2, hEvents, FALSE, SETCONCP_READYTIMEOUT);

		if (nWait != WAIT_OBJECT_0)
			nWait = WaitForSingleObject(hThread, SETCONCP_TIMEOUT);

		if (nWait == WAIT_OBJECT_0)
		{
			lbRc = sco.lbRc;
		}
		else
		{
			//BUGBUG: На некоторых системых (Win2k3, WinXP) SetConsoleCP (и иже с ними) просто зависают
			apiTerminateThread(hThread,100);
			nCurCP = GetConsoleOutputCP();
			if (nCurCP == wCodePageID)
			{
				lbRc = TRUE; // таки получилось
			}
			else
			{
				_ASSERTE(nCurCP == wCodePageID);
				/*
				msprintf(szErrText, SKIPLEN(countof(szErrText))
				          L"chcp(out) hung detected\n"
				          L"ConEmuHooks.cpp:OnSetConsoleOutputCP\n"
				          L"ReqOutCP=%u, PrevOutCP=%u, CurOutCP=%u\n"
				          //L"\nPress <OK> to stop waiting"
				          ,wCodePageID, nPrevCP, nCurCP);
				Message BoxW(NULL, szErrText, szTitle, MB_ICONSTOP|MB_SYSTEMMODAL);
				*/
			}
			//nWait = WaitForSingleObject(hThread, 0);
			//if (nWait == WAIT_TIMEOUT)
			//	apiTerminateThread(hThread,100);
			//if (GetConsoleOutputCP() == wCodePageID)
			//	lbRc = TRUE;
		}

		CloseHandle(hThread);
	}

	if (sco.hReady)
		CloseHandle(sco.hReady);

	return lbRc;
}
Esempio n. 8
0
	UINT ExecuteDownloader(LPWSTR pszCommand, LPWSTR szCmdDirectory)
	{
		UINT iRc;
		DWORD nWait;
		DWORD nThreadWait = WAIT_TIMEOUT;
		PipeThreadParm threadParm = {this};

		ZeroStruct(m_SI); m_SI.cb = sizeof(m_SI);
		ZeroStruct(m_PI);

		mb_Terminating = false;

		DWORD nCreateFlags = 0
			//| CREATE_NO_WINDOW
			| NORMAL_PRIORITY_CLASS;

		m_SI.dwFlags |= STARTF_USESHOWWINDOW;

		m_SI.wShowWindow = RELEASEDEBUGTEST(SW_HIDE,SW_SHOWNA);

		if (!szCmdStringFormat || !*szCmdStringFormat)
		{
			// We need to redirect only StdError output

			SECURITY_ATTRIBUTES saAttr = {sizeof(saAttr), NULL, TRUE};
			if (!CreatePipe(&mh_PipeErrRead, &mh_PipeErrWrite, &saAttr, 0))
			{
				iRc = GetLastError();
				_ASSERTE(FALSE && "CreatePipe was failed");
				if (!iRc)
					iRc = E_UNEXPECTED;
				goto wrap;
			}
			// Ensure the read handle to the pipe for STDOUT is not inherited.
			SetHandleInformation(mh_PipeErrRead, HANDLE_FLAG_INHERIT, 0);

			mh_PipeErrThread = apiCreateThread(StdErrReaderThread, (LPVOID)&threadParm, &mn_PipeErrThreadId, "Downloader::ReaderThread");
			if (mh_PipeErrThread != NULL)
			{
				m_SI.dwFlags |= STARTF_USESTDHANDLES;
				// Let's try to change only Error pipe?
				m_SI.hStdError = mh_PipeErrWrite;
			}
		}

		// Now we can run the downloader
		if (!CreateProcess(NULL, pszCommand, NULL, NULL, TRUE/*!Inherit!*/, nCreateFlags, NULL, szCmdDirectory, &m_SI, &m_PI))
		{
			iRc = GetLastError();
			_ASSERTE(FALSE && "Create downloader process was failed");
			if (!iRc)
				iRc = E_UNEXPECTED;
			goto wrap;
		}

		nWait = WaitForSingleObject(m_PI.hProcess, INFINITE);

		if (GetExitCodeProcess(m_PI.hProcess, &nWait)
			&& (nWait == 0)) // CERR_DOWNLOAD_SUCCEEDED is not returned for compatibility purposes
		{
			iRc = 0; // OK
		}
		else
		{
			_ASSERTE(nWait == 0 && "Downloader has returned an error");
			iRc = nWait;
		}

	wrap:
		// Finalize reading routine
		mb_Terminating = true;
		if (mh_PipeErrThread)
		{
			nThreadWait = WaitForSingleObject(mh_PipeErrThread, 0);
			if (nThreadWait == WAIT_TIMEOUT)
			{
				apiCancelSynchronousIo(mh_PipeErrThread);
			}
		}
		SafeCloseHandle(mh_PipeErrRead);
		SafeCloseHandle(mh_PipeErrWrite);
		if (mh_PipeErrThread)
		{
			if (nThreadWait == WAIT_TIMEOUT)
			{
				nThreadWait = WaitForSingleObject(mh_PipeErrThread, 5000);
			}
			if (nThreadWait == WAIT_TIMEOUT)
			{
				_ASSERTE(FALSE && "StdErr reading thread hangs, terminating");
				apiTerminateThread(mh_PipeErrThread, 999);
			}
			SafeCloseHandle(mh_PipeErrThread);
		}
		// Exit
		return iRc;
	};
Esempio n. 9
0
int MyAssertProc(const wchar_t* pszFile, int nLine, const wchar_t* pszTest, bool abNoPipe)
{
	HooksUnlocker;

	#ifdef _DEBUG
	if (MyAssertSkip(pszFile, nLine, pszTest, abNoPipe))
		return 1;
	#endif

	MyAssertDumpToFile(pszFile, nLine, pszTest);

	HANDLE hHeap = GetProcessHeap();
	MyAssertInfo* pa = (MyAssertInfo*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(MyAssertInfo));
	if (!pa)
		return -1;
	wchar_t *szExeName = (wchar_t*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, (MAX_PATH+1)*sizeof(wchar_t));
	if (szExeName && !GetModuleFileNameW(NULL, szExeName, MAX_PATH+1)) szExeName[0] = 0;
	pa->bNoPipe = abNoPipe;
	msprintf(pa->szTitle, countof(pa->szTitle), L"CEAssert PID=%u TID=%u", GetCurrentProcessId(), GetCurrentThreadId());
	wchar_t szVer4[2] = WSTRING(MVV_4a);
	msprintf(pa->szDebugInfo, countof(pa->szDebugInfo), L"Assertion in %s [%02u%02u%02u%s]\n%s\n\n%s: %i\n\nPress 'Retry' to trap.",
	                szExeName ? szExeName : L"<HeapAllocFailed>",
					MVV_1, MVV_2, MVV_3, szVer4,
					pszTest ? pszTest : L"", pszFile, nLine);
	DWORD dwCode = 0;

	if (gAllowAssertThread == am_Thread)
	{
		DWORD dwTID;
		HANDLE hThread = apiCreateThread(MyAssertThread, pa, &dwTID, "MyAssertThread");

		if (hThread == NULL)
		{
			dwCode = IDRETRY;
			goto wrap;
		}

		WaitForSingleObject(hThread, INFINITE);
		GetExitCodeThread(hThread, &dwCode);
		CloseHandle(hThread);
		goto wrap;
	}
	
#ifdef ASSERT_PIPE_ALLOWED
#ifdef _DEBUG
	if (!abNoPipe && (gAllowAssertThread == am_Pipe))
	{
		HWND hConWnd = GetConEmuHWND(2);
		HWND hGuiWnd = ghConEmuWnd;

		// -- искать - нельзя. Если мы НЕ в ConEmu - нельзя стучаться в другие копии!!!
		//#ifndef CONEMU_MINIMAL
		//if (hGuiWnd == NULL)
		//{
		//	hGuiWnd = FindWindowEx(NULL, NULL, VirtualConsoleClassMain, NULL);
		//}
		//#endif

		if (hGuiWnd && gnInMyAssertTrap <= 0)
		{
			InterlockedIncrement(&gnInMyAssertTrap);
			InterlockedIncrement(&gnInMyAssertPipe);
			gnInMyAssertThread = GetCurrentThreadId();
			ResetEvent(ghInMyAssertTrap);
			
			dwCode = GuiMessageBox(abNoPipe ? NULL : hGuiWnd, pa->szDebugInfo, pa->szTitle, MB_SETFOREGROUND|MB_SYSTEMMODAL|MB_RETRYCANCEL);
			InterlockedDecrement(&gnInMyAssertTrap);
			InterlockedDecrement(&gnInMyAssertPipe);
			SetEvent(ghInMyAssertTrap);
			gnInMyAssertThread = 0;
			goto wrap;
		}
	}

	while (gnInMyAssertPipe>0 && (gnInMyAssertThread != GetCurrentThreadId()))
	{
		Sleep(250);
	}
#endif
#endif

	// В консольных приложениях попытка запустить CreateThread(MyAssertThread) может зависать
	dwCode = MyAssertThread(pa);

wrap:
	if (pa)
		HeapFree(hHeap, 0, pa);
	if (szExeName)
		HeapFree(hHeap, 0, szExeName);
	return (dwCode == IDRETRY) ? -1 : 1;
}
Esempio n. 10
0
// Warning, напрямую НЕ вызывать. Пользоваться "общей" PostMacro
void CPluginW1900::PostMacroApi(const wchar_t* asMacro, INPUT_RECORD* apRec, bool abShowParseErrors)
{
	if (!InfoW1900 || !InfoW1900->AdvControl)
		return;

	MacroSendMacroText mcr = {sizeof(MacroSendMacroText)};
	//mcr.Flags = 0; // По умолчанию - вывод на экран разрешен

	while ((asMacro[0] == L'@' || asMacro[0] == L'^') && asMacro[1] && asMacro[1] != L' ')
	{
		switch (*asMacro)
		{
		case L'@':
			mcr.Flags |= KMFLAGS_DISABLEOUTPUT;
			break;
		case L'^':
			mcr.Flags |= KMFLAGS_NOSENDKEYSTOPLUGINS;
			break;
		}
		asMacro++;
	}

	wchar_t* pszMacroCopy = NULL;

	//Far3 build 2576: удален $Text
	//т.к. макросы у нас фаро-независимые - нужны танцы с бубном
	pszMacroCopy = lstrdup(asMacro);
	CharUpperBuff(pszMacroCopy, lstrlen(pszMacroCopy));
	if (wcsstr(pszMacroCopy, L"$TEXT") && !InfoW1900->MacroControl(&guid_ConEmu, MCTL_SENDSTRING, MSSC_CHECK, &mcr))
	{
		SafeFree(pszMacroCopy);
		pszMacroCopy = (wchar_t*)calloc(lstrlen(asMacro)+1,sizeof(wchar_t)*2);
		wchar_t* psz = pszMacroCopy;
		while (*asMacro)
		{
			if (asMacro[0] == L'$'
				&& (asMacro[1] == L'T' || asMacro[1] == L't')
				&& (asMacro[2] == L'E' || asMacro[2] == L'e')
				&& (asMacro[3] == L'X' || asMacro[3] == L'x')
				&& (asMacro[4] == L'T' || asMacro[4] == L't'))
			{
				lstrcpy(psz, L"print("); psz += 6;

				// Пропустить spasing-symbols
				asMacro += 5;
				while (*asMacro == L' ' || *asMacro == L'\t' || *asMacro == L'\r' || *asMacro == L'\n')
					asMacro++;
				// Копировать строку или переменную
				if (*asMacro == L'@' && *(asMacro+1) == L'"')
				{
					*(psz++) = *(asMacro++); *(psz++) = *(asMacro++);
					while (*asMacro)
					{
						*(psz++) = *(asMacro++);
						if (*(asMacro-1) == L'"')
						{
							if (*asMacro != L'"')
								break;
							*(psz++) = *(asMacro++);
						}
					}
				}
				else if (*asMacro == L'"')
				{
					*(psz++) = *(asMacro++);
					while (*asMacro)
					{
						*(psz++) = *(asMacro++);
						if (*(asMacro-1) == L'\\' && *asMacro == L'"')
						{
							*(psz++) = *(asMacro++);
						}
						else if (*(asMacro-1) == L'"')
						{
							break;
						}
					}
				}
				else if (*asMacro == L'%')
				{
					*(psz++) = *(asMacro++);
					while (*asMacro)
					{
						if (wcschr(L" \t\r\n", *asMacro))
							break;
						*(psz++) = *(asMacro++);
					}
				}
				else
				{
					SafeFree(pszMacroCopy);
					break; // ошибка
				}
				// закрыть скобку
				*(psz++) = L')';
			}
			else
			{
				*(psz++) = *(asMacro++);
			}
		}

		// Если успешно пропатчили макрос
		if (pszMacroCopy)
			asMacro = pszMacroCopy;
	}

	mcr.SequenceText = asMacro;
	if (apRec)
		mcr.AKey = *apRec;

	mcr.Flags |= KMFLAGS_SILENTCHECK;

	if (!InfoW1900->MacroControl(&guid_ConEmu, MCTL_SENDSTRING, MSSC_CHECK, &mcr))
	{
		if (abShowParseErrors)
		{
			wchar_t* pszErrText = NULL;
			size_t iRcSize = InfoW1900->MacroControl(&guid_ConEmu, MCTL_GETLASTERROR, 0, NULL);
			MacroParseResult* Result = iRcSize ? (MacroParseResult*)calloc(iRcSize,1) : NULL;
			if (Result)
			{
				Result->StructSize = sizeof(*Result);
				_ASSERTE(FALSE && "Check MCTL_GETLASTERROR");
				InfoW1900->MacroControl(&guid_ConEmu, MCTL_GETLASTERROR, iRcSize, Result);

				size_t cchMax = (Result->ErrSrc ? lstrlen(Result->ErrSrc) : 0) + lstrlen(asMacro) + 255;
				pszErrText = (wchar_t*)malloc(cchMax*sizeof(wchar_t));
				_wsprintf(pszErrText, SKIPLEN(cchMax)
					L"Error in Macro. Far %u.%u build %u r%u\n"
					L"ConEmu plugin %02u%02u%02u%s[%u] {1900}\n"
					L"Code: %u, Line: %u, Col: %u%s%s\n"
					L"----------------------------------\n"
					L"%s",
					gFarVersion.dwVerMajor, gFarVersion.dwVerMinor, gFarVersion.dwBuild, gFarVersion.Bis ? 1 : 0,
					MVV_1, MVV_2, MVV_3, _CRT_WIDE(MVV_4a), WIN3264TEST(32,64),
					Result->ErrCode, (UINT)(int)Result->ErrPos.Y+1, (UINT)(int)Result->ErrPos.X+1,
					Result->ErrSrc ? L", Hint: " : L"", Result->ErrSrc ? Result->ErrSrc : L"",
					asMacro);

				SafeFree(Result);
			}
			else
			{
				size_t cchMax = lstrlen(asMacro) + 255;
				pszErrText = (wchar_t*)malloc(cchMax*sizeof(wchar_t));
				_wsprintf(pszErrText, SKIPLEN(cchMax)
					L"Error in Macro. Far %u.%u build %u r%u\n"
					L"ConEmu plugin %02u%02u%02u%s[%u] {1900}\n"
					L"----------------------------------\n"
					L"%s",
					gFarVersion.dwVerMajor, gFarVersion.dwVerMinor, gFarVersion.dwBuild, gFarVersion.Bis ? 1 : 0,
					MVV_1, MVV_2, MVV_3, _CRT_WIDE(MVV_4a), WIN3264TEST(32,64),
					asMacro);
			}

			if (pszErrText)
			{
				DWORD nTID;
				HANDLE h = apiCreateThread(BackgroundMacroError, pszErrText, &nTID, "BackgroundMacroError");
				SafeCloseHandle(h);
			}
		}
	}
	else
	{
		//gFarVersion.dwBuild
		InfoW1900->MacroControl(&guid_ConEmu, MCTL_SENDSTRING, 0, &mcr);
	}

	SafeFree(pszMacroCopy);
}