2010-03-12 3 views
22

Sto cercando di acquisire il contenuto del controllo WebBrowser. DrawToBitmap() funzionerebbe perfettamente, ma non è supportato nella documentazione per il controllo WebBrowser. Ho cercato di trovare un altro modo per acquisire i contenuti del controllo WebBrowser e salvarli in un file immagine locale.WebBrowser.DrawToBitmap() o altri metodi?

Qualcuno ha qualche soluzione alternativa o altri metodi per salvare il contenuto del controllo WebBrowser in un file immagine locale?

+0

A q correlato/a: [Come correggere un bug di opacità con DrawToBitmap su Controllo WebBrowser?] (http://stackoverflow.com/q/21697048/1768303) – Noseratio

+0

[In ritardo ma forse di interesse] (http://stackoverflow.com/questions/24343393/webbrowser-madness?noredirect=1#comment37647172_24343393) - La soluzione alla fine è semplice e sembra affidabile. – TaW

risposta

36

Il Control.DrawToBitmap non funziona sempre così ho fatto ricorso alle seguenti chiamate API native che forniscono risultati più coerenti:

classe I Programmi di utilità. Chiamata Utilities.CaptureWindow (Control.Handle) per catturare un controllo specifico:

public static class Utilities 
{ 
    public static Image CaptureScreen() 
    { 
     return CaptureWindow(User32.GetDesktopWindow()); 
    } 

    public static Image CaptureWindow(IntPtr handle) 
    { 

     IntPtr hdcSrc = User32.GetWindowDC(handle); 

     RECT windowRect = new RECT(); 
     User32.GetWindowRect(handle, ref windowRect); 

     int width = windowRect.right - windowRect.left; 
     int height = windowRect.bottom - windowRect.top; 

     IntPtr hdcDest = Gdi32.CreateCompatibleDC(hdcSrc); 
     IntPtr hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, width, height); 

     IntPtr hOld = Gdi32.SelectObject(hdcDest, hBitmap); 
     Gdi32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, ApiConstants.SRCCOPY); 
     Gdi32.SelectObject(hdcDest, hOld); 
     Gdi32.DeleteDC(hdcDest); 
     User32.ReleaseDC(handle, hdcSrc); 

     Image image = Image.FromHbitmap(hBitmap); 
     Gdi32.DeleteObject(hBitmap); 

     return image; 
    } 
} 

La classe gdi32:

public class Gdi32 
{ 
    [DllImport("gdi32.dll")] 
    public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int nYSrc, int dwRop); 
    [DllImport("gdi32.dll")] 
    public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight); 
    [DllImport("gdi32.dll")] 
    public static extern IntPtr CreateCompatibleDC(IntPtr hDC); 
    [DllImport("gdi32.dll")] 
    public static extern bool DeleteDC(IntPtr hDC); 
    [DllImport("gdi32.dll")] 
    public static extern bool DeleteObject(IntPtr hObject); 
    [DllImport("gdi32.dll")] 
    public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); 
} 

La classe User32:

public static class User32 
{ 
    [DllImport("user32.dll")] 
    public static extern IntPtr GetDesktopWindow(); 
    [DllImport("user32.dll")] 
    public static extern IntPtr GetWindowDC(IntPtr hWnd); 
    [DllImport("user32.dll")] 
    public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect); 
    [DllImport("user32.dll")] 
    public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC); 
} 

Le costanti utilizzate:

public const int SRCCOPY = 13369376; 

T egli le strutture usato:

[StructLayout(LayoutKind.Sequential)] 
public struct RECT 
{ 
    public int left; 
    public int top; 
    public int right; 
    public int bottom; 
} 

Un amichevole metodo di estensione di controllo:

public static class ControlExtensions 
{ 
    public static Image DrawToImage(this Control control) 
    { 
     return Utilities.CaptureWindow(control.Handle); 
    } 
} 

Si tratta di un frammento di codice dal mio progetto CC.Utilities e io in particolare lo ha scritto per prendere screenshot dal controllo WebBrowser.

+0

Grazie darò uno scatto. – Matt

+1

