CreateProcess, a wywoływanie poleceń systemowych [WIN]

0

Witam !
Mam problem z funkcją CreateProcess. Mianowicie założenie jest takie, aby polecenia z pliku *.bat podane jako parametr były wykonywane po kolei na argv[2] procesach. Polecenia zapisane są linijka po linijce, tak aby pobierać linijkę z pliku *.bat, zapisywać ją w tablicy stringów, a później aby jeden proces mógł ją wykonać. Jeżeli plik *.bat ma 90 linijek, a my chcemy aby pracowało 10 procesów równolegle to tak będzie. Niestety nie wiem co robię źle. Błąd jaki dostaję to: 0x2, co oznacza: **The system cannot find the file specified. ** Wszystko pięknie ale nie wiem do czego to się odwołuje. Aktualnie program ma instrukcje jakie ma wykonać w tablicy stringów, ale niestety CreatProcess ich nie wykonuje. Polecenia mają być wykonywane w konsoli [cmd.exe]
Mój kod:

 
// threads.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <string>
#include <fstream>
#include <process.h>
#include <cstdlib>
#include <sstream>
#include <Windows.h>

using namespace std;

bool fileExists (const string& fileName)
{
        fstream plik;
		plik.open(fileName.c_str(), ios::in);
        if ( plik.is_open() )
        {
			printf("Plik istnieje.\n");
            plik.close();
            return true;
        }
		printf("Plik nie istnieje.\n");
        plik.close();
        return false;
}

int main(int argc, char* argv[])
{
	int iNumOfProcesses;
	string linia;
	int iNumOfLines = 1;
	bool check;
	int iNumOfFors;
	int temp;
	int startAtLine = 0;
	

	int i = 0;
	int j;

	if(argc!= 3)
	{
		cout << "Error: Za malo argumentow!\n";
		getchar();
		return -1;
	}
	while( atoi(argv[2]) == 0 || atoi(argv[2]) > 30)
	{
		cout << "Nie poprawny argument drugi. " <<endl;
		cout << "Argument drugi powinien byc z przedzialu <1 , 30> " << endl;
	}
	iNumOfProcesses = abs(atoi(argv[2]));
		
	check = fileExists(argv[1]);

	fstream plik;
	plik.open(argv[1], ios::in);

	while(!plik.eof())
	{
		getline(plik, linia);
		iNumOfLines = iNumOfLines + 1;
	}
	plik.close();
	
	string *polecenia = new string[iNumOfLines];
	plik.open(argv[1], ios::in);
	while(!plik.eof())
	{
		getline(plik, linia);
		polecenia[i] = linia;
		//cout << linia << endl;
		//cout << polecenia[i] << endl;
		i = i + 1;
	}
	plik.close();


	cout << "Ilosc lini w pliku " << argv[1] << " : " << iNumOfLines << endl;

	PROCESS_INFORMATION *pi = new PROCESS_INFORMATION[iNumOfProcesses];
	HANDLE *hThreadArray = new HANDLE[iNumOfProcesses]; 
	STARTUPINFO si = {0};
	ZeroMemory(&si, sizeof(si));
	int *pi_index = new int[iNumOfProcesses];

	for(i=0; i<iNumOfProcesses; i++)
	{
		ZeroMemory(&pi[i], sizeof(pi[i]));
	}

	si.cb = sizeof(si);

	j = 0;
	iNumOfFors = iNumOfLines / iNumOfProcesses;
	temp = iNumOfLines - ((int)iNumOfFors * iNumOfProcesses);
	startAtLine = iNumOfLines - temp;

	while( j < (int)iNumOfFors)
	{
		for(i = 0; i < iNumOfProcesses; i++)
		{
			pi_index[i] = CreateProcess((LPCWSTR)polecenia[i+(iNumOfProcesses * j)].c_str(), _T(""), 0, 0, 0, 0, 0, 0, &si, &pi[i]);
			//cout << GetLastError() << endl;
			hThreadArray[i] = pi[i].hProcess;
		}
		WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, hThreadArray, TRUE, INFINITE);
		
		for(i = 0; i < iNumOfProcesses; i++)
		{
			CloseHandle(pi[i].hProcess);
			CloseHandle(pi[i].hThread);
		}
		j = j + 1;
	}

	j = 0;
	Sleep(1000);
	if(temp > 1 )
	{
		while(j < temp)
		{
			for(i = 0 ; i < temp; i++)
			{
				pi_index[i] = CreateProcess((LPCWSTR)polecenia[i+startAtLine].c_str(), _T(""), 0, 0, 0, 0, 0, 0, &si, &pi[i]);
				hThreadArray[i] = pi[i].hProcess;
			}
			WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, hThreadArray, TRUE, INFINITE);
		
			for(i = 0; i < temp; i++)
			{
				CloseHandle(pi[i].hProcess);
				CloseHandle(pi[i].hThread);
			}
			j = j + 1;
		}
	}
	
	delete[] pi, pi_index, hThreadArray, polecenia;
	cout << "Konczenie programu\n";

	//system("Pause");
	return 0;
}
0

