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:
ele espera que o processo filho termine antes de terminar a si mesmo.
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.
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.
Exemplos:
PowerShell:
Shell CMD:
Observações:
Start-Process
(PowerShell) estart /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 -- "c:\full path\to\something.exe" arg1 arg2 "quoted arg3"
CreateNoWindow = true
se o parâmetroProcessStartInfo
. 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 configurarWindowStyle = ProcessWindowStyle.Hidden
, mas isso na verdade não funciona, a menos que você use ,ShellExecute = true
o 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.Chame
WaitForSingleObject
e passe o identificador do processo retornado porCreateProcess
como argumento de destino: