Laden...

WPF-Anwendung langsam

Letzter Beitrag vor 3 Monaten 24 Posts 1.019 Views
WPF-Anwendung langsam

@Communtiy.

In meiner WPF-Anwendung werden die geöffneten Programme als Schaltflächen in einem Stackpanel abgelegt. Bei einem Klick auf die Schaltfläche soll das jeweilige Programmfenster wieder angezeigt und aktiviert werden. (Ähnlich wie wenn auf die Programmsymbol in der Taskleiste geklickt wird) Es funktioniert leider nur sehr mässig. Es dauert eine Weile bis das jeweilige Programmfenster wiederhergestellt wird. Der Benutzer hat keinen Zugriff weder auf dem Desktop noch auf die Taskleiste. Daher diese Lösung.

Für jede Art für Unterstützung wäre ich sehr dankbar.

 public partial class MainWindow : Window
 {
  
     [DllImport("user32.dll")]
     private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

     public MainWindow()
     {
         InitializeComponent();

         DispatcherTimer dt = new DispatcherTimer() {
             Interval = TimeSpan.FromMilliseconds(500)
         };
         dt.Tick += new EventHandler(dispatcherTimer_Tick);
         dt.Start();
     }
     private void dispatcherTimer_Tick(object sender, EventArgs e)
     {
         OpenProgramms.Children.Clear();

         foreach (Process p in Process.GetProcesses())
         {
             string processPath = String.Empty;
             try
             {
                 processPath = p.MainModule.FileName;
             }
             catch ()
             {
                 continue;
             }

             if (String.IsNullOrEmpty(p.MainWindowTitle))
                 continue;

             string iconPath = $"\\cache\\{p.MainWindowHandle}";
             if (!File.Exists(iconPath))
             {
                 Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(processPath);
                 icon.ToBitmap().Save(iconPath);
                 icon.Dispose();
             }

             Button button = new Button()
             {
                 Content = new System.Windows.Controls.Image
                 {
                     Stretch = Stretch.Fill,
                     Source = new BitmapImage(new Uri(iconPath))
                 },
                 Tag = p.MainWindowHandle,
                 ToolTip = p.ProcessName
             };
             button.Click += Button_Click;

             OpenPrograms.Children.Add(button);
         }
     }

     private void Button_Click(object sender, EventArgs e)
     {
         conse int SW_RESTORE = 9:

         IntPtr handle = (IntPtr)((Button)sender).Tag;
         bool retValue = ShowWindowAsync(handle, SW_RESTORE);
     }
 }

Du kannst mit dem Debugger schauen, was langsam ist. Wir müssten nun mit der Glaskugel raten.
[Artikel] Debugger: Wie verwende ich den von Visual Studio?

Ansonsten Feedback:

  • WPF ist so konzipiert, dass Du MVVM anwendest, was hier nicht der Fall ist
  • Der korrekte Start für einen Hintergrundtask ist eben ein Task, kein DispatcherTimer
  • Das Laden sollte asynchron sein, sonst hängt dein UI (dafür kann man auch den Window_Loaded Event abonnieren und via async/await arbeiten)
  • Man kann das alles mit Reactive Extensions sehr vereinfachen und stabiler umsetzen (über eine Subscription)

Zitat von Abt

Du kannst mit dem Debugger schauen, was langsam ist. Wir müssten nun mit der Glaskugel raten.
[Artikel] Debugger: Wie verwende ich den von Visual Studio?

Aus dem Artikel kann ich nicht entnehmen, wie ich ich mit dem Debugger feststellen kann, wie lange Programmteile für die Ausführung benötigt. Die Programmfenster werden erst sehr verzögert wiederhergestellt. (zwei bis drei Sekunden nach dem auf die jeweilige Schaltfläche geklickt wurde)

Danke für das Feedback. Da ergeben sich doch Fragen als C#-Neuling.

  • WPF ist so konzipiert, dass Du MVVM anwendest, was hier nicht der Fall ist

