Laden...

Anwendung "klaut" Konsolenfenster?

Erstellt von lot2learn vor 11 Jahren Letzter Beitrag vor 11 Jahren 4.742 Views
L
lot2learn Themenstarter:in
43 Beiträge seit 2013
vor 11 Jahren
Anwendung "klaut" Konsolenfenster?

Hallo zusammen,

ich habe folgendes Problem mit meinem neusten Programm:

Und zwar starte ich über die Software eines Messgeräts eine von mir geschriebene Konsolenanwendung (Die Software des Messgeräts ist nicht von mir, unterstützt aber das Aufrufen von externen Konsolenanwendungen).

Wenn ich die Konsolenanwendung allein teste funktioniert auch alles so wie es soll. Lasse ich die Anwendung aber über die Mess-Software starten, dann stürzt meine Anwendung ab.

Durch Einfügen etlicher Debug-Textmeldungen habe ich den Problematischen Bereich auch schon gefunden:


System.Console.SetWindowSize(90, System.Console.WindowHeight);

Wenn ich diese Zeile auskommentiere funktioniert alles, allerdings wird während der gesamten Ausführung gar kein Konsolenfenster angezeigt. In diesem Fenster sollen aber wichtige Informationen angezeigt werden.

Kann es sein dass die Mess-Software das Konsolenfenster "klaut" oder irgendwie sonst unterbindet? Und was könnte man dagegen tun?

Schöne Grüße,
l2l

S
417 Beiträge seit 2008
vor 11 Jahren

Hallo,

Lasse ich die Anwendung aber über die Mess-Software starten, dann stürzt meine Anwendung ab.

Was ist denn die Fehlermeldung die beim Absturz deiner Anwendung auftritt? Wenn in der von dir ermittelten Zeile eine Exception geworfen wird, dann setzt doch ein try-block darum, und lass dir im Catch-Block die Exception ausgeben.
Dann weisst du warum die Anwendung abschmiert.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo lot2learn,

Wenn ich diese Zeile auskommentiere funktioniert alles, allerdings wird während der gesamten Ausführung gar kein Konsolenfenster angezeigt

wo hast du dann deine Debug-Textmeldungen gesehen?

Wie dem auch sei. Natürlich kann es sein, dass die Messsoftware dein Programm so startet, dass kein Konsolenfenster angezeigt wird. Daran könnt aber m.E. nur die Messanwendung was ändern.

herbivore

L
lot2learn Themenstarter:in
43 Beiträge seit 2013
vor 11 Jahren

Hallo,

Danke schonmal für die Rückmeldungen!

Also die Debug Textmeldungen hab ich mir per MessageBoxes ausgeben lassen, nicht per Konsole. Damit konnte ich Zeile für Zeile verfolgen ab welcher Stelle der Code abstürzt, was mich dann zu oben genannter Zeile gebracht hat.

Ich hab mir mittlerweile die Exception auch mal als MessageBox ausgeben lassen und hab diese hier angehängt. Leider sagt mir die Fehlermeldung nicht wirklich viel...

Kann ich nicht von der laufenden Konsolenanwendung irgendwie ein Konsolenfenster öffnen? Die Mess-Software kann ich leider nicht ändern, da diese nicht von mir stammt...

Schöne Grüße,
l2l

1.696 Beiträge seit 2006
vor 11 Jahren

Hallo,

der Fehlermeldung nach sagt das System, dass es Probleme hat Fensterhöhe zu ermitteln, wahrscheinlich weil keins da ist.

Was wäre wenn du statt Konsolen- eine Winform-Anwendung daraus machst und deine Ausgabe in was auch immer ausgibst? Ich vermute, dass die MessSoftware irgendwie keine weitere Konsolenfenster sichtbar zuläßt.

Grüße

Ich bin verantwortlich für das, was ich sage, nicht für das, was du verstehst.

**:::

L
lot2learn Themenstarter:in
43 Beiträge seit 2013
vor 11 Jahren

Hallo,

der Fehlermeldung nach sagt das System, dass es Probleme hat Fensterhöhe zu ermitteln, wahrscheinlich weil keins da ist.

Ok, das macht Sinn.

Was wäre wenn du statt Konsolen- eine Winform-Anwendung daraus machst und deine Ausgabe in was auch immer ausgibst?

Das Problem ist dass die Mess-Software ausschließlich Konsolenanwendungen zulässt, was anderes kann man leider gar nicht erst aufrufen 😦

