C# Oczekiwanie na Console.ReadKey() podczas pętli z async/await.

0

Hej. napotkałem mały problem. Chciałem zrobić tryb cichy w rozwiązaniu z poprzedniego wątku, z możliwością wyświetlenia statusu, albo przełączenia trybów cichy/informacyjny.
Nie mogę uporać się z tym problemem. Zrobiłem coś takiego (wybrałem tylko istotnie kawałki kodu). Pętla w PutRandomPassTo7z startuje, ale MonitorKeypress nie przechwytuje klawisza. Co jest nie tak??? MonitorKeypress zaczerpnąłem z jakiegoś forum.

using System;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Threading.Tasks;

namespace RandomPassword
{

    class Program
    {
        public static int TaskCount = 6;
        public static int Processes7z = 0;
        public static int DisplayMode = 0;
        
        
        static void Main(string[] args)
        {
            Task.Run(() => PutRandomPassTo7z(MyTaskCount)).ConfigureAwait(false);
			Console.ReadLine();          
        }

        private static async void PutRandomPassTo7z(int NewTaskCount)
        {
            Console.WriteLine("Cracking started...");          
           
            var cancelSource = new CancellationTokenSource();
            Task consoleKeyTask = Task.Run(() => { MonitorKeypress(cancelSource.Token); });
            await consoleKeyTask;

            List<Task> ListOfTasks = new List<Task>();
            
            for (int i = 1; i < 100000001; i++)
            {
                    ListOfTasks.Add(Task.Run(async () => await ExtractFile(MainDir, ZipDir, ToDecode, MyDest, MyPass, PassCount, RepsCount, MySpeed, "Random").ConfigureAwait(false)));

                    while (ListOfTasks.Count >= TaskCount)
                    {
                        await Task.WhenAny(ListOfTasks);
                        ListOfTasks.RemoveAll(x => x.IsCompleted);
                    }
                }
            }
        }

        private static async Task ExtractFile(string MainDir, string ZipDir, string sourceArchive, string MyDest, string MyPasss, int PassCount, int PassRep, int MySpeed, string FileOrRandom)
        {
            try
            {
                ProcessStartInfo pro = new ProcessStartInfo();
                pro.WindowStyle = ProcessWindowStyle.Hidden;
                pro.UseShellExecute = false;
                pro.RedirectStandardError = true;
                pro.RedirectStandardOutput = true;
                pro.FileName = ZipDir;              

                pro.Arguments = string.Format("t \"{0}\" -p\"{1}\" -y -o\"{2}\"", sourceArchive, MyPasss, MyDest); 
                //x or t, t is much faster, and password is saved to file (it is not necessary to decrypt file)
                Process x = await Task.Run(() => Process.Start(pro)).ConfigureAwait(false);
                //x.WaitForExit();
                string errorOutput = x.StandardError.ReadToEnd();
                string standardOutput = x.StandardOutput.ReadToEnd();
                
                if (x.ExitCode == 0)
                {
                    //save password to file
                }
                
                if (x.ExitCode == 2)
                {
					switch (Program.DisplayMode)
						{
							case 1:
								Console.WriteLine("Something"); //Continous WriteLine
								break;
							case 2:
								Console.WriteLine("Something"); //When in "silent" mode, print once and switch to silent mode
								DisplayMode = 3;
								break;
							case 3:
								DisplayMode = 3; //Silent mode
								break;
						}
                    
                }
            }

            catch (System.Exception Ex)
            {
                SysEx++;
                Console.WriteLine(Ex.Message);
            } 
        }


        //------------------------------------------------------------------------------------Display Mode
        public static void MonitorKeypress(CancellationToken cancellationToken)
        {
            ConsoleKeyInfo cki = new ConsoleKeyInfo();
            do
            {
                // true hides the pressed character from the console
                cki = Console.ReadKey();
                switch (cki.KeyChar)
                {
                    case '1':
                        Program.DisplayMode = 1;
                        break;
                    case '2':
                        Program.DisplayMode = 2;
                        break;
                    case '3':
                        Program.DisplayMode = 3;
                        break;
                }

                // Wait for an ESC
            } while (cki.Key != ConsoleKey.Escape);

            // Cancel the token
            CancellationTokenSource source = new CancellationTokenSource();
            source.Cancel();
        }

    }
}


1

Operacje Console.Read... zrób w głównym wątku. Dla pozostałych dedykowany wątek.

0

Nie wiem, czy dobrze rozumiem, ale to nie zadziała. Console.ReadKey blokuje wywoływanie kodu do czasu odczytania klawisza. Mnie chodzi o to, żeby MonitorKeypress działał "w tle" i czekał na klawisz, żeby zmodyfikować globalną zmienną Program.DisplayMode, dzięki której będę mógł modyfikować sposób działania Task ExtractFile. Ten task jest uruchamiany w pętli w async PutRandomTo7z. Chodzi o to, żeby PutRandom.... ze swoją pętlą działało cały czas równolegle z oczekiwaniem na klawisz MonitorKeyPress. Niby MonitorKeyPress się uruchamia, wchodzi w pętlę i tam staje, nie przechwytuje klawisza. Może coś sknociłem w switch()

0
Arduan napisał(a):

Może coś sknociłem w switch()

Dodaj w switch default i wypisz na konsole wynik

0

Console.ReadLine(); blokuje odczyt konsoli
zmień typ zwracany na "Task" PutRandomPassTo7z i go awaituj, zmień Main na "async Task Main", wyrzuć Console.ReadLine

a najlepiej zamiast blokować wątek główny niepotrzebnie i tworzyć nowy wątek czytający klawiaturę, po prostu przenieś logikę odczytu do głównego wątku (zamień Console.ReadLine na MonitorKeypress

            CancellationTokenSource source = new CancellationTokenSource();
            source.Cancel();

ten kod nie ma sensu i nic nie robi, anulujesz token który nigdzie nie jest wykorzystywany (nawet nie odczytujesz tokena z tokensource)

0

Problem leży tutaj. Uruchamiasz wątek asynchroniczny, i zanim metoda MonitorKeypress poprosi o wpisanie klawisza to Twój program jest już na ostatniej instrukcji Console.ReadLine();. Wciśniesz przycisk, wychodzisz z main i dziękuję koniec programu. Musisz to wykombinować inaczej. Powodzenia :D

        static void Main(string[] args)
        {
            Task.Run(() => PutRandomPassTo7z(MyTaskCount)).ConfigureAwait(false);
            Console.ReadLine();          
        }
0

@gswidwa1: Racja, dzięki, nie zauważyłem. Pierwotnie musiałem wstawić w Main Console.Readline(), żeby nie kończył głównego wątku, utrzymując działanie taska PutRandomPassTo7z i w moich próbach to ten ReadLine zbierał klawisze....
Zamieniłem ReadLine na infinite loop i działa...

static void Main(string[] args)
        {
            Task.Run(() => PutRandomPassTo7z(MyTaskCount)).ConfigureAwait(false);
                     var cancelSource = new CancellationTokenSource();
                    Task consoleKeyTask = Task.Run(() => { MonitorKeypress(cancelSource.Token); });
                    //await consoleKeyTask;
            while (true)
                 {}          
        }

i wygląda, że awaitowanie nie jest potrzebne, no chyba że....

2

działa, ale zabiera ci w 100% jeden core procesora
po co chcesz zajmować główny wątek niczym skoro możesz w nim coś robić? zamień ReadLine na MonitorKeypress i tyle

w tej chwili bezsensownie tworzysz nowy wątek tylko po to żeby ten pierwszy nic nie robił

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