wnd[] _FindAll(WndList_ k, wnd wParent) { using (k) { using var ab = new ArrayBuilder_ <wnd>(); _FindInList(wParent, k, w => ab.Add(w)); //CONSIDER: ab could be part of _WndList. Now the delegate creates garbage. return(ab.ToArray()); } }
/// <summary> /// Returns true if control c properties match the specified properties. /// </summary> /// <param name="c">A control. Can be 0/invalid, then returns false.</param> /// <param name="wParent">Direct or indirect parent window. If used, returns false if it isn't parent (also depends on flag DirectChild).</param> public bool IsMatch(wnd c, wnd wParent = default) { if (!wParent.Is0 && !c.IsChildOf(wParent)) { Result = default; return(false); } return(0 == _FindInList(wParent, new WndList_(c))); }
ArrayBuilder_ <wnd> _AllChildren(wnd wParent) { wParent.ThrowIfInvalid(); return(EnumWindows2(EnumAPI.EnumChildWindows, onlyVisible: 0 == (_flags & WCFlags.HiddenToo), sortFirstVisible: true, wParent: wParent, directChild: 0 != (_flags & WCFlags.DirectChild))); }
static unsafe uint[,] _Pixels(RECT r, wnd w = default, bool printWindow = false) { using var mb = new MemoryBitmap(r.Width, r.Height); _CaptureToDC(mb, r, w, printWindow); var a = new uint[r.Height, r.Width]; fixed(uint *p = a) { _GetPixelsFromDC(mb, r, p); } return(a); }
static wnd _WindowFromRect(ICResult r) { Thread.Sleep(25); //after the window is closed, sometimes need several ms until OS sets correct Z order. Until that may get different w1 and w2. wnd w1 = wnd.fromXY((r.rect.left, r.rect.top)); wnd w2 = (r.image == null) ? w1 : wnd.fromXY((r.rect.right - 1, r.rect.bottom - 1)); if (w2 != w1 || !_IsInClientArea(w1)) { wnd w3 = w1.Window, w4 = w2.Window; w1 = (w4 == w3 && _IsInClientArea(w3)) ? w3 : default; } return(w1); bool _IsInClientArea(wnd w) => w.GetClientRect(out var rc, true) && rc.Contains(r.rect); }
/// <returns>0 Cancel, 1 OK, 2 Retry.</returns> public int Show(Bitmap img, ICFlags flags, RECT r) { _img = img; _flags = flags; _cursor = MouseCursor.Load(ResourceUtil.GetBytes("<Au>resources/red_cross_cursor.cur"), 32); _dpi = screen.primary.Dpi; _w = WndUtil.CreateWindow(_WndProc, true, "#32770", "Au.uiimage.CaptureUI", WS.POPUP | WS.VISIBLE, WSE.TOOLWINDOW | WSE.TOPMOST, r.left, r.top, r.Width, r.Height); _w.ActivateL(); try { while (Api.GetMessage(out var m) > 0 && m.message != Api.WM_APP) { switch (m.message) { case Api.WM_KEYDOWN when !_capturing: switch ((KKey)(int)m.wParam) { case KKey.Escape: return(0); case KKey.F3: return(2); } break; case Api.WM_RBUTTONUP when m.hwnd == _w: switch (popupMenu.showSimple("1 Retry\tF3|2 Cancel\tEsc", owner: _w)) { case 1: return(2); case 2: return(0); } break; } Api.DispatchMessage(m); } } finally { var w = _w; _w = default; Api.DestroyWindow(w); } return(_res); }
static unsafe Bitmap _Capture(RECT r, wnd w = default, bool printWindow = false, GraphicsPath path = null) { //Transfer from screen/window DC to memory DC (does not work without this) and get pixels. //rejected: parameter includeNonClient (GetWindowDC). // Nothing good. If in background, captures incorrect caption etc. // If need nonclient part, better activate window and capture window rectangle from screen. //FUTURE: if w is DWM-scaled... using var mb = new MemoryBitmap(r.Width, r.Height); _CaptureToDC(mb, r, w, printWindow); #if !true //with this code can allocate gigabytes without triggering GC var R = new Bitmap(r.Width, r.Height, PixelFormat.Format32bppRgb); try { var d = R.LockBits(new Rectangle(0, 0, r.Width, r.Height), ImageLockMode.ReadWrite, R.PixelFormat); //tested: fast, no copy try { _GetPixelsFromDC(mb, r, (uint *)d.Scan0, path); } finally { R.UnlockBits(d); } //tested: fast, no copy return(R); } catch { R.Dispose(); throw; } #elif !true //with this code GC should be OK, but isn't. Allocates large amount before starting to free. Also unsafe etc because uses Tag. var a = GC.AllocateUninitializedArray <uint>(r.Width * r.Height, pinned: true); fixed(uint *p = a) { _GetPixelsFromDC(mb, r, p, path); return(new Bitmap(r.Width, r.Height, r.Width * 4, PixelFormat.Format32bppRgb, (IntPtr)p) { Tag = a }); } #else var R = new Bitmap(r.Width, r.Height, PixelFormat.Format32bppRgb); GC_.AddObjectMemoryPressure(R, r.Width * r.Height * 4); var d = R.LockBits(new(0, 0, r.Width, r.Height), ImageLockMode.ReadWrite, R.PixelFormat); //tested: fast, no copy try { _GetPixelsFromDC(mb, r, (uint *)d.Scan0, path); } finally { R.UnlockBits(d); } //tested: fast, no copy return(R); #endif }
static void _CaptureToDC(MemoryBitmap mb, RECT r, wnd w = default, bool printWindow = false) { if (printWindow) { if (!Api.PrintWindow(w, mb.Hdc, Api.PW_CLIENTONLY | (osVersion.minWin8_1 ? Api.PW_RENDERFULLCONTENT : 0))) { w.ThrowNoNative("Failed to get pixels"); } } else { using var dc = new WindowDC_(w); if (dc.Is0) { w.ThrowNoNative("Failed to get pixels"); } uint rop = !w.Is0 ? Api.SRCCOPY : Api.SRCCOPY | Api.CAPTUREBLT; bool ok = Api.BitBlt(mb.Hdc, 0, 0, r.Width, r.Height, dc, r.left, r.top, rop); Debug.Assert(ok); //the API fails only if a HDC is invalid } }
nint _WndProc(wnd w, int msg, nint wParam, nint lParam) { //WndUtil.PrintMsg(w, msg, wParam, lParam); switch (msg) { case Api.WM_NCDESTROY: _img.Dispose(); _cursor?.Dispose(); if (_w != default) { _w = default; _w.Post(Api.WM_APP); } break; case Api.WM_SETCURSOR: Api.SetCursor(_cursor.Handle); return(1); case Api.WM_ERASEBKGND: return(default); case Api.WM_PAINT: var dc = Api.BeginPaint(w, out var ps); _WmPaint(dc); Api.EndPaint(w, ps); return(default); case Api.WM_MOUSEMOVE: _WmMousemove(Math2.NintToPOINT(lParam)); break; case Api.WM_LBUTTONDOWN: _WmLbuttondown(Math2.NintToPOINT(lParam)); break; } return(Api.DefWindowProc(w, msg, wParam, lParam)); }
static Bitmap _Capture(List <POINT> outline, wnd w = default, bool printWindow = false) { int n = outline?.Count ?? 0; if (n == 0) { throw new ArgumentException(); } if (n == 1) { return(_Capture(new RECT(outline[0].x, outline[0].y, 1, 1))); } using var path = _CreatePath(outline); RECT r = RECT.From(path.GetBounds(), false); if (r.NoArea) { path.Widen(Pens.Black); //will be transparent, but no exception. Difficult to make non-transparent line. r = RECT.From(path.GetBounds(), false); } return(_Capture(r, w, printWindow, path)); }
/// <summary> /// Finds the specified child control, like <see cref="wnd.Child"/>. /// </summary> /// <returns>If found, returns <see cref="Result"/>, else default(wnd).</returns> /// <param name="wParent">Direct or indirect parent window. Can be top-level window or control.</param> /// <exception cref="AuWndException">Invalid <i>wParent</i>.</exception> /// <remarks> /// Functions <b>Find</b> and <b>Exists</b> differ only in their return types. /// </remarks> public wnd Find(wnd wParent) => Exists(wParent) ? Result : default;
/// <summary> /// Creates image from a rectangle of window client area pixels. /// </summary> /// <param name="w">Window or control.</param> /// <param name="r">Rectangle in <i>w</i> client area coordinates. Use <c>w.ClientRect</c> to get whole client area.</param> /// <param name="printWindow">Get pixels like with flag <see cref="IFFlags.PrintWindow"/>.</param> /// <exception cref="AuWndException">Invalid <i>w</i>.</exception> /// <exception cref="ArgumentException">Empty rectangle.</exception> /// <exception cref="AuException">Failed. For example there is not enough memory for bitmap of this size (width*height*4 bytes).</exception> /// <remarks> /// Unlike <see cref="capture(RECT)"/>, this overload gets pixels directly from window, not from screen. Like with flag <see cref="IFFlags.WindowDC"/> or <see cref="IFFlags.PrintWindow"/>. The window can be under other windows. The captured image can be different than displayed on screen. /// If the window is partially or completely transparent, captures its non-transparent view. /// If the window is DPI-scaled, captures its non-scaled view. And <i>r</i> must contain non-scaled coordinates. /// </remarks> public static Bitmap capture(wnd w, RECT r, bool printWindow = false) => _Capture(r, w.ThrowIfInvalid(), printWindow);
/// <returns>If found, sets <see cref="Result"/> and returns true. Else throws exception or returns false (if <i>waitS</i> negative).</returns> /// <inheritdoc cref="Find(wnd, double)"/> public bool Exists(wnd wParent, double waitS) { var r = waitS == 0d ? Exists(wParent) : wait.forCondition(waitS < 0 ? waitS : -waitS, () => Exists(wParent)); return(r || double.IsNegative(waitS) ? r : throw new NotFoundException()); }
/// <summary> /// Finds the specified control in a list of controls. /// Returns 0-based index, or -1 if not found. /// The <see cref="Result"/> property will be the control. /// </summary> /// <param name="a">List of controls, for example returned by <see cref="wnd.getwnd.Children"/>.</param> /// <param name="wParent">Direct or indirect parent window. Used only for flag DirectChild.</param> public int FindInList(IEnumerable <wnd> a, wnd wParent = default) { using var k = new WndList_(a); return(_FindInList(wParent, k)); }
/// <summary> /// Creates image from a user-selected area of screen pixels. Or gets single pixel color, or just rectangle. /// Returns false if cancelled. /// </summary> /// <param name="result">Receives results.</param> /// <param name="flags"></param> /// <param name="owner">Owner window. Temporarily minimizes it.</param> /// <remarks> /// Gets all screen pixels and shows in a full-screen topmost window, where the user can select an area. /// </remarks> public static bool captureUI(out ICResult result, ICFlags flags = 0, AnyWnd owner = default) { result = default; switch (flags & (ICFlags.Image | ICFlags.Color | ICFlags.Rectangle)) { case 0: case ICFlags.Image: case ICFlags.Color: case ICFlags.Rectangle: break; default: throw new ArgumentException(); } wnd wTool = default; try { if (!owner.IsEmpty) { wTool = owner.Hwnd; wTool.ShowMinimized(noAnimation: true); using (new inputBlocker(BIEvents.MouseClicks)) Au.wait.doEvents(300); //time for animations } g1: RECT rs = screen.virtualScreen; //RECT rs = screen.primary.Rect; //for testing, to see Write output in other screen Bitmap bs; bool windowPixels = flags.HasAny(ICFlags.WindowDC | ICFlags.PrintWindow); if (windowPixels) { if (!_WaitForHotkey("Press F3 to select window from mouse pointer. Or Esc.")) { return(false); } var w = wnd.fromMouse(WXYFlags.NeedWindow); var rc = w.ClientRect; using var bw = capture(w, rc, flags.Has(ICFlags.PrintWindow)); bs = new Bitmap(rs.Width, rs.Height); using var g = Graphics.FromImage(bs); g.Clear(Color.Black); w.MapClientToScreen(ref rc); g.DrawImage(bw, rc.left - rs.left, rc.top - rs.top); } else { bs = capture(rs); } var cw = new _CapturingWindow(); switch (cw.Show(bs, flags, rs)) { case 1: break; case 2: if (!windowPixels && !_WaitForHotkey("Press F3 when ready for new screenshot. Or Esc.")) { return(false); } goto g1; default: return(false); } var r = cw.Result; r.w = _WindowFromRect(r); result = r; } finally { if (wTool.IsAlive) { wTool.ShowNotMinimized(); wTool.ActivateL(); } } return(true);
/// <summary> /// Finds all matching child controls, like <see cref="wnd.ChildAll"/>. /// Returns array containing 0 or more control handles as wnd. /// </summary> /// <param name="wParent">Direct or indirect parent window. Can be top-level window or control.</param> /// <exception cref="AuWndException">Invalid wParent.</exception> public wnd[] FindAll(wnd wParent) { return(_FindAll(new WndList_(_AllChildren(wParent)), wParent)); }
/// <summary> /// Gets pixel colors from a rectangle in window client area. /// </summary> /// <returns>2-dimensional array [row, column] containing pixel colors in 0xAARRGGBB format. Alpha 0xFF.</returns> /// <param name="w">Window or control.</param> /// <param name="r">Rectangle in <i>w</i> client area coordinates. Use <c>w.ClientRect</c> to get whole client area.</param> /// <param name="printWindow">Get pixels like with flag <see cref="IFFlags.PrintWindow"/>.</param> /// <exception cref="AuWndException">Invalid <i>w</i>.</exception> /// <exception cref="ArgumentException">Empty rectangle.</exception> /// <exception cref="AuException">Failed. Probably there is not enough memory for bitmap of this size (width*height*4 bytes).</exception> /// <remarks> /// Unlike <see cref="getPixels(RECT)"/>, this overload gets pixels directly from window, not from screen. Like with flag <see cref="IFFlags.WindowDC"/> or <see cref="IFFlags.PrintWindow"/>. The window can be under other windows. The captured image can be different than displayed on screen. /// If the window is partially or completely transparent, captures its non-transparent view. /// If the window is DPI-scaled, captures its non-scaled view. And <i>r</i> must contain non-scaled coordinates. /// </remarks> public static uint[,] getPixels(wnd w, RECT r, bool printWindow = false) => _Pixels(r, w.ThrowIfInvalid(), printWindow);
/// <summary> /// Creates image from a non-rectangular area of window client area pixels. /// </summary> /// <param name="w">Window or control.</param> /// <param name="outline">The outline (shape) of the area in w client area coordinates. If single element, captures single pixel.</param> /// <param name="printWindow"></param> /// <exception cref="AuWndException">Invalid <i>w</i>.</exception> /// <exception cref="ArgumentException"><i>outline</i> is null or has 0 elements.</exception> /// <exception cref="AuException">Failed. Probably there is not enough memory for bitmap of this size.</exception> /// <remarks>More info: <see cref="capture(wnd, RECT, bool)"/>.</remarks> public static Bitmap capture(wnd w, List <POINT> outline, bool printWindow = false) { w.ThrowIfInvalid(); return(_Capture(outline, w, printWindow)); }
/// <returns>If found, sets <see cref="Result"/> and returns true, else false.</returns> /// <inheritdoc cref="Find(wnd)"/> public bool Exists(wnd wParent) { using var k = new WndList_(_AllChildren(wParent)); return(_FindInList(wParent, k) >= 0); }
/// <summary> /// Finds all matching controls in a list of controls. /// Returns array containing 0 or more control handles as wnd. /// </summary> /// <param name="a">List of controls, for example returned by <see cref="wnd.getwnd.Children"/>.</param> /// <param name="wParent">Direct or indirect parent window. Used only for flag DirectChild.</param> public wnd[] FindAllInList(IEnumerable <wnd> a, wnd wParent = default) { return(_FindAll(new WndList_(a), wParent)); }
/// <summary> /// Returns index of matching element or -1. /// Returns -1 if using getAll. /// </summary> /// <param name="wParent">Parent window. Can be default(wnd) if inList is true and no DirectChild flag and not using winforms name.</param> /// <param name="a">List of wnd. Does not dispose it.</param> /// <param name="getAll">If not null, calls it for all matching and returns -1.</param> int _FindInList(wnd wParent, WndList_ a, Action <wnd> getAll = null) { Result = default; if (a.Type == WndList_.ListType.None) { return(-1); } bool inList = a.Type != WndList_.ListType.ArrayBuilder; int skipCount = _skipCount; try { //will need to dispose something for (int index = 0; a.Next(out wnd w); index++) { if (w.Is0) { continue; } if (inList) //else the enum function did this { if (!_flags.Has(WCFlags.HiddenToo)) { if (!w.IsVisibleIn_(wParent)) { continue; } } if (_flags.Has(WCFlags.DirectChild) && !wParent.Is0) { if (w.ParentGWL_ != wParent) { continue; } } } if (_id != null) { if (w.ControlId != _id.Value) { continue; } } if (_className != null) { if (!_className.Match(w.ClassName)) { continue; } } if (_name != null) { string s; switch (_nameIs) { case _NameIs.text: s = w.ControlText; break; case _NameIs.elmName: s = w.NameElm; break; case _NameIs.wfName: if (_wfControls == null) { try { _wfControls = new WinformsControlNames(wParent.Is0 ? w : wParent); } catch (AuWndException) { //invalid parent window return(-1); } catch (AuException e) { //probably process of higher UAC integrity level print.warning($"Failed to get winforms control names. {e.Message}"); return(-1); } } s = _wfControls.GetControlName(w); break; //case _NameIs.label: // s = w.NameLabel; // break; default: s = w.Name; break; } if (!_name.Match(s)) { continue; } } if (_also != null && !_also(w)) { continue; } if (getAll != null) { getAll(w); continue; } if (skipCount-- > 0) { continue; } Result = w; return(index); } } finally { if (_wfControls != null) { _wfControls.Dispose(); _wfControls = null; } } return(-1); }
/// <summary> /// Finds a top-level window and returns its handle as <b>wnd</b>. /// </summary> /// <returns>Returns <c>default(wnd)</c> if not found. See also: <see cref="Is0"/>.</returns> /// <param name="name"> /// Name. /// Full, case-insensitive. Wildcard etc not supported. /// null means 'can be any'. "" means 'no name'. /// </param> /// <param name="cn"> /// Class name. /// Full, case-insensitive. Wildcard etc not supported. /// null means 'can be any'. Cannot be "". /// </param> /// <param name="messageOnly">Search only message-only windows.</param> /// <param name="wAfter">If used, starts searching from the next window in the Z order.</param> /// <remarks> /// Calls API <msdn>FindWindowEx</msdn>. /// Faster than <see cref="find"/>, which uses API <msdn>EnumWindows</msdn>. /// Finds hidden windows too. /// Supports <see cref="lastError"/>. /// It is not recommended to use this function in a loop to enumerate windows. It would be unreliable because window positions in the Z order can be changed while enumerating. Also then it would be slower than <b>Find</b> and <b>FindAll</b>. /// </remarks> public static wnd findFast(string name = null, string cn = null, bool messageOnly = false, wnd wAfter = default) { return(Api.FindWindowEx(messageOnly ? SpecHWND.MESSAGE : default, wAfter, cn, name));
/// <summary> /// Finds the specified child control, like <see cref="wnd.Child"/>. Can wait and throw <b>NotFoundException</b>. /// </summary> /// <returns>If found, returns <see cref="Result"/>. Else throws exception or returns default(wnd) (if <i>waitS</i> negative).</returns> /// <param name="wParent">Direct or indirect parent window. Can be top-level window or control.</param> /// <param name="waitS">The wait timeout, seconds. If 0, does not wait. If negative, does not throw exception when not found.</param> /// <exception cref="AuWndException">Invalid <i>wParent</i>.</exception> /// <exception cref="NotFoundException" /> /// <remarks> /// Functions <b>Find</b> and <b>Exists</b> differ only in their return types. /// </remarks> public wnd Find(wnd wParent, double waitS) => Exists(wParent, waitS) ? Result : default;