Ich vermute, dass die MessSoftware irgendwie keine weitere Konsolenfenster sichtbar zuläßt.

Gibt es eine einfache Möglichkeit das zu verifizieren? Bzw. kann man nicht irgendwie an der Mess-Software "vorbei" ein Konsolenfenster aufmachen?

Ich will letztendlich nur so einfach wie möglich kontinuierlich die Daten ausgeben die von der Konsolenanwendung berechnet werden.

Schöne Grüße,
l2l

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo lot2learn,

du kannst es mal mit Win32 AllocConsole probieren. Wenn das nicht geht, sehe ich keine Chance, außer, dass du dein Programm mit Process.Start selbst nochmal startest, wenn du erkennst, dass keine Konsole da ist.

herbivore

A
764 Beiträge seit 2007
vor 11 Jahren

Hallo lot2learn,

ist es denn notwendig, dass die Ausgabe in einem Consolen-Fenster erfolgt?
Ansonsten könnstest du auch mit einer Kombination aus z.b. log4net oder NLog und TraceEye - Professional LogViewer oder Tail4Win arbeiten.
Also die Ausgabe in Log-Dateien schreiben und die Anzeige mit einem anderen Programm erfolgen lassen.

Gruß, Alf

1.361 Beiträge seit 2007
vor 11 Jahren

Hallo lot2learn,

Ich vermute, dass die MessSoftware irgendwie keine weitere Konsolenfenster sichtbar zuläßt.
Gibt es eine einfache Möglichkeit das zu verifizieren? Bzw. kann man nicht irgendwie an der Mess-Software "vorbei" ein Konsolenfenster aufmachen?

Ja, du kannst überprüfen, ob du ein sichtbares Konsolenfenster hast. Das geht mit den WinAPI Funktionen GetConsoleWindow und IsWindowVisible.

Während des Erzeugens eines Prozesses mittels CreateProcess gibt es mehrere Möglichkeiten ein Konsolenfenster des Kindes zu unterdrücken:
*Parent hat eine Konsole, macht diese aber unsichtbar -> Kind erbt diese (unsichtbare) *Parent setzt beim Start CREATE_NO_WINDOW *Parent ist Konsolenanwendung und setzt beim Start DETACHED_PROCESS *Parent setzt beim Start eines der ShowWindow commands SW_HIDE oder SW_*MINIMIZE(D)

Wie herbivore schon gesagt hat, kannst du aber problemlos mit AllocConsole eine neue Konsole erzeugen, falls du keine bekommen hast.
Darüberhinaus musst du aber diese auch sichtbar machen, falls sie es (durch parent erzwungen) nicht ist. Das geht mit ShowWindow.

Nen ganz fieser Parent könnte das Kind zwar mit seiner Konsole starten, aber stdin und stdout umbiegen, dann müsste man die nochmal setzen, wahrscheinlich mit SetStdHandle, wobei ich das selbst nicht probiert habe.

Für das Sicherstellen, dass du in allen denkbaren Fällen immer eine sichtbare Konsole hast, habe ich mal folgendes Snippet gebastelt:

using System;
using System.Runtime.InteropServices;

namespace System
{
    public class ConsoleHelper
    {
        public static void EnsureConsoleVisibility()
        {
            if (!HasConsoleWindow())
            {
                // processes with the CREATE_NO_WINDOW flag can't directly call AllocConsole.
                // so we call FreeConsole, before we try to create a new one.
                FreeConsole();
                AllocConsole();
            }

            IntPtr consoleHandle = GetConsoleWindow();

            if (!IsWindowVisible(consoleHandle))
            {
                // if special ShowWindow flags were set during process creation, 
                // our first call to ShowWindow is overriden by these flags and possibly hidden.
                // so, let's call it the first time with these default flags.
                STARTUPINFO sInfo;
                GetStartupInfo(out sInfo);
                if ((sInfo.dwFlags & StartupInfoFlags.STARTF_USESHOWWINDOW) != 0)
                    ShowWindow(consoleHandle, ShowWindowFlags.SW_SHOWDEFAULT); // any flag, it is overriden either way

                // now ensure the window's visibility
                ShowWindow(consoleHandle, ShowWindowFlags.SW_SHOW);
            }
        }