Znalazłem podobny problem do mojego w internecie, ale nie mogę sobie poradzić z uruchomieniem go:

CreateProcess(NULL, "cmd /C echo test > c:\test.txt", NULL, NULL, 0, 0, NULL, NULL, &si, &pi))

Dodatkowo u siebie muszę używać rzutowania do LPWSTR, tutaj: http://cboard.cprogramming.com/windows-programming/109024-createprocess-plus-command-line.html działa podobno bez tego. Co robię źle ?

0

dodaj L przed stringiem

CreateProcess(NULL, L"cmd /C echo test > c:\\test.txt", ...)

to nie trzeba będfzie rzutowania.

poza tym możesz nie mieć prawa zapisu do c:\.

PS. podwójny backslash.

0

Fakt. Praw zapisu na dysku C: nie mam dlatego się wypluwa.

0

Wpadłem na jeszcze jeden pomysł. Mianowicie tworzę sobie pliki tymczasowe *.bat. Pliki te posiadają po jednej instrukcji. Dalej będzie pętla do tworzenia N procesów, których zadaniem będzie wykonać N plików tymczasowych *.bat. Na początku tworzę folder, do niego wrzucam tymczasowe pliki.
Tak tworzę nazwy, które wrzucam do tablicy stringów:

	plik.open(argv[1], ios::in);
	for(i = 1; i < iNumOfLines+1; i++)
	{
		getline(plik, linia);
		sprintf(buff, "temp\\temp%d.bat", i);
		fileNameBuffer = buff;
		pliki[i] = fileNameBuffer;
		ofstream MyFile(buff);
		MyFile << linia.c_str();
	}
	plik.close(); 

Tak powstaje proces (póki co chcę wywołać jeden, później na bazie tego będę mógł zrobić pętlę):

 
	PROCESS_INFORMATION pi2;
	STARTUPINFO si = {0};
	si.cb = sizeof(si);
	ZeroMemory(&si, sizeof(si));
	

A tak wygląda samo wywołanie funkcji tworzącej nowy proces:

int wynik = CreateProcess((LPCWSTR)pliki[1].c_str(), _T(""), 0, 0, 0, 0, 0, 0, &si, &pi2); 

W jaki sposób przekazać te nazwy plików z tablicy stringów, aby proces wywoływał pliki ?

0

Na początku tworzę folder, do niego wrzucam tymczasowe pliki.
Do plików tymczasowych istnieje specjalna ścieżka, w C pobierasz ją przez getenv("TEMP"), a w pliku bat %TEMP% (np. %TEMP%\temp1.bat)

0

Rozumiem. Tylko tutaj nazwałem to plikami tymczasowymi, ale one są plikami tworzonymi na potrzeby procesów, przy kończeniu działania programu, w momencie zwalniania pamięci, folder z tymi plikami jest usuwany. Po prostu potrzebuję sposób na przekazanie ścieżki tych plików do funkcji CreateProcess(). "Na sztywno": L:"temp\temp1.bat" działa, ale chciałbym to wyłuskiwać z tablicy stringów o nazwie pliki, za pomocą pliki[i]. Tak aby mógł to wpuścić później w pętlę.

PS.

Doszedłem do takiego czegoś

 
	std::wstring str2(pliki[1].length(), L' ');
	std::copy(pliki[1].begin(), pliki[1].end(), str2.begin());

	int wynik = CreateProcess(str2.c_str(), _T(""), 0, 0, 0, 0, 0, 0, &si, &pi2);

, ale GetLastError dostaję o numerze 1821 - "The specified image file did not contain a resource section.". Zgłupiałem.