Ganz oberflächlich, weiß ich gerade mal, was die Abkürzung MVVM bedeutet. Wie sieht da konkret für mein Progrämmchen aus?

  • Der korrekte Start für einen Hintergrundtask ist eben ein Task, kein DispatcherTimer

Wie ermittelte ich dann regelmäßig die Prozesse? Wie es über ein Hintergrundtask funktionieren soll, verstehe ich nicht.

  • Das Laden sollte asynchron sein, sonst hängt dein UI (dafür kann man auch den Window_Loaded Event abonnieren und via async/await arbeiten)

Was soll hier asynchron abgearbeitet werden?

Danke dass Ihr Euch die Zeit für die Beantwortung nehmt.

Zitat von HermannKregel

Aus dem Artikel kann ich nicht entnehmen, wie ich ich mit dem Debugger feststellen kann, wie lange Programmteile für die Ausführung benötigt.

Für Deinen eher simplen Fall: einfach Zeile für Zeile durchsteppen (ist beschrieben) und schauen, welche Zeile langsam ausgeführt wird.

Die Programmfenster werden erst sehr verzögert wiederhergestellt. (zwei bis drei Sekunden nach dem auf die jeweilige Schaltfläche geklickt wurde)

Neben der Reaktion, dass irgendwas lange dauert, ist das ein Programmierfehler, weil Du Hintergrundaufgaben im UI-Thread ausführst.
Aber das löst sich automatisch, wenn man MVVM verwendet. Hintergrundwissen dazu:
[FAQ] Warum blockiert mein GUI?

Ganz oberflächlich, weiß ich gerade mal, was die Abkürzung MVVM bedeutet. Wie sieht da konkret für mein Progrämmchen aus?

[Artikel] MVVM und DataBinding

Ja, die Einstiegshürde zu WPF und MVVM is leider hoch. Aber WPF ist leider auch dafür konzipiert, was auch die größte Kritik an WPF ist.
Beachtest Du MVVM mit WPF nicht, wirst Du von Workaround zu Workaround stolpern (wie hier).

Wie ermittelte ich dann regelmäßig die Prozesse? Wie es über ein Hintergrundtask funktionieren soll, verstehe ich nicht.

Das Lesen der Prozesse muss eben ausgelagert werden; das kannst Du manuell programmieren oder eben Toolings dafür verwenden wie Reactive Extensions.
Das Wissen fehlt Dir hier, weil Du die Grundlagen zu WPF nicht durchgelesen hast 😃

Aber grober Ablauf:

  • Window wird geöffnet
  • Du abonnierst den Window Loaded Event
  • Du nimmst einen PeriodicTimer, der alle X Zeitabstände feuert
  • Du lädst Prozesse und aktualisierst das ViewModel

Das wäre eine potentielle, korrekte Umsetzung mit WPF ohne Reactive Extensions.


Es ist aber wichtig sich mit den Grundlagen von WPF zu beschäftigen, da hier Konzepte verwendet werden, die man durch Try-and-Error nicht lernen kann.
Die Docs haben dazu mehrere Lernpfade, zB Create a new app with Visual Studio tutorial - WPF .NET | Microsoft Learn

@Abt.

Vielen Dank für Deine Mühe und die weiterführenden Links.

Da kommt ja noch ein großer Brocken Arbeit auf mich zu ....

großer Brocken

Zwecks Erleichterung: der C#-Compiler erwartet 'nur' gewisse formelle Anforderungen an den Code. Schlagwörter wie "MVVM" oder "Reactive Extensions" beschreiben wie man häufige(re) Änderungen oder Erweiterungen (Darstellung von UI-Elementen, Live-Daten,...) i.Z. mit WPF implementieren sollte (i.S. von geschicktem Auslagern im Quellcode oder auf andere Threads).

