WIN32 system()-Äquivalent ohne Blockierung

badday

Erfahrenes Mitglied
Moin zusammen,

ich suche nach einer Möglichkeit, ein anderes Programm aus dem eigentlichen Programm heraus aufzurufen, das dann auch die Beendigung des eigentlichen Programms wartet. Also in etwa so:
p1 ruft p2 auf. p2 wartet auf Beenden von p1. p1 beendet sich. p2 ersetzt das binary von p1. p2 ruft p1(neu) auf.
Ich habe das unter GNU/Linux problemlos mit system() gemacht, allerdings habe ich hier unter Windows das Problem, dass ich beim löschen des alten binarys errno13 bekomme, also Zugriff verweigert. Ich gehe davon aus (ich weiß es nicht), dass system() offenbar das beenden des Programmes verhindert, da es blockiert(?).
Code:
int i=1; 
	for(;i!=0;)
	{
		i = remove(updatepath.c_str());
		PortableSleep(30);
	}
Diese Schleife ist momentan wohl endlos, jedenfalls ist sie selbst nach mehreren Sekunden noch nicht beendet. PortableSleep() ruft für Windows lediglich Sleep() auf.

Daher meine Frage: Wie kann ich das lösen? Gibt es in der Win32-API "sowas" wie system(), das nicht blockiert bzw. blockiert dies überhaupt? Könnte ich dann dem anderen Programm die ProzessID übergeben und dann warten, ob das Programm schon beendet ist? Meine Kenntnisse bzgl. der Win32-API sind nicht gerade ausgeprägt ;) (~0).

Vielen Dank.


Gruß,

badday
 
Hallo!

Du könntest einen neuen Prozess mit CreateProcess() starten.
Der neue Prozess könnte auf zwei Wegen auf die Beendigung der alten Anwendung warten. 1) Über FindWindow an ein WindowHandle kommen oder 2)Innerhalb der alten Anwendung die PID bestimmen mittels GetWindowThreadProcessId() und der neuen übergeben.

folgender Code ist allerdings nicht getestet:

C++:
string newProc = "cmd.exe";
STARTUPINFO si; 
    PROCESS_INFORMATION pi; 

    ZeroMemory( &si, sizeof(si) ); 
    si.cb = sizeof(si); 
    ZeroMemory( &pi, sizeof(pi) ); 

    // Start the child process.  
    if( !CreateProcess( NULL, // No module name (use command line).  
        newProc.c_str(), // Command line.  
        NULL,             // Process handle not inheritable.  
        NULL,             // Thread handle not inheritable.  
        FALSE,            // Set handle inheritance to FALSE.  
        0,                // No creation flags.  
        NULL,             // Use parent's environment block.  
        NULL,             // Use parent's starting directory.  
        &si,              // Pointer to STARTUPINFO structure. 
        &pi )             // Pointer to PROCESS_INFORMATION structure. 
    )  
    { 
        cout <<  "CreateProcess fehlgeschlagen."; 
        PostQuitMessage(0);
    } 
//Programm beenden
PostQuitMessage(0);

In dem neuen Thread kannst du nun wie folgt vorgehen:
C++:
while(NULL !=  FindWindow (NULL,"PARENTWINDOWNAME")) Sleep(5); //er schläft solange, bis Parentwindow nicht mehr vorhanden ist (ggf. Verwechslungsgefahr durch FindWindow!)
//binary operation
//CreateProcess()
PostQuitMessage(0);
mfg kickerxy
 
Moin,

erstmal danke für die ausführliche Antwort. Wie du schon schreibst, besteht bei FindWindow() eine Verwechslungsgefahr, das ist ein Problem, da der Name der Anwendung sehr geläufig ist: Updater.exe. Daher muss ich wohl auf GetWindowThreadProcessId() zurückgreifen. Allerdings weiß ich nicht genau, wie ich hier ein Handle zum Windows erstelle, was es offenbar erwartet.
Zudem beschwert sich der Compiler über &pi als Parameter, da er "const char *" nicht zu "LPWSTR" konvertieren könne. Sollte ich hier einen reinterpret_cast machen?

Gruß,

badday
 
Ja, ein cast wäre nicht schlecht, mein Fehler ;)

also wie folgt:
C++:
    if( !CreateProcess( NULL, // No module name (use command line).  
        const_cast<char*>(newProc.c_str()), // Command line.  
        NULL,             // Process handle not inheritable.  
        NULL,             // Thread handle not inheritable.  
        FALSE,            // Set handle inheritance to FALSE.  
        0,                // No creation flags.  
        NULL,             // Use parent's environment block.  
        NULL,             // Use parent's starting directory.  
        &si,              // Pointer to STARTUPINFO structure. 
        (&pi) )             // Pointer to PROCESS_INFORMATION structure. 
    )  //const_cast<char*>
    { 
        cout << "CreateProcess fehlgeschlagen."; 
        PostQuitMessage(0);
    }

Zu dem zweitem Problem:
Du musst dann in der 1. Anwendung folgenden Code aufrufen:
C++:
DWORD processID;
GetWindowThreadProcessId (g_hwnd, &processID); //g_hwnd stellt das Handle des aktuellen Fensters da; in processID ist also dessen PID
Allerdings musst du nun natürlich die PID noch übergeben. Mit CreateProcess kann man auch Parameter übergeben, etwa so:
C++:
string newProc = "C:\\WINDOWS\\explorer.exe";
STARTUPINFO si; 
    PROCESS_INFORMATION pi; 
 
    ZeroMemory( &si, sizeof(si) ); 
    si.cb = sizeof(si); 
    ZeroMemory( &pi, sizeof(pi) ); 

  
    if( !CreateProcess(  //
        const_cast<char*>(newProc.c_str()),  
        const_cast<char*>(intToString((int)processID).c_str()),
        NULL,             // Process handle not inheritable.  
        NULL,             // Thread handle not inheritable.  
        FALSE,            // Set handle inheritance to FALSE.  
        0,                // No creation flags.  
        NULL,             // Use parent's environment block.  
        NULL,             // Use parent's starting directory.  
        &si,              // Pointer to STARTUPINFO structure. 
        (&pi) )             // Pointer to PROCESS_INFORMATION structure. 
    ) 
    { 
        ccout("CreateProcess fehlgeschlagen."); 
        PostQuitMessage(0);
    }
