c # - Få håndtaget og skriv til konsollen, der lancerede vores proces

Indlæg af Hanne Mølgaard Plasc

Problem



Hvordan kunne jeg skrive til standard output fra nogle allerede åbne konsoller?
Jeg finder den konsol, jeg har brug for med dette stykke kode:


    IntPtr ptr = GetForegroundWindow();           
    int u;
    GetWindowThreadProcessId(ptr, out u);
    Process process = Process.GetProcessById(u);


Problemet er, hvordan man får standard udgangshåndteringspeger (stdHandle) af denne proces.


Jeg vil så gerne have noget som:


                SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
                FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
                Encoding encoding = Encoding.ASCII;
                StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
                standardOutput.AutoFlush = true;
                Console.SetOut(standardOutput);


Kode i C ++ ved hjælp af Windows API er OK - Jeg kan bruge pInvoke.


Effektivt, hvad jeg gerne vil, er at skrive tekst til et allerede åbent konsolvindue, der ikke er fremkaldt af min proces (og det er den, der var i forgrunden, når jeg lancerede min proces via kommandolinjen - men min proces er en WinApp, så konsollen ikke vedhæfter std).


Kan standard output omdirigeres efter processen er blevet oprettet?


PS: Jeg læser om nogle COM-filer, der kan bruges til at gøre dette, så det betyder, at der er en programmatisk måde ...


Tak!

Bedste reference


Jeg har endelig fundet ud af, hvordan man vedhæftes transparent til en konsol, hvis det er forgrundsvinduet, mens man starter Windows-appen.


Spørg mig ikke, hvorfor STD\_ERROR\_HANDLE skal bestås i stedet for STD\_OUTPUT\_HANDLE, men det virker simpelthen, sandsynligvis fordi standardfejlen kan deles.


NB: Konsollen kan acceptere brugerindgang, mens du viser app-meddelelser indeni, men det er lidt forvirrende at bruge det, mens stderr'en sender ud af appen.


Med dette kodestykke, hvis du starter din app fra et konsolvindue med mindst en parameter, vedhæftes det Console.Write til det, og hvis du starter appen med parameteren/debug, så vedhæftes det selv Debug.Write til konsol.


Ring oprydning () før du forlader appen for at frigøre konsollen og sende en indtastningstastatur for at frigive den sidste linje, så konsollen kan bruges som før du starter appen.


PS. Du kan ikke bruge output omdirigering med denne metode, dvs .: yourapp.exe> ​​file.txt fordi
Du får en tom fil. Og prøv ikke selv myapp.exe> ​​file.txt 2> &1, fordi du vil nedbryde appen (omdirigeringsfejl til output betyder, at vi forsøger at vedhæfte til en ikke-leveret buffer).


Her er koden:


[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

[DllImport("kernel32.dll",
    EntryPoint = "GetStdHandle",
    SetLastError = true,
    CharSet = CharSet.Auto,
    CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);

[DllImport("kernel32.dll",
    EntryPoint = "AllocConsole",
    SetLastError = true,
    CharSet = CharSet.Auto,
    CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();

private const int STD\_OUTPUT\_HANDLE = -11;
private const int STD\_ERROR\_HANDLE = -12;
private static bool \_consoleAttached = false;
private static IntPtr consoleWindow;

[STAThread]
static void Main()
{
    args = new List<string>(Environment.GetCommandLineArgs());

    int prId;
    consoleWindow = GetForegroundWindow();            
    GetWindowThreadProcessId(consoleWindow, out prId);
    Process process = Process.GetProcessById(prId);

    if (args.Count > 1 && process.ProcessName == "cmd")
    {
        if (AttachConsole((uint)prId)) {
            \_consoleAttached = true;
            IntPtr stdHandle = GetStdHandle(STD\_ERROR\_HANDLE); // must be error dunno why
            SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
            FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
            Encoding encoding = Encoding.ASCII;
            StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
            standardOutput.AutoFlush = true;
            Console.SetOut(standardOutput);
            if (args.Contains("/debug")) Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
            Console.WriteLine(Application.ProductName + " was launched from a console window and will redirect output to it.");
        }
    }
    // ... do whatever, use console.writeline or debug.writeline
    // if you started the app with /debug from a console
    Cleanup();
}

private static void Cleanup() {
    try
    {
        if (\_consoleAttached)
        {
            SetForegroundWindow(consoleWindow);
            SendKeys.SendWait("{ENTER}");
            FreeConsole();
        }    
    }        
}

Andre referencer 1


Hvis hensigten er at skrive til forældrekonsollen, kan du, hvis nogen, bruge funktionen AttachConsole med ATTACH\_PARENT\_PROCESS argumentet. (se msdn attachconsole)


ATTACH\_PARENT\_PROCESS (DWORD) -1: Brug konsollen til moderen til den aktuelle proces


Og hvis du har brug for at tjekke overgangsprocessen, kan du bruge CreateToolhelp32Snapshot og få den overordnede proces gennem medlemmet th32ParentProcessID i PROCESSENTRY32-strukturen.

Andre referencer 2


Hvis du bare vil skrive til konsollen, der bruges af en anden app, kan du bruge følgende - du skal bruge P/Invoke til at gøre det første skridt:



  • AttachConsole (pid) til vedhæft til den pågældende konsol - hvis din proces allerede er forbundet med en konsol, skal du først have FreeConsole, da en proces kun kan forbindes med kun en konsol ad gangen.

  • Nu, hvor du er vedhæftet, får du konsoludgangshåndtaget ved hjælp af CreateFile ('CONOUT $', GENERIC\_WRITE, FILE\_SHARE\_WRITE, ...) - kan muligvis gøre denne del i administreret kode.

  • Nu, hvor du har HANDLE, pakk den op i administreret kode - denne del ved du allerede.



Når det er sagt, selvom du kan gøre dette, er det ikke nødvendigvis en god ide at gøre det. Der er ikke noget for at stoppe den oprindelige proces fra at skrive til konsollen, mens du gør det samme, og output fra begge bliver blandet -up, afhængigt af hvordan processerne gør buffering. Hvis du vil gøre noget som at underrette brugeren om noget uanset hvilket vindue der er aktivt, kan det være en bedre måde at gøre det på.

Andre referencer 3


En systemproces identificeres entydigt på systemet ved hjælp af procesidentifikatoren. Ligesom mange Windows-ressourcer identificeres en proces også af håndtaget, hvilket måske ikke er unikt på computeren. Et håndtag er det generiske udtryk for en identifikator af en ressource. Operativsystemet fortsætter proceshåndtaget, som åbnes via ProcessHandle-processen i Process-komponenten, selv når processen er gået ud. Således kan du få procesens administrative oplysninger, som f.eks. Process.ExitCode (normalt enten nul for succes eller en ikke-nul fejlkode) og Process.ExitTime. Håndtag er en yderst værdifuld ressource, så lækkende håndtag er mere virulent end lækker hukommelse.


Dette er ikke det præcise svar på dine spørgsmål, men det hjælper dig med at forstå den grundlæggende ting faktisk.