Screenshot bestimmtes Fenster



  • Moin,

    ich habe mir den Screenshot in der FAQ angesehen und ausprobiert. Jetzt habe ich folgende Frage. Bisher arbeite ich um einen bestimmten Bereich auszuschneiden mit absolut Koordinaten des gesamten Bidlschirmes.
    Ist es möglich von einem bestimmten Fenster eines Programmes einen bestimmten Bereich auszuschneiden.
    Also zum Beispiel von einem laufenden Programm die rechte untere Ecke. Dabei soll unabhängig an welcher Stelle sich das Fenster des anderen Programmes befindet immer die gleiche Stelle ausgeschnitten werden.

    Ich habe mir überlegt ob dies mit relativen Koordinaten zu machen ist, d.h. die Position des Fensters auslesen und dann in die jeweiligen Koordinaten umzurechnen. Ich weiss aber nicht ob dies möglich ist.

    Hat jemand eine Idee ??

    FGGF



  • HI,
    schau dir mal die WinAPI Funktion ScreenToClient an:

    BOOL ScreenToClient(

    HWND hWnd, // window handle for source coordinates
    LPPOINT lpPoint // address of structure containing coordinates
    );

    Dürfte genau das sein, was du suchst!

    MfG

    Alexander Sulfrian

    PS: Um das Handle zu bekommen kannst du dir die Funktion FindWindow ansehen!



  • Du kannst dir ja sogar die Handles der einzelnen Controls, also z.B. der StatusBar des Zielfensters besorgen.
    Das alles lässt sich aber nur per WinAPI lösen, verschoben dorthin.



  • Willst du jetzt den Screenshot eines bestimmten Fensters machen, oder was genau 😕
    Dann kannst du ja einfach bei GetDC direkt das entsprechende Fenster-Handle angeben 🙄



  • Servus,

    ich will aus einem anderen Fenster einen bestimmten Bereich als Bild abspeichern.

    Also hab ich gedacht, ich mache nen Screenshot mit den entsprechende Koordinaten. Allerdings ist das Fenster immer woanders. Deswegen muss ich die Koordinaten des jeweiligen Programmfensters umrechnen lassen. Allerdings habe ich es bis jetzt nie mit Win-API zu tun gehabt und habe daher recht wenig Ahnung davon.
    Soweit ich jetzt weiss (aus den oberen Posts, FAQ, Hilfe) muss ich mir also das Handle auf das Programmfenster holen. Danach sind die Koordinaten von linken oberen Rand des Programmfensters ja konstant.

    Kann mir da jemand bisschen helfen oder mir nen Tritt in die richtige Richtung geben ??

    FGGF

    PS: Totaler N00b bei WIN-API.



  • Also ca. so

    int bmpFarbe(HDC hdc, int width, int height,int swidth,int sheight, char *filename)		//das eigendliche erstellen der Bitmap
    {	
    	HDC hdc2;
    	HBITMAP aBmp;
    	BITMAPINFO bi;
    	HGDIOBJ OldObj;
    	void *dibvalues;
    	HANDLE fileHandle;
    
    	BITMAPFILEHEADER bmfh;
    	BITMAPINFOHEADER bmih;
    	DWORD bytes_write;
    	DWORD bytes_written;
    
    	hdc2=CreateCompatibleDC(hdc);
    
    	ZeroMemory(&bmih,sizeof(BITMAPINFOHEADER));
    	bmih.biSize=sizeof(BITMAPINFOHEADER);
    	bmih.biHeight=height-sheight;
    	bmih.biWidth=width-swidth;
    	bmih.biPlanes=1;
    	bmih.biBitCount=24; //24
    	bmih.biCompression=BI_RGB;
    	bmih.biSizeImage = ((((bmih.biWidth * bmih.biBitCount) + 31) & ~31) >> 3) * bmih.biHeight;
    	bmih.biXPelsPerMeter = 0;
    	bmih.biYPelsPerMeter = 0;
    	bmih.biClrImportant = 0;
    
    	bi.bmiHeader=bmih;
    
    	aBmp=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&dibvalues,NULL,NULL);
    
    	if (aBmp==NULL)
    	{
    		OutputDebugString("CreateDIBSection failed!\n");
    		return 0;
    	}
    
    	OldObj=SelectObject(hdc2,aBmp);
    	if (!invertieren) { BitBlt(hdc2,0,0,width-swidth,height-sheight,hdc,swidth,sheight,SRCCOPY);}
    	else {BitBlt(hdc2,0,0,width-swidth,height-sheight,hdc,swidth,sheight,NOTSRCCOPY);}
    
    	ZeroMemory(&bmfh,sizeof(BITMAPFILEHEADER));
    	bmfh.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    	bmfh.bfSize=(3*bmih.biHeight*bmih.biWidth)+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    	bmfh.bfType=0x4d42;
    	bmfh.bfReserved1 = 0;
    	bmfh.bfReserved2 = 0;
    
    	fileHandle=CreateFile(filename,GENERIC_READ | GENERIC_WRITE,(DWORD)0,NULL,
    		CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,(HANDLE) NULL);
    	if (fileHandle==INVALID_HANDLE_VALUE)
    	{
    		OutputDebugString("CreateFile failed!\n");
    		return 0;
    	}
    
    	// Write the BITMAPFILEHEADER
    	bytes_write=sizeof(BITMAPFILEHEADER);
    	if (!WriteFile(fileHandle,(void*)&bmfh,bytes_write,&bytes_written,NULL))
    	{
    		OutputDebugString("WriteFile failed!\n");
    		return 0;
    	}
    
    	//Write the BITMAPINFOHEADER
    	bytes_write=sizeof(BITMAPINFOHEADER);
    	if (!WriteFile(fileHandle,(void*)&bmih,bytes_write,&bytes_written,NULL))
    	{
    		OutputDebugString("WriteFile failed!\n");
    		return 0;
    	}
    
    	//Write the Color Index Array
    	bytes_write=bmih.biSizeImage;
    	if (!WriteFile(fileHandle,(void*)dibvalues,bytes_write,&bytes_written,NULL))
    	{
    		OutputDebugString("WriteFile failed!\n");
    		return 0;
    	}
    
    	CloseHandle(fileHandle);
    
    	DeleteObject(SelectObject(hdc2,OldObj));
    	DeleteDC(hdc2);
    
    	return 1;
    }
    
    RECT rect;
    hdc=GetWindowDC(GetForegroundWindow());
    GetWindowRect(GetForegroundWindow(), &rect);
    bmpFarbe(hdc,rect.right-rect.left,rect.bottom-rect.top,0,0,"c:\Beispiel.bmp");
    

    Habs jetzt nicht getestet!



  • Achso,

    und mit rect.right-rect.left,rect.bottom-rect.top kannst du den Bereich natürlich noch weiter einschränken bezogen auf das aktive Fenster!



  • Danke erstmal für deine Hilfe.

    Also jetzt bitte nicht lachen wenn ich dumme Fragen stelle. Ich kapiers wirklich net so ganz.

    Okay der Funktionsaufruf ist soweit klar. Allerdings bekomme ich eine Fehlermeldung bei

    hdc=GetWindowDC(GetForegroundWindow());
    

    als hdc= undefiniertes Symbol.

    Woran liegt das ??

    Mir würde es ja schon reichen wenn der den Teil-Screenshot in einem Image auf der Form darstellt.

    Also habe ich noch folgende Frage hierzu:
    Wie bekomme ich das Handle des Fensters ? Oder vielmehr was stellt das hdc dar ?
    Und wenn ich das Handle des Fensters habe, würde die Koordinatentransformation auch mit ScreenToClient machbar sein ? So dass ich dann beim Screenshot nur die relativen Koordinaten eintragen muss.
    Oder habe ich vergessen etwas einzubinden ??

    Sorry für die, für euch möglicherweise dummen Fragen.

    FGGF



  • Du benutzt den CBuilder? No Problem.

    // Diese Funktion speichert das Rechteck rc des Fensters hwnd.
    // Die Koordinaten von rc sind relativ zur linken oberen Ecke
    // des Fensters hwnd
    void SaveScreenshot(HWND hwnd, RECT rc, AnsiString FileName)
    {
       Graphics::TBitmap* bmp = new Graphics::TBitmap;
       TCanvas* canvas = new TCanvas;
       RECT rcBmp;
    
       bmp->Width = rc.right - rc.left;
       bmp->Height = rc.bottom - rc.top;
    
       canvas->Handle = GetWindowDC(hwnd);
       SetRect(&rcBmp, 0, 0, bmp->Width, bmp->Height);
       bmp->Canvas->CopyRect(rcBmp, canvas, rc);
       bmp->SaveToFile(FileName);
    
       delete bmp;
       delete canvas;
    }
    

    Das Fensterhandle (hwnd) kannst du dir z.B. über FindWindow() holen. Über einen einfachen Buttonklick geht z.B. sowas:

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
       RECT rc = (RECT)Rect(0, 0, 200, 200);
       SaveScreenshot(Handle, rc, "C:\\Hallo.bmp");
    }
    


  • Servus,

    thx @ WebFritzi funzt einwandfrei.

    Allerdings schaffe ich es nicht mir das Handle eines anderen Fensters zu holen.
    Der Screenshot wird nach dem ButtonClick immer vom eigenen Fenster gemacht.

    Ich habe jetzt mal mit FindWindow rumprobiert. Allerdings ist der Name im Taskmanager anders wie in der Taskbar. Wie kriege ich es hin, dass er das richtige Fenster wählt ??

    Thx

    FGGF



  • Also, du solltest das so deklarieren:

    HDC hdc;

    Das hdc stellt eine Moeglichkeit dar,auf das Fenster zu zeichnen bzw die
    Farbwerte auszulesen.(ganz grob und ungenau gesagt)

    Du hast mit rect.left rect.right rect.top. rect.bottom die Koordinaten des
    aktiven Fenstern.(links rechts oben unten)

    Du kannst diesen Bereich einschraenken.
    z.B.mit rect.left=rect.left-5; oder rect.left=recht.left+5;
    (minus oder plus vergroesssern oder verkleinerd den Bereich)

    Muste halt mit den Werten experimentieren und sehen was bei Veraenderung passiert.



  • Okay thx @ Andreas XXL

    Ich hab noch bisschen gegoogelt dazu und habs ganz grob verstanden (hoffe ich jedenfalls)-

    Jetzt hab eich noch ein kleines Problem. Wie kriege ich das Handle auf ein bestimmtes Fenster einer anderen Anwednung. Ich habs mit FindWindow probiert, aber irgendwie klappt das nicht so richtig. Entweder wird immer das aktive Fenster gewählt, also das eigene Programm oder der Hauptbildschirm.

    Woran liegt das ??

    FGGF



  • Du könntest temporär das gewünschte Fenster TOPMOST setzen (mit SetWindowPos z. B.). Wahrscheinlich überdeckt Dein eigenes Fenster grade den gewünschten Bereich?



  • Mmmhhhh,

    aber wie geht das, ich blick bei WinApi net so recht durch. Gibt es da keinen einfachen Befehl um ein bestimmtes Fenster in den Vordergrund zu bringen ??

    FGGF

    Hab auch schon gesucht, aber irgendwie bin ich zu blöd dies nachzuvollziehen.
    Also FindWindow, SetForegroundWindow usw. habe da Probleme mit den Paramtern.



  • Also solange du es noch nicht einmal hinbekommen hast mit FindWindow dein Fester zu finden brauchst du dir um den Rest doch mal noch gar keine Sorgen machen 🤡 - wie rufst du FindWindow denn genau aus (da wird dann wohl ein Fehler drin sein 🙄 )



  • Du musst nur wissen, was in der Titelleiste des Fensters steht. Sagen wir, da steht "Titelleistentext". Dann bekommst du das Handle wie folgt:

    HWND hwnd = FindWindow(NULL, TEXT("Titelleistentext"));
    


  • Ich glaub es ist besser ich poste mal etwas Code.

    void __fastcall TForm1::BitBtn4Click(TObject *Sender)
    {
    
      RECT rc = (RECT)Rect(0, 0, 280, 90);
    
      HWND hwnd = ::FindWindow(NULL,"Mirc");
      if(hwnd)
      {
        ::PostMessage(hwnd, WM_WINDOWPOSCHANGING,::SetWindowPos(hwnd,NULL,1,1,NULL,NULL,SWP_NOSIZE),1);
      }
      else
      {
        ShowMessage("Fehler");
      }
    
      SaveScreenshot(Handle, rc, "d:\\bild.bmp");
    }
    

    Diese Funktion bringt das Fenster in den Vordergrund.

    void TForm1::SaveScreenshot(HWND hwnd, RECT rc, AnsiString FileName)
    { 
       Graphics::TBitmap* bmp = new Graphics::TBitmap; 
       TCanvas* canvas = new TCanvas; 
       RECT rcBmp; 
    
       bmp->Width = rc.right - rc.left; 
       bmp->Height = rc.bottom - rc.top; 
    
       canvas->Handle = GetWindowDC(hwnd); 
       SetRect(&rcBmp, 0, 0, bmp->Width, bmp->Height); 
       bmp->Canvas->CopyRect(rcBmp, canvas, rc); 
       bmp->SaveToFile(FileName); 
    
       delete bmp; 
       delete canvas; 
    }
    

    Danach soll die 2. Funktion einen bestimmten Bereich des Fensters als Screenshot in einer Bilddatei speichern. Ich habe verschiedene Dinge ausprobiert.Das einzige was passiert ist entweder vom gesamten Bildschirm ein Screenshot, oder vom eigenen C++ Programm.

    Was mache ich falsch ??

    FGGF



  • SetForegroundWindow()



  • Okay, Leute ich hab meinen Fehler gefunden. Ist wirklich ein saublöder Fehler gewesen. 😡 😡 😡

    Hatte den falschen String in FindWindow(). Mein Suchtext stand im Taskmanager, aber nicht im anderen Fenster. Jetzt klappts auf einmal hervorragend.
    Mit SetFore.. und Move und alle dum und dran.

    Auf jeden Fall lässt sich feststellen, dass ich durch meinen Fehler sehr viel neues über WinApi erfahren habe. Also thx an alle die mir auf meinem Weg geholfen haben.

    FGGF


Log in to reply