Sagar R. Kapadia Ответов: 2

64 битный процесс dot net созданный с помощью createprocessasuserw немедленно завершается с кодом исключения 0xc06d007e в windows 7 64 bit


У меня есть приложение C#, которое запускается из библиотеки DLL C++ с помощью CreateProcessAsUserW api. Процесс запускается успешно, но немедленно завершается. Он правильно работает в Windows 10 [как 32-битный, так и 64-битный] и 32-битный в Windows 7.
Пожалуйста помочь.
Спасибо,
Сагар
Метод заключается в следующем
//https://stackoverflow.com/questions/14315013/how-to-get-the-active-user-when-multiple-users-are-logged-on-in-windows


STDMETHODIMP CProcessManager::LaunchProcessAsActiveUser(BSTR processName, LONG* dwProcessId)
{

	//char *lpszPath = _com_util::ConvertBSTRToString(processName);
	wchar_t* path = (wchar_t*)processName;//CharToWideChar(lpszPath);

	DWORD session_id = -1;
	DWORD session_count = 0;

	WTS_SESSION_INFOA *pSession = NULL;


	if (WTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSession, &session_count))
	{
		//log success
	}
	else
	{
		//log error
		return S_OK;
	}
	logger->Log(L"Session Count", session_count);
	logger->Log(L"Begin Enumerating Sesions");
	for (int i = 0; i < session_count; i++)
	{
		session_id = pSession[i].SessionId;
		logger->Log(L"SessionId", session_id);

		WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
		WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;

		DWORD bytes_returned = 0;
		if (::WTSQuerySessionInformation(
			WTS_CURRENT_SERVER_HANDLE,
			session_id,
			WTSConnectState,
			reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
			&bytes_returned))
		{
			wts_connect_state = *ptr_wts_connect_state;
			::WTSFreeMemory(ptr_wts_connect_state);
			if (wts_connect_state != WTSActive) continue;
		}
		else
		{
			//log error
			continue;
		}
		logger->Log(L"End Enumerating Sesions");
		logger->Log(L"Selected Session Id", session_id);
		HANDLE hImpersonationToken;

		if (!WTSQueryUserToken(session_id, &hImpersonationToken))
		{
			//log error
			logger->Log(L"Exception in WTSQueryUserToken", GetLastError());
			continue;
		}


		//Get real token from impersonation token
		DWORD neededSize1 = 0;
		HANDLE *realToken = new HANDLE;
		if (GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize1))
		{
			CloseHandle(hImpersonationToken);
			hImpersonationToken = *realToken;
		}
		else
		{
			//log error
			logger->Log(L"Exception in GetTokenInformation", GetLastError());
			continue;
		}


		HANDLE hUserToken;

		if (!DuplicateTokenEx(hImpersonationToken,
			//0,
			//MAXIMUM_ALLOWED,
			TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS | MAXIMUM_ALLOWED,
			NULL,
			SecurityImpersonation,
			TokenPrimary,
			&hUserToken))
		{
			//log error
			logger->Log(L"Exception in DuplicateTokenEx", GetLastError());
			continue;
		}

		// Get user name of this process
		//LPTSTR pUserName = NULL;
		WCHAR* pUserName;
		DWORD user_name_len = 0;

		if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &pUserName, &user_name_len))
		{
			//log username contained in pUserName WCHAR string
			// char * lpszUserName = WideCharToChar(pUserName);
			logger->Log(pUserName);
			//LocalFree(lpszUserName);
		}
		else
		{
			logger->Log(L"Exception in WTSQuerySessionInformation", GetLastError());
		}

		//Free memory                         
		if (pUserName) WTSFreeMemory(pUserName);

		ImpersonateLoggedOnUser(hUserToken);

		STARTUPINFOW StartupInfo;

		StartupInfo.cb = sizeof(STARTUPINFOW);




		//GetStartupInfoW(&StartupInfo);
		ZeroMemory(&StartupInfo, sizeof(StartupInfo));
		//Uncommented  by Sagar 20th January 20118 1612
		StartupInfo.lpDesktop = CharToWideChar("winsta0\\default");

		//to Hide Console Process 03-10-2018
		StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
		StartupInfo.wShowWindow = SW_SHOW;//SW_HIDE;



		PROCESS_INFORMATION processInfo;



		SECURITY_ATTRIBUTES Security1;
		ZeroMemory(&Security1, sizeof(Security1));

		Security1.nLength = sizeof SECURITY_ATTRIBUTES;


		SECURITY_ATTRIBUTES Security2;
		ZeroMemory(&Security2, sizeof(Security2));
		Security2.nLength = sizeof SECURITY_ATTRIBUTES;

		void* lpEnvironment = NULL;

		// Get all necessary environment variables of logged in user
		// to pass them to the new process
		BOOL resultEnv = CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE);

		if (!resultEnv)
		{
			//log error
			DWORD err = GetLastError();
			logger->Log(L"Exception in CreateEnvironmentBlock", err);

			continue;
		}

		WCHAR PP[1024]; //path and parameters
		ZeroMemory(PP, 1024 * sizeof WCHAR);
		wcscpy_s(PP, path);
		wcscat_s(PP, L" ");
		//wcscat(PP, args);

		// Start the process on behalf of the current user 
		BOOL result = CreateProcessAsUserW(hUserToken,
			PP,
			NULL,
			&Security1,
			&Security2,
			FALSE,
			NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE/*| CREATE_NO_WINDOW*/,//CREATE_NO_WINDOW to Hide Console Process 03-10-2018
			/*lpEnvironment*/NULL,

			//"C:\\ProgramData\\some_dir",
			NULL,
			/*NULL,*/
			&StartupInfo,
			&processInfo);

		if (!result)
		{
			//log error
			//char * lpszPath = WideCharToChar(PP);
			logger->Log(L"Failed to create process", PP);
			//LocalFree(lpszPath);
			DWORD err = GetLastError();
			logger->Log(L"GetLastError returned", err);

		}
		else
		{
			*dwProcessId = processInfo.dwProcessId;
			logger->Log(L"Created Process", *dwProcessId);
			//log success
		}

		DestroyEnvironmentBlock(lpEnvironment);

		CloseHandle(hImpersonationToken);
		CloseHandle(hUserToken);
		CloseHandle(realToken);

		RevertToSelf();
	}

	WTSFreeMemory(pSession);


	return S_OK;
}


