- enhancement: when running a program, redirect a data file to its stdin

This commit is contained in:
royqh1979 2021-10-24 23:13:00 +08:00
parent 4da3b6d1fa
commit 7bb59955e6
11 changed files with 371 additions and 10 deletions

View File

@ -7,6 +7,7 @@ Version 0.7.3
- fix: the "add bookmark" menu item is not correctly disabled on a bookmarked line - fix: the "add bookmark" menu item is not correctly disabled on a bookmarked line
- enhancement: "use utf8 by default" in editor's misc setting - enhancement: "use utf8 by default" in editor's misc setting
- fix: syntax issues not correctly cleared when the file was saved as another name. - fix: syntax issues not correctly cleared when the file was saved as another name.
- enhancement: when running a program, redirect a data file to its stdin
Version 0.7.2 Version 0.7.2
- fix: rainbow parenthesis stop functioning when change editor's general options - fix: rainbow parenthesis stop functioning when change editor's general options

View File

@ -197,12 +197,26 @@ void CompilerManager::run(const QString &filename, const QString &arguments, con
if (mRunner!=nullptr) { if (mRunner!=nullptr) {
return; return;
} }
QChar redirectChar = '0';
QString redirectInputFilename;
bool redirectInput=false;
if (pSettings->executor().redirectInput()
&& !pSettings->executor().inputFilename().isEmpty()) {
redirectInput =true;
redirectChar = '1';
redirectInputFilename = pSettings->executor().inputFilename();
}
if (pSettings->executor().pauseConsole() && programHasConsole(filename)) { if (pSettings->executor().pauseConsole() && programHasConsole(filename)) {
QString newArguments = QString(" 0 \"%1\" %2").arg(toLocalPath(filename)).arg(arguments); QString newArguments = QString(" %1 \"%2\" %3")
.arg(redirectChar)
.arg(toLocalPath(filename)).arg(arguments);
mRunner = new ExecutableRunner(includeTrailingPathDelimiter(pSettings->dirs().app())+"ConsolePauser.exe",newArguments,workDir); mRunner = new ExecutableRunner(includeTrailingPathDelimiter(pSettings->dirs().app())+"ConsolePauser.exe",newArguments,workDir);
if (redirectInput)
mRunner->setRedirectConsoleProgram(true);
} else { } else {
mRunner = new ExecutableRunner(filename,arguments,workDir); mRunner = new ExecutableRunner(filename,arguments,workDir);
} }
mRunner->setRedirectInputFilename(redirectInputFilename);
connect(mRunner, &ExecutableRunner::finished, this ,&CompilerManager::onRunnerTerminated); connect(mRunner, &ExecutableRunner::finished, this ,&CompilerManager::onRunnerTerminated);
connect(mRunner, &ExecutableRunner::finished, pMainWindow ,&MainWindow::onRunFinished); connect(mRunner, &ExecutableRunner::finished, pMainWindow ,&MainWindow::onRunFinished);
connect(mRunner, &ExecutableRunner::runErrorOccurred, pMainWindow ,&MainWindow::onRunErrorOccured); connect(mRunner, &ExecutableRunner::runErrorOccurred, pMainWindow ,&MainWindow::onRunErrorOccured);

View File

@ -12,7 +12,8 @@ ExecutableRunner::ExecutableRunner(const QString &filename, const QString &argum
mFilename(filename), mFilename(filename),
mArguments(arguments), mArguments(arguments),
mWorkDir(workDir), mWorkDir(workDir),
mStop(false) mStop(false),
mRedirectConsoleProgram(false)
{ {
} }
@ -22,6 +23,26 @@ void ExecutableRunner::stop()
mStop = true; mStop = true;
} }
bool ExecutableRunner::redirectConsoleProgram() const
{
return mRedirectConsoleProgram;
}
void ExecutableRunner::setRedirectConsoleProgram(bool newRedirectConsoleProgram)
{
mRedirectConsoleProgram = newRedirectConsoleProgram;
}
const QString &ExecutableRunner::redirectInputFilename() const
{
return mRedirectInputFilename;
}
void ExecutableRunner::setRedirectInputFilename(const QString &newDataFile)
{
mRedirectInputFilename = newDataFile;
}
void ExecutableRunner::run() void ExecutableRunner::run()
{ {
emit started(); emit started();
@ -48,19 +69,31 @@ void ExecutableRunner::run()
} }
env.insert("PATH",path); env.insert("PATH",path);
process.setProcessEnvironment(env); process.setProcessEnvironment(env);
process.setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments * args){ if (redirectConsoleProgram()) {
args->flags |= CREATE_NEW_CONSOLE; process.setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments * args){
args->startupInfo -> dwFlags &= ~STARTF_USESTDHANDLES; args->flags |= CREATE_NEW_CONSOLE;
}); });
} else {
process.setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments * args){
args->flags |= CREATE_NEW_CONSOLE;
args->startupInfo -> dwFlags &= ~STARTF_USESTDHANDLES;
});
}
process.connect(&process, &QProcess::errorOccurred, process.connect(&process, &QProcess::errorOccurred,
[&](){ [&](){
errorOccurred= true; errorOccurred= true;
}); });
// qDebug() << mFilename; // qDebug() << mFilename;
// qDebug() << QProcess::splitCommand(mArguments); // qDebug() << QProcess::splitCommand(mArguments);
if (!redirectConsoleProgram()) {
process.closeWriteChannel();
}
process.start(); process.start();
process.closeWriteChannel();
process.waitForStarted(5000); process.waitForStarted(5000);
if (process.state()==QProcess::Running && redirectConsoleProgram()) {
process.write(ReadFileToByteArray(redirectInputFilename()));
process.closeWriteChannel();
}
while (true) { while (true) {
process.waitForFinished(1000); process.waitForFinished(1000);
if (process.state()!=QProcess::Running) { if (process.state()!=QProcess::Running) {

View File

@ -9,6 +9,12 @@ class ExecutableRunner : public QThread
public: public:
ExecutableRunner(const QString& filename, const QString& arguments, const QString& workDir); ExecutableRunner(const QString& filename, const QString& arguments, const QString& workDir);
const QString &redirectInputFilename() const;
void setRedirectInputFilename(const QString &newDataFile);
bool redirectConsoleProgram() const;
void setRedirectConsoleProgram(bool newRedirectConsoleProgram);
signals: signals:
void started(); void started();
void terminated(); void terminated();
@ -22,6 +28,8 @@ private:
QString mArguments; QString mArguments;
QString mWorkDir; QString mWorkDir;
bool mStop; bool mStop;
QString mRedirectInputFilename;
bool mRedirectConsoleProgram;
// QThread interface // QThread interface
protected: protected:

View File

@ -1115,11 +1115,11 @@ void MainWindow::runExecutable(const QString &exeName,const QString &filename)
showMinimized(); showMinimized();
} }
updateAppTitle(); updateAppTitle();
QString params;
if (pSettings->executor().useParams()) { if (pSettings->executor().useParams()) {
mCompilerManager->run(exeName,pSettings->executor().params(),QFileInfo(exeName).absolutePath()); params = pSettings->executor().params();
} else {
mCompilerManager->run(exeName,"",QFileInfo(exeName).absolutePath());
} }
mCompilerManager->run(exeName,params,QFileInfo(exeName).absolutePath());
} }
void MainWindow::runExecutable() void MainWindow::runExecutable()

