AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79565790
Accepted
arker
arker
Asked: 2025-04-10 13:33:52 +0800 CST2025-04-10 13:33:52 +0800 CST 2025-04-10 13:33:52 +0800 CST

Como posso modificar este C# (dentro do PowerShell) para que ele aguarde o término do processo filho que ele gera?

  • 772

Essa pessoa brilhante Jborean93 criou um script do PowerShell que por sua vez gera um .exe chamado "noGui.exe" aqui

Vou citar o código para que você não precise procurá-lo:

Add-Type -OutputType WindowsApplication -OutputAssembly NoGui.exe -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace NoGui
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public int dwProcessId;
            public int dwThreadId;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFOW
        {
            public Int32 cb;
            public IntPtr lpReserved;
            public IntPtr lpDesktop;
            public IntPtr lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwYSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [DllImport("Kernel32.dll", SetLastError = true)]
        public static extern bool CloseHandle(
            IntPtr hObject);

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool CreateProcessW(
            [MarshalAs(UnmanagedType.LPWStr)] string lpApplicationName,
            StringBuilder lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            int dwCreationFlags,
            IntPtr lpEnvironment,
            [MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory,
            ref STARTUPINFOW lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("Kernel32.dll")]
        public static extern IntPtr GetCommandLineW();

        static void Main()
        {
            IntPtr cmdLinePtr = GetCommandLineW();
            string cmdLine = Marshal.PtrToStringUni(cmdLinePtr);
            int cmdLineArgsIdx = cmdLine.IndexOf(" -- ");
            StringBuilder newCmdLine = new StringBuilder(cmdLine.Substring(cmdLineArgsIdx + 4));

            STARTUPINFOW si = new STARTUPINFOW()
            {
                cb = Marshal.SizeOf<STARTUPINFOW>(),
                dwFlags = 0x00000001, // STARTF_USESHOWWINDOW
                wShowWindow = 0, // SW_HIDE
            };
            PROCESS_INFORMATION pi;
            bool res = CreateProcessW(
                null,
                newCmdLine,
                IntPtr.Zero,
                IntPtr.Zero,
                true,
                0x00000410,  // CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT
                IntPtr.Zero,
                null,
                ref si,
                out pi
            );

            if (res)
            {
                CloseHandle(pi.hProcess);
                CloseHandle(pi.hThread);
            }
        }
    }
}
'@

Quando o NoGui.exe é executado, ele permite que (no meu caso, o PowerShell, mas pode ser qualquer processo) seja executado sem nenhuma janela/shell/prompts/qualquer coisa aparecendo na tela (e funciona).

É quase exatamente o que eu preciso. Mas, pelo que posso perceber, ele não espera que o processo que você solicitou seja encerrado antes de ser encerrado.

Preciso disso porque se eu for usar isso no Microsoft Intune para executar um script do PowerShell, não sei se o Intune detectaria o processo filho do PowerShell e esperaria sua conclusão.

Imagine por um momento que o script do PowerShell gerado pelo NoGui.exe pode levar muito tempo para ser concluído, mas o Intune pensa "ah, bem, o noGui.exe foi concluído, então a instalação deve ser feita". Mas não é o caso.

Então como podemos modificá-lo para:

  1. ele espera que o processo filho termine antes de terminar a si mesmo.

  2. Pontos adicionais, eu acho, se conseguirmos obter o código de saída para retornar ao processo pai e gerar a saída lá. O script do PowerShell explodiu por algum motivo? Tudo bem, o Intune recebeu o código de saída do Nogui.exe, então sabemos o que aconteceu.

Estou ciente de que existem wrappers do VBScript que ocultam a execução do PowerShell, mas não quero usá-los, pois o VBScript será desativado em algum momento no futuro.

c#
  • 2 2 respostas
  • 168 Views