@Matt: Spero che aiuti. Avevo lasciato fuori la classe Gdi32 dal mio post originale ma l'ho risolto ora. –

+1

@Cory Charlton: Funziona alla grande! La mia unica domanda è dire che ho una pagina nel controllo WebBrowser che si estende su più di una pagina. Non c'è modo di usarlo per afferrarlo? – Matt

0

Sto utilizzando DrawToBitmap (Visual Studio 2008 C#) per acquisire immagini di grandi dimensioni (fatture firmate dall'utente, contenuto fuori dallo schermo). Fondamentalmente funziona bene, ma sto ottenendo immagini vuote. Circa 100 dipendenti utilizzano questo software, circa ogni 2 settimane posso vedere un'immagine vuota.

Ho fatto un sacco di test e una cosa divertente che ho trovato è: Ho creato un pulsante per generare l'immagine dal browser web. Di solito va bene, ma se prima clicco sul browser, poi clicco sul pulsante di creazione, apparirà l'immagine vuota.

+3

L'immagine vuota è una lamentela vista molto. La documentazione di Microsoft sul metodo dice alla gente di non usarlo ma tutti lo fanno ancora perché questo è un metodo molto necessario. Sono stato alla ricerca di una soluzione su questo per circa una settimana. Devo fare affidamento su IE per il rendering, quindi non posso usare altre soluzioni. Sarebbe interessante vedere se esiste un metodo migliore per ottenere risultati coerenti dal metodo DrawToBitmap. –

+0

Solo un suggerimento, qualcosa che potrei dover fare da solo se non riesco a risolverlo altrimenti, dovresti essere in grado di determinare che l'immagine è vuota, non solo ogni pixel sarà bianco, ma se sono tutti bianchi allora un po 'di matematica dovrebbe mostrare che c'è solo un pixel di colore nell'immagine. –

3

Il seguente metodo può acquisire l'intera immagine della finestra anche se la finestra è più grande della dimensione dello schermo. Poi si può catturare l'immagine del contenuto della pagina se la finestra essere ridimensionata alla webBrowser.Document.OffsetRectangle.Size

class NativeMethods 
{ 
    [ComImport] 
    [Guid("0000010D-0000-0000-C000-000000000046")] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    interface IViewObject 
    { 
     void Draw([MarshalAs(UnmanagedType.U4)] uint dwAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, IntPtr hdcTargetDev, IntPtr hdcDraw, [MarshalAs(UnmanagedType.Struct)] ref RECT lprcBounds, [In] IntPtr lprcWBounds, IntPtr pfnContinue, [MarshalAs(UnmanagedType.U4)] uint dwContinue); 
    } 

    [StructLayout(LayoutKind.Sequential, Pack = 4)] 
    struct RECT 
    { 
     public int Left; 
     public int Top; 
     public int Right; 
     public int Bottom; 
    } 

    public static void GetImage(object obj, Image destination, Color backgroundColor) 
    { 
     using(Graphics graphics = Graphics.FromImage(destination)) 
     { 
      IntPtr deviceContextHandle = IntPtr.Zero; 
      RECT rectangle = new RECT(); 

      rectangle.Right = destination.Width; 
      rectangle.Bottom = destination.Height; 

      graphics.Clear(backgroundColor); 

      try 
      { 
       deviceContextHandle = graphics.GetHdc(); 

       IViewObject viewObject = obj as IViewObject; 
       viewObject.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, deviceContextHandle, ref rectangle, IntPtr.Zero, IntPtr.Zero, 0); 
      } 
      finally 
      { 
       if(deviceContextHandle != IntPtr.Zero) 
       { 
        graphics.ReleaseHdc(deviceContextHandle); 
       } 
      } 
     } 
    } 
} 

Usage:

Bitmap screenshot = new Bitmap(1024, 768); 
NativeMethods.GetImage(webBrowser.ActiveXInstance, screenshot, Color.White); 
+0

È in grado di eseguire screenshot se la pagina html presenta scroll. Ho provato a usare @Cory Charlton ma funziona, ma solo lo schermo visibile non l'intera pagina – Sandeep