2013-05-03 20 views
8

Durante la cattura manualmente una finestra con il Print Screen + Alt combinazione di tasti, ottengo il seguente:bitmap PrintWindow differisce da PrintScreen bitmap Key

enter image description here

ma se cerco di farlo a livello di codice utilizzando Windows API, ottengo questo:

enter image description here

Perché la discrepanza? Come ottengo il primo al livello di programmazione?

Ecco il mio codice:

[DllImport("user32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags); 

    public Bitmap PrintWindow() 
    { 
     Bitmap bmp = new Bitmap(windowRect.Width, windowRect.Height, PixelFormat.Format32bppArgb); 
     Graphics gfxBmp = Graphics.FromImage(bmp); 
     IntPtr hdcBitmap = gfxBmp.GetHdc(); 

     bool success = PrintWindow(windowHandle, hdcBitmap, 0); 
     gfxBmp.ReleaseHdc(hdcBitmap); 

     if (!success) 
     { 
      Console.WriteLine("Error copying image"); 
      Console.WriteLine(getLastError()); 
     } 

     gfxBmp.Dispose(); 

     return bmp; 
    } 

Aggiornamento: Farlo con BitBlt fa la stessa cosa.

Ecco code from CodeProject che restituisce ancora un'immagine in bianco mascherata:

public Image CaptureWindow(IntPtr handle) 
{ 
    // get te hDC of the target window 
    IntPtr hdcSrc = User32.GetWindowDC(handle); 
    // get the size 
    User32.RECT windowRect = new User32.RECT(); 
    User32.GetWindowRect(handle,ref windowRect); 
    int width = windowRect.right - windowRect.left; 
    int height = windowRect.bottom - windowRect.top; 
    // create a device context we can copy to 
    IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc); 
    // create a bitmap we can copy it to, 
    // using GetDeviceCaps to get the width/height 
    IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc,width,height); 
    // select the bitmap object 
    IntPtr hOld = GDI32.SelectObject(hdcDest,hBitmap); 
    // bitblt over 
    GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt); 
    // restore selection 
    GDI32.SelectObject(hdcDest,hOld); 
    // clean up 
    GDI32.DeleteDC(hdcDest); 
    User32.ReleaseDC(handle,hdcSrc); 
    // get a .NET image object for it 
    Image img = Image.FromHbitmap(hBitmap); 
    // free up the Bitmap object 
    GDI32.DeleteObject(hBitmap); 

    img.Save("SampleImage.png"); 
    return img; 
} 

Ho provato molte combinazioni di CopyPixelOperation, (da qualche parte circa 15.000 del 131.000), ma ancora non funziona.

Utilizzo di Windows 8, AMD Radeon HD 6870.


Update 2

Sembra che la finestra è trasparente, permettendo il colore blu della finestra per sanguinare attraverso. Quando cambio il colore della finestra in nero (usando la finestra di dialogo di personalizzazione di Windows), ottengo qualcosa di simile alla seconda finestra. Il confine manca ancora.

Non ho trovato una soluzione, ma è la comprensione del problema.

+0

Quando hai chiamato 'PrintWindow()', come hai prodotto la DC che hai inviato alla chiamata API? – WhozCraig

+0

aggiunto codice al corpo. – Jason

+0

Cosa ottieni se usi 'PixelFormat.Format24bppRgb'? –

risposta

1

"Perché la discrepanza? Come ottengo il primo a livello di programmazione?"

BitBlt vs PrintWindow

+0

Anche io ho provato BitBlt ma è lo stesso problema. Vedi codice aggiornato in risposta – Jason

+0

Prova BitBlt con GetDesktopWindow come finestra di destinazione, - non so su win8, ma win7 consente in questo modo. – kero

3

La ragione immagine del Alt+PrntScrn sembra diverso è perché non è in realtà uno snapshot della finestra selezionata - sta prendendo una fotografia di qualcosa di simile alla finestra del desktop, ma ritaglio la parte appropriata .

Per provarlo, prendi una finestra sempre in primo piano come il Task Manager e posizionala in modo che copra la finestra che stai catturando. Vedrai che in realtà include entrambe le finestre nell'istantanea. Considerando che una chiamata a PrintWindow restituirà solo la finestra specificata da solo.

Quindi, se si desidera emulare esattamente Alt+PrntScrn, è necessario BitBlt dal desktop. Qualcosa del genere:

IntPtr hDesktop = User32.GetDesktopWindow(); 
IntPtr hdcSrc = User32.GetWindowDC(hDesktop); 
GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, windowRect.left, windowRect.top, CopyPixelOperation.SourceCopy); 

Sto indovinando la sintassi in base al codice di esempio.Le chiamate API di Windows non elaborate sarebbero le seguenti:

HWND hDesktop = GetDesktopWindow(); 
HDC hdcSrc = GetDC(hDesktop); 
BitBlt(hdcDest, 0, 0, width, height, hdcSrc, windowRect.left, windowRect.top, SRCCOPY); 

Almeno questo funziona per me. Se hai problemi a capire il resto da qui, fammi sapere nei commenti.

8

Il motivo per cui PrintWindow non funziona dipende dalla corretta gestione del messaggio WM_PRINT dall'app. Un sacco di app sono traballanti con WM_PRINT e non lo implementano correttamente, né mai lo testano. È quindi una cattiva idea affidarsi a questo, a meno che non lo si usi solo su app conosciute e testate.

Se si desidera afferrare la finestra man mano che appare sullo schermo, basta estrarla dalla maniglia della finestra del desktop (GetDesktopWindow()) e solo il rect che include la finestra.

La trasparenza è un problema con l'acquisizione di finestre. È impossibile catturare le finestre dei moderni sistemi operativi Windows, perché non esiste un tipo di file immagine che supporti la sfocatura come la sfocatura delle immagini sottostanti. Tuttavia, è possibile acquisire una trasparenza semplice in un file PNG.

Prestare attenzione nell'ipotizzare che una finestra abbia l'aspetto migliore vista sullo schermo. A seconda di ciò che è sotto, questo potrebbe non essere vero. Potrebbe anche essere una cattiva idea copiare qualsiasi cosa ci sia sotto perché potrebbe non essere sanzionata per apparire nell'immagine, come un'immagine di sfondo esplicita che appare in una presentazione aziendale sotto uno screenshot di Excel :).

Se si elimina dal desktop, si copia ciò che si vede sullo schermo, comprese le finestre di sovrapposizione e le finestre sottostanti (dove trasparente). Solitamente viene considerato "più pulito" solo per afferrare la finestra, come con PrintWindow, ma è consigliabile comporlo su uno sfondo a tua scelta, come bianco o blu. Se vuoi togliere dallo schermo, ci sono modi per nascondere temporaneamente le finestre che si sovrappongono al tuo obiettivo, ma è un po 'di lavoro con EnumWindows e così via.

Le finestre di aggancio correttamente non è un compito facile in Windows e ci sono molte app di acquisizione dello schermo che competono tra loro in virtù di quanto bene si occupano esattamente di questo problema. Inoltre, in Windows ci sono diversi modi per rendere trasparenti le aree di Windows e possono anche essere usate insieme.

Inoltre, in Vista +, c'è lo DWM thumbnail API che consente di ottenere una copia della finestra di un'applicazione disegnata sulla finestra. Per una demo e una fonte, vedere ShareX (precedentemente zScreen).

Infine, è possibile esaminare la sorgente di Open Broadcaster Software, che utilizza Direct3D per eseguire l'acquisizione di schermate.

+0

Hai lavorato con DWM? sai se è possibile disegnare su un buffer anziché su una finestra? – Jason

+0

Non puoi fare nulla oltre a ciò che la funzione ti permette di fare, quindi questo è un no. Vedi il collegamento zScreen per l'uso. Un ultimo modo (Vista +) per afferrare lo schermo è quello di agganciarsi a D3D e afferrarlo direttamente dal sistema grafico. Se questo sembra strano e pericoloso, dovresti sapere che [Fraps] (http: //www.fraps.it /) utilizza questo sistema e può catturare schermate abbastanza bene (120 fps!). – DDS

+0

DWM ha funzionato come un fascino ed è stato molto semplice da implementare. – KuramaYoko