Beachte hierbei: Der Pfad der Anwendung ist nun im ersten Parameter! Der Vorteil: im zweiten können (wie hier) Parameter angegeben werden. Der Nachteil: Im ersten Parameter muss der absolute Pfad angegeben werden!

falls du ein intToString benötigst:
C++:
#include <sstream>
string intToString (int zahl)
{   
 string ergebnis = "";
 stringstream ss;
 ss << zahl;
 ss >> ergebnis;
return ergebnis;
}

mfg kickerxy

(ich schreib gleich weiter, wie du die PID im neuen Programm verarbeiten kannst, gibt grade essen ;))
 
(Dann guten Appetit!)
Ich habe einfach g_hwnd als HWND deklariert, muss ich dem noch irgendetwas zuweisen?
Zum anderen bleibt das Problem mit &pi erhalten: error C2664: 'CreateProcessW' : cannot convert parameter 1 from 'char *' to 'LPCWSTR'

Vielen Dank soweit.


Gruß,

badday
 
badday hat gesagt.:
(Dann guten Appetit!)
Danke ;)

Dann hast du anscheinend deinen Compiler auf UNICODE eingestellt. Entweder du stellst das in den Projektoptionen um oder aber du nimmst CreateProcessA(gleiche Parameter); oder aber du musst nach WSTR casten. Ganz sicher bin ich mir damit jetzt auch nicht; ich würde dir allerdings empfehlen Unicode abzustellen, solange dein Projekt nicht auf den asiatischen Markt soll.

Anscheinend programmierst du mit der Konsole und nicht mit Windows-Fenstern. Daher kannst du so kein g_hwnd erhalten (oder nur über Umwege). Stattdessen nimm einfach diesen Code
C++:
 int processID = (int)GetCurrentProcessId();


Wenn du es soweit erstmal schaffst, dann musst du ja noch irgendwie den übergebenen Parameter in dem neuen Prozess auslesen.
Daher:
C++:
int main(int argc, char *argv[])
{
    cout << argv[0];
    system("PAUSE");
    return EXIT_SUCCESS;
}

Resultat: Die erste Anwendung liest seinen eigene PID aus, startet den Prozess und übergibt dieser seine PID. In argv[0] steht dann die PID.

#edit: Hätte ich fast vergessen. Hiermit kannst du den alten Prozess auch ganz sicher beenden:
C++:
TerminateProcess(stringToInt(argv[0]),0);
 
Zuletzt bearbeitet:
So, nun klappt es, ich hatte dummerweise noch keinen cast vor dem Parameter-String.
Zum neuen Prozess: Kann ich hier FindWindow() verwenden oder muss ich auch hier etwas "ohne Window" und stattdessen mit Process nehmen?

Vielen Dank.

Gruß,

badday

#edit:: Ich würde gerne auf das beenden warten, daher würde ich gern sehen, wann der Prozess beendet ist.
 
Zuletzt bearbeitet:
du kannst natürlich FindWindow benutzen (wenn keine Verwechslungsgefahr gegeben ist). Hatten wir diese Möglichkeit nicht eben ausgeschlossen? Denn dann brauchst du ja gar keine PID übergeben, sondern startest den Prozess einfach und rufst in dem neuen Prozess folgenden Code auf:
C++:
while(NULL !=  FindWindow (NULL,"PARENTWINDOWNAME")) Sleep(5);

Oder habe ich dich jetzt falsch verstanden:confused:

Ansonsten eine Lösung mittels der PID:

C++:
    DWORD dd = stringToInt(argv[0]);
HANDLE hSnapShot = NULL; 
  

   PROCESSENTRY32 pEntry; 
   pEntry.dwSize =sizeof(pEntry); 

   //Buffer for Process Info 
   char szProcessInfo[255]; 

//Iterate thru all processes 

bool dead;

do
{
     dead = false;
     hSnapShot=CreateToolhelp32Snapshot (TH32CS_SNAPALL,NULL); 
     Process32First(hSnapShot,&pEntry); 
    do 
    { 
        if(pEntry.th32ProcessID  == dd) 
        {
            //Prozess lebt noch
            dead = true;
            printf("ID %d  ",pEntry.th32ProcessID ); 
    printf("ExeFile %s  ",pEntry.szExeFile); 
    printf("Threads %d  ", pEntry.cntThreads ); 
    printf("ParentID %d  ",pEntry.th32ParentProcessID); 
    printf("Prio  %d\n",pEntry.pcPriClassBase ); 
            break;
        }
    }while ( Process32Next(hSnapShot,&pEntry) );
    Sleep(10);
}while(dead == true);

//hier angekommen wurde das Programm beendet
 
Zuletzt bearbeitet:
Ja, das hast du, lag aber wohl an mit ;)
Ich suche schlicht nach einer Funktion wie FindWindow(), die aber eben stattdessen PID verwendet.

Gruß,

badday
 
sorry, mein edit kam erst nach deiner antwort;)

Ich kenne nur die Möglichkeit; mag sein, dass es auch einfacher geht. Aber so geht es auf jeden Fall (habs grade getestet)
 
Zurück