CreateProcess works on some computers, not on others. Why? - c++

I work on an app that calls another app via CreateProcess. I'm on Win7 64 bits. The called app is a console that receive data through a pipe. The calling code looks like this:
STARTUPINFOA si;
PROCESS_INFORMATION pi;
GetStartupInfoA(&si);
memset( &si, 0, sizeof(STARTUPINFOA) );
memset( &pi, 0, sizeof(pi) );
si.cb = sizeof(STARTUPINFOA);
char cmdline[MAX_PATH];
sprintf(cmdline,"\"%s\" %s",AppToCallName,PipeName);
BOOL bRet = CreateProcessA(NULL,cmdline,NULL,NULL,FALSE,CREATE_NEW_CONSOLE|CREATE_BREAKAWAY_FROM_JOB,NULL,NULL,&si,&pi);
On my computers (I tried on two), it works. On other ones, it returns (bRet=)FALSE then GetLastError() returns 5 which means ACCESS_DENIED.
I cannot figure out where is the problem. And the bad thing is that it works for me so I can't debug it!
My setup is:
Win7 Pro 64 bits SP1
VStudio 2005 SP1
(Compiler used: Intel C++ 9.1
I will be glad to furnish more setup info if you need it!
Any idea?

CreateProcess uses the same permissions as the calling process it also will terminate the process if it has not initialized properly so you should wait and verify the process launched even if CreateProcess returns success. However your access denied issue is probably related to your calling process not having execute or write permissions on whatever application you were attempting to launch on the target machine.
In addition to GetLastError when the function succeeds check GetExitCodeProcess as that will probably be your next problem.
Also for reference: http://msdn.microsoft.com/en-us/library/ms682425(v=vs.85).aspx

Related

How to check Win32 CreateProcess() failed reason using ProcMon. exclude GetLastError()

I am having an issue with checking CreateProcess() failure reason, there was a code in production which doesn't log GetLastError() when CreateProcess() failed so i am running ProcMon to check the reason but unable to find the reason (Will procMon log the failure reason something like "C:\dummy.exe path not found or permission denied" ?).
Is there a way (tools ?) to check why CreateProcess() is failing without considering GetLastError() ?
I can't debug customer environment (no access to me) but I can change the code & provide new build & it takes long time due to process. i am currently looking for quick options available. Below is the sample code not exact production code.
int main()
{
STARTUPINFO info = { sizeof(info) };
PROCESS_INFORMATION processInfo;
TCHAR dymmypath[_MAX_PATH] = _T("C:\\dummy.exe");
static TCHAR TempPathString[_MAX_PATH];
STARTUPINFO si = { sizeof(si) }; //default set up
PROCESS_INFORMATION pi; //data structure for CreateProcess
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWMINIMIZED;
if (!CreateProcess(dymmypath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, TempPathString, &si, &pi))
{
printf("Failed");
}
else {
printf("Success");
}
return 0;
}
i am running ProcMon to check the reason but unable to find the reason (Will procMon log the failure reason something like "C:\dummy.exe path not found or permission denied" ?).
Only if the request reaches the filesystem, ie to look for the EXE file, which in your case it sounds like it is not doing that, likely because CreateProcess() is failing to validate your input parameters before it reaches into the filesystem.
Is there a way (tools ?) to check why CreateProcess() is failing without considering GetLastError() ?
As others said, you could try attaching a debugger to your running app, and put a breakpoint in the CreateProcess function itself.
Another option is to use a tool like API Monitor, which will show you the actual API calls your program makes, what their parameter values are, reported error codes, etc.
I can't debug customer environment (no access to me) but I can change the code & provide new build
Then that is what you should do. Fix your code to do proper logging of error codes, don't ignore them anymore.
it takes long time due to process.
Well, that is your own fault for not optimizing your build process better, or breaking up your app into more manageable pieces, etc.
Just at first glance, I see TempPathString is initialized to "", which is not a valid path. So while you're fixing that issue, that's your chance to add proper error handling.
The tool you're looking for is a debugger. You should attach the debugger of your choice, set a breakpoint on the return of CreateProcess, and check the error there.
Besides debugging and error handling (logging etc), you'll have to just get creative. Compare a working environment against production for example.

Issues trying to display a configuration window for my screensaver (from a GUI app running with high mandatory integrity level)

I have an old project that comes with its own screensaver. The GUI application for the project has an option to "Configure screensaver" that should bring up my screensaver configuration window (that you would normally get from a Control Panel -> Display -> Personalization -> Screensaver -> Settings.)
To display the configuration window, one needs to call the screensaver process with the /c parameter as described here. The screensaver itself is placed into the system directory, i.e. "C:\Windows\System32".
So to emulate this from my (32-bit) GUI application I do the following:
//Error checks are omitted for brevity
BOOL bResult = FALSE;
TCHAR buffSysFldr[MAX_PATH];
buffSysFldr[0] = 0;
::GetSystemDirectory(buffSysFldr, SIZEOF(buffSysFldr));
//Make the path, which basically becomes:
// "C:\Windows\System32\mysvr.scr" /c
TCHAR buff[MAX_PATH];
buff[0] = 0;
::StringCbPrintf(buff, sizeof(buff), L"\"%s\\mysvr.scr\" /c", buffSysFldr);
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
ZeroMemory(&si,sizeof(si));
ZeroMemory(&pi,sizeof(pi));
si.cb = sizeof(si);
PVOID OldValue;
Wow64DisableWow64FsRedirection(&OldValue);
//And run it
if(CreateProcess(NULL, buff, NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
NULL, NULL, &si, &pi))
{
//Success
bResult = TRUE;
}
Wow64RevertWow64FsRedirection(OldValue);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
When I deploy it to the Windows 7 machine, the code above returns bResult = TRUE but my screensaver's configuration window is never shown.
I tried it on both 32-bit and 64-bit Windows 7 thinking that Wow64 redirection has something to do with it, but it still didn't change the outcome.
Any idea why this doesn't work?
PS. When I copy and paste that command into a command prompt it runs just fine and displays my configuration window w/o a problem:
EDIT: After further review, it turns out that the issue seems to be connected to the fact that my GUI applications runs with High Mandatory Integrity Level, or S-1-16-12288, which seems to be the reason why the screensaver's configuration window is not shown. If I run my GUI app with its regular Medium integrity level, or S-1-16-8192, the configuration window shows OK.
Otherwise, I can see that my screensaver's RegisterDialogClasses method is called OK, but then when I return TRUE from it, ScreenSaverConfigureDialog is never called.
Since integrity level was introduced to prevent processes with lower levels to send messages to processes with higher levels, evidently the screensaver configuration mechanism tries to send some messages to my GUI app (for whatever undocumented reason) and fails and then silently fails as well ...
So this is the latest. I'll appreciate any input on this from whoever knows how to solve this (apart from lowering the integrity level of my GUI app, that I obviously don't want to do.)
God damn Microsoft documentation (sorry, I just wasted the whole day today trying to fix it.)
For whoever else runs into this -- it turns out one needs to call it as such:
"C:\Windows\System32\mysvr.scr" /c:N
where N is a window handle of the parent window expressed as an integer. Found it out by checking command line of the configuration window displayed via Control Panel (did it using Process Explorer.)
In my case, since my GUI process runs with higher integrity level, doing the following will suffice:
HWND hWndToUse = ::GetDesktopWindow();
::StringCbPrintf(buff, sizeof(buff), L"\"%s\\mysvr.scr\" /c:%d", buffSysFldr, (int)hWndToUse);
And that's it!

