mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 03:48:17 +00:00
d73cc06799
svn path=/trunk/; revision=3955
683 lines
15 KiB
C++
683 lines
15 KiB
C++
/********************************************************************
|
|
* Module: process.cpp. This is part of Visual-MinGW.
|
|
*
|
|
* Purpose: Procedures to invoke MinGW compiler.
|
|
*
|
|
* Authors: Manu B.
|
|
*
|
|
* License: Visual-MinGW is covered by GNU General Public License,
|
|
* Copyright (C) 2001 Manu B.
|
|
* See license.htm for more details.
|
|
*
|
|
* Note: The following article from MSDN explanes how to handle Callback
|
|
* procedures :
|
|
* Calling All Members: Member Functions as Callbacks.
|
|
* by Dale Rogerson.
|
|
* Microsoft Developer Network Technology Group.
|
|
* April 30, 1992.
|
|
* http://msdn.microsoft.com/archive/default.asp
|
|
*
|
|
* Revisions:
|
|
*
|
|
********************************************************************/
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <process.h>
|
|
#include <time.h>
|
|
#include <process.h>
|
|
#include "process.h"
|
|
#include "project.h"
|
|
#include "main.h"
|
|
#include "rsrc.h"
|
|
|
|
extern CCriticalSection CriticalSection;
|
|
extern CMessageBox MsgBox;
|
|
char errmsg[128];
|
|
|
|
// For winApp.isWinNT and winApp.Report.Append
|
|
extern CWinApp winApp;
|
|
|
|
/********************************************************************
|
|
* Class: CCommandDlg.
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Revisions:
|
|
*
|
|
********************************************************************/
|
|
CCommandDlg::CCommandDlg(){
|
|
*cmdLine = '\0';
|
|
}
|
|
|
|
CCommandDlg::~CCommandDlg(){
|
|
}
|
|
|
|
HWND CCommandDlg::Create(void){
|
|
return CreateParam(&winApp, IDD_COMMAND, 0);
|
|
}
|
|
|
|
LRESULT CALLBACK CCommandDlg::CDlgProc(UINT Message, WPARAM wParam, LPARAM lParam){
|
|
switch(Message){
|
|
case WM_INITDIALOG:
|
|
return OnInitDialog((HWND) wParam, lParam);
|
|
|
|
case WM_COMMAND:
|
|
OnCommand(HIWORD(wParam), LOWORD(wParam), (HWND) lParam);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
EndDlg(0);
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CCommandDlg::OnInitDialog(HWND, LPARAM){
|
|
hCmdLine = GetItem(IDC_CMDLINE);
|
|
|
|
SetItemText(hCmdLine, cmdLine);
|
|
// Show the dialog.
|
|
Show();
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CCommandDlg::OnCommand(WORD, WORD wID, HWND){
|
|
switch (wID){
|
|
case IDOK:
|
|
GetItemText(hCmdLine, cmdLine, sizeof(cmdLine));
|
|
//MsgBox.DisplayString(cmdLine);
|
|
winApp.Process.CommandLine(cmdLine);
|
|
return TRUE;
|
|
|
|
case IDCANCEL:
|
|
EndDlg(IDCANCEL);
|
|
return FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
* Class: CTask.
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Revisions:
|
|
*
|
|
********************************************************************/
|
|
CTask::CTask(){
|
|
*cmdLine = '\0';
|
|
*szFileName = '\0';
|
|
creationFlag = 0;
|
|
outputFlag = 0;
|
|
}
|
|
|
|
CTask::~CTask(){
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
* Class: CStack.
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Revisions:
|
|
*
|
|
********************************************************************/
|
|
CStack::CStack(){
|
|
retBuf = NULL;
|
|
}
|
|
|
|
CStack::~CStack(){
|
|
DestroyList();
|
|
if (retBuf)
|
|
delete retBuf;
|
|
}
|
|
|
|
void CStack::DetachCurrent(void){
|
|
// Empty list ?
|
|
if (current != NULL){
|
|
CNode * node = current;
|
|
|
|
// Detach node from the list.
|
|
if (node->next != NULL)
|
|
node->next->prev = node->prev;
|
|
if (node->prev != NULL)
|
|
node->prev->next = node->next;
|
|
|
|
// Set current node.
|
|
if(node->next != NULL)
|
|
current = node->next;
|
|
else
|
|
current = node->prev;
|
|
|
|
if (current == NULL){
|
|
// Now, the list is empty.
|
|
first = last = NULL;
|
|
|
|
}else if (first == node){
|
|
// Detached node was first.
|
|
first = current;
|
|
|
|
}else if (last == node){
|
|
// Detached node was last.
|
|
last = current;
|
|
}
|
|
count--;
|
|
}
|
|
}
|
|
|
|
/********************************************************************
|
|
* Push/Pop/Flush.
|
|
********************************************************************/
|
|
int CStack::Push(CTask * newTask){
|
|
InsertLast(newTask);
|
|
return Length();
|
|
}
|
|
|
|
CTask * CStack::Pop(void){
|
|
// Delete return buffer.
|
|
if (retBuf){
|
|
delete retBuf;
|
|
retBuf = NULL;
|
|
}
|
|
|
|
// Get first node. (FIFO stack)
|
|
retBuf = (CTask*) First();
|
|
|
|
// The Stack is empty ?
|
|
if (!retBuf)
|
|
return NULL;
|
|
|
|
// Detach current node from the list. Return a pointer to it.
|
|
DetachCurrent();
|
|
return retBuf;
|
|
}
|
|
|
|
void CStack::Flush(void){
|
|
DestroyList();
|
|
if (retBuf)
|
|
delete retBuf;
|
|
retBuf = NULL;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
* Class: CPipes.
|
|
*
|
|
* Purpose: Creates needed pipes, depending on creationFlag.
|
|
* Like GNU Make does, we use an Handle array for our pipes.
|
|
* Parent Process Side is stdXXX[0] and Child Process Side is stdXXX[1].
|
|
*
|
|
* Ex: PARENT ->[0]IN_PIPE[1]-> CHILD_IO ->[1]OUT_PIPE[0]-> PARENT
|
|
* ->[1]ERR_PIPE[0]-> PARENT
|
|
* Revisions:
|
|
*
|
|
********************************************************************/
|
|
CPipes::CPipes(){
|
|
hIn[0] = NULL;
|
|
hIn[1] = NULL;
|
|
hOut[0] = NULL;
|
|
hOut[1] = NULL;
|
|
hErr[0] = NULL;
|
|
hErr[1] = NULL;
|
|
}
|
|
|
|
CPipes::~CPipes(){
|
|
}
|
|
|
|
bool CPipes::Create(WORD creationFlag, bool winNT){
|
|
/* Create needed pipes according to creationFlag */
|
|
/* Parent side of pipes is [0], child side is [1] */
|
|
HANDLE hDup;
|
|
SECURITY_ATTRIBUTES sa;
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.bInheritHandle = TRUE;
|
|
sa.lpSecurityDescriptor = NULL;
|
|
|
|
if (winNT){
|
|
/* Create a security descriptor for Windows NT */
|
|
SECURITY_DESCRIPTOR sd;
|
|
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)){
|
|
sprintf(errmsg, "vm error: Process.cpp InitializeSecurityDescriptor(winNT) failed (e=%d)", (int)GetLastError());
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
return false;
|
|
}
|
|
if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)){
|
|
sprintf(errmsg, "vm error: Process.cpp SetSecurityDescriptorDacl(winNT) failed (e=%d)", (int)GetLastError());
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
return false;
|
|
}
|
|
sa.lpSecurityDescriptor = &sd;
|
|
}
|
|
|
|
/* Input pipe */
|
|
if (!CreatePipe(&hIn[1], &hIn[0], &sa, 0)){
|
|
sprintf(errmsg, "vm error: Process.cpp CreatePipe(In) failed (e=%d)", (int)GetLastError());
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if (!DuplicateHandle(GetCurrentProcess(),
|
|
hIn[0],
|
|
GetCurrentProcess(),
|
|
&hDup,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS)){
|
|
sprintf(errmsg, "vm error: Process.cpp DuplicateHandle(In) failed (e=%d)", (int)GetLastError());
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
return false;
|
|
}
|
|
CloseHandle(hIn[0]);
|
|
hIn[0] = hDup;
|
|
|
|
/* Output pipe */
|
|
if (!CreatePipe(&hOut[0], &hOut[1], &sa, 0)){
|
|
sprintf(errmsg, "vm error: Process.cpp CreatePipe(Out) failed (e=%d)", (int)GetLastError());
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if (!DuplicateHandle(GetCurrentProcess(),
|
|
hOut[0],
|
|
GetCurrentProcess(),
|
|
&hDup,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS)){
|
|
sprintf(errmsg, "vm error: Process.cpp DuplicateHandle(Out) failed (e=%d)", (int)GetLastError());
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
return false;
|
|
}
|
|
CloseHandle(hOut[0]);
|
|
hOut[0] = hDup;
|
|
|
|
/* Error pipe */
|
|
if (!(creationFlag & OUTERR_PIPE) && (creationFlag & ERR_PIPE)){
|
|
if (!CreatePipe(&hErr[0], &hErr[1], &sa, 0)){
|
|
sprintf(errmsg, "vm error: Process.cpp CreatePipe(Err) failed (e=%d)", (int)GetLastError());
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if (!DuplicateHandle(GetCurrentProcess(),
|
|
hErr[0],
|
|
GetCurrentProcess(),
|
|
&hDup,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS)){
|
|
sprintf(errmsg, "vm error: Process.cpp DuplicateHandle(Err) failed (e=%d)", (int)GetLastError());
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
return false;
|
|
}
|
|
CloseHandle(hErr[0]);
|
|
hErr[0] = hDup;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CPipes::CloseChildSide(void){
|
|
return Close(1);
|
|
}
|
|
|
|
bool CPipes::CloseParentSide(void){
|
|
return Close(0);
|
|
}
|
|
|
|
bool CPipes::Close(int side){
|
|
|
|
if (side < 0 || side > 1)
|
|
return false;
|
|
|
|
if (hIn[side]){
|
|
CloseHandle(hIn[side]);
|
|
hIn[side] = NULL;
|
|
}
|
|
|
|
if (hOut[side]){
|
|
CloseHandle(hOut[side]);
|
|
hOut[side] = NULL;
|
|
}
|
|
|
|
if (hErr[side]){
|
|
CloseHandle(hErr[side]);
|
|
hErr[side] = NULL;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
* Class: CProcess.
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Revisions:
|
|
*
|
|
********************************************************************/
|
|
CProcess::CProcess(){
|
|
Running = false;
|
|
exitCode = 0;
|
|
|
|
pi.hProcess = 0;
|
|
pi.hThread = 0;
|
|
pi.dwProcessId = 0;
|
|
pi.dwThreadId = 0;
|
|
}
|
|
|
|
CProcess::~CProcess(){
|
|
}
|
|
|
|
/********************************************************************
|
|
* Manage Tasks.
|
|
********************************************************************/
|
|
bool CProcess::isRunning(void){
|
|
if (Running){
|
|
MsgBox.DisplayWarning("A process is already running !");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CTask * CProcess::AddTask(char * cmdLine, WORD creationFlag, WORD outputFlag){
|
|
CTask * newTask = new CTask;
|
|
|
|
strcpy(newTask->cmdLine, cmdLine);
|
|
newTask->creationFlag = creationFlag;
|
|
newTask->outputFlag = outputFlag;
|
|
Push(newTask);
|
|
return newTask;
|
|
}
|
|
|
|
bool CProcess::CmdCat(char * cmdLine){
|
|
CTask * task = (CTask*) GetCurrent();
|
|
if (!task)
|
|
return false;
|
|
|
|
strcat(task->cmdLine, cmdLine);
|
|
return true;
|
|
}
|
|
|
|
/********************************************************************
|
|
* RunNext/Run/RunProcess.
|
|
********************************************************************/
|
|
void __cdecl call_thread(void * ptr){
|
|
/* C++ adapter */
|
|
((CProcess *) ptr)->Run_Thread_Internal();
|
|
}
|
|
|
|
void CProcess::Run(void){
|
|
// Check if something is already running before creating a thread.
|
|
if (!Running){
|
|
// Call Run_Thread_Internal()
|
|
_beginthread(call_thread, 1024 * 1024, (void *) this);
|
|
}
|
|
}
|
|
|
|
void CProcess::Run_Thread_Internal(void){
|
|
exitCode = 0;
|
|
/* Execute each task */
|
|
for ( ; ; ){
|
|
/* If previous task returns an error code, abort */
|
|
if (exitCode != 0)
|
|
break;
|
|
|
|
// Get one task to execute.
|
|
currTask = Pop();
|
|
|
|
// Nothing to run.
|
|
if (!currTask)
|
|
break;
|
|
|
|
/* Show command lines ?*/
|
|
winApp.Report.Append(currTask->cmdLine, LVOUT_NORMAL);
|
|
|
|
if (RunProcess(currTask)){
|
|
winApp.Report.Append("Abort !", LVOUT_NORMAL);
|
|
exitCode = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Successful ?
|
|
if (exitCode == 0)
|
|
winApp.Report.Append("Performed successfully.", LVOUT_NORMAL);
|
|
|
|
Flush();
|
|
Running = false;
|
|
return;
|
|
}
|
|
|
|
bool CProcess::RunProcess(CTask * task){
|
|
if (!task)
|
|
return false;
|
|
|
|
bool usePipes = task->creationFlag;
|
|
STARTUPINFO si = {sizeof(STARTUPINFO), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0};
|
|
|
|
/* PROCESS_INFORMATION */
|
|
pi.hProcess = 0;
|
|
pi.hThread = 0;
|
|
pi.dwProcessId = 0;
|
|
pi.dwThreadId = 0;
|
|
|
|
/* Process creation with pipes */
|
|
if (usePipes){
|
|
/* Create needed pipes according to creationFlag */
|
|
if(!Pipes.Create(task->creationFlag, winApp.isWinNT)){
|
|
Pipes.CloseChildSide();
|
|
Pipes.CloseParentSide();
|
|
return false;
|
|
}
|
|
|
|
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
|
si.wShowWindow = SW_HIDE;
|
|
//si.wShowWindow = SW_SHOWNORMAL;
|
|
|
|
/* Set pipe handles */
|
|
if (Pipes.hIn[1] != NULL && Pipes.hOut[1] != NULL){
|
|
si.hStdInput = Pipes.hIn[1];
|
|
si.hStdOutput = Pipes.hOut[1];
|
|
if (Pipes.hErr[1] == NULL)
|
|
si.hStdError = Pipes.hOut[1];
|
|
else
|
|
si.hStdError = Pipes.hErr[1];
|
|
}else{
|
|
sprintf(errmsg, "vm error: Process.cpp Invalid pipe handle");
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
Pipes.CloseChildSide();
|
|
Pipes.CloseParentSide();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Create the child process */
|
|
Running = CreateProcess(NULL,
|
|
task->cmdLine,
|
|
NULL,
|
|
NULL,
|
|
usePipes,
|
|
0,
|
|
NULL,
|
|
/*startDir[0] ? startDir :*/ NULL,
|
|
&si,
|
|
&pi);
|
|
|
|
if (!Running){
|
|
/* CreateProcess failed. Close handles and return */
|
|
Pipes.CloseChildSide();
|
|
Pipes.CloseParentSide();
|
|
sprintf(errmsg, "vm error: Process.cpp CreateProcess failed (e=%d)", (int)GetLastError());
|
|
winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
return false;
|
|
}else{
|
|
/* Close child process handles */
|
|
Pipes.CloseChildSide();
|
|
|
|
if (!(usePipes & IN_PIPE)){
|
|
/* Don't use the Input pipe */
|
|
::CloseHandle(Pipes.hIn[0]);
|
|
Pipes.hIn[0] = NULL;
|
|
}
|
|
}
|
|
|
|
//sprintf(errmsg, "vm debug: enter io loop");
|
|
//winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
if (usePipes){
|
|
/* Initialize buffers */
|
|
*outBuf = 0;
|
|
chr = outBuf;
|
|
bool bResult;
|
|
for ( ; ; ){
|
|
Sleep(100L);
|
|
|
|
bResult = ReadStdOut(task, Pipes.hOut[0]);
|
|
if (bResult != NO_ERROR)
|
|
break;
|
|
|
|
::GetExitCodeProcess(pi.hProcess, &exitCode);
|
|
if (exitCode != STILL_ACTIVE){
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//sprintf(errmsg, "vm debug: exit io loop");
|
|
//winApp.Report.Append(errmsg, LVOUT_ERROR);
|
|
|
|
/* The child process is running. Perform I/O until terminated */
|
|
::WaitForSingleObject(pi.hProcess, INFINITE);
|
|
/* Process terminated. Get exit code. */
|
|
::GetExitCodeProcess(pi.hProcess, &exitCode);
|
|
if (exitCode == NO_ERROR){
|
|
return NO_ERROR;
|
|
}
|
|
/* Close handles */
|
|
Pipes.CloseParentSide();
|
|
::CloseHandle(pi.hProcess);
|
|
if (pi.hThread){
|
|
::CloseHandle(pi.hThread);
|
|
pi.hThread = NULL;
|
|
}
|
|
return exitCode;
|
|
}
|
|
|
|
/********************************************************************
|
|
* Pipes input/output.
|
|
********************************************************************/
|
|
void CProcess::WriteStdIn(HANDLE hPipe, WORD){
|
|
if (!hPipe)
|
|
return;
|
|
|
|
return;
|
|
}
|
|
|
|
void CProcess::ReadStdErr(HANDLE hPipe, WORD){
|
|
if (!hPipe)
|
|
return;
|
|
|
|
return;
|
|
}
|
|
|
|
long CProcess::ReadStdOut(CTask * task, HANDLE hPipe){
|
|
if (!task || !hPipe)
|
|
return ERROR_INVALID_FUNCTION;
|
|
|
|
/* Copy each char and output lines while there is something to read */
|
|
for ( ; ; ){
|
|
// Copy one char, return if nothing available.
|
|
if (!ReadOneChar(hPipe, chr))
|
|
break;
|
|
|
|
// Ignore CR.
|
|
if (*chr == '\r')
|
|
continue;
|
|
|
|
if (*chr != '\n'){
|
|
chr++;
|
|
/* @@TODO Overflow
|
|
if ((chr - outBuf) >= max_len)
|
|
realloc(buffer);*/
|
|
// End of line
|
|
}else if (*chr =='\n'){
|
|
*chr = '\0';
|
|
// Output error lines to List View.
|
|
if (task->outputFlag == STDOUT_FILE_APPEND){
|
|
WriteFileAppend(task->szFileName, outBuf, (chr - outBuf));
|
|
}else{
|
|
OutputLine(task->outputFlag, outBuf, (chr - outBuf));
|
|
}
|
|
*outBuf = '\0';
|
|
chr = outBuf;
|
|
}
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
int CProcess::ReadOneChar(HANDLE hPipe, char * chrin){
|
|
DWORD bytesRead = 0;
|
|
DWORD bytesAvail = 0;
|
|
|
|
if (!PeekNamedPipe(hPipe, chrin, (DWORD)1, &bytesRead, &bytesAvail, NULL))
|
|
return 0;
|
|
|
|
if (bytesAvail == 0)
|
|
return 0;
|
|
|
|
if (!ReadFile(hPipe, chrin, (DWORD)1, &bytesRead, NULL))
|
|
return 0;
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
bool CProcess::CommandLine(char * cmdLine){
|
|
if (!Pipes.hIn[0])
|
|
return false;
|
|
if (!Running || !currTask || !currTask->creationFlag)
|
|
return false;
|
|
int len = strlen(cmdLine);
|
|
if (len){
|
|
strcpy(inBuf, cmdLine);
|
|
char * s = inBuf;
|
|
s+=len;
|
|
*s = '\r';
|
|
s++;
|
|
*s = '\n';
|
|
s++;
|
|
*s = '\0';
|
|
}
|
|
DWORD written;
|
|
|
|
if (!WriteFile(Pipes.hIn[0], inBuf, strlen(inBuf), &written, 0))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CProcess::WriteFileAppend(char * fileName, char * line, int /*len*/){
|
|
if (!*fileName)
|
|
return false;
|
|
|
|
/* Append one line of text to a file */
|
|
FILE * file = fopen(fileName, "a");
|
|
if (file){
|
|
fprintf(file, line);
|
|
fprintf(file, "\n");
|
|
fclose(file);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CProcess::OutputLine(WORD outputFlag, char * line, int /*len*/){
|
|
/* Output error lines to List View */
|
|
|
|
CriticalSection.Enter();
|
|
winApp.Report.Append(line, outputFlag);
|
|
CriticalSection.Leave();
|
|
return true;
|
|
}
|
|
|