9

Quando si esegue un'applicazione console in Visual Studio, a seconda delle impostazioni, si aggiungerà un prompt dopo l'uscita dal programma:Come posso rilevare se "Premere un tasto qualsiasi per continuare ...". sarà mostrato?

Premere un tasto qualsiasi per continuare. . .

Ho trovato come rilevare se sono in esecuzione con il debugger (utilizzare Debugger.IsAttached), ma non è utile. Premete CTRL-F5 a Avvia senza eseguire debug imposta questo flag per false, ma mostra ancora il prompt.

Voglio rilevare questo perché mi piacerebbe visualizzare il mio messaggio e attendere la pressione di un tasto, ma non raddoppia i controlli di pressione dei tasti.

Non voglio perdere tempo con le mie impostazioni generali di Visual Studio. Se posso disabilitare per questo progetto in un modo che può essere controllato nel controllo del codice sorgente, funzionerebbe anche.

Quale meccanismo viene utilizzato per aggiungere questo prompt e come lo si rileva?

O come posso disabilitarlo per progetto e controllare questa modifica nel controllo del codice sorgente?

risposta

6

Aggiungere il seguente codice per l'applicazione console:

public static class Extensions { 
    [DllImport("kernel32.dll")] 
    static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId); 

    [DllImport("kernel32.dll")] 
    static extern bool TerminateThread(IntPtr hThread, uint dwExitCode); 

    public static Process GetParentProcess(this Process x) { 
     return (
      from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>() 
      where (uint)it["ProcessId"]==x.Id 
      select Process.GetProcessById((int)(uint)it["ParentProcessId"]) 
      ).First(); 
    } 

    public static IEnumerable<Process> GetChildProcesses(this Process x) { 
     return (
      from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>() 
      where (uint)it["ParentProcessId"]==x.Id 
      select Process.GetProcessById((int)(uint)it["ProcessId"]) 
      ); 
    } 

    public static void Abort(this ProcessThread x) { 
     TerminateThread(OpenThread(1, false, (uint)x.Id), 1); 
    } 
} 

e quindi modificare il codice in questo modo:

class Program { 
    static void Main(String[] args) { 
     // ... (your code might goes here) 

     try { 
      Process.GetCurrentProcess().GetParentProcess().Threads.Cast<ProcessThread>().Single().Abort(); 
     } 
     catch(InvalidOperationException) { 
     } 

     Console.Write("Press ONLY key to continue . . . "); 
     Console.ReadKey(true); 
    } 
} 

Quindi, tutto quello che ci aspettiamo è fatto ora. Lo considero una soluzione alternativa. Funziona sotto Windows XP SP3 e credo che funzionerebbe con i più recenti sistemi operativi Windows. Sotto Visual Studio, le applicazioni sono sempre un processo generato. Nei precedenti Visual C++ 6.0, generato dall'IDE chiamando VCSPAWN.EXE; in Visual Studio 2010, l'applicazione viene eseguita con la seguente riga di comando quando Avvia senza eseguire debug:

"% COMSPEC%"/c "" l'applicazione nomefile "& pausa"

Così impossibile raggiungere l'obiettivo nei modi completamente gestiti; perché era NON sotto il dominio dell'applicazione.

Qui si usa il modo riuscito di WMI per enumerare i processi, e incapsulare i non gestiti WINAPI s di terminare i ProcessThread s, perché il ProcessThread non dovrebbe essere normalmente abortito; è fornito come qualcosa per sola lettura.

Come accennato in precedenza, l'applicazione è stata generata con la riga di comando specifica; sarebbe un singolo thread crea un singolo processo firma, quindi abbiamo utilizzato il metodo Single() per recuperare quel thread e terminarlo.

Quando si avvia l'applicazione con un prompt dei comandi esistente, è lo stesso scenario di Start senza debug. Inoltre, quando Avvia debug, il processo di applicazione viene creato da devenv.exe. Ha molti thread, lo sappiamo e non interromperà alcun thread, basta chiedere e attendere la pressione di un tasto. Questa situazione è simile all'avvio dell'applicazione con doppio clic o dal menu di scelta rapida . In questo modo, il processo dell'applicazione viene creato dalla shell di sistema, in genere Explorer.exe e ha anche molti thread.

Infatti, se siamo in grado di abortire il thread implica che abbiamo le autorizzazioni per uccidere il processo genitore. Ma dobbiamo fare NON. Abbiamo solo bisogno di interrompere l'unico thread, il processo termina automaticamente dal sistema quando non ha più thread. Uccidere il processo genitore identificando che il processo chiamante è %comspec%, è un altro modo per fare la stessa cosa, ma è una procedura pericolosa. Poiché il processo che genera l'applicazione potrebbe avere altri thread che hanno un numero qualsiasi di thread, crea un processo che corrisponde a %comspec%. Puoi uccidere un lavoro critico di processo con negligenza o semplicemente aumentando la complessità di controllare se il processo è sicuro da uccidere. Quindi considero un singolo thread crea un singolo processo come una firma del nostro processo genitore che è sicuro da uccidere/abortire.