How to set PROCESS_SET_QUOTA to process?

I want to use SetProcessWorkingSetSize function, and on MSDN i see this:
"The handle must have the PROCESS_SET_QUOTA access right. For more information, see Process Security and Access Rights."
So, how can i set PROCESS_SET_QUOTA to process handle?
I want to write program that runs executable with working set limits, so there is main piece of code:
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
GetStartupInfo(&si);
si.dwFlags = 0;
PROCESS_INFORMATION pi;
if (!CreateProcess(
exePath.c_str(),
cmdParametersBuffer,
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi))
{
cout << "error" << endl;
}
SetProcessWorkingSetSize(pi.hProcess, 20 * 4 * 1024, 100*1024*1024);
Did you actually try the code you have showed and it is not working for you? If so, what error is GetLastError() reporting?
If you read the documentation, it says:
Process Security and Access Rights
PROCESS_ALL_ACCESS
All possible access rights for a process object.
...
The handle returned by the CreateProcess function has PROCESS_ALL_ACCESS access to the process object
So you should be able to call SetProcessWorkingSetSize() after CreateProcess() exits, exactly like you have showed, without doing anything extra to enable PROCESS_SET_QUOTA rights, as it should already be enabled.
Your example code is calling SetProcessWorkingSetSize() successfully, as the error you get is 0. If you got an error like 0x522 ERROR_PRIVILEGE_NOT_HELD then you'd know the call failed.
It might help to know that an app is expected to be able to allocate more memory than its the working set size. The OS will page out memory from RAM. If you use Task Manager to view the Working Set for your process, is it actually exceeding the quota you set?
You might also need to use SetProcessWorkingSetSizeEx wth flag QUOTA_LIMITS_HARDWS_MAX_ENABLE to force the OS to actually apply your setting.

