Veränderung im Fenster registrieren

An GetPixel() wirst du nicht viel Freude haben: Diese Funktion ist unerträglich langsam.
Besser ist es, mit CDC::GetBitmap(), ::GetObject() oder ::GetDIBits() direkt den Bitmap-Speicher zu holen und dann jeweils mit memcmp() vergleichen oder mit der von dir angedachten Hash-Funktion zu arbeiten.

Bei einer Größenveränderung bist du mit einem Pixelvergleich relativ chancenlos. Da ist es am besten, die WM_SIZE Messages des Fensters mit zu überwachen und bei Änderungen sofort deine Vergleichsdaten zu aktualisieren.
 
Hallo C Coder und MCoder,

danke für eure Antworten.
Ich könnte also einen Hook auf die WM_SIZE-Messages des Fensters absenden und würde bei einer Größenveränderung durch den User, die Informationen neu abfragen (dasselbe müsste ich wohl auch machen, falls das Fenster durch den User aktiviert wird, da dann ja der Fenstertexthintergrund dunkelblau dargestellt wird).

@MCoder:
Danke für deine Info, auf einigen Seiten wird ebenfalls von GetPixel aufgrund der Geschwindigkeit abgeraten.

Ich habe nun mit der Funktion:
::GetDIBits(hCaptureDC, hCaptureBitmap, 0, 0, 0, &bminfo, DIB_RGB_COLORS);
gearbeitet und Zugriff auf eine BITMAPINFO-Struktur.

Wie kann ich mit der von dir angesprochenen Funktion memcmp vergleichen ob sich etwas gegenüber dem alten Screenshot verändert hat (welchen Parameter der Struktur übergebe ich der Funktion)? :confused:
 
GetDIBits ist etwas tricky. Du musst erst die Eigenschaften des Bitmaps ermitteln, den BITMAPINFOHEADER damit füttern und den Speichbereich für die Bilddaten reservieren, der als 5. Parameter (lpvBits) an GetDIBits() übergeben wird.

Das könnte in etwa so aussehen:
Code:
// Eingabewerte

HBITMAP hbm;   // Bitmap 
HDC     hdc;   // HDC

// GetDIBits verwenden

BITMAP bm;
::GetObject(hbm, sizeof(bm) &bm); // Bitmap-Eigenschaften ermitteln

BITMAPINFO bmi;

bmi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth        = bm.bmWidth;
bmi.bmiHeader.biHeight       = bm.bmHeight;
bmi.bmiHeader.biBitCount     = bm.bmBitsPixel;
bmi.bmiHeader.biPlanes       = bm.bmPlanes;
bmi.bmiHeader.biCompression  = BI_RGB;
bmi.bmiHeader.biSizeImage    = bm.bmBitsPixel * bm.bmWidth * bm.bmHeight / 8;

BYTE *pbyteBitmapBits = new BYTE[bmi.bmiHeader.biSizeImage];

::GetDIBits( hdc, 
             hbm,
             0,
             bmi.bmiHeader.biHeight,
             pbyteBitmapBits,
             &bmi,
             DIB_RGB_COLORS );

// ....

delete pbyteBitmapBits;
 
Hallo MCoder, danke für den ausführlichen Code.

hbm entspricht nun dem HBITMAP meines Screenshots und hdc dem dazugehörigen HDC.

pbyteBitmapBits enthält nun die benötigte Information zum Bildvergleich, oder?
Nun wollte ich daraus mit dem folgenden Code (dort eingefügt, wo // .... steht) einen Hashwert generieren:

UINT hash = HashKey(pbyteBitmapBits);

Wenn sich der Fensterinhalt verändert hat und ich den ermittelten Hashwert mit dem zuvor gespeicherten Hashwert vergleiche, handelt es sich jeweils um den selben Wert.
Wo liegt der Fehler am Code, so dass ich tatsächlich eine Veränderung im Fenster registrieren kann? :confused:
 
Der Code sollte eigentlich ok sein.
Wie generierst du denn den Screenshot? Evt. geht dabei etwas schief. Hast du dir das Bitmap mal testweise anzeigen lassen?
 
Hi MCoder,

das Bitmap habe ich mir bisher noch nicht anzeigen lassen, da ich davon ausgegangen bin, dass das erfolgreiche Ausführen von BitBlt auch das korrekte Bild enthält.
Hier mal der vollständige Code:

Code:
// ScreenshotFenster = HWND des gewünschten Fensters
HDC hFensterDC = ::GetDC(ScreenshotFenster);
HDC hCaptureDC = CreateCompatibleDC(hFensterDC);
	
// Größe des Fensters holen
CRect CFensterRect;
::GetClientRect(ScreenshotFenster, &CFensterRect);

HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hFensterDC, CFensterRect.Width(), CFensterRect.Height());

SelectObject(hCaptureDC, hCaptureBitmap); 
if (BitBlt(hCaptureDC, 0, 0, CFensterRect.Width(), CFensterRect.Height(), hFensterDC, 0, 0, SRCCOPY) != 0)
{
// GetDIBits verwenden
BITMAP bm;
GetObject(hCaptureBitmap, sizeof(BITMAP), &bm); // Bitmap-Eigenschaften ermitteln

BITMAPINFO bmi;

bmi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth        = bm.bmWidth;
bmi.bmiHeader.biHeight       = bm.bmHeight;
bmi.bmiHeader.biBitCount     = bm.bmBitsPixel;
bmi.bmiHeader.biPlanes       = bm.bmPlanes;
bmi.bmiHeader.biCompression  = BI_RGB;
bmi.bmiHeader.biSizeImage    = bm.bmBitsPixel * bm.bmWidth * bm.bmHeight / 8;

BYTE *pbyteBitmapBits = new BYTE[bmi.bmiHeader.biSizeImage];

::GetDIBits(hCaptureDC, hCaptureBitmap, 0, bmi.bmiHeader.biHeight, pbyteBitmapBits, &bmi, DIB_RGB_COLORS );

UINT hash = HashKey(pbyteBitmapBits); 

delete pbyteBitmapBits;
}

Ist das Erstellen des Screenshots fehlerbehaftet? :confused:
 
Zuletzt bearbeitet:
Hmm, schaut eigentlich prima aus.
Setze mal feste Werte für biBitCount und biPlanes ein, möglicherweise gibt es da sonst Probleme.
Code:
bmi.bmiHeader.biBitCount     = 24;
bmi.bmiHeader.biPlanes       = 1;

Ansonsten mal pbyteBitmapBits mit irgendwas initialisieren mal schauen, ob die Bytes unterschiedlich belegt werden. Und natürlich auch mal den Rückgabewert von GetDIBits() prüfen. Das hatte ich in meinem Beispiel glatt unterschlagen :).
 
Hey MCoder,

ich habe jetzt mal die von dir angesprochenen Schritte ausgeführt:

1. Der Screenshot wird korrekt erstellt
2. Das Setzen der beiden Werte bmi.bmiHeader.biBitCount und bmi.bmiHeader.biPlanes scheint keine Veränderung herbeizuführen
3. GetDIBits() läuft nicht auf Fehler
4. Nach Initialisierung von pbyteBitmapBits mit einem beliebigen Wert scheint dieser durch die Funktion GetDIBits nicht neu gesetzt zu werden, da der initialisierte Wert anscheinend beibehalten wird :(
Egal welches HWND ich übergebe, der Hashwert bleibt stets derselbe.

Woran könnte das liegen und wie kann ich den Fehler beheben? :confused:
 
Also, ich hab's mal schnell ausprobiert. Bei mir funktionierts, d.h. der Buffer wird gefüllt und enthält bei Änderungen auch andere Inhalte (habe ich mal über einen Speichervergleich getestet).
Code:
HWND hWnd = ::FindWindow(NULL, _T("Unbenannt - Paint"));

if( hWnd )
{
    HDC hFensterDC = ::GetDC(hWnd);
    HDC hCaptureDC = ::CreateCompatibleDC(hFensterDC);
    
    CRect fensterRect;
    ::GetClientRect(hWnd, &fensterRect); // Größe des Fensters holen

    HBITMAP hCaptureBitmap = ::CreateCompatibleBitmap( hFensterDC,
                                                       fensterRect.Width(),
                                                       fensterRect.Height() );

    HBITMAP hbmOld = (HBITMAP)::SelectObject(hCaptureDC, hCaptureBitmap); 

    if( ::BitBlt( hCaptureDC,
                  0,
                  0,
                  fensterRect.Width(),
                  fensterRect.Height(),
                  hFensterDC,
                  0,
                  0,
                  SRCCOPY ) )
    {
        // GetDIBits verwenden

        BITMAP bm;
        ::GetObject(hCaptureBitmap, sizeof(BITMAP), &bm); // Bitmap-Eigenschaften ermitteln

        BITMAPINFO bmi;

        bmi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
        bmi.bmiHeader.biWidth        = bm.bmWidth;
        bmi.bmiHeader.biHeight       = bm.bmHeight;
        bmi.bmiHeader.biBitCount     = 24;
        bmi.bmiHeader.biPlanes       = 1;
        bmi.bmiHeader.biCompression  = BI_RGB;
        bmi.bmiHeader.biSizeImage    = bm.bmBitsPixel * bm.bmWidth * bm.bmHeight / 8;

        BYTE *pbBuffer = new BYTE[bmi.bmiHeader.biSizeImage];

        if( ::GetDIBits( hCaptureDC,
                         hCaptureBitmap,
                         0,
                         bmi.bmiHeader.biHeight,
                         pbBuffer,
                         &bmi,
                         DIB_RGB_COLORS ) )
        {
            // ...
        }

        delete pbBuffer;
    }

    // Aufräumen
    ::SelectObject(hCaptureDC, hbmOld); 
    ::DeleteDC(hCaptureDC);
    ::ReleaseDC(hWnd, hFensterDC);
    ::DeleteObject(hCaptureBitmap);
}
 
Hey MCoder,

danke, dass du das noch einmal getestet hast.
Also liegt es definitiv an der Hashfunktion.

Ich habe probiert eine weitere Klasse zu nutzen, um den Hashwert zu generieren (CMD5Checksum).
Aber auch hier bekomme ich ständig denselben Hashwert, ob nun eine Veränderung statt findet oder nicht, wenn ich dieser den aktuellen pbBuffer übergebe.

Kannst du mir da noch einen Tip geben wie ich einen gültigen Hashwert erzeuge, damit ich nicht pbBuffer für einen Vergleich nicht im Speicher behalten muss? :confused:
 
Zurück