I have a program that attempts to restart Explorer in Windows 7; it first ends the process, then starts it again.
Simple?
So it looks -- it indeed seems to work. However, I noticed something funny: When I try to start a program (say, XP's Power Calculator powertoy) under compatibility mode, it no longer works! Nothing happens -- the process is created and immediately quits.
If I run Explorer through the Run dialog of Task Manager, the program runs fine.
What's going on?!
#include <windows.h>
int main()
{
PROCESS_INFORMATION pi;
STARTUPINFO si = {sizeof(si)};
TCHAR path[] = TEXT("explorer");
DWORD f = 0; //I tried a variety of these flags; didn't work
return CreateProcess(NULL, path, NULL, NULL, FALSE, f, NULL, NULL, &si, &pi);
}
Edit 1:
The fact that it was 32-bit with WOW64 redirection disabled made no difference. I made it 64-bit and the issue was the same.
I tried ShellExecute but it didn't work either.
Edit 2:
The same exact code just worked for me a couple of times, and then stopped working again... huh?
This isn't really an answer to the "why", but I managed to find out how to fix it myself:
Instead of copying environmental variables from the current process, if I copy them with CreateEnvironmentBlock, then it works.
I still haven't figured out what's causing it, though...
Related
I need 20 'H' characters to appear at 1 second intervals. This program displays nothing till the end
use std::io::Write;
use std::time::Duration;
fn main() {
let ch = 'H' as u8;
for _ in 0..20 {
let buff = vec![ch];
std::io::stdout().write(&buff).unwrap();
std::io::stdout().flush().unwrap();
std::thread::sleep(Duration::from_millis(1000));
}
}
This equivalent C++ program works fine
#include <iostream>
#include <Windows.h>
int main()
{
for(int i = 0; i < 20; i++)
{
std::cout << (char)'H';
std::cout.flush();
Sleep(1000);
}
}
I tried crossterm and could not make that work but it looks really interesting. I tried crossterm_winapi which has direct calls to WriteConsoleW in it, but then I got no output at all.
I am going to answer my own question because I went through a lot of discovery here and I am sure other will find it useful.
BTW - the original question got derailed in the comments section explaining why my c++ equivalent (without flush) worked / didnt work / shouldnt work / might work. Lets ignore that - I added flush.
Firstly Debuggers
vscode and Vs2019 do stuff with stdout in order to try to be helpful. Most of the time it is helpful but not in this case
vscode steals stdout to make it appear in the terminal window. Normally this would be directed to a console window. It also imposes its own buffering regardless of what you do with flushing etc.
The real end for my rust code is a c# gui application calling the rust code as a cdylib. Originally this dll was written in c++. When running a GUI app vs2019 also steals stdout (not the case for console apps, nothing to do with my mini c++ code above) so that it can display it the 'output' window.
Both these behaviors disappear if the app is run outside the debugger of course. But that makes debugging tough.
This of course confused a lot of my investigation because I was not seeing output in various cases because I was looking in the wrong place.
Solution 1
The original code in my question actually works fine if you run it via cargo run from a console window. Excellent. vscode buffers it all up if ran from inside it - so dont do that.
Problem 2, no console window
Windows gui app launch 'knows' that this program doesnt need a console window so stdout is piped to a bitbucket. I had the same issue in the c# -> c++ version. The secret here is to create a console window and reattach stdout to it.
solution 2
Reaches for winapi crate.
unsafe {
let ff = winapi::um::wincon::FreeConsole();
let ret = winapi::um::consoleapi::AllocConsole();
let err = winapi::um::errhandlingapi::GetLastError();
};
FreeConsole just to detach if we are connected to something weird. Then AllocConsole creates a console window and connects the process to it, not wired up to anything yet though. So now wire the new console window to stdout:
let file = OpenOptions::new().write(true).read(true).open("CONOUT$").unwrap();
unsafe{
let err = winapi::um::processenv::SetStdHandle(winapi::um::winbase::STD_OUTPUT_HANDLE, file.as_raw_handle());
}
CONOUT$ is special name for the output buffer of the connected console window. SetStdHandle connects it to stdout.
Now a new window pops up next to the gui and all stdout writes appear there - whoo hoo.
This also works for vscode debugging. A new 'dos' window appears and the stdout appears there. An added bonus
Problem 3 need lower level access
In my original PDP engine in c++ I was not happy still using std::cout, I needed to get as close the the windows console subsystem as possible (I am emulating the behavior of a raw physical device for the PDP). So I decided to use direct windows IO in this new version
Solution 3
So still AllocConsole and open CONOUT$ but call the Console write directly
let buff: [u8; 1] = [ch];
let ptr: *const c_void = buff.as_ptr() as *const _ as *const c_void;
let mut cells_written: u32 = 0;
// write to console
unsafe {
WriteConsoleA(
file.as_raw_handle(),
ptr,
1,
&mut cells_written,
NULL,
) ;
};
file is the handle return by open of CONOUT$
Works nicely. I know I will need to do the same for stdin / CONIN$ at some point
Suppose your Windows user account is in the Admin group, UAC is enabled, and you're running some program A with normal user privileges. A never asks for elevation and never receives it. Now suppose A wants to launch program B, which has highestAvailable in its manifest.
If A calls CreateProcess(B), this will fail with error 740 ("elevation required")
If A calls ShellExecuteEx(B), Windows will display a UAC box asking to run B elevated. The user can say Yes, in which case B will run elevated, or No, in which case the launch will fail.
My question is: is there any way to achieve a third option, where we simply launch B without elevation?
It seems like this should be possible in principle, since "highestAvailable" means that B prefers to run with elevation but is perfectly capable of running in normal user mode. But I can't figure out any way to accomplish it. I've tried all sorts of things with tokens and CreateProcessAsUser(), but it all seems to come down to this: "highestAvailable" seems to unalterably refer to the latent privileges inherent in the user account, not the actual privileges expressed in any explicitly constructed token.
I'm hoping that there actually is some way to use CreateProcessAsUser() to do this, and that I'm just missing the trick for properly constructing the token.
Update - solved: the __COMPAT_LAYER=RunAsInvoker solution below works nicely. One caveat, though. This coerces the subprocess to run "as invoker" unconditionally: it applies even if the exe being invoked specifies "requireAdministrator" in its manifest. I think the original "elevation required" error is generally preferable when the exe specifies "requireAdministrator". The whole reason I wanted the RunAsInvoker behavior for programs marked with "highestAvailable" is that such programs are expressly saying "I can function properly in either mode" - so let's go ahead and run in normal user mode when it's inconvenient to use Admin mode. But "requireAdministrator" is a different matter: such programs are saying "I can't function properly without elevated privileges". It seems better to fail up front for such programs than to force them to run un-elevated, which might make them encounter privilege/access errors that they're not properly programmed to handle. So I think a complete, general-purpose solution here would require checking the application manifest, and only applying the RunAsInvoker coercion if the manifest says "highestAvailable". An even completer solution would be to use one of the techniques discussed elsewhere to give the caller an option to invoke UAC when presented with a "requireAdministrator" program and offer the user a chance to launch it elevated. I can imagine a CreateProcessEx() cover with a couple of new flags for "treat process privileges as highest available privileges" and "invoke UAC if elevation is required". (The other approach described below, hooking NTDLL!RtlQueryElevationFlags() to tell CreateProcess() that UAC is unavailable, has exactly this same caveat with respect to requireAdministrator programs.)
(It's probably telling that the Windows shell doesn't even offer a way to do this... launching B directly from the shell would give you the UAC box that lets you either launch with Admin privs or not launch at all. If there were any way to accomplish it, the UAC box might offer a third button to launch without privileges. But then again that could just be a UX decision that the third option is too confusing for civilians.)
(Note that there are quite a lot of posts on StackOverflow and the Microsoft dev support sites asking about a very similar-seeming scenario that unfortunately doesn't apply here. That scenario is where you have a parent program that's running elevated, and it wants to launch a non-elevated child process. The canonical example is an installer, running elevated as installers tend to do, that wants to launch the program it just installed, at normal user level, just before it exits. There's lots of posted code about how to do that, and I've based my attempts on some of those techniques, but this is really a different scenario and the solutions don't work in my situation. The big difference is that the child program they're attempting to launch in this case isn't marked with highestAvailable - the child is just a normal program that would launch without any UAC involvement under normal circumstances. There's another difference as well, which is that in those scenarios, the parent is already running elevated, whereas in my scenario the parent is running as normal user level; that changes things slightly because the parent process in this other scenario has access to a few privileged operations on tokens that I can't use because A isn't itself elevated. But as far as I can tell those privileged token operations wouldn't help anyway; it's the fact that the child has the highestAvailable flag that's the key element of my scenario.)
Set the __COMPAT_LAYER environment variable to RunAsInvoker in your process. I don't think this is formally documented anywhere but it works all the way back to Vista.
You can also make it permanent by setting the it under the AppCompatFlags\Layers key in the registry.
the possible hack solution call CreateProcess from not elevated admin user (restricted admin) for exe with highestAvailable in manifest (or from any not elevated user for requireAdministrator exe) - this is hook RtlQueryElevationFlags call and set returned flags to 0.
this is currently work, but of course no any grantee that will be work in next versions of windows, if something changed. however as is.
for hook single time api call - we can set hardware breakpoint to function address and VEX handler . demo working code:
NTSTATUS NTAPI hookRtlQueryElevationFlags (DWORD* pFlags)
{
*pFlags = 0;
return 0;
}
PVOID pvRtlQueryElevationFlags;
LONG NTAPI OnVex(::PEXCEPTION_POINTERS ExceptionInfo)
{
if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP &&
ExceptionInfo->ExceptionRecord->ExceptionAddress == pvRtlQueryElevationFlags)
{
ExceptionInfo->ContextRecord->
#if defined(_X86_)
Eip
#elif defined (_AMD64_)
Rip
#else
#error not implemented
#endif
= (ULONG_PTR)hookRtlQueryElevationFlags;
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
ULONG exec(PCWSTR lpApplicationName)
{
ULONG dwError = NOERROR;
if (pvRtlQueryElevationFlags = GetProcAddress(GetModuleHandle(L"ntdll"), "RtlQueryElevationFlags"))
{
if (PVOID pv = AddVectoredExceptionHandler(TRUE, OnVex))
{
::CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
ctx.Dr7 = 0x404;
ctx.Dr1 = (ULONG_PTR)pvRtlQueryElevationFlags;
if (SetThreadContext(GetCurrentThread(), &ctx))
{
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
if (CreateProcessW(lpApplicationName, 0, 0, 0, 0, 0, 0, 0, &si,&pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
dwError = GetLastError();
}
ctx.Dr7 = 0x400;
ctx.Dr1 = 0;
SetThreadContext(GetCurrentThread(), &ctx);
}
else
{
dwError = GetLastError();
}
RemoveVectoredExceptionHandler(pv);
}
else
{
dwError = GetLastError();
}
}
else
{
dwError = GetLastError();
}
return dwError;
}
I am struggling to find a solution to my problem, but I simply have no clue how to solve it.
I am creating an user-interface for some programs I made (so you can through simply pressing a button start an executable).
So I thought of using qt.
So I read a lot about the QProcess and tried to use it.
At the first executable of mine I tried to start it through QProcess::start(), but it didn't work so I tried it with QProcess:execute():
QProcess *proc = new QProcess(this);
QDir::setCurrent("C:\\DIRTOTHEEXE\\");
QString program="HELLO.exe";
proc->execute(program);
This executes my program perfectly and works nice.
So I tried to do the same with my other exe, but it didn't work
QProcess *myproc = new QProcess(this);
QDir::setCurrent("C:\\DIRTOTHEEXE\\");
QString program="HelloWorld.exe";
myproc->start(program);
The called executable simply prints "Hello World" and returns 0 then.
So now my question is: What could cause this behaviour and why can't I use QProcess::start() for the first executable?
Btw: I also tried to set the workingDirectory() to the path of the exe, but also that didn't work.
Hope someone can help me.
EDIT:
So the program is executed but crashes right after printing out one line.
EDIT: Here the HelloWorld source.
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
cout<<"HELLO WORLD!!"<<endl;
return 0;
}
QProcess has 3 functions for starting external processes, such as: -
start
execute
startDetached
The latter two, execute and startDetached are static, so don't need an instance of QProcess to call them.
If you use start, you should at least be calling waitForStarted() to let the process setup properly. The execute() function will wait for the process to finish, so calling waitForStarted is not required.
As you've only posted a small amount of code, we can't see exactly what you're trying to do afterwards. Is that code in a function that ends, or are you trying to retrieve the output of the process? If so, you definitely should be calling waitForStarted if using start().
If you only want to run the process without waiting for it to finish and your program is not bothered about interacting with the process, then use startDetached: -
QProcess::startDetached("C:\\DIRTOTHEEXE\\HELLO.exe");
I'm working on an automation program, and the WaitForInputIdle function helps me determine when the window of the target app is done initializing. The problem is that, in my case, it works only with the first window - that's how WaitForInputIdle works, only once.
Could the functionality of WaitForInputIdle be implemented in a different way, such that it could be called every time the target process is busy, and wait until it's done?
I thought about posting a dummy message, but I don't think there's a way to find out when it gets removed from the queue.
Edit: I came up with something, an ugly and hacky solution that seems to work:
RECT rc;
if(!GetUpdateRect(hWnd, &rc, FALSE))
{
rc.left = rc.top = 0;
rc.right = rc.bottom = 1;
InvalidateRect(hWnd, &rc, FALSE);
}
do {
Sleep(100);
} while(GetUpdateRect(hWnd, &rc, FALSE));
I really hope there's something better than that.
Edit: sending WM_NULL, as suggested by Raymond Chen, works for me.
It appears that the PrintWindow hack has no advantages, as internally it just sends the WM_PAINT message.
Old message: I came up with a solution that solves my problem, still a hack but not as ugly.
The idea is misusing the PrintWindow function, which basically posts sends a WM_PAINT message and waits for the window to process it - exactly what I need.
Below is the code with some informative comments.
It was tested on Windows XP and Windows 8 and it works as expected, i.e. doesn't fail despite the NULL HDC value.
// BEWARE: HACK BELOW
// PrintWindow is misused here as a synchronization tool
// When calling it, the system sends WM_PAINT and waits for it to be processed
// Note: if hWnd is hung, the following call will hang as well
PrintWindow(hWnd, NULL, 0);
I have a DLL that's loaded into a 3rd party parent process as an extension. From this DLL I instantiate external processes (my own) by using CreateProcess API. This works great in 99.999% of the cases but sometimes this suddenly fails and stops working permanently (maybe a restart of the parent process would solve this but this is undesirable and I don't want to recommend that until I solve the problem.) The failure is symptomized by external process not being invoked any more even though CreteProcess() doesn't report an error and by GetExitCodeProcess() returning 128. Here's the simplified version of what I'm doing:
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
if(!CreateProcess(
NULL, // No module name (use command line).
"<my command line>",
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
FALSE, // Set handle inheritance to FALSE.
CREATE_SUSPENDED, // Create suspended.
NULL, // Use parent's environment block.
NULL, // Use parent's starting directory.
&si, // Pointer to STARTUPINFO structure.
&pi)) // Pointer to PROCESS_INFORMATION structure.
{
// Handle error.
}
else
{
// Do something.
// Resume the external process thread.
DWORD resumeThreadResult = ResumeThread(pi.hThread);
// ResumeThread() returns 1 which is OK
// (it means that the thread was suspended but then restarted)
// Wait for the external process to finish.
DWORD waitForSingelObjectResult = WaitForSingleObject(pi.hProcess, INFINITE);
// WaitForSingleObject() returns 0 which is OK.
// Get the exit code of the external process.
DWORD exitCode;
if(!GetExitCodeProcess(pi.hProcess, &exitCode))
{
// Handle error.
}
else
{
// There is no error but exitCode is 128, a value that
// doesn't exist in the external process (and even if it
// existed it doesn't matter as it isn't being invoked any more)
// Error code 128 is ERROR_WAIT_NO_CHILDREN which would make some
// sense *if* GetExitCodeProcess() returned FALSE and then I were to
// get ERROR_WAIT_NO_CHILDREN with GetLastError()
}
// PROCESS_INFORMATION handles for process and thread are closed.
}
External process can be manually invoked from Windows Explorer or command line and it starts just fine on its own. Invoked like that it, before doing any real work, creates a log file and logs some information about it. But invoked like described above this logging information doesn't appear at all so I'm assuming that the main thread of the external process never enters main() (I'm testing that assumption now.)
There is at least one thing I could do to try to circumvent the problem (not start the thread suspended) but I would first like to understand the root of the failure first. Does anyone has any idea what could cause this and how to fix it?
Quoting from the MSDN article on GetExitCodeProcess:
The following termination statuses can be returned if the process has terminated:
The exit value specified in the
ExitProcess or TerminateProcess
function
The return value from the
main or WinMain function of the
process
The exception value for an
unhandled exception that caused the
process to terminate
Given the scenario you described, I think the most likely cause ist the third: An unhandled exception. Have a look at the source of the processes you create.
Have a look at Desktop Heap memory.
Essentially the desktop heap issue comes down to exhausted resources (eg starting too many processes). When your app runs out of these resources, one of the symptoms is that you won't be able to start a new process, and the call to CreateProcess will fail with code 128.
Note that the context you run in also has some effect. For example, running as a service, you will run out of desktop heap much faster than if you're testing your code in a console app.
This post has a lot of good information about desktop heap
Microsoft Support also has some useful information.
There are 2 issues that i could think of from your code sample
1.Get yourusage of the first 2 paramaters to the creatprocess command working first. Hard code the paths and invoke notepad.exe and see if that comes up. keep tweaking this until you have notepad running.
2.Contrary to your comment, If you have passed the currentdirectory parameter for the new process as NULL, it will use the current working directory of the process to start the new process from and not the parent' starting directory.
I assume that your external process exe cannot start properly due to dll dependencies that cannot be resolved in the new path.
ps : In the debugger watch for #err,hr which will tell you the explanation for the last error code,