Repository: qgindi/LibreAutomate
Branch: master
Commit: 7121bef44741
Files: 768
Total size: 10.8 MB
Directory structure:
gitextract__t3y79t8/
├── .editorconfig
├── .gitattributes
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── Au/
│ ├── Api/
│ │ ├── Api.cs
│ │ ├── Api^kernel32.cs
│ │ ├── Api^user32.cs
│ │ ├── Api_COM.cs
│ │ ├── Api_UIA.cs
│ │ ├── Api_const.cs
│ │ ├── Api_public.cs
│ │ ├── Cpp.cs
│ │ └── WinRT.cs
│ ├── Au.More/
│ │ ├── AppSingleInstance.cs
│ │ ├── BufferedPaint.cs
│ │ ├── CheckListDialog.cs
│ │ ├── ComUtil.cs
│ │ ├── Convert2.cs
│ │ ├── DebugTraceListener.cs
│ │ ├── Dpi.cs
│ │ ├── FastBuffer.cs
│ │ ├── FileOpenSaveDialog.cs
│ │ ├── GdiTextRenderer.cs
│ │ ├── Hash.cs
│ │ ├── HelpUtil.cs
│ │ ├── HttpServerSession.cs
│ │ ├── IconImageCache.cs
│ │ ├── ImageUtil.cs
│ │ ├── KeyToTextConverter.cs
│ │ ├── Math2.cs
│ │ ├── MemoryBitmap.cs
│ │ ├── MemoryUtil.cs
│ │ ├── MenuItemInfo.cs
│ │ ├── MouseCursor.cs
│ │ ├── RecordingUtil.cs
│ │ ├── ResourceUtil.cs
│ │ ├── SecurityUtil.cs
│ │ ├── WaitableTimer.cs
│ │ ├── WinEventHook.cs
│ │ ├── WindowsHook.cs
│ │ └── WinformsControlNames.cs
│ ├── Au.Types/
│ │ ├── ColorInt.cs
│ │ ├── JSettings.cs
│ │ ├── TreeBase.cs
│ │ ├── common.cs
│ │ ├── exceptions.cs
│ │ ├── param types.cs
│ │ ├── structs.cs
│ │ └── unused/
│ │ └── AuClassless.cs
│ ├── Au.cs
│ ├── Au.csproj
│ ├── Ext/
│ │ ├── Bitmap.Resize.cs
│ │ ├── ExtMisc.cs
│ │ ├── ExtWpf.cs
│ │ └── ExtXml.cs
│ ├── Files, data/
│ │ ├── ExplorerFolder.cs
│ │ ├── FileSystemRedirection.cs
│ │ ├── FileTree.cs
│ │ ├── FileWatcher.cs
│ │ ├── Pidl.cs
│ │ ├── TempFile.cs
│ │ ├── filesystem-types.cs
│ │ ├── filesystem.cs
│ │ ├── filesystem.more.cs
│ │ ├── folders.cs
│ │ ├── icon.cs
│ │ ├── pathname.cs
│ │ ├── shortcutFile.cs
│ │ ├── sqlite.cs
│ │ └── sqlite_api.cs
│ ├── GUI/
│ │ ├── EnumUI.cs
│ │ ├── dialog-static.cs
│ │ ├── dialog-types.cs
│ │ ├── dialog-x-obsolete.cs
│ │ ├── dialog.cs
│ │ ├── osd.cs
│ │ ├── popupMenu/
│ │ │ ├── MTBase.cs
│ │ │ ├── pm acc.cs
│ │ │ ├── pm render.cs
│ │ │ ├── pm types.cs
│ │ │ └── popupMenu.cs
│ │ ├── toolbar/
│ │ │ ├── tb acc.cs
│ │ │ ├── tb dialog.cs
│ │ │ ├── tb man.cs
│ │ │ ├── tb render.cs
│ │ │ ├── tb sat.cs
│ │ │ ├── tb types.cs
│ │ │ ├── tb util.cs
│ │ │ └── toolbar.cs
│ │ ├── trayIcon.cs
│ │ ├── wpf-types.cs
│ │ └── wpfBuilder.cs
│ ├── Input/
│ │ ├── RegisteredHotkey.cs
│ │ ├── clipboard.cs
│ │ ├── clipboardData.cs
│ │ ├── inputBlocker.cs
│ │ ├── keys.cs
│ │ ├── keys.more.cs
│ │ ├── keys_static.cs
│ │ ├── keys_types.cs
│ │ ├── keys_util.cs
│ │ ├── miscInfo.cs
│ │ ├── mouse.cs
│ │ └── mouse_types.cs
│ ├── Internal/
│ │ ├── ActCtx_.cs
│ │ ├── ArrayBuilder_.cs
│ │ ├── AssemblyUtil_.cs
│ │ ├── AttachThreadInput_.cs
│ │ ├── Debug_.cs
│ │ ├── GC_.cs
│ │ ├── GDI misc.cs
│ │ ├── Handle_.cs
│ │ ├── ILReader.cs
│ │ ├── IconString_.cs
│ │ ├── Jit_.cs
│ │ ├── LaDebugger_.cs
│ │ ├── LineWriter_.cs
│ │ ├── MiniProgram_.cs
│ │ ├── NamespaceDoc.cs
│ │ ├── NativeFont_.cs
│ │ ├── NativeScrollbar_.cs
│ │ ├── NativeThread_.cs
│ │ ├── PostToThisThread_.cs
│ │ ├── ProcessStarter_.cs
│ │ ├── Ptr_.cs
│ │ ├── Serializer_.cs
│ │ ├── SharedMemory_.cs
│ │ ├── StaTaskScheduler_.cs
│ │ ├── StringBuilder_.cs
│ │ ├── Util_.cs
│ │ ├── misc_.cs
│ │ └── tables.cs
│ ├── Other/
│ │ ├── PrintServer.cs
│ │ ├── ScriptEditor.cs
│ │ ├── internet.cs
│ │ ├── lastError.cs
│ │ ├── opt.cs
│ │ ├── print.cs
│ │ ├── screen.cs
│ │ ├── script+.cs
│ │ └── script.cs
│ ├── Resources/
│ │ ├── AssemblyInfo.cs
│ │ └── red_cross_cursor.cur
│ ├── String/
│ │ ├── ExtString.cs
│ │ ├── SegParser.cs
│ │ ├── StringUtil.cs
│ │ ├── csvTable.cs
│ │ ├── regexp.cs
│ │ ├── regexp_ExtString.cs
│ │ ├── regexp_types.cs
│ │ └── wildcard.cs
│ ├── System/
│ │ ├── CpuUsage.cs
│ │ ├── ProcessMemory.cs
│ │ ├── computer.cs
│ │ ├── consoleProcess.cs
│ │ ├── osVersion.cs
│ │ ├── process.cs
│ │ ├── run.cs
│ │ ├── sound.cs
│ │ └── uacInfo.cs
│ ├── Time/
│ │ ├── WaitLoop.cs
│ │ ├── perf.cs
│ │ ├── timer.cs
│ │ ├── timer2.cs
│ │ ├── wait.cs
│ │ └── wait_for.cs
│ ├── Triggers/
│ │ ├── Trigger.cs
│ │ ├── Triggers.cs
│ │ ├── TriggersListWindow.cs
│ │ ├── Triggers_actions.cs
│ │ ├── Triggers_hooks.cs
│ │ ├── Triggers_util.cs
│ │ └── Types/
│ │ ├── t-autotext.cs
│ │ ├── t-hotkey.cs
│ │ ├── t-mouse.cs
│ │ └── t-window.cs
│ ├── UI objects/
│ │ ├── CaptureScreen.cs
│ │ ├── OcrGoogleCloud.cs
│ │ ├── OcrMicrosoftAzure.cs
│ │ ├── OcrTesseract.cs
│ │ ├── OcrWin10.cs
│ │ ├── elm.cs
│ │ ├── elmFinder.cs
│ │ ├── elm_func.cs
│ │ ├── elm_types.cs
│ │ ├── ocr.cs
│ │ ├── ocrFinder.cs
│ │ ├── ocr_types.cs
│ │ ├── uiimage.cs
│ │ ├── uiimageFinder.cs
│ │ └── uiimage_types.cs
│ ├── resources/
│ │ └── global2.cs
│ ├── wnd/
│ │ ├── WProp.cs
│ │ ├── WTaskbarButton.cs
│ │ ├── WndCopyData.cs
│ │ ├── WndSavedRect.cs
│ │ ├── WndUtil.cs
│ │ ├── inactive/
│ │ │ └── desktop.cs
│ │ ├── wnd.cs
│ │ ├── wndChildFinder.cs
│ │ ├── wndFinder.cs
│ │ ├── wnd_child.cs
│ │ ├── wnd_find.cs
│ │ ├── wnd_fromxy.cs
│ │ ├── wnd_get.cs
│ │ ├── wnd_other.cs
│ │ ├── wnd_private.cs
│ │ └── wnd_wait.cs
│ └── x/
│ └── NuGet.md
├── Au.AppHost/
│ ├── AppHost.cpp
│ ├── Au.AppHost.vcxproj
│ ├── Au.AppHost.vcxproj.filters
│ ├── ResourceHacker.txt
│ └── coreclrhost.h
├── Au.Controls/
│ ├── Au.Controls.cs
│ ├── Au.Controls.csproj
│ ├── KMenuCommands/
│ │ ├── KMenuCommands+.cs
│ │ └── KMenuCommands.cs
│ ├── KPanels/
│ │ ├── FlexStackPanel.cs
│ │ ├── ILeaf.cs
│ │ ├── KPanels.cs
│ │ ├── _Floating.cs
│ │ ├── _Node.cs
│ │ ├── dock.cs
│ │ ├── stack.cs
│ │ └── tab.cs
│ ├── KScintilla/
│ │ ├── KScintilla.cs
│ │ ├── Sci API.cs
│ │ ├── Sci adapter.cs
│ │ ├── Sci loader.cs
│ │ ├── Sci other.cs
│ │ ├── Sci styles.cs
│ │ ├── Sci text.cs
│ │ ├── SciImages.cs
│ │ ├── SciTags.cs
│ │ ├── SciTextBuilder.cs
│ │ └── other/
│ │ └── KSciInfoBox.cs
│ ├── KTreeView/
│ │ ├── KTreeView.cs
│ │ ├── tv-acc.cs
│ │ ├── tv-hh.cs
│ │ ├── tv-misc.cs
│ │ ├── tv-render.cs
│ │ └── tv-types.cs
│ ├── Simple/
│ │ ├── KCheckBox.cs
│ │ ├── KCheckDropdownBox.cs
│ │ ├── KColorPicker.cs
│ │ ├── KDateTime.cs
│ │ ├── KDialogWindow.cs
│ │ ├── KGroupBox.cs
│ │ ├── KHotkeyControl.cs
│ │ ├── KListBoxItemWithImage.cs
│ │ ├── KPasswordBox.cs
│ │ ├── KPopup.cs
│ │ ├── KPopupListBox.cs
│ │ ├── KScreenComboBox.cs
│ │ ├── KTextBox.cs
│ │ └── KWpfMenu.cs
│ ├── Util, Api/
│ │ ├── HwndHostAccessibleBase_.cs
│ │ ├── KApi.cs
│ │ ├── KExtWpf.cs
│ │ └── KImageUtil.cs
│ └── resources/
│ ├── AssemblyInfo.cs
│ ├── Generic.xaml
│ └── XamlResources.cs
├── Au.Editor/
│ ├── App/
│ │ ├── App-resources.xaml
│ │ ├── App.TrayIcon.cs
│ │ ├── App.cs
│ │ ├── AppSettings.cs
│ │ ├── CommandLine.cs
│ │ ├── DOptions.cs
│ │ ├── MainWindow.cs
│ │ └── Menus.cs
│ ├── Au.Editor.cs
│ ├── Au.Editor.csproj
│ ├── Compiler/
│ │ ├── Compiler.cs
│ │ ├── EditorExtension.cs
│ │ ├── ErrBuilder.cs
│ │ ├── MetaComments.cs
│ │ ├── MetaReferences.cs
│ │ ├── RecentTT.cs
│ │ ├── Run task.cs
│ │ ├── TestInternal.cs
│ │ ├── XCompiled.cs
│ │ ├── XPublish.cs
│ │ └── util/
│ │ ├── CompilerUtil.cs
│ │ └── Compiler_resources.cs
│ ├── Debugger/
│ │ ├── PD-stack.cs
│ │ ├── PD-variables.cs
│ │ ├── PD._Debugger.cs
│ │ ├── PD._MiRecord.cs
│ │ ├── PanelBreakpoints.cs
│ │ └── PanelDebug.cs
│ ├── Default/
│ │ ├── Commands.xml
│ │ ├── Layout.xml
│ │ ├── Snippets.xml
│ │ └── Themes/
│ │ ├── Material dark.csv
│ │ ├── One Monokai dark.csv
│ │ └── Visual Studio dark.csv
│ ├── Edit/
│ │ ├── Ci-types.cs
│ │ ├── CiAutocorrect.cs
│ │ ├── CiCompletion.cs
│ │ ├── CiErrors.cs
│ │ ├── CiFind.cs
│ │ ├── CiFindGo.cs
│ │ ├── CiFolding.cs
│ │ ├── CiGoTo.cs
│ │ ├── CiPopupList.cs
│ │ ├── CiPopupText.cs
│ │ ├── CiProjects.cs
│ │ ├── CiQuickInfo.cs
│ │ ├── CiSignature.cs
│ │ ├── CiSnippets.cs
│ │ ├── CiStyling.cs
│ │ ├── CiText.cs
│ │ ├── CiTools.cs
│ │ ├── CiUtil.cs
│ │ ├── CiUtilExt.cs
│ │ ├── CiWinapi.cs
│ │ ├── CiWorkspace.cs
│ │ ├── CodeExporter.cs
│ │ ├── CodeInfo.cs
│ │ ├── EditGoBack.cs
│ │ ├── GenerateCode.cs
│ │ ├── InsertCode.cs
│ │ ├── ModifyCode.cs
│ │ ├── PanelEdit.cs
│ │ ├── Sci-DD.cs
│ │ ├── Sci-TR.cs
│ │ ├── Sci-images.cs
│ │ ├── SciCode.cs
│ │ ├── SciTheme.cs
│ │ └── SciUndo.cs
│ ├── Files/
│ │ ├── DProperties.cs
│ │ ├── FileNode.cs
│ │ ├── Files+.cs
│ │ ├── FilesModel.cs
│ │ ├── FilesView.cs
│ │ ├── Git.cs
│ │ ├── Save.cs
│ │ ├── SyncWithFilesystem.cs
│ │ └── WorkspaceState.cs
│ ├── LibreAutomate.iss
│ ├── Panels/
│ │ ├── PanelBookmarks.cs
│ │ ├── PanelFiles.cs
│ │ ├── PanelFind.cs
│ │ ├── PanelFound.cs
│ │ ├── PanelHelp.cs
│ │ ├── PanelMouse.cs
│ │ ├── PanelOpen.cs
│ │ ├── PanelOutline.cs
│ │ ├── PanelOutput.cs
│ │ ├── PanelRead.cs
│ │ ├── PanelTasks.cs
│ │ └── Panels.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Test.cs
│ ├── Tools/
│ │ ├── CapturingWithHotkey.cs
│ │ ├── ColorQuantizer.cs
│ │ ├── DCustomize.cs
│ │ ├── DCustomizeContextMenu.cs
│ │ ├── DEnumFiles.cs
│ │ ├── DIcons.cs
│ │ ├── DInputRecorder.cs
│ │ ├── DPortable.cs
│ │ ├── DPwnd.cs
│ │ ├── DSnippets.cs
│ │ ├── DWinapi.cs
│ │ ├── Delm.cs
│ │ ├── Dnuget.cs
│ │ ├── Docr.cs
│ │ ├── Duiimage.cs
│ │ ├── Dwnd.cs
│ │ ├── InfoWindow.cs
│ │ ├── KSciCodeBox.cs
│ │ ├── KSciCodeBoxWnd.cs
│ │ ├── KTextExpressionBox.cs
│ │ ├── KeysWindow.cs
│ │ ├── QuickCapture.cs
│ │ ├── RegexWindow.cs
│ │ ├── Scripting.cs
│ │ ├── TUtil-main-process.cs
│ │ ├── TUtil.cs
│ │ ├── ToolProcess.cs
│ │ └── WindowFindCodeFormatter.cs
│ ├── Triggers and toolbars/
│ │ ├── DCommandline.cs
│ │ ├── DSchedule.cs
│ │ ├── TT-tb.cs
│ │ ├── TT-triggers.cs
│ │ ├── TT._CodeAnalysis.cs
│ │ ├── TriggersAndToolbars.cs
│ │ └── WinScheduler.cs
│ ├── _prePostBuild.cs
│ ├── resources/
│ │ ├── AssemblyInfo.cs
│ │ ├── Au.manifest
│ │ └── ci/
│ │ ├── Class.xaml
│ │ ├── Constant.xaml
│ │ ├── Delegate.xaml
│ │ ├── Enum.xaml
│ │ ├── EnumMember.xaml
│ │ ├── Event.xaml
│ │ ├── ExpandScope.xaml
│ │ ├── ExtensionMethod.xaml
│ │ ├── Field.xaml
│ │ ├── GroupBy.xaml
│ │ ├── Interface.xaml
│ │ ├── Keyword.xaml
│ │ ├── Label.xaml
│ │ ├── LocalMethod.xaml
│ │ ├── LocalVariable.xaml
│ │ ├── Method.xaml
│ │ ├── Namespace.xaml
│ │ ├── Operator.xaml
│ │ ├── OverlayAbstract.xaml
│ │ ├── OverlayInternal.xaml
│ │ ├── OverlayPrivate.xaml
│ │ ├── OverlayProtected.xaml
│ │ ├── OverlayStatic.xaml
│ │ ├── Property.xaml
│ │ ├── Region.xaml
│ │ ├── Snippet.xaml
│ │ ├── Structure.xaml
│ │ └── TypeParameter.xaml
│ ├── xAI/
│ │ ├── AI search.cs
│ │ ├── AiModel.cs
│ │ ├── McpServer.cs
│ │ └── McpTools.cs
│ ├── xMisc/
│ │ ├── EnvVarUpdater.cs
│ │ ├── Pip.cs
│ │ ├── PipIPC.cs
│ │ ├── RegHotkeys.cs
│ │ └── UacDragDrop.cs
│ └── xUtil/
│ ├── Downloader.cs
│ ├── Ed util shared.cs
│ ├── Ed util.cs
│ ├── EdExt.cs
│ ├── EdIcons.cs
│ ├── Libs/
│ │ ├── DiffMatchPatch.cs
│ │ └── EnglishPorter2Stemmer.cs
│ ├── MetaCommentsParser.cs
│ ├── NugetDownloader.cs
│ └── RegexParser.cs
├── Au.sln
├── Cookbook/
│ └── files.xml
├── Cpp/
│ ├── Cpp.cpp
│ ├── Cpp.def
│ ├── Cpp.h
│ ├── Cpp.manifest
│ ├── Cpp.rc
│ ├── Cpp.vcxproj
│ ├── Cpp.vcxproj.filters
│ ├── IAccessible2.h
│ ├── ISimpleDOMDocument.h
│ ├── ISimpleDOMNode.h
│ ├── ISimpleDOMText.h
│ ├── JAB.h
│ ├── MemoryPool.cpp
│ ├── MemoryPool.h
│ ├── Util.cpp
│ ├── acc bridge.cpp
│ ├── acc find.cpp
│ ├── acc func.cpp
│ ├── acc get.cpp
│ ├── acc java.cpp
│ ├── acc uia.cpp
│ ├── acc web.cpp
│ ├── acc workaround.cpp
│ ├── acc.h
│ ├── in-proc.cpp
│ ├── internal.h
│ ├── other.cpp
│ ├── rejected.cpp
│ ├── resource.h
│ ├── stdafx.cpp
│ ├── stdafx.h
│ ├── str.cpp
│ ├── str.h
│ ├── test Uia.cpp
│ ├── test.cpp
│ └── util.h
├── LICENSE.txt
├── Libraries/
│ ├── PCRE/
│ │ ├── PCRE.vcxproj
│ │ ├── PCRE.vcxproj.filters
│ │ ├── config.h
│ │ ├── pcre2.h
│ │ ├── pcre2_auto_possess.c
│ │ ├── pcre2_chartables.c
│ │ ├── pcre2_chkdint.c
│ │ ├── pcre2_compile.c
│ │ ├── pcre2_compile.h
│ │ ├── pcre2_compile_class.c
│ │ ├── pcre2_config.c
│ │ ├── pcre2_context.c
│ │ ├── pcre2_error.c
│ │ ├── pcre2_extuni.c
│ │ ├── pcre2_find_bracket.c
│ │ ├── pcre2_internal.h
│ │ ├── pcre2_intmodedep.h
│ │ ├── pcre2_maketables.c
│ │ ├── pcre2_match.c
│ │ ├── pcre2_match_data.c
│ │ ├── pcre2_newline.c
│ │ ├── pcre2_ord2utf.c
│ │ ├── pcre2_pattern_info.c
│ │ ├── pcre2_script_run.c
│ │ ├── pcre2_serialize.c
│ │ ├── pcre2_string_utils.c
│ │ ├── pcre2_study.c
│ │ ├── pcre2_substring.c
│ │ ├── pcre2_tables.c
│ │ ├── pcre2_ucd.c
│ │ ├── pcre2_ucp.h
│ │ ├── pcre2_ucptables.c
│ │ ├── pcre2_util.h
│ │ ├── pcre2_valid_utf.c
│ │ └── pcre2_xclass.c
│ └── scintilla/
│ ├── .editorconfig
│ ├── include/
│ │ ├── ILexer.h
│ │ ├── ILoader.h
│ │ ├── Sci_Position.h
│ │ ├── Scintilla.iface
│ │ ├── ScintillaCall.h
│ │ ├── ScintillaMessages.h
│ │ ├── ScintillaStructures.h
│ │ ├── ScintillaTypes.h
│ │ ├── ScintillaWidget.h
│ │ └── scintilla.h
│ ├── src/
│ │ ├── AutoComplete.cxx
│ │ ├── AutoComplete.h
│ │ ├── CallTip.cxx
│ │ ├── CallTip.h
│ │ ├── CaseConvert.cxx
│ │ ├── CaseConvert.h
│ │ ├── CaseFolder.cxx
│ │ ├── CaseFolder.h
│ │ ├── CellBuffer.cxx
│ │ ├── CellBuffer.h
│ │ ├── ChangeHistory.cxx
│ │ ├── ChangeHistory.h
│ │ ├── CharClassify.cxx
│ │ ├── CharClassify.h
│ │ ├── CharacterCategoryMap.cxx
│ │ ├── CharacterCategoryMap.h
│ │ ├── CharacterType.cxx
│ │ ├── CharacterType.h
│ │ ├── ContractionState.cxx
│ │ ├── ContractionState.h
│ │ ├── DBCS.cxx
│ │ ├── DBCS.h
│ │ ├── Debugging.h
│ │ ├── Decoration.cxx
│ │ ├── Decoration.h
│ │ ├── Document.cxx
│ │ ├── Document.h
│ │ ├── EditModel.cxx
│ │ ├── EditModel.h
│ │ ├── EditView.cxx
│ │ ├── EditView.h
│ │ ├── Editor.cxx
│ │ ├── Editor.h
│ │ ├── ElapsedPeriod.h
│ │ ├── Geometry.cxx
│ │ ├── Geometry.h
│ │ ├── Indicator.cxx
│ │ ├── Indicator.h
│ │ ├── KeyMap.cxx
│ │ ├── KeyMap.h
│ │ ├── LineMarker.cxx
│ │ ├── LineMarker.h
│ │ ├── MarginView.cxx
│ │ ├── MarginView.h
│ │ ├── Partitioning.h
│ │ ├── PerLine.cxx
│ │ ├── PerLine.h
│ │ ├── Platform.h
│ │ ├── Position.h
│ │ ├── PositionCache.cxx
│ │ ├── PositionCache.h
│ │ ├── RESearch.cxx
│ │ ├── RESearch.h
│ │ ├── RunStyles.cxx
│ │ ├── RunStyles.h
│ │ ├── ScintillaBase.cxx
│ │ ├── ScintillaBase.h
│ │ ├── Selection.cxx
│ │ ├── Selection.h
│ │ ├── SparseVector.h
│ │ ├── SplitVector.h
│ │ ├── Style.cxx
│ │ ├── Style.h
│ │ ├── UndoHistory.cxx
│ │ ├── UndoHistory.h
│ │ ├── UniConversion.cxx
│ │ ├── UniConversion.h
│ │ ├── UniqueString.cxx
│ │ ├── UniqueString.h
│ │ ├── ViewStyle.cxx
│ │ ├── ViewStyle.h
│ │ ├── XPM.cxx
│ │ └── XPM.h
│ ├── version.txt
│ └── win32/
│ ├── HanjaDic.cxx
│ ├── HanjaDic.h
│ ├── ListBox.cxx
│ ├── ListBox.h
│ ├── PlatWin.cxx
│ ├── PlatWin.h
│ ├── ScintRes.rc
│ ├── Scintilla.def
│ ├── Scintilla.vcxproj
│ ├── ScintillaDLL.cxx
│ ├── ScintillaWin.cxx
│ ├── ScintillaWin.h
│ ├── SurfaceD2D.cxx
│ ├── SurfaceD2D.h
│ ├── SurfaceGDI.cxx
│ ├── SurfaceGDI.h
│ └── WinTypes.h
├── Notes.txt
├── Other/
│ ├── Au.DllHost/
│ │ ├── Au.DllHost.vcxproj
│ │ ├── Au.DllHost.vcxproj.filters
│ │ └── DllHost.c
│ ├── Au.Net4/
│ │ ├── App.config
│ │ ├── Au.Net4.csproj
│ │ ├── Net4.cs
│ │ ├── Properties/
│ │ │ └── AssemblyInfo.cs
│ │ └── TypelibConverter.cs
│ ├── BuildEvents/
│ │ ├── Au.TestInternal.cs
│ │ ├── BuildEvents.cs
│ │ ├── BuildEvents.csproj
│ │ ├── GitBinaryFiles.cs
│ │ ├── Readme - Roslyn.txt
│ │ └── Sftp.cs
│ ├── DatabasesEtc/
│ │ ├── DatabasesEtc.csproj
│ │ ├── Icons.cs
│ │ ├── Program.cs
│ │ ├── RefAndDoc.cs
│ │ └── RefTxt.cs
│ └── DocFX/
│ ├── DocFX.csproj
│ └── _doc/
│ ├── .gitignore
│ ├── api/
│ │ ├── .gitignore
│ │ └── index.md
│ ├── articles/
│ │ ├── Caller info parameter.md
│ │ ├── Key names and operators.md
│ │ ├── Library.md
│ │ ├── Output tags.md
│ │ ├── UAC.md
│ │ ├── UI element issues.md
│ │ ├── Wait timeout.md
│ │ ├── Wildcard expression.md
│ │ ├── index.md
│ │ └── toc.yml
│ ├── changes/
│ │ ├── future.md
│ │ ├── old/
│ │ │ ├── v0.1.md
│ │ │ ├── v0.10.md
│ │ │ ├── v0.11.md
│ │ │ ├── v0.12.md
│ │ │ ├── v0.13.md
│ │ │ ├── v0.14.md
│ │ │ ├── v0.15.md
│ │ │ ├── v0.16.md
│ │ │ ├── v0.17.md
│ │ │ ├── v0.18.md
│ │ │ ├── v0.19.md
│ │ │ ├── v0.2.md
│ │ │ ├── v0.3.md
│ │ │ ├── v0.4.md
│ │ │ ├── v0.5.md
│ │ │ ├── v0.6.md
│ │ │ ├── v0.7.md
│ │ │ ├── v0.8.md
│ │ │ ├── v0.9.md
│ │ │ ├── v1.0.md
│ │ │ ├── v1.1.md
│ │ │ ├── v1.2.md
│ │ │ ├── v1.3.md
│ │ │ ├── v1.4.md
│ │ │ ├── v1.5.md
│ │ │ ├── v1.6.md
│ │ │ ├── v1.7.md
│ │ │ ├── v1.8.md
│ │ │ └── v1.9.md
│ │ ├── template.md
│ │ ├── v1.10.md
│ │ ├── v1.11.md
│ │ ├── v1.12.md
│ │ ├── v1.13.md
│ │ ├── v1.14.md
│ │ └── v1.15.md
│ ├── docfx.json
│ ├── editor/
│ │ ├── Application.md
│ │ ├── Class files, projects.md
│ │ ├── Code editor.md
│ │ ├── Command line.md
│ │ ├── Compared with QM.md
│ │ ├── Creating exe programs.md
│ │ ├── Debugger.md
│ │ ├── File properties.md
│ │ ├── Git, backup, sync.md
│ │ ├── Icons.md
│ │ ├── LA and AI.md
│ │ ├── Menu commands.md
│ │ ├── NuGet.md
│ │ ├── PiP session.md
│ │ ├── Portable app.md
│ │ ├── Scripts.md
│ │ ├── Settings.md
│ │ ├── Snippets.md
│ │ └── toc.yml
│ ├── filter.yml
│ ├── index.md
│ ├── md-styles.css
│ ├── misc/
│ │ └── privacy-policy.md
│ ├── template1/
│ │ ├── layout/
│ │ │ └── _master.tmpl
│ │ ├── mod.txt
│ │ ├── partials/
│ │ │ ├── class.header.tmpl.partial
│ │ │ ├── footer.tmpl.partial
│ │ │ ├── logo.tmpl.partial
│ │ │ ├── namespace.tmpl.partial
│ │ │ └── navbar.tmpl.partial
│ │ └── styles/
│ │ ├── main.css
│ │ └── main.js
│ ├── template2/
│ │ └── partials/
│ │ ├── collection.tmpl.partial
│ │ └── item.tmpl.partial
│ └── toc.yml
├── README.md
├── Scripts/
│ ├── @Au docs/
│ │ ├── Au docs.cs
│ │ ├── AuDocs analyze.cs
│ │ ├── AuDocs cookbook.cs
│ │ ├── AuDocs other tasks.cs
│ │ ├── AuDocs text.cs
│ │ ├── AuDocs.cs
│ │ ├── LA docs doc-html.db.cs
│ │ ├── LA docs toc.json.cs
│ │ ├── LA menu doc.cs
│ │ └── Readme.txt
│ ├── @WinAPI converter/
│ │ ├── WinAPI converter.cs
│ │ ├── WinApiConverter+.cs
│ │ └── WinApiConverter.cs
│ ├── AuDocsLib.cs
│ ├── Create NuGet package.cs
│ ├── LA docs for AI/
│ │ ├── AI summaries.cs
│ │ └── Upload AI embeddings.cs
│ ├── Minimal .NET SDK.cs
│ ├── Update version.txt.cs
│ └── old/
│ ├── VS goto.cs
│ └── Windows SDK to C#/
│ ├── @SDK converter/
│ │ ├── Classes.cs
│ │ ├── Constants.cs
│ │ ├── Converter.cs
│ │ ├── Expr.cs
│ │ ├── Functions.cs
│ │ ├── Parameters.cs
│ │ ├── SDK converter.cs
│ │ ├── Tokenize.cs
│ │ ├── Types.cs
│ │ └── Util.cs
│ ├── Readme.txt
│ └── Scripts/
│ ├── SDK append 32-bit diff.cs
│ ├── SDK create database.cs
│ ├── SDK headers.h
│ ├── SDK preprocessor.cs
│ ├── SdkUtil.cs
│ └── once/
│ ├── SDK get GUID.cs
│ └── SDK get dll names.cs
├── _/
│ ├── default.exe.manifest
│ ├── dotnet_ref_editor.txt
│ ├── dotnet_ref_task.txt
│ └── gitBinaryRestore.csv
└── global.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
[*.{cs,vb}]
# IDE0032: Use auto property
dotnet_style_prefer_auto_properties = false:silent
# CA1416: Validate platform compatibility
dotnet_diagnostic.CA1416.severity = none
# CA1806: Do not ignore method results
dotnet_diagnostic.CA1806.severity = silent
# CA2014: Do not use stackalloc in loops
dotnet_diagnostic.CA2014.severity = warning
# CS1573: Parameter has no matching param tag in the XML comment (but other parameters do)
dotnet_diagnostic.CS1573.severity = suggestion
[*]
charset = utf-8
[*.iss]
charset = utf-8-bom
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
/Libraries/** linguist-vendored
###############################################################################
================================================
FILE: .github/FUNDING.yml
================================================
github: qgindi
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
## _TODO: Comment the next line if you want to checkin your
## web deploy settings but do note that will include unencrypted
## passwords
#*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.snk
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml
# Au
/Other/Api/
/Libraries/PCRE/excluded/
/_/*
!/_/Default/
!/_/Templates/
!/_/*.manifest
!/_/dotnet_ref_*.txt
!/_/gitBinaryRestore.csv
!/_/32/
/_/32/*
!/_/32/7za.exe
/_/Default/*
!/_/Default/Workspace/
/_/Default/Workspace/*
!/_/Default/Workspace/files/
!/_/Default/Workspace/files.xml
!/_/Default/Workspace/state.db
/_/Templates/*
!/_/Templates/files/
!/_/Templates/files.xml
/Cookbook/*
!/Cookbook/files/
!/Cookbook/files.xml
*.bak
/Au/global.json
.tools/
/Other/DocFX/_doc/cookbook
/Au.Editor/Panels/PanelRead-WB.cs
================================================
FILE: Au/Api/Api.cs
================================================
//[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.System32|DllImportSearchPath.UserDirectories)]
#pragma warning disable 649, 169 //field never assigned/used
namespace Au.Types;
//[DebuggerStepThrough]
static unsafe partial class Api {
#region util
///
/// Gets the native size of a struct variable.
/// Returns Marshal.SizeOf(typeof(T)).
/// Speed: the same (in Release config) as Marshal.SizeOf(typeof(T)), and 2 times faster than Marshal.SizeOf(v).
///
internal static int SizeOf(T v) => Marshal.SizeOf();
///
/// Gets the native size of a type.
/// Returns Marshal.SizeOf(typeof(T)).
///
internal static int SizeOf() => Marshal.SizeOf();
///
/// Gets dll module handle (GetModuleHandle) or loads dll (NativeLibrary.TryLoad), and returns unmanaged exported function address (GetProcAddress).
/// See also: GetDelegate.
///
internal static IntPtr GetProcAddress(string dllName, string funcName) {
IntPtr hmod = GetModuleHandle(dllName);
if (hmod == default && !NativeLibrary.TryLoad(dllName, out hmod)) return default;
return GetProcAddress(hmod, funcName);
}
///
/// Calls (loads dll or gets handle) and .
///
internal static bool GetDelegate(out T deleg, string dllName, string funcName) where T : class {
IntPtr fa = GetProcAddress(dllName, funcName); if (fa == default) { deleg = null; return false; }
deleg = Marshal.GetDelegateForFunctionPointer(fa);
return deleg != null;
}
///
/// Calls API and .
///
internal static bool GetDelegate(out T deleg, IntPtr hModule, string funcName) where T : class {
deleg = null;
IntPtr fa = GetProcAddress(hModule, funcName); if (fa == default) return false;
deleg = Marshal.GetDelegateForFunctionPointer(fa);
return deleg != null;
}
///
/// If o is not null, calls .
///
internal static void ReleaseComObject(T o) where T : class {
if (o != null) Marshal.ReleaseComObject(o);
}
#endregion
#region gdi32
[DllImport("gdi32.dll")] //this and many other GDI functions don't use SetLastError
internal static extern bool DeleteObject(IntPtr ho);
[DllImport("gdi32.dll")]
internal static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2);
internal const int RGN_AND = 1;
internal const int RGN_OR = 2;
internal const int RGN_XOR = 3;
internal const int RGN_DIFF = 4;
internal const int RGN_COPY = 5;
[DllImport("gdi32.dll")]
internal static extern int CombineRgn(IntPtr hrgnDst, IntPtr hrgnSrc1, IntPtr hrgnSrc2, int iMode);
[DllImport("gdi32.dll")]
internal static extern bool SetRectRgn(IntPtr hrgn, int left, int top, int right, int bottom);
[DllImport("gdi32.dll")]
internal static extern IntPtr CreateRectRgnIndirect(in RECT lprect);
[DllImport("gdi32.dll")]
internal static extern bool PtInRegion(IntPtr hrgn, int x, int y);
[DllImport("gdi32.dll")]
internal static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
internal static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll")]
internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr h);
[DllImport("gdi32.dll", EntryPoint = "GetObjectW")]
internal static extern int GetObject(IntPtr h, int c, void* pv);
/// 1 transparent, 2 opaque.
[DllImport("gdi32.dll")]
internal static extern int SetBkMode(IntPtr hdc, int mode);
[DllImport("gdi32.dll")]
internal static extern int SetBkColor(IntPtr hdc, int color);
[DllImport("gdi32.dll", EntryPoint = "TextOutW")]
internal static extern bool TextOut(IntPtr hdc, int x, int y, string lpString, int c);
[DllImport("gdi32.dll", EntryPoint = "TextOutW")]
internal static extern bool TextOut(IntPtr hdc, int x, int y, char* lpString, int c);
[DllImport("gdi32.dll", EntryPoint = "ExtTextOutW")]
internal static extern bool ExtTextOut(IntPtr hdc, int x, int y, uint options, in RECT lprect, char* lpString, int c, int* lpDx = null);
internal const uint ETO_CLIPPED = 0x4;
[DllImport("gdi32.dll")]
internal static extern bool MoveToEx(IntPtr hdc, int x, int y, out POINT lppt);
[DllImport("gdi32.dll")]
internal static extern bool GetCurrentPositionEx(IntPtr hdc, out POINT lppt);
[DllImport("gdi32.dll")]
internal static extern uint SetTextAlign(IntPtr hdc, uint align);
[DllImport("gdi32.dll")]
internal static extern int SetTextColor(IntPtr hdc, int color);
[DllImport("gdi32.dll")]
internal static extern IntPtr CreatePen(int iStyle, int cWidth, int color);
[DllImport("gdi32.dll")]
internal static extern bool LineTo(IntPtr hdc, int x, int y);
[DllImport("gdi32.dll")] //tested: does not set last error
internal static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int cx, int cy);
[DllImport("gdi32.dll")]
internal static extern int GetDeviceCaps(IntPtr hdc, int index);
[DllImport("gdi32.dll", EntryPoint = "GetTextExtentPoint32W")]
internal static extern bool GetTextExtentPoint32(IntPtr hdc, string lpString, int c, out SIZE psizl);
[DllImport("gdi32.dll", EntryPoint = "GetTextExtentPoint32W")]
internal static extern bool GetTextExtentPoint32(IntPtr hdc, char* lpString, int c, out SIZE psizl);
[DllImport("gdi32.dll", EntryPoint = "CreateFontW")]
internal static extern IntPtr CreateFont(int cHeight, int cWidth = 0, int cEscapement = 0, int cOrientation = 0, int cWeight = 0, int bItalic = 0, int bUnderline = 0, int bStrikeOut = 0, int iCharSet = 0, int iOutPrecision = 0, int iClipPrecision = 0, int iQuality = 0, int iPitchAndFamily = 0, string pszFaceName = null);
[DllImport("gdi32.dll", EntryPoint = "CreateFontIndirectW")]
internal static extern IntPtr CreateFontIndirect(in LOGFONT lplf);
internal const uint SRCCOPY = 0xCC0020;
internal const uint CAPTUREBLT = 0x40000000;
[DllImport("gdi32.dll")] //tested: in some cases does not set last error even if returns false
internal static extern bool BitBlt(IntPtr hdc, int x, int y, int cx, int cy, IntPtr hdcSrc, int x1, int y1, uint rop);
[DllImport("gdi32.dll")]
internal static extern bool StretchBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSrc, int xSrc, int ySrc, int wSrc, int hSrc, uint rop);
[DllImport("gdi32.dll")]
internal static extern IntPtr CreateDIBSection(IntPtr hdc, in Api.BITMAPINFO pbmi, uint usage, out uint* ppvBits, IntPtr hSection = default, uint offset = 0);
internal struct BITMAPINFOHEADER {
public int biSize;
public int biWidth;
public int biHeight;
public ushort biPlanes;
public ushort biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
}
///
/// BITMAPINFOHEADER members and 3 uints for color table etc.
///
internal struct BITMAPINFO {
public readonly int biSize;
public int biWidth;
public int biHeight;
public ushort biPlanes;
public ushort biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
public fixed uint bmiColors[3]; //info: GetDIBits(DIB_RGB_COLORS) sets 0xFF0000, 0xFF00, 0xFF. Note: with 8-bit colors bitmaps need 256, but this library does not use it.
///
/// Sets biSize=sizeof(BITMAPINFOHEADER). Note: it is less than sizeof(BITMAPINFO).
///
public BITMAPINFO() { biSize = sizeof(BITMAPINFOHEADER); }
///
/// Sets width/height/bitcount/planes fields. Sets biSize=sizeof(BITMAPINFOHEADER). Note: it is less than sizeof(BITMAPINFO).
///
public BITMAPINFO(int width, int height, int bitCount = 32) {
biSize = sizeof(BITMAPINFOHEADER);
biWidth = width;
biHeight = height;
biBitCount = (ushort)bitCount;
biPlanes = 1;
}
//little tested
/////
///// Gets DIB bits of compatible bitmap. Uses API GetDIBits. Returns null if failed.
/////
///// Bitmap handle.
///// Create top-down DIB.
///// Eg .
///// Use DIB_PAL_COLORS.
//public byte[] GetBitmapBits(IntPtr hb, bool topDown, IntPtr dc, bool palColors = false) {
// biBitCount = 0; //biBitCount=(ushort)bitCount; //somehow fails if bitCount is 32 and bitmap is 32-bit
// if (0 == GetDIBits(dc, hb, 0, 0, null, ref this, palColors ? 1 : 0)) return null;
// var r = new byte[biSizeImage];
// fixed (byte* p = r) {
// int hei = biHeight; if (topDown) biHeight = -hei;
// var k = GetDIBits(dc, hb, 0, hei, p, ref this, palColors ? 1 : 0);
// biHeight = hei;
// if (k == 0) return null;
// }
// return r;
//}
}
[DllImport("gdi32.dll")]
internal static extern int GetDIBits(IntPtr hdc, IntPtr hbm, int start, int cLines, void* lpvBits, ref BITMAPINFO lpbmi, int usage);
///
/// lpbmi can be BITMAPINFOHEADER/BITMAPV5HEADER or BITMAPCOREHEADER.
///
[DllImport("gdi32.dll")]
internal static extern int SetDIBitsToDevice(IntPtr hdc, int xDest, int yDest, int w, int h, int xSrc, int ySrc, int StartScan, int cLines, void* lpvBits, void* lpbmi, uint ColorUse = 0); //DIB_RGB_COLORS
//internal const int WHITE_BRUSH = 0;
//internal const int LTGRAY_BRUSH = 1;
//internal const int GRAY_BRUSH = 2;
//internal const int DKGRAY_BRUSH = 3;
//internal const int BLACK_BRUSH = 4;
//internal const int NULL_BRUSH = 5;
//internal const int HOLLOW_BRUSH = 5;
//internal const int WHITE_PEN = 6;
//internal const int BLACK_PEN = 7;
//internal const int NULL_PEN = 8;
//internal const int OEM_FIXED_FONT = 10;
//internal const int ANSI_FIXED_FONT = 11;
//internal const int ANSI_VAR_FONT = 12;
//internal const int SYSTEM_FONT = 13;
//internal const int DEVICE_DEFAULT_FONT = 14;
//internal const int DEFAULT_PALETTE = 15;
//internal const int SYSTEM_FIXED_FONT = 16;
//internal const int DEFAULT_GUI_FONT = 17;
//internal const int DC_BRUSH = 18;
//internal const int DC_PEN = 19;
[DllImport("gdi32.dll")]
internal static extern IntPtr GetStockObject(int i);
[DllImport("gdi32.dll")]
internal static extern IntPtr CreateSolidBrush(int color);
[DllImport("gdi32.dll")]
internal static extern int IntersectClipRect(IntPtr hdc, int left, int top, int right, int bottom);
internal struct LOGFONT {
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public int lfWeight;
public byte lfItalic;
public byte lfUnderline;
public byte lfStrikeOut;
public byte lfCharSet;
public byte lfOutPrecision;
public byte lfClipPrecision;
public byte lfQuality;
public byte lfPitchAndFamily;
public fixed char lfFaceName[32];
}
internal struct NONCLIENTMETRICS {
public int cbSize;
public int iBorderWidth;
public int iScrollWidth;
public int iScrollHeight;
public int iCaptionWidth;
public int iCaptionHeight;
public LOGFONT lfCaptionFont;
public int iSmCaptionWidth;
public int iSmCaptionHeight;
public LOGFONT lfSmCaptionFont;
public int iMenuWidth;
public int iMenuHeight;
public LOGFONT lfMenuFont;
public LOGFONT lfStatusFont;
public LOGFONT lfMessageFont;
public int iPaddedBorderWidth;
}
[DllImport("gdi32.dll")] //does not set last error when fails
internal static extern uint GetPixel(IntPtr hdc, int x, int y);
#endregion
#region advapi32
[DllImport("advapi32.dll")]
internal static extern int RegSetValueEx(IntPtr hKey, string lpValueName, int Reserved, Microsoft.Win32.RegistryValueKind dwType, void* lpData, int cbData);
[DllImport("advapi32.dll")]
internal static extern int RegQueryValueEx(IntPtr hKey, string lpValueName, IntPtr Reserved, out Microsoft.Win32.RegistryValueKind dwType, void* lpData, ref int cbData);
internal const uint TOKEN_WRITE = STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT;
internal const uint TOKEN_SOURCE_LENGTH = 8;
internal const uint TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
internal const uint TOKEN_QUERY_SOURCE = 16;
internal const uint TOKEN_QUERY = 8;
internal const uint TOKEN_IMPERSONATE = 4;
internal const uint TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;
internal const uint TOKEN_DUPLICATE = 2;
internal const uint TOKEN_AUDIT_SUCCESS_INCLUDE = 1;
internal const uint TOKEN_AUDIT_SUCCESS_EXCLUDE = 2;
internal const uint TOKEN_AUDIT_FAILURE_INCLUDE = 4;
internal const uint TOKEN_AUDIT_FAILURE_EXCLUDE = 8;
internal const uint TOKEN_ASSIGN_PRIMARY = 1;
internal const uint TOKEN_ALL_ACCESS_P = STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT;
internal const uint TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;
internal const uint TOKEN_ADJUST_SESSIONID = 256;
internal const uint TOKEN_ADJUST_PRIVILEGES = 32;
internal const uint TOKEN_ADJUST_GROUPS = 64;
internal const uint TOKEN_ADJUST_DEFAULT = 128;
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out Handle_ TokenHandle);
internal enum TOKEN_INFORMATION_CLASS {
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
//Win8
TokenIsAppContainer,
TokenCapabilities,
TokenAppContainerSid,
TokenAppContainerNumber,
TokenUserClaimAttributes,
TokenDeviceClaimAttributes,
TokenRestrictedUserClaimAttributes,
TokenRestrictedDeviceClaimAttributes,
TokenDeviceGroups,
TokenRestrictedDeviceGroups,
TokenSecurityAttributes,
TokenIsRestricted,
TokenProcessTrustLevel,
TokenPrivateNameSpace,
MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum
}
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool GetTokenInformation(HandleRef TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, void* TokenInformation, uint TokenInformationLength, out uint ReturnLength);
[DllImport("advapi32.dll")]
internal static extern byte* GetSidSubAuthorityCount(IntPtr pSid);
[DllImport("advapi32.dll")]
internal static extern uint* GetSidSubAuthority(IntPtr pSid, uint nSubAuthority);
internal enum SECURITY_IMPERSONATION_LEVEL {
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
internal enum TOKEN_TYPE {
TokenPrimary = 1,
TokenImpersonation
}
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, SECURITY_ATTRIBUTES lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, TOKEN_TYPE TokenType, out IntPtr phNewToken);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool CreateProcessWithTokenW(IntPtr hToken, uint dwLogonFlags, string lpApplicationName, char[] lpCommandLine, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory, in STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
internal struct LUID {
public uint LowPart;
public int HighPart;
}
[DllImport("advapi32.dll", EntryPoint = "LookupPrivilegeValueW", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct LUID_AND_ATTRIBUTES {
public LUID Luid;
public uint Attributes;
}
internal struct TOKEN_PRIVILEGES {
public int PrivilegeCount;
public LUID_AND_ATTRIBUTES Privileges; //[1]
}
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, in TOKEN_PRIVILEGES NewState, uint BufferLength, [Out] TOKEN_PRIVILEGES[] PreviousState, IntPtr ReturnLength);
[StructLayout(LayoutKind.Sequential)]
internal sealed class SECURITY_ATTRIBUTES : IDisposable {
public int nLength;
public void* lpSecurityDescriptor;
public int bInheritHandle;
///
/// Creates SECURITY_ATTRIBUTES from string security descriptor.
/// securityDescriptor can be null; then lpSecurityDescriptor will be null.
///
public SECURITY_ATTRIBUTES(string securityDescriptor) {
nLength = IntPtr.Size * 3;
if (securityDescriptor != null && !ConvertStringSecurityDescriptorToSecurityDescriptor(securityDescriptor, 1, out lpSecurityDescriptor)) throw new AuException(0, "SECURITY_ATTRIBUTES");
}
public void Dispose() {
if (lpSecurityDescriptor != null) {
LocalFree(lpSecurityDescriptor);
lpSecurityDescriptor = null;
}
}
~SECURITY_ATTRIBUTES() => Dispose();
///
/// Creates SECURITY_ATTRIBUTES that allows UAC low IL processes to open the kernel object.
///
public static readonly SECURITY_ATTRIBUTES ForLowIL = new SECURITY_ATTRIBUTES("D:NO_ACCESS_CONTROLS:(ML;;NW;;;LW)");
///
/// Creates SECURITY_ATTRIBUTES that allows UAC medium IL processes to open the pipe.
/// Like of PipeSecurity that allows ReadWrite for AuthenticatedUserSid.
///
public static readonly SECURITY_ATTRIBUTES ForPipes = new SECURITY_ATTRIBUTES("D:(A;;0x12019b;;;AU)");
}
[DllImport("advapi32.dll", EntryPoint = "ConvertStringSecurityDescriptorToSecurityDescriptorW", SetLastError = true)]
internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string StringSecurityDescriptor, uint StringSDRevision, out void* SecurityDescriptor, uint* SecurityDescriptorSize = null);
//[DllImport("advapi32.dll", EntryPoint = "ConvertSecurityDescriptorToStringSecurityDescriptorW")]
//internal static extern bool ConvertSecurityDescriptorToStringSecurityDescriptor(void* SecurityDescriptor, uint RequestedStringSDRevision, uint SecurityInformation, out char* StringSecurityDescriptor, out uint StringSecurityDescriptorLen);
[DllImport("advapi32.dll", EntryPoint = "InitiateSystemShutdownW", SetLastError = true)]
internal static extern bool InitiateSystemShutdown(string lpMachineName, string lpMessage, int dwTimeout, bool bForceAppsClosed, bool bRebootAfterShutdown);
#endregion
#region shell32
//[DllImport("shell32.dll")]
//internal static extern bool IsUserAnAdmin();
internal const uint SHGFI_ICON = 0x000000100; // get icon;
internal const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name;
internal const uint SHGFI_TYPENAME = 0x000000400; // get type name;
internal const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes;
internal const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location;
internal const uint SHGFI_EXETYPE = 0x000002000; // return exe type;
internal const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index;
internal const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon;
internal const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state;
internal const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes;
internal const uint SHGFI_LARGEICON = 0x000000000; // get large icon;
internal const uint SHGFI_SMALLICON = 0x000000001; // get small icon;
internal const uint SHGFI_OPENICON = 0x000000002; // get open icon;
internal const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon;
internal const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl;
internal const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute;
internal const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays;
internal const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay;
internal struct SHFILEINFO {
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
}
//[DllImport("shell32.dll", EntryPoint = "SHGetFileInfoW")]
//internal static extern nint SHGetFileInfo(string pszPath, uint dwFileAttributes, out SHFILEINFO psfi, int cbFileInfo, uint uFlags);
[DllImport("shell32.dll", EntryPoint = "SHGetFileInfoW")]
static extern nint _SHGetFileInfo(nint pidl, uint dwFileAttributes, out SHFILEINFO psfi, int cbFileInfo, uint uFlags);
internal static nint SHGetFileInfo(nint pidl, out SHFILEINFO psfi, uint uFlags, uint dwFileAttributes = 0) {
return _SHGetFileInfo(pidl, dwFileAttributes, out psfi, Api.SizeOf(), uFlags);
}
internal static nint SHGetFileInfo(string path, out SHFILEINFO psfi, uint uFlags, uint dwFileAttributes = 0) {
//if (0 != (uFlags & SHGFI_PIDL)) { //not tested
// var pidl = Pidl.FromString_(path);
// if (pidl == 0) { psfi = default; return 0; }
// try { return _SHGetFileInfo(pidl, dwFileAttributes, out psfi, Api.SizeOf(), uFlags); }
// finally { Marshal.FreeCoTaskMem(pidl); }
//} else {
fixed (char* p = path) return _SHGetFileInfo((nint)p, dwFileAttributes, out psfi, Api.SizeOf(), uFlags);
//}
}
//[DllImport("shell32.dll")]
//internal static extern int SHGetDesktopFolder(out IShellFolder ppshf);
[DllImport("shell32.dll")]
internal static extern int SHParseDisplayName(string pszName, IntPtr pbc, out IntPtr pidl, uint sfgaoIn, uint* psfgaoOut);
[DllImport("shell32.dll")]
internal static extern int SHGetNameFromIDList(IntPtr pidl, SIGDN sigdnName, out string ppszName);
[DllImport("shell32.dll")]
internal static extern int SHCreateShellItem(IntPtr pidlParent, IShellFolder psfParent, IntPtr pidl, out IShellItem ppsi);
//This classic API supports absolute PIDL and parent+relative PIDL.
//There are 2 newer API - SHCreateItemFromIDList (absoulte) and SHCreateItemWithParent (parent+relative). They can get IShellItem2 too, which is currently not useful here. Same speed.
//[DllImport("shell32.dll")]
//internal static extern int SHCreateItemFromIDList(IntPtr pidl, in Guid riid, out IShellItem ppv); //or IShellItem2
[DllImport("shell32.dll")]
internal static extern int SHBindToParent(IntPtr pidl, in Guid riid, out IShellFolder ppv, out IntPtr ppidlLast);
[DllImport("shell32.dll")]
internal static extern int SHGetPropertyStoreForWindow(wnd hwnd, in Guid riid, out IPropertyStore ppv);
internal static PROPERTYKEY PKEY_AppUserModel_ID = new PROPERTYKEY() { fmtid = new Guid(0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3), pid = 5 };
[DllImport("shell32.dll")]
internal static extern char** CommandLineToArgvW(string lpCmdLine, out int pNumArgs);
[DllImport("shell32.dll", EntryPoint = "Shell_NotifyIconW", SetLastError = true)]
internal static extern bool Shell_NotifyIcon(int dwMessage, in NOTIFYICONDATA lpData);
internal const int NIM_ADD = 0x0;
internal const int NIM_MODIFY = 0x1;
internal const int NIM_DELETE = 0x2;
internal const int NIM_SETFOCUS = 0x3;
internal const int NIM_SETVERSION = 0x4;
internal const int NOTIFYICON_VERSION_4 = 4;
internal const uint NIF_MESSAGE = 0x1;
internal const uint NIF_ICON = 0x2;
internal const uint NIF_TIP = 0x4;
internal const uint NIF_STATE = 0x8;
internal const uint NIF_INFO = 0x10;
internal const uint NIF_GUID = 0x20;
internal const uint NIF_REALTIME = 0x40;
internal const uint NIF_SHOWTIP = 0x80;
internal const uint NIS_HIDDEN = 0x1;
internal const uint NIS_SHAREDICON = 0x2;
internal struct NOTIFYICONDATA {
///
/// Sets cbSize, hWnd and uFlags.
///
///
///
public NOTIFYICONDATA(wnd wNotify, uint nifFlags = 0) {
cbSize = Api.SizeOf();
hWnd = wNotify;
uFlags = nifFlags;
}
public int cbSize;
public wnd hWnd;
public int uID;
public uint uFlags;
public int uCallbackMessage;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string szTip;
public uint dwState;
public uint dwStateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string szInfo;
public int uVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public string szInfoTitle;
public uint dwInfoFlags;
public Guid guidItem;
public IntPtr hBalloonIcon;
}
internal const int NIN_SELECT = 0x400;
internal const int NIN_KEYSELECT = 0x401;
internal const int NIN_BALLOONSHOW = 0x402;
internal const int NIN_BALLOONHIDE = 0x403;
internal const int NIN_BALLOONTIMEOUT = 0x404;
internal const int NIN_BALLOONUSERCLICK = 0x405;
internal const int NIN_POPUPOPEN = 0x406;
internal const int NIN_POPUPCLOSE = 0x407;
[DllImport("shell32.dll")]
internal static extern int Shell_NotifyIconGetRect(in NOTIFYICONIDENTIFIER identifier, out RECT iconLocation);
internal struct NOTIFYICONIDENTIFIER {
public int cbSize;
public wnd hWnd;
public int uID;
public Guid guidItem;
}
internal struct SHSTOCKICONINFO {
public int cbSize;
public IntPtr hIcon;
public int iSysImageIndex;
public int iIcon;
public fixed char szPath[260];
}
[DllImport("shell32.dll")]
internal static extern int SHGetStockIconInfo(StockIcon siid, uint uFlags, ref SHSTOCKICONINFO psii);
[DllImport("shell32.dll", EntryPoint = "#6")]
internal static extern int SHDefExtractIcon(string pszIconFile, int iIndex, uint uFlags, IntPtr* phiconLarge, IntPtr* phiconSmall, int nIconSize);
internal const int SHIL_LARGE = 0;
internal const int SHIL_SMALL = 1;
internal const int SHIL_EXTRALARGE = 2;
//internal const int SHIL_SYSSMALL = 3;
internal const int SHIL_JUMBO = 4;
//[DllImport("shell32.dll", EntryPoint = "#727")]
//internal static extern int SHGetImageList(int iImageList, in Guid riid, out IImageList ppvObj);
[DllImport("shell32.dll", EntryPoint = "#727")]
internal static extern int SHGetImageList(int iImageList, in Guid riid, out IntPtr ppvObj);
internal const uint SHCNE_RENAMEITEM = 0x1;
internal const uint SHCNE_CREATE = 0x2;
internal const uint SHCNE_DELETE = 0x4;
internal const uint SHCNE_MKDIR = 0x8;
internal const uint SHCNE_RMDIR = 0x10;
internal const uint SHCNE_MEDIAINSERTED = 0x20;
internal const uint SHCNE_MEDIAREMOVED = 0x40;
internal const uint SHCNE_DRIVEREMOVED = 0x80;
internal const uint SHCNE_DRIVEADD = 0x100;
internal const uint SHCNE_NETSHARE = 0x200;
internal const uint SHCNE_NETUNSHARE = 0x400;
internal const uint SHCNE_ATTRIBUTES = 0x800;
internal const uint SHCNE_UPDATEDIR = 0x1000;
internal const uint SHCNE_UPDATEITEM = 0x2000;
internal const uint SHCNE_SERVERDISCONNECT = 0x4000;
internal const uint SHCNE_UPDATEIMAGE = 0x8000;
internal const uint SHCNE_DRIVEADDGUI = 0x10000;
internal const uint SHCNE_RENAMEFOLDER = 0x20000;
internal const uint SHCNE_FREESPACE = 0x40000;
internal const uint SHCNE_EXTENDED_EVENT = 0x4000000;
internal const uint SHCNE_ASSOCCHANGED = 0x8000000;
internal const uint SHCNE_DISKEVENTS = 0x2381F;
internal const uint SHCNE_GLOBALEVENTS = 0xC0581E0;
internal const uint SHCNE_ALLEVENTS = 0x7FFFFFFF;
internal const uint SHCNE_INTERRUPT = 0x80000000;
internal const uint SHCNF_IDLIST = 0x0;
internal const uint SHCNF_DWORD = 0x3;
internal const uint SHCNF_PATH = 0x5;
internal const uint SHCNF_PRINTER = 0x6;
internal const uint SHCNF_FLUSH = 0x1000;
internal const uint SHCNF_FLUSHNOWAIT = 0x3000;
internal const uint SHCNF_NOTIFYRECURSIVE = 0x10000;
[DllImport("shell32.dll")]
internal static extern void SHChangeNotify(uint wEventId, uint uFlags, string dwItem1, string dwItem2);
internal const uint SEE_MASK_CONNECTNETDRV = 0x80;
internal const uint SEE_MASK_NOZONECHECKS = 0x800000;
internal const uint SEE_MASK_UNICODE = 0x4000;
internal const uint SEE_MASK_FLAG_NO_UI = 0x400;
internal const uint SEE_MASK_INVOKEIDLIST = 0xC;
internal const uint SEE_MASK_NOCLOSEPROCESS = 0x40;
internal const uint SEE_MASK_NOASYNC = 0x100;
internal const uint SEE_MASK_NO_CONSOLE = 0x8000;
//internal const uint SEE_MASK_HMONITOR = 0x200000;
//internal const uint SEE_MASK_WAITFORINPUTIDLE = 0x2000000;
internal const uint SEE_MASK_FLAG_LOG_USAGE = 0x4000000;
internal struct SHELLEXECUTEINFO {
public int cbSize;
public uint fMask;
public wnd hwnd;
public string lpVerb;
public string lpFile;
public string lpParameters;
public string lpDirectory;
public int nShow;
public IntPtr hInstApp;
public IntPtr lpIDList;
public string lpClass;
public IntPtr hkeyClass;
public uint dwHotKey;
public IntPtr hMonitor;
public Handle_ hProcess;
}
[DllImport("shell32.dll", EntryPoint = "ShellExecuteExW", SetLastError = true)]
internal static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO pExecInfo);
[DllImport("shell32.dll")]
internal static extern int SHOpenFolderAndSelectItems(HandleRef pidlFolder, uint cidl, IntPtr[] apidl, uint dwFlags);
[DllImport("shell32.dll", EntryPoint = "#152")]
internal static extern int ILGetSize(IntPtr pidl);
[DllImport("shell32.dll", EntryPoint = "#25")]
internal static extern IntPtr ILCombine(IntPtr pidl1, IntPtr pidl2);
[DllImport("shell32.dll", EntryPoint = "#21")]
internal static extern bool ILIsEqual(IntPtr pidl1, IntPtr pidl2);
internal const uint FO_MOVE = 0x1;
internal const uint FO_COPY = 0x2;
internal const uint FO_DELETE = 0x3;
internal const uint FO_RENAME = 0x4;
internal const uint FOF_MULTIDESTFILES = 0x1;
internal const uint FOF_CONFIRMMOUSE = 0x2;
internal const uint FOF_SILENT = 0x4;
internal const uint FOF_RENAMEONCOLLISION = 0x8;
internal const uint FOF_NOCONFIRMATION = 0x10;
internal const uint FOF_WANTMAPPINGHANDLE = 0x20;
internal const uint FOF_ALLOWUNDO = 0x40;
internal const uint FOF_FILESONLY = 0x80;
internal const uint FOF_SIMPLEPROGRESS = 0x100;
internal const uint FOF_NOCONFIRMMKDIR = 0x200;
internal const uint FOF_NOERRORUI = 0x400;
internal const uint FOF_NOCOPYSECURITYATTRIBS = 0x800;
internal const uint FOF_NORECURSION = 0x1000;
internal const uint FOF_NO_CONNECTED_ELEMENTS = 0x2000;
internal const uint FOF_WANTNUKEWARNING = 0x4000;
internal const uint FOF_NORECURSEREPARSE = 0x8000;
internal const uint FOF_NO_UI = 0x614;
internal struct SHFILEOPSTRUCT {
public wnd hwnd;
public uint wFunc;
public string pFrom;
public string pTo;
public ushort fFlags;
//workaround for this problem: the 32-bit version of SHFILEOPSTRUCT uses Pack = 1, ie no 2-byte gap after fFlags.
// I don't want to use two versions. Then also would need two versions of code that use this struct.
// The last two members are not useful, but we need fAnyOperationsAborted. This workaround gets it through a property function.
// Update: we don't need fAnyOperationsAborted. We use FOF_SILENT therefore cannot be aborted. And it is unreliable.
// But the workaround is tested, on both platformas.
#if use_fAnyOperationsAborted
//public bool fAnyOperationsAborted; //BOOL
ushort _fAnyOperationsAborted_32, _fAnyOperationsAborted_common, _fAnyOperationsAborted_64;
public bool fAnyOperationsAborted
{
get
{
if(_fAnyOperationsAborted_common != 0) return true;
var v = osVersion.is32BitProcess ? _fAnyOperationsAborted_32 : _fAnyOperationsAborted_64;
return v != 0;
}
}
#else
private int fAnyOperationsAborted;
#endif
private IntPtr hNameMappings;
private string lpszProgressTitle;
//these are private and not used, because would be at invalid offsets on 32-bit
}
//internal struct SHFILEOPSTRUCT
//{
// public wnd hwnd;
// public uint wFunc;
// public string pFrom;
// public string pTo;
// public ushort fFlags;
// public bool fAnyOperationsAborted;
// public IntPtr hNameMappings;
// public string lpszProgressTitle;
//}
//[StructLayout(LayoutKind.Sequential, Pack = 1)]
//internal struct SHFILEOPSTRUCT__32
//{
// public wnd hwnd;
// public uint wFunc;
// public string pFrom;
// public string pTo;
// public ushort fFlags;
// public bool fAnyOperationsAborted;
// public IntPtr hNameMappings;
// public string lpszProgressTitle;
//}
[DllImport("shell32.dll", EntryPoint = "SHFileOperationW")]
internal static extern int SHFileOperation(in SHFILEOPSTRUCT lpFileOp);
[DllImport("shell32.dll", EntryPoint = "DragQueryFileW")]
internal static extern int DragQueryFile(IntPtr hDrop, int iFile, char* lpszFile, int cch);
[DllImport("shell32.dll")]
internal static extern bool IsUserAnAdmin();
[DllImport("shell32.dll", EntryPoint = "SHEmptyRecycleBinW")]
internal static extern int SHEmptyRecycleBin(wnd hwnd, string pszRootPath, int dwFlags);
#endregion
#region shlwapi
[DllImport("shlwapi.dll")]
static extern int IUnknown_QueryService([MarshalAs(UnmanagedType.IUnknown)] object punk, in Guid guidService, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppvOut);
public static bool QueryService(object from, in Guid guidService, out T result) where T : class {
bool ok = 0 == IUnknown_QueryService(from, guidService, typeof(T).GUID, out var o);
result = ok ? (T)o : null;
return ok;
}
//[DllImport("shlwapi.dll")]
//internal static extern uint ColorAdjustLuma(uint clrRGB, int n, bool fScale);
[DllImport("shlwapi.dll")]
internal static extern void ColorRGBToHLS(int clrRGB, out ushort pwHue, out ushort pwLuminance, out ushort pwSaturation);
[DllImport("shlwapi.dll")]
internal static extern int ColorHLSToRGB(ushort wHue, ushort wLuminance, ushort wSaturation);
[DllImport("shlwapi.dll")]
internal static extern int PathCreateFromUrlAlloc(string pszIn, out string ppszOut, uint dwFlags = 0);
//internal enum ASSOCSTR
//{
// ASSOCSTR_COMMAND = 1,
// ASSOCSTR_EXECUTABLE,
// ASSOCSTR_FRIENDLYDOCNAME,
// ASSOCSTR_FRIENDLYAPPNAME,
// ASSOCSTR_NOOPEN,
// ASSOCSTR_SHELLNEWVALUE,
// ASSOCSTR_DDECOMMAND,
// ASSOCSTR_DDEIFEXEC,
// ASSOCSTR_DDEAPPLICATION,
// ASSOCSTR_DDETOPIC,
// ASSOCSTR_INFOTIP,
// ASSOCSTR_QUICKTIP,
// ASSOCSTR_TILEINFO,
// ASSOCSTR_CONTENTTYPE,
// ASSOCSTR_DEFAULTICON,
// ASSOCSTR_SHELLEXTENSION,
// ASSOCSTR_DROPTARGET,
// ASSOCSTR_DELEGATEEXECUTE,
// ASSOCSTR_SUPPORTED_URI_PROTOCOLS,
// ASSOCSTR_PROGID,
// ASSOCSTR_APPID,
// ASSOCSTR_APPPUBLISHER,
// ASSOCSTR_APPICONREFERENCE,
// ASSOCSTR_MAX
//}
//[DllImport("shlwapi.dll", EntryPoint = "AssocQueryStringW")]
//internal static extern int AssocQueryString(uint flags, /*ASSOCSTR*/ int str, string pszAssoc, string pszExtra, char[] pszOut, ref int pcchOut);
/////
///// Returns executable path of file type.
/////
/////
//internal static string AssocQueryString(string dotExt/*, ASSOCSTR what = ASSOCSTR.ASSOCSTR_EXECUTABLE*/)
//{
// var b = ApiBuffer_.Char_(300, out var n);
// int hr = AssocQueryString(0x20, 2, dotExt, null, b, ref n); //ASSOCF_NOTRUNCATE
// if(hr == E_POINTER) hr = AssocQueryString(0x20, 2, dotExt, null, b = ApiBuffer_.Char_(n), ref n);
// return hr == 0 ? b.ToString(n) : null;
//}
#endregion
#region comctl32
[DllImport("comctl32.dll")]
internal static extern IntPtr ImageList_GetIcon(IntPtr himl, int i, uint flags);
[DllImport("comctl32.dll")]
internal static extern bool ImageList_GetIconSize(IntPtr himl, out int cx, out int cy);
internal const uint TME_LEAVE = 0x2;
internal const uint TME_NONCLIENT = 0x10;
internal const uint TME_CANCEL = 0x80000000;
internal struct TRACKMOUSEEVENT {
public int cbSize;
public uint dwFlags;
public wnd hwndTrack;
public int dwHoverTime;
public TRACKMOUSEEVENT(wnd w, uint flags, int hoverTime = 0) {
cbSize = sizeof(TRACKMOUSEEVENT);
hwndTrack = w;
dwFlags = flags;
dwHoverTime = hoverTime;
}
}
[DllImport("comctl32.dll", EntryPoint = "_TrackMouseEvent")]
internal static extern bool TrackMouseEvent(ref TRACKMOUSEEVENT lpEventTrack);
///
/// Calls TrackMouseEvent with TME_LEAVE.
///
///
/// true to start, false to cancel.
internal static bool TrackMouseLeave(wnd w, bool track) {
var t = new TRACKMOUSEEVENT(w, track ? TME_LEAVE : TME_LEAVE | TME_CANCEL);
return TrackMouseEvent(ref t);
}
[DllImport("comctl32.dll", EntryPoint = "#380")]
internal static extern int LoadIconMetric(IntPtr hinst, nint pszName, int lims, out IntPtr phico);
[DllImport("comctl32.dll", EntryPoint = "#410")]
internal static extern bool SetWindowSubclass(wnd hWnd, SUBCLASSPROC pfnSubclass, nint uIdSubclass, nint dwRefData = 0);
internal delegate nint SUBCLASSPROC(wnd hWnd, int uMsg, nint wParam, nint lParam, nint uIdSubclass, nint dwRefData);
[DllImport("comctl32.dll", EntryPoint = "#413")]
internal static extern nint DefSubclassProc(wnd hWnd, int uMsg, nint wParam, nint lParam);
[DllImport("comctl32.dll", EntryPoint = "#412")]
internal static extern bool RemoveWindowSubclass(wnd hWnd, SUBCLASSPROC pfnSubclass, nint uIdSubclass);
#endregion
#region oleaut32
[DllImport("oleaut32.dll", EntryPoint = "#6")]
internal static extern void SysFreeString(char* bstrString);
[DllImport("oleaut32.dll", EntryPoint = "#7")]
internal static extern int SysStringLen(char* pbstr);
[DllImport("oleaut32.dll", EntryPoint = "#4")]
internal static extern BSTR SysAllocStringLen(string strIn, int len);
[DllImport("oleaut32.dll", EntryPoint = "#2")]
internal static extern BSTR SysAllocString(char* psz);
[DllImport("oleaut32.dll", EntryPoint = "#147")]
internal static extern int VariantChangeTypeEx(ref VARIANT pvargDest, in VARIANT pvarSrc, uint lcid, ushort wFlags, VARENUM vt);
[DllImport("oleaut32.dll", EntryPoint = "#9")]
internal static extern int VariantClear(ref VARIANT pvarg);
[DllImport("oleaut32.dll", EntryPoint = "#35")]
internal static extern int GetActiveObject(in Guid rclsid, IntPtr pvReserved, [MarshalAs(UnmanagedType.IUnknown)] out object ppunk);
#endregion
#region ole32
[DllImport("ole32.dll")]
internal static extern int CoCreateInstance(in Guid rclsid, nint pUnkOuter, uint dwClsContext, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);
[DllImport("ole32.dll")]
internal static extern int PropVariantClear(ref PROPVARIANT pvar);
//internal enum APTTYPE
//{
// APTTYPE_CURRENT = -1,
// APTTYPE_STA,
// APTTYPE_MTA,
// APTTYPE_NA,
// APTTYPE_MAINSTA
//}
//[DllImport("ole32.dll")]
//internal static extern int CoGetApartmentType(out APTTYPE pAptType, out int pAptQualifier);
[DllImport("ole32.dll")]
internal static extern int OleInitialize(IntPtr pvReserved);
[DllImport("ole32.dll")]
internal static extern void OleUninitialize();
[ComImport, Guid("00000122-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IDropTarget {
void DragEnter(System.Runtime.InteropServices.ComTypes.IDataObject d, int grfKeyState, POINT pt, ref int effect);
void DragOver(int grfKeyState, POINT pt, ref int effect);
void DragLeave();
void Drop(System.Runtime.InteropServices.ComTypes.IDataObject d, int grfKeyState, POINT pt, ref int effect);
}
[DllImport("ole32.dll")]
internal static extern int RegisterDragDrop(wnd hwnd, IDropTarget pDropTarget);
[DllImport("ole32.dll")]
internal static extern int RevokeDragDrop(wnd hwnd);
[DllImport("ole32.dll")]
internal static extern void ReleaseStgMedium(ref System.Runtime.InteropServices.ComTypes.STGMEDIUM medium);
[DllImport("ole32.dll")]
internal static extern int CLSIDFromProgID(string lpszProgID, out Guid lpclsid);
#endregion
#region oleacc
[DllImport("oleacc.dll")]
internal static extern int AccessibleObjectFromWindow(wnd hwnd, EObjid dwId, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object o);
[DllImport("oleacc.dll")]
internal static extern int AccessibleObjectFromEvent(wnd hwnd, EObjid dwObjectId, int dwChildId, out IntPtr ppacc, out VARIANT pvarChild);
[DllImport("oleacc.dll")]
internal static extern Handle_ GetProcessHandleFromHwnd(wnd hwnd);
[DllImport("oleacc.dll")]
internal static extern int CreateStdAccessibleObject(wnd hwnd, EObjid idObject, in Guid riid, out IAccessible ppvObject);
[ComImport, Guid("618736e0-3c3d-11cf-810c-00aa00389b71"), InterfaceType(ComInterfaceType.InterfaceIsDual)]
internal interface IAccessible {
IAccessible get_accParent();
int get_accChildCount();
[PreserveSig] int get_accChild(VarInt varChild, [MarshalAs(UnmanagedType.IDispatch)] out object ppdispChild);
string get_accName(VarInt varChild);
string get_accValue(VarInt varChild);
string get_accDescription(VarInt varChild);
VarInt get_accRole(VarInt varChild);
VarInt get_accState(VarInt varChild);
string get_accHelp(VarInt varChild);
int get_accHelpTopic(out string pszHelpFile, VarInt varChild);
string get_accKeyboardShortcut(VarInt varChild);
object get_accFocus();
object get_accSelection();
string get_accDefaultAction(VarInt varChild);
void accSelect(ESelect flagsSelect, VarInt varChild);
void accLocation(out int pxLeft, out int pyTop, out int pcxWidth, out int pcyHeight, VarInt varChild);
object accNavigate(NAVDIR navDir, VarInt varStart);
VarInt accHitTest(int xLeft, int yTop);
void accDoDefaultAction(VarInt varChild);
void put_accName(VarInt varChild, string szName);
void put_accValue(VarInt varChild, string szValue);
//NOTE: although some members are obsolete or useless, don't use default implementation, because then exception at run time.
}
#pragma warning disable 169
internal struct VarInt {
ushort _vt, _1, _2, _3;
nint _int, _4;
public static implicit operator VarInt(int i) => new VarInt { _vt = 3, _int = i + 1 };
public static implicit operator int(VarInt v) {
if (v._vt == 3) return (int)v._int - 1;
Debug_.Print($"VarInt vt={v._vt}, value={v._int}");
throw new ArgumentException();
}
}
#pragma warning restore 169
internal enum NAVDIR { UP = 1, DOWN, LEFT, RIGHT, NEXT, PREVIOUS, FIRSTCHILD, LASTCHILD }
#endregion
#region msvcrt
//don't use, because for eg "0xffffffff" returns int.Max instead of -1.
//[DllImport("msvcrt.dll", EntryPoint = "wcstol", CallingConvention = CallingConvention.Cdecl)]
//internal static extern int strtoi(char* s, char** endPtr = null, int radix = 0);
//don't use the u API because they return 1 if the value is too big and the string contains '-'.
//[DllImport("msvcrt.dll", EntryPoint = "wcstoul", CallingConvention = CallingConvention.Cdecl)]
//internal static extern uint strtoui(char* s, char** endPtr = null, int radix = 0);
[DllImport("msvcrt.dll", EntryPoint = "_wcstoi64", CallingConvention = CallingConvention.Cdecl)]
internal static extern long strtoi64(char* s, char** endPtr = null, int radix = 0);
//info: ntdll also has wcstol, wcstoul, _wcstoui64, but not _wcstoi64.
//[DllImport("msvcrt.dll", EntryPoint = "_wcstoui64", CallingConvention = CallingConvention.Cdecl)]
//internal static extern ulong strtoui64(char* s, char** endPtr = null, int radix = 0);
[DllImport("msvcrt.dll", EntryPoint = "_strtoi64", CallingConvention = CallingConvention.Cdecl)]
internal static extern long strtoi64(byte* s, byte** endPtr = null, int radix = 0);
//not used
//[DllImport("msvcrt.dll", EntryPoint = "_strtoui64", CallingConvention = CallingConvention.Cdecl)]
//internal static extern long strtoui64(byte* s, byte** endPtr = null, int radix = 0);
//This is used when working with char*. With C# strings use ExtString.ToInt32 etc.
internal static int strtoi(char* s, char** endPtr = null, int radix = 0) {
return (int)strtoi64(s, endPtr, radix);
}
//This is used with UTF-8 text.
internal static int strtoi(byte* s, byte** endPtr = null, int radix = 0) {
return (int)strtoi64(s, endPtr, radix);
}
#if false //not used, because we have ExtString.ToInt32 etc, which has no overflow problems. But it supports only decimal and hex, not any radix.
///
/// Converts part of string to int.
/// Returns the int value.
/// Returns 0 if the string is null, "" or does not begin with a number; then numberEndIndex will be = startIndex.
///
/// String.
/// Offset in string where to start parsing.
/// Receives offset in string where the number part ends.
/// If 0, parses the string as hexadecimal number if starts with "0x", as octal if starts with "0", else as decimal. Else it can be 2 to 36. Examples: 10 - parse as decimal (don't support "0x" etc); 16 - as hexadecimal (eg returns 26 if string is "1A" or "0x1A"); 2 - as binary (eg returns 5 if string is "101").
/// startIndex is invalid.
internal static int strtoi(string s, int startIndex, out int numberEndIndex, int radix = 0) {
int R = 0, len = s == null ? 0 : s.Length - startIndex;
if(len < 0) throw new ArgumentOutOfRangeException("startIndex");
if(len != 0)
fixed (char* p = s) {
char* t = p + startIndex, e = t;
R = strtoi(t, &e, radix);
len = (int)(e - t);
}
numberEndIndex = startIndex + len;
return R;
}
///
/// Converts part of string to long.
/// Returns the long value.
/// Returns 0 if the string is null, "" or does not begin with a number; then numberEndIndex will be = startIndex.
///
/// String.
/// Offset in string where to start parsing.
/// Receives offset in string where the number part ends.
/// If 0, parses the string as hexadecimal number if starts with "0x", as octal if starts with "0", else as decimal. Else it can be 2 to 36. Examples: 10 - parse as decimal (don't support "0x" etc); 16 - as hexadecimal (eg returns 26 if string is "1A" or "0x1A"); 2 - as binary (eg returns 5 if string is "101").
/// startIndex is invalid.
internal static long strtoi64(string s, int startIndex, out int numberEndIndex, int radix = 0) {
long R = 0;
int len = s == null ? 0 : s.Length - startIndex;
if(len < 0) throw new ArgumentOutOfRangeException("startIndex");
if(len != 0)
fixed (char* p = s) {
char* t = p + startIndex, e = t;
R = strtoi64(t, &e, radix);
len = (int)(e - t);
}
numberEndIndex = startIndex + len;
return R;
}
internal static int strtoi(string s, int startIndex = 0, int radix = 0) {
return strtoi(s, startIndex, out _, radix);
}
internal static long strtoi64(string s, int startIndex = 0, int radix = 0) {
return strtoi64(s, startIndex, out _, radix);
}
#endif
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern char* _ltoa(int value, byte* s, int radix);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void* memmove(void* to, void* from, nint n);
//[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
//internal static extern void* memset(void* ptr, int ch, nint n);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int memcmp(void* p1, void* p2, nint count);
#endregion
#region winmm
[DllImport("winmm.dll")]
internal static extern uint timeBeginPeriod(uint uPeriod);
[DllImport("winmm.dll")]
internal static extern uint timeEndPeriod(uint uPeriod);
[DllImport("winmm.dll")]
internal static extern uint waveOutSetVolume(IntPtr hwo, uint dwVolume);
[DllImport("winmm.dll")]
internal static extern uint waveOutGetVolume(IntPtr hwo, out uint pdwVolume);
[DllImport("winmm.dll", EntryPoint = "PlaySoundW")]
internal static extern bool PlaySound(string pszSound, IntPtr hmod, uint fdwSound);
internal const uint SND_FILENAME = 0x20000;
internal const uint SND_NODEFAULT = 0x2;
internal const uint SND_SYSTEM = 0x200000;
internal const uint SND_ASYNC = 0x1;
internal const uint SND_ALIAS = 0x10000;
internal const uint SND_APPLICATION = 0x80;
[DllImport("user32.dll")]
internal static extern bool MessageBeep(uint uType);
[DllImport("kernel32.dll")]
internal static extern bool Beep(int dwFreq, int dwDuration);
#endregion
#region dwmapi
internal enum DWMWA {
NCRENDERING_ENABLED = 1,
NCRENDERING_POLICY,
TRANSITIONS_FORCEDISABLED,
ALLOW_NCPAINT,
CAPTION_BUTTON_BOUNDS,
NONCLIENT_RTL_LAYOUT,
FORCE_ICONIC_REPRESENTATION,
FLIP3D_POLICY,
EXTENDED_FRAME_BOUNDS,
HAS_ICONIC_BITMAP,
DISALLOW_PEEK,
EXCLUDED_FROM_PEEK,
CLOAK,
CLOAKED,
FREEZE_REPRESENTATION,
PASSIVE_UPDATE_MODE,
}
[DllImport("dwmapi.dll")]
internal static extern int DwmGetWindowAttribute(wnd hwnd, DWMWA dwAttribute, void* pvAttribute, int cbAttribute);
//[DllImport("dwmapi.dll")]
//internal static extern int DwmSetWindowAttribute(wnd hwnd, DWMWA dwAttribute, void* pvAttribute, int cbAttribute);
//[DllImport("dwmapi.dll")]
//internal static extern int DwmRegisterThumbnail(wnd hwndDestination, wnd hwndSource, out IntPtr phThumbnailId);
//[DllImport("dwmapi.dll")]
//internal static extern int DwmUnregisterThumbnail(IntPtr hThumbnailId);
//[DllImport("dwmapi.dll")]
//internal static extern int DwmQueryThumbnailSourceSize(IntPtr hThumbnail, out SIZE pSize);
//[DllImport("dwmapi.dll")]
//internal static extern int DwmUpdateThumbnailProperties(IntPtr hThumbnailId, in DWM_THUMBNAIL_PROPERTIES ptnProperties);
//[StructLayout(LayoutKind.Sequential, Pack = 1)]
//internal struct DWM_THUMBNAIL_PROPERTIES {
// public uint dwFlags;
// public RECT rcDestination;
// public RECT rcSource;
// public byte opacity;
// public bool fVisible;
// public bool fSourceClientAreaOnly;
//}
//internal const uint DWM_TNP_SOURCECLIENTAREAONLY = 0x10;
//internal const uint DWM_TNP_RECTDESTINATION = 0x1;
//internal const uint DWM_TNP_VISIBLE = 0x8;
//internal const uint DWM_TNP_RECTSOURCE = 0x2;
#endregion
#region uxtheme
[DllImport("uxtheme.dll")]
static extern IntPtr OpenThemeData(wnd hwnd, string pszClassList);
[DllImport("uxtheme.dll")]
static extern IntPtr OpenThemeDataForDpi(wnd hwnd, string pszClassList, int dpi);
internal static IntPtr OpenThemeData(wnd hwnd, string classList, int dpi) {
if (osVersion.minWin10_1703) return OpenThemeDataForDpi(hwnd, classList, dpi);
return OpenThemeData(hwnd, classList); //never mind: bad on Win8.1 non-primary screen with different DPI than primary if hwnd==0
}
[DllImport("uxtheme.dll")]
internal static extern int CloseThemeData(IntPtr hTheme);
[DllImport("uxtheme.dll")]
internal static extern int GetThemePartSize(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, RECT* prc, THEMESIZE eSize, out SIZE psz);
internal enum THEMESIZE {
TS_MIN,
TS_TRUE,
TS_DRAW
}
[DllImport("uxtheme.dll")]
internal static extern int DrawThemeBackground(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, in RECT pRect, RECT* pClipRect = null);
//[DllImport("uxtheme.dll")]
//internal static extern int SetWindowTheme(wnd hwnd, string pszSubAppName, string pszSubIdList);
[DllImport("uxtheme.dll")]
internal static extern int GetThemeSysColor(IntPtr hTheme, int iColorId);
[DllImport("uxtheme.dll")]
internal static extern IntPtr GetThemeSysColorBrush(IntPtr hTheme, int iColorId);
[DllImport("uxtheme.dll")]
internal static extern int BufferedPaintInit();
//[DllImport("uxtheme.dll")]
//internal static extern int BufferedPaintUnInit();
[DllImport("uxtheme.dll")]
internal static extern IntPtr BeginBufferedPaint(IntPtr hdcTarget, in RECT prcTarget, BP_BUFFERFORMAT dwFormat, ref BP_PAINTPARAMS pPaintParams, out IntPtr phdc);
[DllImport("uxtheme.dll")]
internal static extern int EndBufferedPaint(IntPtr hBufferedPaint, bool fUpdateTarget);
internal enum BP_BUFFERFORMAT {
BPBF_COMPATIBLEBITMAP,
BPBF_DIB,
BPBF_TOPDOWNDIB,
BPBF_TOPDOWNMONODIB
}
internal struct BP_PAINTPARAMS {
public int cbSize;
public uint dwFlags;
public RECT* prcExclude;
//public BLENDFUNCTION* pBlendFunction;
uint pBlendFunction;
}
//internal struct BLENDFUNCTION {
// public byte BlendOp;
// public byte BlendFlags;
// public byte SourceConstantAlpha;
// public byte AlphaFormat;
//}
#endregion
#region wtsapi
[DllImport("wtsapi32.dll", SetLastError = true)]
internal static extern bool WTSTerminateProcess(IntPtr hServer, int ProcessId, int ExitCode);
[DllImport("wtsapi32.dll")]
internal static extern void WTSFreeMemory(void* pMemory);
[DllImport("wtsapi32.dll", EntryPoint = "WTSQuerySessionInformationW", SetLastError = true)]
static extern bool _WTSQuerySessionInformation(nint hServer, int SessionId, WTS_INFO_CLASS WTSInfoClass, out char* ppBuffer, out int pBytesReturned);
internal enum WTS_INFO_CLASS {
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType,
WTSIdleTime,
WTSLogonTime,
WTSIncomingBytes,
WTSOutgoingBytes,
WTSIncomingFrames,
WTSOutgoingFrames,
WTSClientInfo,
WTSSessionInfo,
WTSSessionInfoEx,
WTSConfigInfo,
WTSValidationInfo,
WTSSessionAddressV4,
WTSIsRemoteSession
}
internal static unsafe bool WTSQuerySessionInformation(int sessionId, Api.WTS_INFO_CLASS what, out string r) {
if (_WTSQuerySessionInformation(0, -1, what, out char* p, out int len)) {
r = len > 2 ? new(p, 0, len / 2 - 1) : null;
Api.WTSFreeMemory(p);
return r != null;
}
r = null;
return false;
}
internal static unsafe bool WTSQuerySessionInformation(int sessionId, Api.WTS_INFO_CLASS what, out T r) where T : unmanaged {
if (_WTSQuerySessionInformation(0, -1, what, out char* p, out int len)) {
Debug_.PrintIf(len != sizeof(T), len.ToS());
r = len == sizeof(T) ? *(T*)p : default;
Api.WTSFreeMemory(p);
return len == sizeof(T);
}
r = default;
return false;
}
[DllImport("wtsapi32.dll")]
internal static extern bool WTSGetChildSessionId(out int pSessionId);
#endregion
#region ntdll
internal struct RTL_OSVERSIONINFOW {
public int dwOSVersionInfoSize;
public uint dwMajorVersion;
public uint dwMinorVersion;
public uint dwBuildNumber;
public uint dwPlatformId;
public fixed char szCSDVersion[128];
}
[DllImport("ntdll.dll", ExactSpelling = true)]
internal static extern int RtlGetVersion(ref RTL_OSVERSIONINFOW lpVersionInformation);
[DllImport("ntdll.dll")]
internal static extern uint NtQueryTimerResolution(out uint maxi, out uint mini, out uint current);
//info: NtSetTimerResolution can set min 0.5 ms resolution. timeBeginPeriod min 1.
[DllImport("ntdll.dll")]
internal static extern void MD5Init(out Hash.MD5Context context);
[DllImport("ntdll.dll")]
internal static extern void MD5Update(ref Hash.MD5Context context, void* data, int dataLen);
[DllImport("ntdll.dll")]
internal static extern void MD5Final(ref Hash.MD5Context context);
#pragma warning disable 169
[DllImport("ntdll.dll")]
internal static extern int NtQueryInformationProcess(IntPtr ProcessHandle, int ProcessInformationClass, void* ProcessInformation, int ProcessInformationLength, out int ReturnLength);
//all structs are same in 64-bit and 32-bit processes
internal record struct PROCESS_BASIC_INFORMATION {
long Reserved1;
public long PebBaseAddress;
long Reserved2_0, Reserved2_1;
public long UniqueProcessId;
public long ParentProcessId;
}
internal struct RTL_USER_PROCESS_PARAMETERS {
fixed byte Reserved[96];
public UNICODE_STRING ImagePathName;
public UNICODE_STRING CommandLine;
}
internal struct UNICODE_STRING {
public ushort Length;
public ushort MaximumLength;
public char* Buffer;
}
internal struct SYSTEM_PROCESS_INFORMATION {
internal uint NextEntryOffset;
internal uint NumberOfThreads;
long SpareLi1;
long SpareLi2;
long SpareLi3;
internal long CreateTime;
internal long UserTime;
internal long KernelTime;
internal ushort NameLength; // UNICODE_STRING
internal ushort MaximumNameLength;
internal IntPtr NamePtr; // This will point into the data block returned by NtQuerySystemInformation
internal int BasePriority;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
internal uint HandleCount;
internal uint SessionId;
//unused members
}
#pragma warning restore 169
internal const int STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004);
[DllImport("ntdll.dll")]
internal static extern int NtQuerySystemInformation(int five, SYSTEM_PROCESS_INFORMATION* SystemInformation, int SystemInformationLength, out int ReturnLength);
[DllImport("ntdll.dll")]
internal static extern int NtSuspendProcess(IntPtr handle);
[DllImport("ntdll.dll")]
internal static extern int NtResumeProcess(IntPtr handle);
#endregion
#region other
[DllImport("msi.dll", EntryPoint = "#217")]
internal static extern int MsiGetShortcutTarget(string szShortcutPath, char* szProductCode, char* szFeatureId, char* szComponentCode);
[DllImport("msi.dll", EntryPoint = "#173")]
internal static extern int MsiGetComponentPath(char* szProduct, char* szComponent, char* lpPathBuf, ref int pcchBuf);
[DllImport("powrprof.dll")]
internal static extern byte SetSuspendState(byte bHibernate, byte bForce, byte bWakeupEventsDisabled);
[DllImport("powrprof.dll")]
internal static extern byte IsPwrSuspendAllowed();
//[DllImport("urlmon.dll")]
//internal static extern int FindMimeFromData(IntPtr pBC, string pwzUrl, byte[] pBuffer, int cbSize, string pwzMimeProposed, uint dwMimeFlags, out string ppwzMimeOut, uint dwReserved);
#endregion
#region struct
internal struct NEWHEADER {
public ushort wReserved;
public ushort wResType;
public ushort wResCount;
};
[StructLayout(LayoutKind.Sequential, Pack = 2)]
internal struct ICONDIRENTRY {
public byte bWidth;
public byte bHeight;
public byte bColorCount;
public byte bReserved;
public ushort wPlanes;
public ushort wBitCount;
public int dwBytesInRes;
public int dwImageOffset;
};
#endregion
}
================================================
FILE: Au/Api/Api^kernel32.cs
================================================
namespace Au.Types;
static unsafe partial class Api {
[DllImport("kernel32.dll", SetLastError = true)] //note: without `SetLastError = true` Marshal.GetLastWin32Error is unaware that we set the code to 0 etc and returns old captured error code
internal static extern void SetLastError(int errCode);
internal const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x1000;
internal const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x100;
internal const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x200;
[DllImport("kernel32.dll", EntryPoint = "FormatMessageW")]
internal static extern int FormatMessage(uint dwFlags, IntPtr lpSource, int code, uint dwLanguageId, char** lpBuffer, int nSize, IntPtr Arguments);
[DllImport("kernel32.dll", EntryPoint = "SetDllDirectoryW", SetLastError = true)]
internal static extern bool SetDllDirectory(string lpPathName);
[SuppressGCTransition] //makes slightly faster. Not faster with [MethodImpl].
[DllImport("kernel32.dll")]
internal static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
[DllImport("kernel32.dll")]
internal static extern bool QueryPerformanceFrequency(out long lpFrequency);
[DllImport("kernel32.dll")]
internal static extern bool QueryUnbiasedInterruptTime(out long UnbiasedTime);
[DllImport("kernel32.dll")]
internal static extern long GetTickCount64();
[DllImport("kernel32.dll")]
internal static extern int GetTickCount();
internal struct SYSTEMTIME {
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
[DllImport("kernel32.dll")]
internal static extern void GetLocalTime(out SYSTEMTIME lpSystemTime);
[DllImport("kernel32.dll")]
internal static extern void GetSystemTimeAsFileTime(out long lpSystemTimeAsFileTime);
//[DllImport("kernel32.dll", SetLastError = true)]
//internal static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetProcessTimes(IntPtr hProcess, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int GetThreadDescription(IntPtr hThread, out char* ppszThreadDescription);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool QueryProcessCycleTime(nint ProcessHandle, out long CycleTime);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool QueryThreadCycleTime(nint ThreadHandle, out long CycleTime);
[DllImport("kernel32.dll")]
internal static extern bool QueryIdleProcessorCycleTimeEx(ushort Group, ref int BufferLength, long* ProcessorIdleCycleTime);
[DllImport("kernel32.dll")]
internal static extern ushort GetActiveProcessorGroupCount();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int GetThreadPriority(nint hThread);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetThreadPriority(nint hThread, int nPriority);
internal const int THREAD_PRIORITY_TIME_CRITICAL = 15;
[DllImport("kernel32.dll", EntryPoint = "CreateEventW", SetLastError = true)]
internal static extern IntPtr CreateEvent2(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
internal static Handle_ CreateEvent(bool bManualReset)
=> new(CreateEvent2(default, bManualReset, false, null));
[DllImport("kernel32.dll", EntryPoint = "OpenEventW", SetLastError = true)]
internal static extern IntPtr OpenEvent(uint dwDesiredAccess, bool bInheritHandle, string lpName);
internal const uint EVENT_MODIFY_STATE = 0x2;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetEvent(IntPtr hEvent);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool ResetEvent(IntPtr hEvent);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);
//[DllImport("kernel32.dll")]
//internal static extern int SignalObjectAndWait(IntPtr hObjectToSignal, IntPtr hObjectToWaitOn, int dwMilliseconds, bool bAlertable);
//note: don't know why, this often is much slower than setevent/waitforsingleobject.
[DllImport("kernel32.dll")] //note: no SetLastError = true
internal static extern bool CloseHandle(IntPtr hObject);
//currently not used
//[DllImport("kernel32.dll")] //note: no SetLastError = true
//internal static extern bool CloseHandle(HandleRef hObject);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetHandleInformation(IntPtr hObject, uint dwMask, uint dwFlags);
[DllImport("kernel32.dll", EntryPoint = "CreateMutexW", SetLastError = true)]
internal static extern nint CreateMutex(SECURITY_ATTRIBUTES lpMutexAttributes, bool bInitialOwner, string lpName);
[DllImport("kernel32.dll", EntryPoint = "OpenMutexW", SetLastError = true)]
internal static extern nint OpenMutex(uint dwDesiredAccess, bool bInheritHandle, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool ReleaseMutex(nint hMutex);
///
/// Note: use only for private threads. Not everything works like with Thread.Start. For example .NET does not auto-release COM objects when thread ends.
///
/// [UnmanagedCallersOnly]
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr CreateThread(nint lpThreadAttributes, nint dwStackSize, delegate* unmanaged lpStartAddress, GCHandle lpParameter, uint dwCreationFlags, out int lpThreadId);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentThread();
[SuppressGCTransition]
[DllImport("kernel32.dll")]
internal static extern int GetCurrentThreadId();
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[SuppressGCTransition]
[DllImport("kernel32.dll")]
internal static extern int GetCurrentProcessId();
[DllImport("kernel32.dll", EntryPoint = "QueryFullProcessImageNameW", SetLastError = true)]
internal static extern bool QueryFullProcessImageName(IntPtr hProcess, bool nativeFormat, char* lpExeName, ref int lpdwSize);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool TerminateProcess(IntPtr hProcess, int uExitCode);
[DllImport("kernel32.dll")]
internal static extern void ExitProcess(int uExitCode);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool IsWow64Process(IntPtr hProcess, out bool Wow64Process);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern Handle_ CreateFileMapping(IntPtr hFile, SECURITY_ATTRIBUTES lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);
[DllImport("kernel32.dll", EntryPoint = "OpenFileMappingW", SetLastError = true)]
internal static extern Handle_ OpenFileMapping(uint dwDesiredAccess, bool bInheritHandle, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern void* MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, nint dwNumberOfBytesToMap);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool UnmapViewOfFile(void* lpBaseAddress);
[DllImport("kernel32.dll", EntryPoint = "GetModuleHandleW", SetLastError = true)]
internal static extern IntPtr GetModuleHandle(string name);
//Better use NativeLibrary.TryLoad.
//Dlls loaded by LoadLibrary don't find other used dlls from the same directory if it's not the app directory. Need LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH, and probably NativeLibrary.TryLoad uses it.
//[DllImport("kernel32.dll", EntryPoint = "LoadLibraryW", SetLastError = true)]
//internal static extern IntPtr LoadLibrary(string lpLibFileName);
internal const uint LOAD_LIBRARY_AS_DATAFILE = 0x2;
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryExW", SetLastError = true)]
internal static extern IntPtr LoadLibraryEx(string lpLibFileName, IntPtr hFile, uint dwFlags);
[DllImport("kernel32.dll")]
internal static extern bool FreeLibrary(IntPtr hLibModule);
[DllImport("kernel32.dll", BestFitMapping = false, SetLastError = true)]
internal static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
internal const uint PROCESS_TERMINATE = 0x0001;
internal const uint PROCESS_CREATE_THREAD = 0x0002;
internal const uint PROCESS_SET_SESSIONID = 0x0004;
internal const uint PROCESS_VM_OPERATION = 0x0008;
internal const uint PROCESS_VM_READ = 0x0010;
internal const uint PROCESS_VM_WRITE = 0x0020;
internal const uint PROCESS_DUP_HANDLE = 0x0040;
internal const uint PROCESS_CREATE_PROCESS = 0x0080;
internal const uint PROCESS_SET_QUOTA = 0x0100;
internal const uint PROCESS_SET_INFORMATION = 0x0200;
internal const uint PROCESS_QUERY_INFORMATION = 0x0400;
internal const uint PROCESS_SUSPEND_RESUME = 0x0800;
internal const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
internal const uint PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF;
internal const uint DELETE = 0x00010000;
internal const uint READ_CONTROL = 0x00020000;
internal const uint WRITE_DAC = 0x00040000;
internal const uint WRITE_OWNER = 0x00080000;
internal const uint SYNCHRONIZE = 0x00100000;
internal const uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
internal const uint STANDARD_RIGHTS_READ = READ_CONTROL;
internal const uint STANDARD_RIGHTS_WRITE = READ_CONTROL;
internal const uint STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
internal const uint STANDARD_RIGHTS_ALL = 0x001F0000;
internal const uint TIMER_MODIFY_STATE = 0x2;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern Handle_ OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", EntryPoint = "GetFullPathNameW", SetLastError = true)]
static extern int _GetFullPathName(string lpFileName, int nBufferLength, char* lpBuffer, char** lpFilePart);
///
/// Calls API GetFullPathName.
/// Returns false if failed or result is same; then r is s.
/// r can be same variable as s.
///
[SkipLocalsInit]
internal static bool GetFullPathName(string s, out string r) {
using FastBuffer b = new();
for (; ; ) if (b.GetString(_GetFullPathName(s, b.n, b.p, null), out r, 0, s)) return (object)r != s;
}
[DllImport("kernel32.dll", EntryPoint = "GetLongPathNameW", SetLastError = true)]
static extern int _GetLongPathName(string lpszShortPath, char* lpszLongPath, int cchBuffer);
///
/// Calls API GetFullPathName.
/// Returns false if failed or result is same; then r is s.
/// r can be same variable as s.
///
[SkipLocalsInit]
internal static bool GetLongPathName(string s, out string r) {
using FastBuffer b = new();
for (; ; ) if (b.GetString(_GetLongPathName(s, b.p, b.n), out r, 0, s)) return (object)r != s;
}
[DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", SetLastError = true)]
static extern int _GetFinalPathNameByHandle(IntPtr hFile, char* lpszFilePath, int cchFilePath, uint dwFlags);
[SkipLocalsInit]
internal static bool GetFinalPathNameByHandle(IntPtr h, out string r, uint dwFlags = 0) {
using FastBuffer b = new();
for (; ; ) {
g1: if (b.GetString(_GetFinalPathNameByHandle(h, b.p, b.n, dwFlags), out r, 0)) {
if (r != null) return r.Length > 0;
if (0u != (dwFlags & 3u)) { dwFlags &= ~3u; goto g1; } //if VOLUME_NAME_GUID, fails if network path
return false;
}
}
}
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool ProcessIdToSessionId(int dwProcessId, out int pSessionId);
internal const uint PAGE_NOACCESS = 0x1;
internal const uint PAGE_READONLY = 0x2;
internal const uint PAGE_READWRITE = 0x4;
internal const uint PAGE_WRITECOPY = 0x8;
internal const uint PAGE_EXECUTE = 0x10;
internal const uint PAGE_EXECUTE_READ = 0x20;
internal const uint PAGE_EXECUTE_READWRITE = 0x40;
internal const uint PAGE_EXECUTE_WRITECOPY = 0x80;
internal const uint PAGE_GUARD = 0x100;
internal const uint PAGE_NOCACHE = 0x200;
internal const uint PAGE_WRITECOMBINE = 0x400;
internal const uint MEM_COMMIT = 0x1000;
internal const uint MEM_RESERVE = 0x2000;
internal const uint MEM_DECOMMIT = 0x4000;
internal const uint MEM_RELEASE = 0x8000;
internal const uint MEM_RESET = 0x80000;
internal const uint MEM_TOP_DOWN = 0x100000;
internal const uint MEM_WRITE_WATCH = 0x200000;
internal const uint MEM_PHYSICAL = 0x400000;
internal const uint MEM_RESET_UNDO = 0x1000000;
internal const uint MEM_LARGE_PAGES = 0x20000000;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern void* VirtualAlloc(void* lpAddress, nint dwSize, uint flAllocationType = MEM_COMMIT | MEM_RESERVE, uint flProtect = PAGE_READWRITE);
//note: with PAGE_EXECUTE_READWRITE writing to the memory first time is much slower.
[DllImport("kernel32.dll")]
internal static extern bool VirtualFree(void* lpAddress, nint dwSize = 0, uint dwFreeType = MEM_RELEASE);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr VirtualAllocEx(HandleRef hProcess, IntPtr lpAddress, nint dwSize, uint flAllocationType = MEM_COMMIT | MEM_RESERVE, uint flProtect = PAGE_EXECUTE_READWRITE);
[DllImport("kernel32.dll")]
internal static extern bool VirtualFreeEx(HandleRef hProcess, IntPtr lpAddress, nint dwSize = 0, uint dwFreeType = MEM_RELEASE);
[DllImport("kernel32.dll", EntryPoint = "GetFileAttributesW", SetLastError = true)]
internal static extern FileAttributes GetFileAttributes(string lpFileName);
[DllImport("kernel32.dll", EntryPoint = "SetFileAttributesW", SetLastError = true)]
internal static extern bool SetFileAttributes(string lpFileName, FileAttributes dwFileAttributes);
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct WIN32_FILE_ATTRIBUTE_DATA {
public FileAttributes dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public long Size => (long)nFileSizeHigh << 32 | nFileSizeLow;
}
[DllImport("kernel32.dll", EntryPoint = "GetFileAttributesExW", SetLastError = true)]
internal static extern bool GetFileAttributesEx(string lpFileName, int zero, out WIN32_FILE_ATTRIBUTE_DATA lpFileInformation);
[DllImport("kernel32.dll", EntryPoint = "SearchPathW", SetLastError = true)]
static extern int _SearchPath(string lpPath, string lpFileName, string lpExtension, int nBufferLength, char* lpBuffer, char** lpFilePart);
///
/// Calls API SearchPath. Returns full path, or null if not found.
///
/// Parent directory or null.
///
/// null or extension like ".ext" to add if lpFileName is without extension.
[SkipLocalsInit]
internal static string SearchPath(string lpPath, string lpFileName, string lpExtension = null) {
using FastBuffer b = new();
for (; ; ) if (b.GetString(_SearchPath(lpPath, lpFileName, lpExtension, b.n, b.p, null), out var s)) return s;
}
internal const uint BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE = 0x1;
internal const uint BASE_SEARCH_PATH_DISABLE_SAFE_SEARCHMODE = 0x10000;
internal const uint BASE_SEARCH_PATH_PERMANENT = 0x8000;
[DllImport("kernel32.dll")]
internal static extern bool SetSearchPathMode(uint Flags);
internal const uint SEM_FAILCRITICALERRORS = 0x1;
internal const uint SEM_NOGPFAULTERRORBOX = 0x2;
[DllImport("kernel32.dll")]
internal static extern uint SetErrorMode(uint uMode);
//[DllImport("kernel32.dll")]
//internal static extern uint GetErrorMode();
//[DllImport("kernel32.dll", SetLastError = true)]
//internal static extern IntPtr LocalAlloc(uint uFlags, nint uBytes);
[DllImport("kernel32.dll")]
internal static extern IntPtr LocalFree(void* hMem);
[DllImport("kernel32.dll", EntryPoint = "lstrcpynW")]
internal static extern char* lstrcpyn(char* sTo, string sFrom, int sToBufferLength);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool Wow64DisableWow64FsRedirection(out IntPtr OldValue);
[DllImport("kernel32.dll")]
internal static extern bool Wow64RevertWow64FsRedirection(IntPtr OlValue);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetExitCodeProcess(IntPtr hProcess, out int lpExitCode);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetProcessHeap();
[DllImport("kernel32.dll")]
internal static extern void* HeapAlloc(IntPtr hHeap, uint dwFlags, nint dwBytes);
[DllImport("kernel32.dll")]
internal static extern void* HeapReAlloc(IntPtr hHeap, uint dwFlags, void* lpMem, nint dwBytes);
[DllImport("kernel32.dll")]
internal static extern bool HeapFree(IntPtr hHeap, uint dwFlags, void* lpMem);
internal const int CP_UTF8 = 65001;
internal const uint MB_ERR_INVALID_CHARS = 0x8;
internal const uint WC_ERR_INVALID_CHARS = 0x80;
[DllImport("kernel32.dll")]
internal static extern int MultiByteToWideChar(uint CodePage, uint dwFlags, byte* lpMultiByteStr, int cbMultiByte, char* lpWideCharStr, int cchWideChar);
[DllImport("kernel32.dll")]
internal static extern int WideCharToMultiByte(uint CodePage, uint dwFlags, char* lpWideCharStr, int cchWideChar, byte* lpMultiByteStr, int cbMultiByte, IntPtr lpDefaultChar = default, int* lpUsedDefaultChar = null);
[Flags]
internal enum Access : uint { }
internal const Access FILE_READ_DATA = (Access)0x1;
internal const Access FILE_LIST_DIRECTORY = (Access)0x1;
internal const Access FILE_WRITE_DATA = (Access)0x2;
internal const Access FILE_ADD_FILE = (Access)0x2;
internal const Access FILE_APPEND_DATA = (Access)0x4;
internal const Access FILE_ADD_SUBDIRECTORY = (Access)0x4;
internal const Access FILE_CREATE_PIPE_INSTANCE = (Access)0x4;
internal const Access FILE_READ_EA = (Access)0x8;
internal const Access FILE_WRITE_EA = (Access)0x10;
internal const Access FILE_EXECUTE = (Access)0x20;
internal const Access FILE_TRAVERSE = (Access)0x20;
internal const Access FILE_DELETE_CHILD = (Access)0x40;
internal const Access FILE_READ_ATTRIBUTES = (Access)0x80;
internal const Access FILE_WRITE_ATTRIBUTES = (Access)0x100;
internal const Access FILE_ALL_ACCESS = (Access)0x1F01FF;
internal const Access FILE_GENERIC_READ = (Access)0x120089;
internal const Access FILE_GENERIC_WRITE = (Access)0x120116;
internal const Access FILE_GENERIC_EXECUTE = (Access)0x1200A0;
internal const Access GENERIC_READ = (Access)0x80000000;
internal const Access GENERIC_WRITE = (Access)0x40000000;
internal enum CfCreation { }
internal const CfCreation CREATE_NEW = (CfCreation)1;
internal const CfCreation CREATE_ALWAYS = (CfCreation)2;
internal const CfCreation OPEN_EXISTING = (CfCreation)3;
internal const CfCreation OPEN_ALWAYS = (CfCreation)4;
internal const CfCreation TRUNCATE_EXISTING = (CfCreation)5;
[Flags]
internal enum CfShare : uint { }
internal const CfShare FILE_SHARE_READ = (CfShare)0x1;
internal const CfShare FILE_SHARE_WRITE = (CfShare)0x2;
internal const CfShare FILE_SHARE_DELETE = (CfShare)0x4;
internal const CfShare FILE_SHARE_ALL = (CfShare)0x7;
//the commented out attributes are not documented for CreateFile
internal const uint FILE_ATTRIBUTE_READONLY = 0x1;
internal const uint FILE_ATTRIBUTE_HIDDEN = 0x2;
internal const uint FILE_ATTRIBUTE_SYSTEM = 0x4;
//internal const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;
internal const uint FILE_ATTRIBUTE_ARCHIVE = 0x20;
//internal const uint FILE_ATTRIBUTE_DEVICE = 0x40;
internal const uint FILE_ATTRIBUTE_NORMAL = 0x80;
internal const uint FILE_ATTRIBUTE_TEMPORARY = 0x100;
//internal const uint FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
//internal const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
//internal const uint FILE_ATTRIBUTE_COMPRESSED = 0x800;
internal const uint FILE_ATTRIBUTE_OFFLINE = 0x1000;
//internal const uint FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
internal const uint FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
//internal const uint FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
//internal const uint FILE_ATTRIBUTE_VIRTUAL = 0x10000;
//internal const uint FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
internal const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
internal const uint FILE_FLAG_OVERLAPPED = 0x40000000;
internal const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
internal const uint FILE_FLAG_RANDOM_ACCESS = 0x10000000;
internal const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x8000000;
internal const uint FILE_FLAG_DELETE_ON_CLOSE = 0x4000000;
internal const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;
internal const uint FILE_FLAG_POSIX_SEMANTICS = 0x1000000;
internal const uint FILE_FLAG_SESSION_AWARE = 0x800000;
internal const uint FILE_FLAG_OPEN_REPARSE_POINT = 0x200000;
internal const uint FILE_FLAG_OPEN_NO_RECALL = 0x100000;
internal const uint FILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000;
internal const uint FILE_FLAG_OPEN_REQUIRING_OPLOCK = 0x40000;
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true)]
static extern IntPtr _CreateFile(string lpFileName, Access dwDesiredAccess, CfShare dwShareMode, SECURITY_ATTRIBUTES lpSecurityAttributes, CfCreation creationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
internal static Handle_ CreateFile(string lpFileName, Access dwDesiredAccess, CfShare dwShareMode, CfCreation creationDisposition, uint dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, IntPtr hTemplateFile = default, SECURITY_ATTRIBUTES lpSecurityAttributes = null)
=> new Handle_(_CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, creationDisposition, dwFlagsAndAttributes, hTemplateFile));
//note: cannot return Handle_ directly from API because returns -1 if failed. The ctor then makes 0.
//note: not using parameter types SECURITY_ATTRIBUTES and OVERLAPPED* because it makes JIT-compiling much slower in some time-critical places.
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool ReadFile(IntPtr hFile, void* lpBuffer, int nBytesToRead, out int nBytesRead, void* lpOverlapped = null);
internal static bool ReadFileArr(IntPtr hFile, byte[] a, out int nBytesRead, void* lpOverlapped = null) {
fixed (byte* p = a) return ReadFile(hFile, p, a.Length, out nBytesRead, lpOverlapped);
}
internal static bool ReadFileArr(IntPtr hFile, out byte[] a, int size, out int nBytesRead, void* lpOverlapped = null) {
a = new byte[size];
return ReadFileArr(hFile, a, out nBytesRead, lpOverlapped);
}
//internal static byte[] ReadFileArr(string file) {
// using var h = CreateFile(file, Api.GENERIC_READ, FILE_SHARE_ALL, OPEN_EXISTING);
// if (h.Is0 || !GetFileSizeEx(h, out long size) || !ReadFileArr(h, out var a, (int)size, out _)) throw new AuException(0);
// return a;
//}
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool WriteFile(IntPtr hFile, void* lpBuffer, int nBytesToWrite, out int nBytesWritten, void* lpOverlapped = null);
//note: lpNumberOfBytesWritten can be null only if lpOverlapped is not null.
//note: don't use overloads, because we Jit_.Compile("WriteFile").
internal static bool WriteFile2(IntPtr hFile, RByte a, out int nBytesWritten) {
fixed (byte* p = a) return WriteFile(hFile, p, a.Length, out nBytesWritten);
}
//internal static bool WriteFile2(IntPtr hFile, RByte a, out int nBytesWritten, void* lpOverlapped)
//{
// fixed (byte* p = a) return WriteFile(hFile, p, a.Length, out nBytesWritten, lpOverlapped);
//}
internal struct OVERLAPPED {
nint _1, _2;
int _3, _4;
public IntPtr hEvent;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct BY_HANDLE_FILE_INFORMATION {
public uint dwFileAttributes;
public long ftCreationTime;
public long ftLastAccessTime;
public long ftLastWriteTime;
public uint dwVolumeSerialNumber;
uint _nFileSizeHigh;
uint _nFileSizeLow;
public uint nNumberOfLinks;
uint _nFileIndexHigh;
uint _nFileIndexLow;
public long FileSize => (long)((ulong)_nFileSizeHigh << 32 | _nFileSizeLow);
public long FileIndex => (long)((ulong)_nFileIndexHigh << 32 | _nFileIndexLow);
}
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);
//internal enum FILE_INFO_BY_HANDLE_CLASS
//{
// FileBasicInfo,
// FileStandardInfo,
// FileNameInfo,
// FileRenameInfo,
// FileDispositionInfo,
// FileAllocationInfo,
// FileEndOfFileInfo,
// FileStreamInfo,
// FileCompressionInfo,
// FileAttributeTagInfo,
// FileIdBothDirectoryInfo,
// FileIdBothDirectoryRestartInfo,
// FileIoPriorityHintInfo,
// FileRemoteProtocolInfo,
// FileFullDirectoryInfo,
// FileFullDirectoryRestartInfo,
// FileStorageInfo,
// FileAlignmentInfo,
// FileIdInfo,
// FileIdExtdDirectoryInfo,
// FileIdExtdDirectoryRestartInfo,
// MaximumFileInfoByHandleClass
//}
//internal struct FILE_BASIC_INFO
//{
// public long CreationTime;
// public long LastAccessTime;
// public long LastWriteTime;
// public long ChangeTime;
// public uint FileAttributes;
//}
//[DllImport("kernel32.dll", SetLastError = true)]
//internal static extern bool GetFileInformationByHandleEx(IntPtr hFile, int FileInformationClass, void* lpFileInformation, int dwBufferSize);
//[DllImport("kernel32.dll", SetLastError = true)]
//internal static extern bool SetFileInformationByHandle(IntPtr hFile, int FileInformationClass, void* lpFileInformation, int dwBufferSize);
internal const int FILE_BEGIN = 0;
internal const int FILE_CURRENT = 1;
internal const int FILE_END = 2;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetFilePointerEx(IntPtr hFile, long liDistanceToMove, long* lpNewFilePointer, int dwMoveMethod);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetEndOfFile(IntPtr hFile);
[DllImport("kernel32.dll", EntryPoint = "CreateMailslotW", SetLastError = true)]
static extern IntPtr _CreateMailslot(string lpName, uint nMaxMessageSize, int lReadTimeout, SECURITY_ATTRIBUTES lpSecurityAttributes);
internal static Handle_ CreateMailslot(string lpName, uint nMaxMessageSize, int lReadTimeout, SECURITY_ATTRIBUTES lpSecurityAttributes)
=> new Handle_(_CreateMailslot(lpName, nMaxMessageSize, lReadTimeout, lpSecurityAttributes));
//note: cannot return Handle_ directly from API because returns -1 if failed. The ctor then makes 0.
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetMailslotInfo(IntPtr hMailslot, uint* lpMaxMessageSize, out int lpNextSize, out int lpMessageCount, int* lpReadTimeout = null);
[DllImport("kernel32.dll")]
internal static extern int GetApplicationUserModelId(IntPtr hProcess, ref int AppModelIDLength, char* sbAppUserModelID);
[DllImport("kernel32.dll", EntryPoint = "GetEnvironmentVariableW", SetLastError = true)]
static extern int _GetEnvironmentVariable(string lpName, char* lpBuffer, int nSize);
///
/// Calls API GetEnvironmentVariable.
/// Returns null if variable not found.
/// Does not support folders.X.
///
/// Case-insensitive name. Without %.
[SkipLocalsInit]
internal static string GetEnvironmentVariable(string name) {
using FastBuffer b = new();
for (; ; ) if (b.GetString(_GetEnvironmentVariable(name, b.p, b.n), out var s)) return s;
}
///
/// Returns true if environment variable exists.
///
internal static bool EnvironmentVariableExists(string name) => 0 != _GetEnvironmentVariable(name, null, 0);
[DllImport("kernel32.dll", EntryPoint = "SetEnvironmentVariableW", SetLastError = true)]
internal static extern bool SetEnvironmentVariable(string lpName, string lpValue);
[DllImport("kernel32.dll", EntryPoint = "ExpandEnvironmentStringsW")]
static extern int _ExpandEnvironmentStrings(string lpSrc, char* lpDst, int nSize);
///
/// Calls API ExpandEnvironmentStrings.
/// Returns false if failed or result is same; then r is s.
/// r can be same variable as s.
///
[SkipLocalsInit]
internal static bool ExpandEnvironmentStrings(string s, out string r) {
using FastBuffer b = new();
for (; ; ) if (b.GetString(_ExpandEnvironmentStrings(s, b.p, b.n), out r, BSFlags.ReturnsLengthWith0, s)) return (object)r != s;
}
[DllImport("kernel32.dll", EntryPoint = "GetEnvironmentStringsW")]
internal static extern char* GetEnvironmentStrings();
[DllImport("kernel32.dll", EntryPoint = "FreeEnvironmentStringsW")]
internal static extern bool FreeEnvironmentStrings(char* penv);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int GetProcessId(IntPtr Process);
internal struct FILETIME {
public uint dwLowDateTime;
public uint dwHighDateTime;
public static implicit operator long(FILETIME ft) => (long)((ulong)ft.dwHighDateTime << 32 | ft.dwLowDateTime);
public static implicit operator FILETIME(long ft) => new() { dwHighDateTime = (uint)(ft >>> 32), dwLowDateTime = (uint)ft };
}
internal struct WIN32_FIND_DATA {
public FileAttributes dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
public fixed char cFileName[260];
public fixed char cAlternateFileName[14];
///
/// Returns cFileName as string, or null if it's ".." or ".".
///
public unsafe string Name {
get {
fixed (char* p = cFileName) {
if (p[0] == '.') {
if (p[1] == '\0') return null;
if (p[1] == '.' && p[2] == '\0') return null;
}
return new string(p);
}
}
}
///
/// Returns nonzero if this is a NTFS link: 1 symlink, 2 mount, 3 other.
///
public int IsNtfsLink
=> dwFileAttributes.Has(FileAttributes.ReparsePoint) && 0 != (dwReserved0 & 0x20000000)
? dwReserved0 switch { 0xA000000C => 1, 0xA0000003 => 2, _ => 3 }
: 0;
}
[DllImport("kernel32.dll", EntryPoint = "FindFirstFileW", SetLastError = true)]
internal static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", EntryPoint = "FindNextFileW", SetLastError = true)]
internal static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll")]
internal static extern bool FindClose(IntPtr hFindFile);
#if TEST_FINDFIRSTFILEEX
internal enum FINDEX_INFO_LEVELS
{
FindExInfoStandard,
FindExInfoBasic,
FindExInfoMaxInfoLevel
}
internal const uint FIND_FIRST_EX_LARGE_FETCH = 0x2;
[DllImport("kernel32.dll", EntryPoint = "FindFirstFileExW")]
internal static extern IntPtr FindFirstFileEx(string lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, out WIN32_FIND_DATA lpFindFileData, int fSearchOp, IntPtr lpSearchFilter, uint dwAdditionalFlags);
#endif
internal const uint MOVEFILE_REPLACE_EXISTING = 0x1;
internal const uint MOVEFILE_COPY_ALLOWED = 0x2;
internal const uint MOVEFILE_DELAY_UNTIL_REBOOT = 0x4;
internal const uint MOVEFILE_WRITE_THROUGH = 0x8;
internal const uint MOVEFILE_CREATE_HARDLINK = 0x10;
internal const uint MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20;
[DllImport("kernel32.dll", EntryPoint = "MoveFileExW", SetLastError = true)]
internal static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, uint dwFlags);
//[DllImport("kernel32.dll", EntryPoint = "CopyFileW", SetLastError = true)]
//internal static extern bool CopyFile(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);
internal const uint COPY_FILE_FAIL_IF_EXISTS = 0x1;
internal const uint COPY_FILE_RESTARTABLE = 0x2;
internal const uint COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x4;
internal const uint COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x8;
internal const uint COPY_FILE_COPY_SYMLINK = 0x800;
internal const uint COPY_FILE_NO_BUFFERING = 0x1000;
[DllImport("kernel32.dll", EntryPoint = "CopyFileExW", SetLastError = true)]
static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName, nint lpProgressRoutine, IntPtr lpData, int* pbCancel, uint dwCopyFlags);
internal static bool CopyFileEx(string lpExistingFileName, string lpNewFileName, uint dwCopyFlags) {
if (!CopyFileEx(lpExistingFileName, lpNewFileName, 0, 0, null, dwCopyFlags)) return false;
//Workaround for: when copying in Vmware virtual PC from host path like @"\\vmware-host\Shared Folders\...", adds Readonly attribute.
// Also GetFileAttributes[Ex] for the source adds Readonly. But FindFirstFile[Ex] doesn't.
if (GetFileAttributes(lpNewFileName) is var a1 && a1.Has(FileAttributes.ReadOnly) && a1 != (FileAttributes)(-1)) {
var h = FindFirstFile(lpExistingFileName, out var d);
if (h != -1) {
FindClose(h);
if (!d.dwFileAttributes.Has(FileAttributes.ReadOnly)) {
Debug_.Print("Readonly attribute");
SetFileAttributes(lpNewFileName, d.dwFileAttributes);
}
}
}
return true;
}
[DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true)]
internal static extern bool DeleteFile(string lpFileName);
[DllImport("kernel32.dll", EntryPoint = "RemoveDirectoryW", SetLastError = true)]
internal static extern bool RemoveDirectory(string lpPathName);
[DllImport("kernel32.dll", EntryPoint = "CreateDirectoryW", SetLastError = true)]
internal static extern bool CreateDirectory(string lpPathName, IntPtr lpSecurityAttributes); //ref SECURITY_ATTRIBUTES
[DllImport("kernel32.dll", EntryPoint = "CreateDirectoryExW", SetLastError = true)]
internal static extern bool CreateDirectoryEx(string lpTemplateDirectory, string lpNewDirectory, IntPtr lpSecurityAttributes); //ref SECURITY_ATTRIBUTES
[DllImport("kernel32.dll", EntryPoint = "ReplaceFileW", SetLastError = true)]
internal static extern bool ReplaceFile(string lpReplacedFileName, string lpReplacementFileName, string lpBackupFileName, uint dwReplaceFlags, IntPtr lpExclude = default, IntPtr lpReserved = default);
[DllImport("kernel32.dll", EntryPoint = "GlobalAddAtomW")]
internal static extern ushort GlobalAddAtom(string lpString);
[DllImport("kernel32.dll")]
internal static extern ushort GlobalDeleteAtom(ushort nAtom);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool ReadProcessMemory(HandleRef hProcess, IntPtr lpBaseAddress, void* lpBuffer, nint nSize, nint* lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool WriteProcessMemory(HandleRef hProcess, IntPtr lpBaseAddress, void* lpBuffer, nint nSize, nint* lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
internal extern static IntPtr CreateActCtx(in ACTCTX actctx);
[DllImport("kernel32.dll", SetLastError = true)]
internal extern static bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);
[DllImport("kernel32.dll", SetLastError = true)]
internal extern static bool DeactivateActCtx(int dwFlags, IntPtr lpCookie);
[DllImport("kernel32.dll")]
internal static extern void ReleaseActCtx(IntPtr hActCtx);
internal const int ACTCTX_FLAG_RESOURCE_NAME_VALID = 0x8;
internal const int ACTCTX_FLAG_HMODULE_VALID = 0x80;
internal struct ACTCTX {
public int cbSize;
public uint dwFlags;
public string lpSource;
public ushort wProcessorArchitecture;
public ushort wLangId;
public IntPtr lpAssemblyDirectory;
public IntPtr lpResourceName;
public IntPtr lpApplicationName;
public IntPtr hModule;
}
//internal const uint THREAD_TERMINATE = 0x1;
internal const uint THREAD_SUSPEND_RESUME = 0x2;
internal const uint THREAD_SET_CONTEXT = 0x10;
internal const uint THREAD_QUERY_LIMITED_INFORMATION = 0x800;
internal const uint THREAD_ALL_ACCESS = 0x1FFFFF;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern Handle_ OpenThread(uint dwDesiredAccess, bool bInheritHandle, int dwThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern uint ResumeThread(IntPtr hThread);
//[DllImport("kernel32.dll", SetLastError = true)]
//internal static extern bool TerminateThread(IntPtr hThread, int dwExitCode);
internal const uint GMEM_FIXED = 0x0;
internal const uint GMEM_MOVEABLE = 0x2;
internal const uint GMEM_ZEROINIT = 0x40;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GlobalAlloc(uint uFlags, nint dwBytes);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GlobalFree(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern nint GlobalSize(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetFileSizeEx(IntPtr hFile, out long lpFileSize);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int WaitForMultipleObjectsEx(int nCount, IntPtr* pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable);
[DllImport("kernel32.dll")]
internal static extern int SleepEx(int dwMilliseconds, bool bAlertable);
[DllImport("kernel32.dll", EntryPoint = "GetStartupInfoW")]
internal static extern void GetStartupInfo(out STARTUPINFO lpStartupInfo);
internal struct STARTUPINFO {
public int cb;
public IntPtr lpReserved;
public char* lpDesktop;
public char* lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public ushort wShowWindow;
public ushort cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
internal struct STARTUPINFOEX {
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
internal struct PROCESS_INFORMATION : IDisposable {
public Handle_ hProcess;
public Handle_ hThread;
public int dwProcessId;
public int dwThreadId;
public void Dispose() {
hThread.Dispose();
hProcess.Dispose();
}
}
//CreateProcess flags
internal const uint CREATE_SUSPENDED = 0x4;
internal const uint CREATE_NEW_CONSOLE = 0x10;
internal const uint CREATE_UNICODE_ENVIRONMENT = 0x400;
internal const uint EXTENDED_STARTUPINFO_PRESENT = 0x80000;
//STARTUPINFO flags
internal const uint STARTF_USESHOWWINDOW = 0x1;
internal const uint STARTF_FORCEOFFFEEDBACK = 0x80;
internal const uint STARTF_USESTDHANDLES = 0x100;
[DllImport("kernel32.dll", EntryPoint = "CreateProcessW", SetLastError = true)]
internal static extern bool CreateProcess(string lpApplicationName, char[] lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory, in STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUserW", SetLastError = true)]
internal static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, char[] lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory, in STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", EntryPoint = "CreateWaitableTimerW", SetLastError = true)]
internal static extern Handle_ CreateWaitableTimer(SECURITY_ATTRIBUTES lpTimerAttributes, bool bManualReset, string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetWaitableTimer(IntPtr hTimer, ref long lpDueTime, int lPeriod = 0, IntPtr pfnCompletionRoutine = default, IntPtr lpArgToCompletionRoutine = default, bool fResume = false);
[DllImport("kernel32.dll", EntryPoint = "OpenWaitableTimerW", SetLastError = true)]
internal static extern Handle_ OpenWaitableTimer(uint dwDesiredAccess, bool bInheritHandle, string lpTimerName);
[DllImport("kernel32.dll", EntryPoint = "GetModuleFileNameW", SetLastError = true)]
internal static extern int GetModuleFileName(IntPtr hModule, char* lpFilename, int nSize);
internal const uint PIPE_ACCESS_INBOUND = 0x1;
internal const uint PIPE_ACCESS_OUTBOUND = 0x2;
internal const uint PIPE_ACCESS_DUPLEX = 0x3;
internal const uint PIPE_TYPE_MESSAGE = 0x4;
internal const uint PIPE_READMODE_MESSAGE = 0x2;
internal const uint PIPE_REJECT_REMOTE_CLIENTS = 0x8;
[DllImport("kernel32.dll", EntryPoint = "CreateNamedPipeW", SetLastError = true)]
static extern IntPtr _CreateNamedPipe(string lpName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, SECURITY_ATTRIBUTES lpSecurityAttributes);
internal static Handle_ CreateNamedPipe(string lpName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, SECURITY_ATTRIBUTES lpSecurityAttributes)
=> new Handle_(_CreateNamedPipe(lpName, dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, nInBufferSize, nDefaultTimeOut, lpSecurityAttributes));
//note: cannot return Handle_ directly from API because returns -1 if failed. The ctor then makes 0.
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool CreatePipe(out Handle_ hReadPipe, out Handle_ hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool ConnectNamedPipe(IntPtr hNamedPipe, OVERLAPPED* lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool DisconnectNamedPipe(IntPtr hNamedPipe);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool GetOverlappedResult(IntPtr hFile, ref OVERLAPPED lpOverlapped, out int lpNumberOfBytesTransferred, bool bWait);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool CancelIo(IntPtr hFile);
[DllImport("kernel32.dll", EntryPoint = "WaitNamedPipeW", SetLastError = true)]
internal static extern bool WaitNamedPipe(string lpNamedPipeName, int nTimeOut);
//[DllImport("kernel32.dll", EntryPoint = "CallNamedPipeW", SetLastError = true)]
//internal static extern bool CallNamedPipe(string lpNamedPipeName, void* lpInBuffer, int nInBufferSize, out int lpOutBuffer, int nOutBufferSize, out int lpBytesRead, int nTimeOut);
//[DllImport("kernel32.dll", SetLastError = true)]
//internal static extern bool GetNamedPipeClientProcessId(IntPtr Pipe, out int ClientProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool PeekNamedPipe(IntPtr hNamedPipe, void* lpBuffer, int nBufferSize, out int lpBytesRead, out int lpTotalBytesAvail, IntPtr lpBytesLeftThisMessage = default);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool AllocConsole();
[DllImport("kernel32.dll", EntryPoint = "OutputDebugStringW")]
internal static extern void OutputDebugString(string lpOutputString);
[DllImport("kernel32.dll")]
internal static extern bool SetProcessWorkingSetSize(IntPtr hProcess, nint dwMinimumWorkingSetSize, nint dwMaximumWorkingSetSize);
//internal struct PROCESS_MEMORY_COUNTERS
//{
// public int cb;
// public int PageFaultCount;
// public nint PeakWorkingSetSize;
// public nint WorkingSetSize;
// public nint QuotaPeakPagedPoolUsage;
// public nint QuotaPagedPoolUsage;
// public nint QuotaPeakNonPagedPoolUsage;
// public nint QuotaNonPagedPoolUsage;
// public nint PagefileUsage;
// public nint PeakPagefileUsage;
//}
//[DllImport("kernel32.dll", EntryPoint = "K32GetProcessMemoryInfo")]
//internal static extern bool GetProcessMemoryInfo(IntPtr Process, ref PROCESS_MEMORY_COUNTERS ppsmemCounters, int cb);
[DllImport("kernel32.dll", EntryPoint = "FindResourceW", SetLastError = true)]
public static extern IntPtr FindResource(IntPtr hModule, nint lpName, nint lpType);
internal const int RT_GROUP_ICON = 14;
//internal const int STD_INPUT_HANDLE = -10;
internal const int STD_OUTPUT_HANDLE = -11;
[DllImport("kernel32.dll")]
internal static extern nint GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern uint GetConsoleOutputCP();
[DllImport("kernel32.dll")]
internal static extern nint SetUnhandledExceptionFilter(nint _);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern uint QueueUserAPC(delegate* unmanaged pfnAPC, IntPtr hThread, GCHandle dwData);
internal delegate void PAPCFUNC(nint Parameter);
[DllImport("kernel32.dll", EntryPoint = "GetDriveTypeW")]
internal static extern int GetDriveType(string lpRootPathName);
///
/// Use this API instead of Directory.CreateSymbolicLink which has a bug: does not throw exception when fails (eg non-admin).
/// Note: the API fails if non-admin.
/// With flag 2 does not fail if enabled developer mode.
/// It seems can be enabled for non-admin in gpedit.msc; not tested; google for more info.
/// Somewhere found this info, but it's incorrect: "Windows 11 doesn’t require administrative privileges to create symbolic links".
///
/// 1 - directory.
[DllImport("kernel32.dll", EntryPoint = "CreateSymbolicLinkW", SetLastError = true)]
[return: MarshalAs(UnmanagedType.U1)] //BOOLEAN
internal static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, uint dwFlags);
[DllImport("kernel32.dll")]
internal static extern int GetACP();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, void* lpInBuffer, int nInBufferSize, void* lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, nint lpOverlapped = 0);
internal const int IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400;
internal struct STORAGE_PROPERTY_QUERY {
public int PropertyId;
public int QueryType;
public byte AdditionalParameters;
}
internal struct DEVICE_SEEK_PENALTY_DESCRIPTOR {
public uint Version;
public uint Size;
public byte IncursSeekPenalty;
}
[DllImport("kernel32.dll", EntryPoint = "GetVolumePathNameW", SetLastError = true)]
internal static extern bool GetVolumePathName(string lpszFileName, char* lpszVolumePathName, int cchBufferLength);
[DllImport("kernel32.dll", EntryPoint = "GetVolumeNameForVolumeMountPointW", SetLastError = true)]
internal static extern bool GetVolumeNameForVolumeMountPoint(string lpszVolumeMountPoint, char* lpszVolumeName, int cchBufferLength);
[DllImport("kernel32.dll", EntryPoint = "GetCommandLineW")]
internal static extern char* GetCommandLine();
[DllImport("kernel32.dll")]
internal static extern int WTSGetActiveConsoleSessionId();
#region undocumented
internal delegate int CheckElevationEnabled(out int pResult);
#endregion
}
================================================
FILE: Au/Api/Api^user32.cs
================================================
namespace Au.Types;
//#pragma warning disable 649 //field never assigned
static unsafe partial class Api {
[DllImport("user32.dll", EntryPoint = "SendMessageW", SetLastError = true)]
internal static extern nint SendMessage(wnd hWnd, int msg, nint wParam, nint lParam);
[DllImport("user32.dll", EntryPoint = "SendMessageTimeoutW", SetLastError = true)]
internal static extern nint SendMessageTimeout(wnd hWnd, int Msg, nint wParam, nint lParam, SMTFlags flags, int uTimeout, out nint lpdwResult);
[DllImport("user32.dll", EntryPoint = "SendNotifyMessageW", SetLastError = true)]
internal static extern bool SendNotifyMessage(wnd hWnd, int Msg, nint wParam, nint lParam);
[DllImport("user32.dll", EntryPoint = "PostMessageW", SetLastError = true)]
internal static extern bool PostMessage(wnd hWnd, int Msg, nint wParam, nint lParam);
[DllImport("user32.dll", EntryPoint = "PostThreadMessageW", SetLastError = true)]
internal static extern bool PostThreadMessage(int idThread, int Msg, nint wParam, nint lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern nint GetWindowLongW(wnd hWnd, int nIndex);
[DllImport("user32.dll", SetLastError = true)]
static extern nint GetWindowLongPtrW(wnd hWnd, int nIndex);
//info: 32-bit user32.dll does not have GetWindowLongPtrW etc. In C++, in x86 config it is defined as GetWindowLongW etc.
internal static nint GetWindowLongPtr(wnd w, int nIndex)
=> IntPtr.Size == 8 ? GetWindowLongPtrW(w, nIndex) : GetWindowLongW(w, nIndex);
[DllImport("user32.dll", SetLastError = true)]
static extern nint SetWindowLongW(wnd hWnd, int nIndex, nint dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
static extern nint SetWindowLongPtrW(wnd hWnd, int nIndex, nint dwNewLong);
internal static nint SetWindowLongPtr(wnd w, int nIndex, nint dwNewLong)
=> IntPtr.Size == 8 ? SetWindowLongPtrW(w, nIndex, dwNewLong) : SetWindowLongW(w, nIndex, dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
static extern nint GetClassLongW(wnd hWnd, int nIndex);
[DllImport("user32.dll", SetLastError = true)]
static extern nint GetClassLongPtrW(wnd hWnd, int nIndex);
internal static nint GetClassLongPtr(wnd w, int nIndex)
=> IntPtr.Size == 8 ? GetClassLongPtrW(w, nIndex) : GetClassLongW(w, nIndex);
[DllImport("user32.dll", SetLastError = true)]
static extern nint SetClassLongW(wnd hWnd, int nIndex, nint dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
static extern nint SetClassLongPtrW(wnd hWnd, int nIndex, nint dwNewLong);
internal static nint SetClassLongPtr(wnd w, int nIndex, nint dwNewLong)
=> IntPtr.Size == 8 ? SetClassLongPtrW(w, nIndex, dwNewLong) : SetClassLongW(w, nIndex, dwNewLong);
[DllImport("user32.dll", EntryPoint = "GetClassNameW", SetLastError = true)]
internal static extern int GetClassName(wnd hWnd, char* lpClassName, int nMaxCount);
[DllImport("user32.dll", EntryPoint = "InternalGetWindowText", SetLastError = true)]
internal static extern int InternalGetWindowText(wnd hWnd, char* pString, int cchMaxCount);
[DllImport("user32.dll")]
internal static extern bool IsWindow(wnd hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool IsWindowVisible(wnd hWnd);
internal const int SW_HIDE = 0;
internal const int SW_SHOWNORMAL = 1;
internal const int SW_SHOWMINIMIZED = 2;
internal const int SW_SHOWMAXIMIZED = 3;
internal const int SW_SHOWNOACTIVATE = 4; //restores min/max window
internal const int SW_SHOW = 5;
internal const int SW_MINIMIZE = 6;
internal const int SW_SHOWMINNOACTIVE = 7;
internal const int SW_SHOWNA = 8;
internal const int SW_RESTORE = 9;
internal const int SW_SHOWDEFAULT = 10;
internal const int SW_FORCEMINIMIZE = 11;
[DllImport("user32.dll", SetLastError = true)]
internal static extern void ShowWindow(wnd hWnd, int SW_X);
//note: the return value does not say succeeded/failed.
// It is non-zero if was visible, 0 if was hidden.
// Declared void to avoid programming errors.
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool IsWindowEnabled(wnd hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern void EnableWindow(wnd hWnd, bool bEnable);
//note: the returns value does not say succeeded/failed.
// It is non-zero if was disabled, 0 if was enabled.
// Declared void to avoid programming errors.
[DllImport("user32.dll", EntryPoint = "FindWindowExW", SetLastError = true)]
internal static extern wnd _FindWindowEx(wnd hWndParent, wnd hWndChildAfter, string lpszClass, string lpszWindow);
internal static wnd FindWindowEx(wnd wParent = default, wnd wAfter = default, string cn = null, string name = null) {
//Windows 11 bug: FindWindow[Ex] occasionally returns 0 for an existing window.
// Second or third call usually succeeds.
// More often fails when there is some activity, eg when Visual Studio compiles/launches an app, or when opening/closing Notepad++.
// GetLastError 0.
// To reproduce, call every 2 ms, eg to find Notepad by classname.
// EnumWindows does not fail.
// On Win10 never noticed and cannot reproduce.
// Maybe FindWindowEx uses an unsafe loop like with GetWindow. When Z order changes, skips part of windows.
// FUTURE: remove this workaround when API fixed. Also in wn::FindWndEx.
if (osVersion.minWin11) {
for (int i = 5; --i >= 0;) {
var w = _FindWindowEx(wParent, wAfter, cn, name);
if (!w.Is0) return w;
}
return default;
} else {
return _FindWindowEx(wParent, wAfter, cn, name);
}
}
internal struct WNDCLASSEX {
public int cbSize;
public uint style;
public IntPtr lpfnWndProc; //not WNDPROC to avoid auto-marshaling where don't need. Use Marshal.GetFunctionPointerForDelegate/GetDelegateForFunctionPointer.
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public nint hbrBackground;
#pragma warning disable 169
IntPtr lpszMenuName;
#pragma warning restore 169
public char* lpszClassName; //not string because CLR would call CoTaskMemFree
public IntPtr hIconSm;
///
/// If ex null, sets arrow cursor and style CS_VREDRAW | CS_HREDRAW.
///
public WNDCLASSEX(RWCEtc ex) {
cbSize = sizeof(WNDCLASSEX);
if (ex == null) {
hCursor = LoadCursor(default, MCursor.Arrow);
style = CS_VREDRAW | CS_HREDRAW;
} else {
style = ex.style;
cbClsExtra = ex.cbClsExtra;
cbWndExtra = ex.cbWndExtra;
//hInstance = ex.hInstance;
hIcon = ex.hIcon;
if (ex.hCursor != default) hCursor = ex.hCursor; else if (ex.mCursor != default) hCursor = LoadCursor(default, ex.mCursor);
hbrBackground = ex.hbrBackground;
hIconSm = ex.hIconSm;
}
}
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern ushort RegisterClassEx(in WNDCLASSEX lpwcx);
[DllImport("user32.dll", EntryPoint = "GetClassInfoExW", SetLastError = true)]
internal static extern ushort GetClassInfoEx(IntPtr hInstance, string lpszClass, ref WNDCLASSEX lpwcx);
[DllImport("user32.dll", EntryPoint = "UnregisterClassW", SetLastError = true)]
internal static extern bool UnregisterClass(string lpClassName, IntPtr hInstance);
[DllImport("user32.dll", EntryPoint = "UnregisterClassW", SetLastError = true)]
internal static extern bool UnregisterClass(uint classAtom, IntPtr hInstance);
[DllImport("user32.dll", EntryPoint = "CreateWindowExW", SetLastError = true)]
internal static extern wnd CreateWindowEx(WSE dwExStyle, string lpClassName, string lpWindowName, WS dwStyle, int x, int y, int nWidth, int nHeight, wnd hWndParent = default, nint hMenu = 0, IntPtr hInstance = default, nint lpParam = 0);
internal const int CW_USEDEFAULT = 1 << 31;
[DllImport("user32.dll", EntryPoint = "DefWindowProcW")]
internal static extern nint DefWindowProc(wnd hWnd, int msg, nint wParam, nint lParam);
[DllImport("user32.dll", EntryPoint = "CallWindowProcW")]
internal static extern nint CallWindowProc(nint lpPrevWndFunc, wnd hWnd, int Msg, nint wParam, nint lParam);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool DestroyWindow(wnd hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern void PostQuitMessage(int nExitCode);
[DllImport("user32.dll", EntryPoint = "GetMessage", SetLastError = true)]
internal static extern int _GetMessage(out MSG lpMsg, wnd hWnd, int wMsgFilterMin, int wMsgFilterMax);
internal static bool GetMessage(out MSG m, wnd hWnd = default, int wMsgFilterMin = 0, int wMsgFilterMax = 0) {
int r = _GetMessage(out m, hWnd, wMsgFilterMin, wMsgFilterMax);
if (r == -1) throw new Win32Exception();
return r != 0;
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool TranslateMessage(in MSG lpMsg);
[DllImport("user32.dll", SetLastError = true)]
internal static extern nint DispatchMessage(in MSG lpmsg);
internal const uint PM_NOREMOVE = 0x0;
internal const uint PM_REMOVE = 0x1;
internal const uint PM_NOYIELD = 0x2;
internal const uint PM_QS_SENDMESSAGE = 0x400000;
internal const uint PM_QS_POSTMESSAGE = 0x980000;
internal const uint PM_QS_PAINT = 0x200000;
internal const uint PM_QS_INPUT = 0x1C070000;
[DllImport("user32.dll", EntryPoint = "PeekMessageW", SetLastError = true)]
internal static extern bool PeekMessage(out MSG lpMsg, wnd hWnd = default, int wMsgFilterMin = 0, int wMsgFilterMax = 0, uint wRemoveMsg = PM_REMOVE);
[DllImport("user32.dll")]
internal static extern bool WaitMessage();
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ReplyMessage(nint lResult);
internal const int GA_PARENT = 1;
internal const int GA_ROOT = 2;
internal const int GA_ROOTOWNER = 3;
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd GetAncestor(wnd hwnd, uint GA_X);
[DllImport("user32.dll")]
internal static extern wnd GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetForegroundWindow(wnd hWnd);
internal const int ASFW_ANY = -1;
#if true
[DllImport("user32.dll", EntryPoint = "AllowSetForegroundWindow", SetLastError = true)]
static extern bool _AllowSetForegroundWindow(int dwProcessId);
internal static bool AllowSetForegroundWindow(int dwProcessId = ASFW_ANY) {
return _AllowSetForegroundWindow(ASFW_ANY);
//Ignore dwProcessId, because AllowSetForegroundWindow doc says:
//"The process specified by the dwProcessId parameter loses the ability to set the foreground window the next time that either the user generates input, unless the input is directed at that process, or the next time a process calls AllowSetForegroundWindow, unless the same process is specified as in the previous call to AllowSetForegroundWindow."
//If it's true (not tested), if we call AllowSetForegroundWindow with a process id, we disable setforegroundwindow in all other processes.
}
#else
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool AllowSetForegroundWindow(int dwProcessId = ASFW_ANY);
#endif
internal const uint LSFW_LOCK = 1;
internal const uint LSFW_UNLOCK = 2;
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool LockSetForegroundWindow(uint LSFW_X);
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd SetFocus(wnd hWnd);
[DllImport("user32.dll")]
internal static extern wnd GetFocus();
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd SetActiveWindow(wnd hWnd);
[DllImport("user32.dll")]
internal static extern wnd GetActiveWindow();
internal struct WINDOWPOS {
public wnd hwnd;
public wnd hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public SWPFlags flags;
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetWindowPos(wnd hWnd, wnd hWndInsertAfter, int X, int Y, int cx, int cy, SWPFlags swpFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, wnd hWnd, wnd hWndInsertAfter, int x, int y, int cx, int cy, SWPFlags uFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
internal struct FLASHWINFO {
public int cbSize;
public wnd hwnd;
public uint dwFlags;
public int uCount;
public int dwTimeout;
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool FlashWindowEx(ref FLASHWINFO pfwi);
internal const int GW_OWNER = 4;
internal const int GW_HWNDPREV = 3;
internal const int GW_HWNDNEXT = 2;
internal const int GW_HWNDLAST = 1;
internal const int GW_HWNDFIRST = 0;
internal const int GW_ENABLEDPOPUP = 6;
internal const int GW_CHILD = 5;
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd GetWindow(wnd hWnd, int GW_X);
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd GetTopWindow(wnd hWnd);
//rejected. Obsolete, confusing.
// Returns owner for top-level windows with WS_POPUP style.
// Returns 0 for child windows without WS_CHILD style, eg message-only windows and QM2 toolbar owners.
//[DllImport("user32.dll", SetLastError = true)]
//internal static extern wnd GetParent(wnd hWnd);
[DllImport("user32.dll")]
internal static extern wnd GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd GetShellWindow();
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd GetLastActivePopup(wnd hWnd);
[DllImport("user32.dll")]
internal static extern bool IntersectRect(out RECT lprcDst, in RECT lprcSrc1, in RECT lprcSrc2);
[DllImport("user32.dll")]
internal static extern bool UnionRect(out RECT lprcDst, in RECT lprcSrc1, in RECT lprcSrc2);
///
/// GetPhysicalCursorPos.
///
///
/// Gets DPI physical cursor pos, ie always in pixels.
/// The classic GetCursorPos API behavior is undefined. Sometimes physical, sometimes logical.
/// Make sure the process is fully DPI-aware.
///
[DllImport("user32.dll", EntryPoint = "GetPhysicalCursorPos", SetLastError = true)]
internal static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll", EntryPoint = "LoadImageW", SetLastError = true)]
internal static extern IntPtr LoadImage(IntPtr hInst, string name, int type, int cx, int cy, uint LR_X);
[DllImport("user32.dll", EntryPoint = "LoadImageW", SetLastError = true)]
internal static extern IntPtr LoadImage(IntPtr hInst, nint resId, int type, int cx, int cy, uint LR_X);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr CopyImage(IntPtr h, int type, int cx, int cy, uint flags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool DestroyIcon(IntPtr hIcon);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetWindowRect(wnd hWnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetClientRect(wnd hWnd, out RECT lpRect);
internal const uint WPF_SETMINPOSITION = 0x1;
internal const uint WPF_RESTORETOMAXIMIZED = 0x2;
internal const uint WPF_ASYNCWINDOWPLACEMENT = 0x4;
internal struct WINDOWPLACEMENT {
public int length;
/// WPF_x
public uint flags;
public int showCmd;
public POINT ptMinPosition;
public POINT ptMaxPosition;
public RECT rcNormalPosition;
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetWindowPlacement(wnd hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetWindowPlacement(wnd hWnd, in WINDOWPLACEMENT lpwndpl);
internal struct WINDOWINFO {
public int cbSize;
public RECT rcWindow;
public RECT rcClient;
public WS dwStyle;
public WSE dwExStyle;
public uint dwWindowStatus;
public int cxWindowBorders;
public int cyWindowBorders;
public ushort atomWindowType;
public ushort wCreatorVersion;
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetWindowInfo(wnd hwnd, ref WINDOWINFO pwi);
//[DllImport("user32.dll", SetLastError = true)]
//internal static extern bool GetTitleBarInfo(wnd hwnd, ref TITLEBARINFO pti);
//internal struct TITLEBARINFO {
// public int cbSize;
// public RECT r;
// public fixed uint rgstate[6];
//}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool IsZoomed(wnd hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool IsIconic(wnd hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int GetWindowThreadProcessId(wnd hWnd, int* lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool IsWindowUnicode(wnd hWnd);
[DllImport("user32.dll", EntryPoint = "GetPropW", SetLastError = true)]
internal static extern nint GetProp(wnd hWnd, string lpString);
[DllImport("user32.dll", EntryPoint = "GetPropW", SetLastError = true)]
//internal static extern nint GetProp(wnd hWnd, [MarshalAs(UnmanagedType.SysInt)] ushort atom); //exception, must be U2
internal static extern nint GetProp(wnd hWnd, nint atom);
[DllImport("user32.dll", EntryPoint = "SetPropW", SetLastError = true)]
internal static extern bool SetProp(wnd hWnd, string lpString, nint hData);
[DllImport("user32.dll", EntryPoint = "SetPropW", SetLastError = true)]
internal static extern bool SetProp(wnd hWnd, nint atom, nint hData);
[DllImport("user32.dll", EntryPoint = "RemovePropW", SetLastError = true)]
internal static extern nint RemoveProp(wnd hWnd, string lpString);
[DllImport("user32.dll", EntryPoint = "RemovePropW", SetLastError = true)]
internal static extern nint RemoveProp(wnd hWnd, nint atom);
internal delegate bool PROPENUMPROCEX(wnd hwnd, IntPtr lpszString, nint hData, nint dwData);
[DllImport("user32.dll", EntryPoint = "EnumPropsExW", SetLastError = true)]
internal static extern int EnumPropsEx(wnd hWnd, PROPENUMPROCEX lpEnumFunc, nint lParam);
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd GetDlgItem(wnd hDlg, int nIDDlgItem);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int GetDlgCtrlID(wnd hWnd);
internal delegate int WNDENUMPROC(wnd w, void* p);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, void* p = null);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool EnumThreadWindows(int dwThreadId, WNDENUMPROC lpEnumFunc, void* p = null);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool EnumChildWindows(wnd hWndParent, WNDENUMPROC lpEnumFunc, void* p = null);
[DllImport("user32.dll", EntryPoint = "RegisterWindowMessageW", SetLastError = true)]
internal static extern int RegisterWindowMessage(string lpString);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool IsChild(wnd hWndParent, wnd hWnd);
//rejected. As slow as GetAncestor(GA_PARENT).
//[DllImport("user32.dll", SetLastError = true), Obsolete("Undocumented API")]
//internal static extern bool IsTopLevelWindow(wnd hWnd);
[DllImport("user32.dll")]
internal static extern IntPtr MonitorFromPoint(POINT pt, SODefault dwFlags);
[DllImport("user32.dll")]
internal static extern IntPtr MonitorFromRect(in RECT lprc, SODefault dwFlags);
[DllImport("user32.dll")]
internal static extern IntPtr MonitorFromWindow(wnd hwnd, SODefault dwFlags);
internal struct MONITORINFO {
public int cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
[DllImport("user32.dll", EntryPoint = "GetMonitorInfoW")]
static extern bool _GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
internal static bool GetMonitorInfo(IntPtr hMonitor, out MONITORINFO lpmi) {
lpmi = new MONITORINFO { cbSize = sizeof(MONITORINFO) };
return _GetMonitorInfo(hMonitor, ref lpmi);
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool EnumDisplayMonitors(nint hdc, RECT* lprcClip, delegate* unmanaged lpfnEnum, nint* a);
#region GetSystemMetrics, SystemParametersInfo
internal const int SM_YVIRTUALSCREEN = 77;
internal const int SM_XVIRTUALSCREEN = 76;
internal const int SM_TABLETPC = 86;
internal const int SM_SWAPBUTTON = 23;
internal const int SM_STARTER = 88;
internal const int SM_SLOWMACHINE = 73;
internal const int SM_SHUTTINGDOWN = 8192;
internal const int SM_SHOWSOUNDS = 70;
internal const int SM_SERVERR2 = 89;
internal const int SM_SECURE = 44;
internal const int SM_SAMEDISPLAYFORMAT = 81;
internal const int SM_RESERVED4 = 27;
internal const int SM_RESERVED3 = 26;
internal const int SM_RESERVED2 = 25;
internal const int SM_RESERVED1 = 24;
internal const int SM_REMOTESESSION = 4096;
internal const int SM_REMOTECONTROL = 8193;
internal const int SM_PENWINDOWS = 41;
internal const int SM_NETWORK = 63;
internal const int SM_MOUSEWHEELPRESENT = 75;
internal const int SM_MOUSEPRESENT = 19;
internal const int SM_MIDEASTENABLED = 74;
internal const int SM_MENUDROPALIGNMENT = 40;
internal const int SM_MEDIACENTER = 87;
internal const int SM_IMMENABLED = 82;
internal const int SM_DEBUG = 22;
internal const int SM_DBCSENABLED = 42;
internal const int SM_CYVTHUMB = 9;
internal const int SM_CYVSCROLL = 20;
internal const int SM_CYVIRTUALSCREEN = 79;
internal const int SM_CYSMSIZE = 53;
internal const int SM_CYSMICON = 50;
internal const int SM_CYSMCAPTION = 51;
internal const int SM_CYSIZEFRAME = SM_CYFRAME;
internal const int SM_CYSIZE = 31;
internal const int SM_CYSCREEN = 1;
internal const int SM_CYMINTRACK = 35;
internal const int SM_CYMINSPACING = 48;
internal const int SM_CYMINIMIZED = 58;
internal const int SM_CYMIN = 29;
internal const int SM_CYMENUSIZE = 55;
internal const int SM_CYMENUCHECK = 72;
internal const int SM_CYMENU = 15;
internal const int SM_CYMAXTRACK = 60;
internal const int SM_CYMAXIMIZED = 62;
internal const int SM_CYKANJIWINDOW = 18;
internal const int SM_CYICONSPACING = 39;
internal const int SM_CYICON = 12;
internal const int SM_CYHSCROLL = 3;
internal const int SM_CYFULLSCREEN = 17;
internal const int SM_CYFRAME = 33;
internal const int SM_CYFOCUSBORDER = 84;
internal const int SM_CYFIXEDFRAME = SM_CYDLGFRAME;
internal const int SM_CYEDGE = 46;
internal const int SM_CYDRAG = 69;
internal const int SM_CYDOUBLECLK = 37;
internal const int SM_CYDLGFRAME = 8;
internal const int SM_CYCURSOR = 14;
internal const int SM_CYCAPTION = 4;
internal const int SM_CYBORDER = 6;
internal const int SM_CXVSCROLL = 2;
internal const int SM_CXVIRTUALSCREEN = 78;
internal const int SM_CXSMSIZE = 52;
internal const int SM_CXSMICON = 49;
internal const int SM_CXSIZEFRAME = SM_CXFRAME;
internal const int SM_CXSIZE = 30;
internal const int SM_CXSCREEN = 0;
internal const int SM_CXMINTRACK = 34;
internal const int SM_CXMINSPACING = 47;
internal const int SM_CXMINIMIZED = 57;
internal const int SM_CXMIN = 28;
internal const int SM_CXMENUSIZE = 54;
internal const int SM_CXMENUCHECK = 71;
internal const int SM_CXMAXTRACK = 59;
internal const int SM_CXMAXIMIZED = 61;
internal const int SM_CXICONSPACING = 38;
internal const int SM_CXICON = 11;
internal const int SM_CXHTHUMB = 10;
internal const int SM_CXHSCROLL = 21;
internal const int SM_CXFULLSCREEN = 16;
internal const int SM_CXFRAME = 32;
internal const int SM_CXFOCUSBORDER = 83;
internal const int SM_CXFIXEDFRAME = SM_CXDLGFRAME;
internal const int SM_CXEDGE = 45;
internal const int SM_CXDRAG = 68;
internal const int SM_CXDOUBLECLK = 36;
internal const int SM_CXDLGFRAME = 7;
internal const int SM_CXCURSOR = 13;
internal const int SM_CXBORDER = 5;
internal const int SM_CMOUSEBUTTONS = 43;
internal const int SM_CMONITORS = 80;
internal const int SM_CMETRICS = 90;
internal const int SM_CLEANBOOT = 67;
internal const int SM_CARETBLINKINGENABLED = 8194;
internal const int SM_ARRANGE = 56;
[DllImport("user32.dll", SetLastError = true)]
internal static extern int GetSystemMetrics(int nIndex);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int GetSystemMetricsForDpi(int nIndex, int dpi);
internal const int SPI_SETWORKAREA = 47;
internal const int SPI_SETWHEELSCROLLLINES = 105;
internal const int SPI_SETUIEFFECTS = 4159;
internal const int SPI_SETTOOLTIPFADE = 4121;
internal const int SPI_SETTOOLTIPANIMATION = 4119;
internal const int SPI_SETTOGGLEKEYS = 53;
internal const int SPI_SETSTICKYKEYS = 59;
internal const int SPI_SETSOUNDSENTRY = 65;
internal const int SPI_SETSNAPTODEFBUTTON = 96;
internal const int SPI_SETSHOWSOUNDS = 57;
internal const int SPI_SETSHOWIMEUI = 111;
internal const int SPI_SETSERIALKEYS = 63;
internal const int SPI_SETSELECTIONFADE = 4117;
internal const int SPI_SETSCREENSAVETIMEOUT = 15;
internal const int SPI_SETSCREENSAVERRUNNING = 97;
internal const int SPI_SETSCREENSAVEACTIVE = 17;
internal const int SPI_SETSCREENREADER = 71;
internal const int SPI_SETPOWEROFFTIMEOUT = 82;
internal const int SPI_SETPOWEROFFACTIVE = 86;
internal const int SPI_SETPENWINDOWS = 49;
internal const int SPI_SETNONCLIENTMETRICS = 42;
internal const int SPI_SETMOUSEVANISH = 4129;
internal const int SPI_SETMOUSETRAILS = 93;
internal const int SPI_SETMOUSESPEED = 113;
internal const int SPI_SETMOUSESONAR = 4125;
internal const int SPI_SETMOUSEKEYS = 55;
internal const int SPI_SETMOUSEHOVERWIDTH = 99;
internal const int SPI_SETMOUSEHOVERTIME = 103;
internal const int SPI_SETMOUSEHOVERHEIGHT = 101;
internal const int SPI_SETMOUSECLICKLOCKTIME = 8201;
internal const int SPI_SETMOUSECLICKLOCK = 4127;
internal const int SPI_SETMOUSEBUTTONSWAP = 33;
internal const int SPI_SETMOUSE = 4;
internal const int SPI_SETMINIMIZEDMETRICS = 44;
internal const int SPI_SETMENUUNDERLINES = SPI_SETKEYBOARDCUES;
internal const int SPI_SETMENUSHOWDELAY = 107;
internal const int SPI_SETMENUFADE = 4115;
internal const int SPI_SETMENUDROPALIGNMENT = 28;
internal const int SPI_SETMENUANIMATION = 4099;
internal const int SPI_SETLOWPOWERTIMEOUT = 81;
internal const int SPI_SETLOWPOWERACTIVE = 85;
internal const int SPI_SETLISTBOXSMOOTHSCROLLING = 4103;
internal const int SPI_SETLANGTOGGLE = 91;
internal const int SPI_SETKEYBOARDSPEED = 11;
internal const int SPI_SETKEYBOARDPREF = 69;
internal const int SPI_SETKEYBOARDDELAY = 23;
internal const int SPI_SETKEYBOARDCUES = 4107;
internal const int SPI_SETICONTITLEWRAP = 26;
internal const int SPI_SETICONTITLELOGFONT = 34;
internal const int SPI_SETICONS = 88;
internal const int SPI_SETICONMETRICS = 46;
internal const int SPI_SETHOTTRACKING = 4111;
internal const int SPI_SETHIGHCONTRAST = 67;
internal const int SPI_SETHANDHELD = 78;
internal const int SPI_SETGRIDGRANULARITY = 19;
internal const int SPI_SETGRADIENTCAPTIONS = 4105;
internal const int SPI_SETFOREGROUNDLOCKTIMEOUT = 8193;
internal const int SPI_SETFOREGROUNDFLASHCOUNT = 8197;
internal const int SPI_SETFONTSMOOTHINGTYPE = 8203;
internal const int SPI_SETFONTSMOOTHINGORIENTATION = 8211;
internal const int SPI_SETFONTSMOOTHINGCONTRAST = 8205;
internal const int SPI_SETFONTSMOOTHING = 75;
internal const int SPI_SETFOCUSBORDERWIDTH = 8207;
internal const int SPI_SETFOCUSBORDERHEIGHT = 8209;
internal const int SPI_SETFLATMENU = 4131;
internal const int SPI_SETFILTERKEYS = 51;
internal const int SPI_SETFASTTASKSWITCH = 36;
internal const int SPI_SETDROPSHADOW = 4133;
internal const int SPI_SETDRAGWIDTH = 76;
internal const int SPI_SETDRAGHEIGHT = 77;
internal const int SPI_SETDRAGFULLWINDOWS = 37;
internal const int SPI_SETDOUBLECLKWIDTH = 29;
internal const int SPI_SETDOUBLECLKHEIGHT = 30;
internal const int SPI_SETDOUBLECLICKTIME = 32;
internal const int SPI_SETDESKWALLPAPER = 20;
internal const int SPI_SETDESKPATTERN = 21;
internal const int SPI_SETDEFAULTINPUTLANG = 90;
internal const int SPI_SETCURSORSHADOW = 4123;
internal const int SPI_SETCURSORS = 87;
internal const int SPI_SETCOMBOBOXANIMATION = 4101;
internal const int SPI_SETCARETWIDTH = 8199;
internal const int SPI_SETBORDER = 6;
internal const int SPI_SETBLOCKSENDINPUTRESETS = 4135;
internal const int SPI_SETBEEP = 2;
internal const int SPI_SETANIMATION = 73;
internal const int SPI_SETACTIVEWNDTRKZORDER = 4109;
internal const int SPI_SETACTIVEWNDTRKTIMEOUT = 8195;
internal const int SPI_SETACTIVEWINDOWTRACKING = 4097;
internal const int SPI_SETACCESSTIMEOUT = 61;
internal const int SPI_LANGDRIVER = 12;
internal const int SPI_ICONVERTICALSPACING = 24;
internal const int SPI_ICONHORIZONTALSPACING = 13;
internal const int SPI_GETWORKAREA = 48;
internal const int SPI_GETWINDOWSEXTENSION = 92;
internal const int SPI_GETWHEELSCROLLLINES = 104;
internal const int SPI_GETUIEFFECTS = 4158;
internal const int SPI_GETTOOLTIPFADE = 4120;
internal const int SPI_GETTOOLTIPANIMATION = 4118;
internal const int SPI_GETTOGGLEKEYS = 52;
internal const int SPI_GETSTICKYKEYS = 58;
internal const int SPI_GETSOUNDSENTRY = 64;
internal const int SPI_GETSNAPTODEFBUTTON = 95;
internal const int SPI_GETSHOWSOUNDS = 56;
internal const int SPI_GETSHOWIMEUI = 110;
internal const int SPI_GETSERIALKEYS = 62;
internal const int SPI_GETSELECTIONFADE = 4116;
internal const int SPI_GETSCREENSAVETIMEOUT = 14;
internal const int SPI_GETSCREENSAVERRUNNING = 114;
internal const int SPI_GETSCREENSAVEACTIVE = 16;
internal const int SPI_GETSCREENREADER = 70;
internal const int SPI_GETPOWEROFFTIMEOUT = 80;
internal const int SPI_GETPOWEROFFACTIVE = 84;
internal const int SPI_GETNONCLIENTMETRICS = 41;
internal const int SPI_GETMOUSEVANISH = 4128;
internal const int SPI_GETMOUSETRAILS = 94;
internal const int SPI_GETMOUSESPEED = 112;
internal const int SPI_GETMOUSESONAR = 4124;
internal const int SPI_GETMOUSEKEYS = 54;
internal const int SPI_GETMOUSEHOVERWIDTH = 98;
internal const int SPI_GETMOUSEHOVERTIME = 102;
internal const int SPI_GETMOUSEHOVERHEIGHT = 100;
internal const int SPI_GETMOUSECLICKLOCKTIME = 8200;
internal const int SPI_GETMOUSECLICKLOCK = 4126;
internal const int SPI_GETMOUSE = 3;
internal const int SPI_GETMINIMIZEDMETRICS = 43;
internal const int SPI_GETMENUUNDERLINES = SPI_GETKEYBOARDCUES;
internal const int SPI_GETMENUSHOWDELAY = 106;
internal const int SPI_GETMENUFADE = 4114;
internal const int SPI_GETMENUDROPALIGNMENT = 27;
internal const int SPI_GETMENUANIMATION = 4098;
internal const int SPI_GETLOWPOWERTIMEOUT = 79;
internal const int SPI_GETLOWPOWERACTIVE = 83;
internal const int SPI_GETLISTBOXSMOOTHSCROLLING = 4102;
internal const int SPI_GETKEYBOARDSPEED = 10;
internal const int SPI_GETKEYBOARDPREF = 68;
internal const int SPI_GETKEYBOARDDELAY = 22;
internal const int SPI_GETKEYBOARDCUES = 4106;
internal const int SPI_GETICONTITLEWRAP = 25;
internal const int SPI_GETICONTITLELOGFONT = 31;
internal const int SPI_GETICONMETRICS = 45;
internal const int SPI_GETHOTTRACKING = 4110;
internal const int SPI_GETHIGHCONTRAST = 66;
internal const int SPI_GETGRIDGRANULARITY = 18;
internal const int SPI_GETGRADIENTCAPTIONS = 4104;
internal const int SPI_GETFOREGROUNDLOCKTIMEOUT = 8192;
internal const int SPI_GETFOREGROUNDFLASHCOUNT = 8196;
internal const int SPI_GETFONTSMOOTHINGTYPE = 8202;
internal const int SPI_GETFONTSMOOTHINGORIENTATION = 8210;
internal const int SPI_GETFONTSMOOTHINGCONTRAST = 8204;
internal const int SPI_GETFONTSMOOTHING = 74;
internal const int SPI_GETFOCUSBORDERWIDTH = 8206;
internal const int SPI_GETFOCUSBORDERHEIGHT = 8208;
internal const int SPI_GETFLATMENU = 4130;
internal const int SPI_GETFILTERKEYS = 50;
internal const int SPI_GETFASTTASKSWITCH = 35;
internal const int SPI_GETDROPSHADOW = 4132;
internal const int SPI_GETDRAGFULLWINDOWS = 38;
internal const int SPI_GETDESKWALLPAPER = 115;
internal const int SPI_GETDEFAULTINPUTLANG = 89;
internal const int SPI_GETCURSORSHADOW = 4122;
internal const int SPI_GETCOMBOBOXANIMATION = 4100;
internal const int SPI_GETCARETWIDTH = 8198;
internal const int SPI_GETBORDER = 5;
internal const int SPI_GETBLOCKSENDINPUTRESETS = 4134;
internal const int SPI_GETBEEP = 1;
internal const int SPI_GETANIMATION = 72;
internal const int SPI_GETACTIVEWNDTRKZORDER = 4108;
internal const int SPI_GETACTIVEWNDTRKTIMEOUT = 8194;
internal const int SPI_GETACTIVEWINDOWTRACKING = 4096;
internal const int SPI_GETACCESSTIMEOUT = 60;
internal const int SPI_GETMOUSEWHEELROUTING = 0x201C;
internal const uint SPIF_UPDATEINIFILE = 0x1;
internal const uint SPIF_SENDCHANGE = 0x2;
///
/// Gets or sets any value. This is the direct API call.
///
[DllImport("user32.dll", EntryPoint = "SystemParametersInfoW", SetLastError = true)]
internal static extern bool SystemParametersInfo(uint uiAction, int uiParam, void* pvParam, uint fWinIni = 0);
///
/// Gets 32-bit integer value. Returns def if failed.
///
internal static int SystemParametersInfo(uint uiAction, int def) {
int r = 0;
return SystemParametersInfo(uiAction, 0, &r) ? r : def;
}
///
/// Gets BOOL value. Returns false if failed.
///
internal static bool SystemParametersInfo(uint uiAction) {
int r = 0;
return SystemParametersInfo(uiAction, 0, &r) && r != 0;
}
//internal static bool SystemParametersInfo(uint uiAction, ref T pvParam) where T : unmanaged {
// fixed (T* p = &pvParam) return SystemParametersInfo(uiAction, sizeof(T), p, 0);
//}
///
/// Sets value.
///
internal static bool SystemParametersInfo(uint uiAction, int uiParam, void* pvParam, bool save, bool notify) {
uint f = 0;
if (save) f |= 1; //SPIF_UPDATEINIFILE
if (notify) f |= 2; //SPIF_SENDCHANGE
return SystemParametersInfo(uiAction, uiParam, pvParam, f);
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SystemParametersInfoForDpi(uint uiAction, int uiParam, void* pvParam, uint fWinIni, int dpi);
#endregion
///
/// WindowFromPhysicalPoint. On Win8.1+ it is the same as WindowFromPoint.
///
[DllImport("user32.dll", EntryPoint = "WindowFromPhysicalPoint")]
internal static extern wnd WindowFromPoint(POINT pt);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ScreenToClient(wnd hWnd, ref POINT lpPoint);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ClientToScreen(wnd hWnd, ref POINT lpPoint);
//internal static bool ClientToScreenIgnoreRtl(wnd w, ref POINT p)
//{
// if(!w.HasExStyle(WSE.LAYOUTRTL)) return ClientToScreen(w, ref p);
// if(!GetClientRect(w, out var r) || !MapWindowPoints(w, default, ref r, out _)) return false;
// p.Offset(r.left, r.top);
// return true;
//}
[DllImport("user32.dll", SetLastError = true)]
static extern int MapWindowPoints(wnd hWndFrom, wnd hWndTo, void* lpPoints, int cPoints);
internal static bool MapWindowPoints(wnd wFrom, wnd wTo, void* points, int cPoints, out int ret) {
lastError.clear();
ret = MapWindowPoints(wFrom, wTo, points, cPoints);
return ret != 0 ? true : lastError.code == 0;
}
internal static bool MapWindowPoints(wnd wFrom, wnd wTo, ref RECT r, out int ret) {
fixed (void* u = &r) return MapWindowPoints(wFrom, wTo, u, 2, out ret);
}
internal static bool MapWindowPoints(wnd wFrom, wnd wTo, ref POINT p, out int ret) {
fixed (void* u = &p) return MapWindowPoints(wFrom, wTo, u, 1, out ret);
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetGUIThreadInfo(int idThread, ref GUITHREADINFO pgui);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool AttachThreadInput(int idAttach, int idAttachTo, bool fAttach);
internal const uint KEYEVENTF_EXTENDEDKEY = 0x1;
internal const uint KEYEVENTF_KEYUP = 0x2;
internal const uint KEYEVENTF_UNICODE = 0x4;
internal const uint KEYEVENTF_SCANCODE = 0x8;
internal struct INPUTK {
nint _type;
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public int time;
public nint dwExtraInfo;
#pragma warning disable 414 //never used
int _u1, _u2; //need INPUT size
#pragma warning restore 414
public INPUTK(KKey vk, ushort sc, uint flags = 0) {
_type = INPUT_KEYBOARD; dwExtraInfo = AuExtraInfo;
wVk = (ushort)vk; wScan = sc; dwFlags = flags;
time = 0; _u2 = _u1 = 0;
Debug.Assert(sizeof(INPUTK) == sizeof(INPUTM));
}
public void Set(KKey vk, ushort sc, uint flags = 0) {
_type = INPUT_KEYBOARD; dwExtraInfo = AuExtraInfo;
wVk = (ushort)vk; wScan = sc; dwFlags = flags;
}
//public void InitCommonFields()
//{
// _type = INPUT_KEYBOARD; dwExtraInfo = AuExtraInfo;
//}
const int INPUT_KEYBOARD = 1;
}
[Flags]
internal enum IMFlags : uint {
Move = 1,
LeftDown = 2, LeftUp = 4,
RightDown = 8, RightUp = 16,
MiddleDown = 32, MiddleUp = 64,
XDown = 0x80, XUp = 0x100,
Wheel = 0x0800, HWheel = 0x01000,
NoCoalesce = 0x2000,
VirtualdDesktop = 0x4000,
Absolute = 0x8000,
//not API
X1 = 0x1000000,
X2 = 0x2000000,
};
internal struct INPUTM {
nint _type;
public int dx;
public int dy;
public int mouseData;
public IMFlags dwFlags;
public int time;
public nint dwExtraInfo;
public INPUTM(IMFlags flags, int x = 0, int y = 0, int data = 0) {
_type = INPUT_MOUSE;
dx = x; dy = y; dwFlags = flags; mouseData = data;
time = 0; dwExtraInfo = AuExtraInfo;
}
const int INPUT_MOUSE = 0;
}
///
/// Extra info value of key and mouse events sent by functions of this library.
///
internal const int AuExtraInfo = 0x71427fa5;
[DllImport("user32.dll", SetLastError = true)]
static extern int SendInput(int cInputs, void* pInputs, int cbSize);
//tested: Returns 0 when: invalid argument; other desktop active.
// Does not return 0 when: UAC (documented); BlockInput; ClipCursor.
///
internal static void SendInput(INPUTK* ip, int n = 1, bool dontThrow = false) {
if (n != SendInput(n, ip, sizeof(INPUTK))) {
if (!dontThrow) InputDesktopException.ThrowIfBadDesktop("*send keyboard input.");
//tested: if bad input desktop, GetLastError returns 'access denied'.
//throw new AuException("*send keyboard input."); //rejected. Anyway in most cases cannot detect when fails (because API returns not 0).
Debug_.Print($"SendInput(key) failed. {lastError.message}");
}
}
///
internal static void SendInput(INPUTM* ip, int n = 1, bool dontThrow = false) {
if (n != SendInput(n, ip, sizeof(INPUTM))) {
if (!dontThrow) InputDesktopException.ThrowIfBadDesktop("*send mouse input.");
//throw new AuException("*send mouse input.");
Debug_.Print($"SendInput(mouse) failed. {lastError.message}");
}
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool IsHungAppWindow(wnd hwnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetLayeredWindowAttributes(wnd hwnd, uint crKey, byte bAlpha, uint dwFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetLayeredWindowAttributes(wnd hwnd, out uint pcrKey, out byte pbAlpha, out uint pdwFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr CreateIcon(IntPtr hInstance, int nWidth, int nHeight, byte cPlanes, byte cBitsPixel, byte[] lpbANDbits, byte[] lpbXORbits);
[DllImport("user32.dll", EntryPoint = "LoadCursorW", SetLastError = true)]
internal static extern IntPtr LoadCursor(IntPtr hInstance, MCursor cursorId);
internal delegate void TIMERPROC(wnd param1, int param2, nint param3, uint param4);
[DllImport("user32.dll", SetLastError = true)]
internal static extern nint SetTimer(wnd hWnd, nint nIDEvent, int uElapse, TIMERPROC lpTimerFunc);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool KillTimer(wnd hWnd, nint uIDEvent);
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd SetParent(wnd hWndChild, wnd hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool AdjustWindowRectEx(ref RECT lpRect, WS dwStyle, bool bMenu, WSE dwExStyle);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool AdjustWindowRectExForDpi(ref RECT lpRect, WS dwStyle, bool bMenu, WSE dwExStyle, int dpi);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ChangeWindowMessageFilter(int message, uint dwFlag);
[DllImport("user32.dll", SetLastError = true)]
internal static extern short GetKeyState(int nVirtKey); //not KKey because it is :byte
[DllImport("user32.dll", SetLastError = true)]
internal static extern short GetAsyncKeyState(int vKey); //not KKey because it is :byte
internal const uint MOD_ALT = 0x1;
internal const uint MOD_CONTROL = 0x2;
internal const uint MOD_SHIFT = 0x4;
internal const uint MOD_WIN = 0x8;
internal const uint MOD_NOREPEAT = 0x4000;
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool RegisterHotKey(wnd hWnd, int id, uint fsModifiers, KKey vk);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool UnregisterHotKey(wnd hWnd, int id);
internal const uint MWMO_WAITALL = 0x1;
internal const uint MWMO_ALERTABLE = 0x2;
internal const uint MWMO_INPUTAVAILABLE = 0x4;
[DllImport("user32.dll", SetLastError = true)]
internal static extern int MsgWaitForMultipleObjectsEx(int nCount, IntPtr* pHandles, int dwMilliseconds, uint dwWakeMask, uint MWMO_Flags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool InvalidateRect(wnd hWnd, RECT* lpRect, bool bErase);
internal static bool InvalidateRect(wnd hWnd, bool bErase = false) => InvalidateRect(hWnd, null, bErase);
internal static bool InvalidateRect(wnd hWnd, RECT r, bool bErase = false) => InvalidateRect(hWnd, &r, bErase);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ValidateRect(wnd hWnd, RECT* lpRect);
internal static bool ValidateRect(wnd hWnd) => ValidateRect(hWnd, null);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetUpdateRect(wnd hWnd, out RECT lpRect, bool bErase);
internal const int ERROR = 0;
internal const int NULLREGION = 1;
internal const int SIMPLEREGION = 2;
internal const int COMPLEXREGION = 3;
[DllImport("user32.dll", SetLastError = true)]
internal static extern int GetUpdateRgn(wnd hWnd, IntPtr hRgn, bool bErase);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool InvalidateRgn(wnd hWnd, IntPtr hRgn, bool bErase);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool DragDetect(wnd hwnd, POINT pt);
[DllImport("user32.dll")]
internal static extern IntPtr GetCursor();
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetCursor(IntPtr hCursor);
[DllImport("user32.dll", SetLastError = true)]
internal static extern wnd SetCapture(wnd hWnd);
[DllImport("user32.dll")]
internal static extern wnd GetCapture();
[DllImport("user32.dll")]
internal static extern bool ReleaseCapture();
//[DllImport("user32.dll", EntryPoint = "CharLowerBuffW")]
//internal static unsafe extern int CharLowerBuff(char* lpsz, int cchLength);
//[DllImport("user32.dll", CallingConvention = CallingConvention.Cdecl)]
//internal static extern int wsprintfW(char* lpOut1024, string lpFmt, __arglist);
//note: with __arglist always returns 0. Could instead use void*, but then much work to properly pack arguments.
//tested speed (time %) of various formatting functions, with two int and one string arg, with converting to string:
// StringBuilder.Append + int.ToString(CultureInfo.InvariantCulture): 85% - FASTEST
// StringBuilder.Append: 100%
// StringBuilder.AppendFormat + int.ToString() (avoid int boxing): 140%
// StringBuilder.AppendFormat: 150%
// $"{var} string": 160% (probably uses StringBuilder)
// wsprintfW (user32.dll): 150%
// _snwprintf (msvcrt.dll): 500% - SLOWEST
[DllImport("user32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern int wsprintfA(byte* lpOut1024, string lpFmt, __arglist);
[DllImport("user32.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int wsprintfW(char* lpOut1024, string lpFmt, __arglist);
internal struct PAINTSTRUCT {
public IntPtr hdc;
public bool fErase;
public RECT rcPaint;
public bool fRestore;
public bool fIncUpdate;
fixed byte rgbReserved[32];
}
[DllImport("user32.dll")]
internal static extern IntPtr BeginPaint(wnd hWnd, out PAINTSTRUCT lpPaint);
[DllImport("user32.dll")]
internal static extern bool EndPaint(wnd hWnd, in PAINTSTRUCT lpPaint);
[DllImport("user32.dll")]
internal static extern bool UpdateWindow(wnd hWnd);
[DllImport("user32.dll")]
internal static extern nint GetKeyboardLayout(int idThread);
[DllImport("user32.dll", EntryPoint = "MapVirtualKeyExW")]
internal static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, nint dwhkl);
[DllImport("user32.dll", EntryPoint = "VkKeyScanExW")]
internal static extern short VkKeyScanEx(char ch, nint dwhkl);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool OpenClipboard(wnd hWndNewOwner);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool CloseClipboard();
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool EmptyClipboard();
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetClipboardData(int uFormat, IntPtr hMem);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetClipboardData(int uFormat);
//[DllImport("user32.dll", SetLastError = true)]
//internal static extern wnd SetClipboardViewer(wnd hWndNewViewer);
//[DllImport("user32.dll")]
//internal static extern bool ChangeClipboardChain(wnd hWndRemove, wnd hWndNewNext);
[DllImport("user32.dll")]
internal static extern wnd GetOpenClipboardWindow();
[DllImport("user32.dll")]
internal static extern uint GetClipboardSequenceNumber();
[DllImport("user32.dll", EntryPoint = "RegisterClipboardFormatW")]
internal static extern int RegisterClipboardFormat(string lpszFormat);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool AddClipboardFormatListener(wnd hwnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool RemoveClipboardFormatListener(wnd hwnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int EnumClipboardFormats(int format);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool IsClipboardFormatAvailable(int format);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int GetPriorityClipboardFormat(int[] paFormatPriorityList, int cFormats);
[DllImport("user32.dll", EntryPoint = "GetClipboardFormatNameW", SetLastError = true)]
internal static unsafe extern int GetClipboardFormatName(int format, char* lpszFormatName, int cchMaxCount);
[DllImport("user32.dll")]
internal static extern int GetDoubleClickTime();
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool DrawIconEx(IntPtr hdc, int xLeft, int yTop, IntPtr hIcon, int cxWidth, int cyWidth, int istepIfAniCur = 0, IntPtr hbrFlickerFreeDraw = default, uint diFlags = 3); //DI_NORMAL
internal const uint CURSOR_SHOWING = 0x1;
internal struct CURSORINFO {
public int cbSize;
public uint flags;
public IntPtr hCursor;
public POINT ptScreenPos;
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetCursorInfo(ref CURSORINFO pci);
internal struct ICONINFO : IDisposable {
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
public ICONINFO(IntPtr hIcon) {
GetIconInfo(hIcon, out this);
//never mind if failed. Then hbm members are default, and caller can either check it or simply let other API fail.
}
public void Dispose() {
if (hbmMask != default) DeleteObject(hbmMask);
if (hbmColor != default) DeleteObject(hbmColor);
}
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
//tested: GetIconInfoEx gets resource info only for icons loaded from a module loaded in this process.
internal struct BITMAP {
public int bmType;
public int bmWidth;
public int bmHeight;
public int bmWidthBytes;
public ushort bmPlanes;
public ushort bmBitsPixel;
public IntPtr bmBits;
}
internal const int WH_MSGFILTER = -1;
internal const int WH_KEYBOARD = 2;
internal const int WH_GETMESSAGE = 3;
internal const int WH_CALLWNDPROC = 4;
internal const int WH_CBT = 5;
//internal const int WH_SYSMSGFILTER = 6; //hook proc must be in dll
internal const int WH_MOUSE = 7;
internal const int WH_DEBUG = 9;
internal const int WH_SHELL = 10;
internal const int WH_FOREGROUNDIDLE = 11;
internal const int WH_CALLWNDPROCRET = 12;
internal const int WH_KEYBOARD_LL = 13;
internal const int WH_MOUSE_LL = 14;
internal delegate nint HOOKPROC(int code, nint wParam, nint lParam);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetWindowsHookEx(int WH_X, HOOKPROC lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
internal static extern nint CallNextHookEx(IntPtr hhk, int nCode, nint wParam, nint lParam);
internal const uint LLKHF_EXTENDED = 0x1;
internal const uint LLKHF_INJECTED = 0x10;
internal const uint LLKHF_ALTDOWN = 0x20;
internal const uint LLKHF_UP = 0x80;
internal struct KBDLLHOOKSTRUCT {
public uint vkCode;
public uint scanCode;
public uint flags;
public int time;
public nint dwExtraInfo;
public bool IsUp => 0 != (flags & LLKHF_UP);
///
/// true if the event was generated by software.
///
public bool IsInjected => 0 != (flags & LLKHF_INJECTED);
///
/// true if the event was generated by functions of this library.
///
public bool IsInjectedByAu => 0 != (flags & LLKHF_INJECTED) && dwExtraInfo == AuExtraInfo;
//CONSIDER: also add IsInjectedByAuOrQM2. And let QM2 recognize the extra info too. Or let they use the same extra info; probably bad idea.
///
/// The set function adds or removes flag 0x80000000.
/// The get function returns true if flag 0x80000000 is set.
///
public bool BlockEvent {
get => 0 != (flags & 0x80000000);
set { if (value) flags |= 0x80000000; else flags &= ~0x80000000; }
}
}
internal const uint LLMHF_INJECTED = 0x1;
internal struct MSLLHOOKSTRUCT {
public POINT pt;
public uint mouseData;
public uint flags;
public int time;
public nint dwExtraInfo;
///
/// true if the event was generated by software.
///
public bool IsInjected => 0 != (flags & LLMHF_INJECTED);
///
/// true if the event was generated by functions of this library.
///
public bool IsInjectedByAu => 0 != (flags & LLMHF_INJECTED) && dwExtraInfo == AuExtraInfo;
///
/// The set function adds or removes flag 0x80000000.
/// The get function returns true if flag 0x80000000 is set.
///
public bool BlockEvent {
get => 0 != (flags & 0x80000000);
set { if (value) flags |= 0x80000000; else flags &= ~0x80000000; }
}
}
internal const int HC_NOREMOVE = 3;
internal delegate void WINEVENTPROC(IntPtr hWinEventHook, EEvent event_, wnd hwnd, EObjid idObject, int idChild, int idEventThread, int dwmsEventTime);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetWinEventHook(EEvent eventMin, EEvent eventMax, IntPtr hmodWinEventProc, WINEVENTPROC pfnWinEventProc, int idProcess, int idThread, EHookFlags dwFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[Flags]
internal enum AnimationFlags : uint {
Roll = 0x0000, // Uses a roll animation.
HorizontalPositive = 0x00001, // Animates the window from left to right. This flag can be used with roll or slide animation.
HorizontalNegative = 0x00002, // Animates the window from right to left. This flag can be used with roll or slide animation.
VerticalPositive = 0x00004, // Animates the window from top to bottom. This flag can be used with roll or slide animation.
VerticalNegative = 0x00008, // Animates the window from bottom to top. This flag can be used with roll or slide animation.
Center = 0x00010, // Makes the window appear to collapse inward if Hide is used or expand outward if the Hide is not used.
Hide = 0x10000, // Hides the window. By default, the window is shown.
Activate = 0x20000, // Activates the window.
Slide = 0x40000, // Uses a slide animation. By default, roll animation is used.
Blend = 0x80000, // Uses a fade effect. This flag can be used only with a top-level window.
Mask = 0xfffff,
}
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool AnimateWindow(wnd hWnd, int dwTime, AnimationFlags dwFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetCaretPos(out POINT lpPoint);
internal static bool GetCaretPosInScreen_(out POINT p) => GetCaretPos(out p) && GetFocus().MapClientToScreen(ref p);
[DllImport("user32.dll")]
internal static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte* lpKeyState, char* pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
internal const uint PW_CLIENTONLY = 0x1;
internal const uint PW_RENDERFULLCONTENT = 0x2;
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool PrintWindow(wnd hwnd, IntPtr hdcBlt, uint nFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetDC(wnd hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetWindowDC(wnd hWnd);
[DllImport("user32.dll")] //note: no SetLastError = true
internal static extern int ReleaseDC(wnd hWnd, IntPtr hDC);
[DllImport("user32.dll")]
internal static extern int FillRect(IntPtr hDC, in RECT lprc, nint hbr);
[DllImport("user32.dll")]
internal static extern int FrameRect(IntPtr hDC, in RECT lprc, IntPtr hbr);
internal const uint RDW_INVALIDATE = 0x1;
internal const uint RDW_ERASE = 0x4;
internal const uint RDW_ALLCHILDREN = 0x80;
internal const uint RDW_FRAME = 0x400;
[DllImport("user32.dll")]
internal static extern bool RedrawWindow(wnd hWnd, RECT* lprcUpdate = null, IntPtr hrgnUpdate = default, uint flags = 0);
/// Au.Controls.PopupAlignment
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool CalculatePopupWindowPosition(in POINT anchorPoint, in SIZE windowSize, uint flags, in RECT excludeRect, out RECT popupWindowPosition);
[DllImport("user32.dll")]
internal static extern int MenuItemFromPoint(wnd hWnd, IntPtr hMenu, POINT ptScreen);
[DllImport("user32.dll")]
internal static extern int GetMenuItemID(IntPtr hMenu, int nPos);
internal struct MENUITEMINFO {
public int cbSize;
public uint fMask;
public uint fType;
public uint fState;
public int wID;
public IntPtr hSubMenu;
public IntPtr hbmpChecked;
public IntPtr hbmpUnchecked;
public nint dwItemData;
public char* dwTypeData;
public int cch;
public IntPtr hbmpItem;
public MENUITEMINFO(uint miim) {
cbSize = sizeof(MENUITEMINFO);
fMask = miim;
}
}
[DllImport("user32.dll", EntryPoint = "GetMenuItemInfoW")]
internal static extern bool GetMenuItemInfo(IntPtr hmenu, int item, bool fByPosition, ref MENUITEMINFO lpmii);
internal const uint MIIM_STRING = 0x40;
[DllImport("user32.dll")]
internal static extern IntPtr GetSystemMenu(wnd hWnd, bool bRevert);
[DllImport("user32.dll")]
internal static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
internal const uint MF_GRAYED = 0x1;
internal const uint SIF_RANGE = 0x1;
internal const uint SIF_PAGE = 0x2;
internal const uint SIF_POS = 0x4;
internal const uint SIF_TRACKPOS = 0x10;
internal const int SB_LINEUP = 0;
//internal const int SB_LINELEFT = 0;
internal const int SB_LINEDOWN = 1;
//internal const int SB_LINERIGHT = 1;
internal const int SB_PAGEUP = 2;
//internal const int SB_PAGELEFT = 2;
internal const int SB_PAGEDOWN = 3;
//internal const int SB_PAGERIGHT = 3;
//internal const int SB_THUMBPOSITION = 4;
internal const int SB_THUMBTRACK = 5;
internal const int SB_TOP = 6;
//internal const int SB_LEFT = 6;
internal const int SB_BOTTOM = 7;
//internal const int SB_RIGHT = 7;
//internal const int SB_ENDSCROLL = 8;
internal const int SB_HORZ = 0;
internal const int SB_VERT = 1;
//internal const int SB_CTL = 2;
internal const int SB_BOTH = 3;
internal struct SCROLLINFO {
public int cbSize;
public uint fMask;
public int nMin;
public int nMax;
public int nPage;
public int nPos;
public int nTrackPos;
public SCROLLINFO(uint mask) {
cbSize = sizeof(SCROLLINFO);
fMask = mask;
}
public int Set(wnd w, bool vertical, bool redraw = true)
=> SetScrollInfo(w, vertical ? SB_VERT : SB_HORZ, this, redraw);
public bool Get(wnd w, bool vertical)
=> GetScrollInfo(w, vertical ? SB_VERT : SB_HORZ, ref this);
public static SCROLLINFO Get(wnd w, bool vertical, uint mask) {
SCROLLINFO v = new(mask);
v.Get(w, vertical);
return v;
}
public static int GetTrackPos(wnd w, bool vertical) => Get(w, vertical, SIF_TRACKPOS).nTrackPos;
public static void SetPos(wnd w, bool vertical, int pos, bool redraw = true) {
new SCROLLINFO(SIF_POS) { nPos = pos }.Set(w, vertical, redraw);
}
public static void SetRange(wnd w, bool vertical, int max, int page, bool redraw = true) {
new SCROLLINFO(SIF_RANGE | SIF_PAGE) { nMax = max, nPage = page }.Set(w, vertical, redraw);
}
}
[DllImport("user32.dll")]
internal static extern int SetScrollInfo(wnd hwnd, int nBar, in SCROLLINFO lpsi, bool redraw);
[DllImport("user32.dll")]
internal static extern bool GetScrollInfo(wnd hwnd, int nBar, ref SCROLLINFO lpsi);
[DllImport("user32.dll")]
internal static extern bool ShowScrollBar(wnd hWnd, int wBar, bool bShow);
//[DllImport("user32.dll", SetLastError = true)]
//internal static extern bool GetScrollBarInfo(wnd hwnd, EObjid idObject, ref SCROLLBARINFO psbi);
//internal struct SCROLLBARINFO
//{
// public int cbSize;
// public RECT rcScrollBar;
// public int dxyLineButton;
// public int xyThumbTop;
// public int xyThumbBottom;
// public int reserved;
// //public fixed uint rgstate[6];
// public uint stateScrollbar, stateArrowTopRight, statePageUpRight, stateThumb, statePageDownLeft, stateArrowBottomLeft;
//}
//internal const uint STATE_SYSTEM_INVISIBLE = 0x8000;
//internal const uint STATE_SYSTEM_OFFSCREEN = 0x10000;
//internal const uint STATE_SYSTEM_PRESSED = 0x8;
//internal const uint STATE_SYSTEM_UNAVAILABLE = 0x1;
[DllImport("user32.dll", EntryPoint = "MessageBoxW")]
internal static extern int MessageBox(wnd hWnd, string lpText, string lpCaption, uint uType);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int GetWindowRgn(wnd hWnd, IntPtr hRgn);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int GetDpiForWindow(wnd hWnd);
[DllImport("user32.dll")]
internal static extern IntPtr GetWindowDpiAwarenessContext(wnd hwnd);
[DllImport("user32.dll")]
internal static extern Dpi.Awareness GetAwarenessFromDpiAwarenessContext(IntPtr value);
[DllImport("user32.dll", SetLastError = true)]
internal static extern nint SetThreadDpiAwarenessContext(nint dpiContext);
[DllImport("shcore.dll")]
internal static extern int GetDpiForMonitor(IntPtr hmonitor, int dpiType, out int dpiX, out int dpiY);
[DllImport("shcore.dll")]
internal static extern int GetProcessDpiAwareness(IntPtr hprocess, out Dpi.Awareness value); //Dpi.Awareness is PROCESS_DPI_AWARENESS
[DllImport("user32.dll")]
internal static extern bool PhysicalToLogicalPointForPerMonitorDPI(wnd hWnd, ref POINT lpPoint);
//[DllImport("user32.dll")]
//internal static extern bool PhysicalToLogicalPoint(wnd hWnd, ref POINT lpPoint);
//internal static bool PhysicalToLogicalPoint_AnyOS(wnd w, ref POINT p) => osVersion.minWin8_1 ? PhysicalToLogicalPointForPerMonitorDPI(w, ref p) : PhysicalToLogicalPoint(w, ref p);
//[DllImport("user32.dll")]
//internal static extern bool LogicalToPhysicalPointForPerMonitorDPI(wnd hWnd, ref POINT lpPoint);
[DllImport("user32.dll")]
internal static extern bool LogicalToPhysicalPoint(wnd hWnd, ref POINT lpPoint);
//internal static bool LogicalToPhysicalPoint_AnyOS(wnd w, ref POINT p) => osVersion.minWin8_1 ? LogicalToPhysicalPointForPerMonitorDPI(w, ref p) : LogicalToPhysicalPoint(w, ref p);
[DllImport("user32.dll")]
internal static extern int GetSysColor(int nIndex);
[DllImport("user32.dll")]
internal static extern IntPtr GetSysColorBrush(int nIndex);
//internal struct DRAWTEXTPARAMS
//{
// public int cbSize;
// public int iTabLength;
// public int iLeftMargin;
// public int iRightMargin;
// public int uiLengthDrawn;
//}
//[DllImport("user32.dll", SetLastError = true)]
//static extern int DrawTextExW(IntPtr hdc, char* lpchText, int cchText, ref RECT lprc, TFFlags format, DRAWTEXTPARAMS* lpdtp);
[DllImport("user32.dll", SetLastError = true)]
static extern int DrawTextExW(IntPtr hdc, char* lpchText, int cchText, ref RECT lprc, TFFlags format, void* lpdtp);
internal static int DrawText(IntPtr hdc, RStr s, ref RECT lprc, TFFlags format) {
if (format.Has(TFFlags.MODIFYSTRING)) throw new NotSupportedException("MODIFYSTRING");
fixed (char* p = s) return DrawTextExW(hdc, p, s.Length, ref lprc, format, null);
//DRAWTEXTPARAMS doc incorrect. Left nad right margin fields are in pixels, not average char widths. Not tested tab width.
}
internal const WS TTS_ALWAYSTIP = (WS)0x1;
internal const WS TTS_NOPREFIX = (WS)0x2;
internal const WS TTS_BALLOON = (WS)0x40;
internal const int TTM_ACTIVATE = 0x401;
internal const int TTM_SETMAXTIPWIDTH = 0x418;
internal const int TTM_ADDTOOL = 0x432;
internal const int TTM_DELTOOL = 0x433;
internal const int TTM_RELAYEVENT = 0x407;
//internal const uint TTF_SUBCLASS = 0x10;
internal struct TTTOOLINFO {
public int cbSize;
public uint uFlags;
public wnd hwnd;
public nint uId;
public RECT rect;
public IntPtr hinst;
public char* lpszText;
public nint lParam;
public void* lpReserved;
}
[DllImport("user32.dll")]
internal static extern bool DrawEdge(IntPtr hdc, ref RECT qrc, uint edge, uint grfFlags);
internal const uint EDGE_ETCHED = 0x6;
internal const uint BF_LEFT = 0x1;
internal const uint BF_TOP = 0x2;
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetThreadDesktop(int dwThreadId);
[DllImport("user32.dll", EntryPoint = "GetUserObjectInformationW", SetLastError = true)]
internal static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, void* pvInfo, int nLength, out int lpnLengthNeeded);
internal const int UOI_IO = 6;
//internal const int UOI_NAME = 2;
//[DllImport("user32.dll", SetLastError = true)]
//internal static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);
//[DllImport("user32.dll")]
//internal static extern bool CloseDesktop(IntPtr hDesktop);
internal const uint ISMEX_SEND = 0x1;
internal const uint ISMEX_REPLIED = 0x8;
[DllImport("user32.dll")]
internal static extern uint InSendMessageEx(nint lpReserved = 0);
public static bool InSendMessageBlocked => (InSendMessageEx() & (ISMEX_SEND | ISMEX_REPLIED)) == ISMEX_SEND;
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ExitWindowsEx(int uFlags, uint dwReason);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool LockWorkStation();
[DllImport("user32.dll", SetLastError = true)]
internal static extern nint RegisterSuspendResumeNotification(IntPtr hRecipient, uint Flags);
internal struct DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {
public delegate* unmanaged Callback;
public void* Context;
}
//internal const uint DEVICE_NOTIFY_WINDOW_HANDLE = 0x0;
//internal const int DEVICE_NOTIFY_CALLBACK = 2;
[DllImport("user32.dll")]
internal static extern bool UnregisterSuspendResumeNotification(nint Handle);
//internal struct DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {
// public IntPtr Callback; //DEVICE_NOTIFY_CALLBACK_ROUTINE
// public nint Context;
//}
//internal delegate uint DEVICE_NOTIFY_CALLBACK_ROUTINE(nint context, uint type, nint setting);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ClipCursor(RECT* lpRect);
[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
struct LASTINPUTINFO {
public int cbSize;
public uint dwTime;
}
///
/// Calls API and makes the result 64-bit.
///
internal static long GetLastInputTime() {
var r = new LASTINPUTINFO { cbSize = 8 };
if (!GetLastInputInfo(ref r)) return 0;
return TickCount32To64(r.dwTime);
}
///
/// Extends a 32-bit tick count (like from many Windows API) into the 64-bit tick count space.
///
/// 32-bit time, like from API GetTickCount. Must not be in the future.
internal static long TickCount32To64(uint tick32) {
long now64 = Environment.TickCount64;
long r = (now64 & ~0xffffffffL) | tick32;
if (r > now64) r -= 0x1_0000_0000L;
return r;
}
}
================================================
FILE: Au/Api/Api_COM.cs
================================================
namespace Au.Types;
static unsafe partial class Api {
internal struct STRRET {
public uint uType;
[StructLayout(LayoutKind.Explicit)]
internal struct TYPE_1 {
[FieldOffset(0)]
public char* pOleStr;
[FieldOffset(0)]
public uint uOffset;
[FieldOffset(0)]
public fixed sbyte cStr[260];
}
public TYPE_1 _2;
}
//internal static Guid IID_IShellFolder = new Guid(0x000214E6, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
[ComImport, Guid("000214E6-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellFolder {
//[PreserveSig] int ParseDisplayName(wnd hwnd, IntPtr pbc, [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, uint* pchEaten, out IntPtr ppidl, uint* pdwAttributes);
//[PreserveSig] int EnumObjects(wnd hwnd, uint grfFlags, out IEnumIDList ppenumIDList);
//[PreserveSig] int BindToObject(IntPtr pidl, IntPtr pbc, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);
//[PreserveSig] int BindToStorage(IntPtr pidl, IntPtr pbc, in Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);
//[PreserveSig] int CompareIDs(nint lParam, IntPtr pidl1, IntPtr pidl2);
//[PreserveSig] int CreateViewObject(wnd hwndOwner, in Guid riid, out IntPtr ppv);
//[PreserveSig] int GetAttributesOf(uint cidl, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, ref uint rgfInOut);
void _0();
void _1();
void _2();
void _3();
void _4();
void _5();
void _6();
[PreserveSig] int GetUIObjectOf(wnd hwndOwner, uint cidl, in IntPtr pidl, in Guid riid, nint rgfReserved, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);
//[PreserveSig] int GetDisplayNameOf(IntPtr pidl, uint uFlags, out STRRET pName);
//[PreserveSig] int SetNameOf(wnd hwnd, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string pszName, uint uFlags, out IntPtr ppidlOut);
}
internal static bool GetUIObjectOf(this IShellFolder t, IntPtr pidl, out T result) where T : class {
result = null;
if (0 != t.GetUIObjectOf(default, 1, pidl, typeof(T).GUID, 0, out var o) || o is not T r) return false;
result = r;
return true;
}
//internal static Guid IID_IShellItem = new Guid(0x43826D1E, 0xE718, 0x42EE, 0xBC, 0x55, 0xA1, 0xE2, 0x61, 0xC3, 0x7B, 0xFE);
[ComImport, Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItem {
//[PreserveSig] int BindToHandler(IntPtr pbc, in Guid bhid, in Guid riid, out IntPtr ppv); //IBindCtx
//[PreserveSig] int GetParent(out IShellItem ppsi);
void _0();
void _1();
[PreserveSig] int GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
[PreserveSig] int GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
//[PreserveSig] int Compare(IShellItem psi, uint hint, out int piOrder);
}
//[ComImport, Guid("000214F2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
//internal interface IEnumIDList {
// [PreserveSig] int Next(int celt, [MarshalAs(UnmanagedType.LPArray)][Out] IntPtr[] rgelt, out int pceltFetched);
// [PreserveSig] int Skip(int celt);
// [PreserveSig] int Reset();
// [PreserveSig] int Clone(out IEnumIDList ppenum);
//}
[ComImport, Guid("000214fa-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IExtractIcon {
[PreserveSig] int GetIconLocation(uint uFlags, StringBuilder pszIconFile, int cchMax, out int piIndex, out uint pwFlags);
//[PreserveSig] int Extract([MarshalAs(UnmanagedType.LPWStr)] string pszFile, int nIconIndex, out IntPtr phiconLarge, out IntPtr phiconSmall, uint nIconSize);
}
[ComImport, Guid("000214F9-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellLink {
[PreserveSig] int GetPath(char* pszFile, int cch, IntPtr pfd = default, uint fFlags = 0);
[PreserveSig] int GetIDList(out IntPtr ppidl);
[PreserveSig] int SetIDList(IntPtr pidl);
[PreserveSig] int GetDescription(char* pszName, int cch);
[PreserveSig] int SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
[PreserveSig] int GetWorkingDirectory(char* pszDir, int cch);
[PreserveSig] int SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
[PreserveSig] int GetArguments(char* pszArgs, int cch);
[PreserveSig] int SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
[PreserveSig] int GetHotkey(out ushort pwHotkey);
[PreserveSig] int SetHotkey(ushort wHotkey);
[PreserveSig] int GetShowCmd(out int piShowCmd);
[PreserveSig] int SetShowCmd(int iShowCmd);
[PreserveSig] int GetIconLocation(char* pszIconPath, int cch, out int piIcon);
[PreserveSig] int SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
[PreserveSig] int SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved = 0);
[PreserveSig] int Resolve(wnd hwnd, uint fFlags);
[PreserveSig] int SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
//info: default string marshaling in COM interfaces is BSTR, but in this interface strings are LPWSTR. Cannot use plain string and char[].
// Instead of [MarshalAs(UnmanagedType.LPArray)] [Out] char[] can be just char*. Then also need fixed when calling.
}
[ComImport, Guid("00021401-0000-0000-C000-000000000046"), ClassInterface(ClassInterfaceType.None)]
internal class ShellLink { }
[ComImport, Guid("0000010b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPersistFile {
// IPersist
[PreserveSig] int GetClassID(out Guid pClassID);
// IPersistFile
[PreserveSig] int IsDirty();
[PreserveSig] int Load([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode);
[PreserveSig] int Save([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fRemember);
//[PreserveSig] int SaveCompleted([MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
//[PreserveSig] int GetCurFile(out IntPtr ppszFileName);
}
//see also VARIANT in Struct.cs
internal struct PROPVARIANT : IDisposable {
public VARENUM vt; //ushort
ushort _u1;
uint _u2;
public nint value;
public nint value2;
///
/// Calls PropVariantClear.
///
public void Dispose() {
PropVariantClear(ref this);
}
}
internal struct PROPERTYKEY {
public Guid fmtid;
public uint pid;
}
internal static Guid IID_IPropertyStore = new Guid(0x886D8EEB, 0x8CF2, 0x4446, 0x8D, 0x02, 0xCD, 0xBA, 0x1D, 0xBD, 0xCF, 0x99);
[ComImport, Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPropertyStore {
[PreserveSig] int GetCount(out int cProps);
[PreserveSig] int GetAt(int iProp, out PROPERTYKEY pkey);
[PreserveSig] int GetValue(in PROPERTYKEY key, out PROPVARIANT pv);
[PreserveSig] int SetValue(in PROPERTYKEY key, ref PROPVARIANT propvar);
[PreserveSig] int Commit();
}
//note: this is used in the lib, even if IImageList isn't.
internal static Guid IID_IImageList = new Guid(0x46EB5926, 0x582E, 0x4017, 0x9F, 0xDF, 0xE8, 0x99, 0x8D, 0xAA, 0x09, 0x50);
[ComImport, Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IVirtualDesktopManager {
[PreserveSig] int IsWindowOnCurrentVirtualDesktop(wnd topLevelWindow, [MarshalAs(UnmanagedType.Bool)] out bool onCurrentDesktop);
[PreserveSig] int GetWindowDesktopId(wnd topLevelWindow, out Guid desktopId);
[PreserveSig] int MoveWindowToDesktop(wnd topLevelWindow, in Guid desktopId);
}
[ComImport, Guid("aa509086-5ca9-4c25-8f95-589d3c07b48a"), ClassInterface(ClassInterfaceType.None)]
internal class VirtualDesktopManager { }
}
================================================
FILE: Au/Api/Api_UIA.cs
================================================
namespace Au.Types;
///
/// Wraps some UI Automation API.
///
static class UiaUtil {
public static UiaApi.IUIAutomation Uia => _uia ??= osVersion.minWin8 ? new UiaApi.CUIAutomation8() as UiaApi.IUIAutomation : new UiaApi.CUIAutomation() as UiaApi.IUIAutomation;
[ThreadStatic] static UiaApi.IUIAutomation _uia;
///
/// Gets UI element from point.
///
/// Screen coordinates.
/// null if failed.
public static UiaApi.IUIAutomationElement ElementFromPoint(POINT xy) {
return 0 == Uia.ElementFromPoint(xy, out var e) ? e : null;
}
///
/// Gets the focused element.
///
/// null if failed.
public static UiaApi.IUIAutomationElement ElementFocused() {
return 0 == Uia.GetFocusedElement(out var e) ? e : null;
}
/////
///// Gets the container control of this or nearest ancestor element that can retrieve it.
/////
//public static wnd Hwnd(this UiaApi.IUIAutomationElement t) {
// if (0 == t.get_CurrentNativeWindowHandle(out var w) && !w.Is0) return w;
// if (0 == Uia.get_RawViewWalker(out var walker)) {
// while (0 == walker.GetParentElement(t, out var p) && p != null && p != t) {
// t = p;
// if (0 == t.get_CurrentNativeWindowHandle(out w) && !w.Is0) return w;
// }
// }
// return default;
//}
///
/// Gets caret rectangle in screen from this focused element.
///
public static bool GetCaretRect(this UiaApi.IUIAutomationElement t, out RECT r) {
if (0 == t.GetCurrentPattern(UiaApi.UIA_TextPattern2Id, out var o) && o is UiaApi.IUIAutomationTextPattern2 p2) {
if (0 == p2.GetCaretRange(out bool isActive, out var tr) && tr != null /*&& isActive*/) {
return tr.GetRect(out r, t);
}
}
if (0 == t.GetCurrentPattern(UiaApi.UIA_TextPatternId, out o) && o is UiaApi.IUIAutomationTextPattern p) {
if (0 == p.GetSelection(out var ranges) && 0 == ranges.GetElement(0, out var tr)) {
return tr.GetRect(out r, t, selectionToCaret: true);
}
}
r = default;
return false;
}
///
/// Gets rectangle in screen.
///
public static unsafe bool GetRect(this UiaApi.IUIAutomationTextRange t, out RECT r, UiaApi.IUIAutomationElement e, bool selectionToCaret = false) {
if (_GetRect(t, out r)) {
if (selectionToCaret) r.left = r.right - 1;
return true;
}
//probably no selection
if (0 == t.ExpandToEnclosingUnit(UiaApi.TextUnit.TextUnit_Character) && _GetRect(t, out r)) {
r.right = r.left + 1;
return true;
}
//probably caret at the end
if (0 == t.MoveEndpointByUnit(UiaApi.TextPatternRangeEndpoint.TextPatternRangeEndpoint_Start, UiaApi.TextUnit.TextUnit_Character, -1, out int m) && m < 0 && _GetRect(t, out r)) {
//moved to previous line?
if (0 == t.GetText(2, out var s) && !s.NE() && s[0] is '\r' or '\n' && 0 == t.ExpandToEnclosingUnit(UiaApi.TextUnit.TextUnit_Line) && _GetRect(t, out var r2)) {
r.Offset(0, r2.Height);
r.right = (r.left = r2.left) + 1;
} else {
r.right = (r.left = r.right) + 1;
}
return true;
}
//probably no text
//get the left edge of e rect
if (0 == e.get_CurrentBoundingRectangle(out r)) {
int dpi = Dpi.OfWindow(wnd.active);
int h = (int)Dpi.Unscale(r.Height, dpi);
if (h < 111) { //assume it's a single-line edit control
if (h > 32) r.top = r.bottom - Dpi.Scale(32, dpi); //get the bottom max 32 logical pixels
r.right = r.left + 1;
return true;
}
}
return false;
static unsafe bool _GetRect(UiaApi.IUIAutomationTextRange t, out RECT r) {
r = default;
if (0 != t.GetBoundingRectangles(out var sap) || sap == null) return false;
uint n = sap->rgsabound.cElements / 4;
if (n > 0) {
var p = (double*)sap->pvData + sap->rgsabound.cElements - 4;
r = new(p[0].ToInt(), p[1].ToInt(), p[2].ToInt(), p[3].ToInt());
}
UiaApi.SafeArrayDestroy(sap);
return n > 0;
}
}
public static bool GetCaretRectInPowerShell(out RECT r) {
//GetGUIThreadInfo and MSAA don't work with PowerShell.
// Does not support IUIAutomationTextPattern2.
// IUIAutomationTextPattern.GetSelection -> IUIAutomationTextRange.GetBoundingRectangles returns client coord, which may be fixed in the future.
// Win+; doesn't work too. But PhraseExpress works. And IME (interesting: temporarily replaces the caret).
// I would't care, but this was a user request.
//Now instead using an undocumented PS feature.
var t = ElementFocused();
if (t != null && 0 == t.get_CurrentControlType(out var ct) && ct == UiaApi.TypeId.Edit) {
if (0 == Uia.get_RawViewWalker(out var walker)) {
UiaApi.IUIAutomationElement e = null;
while (0 == (e == null ? walker.GetFirstChildElement(t, out e) : walker.GetNextSiblingElement(e, out e)) && e != null) {
//if (0 == e.get_CurrentControlType(out ct)) print.it(ct);
//if (0 == e.get_CurrentAutomationId(out var ai)) print.it(ai);
if (0 == e.get_CurrentAutomationId(out var ai) && ai.Find("Caret", true) >= 0 && 0 == e.get_CurrentControlType(out ct) && ct == UiaApi.TypeId.Custom) {
if (0 == e.get_CurrentBoundingRectangle(out r)) {
return true;
}
}
}
//There are 3 child elements. The first is caret.
//This is slow. Tested MSAA, but it gets only child element "selection", and cannot get rect when selection empty.
}
}
r = default;
return false;
}
/////
///// Gets text of TextPattern paragraph from point.
/////
/////
///// Screen coordinates.
///// null if the element does not support TextPattern or if failed.
//public static string PatternTextFromPoint(this UiaApi.IUIAutomationElement t, POINT xy) {
// if (0 == t.GetCurrentPattern(UiaApi.UIA_TextPatternId, out var o) && o is UiaApi.IUIAutomationTextPattern p) {
// if (0 == p.RangeFromPoint(xy, out var tr) && 0 == tr.ExpandToEnclosingUnit(UiaApi.TextUnit.TextUnit_Paragraph)) {
// if (0 == tr.GetText(5000, out var s)) return s;
// }
// }
// return null;
//}
/////
///// Gets text of ValuePattern.
/////
/////
///// null if the element does not support ValuePattern or if failed.
//public static string ValueText(this UiaApi.IUIAutomationElement t) {
// if (0 == t.GetCurrentPattern(UiaApi.UIA_ValuePatternId, out var o) && o is UiaApi.IUIAutomationValuePattern p) {
// if (0 == p.get_CurrentValue(out var s)) return s;
// }
// return null;
//}
}
#pragma warning disable 1591, 649, 169
///
/// Declarations of some UI Automation API.
///
unsafe class UiaApi : NativeApi {
[ComImport, Guid("ff48dba4-60ef-4201-aa87-54103eef594e"), ClassInterface(ClassInterfaceType.None)]
internal class CUIAutomation { }
[ComImport, Guid("e22ad333-b25f-460c-83d0-0581107395c9"), ClassInterface(ClassInterfaceType.None)]
internal class CUIAutomation8 { }
[ComImport, Guid("30cbe57d-d9d0-452a-ab13-7ac5ac4825ee"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomation {
[PreserveSig] int CompareElements(IUIAutomationElement el1, IUIAutomationElement el2, [MarshalAs(UnmanagedType.Bool)] out bool areSame);
[PreserveSig] int CompareRuntimeIds(SAFEARRAY* runtimeId1, SAFEARRAY* runtimeId2, [MarshalAs(UnmanagedType.Bool)] out bool areSame);
[PreserveSig] int GetRootElement(out IUIAutomationElement root);
[PreserveSig] int ElementFromHandle(void* hwnd, out IUIAutomationElement element);
[PreserveSig] int ElementFromPoint(POINT pt, out IUIAutomationElement element);
[PreserveSig] int GetFocusedElement(out IUIAutomationElement element);
[PreserveSig] int GetRootElementBuildCache(IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement root);
[PreserveSig] int ElementFromHandleBuildCache(void* hwnd, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement element);
[PreserveSig] int ElementFromPointBuildCache(POINT pt, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement element);
[PreserveSig] int GetFocusedElementBuildCache(IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement element);
[PreserveSig] int CreateTreeWalker(IUIAutomationCondition pCondition, out IUIAutomationTreeWalker walker);
[PreserveSig] int get_ControlViewWalker(out IUIAutomationTreeWalker walker);
[PreserveSig] int get_ContentViewWalker(out IUIAutomationTreeWalker walker);
[PreserveSig] int get_RawViewWalker(out IUIAutomationTreeWalker walker);
[PreserveSig] int get_RawViewCondition(out IUIAutomationCondition condition);
[PreserveSig] int get_ControlViewCondition(out IUIAutomationCondition condition);
[PreserveSig] int get_ContentViewCondition(out IUIAutomationCondition condition);
[PreserveSig] int CreateCacheRequest(out IUIAutomationCacheRequest cacheRequest);
[PreserveSig] int CreateTrueCondition(out IUIAutomationCondition newCondition);
[PreserveSig] int CreateFalseCondition(out IUIAutomationCondition newCondition);
[PreserveSig] int CreatePropertyCondition(int propertyId, object value, out IUIAutomationCondition newCondition);
[PreserveSig] int CreatePropertyConditionEx(int propertyId, object value, PropertyConditionFlags flags, out IUIAutomationCondition newCondition);
[PreserveSig] int CreateAndCondition(IUIAutomationCondition condition1, IUIAutomationCondition condition2, out IUIAutomationCondition newCondition);
[PreserveSig] int CreateAndConditionFromArray(SAFEARRAY* conditions, out IUIAutomationCondition newCondition);
[PreserveSig] int CreateAndConditionFromNativeArray([MarshalAs(UnmanagedType.LPArray)][In] IUIAutomationCondition[] conditions, int conditionCount, out IUIAutomationCondition newCondition);
[PreserveSig] int CreateOrCondition(IUIAutomationCondition condition1, IUIAutomationCondition condition2, out IUIAutomationCondition newCondition);
[PreserveSig] int CreateOrConditionFromArray(SAFEARRAY* conditions, out IUIAutomationCondition newCondition);
[PreserveSig] int CreateOrConditionFromNativeArray([MarshalAs(UnmanagedType.LPArray)][In] IUIAutomationCondition[] conditions, int conditionCount, out IUIAutomationCondition newCondition);
[PreserveSig] int CreateNotCondition(IUIAutomationCondition condition, out IUIAutomationCondition newCondition);
[PreserveSig] int AddAutomationEventHandler(int eventId, IUIAutomationElement element, TreeScope scope, IUIAutomationCacheRequest cacheRequest, IUIAutomationEventHandler handler);
[PreserveSig] int RemoveAutomationEventHandler(int eventId, IUIAutomationElement element, IUIAutomationEventHandler handler);
[PreserveSig] int AddPropertyChangedEventHandlerNativeArray(IUIAutomationElement element, TreeScope scope, IUIAutomationCacheRequest cacheRequest, IUIAutomationPropertyChangedEventHandler handler, [MarshalAs(UnmanagedType.LPArray)][In] int[] propertyArray, int propertyCount);
[PreserveSig] int AddPropertyChangedEventHandler(IUIAutomationElement element, TreeScope scope, IUIAutomationCacheRequest cacheRequest, IUIAutomationPropertyChangedEventHandler handler, SAFEARRAY* propertyArray);
[PreserveSig] int RemovePropertyChangedEventHandler(IUIAutomationElement element, IUIAutomationPropertyChangedEventHandler handler);
[PreserveSig] int AddStructureChangedEventHandler(IUIAutomationElement element, TreeScope scope, IUIAutomationCacheRequest cacheRequest, IUIAutomationStructureChangedEventHandler handler);
[PreserveSig] int RemoveStructureChangedEventHandler(IUIAutomationElement element, IUIAutomationStructureChangedEventHandler handler);
[PreserveSig] int AddFocusChangedEventHandler(IUIAutomationCacheRequest cacheRequest, IUIAutomationFocusChangedEventHandler handler);
[PreserveSig] int RemoveFocusChangedEventHandler(IUIAutomationFocusChangedEventHandler handler);
[PreserveSig] int RemoveAllEventHandlers();
[PreserveSig] int IntNativeArrayToSafeArray([MarshalAs(UnmanagedType.LPArray)][In] int[] array, int arrayCount, out SAFEARRAY* safeArray);
[PreserveSig] int IntSafeArrayToNativeArray(SAFEARRAY* intArray, out int* array, out int arrayCount);
[PreserveSig] int RectToVariant(RECT rc, out object var);
[PreserveSig] int VariantToRect(object var, out RECT rc);
[PreserveSig] int SafeArrayToRectNativeArray(SAFEARRAY* rects, out RECT* rectArray, out int rectArrayCount);
[PreserveSig] int CreateProxyFactoryEntry();
[PreserveSig] int get_ProxyFactoryMapping();
[PreserveSig] int GetPropertyProgrammaticName(int property, out string name);
[PreserveSig] int GetPatternProgrammaticName(int pattern, out string name);
[PreserveSig] int PollForPotentialSupportedPatterns(IUIAutomationElement pElement, out SAFEARRAY* patternIds, out SAFEARRAY* patternNames);
[PreserveSig] int PollForPotentialSupportedProperties(IUIAutomationElement pElement, out SAFEARRAY* propertyIds, out SAFEARRAY* propertyNames);
[PreserveSig] int CheckNotSupported(object value, [MarshalAs(UnmanagedType.Bool)] out bool isNotSupported);
[PreserveSig] int get_ReservedNotSupportedValue([MarshalAs(UnmanagedType.IUnknown)] out object notSupportedValue);
[PreserveSig] int get_ReservedMixedAttributeValue([MarshalAs(UnmanagedType.IUnknown)] out object mixedAttributeValue);
[PreserveSig] int ElementFromIAccessible();
[PreserveSig] int ElementFromIAccessibleBuildCache();
}
[ComImport, Guid("c270f6b5-5c69-4290-9745-7a7f97169468"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationFocusChangedEventHandler {
[PreserveSig] int HandleFocusChangedEvent(IUIAutomationElement sender);
}
[ComImport, Guid("e81d1b4e-11c5-42f8-9754-e7036c79f054"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationStructureChangedEventHandler {
[PreserveSig] int HandleStructureChangedEvent(IUIAutomationElement sender, StructureChangeType changeType, SAFEARRAY* runtimeId);
}
[ComImport, Guid("40cd37d4-c756-4b0c-8c6f-bddfeeb13b50"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationPropertyChangedEventHandler {
[PreserveSig] int HandlePropertyChangedEvent(IUIAutomationElement sender, int propertyId, object newValue);
}
[ComImport, Guid("146c3c17-f12e-4e22-8c27-f894b9b79c69"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationEventHandler {
[PreserveSig] int HandleAutomationEvent(IUIAutomationElement sender, int eventId);
}
[Flags]
internal enum PropertyConditionFlags : uint {
PropertyConditionFlags_None,
PropertyConditionFlags_IgnoreCase,
PropertyConditionFlags_MatchSubstring
}
[ComImport, Guid("4042c624-389c-4afc-a630-9df854a541fc"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationTreeWalker {
[PreserveSig] int GetParentElement(IUIAutomationElement element, out IUIAutomationElement parent);
[PreserveSig] int GetFirstChildElement(IUIAutomationElement element, out IUIAutomationElement first);
[PreserveSig] int GetLastChildElement(IUIAutomationElement element, out IUIAutomationElement last);
[PreserveSig] int GetNextSiblingElement(IUIAutomationElement element, out IUIAutomationElement next);
[PreserveSig] int GetPreviousSiblingElement(IUIAutomationElement element, out IUIAutomationElement previous);
[PreserveSig] int NormalizeElement(IUIAutomationElement element, out IUIAutomationElement normalized);
[PreserveSig] int GetParentElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement parent);
[PreserveSig] int GetFirstChildElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement first);
[PreserveSig] int GetLastChildElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement last);
[PreserveSig] int GetNextSiblingElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement next);
[PreserveSig] int GetPreviousSiblingElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement previous);
[PreserveSig] int NormalizeElementBuildCache(IUIAutomationElement element, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement normalized);
[PreserveSig] int get_Condition(out IUIAutomationCondition condition);
}
[ComImport, Guid("d22108aa-8ac5-49a5-837b-37bbb3d7591e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationElement {
[PreserveSig] int SetFocus();
[PreserveSig] int GetRuntimeId(out SAFEARRAY* runtimeId);
[PreserveSig] int FindFirst(TreeScope scope, IUIAutomationCondition condition, out IUIAutomationElement found);
[PreserveSig] int FindAll(TreeScope scope, IUIAutomationCondition condition, out IUIAutomationElementArray found);
[PreserveSig] int FindFirstBuildCache(TreeScope scope, IUIAutomationCondition condition, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement found);
[PreserveSig] int FindAllBuildCache(TreeScope scope, IUIAutomationCondition condition, IUIAutomationCacheRequest cacheRequest, out IUIAutomationElementArray found);
[PreserveSig] int BuildUpdatedCache(IUIAutomationCacheRequest cacheRequest, out IUIAutomationElement updatedElement);
[PreserveSig] int GetCurrentPropertyValue(int propertyId, out object retVal);
[PreserveSig] int GetCurrentPropertyValueEx(int propertyId, [MarshalAs(UnmanagedType.Bool)] bool ignoreDefaultValue, out object retVal);
[PreserveSig] int GetCachedPropertyValue(int propertyId, out object retVal);
[PreserveSig] int GetCachedPropertyValueEx(int propertyId, [MarshalAs(UnmanagedType.Bool)] bool ignoreDefaultValue, out object retVal);
[PreserveSig] int GetCurrentPatternAs(int patternId, in Guid riid, void** patternObject);
[PreserveSig] int GetCachedPatternAs(int patternId, in Guid riid, void** patternObject);
[PreserveSig] int GetCurrentPattern(int patternId, [MarshalAs(UnmanagedType.IUnknown)] out object patternObject);
[PreserveSig] int GetCachedPattern(int patternId, [MarshalAs(UnmanagedType.IUnknown)] out object patternObject);
[PreserveSig] int GetCachedParent(out IUIAutomationElement parent);
[PreserveSig] int GetCachedChildren(out IUIAutomationElementArray children);
[PreserveSig] int get_CurrentProcessId(out int retVal);
[PreserveSig] int get_CurrentControlType(out TypeId retVal);
[PreserveSig] int get_CurrentLocalizedControlType(out string retVal);
[PreserveSig] int get_CurrentName(out string retVal);
[PreserveSig] int get_CurrentAcceleratorKey(out string retVal);
[PreserveSig] int get_CurrentAccessKey(out string retVal);
[PreserveSig] int get_CurrentHasKeyboardFocus([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CurrentIsKeyboardFocusable([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CurrentIsEnabled([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CurrentAutomationId(out string retVal);
[PreserveSig] int get_CurrentClassName(out string retVal);
[PreserveSig] int get_CurrentHelpText(out string retVal);
[PreserveSig] int get_CurrentCulture(out int retVal);
[PreserveSig] int get_CurrentIsControlElement([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CurrentIsContentElement([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CurrentIsPassword([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CurrentNativeWindowHandle(out wnd retVal);
[PreserveSig] int get_CurrentItemType(out string retVal);
[PreserveSig] int get_CurrentIsOffscreen([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CurrentOrientation(out OrientationType retVal);
[PreserveSig] int get_CurrentFrameworkId(out string retVal);
[PreserveSig] int get_CurrentIsRequiredForForm([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CurrentItemStatus(out string retVal);
[PreserveSig] int get_CurrentBoundingRectangle(out RECT retVal);
[PreserveSig] int get_CurrentLabeledBy(out IUIAutomationElement retVal);
[PreserveSig] int get_CurrentAriaRole(out string retVal);
[PreserveSig] int get_CurrentAriaProperties(out string retVal);
[PreserveSig] int get_CurrentIsDataValidForForm([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CurrentControllerFor(out IUIAutomationElementArray retVal);
[PreserveSig] int get_CurrentDescribedBy(out IUIAutomationElementArray retVal);
[PreserveSig] int get_CurrentFlowsTo(out IUIAutomationElementArray retVal);
[PreserveSig] int get_CurrentProviderDescription(out string retVal);
[PreserveSig] int get_CachedProcessId(out int retVal);
[PreserveSig] int get_CachedControlType(out int retVal);
[PreserveSig] int get_CachedLocalizedControlType(out string retVal);
[PreserveSig] int get_CachedName(out string retVal);
[PreserveSig] int get_CachedAcceleratorKey(out string retVal);
[PreserveSig] int get_CachedAccessKey(out string retVal);
[PreserveSig] int get_CachedHasKeyboardFocus([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CachedIsKeyboardFocusable([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CachedIsEnabled([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CachedAutomationId(out string retVal);
[PreserveSig] int get_CachedClassName(out string retVal);
[PreserveSig] int get_CachedHelpText(out string retVal);
[PreserveSig] int get_CachedCulture(out int retVal);
[PreserveSig] int get_CachedIsControlElement([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CachedIsContentElement([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CachedIsPassword([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CachedNativeWindowHandle(void** retVal);
[PreserveSig] int get_CachedItemType(out string retVal);
[PreserveSig] int get_CachedIsOffscreen([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CachedOrientation(out OrientationType retVal);
[PreserveSig] int get_CachedFrameworkId(out string retVal);
[PreserveSig] int get_CachedIsRequiredForForm([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CachedItemStatus(out string retVal);
[PreserveSig] int get_CachedBoundingRectangle(out RECT retVal);
[PreserveSig] int get_CachedLabeledBy(out IUIAutomationElement retVal);
[PreserveSig] int get_CachedAriaRole(out string retVal);
[PreserveSig] int get_CachedAriaProperties(out string retVal);
[PreserveSig] int get_CachedIsDataValidForForm([MarshalAs(UnmanagedType.Bool)] out bool retVal);
[PreserveSig] int get_CachedControllerFor(out IUIAutomationElementArray retVal);
[PreserveSig] int get_CachedDescribedBy(out IUIAutomationElementArray retVal);
[PreserveSig] int get_CachedFlowsTo(out IUIAutomationElementArray retVal);
[PreserveSig] int get_CachedProviderDescription(out string retVal);
[PreserveSig] int GetClickablePoint(out POINT clickable, [MarshalAs(UnmanagedType.Bool)] out bool gotClickable);
}
internal enum OrientationType {
OrientationType_None,
OrientationType_Horizontal,
OrientationType_Vertical
}
[ComImport, Guid("b32a92b5-bc25-4078-9c08-d7ee95c48e03"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationCacheRequest {
[PreserveSig] int AddProperty(int propertyId);
[PreserveSig] int AddPattern(int patternId);
[PreserveSig] int Clone(out IUIAutomationCacheRequest clonedRequest);
[PreserveSig] int get_TreeScope(out TreeScope scope);
[PreserveSig] int put_TreeScope(TreeScope scope);
[PreserveSig] int get_TreeFilter(out IUIAutomationCondition filter);
[PreserveSig] int put_TreeFilter(IUIAutomationCondition filter);
[PreserveSig] int get_AutomationElementMode(out AutomationElementMode mode);
[PreserveSig] int put_AutomationElementMode(AutomationElementMode mode);
}
[ComImport, Guid("14314595-b4bc-4055-95f2-58f2e42c9855"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationElementArray {
[PreserveSig] int get_Length(out int length);
[PreserveSig] int GetElement(int index, out IUIAutomationElement element);
}
[ComImport, Guid("352ffba8-0973-437c-a61f-f64cafd81df9"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationCondition { }
[Flags]
internal enum TreeScope : uint {
TreeScope_None,
TreeScope_Element,
TreeScope_Children,
TreeScope_Descendants = 0x4,
TreeScope_Parent = 0x8,
TreeScope_Ancestors = 0x10,
TreeScope_Subtree = 0x7
}
internal struct SAFEARRAY {
public ushort cDims;
public ushort fFeatures;
public uint cbElements;
public uint cLocks;
public void* pvData;
/*[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]*/
public SAFEARRAYBOUND rgsabound;
}
internal struct SAFEARRAYBOUND {
public uint cElements;
public int lLbound;
}
internal enum AutomationElementMode {
AutomationElementMode_None,
AutomationElementMode_Full
}
internal enum StructureChangeType {
StructureChangeType_ChildAdded,
StructureChangeType_ChildRemoved,
StructureChangeType_ChildrenInvalidated,
StructureChangeType_ChildrenBulkAdded,
StructureChangeType_ChildrenBulkRemoved,
StructureChangeType_ChildrenReordered
}
internal enum TypeId {
Button = 50000,
Calendar = 50001,
CheckBox = 50002,
ComboBox = 50003,
Edit = 50004,
Hyperlink = 50005,
Image = 50006,
ListItem = 50007,
List = 50008,
Menu = 50009,
MenuBar = 50010,
MenuItem = 50011,
ProgressBar = 50012,
RadioButton = 50013,
ScrollBar = 50014,
Slider = 50015,
Spinner = 50016,
StatusBar = 50017,
Tab = 50018,
TabItem = 50019,
Text = 50020,
ToolBar = 50021,
ToolTip = 50022,
Tree = 50023,
TreeItem = 50024,
Custom = 50025,
Group = 50026,
Thumb = 50027,
DataGrid = 50028,
DataItem = 50029,
Document = 50030,
SplitButton = 50031,
Window = 50032,
Pane = 50033,
Header = 50034,
HeaderItem = 50035,
Table = 50036,
TitleBar = 50037,
Separator = 50038,
SemanticZoom = 50039,
AppBar = 50040,
CustomLandmark = 80000,
FormLandmark = 80001,
MainLandmark = 80002,
NavigationLandmark = 80003,
SearchLandmark = 80004,
}
[DllImport("oleaut32.dll", EntryPoint = "#16", PreserveSig = true)]
internal static extern int SafeArrayDestroy(SAFEARRAY* psa);
public const int UIA_TextPatternId = 10014;
internal const int UIA_TextPattern2Id = 10024;
[ComImport, Guid("32eba289-3583-42c9-9c59-3b6d9a1e9b6a"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IUIAutomationTextPattern {
[PreserveSig] int RangeFromPoint(POINT pt, out IUIAutomationTextRange range);
[PreserveSig] int RangeFromChild(IUIAutomationElement child, out IUIAutomationTextRange range);
[PreserveSig] int GetSelection(out IUIAutomationTextRangeArray ranges);
[PreserveSig] int GetVisibleRanges(out IUIAutomationTextRangeArray ranges);
[PreserveSig] int get_DocumentRange(out IUIAutomationTextRange range);
[PreserveSig] int get_SupportedTextSelection(out SupportedTextSelection supportedTextSelection);
}
public enum SupportedTextSelection {
SupportedTextSelection_None,
SupportedTextSelection_Single,
SupportedTextSelection_Multiple
}
[ComImport, Guid("ce4ae76a-e717-4c98-81ea-47371d028eb6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IUIAutomationTextRangeArray {
[PreserveSig] int get_Length(out int length);
[PreserveSig] int GetElement(int index, out IUIAutomationTextRange element);
}
[ComImport, Guid("a543cc6a-f4ae-494b-8239-c814481187a8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IUIAutomationTextRange {
[PreserveSig] int Clone(out IUIAutomationTextRange clonedRange);
[PreserveSig] int Compare(IUIAutomationTextRange range, [MarshalAs(UnmanagedType.Bool)] out bool areSame);
[PreserveSig] int CompareEndpoints(TextPatternRangeEndpoint srcEndPoint, IUIAutomationTextRange range, TextPatternRangeEndpoint targetEndPoint, out int compValue);
[PreserveSig] int ExpandToEnclosingUnit(TextUnit textUnit);
[PreserveSig] int FindAttribute(int attr, object val, [MarshalAs(UnmanagedType.Bool)] bool backward, out IUIAutomationTextRange found);
[PreserveSig] int FindText(string text, [MarshalAs(UnmanagedType.Bool)] bool backward, [MarshalAs(UnmanagedType.Bool)] bool ignoreCase, out IUIAutomationTextRange found);
[PreserveSig] int GetAttributeValue(int attr, out object value);
[PreserveSig] int GetBoundingRectangles(out SAFEARRAY* boundingRects);
[PreserveSig] int GetEnclosingElement(out IUIAutomationElement enclosingElement);
[PreserveSig] int GetText(int maxLength, out string text);
[PreserveSig] int Move(TextUnit unit, int count, out int moved);
[PreserveSig] int MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count, out int moved);
[PreserveSig] int MoveEndpointByRange(TextPatternRangeEndpoint srcEndPoint, IUIAutomationTextRange range, TextPatternRangeEndpoint targetEndPoint);
[PreserveSig] int Select();
[PreserveSig] int AddToSelection();
[PreserveSig] int RemoveFromSelection();
[PreserveSig] int ScrollIntoView([MarshalAs(UnmanagedType.Bool)] bool alignToTop);
[PreserveSig] int GetChildren(out IUIAutomationElementArray children);
}
[ComImport, Guid("506a921a-fcc9-409f-b23b-37eb74106872"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IUIAutomationTextPattern2 : IUIAutomationTextPattern {
// IUIAutomationTextPattern
[PreserveSig] new int RangeFromPoint(POINT pt, out IUIAutomationTextRange range);
[PreserveSig] new int RangeFromChild(IUIAutomationElement child, out IUIAutomationTextRange range);
[PreserveSig] new int GetSelection(out IUIAutomationTextRangeArray ranges);
[PreserveSig] new int GetVisibleRanges(out IUIAutomationTextRangeArray ranges);
[PreserveSig] new int get_DocumentRange(out IUIAutomationTextRange range);
[PreserveSig] new int get_SupportedTextSelection(out SupportedTextSelection supportedTextSelection);
// IUIAutomationTextPattern2
[PreserveSig] int RangeFromAnnotation(IUIAutomationElement annotation, out IUIAutomationTextRange range);
[PreserveSig] int GetCaretRange([MarshalAs(UnmanagedType.Bool)] out bool isActive, out IUIAutomationTextRange range);
}
public enum TextUnit {
TextUnit_Character,
TextUnit_Format,
TextUnit_Word,
TextUnit_Line,
TextUnit_Paragraph,
TextUnit_Page,
TextUnit_Document
}
public enum TextPatternRangeEndpoint {
TextPatternRangeEndpoint_Start,
TextPatternRangeEndpoint_End
}
}
================================================
FILE: Au/Api/Api_const.cs
================================================
//Windows API constants common to multiple API functions, such as WM_, WS_, errors.
namespace Au.Types;
static unsafe partial class Api
{
#region Errors
internal const int S_OK = 0;
internal const int S_FALSE = 1;
internal const int ERROR_FILE_NOT_FOUND = 2;
internal const int ERROR_PATH_NOT_FOUND = 3;
internal const int ERROR_ACCESS_DENIED = 5;
internal const int ERROR_INVALID_HANDLE = 6;
internal const int ERROR_NOT_SAME_DEVICE = 17;
internal const int ERROR_NO_MORE_FILES = 18;
internal const int ERROR_NOT_READY = 21;
internal const int ERROR_SHARING_VIOLATION = 32;
internal const int ERROR_LOCK_VIOLATION = 33;
internal const int ERROR_HANDLE_EOF = 38;
internal const int ERROR_BAD_NETPATH = 53;
internal const int ERROR_BAD_NET_NAME = 67;
internal const int ERROR_FILE_EXISTS = 80;
internal const int ERROR_INVALID_PARAMETER = 87;
internal const int ERROR_BROKEN_PIPE = 109;
internal const int ERROR_SEM_TIMEOUT = 121;
internal const int ERROR_INSUFFICIENT_BUFFER = 122;
internal const int ERROR_INVALID_NAME = 123;
internal const int ERROR_DIR_NOT_EMPTY = 145;
internal const int ERROR_ALREADY_EXISTS = 183;
internal const int ERROR_MORE_DATA = 234;
internal const int ERROR_DIRECTORY = 267;
internal const int ERROR_PIPE_CONNECTED = 535;
internal const int ERROR_IO_PENDING = 997;
internal const int ERROR_UNABLE_TO_REMOVE_REPLACED = 1175;
internal const int ERROR_USER_MAPPED_FILE = 1224;
internal const int ERROR_PRIVILEGE_NOT_HELD = 1314;
internal const int ERROR_INVALID_WINDOW_HANDLE = 1400;
internal const int ERROR_TIMEOUT = 1460;
internal const int E_NOTIMPL = unchecked((int)0x80004001);
internal const int E_NOINTERFACE = unchecked((int)0x80004002);
internal const int E_FAIL = unchecked((int)0x80004005);
internal const int E_INVALIDARG = unchecked((int)0x80070057);
internal const int E_ACCESSDENIED = unchecked((int)0x80070005);
internal const int E_OUTOFMEMORY = unchecked((int)0x8007000E);
internal const int DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003);
internal const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154);
internal const int RPC_E_SERVER_CANTMARSHAL_DATA = unchecked((int)0x8001000D);
internal const int E_POINTER = unchecked((int)0x80004003);
#endregion
#region WM_
internal const int WM_NULL = 0;
internal const int WM_CREATE = 0x0001;
internal const int WM_DESTROY = 0x0002;
internal const int WM_MOVE = 0x0003;
internal const int WM_SIZE = 0x0005;
internal const int WM_ACTIVATE = 0x0006;
internal const int WM_SETFOCUS = 0x0007;
internal const int WM_KILLFOCUS = 0x0008;
internal const int WM_ENABLE = 0x000A;
internal const int WM_SETREDRAW = 0x000B;
internal const int WM_SETTEXT = 0x000C;
internal const int WM_GETTEXT = 0x000D;
internal const int WM_GETTEXTLENGTH = 0x000E;
internal const int WM_PAINT = 0x000F;
internal const int WM_CLOSE = 0x0010;
internal const int WM_QUERYENDSESSION = 0x0011;
internal const int WM_QUERYOPEN = 0x0013;
internal const int WM_ENDSESSION = 0x0016;
internal const int WM_QUIT = 0x0012;
internal const int WM_ERASEBKGND = 0x0014;
internal const int WM_SYSCOLORCHANGE = 0x0015;
internal const int WM_SHOWWINDOW = 0x0018;
internal const int WM_SETTINGCHANGE = 0x001A;
internal const int WM_DEVMODECHANGE = 0x001B;
internal const int WM_ACTIVATEAPP = 0x001C;
internal const int WM_FONTCHANGE = 0x001D;
internal const int WM_TIMECHANGE = 0x001E;
internal const int WM_CANCELMODE = 0x001F;
internal const int WM_SETCURSOR = 0x0020;
internal const int WM_MOUSEACTIVATE = 0x0021;
internal const int WM_CHILDACTIVATE = 0x0022;
internal const int WM_QUEUESYNC = 0x0023;
internal const int WM_GETMINMAXINFO = 0x0024;
internal const int WM_PAINTICON = 0x0026;
internal const int WM_ICONERASEBKGND = 0x0027;
internal const int WM_NEXTDLGCTL = 0x0028;
internal const int WM_SPOOLERSTATUS = 0x002A;
internal const int WM_DRAWITEM = 0x002B;
internal const int WM_MEASUREITEM = 0x002C;
internal const int WM_DELETEITEM = 0x002D;
internal const int WM_VKEYTOITEM = 0x002E;
internal const int WM_CHARTOITEM = 0x002F;
internal const int WM_SETFONT = 0x0030;
internal const int WM_GETFONT = 0x0031;
internal const int WM_SETHOTKEY = 0x0032;
internal const int WM_GETHOTKEY = 0x0033;
internal const int WM_QUERYDRAGICON = 0x0037;
internal const int WM_COMPAREITEM = 0x0039;
internal const int WM_GETOBJECT = 0x003D;
internal const int WM_COMPACTING = 0x0041;
internal const int WM_WINDOWPOSCHANGING = 0x0046;
internal const int WM_WINDOWPOSCHANGED = 0x0047;
internal const int WM_COPYDATA = 0x004A;
internal const int WM_CANCELJOURNAL = 0x004B;
internal const int WM_NOTIFY = 0x004E;
internal const int WM_INPUTLANGCHANGEREQUEST = 0x0050;
internal const int WM_INPUTLANGCHANGE = 0x0051;
internal const int WM_TCARD = 0x0052;
internal const int WM_HELP = 0x0053;
internal const int WM_USERCHANGED = 0x0054;
internal const int WM_NOTIFYFORMAT = 0x0055;
internal const int WM_CONTEXTMENU = 0x007B;
internal const int WM_STYLECHANGING = 0x007C;
internal const int WM_STYLECHANGED = 0x007D;
internal const int WM_DISPLAYCHANGE = 0x007E;
internal const int WM_GETICON = 0x007F;
internal const int WM_SETICON = 0x0080;
internal const int WM_NCCREATE = 0x0081;
internal const int WM_NCDESTROY = 0x0082;
internal const int WM_NCCALCSIZE = 0x0083;
internal const int WM_NCHITTEST = 0x0084;
internal const int WM_NCPAINT = 0x0085;
internal const int WM_NCACTIVATE = 0x0086;
internal const int WM_GETDLGCODE = 0x0087;
internal const int WM_SYNCPAINT = 0x0088;
internal const int WM_NCMOUSEMOVE = 0x00A0;
internal const int WM_NCLBUTTONDOWN = 0x00A1;
internal const int WM_NCLBUTTONUP = 0x00A2;
internal const int WM_NCLBUTTONDBLCLK = 0x00A3;
internal const int WM_NCRBUTTONDOWN = 0x00A4;
internal const int WM_NCRBUTTONUP = 0x00A5;
internal const int WM_NCRBUTTONDBLCLK = 0x00A6;
internal const int WM_NCMBUTTONDOWN = 0x00A7;
internal const int WM_NCMBUTTONUP = 0x00A8;
internal const int WM_NCMBUTTONDBLCLK = 0x00A9;
internal const int WM_NCXBUTTONDOWN = 0x00AB;
internal const int WM_NCXBUTTONUP = 0x00AC;
internal const int WM_NCXBUTTONDBLCLK = 0x00AD;
internal const int WM_INPUT_DEVICE_CHANGE = 0x00FE;
internal const int WM_INPUT = 0x00FF;
internal const int WM_KEYDOWN = 0x0100;
internal const int WM_KEYUP = 0x0101;
internal const int WM_CHAR = 0x0102;
internal const int WM_DEADCHAR = 0x0103;
internal const int WM_SYSKEYDOWN = 0x0104;
internal const int WM_SYSKEYUP = 0x0105;
internal const int WM_SYSCHAR = 0x0106;
internal const int WM_SYSDEADCHAR = 0x0107;
internal const int WM_UNICHAR = 0x0109;
internal const int WM_IME_STARTCOMPOSITION = 0x010D;
internal const int WM_IME_ENDCOMPOSITION = 0x010E;
internal const int WM_IME_COMPOSITION = 0x010F;
internal const int WM_INITDIALOG = 0x0110;
internal const int WM_COMMAND = 0x0111;
internal const int WM_SYSCOMMAND = 0x0112;
internal const int WM_TIMER = 0x0113;
internal const int WM_HSCROLL = 0x0114;
internal const int WM_VSCROLL = 0x0115;
internal const int WM_INITMENU = 0x0116;
internal const int WM_INITMENUPOPUP = 0x0117;
internal const int WM_MENUSELECT = 0x011F;
internal const int WM_MENUCHAR = 0x0120;
internal const int WM_ENTERIDLE = 0x0121;
internal const int WM_MENURBUTTONUP = 0x0122;
internal const int WM_MENUDRAG = 0x0123;
internal const int WM_MENUGETOBJECT = 0x0124;
internal const int WM_UNINITMENUPOPUP = 0x0125;
internal const int WM_MENUCOMMAND = 0x0126;
internal const int WM_CHANGEUISTATE = 0x0127;
internal const int WM_UPDATEUISTATE = 0x0128;
internal const int WM_QUERYUISTATE = 0x0129;
internal const int WM_CTLCOLORMSGBOX = 0x0132;
internal const int WM_CTLCOLOREDIT = 0x0133;
internal const int WM_CTLCOLORLISTBOX = 0x0134;
internal const int WM_CTLCOLORBTN = 0x0135;
internal const int WM_CTLCOLORDLG = 0x0136;
internal const int WM_CTLCOLORSCROLLBAR = 0x0137;
internal const int WM_CTLCOLORSTATIC = 0x0138;
internal const int WM_MOUSEFIRST = 0x0200;
internal const int WM_MOUSEMOVE = 0x0200;
internal const int WM_LBUTTONDOWN = 0x0201;
internal const int WM_LBUTTONUP = 0x0202;
internal const int WM_LBUTTONDBLCLK = 0x0203;
internal const int WM_RBUTTONDOWN = 0x0204;
internal const int WM_RBUTTONUP = 0x0205;
internal const int WM_RBUTTONDBLCLK = 0x0206;
internal const int WM_MBUTTONDOWN = 0x0207;
internal const int WM_MBUTTONUP = 0x0208;
internal const int WM_MBUTTONDBLCLK = 0x0209;
internal const int WM_MOUSEWHEEL = 0x020A;
internal const int WM_XBUTTONDOWN = 0x020B;
internal const int WM_XBUTTONUP = 0x020C;
internal const int WM_XBUTTONDBLCLK = 0x020D;
internal const int WM_MOUSEHWHEEL = 0x020E;
internal const int WM_MOUSELAST = 0x020E;
internal const int WM_PARENTNOTIFY = 0x0210;
internal const int WM_ENTERMENULOOP = 0x0211;
internal const int WM_EXITMENULOOP = 0x0212;
internal const int WM_NEXTMENU = 0x0213;
internal const int WM_SIZING = 0x0214;
internal const int WM_CAPTURECHANGED = 0x0215;
internal const int WM_MOVING = 0x0216;
internal const int WM_POWERBROADCAST = 0x0218;
internal const int WM_DEVICECHANGE = 0x0219;
internal const int WM_MDICREATE = 0x0220;
internal const int WM_MDIDESTROY = 0x0221;
internal const int WM_MDIACTIVATE = 0x0222;
internal const int WM_MDIRESTORE = 0x0223;
internal const int WM_MDINEXT = 0x0224;
internal const int WM_MDIMAXIMIZE = 0x0225;
internal const int WM_MDITILE = 0x0226;
internal const int WM_MDICASCADE = 0x0227;
internal const int WM_MDIICONARRANGE = 0x0228;
internal const int WM_MDIGETACTIVE = 0x0229;
internal const int WM_MDISETMENU = 0x0230;
internal const int WM_ENTERSIZEMOVE = 0x0231;
internal const int WM_EXITSIZEMOVE = 0x0232;
internal const int WM_DROPFILES = 0x0233;
internal const int WM_MDIREFRESHMENU = 0x0234;
internal const int WM_IME_SETCONTEXT = 0x0281;
internal const int WM_IME_NOTIFY = 0x0282;
internal const int WM_IME_CONTROL = 0x0283;
internal const int WM_IME_COMPOSITIONFULL = 0x0284;
internal const int WM_IME_SELECT = 0x0285;
internal const int WM_IME_CHAR = 0x0286;
internal const int WM_IME_REQUEST = 0x0288;
internal const int WM_IME_KEYDOWN = 0x0290;
internal const int WM_IME_KEYUP = 0x0291;
internal const int WM_MOUSEHOVER = 0x02A1;
internal const int WM_MOUSELEAVE = 0x02A3;
internal const int WM_NCMOUSEHOVER = 0x02A0;
internal const int WM_NCMOUSELEAVE = 0x02A2;
internal const int WM_WTSSESSION_CHANGE = 0x02B1;
internal const int WM_DPICHANGED = 0x2E0;
internal const int WM_DPICHANGED_BEFOREPARENT = 0x2E2;
internal const int WM_DPICHANGED_AFTERPARENT = 0x02E3;
internal const int WM_GETDPISCALEDSIZE = 0x02E4;
internal const int WM_CUT = 0x0300;
internal const int WM_COPY = 0x0301;
internal const int WM_PASTE = 0x0302;
internal const int WM_CLEAR = 0x0303;
internal const int WM_UNDO = 0x0304;
internal const int WM_RENDERFORMAT = 0x0305;
internal const int WM_RENDERALLFORMATS = 0x0306;
internal const int WM_DESTROYCLIPBOARD = 0x0307;
internal const int WM_DRAWCLIPBOARD = 0x0308;
internal const int WM_PAINTCLIPBOARD = 0x0309;
internal const int WM_VSCROLLCLIPBOARD = 0x030A;
internal const int WM_SIZECLIPBOARD = 0x030B;
internal const int WM_ASKCBFORMATNAME = 0x030C;
internal const int WM_CHANGECBCHAIN = 0x030D;
internal const int WM_HSCROLLCLIPBOARD = 0x030E;
internal const int WM_QUERYNEWPALETTE = 0x030F;
internal const int WM_PALETTEISCHANGING = 0x0310;
internal const int WM_PALETTECHANGED = 0x0311;
internal const int WM_HOTKEY = 0x0312;
internal const int WM_PRINT = 0x0317;
internal const int WM_PRINTCLIENT = 0x0318;
internal const int WM_APPCOMMAND = 0x0319;
internal const int WM_THEMECHANGED = 0x031A;
internal const int WM_CLIPBOARDUPDATE = 0x031D;
internal const int WM_DWMCOMPOSITIONCHANGED = 0x031E;
internal const int WM_DWMNCRENDERINGCHANGED = 0x031F;
internal const int WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320;
internal const int WM_DWMWINDOWMAXIMIZEDCHANGE = 0x0321;
internal const int WM_GETTITLEBARINFOEX = 0x033F;
internal const int WM_APP = 0x8000;
internal const int WM_USER = 0x0400;
internal const int WM_CPL_LAUNCH = WM_USER + 0x1000;
internal const int WM_CPL_LAUNCHED = WM_USER + 0x1001;
internal const int WM_SYSTIMER = 0x118;
internal const int WM_HSHELL_ACCESSIBILITYSTATE = 11;
internal const int WM_HSHELL_ACTIVATESHELLWINDOW = 3;
internal const int WM_HSHELL_APPCOMMAND = 12;
internal const int WM_HSHELL_GETMINRECT = 5;
internal const int WM_HSHELL_LANGUAGE = 8;
internal const int WM_HSHELL_REDRAW = 6;
internal const int WM_HSHELL_TASKMAN = 7;
internal const int WM_HSHELL_WINDOWCREATED = 1;
internal const int WM_HSHELL_WINDOWDESTROYED = 2;
internal const int WM_HSHELL_WINDOWACTIVATED = 4;
internal const int WM_HSHELL_WINDOWREPLACED = 13;
internal const int WM_REFLECT = 0x2000;
#endregion
#region control styles, messages etc
//ES_, EM_, EN_
internal const WS ES_MULTILINE = (WS)0x4;
internal const WS ES_PASSWORD = (WS)0x20;
internal const WS ES_AUTOVSCROLL = (WS)0x40;
internal const WS ES_AUTOHSCROLL = (WS)0x80;
internal const WS ES_WANTRETURN = (WS)0x1000;
internal const WS ES_NUMBER = (WS)0x2000;
internal const int EM_SETSEL = 0xB1;
internal const int EM_SETCUEBANNER = 0x1501;
//CBS_, CB_, CBN_
internal const WS CBS_SIMPLE = (WS)1;
internal const WS CBS_DROPDOWN = (WS)2;
internal const WS CBS_DROPDOWNLIST = (WS)3;
internal const WS CBS_AUTOHSCROLL = (WS)0x40;
internal const int CB_INSERTSTRING = 330;
internal const int CB_SETCUEBANNER = 0x1703;
internal const int MN_GETHMENU = 0x1E1;
#endregion
#region CS_
internal const uint CS_VREDRAW = 0x1;
internal const uint CS_HREDRAW = 0x2;
internal const uint CS_DBLCLKS = 0x8;
internal const uint CS_OWNDC = 0x20;
internal const uint CS_CLASSDC = 0x40;
internal const uint CS_PARENTDC = 0x80;
internal const uint CS_NOCLOSE = 0x200;
internal const uint CS_SAVEBITS = 0x800;
internal const uint CS_BYTEALIGNCLIENT = 0x1000;
internal const uint CS_BYTEALIGNWINDOW = 0x2000;
internal const uint CS_GLOBALCLASS = 0x4000;
internal const uint CS_IME = 0x10000;
internal const uint CS_DROPSHADOW = 0x20000;
#endregion
#region HT (hit-test)
internal const int HTERROR = -2;
internal const int HTTRANSPARENT = -1;
internal const int HTNOWHERE = 0;
internal const int HTCLIENT = 1;
internal const int HTCAPTION = 2;
internal const int HTSYSMENU = 3;
internal const int HTSIZE = 4;
internal const int HTMENU = 5;
internal const int HTHSCROLL = 6;
internal const int HTVSCROLL = 7;
internal const int HTMINBUTTON = 8;
internal const int HTMAXBUTTON = 9;
internal const int HTLEFT = 10;
internal const int HTRIGHT = 11;
internal const int HTTOP = 12;
internal const int HTTOPLEFT = 13;
internal const int HTTOPRIGHT = 14;
internal const int HTBOTTOM = 15;
internal const int HTBOTTOMLEFT = 16;
internal const int HTBOTTOMRIGHT = 17;
internal const int HTBORDER = 18;
internal const int HTOBJECT = 19;
internal const int HTCLOSE = 20;
internal const int HTHELP = 21;
internal const int HTSIZEFIRST = HTLEFT;
internal const int HTSIZELAST = HTBOTTOMRIGHT;
#endregion
#region SC_
internal const int SC_SIZE = 0xF000;
internal const int SC_MOVE = 0xF010;
internal const int SC_MINIMIZE = 0xF020;
internal const int SC_MAXIMIZE = 0xF030;
internal const int SC_NEXTWINDOW = 0xF040;
internal const int SC_PREVWINDOW = 0xF050;
internal const int SC_CLOSE = 0xF060;
internal const int SC_VSCROLL = 0xF070;
internal const int SC_HSCROLL = 0xF080;
internal const int SC_MOUSEMENU = 0xF090;
internal const int SC_KEYMENU = 0xF100;
internal const int SC_ARRANGE = 0xF110;
internal const int SC_RESTORE = 0xF120;
internal const int SC_TASKLIST = 0xF130;
internal const int SC_SCREENSAVE = 0xF140;
internal const int SC_HOTKEY = 0xF150;
internal const int SC_DEFAULT = 0xF160;
internal const int SC_MONITORPOWER = 0xF170;
internal const int SC_CONTEXTHELP = 0xF180;
internal const int SC_SEPARATOR = 0xF00F;
#endregion
#region COLOR_
internal const int COLOR_SCROLLBAR = 0;
internal const int COLOR_BACKGROUND = 1;
internal const int COLOR_ACTIVECAPTION = 2;
internal const int COLOR_INACTIVECAPTION = 3;
internal const int COLOR_MENU = 4;
internal const int COLOR_WINDOW = 5;
internal const int COLOR_WINDOWFRAME = 6;
internal const int COLOR_MENUTEXT = 7;
internal const int COLOR_WINDOWTEXT = 8;
internal const int COLOR_CAPTIONTEXT = 9;
internal const int COLOR_ACTIVEBORDER = 10;
internal const int COLOR_INACTIVEBORDER = 11;
internal const int COLOR_APPWORKSPACE = 12;
internal const int COLOR_HIGHLIGHT = 13;
internal const int COLOR_HIGHLIGHTTEXT = 14;
internal const int COLOR_BTNFACE = 15;
internal const int COLOR_BTNSHADOW = 16;
internal const int COLOR_GRAYTEXT = 17;
internal const int COLOR_BTNTEXT = 18;
internal const int COLOR_INACTIVECAPTIONTEXT = 19;
internal const int COLOR_BTNHIGHLIGHT = 20;
internal const int COLOR_3DDKSHADOW = 21;
internal const int COLOR_3DLIGHT = 22;
internal const int COLOR_INFOTEXT = 23;
internal const int COLOR_INFOBK = 24;
internal const int COLOR_HOTLIGHT = 26;
internal const int COLOR_GRADIENTACTIVECAPTION = 27;
internal const int COLOR_GRADIENTINACTIVECAPTION = 28;
internal const int COLOR_MENUHILIGHT = 29;
internal const int COLOR_MENUBAR = 30;
internal const int COLOR_DESKTOP = 1;
internal const int COLOR_3DFACE = 15;
internal const int COLOR_3DSHADOW = 16;
internal const int COLOR_3DHIGHLIGHT = 20;
internal const int COLOR_3DHILIGHT = 20;
internal const int COLOR_BTNHILIGHT = 20;
#endregion
#region QS_
internal const uint QS_KEY = 0x1;
internal const uint QS_MOUSEMOVE = 0x2;
internal const uint QS_MOUSEBUTTON = 0x4;
internal const uint QS_POSTMESSAGE = 0x8;
internal const uint QS_TIMER = 0x10;
internal const uint QS_PAINT = 0x20;
internal const uint QS_SENDMESSAGE = 0x40;
internal const uint QS_HOTKEY = 0x80;
internal const uint QS_ALLPOSTMESSAGE = 0x100;
internal const uint QS_RAWINPUT = 0x400;
internal const uint QS_TOUCH = 0x800;
internal const uint QS_POINTER = 0x1000;
internal const uint QS_MOUSE = 0x6;
internal const uint QS_INPUT = 0x1C07;
internal const uint QS_ALLEVENTS = 0x1CBF;
internal const uint QS_ALLINPUT = 0x1CFF;
#endregion
#region WAIT_
internal const int WAIT_FAILED = -1;
internal const int WAIT_OBJECT_0 = 0x0;
internal const int WAIT_ABANDONED = 0x80;
internal const int WAIT_ABANDONED_0 = 0x80;
internal const int WAIT_IO_COMPLETION = 0xC0;
internal const int WAIT_TIMEOUT = 0x102;
#endregion
#region LR_, IMAGE_
internal const int IMAGE_BITMAP = 0;
internal const int IMAGE_ICON = 1;
internal const int IMAGE_CURSOR = 2;
internal const uint LR_MONOCHROME = 0x1;
internal const uint LR_COLOR = 0x2;
internal const uint LR_COPYRETURNORG = 0x4;
internal const uint LR_COPYDELETEORG = 0x8;
internal const uint LR_LOADFROMFILE = 0x10;
internal const uint LR_LOADTRANSPARENT = 0x20;
internal const uint LR_DEFAULTSIZE = 0x40;
internal const uint LR_VGACOLOR = 0x80;
internal const uint LR_LOADMAP3DCOLORS = 0x1000;
internal const uint LR_CREATEDIBSECTION = 0x2000;
internal const uint LR_COPYFROMRESOURCE = 0x4000;
internal const uint LR_SHARED = 0x8000;
#endregion
#region SFGAO_
internal const uint SFGAO_CANCOPY = 1;
internal const uint SFGAO_CANMOVE = 2;
internal const uint SFGAO_CANLINK = 4;
internal const uint SFGAO_STORAGE = 0x00000008;
internal const uint SFGAO_CANRENAME = 0x00000010;
internal const uint SFGAO_CANDELETE = 0x00000020;
internal const uint SFGAO_HASPROPSHEET = 0x00000040;
internal const uint SFGAO_DROPTARGET = 0x00000100;
internal const uint SFGAO_CAPABILITYMASK = 0x00000177;
internal const uint SFGAO_SYSTEM = 0x00001000;
internal const uint SFGAO_ENCRYPTED = 0x00002000;
internal const uint SFGAO_ISSLOW = 0x00004000;
internal const uint SFGAO_GHOSTED = 0x00008000;
internal const uint SFGAO_LINK = 0x00010000;
internal const uint SFGAO_SHARE = 0x00020000;
internal const uint SFGAO_READONLY = 0x00040000;
internal const uint SFGAO_HIDDEN = 0x00080000;
internal const uint SFGAO_DISPLAYATTRMASK = 0x000FC000;
internal const uint SFGAO_FILESYSANCESTOR = 0x10000000;
internal const uint SFGAO_FOLDER = 0x20000000;
internal const uint SFGAO_FILESYSTEM = 0x40000000;
internal const uint SFGAO_HASSUBFOLDER = 0x80000000;
internal const uint SFGAO_CONTENTSMASK = 0x80000000;
internal const uint SFGAO_VALIDATE = 0x01000000;
internal const uint SFGAO_REMOVABLE = 0x02000000;
internal const uint SFGAO_COMPRESSED = 0x04000000;
internal const uint SFGAO_BROWSABLE = 0x08000000;
internal const uint SFGAO_NONENUMERATED = 0x00100000;
internal const uint SFGAO_NEWCONTENT = 0x00200000;
internal const uint SFGAO_CANMONIKER = 0x00400000;
internal const uint SFGAO_HASSTORAGE = 0x00400000;
internal const uint SFGAO_STREAM = 0x00400000;
internal const uint SFGAO_STORAGEANCESTOR = 0x00800000;
internal const uint SFGAO_STORAGECAPMASK = 0x70C50008;
internal const uint SFGAO_PKEYSFGAOMASK = 0x81044000;
#endregion
#region STGM_
internal const uint STGM_DIRECT = 0x0;
internal const uint STGM_TRANSACTED = 0x10000;
internal const uint STGM_SIMPLE = 0x8000000;
internal const uint STGM_READ = 0x0;
internal const uint STGM_WRITE = 0x1;
internal const uint STGM_READWRITE = 0x2;
internal const uint STGM_SHARE_DENY_NONE = 0x40;
internal const uint STGM_SHARE_DENY_READ = 0x30;
internal const uint STGM_SHARE_DENY_WRITE = 0x20;
internal const uint STGM_SHARE_EXCLUSIVE = 0x10;
internal const uint STGM_PRIORITY = 0x40000;
internal const uint STGM_DELETEONRELEASE = 0x4000000;
internal const uint STGM_NOSCRATCH = 0x100000;
internal const uint STGM_CREATE = 0x1000;
internal const uint STGM_CONVERT = 0x20000;
internal const uint STGM_FAILIFTHERE = 0x0;
internal const uint STGM_NOSNAPSHOT = 0x200000;
internal const uint STGM_DIRECT_SWMR = 0x400000;
#endregion
#region MA_
internal const int MA_ACTIVATE = 1;
internal const int MA_ACTIVATEANDEAT = 2;
internal const int MA_NOACTIVATE = 3;
internal const int MA_NOACTIVATEANDEAT = 4;
#endregion
#region MK_
internal const int MK_LBUTTON = 0x1;
internal const int MK_RBUTTON = 0x2;
internal const int MK_SHIFT = 0x4;
internal const int MK_CONTROL = 0x8;
internal const int MK_MBUTTON = 0x10;
#endregion
#region CF_
internal const int CF_TEXT = 1;
internal const int CF_BITMAP = 2;
internal const int CF_METAFILEPICT = 3;
internal const int CF_SYLK = 4;
internal const int CF_DIF = 5;
internal const int CF_TIFF = 6;
internal const int CF_OEMTEXT = 7;
internal const int CF_DIB = 8;
internal const int CF_PALETTE = 9;
//internal const int CF_PENDATA = 10; //obsolete
internal const int CF_RIFF = 11;
internal const int CF_WAVE = 12;
internal const int CF_UNICODETEXT = 13;
internal const int CF_ENHMETAFILE = 14;
internal const int CF_HDROP = 15;
internal const int CF_LOCALE = 16;
internal const int CF_DIBV5 = 17;
internal const int CF_MAX = 18;
//internal const int CF_OWNERDISPLAY = 0x80; //these are rare and not supported by this library
//internal const int CF_DSPTEXT = 0x81;
//internal const int CF_DSPBITMAP = 0x82;
//internal const int CF_DSPMETAFILEPICT = 0x83;
//internal const int CF_DSPENHMETAFILE = 0x8E;
//internal const int CF_PRIVATEFIRST = 0x200;
//internal const int CF_PRIVATELAST = 0x2FF;
//internal const int CF_GDIOBJFIRST = 0x300;
//internal const int CF_GDIOBJLAST = 0x3FF;
#endregion
#region misc
internal const int IDI_APPLICATION = 32512;
internal const int PBT_APMSUSPEND = 0x4;
#endregion
#region ENUM
[Flags]
internal enum VARENUM : ushort
{
VT_EMPTY,
VT_NULL,
VT_I2,
VT_I4,
VT_R4,
VT_R8,
VT_CY,
VT_DATE,
VT_BSTR,
VT_DISPATCH,
VT_ERROR,
VT_BOOL,
VT_VARIANT,
VT_UNKNOWN,
VT_DECIMAL,
VT_I1 = 16,
VT_UI1,
VT_UI2,
VT_UI4,
VT_I8,
VT_UI8,
VT_INT,
VT_UINT,
VT_VOID,
VT_HRESULT,
VT_PTR,
VT_SAFEARRAY,
VT_CARRAY,
VT_USERDEFINED,
VT_LPSTR,
VT_LPWSTR,
VT_RECORD = 36,
VT_INT_PTR,
VT_UINT_PTR,
VT_FILETIME = 64,
VT_BLOB,
VT_STREAM,
VT_STORAGE,
VT_STREAMED_OBJECT,
VT_STORED_OBJECT,
VT_BLOB_OBJECT,
VT_CF,
VT_CLSID,
VT_VERSIONED_STREAM,
VT_BSTR_BLOB = 0xFFF,
VT_VECTOR,
VT_ARRAY = 0x2000,
VT_BYREF = 0x4000,
VT_RESERVED = 0x8000,
VT_ILLEGAL = 0xFFFF,
VT_ILLEGALMASKED = 0xFFF,
VT_TYPEMASK = 0xFFF
}
#endregion
#region strings
internal const string string_IES = "Internet Explorer_Server";
#endregion
}
================================================
FILE: Au/Api/Api_public.cs
================================================
#pragma warning disable 649, 169 //field never assigned/used
#pragma warning disable 1591 //missing XML documentation
namespace Au.Types;
///
/// Window styles.
///
///
/// Reference: Window Styles.
/// Here names are without prefix WS_. For example, instead of WS_BORDER use WS.BORDER. Not included constants that are 0 (eg WS_TILED) or are duplicate (eg WS_SIZEBOX is same as WS_THICKFRAME) or consist of multiple other constants (eg WS_TILEDWINDOW).
///
[Flags]
public enum WS : uint {
POPUP = 0x80000000,
CHILD = 0x40000000,
MINIMIZE = 0x20000000,
VISIBLE = 0x10000000,
DISABLED = 0x08000000,
CLIPSIBLINGS = 0x04000000,
CLIPCHILDREN = 0x02000000,
MAXIMIZE = 0x01000000,
BORDER = 0x00800000,
DLGFRAME = 0x00400000,
VSCROLL = 0x00200000,
HSCROLL = 0x00100000,
SYSMENU = 0x00080000,
THICKFRAME = 0x00040000,
MINIMIZEBOX = 0x00020000,
GROUP = 0x00020000,
MAXIMIZEBOX = 0x00010000,
TABSTOP = 0x00010000,
CAPTION = BORDER | DLGFRAME,
//these can cause bugs and confusion because consist of several styles. Not useful in C#, because used only to create native windows.
//OVERLAPPEDWINDOW = CAPTION | SYSMENU | THICKFRAME | MINIMIZEBOX | MAXIMIZEBOX,
//POPUPWINDOW = POPUP | BORDER | SYSMENU,
}
///
/// Window extended styles.
///
///
/// Reference: Extended Window Styles.
/// Here names are without prefix WS_EX_. For example, instead of WS_EX_TOOLWINDOW use WSE.TOOLWINDOW. Not included constants that are 0 (eg WS_EX_LEFT).
///
[Flags]
public enum WSE : uint {
DLGMODALFRAME = 0x00000001,
NOPARENTNOTIFY = 0x00000004,
TOPMOST = 0x00000008,
ACCEPTFILES = 0x00000010,
TRANSPARENT = 0x00000020,
MDICHILD = 0x00000040,
TOOLWINDOW = 0x00000080,
WINDOWEDGE = 0x00000100,
CLIENTEDGE = 0x00000200,
CONTEXTHELP = 0x00000400,
//LEFT = 0x00000000,
RIGHT = 0x00001000,
//LTRREADING = 0x00000000,
RTLREADING = 0x00002000,
//RIGHTSCROLLBAR = 0x00000000,
LEFTSCROLLBAR = 0x00004000,
CONTROLPARENT = 0x00010000,
STATICEDGE = 0x00020000,
APPWINDOW = 0x00040000,
LAYERED = 0x00080000,
NOINHERITLAYOUT = 0x00100000,
NOREDIRECTIONBITMAP = 0x00200000,
LAYOUTRTL = 0x00400000,
COMPOSITED = 0x02000000,
NOACTIVATE = 0x08000000,
}
/// API MSG
public struct MSG //WinMSG
{
public wnd hwnd;
public int message;
public nint wParam;
public nint lParam;
public int time;
public POINT pt;
public override string ToString() {
WndUtil.PrintMsg(out string s, this, new() { Indent = false, Number = false });
return s;
}
public static implicit operator MSG(in System.Windows.Forms.Message m)
=> new MSG { hwnd = (wnd)m.HWnd, message = m.Msg, wParam = m.WParam, lParam = m.LParam };
public static implicit operator MSG(in System.Windows.Interop.MSG m)
=> new MSG { hwnd = (wnd)m.hwnd, message = m.message, wParam = m.wParam, lParam = m.lParam, time = m.time, pt = (m.pt_x, m.pt_y) };
}
/// flags.
[Flags]
public enum GTIFlags {
CARETBLINKING = 0x1,
INMOVESIZE = 0x2,
INMENUMODE = 0x4,
SYSTEMMENUMODE = 0x8,
POPUPMENUMODE = 0x10,
}
/// API GUITHREADINFO
public struct GUITHREADINFO {
public int cbSize;
public GTIFlags flags;
public wnd hwndActive;
public wnd hwndFocus;
public wnd hwndCapture;
public wnd hwndMenuOwner;
public wnd hwndMoveSize;
public wnd hwndCaret;
public RECT rcCaret;
}
/// API CREATESTRUCT
public unsafe struct CREATESTRUCT {
public nint lpCreateParams;
public IntPtr hInstance;
public nint hMenu;
public wnd hwndParent;
public int cy;
public int cx;
public int y;
public int x;
public WS style;
public char* lpszName;
public char* lpszClass;
public WSE dwExStyle;
public RStr Name => lpszName == default ? default
: new RStr(lpszName, Ptr_.Length(lpszName));
//public string Name => lpszName == default ? null : new string(lpszName);
///
/// If lpszClass is atom, returns string with # prefix and atom value, like "#32770".
///
public RStr ClassName => (nuint)lpszClass < 0x10000 ? "#" + ((int)lpszClass).ToS()
: new RStr(lpszClass, Ptr_.Length(lpszClass));
//public string ClassName => (nuint)lpszClass < 0x10000 ? "#" + ((int)lpszClass).ToS() : new string(lpszClass);
//tested and documented: CBT hook can change only x y cx cy.
}
/// API SIGDN
public enum SIGDN : uint {
NORMALDISPLAY,
PARENTRELATIVEPARSING = 0x80018001,
DESKTOPABSOLUTEPARSING = 0x80028000,
PARENTRELATIVEEDITING = 0x80031001,
DESKTOPABSOLUTEEDITING = 0x8004C000,
FILESYSPATH = 0x80058000,
URL = 0x80068000,
PARENTRELATIVEFORADDRESSBAR = 0x8007C001,
PARENTRELATIVE = 0x80080001,
PARENTRELATIVEFORUI = 0x80094001
}
/// API SetWindowPos flags. Can be used with .
[Flags]
public enum SWPFlags : uint {
NOSIZE = 0x1,
NOMOVE = 0x2,
NOZORDER = 0x4,
NOREDRAW = 0x8,
NOACTIVATE = 0x10,
FRAMECHANGED = 0x20,
SHOWWINDOW = 0x40,
HIDEWINDOW = 0x80,
NOCOPYBITS = 0x100,
NOOWNERZORDER = 0x200,
NOSENDCHANGING = 0x400,
_NOCLIENTSIZE = 0x800,
_NOCLIENTMOVE = 0x1000,
DEFERERASE = 0x2000,
ASYNCWINDOWPOS = 0x4000,
_STATECHANGED = 0x8000,
//_10000000 = 0x10000000,
_KNOWNFLAGS = 0xffff,
//the undocumented flags would break ToString() if not defined
}
///
/// Special window handle values. Can be used with .
/// See API SetWindowPos.
///
public enum SpecHWND {
TOP = 0,
BOTTOM = 1,
TOPMOST = -1,
NOTOPMOST = -2,
MESSAGE = -3,
BROADCAST = 0xffff,
}
///
/// Window long constants. Used with and .
/// See API GetWindowLong. See also API SetWindowSubclass.
///
public static class GWL {
public const int WNDPROC = -4;
public const int USERDATA = -21;
public const int STYLE = -16;
public const int ID = -12;
public const int HWNDPARENT = -8;
public const int HINSTANCE = -6;
public const int EXSTYLE = -20;
//info: also there are GWLP_, but their values are the same.
public static class DWL {
public static readonly int MSGRESULT = 0;
public static readonly int DLGPROC = IntPtr.Size;
public static readonly int USER = IntPtr.Size * 2;
}
}
///
/// Window class long constants. Used with .
/// See API WNDCLASSEX, GetClassLong.
///
public static class GCL {
public const int ATOM = -32;
public const int WNDPROC = -24;
public const int STYLE = -26;
public const int MENUNAME = -8;
public const int HMODULE = -16;
public const int HICONSM = -34;
public const int HICON = -14;
public const int HCURSOR = -12;
public const int HBRBACKGROUND = -10;
public const int CBWNDEXTRA = -18;
public const int CBCLSEXTRA = -20;
//info: also there are GCLP_, but their values are the same.
}
/// API WNDPROC
public delegate nint WNDPROC(wnd w, int msg, nint wp, nint lp);
/// API SendMessageTimeout flags. Used with .
[Flags]
public enum SMTFlags : uint {
BLOCK = 0x0001,
ABORTIFHUNG = 0x0002,
NOTIMEOUTIFNOTHUNG = 0x0008,
ERRORONEXIT = 0x0020,
}
/// API DrawTextEx format flags.
[Flags]
public enum TFFlags {
CENTER = 0x1,
RIGHT = 0x2,
VCENTER = 0x4,
BOTTOM = 0x8,
WORDBREAK = 0x10,
SINGLELINE = 0x20,
EXPANDTABS = 0x40,
TABSTOP = 0x80,
NOCLIP = 0x100,
EXTERNALLEADING = 0x200,
CALCRECT = 0x400,
NOPREFIX = 0x800,
INTERNAL = 0x1000,
EDITCONTROL = 0x2000,
PATH_ELLIPSIS = 0x4000,
END_ELLIPSIS = 0x8000,
MODIFYSTRING = 0x10000,
RTLREADING = 0x20000,
WORD_ELLIPSIS = 0x40000,
NOFULLWIDTHCHARBREAK = 0x80000,
HIDEPREFIX = 0x100000,
PREFIXONLY = 0x200000
}
================================================
FILE: Au/Api/Cpp.cs
================================================
using static Au.Types.GWL;
namespace Au.Types;
[DebuggerStepThrough]
static unsafe partial class Cpp {
static List<(string dll, nint h)> _dlls = [];
static Cpp() {
LoadAuNativeDll("AuCpp.dll");
Cpp_SetHelperCallback(&_HelperCallback); //note: not in elm ctor. Eg by the elm tool not via elm. Fast.
}
///
/// Loads correct 64/32/ARM64 version of a private native dll. Then [DllImport] will use it.
/// Used for Au dlls (AuCpp) and LA dlls (Scintilla).
///
/// Dll file name like "name.dll".
/// Handle.
///
///
/// Searches in:
/// - subfolder 64 or 32 or 64\ARM of the Au.dll folder.
/// - calls NativeLibrary.TryLoad, which works like simple [DllImport], eg may use info from deps.json.
/// - subfolder 64 etc of folder specified in environment variable Au.Path. For example the dll is unavailable if used in an assembly (managed dll) loaded in a nonstandard environment, eg VS forms designer or VS C# Interactive (then folders.ThisApp is "C:\Program Files (x86)\Microsoft Visual Studio\..."). Workaround: set environment variable Au.Path = the main Au directory and restart Windows.
///
public static nint LoadAuNativeDll(string fileName) {
//Debug.Assert(default == Api.GetModuleHandle(fileName)); //no, asserts if cpp dll is injected by acc
nint h = 0;
string rel = (RuntimeInformation.ProcessArchitecture switch { Architecture.X86 => @"32\", Architecture.Arm64 => @"64\ARM\", _ => @"64\" }) + fileName;
//rejected: use standard NuGet "runtimes" folder instead. I did not find info whether it can be used.
//Au.dll dir + rel
var asm = typeof(Cpp).Assembly;
if (asm.Location is [_, ..] s1) {
s1 = s1[..(s1.LastIndexOf('\\') + 1)] + rel;
if (NativeLibrary.TryLoad(s1, out h)) return h;
}
//like [DllImport]. It uses NATIVE_DLL_SEARCH_DIRECTORIES, which was built at startup by our AppHost or from deps.json.
// Also finds in temp dir when +.
if (NativeLibrary.TryLoad(fileName, asm, null, out h)) return h;
//environment variable + rel
if (Environment.GetEnvironmentVariable("Au.Path") is string s2)
if (NativeLibrary.TryLoad(pathname.combine(s2, rel), out h)) return h;
throw new DllNotFoundException(fileName + " not found");
}
internal struct Cpp_Acc {
public IntPtr acc;
public int elem;
public elm.Misc_ misc;
public Cpp_Acc(IntPtr iacc, int elem_) { acc = iacc; elem = elem_; misc = default; }
public Cpp_Acc(elm e) { acc = e._iacc; elem = e._elem; misc = e._misc; }
public static implicit operator Cpp_Acc(elm e) => new(e);
}
internal delegate int Cpp_AccFindCallbackT(Cpp_Acc a, RECT* r);
internal struct Cpp_AccFindParams {
string _role, _name, _prop;
int _roleLength, _nameLength, _propLength;
public EFFlags flags;
public int skip;
char _resultProp; //elmFinder.RProp
int _flags2;
public Cpp_AccFindParams(string role, string name, string prop, EFFlags flags, int skip, char resultProp) {
if (role != null) { _role = role; _roleLength = role.Length; }
if (name != null) { _name = name; _nameLength = name.Length; }
if (prop != null) { _prop = prop; _propLength = prop.Length; }
this.flags = flags;
this.skip = skip;
_resultProp = resultProp;
}
///
/// Parses role. Enables Chrome acc if need.
///
public void RolePrefix(wnd w) {
if (_roleLength < 4) return;
int i = _role.IndexOf(':'); if (i < 3) return;
_flags2 = Cpp_AccRolePrefix(_role, i, w);
if (_flags2 != 0) {
_roleLength -= ++i;
_role = _roleLength > 0 ? _role[i..] : null;
}
}
}
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int Cpp_AccRolePrefix(string s, int len, wnd w);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern EError Cpp_AccFind(wnd w, Cpp_Acc* aParent, Cpp_AccFindParams ap, Cpp_AccFindCallbackT also, out Cpp_Acc aResult, [MarshalAs(UnmanagedType.BStr)] out string sResult, bool getRects = false);
internal enum EError {
NotFound = 0x1001, //UI element not found. With FindAll - no errors. This is actually not an error.
InvalidParameter = 0x1002, //invalid parameter, for example wildcard expression (or regular expression in it)
WindowClosed = 0x1003, //the specified window handle is invalid or the window was destroyed while injecting
}
internal static bool IsCppError(int hr) {
return hr >= (int)EError.NotFound && hr <= (int)EError.WindowClosed;
}
///
/// flags: 1 not inproc, 2 get only name.
///
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccFromWindow(int flags, wnd w, EObjid objId, out Cpp_Acc aResult, out BSTR sResult);
internal delegate EXYFlags Cpp_AccFromPointCallbackT(EXYFlags flags, wnd wFP, wnd wTL);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccFromPoint(POINT p, EXYFlags flags, Cpp_AccFromPointCallbackT callback, out Cpp_Acc aResult);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccGetFocused(wnd w, EFocusedFlags flags, out Cpp_Acc aResult);
//These are called from elm class functions like Cpp.Cpp_Func(this, ...); GC.KeepAlive(this);.
//We can use 'this' because Cpp_Acc has an implicit conversion from elm operator.
//Need GC.KeepAlive(this) everywhere. Else GC can collect the elm (and release _iacc) while in the Cpp func.
//Alternatively could make the Cpp parameter 'const Cpp_Acc&', and pass elm directly. But I don't like it.
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccNavigate(Cpp_Acc aFrom, string navig, out Cpp_Acc aResult);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccGetStringProp(Cpp_Acc a, char prop, out BSTR sResult);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccWeb(Cpp_Acc a, string what, out BSTR sResult);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccGetRect(Cpp_Acc a, out RECT r);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccGetRole(Cpp_Acc a, out ERole roleInt, out BSTR roleStr);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccGetInt(Cpp_Acc a, char what, out int R);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccAction(Cpp_Acc a, char action = 'a', [MarshalAs(UnmanagedType.BStr)] string param = null);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccSelect(Cpp_Acc a, ESelect flagsSelect);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccGetSelection(Cpp_Acc a, out BSTR sResult);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_AccGetProps(Cpp_Acc a, string props, out BSTR sResult);
/// 1 - wait less.
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void Cpp_Unload(uint flags);
#if DEBUG
internal static void DebugUnload() {
GC.Collect();
GC.WaitForPendingFinalizers();
Cpp_Unload(0);
}
// [DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
// internal static extern IInterface Cpp_GetInterface();
// [ComImport, Guid("3AB5235E-2768-47A2-909A-B5852A9D1868"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
// internal interface IInterface {
// [PreserveSig] int Add(int a, int b);
// //int Prop { set; get; }
// //[return: MarshalAs(UnmanagedType.Bool)] bool Prop { set; get; }
// //bool Prop { [return: MarshalAs(UnmanagedType.Bool)] set; [return: MarshalAs(UnmanagedType.Bool)] get; }
// //[property: MarshalAs(UnmanagedType.Bool)] bool Prop { set; get; }
// void put_Prop([MarshalAs(UnmanagedType.Bool)] bool r);
// [return: MarshalAs(UnmanagedType.Bool)] bool get_Prop();
// //void put_Prop2([MarshalAs(UnmanagedType.LPStr)] string r);
// //[return: MarshalAs(UnmanagedType.LPStr)] string get_Prop2();
// //[return: MarshalAs(UnmanagedType.IUnknown)] object Prop2 { get; set; }
// //object Prop2 { [return: MarshalAs(UnmanagedType.IUnknown)] get; set; }
//#if false
// void put_Prop2([MarshalAs(UnmanagedType.IUnknown)] object r);
// [return: MarshalAs(UnmanagedType.IUnknown)] object get_Prop2();
//#else
// void put_Prop2(IUnknown r);
// IUnknown get_Prop2();
// [ComImport, Guid("00000000-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
// internal interface IUnknown { }
//#endif
// }
#endif
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void Cpp_SetHelperCallback(delegate* unmanaged callback);
[UnmanagedCallersOnly]
static int _HelperCallback(int action, wnd w) {
if (action == 1) { //AccEnableChrome asks to detect whether the command line contains --force-renderer-accessibility. If yes, will not try to enable acc.
if (process.getCommandLine(w.ProcessId, removeProgram: true) is string s) {
if (s.Contains("--force-renderer-accessibility")) return 1;
//print.warning("To use UI elements in web pages, start browser with command line --force-renderer-accessibility. Without it the code may fail sometimes or stop working in the future.");
}
} else if (action == 2) { //AccEnableChrome failed
print.warning("To use UI elements in web pages, start browser with command line --force-renderer-accessibility.");
}
return 0;
}
// OTHER
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr Cpp_ModuleHandle();
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern char* Cpp_LowercaseTable();
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr Cpp_Clipboard(IntPtr hh);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool Cpp_ShellExec(in Api.SHELLEXECUTEINFO x, out int pid, out int injectError, out int execError);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern nint Cpp_AccWorkaround(Api.IAccessible a, nint wParam, ref nint obj);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void Cpp_UEF(bool on);
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void Cpp_InactiveWindowWorkaround(bool on);
/// 0 failed, 1 x86, 2 x64, 3 arm64
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int Cpp_GetProcessArchitecture(int pid);
// TEST
#if DEBUG
[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void Cpp_Test();
//[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
//internal static extern void Cpp_TestWildex(string s, string w);
//[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
//internal static extern int Cpp_TestInt(int a);
//[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
//internal static extern int Cpp_TestString(string a, int b, int c);
//[ComImport, Guid("3426CF3C-F7C2-4322-A292-463DB8729B54"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
//internal interface ICppTest
//{
// [PreserveSig] int TestInt(int a, int b, int c);
// [PreserveSig] int TestString([MarshalAs(UnmanagedType.LPWStr)] string a, int b, int c);
// [PreserveSig] int TestBSTR(string a, int b, int c);
//}
//[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
//internal static extern ICppTest Cpp_Interface();
//[ComImport, Guid("57017F56-E7CA-4A7B-A8F8-2AE36077F50D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
//internal interface IThreadExitEvent
//{
// [PreserveSig] int Unsubscribe();
//}
//[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
//internal static extern IThreadExitEvent Cpp_ThreadExitEvent(IntPtr callback);
//[DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]
//internal static extern void Cpp_ThreadExitEvent2(IntPtr callback);
#endif
}
================================================
FILE: Au/Api/WinRT.cs
================================================
//WinRT common API and utils. Others are in files where used.
//This library does not use C#/WinRT (Microsoft.Windows.SDK.NET.dll and WinRT.Runtime.dll).
//Would be easier, but it has problems:
// - Adds 2 files, 22 MB.
// - OCR: if once used in a STA thread, then does not work in MTA threads.
// - OCR: slower.
namespace Au.Types;
#pragma warning disable 649, 169 //field never assigned/used
static unsafe partial class WinRT {
[DllImport("combase.dll", PreserveSig = true)]
internal static extern int WindowsCreateString(string s, int length, out IntPtr hstring);
[DllImport("combase.dll", PreserveSig = true)]
internal static extern int WindowsDeleteString(IntPtr hstring);
[DllImport("combase.dll")]
internal static extern char* WindowsGetStringRawBuffer(IntPtr hstring, out int length);
//probably don't need. Always returns 1 (already initialized) when called with current apartment state.
//[DllImport("combase.dll", PreserveSig = true)]
//internal static extern int RoInitialize(int initType);
[DllImport("combase.dll", PreserveSig = true)]
internal static extern int RoGetActivationFactory(IntPtr activatableClassId, in Guid iid, out IntPtr factory);
internal static T Create(string progId) where T : struct {
using var hs = new _Hstring(progId);
HR(RoGetActivationFactory(hs, typeof(T).GUID, out var r));
return Unsafe.As(ref r);
}
//rejected: use static factories. Now fast.
internal static void HR(int hr) {
if (hr < 0) throw Marshal.GetExceptionForHR(hr);
}
internal static T As(IntPtr ip) where T : unmanaged {
Debug.Assert(sizeof(T) == sizeof(IntPtr));
return Unsafe.As(ref ip);
}
///
/// A COM interface pointer.
///
internal struct IUnknown : IDisposable {
IntPtr _u;
IUnknown(IntPtr iunknown) { _u = iunknown; }
public void Dispose() {
if (_u != default) { Marshal.Release(_u); _u = default; }
}
public bool IsNull => _u == default;
int _QI(Type type, out IntPtr r) {
#if NET9_0_OR_GREATER //changed ref -> in
return Marshal.QueryInterface(_u, type.GUID, out r);
#else
var guid = type.GUID;
return Marshal.QueryInterface(_u, ref guid, out r);
#endif
}
///
/// Calls QueryInterface. Throws exception if failed.
///
public T QI() where T : unmanaged {
HR(_QI(typeof(T), out var r));
return As(r);
}
///
/// Calls QueryInterface. Returns false if failed.
///
public bool QI(out T r) where T : unmanaged {
bool ok = 0 == _QI(typeof(T), out var v);
r = ok ? As(v) : default;
return ok;
}
///
/// Gets COM interface function at index i in vtbl.
///
public nint this[int i] => (*(nint**)_u)[i];
public static implicit operator IUnknown(IntPtr p) => new(p);
public static implicit operator IntPtr(IUnknown p) => p._u;
public override string ToString() => _u.ToString();
///
/// Calls a 0-param function that returns HSTRING.
///
/// Interface function index in vtbl.
public string GetString(int i) {
using var s1 = new _Hstring(_GetPtr(i));
return s1.ToString();
}
IntPtr _GetPtr(int i) {
HR(((delegate* unmanaged[Stdcall])this[i])(_u, out var r));
return r;
}
///
/// Calls a 0-param function with a pointer-size return type (COM pointer, etc).
///
/// Interface function index in vtbl.
public T GetPtr(int i) where T : unmanaged => As(_GetPtr(i));
//when calling: System.BadImageFormatException: Bad element type in SizeOf.
//public IntPtr GetPtr(int i, T1 p1) where T1 : unmanaged {
// HR(((delegate* unmanaged[Stdcall])this[i])(_u, p1, out var r));
// return r;
//}
///
/// QI(IClosable).Close()
///
public void Close() {
using var c = QI();
c.Close();
}
}
internal interface IComPtr : IDisposable { }
internal struct IVectorView : IComPtr where T : unmanaged {
IUnknown _u; public IUnknown U => _u;
public void Dispose() => _u.Dispose();
IntPtr _GetAt(int i) {
HR(((delegate* unmanaged[Stdcall])_u[6])(_u, i, out var r));
return r;
}
public T this[int i] => As(_GetAt(i));
public int Size {
get {
HR(((delegate* unmanaged[Stdcall])_u[7])(_u, out int r));
return r;
}
}
public IEnumerable Items(bool disposeItems = true) {
for (int i = 0, n = Size; i < n; i++) {
var v = _GetAt(i);
yield return As(v);
if (disposeItems) Marshal.Release(v);
}
}
}
#if true
internal struct IAsyncOperation : IComPtr {
IUnknown _u; public IUnknown U => _u;
public void Dispose() => _u.Dispose();
public T Await(bool dispose = true) where T : unmanaged {
try {
using (var ai = _u.QI()) {
for (int i = 4; ; i++) {
var status = ai.Status;
//print.it(status, i / 4);
if (status == AsyncStatus.Completed) break;
if (status != AsyncStatus.Started) throw new AuException();
wait.ms(i / 4);
}
}
return _u.GetPtr(8); //GetResults
}
finally { if (dispose) Dispose(); }
}
[Guid("00000036-0000-0000-C000-000000000046")]
struct IAsyncInfo : IComPtr {
IUnknown _u; public IUnknown U => _u;
public void Dispose() => _u.Dispose();
public AsyncStatus Status {
get {
HR(((delegate* unmanaged[Stdcall])_u[7])(_u, out var r));
return r;
}
}
}
enum AsyncStatus { Canceled = 2, Completed = 1, Error = 3, Started = 0 }
}
#else //tried to use Completed callback, unsuccessfully
internal struct IAsyncOperation : IComPtr {
IUnknown _u; public IUnknown U => _u;
public void Dispose() => _u.Dispose();
public T Await(bool dispose = true) where T : unmanaged {
try {
//static void _Handler(IAsyncOperation ai, AsyncStatus status) { print.it("completed"); }
//delegate* p1 = &_Handler;
//HR(((delegate* unmanaged[Stdcall])_u[6])(_u, p1)); //put_Completed
var del = new AsyncOperationCompletedHandler(static (IAsyncOperation ai, AsyncStatus status) => { print.it("completed"); });
HR(((delegate* unmanaged[Stdcall])_u[6])(_u, del)); //put_Completed
dialog.show("");
GC.KeepAlive(del);
return _u.GetPtr(8); //GetResults
}
finally { if (dispose) Dispose(); }
}
enum AsyncStatus { Canceled = 2, Completed = 1, Error = 3, Started = 0 }
delegate void AsyncOperationCompletedHandler(IAsyncOperation ao, AsyncStatus status);
}
#endif
[Guid("30d5a829-7fa4-4026-83bb-d75bae4ea99e")]
internal struct IClosable : IComPtr {
IUnknown _u; public IUnknown U => _u;
public void Dispose() => _u.Dispose();
public void Close() {
HR(((delegate* unmanaged[Stdcall])_u[6])(_u));
}
}
unsafe class _Hstring : IDisposable {
IntPtr _h;
public _Hstring(IntPtr hstring) {
_h = hstring;
}
public _Hstring(string s) {
WindowsCreateString(s, s.Lenn(), out _h);
}
public void Dispose() {
if (_h != default) {
WindowsDeleteString(_h);
_h = default;
}
GC.SuppressFinalize(this);
}
~_Hstring() {
if (_h != default) WindowsDeleteString(_h);
}
//public static implicit operator _Hstring(string s) => new(s);
public static implicit operator IntPtr(_Hstring s) => s._h;
public static implicit operator string(_Hstring s) => s.ToString();
public override string ToString() {
if (_h == default) return null;
char* p = WindowsGetStringRawBuffer(_h, out int len);
return new(p, 0, len);
}
}
}
================================================
FILE: Au/Au.More/AppSingleInstance.cs
================================================
namespace Au.More;
///
/// Implements "single instance process" feature.
///
///
/// {
/// print.it("AppSingleInstance.Notified", a);
/// b.Window.Activate();
/// };
/// if (!b.ShowDialog()) return;
/// ]]>
///
///
/// Implements "single instance process" feature.
///
///
public static class AppSingleInstance {
static Mutex _mutex;
static wnd _wNotify;
///
/// Detects whether a process of this app is already running.
///
/// A unique string to use for mutex name (see ). If prefix @"Global\" used, detects processes in all user sessions.
///
/// If not null:
///
• If already running, sends it to that process, which receives it in event.
///
• Else enables event in this process.
///
/// Milliseconds to wait until this process can run. No timeout if -1.
/// True if already running.
/// This function already called.
/// Exceptions of .
public static bool AlreadyRunning(string mutex, IEnumerable notifyArgs = null, int waitMS = 0) {
var m = new Mutex(true, mutex, out bool createdNew);
if (null != Interlocked.CompareExchange(ref _mutex, m, null)) { m.Dispose(); throw new InvalidOperationException(); }
if (!createdNew && waitMS != 0) {
try { createdNew = m.WaitOne(waitMS); }
catch (AbandonedMutexException) { createdNew = true; }
}
if (notifyArgs != null) {
if (createdNew) {
WndUtil.RegisterWindowClass(mutex, _WndProc);
_wNotify = WndUtil.CreateMessageOnlyWindow(mutex, "AppSingleInstance");
WndCopyData.EnableReceivingWM_COPYDATA();
} else {
var w = wnd.findFast("AppSingleInstance", mutex, messageOnly: true);
if (!w.Is0) WndCopyData.Send(w, 1, string.Join('\0', notifyArgs));
}
}
return !createdNew;
}
static nint _WndProc(wnd w, int msg, nint wp, nint lp) {
if (msg == Api.WM_COPYDATA) {
var x = new WndCopyData(lp);
if (x.DataId == 1) {
Notified?.Invoke(x.GetString().Split('\0'));
}
}
return Api.DefWindowProc(w, msg, wp, lp);
}
///
/// When in new process detected that this process is running.
/// Receives notifyArgs passed to it.
///
///
/// To enable this event, call with non-null notifyArgs. The event handler runs in the same thread. The thread must dispatch Windows messages (for example show a window or dialog, or call ).
///
public static event Action Notified;
}
================================================
FILE: Au/Au.More/BufferedPaint.cs
================================================
namespace Au.More;
///
/// Wraps buffered paint API BeginBufferedPaint etc.
/// Must be disposed locally, like in the example.
///
///
///
///
public struct BufferedPaint : IDisposable {
//rejected. Users often will forget it. Better init automatically.
/////
///// Calls API BufferedPaintInit.
/////
//public static void Init() { Api.BufferedPaintInit(); } //fast
/////
///// Calls API BufferedPaintUnInit.
/////
//public static void Uninit() { Api.BufferedPaintUnInit(); }
[ThreadStatic] static bool s_inited;
//never mind: should BufferedPaintUnInit before thread exits.
// Not very important. Usually a process has single UI thread. Tested: 10000 threads without BufferedPaintUnInit don't leak much.
// To detect thread exit could use eg FlsAlloc(callback)+FlsSetValue, or unmanaged dll thread detach.
// But it can be dangerous (too late, eg C# thread variables are already cleared).
wnd _w;
IntPtr _dcn, _dcb;
bool _wmPaint;
Api.PAINTSTRUCT _ps;
IntPtr _hb;
RECT _r;
///
/// Gets non-buffered DC with API BeginPaint or GetDC. Then gets buffered DC with API BeginBufferedPaint for entire client area or rectangle r.
///
///
/// Use API BeginPaint/EndPaint. If false, uses GetDC/ReleaseDC.
/// Part of client area.
public unsafe BufferedPaint(wnd w, bool wmPaint, RECT? r = null) {
if (!s_inited) s_inited = 0 == Api.BufferedPaintInit();
_w = w;
if (_wmPaint = wmPaint) {
_dcn = Api.BeginPaint(w, out _ps);
} else {
_dcn = Api.GetDC(_w);
}
_r = r ?? _w.ClientRect;
Api.BP_PAINTPARAMS pp = new() { cbSize = sizeof(Api.BP_PAINTPARAMS) };
//var ru = wmPaint ? _ps.rcPaint : _r; //the buffer bitmap is smaller when rcPaint smaller, but in most cases don't need to change painting code, although GetViewportOrgEx etc get 0 offsets of the buffer DC. However problem with brush alignment.
_hb = Api.BeginBufferedPaint(_dcn, _r, Api.BP_BUFFERFORMAT.BPBF_TOPDOWNDIB, ref pp, out _dcb); //BPBF_COMPATIBLEBITMAP slower //tested: works with 16 and 8 bit colors too
Debug_.PrintIf(_hb == default && !_r.NoArea, $"BeginBufferedPaint, {_r}");
if (_hb == default) _dcb = _dcn;
}
///
/// Calls API EndBufferedPaint and EndPaint or ReleaseDC.
///
public void Dispose() {
if (_dcn == default) return;
if (_hb != default) Api.EndBufferedPaint(_hb, true);
if (_wmPaint) Api.EndPaint(_w, _ps); else Api.ReleaseDC(_w, _dcn);
_dcn = default;
}
///
/// Gets window DC.
///
public IntPtr NonBufferedDC => _dcn;
///
/// Gets the buffered DC. Returns if API BeginBufferedPaint failed.
///
public IntPtr DC => _dcb;
///
/// Gets client area rectangle or rectangle passed to constructor.
///
public RECT Rect => _r;
///
/// Gets bounding rectangle of the update region in client area rectangle.
///
public RECT UpdateRect {
get {
if (_wmPaint) return _ps.rcPaint;
Api.GetUpdateRect(_w, out var r, false);
return r;
}
}
}
================================================
FILE: Au/Au.More/CheckListDialog.cs
================================================
using System.Windows;
using System.Windows.Controls;
using System.Collections;
namespace Au.More;
///
/// Dialog with list of check boxes.
///
///
///
///
///
public class CheckListDialog {
wpfBuilder _b;
TextBlock _text;
ListBox _lb;
List _ac = new();
///
/// Creates , sets some window properties, optionally sets static text.
///
/// Text above the list. Can be null. See also .
/// Window name. If null, uses .
public CheckListDialog(string text = null, string title = null) {
_b = new wpfBuilder(title ?? dialog.options.defaultTitle)
.WinProperties(resizeMode: ResizeMode.NoResize)
.Width(250..600);
_b.R.Add(out _text, text).Wrap(TextWrapping.Wrap);
if (text == null) _text.Visibility = Visibility.Collapsed;
}
///
/// Adds a checkbox.
///
/// The checkbox.
public CheckBox Add(string text, bool check = false, string tooltip = null) {
if (_lb == null) _b.R.Add(out _lb).Height(..550);
//var content = UseAccessKey ? text : text?.Replace("_", "__");
var content = text?.Replace("_", "__");
CheckBox c = new() { Content = content, Tag = text, IsChecked = check, ToolTip = tooltip };
_ac.Add(c);
_lb.Items.Add(c);
return c;
}
///
/// Adds multiple checkboxes.
///
/// Array, List, etc containing text strings for checkboxes.
/// Whether to check all checkboxes.
public void Add(IEnumerable items, bool check = false) {
foreach (var v in items) Add(v, check);
}
/////
///// Character '_' in checkbox text isn't displayed. Instead the next character toggles the checkbox when pressed with Alt. For literal "_" use "__".
/////
//public bool UseAccessKey { get; set; }
///
/// Sets formatted text, like .
///
///
public void FormatText(wpfBuilder.InterpolatedString text) {
wpfBuilder.formatTextOf(_text, text);
_text.Visibility = Visibility.Visible;
}
///
/// Changes text of OK and Cancel buttons.
///
public void SetButtons(string ok = "OK", string cancel = "Cancel") {
_ok = ok;
_cancel = cancel;
}
string _ok, _cancel;
///
/// Gets the that builds the dialog.
/// You can use it to add more controls, change window properties, etc; see example.
///
///
///
///
public wpfBuilder Builder => _b;
///
/// Adds OK/Cancel buttons, shows dialog, sets result properties.
///
///
/// Owner window, or an element in it.
/// If used, sets = false. Else sets = true, unless is false or the active window belongs to this process.
///
/// true if pressed OK.
public bool ShowDialog(DependencyObject owner = null) {
_b.R.AddOkCancel(_ok ?? "OK", _cancel ?? "Cancel");
_b.End();
var w = _b.Window;
if (owner != null) w.ShowInTaskbar = false;
else if (dialog.options.topmostIfNoOwnerWindow && !wnd.active.IsOfThisProcess) w.Topmost = true;
if (!_b.ShowDialog(owner)) return false;
BitArray ba = new(_ac.Count);
int n = 0;
for (int i = 0; i < ba.Length; i++) if (ba[i] = _ac[i].IsChecked == true) n++;
ResultIndices = new int[n];
for (int i = 0, j = 0; i < ba.Length; i++) if (ba[i]) ResultIndices[j++] = i;
ResultItems = new string[n];
for (int i = 0, j = 0; i < ba.Length; i++) if (ba[i]) ResultItems[j++] = _ac[i].Tag as string;
ResultBits = ba;
return true;
}
///
/// Gets a bit array where elements represent checkbox states (true if checked).
/// This property is set by .
///
public BitArray ResultBits { get; private set; }
///
/// Gets 0-based indices of checked items.
/// This property is set by .
///
public int[] ResultIndices { get; private set; }
///
/// Gets strings of checked items.
/// This property is set by .
///
///
/// a = ["one", "two"];
/// var d = new CheckListDialog("Info.");
/// d.Add(a);
/// if (!d.ShowDialog() || !d.ResultItems.Any()) return;
/// a = d.ResultItems.ToList();
/// ]]>
///
public string[] ResultItems { get; private set; }
}
================================================
FILE: Au/Au.More/ComUtil.cs
================================================
namespace Au.More;
///
/// COM utility functions.
///
public static class ComUtil {
///
/// Gets a COM object existing in other process and registered in ROT.
///
/// ProgID of the COM class. Example: "Excel.Application".
/// If fails, don't throw exception but return null.
///
///
• progID not found in the registry. Probably it is incorrect, or the program isn't installed.
///
• An object of this type currently is unavailable. Probably the program is not running, or running with a different UAC integrity level.
///
///
/// Calls API GetActiveObject.
///
/// This process must have the same [](xref:uac) integrity level as the target process (of the COM object). In script Properties select uac user.
///
///
///
///
///
public static object GetActiveObject(string progID, bool dontThrow = false) {
int hr = Api.CLSIDFromProgID(progID, out var clsid);
if (hr < 0) return dontThrow ? null : throw Marshal.GetExceptionForHR(hr);
return GetActiveObject(clsid, dontThrow);
}
/// An object of this type currently is unavailable. Probably its program is not running, or running with a different UAC integrity level.
public static object GetActiveObject(in Guid clsid, bool dontThrow = false) {
int hr = Api.GetActiveObject(clsid, default, out var r);
if (hr < 0) return dontThrow ? null : throw Marshal.GetExceptionForHR(hr);
return r;
}
///
/// Gets COM object from a window using API AccessibleObjectFromWindow(OBJID_NATIVEOM, IID_IDispatch).
///
/// Window or control.
/// Child window class name. Format: [wildcard expression](xref:wildcard_expression). If used, gets COM object from the first found child or descendant window where it succeeds. If null, gets COM object from w.
/// If fails to get COM object, don't throw exception but return null.
/// w 0 or invalid.
/// Failed.
///
/// Microsoft Excel.
///
/// Microsoft Word.
///
/// Microsoft PowerPoint.
///
/// Microsoft Access.
///
/// To get COM object from Microsoft Outlook or Publisher, use .
///
public static object GetWindowObject(wnd w, string cnChild = null, bool dontThrow = false) {
w.ThrowIfInvalid();
if (cnChild != null) {
foreach (var c in w.ChildAll(cn: cnChild)) {
if (0 == Api.AccessibleObjectFromWindow(c, EObjid.NATIVEOM, IID_IDispatch, out var o)) return o;
}
} else {
if (0 == Api.AccessibleObjectFromWindow(w, EObjid.NATIVEOM, IID_IDispatch, out var o)) return o;
}
if (!dontThrow) throw new AuException($"*get COM object from window {w}");
return null;
}
static readonly Guid IID_IDispatch = new("00020400-0000-0000-C000-000000000046");
///
/// Creates COM object using ProgID.
///
/// The programmatic identifier (ProgID) of the COM type.
///
///
/// Use this function when you don't have the COM interface definition or the interop assembly. Else use code like var x = new InterfaceType(); or var x = new CoclassType() as InterfaceType.
///
///
///
///
public static object CreateObject(string progID) {
return Activator.CreateInstance(Type.GetTypeFromProgID(progID, throwOnError: true));
}
///
/// Creates COM object using CLSID.
///
///
public static object CreateObject(in Guid clsid) {
return Activator.CreateInstance(Type.GetTypeFromCLSID(clsid, throwOnError: true));
}
///
/// Default value for optional parameters of type VARIANT (object in C#) of COM functions.
/// The same as .
///
public static readonly System.Reflection.Missing Missing = System.Reflection.Missing.Value;
}
================================================
FILE: Au/Au.More/Convert2.cs
================================================
using System.IO.Compression;
using System.Security.Cryptography;
namespace Au.More;
///
/// Data conversion functions. Compression, encryption, hex encoding, etc.
///
public static unsafe class Convert2 {
#region hex
///
/// Converts data in byte[] or other memory to hex-encoded string.
///
/// Data. See also: , .
/// Let the hex string contain A-F, not a-f.
///
/// The result string length is 2 * data length.
/// Often it's better to use , then result is 4/3 of data length. But cannot use Base64 in file names and URLs because it is case-sensitive and may contain character '/'. Both functions are fast.
///
public static string HexEncode(RByte data, bool upperCase = false) {
fixed (byte* p = data) {
return HexEncode(p, data.Length, upperCase);
}
}
///
/// Converts binary data in any memory to hex-encoded string.
///
/// The data. Can be any valid memory of specified size, for example a struct address.
/// data memory size (bytes).
///
public static string HexEncode(void* data, int size, bool upperCase = false) {
int u = (upperCase ? 'A' : 'a') - 10;
var bytes = (byte*)data;
string R = new('\0', size * 2);
fixed (char* cp = R) {
int* p = (int*)cp;
for (int i = 0; i < size; i++) {
int b = bytes[i];
int t = b & 0xf; t += t < 10 ? '0' : u;
b >>= 4; b += b < 10 ? '0' : u;
p[i] = t << 16 | b;
}
}
return R;
//tested: ~50% of time takes string allocation.
//tested: with string.Create same speed.
}
///
/// Converts a struct variable to hex-encoded string.
///
/// Variable.
///
public static string HexEncode(T x, bool upperCase = false) where T : unmanaged
=> HexEncode(&x, sizeof(T), upperCase);
///
/// Converts hex-encoded string to binary data.
///
/// String or char[] or span of string/array/memory containing hex encoded data.
///
/// Skips spaces and other non-hex-digit characters. Example: "01 23 46 67" is the same as "01234667".
/// The number of hex digit characters should be divisible by 2, else the last character is ignored.
///
[SkipLocalsInit]
public static byte[] HexDecode(RStr encoded) {
using FastBuffer b = new(encoded.Length / 2);
int n = HexDecode(encoded, b.p, b.n);
var r = new byte[n];
Marshal.Copy((IntPtr)b.p, r, 0, n);
return r;
}
///
/// Converts hex-encoded string to binary data. Writes to caller's memory buffer.
///
/// The number of bytes written in decoded memory. It is equal or less than Math.Min(bufferSize, encoded.Length/2).
/// Memory buffer for result.
/// Max number of bytes that can be written to the decoded memory buffer.
///
public static int HexDecode(RStr encoded, void* decoded, int bufferSize) {
if (encoded.Length == 0) return 0;
var t = Tables_.Hex;
byte* r = (byte*)decoded, rTo = r + bufferSize;
uint k = 1;
for (int i = 0; i < encoded.Length; i++) {
uint c = (uint)(encoded[i] - '0'); if (c >= 55) continue;
c = t[c]; if (c == 0xFF) continue;
k <<= 4;
k |= c;
if (0 != (k & 0x100)) {
if (r >= rTo) break;
*r++ = (byte)k;
k = 1;
}
}
return (int)(r - (byte*)decoded);
//speed: slightly slower than Base64Decode for the same binary data size.
}
///
/// Converts hex-encoded string to a struct variable.
///
/// false if decoded size != sizeof(T).
/// The result variable.
///
public static bool HexDecode(RStr encoded, out T decoded) where T : unmanaged {
T t;
if (HexDecode(encoded, &t, sizeof(T)) != sizeof(T)) { decoded = default; return false; }
decoded = t;
return true;
}
#endregion
#region compress
///
/// Compresses data. Uses .
///
/// Data. See also: , .
/// Exceptions of .
public static byte[] DeflateCompress(RByte data) {
using var m = new MemoryStream();
using (var x = new DeflateStream(m, CompressionLevel.Optimal)) x.Write(data); //note: must dispose before ToArray
return m.ToArray();
//tested: GZipStream same compression but adds 18 bytes header. DeflateStream does not add any header.
//tested: bz2 and 7z compression isn't much better with single 15 kb bmp file.
}
///
/// Decompresses data. Uses .
///
/// Decompressed data.
/// Compressed data.
/// Exceptions of .
public static byte[] DeflateDecompress(RByte compressed) {
using var m = new MemoryStream();
DeflateDecompress(compressed, m);
return m.ToArray();
}
///
/// Decompresses data to a caller-provided memory stream. Uses .
///
/// Compressed data.
/// Stream for decompressed data.
/// Exceptions of .
public static void DeflateDecompress(RByte compressed, Stream decompressed) {
fixed (byte* p = compressed) {
using var m = new UnmanagedMemoryStream(p, compressed.Length);
using var x = new DeflateStream(m, CompressionMode.Decompress);
x.CopyTo(decompressed);
}
//note: cannot deflateStream.Read directly to array because its Length etc are not supported.
//note: also cannot use decompressedStream.GetBuffer because it can be bigger.
}
///
/// Compresses data. Uses .
///
/// Data. See also: , .
/// Exceptions of .
public static byte[] GzipCompress(RByte data) {
using var m = new MemoryStream();
using (var x = new GZipStream(m, CompressionLevel.Optimal)) x.Write(data);
return m.ToArray();
}
///
/// Decompresses data. Uses .
///
/// Decompressed data.
/// Compressed data.
/// Exceptions of .
public static byte[] GzipDecompress(RByte compressed) {
using var m = new MemoryStream();
GzipDecompress(compressed, m);
return m.ToArray();
}
///
/// Decompresses data to a caller-provided memory stream. Uses .
///
/// Compressed data.
/// Stream for decompressed data.
/// Exceptions of .
public static void GzipDecompress(RByte compressed, Stream decompressed) {
fixed (byte* p = compressed) {
using var m = new UnmanagedMemoryStream(p, compressed.Length);
using var x = new GZipStream(m, CompressionMode.Decompress);
x.CopyTo(decompressed);
}
}
///
/// Compresses data. Uses .
///
/// Data. See also: , .
/// Compression level, 0 (no compression) to 11 (maximal compression). Default 6. Bigger levels don't make much smaller but can make much slower.
/// Invalid level.
///
/// failed.
public static unsafe byte[] BrotliCompress(RByte data, int level = 6) {
int n = BrotliEncoder.GetMaxCompressedLength(data.Length)
+ data.Length / 1000 + 1000; //GetMaxCompressedLength returns too small value, and TryCompress fails when data is already compressed
var b = MemoryUtil.Alloc(n);
try {
if (!BrotliEncoder.TryCompress(data, new(b, n), out n, level, 22)) throw new AuException();
return new Span(b, n).ToArray();
}
finally { MemoryUtil.Free(b); }
}
///
/// Decompresses data. Uses .
///
/// Decompressed data.
/// Compressed data.
/// Invalid data.
///
public static unsafe byte[] BrotliDecompress(RByte compressed) {
int n = checked(compressed.Length * 4 + 8000);
for (int i = 0; i < 3; i++) if (n < 512_000) n *= 2;
//print.it(compressed.Length, n, n/compressed.Length); //usually ~ 80 KB
for (; ; n = checked(n * 2)) {
byte* b = MemoryUtil.Alloc(n);
try {
if (BrotliDecoder.TryDecompress(compressed, new(b, n), out int nw)) return new Span(b, nw).ToArray();
if (nw == 0) throw new ArgumentException("cannot decompress this data");
//print.it(n);
}
finally { MemoryUtil.Free(b); }
}
}
#endregion
#region encrypt
///
/// AES-encrypts a byte[] or string. Returns byte[].
///
/// Data to encrypt. Can be byte[] or string.
/// Encryption key. Can be non-empty string (eg a password) or byte[] of length 16 (eg a password hash).
/// Encrypted data. The first 16 bytes is initialization vector (not secret).
///
///
public static byte[] AesEncryptB(object data, object key) {
Not_.Null(data, key);
var enc = AesEncryptB(data, key, out var iv);
var r = new byte[enc.Length + iv.Length];
iv.CopyTo(r, 0);
enc.CopyTo(r, iv.Length);
return r;
}
///
/// AES-encrypts a byte[] or string. Returns byte[].
///
/// Data to encrypt. Can be byte[] or string.
/// Encryption key. Can be non-empty string (eg a password) or byte[] of length 16 (eg a password hash).
/// Receives an initialization vector. The function generates a random value. Use it with decrypt functions. Don't need to keep it in secret.
/// Encrypted data.
///
///
public static byte[] AesEncryptB(object data, object key, out byte[] IV) {
Not_.Null(data, key);
var d = data switch { byte[] b => b, string s => Encoding.UTF8.GetBytes(s), _ => throw new ArgumentException("Expected byte[] or string", "data") };
using var aes = Aes.Create();
using var x = aes.CreateEncryptor(_Key(key), IV = aes.IV);
return x.TransformFinalBlock(d, 0, d.Length);
}
///
/// AES-encrypts a byte[] or string.
/// Calls and converts the returned byte[] to Base64 string.
///
///
///
///
///
public static string AesEncryptS(object data, object key) {
Not_.Null(data, key);
return Convert.ToBase64String(AesEncryptB(data, key));
}
///
/// AES-encrypts a byte[] or string.
/// Calls and converts the returned byte[] to Base64 string.
///
///
public static string AesEncryptS(object data, object key, out byte[] IV) {
Not_.Null(data, key);
return Convert.ToBase64String(AesEncryptB(data, key, out IV));
}
///
/// AES-decrypts data. Returns byte[].
///
/// Encryped data as byte[] or Base64 string.
/// Encryption key. Can be non-empty string (eg a password) or byte[] of length 16 (eg a password hash).
/// If used AesEncryptX that returns an initialization vector, pass it here.
///
///
public static byte[] AesDecryptB(object data, object key, byte[] IV = null) {
Not_.Null(data, key);
var d = data switch { byte[] b => b, string s => Convert.FromBase64String(s), _ => throw new ArgumentException("Expected byte[] or string", "data") };
using var aes = Aes.Create();
using var x = aes.CreateDecryptor(_Key(key), IV ?? d[..16]);
var (from, len) = IV != null ? (0, d.Length) : (16, d.Length - 16);
return x.TransformFinalBlock(d, from, len);
}
///
/// AES-decrypts data.
/// Calls and converts the returned byte[] to string.
///
///
public static string AesDecryptS(object data, object key, byte[] IV = null) {
Not_.Null(data, key);
return Encoding.UTF8.GetString(AesDecryptB(data, key, IV));
}
static byte[] _Key(object key) {
switch (key) {
case byte[] b:
if (b.Length != 16) throw new ArgumentException("Expected length 16", "key");
return b;
case string s:
if (s.Length == 0) throw new ArgumentException("Empty string", "key");
return Hash.MD5(s).ToArray();
}
throw new ArgumentException("Expected byte[] or string", "key");
}
#endregion
#region utf8
///
/// Converts string to UTF-8 byte[]. Can append "\0" (default) or some other string.
///
/// String or char[] or span of string/array/memory.
/// A string to append, or null. For example "\0" (default) or "\r\n". Must contain only ASCII characters.
/// append contains non-ASCII characters.
public static byte[] Utf8Encode(RStr chars, string append = "\0") {
int n = Encoding.UTF8.GetByteCount(chars);
int nAppend = append.Lenn();
var r = new byte[n + nAppend];
int nn = Encoding.UTF8.GetBytes(chars, r);
Debug.Assert(nn == n);
if (nAppend > 0 && !(nAppend == 1 && append[0] == '\0')) {
foreach (char c in append) {
if (c > 127) throw new ArgumentException("append must be ASCII");
r[nn++] = (byte)c;
}
}
return r;
//speed: faster than WideCharToMultiByte. Same as System.Text.Unicode.Utf8.FromUtf16.
}
///
/// Converts '\0'-terminated UTF-8 string to C# string (UTF-16).
///
/// UTF-8 string. If null, returns null.
///
/// Finds '\0' and calls . Don't use this function when UTF-8 string length is known; call Encoding.UTF8.GetString directly.
///
public static string Utf8Decode(byte* utf8) => utf8 == null ? null : Encoding.UTF8.GetString(utf8, Ptr_.Length(utf8));
///
/// Converts string to UTF-8. If non-ASCII, gets UTF-8 character offsets.
///
///
/// Tuple:
///
• text - Encoding.UTF8.GetBytes(s).
///
• offsets - null if s is ASCII. Else UTF-8 character offsets for each s character plus at s.Length.
///
internal static (byte[] text, int[] offsets) Utf8EncodeAndGetOffsets_(RStr s, bool append0 = false) {
var s2 = Utf8Encode(s, append0 ? "\0" : ""); //always creates valid UTF-8. Replaces invalid UTF-16 chars.
int len2 = s2.Length; if (append0) len2--;
if (len2 == s.Length) return (s2, null); //ASCII
var a = new int[s.Length + 1];
int i8 = 0, i16 = 0;
while (i8 < len2) {
a[i16++] = i8;
int c = s2[i8];
if (c < 0xC0) i8++;
else if (c < 0xE0) i8 += 2;
else if (c < 0xF0) i8 += 3;
else { a[i16++] = i8; i8 += 4; }
}
Debug.Assert(i8 == len2);
Debug.Assert(i16 == s.Length);
a[i16] = i8;
return (s2, a);
}
#endregion
#region base64 (rejected)
/////
///// Gets Base64 encoded string length for non-encoded length.
///// It is (length + 2) / 3 * 4.
/////
//public static int Base64EncodeLength(int length) => checked((length + 2) / 3 * 4);
/////
///// Gets decoded data length from Base64 encoded string length, assuming there are no newlines and other whitespace characters.
///// It is (int)(len * 3L / 4) minus the number of padding '=' characters (max 2).
/////
//public static int Base64DecodeLength(RStr encoded) {
// int len = encoded.Length;
// if (len == 0) return 0;
// int r = (int)(len * 3L / 4);
// if (0 == (len & 3)) {
// if (encoded[len - 1] == '=') {
// r--;
// if (encoded[len - 2] == '=') r--;
// }
// }
// return r;
//}
//currently not used in this lib. Not tested speed.
/////
///// Converts string span containing Base64 encoded data to byte[].
/////
///// String or char[] or span of string/array/memory containing Base64 encoded data.
/////
///// false if failed.
/////
///// Uses .
/////
//public static bool TryBase64Decode(RStr encoded, out byte[] result) {
// result = null;
// encoded = encoded.Trim();
// //todo: calc length: skip whitespace. Maybe bool parameter. Or use FastBuffer and copy, like in HexDecode.
// var a = new byte[Base64DecodeLength(encoded)];
// if (!Convert.TryFromBase64Chars(encoded, a, out int len)) return false;
// if (len != a.Length) {
// Debug_.Print($"{a.Length} {len}");
// a = a.AsSpan(0, len).ToArray();
// }
// result = a;
// return true;
//}
//rejected: URL-safe Base64.
// Not used in this lib.
// Cannot be used in file names because case-sensitive.
// If somebody wants URL-safe Base64, it's easy/fast to replace unsafe characters. Nobody would find and use these functions.
/////
///// Converts byte[] or other memory to Base64 encoded string that can be used in URL.
/////
///// Data to encode.
///// Like , but instead of '/' and '+' uses '_' and '-'.
//public static string Base64UrlEncode(RByte data) {
// fixed (byte* p = data) return Base64UrlEncode(p, data.Length);
// //speed: same as Convert.ToBase64String
//}
/////
///// Converts binary data stored in any memory to Base64 encoded string that can be used in URL.
/////
///// Data to encode.
///// Number of bytes to encode.
///// Instead of '/' and '+' uses '_' and '-'.
//public static string Base64UrlEncode(void* data, int length) {
// var ip = (IntPtr)data;
// return string.Create(Base64EncodeLength(length), (ip, length), static (span, tu) => {
// Convert.TryToBase64Chars(new RByte((byte*)tu.ip, tu.length), span, out _);
// for (int i = 0; i < span.Length; i++) {
// switch (span[i]) { case '/': span[i] = '_'; break; case '+': span[i] = '-'; break; }
// }
// });
//}
/////
///// Converts a struct variable to Base64 encoded string that can be used in URL.
/////
///// Variable.
///// Instead of '/' and '+' uses '_' and '-'.
//public static string Base64UrlEncode(T x) where T : unmanaged {
// return Base64UrlEncode(&x, sizeof(T));
//}
//static void _Base64_Replace(RStr encoded, char[] a) {
// for (int i = 0; i < encoded.Length; i++) {
// char c = encoded[i];
// a[i] = c switch { '_' => '/', '-' => '+', _ => c, };
// }
//}
/////
///// Converts string containing Base64 encoded data to byte[]. Supports standard encoding and URL-safe encoding.
/////
///// String or char[] or span of string/array/memory containing Base64 encoded data.
///// Like , but the string can contain '_' and '-' instead of '/' and '+'.
///// Exceptions of .
//public static byte[] Base64UrlDecode(RStr encoded) {
// char[] a = ArrayPool.Shared.Rent(encoded.Length);
// try {
// _Base64_Replace(encoded, a);
// return Convert.FromBase64CharArray(a, 0, encoded.Length);
// }
// finally { ArrayPool.Shared.Return(a); }
// //never mind: almost 2 times slower than Convert.FromBase64CharArray.
// // Normally this func is used with short strings and not with many strings in loop.
// // ArrayPool isn't as fast as should be. And copying to new array takes time.
//}
/////
///// Converts string containing Base64 encoded data to bytes and stores in memory of a Span variable. Supports standard encoding and URL-safe encoding.
///// Returns false if the encoded string is invalid or the buffer is too small.
/////
///// String or char[] or span of string/array/memory containing Base64 encoded data.
///// Memory buffer for the result.
/////
///// The string can contain '_' and '-' instead of '/' and '+'.
//public static bool Base64UrlDecode(RStr encoded, Span decoded, out int decodedLength) {
// char[] a = ArrayPool.Shared.Rent(encoded.Length);
// try {
// _Base64_Replace(encoded, a);
// return Convert.TryFromBase64Chars(new RStr(a, 0, encoded.Length), decoded, out decodedLength);
// }
// finally { ArrayPool.Shared.Return(a); }
//}
/////
///// Converts string containing Base64 encoded data to bytes and stores in any memory. Supports standard encoding and URL-safe encoding.
///// Returns false if the encoded string is invalid or the buffer is too small.
/////
///// String or char[] or span of string/array/memory containing Base64 encoded data.
///// Memory buffer for the result.
///// The max number of bytes that can be written to the decoded memory buffer.
///// Receives the number of bytes written to the decoded memory buffer.
///// The string can contain '_' and '-' instead of '/' and '+'.
//public static bool Base64UrlDecode(RStr encoded, void* decoded, int bufferSize, out int decodedLength) {
// return Base64UrlDecode(encoded, new Span(decoded, bufferSize), out decodedLength);
//}
/////
///// Converts string containing Base64 encoded data to a struct variable. Supports standard encoding and URL-safe encoding.
///// Returns false if the encoded string is invalid or decoded size != sizeof(T).
/////
///// String or char[] or span of string/array/memory containing Base64 encoded data.
///// The result variable.
///// The string can contain '_' and '-' instead of '/' and '+'.
//public static bool Base64UrlDecode(RStr encoded, out T decoded) where T : unmanaged {
// T t;
// if (!Base64UrlDecode(encoded, &t, sizeof(T), out int n) || n != sizeof(T)) { decoded = default; return false; }
// decoded = t;
// return true;
//}
#endregion
}
================================================
FILE: Au/Au.More/DebugTraceListener.cs
================================================
namespace Au.More;
///
/// Replaces the default trace listener with a listener that shows a message box on a failed assertion.
///
///
/// The new trace listener overrides the method.
/// On failed assertion (, , , ) it shows a message box with buttons Exit Debug Ignore, unless debugger is attached or is false.
///
public class DebugTraceListener : DefaultTraceListener {
///
/// Replaces default trace listener.
///
/// Also set = true.
//[Conditional("DEBUG"), Conditional("TRACE")] //no, in most cases this is called by this library, not directly by the app
public static void Setup(bool usePrint) {
if (!s_setup) {
s_setup = true;
Trace.Listeners.Remove("Default"); //remove DefaultTraceListener. It calls Environment.FailFast which shows message box "Unknown hard error".
Trace.Listeners.Add(new DebugTraceListener());
}
print.redirectDebugOutput = usePrint;
}
static bool s_setup;
///
public override void Fail(string message, string detailMessage) {
var s = message;
if (s.NE()) s = detailMessage; else if (!detailMessage.NE()) s = message + "\r\n" + detailMessage;
if (!s.NE()) s += "\r\n";
string st = new StackTrace(2, true).ToString(), st1 = null;
if (st.RxMatch(@"(?m)^\s+at (?!System\.Diagnostics\.)", 0, out RXGroup g)) {
st = st[g.Start..];
st1 = st.Lines(true)[0];
}
var s2 = "---- Debug assertion failed ----\r\n" + s + st;
Trace.WriteLine(s2);
if (!(print.redirectDebugOutput && print.qm2.use)) print.qm2.write(s2);
if (Debugger.IsAttached) {
Debugger.Break();
} else {
if (!AssertUiEnabled) return; //like default listener
s = $"{s}{st1}\n\nProcess id: {process.thisProcessId}";
//int r = dialog.showWarning("Debug assertion failed", s, "1 Exit|2 Ignore|3 script.debug|4 Debugger.Launch", expandedText: st); //no. Need to block all messages in this thread, to prevent reentering this or executing code somewhere else.
//int r = Task.Run(() => dialog.showWarning("Debug assertion failed", s, "1 Exit|2 Ignore|3 script.debug|4 Debugger.Launch", expandedText: st)).Result; //no. It seems .NET adds messages (eg WM_TIMER) to a queue, and finally reposts all.
int r = 0;
var thread = run.thread(() => { r = dialog.showWarning("Debug assertion failed", s, "1 Exit|2 Ignore|3 script.debug|4 Debugger.Launch", expandedText: st); });
do 100.ms(); while (thread.IsAlive);
if (r == 1) Api.ExitProcess(-1);
if (r == 2) return;
if (r == 4) Debugger.Launch();
else {
script.debug();
Debugger.Break();
}
}
}
}
================================================
FILE: Au/Au.More/Dpi.cs
================================================
namespace Au.More {
///
/// Functions for high-DPI screen support.
///
///
/// To find DPI % on Windows 10 and 11: Settings > System > Display > Scale and layout. If not 100%, it means high DPI.
///
/// This program must be per-monitor-DPI-aware. Else results are undefined.
///
public static class Dpi {
///
/// Gets DPI of the primary screen at the time this process started.
///
public static int System {
get {
if (_systemDPI == 0) {
using var dcs = new ScreenDC_();
_systemDPI = Api.GetDeviceCaps(dcs, 90); //LOGPIXELSY
//could use GetDpiForSystem instead (Windows 10 1607).
// Normally the same result (tested), but probably not if thread awareness context is Unaware (not tested).
}
return _systemDPI;
}
}
static int _systemDPI;
///
/// Gets the DPI of a window, as used in the window's process. It never changes for that window instance.
///
/// If failed, returns the system DPI ().
/// A top-level window or control. Can belong to any process.
///
/// If true, works on Windows 8.1 and later; however on Windows 8.1 slower and less reliable.
/// If false (default), works on Windows 10 1607 and later.
///
///
/// The result depends on the DPI awareness of the window:
/// - per-monitor-DPI-aware - usually DPI of the windows's screen.
/// - system aware - system DPI (DPI of the primary screen).
/// - unaware - 96.
///
/// The result also depends on the Windows version:
/// - Works best on Windows 10 1607 and later. Uses API GetDpiForWindow.
/// - On Windows 8.1 works if supportWin81 true. If false (default), returns .
/// - On Windows 7 and 8.0 always returns , because there are no Windows API. Most apps are system-DPI-aware and the result is correct; for unaware apps the result is incorrect. These Windows versions don't support per-monitor DPI.
///
public static int OfWindow(wnd w, bool supportWin81 = false) {
if (!osVersion.minWin8_1) return System;
int R = 0;
if (!w.Is0) {
if (osVersion.minWin10_1607) R = Api.GetDpiForWindow(w);
else if (supportWin81) {
var v = WindowDpiAwareness(w); //info: quickly returns Awareness.PerMonitor if w.IsOfThisProcess
if (v == Awareness.Unaware) R = 96;
else if (v == Awareness.PerMonitor)
if (0 != Api.GetDpiForMonitor(Api.MonitorFromWindow(w.Window, SODefault.Nearest), 0, out R, out _)) R = 0;
}
}
return R != 0 ? R : System;
}
///
/// Returns OfWindow(w.Hwnd()).
///
/// A Winforms window or control.
public static int OfWindow(System.Windows.Forms.Control w) => OfWindow(w.Hwnd());
//rejected: supportWin81
///
/// Returns OfWindow(w.Hwnd()).
///
/// A WPF window or element.
public static int OfWindow(System.Windows.DependencyObject w) => OfWindow(w.Hwnd());
///
/// Gets DPI of a screen.
///
/// if fails or if not supported on this Windows version.
/// Native screen handle (HMONITOR).
/// Support Windows 8.1 and later. If false (default), supports Windows 10 1607 and later.
///
/// Uses API GetDpiForMonitor.
///
///
public static int OfScreen(IntPtr hMonitor, bool supportWin81 = false) {
bool os = supportWin81 ? osVersion.minWin8_1 : osVersion.minWin10_1607;
return os && 0 == Api.GetDpiForMonitor(hMonitor, 0, out int R, out _) ? R : System;
}
/////
//public static int OfScreen(screen s, bool supportWin81 = false) => OfScreen(s.Now, supportWin81);
///
/// Scales int if the specified DPI isn't 96 (100%).
///
public static int Scale(int i, DpiOf dpiOf) => Math2.MulDiv(i, dpiOf, 96);
//no. Eg also would be used for uint, long... Or name eg ScaleD. Or add double extension method.
/////
///// Scales int if the specified DPI isn't 96 (100%).
/////
//public static int Scale(double i, DpiOf dpiOf) => (i*(int)dpiOf/96).ToInt();
///
/// Unscales int if the specified DPI isn't 96 (100%).
///
public static double Unscale(int i, DpiOf dpiOf) => i * (96d / dpiOf);
//Unscaling sometimes useful with WPF. Unscale to double, not int, else result often incorrect.
///
/// Scales if the specified DPI isn't 96 (100%).
///
public static SIZE Scale(SIZE z, DpiOf dpiOf) {
int dpi = dpiOf;
z.width = Math2.MulDiv(z.width, dpi, 96);
z.height = Math2.MulDiv(z.height, dpi, 96);
return z;
}
///
/// Scales if the specified DPI isn't 96 (100%).
///
public static SIZE Scale(System.Windows.Size z, DpiOf dpiOf) {
double f = (int)dpiOf / 96d;
z.Width *= f;
z.Height *= f;
return new(z.Width.ToInt(), z.Height.ToInt());
}
///
/// Unscales if the specified DPI isn't 96 (100%).
///
public static System.Windows.Size Unscale(SIZE z, DpiOf dpiOf) {
double f = 96d / dpiOf;
return new(z.width * f, z.height * f);
}
///
/// Scales if the specified DPI isn't 96 (100%).
///
public static RECT Scale(RECT r, DpiOf dpiOf) {
int dpi = dpiOf;
r.left = Math2.MulDiv(r.left, dpi, 96);
r.top = Math2.MulDiv(r.top, dpi, 96);
r.right = Math2.MulDiv(r.right, dpi, 96);
r.bottom = Math2.MulDiv(r.bottom, dpi, 96);
return r;
}
///
/// Unscales if the specified DPI isn't 96 (100%).
///
public static System.Windows.Rect Unscale(RECT r, DpiOf dpiOf) {
double f = 96d / dpiOf;
return new(r.left * f, r.top * f, r.Width * f, r.Height * f);
}
///
/// Calls API GetSystemMetricsForDpi if available, else GetSystemMetrics.
///
public static int GetSystemMetrics(int nIndex, DpiOf dpiOf)
=> osVersion.minWin10_1607
? Api.GetSystemMetricsForDpi(nIndex, dpiOf)
: Api.GetSystemMetrics(nIndex);
///
/// Calls API SystemParametersInfoForDpi if available, else SystemParametersInfo.
/// Use only with uiAction = SPI_GETICONTITLELOGFONT, SPI_GETICONMETRICS, SPI_GETNONCLIENTMETRICS.
///
public static unsafe bool SystemParametersInfo(uint uiAction, int uiParam, void* pvParam, DpiOf dpiOf)
=> osVersion.minWin10_1607
? Api.SystemParametersInfoForDpi(uiAction, uiParam, pvParam, 0, dpiOf)
: Api.SystemParametersInfo(uiAction, uiParam, pvParam);
///
/// Calls API AdjustWindowRectExForDpi if available, else AdjustWindowRectEx.
///
///
/// Also adds scrollbar width or/and height if need.
///
public static bool AdjustWindowRectEx(DpiOf dpiOf, ref RECT r, WS style, WSE exStyle, bool hasMenu = false) {
int dpi = dpiOf;
bool ok = osVersion.minWin10_1607
? Api.AdjustWindowRectExForDpi(ref r, style, hasMenu, exStyle, dpi)
: Api.AdjustWindowRectEx(ref r, style, hasMenu, exStyle);
if (ok) {
if (style.Has(WS.VSCROLL)) r.Width += ScrollbarV_(dpi);
if (style.Has(WS.HSCROLL)) r.Width += ScrollbarH_(dpi);
}
return ok;
}
internal static int ScrollbarV_(int dpi) => GetSystemMetrics(Api.SM_CXVSCROLL, dpi);
internal static int ScrollbarH_(int dpi) => GetSystemMetrics(Api.SM_CYHSCROLL, dpi);
///
/// DPI awareness of a window or process.
///
public enum Awareness { //== DPI_AWARENESS == PROCESS_DPI_AWARENESS
///
Invalid = -1,
///
Unaware,
///
System,
///
PerMonitor
}
///
/// Gets DPI awareness of a window.
///
/// if failed.
/// A top-level window or control. Can belong to any process.
///
/// Works best on Windows 10 1607 and later; uses API GetWindowDpiAwarenessContext.
/// On Windows 8.1 returns if w is of this process; else uses API GetProcessDpiAwareness, which is slower and less reliable.
/// On Windows 7 and 8.0 always returns , because there are no Windows API.
///
public static Awareness WindowDpiAwareness(wnd w) {
if (osVersion.minWin10_1607) {
return Api.GetAwarenessFromDpiAwarenessContext(Api.GetWindowDpiAwarenessContext(w));
} else if (osVersion.minWin8_1) {
if (w.IsOfThisProcess) return Awareness.PerMonitor;
using var hp = Handle_.OpenProcess(w);
return (!hp.Is0 && 0 == Api.GetProcessDpiAwareness(hp, out var a)) ? a : Awareness.Invalid;
} else {
return Awareness.System;
//could use, IsWindowVirtualized (except if of this process), but slow and unreliable.
}
}
///
/// Detects whether the window is DPI-scaled/virtualized.
///
/// false if not DPI-scaled/virtualized or if fails to detect or if invalid window handle.
/// A top-level window or control. Can belong to any process.
///
/// Such windows are a little blurry (entire client area).
///
/// OS scales a window when it is on a high-DPI screen, depending on the DPI awareness of the window:
/// - Unaware - always.
/// - System - if the screen DPI is not equal to the system DPI of that process (which usually is of the primary screen, but not always).
///
/// Such windows have various problems for automation apps:
/// - Often difficult or impossible to get correct rectangles of UI elements (and therefore cannot click etc) or get correct UI element from point. It depends on used API (UIA, MSAA, JAB), inproc/notinproc, OS version and application.
/// - On Windows 7 and 8.0 cannot easily get correct rectangles of such windows and their controls. This library ignores it, because would be too much work to apply workarounds in so many places and just for legacy OS versions (it has been fixed in Windows 8.1).
/// - If with want to use window pixels, need to capture image from window pixels, not from screen pixels.
///
/// This function is not completely reliable. And not very fast. This process must be per-monitor-DPI-aware.
///
public static bool IsWindowVirtualized(wnd w) {
if (GetScalingInfo_(w, out bool scaled, out _, out _)) return scaled;
return IsWindowVirtualizedLegacy_(w);
//note: child windows can have different DPI awareness (minWin10_1607). See GetWindowDpiHostingBehavior. Not tested, not seen.
//Also tested detecting with GDI. GDI functions return logical (not DPI-scaled) offsets/rectangles/etc. Works, but much slower.
}
///
/// On Win10+ returns . Else false.
///
internal static bool IsWindowVirtualizedWin10_(wnd w) => osVersion.minWin10_1607 && IsWindowVirtualized(w);
///
/// If possible, gets whether the window is DPI-scaled/virtualized, and gets physical and logical rects if scaled.
/// Returns false if !osVersion.minWin10_1607 or if cannot get that info.
/// Gets that info in a fast and reliable way.
///
internal static bool GetScalingInfo_(wnd w, out bool scaled, out RECT rPhysical, out RECT rLogical) {
scaled = false; rPhysical = default; rLogical = default;
if (!osVersion.minWin10_1607) return false;
var awareness = WindowDpiAwareness(w); //fast on Win10
if (awareness is Awareness.System or Awareness.Unaware) { //tested: unaware-gdi-scaled same as unaware
if (awareness == Awareness.System && Api.GetDpiForWindow(w) != System) { /*fast*/
//Cannot get rLogical. It's rare and temporary, ie when the user recently changed DPI of the primary screen.
//Even if this func isn't used to get rects, without this fast code could be unreliable.
Debug_.Print("w System DPI != our System DPI");
return false;
}
for (; ; ) {
RECT r1 = w.Rect, r2, r3; //note: with ClientRect 4 times faster, but unreliable if small rect. Now fast enough.
bool rectWorkaround = false;
using (var u = new AwarenessContext(awareness == Awareness.System ? -2 : -1)) {
if (Api.GetAwarenessFromDpiAwarenessContext(u.Previous_) != Awareness.PerMonitor) { /*fast*/
//cannot get rPhysical. But let's set PM awareness and get it. Works even if this process is Unaware.
rectWorkaround = _GetRect(w, out r1);
Debug_.Print("bad DPI awareness of this thread; workaround " + (rectWorkaround ? "OK" : "failed"));
if (!rectWorkaround) return false; //unlikely. Then the caller probably will call the legacy func, it works with any DPI awareness.
}
r2 = w.Rect;
if (r2 == r1) break;
}
if (!rectWorkaround) r3 = w.Rect; else _GetRect(w, out r3);
if (r3 != r1) continue; //moved, resized or closed between Rect and Rect
scaled = true;
rPhysical = r1;
rLogical = r2;
break;
}
static bool _GetRect(wnd w, out RECT r) {
using (var u2 = new AwarenessContext(-4)) { //per-monitor-v2
if (u2.Previous_ == 0) { r = default; return false; } //API failed. Unlikely. Works even if this process is Unaware.
r = w.Rect;
}
return true;
}
}
return true;
}
internal static bool IsWindowVirtualizedLegacy_(wnd w) {
//less reliable if control.
// LogicalToPhysicalPoint fails if not in top-level window rect.
// It seems PhysicalToLogicalPointForPerMonitorDPI doesn't fail, but it fails if not in that screen.
w = w.Window;
RECT rPrev = default;
for (int i = 0; i < 5; i++) {
if (!Api.GetWindowRect(w, out var r)) break; //Win10 1 mcs hot, 20 cold, old OS fast
if (r != rPrev) { i = 0; rPrev = r; } //moved or resized
POINT p = new(r.CenterX, r.CenterY); //p must be inside the window
if (i == 1) p.y = r.bottom - 1; else if (i == 2) p.y = r.top; else if (i == 3) p.x = r.right - 1; else if (i == 4) p.x = r.left; //and p must be in correct screen, which is unknown and therefore we use this code to guess. Normally succeeds at i==0, but may fail when the window is more than in 1 screen.
POINT k = p;
if (osVersion.minWin8_1 ? Api.PhysicalToLogicalPointForPerMonitorDPI(w, ref p) : Api.LogicalToPhysicalPoint(w, ref p)) { //Win10 3 mcs hot, old OS fast
//API bug: may scale the point although the window isn't scaled. Never mind.
// When window's center is between 2 screens and at the same time half of the window is offscreen. The area is several pixels wide.
//if (p != k) print.it(k, p);
if (p != k) return true;
break;
}
}
return false;
//tested: the API works with hidden and off-screen top-level windows too. Minimized windows are off-screen.
//tested: works even if this process is DPI System or Unaware. Tested on Win10.
//speed on Win10 when not PM-aware: 4 mcs hot, 40 mcs cold. All API much faster on old OS (tested on Vmware).
}
//No. In some cases (window positions) screen.of(w) does not match scaling. Not much faster.
//public static bool IsWindowVirtualized2(wnd w) {
// if (osVersion.minWin10_1607) {
// if (WindowDpiAwareness(w) is Awareness.PerMonitor or Awareness.Invalid) return false; //fast on Win10; slow on Win8.1, unavailable on Win7/8. Other API very slow on Win10, much faster on old OS (Vmware).
// w = w.Window; if (w.Is0) return false; //0.6 mcs hot
// var m = screen.of(w);
// int d1 = m.Dpi, d2 = OfWindow(w);
// //if (d1 == d2) print.it(d1);
// if (d1 == d2) return false;
// return w.IsAlive;
// } else {
// //same as now
// return false;
// }
//}
///
/// Can be used to temporarily change thread's DPI awareness context with API SetThreadDpiAwarenessContext.
/// Does nothing if the API is unavailable (added in Windows 10 version 1607).
///
///
/// Programs that use this library should use manifest with dpiAwareness = PerMonitorV2 and dpiAware = True/PM. Then default DPI awareness context is DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2.
///
///
///
///
public struct AwarenessContext : IDisposable {
nint _dac;
///
/// If , calls API SetThreadDpiAwarenessContext.
///
/// One of DPI_AWARENESS_CONTEXT constants: -1 unaware, -2 system, -3 per-monitor, -4 per-monitor-v2, -5 unaware-gdiscaled. Or a DPI_AWARENESS_CONTEXT handle.
public AwarenessContext(nint dpiContext) {
_dac = osVersion.minWin10_1607 ? Api.SetThreadDpiAwarenessContext(dpiContext) : default;
}
//rejected: enum dpiContext.
///
/// If , calls API GetWindowDpiAwarenessContext and SetThreadDpiAwarenessContext. Sets the awareness context of this thread equal to that of the window.
///
public AwarenessContext(wnd w) : this(Available ? Api.GetWindowDpiAwarenessContext(w) : 0) { }
///
/// Restores previous DPI awareness context.
///
public void Dispose() {
if (_dac == default) return;
Api.SetThreadDpiAwarenessContext(_dac);
_dac = default;
}
///
internal nint Previous_ => _dac;
///
/// Returns true if thread DPI awareness contexts are available on this Windows version (Windows 10 1607 and later).
///
public static bool Available => osVersion.minWin10_1607;
/////
//public const nint DPI_AWARENESS_CONTEXT_UNAWARE = -1;
/////
//public const nint DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2;
/////
//public const nint DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3;
/////
//public const nint DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4;
/////
//public const nint DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = -5;
}
}
}
namespace Au.Types {
///
/// Used for dpiOf parameter of functions. Allows to pass either a DPI value or an object from which gets DPI (window, screen etc).
///
///
/// Has implicit conversions from:
///
• int - DPI.
///
• - gets DPI of the window.
///
• IntPtr or nint - (screen handle) gets DPI of the screen.
///
• - gets DPI of the screen that contains the point.
///
• - gets DPI of screen that contains the rectangle.
///
• - gets DPI of the winforms control.
///
• - gets DPI of the WPF element.
///
/// The conversion operators set the property and the function can use it.
///
public struct DpiOf {
readonly int _dpi;
///
public DpiOf(int dpi) { _dpi = dpi; }
/// Invalid window handle.
public DpiOf(wnd w) {
w.ThrowIfInvalid();
_dpi = More.Dpi.OfWindow(w);
}
//rejected: parameter supportWin81. Rarely used. Can use Dpi functions when need.
///
/// Invalid window handle.
public DpiOf(System.Windows.Forms.Control c) : this(Not_.NullRet(c).Hwnd()) { }
///
/// Invalid window handle.
public DpiOf(System.Windows.DependencyObject c) : this(Not_.NullRet(c).Hwnd()) { }
///
public DpiOf(IntPtr hMonitor) { _dpi = More.Dpi.OfScreen(hMonitor); }
///
public DpiOf(POINT screenOf) : this(screen.of(screenOf).Handle) { }
///
public DpiOf(RECT screenOf) : this(screen.of(screenOf).Handle) { }
///
public static implicit operator DpiOf(int dpi) => new(dpi);
/// Invalid window handle.
public static implicit operator DpiOf(wnd w) => new(w);
///
/// Invalid window handle.
public static implicit operator DpiOf(System.Windows.Forms.Control c) => new(c);
///
/// Invalid window handle.
public static implicit operator DpiOf(System.Windows.DependencyObject c) => new(c);
///
public static implicit operator DpiOf(IntPtr hMonitor) => new(hMonitor);
///
public static implicit operator DpiOf(POINT screenOf) => new(screenOf);
///
public static implicit operator DpiOf(RECT screenOf) => new(screenOf);
///
public int Dpi => _dpi;
///
public static implicit operator int(DpiOf d) => d._dpi;
}
}
================================================
FILE: Au/Au.More/FastBuffer.cs
================================================
namespace Au.More {
///
/// Memory buffer on stack with ability to expand and use heap memory.
/// Can be used for calling Windows API or building arrays.
/// Must be used with [SkipLocalsInit] attribute; add it to the caller function or class.
///
///
/// b = new();
/// for (; ; ) if (b.GetString(_GetEnvironmentVariable(name, b.p, b.n), out var s)) return s;
/// }
/// }
/// ]]>
///
[StructLayout(LayoutKind.Sequential, Size = 16 + StackSize + 16)] //16 for other fields + 16 for safety
public unsafe ref struct FastBuffer where T : unmanaged {
T* _p; //buffer pointer (on stack or native heap)
int _n; //buffer length (count of T elements)
bool _free; //if false, buffer is on stack in this variable (_p=&_stack). If true, buffer is allocated with MemoryUtil.Alloc.
long _stack; //start of buffer of StackSize size
///
/// A variable contains a field of this size. It is a memory buffer on stack.
/// It is a byte count and does not depend on T. To get count of T elements on stack: StackSize/sizeof(T).
///
public const int StackSize = 2048;
//const int StackSize = 16; //test More() and GetString()
//Also tested:
// 1. Struct of normal size, when caller passes stackalloc'ed Span. Slow in Debug. And more caling code.
// 2. See the commented out Buffer_.
// 3. Callback. Good: easy to use, less calling code, don't need [SkipLocalsInit]. Bad: problem with captured variables (garbage, slow); slower in any case.
// 4. foreach. Nothing good.
///
/// Memory buffer pointer.
///
public T* p => _p;
///
/// Returns memory buffer pointer ().
///
public static implicit operator T*(in FastBuffer b) => b._p;
///
/// Gets reference to p[i]. Does not check bounds.
///
public ref T this[int i] => ref _p[i];
///
/// Memory buffer length as number of elements of type T.
///
public int n => _n;
///
/// Allocates first buffer of default size. It is on stack (in this variable), and its length is StackSize/sizeof(T) elements of type T (2048 bytes or 1024 chars or 512 ints...).
///
public FastBuffer() {
//With this overload slightly faster. Also, the int overload is confusing when need buffer of default size.
_stack = 0;
fixed (long* t = &_stack) { _p = (T*)t; }
//_p = (T*)Unsafe.AsPointer(ref _stack); //slower in Debug, same speed in Release
_n = StackSize / sizeof(T);
_free = false;
}
///
/// Allocates first buffer of specified size.
///
///
/// Buffer length (number of elements of type T).
/// If <= StackSize/sizeof(T), the buffer contains StackSize/sizeof(T) elements on stack (in this variable); it is 2048 bytes or 1024 chars or 512 ints... Else allocates native memory (much slower).
///
public FastBuffer(int n) {
_stack = 0;
int nStack = StackSize / sizeof(T);
if (_free = n > nStack) {
_p = MemoryUtil.Alloc(n + 1); //+1 for safety
_n = n;
} else {
_n = nStack;
fixed (long* t = &_stack) { _p = (T*)t; }
//_p = (T*)Unsafe.AsPointer(ref _stack);
}
//rejected: for medium-size buffers use ArrayPool.
// It is usually much faster than MemoryUtil, but getting pinned pointer from array is slow.
// To get pinned pointer, I know 3 ways.
// 1. fixed(...){...}. Here cannot be used.
// 2. GCHandle. Makes 3 times slower than just ArrayPool Rent/Return. Then MemoryUtil is only 50% slower. With this buffer size it does not matter.
// 3. Memory/MemoryHandle. Same speed as GCHandle. MemoryHandle is bigger and managed.
// Tested: MemoryPool is slower than ArrayPool and creates garbage.
//tested: heap memory allocation becomes much slower starting from 1 MB. Then virtual memory is several times faster (else much slower). But with this buffer size it does not matter.
}
///
/// Allocates new bigger buffer of specified length. Frees old buffer if need.
///
/// Number of elements of type T.
/// Copy previous buffer contents to the new buffer.
/// n <= current buffer length.
public void More(int n, bool preserve = false) {
if (_n == 0) throw new ArgumentNullException(null, "No buffer. Use another constructor."); //with many API would still work, but very slow
if (n <= _n) throw new ArgumentException("n <= this.n");
if (!preserve) {
Dispose();
_p = MemoryUtil.Alloc(n + 1); //+1 for safety
} else if (_free) {
MemoryUtil.ReAlloc(ref _p, n + 1);
} else {
var p = MemoryUtil.Alloc(n + 1);
MemoryUtil.Copy(_p, p, _n * sizeof(T));
_p = p;
}
_n = n;
_free = true;
}
///
/// Allocates new bigger buffer of at least n*2 length. Frees old buffer if need.
///
/// Copy previous buffer contents to the new buffer.
public void More(bool preserve = false) => More(Math.Max(checked(_n * 2), 0x4000 / sizeof(T)), preserve); //16 KB = StackSize * 8
///
/// Frees allocated memory if need.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() {
if (_free) { MemoryUtil.Free(_p); _p = null; _n = 0; }
}
///
/// Gets API result as string, or allocates bigger buffer if old buffer was too small.
/// This function can be used when T is char.
///
/// The return value of the called Windows API function, if it returns string length or required buffer length. Or you can call .
/// Receives the result string if succeeded, else sDefault (default null).
///
/// Use if the API function isn't like this:
///
• If succeeds, returns string length without the terminating '\0' character.
///
• If buffer too small, returns required buffer length.
///
• If fails, returns 0.
///
/// Set s = this string if buffer too small or r < 1 or if the retrieved string == this string (avoid creating new string).
///
/// If r > , calls More(r); and returns false.
/// Else creates new string of r length and returns true.
///
public bool GetString(int r, out string s, BSFlags flags = 0, string sDefault = null) {
if (sizeof(T) != 2) throw new InvalidOperationException(); //cannot use extension method that would be added only to FastBuffer. See GetString2 comments below.
s = sDefault;
if (r >= _n - 1) {
if (0 != (flags & BSFlags.Truncates)) {
if (r >= (0 != (flags & BSFlags.ReturnsLengthWith0) ? _n : _n - 1)) {
More();
return false;
}
} else if (r > _n) {
More(r);
return false;
}
}
if (r > 0) {
if (0 != (flags & BSFlags.ReturnsLengthWith0)) r--;
if (sDefault == null || !new Span(_p, r).SequenceEqual(sDefault)) s = new string((char*)_p, 0, r);
}
return true;
}
///
/// Finds length of '\0'-terminated UTF-16 string in buffer and converts to C# string.
/// This function can be used when T is char. Use when length is unknown.
///
///
/// If there is no '\0' character, gets whole buffer, and the string probably is truncated.
///
public string GetStringFindLength() {
return new((char*)_p, 0, FindStringLength());
}
///
/// Finds length of '\0'-terminated char string in buffer.
/// Returns if there is no '\0' character.
///
public int FindStringLength() {
if (sizeof(T) != 2) throw new InvalidOperationException();
return Ptr_.Length((char*)_p, _n);
}
///
/// Finds length of '\0'-terminated byte string in buffer.
/// Returns if there is no '\0' character.
///
public int FindByteStringLength() {
if (sizeof(T) != 1) throw new InvalidOperationException();
return Ptr_.Length((byte*)_p, _n);
}
}
}
namespace Au.Types {
//error CS1657: Cannot use 'b' as a ref or out value because it is a 'using variable'.
//If in, compiles, but very slow. Probably copies t because calls More() which isn't readonly.
//public static partial class ExtAu
//{
// public static unsafe bool GetString2(this ref FastBuffer t, int r, out string s, BSFlags flags = 0, string sDefault = null) {
// ...
// }
//}
///
/// Flags for .
///
[Flags]
public enum BSFlags {
///
/// If buffer too small, the API gets part of string instead of returning required buffer length.
///
Truncates = 1,
///
/// The API returns string length including the terminating '\0' character.
///
ReturnsLengthWith0 = 2,
}
}
//rejected. Maybe 5% faster, but not so easy to use. Need more code, and easy to forget something. Also VS warning if: for(var p = stackalloc ...).
/////
///// Allocates and frees native memory buffers for calling Windows API and other functions.
///// Used when need to retry when the primary stackalloc'ed buffer was too small.
/////
/////
///// b = new(); int n, r;
///// for (var p = stackalloc char[n = 1024]; ; p = b.Alloc(n = r)) {
///// r = api.GetCurrentDirectory(n, p);
///// if(r < n) return r > 0 ? new(p, 0, r) : null;
///// }
///// }
/////
///// [SkipLocalsInit]
///// unsafe static string WinText(wnd w) {
///// using Buffer_ b = new(); int n, r;
///// for (var p = stackalloc char[n = 1024]; ; p = b.Alloc(checked(n *= 2))) {
///// r = api.InternalGetWindowText(w, p, n);
///// if (r < n - 1) return new string(p, 0, r);
///// }
///// }
///// ]]>
/////
//unsafe ref struct Buffer_ where T : unmanaged
//{
// T* _p;
// ///
// /// Allocates n elements of type T of native memory. Frees old memory.
// ///
// public T* Alloc(int n) {
// if (_p != null) { var p = _p; _p = null; MemoryUtil.Free(p); }
// return _p = MemoryUtil.Alloc(n);
// }
// ///
// /// Frees allocated memory.
// ///
// public void Dispose() {
// if (_p != null) { var p = _p; _p = null; MemoryUtil.Free(p); }
// }
//}
================================================
FILE: Au/Au.More/FileOpenSaveDialog.cs
================================================
namespace Au.More {
///
/// Shows standard "Open", "Save As" or "Select Folder" dialog to select a file or folder.
///
///
/// This class exists because the similar .NET classes have these problems:
/// - May disable the event.
/// - As owner window they support only Window or Form. This class also supports window handles.
/// - They support only filesystem items.
/// - There is no option to not add the selected file to recent files that are displayed in the context menu of the taskbar button etc.
/// - The WPF class does not have a client GUID property.
///
/// There are 2 main dialog types - open () and save (). All other functions of this class (properties, etc) are common to opening and saving.
///
///
///
///
public class FileOpenSaveDialog {
readonly string _clientGuid;
readonly bool _clearClientData;
/// A GUID used to save the recently used folder path and other data of this dialog. Optional. See IFileDialog.SetClientGuid.
/// Clear the recently used folder path and other data of this dialog.
public FileOpenSaveDialog(string clientGuid = null, bool clearClientData = false) {
_clientGuid = clientGuid;
_clearClientData = clearClientData;
}
///
/// Options common to all dialog types. Rarely used.
///
public FOSFlags CommonFlags { get; set; }
///
/// List of file types, like with .
/// Example: "Text files|*.txt|Office files|*.doc;*.xls|All files|*.*"
///
public Strings FileTypes { get; set; }
///
/// 1-based index of the selected file type (see ).
///
public int FileTypeIndex { get; set; }
///
/// Default extension to add to file names.
/// Must be like "txt", not like ".txt".
/// If null (default), gets from ; set "" to prevent it.
///
public string DefaultExt { get; set; }
///
/// Initial folder for the first time. Later is used the recently used folder instead.
///
public string InitFolderFirstTime { get; set; }
///
/// Initial folder to use now.
/// In most cases it's recommended to use instead.
///
///
///
///
public string InitFolderNow { get; set; }
///
/// Sets the initial file name text in the edit box.
///
public string FileNameText { private get; set; }
///
/// Sets the file name edit box label.
///
public string FileNameLabel { private get; set; }
///
/// Sets the OK button label.
///
public string OkButtonLabel { private get; set; }
///
/// Sets the dialog window name.
///
public string Title { private get; set; }
object _Show(bool save, _Api.FOS f, AnyWnd owner, string saveFile = null) {
var w = owner.Hwnd;
if (w.Is0) {
w = wnd.active;
if (!w.IsOfThisThread) w = default;
}
var d = (_Api.IFileDialog)Activator.CreateInstance(Type.GetTypeFromCLSID(save ? _Api.CLSID_FileSaveDialog : _Api.CLSID_FileOpenDialog));
if (_clientGuid != null) d.SetClientGuid(new(_clientGuid));
if (_clearClientData) d.ClearClientData();
f |= (_Api.FOS)CommonFlags ^ (_Api.FOS.FOS_DONTADDTORECENT | _Api.FOS.FOS_PATHMUSTEXIST); //print.it(f);
d.SetOptions(f);
var defExt = DefaultExt;
var ft = FileTypes.Value == null ? null : FileTypes.ToArray();
if (ft != null) {
int n = ft.Length / 2, i = FileTypeIndex;
d.SetFileTypes(n, ft);
if (i > 0) d.SetFileTypeIndex(i--);
if (defExt == null && (uint)i < n) ft[i * 2 + 1].RxMatch(@"^\*\.([^*?;]+)", 1, out defExt);
}
if (!defExt.NE()) d.SetDefaultExtension(defExt);
if (InitFolderFirstTime != null && _ShellItemFromString(InitFolderFirstTime, out var si1)) d.SetDefaultFolder(si1);
if (InitFolderNow != null && _ShellItemFromString(InitFolderNow, out var si2)) d.SetFolder(si2);
if (FileNameText != null) d.SetFileName(FileNameText);
if (FileNameLabel != null) d.SetFileNameLabel(FileNameLabel);
if (OkButtonLabel != null) d.SetOkButtonLabel(OkButtonLabel);
if (Title != null) d.SetTitle(Title);
if (saveFile != null && d is _Api.IFileSaveDialog sd && _ShellItemFromString(saveFile, out var si3)) sd.SetSaveAsItem(si3);
//if (0!=d.Show(w)) return null;
//API bug: after d.Show stops working AppDomain.UnhandledException event.
// Same with .NET wrappers; same when the native API called in C++; same with API GetOpenFileName.
// The API removes the .NET's unhandled exception filter and sets null filter. Need to restore it.
// AppModuleInit_ auto-restores, but in some apps it isn't called.
// Fixed in Win11.
var uef = Api.SetUnhandledExceptionFilter(0);
Api.SetUnhandledExceptionFilter(uef);
try { if (0 != d.Show(w)) return null; }
finally { Api.SetUnhandledExceptionFilter(uef); }
if (ft != null) FileTypeIndex = d.GetFileTypeIndex();
//FileNameText=d.GetFileName(); //fails here. And not useful.
if (!f.Has(_Api.FOS.FOS_ALLOWMULTISELECT)) return _ShellItemToString(d.GetResult());
var r = ((_Api.IFileOpenDialog)d).GetResults();
var a = new string[r.GetCount()];
for (int i = 0; i < a.Length; i++) a[i] = _ShellItemToString(r.GetItemAt(i));
return a;
}
object _ShowOpen(AnyWnd owner = default, bool multiSelect = false, bool selectFolder = false, bool onlyFilesystem = true, bool fileMustExist = true, bool previewPane = false) {
//default FOS_NOCHANGEDIR, FOS_PATHMUSTEXIST, FOS_FILEMUSTEXIST
var f = _Api.FOS.FOS_NOCHANGEDIR;
if (multiSelect) f |= _Api.FOS.FOS_ALLOWMULTISELECT;
if (selectFolder) f |= _Api.FOS.FOS_PICKFOLDERS;
if (onlyFilesystem) f |= _Api.FOS.FOS_FORCEFILESYSTEM; else f |= _Api.FOS.FOS_ALLNONSTORAGEITEMS;
if (fileMustExist) f |= _Api.FOS.FOS_FILEMUSTEXIST;
if (previewPane) f |= _Api.FOS.FOS_FORCEPREVIEWPANEON;
return _Show(false, f, owner);
}
///
/// Shows "Open" or "Select Folder" dialog that allows to select single item.
///
/// Full path of the selected file.
/// Owner window. Optional.
/// Select folders, not files.
/// The dialog allows to select only file system items (files, folders), not other shell items or URLs. Default true. If false, other shell items are returned like ":: ITEMIDLIST"; see .
/// The dialog can return only existing items. Default true.
/// Display the preview pane.
/// true on OK, false on Cancel or error.
public bool ShowOpen(out string result, AnyWnd owner = default, bool selectFolder = false, bool onlyFilesystem = true, bool fileMustExist = true, bool previewPane = false) {
result = _ShowOpen(owner, false, selectFolder, onlyFilesystem, fileMustExist, previewPane) as string;
return result != null;
}
///
/// Shows "Open" or "Select Folder" dialog that allows to select multiple items.
///
/// Full paths of the selected files.
///
public bool ShowOpen(out string[] result, AnyWnd owner = default, bool selectFolder = false, bool onlyFilesystem = true, bool fileMustExist = true, bool previewPane = false) {
result = _ShowOpen(owner, true, selectFolder, onlyFilesystem, fileMustExist, previewPane) as string[];
return result != null;
}
///
/// Shows "Save As" dialog.
///
/// Full path of the selected file.
/// Owner window. Optional.
/// If the selected file already exists, show a message box. Default true.
/// The initially selected file. Its name is displayed in the file name edit box, and the containing folder is opened. This would generally be used when the application is saving a file that already exists. For new files use .
/// true on OK, false on Cancel or error.
public bool ShowSave(out string result, AnyWnd owner = default, bool overwritePrompt = true, string initFile = null) {
//default FOS_OVERWRITEPROMPT, FOS_NOCHANGEDIR, FOS_PATHMUSTEXIST, FOS_NOREADONLYRETURN
var f = _Api.FOS.FOS_NOCHANGEDIR;
if (overwritePrompt) f |= _Api.FOS.FOS_OVERWRITEPROMPT;
/*if(!overwriteReadonly)*/
f |= _Api.FOS.FOS_NOREADONLYRETURN; //always works like with this flag
//if(createPrompt) f|=_Api.FOS.FOS_CREATEPROMPT; //does not work. The .NET wrapper shows messagebox explicitly.
//if(!testCreate) f|=_Api.FOS.FOS_NOTESTFILECREATE; //not important, not tested
//if(strictFileTypes) f|=_Api.FOS.FOS_STRICTFILETYPES; //does not work
result = _Show(true, f, owner, initFile) as string;
return result != null;
}
static string _ShellItemToString(_Api.IShellItem r) {
//if(!f.Has(_Api.FOS.FOS_FORCEFILESYSTEM)) {
// var k=r.GetAttributes(0xffffffff);
// if(0==(k&_Api.SFGAO_FILESYSTEM)) {
// print.it(k);
// }
//}
var s = r.GetDisplayName(SIGDN.FILESYSPATH | SIGDN.URL); //info: for a non-FS item, even with SIGDN.FILESYSPATH gets string like "::{GUID}"
return Pidl.ClsidToItemidlist_(s);
}
static bool _ShellItemFromString(string path, out _Api.IShellItem si) {
if (path.Starts(":: ")) {
var p = Pidl.FromString(path); if (p == null) { si = null; return false; }
return 0 == _Api.SHCreateItemFromIDList(p.UnsafePtr, typeof(_Api.IShellItem).GUID, out si);
} else {
return 0 == _Api.SHCreateItemFromParsingName(path, default, typeof(_Api.IShellItem).GUID, out si);
}
}
unsafe class _Api {
internal static Guid CLSID_FileOpenDialog = new(0xDC1C5A9C, 0xE88A, 0x4DDE, 0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7);
internal static Guid CLSID_FileSaveDialog = new(0xC0B4E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B);
[ComImport, Guid("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IFileOpenDialog : IFileDialog {
//IModalWindow
[PreserveSig] new int Show(wnd hwndOwner);
//IFileDialog
new void SetFileTypes(int cFileTypes, [MarshalAs(UnmanagedType.LPArray)][In] string[] rgFilterSpec);
new void SetFileTypeIndex(int iFileType);
new int GetFileTypeIndex();
[PreserveSig] new int Advise(/*IFileDialogEvents pfde, out uint pdwCookie*/);
[PreserveSig] new int Unadvise(uint dwCookie);
new void SetOptions(_Api.FOS fos);
new _Api.FOS GetOptions();
new void SetDefaultFolder(IShellItem psi);
new void SetFolder(IShellItem psi);
new IShellItem GetFolder();
new IShellItem GetCurrentSelection();
new void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);
[return: MarshalAs(UnmanagedType.LPWStr)] new string GetFileName();
new void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
new void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);
new void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
new IShellItem GetResult();
[PreserveSig] new int AddPlace(/*IShellItem psi, FDAP fdap*/);
new void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
[PreserveSig] new int Close(int hr);
new void SetClientGuid(in Guid guid);
new void ClearClientData();
[PreserveSig] new int SetFilter(/*IShellItemFilter pFilter*/);
//IFileOpenDialog
IShellItemArray GetResults();
IShellItemArray GetSelectedItems();
}
[ComImport, Guid("84bccd23-5fde-4cdb-aea4-af64b83d78ab"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IFileSaveDialog : IFileDialog {
//IModalWindow
[PreserveSig] new int Show(wnd hwndOwner);
//IFileDialog
new void SetFileTypes(int cFileTypes, [MarshalAs(UnmanagedType.LPArray)][In] string[] rgFilterSpec);
new void SetFileTypeIndex(int iFileType);
new int GetFileTypeIndex();
[PreserveSig] new int Advise(/*IFileDialogEvents pfde, out uint pdwCookie*/);
[PreserveSig] new int Unadvise(uint dwCookie);
new void SetOptions(_Api.FOS fos);
new _Api.FOS GetOptions();
new void SetDefaultFolder(IShellItem psi);
new void SetFolder(IShellItem psi);
new IShellItem GetFolder();
new IShellItem GetCurrentSelection();
new void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);
[return: MarshalAs(UnmanagedType.LPWStr)] new string GetFileName();
new void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
new void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);
new void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
new IShellItem GetResult();
[PreserveSig] new int AddPlace(/*IShellItem psi, FDAP fdap*/);
new void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
[PreserveSig] new int Close(int hr);
new void SetClientGuid(in Guid guid);
new void ClearClientData();
[PreserveSig] new int SetFilter(/*IShellItemFilter pFilter*/);
//IFileSaveDialog
void SetSaveAsItem(IShellItem psi);
[PreserveSig] int SetProperties(/*IPropertyStore pStore*/);
[PreserveSig] int SetCollectedProperties(/*IPropertyDescriptionList pList, [MarshalAs(UnmanagedType.Bool)] bool fAppendDefault*/);
[PreserveSig] int GetProperties(/*out IPropertyStore ppStore*/);
[PreserveSig] int ApplyProperties(/*IShellItem psi, IPropertyStore pStore, wnd hwnd, IFileOperationProgressSink pSink*/);
}
[ComImport, Guid("42f85136-db7e-439c-85f1-e4075d135fc8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IFileDialog {
//IModalWindow
[PreserveSig] int Show(wnd hwndOwner);
//IFileDialog
void SetFileTypes(int cFileTypes, [MarshalAs(UnmanagedType.LPArray)][In] string[] rgFilterSpec);
void SetFileTypeIndex(int iFileType);
int GetFileTypeIndex();
[PreserveSig] int Advise(/*IFileDialogEvents pfde, out uint pdwCookie*/);
[PreserveSig] int Unadvise(uint dwCookie);
void SetOptions(_Api.FOS fos);
_Api.FOS GetOptions();
void SetDefaultFolder(IShellItem psi);
void SetFolder(IShellItem psi);
IShellItem GetFolder();
IShellItem GetCurrentSelection();
void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);
[return: MarshalAs(UnmanagedType.LPWStr)] string GetFileName();
void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);
void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
IShellItem GetResult();
[PreserveSig] int AddPlace(/*IShellItem psi, FDAP fdap*/);
void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
[PreserveSig] int Close(int hr);
void SetClientGuid(in Guid guid);
void ClearClientData();
[PreserveSig] int SetFilter(/*IShellItemFilter pFilter*/);
}
[ComImport, Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItem {
[PreserveSig] int BindToHandler(IntPtr pbc, in Guid bhid, in Guid riid, void** ppv);
[PreserveSig] int GetParent(out IShellItem ppsi);
[return: MarshalAs(UnmanagedType.LPWStr)] string GetDisplayName(SIGDN sigdnName);
uint GetAttributes(uint sfgaoMask);
[PreserveSig] int Compare(IShellItem psi, uint hint, out int piOrder);
}
[ComImport, Guid("b63ea76d-1f85-456f-a19c-48159efa858b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItemArray {
[PreserveSig] int BindToHandler(/*IntPtr pbc, in Guid bhid, in Guid riid, void** ppvOut*/);
[PreserveSig] int GetPropertyStore(/*GETPROPERTYSTOREFLAGS flags, in Guid riid, void** ppv*/);
[PreserveSig] int GetPropertyDescriptionList(/*in PROPERTYKEY keyType, in Guid riid, void** ppv*/);
[PreserveSig] int GetAttributes(/*SIATTRIBFLAGS AttribFlags, uint sfgaoMask, out uint psfgaoAttribs*/);
int GetCount();
IShellItem GetItemAt(int dwIndex);
[PreserveSig] int EnumItems(/*out IEnumShellItems ppenumShellItems*/);
}
[Flags]
internal enum FOS : uint {
FOS_OVERWRITEPROMPT = 0x2,
FOS_STRICTFILETYPES = 0x4,
FOS_NOCHANGEDIR = 0x8,
FOS_PICKFOLDERS = 0x20,
FOS_FORCEFILESYSTEM = 0x40,
FOS_ALLNONSTORAGEITEMS = 0x80,
FOS_NOVALIDATE = 0x100,
FOS_ALLOWMULTISELECT = 0x200,
FOS_PATHMUSTEXIST = 0x800,
FOS_FILEMUSTEXIST = 0x1000,
FOS_CREATEPROMPT = 0x2000,
FOS_SHAREAWARE = 0x4000,
FOS_NOREADONLYRETURN = 0x8000,
FOS_NOTESTFILECREATE = 0x10000,
FOS_HIDEMRUPLACES = 0x20000,
FOS_HIDEPINNEDPLACES = 0x40000,
FOS_NODEREFERENCELINKS = 0x100000,
FOS_OKBUTTONNEEDSINTERACTION = 0x200000,
FOS_DONTADDTORECENT = 0x2000000,
FOS_FORCESHOWHIDDEN = 0x10000000,
FOS_DEFAULTNOMINIMODE = 0x20000000,
FOS_FORCEPREVIEWPANEON = 0x40000000,
FOS_SUPPORTSTREAMABLEITEMS = 0x80000000
}
[DllImport("shell32.dll", PreserveSig = true)]
internal static extern int SHCreateItemFromParsingName(string pszPath, IntPtr pbc, in Guid riid, out IShellItem ppv);
[DllImport("shell32.dll", PreserveSig = true)]
internal static extern int SHCreateItemFromIDList(IntPtr pidl, in Guid riid, out IShellItem ppv);
}
}
}
namespace Au.Types {
///
/// .
///
[Flags]
public enum FOSFlags : uint //the values are like in FOS, some xored
{
/// Add the selected file to recent documents. See API SHAddToRecentDocs.
AddToRecent = 0x2000000,
/// Do not check for situations that would prevent an application from opening the selected file, such as sharing violations or access denied errors.
NoValidateAccess = 0x100,
///// The user can enter a path that does not exist, like C:\does not exist\file.txt.
//NoValidatePath = 0x800, //does not work. Always validates.
/// Shortcuts should not be treated as their target items, allowing an application to open a .lnk file.
NoDereferenceLinks = 0x100000,
/// Show hidden and system items.
ShowHidden = 0x10000000,
}
}
================================================
FILE: Au/Au.More/GdiTextRenderer.cs
================================================
//FUTURE: support columns. Maybe use \t of specified widths.
namespace Au.More;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
///
/// Draws text using fastest GDI API such as TextOut and standard UI font.
/// Can easily draw string parts with different colors/styles without measuring.
/// Must be disposed.
///
public unsafe class GdiTextRenderer : IDisposable {
IntPtr _dc, _oldFont;
uint _oldAlign;
int _color, _oldColor;
int _dpi;
bool _releaseDC;
/// Object created with this ctor can draw and measure.
/// Device context handle. Dispose will not release it.
///
public GdiTextRenderer(IntPtr hdc, int dpi) {
_dpi = dpi;
_dc = hdc;
_oldFont = Api.SelectObject(_dc, NativeFont_.RegularCached(_dpi));
_oldAlign = 0xffffffff;
Api.SetBkMode(_dc, 1);
_oldColor = Api.SetTextColor(_dc, _color = 0);
}
/// Object created with this ctor can measure only. Uses screen DC.
public GdiTextRenderer(int dpi) {
_dpi = dpi;
_releaseDC = true;
_dc = Api.GetDC(default);
_oldFont = Api.SelectObject(_dc, NativeFont_.RegularCached(_dpi));
}
public void Dispose() {
if (_dc != default) {
Api.SelectObject(_dc, _oldFont);
if (_releaseDC) Api.ReleaseDC(default, _dc);
else {
if (_oldAlign != 0xffffffff) Api.SetTextAlign(_dc, _oldAlign);
if (_oldColor != _color) Api.SetTextColor(_dc, _oldColor);
}
_dc = default;
}
//never mind: we don't restore the old current position. Nobody later would draw at current position without movetoex. As well as bkmode.
}
///
/// Sets the current drawing position of the DC.
/// Returns previous position.
///
public POINT MoveTo(int x, int y) {
Api.MoveToEx(_dc, x, y, out var p);
return p;
}
///
/// Gets the current drawing position of the DC.
///
public POINT GetCurrentPosition() {
Api.GetCurrentPositionEx(_dc, out var p);
return p;
}
///
/// Sets non-bold font.
///
public void FontNormal() => Api.SelectObject(_dc, NativeFont_.RegularCached(_dpi));
///
/// Sets bold font.
///
public void FontBold() => Api.SelectObject(_dc, NativeFont_.BoldCached(_dpi));
//public void FontItalic() => Api.SelectObject(_dc, NativeFont_.ItalicCached(_dpi));
//public void FontBoldItalic() => Api.SelectObject(_dc, NativeFont_.BoldItalicCached(_dpi));
///
/// Draws text at the current drawing position of the DC, and updates it.
///
/// Text color 0xBBGGRR.
/// Background color 0xBBGGRR. Transparent if null.
public void DrawText(string s, int color = 0, Range? range = null, int? backColor = null) {
var (from, len) = range.GetOffsetAndLength(s.Lenn()); if (len == 0) return;
if (_oldAlign == 0xffffffff) _oldAlign = Api.SetTextAlign(_dc, 1); //TA_UPDATECP
_DrawText(s, 0, 0, color, from, len, backColor);
}
///
/// Draws text at specified position. Does not use/update the current drawing position of the DC.
///
/// Text color 0xBBGGRR.
/// Background color 0xBBGGRR. Transparent if null.
public void DrawText(string s, POINT p, int color = 0, Range? range = null, int? backColor = null) {
var (from, len) = range.GetOffsetAndLength(s.Lenn()); if (len == 0) return;
if (_oldAlign != 0xffffffff) { Api.SetTextAlign(_dc, _oldAlign); _oldAlign = 0xffffffff; }
_DrawText(s, p.x, p.y, color, from, len, backColor);
}
///
/// Draws text clipped in specified rectangle. Does not use/update the current drawing position of the DC.
///
/// Text color 0xBBGGRR.
/// Background color 0xBBGGRR. Transparent if null.
public void DrawText(string s, in RECT r, int color = 0, Range? range = null, int? backColor = null) {
var (from, len) = range.GetOffsetAndLength(s.Lenn()); if (len == 0) return;
if (_oldAlign != 0xffffffff) { Api.SetTextAlign(_dc, _oldAlign); _oldAlign = 0xffffffff; }
_DrawText(s, r, color, from, len, backColor);
}
//[Ext]TextOut fails if >= 1024 * 64.
// On Win7 and 8.1 depends on text width (ushort.MaxValue?). Eg fails if > ~2900 'W'.
// ExtTextOut doc: "This value may not exceed 8192.".
static int _LimitLength(int len) => Math.Min(len, osVersion.minWin10 ? 1024 * 64 - 1 : 4000);
unsafe void _DrawText(string s, int x, int y, int color, int from, int len, int? backColor) {
if (color != _color) Api.SetTextColor(_dc, _color = color);
using var bc = new _BackColor(_dc, backColor);
fixed (char* p = s) Api.TextOut(_dc, x, y, p + from, _LimitLength(len));
}
unsafe void _DrawText(string s, in RECT r, int color, int from, int len, int? backColor) {
if (color != _color) Api.SetTextColor(_dc, _color = color);
using var bc = new _BackColor(_dc, backColor);
fixed (char* p = s) Api.ExtTextOut(_dc, r.left, r.top, Api.ETO_CLIPPED, r, p + from, _LimitLength(len));
}
public SIZE MeasureText(string s, Range? range = null) {
var (from, len) = range.GetOffsetAndLength(s.Lenn()); if (len == 0) return default;
fixed (char* p = s) {
Api.GetTextExtentPoint32(_dc, p + from, _LimitLength(len), out var z);
return z;
}
}
ref struct _BackColor {
int _oldColor, _oldMode;
IntPtr _dc;
public _BackColor(IntPtr dc, int? color) {
if (color != null) {
_dc = dc;
_oldMode = Api.SetBkMode(_dc, 2);
_oldColor = Api.SetBkColor(_dc, color.Value);
}
}
public void Dispose() {
if (_dc != default) {
Api.SetBkColor(_dc, _oldColor);
Api.SetBkMode(_dc, _oldMode);
}
}
}
}
================================================
FILE: Au/Au.More/Hash.cs
================================================
using System.Security.Cryptography;
using System.Buffers.Text;
namespace Au.More;
///
/// Data hash functions.
///
[EditorBrowsable(EditorBrowsableState.Never)] //obsolete as public, but still used internally. Created before the easy and fast .NET hash classes existed.
public static unsafe class Hash {
#region FNV1
///
/// 32-bit FNV-1 hash.
/// Useful for fast hash table and checksum use, not cryptography. Similar to CRC32; faster but creates more collisions.
///
public static int Fnv1(RStr data) {
if (data.IsNull()) return 0;
uint hash = 2166136261;
for (int i = 0; i < data.Length; i++)
hash = (hash * 16777619) ^ data[i];
return (int)hash;
}
///
public static int Fnv1(char* data, int lengthChars) => Fnv1(new RStr(data, lengthChars));
/// Data. See also: , .
///
public static int Fnv1(RByte data) {
if (data.IsNull()) return 0;
uint hash = 2166136261;
for (int i = 0; i < data.Length; i++)
hash = (hash * 16777619) ^ data[i];
return (int)hash;
}
///
public static int Fnv1(byte* data, int lengthBytes) => Fnv1(new RByte(data, lengthBytes));
///
public static int Fnv1(T data) where T : unmanaged
=> Fnv1((byte*)&data, sizeof(T));
///
/// 64-bit FNV-1 hash.
///
public static long Fnv1Long(RStr data) {
if (data.IsNull()) return 0;
ulong hash = 14695981039346656037;
for (int i = 0; i < data.Length; i++)
hash = (hash * 1099511628211) ^ data[i];
return (long)hash;
}
///
/// 64-bit FNV-1 hash.
///
public static long Fnv1Long(char* data, int lengthChars) => Fnv1(new RStr(data, lengthChars));
///
/// 64-bit FNV-1 hash.
///
/// Data. See also: , .
public static long Fnv1Long(RByte data) {
if (data.IsNull()) return 0;
ulong hash = 14695981039346656037;
for (int i = 0; i < data.Length; i++)
hash = (hash * 1099511628211) ^ data[i];
return (long)hash;
}
///
/// 64-bit FNV-1 hash.
///
public static long Fnv1Long(byte* data, int lengthBytes) => Fnv1(new RByte(data, lengthBytes));
///
/// 64-bit FNV-1 hash.
///
public static long Fnv1Long(T data) where T : unmanaged
=> Fnv1Long((byte*)&data, sizeof(T));
///
/// FNV-1 hash, modified to make faster with long strings (then takes every n-th character).
///
[Obsolete] //unused
public static int Fast(char* data, int lengthChars) {
if (data == null) return 0;
//Also we take the last 1-2 characters (in the second loop), because often there are several strings like Chrome_WidgetWin_0, Chrome_WidgetWin_1...
//Also we hash uints, not chars, unless the string is very short.
//Tested speed with 400 unique strings (window/control names/classnames/programnames). The time was 7 mcs. For single call 17 ns.
uint hash = 2166136261;
int i = 0;
if (lengthChars > 8) {
int lc = lengthChars--;
lengthChars /= 2; //we'll has uints, not chars
int every = lengthChars / 8 + 1;
for (; i < lengthChars; i += every)
hash = (hash * 16777619) ^ ((uint*)data)[i];
i = lengthChars * 2;
lengthChars = lc;
}
for (; i < lengthChars; i++)
hash = (hash * 16777619) ^ data[i];
return (int)hash;
}
///
/// FNV-1 hash, modified to make faster with long strings (then takes every n-th character).
///
/// String. Can be null.
[Obsolete] //unused
public static int Fast(RStr s) {
fixed (char* p = s) return Fast(p, s.Length);
}
#endregion
#region MD5
///
/// Computes MD5 hash of data.
/// Call an method one or more times. Finally use to get result.
///
[StructLayout(LayoutKind.Explicit)]
public struct MD5Context { //MD5_CTX + _state
[FieldOffset(88)] MD5Result _result;
[FieldOffset(104)] long _state; //1 inited/added, 2 finalled
///
/// true if no data was added.
///
public bool IsEmpty => _state == 0;
/// Adds data.
/// size < 0.
/// data is null and size > 0.
public void Add(void* data, int size) {
if (size < 0) throw new ArgumentOutOfRangeException();
if (size > 0) Not_.Null(data); //allow null if size 0. Eg 'fixed' gets null pointer if the span or array is empty.
if (_state != 1) { Api.MD5Init(out this); _state = 1; }
if (size > 0) Api.MD5Update(ref this, data, size);
}
/// Adds data.
public void Add(T data) where T : unmanaged
=> Add(&data, sizeof(T));
/// Adds data.
/// Data. See also: , .
public void Add(RByte data) {
fixed (byte* p = data) Add(p, data.Length); //note: p null if data empty
}
/// Adds string converted to UTF-8.
/// data is null.
public void Add(string data) => Add(Encoding.UTF8.GetBytes(data));
//CONSIDER: alloc on stack to avoid garbage. This func works, but not faster.
//[SkipLocalsInit]
//public void Add2(string data) {
// if (data.Length < 3000) {
// Span p = stackalloc byte[data.Length * 3];
// int n = Encoding.UTF8.GetBytes(data, p);
// //print.it(n);
// Add(p[..n]);
// } else {
// Add(Encoding.UTF8.GetBytes(data));
// }
//}
//rejected. Better use unsafe address, then will not need to copy data.
///// Adds data.
//public void Add(T data) where T: unmanaged
//{
// Add(&data, sizeof(T));
//}
///
/// Computes final hash of datas added with an method.
///
/// Add was not called.
///
/// Resets state, so that if Add called again, it will start adding new datas.
///
public MD5Result Hash {
get {
if (_state != 2) {
if (_state != 1) throw new InvalidOperationException();
Api.MD5Final(ref this);
_state = 2;
}
return _result;
}
}
}
///
/// Result of .
/// It is 16 bytes stored in 2 long fields r1 and r2.
/// If need, can be converted to byte[] with or to hex string with .
///
public record struct MD5Result {
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public readonly long r1, r2;
public MD5Result(long r1, long r2) { this.r1 = r1; this.r2 = r2; }
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
///
/// Converts this to byte[] of length 16.
///
public byte[] ToArray() {
var r = new byte[16];
fixed (byte* p = r) {
*(long*)p = r1;
*(long*)(p + 8) = r2;
}
return r;
}
///
/// Converts this to hex string.
///
public override string ToString() => Convert2.HexEncode(this);
///
/// Creates from hex string returned by .
///
/// false if encoded is invalid.
public static bool FromString(RStr encoded, out MD5Result r) => Convert2.HexDecode(encoded, out r);
#if NET9_0_OR_GREATER
///
/// Converts this to Base64url string (URL-safe Base64).
///
public string ToStringBase64Url() => Base64Url.EncodeToString(_AsSpan());
///
/// Creates from string returned by .
///
public static bool FromStringBase64Url(RStr encoded, out MD5Result r) {
r = default;
return Base64Url.TryDecodeFromChars(encoded, r._AsSpan(), out _);
}
Span _AsSpan() => MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this, 1));
#endif
}
///
/// Computes MD5 hash of data.
/// Uses .
///
/// Data. See also: , .
public static MD5Result MD5(RByte data) {
MD5Context md = default;
md.Add(data);
return md.Hash;
}
//rejected. Problems with overload resolution and implicit conversion Span to ReadOnlySpan. Not so often used. Then would need the same everywhere. Instead added doc.
/////
///// Computes MD5 hash of data.
///// Uses .
/////
//public static MD5Result MD5(ReadOnlySpan data) where T : unmanaged
// => MD5(MemoryMarshal.AsBytes(data));
///
/// Computes MD5 hash of string converted to UTF-8.
/// Uses .
///
public static MD5Result MD5(string data) {
MD5Context md = default;
md.Add(data);
return md.Hash;
}
///
/// Computes MD5 hash of data. Returns result as hex or base64 string.
/// Uses .
///
/// Data. See also: , .
///
public static string MD5(RByte data, bool base64) {
var h = MD5(data);
return base64 ? Convert.ToBase64String(new RByte((byte*)&h, 16)) : h.ToString();
}
/////
///// Computes MD5 hash of data. Returns result as hex or base64 string.
///// Uses .
/////
//public static string MD5(ReadOnlySpan data, bool base64) where T : unmanaged
// => MD5(MemoryMarshal.AsBytes(data), base64);
///
/// Computes MD5 hash of string converted to UTF-8. Returns result as hex or base64 string.
/// Uses .
///
public static string MD5(string data, bool base64) {
var h = MD5(data);
return base64 ? Convert.ToBase64String(new RByte((byte*)&h, 16)) : h.ToString();
}
#endregion
#region other
///
/// Computes data hash using the specified cryptographic algorithm.
///
/// Data. See also: , .
/// Algorithm name, eg "SHA256". See .
public static byte[] Crypto(RByte data, string algorithm) {
using var x = (HashAlgorithm)CryptoConfig.CreateFromName(algorithm);
var r = new byte[x.HashSize / 8];
x.TryComputeHash(data, r, out _);
return r;
}
///
/// Computes hash of string converted to UTF-8, using the specified cryptographic algorithm.
///
///
/// Algorithm name, eg "SHA256". See .
public static byte[] Crypto(string data, string algorithm)
=> Crypto(Encoding.UTF8.GetBytes(data), algorithm);
///
/// Computes data hash using the specified cryptographic algorithm. Returns result as hex or base64 string.
///
/// Data. See also: , .
/// Algorithm name, eg "SHA256". See .
///
public static string Crypto(RByte data, string algorithm, bool base64) {
var b = Crypto(data, algorithm);
return base64 ? Convert.ToBase64String(b) : Convert2.HexEncode(b);
}
///
/// Computes hash of string converted to UTF-8, using the specified cryptographic algorithm. Returns result as hex or base64 string.
///
///
/// Algorithm name, eg "SHA256". See .
///
public static string Crypto(string data, string algorithm, bool base64) {
var b = Crypto(data, algorithm);
return base64 ? Convert.ToBase64String(b) : Convert2.HexEncode(b);
}
#endregion
}
================================================
FILE: Au/Au.More/HelpUtil.cs
================================================
namespace Au.More;
///
/// Static functions to open a help topic etc.
///
public static class HelpUtil {
///
/// Opens a LibreAutomate help topic.
///
/// Topic file name, like "wnd.find" or "Au.Types.RECT" or "articles/Wildcard expression".
public static void AuHelp(string topic) {
#pragma warning disable CS0612 //Type or member is obsolete
var url = AuHelpUrl(topic);
#pragma warning restore CS0612 //Type or member is obsolete
if (AuHelpEvent_ is { } e) {
var k = new AuHelpEventArgs_ { Url = url };
e(k);
if (k.Cancel) return;
url = k.Url;
}
run.itSafe(url);
}
///
/// Gets URL of a LibreAutomate help topic.
///
/// Topic file name, like "wnd.find" or "Au.Types.RECT" or "articles/Wildcard expression".
public static string AuHelpUrl(string topic) {
string fragment = null;
int i = topic.IndexOf('#');
if (i >= 0) {
fragment = topic[i..];
topic = topic[..i];
}
if (topic.Ends(".this[]")) topic = topic.ReplaceAt(^7.., ".Item");
else if (topic.Ends(".this")) topic = topic.ReplaceAt(^5.., ".Item");
else if (topic.Ends("[]")) topic = topic.ReplaceAt(^2.., ".Item");
else if (topic.Contains("timer")) topic = topic.RxReplace(@"\btimer2?\.(after|every)\b\K", "_1"); //the filename has this suffix because of the instance method After/Every
var url = AuHelpBaseUrl;
if (!url.Ends('/')) url += "/";
if (!topic.NE()) url = url
+ (topic.Contains('/') ? null : (topic.Starts("Au.") ? "api/" : "api/Au."))
+ topic
+ (topic.Ends(".html") || topic.Ends('/') ? null : ".html")
+ fragment;
return url;
}
///
/// s.Starts(false, "Au.", "articles/", "editor/", "cookbook/", "api/") > 0
///
internal static bool IsAuHelp_(string s) => s.Starts(false, "Au.", "articles/", "editor/", "cookbook/", "api/") > 0;
///
/// URL of the LibreAutomate documentation website: "https://www.libreautomate.com/".
///
public static string AuHelpBaseUrl => "https://www.libreautomate.com/";
internal static event Action AuHelpEvent_;
internal class AuHelpEventArgs_ : CancelEventArgs {
public string Url { get; set; }
}
}
================================================
FILE: Au/Au.More/HttpServerSession.cs
================================================
using System.Net;
using System.Net.Sockets;
using System.Text.Json;
namespace Au.More {
///
/// Simple HTTP 1.1 server. Can be used for communicating between two scripts or apps on local network, internet or same computer. Also can receive requests from web browsers etc.
///
///
/// To receive HTTP messages, create a class with base HttpServerSession and add function . Example in .
///
public abstract class HttpServerSession {
///
/// Simple HTTP 1.1 server.
///
/// TCP port. Default 4455. If 0, uses the first available port in the dynamic/private ports range (49152-65535); you can use the started callback to discover it.
/// A local IPv4 or IPv6 address, like "127.0.0.1" or "[::1]". If null (default), uses and dual mode (supports IPv6 and IPv4 connections).
/// Called when the server started (after ).
/// Exceptions of functions. Unlikely.
///
/// Runs all the time and listens for new TCP client connections. For each connected client starts new thread, creates new object of your -based type, and calls , which calls . Supports keep-alive. Multiple sessions can run simultaneously.
///
/// Uses , not , therefore don't need administrator privileges, netsh and opening ports in firewall. Just the standard firewall dialog first time.
///
/// The HTTP server is accessible from local network computers. Usually not accessible from the internet. To make accessible from the internet, you can use ngrok or similar software. This server does not support https (secure connections), but ngrok makes internet connections secure.
///
/// The Windows Firewall shows a dialog when the app uses a HTTP server the first time, unless ip is "127.0.0.1".
///
///
/// HTTP server.
/// ();
///
/// class MyHttpSession : HttpServerSession {
/// //protected override void Run() {
/// // print.it($"Session started. Client IP: {ClientIP}");
/// // base.Run();
/// // print.it("Session ended");
/// //}
///
/// protected override void MessageReceived(HSMessage m, HSResponse r) {
/// //Auth((u, p) => p == "password206474");
///
/// print.it(m.Method, m.TargetPath, m.UrlParameters, m.Headers, m.ContentText);
///
/// string response = "RESPONSE";
/// r.SetContent(response);
/// //or
/// //r.Content = response.ToUTF8();
/// //r.Headers.Add("Content-Type", "text/plain; charset=utf-8");
/// }
/// }
/// ]]>
/// HTTP client.
///
///
public static void Listen(int port = 4455, string ip = null, Action started = null) where TSession : HttpServerSession, new() {
var server = ip == null
? TcpListener.Create(port) //IPAddress.IPv6Any, dual mode (supports IPv6 and IPv4 connections); shows Firewall popup.
: new(IPAddress.Parse(ip), port); //no Firewall popup if "127.0.0.1" or "[::1]", but then no dual mode; `server.Server.DualMode=true` fails.
server.Start();
started?.Invoke(server);
try {
for (; ; ) {
var client = server.AcceptTcpClient();
run.thread(() => {
new TSession()._Run(client);
Interlocked.Decrement(ref s_nThreads);
}).Name = "Au.HttpServerSession";
Interlocked.Increment(ref s_nThreads);
while (s_nThreads >= (osVersion.is32BitProcess ? 200 : 2000)) 200.ms();
}
}
finally {
server.Stop();
}
}
static int s_nThreads;
TcpClient _client;
NetworkStream _ns;
HSMessage _message; //current message
///
/// Gets the object of this session.
///
protected TcpClient Client => _client;
///
/// Gets the IP address of the client.
///
protected IPAddress ClientIP => ((System.Net.IPEndPoint)_client.Client.RemoteEndPoint).Address;
///
/// Print warning when something fails. This is for debug.
///
protected bool Verbose { get; set; }
///
/// Keep-alive timeout, in milliseconds. Default 10000.
///
protected int KeepAliveTimeout { get; set; } = 10_000;
///
/// Called when the server receives a HTTP request message.
///
/// Contains request info and content.
/// Allows to set response info and content.
///
/// Not called if failed to read the message.
///
/// The server uses try/catch when calling this. Prints unhandled exceptions if true. On unhandled exception sends error 500 (InternalServerError) and closes the connection.
///
protected abstract void MessageReceived(HSMessage m, HSResponse r);
///
/// Performs basic authentication. If fails (either the client did not use basic authentication or auth returned false), throws exception. The client will receive error 401 (Unauthorized) and can retry.
///
/// Callback function. Receives the user name and password. Returns true to continue or false to fail.
///
/// After successful authentication does not repeat it again when the client sends more messages in this session.
///
///
protected void Auth(Func auth) {
if (_auth != true) {
_auth = _message.Headers.TryGetValue("Authorization", out var s)
&& s.Split2_(' ', out var s1, out var s2, 5, 1)
&& s1.Eqi("Basic")
&& Convert.FromBase64String(s2).ToStringUTF8().Split2_(':', out s1, out s2, 0, 0)
&& auth(s1, s2);
if (_auth == false) throw new UnauthorizedAccessException();
}
}
bool? _auth;
void _Run(TcpClient client) {
_client = client;
_client.ReceiveTimeout = 30_000; //tested: throws when the socked receives 0 bytes in this time. It's not the total time of a Read.
_client.SendTimeout = 30_000; //it seems this is how long the socket waits until previous chunk is sent. We send in max 64 KB chunks.
try { Run(); }
catch (Exception e1) {
if (Verbose) print.warning(e1, "HttpServerSession: ");
}
_client.Close();
}
///
/// Executes the session: reads requests, calls your , writes responses, implements keep-alive.
///
///
/// The server uses try/catch when calling this. Prints unhandled exceptions if true. On unhandled exception closes the connection.
///
[SkipLocalsInit]
protected virtual void Run() {
_ns = _client.GetStream();
g1:
HttpStatusCode status;
try {
status = _ReadRequest();
}
catch (HttpReadException_ eh) {
status = eh.status;
}
if (status == HttpReader_.Disconnected) return;
HSResponse response = new() { Status = status };
if (status == HttpStatusCode.OK) {
try { MessageReceived(_message, response); }
catch (UnauthorizedAccessException) when (_auth == false) {
_auth = null;
response.Status = HttpStatusCode.Unauthorized;
response.Headers["WWW-Authenticate"] = "Basic";
}
catch (Exception e1) {
if (Verbose) print.warning(e1, "HttpServerSession: ");
if (response.Status == HttpStatusCode.OK) response.Status = HttpStatusCode.InternalServerError;
}
}
bool keepAlive = KeepAliveTimeout > 0 && status == HttpStatusCode.OK && !(_message.Headers.TryGetValue("Connection", out var v1) && v1.Eqi("close"));
if (!keepAlive) response.Headers["Connection"] = "close";
_WriteResponse(response);
_message = null;
if (keepAlive) {
if (_ns.Socket.Poll(Math.Min(KeepAliveTimeout, int.MaxValue / 1000) * 1000, SelectMode.SelectRead) && _ns.DataAvailable) goto g1; //else timeout or closed or error
} else if (status != HttpStatusCode.OK) {
//Cannot close until the client writes all data and reads the response. Else instead of response it may receive error 'connection reset'.
// On error often not all request bytes are read. And the size of this message is unknown.
// Read all remaining request bytes until the client closes connection.
// Client should close when it reads the response, because: 1. It's an error. 2. We send 'Connection: close'.
// tested: _ns.Socket.LingerState does not work.
// not tested (because this code works well): _ns.Close(timeout);.
_client.ReceiveTimeout = 10_000; //if client never closes, we'll get timeout exception
Span b = stackalloc byte[0x4000];
while (_ns.Read(b) != 0) { }
}
}
void _WriteResponse(HSResponse r) {
r.Headers.TryAdd("Date", DateTime.UtcNow.ToString("R"));
r.Headers.TryAdd("Server", "Au");
byte[] content = r.Content;
bool hasContent = !content.NE_();
if (hasContent) {
//r.Headers.TryAdd("Content-Type", "Content-Type: text/plain; charset=utf-8"); //no. Server cannot guess. The HTML default is application/octet-stream.
//compress
if (content.Length > 1000 && _message.Headers.TryGetValue("Accept-Encoding", out var ae) && !r.Headers.ContainsKey("Content-Encoding")) {
var h = ae.Lower().Split(new char[] { ',', ';' }, StringSplitOptions.TrimEntries); //use ';' to split items like "br;q=1.0", and ignore q
var (content2, s2) =
h.Contains("br") ? (Convert2.BrotliCompress(content), "br")
: h.Contains("gzip") ? (Convert2.GzipCompress(content), "gzip")
: h.Contains("deflate") ? (Convert2.DeflateCompress(content), "deflate")
: (content, null);
if (content2.Length < content.Length) {
content = content2;
r.Headers["Content-Encoding"] = s2;
}
}
}
r.Headers["Content-Length"] = content.Lenn_().ToS(); //if no content, set 0, else client may wait even if there is empty line
int bs = 50 + r.Reason.Lenn() + r.Headers.Sum(o => o.Key.Length + o.Value.Length + 4);
using (var w = new StreamWriter(_ns, Encoding.Latin1, bs, leaveOpen: true)) {
w.Write($"HTTP/1.1 {(int)r.Status} {r.Status}");
if (!r.Reason.NE()) w.Write(", " + r.Reason);
w.WriteLine();
foreach (var (k, v) in r.Headers) w.WriteLine(k + ": " + v);
w.WriteLine();
}
if (hasContent) {
//perf.first();
//_ns.Write(content); //does not wait until client reads all data. Usually returns after ~1 ms regardless of size etc.
for (int i = 0; i < content.Length;) {
int n = Math.Min(0x10000, content.Length - i);
_ns.Write(content, i, n); //waits if previous buffer still not sent
i += n;
}
//perf.nw();
}
}
[SkipLocalsInit]
unsafe HttpStatusCode _ReadRequest() {
_message = new();
HttpReader_ reader = new(_ns);
//read the request line
{
string s; do s = reader.ReadLine(); while (s.Length == 0);
if (!s.RxMatch(@"^(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH) (.+) HTTP/(\d\.\d)$", out RXMatch m)) return HttpStatusCode.BadRequest;
if (m[3].Value is not ("1.1" or "1.0")) return HttpStatusCode.HttpVersionNotSupported;
_message.Method = m[1].Value;
_message.RawTarget = s = m[2].Value;
int i = s.IndexOf('?');
if (i >= 0) {
_message.UrlParameters = HSMessage.ParseUrlParameters_(s.AsSpan(i + 1));
s = s[..i];
}
_message.TargetPath = WebUtility.UrlDecode(s);
}
//read headers
var headers = _message.Headers;
reader.ReadHeaders(headers);
//read content
_message.Content = reader.ReadContent(headers);
//print.it("Target", message.RawTarget, message.TargetPath, message.UrlParameters);
//print.it("Headers", message.Headers);
//print.it("Content", message.ContentText);
return HttpStatusCode.OK;
}
}
///
/// Reads an HTTP request or response message.
/// All functions may throw and exceptions of NetworkStream.Read.
/// The class was designed to read request, therefore throws exceptions such as BadRequest, but can be used to read response too.
/// Uses 16 KB of stack. Consider [SkipLocalsInit].
///
unsafe ref struct HttpReader_ {
readonly NetworkStream _ns;
Span _b, _line;
int _n; //current buffered bytes
int _pos; //current position in _b (0.._n)
const int c_bsize = 0x4000; //16 K
fixed byte __b[c_bsize];
public const HttpStatusCode Disconnected = 0;
public HttpReader_(NetworkStream ns) {
_ns = ns;
fixed (byte* p = __b) _b = new(p, c_bsize);
}
void _Buffer() {
if (_n > 0) {
_b = _b.Slice(_n);
if (_b.IsEmpty) {
if (_line.Length == c_bsize) throw new HttpReadException_(HttpStatusCode.RequestHeaderFieldsTooLarge);
fixed (byte* p = __b) _b = new(p, c_bsize);
_line.CopyTo(_b);
int ll = _line.Length;
_line = _b;
_b = _b.Slice(ll);
}
_pos = 0;
}
_n = _ns.Read(_b);
if (_n == 0) throw new HttpReadException_(Disconnected);
}
//note: would be simpler with _ns.ReadByte(), but it is slow. Therefore we _ns.Read() chunks into __b, and then read bytes from there.
int _ReadByte() {
if (_pos == _n) _Buffer();
return _b[_pos++];
}
public string ReadLine() {
_line = _b.Slice(_pos);
for (int len = 0; ; len++) {
int c = _ReadByte();
if (c is 10 or 13) {
_ReadRN(c);
return Encoding.Latin1.GetString(_line.Slice(0, len));
}
}
}
void _ReadRN(int c) {
if (c == 10) return;
if (c == 13 && _ReadByte() == 10) return;
throw new HttpReadException_(HttpStatusCode.BadRequest);
}
public void ReadRN() {
_line = _b.Slice(_pos);
_ReadRN(_ReadByte());
}
public byte[] Read(int length) {
var a = GC.AllocateUninitializedArray(length);
Span span = a;
//at first read from _b
int n = _n - _pos;
if (n > 0) {
n = Math.Min(n, length);
_b.Slice(_pos, n).CopyTo(a);
_pos += n;
span = span.Slice(n);
}
//then read from _ns, if did not read all from _b
while (!span.IsEmpty) {
n = _ns.Read(span);
if (n == 0) throw new HttpReadException_(Disconnected);
span = span.Slice(n);
}
return a;
}
///
/// Reads headers until empty line.
///
/// The function adds headers here.
public void ReadHeaders(Dictionary headers) {
string header = null;
for (; ; ) {
var s = ReadLine();
if (s.Length == 0) break;
if (s[0] is ' ' or '\t') {
if (header == null) throw new HttpReadException_(HttpStatusCode.BadRequest);
header = string.Concat(header, " ", s.AsSpan(1));
} else {
_Header();
header = s;
}
}
_Header();
void _Header() {
if (header != null) {
int i = header.IndexOf(':'); if (i <= 0 || header[i - 1] <= ' ') throw new HttpReadException_(HttpStatusCode.BadRequest);
var k = header[..i];
var v = header.AsSpan(i + 1).Trim().ToString();
if (!headers.TryAdd(k, v)) headers[k] += ", " + v;
}
}
}
///
/// Reads content and trailing headers.
///
/// The function gets content length etc from here. Also reads trailing headers here.
/// null if headers don't contain content-length or transfer-encoding.
public byte[] ReadContent(Dictionary headers) {
byte[] content = null;
//get content info etc from headers
int contentLength = 0;
bool chunked = false, trailer = false;
foreach (var (k, v) in headers) {
if (k.Eqi("Content-Length")) contentLength = v.ToInt();
else if (k.Eqi("Transfer-Encoding")) {
if (v.Eq("chunked")) chunked = true;
else throw new HttpReadException_(HttpStatusCode.NotImplemented, k);
} else if (k.Eqi("Trailer")) trailer = true;
}
//read content
if (chunked || contentLength > 0) {
if (chunked) {
List chunks = new();
for (; ; ) {
var s = ReadLine();
if (!s.ToInt(out int len, flags: STIFlags.IsHexWithout0x | STIFlags.DontSkipSpaces)) throw new HttpReadException_(HttpStatusCode.BadRequest);
if (len == 0) break;
chunks.Add(Read(len));
ReadRN();
}
if (chunks.Count > 0) {
byte[] ac = chunks[0];
if (chunks.Count > 1) {
ac = new byte[chunks.Sum(o => o.Length)];
int i = 0;
foreach (var a in chunks) { a.CopyTo(ac, i); i += a.Length; }
}
content = ac;
}
if (trailer) { //headers after content
ReadHeaders(headers);
} else {
ReadRN();
}
} else {
content = Read(contentLength);
}
}
return content;
}
}
class HttpReadException_ : Exception {
public HttpReadException_(HttpStatusCode status, string message = null) : base(message) { this.status = status; }
public readonly HttpStatusCode status;
}
}
namespace Au.Types {
///
/// See .
///
public class HSMessage {
///
/// Method, like "GET" or "POST".
///
public string Method { get; internal set; }
///
/// Target, like "/file.html" or "/file.html?a=1&b=2" or "/". May be URL-encoded.
///
public string RawTarget { get; internal set; }
///
/// Target without URL parameters, like "/file.html" or "/". Not URL-encoded.
///
public string TargetPath { get; internal set; }
///
/// URL parameters (query string). Not URL-encoded.
///
/// null if there are no URL parameters.
public Dictionary UrlParameters { get; internal set; }
///
/// Headers. Case-insensitive.
///
public Dictionary Headers { get; } = new(StringComparer.OrdinalIgnoreCase);
///
/// Raw content. For example POST data as UTF-8 text or binary.
///
/// null if the message is without content.
public byte[] Content { get; internal set; }
///
/// Content-Type header info.
///
/// null if Content-Type header is missing or invalid.
public HSContentType ContentType => _contentType ??= HSContentType.Create(Headers);
HSContentType _contentType;
///
/// converted to text.
///
/// null if there is no content.
/// If text encoding unspecified, uses UTF-8; if specified invalid, uses ASCII.
public string Text => _contentText ??= Content == null ? null : (ContentType?.Encoding ?? Encoding.UTF8).GetString(Content);
string _contentText;
///
/// JSON-deserializes to object of type T.
///
/// default(T) if the request does not have body data.
/// Exceptions of .
public T Json() => Content == null ? default : JsonSerializer.Deserialize(Content, InternetUtil_.JsonSerializerOptions);
///
/// JSON-deserializes to object of specified type.
///
/// null if the request does not have body data.
/// Exceptions of .
public object Json(Type type) => Content == null ? default : JsonSerializer.Deserialize(Content, type, InternetUtil_.JsonSerializerOptions);
///
/// Keys/values from POST content with Content-Type: application/x-www-form-urlencoded.
///
/// null if the message has no content of this type.
public Dictionary Urlencoded {
get {
if (_contentUrlParameters == null && Content != null && Headers.TryGetValue("Content-Type", out var v) && v.Starts("application/x-www-form-urlencoded", true)) {
_contentUrlParameters = ParseUrlParameters_(Encoding.Latin1.GetString(Content));
}
return _contentUrlParameters;
}
}
Dictionary _contentUrlParameters;
internal static Dictionary ParseUrlParameters_(RStr s) {
Dictionary d = null;
for (int i = 0, j; i < s.Length; i = j + 1) {
int q = -1;
for (j = i; j < s.Length && s[j] != '&'; j++) if (s[j] == '=' && q < 0) q = j;
if (q > 0) (d ??= new())[WebUtility.UrlDecode(s.Slice(i, q - i).ToString())] = WebUtility.UrlDecode(s.Slice(++q, j - q).ToString());
}
return d;
}
///
/// Parts of multipart content. For example of POST content with Content-Type: multipart/form-data.
///
/// null if the message has no multipart content.
public Dictionary Multipart {
get {
if (_contentParts == null && Content != null && Headers.TryGetValue("Content-Type", out var v) && v.Starts("multipart/", true)) {
_contentParts = _GetContentMultipart();
}
return _contentParts;
}
}
Dictionary _contentParts;
Dictionary _GetContentMultipart() {
if (Content == null || ContentType?.Boundary is not string sb || Content.Length < sb.Length * 2 + 8) return null;
//print.it($"'{Content.ToStringUTF8()}'");
Dictionary a = null;
//need to parse bytes, not string, because part bodies can be binary or use various encodings
RByte k = Content, b = Encoding.Latin1.GetBytes("--" + sb), b0 = b.Slice(2);
if (!_FindBound(k, b, 0, out int startBound, out int endBound, out bool last)) return null;
for (int index = 0; !last;) {
int startPart = endBound;
if (!_FindBound(k, b, startPart, out startBound, out endBound, out last)) return null;
var part = k.Slice(startPart, startBound - startPart);
//print.it($"<<{part.ToStringUTF8()}>>");
int i = 0;
if (!part.StartsWith("\r\n"u8)) {
i = part.IndexOf("\r\n\r\n"u8) + 2; if (i < 2) continue;
}
var dh = new Dictionary(StringComparer.OrdinalIgnoreCase);
if (i != 0) {
var sh = Encoding.Latin1.GetString(part.Slice(0, i - 2)).RxReplace(@"\n\h", " ");
foreach (var v in sh.Lines(true)) {
if (v.Split2_(':', out var s1, out var s2, 1, 0)) dh[s1] = s2;
}
}
HSContentPart p = new(index++, dh, part.Slice(i + 2).ToArray());
(a ??= new())[p.Name] = p;
}
return a;
static bool _FindBound(RByte k, RByte b, int i, out int start, out int end, out bool last) {
start = end = 0; last = false;
for (; i < k.Length; i = end) {
int j = k.Slice(i).IndexOf(b);
if (j < 0) break;
start = i + j;
end = start + b.Length + 2;
if (end > k.Length) break;
if (start == 0 || (start >= 2 && k[start - 1] == '\n' && k[start - 2] == '\r')) {
if (start >= 2) start -= 2;
if (k[end - 1] == '\n' && k[end - 2] == '\r') return true;
if (k[end - 1] == '-' && k[end - 2] == '-') { last = true; return true; }
}
}
return false;
}
}
}
///
/// Contains a single part of a multipart POST data.
///
/// 0-based index of this part in the list of parts.
/// Headers of this part.
/// Raw content of this part. For example UTF-8 text.
public record class HSContentPart(int Index, Dictionary Headers, byte[] Content) {
///
public HSContentType ContentType => _contentType ??= HSContentType.Create(Headers);
HSContentType _contentType;
///
/// converted to text.
///
/// null if there is no content.
/// If text encoding unspecified, uses UTF-8; if specified invalid, uses ASCII.
public string Text => _contentText ??= Content == null ? null : (ContentType?.Encoding ?? Encoding.UTF8).GetString(Content);
string _contentText;
System.Net.Mime.ContentDisposition _ContentDisposition() {
if (_contentDisposition == null && Headers.TryGetValue("Content-Disposition", out var s)) try { _contentDisposition = new(s); } catch { }
return _contentDisposition;
}
System.Net.Mime.ContentDisposition _contentDisposition;
///
/// Gets name from Content-Disposition header.
///
/// If Content-Disposition header or name is missing, returns Index.ToS().
///
/// Decodes "=?utf-8?B?base64?=".
///
public string Name => _name ??= _DecodeMime(_ContentDisposition()?.Parameters["name"] ?? Index.ToS());
string _name;
///
/// Gets filename from Content-Disposition header.
///
/// null if Content-Disposition header or filename is missing.
///
/// Decodes "utf-8''urlencoded" or "=?utf-8?B?base64?=".
///
public string FileName {
get {
if (_fileName == null && _ContentDisposition() is { } cd) {
if (cd.Parameters["filename*"] is string s && s.Starts("utf-8''")) { //never mind: can be "any-charset'language'"
try { _fileName = WebUtility.UrlDecode(s[7..]); } catch { }
}
_fileName ??= _DecodeMime(cd.Parameters["filename"]);
}
return _fileName;
}
}
string _fileName;
static string _DecodeMime(string s) {
if (s != null && s.Starts("=?utf-8?B?", true) && s.Ends("?=")) {
try { return Convert.FromBase64String(s[10..^2]).ToStringUTF8(); } catch { }
}
return s;
}
}
///
/// Contains properties of HTTP Content-Type header.
/// See .
///
public class HSContentType {
///
/// Creates from Content-Type header.
///
/// null if Content-Type header is missing or invalid.
public static HSContentType Create(Dictionary headers) {
if (headers.TryGetValue("Content-Type", out var s)) {
try { return new(new(s)); }
catch { }
}
return null;
}
HSContentType(System.Net.Mime.ContentType t) {
MediaType = t.MediaType;
Boundary = t.Boundary;
Charset = t.CharSet;
Encoding = _GetEncoding(t);
}
///
public string MediaType { get; }
///
/// Returns the boundary parameter without double quotes, or null if not specified.
///
public string Boundary { get; }
///
/// Returns the charset parameter, or null if not specified.
///
public string Charset { get; }
///
/// Gets text encoding.
///
/// Returns:
///
• null if multipart content ( not null).
///
• UTF-8 if charset is utf-8 or not specified.
///
• Encoding that matches charset.
///
• ASCII if charset is invalid.
///
public Encoding Encoding { get; }
Encoding _GetEncoding(System.Net.Mime.ContentType t) {
var s = Charset;
if (s == null) return t.Boundary != null ? null : Encoding.UTF8;
if (s.Eqi("utf-8")) return Encoding.UTF8;
if (s.Eqi("us-ascii")) return Encoding.ASCII;
if (s.Eqi("iso-8859-1")) return Encoding.Latin1;
return StringUtil.GetEncoding(s) ?? Encoding.ASCII;
}
}
///
/// See .
///
public class HSResponse {
///
/// Response status code. Initially OK.
///
public HttpStatusCode Status { get; set; }
///
/// Response reason phrase. Initially null.
///
public string Reason { get; set; }
///
/// Response headers. Initially empty.
/// The server later may add Date, Server, Content-Encoding, Content-Length.
///
public Dictionary Headers { get; } = new(StringComparer.OrdinalIgnoreCase);
///
/// Raw response content.
///
///
///
///
///
/// The server may send this data compressed (it depends on headers etc).
///
public byte[] Content { get; set; }
///
/// Sets response content text.
///
/// Sets : Content = content.ToUTF8();.
/// If not null, sets Content-Type header.
public void SetContentText(string content, string contentType = "text/plain; charset=utf-8") {
Content = content?.ToUTF8();
if (contentType != null) Headers["Content-Type"] = contentType;
}
///
/// JSON-serializes object of type T, and sets . Also sets Content-Type header.
///
/// Object of type T.
/// If not null, sets Content-Type header.
/// Exceptions of .
public void SetContentJson(T obj, string contentType = "application/json; charset=utf-8") {
Content = JsonSerializer.SerializeToUtf8Bytes(obj, InternetUtil_.JsonSerializerOptions);
if (contentType != null) Headers["Content-Type"] = contentType;
}
///
/// JSON-serializes object of specified type, and sets . Also sets Content-Type header.
///
/// Object.
/// obj type.
/// If not null, sets Content-Type header.
/// Exceptions of .
public void SetContentJson(object obj, Type type, string contentType = "application/json; charset=utf-8") {
Content = JsonSerializer.SerializeToUtf8Bytes(obj, type, InternetUtil_.JsonSerializerOptions);
if (contentType != null) Headers["Content-Type"] = contentType;
}
}
static class InternetUtil_ {
static readonly Lazy s_defaultSerializerOptions = new(() => new(JsonSerializerDefaults.Web));
public static JsonSerializerOptions JsonSerializerOptions => s_defaultSerializerOptions.Value;
}
}
================================================
FILE: Au/Au.More/IconImageCache.cs
================================================
//TODO2: 2025-11: often the main floating toolbar expands slowly. 2025-12: fast again.
using System.Drawing;
using Microsoft.Win32;
namespace Au.More;
///
/// Gets images as of same logical size to be displayed as icons. Can get file icons or load images from files etc.
///
///
/// Uses memory cache and optionally file cache to avoid loading same image multiple times. Getting images from cache is much faster.
///
/// Bitmap objects retrieved by this class are stored in memory cache. Don't dispose them before disposing the IconImageCache object. Usually don't need to dispose these Bitmap objects explicitly (GC will do it).
///
/// Thread-safe.
///
public sealed class IconImageCache : IDisposable {
record class _DpiImages {
readonly IconImageCache _cache;
public readonly int dpi;
public readonly string indexFile;
public Dictionary dNameHash; //index file data
public readonly Dictionary dNameBitmap = new(); //memory cache
public readonly Dictionary dHashBitmap = new(); //let all identical images share single Bitmap object
//info: dictionary string keys are case-sensitive. We have not only paths but also base64 MD5. For paths we call Lower.
public _DpiImages(IconImageCache cache, int dpi) {
_cache = cache;
this.dpi = dpi;
if (_cache._dir is string s) indexFile = s + "\\" + dpi.ToS() + ".dpi";
}
///
/// If the index file for this DPI still not loaded, loads it into dNameHash.
///
public void LoadIndexFile() {
if (dNameHash == null && filesystem.exists(indexFile, useRawPath: true)) {
try {
bool save = false;
Dictionary d = new(StringComparer.OrdinalIgnoreCase);
filesystem.waitIfLocked(() => {
var fs = File.OpenRead(indexFile);
using var br = new BinaryReader(fs);
for (var len = fs.Length; fs.Position < len;) {
var imageKey = br.ReadString();
Hash.MD5Result hash = new(br.ReadInt64(), br.ReadInt64());
ref var r = ref d.GetValueRefOrAddDefault_(imageKey, out bool exists);
r = hash;
if (exists) save = true; //save without duplicates. Keep the newest hash.
}
});
dNameHash = d;
if (save) _SaveIndex();
}
catch (Exception e1) { Debug_.Print(e1); }
}
dNameHash ??= new();
}
void _SaveIndex() {
//print.it("<>SaveIndex<>");
filesystem.waitIfLocked(() => {
var fs = File.Create(indexFile);
using var bw = new BinaryWriter(fs);
foreach (var v in dNameHash) {
bw.Write(v.Key);
bw.Write(v.Value.r1);
bw.Write(v.Value.r2);
}
});
}
public void AppendToIndexFile(string imageKey, Hash.MD5Result hash) {
filesystem.waitIfLocked(() => {
var fs = File.Open(indexFile, FileMode.Append);
using var bw = new BinaryWriter(fs);
bw.Write(imageKey);
bw.Write(hash.r1);
bw.Write(hash.r2);
});
}
}
readonly string _dir;
readonly List<_DpiImages> _aDpi = new(); //for each used DPI
readonly int _imageSize;
bool _disposed, _onceUsedFiles;
readonly HashSet _extDynamicIcon = new();
static List> s_caches = new();
/// Width and height of images. Min 16, max 256.
/// Path of cache directory. If null, will be used only memory cache.
/// Not full path.
public IconImageCache(int imageSize = 16, string directory = null) {
if (imageSize < 16 || imageSize > 256) throw new ArgumentOutOfRangeException(nameof(imageSize));
_imageSize = imageSize;
//directory = null; //test memory-only cache
if (directory != null) _dir = pathname.normalize(directory);
lock (s_caches) s_caches.Add(new(this));
//_InitNotifyWindow();
script.GetAuxThread_(); //ensure the aux thread is running. It calls ClearAll_ when receives message "clear image caches".
}
///
/// Width and height of images.
///
public int ImageSize => _imageSize;
//public string CacheDirectory => _dir;
///
/// Common cache for icons of size 16. Used by menus, toolbars and editor.
///
///
/// Uses cache directory folders.ThisAppDataLocal + "iconCache".
///
public static IconImageCache Common => CommonOfSize(16);
///
/// Common cache for icons of given size. Used by menus and toolbars if custom .
///
/// Width and height of images. Min 16, max 256.
public static IconImageCache CommonOfSize(int imageSize) {
if (imageSize < 16 || imageSize > 256) throw new ArgumentOutOfRangeException(nameof(imageSize));
return s_commonDict.GetOrAdd(imageSize, o => new(o, folders.ThisAppDataLocal + "iconCache" + (o == 16 ? "" : o.ToS())));
}
static ConcurrentDictionary s_commonDict = new();
//rejected: if this is a portable app in a removable drive, use only memory cache (dir=null). Maybe only if folders.ThisAppDataLocal is in a fixed drive.
// Cannot reliably detect whether this app is portable. And whether the app (or user) wants to use the file cache.
// Eg this app may be in an external SSD drive, but external SSD drives are detected as fixed, and impossible to know whether it is used as a portable app.
// Instead, if this is a portable app and does not want to write in other drives, let it set folders.ThisAppDataLocal. And portable LA does it (as well as script processes started from it).
///
/// Gets image from cache or file etc.
///
/// File path, or resource path that starts with "resources/" or has prefix "resource:", or icon name like "*Pack.Icon color", etc. See isImage parameter.
/// DPI of window that will display the image. See .
///
/// false - get file/folder/filetype/url/etc icon with . If imageSource is relative path of a .cs file, gets its custom icon as image; returns null if no custom icon or if editor isn't running.
/// true - load image from xaml/png/etc file, database, resource or string with or . Can be icon name like "*Pack.Icon color" (see menu Tools > Icons).
///
/// To detect whether a string is an image, call ; if it returns true, it is image.
///
/// Action to call when fails to load image. If null, then silently returns null. Parameters are image source string and exception.
public unsafe Bitmap Get(string imageSource, int dpi, bool isImage, Action onException = null) {
//print.it(imageSource, isImage);
if (_disposed) throw new ObjectDisposedException(nameof(IconImageCache));
bool isXaml = isImage && (imageSource.Starts('<') || imageSource.Ends(".xaml", true));
bool isStore = !isImage && imageSource.Starts(@"shell:AppsFolder\"); //compare case-sensitive. Then users can pass eg "shell:appsFolder..." to display white icons in blue background.
bool ofWorkspaceFile = false;
if (!isImage && !isStore && imageSource.Ends(".cs", true) && !pathname.isFullPath(imageSource, orEnvVar: true)) { //eg `script.run(@"x.cs");`
imageSource = ScriptEditor.GetIcon(imageSource, EGetIcon.PathToIconName);
if (imageSource == null) return null;
isImage = ofWorkspaceFile = true;
//rejected: use Dictionary to avoid frequent GetIcon for same imageSource. In LA process fast, elsewhere not too slow.
//rejected: Move this code to the caller that needs it (MTBase).
}
bool isIconName = isImage && !isXaml && imageSource.Starts('*');
if (isIconName) {
isXaml = true;
imageSource = IconString_.NormalizeColor(imageSource); //color can be "normal|highContrast"
}
if (!isXaml && !isStore) dpi = 96; //will scale when drawing, it's fast and not so bad. Tested scaling with Lanczos3 etc filters, but the result for icons isn't better.
string imageKey = imageSource;
if (!isIconName) {
if ((isXaml && imageKey.Starts('<')) || (isImage && ImageUtil.HasImageStringPrefix(imageKey))) imageKey = Hash.MD5(imageSource, base64: true);
else imageKey = imageKey.Lower();
}
bool isIco = !isImage && !isStore && imageKey.Ends(".ico");
lock (this) {
//get _DpiImages for dpi
_DpiImages dd;
foreach (var v in _aDpi) if (v.dpi == dpi) { dd = v; goto g1; }
_aDpi.Add(dd = new _DpiImages(this, dpi));
g1:
if (dd.dNameBitmap.TryGetValue(imageKey, out var b)) return b;
//print.it(imageSource, isImage);
//using var p1 = perf.local();
//bool useHash = !isImage && !isIco;
bool useHash = isImage ? isIconName : !isIco; //use file cache for *icon too
//use file cache for *icon too.
// Because:
// Loads XAML icon slowly first time (~100 ms; not measured after reboot), even in LA, and even later loads slower than from the cache file.
// Non-WPF process uses much more memory (because loads WPF), eg 14 -> 28 MB.
// Bad: when trying to find icons, users try many icons, colors, sizes. Then the cache is full of garbage.
// TODO3: remove from cache if not using anymore. Or add only if frequently using.
//FUTURE: to make loading XAML icons faster etc, try Windows.UI.Xaml.Markup.XamlReader.Load. Use Microsoft.Windows.SDK.NET.dll, or directly COM if possible. When the library will not support Win7/8.
bool useFile = _dir != null && useHash;
if (useFile) {
try {
if (!_onceUsedFiles) {
_onceUsedFiles = true;
var fe = filesystem.exists(_dir);
if (fe.File) filesystem.delete(_dir); //fbc (was .db file)
if (!fe.Directory) filesystem.createDirectory(_dir);
_InitFiles();
}
dd.LoadIndexFile();
bool useExt = false; g2:
if (dd.dNameHash.TryGetValue(imageKey, out var hash)) {
if (dd.dHashBitmap.TryGetValue(hash, out var bb)) return bb;
var path = _dir + "\\" + hash.ToString() + ".png";
try {
//b = (Bitmap)Image.FromFile(path); //no, locks file
using (var stream = File.OpenRead(path)) b = (Bitmap)Image.FromStream(stream);
dd.dNameBitmap[imageKey] = b;
dd.dHashBitmap[hash] = b;
return b;
}
catch (Exception e1) { Debug_.PrintIf(filesystem.exists(path), e1); }
} else if (!useExt && !isImage && !isStore && !imageKey.Ends(".exe") && !imageKey.Ends(".lnk") && pathname.isFullPath(imageSource) && filesystem.exists(imageSource, useRawPath: true).File) {
var ext = pathname.getExtension(imageKey);
bool noExt = ext.Length == 0;
if (noExt) ext = ".#"; //will get the icon of unknown file types
if (dd.dNameBitmap.TryGetValue(ext, out var b1)) return b1;
if (!_extDynamicIcon.Contains(ext)) {
if (!noExt && _DynamicIcon(ext)) _extDynamicIcon.Add(ext); else { imageKey = imageSource = ext; useExt = true; goto g2; }
}
}
}
catch (Exception e1) { print.warning(e1); useFile = false; } //failed to create _dir
}
//p1.Next('C');
try {
//long t1 = perf.mcs;
if (!isImage) {
b = isStore ? icon.winStoreAppImage(imageSource, Dpi.Scale(_imageSize, dpi)) : null;
b ??= icon.of(imageSource, _imageSize)?.ToGdipBitmap();
} else {
if (isIconName) {
imageSource = ScriptEditor.GetIcon_(imageSource, EGetIcon.IconNameToXaml, skipResources: ofWorkspaceFile);
//p1.Next('X');
if (imageSource == null) return null;
}
if (isXaml) b = ImageUtil.LoadGdipBitmapFromXaml(imageSource, dpi, (_imageSize, _imageSize));
else b = ImageUtil.LoadGdipBitmap(imageSource);
}
//if (useFile) useFile = perf.mcs - t1 > 1000; //reduces the index file size in worst cases, but makes significantly slower later
}
catch (Exception ex) {
if (onException != null) onException(imageSource, ex);
//else print.warning("IconImageCache.Get() failed. " + ex.ToStringWithoutStack()); //no. Often prints while editing text if editor shows images in text.
}
//p1.Next('L');
if (b != null && (isImage ? useFile : !isIco)) {
try {
var ms = new MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Png); //~200 mcs. It's fast if compared with icon.of etc and saving.
var hash = Hash.MD5(ms.GetBuffer().AsSpan(0, (int)ms.Position));
ref var br = ref dd.dHashBitmap.GetValueRefOrAddDefault_(hash, out bool exists);
if (!exists) br = b; else { b.Dispose(); b = br; }
if (useFile) {
//p1.Next();
if (!exists) {
//print.it("<>save<>");
filesystem.waitIfLocked(() => {
using var fs = File.Create($@"{_dir}\{hash.ToString()}.png");
fs.Write(ms.GetBuffer().AsSpan(0, (int)ms.Position));
});
}
dd.dNameHash[imageKey] = hash;
//p1.Next();
dd.AppendToIndexFile(imageKey, hash);
}
}
catch (Exception e1) { Debug_.Print(e1); }
}
dd.dNameBitmap[imageKey] = b;
return b;
}
//rejected: Don't cache if non-literal (non-interned) string. Caller may generate many random strings, eg icon colors. Impossible to detect reliably.
static bool _DynamicIcon(string ext) {
if (Registry.GetValue(@"HKEY_CLASSES_ROOT\" + ext, "", null) is string s1) {
//if (Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\" + ext + @"\UserChoice", "ProgId", null) is string s2) return _DI(s2); //it seems Windows ignores this when looking for icon handler or %1
if (_DI(s1)) return true;
}
return false;
static bool _DI(string progid)
=> Registry.GetValue(@"HKEY_CLASSES_ROOT\" + progid + @"\ShellEx\IconHandler", "", null) is string
|| Registry.GetValue(@"HKEY_CLASSES_ROOT\" + progid + @"\DefaultIcon", "", null) is "\"%1\"" or "%1"; //exe, ico, cur, ani
}
}
void _InitFiles() {
if (_dir == null) return;
//delete cache files if changed OS/.NET/Au version
var verPath = _dir + @"\version.txt";
try {
var sNew = osVersion.onaString;
if (filesystem.exists(verPath, useRawPath: true)) {
var sOld = filesystem.loadText(verPath);
if (sNew == sOld) return;
_ClearFiles();
}
filesystem.saveText(verPath, sNew);
}
catch (Exception e1) { Debug_.Print(e1); }
}
///
/// Removes images from memory cache (but does not dispose) and makes this object unusable.
/// Optional.
///
public void Dispose() {
_disposed = true;
_aDpi.Clear();
_Dispose();
GC.SuppressFinalize(this);
}
///
~IconImageCache() { /*print.it("~IconImageCache");*/ _Dispose(); }
void _Dispose() {
lock (s_caches) {
for (int i = s_caches.Count; --i >= 0;) {
if (!s_caches[i].TryGetTarget(out var v) || v == this) s_caches.RemoveAt(i);
}
}
}
///
/// Clears the cache (removes images from memory cache and file cache).
///
/// Redraw (asynchronously) all visible windows of this process.
public void Clear(bool redrawWindows = false) {
if (_disposed) throw new ObjectDisposedException(nameof(IconImageCache));
if (_Clear() && redrawWindows) _RedrawWindowsOfThisProcess();
}
bool _Clear() {
lock (this) {
if (_disposed) return false;
_aDpi.Clear();
_extDynamicIcon.Clear();
}
_ClearFiles();
Cleared?.Invoke();
return true;
}
///
/// When the cache cleared.
///
public event Action Cleared;
void _ClearFiles() => _ClearFiles(_dir);
static void _ClearFiles(string dir) {
if (dir == null || !filesystem.exists(dir, true).Directory) return;
try {
bool deletedAll = true;
foreach (var v in Directory.GetFiles(dir, "*.dpi")) deletedAll &= Api.DeleteFile(v);
if (deletedAll) {
foreach (var v in Directory.GetFiles(dir, "*.png")) deletedAll &= Api.DeleteFile(v);
if (deletedAll && !dir.Ends(@"\iconCache", true) && Api.DeleteFile(dir + @"\version.txt")) Api.RemoveDirectory(dir);
}
}
catch (Exception e1) { Debug_.Print(e1); }
}
static unsafe void _RedrawWindowsOfThisProcess() {
foreach (var w in wnd.findAll(of: WOwner.Process(process.thisProcessId), flags: WFlags.CloakedToo))
Api.RedrawWindow(w, flags: Api.RDW_INVALIDATE | Api.RDW_ALLCHILDREN);
}
///
/// Clears caches of all instances of this or all processes. Redraws (asynchronously) all visible windows of these processes.
///
/// Clear in all processes of this user session.
public static void ClearAll(bool allProcesses = true) {
ClearAll_();
if (allProcesses) {
//if called in LA process (eg menu Tools > Update icons), clear the cache dir of scripts.
// Without it would clear only if some script processes already used the cache.
if (script.role == SRole.EditorExtension && Common._dir is string s1) {
Debug.Assert(s1.Ends(@"\iconCache"));
s1 = s1.ReplaceAt(^9.., "_script");
//clear caches of all image sizes
var a = filesystem.enumDirectories(s1, "iconCache*").ToArray();
foreach (var f in a) {
_ClearFiles(f.FullPath);
}
}
for (var w = wnd.findFast(null, script.c_auxWndClassName, true); !w.Is0; w = wnd.findFast(null, script.c_auxWndClassName, true, w))
if (!w.IsOfThisProcess) w.SendNotify(script.c_msg_IconImageCache_ClearAll);
}
}
internal static void ClearAll_() {
List a = new();
lock (s_caches) foreach (var c in s_caches) if (c.TryGetTarget(out var v)) a.Add(v);
bool redrawWindows = false;
foreach (var v in a) redrawWindows |= v._Clear();
if (redrawWindows) _RedrawWindowsOfThisProcess();
}
//static int s_auxInited;
//static void _InitNotifyWindow() {
// //if (0 == Interlocked.Exchange(ref s_auxInited, 1)) {
// // var t = script.GetAuxThread_();
// // t.QueueAPC(_InitAuxThread);
// //}
// //static void _InitAuxThread() {
// // //rejected. Assoc may be changed frequently. The cache probably even does not contain icons of those file types.
// // // Anyway cannot auto-clear if changed while cache not running.
// // // Also cannot auto-clear when icons changed not because of a changed assoc. Eg changed .exe icon, edited .ico, changed .lnk icon.
// // //using var pidl = Pidl.FromString(":: ");
// // //var e = new api.SHChangeNotifyEntry { pidl = pidl.UnsafePtr, fRecursive = true };
// // //Api.SHChangeNotifyRegister(script.AuxWnd_, api.SHCNRF_ShellLevel, api.SHCNE_ASSOCCHANGED, script.c_msg_IconImageCache_ClearAll, 1, e);
// // ////undocumented: is it important to call SHChangeNotifyDeregister when this process ends?
// //}
//}
}
================================================
FILE: Au/Au.More/ImageUtil.cs
================================================
using System.Windows;
using System.Windows.Markup;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Au.More;
///
/// Loads WPF and GDI+ images from file, resource or string.
///
///
public static partial class ImageUtil {
///
/// Returns true if string starts with "image:".
///
public static bool HasImageStringPrefix(string s) => s.Starts("image:");
///
/// Returns true if string starts with "resource:", "resources/", "image:" (Base64 encoded image), "imagefile:" (file path), "*" (XAML icon name) or "<" (possibly XAML image).
///
public static bool HasImageOrResourcePrefix(string s) => s.Starts('*') || s.Starts('<') || s.Starts("image:") || s.Starts("imagefile:") || ResourceUtil.HasResourcePrefix(s);
///
/// Loads image file data as stream from Base64 string.
///
/// Base64 encoded image string with prefix "image:".
/// String does not start with "image:" or is invalid Base64.
/// exceptions (when compressed .bmp).
public static MemoryStream LoadImageStreamFromString(string s) {
if (!HasImageStringPrefix(s)) throw new ArgumentException("String must start with \"image:\".");
int start = 6; while (start < s.Length && s[start] <= ' ') start++; //can be eg "image:\r\n..."
bool compressedBmp = s.Eq(start, "WkJN");
if (compressedBmp) start += 4;
int n = (int)((s.Length - start) * 3L / 4);
var b = new byte[n];
if (!Convert.TryFromBase64Chars(s.AsSpan(start), b, out n)) throw new ArgumentException("Invalid Base64 string");
if (!compressedBmp) return new MemoryStream(b, 0, n, false);
return new MemoryStream(Convert2.BrotliDecompress(b.AsSpan(0, n)), false);
}
///
/// Loads GDI+ image from Base64 string.
///
/// Base64 encoded image string with prefix "image:".
/// Exceptions of and .
static System.Drawing.Bitmap _LoadGdipBitmapFromString(string s)
=> new(LoadImageStreamFromString(s));
///
/// Loads WPF image from Base64 string.
///
/// Base64 encoded image string with prefix "image:".
/// Exceptions of and .
static BitmapFrame _LoadWpfImageFromString(string s)
=> BitmapFrame.Create(LoadImageStreamFromString(s));
//not used in library
/////
///// Calls and handles exceptions. On exception returns null and optionally prints a warning.
/////
//public static System.Drawing.Bitmap TryLoadGdipBitmapFromString(string s, bool warning) {
// try { return LoadGdipBitmapFromString(s); }
// catch (Exception ex) { if (warning) print.warning(ex.ToStringWithoutStack()); }
// return null;
//}
/////
///// Calls and handles exceptions. On exception returns null and optionally prints warning.
/////
//public static BitmapFrame TryLoadWpfImageFromString(string s, bool warning) {
// try { return LoadWpfImageFromString(s); }
// catch (Exception ex) { if (warning) print.warning(ex.ToStringWithoutStack()); }
// return null;
//}
///
/// Loads GDI+ image from file, resource or string.
///
///
/// Can be:
///
• file path. Can have prefix "imagefile:".
///
• resource path that starts with "resources/" or has prefix "resource:" ()
///
• Base64 encoded image string with prefix "image:".
///
/// If not null, supports XAML images. See .
/// Depending on image string format, exceptions of , , etc.
public static System.Drawing.Bitmap LoadGdipBitmap(string image, (int dpi, SIZE? size)? xaml = null) {
if (HasImageStringPrefix(image))
return _LoadGdipBitmapFromString(image);
if (xaml != null && (image.Starts('<') || image.Ends(".xaml", true)))
return LoadGdipBitmapFromXaml(image, xaml.Value.dpi, xaml.Value.size);
if (ResourceUtil.HasResourcePrefix(image))
return ResourceUtil.GetGdipBitmap(image);
if (image.Starts("imagefile:")) image = image[10..];
image = pathname.normalize(image, folders.ThisAppImages);
//return new(image); //no, the file remains locked until the Bitmap is disposed (documented, tested)
using var fs = File.OpenRead(image);
return new(fs);
}
///
/// Loads WPF image or icon from file, resource or string.
///
///
/// Can be:
///
• file path. Can have prefix "imagefile:".
///
• resource path that starts with "resources/" or has prefix "resource:" ()
///
• Base64 encoded image string with prefix "image:".
///
///
public static BitmapFrame LoadWpfImage(string image) {
if (HasImageStringPrefix(image)) return _LoadWpfImageFromString(image);
if (ResourceUtil.HasResourcePrefix(image)) return ResourceUtil.GetWpfImage(image);
if (image.Starts("imagefile:")) image = image[10..];
image = pathname.normalize(image, folders.ThisAppImages, flags: PNFlags.CanBeUrlOrShell); //CanBeUrlOrShell: support "pack:"
return BitmapFrame.Create(new Uri(image));
//rejected: support XAML and "*iconName". Possible but not easy. Probably would be blurred when autoscaled.
}
///
/// Loads WPF image element from file, resource or string. Supports xaml, png and other image formats supported by WPF.
///
///
/// Can be:
///
• file path. Can be .xaml, .png etc. Supports environment variables etc, see . Can have prefix "imagefile:".
///
• resource path that starts with "resources/" or has prefix "resource:". This function calls if ends with ".xaml", else .
///
• Base64 encoded image with prefix "image:". See .
///
• XAML string that starts with "<". For example from the Icons tool of LibreAutomate.
///
• XAML icon name like "*Pack.Icon color" or "*Pack.Icon color @size" or "*Pack1.Icon1 color1; *Pack2.Icon2 color2 %8,8,,". More info in Remarks.
///
///
/// If image is XAML icon name or starts with "<" or ends with ".xaml" (case-insensitive), returns new WPF element of type specified by the XAML root element (uses ). Else returns with Source = (uses ).
///
///
/// image can be an XAML icon name from the Icons tool of LibreAutomate (LA), like "*Pack.Icon color". Full format: "[*<library>]*pack.name[ color][ @size][ %margin][;more icons]". Here parts enclosed in [] are optional. The color, size and margin parts can be in any order.
///
• color - #RRGGBB or color name (WPF). If 2 colors like "#008000|#00FF00", the second color is for high contrast dark theme. If omitted, will use the system color of control text. Also can be like "#008000|" to use control text only for dark contrast theme, or "|#00FF00" for vice versa.
///
• size - icon size 1 to 16, like "*Pack.Icon blue @12". Can be used to make the displayed icon smaller or in some cases less blurry. It is the logical width and height of the icon rendered at the center of a box of logical size 16x16. To make icon bigger, instead set properties Width and Height of the returned element; or for a toolbar or menu.
///
• margin - icon margins inside a box of logical size 16x16. Format: %left,top,right,bottom,stretch,snap. All parts are optional. Examples: "*Pack.Icon blue %,,8,8", "*Pack.Icon blue %8,8", "*Pack.Icon blue %4,,4,,f". The stretch part can be f (fill) or m (move); default is uniform. The snap part can be p (sets SnapsToDevicePixels=True). Can be used either margin or size, not both.
///
• more icons - can be specified multiple icons separated by semicolon, like "*Pack1.Icon1 color1; *Pack2.Icon2 color2". It allows to create multi-color icons (for example a "filled" icon of one color + an "outline" icon of another color) or to add a small overlay icon (eg to indicate disabled state) at a corner (use margin).
///
• library - name of assembly containing the resource. If omitted, uses .
///
The LA compiler finds icon strings anywhere in code, gets their XAML from the database, and adds the XAML to the assembly as a string resource (see Properties > Resource > Options). This function gets the XAML from resources (). If fails, then tries to get XAML from database, and fails if LA isn't running. Uses .
///
///
public static FrameworkElement LoadWpfImageElement(string image) {
if (image.Starts('*')) {
image = ScriptEditor.GetIcon(image, EGetIcon.IconNameToXaml) ?? throw new AuException("*get icon " + image);
}
if (image.Starts('<')) return (FrameworkElement)XamlReader.Parse(image);
if (image.Ends(".xaml", true)) {
if (ResourceUtil.HasResourcePrefix(image)) return (FrameworkElement)ResourceUtil.GetXamlObject(image);
if (image.Starts("imagefile:")) image = image[10..];
using var stream = File.OpenRead(image);
return (FrameworkElement)XamlReader.Load(stream);
} else {
var bf = LoadWpfImage(image);
return new Image { Source = bf };
}
//Could set UseLayoutRounding=true as a workaround for blurry images, but often it does not work and have to be set on parent element.
// Then does not work even if wrapped eg in a Border with UseLayoutRounding.
}
///
/// Loads GDI+ image from WPF XAML file or string.
///
/// XAML file, resource or string. See .
/// DPI of window that will display the image.
/// Final image size in logical pixels (not DPI-scaled). If null, uses element's DesiredSize property, max 1024x1024.
/// New Bitmap. Note: its pixel format is Format32bppPArgb (premultiplied ARGB).
///
///
/// Calls and .
/// Don't use the Tag property of the bitmap. It keeps bitmap data.
///
[MethodImpl(MethodImplOptions.NoInlining)]
public static System.Drawing.Bitmap LoadGdipBitmapFromXaml(string image, int dpi, SIZE? size = null) {
var e = LoadWpfImageElement(image);
//s_cwt.Add(e, new());
return ConvertWpfImageElementToGdipBitmap(e, dpi, size);
}
///
/// Converts WPF image element to GDI+ image.
///
/// For example .
/// DPI of window that will display the image.
///
/// Final image size in logical pixels (not DPI-scaled).
/// If null, uses element's DesiredSize property, max 1024x1024.
/// If not null, sets element's Width and Height; the element should not be used in UI.
///
/// New Bitmap. Note: its pixel format is Format32bppPArgb (premultiplied ARGB).
public static unsafe System.Drawing.Bitmap ConvertWpfImageElementToGdipBitmap(FrameworkElement e, int dpi, SIZE? size = null) {
bool measured = e.IsMeasureValid;
if (size != null) {
measured = false;
e.Width = size.Value.width;
e.Height = size.Value.height;
}
if (!measured) e.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
bool arranged = measured && e.IsArrangeValid;
if (!arranged) e.Arrange(new Rect(e.DesiredSize));
if (!arranged) e.UpdateLayout(); //prevent memory leak
if (size == null) {
var z = e.DesiredSize; //if using RenderSize or ActualX, if element height!=width, draws in wrong place, clipped
size = new(Math.Min(1024d, z.Width).ToInt(), Math.Min(1024d, z.Height).ToInt());
}
var (wid, hei) = Dpi.Scale(size.Value, dpi);
var rtb = new RenderTargetBitmap(wid, hei, dpi, dpi, PixelFormats.Pbgra32);
//var rtb = t_rtb ??= new RenderTargetBitmap(wid, hei, dpi, dpi, PixelFormats.Pbgra32); rtb.Clear(); //not better
//note: if Bgra32, throws exception "'Bgra32' PixelFormat is not supported for this operation".
rtb.Render(e);
int stride = wid * 4, msize = hei * stride;
var b = new System.Drawing.Bitmap(wid, hei, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using var d = b.Data(System.Drawing.Imaging.ImageLockMode.ReadWrite);
rtb.CopyPixels(new(0, 0, wid, hei), d.Scan0, msize, stride);
b.SetResolution(dpi, dpi);
return b;
//tested: GC OK. Don't need GC_.AddObjectMemoryPressure. WPF makes enough garbage to trigger GC when need.
}
///
/// Converts WPF image element to native icon file data.
///
/// Stream to write icon file data. Writes from start.
/// Image element. See .
/// Sizes of icon images to add. For example 16, 24, 32, 48, 64. Sizes can be 1 to 256 inclusive.
/// An invalid size.
///
internal static unsafe void ConvertWpfImageElementToIcon_(Stream stream, FrameworkElement e, int[] sizes) {
stream.Position = Math2.AlignUp(sizeof(Api.NEWHEADER) + sizeof(Api.ICONDIRENTRY) * sizes.Length, 4);
var a = stackalloc Api.ICONDIRENTRY[sizes.Length];
for (int i = 0; i < sizes.Length; i++) {
int size = sizes[i];
if (size < 1 || size > 256) throw new ArgumentOutOfRangeException();
using var b = ImageUtil.ConvertWpfImageElementToGdipBitmap(e, 96, (size, size));
int pos = (int)stream.Position;
b.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
byte bsize = (byte)(size == 256 ? 0 : checked((byte)size));
a[i] = new Api.ICONDIRENTRY { bWidth = bsize, bHeight = bsize, wBitCount = 32, dwBytesInRes = (int)stream.Position - pos, dwImageOffset = pos };
}
var posEnd = stream.Position;
stream.Position = 0;
var h = new Api.NEWHEADER { wResType = 1, wResCount = (ushort)sizes.Length };
stream.Write(new(&h, sizeof(Api.NEWHEADER)));
stream.Write(new(a, sizeof(Api.ICONDIRENTRY) * sizes.Length));
stream.Position = posEnd;
}
///
/// Converts WPF image element to native icon file.
///
///
internal static void ConvertWpfImageElementToIcon_(string icoFile, FrameworkElement e, int[] sizes) {
icoFile = pathname.NormalizeMinimally_(icoFile);
using var stream = File.OpenWrite(icoFile);
ConvertWpfImageElementToIcon_(stream, e, sizes);
}
///
/// Converts XAML icon to GDI+ icon.
///
/// Icon name or XAML etc. See .
///
/// If fails, prints warning and returns null.
internal static System.Drawing.Icon XamlIconToGdipIcon_(string s, int size) {
try {
var e = ImageUtil.LoadWpfImageElement(s);
var ms = new MemoryStream();
ImageUtil.ConvertWpfImageElementToIcon_(ms, e, [size]);
ms.Position = 0;
return new System.Drawing.Icon(ms);
}
catch (Exception ex) { print.warning(ex); return null; }
}
}
================================================
FILE: Au/Au.More/KeyToTextConverter.cs
================================================
namespace Au.More;
///
/// Converts virtual-key codes to text characters.
///
///
/// To record user input, can be used .
/// When recording user input, use same KeyToTextConverter variable for all keys.
///
public class KeyToTextConverter
{
(KKey vk, KMod mod, uint sc, nint hkl) _deadKey;
///
/// Converts a virtual-key code to text.
///
/// Receives text. Can be 1 character c, or string s with 2 or more characters. Receives default if this function returns false or if the key is a dead key.
/// Virtual-key code.
/// Scan code.
/// Modifier keys (Shift etc). See .
/// Thread id of the focused or active window. Need for keyboard layout. If 0, uses this thread.
/// true if it's a text key or dead key.
public unsafe bool Convert(out (char c, string s) text, KKey vk, uint sc, KMod mod, int threadId) {
text = default;
if (vk == KKey.Packet) {
text.c = (char)sc;
} else {
if (!IsPossiblyChar_(mod, vk)) return false;
var hkl = Api.GetKeyboardLayout(threadId);
var ks = stackalloc byte[256];
_SetKS(mod);
var c = stackalloc char[8];
bool win10 = osVersion.minWin10_1607; //the API resets dead key etc, but on new OS flag 4 prevents it
int n = Api.ToUnicodeEx((uint)vk, sc, ks, c, 8, win10 ? 4u : 0u, hkl);
if (n == 1 && c[0] < ' ') {
Debug_.Print($"{(int)c[0]}, {c[0]}");
if (c[0] == '\r') c[n++] = '\n'; else if (c[0] is not ('\t' or '\n')) n = 0;
}
if (n == 1) text.c = c[0]; else if (n > 0) text.s = new(c, 0, n);
if (!win10) { //if need, set dead key again
if (_deadKey.vk != 0 && _deadKey.hkl == hkl) {
_SetKS(_deadKey.mod);
Api.ToUnicodeEx((uint)_deadKey.vk, _deadKey.sc, ks, c, 8, 0, hkl);
_deadKey.vk = 0;
} else if (n < 0) {
_deadKey = (vk, mod, sc, hkl);
Api.ToUnicodeEx((uint)vk, sc, ks, c, 8, 0, hkl);
}
}
if (n == 0) return false; //non-char key
void _SetKS(KMod m) {
ks[(int)KKey.Shift] = (byte)((0 != (m & KMod.Shift)) ? 0x80 : 0);
ks[(int)KKey.Ctrl] = (byte)((0 != (m & KMod.Ctrl)) ? 0x80 : 0);
ks[(int)KKey.Alt] = (byte)((0 != (m & KMod.Alt)) ? 0x80 : 0);
//ks[(int)KKey.Win] = (byte)((0 != (m & KMod.Win)) ? 0x80 : 0);
ks[(int)KKey.CapsLock] = (byte)(keys.isCapsLock ? 1 : 0); //don't need this for num lock
}
}
return true;
//Notes:
//1. Does not work with eg Chinese input method.
//2. Catches everything that would later be changed by the app, or by a next hook, etc.
//3. Don't know how to get Alt+numpad characters. Ignore them.
// On Alt up could call tounicodeex with sc with flag 0x8000. It gets the char, but resets keyboard state, and the char is not typed.
//4. In console windows does not work with Unicode characters.
//if(MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, hkl)&0x80000000) { print.it("DEAD"); return -1; } //this cannot be used because resets dead key
}
///
/// Clears internal fields such as dead key state.
///
public void Clear() {
_deadKey = default;
}
///
/// Returns true if the key + modifiers could generate a character, including Enter and Tab but not other control characters.
///
internal static bool IsPossiblyChar_(KMod m, KKey k) {
if (m.Has(KMod.Win) || (m & ~KMod.Shift) == KMod.Ctrl || m == (KMod.Alt | KMod.Shift)) return false;
if (k is KKey.Back or KKey.Escape) return false;
return true;
}
}
================================================
FILE: Au/Au.More/Math2.cs
================================================
namespace Au.More;
///
/// Simple calculation functions.
///
//[DebuggerStepThrough]
public static class Math2 {
///
/// Creates uint by placing (ushort)loWord in bits 1-16 and (ushort)hiWord in bits 17-32.
/// Like C macro MAKELONG, MAKEWPARAM, MAKELPARAM, MAKELRESULT.
///
/// The return value is of type nint. It can be used with Windows message API as lParam or wParam or return value.
public static nint MakeLparam(int loWord, int hiWord) => MakeLparam((uint)loWord, (uint)hiWord);
//Returns nint, because usually used as sendmessage etc parameter. If uint, would need to explicitly cast to nint. If somebody casts to int, the result may be incorrect, ie negative.
//Why named MakeLparam, MakeWord, LoWord, HiWord:
// 1. Like C macros MAKELPARAM/MAKEWORD/LOWORD/HIWORD.
// 2. MakeLparam used mostly as lParam of sendmessage etc.
///
public static nint MakeLparam(uint loWord, uint hiWord) => (nint)(((hiWord & 0xffff) << 16) | (loWord & 0xffff));
///
/// Creates uint by placing (ushort)p.x in bits 1-16 and (ushort)p.y in bits 17-32.
/// Like C macro MAKELONG, MAKEWPARAM, MAKELPARAM, MAKELRESULT.
///
/// The return value is of type nint. It can be used with Windows message API as lParam or wParam or return value.
public static nint MakeLparam(POINT p) => MakeLparam((uint)p.x, (uint)p.y);
///
/// Creates ushort by placing (byte)loByte in bits 1-8 and (byte)hiByte in bits 9-16.
/// Like C macro MAKEWORD.
///
public static ushort MakeWord(int loByte, int hiByte) => MakeWord((uint)loByte, (uint)hiByte);
///
public static ushort MakeWord(uint loByte, uint hiByte) => (ushort)(((hiByte & 0xff) << 8) | (loByte & 0xff));
///
/// Gets bits 1-16 as ushort.
/// Like C macro LOWORD.
///
///
/// The parameter is interpreted as uint. The parameter type nint allows to avoid explicit cast from int and IntPtr.
///
public static ushort LoWord(nint x) => (ushort)((uint)x & 0xFFFF);
///
/// Gets bits 17-32 as ushort.
/// Like C macro HIWORD.
///
///
public static ushort HiWord(nint x) => (ushort)((uint)x >> 16);
///
/// Gets bits 1-16 as short.
/// Like C macro GET_X_LPARAM.
///
///
public static short LoShort(nint x) => (short)((uint)x & 0xFFFF);
///
/// Gets bits 17-32 as short.
/// Like C macro GET_Y_LPARAM.
///
///
public static short HiShort(nint x) => (short)((uint)x >> 16);
///
/// Gets bits 1-8 as byte.
/// Like C macro LOBYTE.
///
public static byte LoByte(ushort x) => (byte)((uint)x & 0xFF);
///
/// Gets bits 9-16 as byte.
/// Like C macro HIBYTE.
///
public static byte HiByte(ushort x) => (byte)((uint)x >> 8);
///
/// Converts nint containing x and y coordinates to .
///
public static POINT NintToPOINT(nint xy) => new(LoShort(xy), HiShort(xy));
///
/// Returns number * multiply / divide.
/// Multiplies without overflow and rounds up or down to the nearest integer.
///
///
///
public static int MulDiv(int number, int multiply, int divide) {
if (divide == multiply) return number;
long r = (long)number * multiply;
int d = divide / 2; if (r < 0 == divide < 0) r += d; else r -= d; //round
return checked((int)(r / divide));
//This code produces the same results as API MulDiv. Tested with millions of random and edge values. Faster.
//The only difference, API does not support int.MinValue.
}
//public static int MulDiv(int number, int multiply, int divide) => Api.MulDiv(number, multiply, divide);
///
/// Calculates how many % of whole is part: 100L * part / whole.
///
///
///
/// Round down or up. If false (default), can only round down.
///
public static int PercentFromValue(int whole, int part, bool canRoundUp = false)
=> whole == default ? default : (canRoundUp ? MulDiv(100, part, whole) : checked((int)(100L * part / whole)));
///
/// Calculates how many % of whole is part: 100 * part / whole.
///
public static double PercentFromValue(double whole, double part)
=> whole == default ? default : (100.0 * part / whole);
///
/// Returns percent % of whole: (long)whole * percent / 100.
///
///
///
/// Use , which can round down or up. If false (default), can only round down.
///
public static int PercentToValue(int whole, int percent, bool canRoundUp = false)
=> canRoundUp ? MulDiv(whole, percent, 100) : checked((int)((long)whole * percent / 100L));
///
/// Returns percent % of whole: whole * percent / 100.
///
public static double PercentToValue(double whole, double percent)
=> whole * percent / 100.0;
///
/// If value is divisible by alignment, returns value. Else returns the nearest bigger number that is divisible by alignment.
///
/// An integer value.
/// Alignment. Must be a power of two (2, 4, 8, 16...).
///
/// For example if alignment is 4, returns 4 if value is 1-4, returns 8 if value is 5-8, returns 12 if value is 9-10, and so on.
///
///
///
///
public static int AlignUp(int value, uint alignment) => (int)AlignUp((uint)value, alignment);
///
public static uint AlignUp(uint value, uint alignment) => (value + (alignment - 1)) & ~(alignment - 1);
//shorter: (value + --alignment) & ~alignment. But possibly less optimized. Now (alignment - 1) and ~(alignment - 1) usually are constants.
///
/// Swaps values of two variables: T t = a; a = b; b = t;
///
public static void Swap(ref T a, ref T b) {
T t = a; a = b; b = t;
}
///
/// Swaps two ranges of bits.
///
///
/// Position of first range of bits.
/// Position of second range of bits.
/// Number of bits in each range.
public static int SwapBits(int value, int i, int j, int n) => (int)SwapBits((uint)value, i, j, n);
///
/// Swaps two ranges of bits.
///
///
/// Position of first range of bits.
/// Position of second range of bits.
/// Number of bits in each range.
public static uint SwapBits(uint value, int i, int j, int n) {
// http://graphics.stanford.edu/~seander/bithacks.html#SwappingBitsXOR
uint x = ((value >> i) ^ (value >> j)) & ((1U << n) - 1); // XOR temporary
return value ^ ((x << i) | (x << j));
}
//rejected. Too simple and does not save any code. Also would need generic, for enum too.
/////
///// Clears oldFlags bits specified in mask and adds newFlags bits specified in mask.
/////
//int SetFlagsMasked(int oldFlags, int newFlags, int mask) => (oldFlags&~mask) | (newFlags&mask);
///
/// Calculates angle degrees from coordinates x and y.
///
public static double AngleFromXY(int x, int y) => Math.Atan2(y, x) * (180 / Math.PI);
///
/// Calculates distance between two points.
///
public static double Distance(POINT p1, POINT p2) {
if (p1.y == p2.y) return Math.Abs(p2.x - p1.x); //horizontal line
if (p1.x == p2.x) return Math.Abs(p2.y - p1.y); //vertical line
long dx = p2.x - p1.x, dy = p2.y - p1.y;
return Math.Sqrt(dx * dx + dy * dy);
}
///
/// Calculates distance between rectangle and point.
///
/// If the point is outside, returns the nearest distance, else 0.
public static double Distance(RECT r, POINT p) {
r.Normalize(swap: true);
if (r.Contains(p)) return 0;
int x = p.x < r.left ? r.left : (p.x > r.right ? r.right : p.x);
int y = p.y < r.top ? r.top : (p.y > r.bottom ? r.bottom : p.y);
return Distance((x, y), p);
}
}
================================================
FILE: Au/Au.More/MemoryBitmap.cs
================================================
namespace Au.More;
///
/// Creates and manages native bitmap handle and memory DC (GDI device context).
/// The bitmap is selected in the DC.
///
public class MemoryBitmap : IDisposable {
IntPtr _dc, _bm, _oldbm;
bool _disposed;
///
/// DC handle.
///
public IntPtr Hdc => _dc;
///
/// Bitmap handle.
///
public IntPtr Hbitmap => _bm;
///
/// Does nothing. Later you can call or .
///
public MemoryBitmap() { }
///
/// Calls .
///
/// width or height is less than 1.
/// Failed. Probably there is not enough memory for bitmap of specified size (need with*height*4 bytes).
public MemoryBitmap(int width, int height) {
if (width <= 0 || height <= 0) throw new ArgumentException();
if (!Create(width, height)) throw new AuException("*create memory bitmap of specified size");
}
//rejected: not obvious, whether it attaches or copies. Also, attaching is rarely used.
/////
///// Calls .
/////
//public MemoryBitmap(IntPtr hBitmap)
//{
// Attach(hBitmap);
//}
///
protected virtual void Dispose(bool disposing) {
if (_disposed) return;
_disposed = true;
Delete();
}
///
/// Deletes the bitmap and DC.
///
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
///
~MemoryBitmap() => Dispose(false);
//Calls DeleteDC. MSDN says that ReleaseDC must be called from the same thread. But does not say it about DeleteDC and others.
// Tested: DeleteDC returns true in finalizer (other thread).
///
/// Deletes the bitmap and DC.
///
public void Delete() {
if (_dc == default) return;
if (_bm != default) {
Api.SelectObject(_dc, _oldbm);
Api.DeleteObject(_bm);
_bm = default;
}
Api.DeleteDC(_dc);
_dc = default;
}
///
/// Creates new memory DC and bitmap of specified size and selects it into the DC.
///
/// false if failed. In any case deletes previous bitmap and DC.
/// Width, pixels. Must be > 0.
/// Height, pixels. Must be > 0.
public bool Create(int width, int height) {
if (_disposed) throw new ObjectDisposedException(nameof(MemoryBitmap));
using var dcs = new ScreenDC_();
Attach(Api.CreateCompatibleBitmap(dcs, width, height));
return _bm != default;
}
///
/// Sets this variable to manage an existing bitmap.
/// Selects the bitmap into a memory DC.
/// Deletes previous bitmap and DC.
///
/// Native bitmap handle.
public void Attach(IntPtr hBitmap) {
if (_disposed) throw new ObjectDisposedException(nameof(MemoryBitmap));
Delete();
if (hBitmap != default) {
_dc = Api.CreateCompatibleDC(default);
_oldbm = Api.SelectObject(_dc, _bm = hBitmap);
}
}
///
/// Deletes memory DC, clears this variable and returns its bitmap (native bitmap handle).
/// The returned bitmap is not selected into a DC. Will need to delete it with API DeleteObject.
///
public IntPtr Detach() {
IntPtr bret = _bm;
if (_bm != default) {
Api.SelectObject(_dc, _oldbm);
Api.DeleteDC(_dc);
_dc = default; _bm = default;
}
return bret;
}
}
================================================
FILE: Au/Au.More/MemoryUtil.cs
================================================
namespace Au.More;
///
/// Allocates memory from native heap of this process using heap API.
/// Also has more functions to work with memory: copy, move, virtual alloc.
///
///
/// Uses the common heap of this process, API GetProcessHeap.
///
public static unsafe class MemoryUtil {
static IntPtr _processHeap = Api.GetProcessHeap();
///
/// Allocates new memory block and returns its address.
///
/// Byte count.
/// Set all bytes = 0.
/// Failed. Probably size is too big.
///
/// Calls API HeapAlloc.
/// The memory is unmanaged and will not be freed automatically. Always call when done. Call or if need to resize.
///
public static byte* Alloc(nint size, bool zeroInit = false)
=> _ReAllocBytes(null, size, zeroInit);
/// Count of elements of type T.
///
public static T* Alloc(nint count, bool zeroInit = false) where T : unmanaged
=> (T*)_ReAllocBytes(null, count * sizeof(T), zeroInit);
//Rejected. With the above overload the calling code is easier to read. Not so often used.
//public static void Alloc(out T* mem, nint count, bool zeroInit = false) where T : unmanaged
// => mem = (T*)_ReAllocBytes(null, count * sizeof(T), zeroInit);
static byte* _ReAllocBytes(void* mem, nint size, bool zeroInit = false) {
uint flag = zeroInit ? 8u : 0u;
for (int i = 0; i < 5; i++) {
if (i > 0) {
GC.Collect();
GC.WaitForPendingFinalizers();
Thread.Sleep(i * 100);
}
void* r;
if (mem == null) r = Api.HeapAlloc(_processHeap, flag, size);
else r = Api.HeapReAlloc(_processHeap, flag, mem, size);
if (r != null) return (byte*)r;
}
throw new OutOfMemoryException();
//note: don't need GC.AddMemoryPressure.
// Native memory usually is used for temporary buffers etc and is soon released eg with try/finally.
// Marshal.AllocHGlobal does not do it too.
}
///
/// Reallocates a memory block to make it bigger or smaller.
///
/// Input: old memory address; if null, allocates new memory like . Output: new memory address. Unchanged if exception.
/// New count of elements of type T.
/// When size is growing, set all added bytes = 0.
/// Failed. Probably count is too big.
///
/// Calls API HeapReAlloc or HeapAlloc.
/// Preserves data in Math.Min(oldCount, newCount) elements of old memory (copies from old memory if need).
/// The memory is unmanaged and will not be freed automatically. Always call when done. Call ReAlloc or if need to resize.
///
public static void ReAlloc(ref T* mem, nint count, bool zeroInit = false) where T : unmanaged
=> mem = (T*)_ReAllocBytes(mem, count * sizeof(T), zeroInit);
///
/// Frees a memory block.
/// Does nothing if mem is null.
///
///
/// Calls API HeapFree.
///
public static void Free(void* mem) {
if (mem != null) Api.HeapFree(_processHeap, 0, mem);
}
///
/// Frees a memory block (if not null) and allocates new.
///
/// Input: old memory address or null. Output: new memory address; null if exception (it prevents freeing twice).
/// New count of elements of type T.
/// Set all bytes = 0.
/// Failed. Probably count is too big.
///
/// At first sets mem = null, to avoid double if this function throws exception. Then calls and .
///
public static void FreeAlloc(ref T* mem, nint count, bool zeroInit = false) where T : unmanaged {
var m = mem; mem = null;
Free(m);
mem = Alloc(count, zeroInit);
}
///
/// Allocates new virtual memory block with API VirtualAlloc and returns its address: VirtualAlloc(default, size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE).
///
/// Byte count.
/// Failed. Probably size is too big.
///
/// Faster than managed and when memory size is large, more than 1 MB; else slower.
/// The memory is initialized to zero (all bytes 0).
///
public static byte* VirtualAlloc(nint size) {
for (int i = 0; i < 5; i++) {
if (i > 0) {
GC.Collect();
GC.WaitForPendingFinalizers();
Thread.Sleep(i * 100);
}
var r = (byte*)Api.VirtualAlloc(default, size, Api.MEM_COMMIT | Api.MEM_RESERVE, Api.PAGE_READWRITE);
if (r != null) return r;
}
throw new OutOfMemoryException();
//note: don't need GC.AddMemoryPressure.
// Native memory usually is used for temporary buffers etc and is soon released eg with try/finally.
// Marshal.AllocHGlobal does not do it too.
}
///
/// Frees a memory block allocated with .
/// Does nothing if mem is null.
///
public static void VirtualFree(void* mem) {
if (mem != null) Api.VirtualFree(mem);
}
///
/// Copies memory with .
///
///
/// If some part of memory blocks overlaps, this function is much slower than . Else same speed or slightly faster.
///
public static void Copy(void* from, void* to, nint size) => Buffer.MemoryCopy(from, to, size, size);
//speed Buffer.MemoryCopy vs memcpy, non-overlapped: same if small, slightly faster if big.
//speed Span.CopyTo vs Buffer.MemoryCopy: same if non-overlapped, slower if overlapped.
///
/// Copies memory with API memmove.
///
///
/// If some part of memory blocks overlaps, this function is much faster than . Else same speed or slightly slower.
///
public static void Move(void* from, void* to, nint size) => Api.memmove(to, from, size);
}
================================================
FILE: Au/Au.More/MenuItemInfo.cs
================================================
namespace Au.More
{
///
/// Gets item id, text and other info of a classic menu.
///
public class MenuItemInfo
{
IntPtr _hm;
int _id;
bool _isSystem;
wnd _ow;
private MenuItemInfo() { }
///
/// Gets info of a menu item from point.
///
/// null if failed, eg the point is not in the menu or the window is hung.
/// Point in screen coordinates.
/// Popup menu window, class name "#32768".
/// Timeout (ms) to use when the window is busy or hung.
public static MenuItemInfo FromXY(POINT pScreen, wnd w, int msTimeout = 5000) {
if (!w.SendTimeout(msTimeout, out var hm, Api.MN_GETHMENU)) return null;
int i = Api.MenuItemFromPoint(default, hm, pScreen); if (i == -1) return null;
i = Api.GetMenuItemID(hm, i); if (i == -1 || i == 0) return null;
return new MenuItemInfo { _hm = hm, _id = i };
}
///
/// Gets info of a menu item from mouse.
///
/// null if failed, eg the point is not in a menu or the window is hung.
/// Timeout (ms) to use when the window is busy or hung.
public static MenuItemInfo FromXY(int msTimeout = 5000) {
var p = mouse.xy;
var w = wnd.fromXY(p, WXYFlags.Raw);
if (!w.ClassNameIs("#32768")) return null;
return FromXY(p, w, msTimeout);
}
///
/// Gets the popup menu handle.
///
public IntPtr MenuHandle => _hm;
///
/// Gets menu item id.
///
public int ItemId => _id;
///
/// Gets the owner window of the popup menu.
///
public wnd OwnerWindow => _OwnerSystem().ow;
///
/// true if it is a system menu, eg when right-clicked the title bar of a window.
///
public bool IsSystem => _OwnerSystem().sys;
(wnd ow, bool sys) _OwnerSystem() {
if (_ow.Is0 && miscInfo.getGUIThreadInfo(out var g)) {
_ow = g.hwndMenuOwner;
_isSystem = g.flags.Has(GTIFlags.SYSTEMMENUMODE);
}
return (_ow, _isSystem);
}
///
/// Gets menu item text.
///
/// null if failed.
/// If contains '\t' character, get substring before it.
/// Call .
public string GetText(bool removeHotkey, bool removeAmp) => GetText(_hm, _id, false, removeHotkey, removeAmp);
///
/// Gets menu item text.
///
/// null if failed.
///
///
/// id is 0-based index. For example you can use it to get text of a submenu-item, because such items usually don't have id.
/// If contains '\t' character, get substring before it.
/// Call .
[SkipLocalsInit]
public static unsafe string GetText(IntPtr menuHandle, int id, bool byIndex, bool removeHotkey, bool removeAmp) {
var mi = new Api.MENUITEMINFO(Api.MIIM_STRING);
if (!Api.GetMenuItemInfo(menuHandle, id, byIndex, ref mi)) return null; //get required buffer size
if (mi.cch == 0) return "";
using FastBuffer b = new(mi.cch + 1);
mi.cch = b.n;
mi.dwTypeData = b.p;
if (!Api.GetMenuItemInfo(menuHandle, id, byIndex, ref mi)) return null;
var s = b.GetStringFindLength();
if (removeHotkey) { int i = s.IndexOf('\t'); if (i >= 0) s = s[..i]; }
if (removeAmp) s = StringUtil.RemoveUnderlineChar(s);
return s;
}
}
}
================================================
FILE: Au/Au.More/MouseCursor.cs
================================================
namespace Au.More;
///
/// Helps to load cursors, etc. Contains native cursor handle.
///
///
/// To load cursors for winforms can be used constructors, but they don't support colors, ani cursors and custom size.
/// Don't use this class to load cursors for WPF. Its Cursor class loads cursors correctly.
///
public class MouseCursor {
IntPtr _handle;
//rejected: use HandleCollector like with icon. Unlikely somebody will use many cursors or even find this class.
///
/// Sets native cursor handle.
/// The cursor will be destroyed when disposing this variable or when converting to object of other type.
///
public MouseCursor(IntPtr hcursor) { _handle = hcursor; }
///
/// Destroys native cursor handle.
///
public void Dispose() {
if (_handle != default) { Api.DestroyIcon(_handle); _handle = default; }
}
///
~MouseCursor() => Dispose();
///
/// Gets native cursor handle.
///
public IntPtr Handle => _handle;
/////
///// Gets native cursor handle.
/////
//public static implicit operator IntPtr(MouseCursor cursor) => cursor._handle;
///
/// Loads cursor from file.
///
/// default(MouseCursor) if failed.
/// .cur or .ani file. If not full path, uses .
/// Width and height. If 0, uses system default size, which depends on DPI.
public static MouseCursor Load(string file, int size = 0) {
file = pathname.normalize(file, folders.ThisAppImages);
if (file == null) return null;
uint fl = Api.LR_LOADFROMFILE; if (size == 0) fl |= Api.LR_DEFAULTSIZE;
return new MouseCursor(Api.LoadImage(default, file, Api.IMAGE_CURSOR, size, size, fl));
}
///
/// Creates cursor from cursor file data in memory, for example from a managed resource.
///
/// default(MouseCursor) if failed.
/// Data of .cur or .ani file.
/// Width and height. If 0, uses system default size, which depends on DPI.
///
/// This function creates/deletes a temporary file, because there is no good API to load cursor from memory.
///
public static MouseCursor Load(byte[] cursorData, int size = 0) {
using var tf = new TempFile();
File.WriteAllBytes(tf, cursorData);
return Load(tf, size);
//If want to avoid temp file, can use:
// 1. CreateIconFromResourceEx.
// But quite much unsafe code (at first need to find cursor offset (of size) and set hotspot), less reliable (may fail for some .ani files), in some cases works not as well (may get wrong-size cursor).
// The code moved to the Unused project.
// 2. CreateIconIndirect. Not tested. No ani. Need to find cursor offset (of size).
//WPF Cursor ctor uses temp file too.
}
///
/// Creates object that shares native cursor handle with this object.
///
/// null if is default(IntPtr).
public System.Windows.Forms.Cursor ToGdipCursor() {
if (_handle == default) return null;
var R = new System.Windows.Forms.Cursor(_handle);
s_cwt.Add(R, this);
return R;
}
static readonly ConditionalWeakTable s_cwt = new();
//rejected. Don't need. WPF can load from file or stream. Loads correctly.
/////
///// Creates WPF Cursor object from this native cursor.
/////
///// null if Handle is default(IntPtr).
///// If true (default), the returned variable owns the unmanaged cursor and destroys it when disposing. If false, the returned variable just uses the cursor handle and will not destroy; later will need to dispose this variable.
//public System.Windows.Input.Cursor ToWpfCursor(bool destroyCursor = true) {
// if (_handle == default) return null;
// var R = System.Windows.Interop.CursorInteropHelper.Create(new _CursorHandle(_handle, destroyCursor));
// if (destroyCursor) _handle = default;
// return R;
//}
//class _CursorHandle : SafeHandleZeroOrMinusOneIsInvalid
//{
// public _CursorHandle(IntPtr handle, bool ownsHandle) : base(ownsHandle) { base.handle = handle; }
// protected override bool ReleaseHandle() => Api.DestroyCursor(handle);
//}
///
/// Calculates 64-bit FNV1 hash of cursor's mask bitmap.
///
/// 0 if failed.
public static unsafe long Hash(IntPtr hCursor) {
using Api.ICONINFO ii = new(hCursor);
var hb = Api.CopyImage(ii.hbmMask, Api.IMAGE_BITMAP, 0, 0, Api.LR_COPYDELETEORG | Api.LR_CREATEDIBSECTION);
long R = 0; Api.BITMAP b;
if (0 != Api.GetObject(hb, sizeof(Api.BITMAP), &b) && b.bmBits != default)
R = More.Hash.Fnv1Long((byte*)b.bmBits, b.bmHeight * b.bmWidthBytes);
return R;
}
/////
///// Calculates 64-bit FNV1 hash of cursor's mask bitmap.
/////
///// 0 if failed.
//public unsafe long Hash() => Hash(_handle);
///
/// Gets current global mouse cursor.
///
/// false if cursor is hidden.
/// Receives native handle. Don't destroy.
public static bool GetCurrentVisibleCursor(out IntPtr cursor) {
Api.CURSORINFO ci = default; ci.cbSize = Api.SizeOf(ci);
if (Api.GetCursorInfo(ref ci) && ci.hCursor != default && 0 != (ci.flags & Api.CURSOR_SHOWING)) { cursor = ci.hCursor; return true; }
cursor = default; return false;
}
///
/// Workaround for brief "wait" cursor when mouse enters a window of current thread first time.
/// Reason: default thread's cursor is "wait". OS shows it before the first WM_SETCURSOR sets correct cursor.
/// Call eg on WM_CREATE.
///
internal static void SetArrowCursor_() {
var h = Api.LoadCursor(default, MCursor.Arrow);
if (Api.GetCursor() != h) Api.SetCursor(h);
}
}
================================================
FILE: Au/Au.More/RecordingUtil.cs
================================================
namespace Au.More
{
///
/// Functions for keyboard/mouse/etc recorder tools.
///
public static partial class RecordingUtil
{
///
/// Converts multiple recorded mouse movements to string for .
///
///
/// List of x y distances from previous.
/// The first distance is from mouse position before the first movement; at run time it will be distance from .
/// To create uint value from distance dx dy use and cast to uint.
///
///
/// recorded also contains sleep times (milliseconds) alternating with distances.
/// It must start with a sleep time. Example: {time1, dist1, time2, dist2}. Another example: {time1, dist1, time2, dist2, time3}. This is invalid: {dist1, time1, dist2, time2}.
///
public static string MouseToString(IEnumerable recorded, bool withSleepTimes) {
var a = new List();
byte flags = 0;
if (withSleepTimes) flags |= 1;
a.Add(flags);
int pdx = 0, pdy = 0;
bool isSleep = withSleepTimes;
foreach (var u in recorded) {
int v, nbytes = 4;
if (isSleep) {
v = (int)Math.Min(u, 0x3fffffff);
if (v > 3) v--; //_SendMove usually takes 0.5-1.5 ms
if (v <= 1 << 6) nbytes = 1;
else if (v <= 1 << 14) nbytes = 2;
else if (v <= 1 << 22) nbytes = 3;
//print.it($"nbytes={nbytes} sleep={v}");
//never mind: ~90% is 7. Removing it would make almost 2 times smaller string. But need much more code. Or compress (see comment below).
} else {
//info: to make more compact, we write not distances (dx dy) but distance changes (x y).
int dx = Math2.LoShort((nint)u), x = dx - pdx; pdx = dx;
int dy = Math2.HiShort((nint)u), y = dy - pdy; pdy = dy;
if (x >= -4 && x < 4 && y >= -4 && y < 4) nbytes = 1; //3+3+2=8 bits, 90%
else if (x >= -64 && x < 64 && y >= -64 && y < 64) nbytes = 2; //7+7+2=16 bits, ~10%
else if (x >= -1024 && x < 1024 && y >= -1024 && y < 1024) nbytes = 3; //11+11+2=24 bits, ~0%
int shift = nbytes * 4 - 1, mask = (1 << shift) - 1;
v = (x & mask) | ((y & mask) << shift);
//print.it($"dx={dx} dy={dy} x={x} y={y} nbytes={nbytes} v=0x{v:X}");
}
v <<= 2; v |= (nbytes - 1);
for (; nbytes != 0; nbytes--, v >>= 8) a.Add((byte)v);
isSleep ^= withSleepTimes;
}
//rejected: by default compresses to ~80% (20% smaller). When withSleepTimes, to ~50%, but never mind, rarely used.
//print.it(a.Count, Convert2.Compress(a.ToArray()).Length);
return Convert.ToBase64String(a.ToArray());
}
}
}
================================================
FILE: Au/Au.More/ResourceUtil.cs
================================================
using System.Resources;
using System.Windows.Media.Imaging;
using System.Windows.Markup;
using System.Windows;
using System.Windows.Controls;
namespace Au.More;
///
/// Gets managed resources from a .NET assembly.
///
///
/// Internally uses . Uses .
///
/// Loads resources from managed resource "AssemblyName.g.resources". To add such resource files in Visual Studio, set file build action = Resource. Don't use .resx files and the Resources page in Project Properties.
///
/// By default loads resources from the app entry assembly. In script with role miniProgram - from the script's assembly. To specify another loaded assembly, use prefix like "<AssemblyName>" or "*<AssemblyName>".
///
/// The resource name argument can optionally start with "resource:".
///
/// Does not use caching. Creates new object even when loading the resource not the first time.
///
public static class ResourceUtil {
///
/// Gets resource of any type.
///
/// Resource name, like "file.txt" or "sub/file.txt". More info: .
/// Cannot find assembly or resource.
/// The resource is of different type. This function does not convert.
/// Other exceptions that may be thrown by used .NET functions.
public static T Get(string name) {
var o = _GetObject(ref name);
if (o is T r) return r;
throw new InvalidOperationException($"Resource '{name}' is not {typeof(T).Name}; it is {o.GetType().Name}.");
}
///
/// Gets stream.
///
/// Resource name, like "file.png" or "sub/file.png". More info: .
/// Cannot find assembly or resource.
/// The resource type is not stream.
/// Other exceptions that may be thrown by used .NET functions.
///
/// Don't need to dispose the stream.
///
public static UnmanagedMemoryStream GetStream(string name) {
//if (name.Starts("pack:")) return _Pack(name); //rejected
return Get(name);
}
///
/// Gets string.
///
/// Resource name, like "myString" or "file.txt" or "sub/file.txt". More info: .
/// Cannot find assembly or resource.
/// Unsupported resource type.
/// Other exceptions that may be thrown by used .NET functions.
///
/// Supports resources of type string, byte[] (UTF-8), stream (UTF-8).
///
public static string GetString(string name) {
var o = _GetObject(ref name);
switch (o) {
case string s: return s;
case byte[] a: return Encoding.UTF8.GetString(a);
case UnmanagedMemoryStream m: return new StreamReader(m, Encoding.UTF8).ReadToEnd();
}
throw new InvalidOperationException($"Resource '{name}' is not string, byte[] or stream; it is {o.GetType().Name}.");
}
internal static string TryGetString_(string name) => _TryGetObject(ref name) as string;
///
/// Gets byte[].
///
/// Resource name, like "file.txt" or "sub/file.txt". More info: .
/// Cannot find assembly or resource.
/// Unsupported resource type.
/// Other exceptions that may be thrown by used .NET functions.
///
/// Supports resources of type byte[], string (gets UTF-8 bytes), stream.
///
public static byte[] GetBytes(string name) {
var o = _GetObject(ref name);
switch (o) {
case byte[] a: return a;
case string s: return Encoding.UTF8.GetBytes(s);
case UnmanagedMemoryStream m:
var b = new byte[m.Length];
m.Read(b);
return b;
}
throw new InvalidOperationException($"Resource '{name}' is not byte[], string or stream; it is {o.GetType().Name}.");
}
///
/// Gets GDI+ image.
///
/// Resource name, like "file.png" or "sub/file.png". More info: .
/// Cannot find assembly or resource.
/// The resource type is not stream.
/// Other exceptions that may be thrown by used .NET functions.
public static System.Drawing.Bitmap GetGdipBitmap(string name) {
return new System.Drawing.Bitmap(GetStream(name));
}
//rejected. Too simple and rare.
/////
///// Gets GDI+ icon.
/////
///// Resource name, like "file.ico" or "sub/file.ico". More info: .
///// Cannot find assembly or resource.
///// The resource type is not stream.
///// Other exceptions that may be thrown by used .NET functions.
//public static System.Drawing.Icon GetGdipIcon(string name) {
// return new System.Drawing.Icon(GetStream(name));
//}
///
/// Gets WPF image or icon that can be used as ImageSource.
///
/// Resource name, like "file.png" or "sub/file.png". More info: .
/// Cannot find assembly or resource.
/// The resource type is not stream.
/// Other exceptions that may be thrown by used .NET functions.
public static BitmapFrame GetWpfImage(string name) {
return BitmapFrame.Create(GetStream(name));
}
///
/// Gets WPF object from XAML resource, for example image.
///
/// An object of type of the XAML root object, for example if .
/// Resource name, like "file.xaml" or "sub/file.xaml". More info: .
/// Cannot find assembly or resource.
/// The resource type is not stream.
/// Other exceptions that may be thrown by used .NET functions.
public static object GetXamlObject(string name) {
return XamlReader.Load(GetStream(name));
}
///
/// Gets WPF image element from XAML or other image resource.
///
/// Resource name, like "file.png" or "sub/file.xaml". More info: .
/// Cannot find assembly or resource.
/// The resource type is not stream.
/// Other exceptions that may be thrown by used .NET functions.
///
/// If name ends with ".xaml" (case-insensitive), calls . Else returns with Source = .
///
public static FrameworkElement GetWpfImageElement(string name) {
if (name.Ends(".xaml", true)) return (FrameworkElement)GetXamlObject(name);
return new Image { Source = GetWpfImage(name) };
}
//probably not useful
/////
///// Gets WPF image as BitmapImage.
/////
///// Resource name, like "file.png" or "sub/file.png". More info: .
///// Cannot find assembly or resource.
///// The resource type is not stream.
///// Other exceptions that may be thrown by used .NET functions.
//public static BitmapImage GetWpfBitmapImage(string name) {
// var st = GetStream(name);
// var bi = new BitmapImage();
// bi.BeginInit();
// bi.CacheOption = BitmapCacheOption.OnLoad;
// bi.StreamSource = st;
// bi.EndInit();
// return bi;
//}
///
/// Returns true if string starts with "resource:" or "resources/".
///
public static bool HasResourcePrefix(string s) {
return s.Starts("resource:") || s.Starts("resources/")/* || s.Starts("pack:")*/;
}
//[MethodImpl(MethodImplOptions.NoInlining)] //avoid loading WPF dlls if no "pack:"
//static UnmanagedMemoryStream _Pack(string name) {
// if (script.role == SRole.MiniProgram && !name.Contains(";component/") && name.Starts("pack://application:,,,/")) name = name.Insert(23, script.name + ";component/");
// if (Application.Current == null) new Application();
// return Application.GetResourceStream(new Uri(name)).Stream as UnmanagedMemoryStream;
//}
static object _GetObject(ref string name)
=> _TryGetObject(ref name) ?? throw new FileNotFoundException($"Cannot find resource '{name}'.");
static object _TryGetObject(ref string name) {
var rs = _RS(ref name, true);
if (rs == null) return null;
var r = rs.GetObject(name);
if (r == null) r = rs.GetObject(name.Lower());
return r;
}
static ResourceSet _RS(ref string name, bool noThrow = false) {
if (name.Starts("resource:")) name = name[9..];
string asmName = "";
if (name is ['<', ..] or ['*', '<', ..]) {
int i = name[0] == '*' ? 2 : 1;
int j = name.IndexOf('>', i);
if (j >= i) {
asmName = name[i..j];
name = name[++j..];
}
}
lock (s_dict) {
if (!s_dict.TryGetValue(asmName, out var rs)) {
var asm = asmName.Length == 0 ? AssemblyUtil_.GetEntryAssembly() : _FindAssembly(asmName);
if (asm == null) return noThrow ? null : throw new FileNotFoundException($"Cannot find loaded resource assembly '{asmName}'.");
var rm = new ResourceManager(asm.GetName().Name + ".g", asm);
rs = rm.GetResourceSet(CultureInfo.InvariantCulture, true, false);
if (rs == null) return noThrow ? null : throw new FileNotFoundException($"Cannot find resources in assembly '{asmName}'.");
s_dict.Add(asmName, rs);
}
return rs;
}
}
static readonly Dictionary s_dict = new(StringComparer.OrdinalIgnoreCase);
static Assembly _FindAssembly(string name) {
foreach (var v in AppDomain.CurrentDomain.GetAssemblies()) if (v.GetName().Name.Eqi(name)) return v;
return null;
}
}
================================================
FILE: Au/Au.More/SecurityUtil.cs
================================================
namespace Au.More
{
///
/// Security-related functions, such as enabling privileges.
///
public static class SecurityUtil
{
///
/// Enables or disables a privilege for this process.
///
/// false if failed. Supports .
public static bool SetPrivilege(string privilegeName, bool enable, string computer = null) {
bool ok = false;
var p = new Api.TOKEN_PRIVILEGES { PrivilegeCount = 1, Privileges = new Api.LUID_AND_ATTRIBUTES { Attributes = enable ? 2u : 0 } }; //SE_PRIVILEGE_ENABLED
if (Api.LookupPrivilegeValue(computer, privilegeName, out p.Privileges.Luid)) {
Api.OpenProcessToken(Api.GetCurrentProcess(), Api.TOKEN_ADJUST_PRIVILEGES, out Handle_ hToken);
Api.AdjustTokenPrivileges(hToken, false, p, 0, null, default);
ok = 0 == lastError.code;
hToken.Dispose();
}
return ok;
}
}
}
================================================
FILE: Au/Au.More/WaitableTimer.cs
================================================
namespace Au.More;
///
/// Wraps a waitable timer handle.
///
///
/// More info: API CreateWaitableTimer.
///
public class WaitableTimer : WaitHandle {
WaitableTimer(IntPtr h) => SafeWaitHandle = new Microsoft.Win32.SafeHandles.SafeWaitHandle(h, true);
///
/// Calls API CreateWaitableTimer and creates a object that wraps the timer handle.
///
///
/// Timer name. If a timer with this name already exists, opens it if possible. If null, creates unnamed timer.
/// Failed. For example, a non-timer kernel object with this name already exists.
public static WaitableTimer Create(bool manualReset = false, string timerName = null) {
var h = Api.CreateWaitableTimer(Api.SECURITY_ATTRIBUTES.ForLowIL, manualReset, timerName);
if (h.Is0) throw new AuException(0, "*create timer");
return new WaitableTimer(h);
}
///
/// Calls API OpenWaitableTimer and creates a object that wraps the timer handle.
///
/// Timer name. Fails if it does not exist; to open-or-create use .
/// See Synchronization Object Security and Access Rights. The default value TIMER_MODIFY_STATE|SYNCHRONIZE allows to set and wait.
///
/// If fails, return null, don't throw exception. Supports .
/// Failed. For example, the timer does not exist.
public static WaitableTimer Open(string timerName, uint access = Api.TIMER_MODIFY_STATE | Api.SYNCHRONIZE, bool inheritHandle = false, bool noException = false) {
var h = Api.OpenWaitableTimer(access, inheritHandle, timerName);
if (h.Is0) {
var e = lastError.code;
if (noException) {
lastError.code = e;
return null;
}
throw new AuException(e, "*open timer");
}
return new WaitableTimer(h);
}
///
/// Calls API SetWaitableTimer.
///
/// false if failed. Supports .
///
/// The time after which the state of the timer is to be set to signaled. It is relative time (from now).
/// If positive, in milliseconds. If negative, in 100 nanosecond intervals (microseconds * 10), see FILETIME.
/// Also can be 0, to set minimal time.
/// The period of the timer, in milliseconds. If 0, the timer is signaled once. If greater than 0, the timer is periodic.
/// dueTime*10000 is greater than .
public bool Set(long dueTime, int period = 0) {
if (dueTime > 0) dueTime = -checked(dueTime * 10000);
return Api.SetWaitableTimer(this.SafeWaitHandle.DangerousGetHandle(), ref dueTime, period, default, default, false);
}
///
/// Calls API SetWaitableTimer.
///
/// false if failed. Supports .
/// The UTC date/time at which the state of the timer is to be set to signaled.
/// The period of the timer, in milliseconds. If 0, the timer is signaled once. If greater than 0, the timer is periodic.
public bool SetAbsolute(DateTime dueTime, int period = 0) {
var t = dueTime.ToFileTimeUtc();
return Api.SetWaitableTimer(this.SafeWaitHandle.DangerousGetHandle(), ref t, period, default, default, false);
}
}
================================================
FILE: Au/Au.More/WinEventHook.cs
================================================
namespace Au.More {
///
/// Helps with UI element event hooks. See API SetWinEventHook.
///
///
/// The thread that uses hooks must process Windows messages. For example have a window/dialog/messagebox, or use a "wait-for" function that dispatches messages or has such option (see ).
///
/// The variable should be disposed when don't need, or at least unhooked, either explicitly (call or in same thread) or with using. Can do it in hook procedure.
///
///
/// {
/// print.it(x.event_, x.w);
/// var e = x.GetElm();
/// print.it(e);
/// if(x.w.ClassNameIs("Shell_TrayWnd")) stop = true;
/// });
/// dialog.show("hook");
/// //or
/// //wait.doEventsUntil(-10, () => stop); //wait max 10 s for activated taskbar
/// //print.it("the end");
/// ]]>
///
[DebuggerStepThrough]
public sealed class WinEventHook : IDisposable {
IntPtr[] _a;
Api.WINEVENTPROC _proc1; //our intermediate hook proc that calls _proc2
Action _proc2; //caller's hook proc
[ThreadStatic] static List t_antiGC;
///
/// Sets a hook for an event or a range of events.
///
/// The lowest event constant value in the range of events. Can be to indicate the lowest possible event value. Events reference: SetWinEventHook. Value 0 is ignored.
/// The highest event constant value in the range of events. Can be to indicate the highest possible event value. If 0, uses eventMin.
/// The hook procedure (function that handles hook events).
/// The id of the process from which the hook function receives events. If 0 - all processes on the current desktop.
/// The native id of the thread from which the hook function receives events. If 0 - all threads.
///
/// Failed.
/// See .
public WinEventHook(EEvent eventMin, EEvent eventMax, Action hookProc, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {
Not_.Null(hookProc);
_proc1 = _HookProc;
Hook(eventMin, eventMax, idProcess, idThread, flags);
_proc2 = hookProc;
(t_antiGC ??= new()).Add(this);
}
///
/// Sets multiple hooks.
///
/// Events. Reference: API SetWinEventHook. Elements with value 0 are ignored.
///
public WinEventHook(EEvent[] events, Action hookProc, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {
Not_.Null(hookProc);
_proc1 = _HookProc;
Hook(events, idProcess, idThread, flags);
_proc2 = hookProc;
(t_antiGC ??= new()).Add(this);
}
/// Hooks are already set and not called.
///
public void Hook(EEvent eventMin, EEvent eventMax = 0, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {
_Throw1();
_a = new IntPtr[1];
_SetHook(0, eventMin, eventMax, idProcess, idThread, flags);
}
/// Hooks are already set and not called.
///
public void Hook(EEvent[] events, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {
_Throw1();
_a = new IntPtr[events.Length];
for (int i = 0; i < events.Length; i++) _SetHook(i, events[i], 0, idProcess, idThread, flags);
}
void _SetHook(int i, EEvent eMin, EEvent eMax, int idProcess, int idThread, EHookFlags flags) {
if (eMin == 0) return;
if (eMax == 0) eMax = eMin;
var hh = Api.SetWinEventHook(eMin, eMax, default, _proc1, idProcess, idThread, flags);
if (hh == default) {
var ec = lastError.code;
Unhook();
throw new AuException(ec, "*set hook for " + eMin.ToString());
}
_a[i] = hh;
}
void _Throw1() {
if (_a != null) throw new InvalidOperationException();
if (_proc1 == null) throw new ObjectDisposedException(nameof(WinEventHook));
}
///
/// Adds a hook for an event or a range of events.
///
/// An int value greater than 0 that can be used with .
///
/// Parameters are the same as of the constructor, but values can be different.
///
/// This function together with can be used to temporarily add/remove one or more hooks while using the same variable and hook procedure. Don't need to call before.
///
///
public int Add(EEvent eventMin, EEvent eventMax = 0, int idProcess = 0, int idThread = 0, EHookFlags flags = 0) {
if (_proc1 == null) throw new ObjectDisposedException(nameof(WinEventHook));
int i = 0;
if (_a == null) {
_a = new IntPtr[1];
} else {
for (; i < _a.Length; i++) if (_a[i] == default) goto g1;
Array.Resize(ref _a, i + 1);
}
g1:
_SetHook(i, eventMin, eventMax, idProcess, idThread, flags);
return i + 1;
}
///
/// Removes a hook added by .
///
/// A return value of .
///
public void Remove(int addedId) {
addedId--;
if (_a == null || (uint)addedId >= _a.Length || _a[addedId] == default) throw new ArgumentException();
if (!Api.UnhookWinEvent(_a[addedId])) print.warning("Failed to unhook WinEventHook.");
_a[addedId] = default;
}
/////
///// True if hooks are set.
/////
//public bool Installed => _a != null;
///
/// Removes all hooks.
///
///
/// Does nothing if already removed or wasn't set.
/// Must be called from the same thread that sets the hook.
///
public void Unhook() {
if (_a != null) {
foreach (var hh in _a) {
if (hh == default) continue;
if (!Api.UnhookWinEvent(hh)) print.warning("WinEventHook.Unhook() failed.");
}
_a = null;
}
}
///
/// Calls .
///
public void Dispose() {
Unhook();
_proc1 = null;
t_antiGC.Remove(this);
GC.SuppressFinalize(this);
}
///
/// Prints a warning if the variable is not disposed. Cannot dispose in finalizer.
///
~WinEventHook() {
//MSDN: UnhookWinEvent fails if called from a thread different from the call that corresponds to SetWinEventHook.
if (_a != null) print.warning("Non-disposed WinEventHook variable.");
}
void _HookProc(IntPtr hHook, EEvent ev, wnd w, EObjid idObject, int idChild, int thread, int time) {
try {
_proc2(new HookData.WinEvent(this, ev, w, idObject, idChild, thread, time));
}
catch (Exception ex) { WindowsHook.OnException_(ex); }
}
}
}
namespace Au.Types {
public static partial class HookData {
///
/// Hook data for the hook procedure set by .
/// More info: API WinEventProc.
///
public unsafe struct WinEvent {
/// The caller object of your hook procedure. For example can be used to unhook.
public readonly WinEventHook hook;
/// API WinEventProc
public readonly EEvent event_;
/// API WinEventProc
public readonly wnd w;
/// API WinEventProc
public readonly EObjid idObject;
/// API WinEventProc
public readonly int idChild;
/// API WinEventProc
public readonly int thread;
/// API WinEventProc
public readonly int time;
internal WinEvent(WinEventHook hook, EEvent event_, wnd hwnd, EObjid idObject, int idChild, int thread, int time) {
this.hook = hook;
this.event_ = event_;
this.w = hwnd;
this.idObject = idObject;
this.idChild = idChild;
this.thread = thread;
this.time = time;
}
///
/// Calls .
///
public elm GetElm() {
return elm.fromEvent(w, idObject, idChild);
}
}
}
}
================================================
FILE: Au/Au.More/WindowsHook.cs
================================================
namespace Au.More {
///
/// Wraps API SetWindowsHookEx.
///
///
/// Hooks are used to receive notifications about various system events. Keyboard and mouse input, window messages, various window events.
///
/// Threads that use hooks must process Windows messages. For example have a window/dialog/messagebox, or use a "wait-for" function that dispatches messages or has such option (see ).
///
/// The variable should be disposed when don't need, or at least unhooked, either explicitly (call or in same thread) or with using. Can do it in hook procedure.
///
/// Avoid many hooks. Each low-level keyboard or mouse hook makes the computer slower, even if the hook procedure is fast. On each input event (key down, key up, mouse move, click, wheel) Windows sends a message to your thread.
///
/// To receive hook events is used a callback function, aka hook procedure. Hook procedures of some hook types can block some events (call or return true). Blocked events are not sent to apps and older hooks.
///
/// Delegates of hook procedures are protected from GC until called or until the thread ends, even of unreferenced WindowsHook variables.
///
/// UI element functions may fail in hook procedures of low-level keyboard and mouse hooks. Workarounds exist.
///
/// Exists an alternative way to monitor keyboard or mouse events - raw input API. Good: less overhead; can detect from which device the input event came. Bad: cannot block events; incompatible with low-level keyboard hooks. This library does not have functions to make the API easier to use.
///
[DebuggerStepThrough]
public sealed class WindowsHook : IDisposable {
IntPtr _hh; //HHOOK
readonly Api.HOOKPROC _proc1; //our intermediate dispatcher hook proc that calls _proc2
Delegate _proc2; //caller's hook proc
readonly string _hookTypeString; //"Keyboard" etc
readonly int _hookType; //Api.WH_
readonly bool _ignoreAuInjected;
[ThreadStatic] static List t_antiGC;
///
/// Sets a low-level keyboard hook (WH_KEYBOARD_LL).
/// See API SetWindowsHookEx.
///
/// New object that manages the hook.
///
/// The hook procedure (function that handles hook events).
/// Must return as soon as possible. More info: .
/// If calls or (true), the event is not sent to apps and other hooks.
/// Event data cannot be modified.
/// NOTE: When the hook procedure returns, the parameter variable becomes invalid and unsafe to use. If you need the data for later use, copy its properties and not whole variable.
///
/// Don't call the hook procedure for events sent by functions of this library. Default true.
/// Set hook now. Default true.
/// Failed.
///
/// {
/// print.it(x);
/// if(x.vkCode == KKey.Escape) { stop = true; x.BlockEvent(); }
/// });
/// dialog.show("hook");
/// //or
/// //wait.doEventsUntil(-10, () => stop); //wait max 10 s for Esc key
/// //print.it("the end");
/// ]]>
///
public static WindowsHook Keyboard(Action hookProc, bool ignoreAuInjected = true, bool setNow = true)
=> new(Api.WH_KEYBOARD_LL, hookProc, setNow, 0, ignoreAuInjected);
///
/// Sets a low-level mouse hook (WH_MOUSE_LL).
/// See API SetWindowsHookEx.
///
/// New object that manages the hook.
///
/// The hook procedure (function that handles hook events).
/// Must return as soon as possible. More info: .
/// If calls or (true), the event is not sent to apps and other hooks.
/// Event data cannot be modified.
/// NOTE: When the hook procedure returns, the parameter variable becomes invalid and unsafe to use. If you need the data for later use, copy its properties and not whole variable.
///
/// Don't call the hook procedure for events sent by functions of this library. Default true.
/// Set hook now. Default true.
/// Failed.
///
/// {
/// print.it(x);
/// if(x.Event == HookData.MouseEvent.RightButton) { stop = x.IsButtonUp; x.BlockEvent(); }
/// });
/// dialog.show("hook");
/// //or
/// //wait.doEventsUntil(-10, () => stop); //wait max 10 s for right-click
/// //print.it("the end");
/// ]]>
///
public static WindowsHook Mouse(Action hookProc, bool ignoreAuInjected = true, bool setNow = true)
=> new(Api.WH_MOUSE_LL, hookProc, setNow, 0, ignoreAuInjected);
internal static WindowsHook MouseRaw_(Func hookProc, bool ignoreAuInjected = true, bool setNow = true)
=> new(Api.WH_MOUSE_LL, hookProc, setNow, 0, ignoreAuInjected, "Mouse");
///
/// Sets a WH_CBT hook for a thread of this process.
/// See API SetWindowsHookEx.
///
/// New object that manages the hook.
///
/// Hook procedure (function that handles hook events).
/// Must return as soon as possible.
/// If returns true, the event is canceled. For some events you can modify some fields of event data.
/// NOTE: When the hook procedure returns, the parameter variable becomes invalid and unsafe to use. If you need the data for later use, copy its properties and not the variable.
///
/// Native thread id, or 0 for this thread. The thread must belong to this process.
/// Set hook now. Default true.
/// Failed.
///
/// {
/// print.it(x.code);
/// switch(x.code) {
/// case HookData.CbtEvent.ACTIVATE:
/// print.it(x.Hwnd);
/// break;
/// case HookData.CbtEvent.CREATEWND:
/// var c=x.CreationInfo->lpcs;
/// print.it(x.Hwnd, c->x, c->lpszName);
/// c->x=500;
/// break;
/// }
/// return false;
/// });
/// dialog.showOkCancel("hook");
/// //new Form().ShowDialog(); //to test MINMAX
/// ]]>
///
public static WindowsHook ThreadCbt(Func hookProc, int threadId = 0, bool setNow = true)
=> new(Api.WH_CBT, hookProc, setNow, threadId);
///
/// Sets a WH_GETMESSAGE hook for a thread of this process.
/// See API SetWindowsHookEx.
///
/// New object that manages the hook.
///
/// The hook procedure (function that handles hook events).
/// Must return as soon as possible.
/// The event cannot be canceled. As a workaround, you can set msg->message=0. Also can modify other fields.
/// NOTE: When the hook procedure returns, the pointer field of the parameter variable becomes invalid and unsafe to use.
///
/// Native thread id, or 0 for this thread. The thread must belong to this process.
/// Set hook now. Default true.
/// Failed.
///
/// {
/// print.it(x.msg->ToString(), x.PM_NOREMOVE);
/// });
/// dialog.show("hook");
/// ]]>
///
public static WindowsHook ThreadGetMessage(Action hookProc, int threadId = 0, bool setNow = true)
=> new(Api.WH_GETMESSAGE, hookProc, setNow, threadId);
///
/// Sets a WH_GETMESSAGE hook for a thread of this process.
/// See API SetWindowsHookEx.
///
/// New object that manages the hook.
///
/// The hook procedure (function that handles hook events).
/// Must return as soon as possible.
/// If returns true, the event is canceled.
///
/// Native thread id, or 0 for this thread. The thread must belong to this process.
/// Set hook now. Default true.
/// Failed.
///
/// {
/// print.it(x.key, 0 != (x.lParam & 0x80000000) ? "up" : "", x.lParam, x.PM_NOREMOVE);
/// return false;
/// });
/// dialog.show("hook");
/// ]]>
///
public static WindowsHook ThreadKeyboard(Func hookProc, int threadId = 0, bool setNow = true)
=> new(Api.WH_KEYBOARD, hookProc, setNow, threadId);
///
/// Sets a WH_MOUSE hook for a thread of this process.
/// See API SetWindowsHookEx.
///
/// New object that manages the hook.
///
/// The hook procedure (function that handles hook events).
/// Must return as soon as possible.
/// If returns true, the event is canceled.
/// NOTE: When the hook procedure returns, the pointer field of the parameter variable becomes invalid and unsafe to use.
///
/// Native thread id, or 0 for this thread. The thread must belong to this process.
/// Set hook now. Default true.
/// Failed.
///
/// {
/// print.it(x.message, x.m->pt, x.m->hwnd, x.PM_NOREMOVE);
/// return false;
/// });
/// dialog.show("hook");
/// ]]>
///
public static WindowsHook ThreadMouse(Func hookProc, int threadId = 0, bool setNow = true)
=> new(Api.WH_MOUSE, hookProc, setNow, threadId);
///
/// Sets a WH_CALLWNDPROC hook for a thread of this process.
/// See API SetWindowsHookEx.
///
/// A new object that manages the hook.
///
/// The hook procedure (function that handles hook events).
/// Must return as soon as possible.
/// The event cannot be canceled or modified.
/// NOTE: When the hook procedure returns, the pointer field of the parameter variable becomes invalid and unsafe to use.
///
/// Native thread id, or 0 for this thread. The thread must belong to this process.
/// Set hook now. Default true.
/// Failed.
///
/// {
/// ref var m = ref *x.msg;
/// WndUtil.PrintMsg(out var s, m.hwnd, m.message, m.wParam, m.lParam);
/// print.it(s, x.sentByOtherThread);
/// });
/// dialog.show("hook");
/// ]]>
///
public static WindowsHook ThreadCallWndProc(Action hookProc, int threadId = 0, bool setNow = true)
=> new(Api.WH_CALLWNDPROC, hookProc, setNow, threadId);
///
/// Sets a WH_CALLWNDPROCRET hook for a thread of this process.
/// See API SetWindowsHookEx.
///
///
public static WindowsHook ThreadCallWndProcRet(Action hookProc, int threadId = 0, bool setNow = true)
=> new(Api.WH_CALLWNDPROCRET, hookProc, setNow, threadId);
WindowsHook(int hookType, Delegate hookProc, bool setNow, int tid, bool ignoreAuInjected = false, [CallerMemberName] string m_ = null) {
Not_.Null(hookProc);
_proc2 = hookProc;
_hookType = hookType;
_hookTypeString = m_;
_ignoreAuInjected = ignoreAuInjected;
if (hookType is Api.WH_KEYBOARD_LL or Api.WH_MOUSE_LL) {
_proc1 = _HookProcLL;
//JIT-compile our hook proc and some functions it may call. OS gives us only 300 ms by default.
if (!s_jit1) {
s_jit1 = true;
Jit_.Compile(typeof(WindowsHook), nameof(_HookProcLL));
_ = perf.ms;
_ = keys.KeyTypes_.IsMod(KKey.Shift) && _DontBlockMod;
}
} else {
_proc1 = _HookProc;
}
if (setNow) Hook(tid);
(t_antiGC ??= new()).Add(this);
}
static bool s_jit1;
///
/// Sets the hook.
///
/// If the hook type is a thread hook - thread id, or 0 for current thread. Else not used and must be 0.
/// Failed.
/// The hook is already set.
/// threadId not 0 and the hook type is not a thread hook.
///
/// Usually don't need to call this function, because the WindowsHook static methods that return a new WindowsHook object by default call it.
///
public void Hook(int threadId = 0) {
if (_proc2 == null) throw new ObjectDisposedException(nameof(WindowsHook));
if (_hh != default) throw new InvalidOperationException("The hook is already set.");
if (_hookType is Api.WH_KEYBOARD_LL or Api.WH_MOUSE_LL) {
if (threadId != 0) throw new ArgumentException("threadId must be 0");
} else if (threadId == 0) {
threadId = Api.GetCurrentThreadId();
}
_hh = Api.SetWindowsHookEx(_hookType, _proc1, default, threadId);
if (_hh == default) throw new AuException(0, "*set hook");
}
///
/// Removes the hook.
///
///
/// Does nothing if already removed or wasn't set.
/// Later you can call to set hook again.
/// Note: call instead if will not need to hook again.
///
public void Unhook() {
if (_hh != default) {
_Restore_UnhookOld();
bool ok = Api.UnhookWindowsHookEx(_hh);
if (!ok) print.warning($"WindowsHook.Unhook() failed ({_hookTypeString}). {lastError.message}");
_hh = default;
}
}
///
/// Rehooks this low-level keyboard or mouse hook.
///
///
/// Low level hooks may be occasionally disabled by the OS or other hooks. Workaround - call this function eg every 10 s in same thread. For example use . Don't call too frequently, eg every 1 s.
/// This function unhooks current hook and sets new hook. Ensures that no events are missed or duplicate during it.
///
/// The hook type isn't low-level keyboard or mouse.
public void Restore() {
if (_hookType is not (Api.WH_KEYBOARD_LL or Api.WH_MOUSE_LL)) throw new InvalidOperationException();
if (_proc2 == null) throw new ObjectDisposedException(nameof(WindowsHook));
if (_hookType is Api.WH_KEYBOARD_LL && DontRestoreKeyboardHooks_) return;
//If we simply unhook/hook here, some events are missed.
// Restoring usually takes 0.2 - 0.5 ms. And it seems the new hook starts working with a delay.
// If restoring every 10 s, could miss maybe 1/10000 triggers.
// Tested: when restoring every 15 ms, missed 3/50 triggers.
// Solution: unhook the old hook after several ms. To avoid duplicate events, unhook it in the hook proc too.
#if false
if (_hh != default) Api.UnhookWindowsHookEx(_hh);
_hh = Api.SetWindowsHookEx(_hookType, _proc1, default, 0);
if (_hh == default) throw new AuException(0, "*set hook");
#else
_Restore_UnhookOld();
var hh = Api.SetWindowsHookEx(_hookType, _proc1, default, 0);
if (hh != default) {
if (_hh != default) timer.after(10, _ => _Restore_UnhookOld());
_oldHook = _hh;
_hh = hh;
} else {
Debug_.Print("failed");
}
#endif
}
IntPtr _oldHook;
///
/// Can be used to temporarily disable of all keyboard hooks in all processes.
/// For example when a hotkey control is focused.
///
///
/// Restore is disabled when the number of =true calls is greater than the number of =false calls.
///
internal static unsafe bool DontRestoreKeyboardHooks_ {
get => SharedMemory_.Ptr->winHook.dontRestoreKeyboardHooks > 0;
set => Interlocked.Add(ref SharedMemory_.Ptr->winHook.dontRestoreKeyboardHooks, value ? 1 : -1);
}
void _Restore_UnhookOld() {
if (_oldHook != default) {
bool ok = Api.UnhookWindowsHookEx(_oldHook);
_oldHook = default;
Debug_.PrintIf(!ok, "failed to unhook old");
}
}
///
/// Returns true if the hook is set.
///
public bool IsSet => _hh != default;
/////
///// Disable warning "Non-disposed WindowsHook variable".
/////
//public bool NoWarningNondisposed { get; set; }
///
/// Calls and disposes this object.
///
public void Dispose() {
Unhook();
_proc2 = null;
t_antiGC.Remove(this);
GC.SuppressFinalize(this);
}
///
/// Prints a warning if the variable is not disposed. Cannot dispose in finalizer.
///
~WindowsHook() {
//unhooking in finalizer thread makes no sense. Must unhook in same thread, else fails.
if (_hh != default) print.warning($"Non-disposed WindowsHook ({_hookTypeString}) variable.");
//ok if unhooked but not disposed. If we are here, the thread ended and therefore don't need to remove this from t_antiGC.
}
unsafe nint _HookProc(int code, nint wParam, nint lParam) {
if (code >= 0) {
try {
bool eat = false;
switch (_proc2) {
case Func p:
eat = p(new HookData.ThreadCbt(this, code, wParam, lParam));
break;
case Action p:
p(new HookData.ThreadGetMessage(this, wParam, lParam));
break;
case Func p:
eat = p(new HookData.ThreadKeyboard(this, code, wParam, lParam));
break;
case Func p:
eat = p(new HookData.ThreadMouse(this, code, wParam, lParam));
break;
case Action p:
p(new HookData.ThreadCallWndProc(this, wParam, lParam));
break;
case Action p:
p(new HookData.ThreadCallWndProcRet(this, wParam, lParam));
break;
}
if (eat) return 1;
}
catch (Exception ex) { OnException_(ex); }
}
return Api.CallNextHookEx(default, code, wParam, lParam);
}
unsafe nint _HookProcLL(int code, nint wParam, nint lParam) {
_Restore_UnhookOld();
if (code >= 0) {
try {
//using var p1 = perf.local();
bool eat = false;
long t1 = 0;
Action pm1;
Func pm2;
switch (_proc2) {
case Action p:
var kll = (Api.KBDLLHOOKSTRUCT*)lParam;
var vk = (KKey)kll->vkCode;
if (kll->IsInjected) {
if (kll->IsInjectedByAu) {
if (kll->vkCode == 0) goto gr; //used to enable activating windows
if (!kll->IsUp) Triggers.AutotextTriggers.ResetEverywhere = true;
if (_ignoreAuInjected) goto gr;
}
if (vk == KKey.MouseX2 && kll->dwExtraInfo == 1354291109) goto gr; //QM2 sync code
} else {
//When keys.Internal_.ReleaseModAndCapsLock sends Shift to turn off CapsLock,
// hooks receive a non-injected LShift down, CapsLock down/up and injected LShift up.
// Our triggers would recover, but cannot auto-repeat. Better don't call the hookproc.
if ((vk == KKey.CapsLock || vk == KKey.LShift) && _ignoreAuInjected && _IgnoreLShiftCaps) goto gr;
//Test how our triggers recover when a modifier down or up event is lost. Or when triggers started while a modifier is down.
//if(keys.isScrollLock) {
// //if(vk == KKey.LCtrl && !kll->IsUp) { print.it("lost Ctrl down"); goto gr; }
// if(vk == KKey.LCtrl && kll->IsUp) { print.it("lost Ctrl up"); goto gr; }
//}
}
//if (keys.KeyTypes_.IsMod(vk) && _DontBlockMod) goto gr; //old version, creates problems
t1 = perf.ms;
//p1.Next();
p(new HookData.Keyboard(this, lParam)); //info: wParam is message, but it is not useful, everything is in lParam
if (eat = kll->BlockEvent) {
kll->BlockEvent = false;
if (keys.KeyTypes_.IsMod(vk) && _DontBlockMod && kll->IsUp) eat = false;
}
break;
case Action p:
pm1 = p; pm2 = null;
gm1:
var mll = (Api.MSLLHOOKSTRUCT*)lParam;
switch ((int)wParam) {
case Api.WM_LBUTTONDOWN: case Api.WM_RBUTTONDOWN: Triggers.AutotextTriggers.ResetEverywhere = true; break;
}
if (_ignoreAuInjected && mll->IsInjectedByAu) goto gr;
//API bug workaround. In DPI-scaled windows on click mhsLL->pt is logical, although on move/wheel is physical. Must be always physical.
//At first noticed only on Win10. But then noticed the same on Win7, although used to be correct. OK on Win8.1. Maybe depends on some other conditions, eg UAC IL, DPI, multimonitor.
//Now it seems the bug is fixed on Win10. Found on SO: "Microsoft fixed it in 10.0.14393"; it is version 1607, August 2, 2016; but I cannot confirm it.
//The wrong coords are the same as GetCursorPos. Only GetPhysicalCursorPos does not lie. Api.GetCursorPos is mapped to GetPhysicalCursorPos.
//Note: on WM_MOUSEMOVE Get[Physical]CursorPos returns previous coords. On other messages same as hook.
if (wParam != Api.WM_MOUSEMOVE /*&& osVersion.winVer < osVersion.win10*/) Api.GetCursorPos(out mll->pt);
t1 = perf.ms;
if (pm2 != null) {
eat = pm2(wParam, lParam);
} else {
pm1(new HookData.Mouse(this, wParam, lParam));
if (eat = mll->BlockEvent) mll->BlockEvent = false;
}
break;
case Func p: //raw mouse
pm2 = p; pm1 = null;
goto gm1;
}
//Prevent Windows disabling the low-level key/mouse hook.
// Hook proc must return in HKEY_CURRENT_USER\Control Panel\Desktop:LowLevelHooksTimeout ms.
// Default 300. On Win10 max 1000 (bigger registry value is ignored and used 1000).
// On timeout Windows:
// 1. Does not wait more. Passes the message to the next hook etc, and we cannot return 1 to block it.
// 2. Kills the hook after several such cases. Usually 6 keys or 11 mouse events.
// 3. Makes the hook useless: next times does not wait for it, and we cannot return 1 to block the event.
// Somehow does not apply 2 and 3 to some apps, eg C# apps created by Visual Studio, although applies to those created not by VS. I did not find why.
if (t1 != 0 && (t1 = perf.ms - t1) > 200 && !Debugger.IsAttached) {
if (t1 > LowLevelHooksTimeout - 50) {
var s1 = _hookType == Api.WH_KEYBOARD_LL ? "key" : "mouse";
var s2 = eat ? $" On timeout the {s1} message is passed to the active window, other hooks, etc." : null;
//print.warning($"Possible hook timeout. Hook procedure time: {t1} ms. LowLevelHooksTimeout: {LowLevelHooksTimeout} ms.{s2}"); //too slow first time
//print.it($"Warning: Possible hook timeout. Hook procedure time: {t1} ms. LowLevelHooksTimeout: {LowLevelHooksTimeout} ms.{s2}\r\n{new StackTrace(0, false)}"); //first Write() JIT 30 ms
ThreadPool.QueueUserWorkItem(s3 => print.it(s3), $"Warning: Possible hook timeout. Hook procedure time: {t1} ms. LowLevelHooksTimeout: {LowLevelHooksTimeout} ms.{s2}\r\n{new StackTrace(0, false)}"); //fast if with false. But async print can be confusing.
}
//FUTURE: print warning if t1 is >25 frequently. Unhook and don't rehook if >LowLevelHooksTimeout frequently.
Unhook();
_hh = Api.SetWindowsHookEx(_hookType, _proc1, default, 0);
}
if (eat) return 1;
}
catch (Exception ex) { OnException_(ex); }
}
gr:
return Api.CallNextHookEx(default, code, wParam, lParam);
}
///
/// Gets the max time in milliseconds allowed by Windows for low-level keyboard and mouse hook procedures.
///
///
/// Gets registry value HKEY_CURRENT_USER\Control Panel\Desktop:LowLevelHooksTimeout. If it is missing, returns 300; it is the default value used by Windows. If greater than 1000, returns 1000, because Windows 10 ignores bigger values.
///
/// If a hook procedure takes more time, Windows does not wait. Then its return value is ignored, and the event is passed to other apps, hooks, etc. After several such cases Windows may fully or partially disable the hook. This class detects such cases; then restores the hook and prints a warning. If the warning is rare, you can ignore it. If frequent, it means your hook procedure is too slow.
///
/// Callback functions of keyboard and mouse triggers are called in a hook procedure, therefore must be as fast as possible. More info: .
///
/// More info: registry LowLevelHooksTimeout.
///
/// Note: After changing the timeout in registry, it is not applied immediately. Need to log off/on.
///
public static int LowLevelHooksTimeout {
get {
if (s_lowLevelHooksTimeout == 0) {
//default 300, tested on Win10 and 7
//max 1000 on Win10. On Win7 more. Not tested on Win8. On Win7/8 may be changed by a Windows update.
s_lowLevelHooksTimeout =
Microsoft.Win32.Registry.GetValue(@"HKEY_CURRENT_USER\Control Panel\Desktop", "LowLevelHooksTimeout", null) is int v
? (int)Math.Min(1000u, (uint)v)
: 300;
}
return s_lowLevelHooksTimeout;
}
internal set {
int v = Math.Clamp(value, 0, 5000);
Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\Control Panel\Desktop", "LowLevelHooksTimeout", v);
s_lowLevelHooksTimeout = v;
}
}
static int s_lowLevelHooksTimeout;
internal static void OnException_(Exception e) {
print.warning("Unhandled exception in hook procedure. " + e.ToString(), -1);
}
[StructLayout(LayoutKind.Sequential, Size = 32)] //note: this struct is in shared memory. Size must be same in all library versions.
internal struct SharedMemoryData_ {
public long dontBlockModUntil, dontBlocLShiftCapsUntil;
public int dontRestoreKeyboardHooks;
//12 bytes reserved
}
///
/// Let other hooks (in all processes) don't block modifier key up events for timeMS milliseconds. If 0 - restore.
/// Used by mouse triggers waiting for mod keys released, to prevent inputblockers blocking mod up events, eg when sending keys/text.
/// Returns the timeout time (Environment.TickCount64 + timeMS) or 0.
///
internal unsafe long DontBlockModInOtherHooks_(long timeMS) {
_ignoreModExceptThisHook = timeMS > 0;
var r = _ignoreModExceptThisHook ? Environment.TickCount64 + timeMS : 0;
SharedMemory_.Ptr->winHook.dontBlockModUntil = r;
return r;
}
unsafe bool _DontBlockMod => SharedMemory_.Ptr->winHook.dontBlockModUntil > Environment.TickCount64 && !_ignoreModExceptThisHook;
bool _ignoreModExceptThisHook;
///
/// Let all hooks (in all processes) ignore LShift and CapsLock for timeMS milliseconds. If 0 - restore.
/// Returns the timeout time (Environment.TickCount64 + timeMS) or 0.
/// Used when turning off CapsLock with Shift.
///
internal static unsafe long IgnoreLShiftCaps_(long timeMS) {
var r = timeMS > 0 ? Environment.TickCount64 + timeMS : 0;
SharedMemory_.Ptr->winHook.dontBlocLShiftCapsUntil = r;
return r;
}
static unsafe bool _IgnoreLShiftCaps => SharedMemory_.Ptr->winHook.dontBlocLShiftCapsUntil > Environment.TickCount64;
}
}
namespace Au.Types {
///
/// Contains types of hook data for hook procedures set by and .
///
public static partial class HookData {
///
/// Event data for the hook procedure set by .
/// More info: API LowLevelKeyboardProc.
///
public unsafe struct Keyboard {
/// The caller object of your hook procedure. For example can be used to unhook.
public readonly WindowsHook hook;
readonly Api.KBDLLHOOKSTRUCT* _x;
internal Keyboard(WindowsHook hook, nint lParam) {
this.hook = hook;
_x = (Api.KBDLLHOOKSTRUCT*)lParam;
}
///
/// Call this function to steal this event from other hooks and apps.
///
public void BlockEvent() => _x->BlockEvent = true;
///
/// Is extended key.
///
public bool IsExtended => 0 != (_x->flags & Api.LLKHF_EXTENDED);
///
/// true if the event was generated by API such as SendInput.
/// false if the event was generated by the keyboard.
///
public bool IsInjected => 0 != (_x->flags & Api.LLKHF_INJECTED);
///
/// true if the event was generated by functions of this library.
///
public bool IsInjectedByAu => 0 != (_x->flags & Api.LLKHF_INJECTED) && _x->dwExtraInfo == Api.AuExtraInfo;
///
/// Key Alt is pressed.
///
public bool IsAlt => 0 != (_x->flags & Api.LLKHF_ALTDOWN);
///
/// Is key-up event.
///
public bool IsUp => 0 != (_x->flags & Api.LLKHF_UP);
///
/// If the key is a modifier key (Shift, Ctrl, Alt, Win), returns the modifier flag. Else returns 0.
///
public KMod Mod => keys.Internal_.KeyToMod((KKey)_x->vkCode);
///
/// If is a left or right modifier key code (LShift, LCtrl, LAlt, RShift, RCtrl, RAlt, RWin), returns the common modifier key code (Shift, Ctrl, Alt, Win). Else returns .
///
public KKey Key {
get {
var vk = (KKey)_x->vkCode;
switch (vk) {
case KKey.LShift: case KKey.RShift: return KKey.Shift;
case KKey.LCtrl: case KKey.RCtrl: return KKey.Ctrl;
case KKey.LAlt: case KKey.RAlt: return KKey.Alt;
case KKey.RWin: return KKey.Win;
}
return vk;
}
}
///
/// Returns true if key == or key is Shift, Ctrl, Alt or Win and is LShift/RShift, LCtrl/RCtrl, LAlt/RAlt or RWin.
///
public bool IsKey(KKey key) {
var vk = (KKey)_x->vkCode;
if (key == vk) return true;
switch (key) {
case KKey.Shift: return vk == KKey.LShift || vk == KKey.RShift;
case KKey.Ctrl: return vk == KKey.LCtrl || vk == KKey.RCtrl;
case KKey.Alt: return vk == KKey.LAlt || vk == KKey.RAlt;
case KKey.Win: return vk == KKey.RWin;
}
return false;
}
///
/// Converts flags to API SendInput flags KEYEVENTF_KEYUP and KEYEVENTF_EXTENDEDKEY.
///
internal byte SendInputFlags_ {
get {
uint f = 0;
if (IsUp) f |= Api.KEYEVENTF_KEYUP;
if (IsExtended) f |= Api.KEYEVENTF_EXTENDEDKEY;
return (byte)f;
}
}
///
public override string ToString() {
return $"{vkCode.ToString()} {(IsUp ? "up" : "")}{(IsInjected ? " (injected)" : "")}";
}
/// API KBDLLHOOKSTRUCT
public KKey vkCode => (KKey)_x->vkCode;
/// API KBDLLHOOKSTRUCT
public uint scanCode => _x->scanCode;
/// API KBDLLHOOKSTRUCT
public uint flags => _x->flags;
/// API KBDLLHOOKSTRUCT
public int time => _x->time;
/// API KBDLLHOOKSTRUCT
public nint dwExtraInfo => _x->dwExtraInfo;
internal Api.KBDLLHOOKSTRUCT* NativeStructPtr_ => _x;
}
///
/// Extra info value used by functions of this library that generate keyboard events. Low-level hooks receive it in dwExtraInfo.
///
public const int AuExtraInfo = Api.AuExtraInfo;
///
/// Hook data for the hook procedure set by .
/// More info: API LowLevelMouseProc.
///
public unsafe struct Mouse {
/// The caller object of your hook procedure. For example can be used to unhook.
public readonly WindowsHook hook;
readonly Api.MSLLHOOKSTRUCT* _x;
readonly MouseEvent _event;
internal Mouse(WindowsHook hook, nint wParam, nint lParam) {
this.hook = hook;
var p = (Api.MSLLHOOKSTRUCT*)lParam;
_x = p;
int e = (int)wParam;
switch (e) {
case Api.WM_MOUSEMOVE: IsMove = true; break;
case Api.WM_LBUTTONDOWN: case Api.WM_RBUTTONDOWN: case Api.WM_MBUTTONDOWN: IsButtonDown = true; break;
case Api.WM_LBUTTONUP: case Api.WM_RBUTTONUP: case Api.WM_MBUTTONUP: e--; IsButtonUp = true; break;
case Api.WM_XBUTTONUP: e--; IsButtonUp = true; goto g1;
case Api.WM_XBUTTONDOWN:
IsButtonDown = true;
g1:
switch (p->mouseData >> 16) { case 1: e |= 0x1000; break; case 2: e |= 0x2000; break; }
break;
case Api.WM_MOUSEWHEEL:
case Api.WM_MOUSEHWHEEL:
IsWheel = true;
int wheel = (short)(p->mouseData >> 16);
if (wheel > 0) e |= 0x1000; else if (wheel < 0) e |= 0x2000;
WheelValue = wheel;
break;
}
_event = (MouseEvent)e;
}
///
/// Call this function to steal this event from other hooks and apps.
///
public void BlockEvent() => _x->BlockEvent = true;
///
/// What event it is (button, move, wheel).
///
public MouseEvent Event => _event;
///
/// Is mouse-move event.
///
public bool IsMove { get; }
///
/// Is button-down event.
///
public bool IsButtonDown { get; }
///
/// Is button-up event.
///
public bool IsButtonUp { get; }
///
/// Is button event (down or up).
///
public bool IsButton => IsButtonDown | IsButtonUp;
///
/// Converts to .
///
/// Left, Right, Middle, X1, X2 or 0. The down/up/double flags not used.
public MButton Button {
get {
return _event switch {
MouseEvent.LeftButton => MButton.Left,
MouseEvent.RightButton => MButton.Right,
MouseEvent.MiddleButton => MButton.Middle,
MouseEvent.X1Button => MButton.X1,
MouseEvent.X2Button => MButton.X2,
_ => 0,
};
}
}
///
/// Is wheel event.
///
public bool IsWheel { get; }
///
/// true if the event was generated by API such as SendInput.
/// false if the event was generated by the mouse.
///
public bool IsInjected => 0 != (flags & Api.LLMHF_INJECTED);
///
/// true if the event was generated by functions of this library.
///
public bool IsInjectedByAu => IsInjected && dwExtraInfo == Api.AuExtraInfo;
///
/// Wheel rotation amount, 120 for 1 full tick. Negative if backward.
/// Usually 120 or -120, but some devices or software may produce smaller or bigger values.
///
public int WheelValue { get; }
///
public override string ToString() {
var ud = ""; if (IsButtonDown) ud = "down"; else if (IsButtonUp) ud = "up";
return $"{Event.ToString()} {ud} {pt.ToString()}{(IsInjected ? " (injected)" : "")}";
}
/// API MSLLHOOKSTRUCT
public POINT pt => _x->pt;
/// API MSLLHOOKSTRUCT
public uint mouseData => _x->mouseData;
/// API MSLLHOOKSTRUCT
public uint flags => _x->flags;
/// API MSLLHOOKSTRUCT
public int time => _x->time;
/// API MSLLHOOKSTRUCT
public nint dwExtraInfo => _x->dwExtraInfo;
internal Api.MSLLHOOKSTRUCT* NativeStructPtr_ => _x;
}
///
/// Mouse hook event types. See .
///
public enum MouseEvent {
#pragma warning disable 1591 //no XML doc
Move = 0x0200, //WM_MOUSEMOVE
LeftButton = 0x0201, //WM_LBUTTONDOWN
RightButton = 0x0204, //WM_RBUTTONDOWN
MiddleButton = 0x0207, //WM_MBUTTONDOWN
X1Button = 0x120B, //WM_XBUTTONDOWN | 0x1000
X2Button = 0x220B, //WM_XBUTTONDOWN | 0x2000
WheelForward = 0x120A, //WM_WHEEL | 0x1000
WheelBackward = 0x220A, //WM_WHEEL | 0x2000
WheelRight = 0x120E, //WM_HWHEEL | 0x1000
WheelLeft = 0x220E, //WM_HWHEEL | 0x2000
#pragma warning restore 1591
}
///
/// Hook data for the hook procedure set by .
/// More info: API CBTProc.
///
public struct ThreadCbt {
/// The caller object of your hook procedure. For example can be used to unhook.
public readonly WindowsHook hook;
/// API CBTProc
public readonly CbtEvent code;
/// API CBTProc
public readonly nint wParam;
/// API CBTProc
public readonly nint lParam;
/// Window handle.
public wnd Hwnd => code switch { CbtEvent.ACTIVATE or CbtEvent.CREATEWND or CbtEvent.DESTROYWND or CbtEvent.MINMAX or CbtEvent.MOVESIZE or CbtEvent.SETFOCUS => (wnd)wParam, _ => default };
internal ThreadCbt(WindowsHook hook, int code, nint wParam, nint lParam) {
this.hook = hook;
this.code = (CbtEvent)code;
this.wParam = wParam;
this.lParam = lParam;
}
///
/// Gets event info.
///
/// is not .
public unsafe CBTACTIVATESTRUCT* ActivationInfo => code == CbtEvent.ACTIVATE ? (CBTACTIVATESTRUCT*)lParam : throw new InvalidOperationException();
///
/// API CBTACTIVATESTRUCT.
///
public struct CBTACTIVATESTRUCT {
///
public bool fMouse;
///
public wnd hWndActive;
}
///
/// Gets event info.
/// You can modify x, y, cx, cy, and hwndInsertAfter.
///
/// is not .
public unsafe CBT_CREATEWND* CreationInfo => code == CbtEvent.CREATEWND ? (CBT_CREATEWND*)lParam : throw new InvalidOperationException();
///
/// API CBT_CREATEWND.
///
public unsafe struct CBT_CREATEWND {
///
public CREATESTRUCT* lpcs;
///
public wnd hwndInsertAfter;
}
//rejected. Rarely used or too simple.
/////
///// Gets event info. Returns the mouse message.
/////
///// MOUSEHOOKSTRUCT.
///// is not CbtEvent.CLICKSKIPPED.
//public unsafe uint MouseInfo(out MOUSEHOOKSTRUCT* m) {
// if (code != CbtEvent.CLICKSKIPPED) throw new InvalidOperationException();
// m = (MOUSEHOOKSTRUCT*)lParam;
// return (uint)wParam;
//}
/////
///// Gets event info. Returns the key code.
/////
///// lParam of the key message. Specifies the repeat count, scan code, etc. See API WM_KEYDOWN.
///// is not CbtEvent.KEYSKIPPED.
//public KKey KeyInfo(out uint lParam) {
// if (code != CbtEvent.KEYSKIPPED) throw new InvalidOperationException();
// lParam = (uint)this.lParam;
// return (KKey)(uint)wParam;
//}
/////
///// Gets event info. Returns the window handle.
/////
///// The previously focused window, or default(wnd).
///// is not CbtEvent.SETFOCUS.
//public wnd FocusInfo(out wnd wLostFocus) {
// if (code != CbtEvent.SETFOCUS) throw new InvalidOperationException();
// wLostFocus = (wnd)lParam;
// return (wnd)wParam;
//}
/////
///// Gets event info.
/////
///// is not CbtEvent.MOVESIZE.
//public unsafe RECT* MoveSizeInfo => code == CbtEvent.MOVESIZE ? (RECT*)lParam : throw new InvalidOperationException();
/////
///// Gets event info.
///// Returns the new show state. See API ShowWindow. Minimized 6, maximized 3, restored 9.
/////
///// is not CbtEvent.MINMAX.
//public int MinMaxInfo => code == CbtEvent.MINMAX ? (int)lParam & 0xffff : throw new InvalidOperationException();
}
///
/// CBT hook event types. Used with .
/// More info: API CBTProc.
///
public enum CbtEvent {
#pragma warning disable 1591 //no XML doc
MOVESIZE = 0,
MINMAX = 1,
//QS = 2,
CREATEWND = 3,
DESTROYWND = 4,
ACTIVATE = 5,
CLICKSKIPPED = 6,
KEYSKIPPED = 7,
SYSCOMMAND = 8,
SETFOCUS = 9,
#pragma warning restore 1591
}
///
/// Hook data for the hook procedure set by .
/// More info: API GetMsgProc.
///
public unsafe struct ThreadGetMessage {
/// The caller object of your hook procedure. For example can be used to unhook.
public readonly WindowsHook hook;
///
/// The message has not been removed from the queue, because called API PeekMessage with flag PM_NOREMOVE.
///
public readonly bool PM_NOREMOVE;
///
/// Message parameters.
/// API MSG.
///
public readonly MSG* msg;
internal ThreadGetMessage(WindowsHook hook, nint wParam, nint lParam) {
this.hook = hook;
PM_NOREMOVE = (uint)wParam == Api.PM_NOREMOVE;
msg = (MSG*)lParam;
}
}
///
/// Hook data for the hook procedure set by .
/// More info: API KeyboardProc.
///
public struct ThreadKeyboard {
/// The caller object of your hook procedure. For example can be used to unhook.
public readonly WindowsHook hook;
///
/// The message has not been removed from the queue, because called API PeekMessage with flag PM_NOREMOVE.
///
public readonly bool PM_NOREMOVE;
///
/// The key code.
///
public readonly KKey key;
///
/// lParam of the key message. Specifies the key state, scan code, etc. See API KeyboardProc.
///
public readonly uint lParam;
///
/// Is key-up event.
///
public bool IsUp => 0 != (lParam & 0x80000000);
internal ThreadKeyboard(WindowsHook hook, int code, nint wParam, nint lParam) {
this.hook = hook;
PM_NOREMOVE = code == Api.HC_NOREMOVE;
key = (KKey)(uint)wParam;
this.lParam = (uint)lParam;
}
}
///
/// Hook data for the hook procedure set by .
/// More info: API MouseProc.
///
public unsafe struct ThreadMouse {
/// The caller object of your hook procedure. For example can be used to unhook.
public readonly WindowsHook hook;
///
/// The message has not been removed from the queue, because called API PeekMessage with flag PM_NOREMOVE.
///
public readonly bool PM_NOREMOVE;
///
/// The mouse message, for example WM_MOUSEMOVE.
///
public readonly uint message;
///
/// More info about the mouse message.
/// API MOUSEHOOKSTRUCT.
///
public readonly MOUSEHOOKSTRUCT* m;
internal ThreadMouse(WindowsHook hook, int code, nint wParam, nint lParam) {
this.hook = hook;
PM_NOREMOVE = code == Api.HC_NOREMOVE;
message = (uint)wParam;
m = (MOUSEHOOKSTRUCT*)lParam;
}
}
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
/// API MOUSEHOOKSTRUCT
public struct MOUSEHOOKSTRUCT {
public POINT pt;
public wnd hwnd;
public int wHitTestCode;
public nint dwExtraInfo;
}
/// API CWPSTRUCT
public struct CWPSTRUCT {
public nint lParam;
public nint wParam;
public int message;
public wnd hwnd;
}
/// API CWPRETSTRUCT
public struct CWPRETSTRUCT {
public nint lResult;
public nint lParam;
public nint wParam;
public int message;
public wnd hwnd;
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
///
/// Hook data for the hook procedure set by .
/// More info: API CallWndProc.
///
public unsafe struct ThreadCallWndProc {
/// The caller object of your hook procedure. For example can be used to unhook.
public readonly WindowsHook hook;
///
/// True if the message was sent by another thread.
///
public readonly bool sentByOtherThread; //note: incorrect info in MSDN
///
/// Message parameters.
/// API CWPSTRUCT.
///
public readonly CWPSTRUCT* msg;
internal ThreadCallWndProc(WindowsHook hook, nint wParam, nint lParam) {
this.hook = hook;
sentByOtherThread = 0 != wParam;
msg = (CWPSTRUCT*)lParam;
}
}
///
/// Hook data for the hook procedure set by .
/// More info: API CallWndRetProc.
///
public unsafe struct ThreadCallWndProcRet {
/// The caller object of your hook procedure. For example can be used to unhook.
public readonly WindowsHook hook;
///
/// True if the message was sent by another thread.
///
public readonly bool sentByOtherThread; //note: incorrect info in MSDN
///
/// Message parameters and the return value.
/// API CWPRETSTRUCT.
///
public readonly CWPRETSTRUCT* msg;
internal ThreadCallWndProcRet(WindowsHook hook, nint wParam, nint lParam) {
this.hook = hook;
sentByOtherThread = 0 != wParam;
msg = (CWPRETSTRUCT*)lParam;
}
}
///
/// Calls API API ReplyMessage, which allows to use and COM in the hook procedure.
///
///
/// Don't notify the target window about the event, and don't call other hook procedures.
/// This value is used instead of the return value of the hook procedure, which is ignored.
///
///
/// It can be used as a workaround for this problem: in low-level hook procedure some functions don't work with some windows. For example cannot get a UI element or use a COM object. Error/exception "An outgoing call cannot be made since the application is dispatching an input-synchronous call (0x8001010D)".
///
public static void ReplyMessage(bool cancelEvent) => Api.ReplyMessage(cancelEvent ? 1 : 0);
}
}
================================================
FILE: Au/Au.More/WinformsControlNames.cs
================================================
namespace Au.More;
///
/// Gets programming names of .NET Windows Forms controls.
///
///
/// Usually each control has a unique name. It's the property. Useful to identify controls without a classic name/text.
/// The control id of these controls is not useful, it is not constant.
///
public sealed class WinformsControlNames : IDisposable {
ProcessMemory _pm;
wnd _w;
///
public void Dispose() {
if (_pm != null) { _pm.Dispose(); _pm = null; }
GC.SuppressFinalize(this);
}
static readonly int WM_GETCONTROLNAME = Api.RegisterWindowMessage("WM_GETCONTROLNAME");
///
/// Prepares to get control names.
///
/// Any top-level or child window of that process.
/// w invalid.
/// Failed to allocate process memory (see ) needed to get control names, usually because of [](xref:uac).
public WinformsControlNames(wnd w) {
_pm = new ProcessMemory(w, 4096); //throws
_w = w;
}
///
/// Gets control name.
///
/// null if failed or the name is empty.
/// The control. Can be a top-level window too. Must be of the same process as the window specified in the constructor.
public string GetControlName(wnd c) {
if (_pm == null) return null;
if (!IsWinformsControl(c)) return null;
if (!c.SendTimeout(5000, out var R, WM_GETCONTROLNAME, 4096, _pm.Mem) || (int)R < 1) return null;
int len = (int)R - 1;
if (len == 0) return "";
return _pm.ReadCharString(len);
}
///
/// Returns true if window class name starts with "WindowsForms".
/// Usually it means that we can get Windows Forms control name of w and its child controls.
///
/// The window. Can be top-level or control.
public static bool IsWinformsControl(wnd w) {
return w.ClassNameIs("WindowsForms*");
}
///
/// Gets the programming name of a Windows Forms control.
///
/// null if it is not a Windows Forms control or if failed.
/// The control. Can be top-level window too.
///
/// This function is easy to use and does not throw exceptions. However, when you need names of multiple controls of a single window, better create a instance (once) and for each control call its GetControlName method, it will be faster.
public static string GetSingleControlName(wnd c) {
if (!IsWinformsControl(c)) return null;
try {
using (var x = new WinformsControlNames(c)) return x.GetControlName(c);
}
catch { }
return null;
}
//Don't use this cached version, it does not make significantly faster. Also, keeping process handle in such a way is not good, would need to use other thread to close it after some time.
/////
///// Gets programming name of a Windows Forms control.
///// Returns null if it is not a Windows Forms control or if failed.
/////
///// The control. Can be top-level window too.
///// When need to get control names repeatedly or quite often, this function can be faster than creating instance each time and calling its GetControlName method, because this function remembers the last used process etc. Also it is easier to use and does not throw exceptions.
//public static string GetSingleControlName(wnd c)
//{
// if(!IsWinformsControl(c)) return null;
// uint pid = c.ProcessId; if(pid == 0) return null;
// lock (_prevLock) {
// if(pid != _prevPID || perf.ms - _prevTime > 1000) {
// if(_prev != null) { _prev.Dispose(); _prev = null; }
// try { _prev = new WinformsControlNames(c); } catch { }
// //print.it("new");
// } //else print.it("cached");
// _prevPID = pid; _prevTime = perf.ms;
// if(_prev == null) return null;
// return _prev.GetControlName(c);
// }
//}
//static WinformsControlNames _prev; static uint _prevPID; static long _prevTime; static object _prevLock = new object(); //cache
}
================================================
FILE: Au/Au.Types/ColorInt.cs
================================================
using System.Drawing;
using System.Text.Json.Serialization;
namespace Au.Types;
//rejected: ///
///
/// Color, as int in 0xAARRGGBB format.
/// Can convert from/to , , int (0xAARRGGBB), Windows COLORREF (0xBBGGRR), string.
///
public record struct ColorInt {
///
/// Color value in 0xAARRGGBB format.
///
[JsonInclude]
public int argb;
/// Color value in 0xAARRGGBB or 0xRRGGBB format.
/// Set alpha = 0xFF. If null (default), sets alpha = 0xFF if it is 0 in colorBGR.
public ColorInt(int colorARGB, bool? makeOpaque = null) {
if (makeOpaque == true || (makeOpaque == null && (colorARGB & ~0xFFFFFF) == 0)) colorARGB |= 0xFF << 24;
argb = colorARGB;
}
/// Color value in 0xAARRGGBB or 0xRRGGBB format.
/// Set alpha = 0xFF. If null (default), sets alpha = 0xFF if it is 0 in colorBGR.
public ColorInt(uint colorARGB, bool? makeOpaque) : this((int)colorARGB, makeOpaque) { }
///
/// Converts from an int color value in 0xRRGGBB or 0xAARRGGBB format.
/// Sets alpha = 0xFF if it is 0 in color.
///
//[Obsolete] //to find all references
public static implicit operator ColorInt(int color) => new(color);
///
/// Converts from an uint color value in 0xRRGGBB or 0xAARRGGBB format.
/// Sets alpha = 0xFF if it is 0 in color.
///
//[Obsolete] //to find all references
public static implicit operator ColorInt(uint color) => new((int)color);
///
/// Converts from .
///
public static implicit operator ColorInt(Color color) => new(color.ToArgb(), false);
///
/// Converts from .
///
public static implicit operator ColorInt(System.Windows.Media.Color color)
=> new((color.A << 24) | (color.R << 16) | (color.G << 8) | color.B, false);
///
/// Converts from a color name () or string "0xRRGGBB" or "#RRGGBB".
///
///
/// If s is a hex number that contains 6 or less hex digits, makes opaque (alpha 0xFF).
/// If s is null or invalid, sets c.argb = 0 and returns false.
///
public static bool FromString(string s, out ColorInt c) {
c.argb = 0;
if (s == null || s.Length < 2) return false;
if (s[0] == '0' && s[1] == 'x') {
c.argb = s.ToInt(0, out int end);
if (end < 3) return false;
if (end <= 8) c.argb |= unchecked((int)0xFF000000);
} else if (s[0] == '#') {
c.argb = s.ToInt(1, out int end, STIFlags.IsHexWithout0x);
if (end < 2) return false;
if (end <= 7) c.argb |= unchecked((int)0xFF000000);
} else {
c.argb = Color.FromName(s).ToArgb();
if (c.argb == 0) return false; //invalid is 0, black is 0xFF000000
}
return true;
}
///
/// Converts from Windows native COLORREF (0xBBGGRR to 0xAARRGGBB).
///
/// Color in 0xBBGGRR format.
/// Set alpha = 0xFF. If null (default), sets alpha = 0xFF if it is 0 in colorBGR.
public static ColorInt FromBGR(int colorBGR, bool? makeOpaque = null) => new(SwapRB(colorBGR), makeOpaque);
///
/// Converts to Windows native COLORREF (0xBBGGRR from 0xAARRGGBB).
///
/// color in COLORREF format. Does not modify this variable.
/// Set the alpha byte = 0.
public int ToBGR(bool zeroAlpha = true) {
var r = SwapRB(argb);
if (zeroAlpha) r &= 0xFFFFFF;
return r;
}
//rejected. Easy to create bugs when actually need BGR. Let use ToBGR() when need BGR, or argb field when need ARGB.
///// Returns c.argb.
//public static explicit operator int(ColorInt c) => c.argb;
///// Returns (uint)c.argb.
//public static explicit operator uint(ColorInt c) => (uint)c.argb;
/// Converts to .
public static explicit operator Color(ColorInt c) => Color.FromArgb(c.argb);
/// Converts to .
public static explicit operator System.Windows.Media.Color(ColorInt c) {
uint k = (uint)c.argb;
return System.Windows.Media.Color.FromArgb((byte)(k >> 24), (byte)(k >> 16), (byte)(k >> 8), (byte)k);
}
internal static System.Windows.Media.Color WpfColor_(int rgb)
=> System.Windows.Media.Color.FromRgb((byte)(rgb >> 16), (byte)(rgb >> 8), (byte)rgb);
internal static System.Windows.Media.SolidColorBrush WpfBrush_(int rgb)
=> new(WpfColor_(rgb));
/////
///// FromBGR(GetSysColor).
/////
//internal static ColorInt FromSysColor_(int colorIndex) => FromBGR(Api.GetSysColor(colorIndex), true);
///
public override string ToString() => "#" + argb.ToString("X8");
///
/// Converts color from ARGB (0xAARRGGBB) to ABGR (0xAABBGGRR) or vice versa (swaps the red and blue bytes).
/// ARGB is used in .NET, GDI+ and HTML/CSS.
/// ABGR is used by most Windows API; aka COLORREF.
///
public static int SwapRB(int color) => (color & unchecked((int)0xff00ff00)) | (color << 16 & 0xff0000) | (color >> 16 & 0xff);
///
public static uint SwapRB(uint color) => (color & 0xff00ff00) | (color << 16 & 0xff0000) | (color >> 16 & 0xff);
//rejected. Unclear usage. Instead let users call ToHLS, change L how they want, and call FromHLS.
/////
///// Changes color's luminance (makes darker or brighter).
///// Returns new color. Does not modify this variable.
/////
///// The luminance in units of 0.1 percent of the range (which depends on totalRange). Can be from -1000 to 1000.
///// If true, n is in whole luminance range (from minimal to maximal possible). If false, n is in the range from current luminance of the color to the maximal (if n positive) or minimal (if n negative) luminance.
/////
///// Calls API ColorAdjustLuma.
///// Does not change hue and saturation. Does not use alpha.
/////
//internal ColorInt AdjustLuminance(int n, bool totalRange = false) {
// uint u = (uint)argb;
// u = Api.ColorAdjustLuma(u & 0xffffff, n, !totalRange) | (u & 0xFF000000);
// return new((int)u, false);
// //tested: with SwapRB the same.
//}
///
/// Converts from hue-luminance-saturation (HLS).
///
/// Hue, 0 to 240.
/// Luminance, 0 to 240.
/// Saturation, 0 to 240.
/// Return color in 0xBBGGRR format. If false, 0xRRGGBB.
/// Color in 0xRRGGBB or 0xBBGGRR format, depending on bgr. Alpha 0.
public static int FromHLS(int H, int L, int S, bool bgr) {
if (S == 0) { //ColorHLSToRGB bug: returns 0 if S 0
int i = L * 255 / 240;
return i | (i << 8) | (i << 16);
}
int color = Api.ColorHLSToRGB((ushort)H, (ushort)L, (ushort)S);
if (!bgr) color = SwapRB(color);
return color;
}
///
/// Converts to hue-luminance-saturation (HLS).
///
/// Color in 0xRRGGBB or 0xBBGGRR format, depending on bgr. Ignores alpha.
/// color is in 0xBBGGRR format. If false, 0xRRGGBB.
/// Hue, luminance and saturation. All 0 to 240.
public static (int H, int L, int S) ToHLS(int color, bool bgr) {
if (!bgr) color = SwapRB(color);
Api.ColorRGBToHLS(color, out var H, out var L, out var S);
return (H, L, S);
}
///
/// Calculates color's perceived brightness.
///
/// 0 to 1.
/// Color in 0xRRGGBB or 0xBBGGRR format, depending on bgr. Ignores alpha.
/// color is in 0xBBGGRR format. If false, 0xRRGGBB.
///
/// Unlike and , this function uses different weights for red, green and blue components.
/// Ignores alpha.
///
public static float GetPerceivedBrightness(int color, bool bgr) {
uint u = (uint)color;
if (bgr) u = SwapRB(u);
uint R = u >> 16 & 0xff, G = u >> 8 & 0xff, B = u & 0xff;
return (float)(Math.Sqrt(R * R * .299 + G * G * .587 + B * B * .114) / 255);
}
//same result as ColorAdjustLuma. Probably slower.
//internal static int SetLuminance(int color, bool bgr, double percent, bool totalRange) {
// var (H, L, S) = ToHLS(color, bgr);
// L = (int)Math2.PercentToValue(totalRange ? 240 : L, percent);
// return (int)((uint)FromHLS(H, Math.Clamp(L, 0, 240), S, bgr) | (color & 0xFF000000));
//}
}
================================================
FILE: Au/Au.Types/JSettings.cs
================================================
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Au.Types;
///
/// Base of record classes that contain various settings as public fields or properties. Loads and lazily auto-saves from/to a JSON file.
///
///
/// All functions are thread-safe.
///
///
/// Load(File);
///
/// // examples of settings
/// public int i;
/// public string s = "default";
/// public string[] a = [];
/// public Dictionary d = new();
/// }
/// ]]>
///
public abstract record class JSettings : IDisposable {
string _file;
bool _loadedFile;
byte[] _old;
JsonSerializerOptions _jsOpt;
readonly object _lock = new();
static readonly List s_list = new();
static int s_loadedOnce;
///
/// Loads a JSON file and deserializes to an object of type T, or creates a new object of type T.
///
/// Object of type T. Either deserialized from file or new object with default values.
/// Full path of .json file. If null, does not load and will not save.
/// Use default settings. Don't load from file. Delete file if exists.
/// What to do if failed to load or parse the file: true (default) - backup (rename) the file and use default settings; false - throw exception (for example if invalid JSON); null - show dialog with options to exit or use default settings.
/// Options for deserializing and serializing. If null, uses .
/// Not full path.
/// Field type not supported by .
protected static T Load(string file, bool useDefault = false, bool? useDefaultOnError = true, JsonSerializerOptions jsOpt = null) where T : JSettings
=> (T)_Load(file, typeof(T), useDefault, useDefaultOnError, jsOpt);
static JSettings _Load(string file, Type type, bool useDefault, bool? useDefaultOnError, JsonSerializerOptions jsOpt) {
if (file == null) return Activator.CreateInstance(type) as JSettings;
jsOpt ??= SerializerOptions;
JSettings R = null;
file = pathname.normalize(file);
if (filesystem.exists(file, true)) {
try {
if (useDefault) {
filesystem.delete(file);
} else {
//using var p1 = perf.local(); //first time ~40 ms (similar hot and cold)
var b = filesystem.loadBytes(file);
//p1.Next('f');
R = JsonSerializer.Deserialize(b, type, jsOpt) as JSettings;
//p1.Next('d');
R._loadedFile = true;
}
}
catch (Exception ex) when (ex is not NotSupportedException) {
if (!useDefault) {
if (useDefaultOnError == false) throw;
if (useDefaultOnError == true) {
string backup = file + ".backup";
filesystem.move(file, backup, FIfExists.Delete); //note: don't try/catch
print.it($"<>Failed to load settings from {file}. Will use default settings.\r\n\t<\a>{ex.ToStringWithoutStack()}\a>\r\n\tBackup: {backup}<>");
} else {
if (!Environment.UserInteractive) throw;
int button = dialog.show("Failed to load settings",
new($"{ex.ToStringWithoutStack()}\n\n{file}", o => { run.selectInExplorer(file); }),
"1 Exit|2 Backup (rename) the file and use default settings",
flags: DFlags.CommandLinks,
icon: DIcon.Error);
if (button == 1) Environment.Exit(1);
else if (filesystem.exists(file)) filesystem.move(file, file + ".backup", FIfExists.Delete);
}
}
}
}
R ??= Activator.CreateInstance(type) as JSettings;
R._Ctor2(file, jsOpt);
//autosave
if (Interlocked.Exchange(ref s_loadedOnce, 1) == 0) {
run.thread(() => {
for (; ; ) {
Thread.Sleep(2000);
//Debug_.MemorySetAnchor();
_SaveAllIfNeed(true);
//Debug_.MemoryPrint(); //editor ~6 KB
}
}, sta: false).Name = "Au.JSettings";
process.thisProcessExit += _ => { //info: .NET does not call finalizers when process exits
FileWatcher.DisposeAll_();
_SaveAllIfNeed(false);
s_list.Clear();
};
}
lock (s_list) s_list.Add(R);
return R;
static void _SaveAllIfNeed(bool timer) {
JSettings[] a; //can't lock both s_list and _lock (possible deadlock)
lock (s_list) { a = s_list.ToArray(); }
foreach (var v in a) {
if (v.NoAutoSave) continue;
if (timer && v.NoAutoSaveTimer) continue;
v.SaveIfNeed();
}
}
}
void _Ctor2(string file, JsonSerializerOptions jsOpt) {
_file = file;
_jsOpt = jsOpt;
_old = JsonSerializer.SerializeToUtf8Bytes(this, GetType(), _jsOpt);
}
///
/// Saves now if need, and releases used resources. In the future will not save or reload.
/// Don't need to call if the settings are used until process exit.
///
public void Dispose() {
Dispose(true);
//no finalizer. Users can call Dispose, but usually settings objects live until process exit.
}
///
protected virtual void Dispose(bool disposing) {
lock (s_list) if (!s_list.Remove(this)) return; //note: not inside `lock (_lock)` (possible deadlock)
lock (_lock) {
_watcher?.Dispose();
_watcher = null;
_modifiedExternally = null;
if (!NoAutoSave) SaveIfNeed();
_file = null;
}
}
//repeated serialization speed with same options ~50 times better, eg 15000 -> 300 mcs cold. It's documented. Can be shared by multiple types.
static readonly Lazy s_jsOptions = new(() => new() {
AllowTrailingCommas = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
IncludeFields = true,
IgnoreReadOnlyFields = true,
IgnoreReadOnlyProperties = true,
WriteIndented = true,
});
///
/// Default deserialization and serialization options.
///
///
///
///
public static JsonSerializerOptions SerializerOptions => s_jsOptions.Value;
///
/// true if settings were loaded from file.
///
///
/// Returns false if did not find the file (the settings were not saved) or failed to load/parse or parameter useDefault = true or parameter file = null.
///
[JsonIgnore]
public bool LoadedFile => _loadedFile;
///
/// Don't automatically call .
/// If false (default), calls every 2 s (unless true); also when disposing and when the process exits.
///
protected bool NoAutoSave { get; set; }
///
/// Don't call every 2 s.
/// Default false.
///
protected bool NoAutoSaveTimer { get; set; }
///
/// Saves now if need.
/// Call this when you want to save changed settings immediately; else the changes are auto-saved after max 2 s. See also and .
///
public void SaveIfNeed() {
lock (_lock) {
if (_file is null) return;
bool save = false;
try {
var b = JsonSerializer.SerializeToUtf8Bytes(this, GetType(), _jsOpt);
save = !b.AsSpan().SequenceEqual(_old);
if (save) {
_watcher?.Paused = true;
filesystem.saveBytes(_file, b);
_old = b;
}
}
catch (Exception ex) { print.it($"Failed to save settings to '{_file}'. {ex}"); }
finally { if (save) _watcher?.Paused = false; }
}
}
///
/// When detected that the settings file was externally modified or deleted (for example by another process of your program).
///
///
/// Your event handler can call .
///
/// Runs in a thread pool thread.
///
/// Multiple objects in this process use the same file.
public event Action ModifiedExternally {
add {
lock (_lock) {
if (_file is null) return;
_modifiedExternally += value ?? throw null;
_watcher ??= FileWatcher.Watch(_file, _modifiedExternally);
}
}
remove {
lock (_lock) {
_modifiedExternally -= value;
if (_modifiedExternally == null) { _watcher?.Dispose(); _watcher = null; }
}
}
}
event Action _modifiedExternally;
FileWatcher _watcher;
///
/// Reloads settings from the file.
///
///
/// Receives a new settings object that inherits the behavior of this object but contains updated values of fields defined in the derived class. If the file does not exist, the fields have default values.
/// If failed, receives this object (unchanged).
///
/// false if failed.
///
/// Disposes this object. Does not change field values in it. It will no longer be tracked (e.g., for auto-save); tracking continues with the new object.
///
///
/// Run this 2 times to test how a process of your app auto-reloads settings changed by another process of your app.
/// {
/// if (!sett.Reload(out sett)) return;
/// print.it($"ModifiedExternally. This process: {process.thisProcessId}. {sett}");
/// };
///
/// var b = new wpfBuilder("JSettings example").WinSize(300).Columns(-1, -1, -1);
/// b.R.AddButton("Print", _ => { print.it(sett); });
/// b.AddButton("one++", _ => { sett.one++; sett.SaveIfNeed(); });
/// b.AddButton("two++", _ => { sett.two++; sett.SaveIfNeed(); });
/// b.Window.Topmost = true;
/// b.ShowDialog();
///
/// record Settings : JSettings {
/// public static Settings Load() => Load(@"C:\Test\JSettings.json");
///
/// #pragma warning disable CS0649
/// public int one;
/// public int two = 100;
/// }
/// ]]>
///
public bool Reload(out T newSettings) where T : JSettings {
bool ok = _Reload(out var js);
newSettings = js as T;
return ok;
}
bool _Reload(out JSettings newSettings) {
newSettings = this;
JSettings R;
lock (_lock) {
if (_file is null) {
print.warning($"JSettings.Reload called for a disposed or momory-only settings object");
return false;
}
try {
if (filesystem.exists(_file, true)) {
var b = filesystem.loadBytes(_file);
R = JsonSerializer.Deserialize(b, GetType(), _jsOpt) as JSettings;
R._loadedFile = true;
} else {
R = Activator.CreateInstance(GetType()) as JSettings;
}
}
catch (Exception ex) {
print.warning($"Failed to reload settings from '{_file}'. {ex}", -1);
return false;
}
R._Ctor2(_file, _jsOpt);
R.NoAutoSave = NoAutoSave;
R.NoAutoSaveTimer = NoAutoSaveTimer;
R._watcher = _watcher; _watcher = null;
R._modifiedExternally = _modifiedExternally; _modifiedExternally = null;
_file = null;
}
lock (s_list) { s_list[s_list.IndexOf(this)] = R; } //note: not inside `lock (_lock)` (possible deadlock)
newSettings = R;
return true;
}
}
================================================
FILE: Au/Au.Types/TreeBase.cs
================================================
using System.Xml;
namespace Au.Types;
///
/// Base class for tree classes.
/// The tree can be loaded/saved as XML.
///
///
/// Implemented in the same way as .
///
///
/// Shows how to declare a TreeBase-derived class, load tree of nodes from an XML file, find descendant nodes, save the tree to an XML file.
/// {
/// public string Name { get; set; }
/// public int Id { get; private set; }
/// public bool IsFolder { get; private set; }
///
/// public MyTree(string name, int id, bool isFolder) { Name = name; Id = id; IsFolder = isFolder; }
///
/// //XML element -> MyTree object
/// MyTree(XmlReader x, MyTree parent)
/// {
/// if(parent == null) { //the root XML element
/// if(x.Name != "example") throw new ArgumentException("XML root element must be 'example'");
/// IsFolder = true;
/// } else {
/// switch(x.Name) {
/// case "e": break;
/// case "f": IsFolder = true; break;
/// default: throw new ArgumentException("XML element must be 'e' or 'f'");
/// }
/// #if true //two ways of reading attributes
/// Name = x["name"];
/// Id = x["id"].ToInt();
/// #else
/// while(x.MoveToNextAttribute()) {
/// var v = x.Value;
/// switch(x.Name) {
/// case "name": Name = v; break;
/// case "id": Id = v.ToInt(); break;
/// }
/// }
/// #endif
/// if(Name.NE()) throw new ArgumentException("no 'name' attribute in XML");
/// if(Id == 0) throw new ArgumentException("no 'id' attribute in XML");
/// }
/// }
///
/// public static MyTree Load(string file) => XmlLoad(file, (x, p) => new MyTree(x, p));
///
/// public void Save(string file) => XmlSave(file, (x, n) => n._XmlWrite(x));
///
/// //MyTree object -> XML element
/// void _XmlWrite(XmlWriter x)
/// {
/// if(Parent == null) {
/// x.WriteStartElement("example");
/// } else {
/// x.WriteStartElement(IsFolder ? "f" : "e");
/// x.WriteAttributeString("name", Name);
/// x.WriteAttributeString("id", Id.ToString());
/// }
/// }
///
/// public override string ToString() => $"{new string(' ', Level)}{(IsFolder ? 'f' : 'e')} {Name} ({Id})";
/// }
///
/// static void TNodeExample() {
/// /*
///
///
///
///
///
///
///
///
///
///
///
///
///
/// */
///
/// var x = MyTree.Load(@"C:\test\example.xml");
/// foreach(MyTree n in x.Descendants(true)) print.it(n);
/// //print.it(x.Descendants().FirstOrDefault(k => k.Name == "seven")); //find a descendant
/// //print.it(x.Descendants().Where(k => k.Level > 2)); //find some descendants
/// x.Save(@"C:\test\example2.xml");
/// }
/// ]]>
///
public abstract class TreeBase where T : TreeBase {
T _next;
T _parent;
T _lastChild;
#region properties
///
/// Returns the parent node. Can be null.
///
public T Parent => _parent;
///
/// Returns the root ancestor node. Its is null.
/// Returns this node if its Parent is null.
///
public T RootAncestor {
get {
var p = this as T;
while (p._parent != null) p = p._parent;
return p;
}
}
///
/// Gets the number of ancestors (parent, its parent and so on).
///
public int Level {
get {
int R = 0;
for (var p = _parent; p != null; p = p._parent) R++;
return R;
}
}
///
/// Returns true if this node is a descendant of node n.
///
/// Can be null.
public bool IsDescendantOf(T n) {
for (var p = _parent; p != null; p = p._parent) if (p == n) return true;
return false;
}
///
/// Returns true if this node is a descendant of nearest ancestor node for which predicate returns true.
///
public bool IsDescendantOf(Func predicate) {
for (var p = _parent; p != null; p = p._parent) if (predicate(p)) return true;
return false;
}
///
/// Returns true if this node is an ancestor of node n.
///
/// Can be null.
public bool IsAncestorOf(T n) => n?.IsDescendantOf(this as T) ?? false;
///
/// Returns true if is not null.
///
public bool HasParent => _parent != null;
///
/// Returns true if this node has child nodes.
///
public bool HasChildren => _lastChild != null;
///
/// Gets the last child node, or null if none.
///
public T LastChild => _lastChild;
///
/// Gets the first child node, or null if none.
///
public T FirstChild => _lastChild?._next;
///
/// Gets next sibling node, or null if none.
///
public T Next => _parent == null || this == _parent._lastChild ? null : _next;
///
/// Gets previous sibling node, or null if none.
///
///
/// Can be slow if there are many siblings. This class does not have a "previous" field and therefore has to walk the linked list of siblings.
///
public T Previous {
get {
if (_parent == null) return null;
T n = _parent._lastChild._next;
Debug.Assert(n != null);
T p = null;
while (n != this) {
p = n;
n = n._next;
}
return p;
}
}
///
/// Returns 0-based index of this node in parent.
/// Returns -1 if no parent.
///
///
/// Can be slow if there are many siblings. This class does not have an "index" field and therefore has to walk the linked list of siblings.
///
public int Index {
get {
var p = _parent;
if (p != null) {
var n = p._lastChild;
for (int i = 0; ; i++) {
n = n._next;
if (n == this) return i;
}
}
return -1;
}
}
#endregion
#region methods
void _AddCommon(T n) {
if (n == null || n._parent != null || n == RootAncestor) throw new ArgumentException();
n._parent = this as T;
}
///
/// Adds node n to this node as a child.
///
///
/// Insert n as the first child node. If false (default), appends to the end.
/// n is null, or has parent (need to at first), or is this node, or an ancestor of this node.
public void AddChild(T n, bool first = false) {
_AddCommon(n);
if (_lastChild == null) { //our first child!
n._next = n; //n now is LastChild and FirstChild
} else {
n._next = _lastChild._next; //_next of _lastChild is FirstChild
_lastChild._next = n;
if (first) return;
}
_lastChild = n;
}
///
/// Inserts node n before or after this node as a sibling.
///
///
/// Insert n after this node. If false (default), inserts before this node.
/// See .
/// This node does not have parent ( is null).
public void AddSibling(T n, bool after) {
if (_parent == null) throw new InvalidOperationException("no parent");
_parent._Insert(n, this as T, after);
}
void _Insert(T n, T anchor, bool after) {
if (after && anchor == _lastChild) { //after last child
AddChild(n);
} else if (!after && anchor == _lastChild._next) { //before first child
AddChild(n, true);
} else {
_AddCommon(n);
T prev, next;
if (after) { prev = anchor; next = anchor._next; } else { prev = anchor.Previous; next = anchor; }
n._next = next;
prev._next = n;
}
}
///
/// Removes this node from its parent.
///
///
/// After removing, the property is null.
/// Does nothing if Parent is null.
///
public void Remove() => _parent?._Remove(this as T);
void _Remove(T n) {
Debug.Assert(n?._parent == this);
T p = _lastChild;
while (p._next != n) p = p._next;
if (p == n) {
_lastChild = null;
} else {
if (_lastChild == n) _lastChild = p;
p._next = n._next;
}
n._parent = null;
n._next = null;
}
///
/// Gets ancestor nodes. The order is from this node towards the root node.
///
/// Include this node.
/// Don't include .
public IEnumerable Ancestors(bool andSelf = false, bool noRoot = false) {
var n = andSelf ? this as T : _parent;
while (n != null) {
if (noRoot && n._parent == null) break;
yield return n;
n = n._parent;
}
}
///
/// Gets ancestor nodes. The order is from the root node towards this node.
///
/// Include this node. Default false.
/// Don't include .
public T[] AncestorsFromRoot(bool andSelf = false, bool noRoot = false) {
T nFrom = andSelf ? this as T : _parent;
//count
int len = 0;
for (var n = nFrom; n != null; n = n._parent) {
if (noRoot && n._parent == null) break;
len++;
}
//array
if (len == 0) return [];
var a = new T[len];
for (var n = nFrom; len > 0; n = n._parent) a[--len] = n;
return a;
//info: can use LINQ Reverse, but this func makes less garbage.
}
///
/// Gets all direct child nodes.
///
/// Include this node. Default false.
public IEnumerable Children(bool andSelf = false) {
if (andSelf) yield return this as T;
if (_lastChild != null) {
var n = _lastChild;
do {
n = n._next;
yield return n;
} while (n != _lastChild);
}
}
///
/// Gets number of direct child nodes.
///
public int Count {
get {
int r = 0;
if (_lastChild != null) {
var n = _lastChild;
do { r++; } while ((n = n._next) != _lastChild);
}
return r;
}
}
///
/// Gets all descendant nodes (direct children, their children and so on).
///
/// Include this node. Default false.
/// If not null, called for each descendant node that has children. Let it return false to skip descendants of that node.
public IEnumerable Descendants(bool andSelf = false, Func stepInto = null) {
var n = this as T;
if (andSelf) yield return n;
while (true) {
T last = n._lastChild;
if (last != null && !(stepInto is { } si && n != this && !si(n))) {
n = last._next;
} else {
while (n != null && n != this && n == n._parent._lastChild) n = n._parent;
if (n == null || n == this) break;
n = n._next;
}
yield return n;
}
}
#endregion
#region XML
///
/// Used with
///
protected delegate T XmlNodeReader(XmlReader x, T parent);
///
/// Used with
///
protected delegate void XmlNodeWriter(XmlWriter x, T node);
///
/// Loads XML file and creates tree of nodes from it.
///
/// the root node.
/// XML file. Must be full path. Can contain environment variables etc, see .
/// Callback function that reads current XML element and creates/returns new node. See example.
/// Not full path.
/// Exceptions of .
/// An error occurred while parsing the XML.
///
protected static T XmlLoad(string file, XmlNodeReader nodeReader) {
file = pathname.NormalizeMinimally_(file);
var xs = new XmlReaderSettings() { IgnoreComments = true, IgnoreProcessingInstructions = true, IgnoreWhitespace = true };
using var r = filesystem.waitIfLocked(() => XmlReader.Create(file, xs));
return XmlLoad(r, nodeReader);
}
///
/// Reads XML and creates tree of nodes.
///
/// the root node.
///
/// Callback function that reads current XML element and creates/returns new node.
/// An error occurred while parsing the XML.
///
protected static T XmlLoad(XmlReader x, XmlNodeReader nodeReader) {
Not_.Null(x, nodeReader);
T root = null, parent = null;
while (x.Read()) {
var nodeType = x.NodeType;
if (nodeType == XmlNodeType.Element) {
var n = nodeReader(x, parent);
if (root == null) root = n;
else parent.AddChild(n);
x.MoveToElement();
if (!x.IsEmptyElement) parent = n;
} else if (nodeType == XmlNodeType.EndElement) {
if (parent == null) break;
if (parent == root) break;
parent = parent._parent;
}
}
return root;
}
///
/// Saves tree of nodes (this and descendants) to an XML file.
///
/// XML file. Must be full path. Can contain environment variables etc, see .
/// Callback function that writes node's XML start element (see ) and attributes (see ). Must not write children and end element. Also should not write value, unless your reader knows how to read it.
/// XML formatting settings. Optional.
/// If not null, writes these nodes as if they were children of this node.
/// Not full path.
/// Exceptions of and other methods.
///
/// Uses . It ensures that existing file data is not damaged on exception etc.
///
///
protected void XmlSave(string file, XmlNodeWriter nodeWriter, XmlWriterSettings sett = null, IEnumerable children = null) {
file = pathname.NormalizeMinimally_(file);
sett ??= new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true, IndentChars = " " };
filesystem.save(file, temp => {
using var x = XmlWriter.Create(temp, sett);
XmlSave(x, nodeWriter, children);
});
}
///
/// Writes tree of nodes (this and descendants) to an .
///
/// Exceptions of methods.
///
///
protected void XmlSave(XmlWriter x, XmlNodeWriter nodeWriter, IEnumerable children = null) {
Not_.Null(x, nodeWriter);
x.WriteStartDocument();
if (children == null) {
_XmlWrite(x, nodeWriter);
} else {
nodeWriter(x, this as T);
foreach (var n in children) n._XmlWrite(x, nodeWriter);
x.WriteEndElement();
}
x.WriteEndDocument();
}
void _XmlWrite(XmlWriter x, XmlNodeWriter nodeWriter) {
nodeWriter(x, this as T);
if (_lastChild != null) {
var c = _lastChild;
do {
c = c._next;
c._XmlWrite(x, nodeWriter);
} while (c != _lastChild);
}
x.WriteEndElement();
}
#endregion
}
================================================
FILE: Au/Au.Types/common.cs
================================================
using System.Diagnostics.CodeAnalysis;
using static Au.More.Serializer_;
namespace Au.Types {
///
/// In DocFX-generated help files removes documentation and auto-generated links in TOC and class pages. For it is used filter.yml.
///
[EditorBrowsable(EditorBrowsableState.Never), AttributeUsage(AttributeTargets.All)]
public sealed class NoDoc : Attribute { }
//tested: the DocFX /// removes the member from the compilation, not just prevents creating the doc article. Then may be error; don't use it. See https://dotnet.github.io/docfx/docs/dotnet-api-docs.html
///
/// If a class is derived from this class, editor adds undeclared Windows API to its completion list.
///
public abstract class NativeApi {
//Or for it could use an attribute. But this base class easily solves 2 problems:
// 1. In 'new' expression does not show completion list (with types from winapi DB) if the winapi class still does not have types inside. Because the completion service then returns null. Now it is solved, because this class has nested types.
// 2. If class with attributes is after top-level statements, code info often does not work when typing directly above it. Works better if without attributes.
//FUTURE: Also add attribute. Then can specify an alternative DB or text file with declarations. Maybe also some settings.
// But use this class as base too, like now. Eg could add protected util functions. Could use this class as both (base and attribute), but Attribute has static members.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
///
/// Can be used in structures as flexible array member (the last field, defined like Type[1] name; in C).
///
public struct FlexibleArray where T : unmanaged {
T _0;
public ref T this[int index] {
[UnscopedRef]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref Unsafe.Add(ref _0, index);
}
[UnscopedRef]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span AsSpan(int length) {
return MemoryMarshal.CreateSpan(ref _0, length);
}
}
///
/// Windows API BOOL, with implicit conversions to/from C# bool.
///
public readonly record struct BOOL {
public readonly int IntValue;
public BOOL(bool b) { IntValue = b ? 1 : 0; }
public static implicit operator bool(BOOL b) => b.IntValue != 0;
public static implicit operator BOOL(bool b) => new(b);
public override string ToString() => IntValue == 0 ? "False" : "True";
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
}
///
/// Invokes specified action (calls callback function) at the end of using(...) { ... }.
/// Usually returned by functions. Example: .
///
public struct UsingEndAction : IDisposable {
readonly Action _a;
/// Sets action to be invoked when disposing this variable.
public UsingEndAction(Action a) => _a = a;
/// Invokes the action if not null.
public void Dispose() => _a?.Invoke();
}
///
/// Used with to specify string parameter format.
///
public enum PSFormat {
///
None,
///
/// Keys. See .
///
Keys,
///
/// [Wildcard expression](xref:wildcard_expression).
///
Wildex,
///
/// PCRE regular expression.
///
Regexp,
///
/// PCRE regular expression replacement string.
///
RegexpReplacement,
///
/// .NET regular expression.
///
NetRegex,
///
/// Hotkey, except triggers.
///
Hotkey,
///
/// Hotkey trigger.
///
HotkeyTrigger,
///
/// Trigger modifiers without key.
///
TriggerMod,
///
/// Name or path of a script or class file in current workspace.
///
CodeFile,
///
/// Name or path of any file or folder in current workspace.
///
FileInWorkspace,
}
///
/// A function parameter with this attribute is a string of the specified format, for example regular expression.
/// Code editors should help to create correct string arguments: provide tools or reference, show errors.
///
[AttributeUsage(AttributeTargets.Parameter /*| AttributeTargets.Field | AttributeTargets.Property*/, AllowMultiple = false)]
public sealed class ParamStringAttribute : Attribute {
//info: now .NET has similar attribute StringSyntaxAttribute. It was added later.
///
public ParamStringAttribute(PSFormat format) => Format = format;
///
public PSFormat Format { get; set; }
}
/////
///// Specifies whether to set, add or remove flags.
/////
//public enum SetAddRemove
//{
// /// Set flags = the specified value.
// Set = 0,
// /// Add the specified flags, don't change others.
// Add = 1,
// /// Remove the specified flags, don't change others.
// Remove = 2,
// /// Toggle the specified flags, don't change others.
// Xor = 3,
//}
}
namespace System.Runtime.CompilerServices //the attribute must be in this namespace
{
///
[NoDoc]
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class IgnoresAccessChecksToAttribute : Attribute {
///
public IgnoresAccessChecksToAttribute(string assemblyName) { AssemblyName = assemblyName; }
///
public string AssemblyName { get; }
}
}
================================================
FILE: Au/Au.Types/exceptions.cs
================================================
namespace Au.Types {
///
/// The base exception class used in this library.
/// Thrown when something fails and there is no better exception type for that failure.
///
///
/// Some constructors support Windows API error code. Then will contain its error description.
/// If the string passed to the constructor starts with "*", replaces the "*" with "Failed to ". If does not end with ".", appends ".".
///
public class AuException : Exception {
///
/// Sets = message (default "Failed.").
/// Sets = 0.
///
public AuException(string message = "Failed.", Exception innerException = null) : base(message, innerException) { }
///
/// Sets = (errorCode != 0) ? errorCode : lastError.code.
/// Sets = message + " " + lastError.messageFor(NativeErrorCode).
///
public AuException(int errorCode, string message = "Failed.", Exception innerException = null) : base(message, innerException) {
NativeErrorCode = (errorCode != 0) ? errorCode : lastError.code;
}
/// Gets the Windows API error code.
public int NativeErrorCode { get; protected set; }
/// Gets error message.
public override string Message => FormattedMessage ?? FormatMessage();
/// String created by , which should be called by the override if null. Initially null.
protected string FormattedMessage;
///
/// Formats error message. Sets and returns .
///
///
/// As base text, uses the text passed to the constructor (default "Failed.").
/// If it starts with "*", replaces the "*" with "Failed to ".
/// If it ends with "*", replaces the "*" with commonPostfix if it is not empty.
/// If then the message does not end with ".", appends ".".
/// If appendMessage is null, uses lastError.messageFor(NativeErrorCode) if not 0.
/// If then appendMessage is not empty, appends " " + appendMessage.
/// Also appends InnerException.Message in new tab-indented line if is not null.
///
protected string FormatMessage(string appendMessage = null, string commonPostfix = null) {
var m = base.Message;
if (!m.NE()) {
if (m[0] == '*') m = "Failed to " + m[1..];
if (!commonPostfix.NE() && m[^1] == '*') m = m[..^1] + commonPostfix;
if (!m.Ends('.')) m += ".";
}
if (appendMessage == null && NativeErrorCode != 0) appendMessage = lastError.messageFor(NativeErrorCode);
if (!appendMessage.NE()) m = m + " " + appendMessage;
if (InnerException != null) m = m + "\r\n\t" + InnerException.Message;
return FormattedMessage = m;
}
///
/// If errorCode is not 0, throws that includes the code and its message.
/// More info: .
///
/// Windows API error code or HRESULT.
/// Main message. The message of the error code will be appended to it.
public static void ThrowIfHresultNot0(int errorCode, string message = null) {
if (errorCode != 0) throw new AuException(errorCode, message);
}
///
/// If errorCode is less than 0, throws that includes the code and its message.
/// More info: .
///
/// Windows API error code or HRESULT.
/// Main message. The message of the error code will be appended to it.
public static void ThrowIfHresultNegative(int errorCode, string message = null) {
if (errorCode < 0) throw new AuException(errorCode, message);
}
}
///
/// Exception thrown mostly by functions.
///
///
/// Some constructors support Windows API error code. Then also will contain its error description.
/// If error code ERROR_INVALID_WINDOW_HANDLE, also depends on whether the window handle is 0.
/// If parameter errorCode is 0 or not used: if the window handle is invalid, uses ERROR_INVALID_WINDOW_HANDLE.
/// If the string passed to the constructor starts with "*", replaces the "*" with "Failed to ". If ends with "*", replaces the "*" with " window.". If does not end with ".", appends ".".
///
public class AuWndException : AuException {
const string _errStr_0Handle = "The window handle is 0. Usually it means 'window not found'.";
const string _errStr_InvalidHandle = "Invalid window handle. Usually it means 'the window was closed'.";
///
/// Sets = message (default "Failed.").
/// Sets = w.IsAlive ? 0 : ERROR_INVALID_WINDOW_HANDLE.
///
public AuWndException(wnd w, string message = "Failed.", Exception innerException = null)
: base(message, innerException) { Window = w; NativeErrorCode = _Code(0, w); }
///
/// Sets = (errorCode != 0) ? errorCode : (w.IsAlive ? lastError.code : ERROR_INVALID_WINDOW_HANDLE).
/// Sets = message + " " + lastError.messageFor(NativeErrorCode).
///
public AuWndException(wnd w, int errorCode, string message = "Failed.", Exception innerException = null)
: base(_Code(errorCode, w), message, innerException) { Window = w; }
static int _Code(int code, wnd w) {
if (code != 0 || w.IsAlive) return code;
return Api.ERROR_INVALID_WINDOW_HANDLE;
}
/// Gets the window passed to the constructor.
public wnd Window { get; }
/// Gets error message.
public override string Message {
get {
if (FormattedMessage == null) {
string m;
if (Window.Is0) m = _errStr_0Handle;
else if (NativeErrorCode == Api.ERROR_INVALID_WINDOW_HANDLE) m = _errStr_InvalidHandle;
else m = null; //will append lastError.messageFor(NativeErrorCode) if NativeErrorCode not 0, or InnerException.Message if it is not null.
FormatMessage(m, " window.");
}
return FormattedMessage;
}
}
}
///
/// Functions that search for an object can throw this exception when not found.
///
public class NotFoundException : Exception {
///
/// Sets Message = "Not found.".
///
public NotFoundException() : base("Not found.") { }
///
/// Sets Message = message.
///
public NotFoundException(string message) : base(message) { }
}
/////
///// Exception thrown when a callback function returns an invalid value.
/////
//public class BadCallbackException : Exception {
// ///
// /// Sets Message = "The callback function returned an invalid value.".
// ///
// public BadCallbackException() : base("The callback function returned an invalid value.") { }
// ///
// /// Sets Message = message.
// ///
// public BadCallbackException(string message) : base(message) { }
//}
///
/// This exception is thrown when current thread is not on the input desktop and therefore cannot use mouse, keyboard, clipboard and window functions.
/// For example when PC locked (Win+L), screen saver, UAC consent, Ctrl+Alt+Delete or not in the active user session.
///
///
public class InputDesktopException : AuException {
/// Message text before the standard message text of this exception.
public InputDesktopException(string message = null)
: base(message.NE() ? c_message : message + (message.Ends('.') ? " " : ". ") + c_message) { }
const string c_message = "The main input desktop is inactive; mouse, keyboard, clipboard and UI functions are disabled.";
///
/// Calls . If it returns false, throws .
///
/// Message text before the standard message text of this exception.
///
public static void ThrowIfBadDesktop(string message = null, bool detectLocked = false) {
if (!miscInfo.isInputDesktop(detectLocked)) throw new InputDesktopException(message);
}
}
}
namespace Au.Types {
static partial class ExtMisc {
///
/// Returns string containing exception type name and message.
///
public static string ToStringWithoutStack(this Exception t) {
return t.GetType().Name + ", " + t.Message;
}
}
}
================================================
FILE: Au/Au.Types/param types.cs
================================================
namespace Au.Types;
#if false //test docfx preprocessing
///
/// Sum.
/// two
///
/// A.
/// B.
public record class PublicRecord(int A, int B);
record class InternalRecord_ {
public int A { get; set; }
public int B { get; set; }
}
record class InternalRecord2_(int A, int B) {
public int C { get; set; }
}
record class InternalRecord3_(int A, int B);
public class NormalClass {
record class _Record4(int A, int B) {
public int C { get; set; }
}
///
/// Sum.
///
/// Param.
public void Meth(int i) { }
}
#endif
///
/// Contains x or y coordinate in screen or some other rectangle that can be specified in various ways: normal, reverse, fraction, center, max.
/// Used for parameters of functions like , .
///
///
/// To specify a normal coordinate (the origin is the left or top edge), assign an int value (implicit conversion from int to Coord).
/// To specify a reverse coordinate (the origin is the right or bottom edge), use or a "from end" index like ^1. It is towards the left or top edge, unless negative. Or use or .
/// To specify a "fraction of the rectangle" coordinate, use or a value of type float like .5f. Or use .
/// The meaning of default(Coord) depends on function where used. Many functions interpret it as center (same as Coord.Center or .5f).
/// Also there are functions to convert Coord to normal coordinates.
///
///
///
///
public record struct Coord {
//Use single long field that packs int and CoordType.
//If 2 fields (int and CoordType), 64-bit compiler creates huge calling code.
//This version is good in 32-bit, very good in 64-bit. Even better than packing in single int (30 bits value and 2 bits type).
//Don't use struct or union with both int and float fields. It creates slow and huge calling code.
readonly long _v;
///
/// Value type.
///
public CoordType Type => (CoordType)(_v >> 32);
///
/// Non-fraction value.
///
public int Value => (int)_v;
///
/// Fraction value.
///
public unsafe float FractionValue => BitConverter.Int32BitsToSingle((int)_v);
///
/// Returns true if Type == CoordType.None (no value assigned).
///
public bool IsEmpty => Type == CoordType.None;
Coord(CoordType type, int value) { _v = ((long)type << 32) | (uint)value; }
//info: if type and value are constants, compiler knows the final value and does not use the << and | operators in the compiled code.
///
/// Creates of Normal type.
///
//[MethodImpl(MethodImplOptions.NoInlining)] //makes bigger/slower
public static implicit operator Coord(int v) => new(CoordType.Normal, v);
///
/// Creates of Normal or Reverse type. Reverse if the index is from end, like ^1.
///
public static implicit operator Coord(Index v) => new(v.IsFromEnd ? CoordType.Reverse : CoordType.Normal, v.Value);
///
/// Creates of Fraction type.
///
public static implicit operator Coord(float v) => new(CoordType.Fraction, BitConverter.SingleToInt32Bits(v));
//these would create Fraction
///
[Obsolete("The value must be of type int or float.", error: true), NoDoc]
public static implicit operator Coord(uint f) => default;
///
[Obsolete("The value must be of type int or float.", error: true), NoDoc]
public static implicit operator Coord(long f) => default;
///
[Obsolete("The value must be of type int or float.", error: true), NoDoc]
public static implicit operator Coord(ulong f) => default;
//tested: compiler does not allow to assign nint.
///
/// Creates of Reverse type.
/// Value 0 is at the right or bottom, and does not belong to the rectangle. Positive values are towards left or top.
///
///
/// Instead can be use "from end" index, for example argument Coord.Reverse(1) can be replaced with ^1.
///
public static Coord Reverse(int v) => new(CoordType.Reverse, v);
///
/// Creates of Fraction type.
/// Value 0 is the left or top of the rectangle. Value 1.0 is the right or bottom of the rectangle. Values <0 and >=1.0 are outside of the rectangle.
///
///
/// Instead can be used implicit conversion from float, for example argument Coord.Fraction(.5) can be replaced with .5f.
///
public static unsafe Coord Fraction(double v) => (float)v;
///
/// Returns Fraction(0.5).
///
///
public static Coord Center => .5f;
///
/// Returns Reverse(0). Same as ^0.
/// This point will be outside of the rectangle. See also .
///
///
public static Coord Max => Reverse(0);
///
/// Returns Reverse(1). Same as ^1.
/// This point will be inside of the rectangle, at the very right or bottom, assuming the rectangle is not empty.
///
///
public static Coord MaxInside => Reverse(1);
//rejected: this could be used like Coord.Max + 1. Too limited usage.
//public static Coord operator +(Coord c, int i) { return ...; }
static bool _NeedRect(Coord x, Coord y) {
return (x.Type > CoordType.Normal) || (y.Type > CoordType.Normal);
}
///
/// Converts fractional/reverse coordinate to normal coordinate in a range.
///
/// Start of range.
/// End of range.
public int NormalizeInRange(int start, int end) {
return Type switch {
CoordType.Normal => start + Value,
CoordType.Reverse => end - Value,
CoordType.Fraction => start + (int)((end - start) * FractionValue),
_ => 0,
};
}
///
/// Converts fractional/reverse coordinates to normal coordinates in a rectangle.
///
/// X coordinate relative to r.
/// Y coordinate relative to r.
/// The rectangle.
/// Use only width and height of r. If false (default), the function adds r offset (left and top).
/// If x or y is default, use . Not used with widthHeight.
public static POINT NormalizeInRect(Coord x, Coord y, RECT r, bool widthHeight = false, bool centerIfEmpty = false) {
if (widthHeight) r.Move(0, 0);
else if (centerIfEmpty) {
if (x.IsEmpty) x = Center;
if (y.IsEmpty) y = Center;
}
return (x.NormalizeInRange(r.left, r.right), y.NormalizeInRange(r.top, r.bottom));
}
///
/// Returns normal coordinates relative to the client area of a window. Converts fractional/reverse coordinates etc.
///
/// X coordinate relative to the client area of w.
/// Y coordinate relative to the client area of w.
/// The window.
/// x y are relative to the entire w rectangle, not to its client area.
/// If x or y is default, use .
public static POINT NormalizeInWindow(Coord x, Coord y, wnd w, bool nonClient = false, bool centerIfEmpty = false) {
//info: don't need widthHeight parameter because client area left/top are 0. With non-client don't need in this library and probably not useful. But if need, caller can explicitly offset the rect before calling this func.
if (centerIfEmpty) {
if (x.IsEmpty) x = Center;
if (y.IsEmpty) y = Center;
}
POINT p = default;
if (!x.IsEmpty || !y.IsEmpty) {
RECT r;
if (nonClient) {
w.GetRectIn(w, out r);
} else if (_NeedRect(x, y)) {
r = w.ClientRect;
} else r = default;
p.x = x.NormalizeInRange(r.left, r.right);
p.y = y.NormalizeInRange(r.top, r.bottom);
}
return p;
}
///
/// Returns normal coordinates relative to the primary screen. Converts fractional/reverse coordinates etc.
///
/// X coordinate relative to the specified screen (default - primary).
/// Y coordinate relative to the specified screen (default - primary).
/// x y are relative to the work area.
/// If used, x y are relative to this screen. Default - primary screen. Example: screen.index(1).
/// Use only width and height of the screen rectangle. If false, the function adds its offset (left and top, which can be nonzero if using the work area or a non-primary screen).
/// If x or y is default, use .
public static POINT Normalize(Coord x, Coord y, bool workArea = false, screen screen = default, bool widthHeight = false, bool centerIfEmpty = false) {
if (centerIfEmpty) {
if (x.IsEmpty) x = Center;
if (y.IsEmpty) y = Center;
}
POINT p = default;
if (!x.IsEmpty || !y.IsEmpty) {
RECT r;
if (workArea || !screen.IsEmpty || _NeedRect(x, y)) {
r = screen.GetRect(workArea);
if (widthHeight) r.Move(0, 0);
} else r = default;
p.x = x.NormalizeInRange(r.left, r.right);
p.y = y.NormalizeInRange(r.top, r.bottom);
}
return p;
}
///
public override string ToString() {
switch (Type) {
case CoordType.Normal: return Value.ToString() + ", Normal";
case CoordType.Reverse: return Value.ToString() + ", Reverse";
case CoordType.Fraction: return FractionValue.ToS() + ", Fraction";
default: return "default";
}
}
}
///
/// variable value type.
///
public enum CoordType {
///
/// No value. The variable is default(Coord).
///
None,
///
/// is pixel offset from left or top of a rectangle.
///
Normal,
///
/// is pixel offset from right or bottom of a rectangle, towards left or top.
///
Reverse,
///
/// is fraction of a rectangle, where 0.0 is left or top, and 1.0 is right or bottom (outside of the rectangle).
///
Fraction,
}
///
/// Can be used to specify coordinates for various popup windows, like new PopupXY(x, y), (x, y), PopupXY.In(rectangle), PopupXY.Mouse.
///
public class PopupXY {
#pragma warning disable 1591 //XML doc
public Coord x, y;
public screen screen;
public bool workArea;
public bool inRect;
public RECT rect;
#pragma warning restore 1591 //XML doc
///
/// Sets position and/or screen.
///
/// X relative to the screen or work area. Default - center.
/// X relative to the screen or work area. Default - center.
/// x y are relative to the work area of the screen.
/// Can be used to specify a screen. Default - primary. Example: screen.index(1).
///
/// Also there is are implicit conversions from tuple (x, y) and . Instead of new PopupXY(x, y) you can use (x, y). Instead of new PopupXY(p.x, p.y, false) you can use p or (POINT)p .
///
public PopupXY(Coord x = default, Coord y = default, bool workArea = true, screen screen = default) {
this.x = x; this.y = y; this.workArea = workArea; this.screen = screen;
}
///
/// Creates new that specifies position in a rectangle. For example of the owner window.
///
/// Rectangle relative to the primary screen.
/// X relative to the rectangle. Default - center.
/// Y relative to the rectangle. Default - center.
public static PopupXY In(RECT r, Coord x = default, Coord y = default) => new(x, y) { inRect = true, rect = r };
///
/// Creates new that specifies position relative to the work area of the primary screen.
///
public static implicit operator PopupXY((Coord x, Coord y) p) => new(p.x, p.y, true);
/// Creates new that specifies position relative to the primary screen (not to the work area).
public static implicit operator PopupXY(POINT p) => new(p.x, p.y, false);
//info: this conversion can be used with PopupXY.Mouse.
//public bool IsRawXY => !inRect && screen.IsNull && workArea == false && x.Type == Coord.CoordType.Normal && y.Type == Coord.CoordType.Normal;
///
/// Gets point coordinates below mouse cursor, for showing a tooltip-like popup.
///
public static POINT Mouse {
get {
var p = mouse.xy;
var scr = screen.of(p);
var rs = scr.Rect;
int dy = Dpi.Scale(100, scr.Dpi);
if (rs.bottom - p.y < dy) return (p.x, p.y - dy);
int cy = Dpi.GetSystemMetrics(Api.SM_CYCURSOR, p);
if (MouseCursor.GetCurrentVisibleCursor(out var c) && Api.GetIconInfo(c, out var u)) {
if (u.hbmColor != default) Api.DeleteObject(u.hbmColor);
Api.DeleteObject(u.hbmMask);
//print.it(u.xHotspot, u.yHotspot);
p.y += cy - u.yHotspot - 1; //not perfect, but better than just to add SM_CYCURSOR or some constant value.
return p;
}
return (p.x, p.y + cy - 5);
}
}
///
/// Gets if not empty, else screen that contains the specified point.
///
public screen GetScreen() {
if (!screen.IsEmpty) return screen.Now;
POINT p = inRect ? Coord.NormalizeInRect(x, y, rect, centerIfEmpty: true) : Coord.Normalize(x, y, workArea);
return screen.of(p);
}
}
///
/// Used for parameters of functions that need a window handle as but also accept a WPF window/element, winforms form/control and IntPtr.
///
///
/// Has implicit conversions from:
///
• - WPF window or element.
///
• - Form or control.
///
• IntPtr or nint - window handle.
///
public struct AnyWnd {
readonly object _o;
AnyWnd(object o) { _o = o; }
/// Assignment of a value of type .
public static implicit operator AnyWnd(wnd w) => new(w);
/// Assignment of a window handle as IntPtr.
public static implicit operator AnyWnd(IntPtr hwnd) => new((wnd)hwnd);
/// Assignment of a value of type (Form or any control class).
public static implicit operator AnyWnd(System.Windows.Forms.Control c) => new(c);
/// Assignment of a value of type System.Windows.DependencyObject (WPF window or control).
public static implicit operator AnyWnd(System.Windows.DependencyObject c) => c != null ? new AnyWnd(new object[] { c }) : default;
///
/// Gets the window or control handle as .
///
/// default(wnd) if not assigned.
public wnd Hwnd => wnd.Internal_.FromObject(_o);
///
/// true if this is default(AnyWnd).
///
public bool IsEmpty => _o == null;
}
///
/// Used for function parameters to specify multiple strings.
/// Contains a string like "One|Two|Three" or string[] or List<string>. Has implicit conversions from these types. Can be assigned collection initializer like ["a", "b"].
///
[CollectionBuilder(typeof(Strings), "Create")]
public struct Strings : IEnumerable {
readonly object _o;
Strings(object o) { _o = o; }
///
public Strings(params string[] a) { _o = a; }
///
public static implicit operator Strings(string s) => new((object)s);
///
public static implicit operator Strings(string[] e) => new(e);
///
public static implicit operator Strings(List e) => new(e);
///
/// The raw value.
///
public object Value => _o;
///
/// Converts the value to string[].
/// Note: don't modify array elements. If the caller passed an array, this function returns it, not a copy.
///
public string[] ToArray() {
return _o switch {
string[] a => a,
string s => s.Split('|'),
List a => a.ToArray(),
_ => [], //null
};
}
#region support collection expression
//IEnumerable
IEnumerator IEnumerable.GetEnumerator() => ToArray().AsEnumerable().GetEnumerator();
//IEnumerable
IEnumerator IEnumerable.GetEnumerator() => ToArray().GetEnumerator();
///
/// Returns new(span.ToArray()).
///
public static Strings Create(ReadOnlySpan span) => new(span.ToArray());
#endregion
}
///
/// Font name, size and style.
/// If Name not set, will be used standard GUI font; then Size can be 0 to use size of standard GUI font.
/// On high-DPI screen the font size will be scaled.
///
public record class FontNSS(int Size = 0, string Name = null, bool Bold = false, bool Italic = false) {
///
/// Creates font.
///
/// DPI for scaling.
internal NativeFont_ CreateFont(DpiOf dpi) {
if (Name == null) return new(dpi, Bold, Italic, Size);
return new(dpi, Name, Size, Bold, Italic);
}
}
================================================
FILE: Au/Au.Types/structs.cs
================================================
using System.Drawing;
using System.Text.Json.Serialization;
namespace Au.Types {
///
/// Point coordinates x y.
///
public record struct POINT {
#pragma warning disable 1591, 3008 //XML doc, CLS-compliant
[JsonInclude]
public int x, y;
public POINT(int x, int y) { this.x = x; this.y = y; }
public static implicit operator POINT((int x, int y) t) => new(t.x, t.y);
public static implicit operator POINT(Point p) => new(p.X, p.Y);
public static implicit operator Point(POINT p) => new(p.x, p.y);
public static implicit operator PointF(POINT p) => new(p.x, p.y);
public static implicit operator System.Windows.Point(POINT p) => new(p.x, p.y);
///
/// Can round up, for example 1.7 to 2.
///
public static POINT From(PointF p, bool round)
=> new(round ? p.X.ToInt() : checked((int)p.X), round ? p.Y.ToInt() : checked((int)p.Y));
///
/// Can round up, for example 1.7 to 2.
///
public static POINT From(System.Windows.Point p, bool round)
=> new(round ? p.X.ToInt() : checked((int)p.X), round ? p.Y.ToInt() : checked((int)p.Y));
//rejected
///// Specifies position relative to the primary screen or its work area. Calls with centerIfEmpty true.
//public static implicit operator POINT((Coord x, Coord y, bool workArea) t) => _Coord(t.x, t.y, t.workArea, default);
///// Specifies position relative to the specified screen or its work area. Calls with centerIfEmpty true.
//public static implicit operator POINT((Coord x, Coord y, screen screen, bool workArea) t) => _Coord(t.x, t.y, t.workArea, t.screen);
///// Specifies position in the specified rectangle which is relative to the primary screen. Calls with centerIfEmpty true.
//public static implicit operator POINT((RECT r, Coord x, Coord y) t) => Coord.NormalizeInRect(t.x, t.y, t.r, centerIfEmpty: true);
//static POINT _Coord(Coord x, Coord y, bool workArea, screen screen) => Coord.Normalize(x, y, workArea, screen, centerIfEmpty: true);
//maybe in the future
/////
///// Converts coordinates into real coordinates.
///// Calls with centerIfEmpty true.
/////
//public static POINT Normalize(Coord x, Coord y, bool workArea = false, screen screen = default)
// => Coord.Normalize(x, y, workArea, screen, centerIfEmpty: true);
//public static POINT NormalizeIn(RECT r, Coord x = default, Coord y = default)
// => Coord.NormalizeInRect(x, y, r, centerIfEmpty: true);
/// this.x += x; this.y += y;
public void Offset(int x, int y) { this.x += x; this.y += y; }
/// Returns new POINT(p.x + d.x, p.y + d.y).
public static POINT operator +(POINT p, (int x, int y) d) => new(p.x + d.x, p.y + d.y);
public void Deconstruct(out int x, out int y) { x = this.x; y = this.y; }
public override string ToString() => $"{{{x.ToS()}, {y.ToS()}}}";
#pragma warning restore 1591 //XML doc
}
///
/// Width and height.
///
public record struct SIZE {
#pragma warning disable 1591, 3008 //XML doc, CLS-compliant
[JsonInclude]
public int width, height;
public SIZE(int width, int height) { this.width = width; this.height = height; }
public static implicit operator SIZE((int width, int height) t) => new(t.width, t.height);
public static implicit operator SIZE(Size z) => new(z.Width, z.Height);
public static implicit operator Size(SIZE z) => new(z.width, z.height);
public static implicit operator SizeF(SIZE z) => new(z.width, z.height);
public static implicit operator System.Windows.Size(SIZE z) => new(z.width, z.height);
///
/// Can round up, for example 1.7 to 2.
///
public static SIZE From(SizeF z, bool round)
=> new(round ? z.Width.ToInt() : checked((int)z.Width), round ? z.Height.ToInt() : checked((int)z.Height));
///
/// Can round up, for example 1.7 to 2.
///
public static SIZE From(System.Windows.Size z, bool round)
=> new(round ? z.Width.ToInt() : checked((int)z.Width), round ? z.Height.ToInt() : checked((int)z.Height));
/// Returns new SIZE(z.width + d.x, z.height + d.y).
public static SIZE operator +(SIZE z, (int x, int y) d) => new(z.width + d.x, z.height + d.y);
public void Deconstruct(out int width, out int height) { width = this.width; height = this.height; }
public override string ToString() => $"{{{width.ToS()}, {height.ToS()}}}";
#pragma warning restore 1591 //XML doc
}
///
/// Rectangle coordinates left top right bottom.
///
///
/// This type can be used with Windows API functions. The .NET Rectangle etc can't, because their fields are different.
/// Has conversions from/to .
///
public record struct RECT {
#pragma warning disable 1591, 3008 //XML doc, CLS-compliant
[JsonInclude]
public int left, top;
public int right, bottom;
///
/// Sets all fields.
///
///
/// Sets right = left + width; bottom = top + height;. To specify right/bottom instead of width/height, use instead.
///
[JsonConstructor] //without it JSON deserializer sets incorrect Width/Height because does it before setting left/right
public RECT(int left, int top, int width, int height) {
this.left = left; this.top = top;
right = left + width; bottom = top + height;
}
///
/// Creates with specified left, top, right and bottom.
///
public static RECT FromLTRB(int left, int top, int right, int bottom)
=> new() { left = left, top = top, right = right, bottom = bottom };
///
/// Converts from tuple (left, top, width, height).
///
public static implicit operator RECT((int L, int T, int W, int H) t) => new(t.L, t.T, t.W, t.H);
public static implicit operator RECT(Rectangle r) => new(r.Left, r.Top, r.Width, r.Height);
public static implicit operator Rectangle(RECT r) => new(r.left, r.top, r.Width, r.Height);
public static implicit operator RectangleF(RECT r) => new(r.left, r.top, r.Width, r.Height);
public static implicit operator System.Windows.Rect(RECT r) => new(r.left, r.top, r.Width, r.Height);
///
/// Can round up, for example 1.7 to 2.
///
public static RECT From(RectangleF r, bool round) {
if (round) return new(r.Left.ToInt(), r.Top.ToInt(), r.Width.ToInt(), r.Height.ToInt());
checked { return new((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height); }
}
///
/// Can round up, for example 1.7 to 2.
///
public static RECT From(System.Windows.Rect r, bool round) {
if (round) return new(r.Left.ToInt(), r.Top.ToInt(), r.Width.ToInt(), r.Height.ToInt());
checked { return new((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height); }
}
//rejected. Rare.
/////
///// Sets fields like constructor .
/////
//public void Set(int left, int top, int rightOrWidth, int bottomOrHeight, bool useWidthHeight = true) {
// this.left = left; this.top = top;
// right = rightOrWidth; bottom = bottomOrHeight;
// if (useWidthHeight) { right += left; bottom += top; }
//}
///
/// Returns true if all fields == 0.
///
public bool Is0 => left == 0 && top == 0 && right == 0 && bottom == 0;
///
/// Returns true if the rectangle area is empty or invalid: right<=left || bottom<=top;
///
public bool NoArea => right <= left || bottom <= top;
///
/// Gets or sets width.
///
public int Width { get => right - left; set { right = left + value; } }
///
/// Gets or sets height.
///
public int Height { get => bottom - top; set { bottom = top + value; } }
///
/// Returns new POINT(left, top).
///
public POINT XY => new(left, top);
///
/// Returns new SIZE(Width, Height).
///
public SIZE Size => new(Width, Height);
///
/// Gets horizontal center.
///
public int CenterX => left + (right - left) / 2;
//public int CenterX => (int)(((long)left + right) / 2);
///
/// Gets vertical center.
///
public int CenterY => top + (bottom - top) / 2;
//public int CenterY => (int)(((long)top + bottom) / 2);
///
/// Returns width multiplied by height: Math.Abs((long)Width * Height).
///
internal long Area_ => Math.Abs((long)Width * Height);
///
/// Returns true if this rectangle contains the specified point.
///
public bool Contains(int x, int y) => x >= left && x < right && y >= top && y < bottom;
///
/// Returns true if this rectangle contains the specified point.
///
public bool Contains(POINT p) => Contains(p.x, p.y);
///
/// Returns true if this rectangle contains entire specified rectangle.
///
public bool Contains(RECT r2) => r2.left >= left && r2.top >= top && r2.right <= right && r2.bottom <= bottom;
///
/// Makes this rectangle bigger or smaller: left-=dx; right+=dx; top-=dy; bottom+=dy;
/// Use negative dx/dy to make the rectangle smaller. Note: too big negative dx/dy can make it invalid (right < left or bottom < top).
///
public void Inflate(int dx, int dy) { left -= dx; right += dx; top -= dy; bottom += dy; }
///
/// Replaces this rectangle with the intersection of itself and the specified rectangle.
/// If the rectangles don't intersect, makes this variable empty.
///
/// true if the rectangles intersect.
public bool Intersect(RECT r2) => Api.IntersectRect(out this, this, r2);
///
/// Returns the intersection rectangle of two rectangles.
/// If they don't intersect, returns empty rectangle.
///
public static RECT Intersect(RECT r1, RECT r2) { Api.IntersectRect(out RECT r, r1, r2); return r; }
///
/// Returns true if this rectangle and another rectangle intersect.
///
public bool IntersectsWith(RECT r2) => Api.IntersectRect(out _, this, r2);
///
/// Moves this rectangle by the specified offsets: left+=dx; right+=dx; top+=dy; bottom+=dy;
/// Negative dx moves to the left. Negative dy moves up.
///
public void Offset(int dx, int dy) { left += dx; right += dx; top += dy; bottom += dy; }
///
/// Moves this rectangle so that left=x and right=y. Does not change and .
///
public void Move(int x, int y) => Offset(x - left, y - top);
///
/// Replaces this rectangle with the union of itself and the specified rectangle.
/// Union is the smallest rectangle that contains two full rectangles.
///
///
/// If either rectangle is empty ( or is <=0), the result is another rectangle. If both empty - empty rectangle.
///
/// true if finally this rectangle is not empty.
public bool Union(RECT r2) => Api.UnionRect(out this, this, r2);
///
/// Returns the union of two rectangles.
/// Union is the smallest rectangle that contains two full rectangles.
///
///
/// If either rectangle is empty ( or is <=0), the result is another rectangle. If both empty - empty rectangle.
///
public static RECT Union(RECT r1, RECT r2) { Api.UnionRect(out RECT r, r1, r2); return r; }
///
/// If width or height are negative, modifies this rectangle so that they would not be negative.
///
/// true - swap right/left, bottom/top; false - set right = left, bottom = top.
public void Normalize(bool swap) {
if (right < left) { if (swap) Math2.Swap(ref left, ref right); else right = left; }
if (bottom < top) { if (swap) Math2.Swap(ref top, ref bottom); else bottom = top; }
}
///
/// Moves this rectangle to the specified coordinates in the specified screen, and ensures that whole rectangle is in screen.
/// Final rectangle coordinates are relative to the primary screen.
///
/// X coordinate in the specified screen. If default - center. Examples: 10, ^10 (reverse), .5f (fraction).
/// Y coordinate in the specified screen. If default - center.
/// Use this screen. If default, uses the primary screen. Example: screen.index(1).
/// Use the work area, not whole screen. Default true.
/// If part of rectangle is not in screen, move and/or resize it so that entire rectangle would be in screen. Default true.
///
/// This function can be used to calculate new window location before creating it. If window already exists, use .
///
public void MoveInScreen(Coord x, Coord y, screen screen = default, bool workArea = true, bool ensureInScreen = true) {
wnd.Internal_.MoveRectInScreen(false, ref this, x, y, screen, workArea, ensureInScreen);
}
///
/// Moves this rectangle to the specified coordinates in another rectangle r.
///
/// Another rectangle.
/// X coordinate relative to r. Default - center. Examples: 10, ^10 (reverse), .5f (fraction).
/// Y coordinate relative to r. Default - center.
/// If part of rectangle is not in r, move and/or resize it so that entire rectangle would be in r.
public void MoveInRect(RECT r, Coord x = default, Coord y = default, bool ensureInRect = false) {
wnd.Internal_.MoveRectInRect(ref this, x, y, r, ensureInRect);
}
///
/// Adjusts this rectangle to ensure that whole rectangle is in screen.
/// Initial and final rectangle coordinates are relative to the primary screen.
///
/// Use this screen (see ). If default, uses screen of the rectangle (or nearest).
/// Use the work area, not whole screen. Default true.
///
/// This function can be used to calculate new window location before creating it. If window already exists, use .
///
public void EnsureInScreen(screen screen = default, bool workArea = true) {
wnd.Internal_.MoveRectInScreen(true, ref this, default, default, screen, workArea, true);
}
///
/// Returns true if all fields of r1 and r2 are equal or almost equal with max difference +- maxDiff.
///
internal static bool EqualFuzzy_(RECT r1, RECT r2, int maxDiff) {
if (Math.Abs(r1.left - r2.left) > maxDiff) return false;
if (Math.Abs(r1.top - r2.top) > maxDiff) return false;
if (Math.Abs(r1.right - r2.right) > maxDiff) return false;
if (Math.Abs(r1.bottom - r2.bottom) > maxDiff) return false;
return true;
}
///
/// Converts to string "{L=left T=top W=width H=height}".
///
///
public override string ToString() {
return $"{{L={left.ToS()} T={top.ToS()} W={Width.ToS()} H={Height.ToS()}}}";
//note: don't change the format. Some functions parse it, eg TryParse and acc in C++.
//don't need R B. Rarely useful, just makes more difficult to read W H.
//return $"{{L={left} T={top} R={right} B={bottom} W={Width} H={Height}}}";
}
///
/// Converts to string "left top width height".
///
///
public string ToStringSimple() {
return $"{left.ToS()} {top.ToS()} {Width.ToS()} {Height.ToS()}";
}
///
/// Formats string from main fields and properties.
///
///
/// format string. Example: "({0}, {1}, {4}, {5})".
/// This function passes to AppendFormat 6 values in this order: left, top, right, bottom, Width, Height.
///
public string ToStringFormat(string format) {
using (new StringBuilder_(out var b)) {
b.AppendFormat(format, left.ToS(), top.ToS(), right.ToS(), bottom.ToS(), Width.ToS(), Height.ToS());
return b.ToString();
}
}
///
/// Converts string to .
///
/// false if invalid string format.
/// String in format "{L=left T=top W=width H=height}" () or "left top width height" ().
///
public static bool TryParse(string s, out RECT r) {
r = default;
bool ok;
if (s.Starts('{')) {
ok = s.Eq(1, "L=") && s.ToInt(out r.left, 3, out int e)
&& s.Eq(e, " T=") && s.ToInt(out r.top, e + 3, out e)
&& s.Eq(e, " W=") && s.ToInt(out r.right, e + 3, out e)
&& s.Eq(e, " H=") && s.ToInt(out r.bottom, e + 3, out e)
&& s.Length == e + 1 && s[e] == '}';
//tested: regex @"^\{L=(-?\d+) T=(-?\d+) W=(-?\d+) H=(-?\d+)\}$" 9 times slower.
} else {
ok = s.ToInt(out r.left, 0, out int e) && s.ToInt(out r.top, e, out e) && s.ToInt(out r.right, e, out e) && s.ToInt(out r.bottom, e);
}
if (ok) {
r.right += r.left;
r.bottom += r.top;
}
return ok;
}
#pragma warning restore 1591 //XML doc
}
///
/// Struct with fields int start and int end.
///
public record struct StartEnd {
///
public int start;
///
public int end;
///
public StartEnd(int start, int end) { this.start = start; this.end = end; }
///
/// Returns end - start.
///
public int Length => end - start;
///
/// Converts this to .
/// Can be used to get substring, like s[x.Range] instead of s[x.start..x.end].
///
public Range Range => start..end;
///
public static implicit operator Range(StartEnd s) => s.start..s.end;
/// The start or end of the range is from the end.
public static implicit operator StartEnd(Range r) {
if (r.Start.IsFromEnd || r.End.IsFromEnd) throw new ArgumentException();
return new(r.Start.Value, r.End.Value);
}
///
/// Gets string span.
///
[Obsolete, EditorBrowsable(EditorBrowsableState.Never)] //rarely used; easy with string.AsSpan; precedes `start` in intellisense.
public RStr Span(string s) => s.AsSpan(start, end - start);
///
public override string ToString() => $"({start}, {end})";
}
internal unsafe struct VARIANT : IDisposable {
public Api.VARENUM vt; //ushort
ushort _u1;
uint _u2;
public nint value;
public nint value2;
//note: cannot use FieldOffset because of different 32/64 bit size
public VARIANT(int x) { vt = Api.VARENUM.VT_I4; value = x; }
public VARIANT(string x) { vt = Api.VARENUM.VT_BSTR; value = Marshal.StringToBSTR(x); }
public static implicit operator VARIANT(int x) => new(x);
public static implicit operator VARIANT(string x) => new(x);
public int ValueInt { get { Debug.Assert(vt == Api.VARENUM.VT_I4); return (int)value; } }
public BSTR ValueBstr { get { Debug.Assert(vt == Api.VARENUM.VT_BSTR); return BSTR.AttachBSTR((char*)value); } }
///
/// Calls VariantClear.
///
public void Dispose() {
_Clear();
}
void _Clear() {
if (vt >= Api.VARENUM.VT_BSTR) Api.VariantClear(ref this);
else vt = 0; //info: VariantClear just sets vt=0 and does not clear other members
}
///
/// Converts to string.
///
public override string ToString() {
return _ToString();
}
string _ToString() {
switch (vt) {
case Api.VARENUM.VT_BSTR: return value == default ? null : ValueBstr.ToString();
case Api.VARENUM.VT_I4: return value.ToString();
case 0: case Api.VARENUM.VT_NULL: return null;
}
VARIANT v2 = default;
uint lcid = 0x409; //invariant
switch (vt & (Api.VARENUM)0xff) { case Api.VARENUM.VT_DATE: case Api.VARENUM.VT_DISPATCH: lcid = 0x400; break; } //LOCALE_USER_DEFAULT
if (0 != Api.VariantChangeTypeEx(ref v2, this, lcid, 2, Api.VARENUM.VT_BSTR)) return null; //2 VARIANT_ALPHABOOL
return v2.value == default ? null : v2.ValueBstr.ToStringAndDispose();
}
///
/// Converts to string.
/// Disposes this VARIANT.
///
public string ToStringAndDispose() {
var r = _ToString();
Dispose();
return r;
}
}
internal unsafe struct BSTR : IDisposable {
char* _p;
BSTR(char* p) => _p = p;
public static explicit operator BSTR(string s) => new((char*)Marshal.StringToBSTR(s));
public static explicit operator nint(BSTR b) => (nint)b._p;
public static BSTR AttachBSTR(char* bstr) => new(bstr);
public static BSTR CopyFrom(char* anyString) => anyString == null ? default : Api.SysAllocString(anyString);
public static BSTR Alloc(int len) => Api.SysAllocStringLen(null, len);
public char* Ptr => _p;
///
/// Returns true if the string is null.
///
public bool Is0 => _p == null;
public int Length => _p == null ? 0 : Api.SysStringLen(_p);
///
/// Unsafe.
///
public char this[int i] => _p[i];
///
/// Converts to string.
/// Does not dispose.
///
public override string ToString() {
var p = _p; if (p == null) return null;
return Marshal.PtrToStringBSTR((IntPtr)_p);
}
///
/// Converts to string and disposes.
///
public string ToStringAndDispose() {
var p = _p; if (p == null) return null;
int len = Api.SysStringLen(p);
//rejected:
//Some objects can return BSTR containing '\0's. Then probably the rest of string is garbage. I never noticed this but saw comments. Better allow '\0's, because in some cases it can be valid string. When invalid, it will not harm too much.
//int len2 = Ptr_.Length(p, len); Debug_.PrintIf(len2 != len, "BSTR with '\\0'"); len = len2;
string r = len == 0 ? "" : new string(p, 0, len);
Dispose();
return r;
}
public void Dispose() {
var t = _p;
if (t != null) {
_p = null;
Api.SysFreeString(t);
}
}
}
}
================================================
FILE: Au/Au.Types/unused/AuClassless.cs
================================================
//rejected
//namespace Au.Types
//{
// ///
// /// Contains functions of this library that can be called without a class, like Function() instead of Class.Function().
// /// In file global.cs: global using static Au.Types.AuClassless;.
// /// Currently empty. Reserved for the future.
// ///
// public static class AuClassless
// {
// //public static void TestClassless() { }
// }
//}
================================================
FILE: Au/Au.cs
================================================
/*/
role classLibrary;
define IDE_LA,AU,NO_GLOBAL,NO_DEFAULT_CHARSET_UNICODE;
noWarnings 419,649;
preBuild ..\@Au.Editor\_prePostBuild.cs;
outputPath %folders.Workspace%\dll;
miscFlags 1;
noRef *\Au.dll;
resource resources\red_cross_cursor.cur /path;
/*/
//Using outputPath %folders.Workspace%\dll; instead of outputPath %folders.Workspace%\..\Au.Editor; because the compiler would replace Au.dll with the older version.
// _prePostBuild will copy Au.dll to Au.Editor.
================================================
FILE: Au/Au.csproj
================================================
net10.0-windows
true
true
false
Au
Au
true
..\Au.snk
true
bin\Au.xml
419;8981
preview
true
true
False
False
AnyCPU
1.1.6
LibreAutomate
LibreAutomate
didgeridoo
LibreAutomate
LibreAutomate is an automation library for Windows. Mostly desktop and web UI automation. To get the most of it, install the LibreAutomate app.
Copyright (c) Gintaras Didžgalvis 2025
https://www.libreautomate.com
Icon-128.png
NuGet.md
https://github.com/qgindi/LibreAutomate
git
UI automation;automate;windows;desktop;web;UI;hotkey;autotext;trigger;toolbar;keys;mouse;keyboard;clipboard;send;task
MIT
False
portable
$(DefineConstants);AU
portable
$(DefineConstants);AU
================================================
FILE: Au/Ext/Bitmap.Resize.cs
================================================
//The main code is from FreeImage sources.
using System.Drawing;
using System.Drawing.Imaging;
namespace Au.Types;
///
/// Used with
///
public enum BRFilter {
/// Produces sharper image (less blurry) than Graphics.DrawImage with InterpolationMode.HighQualityBicubic.
Lanczos3,
/// Produces slightly sharper image (less blurry) than Graphics.DrawImage with InterpolationMode.HighQualityBicubic.
CatmullRom,
/// Produces image similar to Graphics.DrawImage with InterpolationMode.HighQualityBicubic.
Bicubic
}
public static partial class ExtMisc {
///
/// Resizes this image.
///
/// Resized image (new object). Returns this image if new width and height would be the same as of this image.
///
/// New width.
/// New height. If width or height is 0, calculates it (preserves aspect ratio).
///
/// When resized, call Dispose for this object.
///
/// Let the resized bitmap have PixelFormat = Format32bppPArgb. It prevents distortions at transparent-opaque boundaries.
/// If false: if this bitmap has Format32bppArgb or Format32bppPArgb, does not change, else PixelFormat.Format32bppArgb.
///
/// Unsupported PixelFormat.
public static unsafe Bitmap Resize(this Bitmap b, int width, int height, BRFilter filter, bool dispose, bool premultiplied = false) {
int wid1 = b.Width, hei1 = b.Height;
if (width < 1) width = Math.Max(1, Math2.MulDiv(wid1, height, hei1));
if (height < 1) height = Math.Max(1, Math2.MulDiv(hei1, width, wid1));
if (width == wid1 && height == hei1) return b;
var pf = b.PixelFormat;
if (pf == PixelFormat.Format32bppPArgb) premultiplied = true;
else if (premultiplied) pf = PixelFormat.Format32bppPArgb;
else if(pf is not (PixelFormat.Format32bppArgb or PixelFormat.Format32bppRgb)) pf = PixelFormat.Format32bppArgb;
var r = new Bitmap(width, height, pf);
var d1 = b.LockBits(new(0, 0, wid1, hei1), ImageLockMode.ReadOnly, pf);
var d2 = r.LockBits(new(0, 0, width, height), ImageLockMode.ReadWrite, pf);
try {
_FreeImage.Resize((byte*)d1.Scan0, wid1, hei1, (byte*)d2.Scan0, width, height, filter, premultiplied);
}
finally {
b.UnlockBits(d1);
r.UnlockBits(d2);
}
if (dispose) b.Dispose();
return r;
}
///
/// Scaling factor. For example 2 to make 2 times bigger, or 0.5 to make 2 times smaller.
public static Bitmap Resize(this Bitmap b, double factor, BRFilter filter, bool dispose, bool premultiplied = false) {
if (factor == 1) return b;
var z = b.Size;
int wid = (z.Width * factor).ToInt(), hei = (z.Height * factor).ToInt();
if (wid == z.Width && hei == z.Height) return b;
return Resize(b, wid, hei, filter, dispose, premultiplied);
}
static unsafe class _FreeImage {
internal static void Resize(byte* src, int srcWidth, int srcHeight, byte* dst, int dstWidth, int dstHeight, BRFilter filter, bool premultiplied) {
_Filter filt = filter switch {
BRFilter.Lanczos3 => new _FilterLanczos3(),
BRFilter.CatmullRom => new _FilterCatmullRom(),
BRFilter.Bicubic => new _FilterBicubic(),
_ => null
};
byte* tmp;
if (dstWidth <= srcWidth) {
// xy filtering
if (srcWidth != dstWidth) {
// source and destination widths are different so, we must
// filter horizontally
if (srcHeight != dstHeight) {
// source and destination heights are also different so, we need
// a temporary image
tmp = MemoryUtil.Alloc(dstWidth * srcHeight * 4);
} else {
// source and destination heights are equal so, we can directly
// factor into destination image (second filter method will not
// be invoked)
tmp = dst;
}
// factor source image horizontally into temporary (or destination) image
_horizontalFilter(src, srcHeight, srcWidth, tmp, dstWidth, filt, premultiplied);
} else {
// source and destination widths are equal so, just copy the
// image pointer
tmp = src;
}
if (srcHeight != dstHeight) {
// source and destination heights are different so, factor
// temporary (or source) image vertically into destination image
_verticalFilter(tmp, dstWidth, srcHeight, dst, dstHeight, filt, premultiplied);
}
} else {
// yx filtering
if (srcHeight != dstHeight) {
// source and destination heights are different so, we must
// filter vertically
if (srcWidth != dstWidth) {
// source and destination widths are also different so, we need
// a temporary image
tmp = MemoryUtil.Alloc(srcWidth * dstHeight * 4);
} else {
// source and destination widths are equal so, we can directly
// factor into destination image (second filter method will not
// be invoked)
tmp = dst;
}
// factor source image vertically into temporary (or destination) image
_verticalFilter(src, srcWidth, srcHeight, tmp, dstHeight, filt, premultiplied);
} else {
// source and destination heights are equal so, just copy the
// image pointer
tmp = src;
}
if (srcWidth != dstWidth) {
// source and destination heights are different so, factor
// temporary (or source) image horizontally into destination image
_horizontalFilter(tmp, dstHeight, srcWidth, dst, dstWidth, filt, premultiplied);
}
}
// free temporary image, if not pointing to either src or dst
if (tmp != src && tmp != dst) MemoryUtil.Free(tmp);
}
const int FI_RGBA_RED = 2, FI_RGBA_GREEN = 1, FI_RGBA_BLUE = 0, FI_RGBA_ALPHA = 3;
static void _horizontalFilter(byte* src, int height, int srcWidth, byte* dst, int dstWidth, _Filter filter, bool premultiplied) {
// allocate and calculate the contributions
_WeightsTable weightsTable = new(filter, dstWidth, srcWidth);
// step through rows
for (int y = 0; y < height; y++) {
// factor each row
byte* src_bits = src + srcWidth * 4 * y;
byte* dst_bits = dst + dstWidth * 4 * y;
for (int x = 0; x < dstWidth; x++) {
// loop through row
int iLeft = weightsTable.getLeftBoundary(x);
int iLimit = weightsTable.getRightBoundary(x) - iLeft;
byte* pixel = src_bits + iLeft * 4;
double r = 0, g = 0, b = 0, a = 0;
for (int i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
double weight = weightsTable.getWeight(x, i);
r += (weight * pixel[FI_RGBA_RED]);
g += (weight * pixel[FI_RGBA_GREEN]);
b += (weight * pixel[FI_RGBA_BLUE]);
a += (weight * pixel[FI_RGBA_ALPHA]);
pixel += 4;
}
// clamp and place result in destination pixel
int ai = Math.Clamp((int)(a + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (byte)ai;
if (!premultiplied) ai = 0xFF; //else colors cannot be > alpha
dst_bits[FI_RGBA_RED] = (byte)Math.Clamp((int)(r + 0.5), 0, ai);
dst_bits[FI_RGBA_GREEN] = (byte)Math.Clamp((int)(g + 0.5), 0, ai);
dst_bits[FI_RGBA_BLUE] = (byte)Math.Clamp((int)(b + 0.5), 0, ai);
dst_bits += 4;
}
}
}
static void _verticalFilter(byte* src, int width, int srcHeight, byte* dst, int dstHeight, _Filter filter, bool premultiplied) {
// allocate and calculate the contributions
_WeightsTable weightsTable = new(filter, dstHeight, srcHeight);
// step through columns
byte* src_base = src;
byte* dst_base = dst;
int src_pitch = width * 4;
int dst_pitch = src_pitch;
for (int x = 0; x < width; x++) {
// work on column x in dst
int index = x * 4;
byte* dst_bits = dst_base + index;
// factor each column
for (int y = 0; y < dstHeight; y++) {
// loop through column
int iLeft = weightsTable.getLeftBoundary(y);
int iLimit = weightsTable.getRightBoundary(y) - iLeft;
byte* src_bits = src_base + iLeft * src_pitch + index;
double r = 0, g = 0, b = 0, a = 0;
for (int i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
double weight = weightsTable.getWeight(y, i);
r += (weight * src_bits[FI_RGBA_RED]);
g += (weight * src_bits[FI_RGBA_GREEN]);
b += (weight * src_bits[FI_RGBA_BLUE]);
a += (weight * src_bits[FI_RGBA_ALPHA]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
int ai = Math.Clamp((int)(a + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (byte)ai;
if (!premultiplied) ai = 0xFF; //else colors cannot be > alpha
dst_bits[FI_RGBA_RED] = (byte)Math.Clamp((int)(r + 0.5), 0, ai);
dst_bits[FI_RGBA_GREEN] = (byte)Math.Clamp((int)(g + 0.5), 0, ai);
dst_bits[FI_RGBA_BLUE] = (byte)Math.Clamp((int)(b + 0.5), 0, ai);
dst_bits += dst_pitch;
}
}
}
struct _WeightsTable {
_Contribution[] _table;
int _windowSize;
int _lineLength;
public _WeightsTable(_Filter filter, int uDstSize, int uSrcSize) {
double dWidth;
double dFScale;
double dFilterWidth = filter.width;
// factor factor
double dScale = (double)uDstSize / uSrcSize;
if (dScale < 1.0) {
// minification
dWidth = dFilterWidth / dScale;
dFScale = dScale;
} else {
// magnification
dWidth = dFilterWidth;
dFScale = 1.0;
}
// allocate a new line contributions structure
//
// window size is the number of sampled pixels
_windowSize = 2 * (int)Math.Ceiling(dWidth) + 1;
// length of dst line (no. of rows / cols)
_lineLength = uDstSize;
// allocate list of contributions
_table = new _Contribution[_lineLength];
for (int u = 0; u < _lineLength; u++) {
// allocate contributions for every pixel
_table[u].Weights = new double[_windowSize];
}
// offset for discrete to continuous coordinate conversion
double dOffset = 0.5 / dScale;
for (int u = 0; u < _lineLength; u++) {
// scan through line of contributions
// inverse mapping (discrete dst 'u' to continous src 'dCenter')
double dCenter = u / dScale + dOffset;
// find the significant edge points that affect the pixel
int iLeft = Math.Max(0, (int)(dCenter - dWidth + 0.5));
int iRight = Math.Min((int)(dCenter + dWidth + 0.5), uSrcSize);
_table[u].Left = iLeft;
_table[u].Right = iRight;
double dTotalWeight = 0; // sum of weights (initialized to zero)
for (int iSrc = iLeft; iSrc < iRight; iSrc++) {
// calculate weights
double weight = dFScale * filter.Filter(dFScale * (iSrc + 0.5 - dCenter));
_table[u].Weights[iSrc - iLeft] = weight;
dTotalWeight += weight;
}
if ((dTotalWeight > 0) && (dTotalWeight != 1)) {
// normalize weight of neighbouring points
for (int iSrc = iLeft; iSrc < iRight; iSrc++) {
// normalize point
_table[u].Weights[iSrc - iLeft] /= dTotalWeight;
}
}
// simplify the filter, discarding null weights at the right
{
int iTrailing = iRight - iLeft - 1;
while (_table[u].Weights[iTrailing] == 0) {
_table[u].Right--;
iTrailing--;
if (_table[u].Right == _table[u].Left) {
break;
}
}
}
} // next dst pixel
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double getWeight(int dst_pos, int src_pos) {
return _table[dst_pos].Weights[src_pos];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int getLeftBoundary(int dst_pos) {
return _table[dst_pos].Left;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int getRightBoundary(int dst_pos) {
return _table[dst_pos].Right;
}
struct _Contribution {
public double[] Weights;
public int Left, Right;
}
}
#if true
abstract class _Filter {
public abstract double width { get; }
public abstract double Filter(double dVal);
}
class _FilterLanczos3 : _Filter {
public override double width => 3;
public override double Filter(double dVal) {
dVal = Math.Abs(dVal);
if (dVal < width) {
return (_sinc(dVal) * _sinc(dVal / width));
}
return 0;
}
static double _sinc(double value) {
if (value != 0) {
value *= Math.PI;
return (Math.Sin(value) / value);
}
return 1;
}
}
class _FilterCatmullRom : _Filter {
public override double width => 2;
public override double Filter(double dVal) {
if (dVal < -2) return 0;
if (dVal < -1) return (0.5 * (4 + dVal * (8 + dVal * (5 + dVal))));
if (dVal < 0) return (0.5 * (2 + dVal * dVal * (-5 - 3 * dVal)));
if (dVal < 1) return (0.5 * (2 + dVal * dVal * (-5 + 3 * dVal)));
if (dVal < 2) return (0.5 * (4 + dVal * (-8 + dVal * (5 - dVal))));
return 0;
}
}
class _FilterBicubic : _Filter {
readonly double p0, p2, p3;
readonly double q0, q1, q2, q3;
public _FilterBicubic() {
double b = 1 / 3d, c = b;
p0 = (6 - 2 * b) / 6;
p2 = (-18 + 12 * b + 6 * c) / 6;
p3 = (12 - 9 * b - 6 * c) / 6;
q0 = (8 * b + 24 * c) / 6;
q1 = (-12 * b - 48 * c) / 6;
q2 = (6 * b + 30 * c) / 6;
q3 = (-b - 6 * c) / 6;
}
public override double width => 2;
public override double Filter(double dVal) {
dVal = Math.Abs(dVal);
if (dVal < 1)
return (p0 + dVal * dVal * (p2 + dVal * p3));
if (dVal < 2)
return (q0 + dVal * (q1 + dVal * (q2 + dVal * q3)));
return 0;
}
}
#else
class _Filter {
public readonly double width = 3;
public double Filter(double dVal) {
dVal = Math.Abs(dVal);
if (dVal < width) {
return (_sinc(dVal) * _sinc(dVal / width));
}
return 0;
}
static double _sinc(double value) {
if (value != 0) {
value *= Math.PI;
return (Math.Sin(value) / value);
}
return 1;
}
}
#endif
}
}
================================================
FILE: Au/Ext/ExtMisc.cs
================================================
//note: be careful when adding functions to this class. Eg something may load winforms dlls although it seems not used.
using System.Drawing;
using System.Drawing.Imaging;
using Microsoft.Win32;
namespace Au.Types;
///
/// Adds extension methods for some .NET types.
///
[DebuggerStepThrough]
public static unsafe partial class ExtMisc {
#region value types
///
/// Converts to int with rounding.
/// Calls .
///
///
public static int ToInt(this double t) => Convert.ToInt32(t);
///
/// Converts to int with rounding.
/// Calls .
///
///
public static int ToInt(this float t) => Convert.ToInt32(t);
///
/// Converts to int with rounding.
/// Calls .
///
///
public static int ToInt(this decimal t) => Convert.ToInt32(t);
//rejected. Too simple, and nobody would find and use.
/////
///// Converts to int.
///// Can be used like 0xff123456.ToInt() instead of unchecked((int)0xff123456).
/////
//public static int ToInt(this uint t) => unchecked((int)t);
/////
///// Converts to Color.
///// Can be used like 0xff123456.ToColor_() instead of Color.FromArgb(unchecked((int)0xff123456)).
/////
/////
///// Add 0xff000000.
//internal static Color ToColor_(this uint t, bool makeOpaque = true)
// => Color.FromArgb(unchecked((int)(t | (makeOpaque ? 0xff000000 : 0))));
///
/// Converts to Color. Makes opaque (alpha 0xff).
/// Can be used like 0x123456.ToColor_() instead of Color.FromArgb(unchecked((int)0xff123456)).
///
internal static Color ToColor_(this int t, bool bgr = false) {
if (bgr) t = ColorInt.SwapRB(t);
return Color.FromArgb(unchecked(0xff << 24 | t));
}
///
/// Converts double to string.
/// Uses invariant culture, therefore decimal point is always '.', not ',' etc.
/// Calls .
///
public static string ToS(this double t, string format = null) {
return t.ToString(format, NumberFormatInfo.InvariantInfo);
}
///
/// Converts float to string.
/// Uses invariant culture, therefore decimal point is always '.', not ',' etc.
/// Calls .
///
public static string ToS(this float t, string format = null) {
return t.ToString(format, NumberFormatInfo.InvariantInfo);
}
///
/// Converts decimal to string.
/// Uses invariant culture, therefore decimal point is always '.', not ',' etc.
/// Calls .
///
public static string ToS(this decimal t, string format = null) {
return t.ToString(format, NumberFormatInfo.InvariantInfo);
}
///
/// Converts int to string.
/// Uses invariant culture, therefore minus sign is always ASCII '-', not '−' etc.
/// Calls .
///
public static string ToS(this int t, string format = null) {
return t.ToString(format, NumberFormatInfo.InvariantInfo);
}
///
/// Converts long to string.
/// Uses invariant culture, therefore minus sign is always ASCII '-', not '−' etc.
/// Calls .
///
public static string ToS(this long t, string format = null) {
return t.ToString(format, NumberFormatInfo.InvariantInfo);
}
///
/// Converts nint to string.
/// Uses invariant culture, therefore minus sign is always ASCII '-', not '−' etc.
/// Calls .
///
public static string ToS(this nint t, string format = null) {
return t.ToString(format, NumberFormatInfo.InvariantInfo);
}
//cref not nint.ToString because DocFX does not support it.
///
/// Returns = min && this <= max) ? this : defaultValue]]>.
///
internal static int EnsureValid_(this int t, int min, int max, int defaultValue = 0)
=> (t >= min && t <= max) ? t : defaultValue;
//rare
/////
///// Returns true if t.Width <= 0 || t.Height <= 0.
///// Note: Rectangle.IsEmpty returns true only when all fields are 0.
/////
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public static bool NoArea(this Rectangle t) {
// return t.Width <= 0 || t.Height <= 0;
//}
///
/// Calls and returns start and end instead of start and length.
///
///
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (int start, int end) GetStartEnd(this Range t, int length) {
var v = t.GetOffsetAndLength(length);
return (v.Offset, v.Offset + v.Length);
}
///
/// If this is null, returns (0, length). Else calls and returns start and end instead of start and length.
///
///
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (int start, int end) GetStartEnd(this Range? t, int length)
=> t?.GetStartEnd(length) ?? (0, length);
///
/// If this is null, returns (0, length). Else calls .
///
///
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (int Offset, int Length) GetOffsetAndLength(this Range? t, int length)
=> t?.GetOffsetAndLength(length) ?? (0, length);
///
/// Returns true if null pointer.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNull(this ReadOnlySpan t) => t == ReadOnlySpan.Empty;
//currently not used. Creates shorter string than ToString.
/////
///// Converts this Guid to Base64 string.
/////
//public static string ToBase64(this Guid t) => Convert.ToBase64String(new RByte((byte*)&t, sizeof(Guid)));
//rejected: too simple. We have print.it(uint), also can use $"0x{t:X}" or "0x" + t.ToString("X").
/////
///// Converts int to hexadecimal string like "0x3A".
/////
//public static string ToHex(this int t)
//{
// return "0x" + t.ToString("X");
//}
#endregion
#region enum
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static long _ToLong(T v) where T : unmanaged, Enum {
if (sizeof(T) == 4) return *(int*)&v;
if (sizeof(T) == 8) return *(long*)&v;
if (sizeof(T) == 2) return *(short*)&v;
return *(byte*)&v;
//Compiler removes the if(sizeof(T) == n) and code that is unused with that size, because sizeof(T) is const.
//Faster than with switch(sizeof(T)). It seems the switch code is considered too big to be inlined.
}
//same. Was faster when tested in the past.
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//static long _ToLong2(T v) where T : unmanaged, Enum
//{
// if(sizeof(T) == 4) return Unsafe.As(ref v);
// if(sizeof(T) == 8) return Unsafe.As(ref v);
// if(sizeof(T) == 2) return Unsafe.As(ref v);
// return Unsafe.As(ref v);
//}
///
/// Returns true if this enum variable has all flag bits specified in flag.
///
///
/// One or more flags.
///
/// The same as code (t & flag) == flag or .
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Has(this T t, T flag) where T : unmanaged, Enum {
#if false //Enum.HasFlag used to be slow, but now compiler for it creates the same code as with operator
return t.HasFlag(flag);
//However cannot use this because of JIT compiler bug: in some cases Has returns true when no flag.
//Noticed it in TriggerActionThreads.Run in finally{} of actionWrapper, code o.flags.Has(TOFlags.Single).
//It was elusive, difficult to debug, only in Release, and only after some time/times, when tiered JIT fully optimizes.
//When Has returned true, print.it showed that flags is 0.
//No bug if HasFlag called directly, not in extension method.
#elif true //slightly slower than Enum.HasFlag and code as with operator
var m = _ToLong(flag);
return (_ToLong(t) & m) == m;
#else //slower
switch(sizeof(T)) {
case 4: {
var a = Unsafe.As(ref t);
var b = Unsafe.As(ref flag);
return (a & b) == b;
}
case 8: {
var a = Unsafe.As(ref t);
var b = Unsafe.As(ref flag);
return (a & b) == b;
}
case 2: {
var a = Unsafe.As(ref t);
var b = Unsafe.As(ref flag);
return (a & b) == b;
}
default: {
var a = Unsafe.As(ref t);
var b = Unsafe.As(ref flag);
return (a & b) == b;
}
}
//compiler removes the switch/case, because sizeof(T) is const
#endif
}
///
/// Returns true if this enum variable has one or more flag bits specified in flags.
///
///
/// One or more flags.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasAny(this T t, T flags) where T : unmanaged, Enum {
return (_ToLong(t) & _ToLong(flags)) != 0;
}
//slower
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public static bool HasAny5(this T t, T flags) where T : unmanaged, Enum
//{
// if(sizeof(T) == 4) return (*(int*)&t & *(int*)&flags) != 0;
// if(sizeof(T) == 8) return (*(long*)&t & *(long*)&flags) != 0;
// if(sizeof(T) == 2) return (*(short*)&t & *(short*)&flags) != 0;
// return (*(byte*)&t & *(byte*)&flags) != 0;
//}
///
/// Adds or removes a flag or flags.
///
///
/// One or more flags to add or remove.
/// If true, adds flag, else removes flag.
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static void SetFlag(ref this T t, T flag, bool add) where T : unmanaged, Enum {
long a = _ToLong(t), b = _ToLong(flag);
if (add) a |= b; else a &= ~b;
t = *(T*)&a;
}
///
/// Adds or removes a flag or flags.
///
/// Flag(s) to add or remove.
/// If true, adds the flag(s) (t |= flag), else removes (t &= ~flag).
internal static void SetFlag_(this ref int t, int flag, bool add) {
if (add) t |= flag; else t &= ~flag;
}
///
internal static void SetFlag_(this ref uint t, uint flag, bool set) {
if (set) t |= flag; else t &= ~flag;
}
///
internal static void SetFlag_(this ref ushort t, ushort flag, bool set) {
if (set) t |= flag; else t = (ushort)(t & ~flag);
}
///
internal static void SetFlag_(this ref byte t, byte flag, bool set) {
if (set) t |= flag; else t = (byte)(t & ~flag);
}
#endregion
#region char
///
/// Returns true if character is ASCII '0' to '9'.
///
public static bool IsAsciiDigit(this char c) => char.IsAsciiDigit(c);
///
/// Returns true if character is ASCII 'A' to 'Z' or 'a' to 'z'.
///
//public static bool IsAsciiAlpha(this char c) => (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
public static bool IsAsciiAlpha(this char c) => char.IsAsciiLetter(c);
///
/// Returns true if character is ASCII 'A' to 'Z' or 'a' to 'z' or '0' to '9'.
///
public static bool IsAsciiAlphaDigit(this char c) => char.IsAsciiLetterOrDigit(c);
#endregion
#region array
///
/// Creates a copy of this array with one or more removed elements.
///
///
///
///
///
///
public static T[] RemoveAt(this T[] t, int index, int count = 1) {
if ((uint)index > t.Length || count < 0 || index + count > t.Length) throw new ArgumentOutOfRangeException();
int n = t.Length - count;
if (n == 0) return [];
var r = new T[n];
for (int i = 0; i < index; i++) r[i] = t[i];
for (int i = index; i < n; i++) r[i] = t[i + count];
return r;
}
///
/// Creates a copy of this array with one inserted element.
///
///
///
/// Where to insert. If -1, adds to the end.
///
///
public static T[] InsertAt(this T[] t, int index, T value = default) {
if (index == -1) index = t.Length; else if ((uint)index > t.Length) throw new ArgumentOutOfRangeException();
var r = new T[t.Length + 1];
for (int i = 0; i < index; i++) r[i] = t[i];
for (int i = index; i < t.Length; i++) r[i + 1] = t[i];
r[index] = value;
return r;
}
///
/// Creates a copy of this array with several inserted elements.
///
///
///
/// Where to insert. If -1, adds to the end.
///
///
public static T[] InsertAt(this T[] t, int index, params ReadOnlySpan values) {
if (index == -1) index = t.Length; else if ((uint)index > t.Length) throw new ArgumentOutOfRangeException();
int n = values.Length; if (n == 0) return t;
var r = new T[t.Length + n];
for (int i = 0; i < index; i++) r[i] = t[i];
for (int i = index; i < t.Length; i++) r[i + n] = t[i];
for (int i = 0; i < n; i++) r[i + index] = values[i];
return r;
}
#endregion
#region IEnumerable
///
/// Removes items based on a predicate. For example, all items that have certain value.
///
///
///
///
///
public static void RemoveWhere(this Dictionary t, Func, bool> predicate) {
foreach (var k in t.Where(predicate).Select(kv => kv.Key).ToArray()) { t.Remove(k); }
}
///
/// Gets a reference to a TValue in this dictionary, adding a new entry with a default value if the key does not exist.
/// This extension method just calls .
///
///
///
/// ();
/// for (int i = 0; i < 3; i++) {
/// ref var r = ref d.GetValueRefOrAddDefault("a", out bool exists);
/// print.it(exists);
/// if(!exists) r = 100; else r++;
/// }
/// print.it(d);
/// ]]>
///
internal static ref TValue GetValueRefOrAddDefault_(this Dictionary t, TKey key, out bool exists) {
#pragma warning disable 9088 //weird and undocumented: "This returns a parameter by reference 'exists' but it is scoped to the current method"
return ref CollectionsMarshal.GetValueRefOrAddDefault(t, key, out exists);
}
///
/// Gets a reference to a TValue in this dictionary. If the key does not exist, sets exists = false and returns a reference null.
/// This extension method just calls and .
///
/// Receives true if the key exists.
///
internal static ref TValue GetValueRefOrNullRef_(this Dictionary t, TKey key, out bool exists) {
ref TValue r = ref CollectionsMarshal.GetValueRefOrNullRef(t, key);
exists = !Unsafe.IsNullRef(ref r);
return ref r;
}
///
public static Span AsSpan(this List t)
=> CollectionsMarshal.AsSpan(t);
///
/// Gets a reference to an item.
/// List items must not be added or removed while it is in use.
///
///
/// Item index.
public static ref T Ref(this List t, int i)
=> ref CollectionsMarshal.AsSpan(t)[i];
///
/// Adds key/value to dictionary. If the key already exists, adds the value to the same key as List item and returns the List; else returns null.
///
/// key/value already exists.
internal static List MultiAdd_(this Dictionary t, TKey k, TValue v) where TValue : class {
if (t.TryAdd(k, v)) return null;
var o = t[k];
if (o is List a) {
if (!a.Contains(v)) { a.Add(v); return a; }
} else {
var g = o as TValue;
if (g == null && o != null) throw new ArgumentException("bad type");
if (v != g) { t[k] = a = new List { g, v }; return a; }
}
throw new ArgumentException("key/value already exists");
}
///
/// If dictionary contains key k that contains value v (as single value or in List), removes the value (and key if it was single value) and returns true.
///
internal static bool MultiRemove_(this Dictionary t, TKey k, TValue v) where TValue : class {
if (!t.TryGetValue(k, out var o)) return false;
if (o is List a) {
if (!a.Remove(v)) return false;
if (a.Count == 1) t[k] = a[0];
} else {
var g = o as TValue;
if (g == null && o != null) throw new ArgumentException("bad type");
if (v != g) return false;
t.Remove(k);
}
return true;
}
///
/// If dictionary contains key k, gets its value (v) or list of values (a) and returns true.
///
///
///
/// Receives single value, or null if the key has multiple values.
/// Receives multiple values, or null if the key has single value.
internal static bool MultiGet_(this Dictionary t, TKey k, out TValue v, out List a) where TValue : class {
bool r = t.TryGetValue(k, out var o);
v = o as TValue;
a = o as List;
if (v == null && a == null && o != null) throw new ArgumentException("bad type");
return r;
}
#if true
///
/// Returns