Shell() vs. Process.Start()

    • Allgemein

    Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von jvbsl.

      Shell() vs. Process.Start()

      Hi.

      In diesem kleinen Artikel hebe ich die Unterschiede zwischen Shell() und Process.Start() hervor. Es wird viel diskutiert, welche Funktion denn nun besser, älter oder neuer ist. Allgemein bekannt ist, dass Shell() schon seit langer Zeit existiert und Process.Start() erst in den .NET-Versionen von VB dazugekommen ist. Dabei lohnt sich ein Blick auf die Interna der beiden Aufrufe. Die ausführliche Erklärung der Parameter findet sich im MSDN - Links am Ende des Posts.

      Shell:

      VB.NET-Quellcode

      1. 'Signatur:
      2. Public Shared Function Shell(ByVal PathName As String, ByVal Optional Style As AppWinStyle = 2, ByVal Optional Wait As Boolean = False, ByVal Optional Timeout As Integer = -1) As Integer
      3. 'Der relevante interne Aufruf, der das angegebene Programm letztentlich ausführt:
      4. NativeMethods.CreateProcess(Nothing, PathName, Nothing, Nothing, False, &H20, ptr, Nothing, lpStartupInfo, lpProcessInformation)


      Bei Process.Start kommt es darauf an, ob UseShellExecute des entsprechenden ProcessStartInfo-Objekts auf True oder False festgelegt ist:

      VB.NET-Quellcode

      1. 'interner Aufruf: UseShellExecute = True (Standardwert!)
      2. NativeMethods.ShellExecuteEx(Me._executeInfo)
      3. 'interner Aufruf: UseShellExecute = False
      4. NativeMethods.CreateProcess(Nothing, cmdLine, Nothing, Nothing, True, creationFlags, zero, workingDirectory, lpStartupInfo, lpProcessInformation)

      Alle Aufrufe führen auf das Windows API zurück, was nicht weiter verwunderlich ist. Doch wo liegt nun der Unterschied genau?

      ShellExecuteEx() wird von der DLL shell32.dll exportiert, während CreateProcess() von kernel32.dll exportiert wird. Das bedeutet, dass ShellExecuteEx() aus der Windows Shell stammt und CreateProcess einen Systemaufruf ("SysCall") darstellt. Ironischerweise verwendet der VB-Shell()-Aufruf gerade nicht die Windows Shell, um das übergebene Argument zu verarbeiten, Process.Start() in der Standardeinstellung UseShellExecute = True jedoch schon.
      Der Vorteil der Windows Shell ist, dass damit auch Verknüpfungen und Dokumente aufgelöst werden können, was mit CreateProcess() nicht, also weder mit Shell(), noch mit Process.Start mit UseShellExecute = False, funktioniert. CreateProcess() kann laut Dokumentation nur ausführbare Dateien laden, aber keine Referenzen (Dokumente, Verknüpfungen) auflösen. Hierzu ein paar Beispiele:

      "C:\document.doc" ist ein Microsoft Office-Dokument, "C:\myapp.exe" eine ausführbare Datei.

      VB.NET-Quellcode

      1. 'Dieser Aufruf funktioniert - das Dokument wird in Word geöffnet.
      2. Process.Start("C:\document.doc")
      3. 'Diese beiden ebenso - die Anwendung wird ausgeführt
      4. Process.Start("C:\myapp.exe") 'Die Shell kann auch ausführbare Dateien laden...
      5. Shell("C:\myapp.exe")
      6. 'Die folgenden Aufrufe funktionieren NICHT:
      7. Shell("C:\document.doc") ' <-- Exception
      8. Dim psi As New ProcessStartInfo
      9. psi.UseShellExecute = False
      10. psi.FileName = "C:\document.doc" ' ausführbare Dateien (!) funktionieren auch mit UseShellExecute = False
      11. Process.Start(psi) ' <-- Exception

      Der offensichtliche Nachteil von Shell() ist also, dass im Argument nur ausführbare Dateien stehen dürfen. Nicht ganz so offensichtlich ist, dass diese Methode jederzeit entfernt werden kann, denn sie wurde bereits in den Namespace Microsoft.VisualBasic ausgelagert, der nur Bestandteile aus älteren VB-Versionen <= 6 enthält. Es ist nicht gesichert, dass spätere Versionen des .NET Frameworks diese Rückwärtskompatibilität in diesem Ausmaß weiterführen.

      Es stellt sich heraus, dass Process.Start() die universellere Funktion ist, welche mit der Unterstützung der Windows Shell einen größeren Funktionsumfang und vor allem eine enorme Toleranz gegenüber Eingaben besitzt. Daher sollte ihr der Vorzug gegenüber dem Shell()-Aufruf gegeben werden.

      Zum Schluss noch die Links zur Dokumentation von...
      Microsoft.VisualBasic.Interaction.Shell
      System.Diagnostics.Process.Start(String)
      CreateProcess (WinAPI)
      ShellExecuteEx (WinAPI)

      Ich hoffe, der Unterschied zwischen beiden Aufrufen ist klar geworden. Fragen, Lob, Kritik & Kommentare sind natürlich erlaubt / erwünscht.

      Gruß
      hal2000
      Ich finde das mit dem Alt so eine Sache. Ich meine Shell() funktioniert auch, wenn auch mit Einschränkungen.
      Also von daher ist es dem Entwickler freigestellt, was er nun braucht, bzw. benutzen möchte.

      Aber gut, dass du das Thema einmal aufgegriffen hast.

      LG

      hal2000 schrieb:

      @EiPott: Danke. Welche Funktion "besser" ist, muss jeder für sich entscheiden. Shell() ist veraltet und auf ausführbare Dateien beschränkt, während Process.Start() dem Benutzer einiges an Arbeit abnimmt (z.B. das Suchen nach dem richtigen Programm für ein Dokument, eine Musikdatei, etc.).
      Das ist falsch. Öffne doch mal mit Shell ne txt 8|

      hal2000 schrieb:

      Daher sollte ihr der Vorzug gegenüber dem Shell()-Aufruf gegeben werden.

      Netter Artikel Hal: obwohl ja nun wirklich oft genug geschrieben wurde die Leute sollten sich doch endlich mal angewöhnen den uralten Shell-Aufruf zu lassen, lässt sich das wohl nicht so einfach ausrotten.

      Da Du Dich ja anscheinend tiefer damit auseinandergesetzt hast: kennst Du irgeneine Situation wo der Shell-Aufruf Möglichkeiten bietet die die Process Klasse zusammen mit der StartInfo Klasse nicht hat ?

      Natürlich ausser alten VB6 C&P Code direkt kopieren zu können ... :whistling:

      SenA schrieb:

      Das ist falsch. Öffne doch mal mit Shell ne txt 8|

      Ich habe für meine Nachforschungen ein Windows 7 x64 verwendet - dort kann Shell() keine Textdateien öffnen (siehe Screenshot). Process.Start() funktioniert dagegen wie erwartet (im Screenshot auskommentiert). Die Datei C:\test.txt existiert natürlich, die Fehlermeldung ist irreführend.


      Kangaroo schrieb:

      kennst Du irgeneine Situation wo der Shell-Aufruf Möglichkeiten bietet die die Process Klasse zusammen mit der StartInfo Klasse nicht hat ?

      Nein. Bei sehr vielen zu startenden Prozessen ist Shell() eventuell etwas schneller, da dort ohne große Umwege CreateProcess() aufgerufen wird. Vorher wird lediglich die Struktur StartupInfo mit den Daten des aufrufenden Prozesses befüllt. Process.Start() braucht da schon etwas länger, denn dort wird die volle Funktionalität von CreateProcess() ausgeschöpft (UseShellExecute = False). Mit der genannten Eigenschaft auf True wird das COM-Subsystem für den Aufruf verwendet, sodass alle Werte erst in den unverwalteten Speicher kopiert werden müssen, bevor sie an ShellExecuteEx übergeben werden können, was natürlich Zeit kostet.

      Anmerkung: Die Windows Shell basiert auf COM.
      Gruß
      hal2000
      evtl. würde ShelEx funktionieren^^
      ist jetzt aber ebenso wenig zu empfehlen...

      Anmerkung: Die Windows Shell basiert auf COM.

      das war glaub ich von Anfang an klar...in .Net sollte man immer Managed Code benutzen und nicht die unmanaged WinAPI, solange es nicht nötig ist,...Wenn man denn die WInAPI nutzt, sollte man diese Managed programmieren ;)
      Ich wollte auch mal ne total überflüssige Signatur:
      ---Leer---