how to start a program using a windows service? - c++

I created a windows service in c++ using visual studios and now I want the service to run an exe file. The service is set to start every time the computer starts
i know i need to use code to locate the path of the exe like C:\MyDirectory\MyFile.exe but how to I actually run the file from the service?
i read about the process start method here but i am not sure how to use it

You can use createprocess function in your service to run an exe.
TCHAR* path = L"C:\\MyDirectory\\MyFile.exe";
STARTUPINFO info;
PROCESS_INFORMATION processInfo;
ZeroMemory( &info, sizeof(info) );
info.cb = sizeof(info);
ZeroMemory( &processInfo, sizeof(processInfo) );
if (CreateProcess(path, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
{
::WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}

Related

32bit Application run 64bit registry (with WOW6432Node)

I have a 32bit application that must call C:\Windows\System32\regedit.exe, but instead it runs C:\Windows\SysWOW64\regedit.exe. How can I call the regedit in System32?
void CSecureShellView::OnCommandsRegistry64bit()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
CString szExe;
szExe = "regedit.exe";
if (CreateProcess("C:\\Windows\\Sysnative\\cmd.exe", szExe.GetBuffer(100), 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0, &si, &pi))
{
WaitForSingleObject(pi.hProcess, IGNORE);// optionally wait for process to finish
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
The Condition returns True but regedit does not run.
Instead of SysNative, I put System32, but it does not work. In szExe, I put the string "C:\Windows\regedit", but it does not work. And so on ...
You are trying to run regedit.exe via cmd.exe, why? Any result you get will be for cmd.exe, regardless of what commands it executes.
Just run the Registry Editor instead of cmd.exe. However, there is a wrinkle.
The 64bit C:\Windows\System32 folder does not have a regedit.exe executable. The 64bit regedit.exe is located in C:\Windows instead. If a 32bit process tries to run that executable directly, the 32bit C:\Windows\SysWOW64\regedit.exe will be run, which you don't want.
In order for a 32bit process to run the 64bit regedit.exe, it needs to run C:\Windows\Sysnative\Regedt32.exe instead, which is a stub that will run the 64bit C:\Windows\regedit.exe:
void CSecureShellView::OnCommandsRegistry64bit()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
CString szExe = "C:\\Windows\\Sysnative\\Regedt32.exe";
if (CreateProcess(NULL, szExe.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE); // optionally wait for process to finish
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
If CreateProcess() fails with ERROR_ELEVATION_REQUIRED, and you don't want to run your 32bit process elevated, then try using ShellExecute/Ex() with the runas" verb to launch Regedt32.exe elevated.
That being said, you should not hard-code the path to the Windows installation folder. Ask Windows where it is actually installed, such as via GetWindowsDirectory(), SHGetFolderPath(CSIDL_WINDOWS), SHGetKnownFolderPath(FOLDERID_Windows), etc. And then you can append Sysnative\\Regedt32.exe to the end of that path.
To solve this problem I use ShellExecuteEx() and SHELLEXECUTEINFO.
HRESULT result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
SHELLEXECUTEINFO Sei;
ZeroMemory(&Sei,sizeof(SHELLEXECUTEINFO));
Sei.cbSize = sizeof(SHELLEXECUTEINFO);
Sei.lpFile = "C:\\windows\\regedit.exe";
Sei.nShow = SW_SHOW;
Sei.fMask = SEE_MASK_INVOKEIDLIST;
Sei.lpVerb = "open";
ShellExecuteEx(&Sei);
if (result == S_OK || result == S_FALSE)
CoUninitialize();
For reference: Launching Applications

WIN32 opening a folder

I want to open a folder from my code. I had previously written a visual basic program that did the job. Now I am trying to do this with C++ and WIN32 and not having any luck. Folder contains an address to a server. I tried CreateProcess and ShellExecute and they both cannot open the folder in the server. can you please help?
first method I tried
ShellExecute(NULL, "explore", "\\ftpg.tb.ch\\locations", NULL, NULL, SW_SHOWNORMAL);
second method I tried
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
LPTSTR szCmdline = _tcsdup(TEXT("C:\\Windows\\explorer.exe"));
LPTSTR pathfolder = _tcsdup(TEXT(" /e,/root,\\ftpg.tb.ch\\locations"));
CreateProcess( szCmdline, pathfolder, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
this is the previously working visual studio VB code
Dim testst As String = "\\ftpg.tb.ch\locations"
Process.Start("explorer.exe", testst)
Your translation from the VB is wrong and does not correctly escape your backslash characters. Instead of
"\\ftpg.tb.ch\\locations"
you must write
"\\\\ftpg.tb.ch\\locations"
As an aside, please use ShellExecuteEx rather than ShellExecute. The former provides proper error handling, the latter does not.

Failing to open a chrome browser via CreateProcess API

I am trying to open a chrome browser via CreateProcess API. I was not able to do so.
I tried to do like this:
string commandLine = "\"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe\"";
commandLine += " -- ";
commandLine += pURLinfo->szURL;
CreateProcess(commandLine.c_str(), NULL, NULL, NULL, FALSE,
CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInformation);
CreateProcess returned error 123.
Perhaps there is another way to open it. (i wasn't talking about ShellExecute).
Update: My code now looks like this and still i cannot run chrome.
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
if (!CreateProcessA("C:\\Program Files(x86)\\Google\\Chrome\\Application\\chrome.exe", // No module name (use command line)
NULL,
NULL, // Process handle not inheritable
NULL, // Thread handle not inhberitable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
printf("CreateProcess failed (%d).\n", GetLastError());
getchar();
return 0;
}
Try to remove unneeded quotes from command line
string commandLine = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";

pass filename from existing Process to new Process MFC c++

I'm trying to figure out how to pass filename from within an existing executable to a newly generated executable of same type & then the new exe load said file name. Following is something I'm working on but I'm bit lost really.
CString cstrExePathLoc;
GetModuleFileName(NULL, cstrExePathLoc.GetBuffer(MAX_PATH), MAX_PATH);
wchar_t szCommandLine[1024] = _T("C:\\Users\\Home\\Desktop\\testfile.tmp");
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
::ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
CreateProcess(
cstrExePathLoc, szCommandLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
&startupInfo, &processInfo
);
EDIT: this still doesn't open the file. A new ExeApp is started but no file is loaded. No errors generated at all.
I've searched net but no examples I've come across clearly explain how to do this. Any help would be appreciated. Thanks.
EDIT: simple solution here that's worked thanks to Robson Filho Colodeti below.
CString cstrExeFilePathAndFilePath2Open = cstrExePathLoc;
cstrExeFilePathAndFilePath2Open += L" \"";
cstrExeFilePathAndFilePath2Open += cstrFilePath2Open;
cstrExeFilePathAndFilePath2Open += L"\"";
CreateProcess(csExePath, cstrExeFilePathAndFilePath2Open.GetBuffer(0), NULL, NULL, TRUE, NULL, NULL, NULL, &sui, &pi);
Opening the other program
Using CreateProcess
you are in the right way, you can use the CreateProcess method.
BOOL fSuccess;
CString csDir = L"c:\your\working\directory\";
CString csParameters = L"parameter1 parameter2 parameter3 /parameter4=value";
CString csCommand = L"c:\folder\of\the\executable\executable.exe";
csCommand+= L" ";
csCommand+= csParameters;
// Create the child process.
fSuccess = CreateProcess(NULL, csCommand.GetBuffer(0), NULL, NULL, TRUE, 0, NULL,
csDir, &startupInfo, &processInfo);
Using ShellExecute
an easier way is to use the ShellExecute method because the create process method is a more "advanced" way to call a process since it gives you a lot of possibilities to control the results etc...
Reading the parameters inside the other program
then you will have to read these parameters from the other executable: check this thread

CreateProcessAsUser fails with ERROR_ELEVATION_REQUIRED for a process with UIAccess="true"

I'm trying to use the following code to run a user-mode process from my service application (running as a local system.)
The requirement for the user-mode process is to run without elevatation, but to have UIAccess="true" in its manifest to be able to display top-most windows correctly under Windows 8.
So I do this (from my service) to run my user-mode process:
//NOTE: Error checking is omitted for readability
//'dwSessionID' = user session ID to run user-mode process in
//'pUserProcPath' = L"C:\\Program Files (x86)\\Company\\Software\\user_process.exe"
HANDLE hToken = NULL;
WTSQueryUserToken(dwSessionID, &hToken);
HANDLE hToken2;
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2);
LPVOID pEnvBlock = NULL;
CreateEnvironmentBlock(&pEnvBlock, hToken2, FALSE);
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = L"winsta0\\default";
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
//ImpersonateLoggedOnUser(hToken2); //Not necessary as suggested below
PVOID OldRedir;
Wow64DisableWow64FsRedirection(&OldRedir);
BOOL bSuccess = CreateProcessAsUser(
hToken2, // client's access token
pUserProcPath, // file to execute
NULL, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, // creation flags
pEnvBlock, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
int nOSError = ::GetLastError();
Wow64RevertWow64FsRedirection(OldRedir);
//RevertToSelf(); //Not necessary as suggested below
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
DestroyEnvironmentBlock(pEnvBlock);
CloseHandle(hToken2);
CloseHandle(hToken);
This runs fine, if UIAccess="false" in the manifest for the user_process.exe.
But if I do the following:
Enable UIAccess="true" for the user_process.exe:
Sign it with my code-signing certificate and
Place it in the "C:\Program Files (x86)\Company\Software" folder.
What I get is that CreateProcessAsUser fails with the error ERROR_ELEVATION_REQUIRED.
Can someone suggest how to make it work?
PS. I tried adjusting my service's privileges to enable SE_DEBUG_NAME and SE_TCB_NAME, but neither helped.
PS2. If I simply double-click my user_process.exe process compiled with UIAccess="true", code-signed, and placed in the C:\Program Files (x86)\Company\Software folder, it starts & runs just fine (not elevated.).
I found the answer. Here it is for whoever runs into it as well:
Add this after DuplicateTokenEx call:
HANDLE hToken2;
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2);
//ONLY if requiring UIAccess!
DWORD dwUIAccess = 1;
::SetTokenInformation(hToken2, TokenUIAccess, &dwUIAccess, sizeof(dwUIAccess));