ERROR_INVALID_HANDLE on TerminateProcess (VS C++)

Disclaimer: This is part of the requirement of the program, so it's not meant for anything bad. Feel free to point out any misuse if you spot one. I'm a beginner in C++.
Basically, I'm trying to restart Outlook.exe on Windows using C++.
And this is the code I used to restart Outlook.
#include <TlHelp32.h>
void RestartOutlook() {
PROCESSENTRY32 Pc = { sizeof(PROCESSENTRY32) };
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
MODULEENTRY32 Mo = {sizeof (MODULEENTRY32) };
if(Process32First(hSnapshot, &Pc)){
do{
if(!_stricmp(Pc.szExeFile, "outlook.exe")) {
DWORD pid = Pc.th32ProcessID;
HANDLE hModuleSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
//kill outlook
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
DWORD fdwExit = 0;
GetExitCodeProcess(process, &fdwExit);
TerminateProcess(process, fdwExit);
char * path;
if (Module32First(hModuleSnapshot, &Mo)) {
path = Mo.szExePath;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof (si);
CreateProcess(path, NULL, NULL, NULL, false, NORMAL_PRIORITY_CLASS,
NULL, NULL, &si, &pi);
}
}
}while(Process32Next(hSnapshot, &Pc));
}
}
The funny part is, this piece of code works perfectly fine on Windows 7. While on Windows XP (SP3), I get duplicated Outlook. The GetLastError gives me 6: ERROR_INVALID_HANDLE. I am really clueless after hours of research.
Any idea?
Anyway, C++ is not my field. I do webs :)
And the code above is a mixture of the following sources:
1: http://www.istorya.net/forums/programming/107435-how-can-i-kill-a-process-using-c.html
2: http://code.activestate.com/recipes/576362-list-system-process-and-process-information-on-win/
Environment: Windows 7, Windows XP, VS2010, Outlook 2003, Outlook 2007, Outlook 2010
I found the culprit.
The reason lies in this line:
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
According to http://msdn.microsoft.com/en-us/library/ms684880(v=vs.85).aspx, PROCESS_ALL_ACCESS is too large for Windows XP/NT system, or more details:
The size of the PROCESS_ALL_ACCESS flag increased on Windows Server 2008 and Windows Vista. If an application compiled for Windows Server 2008 and Windows Vista is run on Windows Server 2003 or Windows XP/2000, the PROCESS_ALL_ACCESS flag is too large and the function specifying this flag fails with ERROR_ACCESS_DENIED. To avoid this problem, specify the minimum set of access rights required for the operation.
Definitely I'm compiling this program on 7, while running on XP definitely causing the problem.
So the solution is, change the PROCESS_ALL_ACCESS to PROCESS_TERMINATE, which
HANDLE process = OpenProcess(PROCESS_TERMINATE, TRUE, pid);
Done!
Thanks #DReJ for quick replies :)
I get that you want Outlook to restart but calling TerminateProcess on Outlook seems like a bad idea in the first place. What if it's in the middle of writing a data file?
A better way would be to find all top-level windows that belong to Outlook, send them a WM_CLOSE and then wait for the process to exit. (You may also have to cope with the user having draft messages open which result in "are you sure" prompts, although if you are doing this in the first place then I assume you know the user isn't in the middle of something?)
An even better way would be to use Outlook's automation interface and tell it to shutdown explicitly.
Your problem maybe related to this piece of code
DWORD fdwExit = 0;
GetExitCodeProcess(process, &fdwExit);
TerminateProcess(process, fdwExit);
First, with GetExitCodeProcess you get status STILL_ACTIVE and after that you terminate process with this status which is not proper I think. Remove GetExitCodeProcess from you code and try TerminateProcess(process, 0); instead.

Trouble restarting exe

I need to restart the program that im working on after an update has been downloaded except im running into some issues.
If i use CreateProcess nothing happens, if i use ShellExecute i get an 0xC0150002 error and if i use ShellExecute with the command "runas" it works fine. I can start the command prompt fine using CreateProcess and ShellExecute just not the same exe again and dont want to use runas as this will elevate the exe.
Any Ideas?
Windows 7, visual studio 2008 c++
alt text http://lodle.net/shell_error.jpg
CreateProcess:
char exePath[255];
GetModuleFileName(NULL, exePath, 255);
size_t exePathLen = strlen(exePath);
for (size_t x=exePathLen; x>0; x--)
{
if (exePath[x] == '\\')
break;
else
exePath[x] = '\0';
}
char name[255];
GetModuleFileName(NULL, name, 255);
PROCESS_INFORMATION ProcInfo = {0};
STARTUPINFO StartupInfo = {0};
BOOL res = CreateProcess(name, "-wait", NULL, NULL, false, 0, NULL, exePath, &StartupInfo, &ProcInfo );
ShellExecute:
char exePath[255];
GetModuleFileName(NULL, exePath, 255);
size_t exePathLen = strlen(exePath);
for (size_t x=exePathLen; x>0; x--)
{
if (exePath[x] == '\\')
break;
else
exePath[x] = '\0';
}
char name[255];
GetModuleFileName(NULL, name, 255);
INT_PTR r = (INT_PTR)ShellExecute(NULL, "runas", name, "-wait", exePath, SW_SHOW);
CreateProcess() is an arcane beast. I remember unfondly my first frustrations with it. You should look at the Microsoft CreateProcess Example and the CreateProcess Page. (those links likely have a short lifetime, Googling CreateProcess should work just as well).
I can see 3 problems in your code.
StartupInfo must have "cb" set to the structure size:
STARTUPINFO StartupInfo = {0};
StartupInfo.cb = sizeof(StartupInfo);
The second argument requires both the command and the arguments to form the command line. Your program will see "-wait" as argv[0] and ignore it or pay it no mind.
char command[512];
sprintf(command, "%s -wait", name);
BOOL res = CreateProcess(name, command, // and as you had before
You don't look at GetLastError() if CreateProcess() fails (by returning a zero). It may have helped you but I suspect it would just say "invalid argument" or somesuch. Hey, there's only 10 of them to check, don't be lazy :-)
Another bug I committed is not closing the hProcess and/or hThread handles return in PROCESS_INFORMATION when I was done. I did do hProcess, but not hThread.
Looks like a manifest or registry question judging from the error code. If you can't get the actual error message string for more details, you might try:
moving every possible manifest file (Microsoft.VC80.CRT.manifest and the like) into your exe's directory, to ensure accessibility
cleanly and completely uninstall/wipe out old versions of DLL you may have installer newer versions of (I suggest: uninstall EVERY version, clean the registry with a sweep-clean tool such as Norton's, reinstall the new stuff from scratch)
What happens if you run the process using system()? It gives you less control, but you'll be running it from the same context you're running in. Also, Try monitoring the launch of your second process using ProcMon, it might give you the hint you need about the source of the failure.
Ok worked it all out in the end.
The first time my exe ran it used the default paths and as such loaded vld (a leak detector dll) from the default path. However in the exe i modified the dll path to be the bin folder ([app]\bin) when i restarted the exe using CreateProcess it picked up on a different vld dll (this was my mistake) that had incorrect side by side linkage and it was only after looking at event viewer that i worked it out.
Thanks for all your help.