Ps.2
Pomimo tego, że dostaję błąd o numerze 1812, procesy są wywoływane.

0
std::wstring str2(pliki[1].length(), L' ');
std::copy(pliki[1].begin(), pliki[1].end(), str2.begin());

nie za dużo kombinujesz?

std::wstring str2 = pliki[1];

GetLastError dostaję o numerze 1821 - "The specified image file did not contain a resource section.". Zgłupiałem.
GetLastError ma sens tylko gdy funkcja zwróciła błąd:

MSDN napisał(a)

If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

0

Napotkałem jeszcze problem, mianowicie na koniec programu sprawdzam czy wszystkie procesy zgłosiły koniec swojej pracy, tak abym mógł bezpiecznie zakończyć działanie całego programu. niestety, program się kończy, a procesy wiszą w systemie i pracują dalej.

dwSignal = WaitForMultipleObjects(iNumOfProcesses, hThreadArray, TRUE, INFINITE);
 
0

	PROCESS_INFORMATION* pi = new PROCESS_INFORMATION[iNumOfProcesses];
	PROCESS_INFORMATION pi2;

	HANDLE *hThreadArray = new HANDLE[iNumOfProcesses]; 

	STARTUPINFO si = {0};
	si.cb = sizeof(si);

	ZeroMemory(&si, sizeof(si));
	int *pi_index = new int[iNumOfProcesses];
	
	for(i=0; i<iNumOfProcesses; i++)
	{
		ZeroMemory(&pi[i], sizeof(pi[i]));
		hThreadArray[i] = 0;
	}

	j = 0;
	i = 0;
	int xd = 0;
	int g = 0;
	DWORD dwSignal;

	while(xd < iNumOfLines+1)
	{
		for(j=0; j<iNumOfProcesses; j++)
		{
			if(hThreadArray[j] == 0)
			{
				std::wstring str2(pliki[xd].length(), L' ');
				std::copy(pliki[xd].begin(), pliki[xd].end(), str2.begin());

				pi_index[j] = CreateProcess(str2.c_str(), _T(""), 0, 0, 0, 0, 0, 0, &si, &pi[j]);
				hThreadArray[j] = pi[j].hProcess;
				xd = xd + 1;
				Sleep(20);
			}
		}
		dwSignal = WaitForMultipleObjects(iNumOfProcesses, hThreadArray, false, INFINITE);

		for(g = 0; g< iNumOfProcesses; g++)
		{
			if(dwSignal == WAIT_OBJECT_0 + g)
			{
				CloseHandle(pi[g].hProcess);
				CloseHandle(pi[g].hThread);
				hThreadArray[g] = 0;
			}
		}
		i = i + 1;
	}


	cout << "\nZabijam wszystkie procesy... ";
	dwSignal = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, hThreadArray, true, INFINITE);
	cout << " Done!. "<< endl;
	DeleteDirectory(L"temp", true);

	delete[] pi;
	delete[] pi_index;
	delete[] pliki;
	delete[] hThreadArray;

	cout << "\n-------------\n";
	cout << "Koniec programu\n";

	//system("Pause");
	return 0;
}
 
0

Po stworzeniu procesu nie przerywasz pętli for(j=. Ogólnie jakoś dziko. Zobacz:

int iNumRunning = 0;

for(int iLine = 0; iLine < iNumOfLines; iLine++)
{
	int iProc = iNumRunning;
	if (iNumRunning >= iNumOfProcesses) {
		iProc = WaitForMultipleObjects(iNumRunning, hThreadArray, false, INFINITE) - WAIT_OBJECT_0;
		assert(iProc >= 0 && iProc < iNumOfProcesses);
		CloseHandle(pi[iProc].hProcess);
		CloseHandle(pi[iProc].hThread);			
	} else {
                iNumRunning++;
        }

	CreateProcessA(pliki[iLine].c_str(), "", 0, 0, 0, 0, 0, 0, &si, &pi[iProc]);
	hThreadArray[iProc] = pi[iProc].hProcess;
	Sleep(20);
}

WaitForMultipleObjects(iNumRunning, hThreadArray, true, INFINITE);
0

Mimo wszystko program kończy się wcześniej niż procesy.

0

Kod jest Twój, dokładnie kopiuj / wklej. Chciałem sprawdzić czy to faktycznie zadziała. Programem uruchamiam pliki batowe, gdzie w liniach zapisane jest jedno polecenie. Polecenia są pobierane i tworzone są pliki "tymczasowe", które zostają wywołane przez X procesów. No i właśnie o to chodzi, że każdy proces wykonuje jakiś tymczasowy plik bat, z jednym poleceniem, testuje to na pakowaniu instalatora LibreOffice, który jest w 9 folderach. Pakuję każdy folder na innym procesie. Wyskakuje mi w konsoli "koniec programu", a wiec została wykonana funkcja usuwająca folder temp. W konsoli w momencie kończenia się procesów pojawia się informacja, że "system nie mógł odnaleźć określonej ścieżki". Chodzi mi o to aby wywołanie:

 WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, hThreadArray, true, INFINITE); 