Goalkicker.com // DNC Magazine for .NET Developers // .NET Blogs zum Folgen
Software is like cathedrals: first we build them, then we pray 😉

Natürlich muss man in WPF nicht alles mit MVVM machen. Vor allem für jedes kleine Tool was man baut finde ich das overkill (ist meine Meinung).

Ich hab mal ein bisschen was umgebaut.

Ich finde den Code nicht gut...

Warum das bild immer abspeichern? Warum die List eimmer aktualisieren, auch wenn sich z.B. nichts geändert hat, ...
Das geht sicher besser.

Ich würde eher eine ListBox mit einem Itemtemplate nehmen, als die Items im Code zu erzeugen, aber geht alles.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace WpfApp1
{
    class RunningApp
    {
        public string Name { get; set; }
        public string Path { get; set; }
        public string IconPath { get; set; }
        public IntPtr Handle { get; set; }
    }

    public partial class MainWindow : Window
    {
        private Timer timer;

        [DllImport("user32.dll")]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

        public MainWindow()
        {
            InitializeComponent();

            string iconPath = System.IO.Path.Combine(System.IO.Path.GetTempPath());
            if (!Directory.Exists(System.IO.Path.GetDirectoryName(iconPath)))
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(iconPath));

            this.timer = new System.Timers.Timer();
            this.timer.Interval = 500;
            this.timer.Elapsed += Timer_Elapsed;
            this.timer.AutoReset = false;
            this.timer.Start();
        }

        private void Timer_Elapsed(object? sender, ElapsedEventArgs e)
        {
            var lst = new List<RunningApp>();

            Parallel.ForEach(Process.GetProcesses(), p =>
            {
                try
                {
                    string processPath = String.Empty;
                    processPath = p.MainModule.FileName;
                    if (!string.IsNullOrEmpty(p.MainWindowTitle))
                    {
                        string iconPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "cache", p.MainWindowHandle.ToString());
                        if (!Directory.Exists(System.IO.Path.GetDirectoryName(iconPath)))
                            Directory.CreateDirectory(System.IO.Path.GetDirectoryName(iconPath));
                        if (!File.Exists(iconPath))
                        {
                            Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(processPath);
                            icon.ToBitmap().Save(iconPath);
                            icon.Dispose();
                        }
                        lst.Add(new RunningApp()
                        {
                            Name = p.MainWindowTitle,
                            Path = processPath,
                            IconPath = iconPath,
                            Handle = p.MainWindowHandle
                        });
                    }
                }
                catch (Exception)
                {
                }
            });

            Dispatcher.Invoke(() =>
            {
                OpenProgramms.Children.Clear();
                foreach (var app in lst.OrderBy(x => x.Name))
                {
                    Button button = new Button()
                    {
                        Content = new System.Windows.Controls.Image
                        {
                            Stretch = Stretch.Fill,
                            Source = new BitmapImage(new Uri(app.IconPath))
                        },
                        Tag = app.Handle,
                        ToolTip = app.Name
                    };
                    button.Click += Button_Click;
                    button.Width = 50;
                    button.Height = 50;

                    OpenProgramms.Children.Add(button);
                }
            });
            this.timer.Start();
        }

        private void Button_Click(object sender, EventArgs e)
        {
            const int SW_RESTORE = 9;

            IntPtr handle = (IntPtr)((Button)sender).Tag;
            bool retValue = ShowWindowAsync(handle, SW_RESTORE);
        }
    }
}

cSharp Projekte : https://github.com/jogibear9988

@jogibear9988

Vielen Dank für Deinen Code. Das hätte ich so (noch) nicht hinbekommen...

Ich finde den Code nicht gut...

Warum das bild immer abspeichern? Warum die List eimmer aktualisieren, auch wenn sich z.B. nichts geändert hat, ...
Das geht sicher besser.

Wie könnte ich den Code hier optmieren?

Zitat von jogibear9988

