none
Klassen-Name RRS feed

  • Frage

  • Hallo,

    da ich von C# aus API-Funktionen anspreche, hoffe ich, dass ich in diesem Forum (mit einem API-Problem) aber immer noch richtig bin.

    Ich bin ein wenig verwirrt. Bis jetzt dachte ich, dass ein Klassen-Name immer konstant ist...

    Ziel des ganzen ist es, ein Windows-Handle eines Fensters einer fremden Applikation zu ermitteln.

    Über die FindWindow() API-Funktion kann ich über den Capture Namen das gewünschte Window-Handle ermitteln:
    FindWindow(null, "Monarch Pro");

    Da ich das selbe jedoch über den Klassen-Namen erreichen möchte, habe ich im Anschluss den Klassennamen per API ermittelt:
    GetClassName(intWindowHandle, KlassenName, KlassenName.Capacity);

    Jetzt musste ich jedoch feststellen, dass der Klassen-Name bei jedem Start der Fremd-Applikation im letzten Punkt unterschiedlich ist.

    Nur wenn ich den vollständigen (unterschiedliche) Klassen-Name wieder in meiner FindWindow() API-Funktion einsetze erhalte ich das
    richtige Windows-Handle der Fremd-Applikation.
    intWindowHandle = (System.IntPtr)FindWindow(KlassenName.ToString(), null);

    Wie kann ich einen festen Klassen-Name für eine Applikation bestimmen?

    Fred.

    ------------------------------------------------------------------------------------------------------------

    Als Bsp. die Bestimmung und (Rück) Prüfung mittels der API-Funktionen

                System.IntPtr intWindowHandle = (System.IntPtr) FindWindow(null, "Monarch Pro");                            // Handle des Monarch-Fensters ermitteln
              MessageBox.Show("Monarch-Fenster: " + intWindowHandle.ToString());

                int nRet;
                StringBuilder KlassenName = new StringBuilder(100);
                nRet = GetClassName(intWindowHandle, KlassenName, KlassenName.Capacity);              // Klassen-Name von Monarch bestimmen
                if (nRet != 0)
                {
                    MessageBox.Show("Klassen-Name: " + KlassenName.ToString());
                }

                // Gegenprobe (die Funktioniert):
                intWindowHandle = (System.IntPtr)FindWindow(KlassenName.ToString(), null);   // Handle des Monarch-Fensters über den Klassen-Namen ermitteln
                MessageBox.Show("Monarch-Fenster: " + intWindowHandle.ToString());
    // mit festen Wert jedoch nicht:
                iWindowHandle = (System.IntPtr)FindWindow("Afx:00400000:8:00010003:00000000:07490477", null);                                                      // Handle des Monarch-Fensters ermitteln

    • Bearbeitet perlfred Mittwoch, 15. Dezember 2010 15:43 Ergänzungen
    Mittwoch, 15. Dezember 2010 15:35