View File

@ -250,6 +250,7 @@ bool SettingsDialog::setCurrentWidget(const QString &widgetName, const QString &
for (int i=0;i<pGroupItem->rowCount();i++) { for (int i=0;i<pGroupItem->rowCount();i++) {
QStandardItem* pWidgetItem = pGroupItem->child(i); QStandardItem* pWidgetItem = pGroupItem->child(i);
if (pWidgetItem->text() == widgetName) { if (pWidgetItem->text() == widgetName) {
ui->widgetsView->setCurrentIndex(pWidgetItem->index());
on_widgetsView_clicked(pWidgetItem->index()); on_widgetsView_clicked(pWidgetItem->index());
return true; return true;
} }

View File

@ -835,3 +835,12 @@ bool removeFile(const QString &filename)
QFile file(filename); QFile file(filename);
return file.remove(); return file.remove();
} }
QByteArray ReadFileToByteArray(const QString &fileName)
{
QFile file(fileName);
if (file.open(QFile::ReadOnly)) {
return file.readAll();
}
return QByteArray();
}

View File

@ -154,6 +154,7 @@ QString parseMacros(const QString& s);
QStringList ReadFileToLines(const QString& fileName, QTextCodec* codec); QStringList ReadFileToLines(const QString& fileName, QTextCodec* codec);
QStringList ReadFileToLines(const QString& fileName); QStringList ReadFileToLines(const QString& fileName);
QByteArray ReadFileToByteArray(const QString& fileName);
void ReadFileToLines(const QString& fileName, QTextCodec* codec, LineProcessFunc lineFunc); void ReadFileToLines(const QString& fileName, QTextCodec* codec, LineProcessFunc lineFunc);
void StringsToFile(const QStringList& list, const QString& fileName); void StringsToFile(const QStringList& list, const QString& fileName);
void StringToFile(const QString& str, const QString& fileName); void StringToFile(const QString& str, const QString& fileName);

4
tools/ConsolePauser/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/ConsolePauser.layout
/Makefile.win
/ConsolePauser.exe
/main.o

View File

@ -0,0 +1,73 @@
[Project]
FileName = ConsolePauser.dev
Name = ConsolePauser
Type = 1
Ver = 3
ObjFiles =
Includes =
Libs =
PrivateResource =
ResourceIncludes =
MakeIncludes =
Compiler =
CppCompiler =
Linker =
IsCpp = 1
Icon =
ExeOutput =
ObjectOutput =
LogOutput =
LogOutputEnabled = 0
OverrideOutput = 0
OverrideOutputName = ConsolePauser.exe
HostApplication =
Folders =
CommandLine =
UseCustomMakefile = 0
CustomMakefile =
IncludeVersionInfo = 0
SupportXPThemes = 0
CompilerSet = 0
CompilerSettings = 000000a000110000000010000
UnitCount = 1
UsePrecompiledHeader = 0
PrecompiledHeader =
UseUTF8 = 0
StaticLink = 1
AddCharset = 1
Encoding = AUTO
[VersionInfo]
Major = 1
Minor = 0
Release = 0
Build = 0
LanguageID = 1033
CharsetID = 1252
CompanyName =
FileVersion = 1.0.0.0
FileDescription = Developed using the Dev-C++ IDE
InternalName =
LegalCopyright =
LegalTrademarks =
OriginalFilename =
ProductName =
ProductVersion = 1.0.0.0
AutoIncBuildNr = 0
SyncProduct = 1
[Unit1]
FileName = main.cpp
CompileCpp = 1
Folder =
Compile = 1
Link = 1
Priority = 1000
OverrideBuildCmd = 0
BuildCmd =
UseUTF8 = 1
DetectEncoding = 1
Encoding = 0
FileEncoding = AUTO

View File

@ -0,0 +1,217 @@
// Execute & Pause
// Runs a program, then keeps the console window open after it finishes
#include <string>
using std::string;
#include <stdio.h>
#include <windows.h>
#define MAX_COMMAND_LENGTH 32768
#define MAX_ERROR_LENGTH 2048
HANDLE hJob;
LONGLONG GetClockTick() {
LARGE_INTEGER dummy;
QueryPerformanceCounter(&dummy);
return dummy.QuadPart;
}
LONGLONG GetClockFrequency() {
LARGE_INTEGER dummy;
QueryPerformanceFrequency(&dummy);
return dummy.QuadPart;
}
string GetErrorMessage() {
string result(MAX_ERROR_LENGTH,0);
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),&result[0],result.size(),NULL);
// Clear newlines at end of string
for(int i = result.length()-1;i >= 0;i--) {
if(isspace(result[i])) {
result[i] = 0;
} else {
break;
}
}
return result;
}
void PauseExit(int exitcode, bool reInp) {
HANDLE hInp=NULL;
if (reInp) {
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE hInp = CreateFile("CONIN$", GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ , &sa, OPEN_EXISTING, /*FILE_ATTRIBUTE_NORMAL*/0, NULL);
//si.hStdInput = hInp;
SetStdHandle(STD_INPUT_HANDLE,hInp);
}
//system("pause");
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si,0,sizeof(si));
si.cb = sizeof(si);
memset(&pi,0,sizeof(pi));
DWORD dwCreationFlags = CREATE_BREAKAWAY_FROM_JOB;
if(!CreateProcess(NULL, (LPSTR)"cmd /c \"pause\"", NULL, NULL, true, dwCreationFlags, NULL, NULL, &si, &pi)) {
printf("\n--------------------------------");
printf("\nFailed to execute 'pause' ");
printf("\nError %lu: %s\n",GetLastError(),GetErrorMessage().c_str());
system("pause");
exit(exitcode);
}
WINBOOL bSuccess = AssignProcessToJobObject( hJob, pi.hProcess );
if ( bSuccess == FALSE ) {
printf( "AssignProcessToJobObject failed: error %d\n", GetLastError() );
system("pause");
exit(exitcode);
}
WaitForSingleObject(pi.hProcess, INFINITE); // Wait for it to finish
if (reInp) {
CloseHandle(hInp);
}
CloseHandle( hJob );
exit(exitcode);
}
string GetCommand(int argc,char** argv,bool &reInp) {
string result;
reInp = (strcmp(argv[1],"0")!=0) ;
for(int i = 2;i < argc;i++) {
/*
// Quote the first argument in case the path name contains spaces
// if(i == 1) {
// result += string("\"") + string(argv[i]) + string("\"");
// } else {
// Quote the first argument in case the path name contains spaces
// result += string(argv[i]);
// }
*/
// Quote the first argument in case the path name contains spaces
result += string("\"") + string(argv[i]) + string("\"");
// Add a space except for the last argument
if(i != (argc-1)) {
result += string(" ");
}
}
if(result.length() > MAX_COMMAND_LENGTH) {
printf("\n--------------------------------");
printf("\nError: Length of command line string is over %d characters\n",MAX_COMMAND_LENGTH);
PauseExit(EXIT_FAILURE,reInp);
}
return result;
}
DWORD ExecuteCommand(string& command,bool reInp) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si,0,sizeof(si));
si.cb = sizeof(si);
memset(&pi,0,sizeof(pi));
DWORD dwCreationFlags = CREATE_BREAKAWAY_FROM_JOB;
if(!CreateProcess(NULL, (LPSTR)command.c_str(), NULL, NULL, true, dwCreationFlags, NULL, NULL, &si, &pi)) {
printf("\n--------------------------------");
printf("\nFailed to execute \"%s\":",command.c_str());
printf("\nError %lu: %s\n",GetLastError(),GetErrorMessage().c_str());
PauseExit(EXIT_FAILURE,reInp);
}
WINBOOL bSuccess = AssignProcessToJobObject( hJob, pi.hProcess );
if ( bSuccess == FALSE ) {
printf( "AssignProcessToJobObject failed: error %d\n", GetLastError() );
return 0;
}
WaitForSingleObject(pi.hProcess, INFINITE); // Wait for it to finish
DWORD result = 0;
GetExitCodeProcess(pi.hProcess, &result);
return result;
}
int main(int argc, char** argv) {
// First make sure we aren't going to read nonexistent arrays
if(argc < 3) {
printf("\n--------------------------------");
printf("\nUsage: ConsolePauser.exe <0|1> <filename> <parameters>\n");
printf("\n 1 means the STDIN is redirected by Dev-CPP;0 means not\n");
PauseExit(EXIT_SUCCESS,false);
}
// Make us look like the paused program
SetConsoleTitle(argv[2]);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = FALSE;
hJob= CreateJobObject( &sa, NULL );
if ( hJob == NULL ) {
printf( "CreateJobObject failed: error %d\n", GetLastError() );
return 0;
}
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info = { 0 };
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
WINBOOL bSuccess = SetInformationJobObject( hJob, JobObjectExtendedLimitInformation, &info, sizeof( info ) );
if ( bSuccess == FALSE ) {
printf( "SetInformationJobObject failed: error %d\n", GetLastError() );
return 0;
}
bool reInp;
// Then build the to-run application command
string command = GetCommand(argc,argv,reInp);
HANDLE hOutput = NULL;
if (reInp) {
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
hOutput = CreateFile("CONOUT$", GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE , &sa, OPEN_EXISTING, /*FILE_ATTRIBUTE_NORMAL*/0, NULL);
SetStdHandle(STD_OUTPUT_HANDLE, hOutput);
SetStdHandle(STD_ERROR_HANDLE, hOutput);
freopen("CONOUT$","w+",stdout);
freopen("CONOUT$","w+",stderr);
}
// Save starting timestamp
LONGLONG starttime = GetClockTick();
// Then execute said command
DWORD returnvalue = ExecuteCommand(command,reInp);
// Get ending timestamp
LONGLONG endtime = GetClockTick();
double seconds = (endtime - starttime) / (double)GetClockFrequency();
// Done? Print return value of executed program
printf("\n--------------------------------");
printf("\nProcess exited after %.4g seconds with return value %lu\n",seconds,returnvalue);
PauseExit(returnvalue,reInp);
}