Что я уже пробовал:

Я нашел следующую ссылку,

https://stackoverflow.com/questions/15581142/why-is-this-process-crashing-as-soon-as-it-is-launched

однако process monitor от SysInternals не находит отсутствующей библиотеки dll. [Я могу прикрепить сохраненные журналы из ProcMon] я также попытался передать путь к папке приложения, как это было предложено в другом месте, в параметре lpCurrentDirectory API, но это тоже не сработало.

Я последовал указаниям в /https://stackoverflow.com/questions/14315013/how-to-get-the-active-user-when-multiple-users-are-logged-on-in-windows написать код, который запускает процесс,
и метод, который запускает процесс, вызывается из службы windows.Чтобы эмулировать это из командной строки, я использовал следующее
https://stackoverflow.com/questions/77528/how-do-you-run-cmd-exe-under-the-local-system-account [psexec64 -i -s cmd.exe, а затем запустил процесс с cmd.exe]

Richard Deeming

Похоже, что ваш код создает необработанное исключение. Вам нужно будет поймать его и записать детали где-нибудь, чтобы узнать, что происходит не так.

Sagar R. Kapadia

Исключение возникает до запуска кода dot net. Я уже добавил обработчики исключений в целевое приложение, но они вообще не вызываются. Однако средство просмотра событий показывает приведенное выше исключение

Dave Kreskowiak

Позволь мне все прояснить. Вы пытаетесь запустить пользовательское интерактивное приложение C# на рабочем столе пользователя из Служебного приложения Windows, написанного на C/C++?

Sagar R. Kapadia

Приложение c# [оно не является интерактивным пользователем, но взаимодействует с другими, которые являются] запускается из службы Windows c#, из библиотеки dll C++ / ATL [которая использует winapi для запуска процесса в качестве пользователя]. Он [и другие связанные с ним] [32-битный и 64-битный оба] прекрасно работает в windows 10, а 32-битная версия службы и это приложение прекрасно работают в windows 7, но 64-битный процесс выходит из строя сразу после запуска. [Процесс создан правильно, я вижу его в диспетчере задач, а затем появляется сообщение о том, что приложение разбилось и должно быть сообщено в microsoft, а затем оно исчезает из Диспетчера задач.

Dave Kreskowiak

ну, я не могу ответить, почему он терпит неудачу на Win7x64, но я могу дать вам свое мнение о том, почему то, что вы делаете, является плохой идеей.

Службы не должны взаимодействовать с рабочими столами пользователей и запускать процессы в качестве зарегистрированных пользователей. Период. Если посмотреть на историю того, как Microsoft относится к этой ситуации, начиная с тех времен, когда это было обычным делом и практически неограниченным, и заканчивая тем, что безопасность вокруг нее все больше и больше ужесточается, то лучшим вариантом было бы не запускать процесс из службы.

What you should be doing, and this is far more future-proof that your current method, is to have the service talk to another application and tell it to launch a process. This second application would be launched like any other "tray application", though the registry Run key when the logs in. This second app would be launched as the user account automatically. It can then talk to the service through a named pipe or memory mapped file or some other IPC. The service can then use the IPC channel of choice to tell the second app to launch whatever you need to. The second app can easily launch this app and it automatically runs as the user without you doing anything at all.

Вы также получаете преимущества совместимости вашего приложения с многопользовательскими средами, такими как Citrix, будущей защиты от дополнительных ограничений на взаимодействие с сервисами и лучшей поддержки вашего приложения.

Sagar R. Kapadia

Спасибо за Ваше предложение, Дэйв. Я буду рассматривать его и рефакторить приложение соответственно

2 Ответов

Рейтинг:
2

Sagar R. Kapadia

