Как мне найти положение / местоположение окна, заданного hWnd без NativeMethods? - PullRequest
14 голосов
/ 16 сентября 2009

В настоящее время я работаю с WatiN и считаю его отличным инструментом автоматизации просмотра веб-страниц. Однако, по состоянию на последний выпуск, его функции захвата экрана, кажется, не хватает. Я предложил работоспособное решение для захвата скриншотов с экрана (независимо генерирующий код, подобный этому вопросу StackOverflow ) в дополнение к некоторому коду Чарльза Петцольда . К сожалению, отсутствует компонент: Где находится фактическое окно ?

WatiN удобно предоставляет вам hWnd браузера, поэтому мы можем (на этом упрощенном примере) настроить копирование изображения с экрана, например:

// 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);
}

Успех! Мы получаем скриншоты, но есть такая проблема: hWndX и hWndY всегда указывают на самый верхний левый угол экрана, а не на местоположение окна, из которого мы хотим скопировать.

Затем я посмотрел на Control.FromHandle, однако, похоже, это работает только с созданными вами формами; этот метод возвращает нулевой указатель, если вы передаете в него hWnd.

Затем, дальнейшее чтение привело меня к переключению критериев поиска ... Я искал «местоположение окна», когда большинство людей действительно хотят «положение» окна. Это привело к другому такому вопросу , в котором говорилось об этом, но их ответом было использование нативных методов.

Итак, Существует ли собственный C # способ определения положения окна только с учетом hWnd (желательно только с библиотеками эпохи .NET 2.0)?

Ответы [ 3 ]

33 голосов
/ 16 сентября 2009

Я только что прошел через проект и не смог найти какой-либо управляемый путь C #.

Чтобы добавить к ответу Рида код P / Invoke:

 [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;
  }

Назовите это как:

  RECT rct = new RECT();
  GetWindowRect(hWnd, ref rct);
6 голосов
/ 16 сентября 2009

Нет - если вы не создали форму, вам нужно P / Invoke GetWindowRect . Я не верю, что есть управляемый эквивалент.

4 голосов
/ 22 сентября 2009

Ответ, как говорили другие, вероятно, «Нет, вы не можете сделать скриншот случайного окна из hwnd без нативных методов». Несколько предостережений, прежде чем я покажу это:

предостережение:

Для тех, кто хочет использовать этот код, обратите внимание, что размер, указанный в VisibleClipBounds, составляет всего внутри окна, и не включает в себя границу или строку заголовка. Это нарисованная область. Если бы у вас было это, вы могли бы сделать это без p / invoke.

(Если бы вы могли вычислить границу окна браузера, вы могли бы использовать VisibleClipBounds. При желании вы могли бы использовать объект SystemInformation для получения важной информации, такой как Border3DSize , или вы можете попытаться вычислить ее, создав фиктивную форму и извлекая границу и высоту строки заголовка из этого , но все это звучит как черная магия, из которой сделаны ошибки.)

Это эквивалентно Ctrl + Printscreen окна. Это также не делает тонкостей, которые делает возможность скриншота WatiN, таких как прокрутка браузера и получение изображения всей страницы. Это подходит для моего проекта, но не для вашего.

Улучшения:

Это можно изменить как метод расширения, если вы находитесь в .NET 3 и работаете на более высоком уровне, и опцию для типа изображения можно добавить довольно легко (для этого примера по умолчанию ImageFormat.Bmp).

Код:

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;
    }   
}
...