Montag, 10. Mai 2010

ProzessIDs lesen und beenden / Reading and terminating process IDs

Wenn wir, aus welchem Grund auch immer, sicherstellen wollen, das eine bestimmte Anwendung nur einmal gestartet sein darf, dann kommt üblicherweise ein Singleton-Pattern zum Einsatz. Bei der Arbeit mit Objekten ist dies für viele sicherlich eine immer wiederkehrende Routine und die Funktion PEMSTATUS() wird in diesem Fall wohl auch oft genug zum Einsatz kommen.

Wollen wir jedoch eine komplette Applikation aus dem Arbeitsspeicher entfernen oder einfach nur überprüfen, ob eine Anwendung bereits aktiv ist, dann können wir diese Prüfung nicht mit PEMSTATUS() durchführen.

Bereits im April und Dezember habe ich in meinen Postings zur Druckerstatus Abfrage die Windows Management Instrumentation eingesetzt. Jetzt kommt sie erneut zum Einsatz, um uns Prozessinformationen zu liefern.

Die unten stehende Demofunktion 'TerminateProcess()' dient dem gezielten Entsorgen von unerwünschten Prozessen. Im Bereich der Singleton Patterns ist dies nicht immer die gewünschte Vorgehensweise. Aus diesem Grund verfügt die Funktion auch über Parameter, mit denen gezielte Abfragen und Ansichten ohne Prozesslöschung durchgeführt werden können.

Wird bspw. als einziger Parameter der Programmname übergeben erfolgt eine automatische Löschung sämtlicher gefunden Prozesse dieser Applikation. Die beiden Funktionstests zeigen mögliche unterschiedliche Parametrisierungen auf.

* // Funktionstest 1
* // - Anzahl vorhandener Prozesse zurückmelden
RUN /n notepad.exe
RUN /n notepad.exe
RUN /n notepad.exe

liAnzahl = TerminateProcess( [notepad.exe] , .T. )

CLEAR 
? [gefundene Prozesse: ]
?? liAnzahl

* // Funktionstest 2
* // - alle gefundenen Prozesse entsorgen
* // Alternativer Aufruf um gefundene Prozesse anzuzeigen
* liAnzahl = TerminateProcess( [notepad.exe] , .F. , .F. , .T. )
liAnzahl = TerminateProcess( [notepad.exe] )
 
? [beendete Prozesse:  ]
?? liAnzahl

FUNCTION TerminateProcess as Integer 
LPARAMETERS vAppname as String, vJustCheck as Boolean, vAllButLast as Boolean, vBrowseLast as Boolean
    * // Funktion zum löschen/melden von Prozessen übergebener Programmnamen
    * //
    * // Parameter    Variable    Status
    * // #1           vAppname    optional (default = Aktuelles Programm)
    * //                          Name der Programmdatei
    * // #2           vJustCheck  optional (default = .F.)
    * //                          Nicht löschen, nur Melden
    * // #3           vAllButLast optional (default = .F.)
    * //                          Letzen Prozess nicht löschen
    * // #4           vBrowseLast optional (default = .F.)
    * //                          Gefundene Prozesse anzeigen
    vAppname    = EVL( vAppname , PROGRAM() )
    vAllButLast = EVL( vAllButLast , .F. )
    vBrowseLast = EVL( vBrowseLast , .F. )

    * // Deklaration und Belegung benötigter Arbeitsvariablen. Hierbei
    * // erfolgt bei den zwei Objekt-Variablen eine direkte Referenzierung
    * // auf das WMI-Objekt sowie das Abfrageergebnis
    LOCAL   liReturn as Integer, liCount as Integer , llExit as Boolean, ;
            lcLogname as String , lcComputer as String, ;
            loCIMV2 as Object, loProcCols as Object, lcOwner as String
    liReturn    = 0
    liCount     = 0
    llExit      = .F.
    lcLogname   = ALLTRIM( GETWORDNUM( SYS( 0 ) , 2 , [#] ) )
    lcComputer  = [.]

    TRY 
        loCIMV2    = GETOBJECT( [winmgmts:{impersonationLevel=impersonate}!\\] + lcComputer + [\root\cimv2] )
        loProcCols = loCIMV2.ExecQuery( [select * from Win32_Process where name='] + vAppname + ['] )
    CATCH 
        llExit     = .T.
    ENDTRY 
    
    IF !llExit
        * // Arbeitscursor erstellen und die WMI Objekte verarbeiten
        CREATE CURSOR crsTasks ( ProgOwner c( 30 ) , ProgName c( 30 ) , ProgPath c( 200 ) )
        FOR EACH objProcess in loProcCols
            liCount     = liCount + 1 
            lcOwner     = SPACE( 256 )
            = objProcess.GetOwner( @lcOwner )
            m.ProgOwner = lcOwner
            m.ProgName  = objProcess.Name
            m.ProgPath  = EVL( objProcess.ExecutablePath , [-] )
            INSERT INTO crsTasks FROM MEMVAR 
        ENDFOR 

        * // Ggf. die gefundenen Prozesse angezeigen (Parameter #4)
        IF vBrowseLast
            BROWSE LAST 
        ENDIF 

        IF !vJustCheck
            * // Die gefundenen Prozesse der Reihe nach entsorgen
            FOR EACH objProcess in loProcCols
                liReturn = liReturn + 1 
                * // Wenn der letzte Prozess beibehalten werden soll (Parm.#3)
                * // dann raus aus der Schleife, andernfalls geht's weiter bis
                * // zum bitteren Ende... ;-)
                IF vAllButLast AND liReturn = liCount 
                    EXIT 
                ELSE 
                    SELECT crsTasks
                    GO ( liReturn )
                    IF ALLTRIM( crsTasks.ProgOwner ) == lcLogname
                        objProcess.Terminate( 0 )
                    ENDIF 
                ENDIF 
            ENDFOR 
        ELSE 
            liReturn = liCount
        ENDIF 
        
        * // Arbeitscursor entsorgen, WMI-Objektreferenzen auflösen
        USE IN SELECT( [crsTasks] )
        loCIMV2    = .NULL.
        loProcCols = .NULL.
        
    ENDIF 
    
    * // Anzahl der gelöschten/gefundenen Prozesse zurückgeben
    RETURN liReturn 
    
ENDFUNC 

Weitere Aufrufmöglichkeiten:

Löscht alle Prozesse des übergebenen Programms, der letzte Prozess bleibt jedoch bestehen

liAnzahl = TerminateProcess( [notepad.exe] , .F. , .T. )

Löscht alle Prozesse des übergebenen Programms, zeigt jedocht zuvor eine Liste der gefundenen Prozesse an

liAnzahl = TerminateProcess( [notepad.exe] , .F. , .F. , .T. )

Keine Kommentare:

Kommentar veröffentlichen