Natürlich muss man in WPF nicht alles mit MVVM machen. Vor allem für jedes kleine Tool was man baut finde ich das overkill (ist meine Meinung).

Meine Meinung dazu ist, dass du fast den gleichen Code schreibst allerdings logisch und sauber getrennt in einzelnen Klassen (ViewModel, Model, Service).

Was du anscheinend meinst, ist der Aufwand das Grundgerüst für WPF/MVVM zu erstellen, was man benötigt um überhaupt anfangen zu können. Nun ich habe eine eigene Projektvorlage dafür. Ein Klick und alles ist vorbereitet für WPF/MVVM/DI-Container.

Den Code dann in die unterschiedlichen Klassen zu verteilen dauert kaum länger als deine Variante.

Es hat also eher etwas mit der eigenen Organisation zu tun.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Der eigentliche Overkill ist, wenn man dauernd irgendwelche Workarounds wie Thread-Zugriff schreiben muss (meine Meinung).

Ja, sehe ich auch genau so.

Außerdem ist der Code bzgl. des Cachen der Icons anhand des MainWindowHandle nicht durchdacht, denn z.B. nach einem Neustart ändern sich diese ja und es würden die falschen Icons angezeigt.

Und noch ein Tip:

Directory.CreateDirectory testet intern schon, ob das Verzeichnis existiert (und macht dann nichts), daher braucht man nicht vorher noch auf Directory.Exists abfragen (außer man möchte noch anderen Code zusätzlich aufrufen). Und mehrfach GetDirectoryName aufzurufen verbraucht auch unnötigen Speicher und Performance.

Zitat von HermannKregel

@jogibear9988

Vielen Dank für Deinen Code. Das hätte ich so (noch) nicht hinbekommen...

Ich finde den Code nicht gut...

Warum das bild immer abspeichern? Warum die List eimmer aktualisieren, auch wenn sich z.B. nichts geändert hat, ...
Das geht sicher besser.

Wie könnte ich den Code hier optmieren?

Ich würde z.B.:

  • Die Bilder nicht ins Dateisystem legen sondern nur im Speicher halten. bspw in der Runnings App Klasse Statt dem Pfad, Direkt das Image bzw die Bildaten halten.
  • Die Running apps liste nur mit den neuen Prozessen füllen und die alten entfernen und nicht immer neu bauen
  • Die UI nur so aktualiseren das neue Prozesse dazukommen und alte entfernt werden, nicht immer alles neu Zeichnen
  • Die Liste in XAML mit ItemsTemplate und ItemsSource füllen und nicht die Objekte im Code anlegen

cSharp Projekte : https://github.com/jogibear9988

Zitat von Th69

Außerdem ist der Code bzgl. des Cachen der Icons anhand des MainWindowHandle nicht durchdacht, denn z.B. nach einem Neustart ändern sich diese ja und es würden die falschen Icons angezeigt.

Das hatte ich bedacht, dass sich der MainWindowHandle ändert und den Cache-Ordner immer geleert.

Zitat von jogibear9988

  • Die Bilder nicht ins Dateisystem legen sondern nur im Speicher halten. bspw in der Runnings App Klasse Statt dem Pfad, Direkt das Image bzw die Bildaten halten.

Mein Code, um die Icons im Speicher zu halten, sieht jetzt wie folgt aus:

private void TaskbarTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    List<RunningProgram> runningPrograms = new List<RunningProgram>();

    Parallel.ForEach(Process.GetProcesses(), p =>
    {
        try
        {
            if (!String.IsNullOrEmpty(p.MainWindowTitle))
            {

                string processPath = p.MainModule.FileName;

                MemoryStream memoryStream = new MemoryStream();
                Icon icon = System.Drawing.Icon.ExtractAssociatedIcon(processPath);
                icon.ToBitmap().Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
                PngBitmapDecoder pngBitmapDecoder = new PngBitmapDecoder(memoryStream, 
                                                        BitmapCreateOptions.None, 
                                                        BitmapCacheOption.Default);
                runningPrograms.Add(new RunningProgram()
                {
                    Name = p.MainWindowTitle,
                    Path = processPath,
                    BitmapSource = pngBitmapDecoder.Frames[0],
                    Handle = p.MainWindowHandle
                }
                );
            }
        }
        catch
        {
        }
    }
    );

    Dispatcher.Invoke(() =>
    {
        Taskbar.Children.Clear();

        foreach (RunningProgram runningProgram in runningPrograms.OrderBy(x => x.Name))
        {
            Button button = new Button()
            {
                Content = new System.Windows.Controls.Image
                {
                    Stretch = Stretch.Fill,
                    Source = runningProgram.BitmapSource
                },
                Tag = runningProgram.Handle,
                ToolTip = runningProgram.Name
            };
            button.Click += Button_Click;
            // button.Width = 50;
            // button.Height = 50;

            Taskbar.Children.Add(button);
        }
    }
    );

    taskbarTimer.Start();
}

Zitat von jogibear9988

  • Die Running apps liste nur mit den neuen Prozessen füllen und die alten entfernen und nicht immer neu bauen

Da fehlt mir gerade die Idee, wie ich das umsetzen soll. Muss ich dann die generische ListerunningPrograms global defineren? Findet die Überprüfung der laufenden Prozesse auch im Elapsed-Event statt?

Zitat von jogibear9988

  • Die UI nur so aktualiseren das neue Prozesse dazukommen und alte entfernt werden, nicht immer alles neu Zeichnen
  • Die Liste in XAML mit ItemsTemplate und ItemsSource füllen und nicht die Objekte im Code anlegen

Das ist glaube ich, ist noch zu hoch für mich.

Vielen Dank für Eure Hilfe.

Dein Parallel.ForEach hat hier (vermutlich) kein echten Vorteil; im Gegenteil: durch Parallel.ForEach hast Du einen potentiellen Multi-Thread-Zugriff - schreibend - auf eine Liste, die nicht Multi-Thread-fähig ist.
Du musst hier mit lock() arbeiten, damit das alles Thread-sicher ist und nicht knallt.

Alternativ ginge auch ConcurrentBag als Threadsichere Alternative zu List + Lock!

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

Alternativ geht auch ParallelEnumerable.AsParallel das würde ohne lock oder anderes Concurrent-Gedöns funktionieren.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Braucht man alles nicht. Das erste was man braucht, ist eigentlich die WMI. Über GetProccess bekommt man immer nur die gleichen Prozesse der Architektur, also x86 oder x64. Über WMI bekommt man alles.

Dann baut man sich einfach nen Provider, statt in der UI Logik zu platzieren ([Artikel] Drei-Schichten-Architektur)
Darin hat man nen ConcurrentDictionary mit den Prozessen, die man zwischenspeichern will (Cleanup nicht vergessen, wenn gewünscht, sieh Kommentar).

Und dann ruft man, wann immer man will, Load auf und es werden nur noch neue Prozesse geladen; Key ist der Pfad. Der Rest sind Hilfsklassen, sodass man auch noch in 2 Wochen weiß, was man da programmiert hat.
Alles keine Hexerei.

Hier nen Sample mit ner Consolen App.

// See https://aka.ms/new-console-template for more information
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Management;

Console.WriteLine("Hello, World!");

RunningProcessProvider provider = new();


await provider.Load();

foreach (var entry in provider.RunningApps)
{
    var info = entry.Value;
    Console.WriteLine("Process: " + info.Title);
}

public class RunningProcessProvider
{
    public ConcurrentDictionary<string, ApplicationInfo> RunningApps = new();