Antworten

  • Hallo Fred

    offenbar nutzt du das Win32 API GetClassName
    http://msdn.microsoft.com/en-us/library/ms633582(VS.85).aspx

    und dies hat eine ganz eigene Bedeutung, nicht direktes was mit einer C#/.NET -Klasse identisch wäre.
    Hier die SDK Doku:
    Window Classes
    http://msdn.microsoft.com/en-us/library/ms632596(VS.85).aspx
    About Window Classes
    http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx

    Mittwoch, 15. Dezember 2010 15:46
  • Im Moment weis ich jedoch noch nicht so recht, etwas damit anzufangen.
    naja, genannte MSDN  (C++ SDK) lesen wäre mal ein Anfang, wenn du halt die reine .NET-Welt verlassen willst.

    Kurzversion: all jene Namen ähnlich wie "Afx:00400000:8:00010003:00000000:07490477"  sind hoch-dynamisch zusammengestellte, 'pseudo-eindeutige bis zufällige' Kennungen einer native Win32 'Class'.
    Also du darfst diesen String im Prinzip gar nicht interpretieren oder gar Abhängigkeiten davon codieren, schon gar nicht mit C#/.NET.
      (aber wenn du schon 'Hacks' von Fremd-Apps machst, bleibt uU/worst-case nichts anderes übrig, viel Spass!)

    Technisches Detail: Das "Afx:" am Anfang deutet auf eine Fremd-App mit C++/MFC gebaut (evtl AfxRegisterClass), enthält wohl Handles und andere Werte, welche pro Prozess/Start total anders sind.
    Mittwoch, 15. Dezember 2010 16:15
  • Hallo Thomas, Stefan, Elmar und Frank,

    Vielen! Dank! für die vielen fachkundigen Antworten!!!!!

    Sie haben mir geholfen zu erkennen, dass es nicht so einfach ist, den statischen Klassen-Namen einer Fremd-Applikation zu bestimmen (womit ich ehrlich gesagt nicht gerechnet hätte).

    Der Ausgangspunkt war/ist jedoch: "Ziel des ganzen ist es, ein Windows-Handle eines Fensters einer fremden Applikation zu ermitteln."

    Ich habe es jetzt so gelöst, dass ich alle Windows-Prozesse der Maschiene durchlaufe und anhand des Prozess-Namens und der Start-Zeit der Fremd-Applikation das benötigte Windows-Handle bestimme.

    Nochmals vielen Dank für eure Hilfe!

    Fred.

            /// <summary>
            /// Ermittelt aus allen, auf der Maschiene laufenden Prozessen, das Fenster-Handle des Prozesses, dass den angegebenen Prozess-Namen hat und in der vorgegebenen Zeitspanne gestartet wurde.
            /// </summary>
            /// <param name="zProzessName">Prozess-Name</param>
            /// <param name="StartZeitDif">Zeitspanne, seit dem der Prozess gestartet wurde. 0 = unendlich</param>
            /// <returns>Fenster-Handle des gesuchten Prozesses.</returns>
            public static System.IntPtr FindWinFenster(string zProzessName, TimeSpan StartZeitDif)
            {
                System.IntPtr pWinHandle = new IntPtr();                                                    // Windows-Handle des gesuchten Fenster's
                System.Diagnostics.Process[] aktProzesse = System.Diagnostics.Process.GetProcesses();       // "Alle" Prozesse die akt. auf der Maschine laufen einlesen
                foreach (System.Diagnostics.Process aktProzess in aktProzesse)                              // Alle Prozesse durchgehen
                {
                    if (aktProzess.ProcessName == zProzessName)                                             // Prozess mit dem gesuchten Prozess-Name vorhanden?
                    {
                        if ((DateTime.Now - aktProzess.StartTime <= StartZeitDif) || (StartZeitDif == new TimeSpan(0,0,0)))
                            pWinHandle = aktProzess.MainWindowHandle;                                       // Wenn die Startzeit des gesuchten Prozesses innerhalb der angegebenen Zeitspanne erfolgt ist, Windows-Handle übergeben.
                    }
                }
                return pWinHandle;   
            }

    Donnerstag, 16. Dezember 2010 09:40