        static bool HasConsoleWindow()
        {
            return GetConsoleWindow() != IntPtr.Zero;
        }

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        protected static extern void GetStartupInfo(out STARTUPINFO lpStartupInfo);

        [DllImport("kernel32.dll")]
        protected static extern IntPtr GetConsoleWindow();

        [DllImport("kernel32")]
        protected static extern bool AllocConsole();

        [DllImport("kernel32")]
        protected static extern bool FreeConsole();

        [DllImport("user32.dll")]
        protected static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("user32.dll")]
        protected static extern bool ShowWindow(IntPtr hWnd, ShowWindowFlags nCmdShow);

        [Flags]
        protected enum StartupInfoFlags : uint
        {
            STARTF_USESHOWWINDOW = 0x00000001
        }

        protected enum ShowWindowFlags : uint
        {
            SW_HIDE = 0,
            SW_SHOW = 5,
            SW_SHOWNORMAL = 1,
            SW_SHOWMINIMIZED = 2,
            SW_SHOWMAXIMIZED = 3,
            SW_MAXIMIZE = 3,
            SW_MINIMIZE = 6,
            SW_SHOWNOACTIVATE = 4,
            SW_SHOWMINNOACTIVE = 7,
            SW_SHOWNA = 8,
            SW_RESTORE = 9,
            SW_SHOWDEFAULT = 10,
            SW_FORCEMINIMIZE = 11,
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        protected struct STARTUPINFO
        {
            public UInt32 cb;
            public IntPtr lpReserved;
            public IntPtr lpDesktop;
            public IntPtr lpTitle;
            public UInt32 dwX;
            public UInt32 dwY;
            public UInt32 dwXSize;
            public UInt32 dwYSize;
            public UInt32 dwXCountChars;
            public UInt32 dwYCountChars;
            public UInt32 dwFillAttribute;
            public StartupInfoFlags dwFlags;
            public UInt16 wShowWindow;
            public UInt16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }
    }
}

beste Grüße
zommi

U
1.688 Beiträge seit 2007
vor 11 Jahren

Das Problem ist dass die Mess-Software ausschließlich Konsolenanwendungen zulässt, was anderes kann man leider gar nicht erst aufrufen 😦

Kannst Du das näher erklären?

1.361 Beiträge seit 2007
vor 11 Jahren

Ich würde vermuten, dass die Mess-Software sich das Executable vor dem Starten anschaut, und wenn es keine Konsolenanwendung ist, sie nicht startet.

Entweder mit manuellem Lesen des Headers oder mit Hilfe von SHGetFileInfo.

beste Grüße
zommi

L
lot2learn Themenstarter:in
43 Beiträge seit 2013
vor 11 Jahren

Wow,

vielen Dank für eure ausführliche Hilfe! Ich kann leider erst am Dienstag wieder an das Messgerät, aber dann werd ich das Snippet auf jeden Fall ausprobieren und hier Bescheid geben ob alles geklappt hat 😃

Warum das Messprogramm nur Konsolenanwendungen zulässt weiß ich nicht, keine Ahnung was sich die Entwickler dabei gedacht haben 😄

Schöne Grüße,
l2l

U
1.688 Beiträge seit 2007
vor 11 Jahren

Ich würde vermuten, dass die Mess-Software sich das Executable vor dem Starten anschaut, und wenn es keine Konsolenanwendung ist, sie nicht startet.

Man kann ja auch eine WindowsForms-Anwendung als Konsolenanwendung kompilieren. Dann wäre die Mess-Software zufrieden und es gäbe trotzdem ein Fenster zur Ausgabe. Käme auf einen Test an.

L
lot2learn Themenstarter:in
43 Beiträge seit 2013
vor 11 Jahren

Hallo nochmal,

ich wollte das Snippet heute schonmal Zuhause vorab testen damit ich am Dienstag dann direkt den fertigen Code auf das Messgerät laden kann.

Leider kompiliert das Snippet bei mir nicht sondern gibt folgende Fehlermeldung aus:

Fehlermeldung:
"System.ConsoleHelper.StartupInfoFlags" enthält keine Definition für "HasFlag", und es konnte keine Erweiterungsmethode "HasFlag" gefunden werden, die ein erstes Argument vom Typ "System.ConsoleHelper.StartupInfoFlags" akzeptiert. (Fehlt eine Using-Direktive oder ein Assemblyverweis?)

Aber HasFlag sollte doch im System namespace enthalten sein, oder?

Grüße,
l2l

16.827 Beiträge seit 2008
vor 11 Jahren

Ab .NET 4.0 wie der Dokumentation zu entnehmen ist.

1.361 Beiträge seit 2007
vor 11 Jahren

Habe mein Snippet angepasst.
Ich bin lieber für 2.0 Kompatibilität statt einer winzigen Hilfsmethode.

x.HasFlag(y) ==> (x&y) != 0

beste Grüße
zommi

L
lot2learn Themenstarter:in
43 Beiträge seit 2013
vor 11 Jahren

Danke für das Update,

das Snippet kompiliert jetzt. Hab es auch gleich mal ausgetestet indem ich das Konsolenfenster folgendermaßen versteckt habe:


Console.Title = "test";
IntPtr hWnd = FindWindow(null, "test");
ShowWindow(hWnd, 0);

und dann sol wieder einblenden wollte:


ConsoleHelper.EnsureConsoleVisibility();

Leider stürzt das Programm dann aber ohne eine Exception zu werfen ab. In dem Fenster "Ausgabe" werden aber sehr viele Nachrichten ausgegeben, hier die erste:

Fehlermeldung:
"Test.vshost.exe" (Verwaltet (v4.0.30319)): "C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll" wurde geladen, das Laden von Symbolen wurde übersprungen. Das Modul ist optimiert, und die Debugoption "Nur eigenen Code" ist aktiviert.

1.361 Beiträge seit 2007
vor 11 Jahren

Hey,

hab mir den Fehler mal angeschaut. Es ist ein Problem des Marshallings beim PInvoke.

Die Unicode Strings in der STARTUPINFO Struktur können auf einem 64-Bit Windows nicht korrekt beim output der GetStartupInfo Methode gemarshalled werden. 🤔

Laut Internet sollen nullterminierte Strings in Strukturen irgendwie immer problematisch beim Out-Marshalling sein... verstehe es aber ehrlich gesagt nicht. Denn im 32-Bit Modus klappt es einwandfrei... aber Strings sind doch in 32Bit genauso wie in 64Bit... Alles komisch. Falls wer möchte, ich würde darüber gern in 'nem neuen Thread diskutieren?!

Da man den Inhalt der Strings aber nicht brauch, kann man getrost einen IntPtr nehmen. Ich habe das oben mal angepasst. Dann sollte es auch wieder fehlerfrei klappen.

Notiz an mich: Immer vorher Code auch mit .NET 2.0 und x64 testen 😉

beste Grüße
zommi

L
lot2learn Themenstarter:in
43 Beiträge seit 2013
vor 11 Jahren

Hallo nochmal,

ich habe das Snippet jetzt an dem Messgerät ausprobiert und leider funktioniert es nicht. Die Konsole wird nach wie vor nicht angezeigt und die Fehlermeldung ist die Gleiche. An meinem Rechner Zuhause hat alles tadellos funktioniert wenn ich die Konsole manuell versteckt habe, an dem Messgerät klappts irgendwie nicht 😦

Was ich rausgefunden habe: Wenn ich statt dem Snippet die folgenden Zeilen verwende wird die Konsole angezeigt.


FreeConsole();
AttachConsole(-1);

Leider hab ich trotz Anzeige nach wie vor keinen Zugriff darauf, die Fehlermeldng erscheint trotzdem und auch wenn ich das SetWindowSize auskommentiere wird einfach kein Text in die Konsole ausgeben... Das Programm an sich funktioniert aber ja, dann sollte man den Output doch auch irgendwie in die Konsole kriegen, oder?

Schöne Grüße,
l2l

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo lot2learn,

wenn es dir nur darum geht, dass die Informationen angezeigt werden, dann folge doch dem letzten Ratschlag von ujr und verwende eine Windows Forms Anwendung, die du als Konsolenanwendung compilierst.

herbivore

L
lot2learn Themenstarter:in
43 Beiträge seit 2013
vor 11 Jahren

Hallo,

ja das kann ich natürlich machen, aber mir geht es auch immer darum was zu lernen, deswegen interessiert mich das jetzt warum das nicht funktioniert... Immerhin wird die Konsole ja jetzt schonmal angezeigt dann kann der letzte Schritt ja nicht mehr weit sein.

Ich finds einfach besser solchen Sachen auf den Grund zu gehen, wenn es möglich ist.

Schöne Grüße,
l2l