protected override HandleRef BuildWindowCore(HandleRef hwndParent) { var wParent = (wnd)hwndParent.Handle; _w = WndUtil.CreateWindow(_wndProc = _WndProc, false, "Static", null, WS.CHILD | WS.CLIPCHILDREN, 0, 0, 0, 10, 10, wParent); return(new HandleRef(this, _w.Handle)); }
/// <summary> /// Called after loading workspace. Before executing startup scripts, adding tray icon and creating UI. /// </summary> public static void ProgramLoaded() { WndUtil.UacEnableMessages(Api.WM_COPYDATA, /*Api.WM_DROPFILES, 0x0049,*/ Api.WM_USER, Api.WM_CLOSE); //WM_COPYDATA, WM_DROPFILES and undocumented WM_COPYGLOBALDATA=0x0049 should enable drag/drop from lower UAC IL processes, but only through WM_DROPFILES/DragAcceptFiles, not OLE D&D. WndUtil.RegisterWindowClass(script.c_msgWndClassName, _WndProc); _msgWnd = WndUtil.CreateMessageOnlyWindow(script.c_msgWndClassName); }
//Called from Main() in non-admin process when command line starts with /dd. public static void MainDD(string[] args) { _msgWnd = (wnd)args[1].ToInt(); WndUtil.RegisterWindowClass("Au.Editor.DD", _WndProc); _w = WndUtil.CreateWindow("Au.Editor.DD", null, WS.POPUP | WS.DISABLED, WSE.LAYERED | WSE.NOACTIVATE | WSE.TOOLWINDOW | WSE.TOPMOST); Api.SetLayeredWindowAttributes(_w, 0, 1, 2); Thread.CurrentThread.TrySetApartmentState(ApartmentState.Unknown); //uninit MTA Api.OleInitialize(default);
public OpenClipboard_(bool createOwner, bool noOpenNow = false) { _isOpen = false; _w = default; if (createOwner) { _w = WndUtil.CreateWindowDWP_(messageOnly: true); //MSDN says, SetClipboardData fails if OpenClipboard called with 0 hwnd. It doesn't, but better use hwnd. } if (!noOpenNow) { Reopen(); } }
/// <summary> /// Subclasses clipOwner. /// </summary> /// <param name="paste">true if used for paste, false if for copy.</param> /// <param name="data">If used for paste, can be string containing Unicode text or int/string dictionary containing clipboard format/data.</param> /// <param name="clipOwner">Our clipboard owner window.</param> /// <param name="wFocus">The target control or window.</param> public _ClipboardListener(bool paste, object data, wnd clipOwner, wnd wFocus) { _paste = paste; _data = data; _wndProc = _WndProc; _wFocus = wFocus; WndUtil.SubclassUnsafe_(clipOwner, _wndProc); //rejected: use SetClipboardViewer to block clipboard managers/viewers/etc. This was used in QM2. // Nowadays most such programs don't use SetClipboardViewer. They use AddClipboardFormatListener+WM_CLIPBOARDUPDATE. // known apps that have clipboard viewer installed with SetClipboardViewer: // OpenOffice, LibreOffice: tested Writer, Calc. // VLC: after first Paste. //_wPrevClipViewer = Api.SetClipboardViewer(clipOwner); //print.it(_wPrevClipViewer); }
//const string c_winClassName = "KTreeView"; //static KTreeView() { // WndUtil.RegisterWindowClass(c_winClassName); //} protected override HandleRef BuildWindowCore(HandleRef hwndParent) { var wParent = (wnd)hwndParent.Handle; //WndUtil.CreateWindow(_wndProc = _WndProc, false, c_winClassName, Name, WS.CHILD | WS.CLIPCHILDREN, 0, 0, 0, 10, 10, wParent); _w = WndUtil.CreateWindow(_wndProc = _WndProc, false, "Static", Name, WS.CHILD | WS.CLIPCHILDREN, 0, 0, 0, 10, 10, wParent); _hasHwnd = true; _SetDpiAndItemSize(More.Dpi.OfWindow(_w)); //Mouse messages must go to the parent window. On WM_NCHITTEST return HTTRANSPARENT. // But UIA element from point does not work. Solution: let it be Static; UIA knows it. //Other tested ways don't work: // WS.DISABLED. Scrollbars don't work. // WSE.TRANSPARENT|WSE.LAYERED + Api.SetLayeredWindowAttributes(_w, 0, 0, 0). Unavailable on Win7. Scrollbars and UIA don't work. // Relay mouse messages to the parent window. Does not work. return(new HandleRef(this, _w.Handle)); }
/// <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); }
//Currently supports only moving but not docking. Docking is implemented in _ContextMenu_Move+_MoveTo. public void Drag(POINT p) { //bool canDock = false; //_DockTarget target = null; var w = this.Hwnd(); RECT r = w.Rect; POINT offs = (p.x - r.left, p.y - r.top); bool ok = WndUtil.DragLoop(w, MButtons.Left, d => { if (d.msg.message != Api.WM_MOUSEMOVE) { return; } p = mouse.xy; w.MoveL(p.x - offs.x, p.y - offs.y); //if (!canDock && keys.gui.isAlt) { // canDock = true; // //w.SetTransparency(true, 128); // //_dockIndic = new _DockIndicator(_manager, this); // //_dockIndic.Show(this); //} ////if (canDock) _dockIndic.OnFloatMoved(_manager.PointToClient(p)); }); //if (canDock) { // w.SetTransparency(false); // _dockIndic.Close(); // if (ok) { // target = _dockIndic.OnFloatDropped(); // } // _dockIndic = null; //} //return target; }
void _WmLbuttondown(POINT p0) { bool isColor = false; //bool isAnyShape = false; //rejected. Not useful. var ic = _flags & (ICFlags.Image | ICFlags.Color | ICFlags.Rectangle); if (ic == ICFlags.Color) { isColor = true; } else { var mod = keys.gui.getMod(); if (mod != 0 && ic == ICFlags.Rectangle) { return; } switch (mod) { case 0: break; //case KMod.Shift: isAnyShape = true; break; case KMod.Ctrl when ic == 0: isColor = true; break; default: return; } } Result = new ICResult(); var r = new RECT(p0.x, p0.y, 0, 0); if (isColor) { Result.color = (uint)_img.GetPixel(p0.x, p0.y).ToArgb(); r.right++; r.bottom++; } else { //var a = isAnyShape ? new List<POINT>() { p0 } : null; var pen = Pens.Red; bool notFirstMove = false; _capturing = true; try { if (!WndUtil.DragLoop(_w, MButtons.Left, m => { if (m.msg.message != Api.WM_MOUSEMOVE) { return; } POINT p = m.msg.pt; _w.MapScreenToClient(ref p); using var g = Graphics.FromHwnd(_w.Handle); //if (isAnyShape) { // a.Add(p); // g.DrawLine(pen, p0, p); // p0 = p; //} else { if (notFirstMove) //erase prev rect { r.right++; r.bottom++; g.DrawImage(_img, r, r, GraphicsUnit.Pixel); //FUTURE: prevent flickering. Also don't draw under magnifier. } else { notFirstMove = true; } r = RECT.FromLTRB(p0.x, p0.y, p.x, p.y); r.Normalize(true); g.DrawRectangle(pen, r); //} })) //Esc key etc { Api.InvalidateRect(_w); return; } } finally { _capturing = false; } //GraphicsPath path = null; //if (isAnyShape && a.Count > 1) { // path = _CreatePath(a); // r = RECT.From(path.GetBounds(), false); //} else { r.right++; r.bottom++; //} if (r.NoArea) { Api.DestroyWindow(_w); return; } if (ic != ICFlags.Rectangle) { var b = _img.Clone(r, PixelFormat.Format32bppArgb); var d = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, b.PixelFormat); try { unsafe { _SetAlpha((uint *)d.Scan0, r /*, path*/); } } finally { b.UnlockBits(d); /*path?.Dispose();*/ } Result.image = b; } } _w.MapClientToScreen(ref r); Result.rect = r; _res = 1; Api.DestroyWindow(_w); }
/// <summary> /// Runs/opens a program, document, directory (folder), URL, new email, Control Panel item etc. /// The returned <see cref="RResult"/> variable contains some process info - process id etc. /// </summary> /// <param name="file"> /// Examples: /// - <c>@"C:\file.txt"</c> /// - <c>folders.Documents</c> /// - <c>folders.System + "notepad.exe"</c> /// - <c>@"%folders.System%\notepad.exe"</c> /// - <c>@"%TMP%\file.txt"</c> /// - <c>"notepad.exe"</c> /// - <c>@"..\folder\x.exe"</c> /// - <c>"http://a.b.c/d"</c> /// - <c>"file:///path"</c> /// - <c>"mailto:[email protected]"</c> /// - <c>":: ITEMIDLIST"</c> /// - <c>@"shell:::{CLSID}"</c> /// - <c>@"shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"</c>. /// More info in Remarks. /// </param> /// <param name="args"> /// Command line arguments. /// This function expands environment variables if starts with <c>"%"</c> or <c>"\"%"</c>. /// </param> /// <param name="flags"></param> /// <param name="dirEtc"> /// Allows to specify more parameters: current directory, verb, etc. /// If string, it sets initial current directory for the new process. If "", gets it from <i>file</i>. More info: <see cref="ROptions.CurrentDirectory"/>. /// </param> /// <exception cref="ArgumentException">Used both <b>ROptions.Verb</b> and <b>RFlags.Admin</b> and this process isn't admin.</exception> /// <exception cref="AuException">Failed. For example, the file does not exist.</exception> /// <remarks> /// It works like when you double-click a file icon. It may start new process or not. For example it may just activate window if the program is already running. /// Uses API <msdn>ShellExecuteEx</msdn>. /// Similar to <see cref="Process.Start(string, string)"/>. /// /// The <i>file</i> parameter can be: /// - Full path of a file or directory. Examples: <c>@"C:\file.txt"</c>, <c>folders.Documents</c>, <c>folders.System + "notepad.exe"</c>, <c>@"%folders.System%\notepad.exe"</c>. /// - Filename of a file or directory, like <c>"notepad.exe"</c>. The function calls <see cref="filesystem.searchPath"/>. /// - Path relative to <see cref="folders.ThisApp"/>. Examples: <c>"x.exe"</c>, <c>@"subfolder\x.exe"</c>, <c>@".\subfolder\x.exe"</c>, <c>@"..\another folder\x.exe"</c>. /// - URL. Examples: <c>"https://www.example.com"</c>, <c>"file:///path"</c>. /// - Email, like <c>"mailto:[email protected]"</c>. Subject, body etc also can be specified, and Google knows how. /// - Shell object's ITEMIDLIST like <c>":: ITEMIDLIST"</c>. See <see cref="Pidl.ToHexString"/>, <see cref="folders.shell"/>. Can be used to open virtual folders and items like Control Panel. /// - Shell object's parsing name, like <c>@"shell:::{CLSID}"</c> or <c>@"::{CLSID}"</c>. See <see cref="Pidl.ToShellString"/>. Can be used to open virtual folders and items like Control Panel. /// - To run a Windows Store App, use <c>@"shell:AppsFolder\WinStoreAppId"</c> format. Examples: <c>@"shell:AppsFolder\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"</c>, <c>@"shell:AppsFolder\windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel"</c>. To discover the string use hotkey Ctrl+Shift+Q or function <see cref="WndUtil.GetWindowsStoreAppId"/> or Google. /// /// Supports environment variables, like <c>@"%TMP%\file.txt"</c>. See <see cref="pathname.expand"/>. /// </remarks> /// <seealso cref="wnd.find"/> /// <seealso cref="wnd.findOrRun"/> /// <seealso cref="wnd.runAndFind"/> /// <example> /// Run Notepad and wait for an active Notepad window. /// <code><![CDATA[ /// run.it("notepad.exe"); /// 1.s(); /// wnd w = wnd.wait(10, true, "*- Notepad", "Notepad"); /// ]]></code> /// Run Notepad or activate a Notepad window. /// <code><![CDATA[ /// wnd w = wnd.findOrRun("*- Notepad", run: () => run.it("notepad.exe")); /// ]]></code> /// Run File Explorer and wait for new folder window. Ignores matching windows that already existed. /// <code><![CDATA[ /// var w = wnd.runAndFind( /// () => run.it(@"explorer.exe"), /// 10, cn: "CabinetWClass"); /// ]]></code> /// </example> public static RResult it(string file, string args = null, RFlags flags = 0, ROptions dirEtc = null) { Api.SHELLEXECUTEINFO x = default; x.cbSize = Api.SizeOf(x); x.fMask = Api.SEE_MASK_NOZONECHECKS | Api.SEE_MASK_NOASYNC | Api.SEE_MASK_CONNECTNETDRV | Api.SEE_MASK_UNICODE; x.nShow = Api.SW_SHOWNORMAL; bool curDirFromFile = false; var more = dirEtc; if (more != null) { x.lpVerb = more.Verb; if (x.lpVerb != null) { x.fMask |= Api.SEE_MASK_INVOKEIDLIST; //makes slower. But verbs are rarely used. } if (more.CurrentDirectory is string cd) { if (cd.Length == 0) { curDirFromFile = true; } else { cd = pathname.expand(cd); } x.lpDirectory = cd; } if (!more.OwnerWindow.IsEmpty) { x.hwnd = more.OwnerWindow.Hwnd.Window; } switch (more.WindowState) { case ProcessWindowStyle.Hidden: x.nShow = Api.SW_HIDE; break; case ProcessWindowStyle.Minimized: x.nShow = Api.SW_SHOWMINIMIZED; break; case ProcessWindowStyle.Maximized: x.nShow = Api.SW_SHOWMAXIMIZED; break; } x.fMask &= ~more.FlagsRemove; x.fMask |= more.FlagsAdd; } if (flags.Has(RFlags.Admin)) { if (x.lpVerb == null || x.lpVerb.Eqi("runas")) { x.lpVerb = "runas"; } else if (!uacInfo.isAdmin) { throw new ArgumentException("Cannot use Verb with flag Admin, unless this process is admin"); } } file = _NormalizeFile(false, file, out bool isFullPath, out bool isShellPath); Pidl pidl = null; if (isShellPath) //":: ITEMIDLIST" or "::{CLSID}..." (we convert it too because the API does not support many) { pidl = Pidl.FromString(file); //does not throw if (pidl != null) { x.lpIDList = pidl.UnsafePtr; x.fMask |= Api.SEE_MASK_INVOKEIDLIST; } else { x.lpFile = file; } } else { x.lpFile = file; if (curDirFromFile && isFullPath) { x.lpDirectory = pathname.getDirectory(file); } } x.lpDirectory ??= Directory.GetCurrentDirectory(); if (!args.NE()) { x.lpParameters = pathname.expand(args); } if (0 == (flags & RFlags.ShowErrorUI)) { x.fMask |= Api.SEE_MASK_FLAG_NO_UI; } if (0 == (flags & RFlags.WaitForExit)) { x.fMask |= Api.SEE_MASK_NO_CONSOLE; } if (0 != (flags & RFlags.MostUsed)) { x.fMask |= Api.SEE_MASK_FLAG_LOG_USAGE; } x.fMask |= Api.SEE_MASK_NOCLOSEPROCESS; WndUtil.EnableActivate(-1); bool waitForExit = 0 != (flags & RFlags.WaitForExit); bool needHandle = flags.Has(RFlags.NeedProcessHandle); bool ok = false; int pid = 0, errorCode = 0; bool asUser = !flags.HasAny(RFlags.Admin | RFlags.InheritAdmin) && uacInfo.isAdmin; //info: new process does not inherit uiAccess if (asUser) { ok = Cpp.Cpp_ShellExec(x, out pid, out int injectError, out int execError); if (!ok) { if (injectError != 0) { print.warning("Failed to run as non-admin."); asUser = false; } else { errorCode = execError; } } } if (!asUser) { ok = Api.ShellExecuteEx(ref x); if (!ok) { errorCode = lastError.code; } } pidl?.Dispose(); if (!ok) { throw new AuException(errorCode, $"*run '{file}'"); } var R = new RResult(); WaitHandle_ ph = null; if (needHandle || waitForExit) { if (pid != 0) { x.hProcess = Handle_.OpenProcess(pid, Api.PROCESS_ALL_ACCESS); } if (!x.hProcess.Is0) { ph = new WaitHandle_(x.hProcess, true); } } if (!waitForExit) { if (pid != 0) { R.ProcessId = pid; } else if (!x.hProcess.Is0) { R.ProcessId = process.processIdFromHandle(x.hProcess); } } try { Api.AllowSetForegroundWindow(); if (x.lpVerb != null && Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) { Thread.CurrentThread.Join(50); //need min 5-10 for file Properties. And not Sleep. } if (ph != null) { if (waitForExit) { ph.WaitOne(); if (Api.GetExitCodeProcess(x.hProcess, out var exitCode)) { R.ProcessExitCode = exitCode; } } if (needHandle) { R.ProcessHandle = ph; } } } finally { if (R.ProcessHandle == null) { if (ph != null) { ph.Dispose(); } else { x.hProcess.Dispose(); } } } return(R); //tested: works well in MTA thread. //rejected: in QM2, run also has a 'window' parameter. However it just makes limited, unclear etc, and therefore rarely used. Instead use wnd.findOrRun or Find/Run/Wait like in the examples. //rejected: in QM2, run also has 'autodelay'. Better don't add such hidden things. Let the script decide what to do. }
//rejected. Various problems, eg the program file cannot be moved. Unclear documentation. // Guid _guid; static trayIcon() { s_msgTaskbarCreated = WndUtil.RegisterMessage("TaskbarCreated", uacEnable: true); WndUtil.RegisterWindowClass("trayIcon"); }
/// <summary> /// Makes triggers alive. /// </summary> /// <remarks> /// This function monitors hotkeys, activated windows and other events. When an event matches an added trigger, launches the thrigger's action, which runs in other thread by default. /// Does not return immediately. Runs until this process is terminated or <see cref="Stop"/> called. /// </remarks> /// <example>See <see cref="ActionTriggers"/>.</example> /// <exception cref="InvalidOperationException">Already running.</exception> /// <exception cref="AuException">Something failed.</exception> public unsafe void Run() { //Debug_.PrintLoadedAssemblies(true, true, true); ThrowIfRunning_(); //bool haveTriggers = false; HooksThread.UsedEvents hookEvents = 0; _windowTriggers = null; for (int i = 0; i < _t.Length; i++) { var t = _t[i]; if (t == null || !t.HasTriggers) { continue; } //haveTriggers = true; switch ((TriggerType)i) { case TriggerType.Hotkey: hookEvents |= HooksThread.UsedEvents.Keyboard; break; case TriggerType.Autotext: hookEvents |= HooksThread.UsedEvents.Keyboard | HooksThread.UsedEvents.Mouse; break; case TriggerType.Mouse: hookEvents |= (t as MouseTriggers).UsedHookEvents_; break; case TriggerType.Window: _windowTriggers = t as WindowTriggers; break; } } //print.it(haveTriggers, (uint)llHooks); //if(!haveTriggers) return; //no. The message loop may be used for toolbars etc. if (!s_wasRun) { s_wasRun = true; WndUtil.RegisterWindowClass(c_cn); } _wMsg = WndUtil.CreateMessageOnlyWindow(_WndProc, c_cn); _mainThreadId = Api.GetCurrentThreadId(); _winTimerPeriod = 0; _winTimerLastTime = 0; if (hookEvents != 0) { //prevent big delay later on first LL hook event while hook proc waits if (!s_wasKM) { s_wasKM = true; ThreadPool.QueueUserWorkItem(_ => { try { //using var p1 = perf.local(); new wndFinder("*a").IsMatch(wnd.getwnd.root); //if used window scopes etc _ = WindowsHook.LowLevelHooksTimeout; //slow JIT of registry functions Jit_.Compile(typeof(ActionTriggers), nameof(_WndProc), nameof(_KeyMouseEvent)); Jit_.Compile(typeof(TriggerHookContext), nameof(TriggerHookContext.InitContext), nameof(TriggerHookContext.PerfEnd), nameof(TriggerHookContext.PerfWarn)); Jit_.Compile(typeof(ActionTrigger), nameof(ActionTrigger.MatchScopeWindowAndFunc)); Jit_.Compile(typeof(HotkeyTriggers), nameof(HotkeyTriggers.HookProc)); AutotextTriggers.JitCompile(); MouseTriggers.JitCompile(); } catch (Exception ex) { Debug_.Print(ex); } }); } _thc = new TriggerHookContext(this); _ht = new HooksThread(hookEvents, _wMsg); } try { _evStop = Api.CreateEvent(false); _StartStopAll(true); IntPtr h = _evStop; _Wait(&h, 1); } finally { if (hookEvents != 0) { _ht.Dispose(); _ht = null; } Api.DestroyWindow(_wMsg); _wMsg = default; Stopping?.Invoke(this, EventArgs.Empty); _evStop.Dispose(); _StartStopAll(false); _mainThreadId = 0; _threads?.Dispose(); _threads = null; } void _StartStopAll(bool start) { foreach (var t in _t) { if (t?.HasTriggers ?? false) { t.StartStop(start); } } } }