czekało na zakończenie się procesów, żeby móc zakończyć program.
Teoretycznie (wydaje mi się tak), że moje rozwiązanie powinno działać.

0

Mój kod to tylko snippet, nie skompiluje się. Pokaż to, co skompilowałeś.

0
PROCESS_INFORMATION* pi = new PROCESS_INFORMATION[iNumOfProcesses];
PROCESS_INFORMATION pi2;

HANDLE *hThreadArray = new HANDLE[iNumOfProcesses]; 

STARTUPINFO si = {0};
si.cb = sizeof(si);

ZeroMemory(&si, sizeof(si));
int *pi_index = new int[iNumOfProcesses];
	
for(i=0; i<iNumOfProcesses; i++)
{
	ZeroMemory(&pi[i], sizeof(pi[i]));
	hThreadArray[i] = 0;
	pi_index[i] = 0;
}

j = 0;
i = 0;
int xd = 0;
int g = 0;
DWORD dwSignal;

int iNumRunning = 0;
 
for(int iLine = 0; iLine < iNumOfLines; iLine++)
{
        int iProc = iNumRunning;
        if (iNumRunning >= iNumOfProcesses) {
                iProc = WaitForMultipleObjects(iNumRunning, hThreadArray, false, INFINITE) - WAIT_OBJECT_0;
                assert(iProc >= 0 && iProc < iNumOfProcesses);
                CloseHandle(pi[iProc].hProcess);
                CloseHandle(pi[iProc].hThread);                        
        } else {
                iNumRunning++;
        }
 
        CreateProcessA(pliki[iLine].c_str(), "", 0, 0, 0, 0, 0, 0, (LPSTARTUPINFOA)&si, &pi[iProc]);
        hThreadArray[iProc] = pi[iProc].hProcess;
        Sleep(20);
}
 
WaitForMultipleObjects(iNumRunning, hThreadArray, true, INFINITE);

cout << " Done!. "<< endl;
DeleteDirectory(L"temp", true);

delete[] pi;
delete[] pi_index;
delete[] pliki;
delete[] hThreadArray;

cout << "\n-------------\n";
cout << "Koniec programu\n";

system("Pause");
return 0;
 

Dokładnie to skompilowałem ;).

0

A na jakiej podstawie stwierdzasz, że proces wciąż działa? Powinieneś sprawdzić PID.

Spróbuj:

int iLine = 0;

while (iLine < iNumOfLines || iNumRunning > 0)
{
	int iProc = iNumRunning;
	if (iNumRunning >= iNumOfProcesses || iLine >= iNumOfLines) {
		iProc = WaitForMultipleObjects(iNumRunning, hThreadArray, false, INFINITE) - WAIT_OBJECT_0;
		assert(iProc >= 0 && iProc < iNumOfProcesses);
		CloseHandle(pi[iProc].hProcess);
		CloseHandle(pi[iProc].hThread);
		iNumRunning--;

		cout << "Process closed. PID=" << pi[iProc].dwProcessId << endl;
	}

	if (iLine < iNumOfLines) {
		CreateProcessA(pliki[iLine].c_str(), "", 0, 0, 0, 0, 0, 0, (LPSTARTUPINFOA)&si, &pi[iProc]);
		hThreadArray[iProc] = pi[iProc].hProcess;
		Sleep(20);

		iLine++;
		iNumRunning++;

		cout << "Process created. PID=" << pi[iProc].dwProcessId << endl;
	}
}
cout << " Done!. "<< endl;
DeleteDirectory(L"temp", true);
//...

I zobacz czy faktycznie te procesy wiszą.

1 użytkowników online, w tym zalogowanych: 0, gości: 1