    public Task Load() => Task.Run(InternalLoad);
    private void InternalLoad()
    {
        IEnumerable<AppProcessInfo> processes = GetRunningProcessesFromWmi();

        ParallelOptions parallelOptions = new () { MaxDegreeOfParallelism = Environment.ProcessorCount };
        ParallelQuery<AppProcessInfo> query = processes.AsParallel();

        foreach (AppProcessInfo processInfo in query)
        {
            // skip if process info known
            if (RunningApps.ContainsKey(processInfo.Path))
            {
                continue;
            }

            ApplicationInfo appIn = GetInfo(processInfo);
            RunningApps.TryAdd(processInfo.Path, appIn);
        }

        // Hier wenn gewünscht noch ein Cleanup, ansonsten
        // verbleiben in RunningApps auch Prozesse, die schon beendet wurden, was durch das Handle
        // dann zu ungültigen Leichen führt
    }

    private static IEnumerable<AppProcessInfo> GetRunningProcessesFromWmi()
    {
        var wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
        using ManagementObjectSearcher searcher = new(wmiQueryString);
        using ManagementObjectCollection results = searcher.Get();
        var query = from p in Process.GetProcesses()
                    join mo in results.Cast<ManagementObject>()
                on p.Id equals (int)(uint)mo["ProcessId"]
                    select new
                    {
                        Process = p,
                        Path = (string)mo["ExecutablePath"],
                    };
        foreach (var item in query)
        {
            if (item.Path is null)
            {
                continue;
            }

            yield return new AppProcessInfo(item.Process, item.Path);
        }
    }

    private ApplicationInfo GetInfo(AppProcessInfo processInfo)
    {
        string processPath = processInfo.Path;
        Process process = processInfo.Proc;

        Icon? icon = Icon.ExtractAssociatedIcon(processPath);
        if (icon is not null)
        {
            MemoryStream memoryStream = new();
            icon.ToBitmap().Save(memoryStream, ImageFormat.Png);
        }

        return new ApplicationInfo(
            process.MainWindowTitle, icon, processPath, process.MainWindowHandle);
    }
}

public record class ApplicationInfo(
    string Title, Icon? Icon, string Path, IntPtr WindowHandle);

public record class AppProcessInfo(Process Proc, string Path);

Den Pfad als Key zu verwenden würde ich nicht empfehlen, denn bei mehreren Instanzen wird es zum Zufallsgenerator welche Instanz angezeigt wird. Die ProcessId wäre hier eindeutig.

Den Einwand mit GetProcess kann ich irgendwie nicht nachvollziehen, denn diese Methode ist mir unbekannt.

Wenn damit Process.GetProcesses() gemeint ist, ok, aber wenn das suboptimal ist, dann verstehe ich das hier nicht

var query = from p in Process.GetProcesses()

Kann aber natürlich sein, dass ich das alles nicht verstanden habe.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Verstehe ich jetzt auch nicht. Warum jetzt noch WMI wenn die gleiche API zum auslesen der Prozesse genutzt wird?

cSharp Projekte : https://github.com/jogibear9988

.. einfach mal ausprobieren? Hat mich jetzt 20 Sekunden gekostet.

Processes: 287
WMI Processes: 312

Console.WriteLine("Processes: " + Process.GetProcesses().Count());
Console.WriteLine("WMI Processes: " + RunningProcessProvider.GetWmiProcesses().Count);
    public static List<ManagementObject> GetWmiProcesses()
    {
        string wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
        using ManagementObjectSearcher searcher = new(wmiQueryString);
        using ManagementObjectCollection results = searcher.Get();

        return results.Cast<ManagementObject>().ToList();
    }

@jogibear9988

Es kommt eben nicht nur darauf an, was man ausführt, sondern auch wer

using System.Diagnostics;
using System.Management;

Console.WriteLine( "Processes: " + Process.GetProcesses().Count() );
Console.WriteLine( "WMI Processes: " + GetWmiProcesses().Count );
Console.WriteLine( "RunningProcesses:" + GetRunningProcessesFromWmi().Count );