Я получил ответ на этот вопрос. https://social.msdn.microsoft.com/Forums/vstudio/en-US/fb9d15fb-dc9f-488e-92c4-e0bb438442e1/64-bit-dot-net-process-created-with-createprocessasuserw-exits-immediately-with-exception-code?forum=vcgeneral [RLWA32 ответил на него] возникла проблема с тем, как запускался этот процесс. Его код для запуска процесса выглядит следующим образом, и он отлично работает.

UINT __stdcall RunProgram(LPVOID pv)
{
	WCHAR szTarg32[MAX_PATH] = TARGET32_PATH, szTarg64[MAX_PATH] = TARGET64_PATH;
	LPWSTR pszProcess = nullptr;
	PWTS_SESSION_INFOW pwsi = NULL;
	DWORD dwSession = -1, dwCount = 0;
	HANDLE hToken = NULL;
	LPVOID lpEnvironment = nullptr;
	LPWSTR pszPath = nullptr;
	UINT ret = 1;


	DWORD dwOpcode = reinterpret_cast<DWORD>(pv);

	if (dwOpcode == LAUNCH_X86PROCESS)
		pszProcess = szTarg32;
	else
		pszProcess = szTarg64;

	if (!WTSEnumerateSessionsW(WTS_CURRENT_SERVER, 0, 1, &pwsi, &dwCount))
	{
		Report(L"WTSEnumerateSessions failed with %d\n", GetLastError());
		return ret;
	}

	for (DWORD i = 0; i < dwCount; i++)
	{
		if (pwsi[i].State == WTSActive)
		{
			dwSession = pwsi[i].SessionId;
			break;
		}
	}

	if (dwSession != -1)
	{
		if (WTSQueryUserToken(dwSession, &hToken))
		{
			if (CreateEnvironmentBlock(&lpEnvironment, hToken, FALSE))
			{
				HRESULT hr;
				if (SUCCEEDED((hr = SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, hToken, &pszPath))))
				{
					if (ImpersonateLoggedOnUser(hToken))
					{
						STARTUPINFOW si = { sizeof si };
						PROCESS_INFORMATION pi = {};
						
						if (CreateProcessAsUserW(hToken, pszProcess, nullptr, nullptr, nullptr, FALSE, CREATE_UNICODE_ENVIRONMENT, lpEnvironment, pszPath, &si, &pi))
						{
							CloseHandle(pi.hThread);
							CloseHandle(pi.hProcess);
							Report(L"CreateProcessAsUser started %s, pid is %d\n", pszProcess, pi.dwProcessId);
							ret = 0;
						}
						else
							Report(L"CreateProcessAsUser failed with %d\n", GetLastError());
						
						RevertToSelf();
					}
					else
						Report(L"ImpersonateLoggedOnUser failed with %d\n", GetLastError());
				}
				else
					Report(L"SHGetKnownFolderPath failed with 0x%X\n", hr);
			}
			else
				Report(L"CreateEnvironmentBlock failed with %d\n", GetLastError());
		}
		else
			Report(L"WTSQueryUserToken failed with %d\n", GetLastError());
	}
	else
		Report(L"WTSEnumerateSessions reported no active session\n");

	if (pwsi)
		WTSFreeMemory(pwsi);

	if (hToken)
		CloseHandle(hToken);

	if (lpEnvironment)
		DestroyEnvironmentBlock(lpEnvironment);

	if (pszPath)
		CoTaskMemFree(pszPath);

	return ret;
}


Рейтинг:
0

KarstenK

Вы не написали о правах пользователя, которые получает процесс. Это должен быть действительный пользователь на машине, который имеет все необходимые права на машину. Проверьте с помощью ручного запуска приложения, когда вошли в систему как этот пользователь.

Когда ошибка только на Windows 7 x64, это может быть ошибка где-то в фреймворке, но я предполагаю, что это индивидуальная проблема на этом ПК. (или проверьте на другом ПК W7 x64)

Так может ли это быть проблема в файловой системе.


Sagar R. Kapadia

Я проверил это, запустив процесс из Проводника windows. Он работает без проблем. И это происходит на двух компьютерах, оба с windows 7 64 бит. Я не думаю, что это может быть проблема повреждения файла. Возможно ли, что он не находит задержку загрузки dll, как это было предложено в другом месте? Однако Process monitor (sysinternals )не смог найти ни одной отсутствующей библиотеки DLL.
В реальном проекте этот код вызывается из службы windows, поэтому разрешения не являются проблемой. [Я эмулировал это с помощью psexec64 -i -s cmd.exe, а потом из cmd.exe, запустил процесс, который вызывает запускающий код.
Структура примерно такая,
существует целевое приложение [PluginHost]
библиотека DLL ATL [64 бит] [Win32ApiUtils]
и процесс запуска [ConsoleLauncher здесь,служба Windows в реальном проекте]
Процесс запуска запускается с помощью командной строки с повышенными правами [psexec64] и вызывает библиотеку dll, чтобы попытаться запустить приложение. Приложение действительно запускается успешно, но затем оно немедленно выходит из строя.
Ссылка на код на github есть
https://github.com/ks1974in/ConsoleLauncherApp.git