2 respostas

  • Voted
  1. Best Answer
    zett42
    2025-04-10T20:14:29+08:002025-04-10T20:14:29+08:00

    Como observou o comentarista Jimi , o que você deseja alcançar pode ser feito completamente usando a classe Process do .NET , evitando o código de interoperabilidade de baixo nível. Aqui está uma tentativa de fazer isso.

    • Usa somente API .NET pura.
    • Aguarda o processo filho sair e retorna seu código de saída.
    • Adiciona tratamento de erros capturando quaisquer exceções e exibindo caixas de mensagens.
    • Mostra ajuda da linha de comando quando chamado sem ou com parâmetros inválidos (faltando o marcador "--").
    Add-Type -OutputType WindowsApplication -OutputAssembly NoConsole.exe -ReferencedAssemblies 'System.Windows.Forms.dll' -TypeDefinition @'
    using System;
    using System.Diagnostics;
    using System.Windows.Forms;
    using System.Text.RegularExpressions;
    
    namespace NoConsole
    {
        class Program
        {
            [STAThread]
            static int Main( string[] args )
            {
                // Extract the child command
                string childCommand;
                if( ! TryGetChildCommand( out childCommand ) )
                {
                    MessageBox.Show( "Usage: NoConsole.exe -- <command> [args]", "NoConsole - Missing Arguments",
                                     MessageBoxButtons.OK, MessageBoxIcon.Error );
                    return -1;
                }
                
                // Split the child command into filename and arguments for ProcessStartInfo
                string fileName, arguments;
                ParseCommand( childCommand, out fileName, out arguments );
    
                var psi = new ProcessStartInfo
                {
                    FileName = fileName,
                    Arguments = arguments,
                    UseShellExecute = false,
                    CreateNoWindow = true
                };
    
                try
                {
                    // Launch the child process, wait for exit and return the exit code.
                    using( var process = Process.Start( psi ) )
                    {
                        process.WaitForExit();
                        return process.ExitCode;
                    }
                }
                catch( Exception e )
                {
                    MessageBox.Show(
                        "Failed to start process:\n\n" + childCommand + "\n\n" + e.Message + string.Format( " (0x{0:X8})", e.HResult ),
                        "NoConsole - Error", MessageBoxButtons.OK, MessageBoxIcon.Error );
                    return -1;
                }
            }
            
            // Extracts the raw commandline after the "--" marker, to avoid the hassles of requoting arguments that contain whitespace.
            static bool TryGetChildCommand( out string childCommand )
            {
                string cmdLine = Environment.CommandLine;
                int markerIndex = cmdLine.IndexOf( " -- " );
                if( markerIndex < 0 || markerIndex + 4 >= cmdLine.Length )
                {
                    childCommand = null;
                    return false;
                }
    
                childCommand = cmdLine.Substring( markerIndex + 4 );
                return true;
            }       
    
            // Splits a full commandline into filename and arguments. Supports both quoted and unquoted filename. Arguments are returned as-is.
            static void ParseCommand( string cmdLine, out string fileName, out string arguments )
            {
                var match = Regex.Match( cmdLine.TrimStart(), @"^(""[^""]+""|\S+)(?:\s+(.*))?$" );
                if( match.Success )
                {
                    fileName = match.Groups[1].Value.Trim( '\"' );
                    arguments = match.Groups[2].Success ? match.Groups[2].Value : string.Empty;
                }
                else
                {
                    fileName = cmdLine.Trim();
                    arguments = string.Empty;
                }
            }
        }
    }
    '@
    

    Exemplos:

    PowerShell:

    > $proc = Start-Process -FilePath .\NoConsole.exe -ArgumentList '-- pwsh -Command "exit 42"' -Wait -PassThru
    > "ExitCode: $($proc.ExitCode)"
    
    ExitCode: 42
    

    Shell CMD:

    > start "" /wait NoConsole.exe -- pwsh -Command "exit 42"
    > echo ExitCode: %ERRORLEVEL%
    
    ExitCode: 42
    

    Observações:

    • O código para gerar "NoConsole.exe" deve ser executado no Windows PowerShell (5.1), pois o PS 7 ainda não suporta a criação de executáveis.
    • Os comandos Start-Process(PowerShell) e start /wait (CMD) são necessários para aguardar a saída de "NoConsole.exe", pois se trata de um aplicativo com interface gráfica (GUI) (apesar do nome e de não exibir interface gráfica quando chamado corretamente). Ao contrário de um aplicativo de console, que é executado de forma síncrona, um processo com interface gráfica é executado separadamente do console que o iniciou. Se desejar uma inicialização síncrona, ela pode ser obtida conforme mostrado nos exemplos.
    • "NoConsole.exe" também suporta a citação do caminho do arquivo do processo filho, o que é necessário quando o caminho contém espaços. Aqui está um exemplo de como chamar "NoConsole.exe" a partir do shell cmd: NoConsole.exe -- "c:\full path\to\something.exe" arg1 arg2 "quoted arg3"
    • A ocultação da janela do console é obtida configurando- CreateNoWindow = truese o parâmetro ProcessStartInfo. Embora isso funcione em muitos casos, aplicativos que dependem da existência de uma janela do console podem se comportar de forma inesperada ou falhar. Uma maneira mais robusta seria configurar WindowStyle = ProcessWindowStyle.Hidden, mas isso na verdade não funciona, a menos que você use , ShellExecute = trueo que tem seu próprio conjunto de problemas. O .NET alterou isso no .NET 8, mas como ele está sendo compilado com o .NET Framework, ele não pegará as novas alterações. Se você encontrar esse problema com o aplicativo específico que está iniciando através do NoConsole.exe, recomendo usar o NoGui original , que desde então foi atualizado para suportar a espera pela saída e o retorno do código de saída, entre outros recursos úteis.
    • 3
  2. Mathias R. Jessen
    2025-04-10T18:19:44+08:002025-04-10T18:19:44+08:00

    Chame WaitForSingleObjecte passe o identificador do processo retornado por CreateProcesscomo argumento de destino:

    // ...
    
    [DllImport("kernel32.dll", SetLastError = true)] 
    public static extern int WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
    
    private static readonly uint INFINITE = 0xFFFFFFFF;
    
    static void Main()
    {
        // ...
    
        bool res = CreateProcessW(
            null,
            newCmdLine,
            IntPtr.Zero,
            IntPtr.Zero,
            true,
            0x00000410,  // CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT
            IntPtr.Zero,
            null,
            ref si,
            out pi
        );
    
        if (res && pi.hProcess != IntPtr.Zero)
        {
            // wait on process handle
            int waitOutcome = WaitForSingleObject(pi.hProcess, INFINITE);
    
            if (waitOutcome == 0)
            {
                // successfully waited for process termination
            }
        }
    }
    
    • 2

relate perguntas

  • Polly DecorrelatedJitterBackoffV2 - como calcular o tempo máximo necessário para concluir todas as novas tentativas?

  • Wpf. Role o DataGrid dentro do ScrollViewer

  • A pontuação que ganhei na página do jogo com .NET MAUI MVVM não é visível em outras páginas. Como posso manter os dados de pontuação no dispositivo local

  • Use a hierarquia TreeView com HierarchicalDataTemplate de dentro de um DataTemplate

  • Como posso melhorar essa interface de validação no .NET?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve