Announcement

Collapse
No announcement yet.

PInvokeStackImbalance bei FlashWindow

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • PInvokeStackImbalance bei FlashWindow

    Hi,

    Damit Fehlermeldungen meines Programmes nicht unbemerkt bleiben, nur weil ein anderes Programm im Vordergrund ist, habe ich folgendes versucht:
    [highlight=vbnet]
    Declare Function SetForegroundWindow Lib "user32" Alias "SetForegroundWindow" (ByVal hwnd As IntPtr) As Integer
    Declare Function FlashWindow Lib "user32.dll" (ByVal hwnd As Long, ByVal bInvert As Long) As Long

    Public Sub BringAppToFront()
    Dim WHandle As IntPtr = Process.GetCurrentProcess().MainWindowHandle
    If Not SetForegroundWindow(WHandle) Then
    clsLog.LogLine("SetForegroundWindow fehlgeschlagen.")
    For i As Integer = 1 To 3
    FlashWindow(WHandle, 0)
    FlashWindow(WHandle, 1)
    Next
    End If
    End Sub
    [/highlight]
    Die Idee dazu habe ich bei VBArchiv gefunden.

    Beim Debuggen erhalte ich bei FlashWindow eine PInvokeStackImbalance Meldung, mit dem Hinweis, ich solle die Aufrufsignatur prüfen. Ich habe festgestellt, dass FlashWindow das Handle als Long haben will, also habe ich da ein FlashWindow(WHandle.ToInt64, 0L) draus gemacht, aber ich erhalte den Fehler immer noch. Was kann ich tun?

    Gruß
    Martin Dietz

  • #2
    Hallo,

    um das Handle zu erhalten verwende
    [highlight=c#]
    IntPtr handle = this.Handle;
    [/highlight]

    bzw.
    [highlight=vbnet]
    Dim handle As IntPtr = Me.Handle
    [/highlight]

    mfG Gü
    "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

    Comment


    • #3
      Auch wenn du in der API einen long siehst in .NET solltest du einen Handle immer als IntPtr darstellen. Das bekommt das Framework dann schon richtig gemarshalled.

      http://www.pinvoke.net/default.aspx/...ashWindow.html

      Comment


      • #4
        Da das ganze in einem Modul implementiert ist, das Funktionen wie Min, Max, ShowError, SetDefaultPrinter usw. kapselt, gibt es kein Me oder this, geschweige denn ein Fenster dessen Handle ich mit Me.Handle ziehen könnte, daher der umständliche Umweg über den Prozess, ich denke, wenn ich ein Fenster gehabt hätte, hätte ich auch BringToFront() verwenden können, anstatt umständlich zu recherchieren, um dann im Endeffekt eine API-Funktion aufzurufen. Oder klappt das BringToFront nur innerhalb eines Prozesses, und das z.B. Outlook-Fenster bleibt weiterhin vor meiner Fehlermeldung?

        In den Artikeln, die ich gefunden habe, wurde das mit SetForegroundWindow gelöst, daher muss ich annehmen, dass ich keine Applikation in der Vordergrund bringen kann, sondern nur ein spezielles Fenster und nur über den API-Aufruf, und das sehe ich als großes Manko von Windows an; oder kann ich SetForegroundWindow und FlashWindow auch ein Process.Handle übergeben?

        Das Blinken habe ich jetzt übrigens über FlashWindowEx gelöst (da war auf MSDN hinter der Beschreibung der API-Funktion ein Verwendungsbeispiel), was mir zeigt, dass das Handle schon korrekt war, der PInvokeStackImbalance-Fehler mit FlashWindow also nicht an der Art liegen kann, wie ich das Handle bekomme, ich habe allerdings nicht ganz verstanden, ob und wenn ja wie uCount ausgewertet wird, wenn bei den Flags FLASHW_TIMERNOFG ausgewählt wurde, eine vollständige Beschreibung sieht anders aus! Ich gebe da momentan vorssichtshalber die 5 an (wie im Beispiel), weil ich nicht weiß, was passiert wenn ich eine 0 übergebe.

        Ach noch was: Können FlashWindowEx und SetForegroundWindow Probleme verursachen, wenn das Programm noch kein Fenster geöffnet hat, aus der Abfrage heraus also mit hWnd=0 aufgerufen werden? Sonst muss ich nämlich aufpassen, was ich mit den Fehlermeldungen mache, die in modMain.Main ausgegeben werden sollen, bevor ein Fenster geöffnet wird. Die Beschreibung auf MSDN gibt diesbezüglich nichts her. Sie erwähnen zwar keine Exceptions, die ausgelöst würden, wenn aber trotzdem das Programm abstürzt, nur weil kein Fenster nach vorne gezogen werden kann, bevor ich die MsgBox aufrufe, wäre das auch nicht so prickelnd. Ich kann das zwar abfangen in der Hoffnung, dass sich kein anderes Programm zwischen Programmaufruf und Öffnen des ersten Fensters in den Vordergrund schiebt und die Fehlermeldung dann trotzdem vorne bleibt, aber wie heisst es so schön: Beim Programmieren und auf hoher See ist man in Gottes Hand (oder so ähnlich)

        Und: SetForeground kommt während dem Debuggen mit 0 zurück (also Not SetForegroundWindow verzweigt in Plan B: das Blinken). Liegt das daran, dass ich am Debuggen bin, und sich der Debugger sofort selbst wieder in den Vordergrund zieht, oder läuft da grundsätzlich während des Aufrufs was schief?
        Liegt es daran, dass das Main Window nicht aktiv ist, sondern Dialoge, die davon aufgerufen wurden, und müsste ich eigenrtlich sämtliche Forms in Application.OpenForms durchgehen und auf TopLevel prüfen um das richtige Fenster zu finden, das ich mit SetForegroundWindow nach oben holen kann?

        Gruß
        Martin

        Comment


        • #5
          Da das ganze in einem Modul implementiert ist, das Funktionen wie Min, Max, ShowError, SetDefaultPrinter usw. kapselt, gibt es kein Me oder this, geschweige denn ein Fenster dessen Handle ich mit Me.Handle ziehen könnte, daher der umständliche Umweg über den Prozess,
          Du kannst im Modul das Handle des Fenster abfragen indem du in der Form eine Eigenschaft implentierts welche das Handle zurückgibt (Form = Klasse).

          zB
          [highlight=vbnet]
          Imports System
          Imports System.Windows.Forms

          Namespace WindowsFormsApplication1
          * * Public Partial Class Form1
          * * * * Inherits Form
          * * * * Public Sub New()
          * * * * * * InitializeComponent()
          * * * * End Sub
          * * * *
          * * * * Public ReadOnly Property WindowHandle() As IntPtr
          * * * * * * Get
          * * * * * * * * Return Me.Handle
          * * * * * * End Get
          * * * * End Property
          * * End Class
          End Namespace
          [/highlight]

          Den Rest hab ich nicht gelesen...

          mfG Gü
          "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

          Comment


          • #6
            @Gü : Handle ist doch eh schon public

            @Martin : Process.MainWindowHandle solltest du nicht zwingend trauen. Die Property liefert dir auf jedenfall nicht immer die Form die du bei Application.Run benutzt hast sondern die berücksichtigt noch andere Parameter (Z-Ordering, Sichtbarkeit und was weiß (oder vermute) ich noch).

            hWnd=0 entspricht dem Desktop(auch, der Desktop hat natürlich auch einen richtigen Handle ungleich 0) und nicht einem ungültigen Handle. Je nach API sollte man also aufpassen die mit dem 0 Handle aufzurufen. Bei SetForegroundWindow weiß ich nicht wie sich die verhält. Wenn du aber einen gültigen Handles hast brauchst du SetForegroundWindow aber nicht. Es geht auch Form.FromHandle(MyHandle).BringToFront().

            Ich würde das sowieso lassen mit dem in den Vordergrund bringen (und nur blinken). Es gibt nicht penetranteres als Anwendungen die sich ständig in den Vordergrund drücken und mir den Focus wegnehmen.

            Comment


            • #7
              @Gü : Handle ist doch eh schon public
              Oh Gü, was ist los mit mir? Urlaub wäre super...
              Hab mich in die Irre leiten lassen. Danke für den Hinweis, Ralf.

              mfG Gü
              "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

              Comment


              • #8
                Im Endeffekt brauche ich ja nicht wirklich das Hauptfenster, sondern nur ein Fenster, damit ich die Applikation in den Vordergrund bekomme, bevor ich MsgBox(...) aufrufe, und da das mit dem Hauptfenster anscheinend nicht geklappt hat (trotz gültigem Handle), kann ich es auch beim Blinken belassen,und das geht ja mit Process.MainWindowHandle.

                Irgendwie gebe ich Ralf recht, ein Programm, das sich in den Vordergrund drängt, wenn man gerade am Tippen ist, kann schon lästig sein. Üblicherweise ist man aber bei meinem Programm nicht während einer Aktion wo anders am Tippen, weil nur wenige Operationen länger dauern als ein paar Sekunden. Und bei denen sollte es eigentlich auch kein Problem sein, eine Fehlermeldung nicht sofort zu beachten, außer in wenigen Fällen:
                • Die Software erstellt eine Mail und fängt das Send-Ereignis ab, um die Mail im Programmordner zu speichern. Wenn dabei eine Rückfrage beantwortet werden muss, muss sich die MsgBox in den Vordergrund drängen, sonst hängen sowohl die Software als auch Outlook.
                • Die Software fährt einen Abgleich, die es in einer Datei protokolliert, und muss anhalten, weil der User zwischendrin die Protokolldatei geöffnet und gesperrt hat oder von einem anderen Programm her einen Stream auf die Datei offen hat. Hier muss meine Software den User benachrichtigen, das andere Programm zu schließen.
                • Die Generierung eines Reportes dauert sehr lange, und der User öffnet währendessen eine neue Instanz des Programms, um parallel weiterarbeiten zu können, Wenn nun Instanz 2 zwischendrin DB-Sperren setzt, über die Instanz 1 versucht Rückmeldung zu geben, sollte dies auch sofort auffallen.


                Und irgendwie habe ich Gü nicht so ganz verstanden: Schlägst Du vor, innerhalb von BringAppToFront eine eigene Form-Instanz zu kreieren, damit man ein Handle hat, mit dem man SetForegroundWindow aufrufen kann? Oder habe ich einfach nur das Prinzip meiner modGlobal schlecht erklärt? Ich habe einige Module, die Funktionen enthalten, die aus allen Bereichen aufgerufen werden können: ein Modul mit Print-Funktionen, um Enumerations darzustellen, ein Modul mit Auswertungen, um direkt aus der Datenbank eine entsprechende Excel-Vorlage zu füllen, und halt auch eine modGlobal, die die "klassichen" Allgemeinfunktionen bereitstellt, unter anderem ShowError, ShowQuestion usw. In diesem Modul existiert kein Fenster, da diese Funktionen aus verschiedenen Fenstern aufgerufen werden können, und aus diesem Grund gibt es kein Fenster, dessen Handle ich ziehen könnte.

                MfG
                Martin Dietz

                Comment


                • #9
                  Und irgendwie habe ich Gü nicht so ganz verstanden: Schlägst Du vor, innerhalb von BringAppToFront eine eigene Form-Instanz zu kreieren, damit man ein Handle hat, mit dem man SetForegroundWindow aufrufen kann?
                  Es war so gemeint: Du brauchst das Handle von einem Window. Deshlab war mein Vorschlag das Handle von der Form die geflasht werden soll als öffentliche Eigenschaft anzubieten. Da das Handle aber schon öffentlich ist (wie Ralf korrekt angemerkt hat) ist dies nicht nötig denn ein Verweis auf die Form reicht. Es war nie vorgeschlagen eine Form-Instanz zu kreieren sonder lediglich einen Verweis auf eine bestehende Form zu halten. Das ist der Unterschied.

                  Die Software erstellt eine Mail und fängt das Send-Ereignis ab, um die Mail im Programmordner zu speichern. Wenn dabei eine Rückfrage beantwortet werden muss, muss sich die MsgBox in den Vordergrund drängen, sonst hängen sowohl die Software als auch Outlook.
                  Kann die Email mit den Klassen von System.Net.Mail gesendet werden? Somit ist kein Zugriff auf Outlook nötig.

                  Die Software fährt einen Abgleich, die es in einer Datei protokolliert, und muss anhalten, weil der User zwischendrin die Protokolldatei geöffnet und gesperrt hat oder von einem anderen Programm her einen Stream auf die Datei offen hat. Hier muss meine Software den User benachrichtigen, das andere Programm zu schließen.
                  Ist es möglich den FileStream mit der FileShare-Überladung zu erstellen? Dort kann eingestellt werden wie nachfolgende Prozesse auf den Stream/Datei zugreifen können.

                  Die Generierung eines Reportes dauert sehr lange, und der User öffnet währendessen eine neue Instanz des Programms, um parallel weiterarbeiten zu können, Wenn nun Instanz 2 zwischendrin DB-Sperren setzt, über die Instanz 1 versucht Rückmeldung zu geben, sollte dies auch sofort auffallen.
                  Die Berichterstellung kann in einen eigenen Thread ausgelagert werden. So braucht keine zweite Intanz des Programms gestartet werden. Eine vernüftige Datenbank (keine Ahnung was du verwendest) sollte das Zugriffmanagement schon beherrschen.


                  mfG Gü
                  "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

                  Comment


                  • #10
                    Originally posted by gfoidl View Post
                    Es war so gemeint: Du brauchst das Handle von einem Window. Deshalb war mein Vorschlag das Handle von der Form die geflasht werden soll als öffentliche Eigenschaft anzubieten.
                    Deshalb hatte ich ja geschrieben, dass ein Teil des Problems ist, überhaupt die Form zu finden, die nach vorne gebracht oder geflasht werden soll, da das ganze in einem Modul steckt, das keinerlei Zugriff auf irgendein spezifisches Fenster hat. Gut, da haben wir also anscheinend aneinander vorbei geredet.

                    Ansonsten: Gemäß Firmenvorgabe müssen einige Mails mit Outlook gesendet werden, warum weiß ich selbst nicht, und bei den parallelen Bearbeitungen geht es weniger um die Sperren der Datenbank selber (ist übrigens MS SQLServer), sondern die Sperren, die das Programm vorsehen muss, damit nicht zwei Leute den gleichen Datensatz über das Programm bearbeiten. Diese Sperren müssen auch gesetzt werden, wenn entsprechende Reports Eingabezustände umsetzen, sonst werden die vom User, der das nach dem Report speichert wieder überschrieben. Eigene Threads wären auch eine Möglichkeit, dann können aber die meisten der User die Fehlermeldung nicht mehr dem spezifischen Report zuordnen, weil sie denken, der Report sei fertig sobald man geklickt hat. Und Probleme, dass er dann den Report gleich 3x erzeugt, weil er nach dem ersten Klick im Zielverzeichnis nichts findet sind dabei noch gar nicht mal betrachtet. Aus diesem Grunde wurde hier auch mal entschieden, lieber einen Laufbalken anzuzeigen, als die Generierung im Hintergrund zu starten.

                    Ansonsten: es ist über 2 Jahre nie zu Problemen mit nicht gefundenen Fehlermeldungen gekommen, erst letzens hat mich dann einer der Anwender zu seinem Arbeitsplatz gerufen, um zu fragen, warum denn Outlook so lange brauchen würde, die Mail zu versenden, die mein Programm erstellt hätte, dabei war im Hintergrund eine Nachricht, die Mail hätte nicht in einem bestimmten Verzeichnis gespeichert werden können, und Ihr wisst ja wie der Standardanwender reagiert: Nicht etwa "Oh, Stimmt, das hätte ich auch sehen können" sondern "Kann mich das Programm nicht darauf aufmerksam machen?"

                    Martin

                    Comment


                    • #11
                      dass ein Teil des Problems ist, überhaupt die Form zu finden, die nach vorne gebracht oder geflasht werden soll, da das ganze in einem Modul steckt, das keinerlei Zugriff auf irgendein spezifisches Fenster hat.
                      Dann geh den umgekehrten Weg:
                      • Ins Modul (Klasse) eine Eigenschaft vom Typ Form. Die Form welche geflasht werden soll trägt sich im Konstruktor in diese Eigenschaft ein. Somit hast du im Modul das Handle.
                      • erzeuge im Modul mit new Form() eine Form die geflasht wird.


                      mfG Gü

                      PS: Noch ein Beitrag dann hast du 100
                      "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

                      Comment

                      Working...
                      X