I apologize for the vague title, was not sure how to categorize my problem.
I have a script which I call from a Visual Studio 2013 project (C++). In that script, I try to set my path variable. When the PATH variable gets set, it seems like the it is including visual studio stuff in the path and copying the path more than once. Not sure why.
Note: Running the bat directly and running from the command prompt does not present this error.
.cpp:
int main(void)
{
system("CALL C:\\HFSS\\setup_vars.bat");
return 0;
}
.bat:
:: Set PATH Variable
set path_str=%PATH%
set addPath=%ValueValue%
echo %addPath%
echo %ValueValue%
PAUSE
echo %PATH%| find /i "%addPath%">NUL
if NOT ERRORLEVEL 1 (
SETX PATH "%PATH%
) else (
SETX /M PATH "%PATH%;%addPath%;"
)
Path which gets added:
C:\Program Files\AnsysEM\AnsysEM15.0\Win64;
C:\Program Files\AnsysEM\AnsysEM15.0\Win64;
C:\Program Files\AnsysEM\AnsysEM15.0\Win64;
C:\Program Files\AnsysEM\AnsysEM15.0\Win64;
C:\Program Files\AnsysEM\AnsysEM15.0\Win64;
C:\Program Files (x86)\Microsoft Visual Studio 12.0\;
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\amd64
UPDATE:
I did some research on CreateProcess and how to create a new environment block.
The following code triggers a break point:
#include <iostream>
#include <Windows.h>
#define WINDOWS_LEAN_AND_MEAN
#include <strsafe.h>
#define BUFSIZE 4096
int main(void)
{
LPTSTR lpszCurrentVariable;
TCHAR szAppName[] = TEXT("C:\\windows\\system32\\cmd.exe");
STARTUPINFO si;
PROCESS_INFORMATION pi;
BOOL fSuccess;
LPSTR lpszVariable;
LPWCH lpvEnv;
lpvEnv = GetEnvironmentStrings();
if (lpvEnv == NULL)
{
std::cout << "GetEnvironmentStrings() Failed\n";
system("Pause");
return 0;
}
lpszCurrentVariable = lpvEnv;
if (FreeEnvironmentStrings(lpvEnv) != 0)
{
std::cout << "Freed block!\n";
}
if (FAILED(StringCchCopy(lpszCurrentVariable, BUFSIZE, L"MyNewOwnEnvSetting=ver 2.0")))
{
system("Pause");
return 1;
}
lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1;
if (FAILED(StringCchCopy(lpszCurrentVariable, BUFSIZE, L"MyNewOwnVar=MyPath")))
{
std::cout << "StringCchCopy() - String copy #2 failed\n";
system("Pause");
return 1;
}
lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1;
*lpszCurrentVariable = (WCHAR)0;
SecureZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
fSuccess = CreateProcess(szAppName, L"C:\HFSS\setup_vars.bat", NULL, NULL, TRUE, NULL, (LPVOID)lpszCurrentVariable, NULL, &si, &pi);
DWORD ret = WaitForSingleObject(pi.hProcess, INFINITE);
system("Pause");
return 0;
}
I am not entire sure what the following lines are supposed to be doing:
if (FAILED(StringCchCopy(lpszCurrentVariable, BUFSIZE, L"MyNewOwnEnvSetting=ver 2.0")))
{
system("Pause");
return 1;
}
lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1;
if (FAILED(StringCchCopy(lpszCurrentVariable, BUFSIZE, L"MyNewOwnVar=MyPath")))
{
std::cout << "StringCchCopy() - String copy #2 failed\n";
system("Pause");
return 1;
}
From Windows Dev-center
By default, a child process inherits the environment variables of its
parent process. Programs started by the command processor inherit the
command processor's environment variables. To specify a different
environment for a child process, create a new environment block and
pass a pointer to it as a parameter to the CreateProcess function.
Also: make sure your PATH variable doesn't exceed the maximum allowed size.
Related
I am trying to create a program in which you can execute commands. The output of these commands should be displayed in a GUI. For this I use QT (because I want to get familiar with WinAPI I don't use QProcess). In the current program it is already possible to redirect the output of a command with a handle. Now my question, how is it possible to interrupt the ReadFile if the command expects a user input.
As an example, I want to run the command yarn run from C++.
This returns as output that this command does not exist and asks which command I want to execute instead. At the moment the command aborts there (comparable with CTRL+C) and returns error No command specified. At this point, however, a user input should be possible.
Expected outcome of the program:
The output I get instead:
As you can see in picture 1 yarn asks the user for input. In image 2 there is no question at all. This behaviour is for example possible if you press CTRL+C if the question input shows up.
So how is it possible to make a user input in the gui (for now it would be enough to redirect the value of a variable into the input) and redirect it back to the process. The process should wait until it gets the input.
Command.h
#ifndef COMMAND_H
#define COMMAND_H
#include <string>
#include <cstdlib>
#include <cstdio>
#include <io.h>
#include <fcntl.h>
#include <stdexcept>
#include <windows.h>
#include <iostream>
#define BUFSIZE 256
class Project;
class Command
{
private:
int exitStatus;
const Project * project;
std::string cmd;
HANDLE g_hChildStd_IN_Rd = nullptr;
HANDLE g_hChildStd_IN_Wr = nullptr;
HANDLE g_hChildStd_OUT_Rd = nullptr;
HANDLE g_hChildStd_OUT_Wr = nullptr;
HANDLE g_hInputFile = nullptr;
void setupWindowsPipes();
void createWindowsError(const std::string &errorText);
void readFromPipe();
public:
Command() = delete;
explicit Command(std::string cmd, const Project *project);
void exec();
};
#endif // COMMAND_H
Command.cpp (the entry point which is called by the gui is exec())
#include "command.h"
#include "project.h"
Command::Command(std::string cmd, const Project *project) : exitStatus(0), project(project), cmd(std::move(cmd)) {}
void Command::createWindowsError(const std::string &errorText) {
DWORD code = GetLastError();
LPSTR lpMsgBuf;
if(code == 0) return;
auto size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &lpMsgBuf,
0, NULL );
std::string msg(lpMsgBuf, size);
LocalFree(lpMsgBuf);
throw std::runtime_error(errorText + "()" + std::to_string(code) + ": " + msg);
}
void Command::setupWindowsPipes(){
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = true;
saAttr.lpSecurityDescriptor = nullptr;
if(!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
createWindowsError("StdOutRd CreatePipe");
if(!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
createWindowsError("StdOut SetHandleInformation");
if(!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
createWindowsError("StdInRd CreatePipe");
if(!SetHandleInformation(g_hChildStd_IN_Rd, HANDLE_FLAG_INHERIT, 0))
createWindowsError("StdIn SetHandleInformation");
}
void Command::readFromPipe() {
DWORD dwRead;
char chBuf[BUFSIZE];
bool bSuccess = false;
for (;;)
{
dwRead = 0;
for(int i = 0;i<BUFSIZE;++i) {
chBuf[i] = '\0';
}
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead <= 0 ) break;
std::cout << chBuf;
}
std::cout << std::endl;
}
void Command::exec() {
std::cout << "CMD to run: " << this->cmd << std::endl;
this->setupWindowsPipes();
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&pi, sizeof(pi));
char* dir = nullptr;
if(this->project != nullptr) {
auto n = this->project->getLocalUrl().size() + 1;
auto nString = this->project->getLocalUrl().replace("/", "\\");
dir = new char[n];
std::strncpy(dir, nString.toStdString().c_str(), n);
}
std::string cmdString = "cmd /c ";
cmdString.append(this->cmd);
char cmdCopy[cmdString.size() + 1];
cmdString.copy(cmdCopy, cmdString.size());
cmdCopy[cmdString.size() + 1] = '\0';
bool rc = CreateProcessA( nullptr,
cmdCopy,
nullptr,
nullptr,
true,
CREATE_NO_WINDOW,
nullptr,
dir,
&si,
&pi);
delete []dir;
if(!rc)
createWindowsError("Failed to create process");
std::cout << "PID: " << pi.dwProcessId << std::endl;
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_IN_Rd);
readFromPipe();
std::cout << "fin reading pipe" << std::endl;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
It sounds like you have an XY problem, luckily you described X so we can address it.
The issue is not your failure to call WriteFile to store the response into the redirected input pipe. If the program were trying to read input, it would wait.
The issue is that the program is not requesting input at all. It has detected that interactive input is not possible, because it detects a pipe and assumes that a pipe is not interactive. So it doesn't perform the prompt or try to read from standard input at all. You can't provide an answer to a question that the program didn't ask!
(To confirm this is the behavior of the yarn program you are spawning, you can launch it from cmd.exe using a pipe to provide the input. cmd.exe has well-tested buffering logic for redirected input and output handles and you can be sure that any suspected deadlock in your code doesn't affect cmd.exe)
On Unix-like systems, this is solved by redirecting to a pseudo-tty (ptty) special file instead of a pipe special file, which causes the isatty() function to return true.
On Windows, this used to be effectively impossible, as the console API, implemented at kernel level, was permanently associated to the console subsystem csrss.exe which only exchanged data with the official Console Host process (owner of console windows).
Now however, Windows API supports pseudo-consoles. You can find a complete introduction on the Microsoft Dev Blog
Windows Command-Line: Introducing the Windows Pseudo Console (ConPTY)
The important function you need (in case that link breaks) is CreatePseudoConsole supported starting with Windows 10 version 1809 (October 2018 update).
When you use CreatePseudoConsole to promote the pipes and then supply this console to CreateProcess (instead of attaching pipes to your subprocess standard I/O streams), the subprocess will detect an interactive console, can use console API functions such as AttachConsole, can open the special filenames CONIN$ etc. And the data comes to you (and from you) instead of being linked to a console window.
There's also a complete sample on GitHub.
That same blog post also discusses the workaround used by "Terminal" and "remote shell" type software prior to the addition of CreatePseudoConsole in Windows 10, namely setting up the subprocess with a detached console, hiding the associated console window, and screen-scraping the console screen buffer.
I'm working in Visual Studio 2017. In my current solution, I have 2 active projects. The 2nd project depends on the 1st project and my 2nd project is my startup application. When I run or compile my 2nd project, there is already a compiled executable of my 1st project that is within my solutions directory...
Here is the directory hierarchy of my solutions - projects:
"SolutionName/Project1/"
This contains the source code for Project1
"SolutionName/Project2/"
This contains the source code for Project2
"SolutionName/x64/"
This contains the x64 versions:
"SolutionName/x64/Debug"
This contains the x64 debug builds and executables
"SolutionName/x64/Release"
This contains the x64 release builds and executables
When I run the application with the 2nd project as my startup... The code compiles and runs fine, however, it doesn't appear to be executing the executable from the 1st project...
Here is my main.cpp
#include <Windows.h>
#include <exception>
#include <stdio.h>
#include <tchar.h>
#include <cstdint>
#include <iostream>
uint32_t runProgram(LPCSTR lpApplicationName) {
STARTUPINFOA si;
PROCESS_INFORMATION pi;
// Set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Run the program
CreateProcessA(
lpApplicationName, // the path
NULL, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_NEW_CONSOLE, // Opens file in seperate console
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
);
uint32_t cache_size = 0;
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return cache_size;
}
int main() {
try {
const uint32_t cache_size = runProgram("CacheQuery.exe");
std::cout << cache_size << '\n';
}
catch (const std::exception& e) {
std::cerr << e.what() << "\n\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
If I build my solution in Debug mode, both executables are in the same directory for Debug and if I build my solution in Release mode, again both release versions of my executables are in the same directory...
I am getting an output of 0 in the console so I know that runProgram() is being called and returning, but I'm expecting another console to be opened with the displayed results from the invoked program with its own console handle from its own process. However, I'm not seeing it being invoked or called. CacheQuery.exe which is my 1st project and it doesn't seem to be executed...
I'm a bit new to this part of the Windows API, so I'm not sure if I'm doing this correctly... I need for my second project to call and run the 1st project... This is part 1 of my question. Once I get this resolved and know that project 2 is calling and executing project 1, then I'll ask my next question about how to retrieve the value that the called executable returns upon exit...
Aside from the lack of any error handling on CreateProcessA(), you are trying to launch CacheQuery.exe using a relative path, so the issue is most likely that the current working directory of the calling process is not what you are expecting it to be, causing CreateFileA() to fail if it can't find CacheQuery.exe.
You should never rely on relative paths, always use absolute paths instead!
Assuming the two EXE files are in the same folder, then you can try something like this:
#include <Windows.h>
#include <shlwapi.h>
#include <cstdio>
#include <tchar.h>
#include <cstdint>
#include <iostream>
#include <string>
#include <sstream>
#pragma comment(lib, "Shlwapi.lib")
void ThrowWin32Error(const char *funcname, DWORD errCode) {
std::ostringstream oss;
oss << funcname << " failed.";
if (errCode != 0) {
oss << " Error " << errCode;
}
throw std::runtime_error(oss.str());
}
void CheckWin32Error(const char *funcname, bool result) {
if (!result) ThrowWin32Error(funcname, 0);
}
void CheckWin32ErrorCode(const char *funcname, bool result) {
if (!result) ThrowWin32Error(funcname, GetLastError());
}
std::string getPathToCacheQuery() {
char path[MAX_PATH+16] = {};
DWORD size = GetModuleFileNameA(NULL, path, MAX_PATH);
if (size == MAX_PATH)
ThrowWin32Error("GetModuleFileNameA", ERROR_INSUFFICIENT_BUFFER);
else
CheckWin32ErrorCode("GetModuleFileNameA", size > 0);
CheckWin32Error("PathRemoveFileSpecA",
PathRemoveFileSpecA(path)
);
CheckWin32Error("PathAppendA",
PathAppendA(path, "CacheQuery.exe")
);
return path;
}
uint32_t runProgram(const std::string &ApplicationName) {
STARTUPINFOA si;
PROCESS_INFORMATION pi;
// Set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Run the program
CheckWin32ErrorCode("CreateProcessA",
CreateProcessA(
ApplicationName.c_str(),// the path
NULL, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_NEW_CONSOLE, // Opens file in seperate console
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
)
);
uint32_t cache_size = 0;
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return cache_size;
}
int main() {
try {
std::string path = getPathToCacheQuery();
const uint32_t cache_size = runProgram(path);
std::cout << cache_size << '\n';
}
catch (const std::exception& e) {
std::cerr << e.what() << "\n\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Try with this instead of CreateProcess:
#include <iostream>
#include <windows.h>
int main() {
ShellExecute(NULL, "open", "path\\to\\file.exe", NULL, NULL, SW_SHOWDEFAULT);
}
I want to write a wrappers that does some simple stuff with argv and calls some script. I have the following requirements:
the wrappers must be .exe files
the wrappers must be able to handle spaces and quotes correctly
the wrappers will be generated user-side
the generation process must be small (like using https://bellard.org/tcc )
My initial approach:
Write a c program to first sanitize the arguments, then wrap them in quotes, and then call system.
Unfortunately, I cannot get well-structured behavior from the system or exec* functions.
I expect all of the following examples to output something like arg1=1; arg2=2; arg3=3; arg4= (with some variance in quote wraps), but it errors on some of the examples, and pauses on execl:
Input files:
#:: test.bat
#echo off
echo arg1=%1; arg2=%2; arg3=%3; arg4=%4
//minimal-example.c
#include <Windows.h>
#include <stdio.h>
int main( int argc, char ** argv ) {
puts("\nExample 1:");
system("\"test.bat\" \"1\" \"2\" \"3\" ");
puts("\nExample 2:");
system("test.bat \"1\" \"2\" \"3\" ");
puts("\nExample 3:");
system("test.bat 1 2 \"3\" ");
puts("\nExample 4:");
system("\"test.bat\" 1 \"2\" 3 ");
puts("\nExample 5:");
system("\"test.bat\" 1 2 3 ");
puts("\nExample 6:");
execl(argv[0], "test.bat", "1", "2", "3", NULL);
return 0;
}
Output run:
Example 1:
'test.bat" "1" "2" "3' is not recognized as an internal or external command,
operable program or batch file.
Example 2:
arg1="1"; arg2="2"; arg3="3"; arg4=
Example 3:
arg1=1; arg2=2; arg3="3"; arg4=
Example 4:
'test.bat" 1 "2' is not recognized as an internal or external command,
operable program or batch file.
Example 5:
arg1=1; arg2=2; arg3=3; arg4=
Example 6:
arg1=1; arg2=2; arg3=3; arg4=
(Example 6 pauses until I press Enter)
Question:
Is there a way to correctly wrap the path/arguments in such a way as to allow spaces in system?
Can I escape quotes in arguments to system?
Is there a non-blocking way to run exec*?
Would a exec* approach ensure that the wrapped program's stdin stdout and stderr behaves correctly (no strange overflows or weird blocking things?)
Something like this should work:
string cmd = "test.bat";
for(int i = 1; i < argc; i++) {
cmd += " ";
cmd += argv[i]
}
system(cmd.c_str());
Of course, args that have spaces in them will need to be processed further, by adding quotes, and arguments with quotes may need escaping, and lots of other complications in cases where args contains stuff that isn't straight forward to deal with)
As an alternative, you can take a look at Use CreateProcess to Run a Batch File
Implementation
I ended up writing two wrappers for two different scenarios: One for keeping a terminal window open, and one for a silent run.
It works by renaming the exe file to name-of-script.exe, then the wrapper will try to run <exedir>\bin\name-of-script.bat and pass all the commandline parameters.
I use the program Resource Hacker to put nice icons or version information in the exe, to make it look like a proper program.
Code
tcc.exe -D_UNICODE -DNOSHELL launcher.c -luser32 -lkernel32 -mwindows -o launcher-noshell.exe
tcc.exe -D_UNICODE launcher.c -luser32 -lkernel32 -o launcher.exe
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <stdbool.h>
#include <tchar.h>
#ifdef NOSHELL
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
#else
int main( int argc, char ** argv )
#endif
{
//*******************************************
//Get commanline string as a whole
//*******************************************
TCHAR* cmdArgs = GetCommandLineW();
TCHAR* cmdPath;
cmdPath = (TCHAR*) malloc((_tcslen(cmdArgs)+1)*2);
_tcscpy(cmdPath, cmdArgs);
//*******************************************
//Split filepath, filename, and commandline
//*******************************************
bool inQuote = false;
bool isArgs = false;
int j = 0;
for(int i=0; i<_tcslen(cmdArgs)+1; i++){
//must be easier way to index unicode string
TCHAR c = *(TCHAR *)(&cmdArgs[i*2]);
if(c == L'"'){inQuote = !inQuote;}
if(c == L' ' && !inQuote){ isArgs = true;}
if(isArgs){
cmdPath[i*2] = '\0';
cmdPath[i*2+1] = '\0';
}
//do for both unicode bits
cmdArgs[j*2 ] = cmdArgs[i*2 ];
cmdArgs[j*2+1] = cmdArgs[i*2+1];
//sync j with i after filepath
if(isArgs){ j++; }
}
//*******************************************
//Remove quotes around filepath
//*******************************************
if(*(TCHAR *)(&cmdPath[0]) == L'"'){
cmdPath = &cmdPath[2];
}
int cmdPathEnd = _tcslen(cmdPath);
if(*(TCHAR *)(&cmdPath[(cmdPathEnd-1)*2]) == L'"'){
cmdPath[(cmdPathEnd-1)*2]='\0';
cmdPath[(cmdPathEnd-1)*2+1]='\0';
}
//*******************************************
//Find basedir of cmdPath
//*******************************************
TCHAR* cmdBaseDir;
cmdBaseDir = (TCHAR*) malloc((_tcslen(cmdPath)+1)*2);
_tcscpy(cmdBaseDir, cmdPath);
int nrOfSlashed = 0;
int slashLoc = 0;
for(int i=0; i<_tcslen(cmdBaseDir); i++){
//must be easier way to index unicode string
TCHAR c = *(TCHAR *)(&cmdBaseDir[i*2]);
if(c == L'\\' || c == L'//'){
nrOfSlashed+=1;
slashLoc=i;
}
}
if(nrOfSlashed==0){
_tcscpy(cmdBaseDir, L".");
}else{
cmdBaseDir[2*slashLoc] = '\0';
cmdBaseDir[2*slashLoc+1] = '\0';
}
//*******************************************
//Find filename without .exe
//*******************************************
TCHAR* cmdName;
cmdName = (TCHAR*) malloc((_tcslen(cmdPath)+1)*2);
_tcscpy(cmdName, cmdPath);
cmdName = &cmdPath[slashLoc==0?0:slashLoc*2+2];
int fnameend = _tcslen(cmdName);
if(0 < fnameend-4){
cmdName[(fnameend-4)*2] = '\0';
cmdName[(fnameend-4)*2+1] = '\0';
}
//_tprintf(L"%s\n", cmdName);
//********************************************
//Bat name to be checked
//********************************************
int totlen;
TCHAR* batFile1 = cmdBaseDir;
TCHAR* batFile2 = L"\\bin\\";
TCHAR* batFile3 = cmdName;
TCHAR* batFile4 = L".bat";
totlen = (_tcslen(batFile1)+ _tcslen(batFile2)+ _tcslen(batFile3)+ _tcslen(batFile4));
TCHAR* batFile;
batFile = (TCHAR*) malloc((totlen+1)*2);
_tcscpy(batFile, batFile1);
_tcscat(batFile, batFile2);
_tcscat(batFile, batFile3);
_tcscat(batFile, batFile4);
if(0 != _waccess(batFile, 0)){
system("powershell -command \"[reflection.assembly]::LoadWithPartialName('System.Windows.Forms')|out-null;[windows.forms.messagebox]::Show('Could not find the launcher .bat in bin directory.', 'Execution error')\" ");
};
//_tprintf(L"%s\n", batFile);
//*******************************************
//Get into this form: cmd.exe /c ""c:\path\...bat" arg1 arg2 ... "
//*******************************************
TCHAR* cmdLine1 = L"cmd.exe /c \"";
TCHAR* cmdLine2 = L"\"";
TCHAR* cmdLine3 = batFile;
TCHAR* cmdLine4 = L"\" ";
TCHAR* cmdLine5 = cmdArgs;
TCHAR* cmdLine6 = L"\"";
totlen = (_tcslen(cmdLine1)+_tcslen(cmdLine2)+_tcslen(cmdLine3)+_tcslen(cmdLine4)+_tcslen(cmdLine5)+_tcslen(cmdLine6));
TCHAR* cmdLine;
cmdLine = (TCHAR*) malloc((totlen+1)*2);
_tcscpy(cmdLine, cmdLine1);
_tcscat(cmdLine, cmdLine2);
_tcscat(cmdLine, cmdLine3);
_tcscat(cmdLine, cmdLine4);
_tcscat(cmdLine, cmdLine5);
_tcscat(cmdLine, cmdLine6);
//_tprintf(L"%s\n", cmdLine);
//************************************
//Prepare and run CreateProcessW
//************************************
PROCESS_INFORMATION pi;
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
#ifdef NOSHELL
CreateProcessW(NULL, cmdLine, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
#else
CreateProcessW(NULL, cmdLine, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi);
#endif
//************************************
//Return ErrorLevel
//************************************
DWORD result = WaitForSingleObject(pi.hProcess,15000);
if(result == WAIT_TIMEOUT){return -2;} //Timeout error
DWORD exitCode=0;
if(!GetExitCodeProcess(pi.hProcess, &exitCode) ){return -1;} //Cannot get exitcode
return exitCode; //Correct exitcode
}
This is a simple program. its goal is to simply launches itself again, showing its system process ID and its location in the process list.
here is the Code:
#include <Windows.h>
#include <stdio.h>
#include "pch.h"
#include <iostream>
using namespace std;
void StartClone(int nCloneID)
{
// extract the file name used for the current
//executable file
TCHAR szFilename[MAX_PATH];
GetModuleFileName(NULL, szFilename, MAX_PATH);
// Format the command line for the child process
//and notify its EXE filename and clone ID
TCHAR szCmdLine[MAX_PATH];
sprintf(szCmdLine, "\"%s\" %d", szFilename, nCloneID);
// STARTUPINFO structure for child processes
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
// must be the size of this structure
si.cb = sizeof(si);
// returned process information for the child process
PROCESS_INFORMATION pi;
// Create processes using the same executable and command line, and
//assign the nature of their child processes
BOOL bCreateOK = ::CreateProcess(
szFilename, // The name of the application that generated the EXE
szCmdLine, // tell the flag that behaves like a child process
NULL, // default process security
NULL, // default thread safety
FALSE, // does not inherit the handle
CREATE_NEW_CONSOLE, // use the new console
NULL, // new environment
NULL, // Current directory
&si, // start information
&pi); // returned process information
// release the reference to the child process
if (bCreateOK)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
int main(int argc, char* argv[])
{
// Determine the number of processes derived, and the location of the derived process in the process list
int nClone = 0;
// Modify the statement : int nClone;
//First modification: nClone=0;
if (argc > 1)
{
// Extract the clone ID from the second parameter
::sscanf(argv[1], "%d", &nClone);
}
//Second modification: nClone=0;
// Show the process location
std::cout << "Process ID:" << ::GetCurrentProcessId()
<< ", Clone ID:" << nClone
<< std::endl;
// Check if there is a need to create a child process
const int c_nCloneMax = 5;
if (nClone < c_nCloneMax)
{
// Send the command line and clone number of the new process
StartClone(++nClone);
}
// Wait for the response keyboard input to end the process
getchar();
return 0;
}
But When I compile it i get So many Errors, some of them are
Error C2065 'TCHAR': undeclared identifier OS_EX2-1
Error C2146 syntax error: missing ';' before identifier 'szFilename'
Error C2065 'MAX_PATH': undeclared identifier OS_EX2-1
And In Visual Studio 2010 is working no problem. we just configure The " Character Set" proprety to "Not Set".
Is there Any One who has an idea?? Thanks A lot
I am using _popen to start a process to run a command and gather the output
This is my c++ code:
bool exec(string &cmd, string &result)
{
result = "";
FILE* pipe = _popen(cmd.c_str(), "rt");
if (!pipe)
return(false);
char buffer[128];
while(!feof(pipe))
{
if(fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
_pclose(pipe);
return(true);
}
Is there any way of doing this without a console window opening (as it currently does at the _popen statement)?
On Windows, CreateProcess with a STARTUPINFO structure that has dwFlags to include STARTF_USESSHOWWINDOW. Then setting STARTUPINFO.dwFlags to SW_HIDE will cause the console window to be hidden when triggered. Example code (which may be poorly formatted, and contains a mix of C++ and WinAPI):
#include <windows.h>
#include <iostream>
#include <string>
using std::cout;
using std::endl;
void printError(DWORD);
int main()
{
STARTUPINFOA si = {0};
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
BOOL result = ::CreateProcessA("c:/windows/system32/notepad.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if(result == 0) {
DWORD error = ::GetLastError();
printError(error);
std::string dummy;
std::getline(std::cin, dummy);
return error;
}
LPDWORD retval = new DWORD[1];
::GetExitCodeProcess(pi.hProcess, retval);
cout << "Retval: " << retval[0] << endl;
delete[] retval;
cout << "Press enter to continue..." << endl;
std::string dummy;
std::getline(std::cin, dummy);
return 0;
}
void printError(DWORD error) {
LPTSTR lpMsgBuf = nullptr;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
cout << reinterpret_cast<char*>(lpMsgBuf) << endl;
LocalFree(lpMsgBuf);
}
As far as I know, you can't1: you are starting a console application (cmd.exe, that will run the specified command), and Windows always creates a console window when starting a console application.
although, you can hide the window after the process started, or even create it hidden if you pass the appropriate flags to CreateProcess; problem is, _popen do not pass these flags, so you have to use the Win32 APIs instead of _popen to create your pipe.
[Final Edit]
a similar SO question merges everything said above and gets you your output
C++ popen command without console
[Edited again]
erk. sorry I got excited about spawning processes. I reread your q. and apart from the extra window you're actually trying to get the processes's stdout/stderr. I'd just like to add that for that purpose, all my suggestions are sadly irrelevant. but I'll leave them here for reference.
[Edited]
For no good specific reason (except that "open" works for both windows and macs), I use ShellExecute for spawning processes rather than CreateProcess. I'll research that later..but here is my StartProcess function.
Hidden or Minimized seem to produce the same result. the cmd window does come into being but it is minimized and doesn't ever pop up on the desktop which might be your primary goal.
#if defined(PLATFORM_WIN32)
#include <Windows.h>
#include <shellapi.h>
#elif defined(PLATFORM_OSX)
#include <sys/param.h>
#endif
namespace LGSysUtils
{
// -----------------------------------------------------------------------
// pWindow : {Optional} - can be NULL
// pOperation : "edit", "explore", "find", "open", "print"
// pFile : url, local file to execute
// pParameters : {Optional} - can be NULL otherwise a string of args to pass to pFile
// pDirectory : {Optional} - set cwd for process
// type : kProcessWinNormal, kProcessWinMinimized, kProcessWinMaximized, kProcessHidden
//
bool StartProcess(void* pWindow, const char* pOperation, const char* pFile, const char* pParameters, const char* pDirectory, LGSysUtils::eProcessWin type)
{
bool rc = false;
#if defined(PLATFORM_WIN32)
int showCmd;
switch(type)
{
case kProcessWinMaximized:
showCmd = SW_SHOWMAXIMIZED;
break;
case kProcessWinMinimized:
showCmd = SW_SHOWMINIMIZED;
break;
case kProcessHidden:
showCmd = SW_HIDE;
break;
case kProcessWinNormal:
default:
showCmd = SW_NORMAL;
}
int shellRC = (int)ShellExecute(reinterpret_cast<HWND>(pWindow), pOperation,pFile,pParameters,pDirectory,showCmd);
//Returns a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise.
if( shellRC > 32 )
{
rc = true;
}
#elif defined(PLATFORM_OSX)
char cmd[1024];
sprintf(cmd, "%s %s", pOperation, pFile);
int sysrc = system( cmd );
dbPrintf("sysrc = %d", sysrc);
rc = true;
#endif
return rc;
}
}
[and previously mentioned]
If you are in control of the source code for the application that is launched, you could try adding this to the top of your main.cpp (or whatever you have named it)
// make this process windowless/aka no console window
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
You could also feed those options to the linker directly. The above is easier to play with for different build configurations imho.