WMI è moderno, alcuni di WINAPI s potrebbero diventare deprecati in futuro. Ma la vera ragione di questa composizione è per la sua semplicità. Il vecchio Tool Help Library è così complicato come i modi per convertire ProcessThread in System.Threading.Thread. Con LINQ e metodi di estensione, possiamo rendere il codice più semplice e più semantico.

+0

Sembra promettente. Accetterà del tempo dopo che la taglia è stata assegnata. –

+0

Ah .. Grazie. La taglia è iniziata da me, posso premiare solo gli altri, sto cercando una risposta migliore. –

+0

@ MerlynMorgan-Graham: ho perso il periodo di prova .. –

2

Suoni come questo prompt sono forniti dal comando pause. Questo comando viene automaticamente aggiunto da Visual Studio.

Quando si esegue il progetto all'esterno di Visual Studio, non vi è alcun motivo per "rilevare" questo comando. Puoi tranquillamente presumere che non verrà aggiunto al tuo programma. Questo significa che si può andare avanti e aggiungere qualsiasi prompt che si desidera, simile a:

Console.WriteLine("Press any key..."); 
Console.Read(); 

See this question.

+0

Buona risposta sul meccanismo, a me risulta che non è la parte più utile della mia domanda per me se non trovo un modo per rilevare che sta accadendo. Sai come rilevare se la mia app viene incartata in quel modo? Vorrei rilevare questo perché non voglio disabilitare la funzione VS, e non voglio essere richiesto due volte per uccidere la mia app. –

+0

Ho fatto una modifica, ti aiuta? Non ho familiarità con la riga di comando e C# per sapere se è possibile determinare se il programma corrente è stato chiamato con il parametro 'pause' come parametro. –

1

Quel messaggio non ha nulla a che fare con il vostro programma. Puoi aggiungere al tuo programma tutto ciò che vuoi e funzionerà allo stesso modo in cui lo farebbe, se dovessi eseguirlo dal prompt dei comandi.

Visual Studio lo mostra allo scopo di mostrarti che la sua esecuzione è finita, quindi sai che è finita correttamente. Se vuoi saltarlo puoi provare a "eseguire senza debug" (o qualcosa di simile, è appena sotto "esegui con debugger").

+0

Ctrl-F5 è la scorciatoia per "Avvia senza debug". Modificherà la mia domanda per chiarire. –

+0

@Merlyn - Provate l'altro poi - So che salta il "premere un tasto qualsiasi" su uno dei due (con/senza debug). Dovrebbe comunque. – Rook

+0

Così mentre sono d'accordo con il "Dottore, mi fa male il braccio quando lo faccio" "Beh, non farlo" sentimento, sono anche curioso di sapere se è possibile e come andare a rilevare che il prompt verrà visualizzato. –

2

Ecco un pezzo di codice che dovrebbe farlo:

class Program 
{ 
    static void Main(string[] args) 
    { 
     // do your stuff 

     if (!WasStartedWithPause()) 
     { 
      Console.WriteLine("Press any key to continue . . . "); 
      Console.ReadKey(true); 
     } 
    } 
} 

public static bool WasStartedWithPause() 
{ 
    // Here, I reuse my answer at http://stackoverflow.com/questions/394816/how-to-get-parent-process-in-net-in-managed-way 
    Process parentProcess = ParentProcessUtilities.GetParentProcess(); 

    // are we started by cmd.exe ? 
    if (string.Compare(parentProcess.MainModule.ModuleName, "cmd.exe", StringComparison.OrdinalIgnoreCase) != 0) 
     return false; 

    // get cmd.exe command line 
    string cmdLine = GetProcessCommandLine(parentProcess); 

    // was it started with a pause? 
    return cmdLine != null & cmdLine.EndsWith("& pause\""); 
} 

public static string GetProcessCommandLine(Process process) 
{ 
    if (process == null) 
     throw new ArgumentNullException("process"); 

    // use WMI to query command line 
    ManagementObjectCollection moc = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId=" + process.Id).Get(); 
    foreach (ManagementObject mo in moc) 
    { 
     return (string)mo.Properties["CommandLine"].Value; 
    } 
    return null; 
} 
+0

Mi piace questa risposta meglio di quella accettata, poiché non richiede l'attesa di ~ 500 ms dopo aver premuto un tasto. –

0

Questo messaggio è dato quando si usa il sistema di comando ("pausa")

.

Anche io ho affrontato questo problema. È possibile utilizzare la funzione _getch() in conio.h per attendere la pressione dei tasti.

in modo da poter utilizzare il seguente codice:

 cout<<"your message here" 
     _getch() 

Questo sarebbe aspettare la pressione di un tasto e visualizzare il proprio prompt.

0

Questo è l'output del comando "pausa" che viene aggiunto da Visual Studio. Se vedi questo, il programma è terminato. Il che giunge a una domanda come è possibile che un'applicazione rilevi che esso è terminato. Penso che questo non sia logico.

+0

L'obiettivo è fare l'opposto - rileva se non sei ospitato da VS, quindi dovresti presentare un meccanismo di pausa ed evitare una doppia pausa se sei ospitato da VS. –

Problemi correlati