Alle Antworten

  • Hallo Fred

    offenbar nutzt du das Win32 API GetClassName
    http://msdn.microsoft.com/en-us/library/ms633582(VS.85).aspx

    und dies hat eine ganz eigene Bedeutung, nicht direktes was mit einer C#/.NET -Klasse identisch wäre.
    Hier die SDK Doku:
    Window Classes
    http://msdn.microsoft.com/en-us/library/ms632596(VS.85).aspx
    About Window Classes
    http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx

    Mittwoch, 15. Dezember 2010 15:46
  • Hallo Thomas

    Danke für deine Antwort. Im Moment weis ich jedoch noch nicht so recht, etwas damit anzufangen.

    Ja, ich greif auf die Win32 API zu:

            // API-Funktionen zu Windows-Fenster
            [System.Runtime.InteropServices.DllImport("user32.dll")]
            static extern Int32 FindWindow(string strclassName, string strWindowName);      // Windows-Fenster-Handle ermitteln

            [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
            static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);      // Klassenname ermitteln

    Erzeugt das auf einer 64Bit Maschiene andere Ergebnisse? Bzw gibt es andere API-Funktionen für 64Bit???

    Fred.

    Mittwoch, 15. Dezember 2010 15:57
  • hallo,

    der Name, welchen du mit GetClassName(), bekommst is per se dynamisch, siehe:

    http://msdn.microsoft.com/en-us/library/ms633575%28v=vs.85%29.aspx

    Ein Applikation kann natürlich immer versuchen einen statischen Namen zu nutzen, muss es aber nicht.


    Microsoft MVP Office Access
    https://mvp.support.microsoft.com/profile/Stefan.Hoffmann
    Mittwoch, 15. Dezember 2010 16:13
  • Im Moment weis ich jedoch noch nicht so recht, etwas damit anzufangen.
    naja, genannte MSDN  (C++ SDK) lesen wäre mal ein Anfang, wenn du halt die reine .NET-Welt verlassen willst.

    Kurzversion: all jene Namen ähnlich wie "Afx:00400000:8:00010003:00000000:07490477"  sind hoch-dynamisch zusammengestellte, 'pseudo-eindeutige bis zufällige' Kennungen einer native Win32 'Class'.
    Also du darfst diesen String im Prinzip gar nicht interpretieren oder gar Abhängigkeiten davon codieren, schon gar nicht mit C#/.NET.
      (aber wenn du schon 'Hacks' von Fremd-Apps machst, bleibt uU/worst-case nichts anderes übrig, viel Spass!)

    Technisches Detail: Das "Afx:" am Anfang deutet auf eine Fremd-App mit C++/MFC gebaut (evtl AfxRegisterClass), enthält wohl Handles und andere Werte, welche pro Prozess/Start total anders sind.
    Mittwoch, 15. Dezember 2010 16:15
  • Hallo Stefan!

    Gut zu wissen!! Und ... ich muss den statischen Namen verwenden, ansonsten könnte ich ja gleich über den Fenster-Namen das Windows-Handle bestimmen, aber dieser ist von Version zu Version der Fremd-Applikation unterschiedlich!

    Wie könnte ich den statischen Namen ermitteln????

    Fred.

    • Bearbeitet perlfred Mittwoch, 15. Dezember 2010 16:19 Kongretisieren
    Mittwoch, 15. Dezember 2010 16:16
  • Wie kann ich aber den statischen Namen ermitteln????
    Fred, wie ich oben schrieb, ist die Fremd-App wohl in C++/MFC geschrieben. Wenn du dessen Source-Code hast, dann 'kein' Problem...
    Mittwoch, 15. Dezember 2010 16:18
  • Hallo Fred,

    die Deklaration von GetClassName ist richtig, die geht in etwa so:

    [PostMessage & Findwindow not working on win 7?]
    http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/1dcae178-50e8-446f-9c45-f016ad0f7fc3

    aber der Klassenname von Deinem "Monarch Pro" kann sich ja in einer nativen 64 Bit Applikation durchaus unterscheiden! Und sowieso gibt es dynamische Klassennamen und Klassen-Namen die konstant bleiben. Gut, Du kannst natürlich die Remote-App (die mit FindWindow gesucht werden soll) als 32 Bit Prozess starten, dann wird der Klassenname ggf. wieder der gleiche sein (es kommt auf den Applikations-Typ an). In jedem Fall solltest Du prüfen, ob für Deine Klasse der Klassenname 100% als konstant unterstützt wird. Oft ist er das jedenfalls nicht.


    ciao Frank
    Mittwoch, 15. Dezember 2010 16:19
  • Hallo Fred,

    das hat mit der Anzahl der Bits nichts zu tun.
    Und auch nicht mit dem, was man unter einer (.NET) Klasse versteht.

    Das Windows API verwaltet unter Klassen die Eigenschaften für Fenster.
    Es ist eine Vorlage dafür, mit welchen Einstellungen und Eigenschaften
    ein neues Fenster zu erstellen ist - um das Erzeugen gleichartiger Fenstern zu vereinfachen.

    Bei dem Klassennamen, den die Monarch Pro Anwendung verwendet, handelt
    es sich um einen Namen, der nur für die Applikation selbst von Bedeutung ist,
    also eine Application Global oder Local Class   siehe den Link
    den Thomas bereits angeführt hat: http://msdn.microsoft.com/en-us/library/ms633574%28VS.85%29.aspx#global

    Für die allgemeine Verwendung ist der Name nicht vorgesehen.
    Frameworks (z. B. MFC ) erzeugen solche Klassennamen dynamisch zur Laufzeit -
    einziges Kritierum ist dort, dass sie eindeutig innerhalb der Anwendung sind.
    (Und dem Aufbau sollte man nicht zuviel Bedeutung beimessen, der kann sich
    ändern, siehe z. B.: TN001: Window Class Registration )

    Gruß Elmar

    Mittwoch, 15. Dezember 2010 16:25
    Beantworter
  • Technisches Detail: Das "Afx:" am Anfang deutet auf eine Fremd-App mit C++/MFC gebaut (evtl AfxRegisterClass), enthält wohl Handles und andere Werte, welche pro Prozess/Start total anders sind.


    noch mehr technische 'Kuriositäten: gemäss MFC-Source
    entsteht "Afx:00400000:8:00010003:00000000:07490477"

    http://msdn.microsoft.com/en-us/library/a77269ff.aspx

    atlmfc\src\mfc\wincore.cpp ; AfxRegisterWndClass
    _sntprintf_s(..."Afx:%p:%x:%p:%p:%p"), hInst, nClassStyle, hCursor, hbrBackground, hIcon);

    Mittwoch, 15. Dezember 2010 16:28
  • Hallo Fred,

    der Klassenname ist nicht per se dynamisch (je nachdem, was damit gemeint ist), er kann je nach Applikation auch statisch (gleichbleibend) sein und durchaus immer benutzt werden. Das hängt aber eben davon ab, ob der Entwickler der Applikation das auch (offiziell) supported. Und das ist oft nicht der Fall. Auch der Source hilft einem wenig, wenn auf einmal ein Servicepack kommt, dass dann z.B. den Klassennamen ändert.
    Normal macht man dann als Workaround eine Mixtur/Heuristik aus Klassennamen und Windownamen, bzw. alles, was zur Verfügung steht, also auch Version aus der Registry etc. und bildet sich eben so seine Matrix.

    In modernen Apps gibt es oft öffentliche Automations-IDs für solche Zwecke, aber ein generelles immer funktionierendes Prinzip gibt es nicht, nur pragmatische Ansätze.


    ciao Frank
    Mittwoch, 15. Dezember 2010 16:53
  • Hallo Thomas,

        > Afx:%p:%x:%p ....

    sehe ich auch so, dass es die MFC-Bezeichnungs-Implementation von AfxRegisterWndClass ist, ... aber ich sehe auch, dass das eben je nach Parameter, der Inhalt anders sein kann.
    Allein - es fehlt der offizielle Support IMHO. Insofern würde ich (zumal es auch wiederum viele MFC-Implementationen gibt) hier eher pragmatische Herangehensweisen wählen. wie mit Spy++ den ClassName für die supporteten OS (nebst unterschiedlichen Bittigkeiten) prüfen und dann so implementieren - und ggf. mit anderen Prüf-Werten kombinieren.


    ciao Frank
    Mittwoch, 15. Dezember 2010 17:16
  • Hallo Elmar,

       > das hat mit der Anzahl der Bits nichts zu tun.

    indirekt kann das schon damit zusammenhängen, da 64 Bit-Applikationen ggf. andere (Window) Klassennamen wählen können und z.T. tun, insbesondere dann, wenn sie ein anderes Release (physisch eine andere Datei, als die 32 Bit Version) sind.

     


    ciao Frank
    Donnerstag, 16. Dezember 2010 07:03
  • Hallo Frank,

    wie Du auch dem Beitrag von Thomas entnehmen kannst, handelt es sich dabei um ein Implementationsdetail,
    was ich im weiteren Text wie auch Thomas mit TN001: Window Class Registration erläutere.

    Und dürfte Dirbekannt sein, dass man Handle keinerlei Bedeutung zumessen sollte.
    Und so spielt es keine Rolle dies nun 64-Bit anders realisiert wird als unter 32-Bit.

    Gruß Elmar

    Donnerstag, 16. Dezember 2010 09:14
    Beantworter
  • Elmar,

    Dein Posting hat mit dem, was ich gesagt habe, nichts zu tun und weiterhin mein Hinweis ebenfalls nichts mit Thomas.

    Zu 64 Bit-Applikationen nochmals:

    • indirekt kann das schon damit zusammenhängen, da 64 Bit-Applikationen ggf. andere (Window) Klassennamen wählen können und z.T. tun, insbesondere dann, wenn sie ein anderes Release (physisch eine andere Datei, als die 32 Bit Version) sind.  

    ciao Frank
    Donnerstag, 16. Dezember 2010 09:25
  • Hallo Frank,

    wenn das Deine Auffassung ist, so steht Dir das frei.

    Ich habe meiner Aussage nichts mehr hinzuzufügen.

    Gruß Elmar

    Donnerstag, 16. Dezember 2010 09:34
    Beantworter
  • Hallo Thomas, Stefan, Elmar und Frank,

    Vielen! Dank! für die vielen fachkundigen Antworten!!!!!

    Sie haben mir geholfen zu erkennen, dass es nicht so einfach ist, den statischen Klassen-Namen einer Fremd-Applikation zu bestimmen (womit ich ehrlich gesagt nicht gerechnet hätte).

    Der Ausgangspunkt war/ist jedoch: "Ziel des ganzen ist es, ein Windows-Handle eines Fensters einer fremden Applikation zu ermitteln."

    Ich habe es jetzt so gelöst, dass ich alle Windows-Prozesse der Maschiene durchlaufe und anhand des Prozess-Namens und der Start-Zeit der Fremd-Applikation das benötigte Windows-Handle bestimme.

    Nochmals vielen Dank für eure Hilfe!

    Fred.

            /// <summary>
            /// Ermittelt aus allen, auf der Maschiene laufenden Prozessen, das Fenster-Handle des Prozesses, dass den angegebenen Prozess-Namen hat und in der vorgegebenen Zeitspanne gestartet wurde.
            /// </summary>
            /// <param name="zProzessName">Prozess-Name</param>
            /// <param name="StartZeitDif">Zeitspanne, seit dem der Prozess gestartet wurde. 0 = unendlich</param>
            /// <returns>Fenster-Handle des gesuchten Prozesses.</returns>
            public static System.IntPtr FindWinFenster(string zProzessName, TimeSpan StartZeitDif)
            {
                System.IntPtr pWinHandle = new IntPtr();                                                    // Windows-Handle des gesuchten Fenster's
                System.Diagnostics.Process[] aktProzesse = System.Diagnostics.Process.GetProcesses();       // "Alle" Prozesse die akt. auf der Maschine laufen einlesen
                foreach (System.Diagnostics.Process aktProzess in aktProzesse)                              // Alle Prozesse durchgehen
                {
                    if (aktProzess.ProcessName == zProzessName)                                             // Prozess mit dem gesuchten Prozess-Name vorhanden?
                    {
                        if ((DateTime.Now - aktProzess.StartTime <= StartZeitDif) || (StartZeitDif == new TimeSpan(0,0,0)))
                            pWinHandle = aktProzess.MainWindowHandle;                                       // Wenn die Startzeit des gesuchten Prozesses innerhalb der angegebenen Zeitspanne erfolgt ist, Windows-Handle übergeben.
                    }
                }
                return pWinHandle;   
            }

    Donnerstag, 16. Dezember 2010 09:40
  • Hallo Fred,

    ja, u.a. die StartTime mit einzubeziehen, habe ich auch schon in einigen pragmatischen ähnlichen Situationen so umgesetzt und kann durchaus helfen.
    Natürlich sind Prozessnamen nicht 100% akkurat, da ja auch andere Prozesse einen gleichen Namen haben können, dieses allerdings durch eine geringe Wahrscheinlichkeit ggf. in Kauf genommen werden kann.
    Aber wenn, es jetzt für Deine Szenario in 64 Bit und 32 Bit Umgebungen funktioniert, freut es mich, dass Du mit diesem "Workaround" zum Ziel kommst. Wie gesagt - ein generelles immer funktionierendes Prinzip gibt es hier ja nicht, nur pragmatische Ansätze.


    ciao Frank
    Donnerstag, 16. Dezember 2010 10:21