Click here to Skip to main content
15,886,518 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a C# Application, which is launched from a C++ DLL using CreateProcessAsUserW api. The process is launched successfully, but terminates immediately. It works properly on Windows 10 [both as 32 bit and 64 bit] and 32 bit on Windows 7.
Please help.
Thanks,
Sagar
The method is as follows
//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;
}


What I have tried:

I found the following link,

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

however, process monitor from SysInternals finds no missing dll. [I can attach the saved logs from ProcMon] I also tried passing the path to the application folder, as suggested elsewhere, in the lpCurrentDirectory parameter of the API, but that did not work either.

I followed the guidance in /https://stackoverflow.com/questions/14315013/how-to-get-the-active-user-when-multiple-users-are-logged-on-in-windows to write the code which launches the process,
and the method which launches the process is called from a windows service.To emulate that from the command line, I used the following
https://stackoverflow.com/questions/77528/how-do-you-run-cmd-exe-under-the-local-system-account [psexec64 -i -s cmd.exe, and then launched the process from cmd.exe]
Posted
Updated 20-Nov-18 19:29pm
v2
Comments
Richard Deeming 6-Nov-18 14:17pm    
Sounds like there's an unhandled exception being thrown by your code. You'll need to catch it and log the details somewhere to find out what's going wrong.
Sagar R. Kapadia 7-Nov-18 6:20am    
The exception occurs before the dot net code starts. I have already added exception handlers in the target application, but they are not called at all. However,the event viewer shows the above exception
Dave Kreskowiak 7-Nov-18 12:00pm    
Let me get this straight. You're trying to launch a user-interactive C# application on the users desktop from a Windows Service app written in C/C++?
Sagar R. Kapadia 8-Nov-18 6:32am    
The c# app [its not user-interactive, but interacts with other that are] is launched from a c# windows service, from an C++ / ATL dll [which uses the winapi to launch the process as a user]. It [and others related to it] [32 bit and 64 bit both] works fine on windows 10, and the 32 bit version of the service and this app work fine in windows 7, but the 64 bit process crashes immediately after launch. [The process is created properly, I can see it in task manager, and then there is a message saying that the app has crashed and should this be reported to microsoft, and then it vanishes from the task manager.
Dave Kreskowiak 8-Nov-18 11:57am    
well, I can't answer why it's failing on Win7x64, but I can give you my opinion of why what you're doing is a bad idea.

Services should not interact with users desktops nor launch processes as logged in users. Period. Looking at the history of how Microsoft treats this situation, from back in the day when this used to be common and pretty much unrestricted to security being tightened around it more and more, your better option would be to NOT launch a process from the service.

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.

You also get the benefit of having your app compatible with multi-user environments, like Citrix, future proofing against more restrictions on service interaction, and better supportability of your app.

You havent written about the user rights which gets the process. It should be a valid user on the machine who has all needed rights on the machine. Check with manual start of the app, when logged on as this user.

When the error only is on Windows 7 x64 it can be a bug somewhere in the framework, but I guess that it is a individual problem on that pc. (or check on anotherW7 x64 pc)

So can it be a problem in the file system.
 
Share this answer
 
Comments
Sagar R. Kapadia 7-Nov-18 6:30am    
I checked by running the process from windows explorer. It runs without problems. And this happens on two pcs, both windows 7 64 bit. I dont feel that it might be a file damage problem. Is it possible that it is not finding a delay loaded dll as suggested elsewhere? Process monitor (sysinternals )was unable to find any missing dlls, however.
In the actual project, this code is called from a windows service, so permissions are not an issue. [I emulated this using psexec64 -i -s cmd.exe, and then from cmd.exe, ran the process which calls the launching code.
The structure is somewhat like this,
there is the target application [PluginHost]
an ATL DLL [64 bit] [Win32ApiUtils]
and a launcher process [ConsoleLauncher here,Windows Service in actual project]
The launcher process is launched using an elevated command prompt [psexec64] and it calls the dll to try to launch the app. The app does launch successfully, but then it crashes immediately.
The link to the code on github is
https://github.com/ks1974in/ConsoleLauncherApp.git
I got the answer on 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 answered it] There was a problem with the way in which the process was being launched. His code for launching the process is as follows, and it works fine.
C++
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;
}
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900