ShellExecute bat file elevated (FMX, Win32) - winapi

I want to spawn a batch file from my FMX app (on Win32) with elevated privileges. From Remy's answer at the bottom of this thread on ShellExecute I found how to launch the batch file. Now, i can't figure out how to launch it with elevated privilege. Below is my code:
String Prog = "c:\\Users\\rwp\\Desktop\\test.bat";
int nErrorCode = (int) ShellExecute(NULL, L"runas", Prog.c_str(), NULL, NULL, SW_SHOWNORMAL);
if (nErrorCode <= 32) {
ShowMessage("an error occured");
}
I added "runas" for the second argument after reading this to no avail. Running the batch file manually (right-click and run as admin) works. Here is content of the batch file fyi (just kicks of a system imaging):
c:\Windows\system32\wbAdmin.exe start backup -backupTarget:D: -include:C: -allCritical -quiet
How can i ShellExecute this batch file as admin?
UPDATE 1: I'm attempting to use CreateProcess per Remy suggestion. Here is my code (based on this example):
//Code is inside a __fastcall button click
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.lpReserved = NULL;
siStartInfo.lpReserved2 = NULL;
siStartInfo.cbReserved2 = 0;
siStartInfo.lpDesktop = NULL;
siStartInfo.dwFlags = 0;
// String strCmdLine = "C:\\Users\\rwpatter\\Desktop\\test.bat";
String strCmdLine = "C:\\Windows\\System32\\wbAdmin.exe start backup -backupTarget:T: -include:C: -allCritical -quiet";
// Create the child process.
int rtrn = CreateProcess(
NULL,
strCmdLine.c_str(),
NULL, // process security attributes
NULL, // primary thread security attributes
0, // handles are inherited
0, // creation flags
0, // use parent's environment
0, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// Wait for the processs to finish
DWORD rc = WaitForSingleObject(
piProcInfo.hProcess, // process handle
INFINITE);
ShowMessage(IntToStr(rtrn));
If I run it as shown (right-click on exe and run as admin) it returns 0 which means it failed. If I run it by putting the wbAdmin command line in the test.bat file (see commented line right above String strCmdLine in the code) then CreateProcess returns a 1 (success) but wbAdmin is still not running. It flashed a DOS window and i captured it as shown in the picture below. It shows oriental characters in the title bar and says not recognized as internal or external command. But, if i run that test.bat directly (elevated) it runs wbAdmin no problem.
Any ideas on what is wrong? Besides me obviously being ignorant. (p.s. i'll get to testing Golvind's answer on the ShellExecute after this...)

Running the batch file manually (right-click and run as admin) works.
Because you are running the 64-bit version of cmd when you start it manually.
It shows oriental characters in the title bar and says not recognized
as internal or external command.
Because your application is 32-bit. A 32-bit application does not see the same System32 folder as 64-bit applications. You can access the 64-bit System32 folder in 32-bit applications with the virtual sysnative folder.
#include <shellapi.h>
...
String strCmdLine = "wbAdmin.exe start backup -backupTarget:T: -include:C: -allCritical -quiet";
int rtrn = CreateProcess(
NULL,
strCmdLine.c_str(),
NULL, // process security attributes
NULL, // primary thread security attributes
0, // handles are inherited
0, // creation flags
0, // use parent's environment
0, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
if (!rtrn)
{
String newCmdLine = "c:\\windows\\sysnative\\wbAdmin.exe start backup -backupTarget:T: -include:C: -allCritical -quiet";
rtrn = CreateProcess(
NULL,
newCmdLine.c_str(),
NULL, // process security attributes
NULL, // primary thread security attributes
0, // handles are inherited
0, // creation flags
0, // use parent's environment
0, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
}
Or compile your application to 64-bit.

You need to launch CMD.exe as Administrator with "runas", and specify the batch file as a "run-me-then-exit" (i.e. /c) argument to command prompt, as so:
WCHAR wszCmdPath[MAX_PATH];
GetEnvironmentVariableW(L"ComSpec", wszCmdPath, MAX_PATH);
ShellExecuteW(NULL, L"runas", wszCmdPath, L"/c \"C:\\Path\\BatchFile.bat\"", L"", SW_SHOW);
Both functions called here can fail, and robust code would test for success before proceeding.

Related

how to execute ipconfig using createprocess() in vc++ with visible command prompt?

I am trying to implement a method that executes command "ipconfig" in command prompt and show that output in command prompt.
currently i am using
STARTUPINFO StartUpInfo;
PROCESS_INFORMATION ProcInformation;
memset(&StartUpInfo, 0, sizeof(StartUpInfo) );
memset(&ProcInformation, 0, sizeof(ProcInformation) );
StartUpInfo.dwFlags = STARTF_USESHOWWINDOW;
StartUpInfo.wShowWindow = SW_SHOW;
CreateProcess(csCMDExeFullPath_EP, csCommanddLineParams_EP.GetBuffer
( csCommanddLineParams_EP.GetLength() + ONE), NULL,
NULL, NULL, 0, NULL, NULL,&StartUpInfo, &ProcInformation );
//Wait for the executing process to complete
WaitForSingleObject(ProcInformation.hProcess, INFINITE);
GetExitCodeProcess(ProcInformation.hProcess, &dwExecuteProcess_ExecExitCode_EP );
csCommanddLineParams_EP.ReleaseBuffer();
//Close handle of the process
CloseHandle(ProcInformation.hProcess);
//Close handle of the threads
CloseHandle(ProcInformation.hThread);`
My current code is working fine but command prompt visible and disappears in seconds.
But my requirement is to show output in cmd.
Can any one help me on this.

Prevent console window from being created in custom node.js build

I'm creating a custom build of node.js that should not show a console window to the user.
I've tried changing the linker config in the gyp file to 2 (which should set the linker flag /SUBSYSTEM:WINDOWS), but I still get a console window when I run the resulting node.exe binary.
How can I prevent the console window from appearing?
Edit: Further investigation reveals that the linker config in node.gyp is not taking effect. The generated node.vcxproj still has <Link><SubSystem>Console</SubSystem></Link> (which is very strange to me, since adding 'UACUIAccess': 'true' in the same part of node.gyp did take effect), so the built binary is incorrectly linked.
Solution 1
Save this one line of text as file invisible.vbs:
CreateObject(“Wscript.Shell”).Run “”"” & WScript.Arguments(0) & “”"”, 0, False
To run any program or batch file invisibly, use it like this:
wscript.exe “C:\Wherever\invisible.vbs” “C:\Some Other Place\MyBatchFile.bat”
To also be able to pass-on/relay a list of arguments use only two double quotes
CreateObject(“Wscript.Shell”).Run “” & WScript.Arguments(0) & “”, 0, False
eg: Invisible.vbs “Kill.vbs ME.exe”
Solution 2
Use a command line tool to silently launch a process : Quiet.
Solution 3
Roll your own C++ Win32 App:
PROCESS_INFORMATION procInfo = {0};
STARTUPINFOstartupInfo = {0};
SECURITY_ATTRIBUTESsaAttr = {0};
HANDLEhStdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLEhStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLEhStdErr = GetStdHandle(STD_ERROR_HANDLE);
// build up security attributes
saAttr.nLength = sizeof(saAttr);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// set file handles for process to be created
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdInput = hStdIn;
startupInfo.hStdOutput = hStdOut;
startupInfo.hStdError = hStdErr;
// build command line: format is [cmd.exe /c "%batchScript%" %batchArgs%]
if (-1 == _snprintf_s(cmd, sizeof(cmd),"cmd.exe /c \"%s\" %s", batchScript, batchArgs))
errorExit("_snprintf_s(\"cmd.exe /c \"%%s\" %%s\"), \"%s\", \"%s\") failed.", batchScript, batchArgs);
rc = CreateProcess(NULL, cmd, NULL, &saAttr, TRUE, CREATE_NO_WINDOW, NULL, tempPath, &startupInfo, &procInfo);
You have to change the SubSystem field value in node.exe PE optional header. The current value is 3 which is defined as Windows Console. If you change it to 2 (which is defined as Windows GUI) there would be no console window. In order to patch the executable file, you have to use utilities to change Optional Header of PE.
One example of such a tool is PE tools.
Click on Optinal Header and then change the Subsystem from 3 to 2.
That`s all.
Remember that with this change you can only run js files. You can not use interactive mode.
It appears that you must:
Comment out the 'SubSystem': 1 line in common.gypi. (Changing it to 2 causes the build to fail in mksnapshot.)
Change SubSystem to 2 in node.gyp
Also add 'EntryPointSymbol': 'wmainCRTStartup' to node.gyp.
This builds a node.exe that does not create a console window.

Windows API - CreateProcess() path with space

How do I pass path with space to the CreateProcess() function?
The following works
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if( !CreateProcess(_T("c:\\installer\\ew3d.exe"), // No module name (use command line)
_T("c:\\installer\\ew3d.exe /qr"),//argv[1], // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
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() );
return false;
}
//Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
But if I use a path with space as as the code below, it didn't work.
CreateProcess(_T("c:\\master installer\\ew3d.exe"), // No module name (use command line)
_T("c:\\master installer\\ew3d.exe /qr"),//argv[1], // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
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
)
And quoting the command such as below didn't help too
CreateProcess(_T("\"c:\\master installer\\ew3d.exe\""), // No module name (use command line)
_T("\"c:\\master installer\\ew3d.exe\" /qr"),//argv[1], // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
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
)
What is the right way to pass in the path with space?
In response to another answer, example #3 is NOT the correct one.
The issue is that the quotes should NOT encapsulate the module pathname passed as the first parameter of CreateProcess. However, quotes SHOULD encapsulate arg0 (again module path) as passed for the command line (second parameter of CreateProcess).
So, the correct rendition would be:
CreateProcess(_T("c:\\master installer\\ew3d.exe"),
_T("\"c:\\master installer\\ew3d.exe\" /qr"),
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
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
)
Your 3rd snippet is the correct one, not sure why you have trouble. Having the GetLastError() return value would be valuable here. Do note however that the 2nd argument of CreateProcess is an LPTSTR, not an LPCTSTR. In other words, Windows can write back to the string. Pretty creepy, isn't it? Enough reason perhaps to use ShellExecuteEx() instead.
You don't need to specify the application path in both the first and second arguments. According to the MSDN documentation the second argument should be only command line arguments if you list the application name in the first argument. Otherwise, set the first argument to NULL and then in the second argument enclose the application name in quotes if it contains a space. Not sure why your last listing doesn't work.
Docs are unclear, but it seems possible that if you include a space you must allow param 2 to define the full path.
The lpApplicationName parameter can be
NULL. In that case, the module name
must be the first white
space–delimited token in the
lpCommandLine string. If you are using
a long file name that contains a
space, use quoted strings to indicate
where the file name ends and the
arguments begin; otherwise, the file
name is ambiguous.
Have you tried this variation?
CreateProcess(NULL, // No module name (use command line)
_T("\"c:\\master installer\\ew3d.exe\" /qr"),//argv[1], // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
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
)
EDIT: The following worked for me (dwError is 0). My project is built with multibyte charset.
LPTSTR szCmdLine = _tcsdup(TEXT(
"\"C:\\Program Files\\adobe\\Reader 8.0\\reader\\acrord32.exe\" /qr"));
CreateProcess(NULL,
szCmdLine,
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
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
); // This works. Downcasting of pointer to members in general is fine.
DWORD error = GetLastError();
A bit late to the party. For some reason, I cannot up-vote Praetorian, but he's right. I was suffering from the same problem, and NULLing the Application Name did the trick. I also tried path in App Name & just the command line params in the second argument, to no avail.
I am on Win7 x64.
CreateProcess (NULL, "\"Path to exe\" -x -y -z", ...);
works for me.

How to create a process that is not a child of it's creating process?

I have two processes, A and B. At some point A creates B. After B is created, if A's process tree is killed, I want B to still be around.
I am using CreateProcess() to create B, and I can't seem to find any way to make it create the process without it being a child. Same thing with ShellExecuteEx(), but I am probably missing some flag.
Does anyone know what I could use to do this?
EDIT: I forgot to mention that both processes need a HANDLE or process ID to the other
You can try that process A create process C, which create process B and then process C will be immediatly ended (terminated). In a process B there are exist only information about the direct parent process (process Id of C which is not more running) and not about the process A. So "if A's process tree is killed" the process B will probably stay running.
For example you start Process Explorer (see http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx) then start Total Commander. From the Total Commander you start cmd.exe. From cmd.exe you start notepad.exe. Then type "exit" in the cmd.exe. After terminating of cmd.exe you can see that notepad.exe will no more displayed under Total Commander (totalcmd.exe). After you choose in Process Explorer "Kill Process Tree" for the Total Commander (totalcmd.exe) you can see that notepad.exe stay running.
You can set the paramaeter dwcreationflags as DETACHED_PROCESS in the createprocess API.
When calling kernel32!CreateProcess() you can specify a different parent by using a process attribute. Here is a function that does just that.
bool CreateProcessWithParent(DWORD parentId, PWSTR commandline) {
auto hProcess = ::OpenProcess(PROCESS_CREATE_PROCESS, FALSE, parentId);
if (!hProcess)
return false;
SIZE_T size;
//
// call InitializeProcThreadAttributeList twice
// first, get required size
//
::InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
//
// now allocate a buffer with the required size and call again
//
auto buffer = std::make_unique<BYTE[]>(size);
auto attributes = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(buffer.get());
::InitializeProcThreadAttributeList(attributes, 1, 0, &size);
//
// add the parent attribute
//
::UpdateProcThreadAttribute(attributes, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&hProcess, sizeof(hProcess), nullptr, nullptr);
STARTUPINFOEX si = { sizeof(si) };
//
// set the attribute list
//
si.lpAttributeList = attributes;
PROCESS_INFORMATION pi;
//
// create the process
//
BOOL created = ::CreateProcess(nullptr, commandline, nullptr, nullptr,
FALSE, EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr,
(STARTUPINFO*)&si, &pi);
//
// cleanup
//
::CloseHandle(hProcess);
::DeleteProcThreadAttributeList(attributes);
return created;
}
Source code taken from https://scorpiosoftware.net/2021/01/10/parent-process-vs-creator-process/

Running another program in Windows bat file and not create child process

I have subversion server with a post-commit hook to do something.
I want the checkin finish soon, not wait the hook script.
But by design, the Subversion post-commit hook script will run until all child process exit, so using somthing like:
start another_prog...
in the hook bat file has no use.
So I want to know how to run another program in Windows bat file which not create child process or let the child process detach from the parent.
Synchronous. The second notepad won't launch until you close the first.
notepad.exe c:\temp\a.txt
notepad.exe c:\temp\b.txt
Asynchronous: The second notepad will launch even if you haven't closed the first.
start notepad.exe c:\temp\a.txt
start notepad.exe c:\temp\b.txt
More info about the start command:
http://www.robvanderwoude.com/ntstart.php
EDIT: The following comment was made elsewhere by #zhongshu, the original poster. I'm only copying it here:
start cmd /c doesn't work because SVN
post-commit hook will wait for the
hook and the child process created by
the hook exit. It's the design of SVN.
I have found a solution, Please refer:
http://svn.haxx.se/users/archive-2008-11/0301.shtml
Assuming that he knows what he's talking about, I'm wrong and...undeserving.
I found a method to resolve my question, compile the following c code and named the exe output as runjob.exe, and then in the hook bat file, use " runjob another_prog " , now it's ok.
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
int _tmain()
{
char * pCmd = ::GetCommandLine();
// skip the executable
if (*pCmd++ == L'"')
{
while (*pCmd++ != L'"');
}
else
{
while (*pCmd != NULL && *pCmd != L' ')
++pCmd;
}
while (*pCmd == L' ')
pCmd++;
STARTUPINFO si;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory( &pi, sizeof(pi) );
// Start the child process.
BOOL result = CreateProcess
(
NULL, // No module name (use command line)
pCmd, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set bInheritHandles to FALSE
DETACHED_PROCESS, // Detach process
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (returned)
);
if (result) return 0;
char msg[2048];
FormatMessage
(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
::GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
msg, sizeof(msg),
NULL
);
fputs(msg, stderr);
_flushall();
return -1;
}
What you can do is create a Scheduled Task that executes the batch script or other executable that runs for a long time. Set it to run once, in the past and don't set it to delete the task when no more runs are scheduled. Then in your Subversion hook script, put the following line in:
schtasks /run /tn NameOfYourTaskHere
I confirmed with a test by having my scheduled task run Notepad++ and the Notepad++ executable showed up as a child of svchost.exe, not the cmd.exe window that I executed the schtasks command from.
Use:
start cmd /c "your command"
Cheers.
Try cmd /c "your command"
Could you use the windows task scheduler command line interface "schtasks /run" to start a job that runs the "another_prog"? You'd have to create the job ahead of time. There also used to be a "SOON" program with the Windows (NT) Resource Kit that would create dynamic entries for the "AT" command scheduler to run a job in a few minutes that would not require setting up a job ahead of time, it can still be found with a little searching.
You can create a hybrid batch/JScript file (i.e. a batch file able to run embedded JScript) where the JScript part will run another_prog in detached mode with shell.Run(prog, 0, false):
#if (#X)==(#Y) #end /* JScript comment
rem put any batch code that runs before another_prog here
#cscript //E:JScript //nologo "%~f0" "another_prog" 0 false
rem put any batch code that runs after another_prog here
exit /b %errorlevel%
#if (#X)==(#Y) #end JScript comment */
var ARGS = WScript.Arguments;
var shell = new ActiveXObject("WScript.Shell");
shell.Run(ARGS.Item(0),ARGS.Item(1),ARGS.Item(2));`

Resources