static List<ManagementObject> GetWmiProcesses()
{
    string wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
    using ManagementObjectSearcher searcher = new( wmiQueryString );
    using ManagementObjectCollection results = searcher.Get();

    return results.Cast<ManagementObject>().ToList();
}

static List<(Process Process, string Path)> GetRunningProcessesFromWmi()
{
    var wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
    using ManagementObjectSearcher searcher = new( wmiQueryString );
    using ManagementObjectCollection results = searcher.Get();
    var query =
                from p in Process.GetProcesses() // Hier ist das große Mysterium, denn aus diesem weniger wird dann wieder mehr
                join mo in results.Cast<ManagementObject>()
            on p.Id equals (int)(uint)mo["ProcessId"]
                select (p, (string)mo["ExecutablePath"]);

    return query.ToList();
}

Mir ist es auch ein Rätsel, wenn Process.GetProcesses() 287 Items liefert und Abfrage der WMI 312 und dann ein JOIN aus beiden Abfragen (so wie bei GetRunningProcessesFromWmi()) es auch 312 sein müssten, denn wofür sollte man das sonst so machen.

Aber ich stecke auch nicht so tief drin in der Materie, ich mache da bestimmt etwas falsch, oder habe hier die falschen Einstellungen oder einfach nur die Brille nicht geputzt. Das ist bei mir alles mehr als möglich.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Mir ist es auch ein Rätsel, wenn Process.GetProcesses() 287 Items liefert und Abfrage der WMI 312 und dann ein JOIN aus beiden Abfragen (so wie bei GetRunningProcessesFromWmi()) es auch 312 sein müssten, denn wofür sollte man das sonst so machen.

Der Kern meines Beitrags war/ist, dass Du a) unterschiedliche Resultate bekommst und b) wie man so ein "Caching" einfacher umsetzen und c) WMI "rohe Resultate" liefert.

Ob man nun ein Join ausführt oder nicht: nicht Kern des Beitrags.
Lass es halt weg, wenns Dir nich gefällt/Deine Logik es nicht braucht - machs rein, wenn Deine Logik es wünscht.

Ändert aber am Kern des Beitrags nichts. Ob man da nun mit Typo-Orgien drauf antworten muss, bewert ich mal nicht. Hilft beim Thema nämlich nicht.

Zitat von Abt

Das erste was man braucht, ist eigentlich die WMI. Über GetProccess bekommt man immer nur die gleichen Prozesse der Architektur, also x86 oder x64. Über WMI bekommt man alles.

Das war die Einleitung zum Beitrag.

Um direkt in dem Code die Ergebnisse von Process.GetProcesses() (wir erinnern uns: liefert zu wenig) und der WMI (wir erinnern uns: liefert alles) mit einem join zu verknüpfen und damit das was die WMI mehr liefert gleich wieder in den Ausguss zu spülen.

Das macht einfach keinen Sinn weil man so nicht mehr bekommt.

Auf diese Problematik wollte ich den unbedarften Copy-Paste-Nutzer der Zukunft hinweisen, nicht das der dann da mit seinem kurzen Hemd im Regen herumsteht und die Welt nicht mehr begreift, denn das hilft schon mal gar nicht.

Hat die Blume einen Knick, war der Schmetterling zu dick.

Zitat von Abt

.. einfach mal ausprobieren? Hat mich jetzt 20 Sekunden gekostet.

Processes: 287
WMI Processes: 312

Ich hatte den Code nicht verstanden:


var query = from p in Process.GetProcesses()
                    join mo in results.Cast<ManagementObject>()
                on p.Id equals (int)(uint)mo["ProcessId"]
                    select new
                    {
                        Process = p,
                        Path = (string)mo["ExecutablePath"],
                    };

Hier nutzt du ja auch Process.GetProcesses(), also kommt ja das gleiche raus...

cSharp Projekte : https://github.com/jogibear9988