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

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));`

Related

How to hide a cmd window using Flutter Desktop (Windows)

I have built a networking app for MacOS for private usage. It uses shell commands and runs bash files.
Most of my colleagues are using MacOS, but still need to publish the same app for Windows.
Is there any ways to hide a cmd window when I runs shell script?
Thanks in advance.
On Windows app, you can do that changing the folowing lines in windows/runner/resources/main.cpp
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole();
}
to this:
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole();
}else {
STARTUPINFO si = { 0 };
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi = { 0 };
WCHAR lpszCmd[MAX_PATH] = L"cmd.exe";
if (::CreateProcess(NULL, lpszCmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE | CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
do {
if (::AttachConsole(pi.dwProcessId)) {
::TerminateProcess(pi.hProcess, 0);
break;
}
} while (ERROR_INVALID_HANDLE == GetLastError());
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
}
}
More information here: https://github.com/flutter/flutter/issues/47891
There are some options to run a cmd hidden on Windows for example:
start a cmd window as hidden by using the "hide" option on a bat script or programmatically using the Windows API ShowWindow(SW_HIDE). Hidden in this case means you won't see the GUI and it has no presence on the task-bar.
run it through "Task Scheduler"
run it as background service
Some examples:
Bat script: How to run .BAT files invisibly without displaying the Command Prompt
C-sharp: Hide Command Window in Application
C-sharp: Run CMD command without displaying it
C-sharp: Sample code to schedule a task using WindowsTask Scheduler

ShellExecute bat file elevated (FMX, Win32)

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.

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.

How can I get the current active window at the time a batch script is run?

I have a batch script I want to run with hotkeys, and this script is supposed to make some actions in the active window (for example, creating a particular set of folders, or lowercase all names of the files inside the folder). So the script needs to refer to the active window when it's called.
I have tried to leave the "Start in" field of the alias empty, but echoing %cd% always print "C:\Windows\System32" instead of the current active window.
You can lookup which process got the window in foreground using pinvoke of user32.dll.
I've used this trick for system.window.forms.sendkeys method in a script:
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class Tricks {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}
"#
$a = [tricks]::GetForegroundWindow()
get-process | ? { $_.mainwindowhandle -eq $a } # in my case:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
161 7 13984 15820 91 9,75 7720 Console
For anyone looking for a non-Powershell solution, here's a batch script that uses cscript to invoke a block of JScript. The JScript creates a new child process, gets its PID, then walks up the ParentProcessID line of ancestors until it gets to explorer.exe, then returns the PID of the direct child. It ought to return the correct PID for the console window in which the script runs, even if there are multiple instances of cmd.exe or cscript.exe running.
What can I say? I was feeling creative today.
#if (#a==#b) #end /* JScript multiline comment
:: begin batch portion
#echo off
setlocal
for /f "delims=" %%I in ('cscript /nologo /e:Jscript "%~f0"') do (
echo PID of this console window is %%I
)
goto :EOF
:: end batch portion / begin JScript */
var oShell = WSH.CreateObject('wscript.shell'),
johnConnor = oShell.Exec('%comspec% /k #echo;');
// returns PID of the direct child of explorer.exe
function getTopPID(PID, child) {
var proc = GetObject("winmgmts:Win32_Process=" + PID);
// uncomment the following line to watch the script walk up the ancestor tree
// WSH.Echo(proc.name + ' has a PID of ' + PID);
return (proc.name == 'explorer.exe') ? child : getTopPID(proc.ParentProcessID, PID);
}
var PID = getTopPID(johnConnor.ProcessID);
johnConnor.Terminate();
// send the console window to the back for a second, then refocus, just to show off
oShell.SendKeys('%{ESC}');
WSH.Sleep(1000);
oShell.AppActivate(PID);
// output PID of console window
WSH.Echo(PID);

Waiting for grandchild processes in windows

Is it possible to wait for all processes launched by a child process in Windows? I can't modify the child or grandchild processes.
Specifically, here's what I want to do. My process launches uninstallA.exe. The process uninistallA.exe launches uninstallB.exe and immediately exits, and uninstallB.exe runs for a while. I'd like to wait for uninstallB.exe to exit so that I can know when the uninstall is finished.
Create a Job Object with CreateJobObject. Use CreateProcess to start UninstallA.exe in a suspended state. Assign that new process to your job object with AssignProcessToJobObject. Start UninstallA.exe running by calling ResumeThread on the handle of the thread you got back from CreateProcess.
Then the hard part: wait for the job object to complete its execution. Unfortunately, this is quite a bit more complex than anybody would reasonably hope for. The basic idea is that you create an I/O completion port, then you create the object object, associate it with the I/O completion port, and finally wait on the I/O completion port (getting its status with GetQueuedCompletionStatus). Raymond Chen has a demonstration (and explanation of how this came about) on his blog.
Here's a technique that, while not infallible, can be useful if for some reason you can't use a job object. The idea is to create an anonymous pipe and let the child process inherit the handle to the write end of the pipe.
Typically, grandchild processes will also inherit the write end of the pipe. In particular, processes launched by cmd.exe (e.g., from a batch file) will inherit handles.
Once the child process has exited, the parent process closes its handle to the write end of the pipe, and then attempts to read from the pipe. Since nobody is writing to the pipe, the read operation will block indefinitely. (Of course you can use threads or asynchronous I/O if you want to keep doing stuff while waiting for the grandchildren.)
When (and only when) the last handle to the write end of the pipe is closed, the write end of the pipe is automatically destroyed. This breaks the pipe and the read operation completes and reports an ERROR_BROKEN_PIPE failure.
I've been using this code (and earlier versions of the same code) in production for a number of years.
// pwatch.c
//
// Written in 2011 by Harry Johnston, University of Waikato, New Zealand.
// This code has been placed in the public domain. It may be freely
// used, modified, and distributed. However it is provided with no
// warranty, either express or implied.
//
// Launches a process with an inherited pipe handle,
// and doesn't exit until (a) the process has exited
// and (b) all instances of the pipe handle have been closed.
//
// This effectively waits for any child processes to exit,
// PROVIDED the child processes were created with handle
// inheritance enabled. This is usually but not always
// true.
//
// In particular if you launch a command shell (cmd.exe)
// any commands launched from that command shell will be
// waited on.
#include <windows.h>
#include <stdio.h>
void error(const wchar_t * message, DWORD err) {
wchar_t msg[512];
swprintf_s(msg, sizeof(msg)/sizeof(*msg), message, err);
printf("pwatch: %ws\n", msg);
MessageBox(NULL, msg, L"Error in pwatch utility", MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
ExitProcess(err);
}
int main(int argc, char ** argv) {
LPWSTR lpCmdLine = GetCommandLine();
wchar_t ch;
DWORD dw, returncode;
HANDLE piperead, pipewrite;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
char buffer[1];
while (ch = *(lpCmdLine++)) {
if (ch == '"') while (ch = *(lpCmdLine++)) if (ch == '"') break;
if (ch == ' ') break;
}
while (*lpCmdLine == ' ') lpCmdLine++;
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&piperead, &pipewrite, &sa, 1)) error(L"Unable to create pipes: %u", GetLastError());
GetStartupInfo(&si);
if (!CreateProcess(NULL, lpCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
error(L"Error %u creating process.", GetLastError());
if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) error(L"Error %u waiting for process.", GetLastError());
if (!GetExitCodeProcess(pi.hProcess, &returncode)) error(L"Error %u getting exit code.", GetLastError());
CloseHandle(pipewrite);
if (ReadFile(piperead, buffer, 1, &dw, NULL)) {
error(L"Unexpected data received from pipe; bug in application being watched?", ERROR_INVALID_HANDLE);
}
dw = GetLastError();
if (dw != ERROR_BROKEN_PIPE) error(L"Unexpected error %u reading from pipe.", dw);
return returncode;
}
There is not a generic way to wait for all grandchildren but for your specific case you may be able to hack something together. You know you are looking for a specific process instance. I would first wait for uninstallA.exe to exit (using WaitForSingleObject) because at that point you know that uninstallB.exe has been started. Then use EnumProcesses and GetProcessImageFileName from PSAPI to find the running uninstallB.exe instance. If you don't find it you know it has already finished, otherwise you can wait for it.
An additional complication is that if you need to support versions of Windows older than XP you can't use GetProcessImageFileName, and for Windows NT you can't use PSAPI at all. For Windows 2000 you can use GetModuleFileNameEx but it has some caveats that mean it might fail sometimes (check docs). If you have to support NT then look up Toolhelp32.
Yes this is super ugly.
Use a named mutex.
One possibility is to install Cygwin and then use the ps command to watch for the grandchild to exit

Resources