[Mwinapi-commits] SF.net SVN: mwinapi:[98] trunk/ManagedWinapi
Status: Beta
Brought to you by:
schierlm
From: <sch...@us...> - 2011-01-12 22:19:49
|
Revision: 98 http://mwinapi.svn.sourceforge.net/mwinapi/?rev=98&view=rev Author: schierlm Date: 2011-01-12 22:19:42 +0000 (Wed, 12 Jan 2011) Log Message: ----------- Add a class for creating special screenshots (like screenshots of non-rectangular windows or of scrollable regions). The current scrolling implementation leaves a lot of room for speed optimizations, but it can handle a lot of cases where other freeware scrolling screenshot implementations fail. Modified Paths: -------------- trunk/ManagedWinapi/ManagedWinapi.csproj trunk/ManagedWinapi/SystemWindow.cs Added Paths: ----------- trunk/ManagedWinapi/Screenshot.cs Modified: trunk/ManagedWinapi/ManagedWinapi.csproj =================================================================== --- trunk/ManagedWinapi/ManagedWinapi.csproj 2011-01-02 20:33:36 UTC (rev 97) +++ trunk/ManagedWinapi/ManagedWinapi.csproj 2011-01-12 22:19:42 UTC (rev 98) @@ -62,6 +62,7 @@ <Compile Include="MachineIdentifiers.cs" /> <Compile Include="ProcessMemoryChunk.cs" /> <Compile Include="ProcessTree.cs" /> + <Compile Include="Screenshot.cs" /> <Compile Include="ShortcutBox.cs"> <SubType>Component</SubType> </Compile> Added: trunk/ManagedWinapi/Screenshot.cs =================================================================== --- trunk/ManagedWinapi/Screenshot.cs (rev 0) +++ trunk/ManagedWinapi/Screenshot.cs 2011-01-12 22:19:42 UTC (rev 98) @@ -0,0 +1,607 @@ +using System; +/* + * ManagedWinapi - A collection of .NET components that wrap PInvoke calls to + * access native API by managed code. http://mwinapi.sourceforge.net/ + * Copyright (C) 2011 Michael Schierl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. if not, visit + * http://www.gnu.org/licenses/lgpl.html or write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.InteropServices; +using System.Threading; +using System.Windows.Forms; +using ManagedWinapi.Accessibility; + +namespace ManagedWinapi.Windows +{ + /// <summary> + /// Provides methods to obtain screenshots of different targets. + /// </summary> + public static class Screenshot + { + + /// <summary> + /// Take a screenshot of the full screen. + /// </summary> + /// <param name="includeCursor">Whether to include the mouse cursor.</param> + public static Bitmap TakeScreenshot(bool includeCursor) + { + Rectangle rect = Screen.PrimaryScreen.Bounds; + foreach (Screen screen in Screen.AllScreens) + rect = Rectangle.Union(rect, screen.Bounds); + return TakeScreenshot(rect, includeCursor, null); + } + + /// <summary> + /// Take a screenshot of a given window or object. + /// </summary> + /// <param name="window">Window to take the screenshot from.</param> + /// <param name="clientAreaOnly">Whether to include only the client area or also the decoration (title bar).</param> + /// <param name="includeCursor">Whether to include the mouse cursor.</param> + /// <param name="keepShape">Whether to keep the shape (transparency region) of the window.</param> + public static Bitmap TakeScreenshot(SystemWindow window, bool clientAreaOnly, bool includeCursor, bool keepShape) + { + Region shape = null; + if (keepShape) + { + shape = window.Region; + if (shape != null && clientAreaOnly) + { + shape.Translate(window.Rectangle.Left - window.ClientRectangle.Left, window.Rectangle.Top - window.ClientRectangle.Top); + } + } + return TakeScreenshot(clientAreaOnly ? window.ClientRectangle : window.Rectangle, includeCursor, shape); + } + + /// <summary> + /// Take a screenshot of a given accessible object + /// </summary> + /// <param name="accessibleObject">Accessible object to take the screenshot from.</param> + /// <param name="includeCursor">Whether to include the mouse cursor.</param> + /// <param name="keepShape">Whether to keep the shape (transparency region) of the window.</param> + public static Bitmap TakeScreenshot(SystemAccessibleObject accessibleObject, bool includeCursor, bool keepShape) + { + Region shape = null; + if (keepShape) + { + shape = accessibleObject.Window.Region; + shape.Translate(accessibleObject.Window.Rectangle.Left - accessibleObject.Location.Left, accessibleObject.Window.Rectangle.Top - accessibleObject.Location.Top); + } + return TakeScreenshot(accessibleObject.Location, includeCursor, shape); + } + + /// <summary> + /// Take a screenshot of an arbitrary rectangle on the screen. Optionally a region + /// can be used for clipping the rectangle. The mouse cursor, if included, + /// is not affected by clipping. + /// </summary> + /// <param name="rect">Rectangle to include.</param> + /// <param name="includeCursor">Whether to include the mouse cursor.</param> + /// <param name="shape">Shape (region) used for clipping.</param> + public static Bitmap TakeScreenshot(Rectangle rect, bool includeCursor, Region shape) + { + Bitmap result = new Bitmap(rect.Width, rect.Height); + + using (Graphics g = Graphics.FromImage(result)) + { + g.CopyFromScreen(rect.Location, Point.Empty, rect.Size); + } + if (shape != null) + { + for (int i = 0; i < result.Width; i++) + { + for (int j = 0; j < result.Height; j++) + { + if (!shape.IsVisible(new Point(i, j))) + { + result.SetPixel(i, j, Color.Transparent); + } + } + } + } + if (includeCursor) + { + // Cursors may use XOR operations http://support.microsoft.com/kb/311221 + CURSORINFO ci; + ci.cbSize = Marshal.SizeOf(typeof(CURSORINFO)); + ApiHelper.FailIfZero(GetCursorInfo(out ci)); + if ((ci.flags & CURSOR_SHOWING) != 0) + { + using (Cursor c = new Cursor(ci.hCursor)) + { + Point cursorLocation = new Point(ci.ptScreenPos.X - rect.X - c.HotSpot.X, ci.ptScreenPos.Y - rect.Y - c.HotSpot.Y); + // c.Draw() does not work with XOR cursors (like the default text cursor) + DrawCursor(ref result, c, cursorLocation); + } + } + } + return result; + } + + private static void DrawCursor(ref Bitmap bitmap, Cursor cursor, Point cursorLocation) + { + // http://support.microsoft.com/kb/311221 + // http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/291990e0-fb68-4e0a-ae12-835d43b9275b/ + + IntPtr compatibleHDC; + using (Graphics g = Graphics.FromImage(bitmap)) + { + IntPtr hDC = g.GetHdc(); + compatibleHDC = CreateCompatibleDC(hDC); + g.ReleaseHdc(); + } + IntPtr hBmp = bitmap.GetHbitmap(); + SelectObject(compatibleHDC, hBmp); + DrawIcon(compatibleHDC, cursorLocation.X, cursorLocation.Y, cursor.Handle); + bitmap.Dispose(); + bitmap = Image.FromHbitmap(hBmp); + DeleteObject(hBmp); + } + + /// <summary> + /// Take a screenshot of a window which has a larger display area than on screen (i. e. it has scroll bars). + /// This will send a <code>WM_PRINT</code> or <code>WM_PRINTCLIENT</code> message to the window to try to print it into an + /// offscreen image. This will be repeated with larger images until a transparent border remains. + /// This operation will only work with windows that explicitly support it. + /// </summary> + /// <param name="window">Window to take the screenshot of</param> + /// <param name="clientAreaOnly">Whether to send WM_PRINTCLIENT message</param> + public static Bitmap TakeOverlargeScreenshot(SystemWindow window, bool clientAreaOnly) + { + Rectangle position = window.Position; + int width = position.Width + 1; + int height = position.Height + 1; + while (true) + { + Bitmap result = TakeOverlargeScreenshot(window, clientAreaOnly, width, height); + if (result.GetPixel(0, height - 1).A != 0) + height *= 2; + else if (result.GetPixel(width - 1, 0).A != 0) + width *= 2; + else + return result; + if (width * height > 256 * 1048576) // 1 gigabyte! + return result; + } + } + + /// <summary> + /// Take a screenshot of a window which has a larger display area than on screen (i. e. it has scroll bars). + /// This will send a <code>WM_PRINT</code> or <code>WM_PRINTCLIENT</code> message to the window to try to print it into an + /// offscreen image of the given width and height. + /// </summary> + /// <param name="window">Window to take the screenshot of</param> + /// <param name="clientAreaOnly">Whether to send WM_PRINTCLIENT message</param> + /// <param name="width">Width of the bitmap</param> + /// <param name="height">Height of the bitmap</param> + public static Bitmap TakeOverlargeScreenshot(SystemWindow window, bool clientAreaOnly, int width, int height) + { + Bitmap bmp = new Bitmap(width, height); + Graphics g = Graphics.FromImage(bmp); + IntPtr pTarget = g.GetHdc(); + IntPtr pSource = CreateCompatibleDC(pTarget); + IntPtr pOrig = SelectObject(pSource, bmp.GetHbitmap()); + PrintWindow(window.HWnd, pTarget, clientAreaOnly ? (uint)1 : (uint)0); + IntPtr pNew = SelectObject(pSource, pOrig); + DeleteObject(pNew); + DeleteObject(pSource); + g.ReleaseHdc(pTarget); + g.Dispose(); + return bmp; + } + + /// <summary> + /// Take a screenshot from a vertically scrolling window. The scrolling can be done either by simulating mouse wheel events, + /// or by simulating mouse click events on the scrollbar button. The operation stops when scrolling does not result in any + /// new content or when the mouse is moved by the user. + /// </summary> + /// <param name="scrollPoint">Point inside the window that is inside the scrolling region and that might be used for sending scroll wheel events at</param> + /// <param name="window">Window to take the screenshot of</param> + /// <param name="clickPoint">Point above the scrollbar to click, if desired</param> + public static Bitmap TakeVerticalScrollingScreenshot(Point scrollPoint, SystemWindow window, Point? clickPoint) + { + return TakeVerticalScrollingScreenshot(scrollPoint, window.Rectangle, clickPoint); + } + + /// <summary> + /// Take a screenshot from a vertically scrolling region. The scrolling can be done either by simulating mouse wheel events, + /// or by simulating mouse click events on the scrollbar button. The operation stops when scrolling does not result in any + /// new content or when the mouse is moved by the user. + /// </summary> + /// <param name="scrollPoint">Point inside the window that is inside the scrolling region and that might be used for sending scroll wheel events at</param> + /// <param name="rect">Rectangle to take the screenshot of</param> + /// <param name="clickPoint">Point above the scrollbar to click, if desired</param> + public static Bitmap TakeVerticalScrollingScreenshot(Point scrollPoint, Rectangle rect, Point? clickPoint) + { + int scrollCount; + return TakeScrollingScreenshot(scrollPoint, rect, clickPoint.HasValue ? clickPoint.Value : scrollPoint, clickPoint.HasValue, r => TakeScreenshot(r, false, null), out scrollCount); + } + + /// <summary> + /// Take a screenshot from a horizontally scrolling window. The scrolling can be done either by simulating mouse wheel events, + /// or by simulating mouse click events on the scrollbar button. The operation stops when scrolling does not result in any + /// new content or when the mouse is moved by the user. + /// </summary> + /// <param name="scrollPoint">Point inside the window that is inside the scrolling region and that might be used for sending scroll wheel events at</param> + /// <param name="window">Window to take the screenshot of</param> + /// <param name="clickPoint">Point above the scrollbar to click, if desired</param> + /// <returns></returns> + public static Bitmap TakeHorizontalScrollingScreenshot(Point scrollPoint, SystemWindow window, Point? clickPoint) + { + return TakeHorizontalScrollingScreenshot(scrollPoint, window.Rectangle, clickPoint); + } + + /// <summary> + /// Take a screenshot from a horizontally scrolling region. The scrolling can be done either by simulating mouse wheel events, + /// or by simulating mouse click events on the scrollbar button. The operation stops when scrolling does not result in any + /// new content or when the mouse is moved by the user. + /// </summary> + /// <param name="scrollPoint">Point inside the window that is inside the scrolling region and that might be used for sending scroll wheel events at</param> + /// <param name="rect">Rectangle to take the screenshot of</param> + /// <param name="clickPoint">Point above the scrollbar to click, if desired</param> + /// <returns></returns> + public static Bitmap TakeHorizontalScrollingScreenshot(Point scrollPoint, Rectangle rect, Point? clickPoint) + { + int scrollCount; + return FlipRotate(TakeScrollingScreenshot(new Point(scrollPoint.Y, scrollPoint.X), FlipRotate(rect), clickPoint.HasValue ? clickPoint.Value : scrollPoint, clickPoint.HasValue, r => FlipRotate(TakeScreenshot(FlipRotate(r), false, null)), out scrollCount)); + } + + private delegate Bitmap ScreenshotFunction(Rectangle rect); + + private static Bitmap TakeScrollingScreenshot(Point centerPoint, Rectangle rect, Point mousePoint, bool click, ScreenshotFunction screenshot, out int scrollCount) + { + scrollCount = 0; + Cursor.Position = mousePoint; + Bitmap buffer = screenshot(rect); + int usedHeight = buffer.Height; + buffer = ResizeBitmap(buffer, buffer.Height * 4); + while (Cursor.Position == mousePoint) + { + scrollCount++; + if (click) + { + KeyboardKey.InjectMouseEvent(0x0002, 0, 0, 0, UIntPtr.Zero); + KeyboardKey.InjectMouseEvent(0x0004, 0, 0, 0, UIntPtr.Zero); + } + else + { + KeyboardKey.InjectMouseEvent(0x0800, 0, 0, unchecked((uint)-120), UIntPtr.Zero); + } + Application.DoEvents(); + Bitmap nextPart = screenshot(rect); + int scrollHeight = AppendBelow(buffer, usedHeight, nextPart); + foreach (int delay in new int[] { 0, 2, 10, 100, 200, 1000 }) + { + if (scrollHeight > 0 || Cursor.Position != mousePoint) + break; + Thread.Sleep(delay); + Application.DoEvents(); + nextPart = screenshot(rect); + scrollHeight = AppendBelow(buffer, usedHeight, nextPart); + } + if (scrollHeight == -1) + { + CropToSimilarRange(centerPoint, ref rect, ref buffer, ref usedHeight, ref nextPart); + scrollHeight = AppendBelow(buffer, usedHeight, nextPart); + } + if (scrollHeight <= 0) + break; + usedHeight += scrollHeight; + if (buffer.Height - usedHeight < rect.Height) + buffer = ResizeBitmap(buffer, buffer.Height * 2); + } + return ResizeBitmap(buffer, usedHeight); + } + + private static void CropToSimilarRange(Point centerPoint, ref Rectangle rect, ref Bitmap buffer, ref int usedHeight, ref Bitmap nextPart) + { + Point mousePoint = Cursor.Position; + int offs = usedHeight - nextPart.Height; + int relX = centerPoint.X - rect.X; + int relY = centerPoint.Y - rect.Y; + + // find a different point + int diffX = relX, diffY = relY; + if (buffer.GetPixel(relX, relY + offs) == nextPart.GetPixel(relX, relY)) + { + bool found = false; + int maxDistance = Math.Min(Math.Min(relX, relY), Math.Min(nextPart.Width - relX, nextPart.Height - relY)); + for (int i = 1; !found && i < maxDistance; i++) + { + for (int j = 0; j < i * 2; j++) + { + int x = relX - i + j; + int y = relY - i; + if (buffer.GetPixel(x, y + offs) != nextPart.GetPixel(x, y)) + { + diffX = x; diffY = y; found = true; + break; + } + x = relX + i; + y = relY - i + j; + if (buffer.GetPixel(x, y + offs) != nextPart.GetPixel(x, y)) + { + diffX = x; diffY = y; found = true; + break; + } + x = relX + i - j; + y = relY + i; + if (buffer.GetPixel(x, y + offs) != nextPart.GetPixel(x, y)) + { + diffX = x; diffY = y; found = true; + break; + } + x = relX - i; + y = relY + i - j; + if (buffer.GetPixel(x, y + offs) != nextPart.GetPixel(x, y)) + { + diffX = x; diffY = y; found = true; + break; + } + } + } + if (!found) return; + } + + // score every possible scroll height + int[] scrollScores = new int[nextPart.Height / 2]; + for (int x = 0; x < rect.Width; x++) + { +#if !DEBUG + if (Cursor.Position != mousePoint) return; +#endif + for (int y = 0; y < rect.Height; y++) + { + // look at every pixel that does not match unmoved + Color pixel = nextPart.GetPixel(x, y); + if (buffer.GetPixel(x, y + offs) != pixel) + { + int score = 1000 / (Math.Abs(relX - x) + Math.Abs(relY - y)) + 1; + for (int scrollHeight = 1; scrollHeight < scrollScores.Length; scrollHeight++) + { + if (buffer.GetPixel(x, y + offs + scrollHeight) == pixel) + { + scrollScores[scrollHeight] += score; + } + } + } + } + } + + // remove scores that do not preserve relX/relY or diffX/diffY + for (int scrollHeight = 1; scrollHeight < scrollScores.Length; scrollHeight++) + { + if (buffer.GetPixel(relX, relY + offs + scrollHeight) != nextPart.GetPixel(relX, relY) + || buffer.GetPixel(diffX, diffY + offs + scrollHeight) != nextPart.GetPixel(diffX, diffY)) + { + scrollScores[scrollHeight] = 0; + } + } + + // take the first 5 scroll distances based on score + Rectangle newRect = rect; + int newRectSize = 0; + for (int i = 0; i < 5; i++) + { +#if !DEBUG + if (Cursor.Position != mousePoint) return; +#endif + int maxScore = 0; + for (int scrollHeight = 1; scrollHeight < scrollScores.Length; scrollHeight++) + { + if (scrollScores[scrollHeight] > maxScore) + maxScore = scrollScores[scrollHeight]; + } + if (maxScore == 0) + break; + for (int scrollHeight = 1; scrollHeight < scrollScores.Length; scrollHeight++) + { + if (scrollScores[scrollHeight] == maxScore) + { +#if !DEBUG + if (Cursor.Position != mousePoint) return; +#endif + scrollScores[scrollHeight] = 0; + + // check the maximum rectangle that scrolls and its size + int minY = 0, maxY = rect.Height - 1; + // first scan up and down with a width of 7 pixels + for (int y = relY - 1; y >= minY; y--) + { + bool same = true; + for (int x = relX - 3; x <= relX + 3; x++) + { + if (buffer.GetPixel(x, y + offs + scrollHeight) != nextPart.GetPixel(x, y)) + { + same = false; + break; + } + } + if (!same) + { + minY = y + 1; + } + } + for (int y = relY + 1; y <= maxY; y++) + { + bool same = true; + for (int x = relX - 3; x <= relX + 3; x++) + { + if (buffer.GetPixel(x, y + offs + scrollHeight) != nextPart.GetPixel(x, y)) + { + same = false; + break; + } + } + if (!same) + { + maxY = y - 1; + } + } + // now check left and right + int minX = 0, maxX = rect.Height - 1; + for (int x = relX - 1; x >= minX; x--) + { + bool same = true; + for (int y = minY; y <= maxY; y++) + { + + if (buffer.GetPixel(x, y + offs + scrollHeight) != nextPart.GetPixel(x, y)) + { + same = false; + break; + } + } + if (!same) + minX = x + 1; + } + for (int x = relX + 1; x <= maxX; x++) + { + bool same = true; + for (int y = minY; y <= maxY; y++) + { + if (buffer.GetPixel(x, y + offs + scrollHeight) != nextPart.GetPixel(x, y)) + { + same = false; + break; + } + } + if (!same) + maxX = x - 1; + } + Rectangle rr = new Rectangle(rect.X + minX, rect.Y + minY, maxX - minX + 1, maxY - minY + 1 + scrollHeight); + if (rr.Width > 16 && rr.Height > 16 && rr.Width * rr.Height > newRectSize) + { + newRect = rr; + newRectSize = rr.Width * rr.Height; + } + } + } + } + + // if we found a rectangle + if (newRectSize > 0) + { + // do the cropping + int cropTop = newRect.Top - rect.Top; + int cropLeft = newRect.Left - rect.Left; + int cropRight = rect.Right - newRect.Right; + int cropBottom = rect.Bottom - newRect.Bottom; + buffer = Crop(buffer, cropTop, cropLeft, cropRight, cropBottom); + nextPart = Crop(nextPart, cropTop, cropLeft, cropRight, cropBottom); + usedHeight -= cropTop + cropBottom; + + // update the rectangle + rect = newRect; + } + } + + private static Bitmap Crop(Bitmap original, int cropTop, int cropLeft, int cropRight, int cropBottom) + { + Bitmap result = new Bitmap(original.Width - cropLeft - cropRight, original.Height - cropTop - cropBottom); + using (Graphics g = Graphics.FromImage(result)) + { + g.DrawImage(original, -cropLeft, -cropTop); + } + return result; + } + + private static int AppendBelow(Bitmap buffer, int usedHeight, Bitmap nextPart) + { + int offs = usedHeight - nextPart.Height; + for (int scrollHeight = 0; scrollHeight < nextPart.Height / 2; scrollHeight++) + { + bool same = true; + for (int y = 0; same && y < nextPart.Height - scrollHeight; y++) + { + for (int x = 0; same && x < nextPart.Width; x++) + { + if (nextPart.GetPixel(x, y) != buffer.GetPixel(x, y + offs + scrollHeight)) + same = false; + } + } + if (same) + { + using (Graphics g = Graphics.FromImage(buffer)) + { + g.DrawImage(nextPart, 0, offs + scrollHeight); + } + return scrollHeight; + } + } + return -1; + } + + private static Bitmap ResizeBitmap(Bitmap original, int height) + { + Bitmap result = new Bitmap(original.Width, height); + using (Graphics g = Graphics.FromImage(result)) + { + g.DrawImage(original, 0, 0); + } + return result; + } + + private static Bitmap FlipRotate(Bitmap original) + { + Bitmap result = new Bitmap(original.Height, original.Width); + using (Graphics g = Graphics.FromImage(result)) + { + g.Transform = new Matrix(0, 1, 1, 0, 0, 0); + g.DrawImage(original, 0, 0); + } + return result; + } + + private static Rectangle FlipRotate(Rectangle original) + { + return new Rectangle(original.Y, original.X, original.Height, original.Width); + } + + #region PInvoke Declarations + + [StructLayout(LayoutKind.Sequential)] + struct CURSORINFO + { + public Int32 cbSize; + public Int32 flags; + public IntPtr hCursor; + public POINT ptScreenPos; + } + + [DllImport("user32.dll")] + static extern int GetCursorInfo(out CURSORINFO pci); + + private const Int32 CURSOR_SHOWING = 0x00000001; + + [DllImport("user32.dll")] + static extern int DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon); + + [DllImport("gdi32.dll", SetLastError = true)] + static extern IntPtr CreateCompatibleDC(IntPtr hdc); + + [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)] + static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); + + [DllImport("gdi32.dll")] + static extern bool DeleteObject(IntPtr hObject); + + [DllImport("user32.dll", SetLastError = true)] + static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags); + + #endregion + } +} Modified: trunk/ManagedWinapi/SystemWindow.cs =================================================================== --- trunk/ManagedWinapi/SystemWindow.cs 2011-01-02 20:33:36 UTC (rev 97) +++ trunk/ManagedWinapi/SystemWindow.cs 2011-01-12 22:19:42 UTC (rev 98) @@ -758,6 +758,25 @@ } /// <summary> + /// The position of the window's contents in absolute screen coordinates. Use + /// <see cref="Rectangle"/> if you want to include the title bar etc. + /// </summary> + public RECT ClientRectangle + { + get + { + RECT r = new RECT(); + ApiHelper.FailIfZero(GetClientRect(_hwnd, out r)); + Point p = new Point(); + p.X = p.Y = 0; + ApiHelper.FailIfZero(ClientToScreen(_hwnd, ref p)); + Rectangle result = r; + result.Location = p; + return result; + } + } + + /// <summary> /// Check whether this window is a descendant of <c>ancestor</c> /// </summary> /// <param name="ancestor">The suspected ancestor</param> @@ -1262,6 +1281,12 @@ static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] + static extern int GetClientRect(IntPtr hWnd, out RECT lpRect); + + [DllImport("user32.dll")] + static extern int ClientToScreen(IntPtr hWnd, ref Point lpPoint); + + [DllImport("user32.dll")] static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("gdi32.dll")] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |