2009-09-16 10 views
13

Obecnie pracuję z WatiN i uważam, że jest to świetne narzędzie do automatyzacji przeglądania stron internetowych. Jednak od wydania ostatniego wydaje się, że brakuje funkcji przechwytywania ekranu. Udało mi się stworzyć praktyczne rozwiązanie do przechwytywania zrzutów ekranu z ekranu (niezależnie generującego kod podobny do this StackOverflow question) oraz niektórych code by Charles Petzold. Niestety brakuje elementu: Gdzie jest rzeczywiste okno?Jak znaleźć położenie/położenie okna z hWnd bez NativeMethods?

Watin dogodnie dostarcza przeglądarki hWnd do ciebie, więc możemy (z tego uproszczonego przykładu) Przygotuj się do kopiowania obrazu na ekranie, tak jak poniżej:

// browser is either an WatiN.Core.IE or a WatiN.Core.FireFox... 
IntPtr hWnd = browser.hWnd; 
string filename = "my_file.bmp"; 
using (Graphics browser = Graphics.FromHwnd(browser.hWnd)) 
using (Bitmap screenshot = new Bitmap((int)browser.VisibleClipBounds.Width, 
             (int)browser.VisibleClipBounds.Height, 
             browser)) 
using (Graphics screenGraphics = Graphics.FromImage(screenshot)) 
{ 
    int hWndX = 0; // Upper left of graphics? Nope, 
    int hWndY = 0; // this is upper left of the entire desktop! 

    screenGraphics.CopyFromScreen(hWndX, hWndY, 0, 0, 
          new Size((int)browser.VisibileClipBounds.Width, 
            (int)browser.VisibileClipBounds.Height)); 
    screenshot.Save(filename, ImageFormat.Bmp); 
} 

Sukces! Dostajemy zrzuty ekranu, ale jest taki problem: hWndX i hWndY zawsze wskazują na lewy górny róg ekranu, a nie lokalizację okna, z którego chcemy skopiować.

Następnie przejrzałem Control.FromHandle, ale wydaje się, że działa tylko z utworzonymi formularzami; ta metoda zwraca wskaźnik zerowy, jeśli przekazujesz do niego wartość hWnd.

Następnie dalsze czytanie doprowadziło mnie do zmiany kryteriów wyszukiwania ... Szukałem "lokalizacji okna", kiedy większość ludzi naprawdę chce "pozycji" okna. To prowadziło do another SO question, która mówiła o tym, ale ich odpowiedzią było użycie natywnych metod.

Tak, Czy istnieje macierzysty sposób C# znalezienia pozycji okna, tylko pod warunkiem hWnd (najlepiej tylko z bibliotekami ery .NET 2.0)?

Odpowiedz

29

Właśnie przeszedłem przez to na projekcie i nie byłem w stanie znaleźć żadnego zarządzanego sposobu C#.

Aby dodać do Reeda odpowiedź P/Invoke kod jest:

[DllImport("user32.dll", SetLastError = true)] 
[return: MarshalAs(UnmanagedType.Bool)] 
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); 
[StructLayout(LayoutKind.Sequential)] 
private struct RECT 
{ 
    public int Left; 
    public int Top; 
    public int Right; 
    public int Bottom; 
    } 

Nazwij to jak:

RECT rct = new RECT(); 
    GetWindowRect(hWnd, ref rct); 
+0

Dzięki! Wygląda na to, że to zrobi. –

+0

Witaj Robert, Czy możesz zamieścić pełne rozwiązanie? Chciałbym dodać funkcjonalność screencapture dla firefox i innych przeglądarek (chrome). Z góry dziękuję! Jeroen (lead dev WatiN) –

+0

hej! Pewnie, może zrobić. –

6

Nie - jeśli nie utworzyłeś formularza, musisz P/Invoke GetWindowRect. Nie wierzę, że istnieje zarządzany odpowiednik.

4

Odpowiedź jest jak inni stwierdzili, prawdopodobnie „Nie, nie możesz wziąć zrzut ekranu losowego okna z hwnd bez natywnych metod. ". Kilka ostrzeżenia przed pokażę go:

Ostrzegawcze:

Dla każdego, kto chce użyć tego kodu, trzeba pamiętać, że wielkość podana z VisibleClipBounds jest tylko wewnątrz okna i robi nie zawierać obramowanie lub pasek tytułu. To obszar do rysowania. Jeśli tak, możesz to zrobić bez p/invoke.

(Jeśli można obliczyć granicę okna przeglądarki, można użyć VisibleClipBounds. Jeśli chcesz, możesz użyć obiektu SystemInformation aby uzyskać ważne informacje jak Border3DSize, czy można spróbować go obliczyć poprzez stworzenie manekina Forma i deriving the border and title bar height from that, ale to wszystko brzmi jak czarna magia, z której robione są błędy.)

Jest to odpowiednik Ctrl + ekran drukowania okna. Nie robi to również nic ciekawego, co umożliwia funkcja zrzutu ekranu WatiN, np. Przewija przeglądarkę i robi zdjęcie całej strony. Jest to odpowiednie dla mojego projektu, ale może nie być dla ciebie.

Ulepszenia:

To może być zmieniane za metodę rozszerzenia, jeśli jesteś w .NET 3 i up-ziemi, a opcja dla typu obrazu można dodać dość łatwo (I domyślnie ImageFormat.Bmp dla tego przykładu).

Kod:

using System.Drawing; 
using System.Drawing.Imaging; 
using System.Runtime.InteropServices; 

public class Screenshot 
{ 
    class NativeMethods 
    { 
     // http://msdn.microsoft.com/en-us/library/ms633519(VS.85).aspx 
     [DllImport("user32.dll", SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); 

     // http://msdn.microsoft.com/en-us/library/a5ch4fda(VS.80).aspx 
     [StructLayout(LayoutKind.Sequential)] 
     public struct RECT 
     { 
      public int Left; 
      public int Top; 
      public int Right; 
      public int Bottom; 
     } 
    } 
    /// <summary> 
    /// Takes a screenshot of the browser. 
    /// </summary> 
    /// <param name="b">The browser object.</param> 
    /// <param name="filename">The path to store the file.</param> 
    /// <returns></returns> 
    public static bool SaveScreenshot(Browser b, string filename) 
    { 
     bool success = false; 
     IntPtr hWnd = b.hWnd; 
     NativeMethods.RECT rect = new NativeMethods.RECT(); 
     if (NativeMethods.GetWindowRect(hWnd, ref rect)) 
     { 
      Size size = new Size(rect.Right - rect.Left, 
           rect.Bottom - rect.Top); 
      // Get information about the screen 
      using (Graphics browserGraphics = Graphics.FromHwnd(hWnd)) 
      // apply that info to a bitmap... 
      using (Bitmap screenshot = new Bitmap(size.Width, size.Height, 
                browserGraphics)) 
      // and create an Graphics to manipulate that bitmap. 
      using (Graphics imageGraphics = Graphics.FromImage(screenshot)) 
      { 
       int hWndX = rect.Left; 
       int hWndY = rect.Top; 
       imageGraphics.CopyFromScreen(hWndX, hWndY, 0, 0, size); 
       screenshot.Save(filename, ImageFormat.Bmp); 
       success = true; 
      } 
     } 
     // otherwise, fails. 
     return success; 
    } 
}