diff --git a/.gitattributes b/.gitattributes index e4b52966..668d793f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -45,3 +45,7 @@ test/repro-templates/vb6/** -text # in every checkout for the same reason: PngDump.bas is VB6's, and the copy is compared # with the template line by line. test/repro-templates/png/** -text + +# A package test project's resources are read by its cases byte for byte (TBMANLIB_RES +# compares them with what it returns), and most have no extension git would know. +test/package-tests/*/Resources/** -text diff --git a/WIP.PackageTests.md b/WIP.PackageTests.md index f3b301cc..f2536d42 100644 --- a/WIP.PackageTests.md +++ b/WIP.PackageTests.md @@ -86,6 +86,17 @@ Available Packages with Embedded cleared) and restarts the compiler; an author p their own build. The runner embeds the package from `%TEMP%\tb-twinserv` instead, as `check_examples` does, so it never touches a packages folder. +## Compile cases that expect an error are a project of their own (owner) + +A compile case that expects an error is, by design, a compile error of the project it is in, and +the compiler's test mode runs nothing in a project with errors: `tbrun --tests` reports every +`[TestCase]` *not run*. The first package with such claims (Generics: TB5074 on `New` of a package +generic, TB5124 on its unusable tuples) lost all its run-time cases to one line. So those cases +go in the package folder's `compile/`, a second project that `package_tests.mjs` stages with the +package embedded and judges with the rest. The published project stays `Sources/` alone, which +compiles, so the author's Sub Main still runs every run-time case; the compile cases are the +documentation's own re-check on each new build and version. + ## Markers, and a gate that only tightens A callout names its cases in an HTML comment on the line above it. The marker lives with the diff --git a/WIP.md b/WIP.md index 7142ba3d..a303f340 100644 --- a/WIP.md +++ b/WIP.md @@ -35,7 +35,7 @@ sibling. History goes nowhere: git keeps it. Reference documentation is **complete** for all thirteen packages: VBA, VBRUN, VB, WebView2, Assert, CustomControls, CEF, WinEventLogLib, WinNamedPipesLib, WinServicesLib, tbIDE, WinNativeCommonCtls and AppGlobalClassObject. It is adapted from primary sources: Microsoft VBA-Docs (CC-BY-4.0) for the runtime library, `.twin` source for the twinBASIC-specific packages. The CEF and WebView2 packages also carry a tutorial set. -Twenty-six **contributed** packages from TWINSERV have sections under `docs/Reference/Contributed/`: ArrayListLib, FileProps, Fmt, Generics, OLEGuids, WinReg, TwinTimerLib, tbFileSysTools, TBMANLIB, TBMANLIB_Aes, TBMANLIB_ConsoleComponents, TBMANLIB_ConsoleCore, TBMANLIB_Diagnostic, TBMANLIB_Dialog, TBMANLIB_ListBoxHScroll, TBMANLIB_RES, TBMANLIB_Xml, tbProjPacker, tbRegExp, tsJson, tsPwGen, VBA_JSON and xCalendar complete; AutoCompleteLib, BigNumberLib and VBComDlg their main class only. The two console packages' console claims follow the source, because the samples harness cannot capture a console. Generics (seven tuple classes) and xCalendar (one control) are each one page (owner), so `analyze` in audit mode lists their classes and members as undocumented. What xCalendar's page says it does at run time comes from `test/package-tests/xCalendar/probe/`, which runs the control on a form in a built exe. Fmt documents its test code (`HelloWorldClass`, `HelloWorldModule`, `Pootle`) in one paragraph of its landing page, which `exclude_from_docs` names; Generics' `exclude_from_docs` names its test code, `HelloWorldModule`, which the page does not mention. VBComDlg's `exclude_from_docs` names `CommonDialogBase`, a module whose public procedures only its class calls. tbRegExp's `exclude_from_docs` names its private engine module, `StaticRegex`, and tbProjPacker's its helper modules, `modImpExp` and `modWin32`: the indexer counts a Public Enum as public even inside a Private Module, because a built-in package's are reached through its library symbol written with an asterisk (`*WinNativeCommonCtls`, as `ImlDrawConstants` says). By its bare name no such enum is reachable, in a built-in or a contributed package (TB5079 on BETA 1005). OLEGuids documents each interface's methods on the interface's page. What TBMANLIB_Dialog's pages say its dialogs do comes from `test/package-tests/TBMANLIB_Dialog/probe/`, which operates them in a built exe on a private desktop; run it again when the package's version changes. TBMANLIB_Dialog 1.1.0.0 has a page of its own for each class except cDialog, which keeps a page for each member, so `analyze` in audit mode lists the members of those one-page classes as undocumented; its `exclude_from_docs` names its test module, `TestModule`, and the seven COM interface declarations that cDialogEx calls inside (`IOleWindow`, `IModalWindow`, `IShellItem`, `IShellItemArray`, `IFileDialog`, `IFileOpenDialog`, `IFileSaveDialog`), which its landing page describes in a paragraph. cDialogEx 1.1.0.0 returns the name of the chosen file, not its path, ignores `InitialDir` and ignores a `Filter` that has no closing pipe, each documented as a WARNING. tbRegExp is the first section drafted by the doc-draft workflow ([WIP.Indexer.md](WIP.Indexer.md#draft-and-the-doc-draft-workflow)), then reviewed against the sources, the package's README and VBScript's own **RegExp** under `cscript`. tbRegExp 1.9.0.0 raises `vbObjectError + 3016` from a search that uses up its step budget and returns nothing (1.8.0.0's `SearchAborted` is gone); the budget is 10 steps for each character squared, at least 1,000,000 and at most 100,000,000, for the search of one match, and `RegExp.SearchStepLimit` replaces it with a fixed number, documented on its own page and in a section of the landing page. On the dev box (BETA 1005, win32) `\d+x` costs between 2.0 and 2.2 × n² steps, so it finishes on 7,000 digits (15 s) and raises on 10,000 (16 s); `(a+)+b` raises in 0.2 s on 30 or 300 `a` but takes 16 s on 3,000; `a*` matches 1,000,000 characters whole in about half a second. Its pattern syntax has a page of its own, `PatternSyntax.md`. Each one has a chapter in the book's Packages part. **ArrayListLib, BigNumberLib, TBMANLIB, TBMANLIB_Aes, TBMANLIB_ConsoleComponents, TBMANLIB_ConsoleCore, TBMANLIB_Diagnostic, TBMANLIB_Dialog, TBMANLIB_Xml, WinReg and xCalendar are listed under Experimental Packages** (`docs/Reference/Experimental.md`, owner): ArrayListLib and WinReg do not compile on BETA 1005 (each documented from its source and a copy of it that builds, its samples compile-only; in a copy of ArrayListLib 1.6.151.1 with the arguments of its three `VSwap` calls passed `ByVal` and `LSetByVal` taking a `LongPtr`, a For Each over a list with no items fails, ToString of such a list never returns, Reverse keeps the middle pair of an even run in place and a second GetEnumerator over a list ignores its arguments; in a copy of WinReg's source with its four failing calls passed `ByVal`, ReadString, GetRegistryValueType and the first ConvertValueToString give wrong results), xCalendar has a grid that keeps the old month's dates when Value is set to another month, and text colour properties that have no effect, BigNumberLib 1.0.2.0 compiles, but loses the minus sign after `IsNegative = True` and in a negative operand of the greatest common divisor and the least common multiple, makes a zero from `AssignFromLong` negative, and does not return from the modular inverse of zero (all four confirmed by a second agent), TBMANLIB 1.0.0.29 does not compile in a 64-bit build (TB5001 in cSubClass, where the window procedure is passed as a Long) and has many members that fail or give wrong results, TBMANLIB_ConsoleComponents cannot take a cConsole of the published TBMANLIB_ConsoleCore (it embeds a copy of it, the same package at 1.0.0.1, whose types the compiler treats as different; checked in a project made by the IDE's own import, in either order), TBMANLIB_ConsoleCore reads keys wrongly, TBMANLIB_Diagnostic cannot create its Dictionary and misplaces List items, TBMANLIB_Dialog 1.1.0.0 has classes that fail at their main task (cDialogFindReplace reports nothing, cDialogPageSetup ignores paper and orientation, cDialogEx returns names without their folder, cDialogPassword shows the password in its confirmation boxes; every defect confirmed by a second agent, owner), TBMANLIB_Aes runs other cipher modes than the ones named (its default CBC is ECB) and fails the NIST test vectors, and TBMANLIB_Xml's parser misreads self-closing elements and entities and does not return for some malformed text (and its XPath engine reads the step `..` and `and` with `or` wrongly). TBMANLIB_Xml's `exclude_from_docs` names its test module, `mXmlTests`, which its landing page describes in a paragraph, and the five types of its XPath engine (`XPathTokenType`, `XPathAxisType`, `XPathOperator`, `XPathStep`, `XPathPredicate`), which no public member takes or returns and which the mXPath page mentions in a paragraph; tsPwGen's names its five test modules and its six internal modules, and tsJson's its test and internal modules likewise, which their landing pages describe in a paragraph each. See *Where things live* for how a package moves between the two lists. +Twenty-six **contributed** packages from TWINSERV have sections under `docs/Reference/Contributed/`: ArrayListLib, FileProps, Fmt, Generics, OLEGuids, WinReg, TwinTimerLib, tbFileSysTools, TBMANLIB, TBMANLIB_Aes, TBMANLIB_ConsoleComponents, TBMANLIB_ConsoleCore, TBMANLIB_Diagnostic, TBMANLIB_Dialog, TBMANLIB_ListBoxHScroll, TBMANLIB_RES, TBMANLIB_Xml, tbProjPacker, tbRegExp, tsJson, tsPwGen, VBA_JSON and xCalendar complete; AutoCompleteLib, BigNumberLib and VBComDlg their main class only. TBMANLIB_ConsoleCore's console claims are checked by its test cases, each on a console screen buffer of its own with its input written by `WriteConsoleInput`; TBMANLIB_ConsoleComponents' follow the source, because the samples harness cannot capture a console. Generics (seven tuple classes) and xCalendar (one control) are each one page (owner), so `analyze` in audit mode lists their classes and members as undocumented. What xCalendar's page says it does at run time comes from `test/package-tests/xCalendar/probe/`, which runs the control on a form in a built exe. Fmt documents its test code (`HelloWorldClass`, `HelloWorldModule`, `Pootle`) in one paragraph of its landing page, which `exclude_from_docs` names; Generics' `exclude_from_docs` names its test code, `HelloWorldModule`, which the page does not mention. VBComDlg's `exclude_from_docs` names `CommonDialogBase`, a module whose public procedures only its class calls. tbRegExp's `exclude_from_docs` names its private engine module, `StaticRegex`, and tbProjPacker's its helper modules, `modImpExp` and `modWin32`: the indexer counts a Public Enum as public even inside a Private Module, because a built-in package's are reached through its library symbol written with an asterisk (`*WinNativeCommonCtls`, as `ImlDrawConstants` says). By its bare name no such enum is reachable, in a built-in or a contributed package (TB5079 on BETA 1005). OLEGuids documents each interface's methods on the interface's page. What TBMANLIB_Dialog's pages say its dialogs do comes from `test/package-tests/TBMANLIB_Dialog/probe/`, which operates them in a built exe on a private desktop; run it again when the package's version changes. TBMANLIB_Dialog 1.1.0.0 has a page of its own for each class except cDialog, which keeps a page for each member, so `analyze` in audit mode lists the members of those one-page classes as undocumented; its `exclude_from_docs` names its test module, `TestModule`, and the seven COM interface declarations that cDialogEx calls inside (`IOleWindow`, `IModalWindow`, `IShellItem`, `IShellItemArray`, `IFileDialog`, `IFileOpenDialog`, `IFileSaveDialog`), which its landing page describes in a paragraph. cDialogEx 1.1.0.0 returns the name of the chosen file, not its path, ignores `InitialDir` and ignores a `Filter` that has no closing pipe, each documented as a WARNING. tbRegExp is the first section drafted by the doc-draft workflow ([WIP.Indexer.md](WIP.Indexer.md#draft-and-the-doc-draft-workflow)), then reviewed against the sources, the package's README and VBScript's own **RegExp** under `cscript`. tbRegExp 1.9.0.0 raises `vbObjectError + 3016` from a search that uses up its step budget and returns nothing (1.8.0.0's `SearchAborted` is gone); the budget is 10 steps for each character squared, at least 1,000,000 and at most 100,000,000, for the search of one match, and `RegExp.SearchStepLimit` replaces it with a fixed number, documented on its own page and in a section of the landing page. On the dev box (BETA 1005, win32) `\d+x` costs between 2.0 and 2.2 × n² steps, so it finishes on 7,000 digits (15 s) and raises on 10,000 (16 s); `(a+)+b` raises in 0.2 s on 30 or 300 `a` but takes 16 s on 3,000; `a*` matches 1,000,000 characters whole in about half a second. Its pattern syntax has a page of its own, `PatternSyntax.md`. Each one has a chapter in the book's Packages part. **ArrayListLib, BigNumberLib, TBMANLIB, TBMANLIB_Aes, TBMANLIB_ConsoleComponents, TBMANLIB_ConsoleCore, TBMANLIB_Diagnostic, TBMANLIB_Dialog, TBMANLIB_ListBoxHScroll, TBMANLIB_Xml, WinReg and xCalendar are listed under Experimental Packages** (`docs/Reference/Experimental.md`, owner): ArrayListLib and WinReg do not compile on BETA 1005 (each documented from its source and a copy of it that builds, its samples compile-only; in a copy of ArrayListLib 1.6.151.1 with the arguments of its three `VSwap` calls passed `ByVal` and `LSetByVal` taking a `LongPtr`, a For Each over a list with no items fails, ToString of such a list never returns, Reverse keeps the middle pair of an even run in place and a second GetEnumerator over a list ignores its arguments; in a copy of WinReg's source with its four failing calls passed `ByVal`, ReadString, GetRegistryValueType and the first ConvertValueToString give wrong results), xCalendar has a grid that keeps the old month's dates when Value is set to another month, and text colour properties that have no effect, BigNumberLib 1.0.2.0 compiles, but loses the minus sign after `IsNegative = True` and in a negative operand of the greatest common divisor and the least common multiple, makes a zero from `AssignFromLong` negative, and does not return from the modular inverse of zero (all four confirmed by a second agent), TBMANLIB 1.0.0.29 does not compile in a 64-bit build (TB5001 in cSubClass, where the window procedure is passed as a Long) and has many members that fail or give wrong results, TBMANLIB_ConsoleComponents cannot take a cConsole of the published TBMANLIB_ConsoleCore (it embeds a copy of it, the same package at 1.0.0.1, whose types the compiler treats as different; checked in a project made by the IDE's own import, in either order), TBMANLIB_ConsoleCore reads keys wrongly, TBMANLIB_Diagnostic cannot create its Dictionary and misplaces List items, TBMANLIB_Dialog 1.1.0.0 has classes that fail at their main task (cDialogFindReplace reports nothing, cDialogPageSetup ignores paper and orientation, cDialogEx returns names without their folder, cDialogPassword shows the password in its confirmation boxes; every defect confirmed by a second agent, owner), TBMANLIB_ListBoxHScroll 1.0.0.1 moves the thumb 20 px per arrow and one page per track click while the list box scrolls its contents by its own step, so thumb and text disagree (confirmed by a second agent, owner), TBMANLIB_Aes runs other cipher modes than the ones named (its default CBC is ECB) and fails the NIST test vectors, and TBMANLIB_Xml's parser misreads self-closing elements and entities and does not return for some malformed text (and its XPath engine reads the step `..` and `and` with `or` wrongly). TBMANLIB_Xml's `exclude_from_docs` names its test module, `mXmlTests`, which its landing page describes in a paragraph, and the five types of its XPath engine (`XPathTokenType`, `XPathAxisType`, `XPathOperator`, `XPathStep`, `XPathPredicate`), which no public member takes or returns and which the mXPath page mentions in a paragraph; tsPwGen's names its five test modules and its six internal modules, and tsJson's its test and internal modules likewise, which their landing pages describe in a paragraph each. See *Where things live* for how a package moves between the two lists. The rest of this file is the maintenance guide for updating existing pages or adding new ones: package surface notes, page conventions, and the integrity check. @@ -80,7 +80,7 @@ The rest of this file is the maintenance guide for updating existing pages or ad > would file the page as a *static file* and serve the raw markdown. Editors on > Windows add a BOM unasked. `lib/frontmatter.mjs` strips a leading BOM before parsing. - `docs/Reference/Built-In/tbIDE/` — IDE Extensibility package (this is the **addin SDK**). The package is type-only — it ships **public interfaces + CoClasses** that an addin DLL binds to; every implementation behind them lives in the twinBASIC IDE itself. The user-facing surface is one entry-point factory (`tbCreateCompilerAddin`) plus 23 CoClasses grouped by role: the addin contract (`AddIn`), the root API (`Host`), the loaded `Project`, the editors collection (`Editor` / `CodeEditor` / `Editors`), the virtual file system (`FileSystem` / `FileSystemItem` / `Folder` / `File`), the in-IDE UI surface (`Toolbar` / `Toolbars` / `Button` / `ToolWindow` / `ToolWindows`), the HTML DOM inside a tool window (`HtmlElement` / `HtmlElements` / `HtmlElementProperty` / `HtmlElementProperties` / `HtmlEventProperty` / `HtmlEventProperties`), the `DebugConsole`, `KeyboardShortcuts`, `Themes`, and the single concrete user-instantiable helper class `AddinTimer`. Flat layout — one page per CoClass / Class plus the index landing. -- `docs/Reference/Contributed/` — the contributed packages, one folder each, landing page `Contributed.md` beside it. **A package's landing page opens, under its H1, with `**Author:**`** (owner): the publisher TWINSERV lists (`publisher` in `indexer/.packages/manifests/contributed.json`), or the author the package's own sources name when that is someone else (VBA_JSON: Tim Hall, published by WaynePhillipsEA). Next to the author, on the same line, `**Version:**`: the version the section documents, which is its `indexed_from` and changes with it (owner). Then `**On GitHub:**` with a link, when a repository holds the package, or the library the package says it is made from; search GitHub for the package's name and the author's account, and check that the repository holds the package's code (its history too: `TBMANLIB_*` were never in woeoio's `tbman`). A landing page with one or two sections has no `* TOC` list. A class with member pages is a folder, `/index.md` at `/tB/Packages/Contributed///` with its members beside it, so a member links to its class as `(.)`; a class documented on one page is `.md`. Each package is a chapter of the book's Packages part in `_book.yml`. A section's `indexed_from` is the four-part TWINSERV version, and its snapshot is under `indexer/snapshots/contributed/`. **Every NOTE and WARNING of a section is backed by test cases** in the package's test project, `test/package-tests//` (owner; [its README](test/package-tests/README.md)), and names them in a `` marker on the line above it, which `check_package_tests.mjs` in `test.bat` enforces. **A NOTE or WARNING is only for a real difference or limit** (owner): a statement that describes how the package works stays prose, tied to its cases by a region, the marker on a line of its own above it and `` after it (owner). A difference from the object the package replaces that gives a different result silently (tbRegExp's `\1(a)`, `{,m}`, `$~` against VBScript) is a NOTE, not a WARNING (owner). A case checks what is correct, not what the package does (owner): a defect is a failing case, recorded in the project's `expect.json`, so the project tells the package's author what to fix. A section whose project is not written yet is in that gate's `PENDING` set. `package-tests.bat` runs the cases; a case it reports *fixed* means the callout goes. **The landing page ends, after See Also, with `## Test cases`**: one or two sentences linking to `-tests.twinproj` (owner), which the build publishes beside the section without the package's code in it (owner): it references the package as a linked package, which the reader puts in `%APPDATA%\twinBASIC\packages` ([WIP.PackageTests.md](WIP.PackageTests.md)). Its samples compile in the `contributed-` sample template, whose `Settings` names the package's version: **change that version with `indexed_from`** ([WIP.ExamplesBuild.md](WIP.ExamplesBuild.md#template-projects)). **A contributed package is documented and its samples compiled against the newest twinBASIC BETA installed, never an older one** (owner): the version that matters is the package's, and a statement about the compiler (such as "does not compile on BETA 1005") names that newest build. **A contributed package that does not compile on that build, or has defects that a program would meet, is listed under Experimental Packages** (owner); a minor defect that the section documents does not make it Experimental (owner: VBComDlg, whose Replace All raises no event, stays Contributed; owner: nor does a fixed limit that the section states at the top of its landing page). **A package moves from Contributed to Experimental for its defects only after a separate agent has reproduced each defect independently** and ruled out misuse, a wrong expectation, a twinBASIC defect and the harness (owner): `docs/Reference/Experimental.md`, at `/tB/Packages/Experimental/`, after Contributed Packages. Its landing page's `parent:` is `Experimental Packages`, its line is in `Experimental.md` and not in `Contributed.md`, and its book chapter follows the Experimental Packages chapter. Its folder and URLs stay under `Contributed/`, so a package that is fixed moves back by changing only those three, and its pages keep their addresses. +- `docs/Reference/Contributed/` — the contributed packages, one folder each, landing page `Contributed.md` beside it. **A package's landing page opens, under its H1, with `**Author:**`** (owner): the publisher TWINSERV lists (`publisher` in `indexer/.packages/manifests/contributed.json`), or the author the package's own sources name when that is someone else (VBA_JSON: Tim Hall, published by WaynePhillipsEA). Next to the author, on the same line, `**Version:**`: the version the section documents, which is its `indexed_from` and changes with it (owner). Then `**On GitHub:**` with a link, when a repository holds the package, or the library the package says it is made from; search GitHub for the package's name and the author's account, and check that the repository holds the package's code (its history too: `TBMANLIB_*` were never in woeoio's `tbman`). A landing page with one or two sections has no `* TOC` list. A class with member pages is a folder, `/index.md` at `/tB/Packages/Contributed///` with its members beside it, so a member links to its class as `(.)`; a class documented on one page is `.md`. Each package is a chapter of the book's Packages part in `_book.yml`. A section's `indexed_from` is the four-part TWINSERV version, and its snapshot is under `indexer/snapshots/contributed/`. **Every NOTE and WARNING of a section is backed by test cases** in the package's test project, `test/package-tests//` (owner; [its README](test/package-tests/README.md)), and names them in a `` marker on the line above it, which `check_package_tests.mjs` in `test.bat` enforces. **A NOTE or WARNING is only for a real difference or limit** (owner): a statement that describes how the package works stays prose, tied to its cases by a region, the marker on a line of its own above it and `` after it (owner). A difference from the object the package replaces that gives a different result silently (tbRegExp's `\1(a)`, `{,m}`, `$~` against VBScript) is a NOTE, not a WARNING (owner). A case checks what is correct, not what the package does (owner): a defect is a failing case, recorded in the project's `expect.json`, so the project tells the package's author what to fix. A section whose project is not written yet is in that gate's `PENDING` set. **A package that does not compile on the newest BETA gets no test project** (owner: ArrayListLib, WinReg): it stays in `PENDING` until a version of it compiles. One that compiles for one architecture only is tested on that one (owner: TBMANLIB, win32; `arches` in its `expect.json`, test/package-tests/README.md). `package-tests.bat` runs the cases; a case it reports *fixed* means the callout goes. **The landing page ends, after See Also, with `## Test cases`**: one or two sentences linking to `-tests.twinproj` (owner), which the build publishes beside the section without the package's code in it (owner; one exception, owner: TBMANLIB_ConsoleComponents' project carries verbatim copies of seven of its component files, in `Sources/ComponentSources/`, because the package's own components cannot be given a console object, and its drawing can be checked only on the copies): it references the package as a linked package, which the reader puts in `%APPDATA%\twinBASIC\packages` ([WIP.PackageTests.md](WIP.PackageTests.md)). Its samples compile in the `contributed-` sample template, whose `Settings` names the package's version: **change that version with `indexed_from`** ([WIP.ExamplesBuild.md](WIP.ExamplesBuild.md#template-projects)). **A contributed package is documented and its samples compiled against the newest twinBASIC BETA installed, never an older one** (owner): the version that matters is the package's, and a statement about the compiler (such as "does not compile on BETA 1005") names that newest build. **A contributed package that does not compile on that build, or has defects that a program would meet, is listed under Experimental Packages** (owner); a minor defect that the section documents does not make it Experimental (owner: VBComDlg, whose Replace All raises no event, stays Contributed; owner: nor does a fixed limit that the section states at the top of its landing page; owner: tsJson 1.0.15.0 stays Contributed, its defects confirmed by a second agent and each stated in a WARNING with a way round it). **A package moves from Contributed to Experimental for its defects only after a separate agent has reproduced each defect independently** and ruled out misuse, a wrong expectation, a twinBASIC defect and the harness (owner): `docs/Reference/Experimental.md`, at `/tB/Packages/Experimental/`, after Contributed Packages. Its landing page's `parent:` is `Experimental Packages`, its line is in `Experimental.md` and not in `Contributed.md`, and its book chapter follows the Experimental Packages chapter. Its folder and URLs stay under `Contributed/`, so a package that is fixed moves back by changing only those three, and its pages keep their addresses. - `docs/Downloads/index.md` — the Downloads page, `/Downloads`, a top-level page at `nav_order: 4` and in `_book.yml`'s `left_out:`. **Every file the documentation publishes for readers has a card or a row there**, and the page that documents it keeps its own link (owner). Cards are `
` inside `
` (styles at the end of `custom.scss`), with the theme's `.btn` for each file. The release job publishes every same-site link on the built page that has the `download` attribute, beside the zip and the PDF, so a new download needs no change to the workflow. `{{tbdocs:size:}}` gives a published file's size and `{{tbdocs:packageTestRows}}` the test projects' rows, both expanded before render; the help add-in's size is prose ("about 51 MB" in October 2026; it is also on the Help Add-In page), because its project embeds the offline tree, this page included, so it cannot be known before render. The PDF's release URL spells it `twinBASIC.Book.pdf`: GitHub turns the space into a dot, and the spaced name 404s. The footer's **Read Offline** link (`read_offline_link` in `_config.yml`) goes to the page's `#read-offline`. - `docs/Reference/Statements.md` — alphabetical index of language statements. - `docs/Reference/Procedures and Functions.md` — alphabetical index of procedures/functions. diff --git a/builder/package-tests.mjs b/builder/package-tests.mjs index f1e5e7e4..0e65d899 100644 --- a/builder/package-tests.mjs +++ b/builder/package-tests.mjs @@ -18,7 +18,14 @@ import { promises as fs } from "node:fs"; import path from "node:path"; -import { downloadName, packageTestNames, templateSettings, testSettings, testSources } from "../lib/package-tests.mjs"; +import { + downloadName, + packageTestNames, + resourceFiles, + templateSettings, + testSettings, + testSources, +} from "../lib/package-tests.mjs"; import { packFiles } from "../scripts/impexp.mjs"; import { posix } from "./paths.mjs"; @@ -50,6 +57,7 @@ export async function packPackageTests(cfg, pkg) { const files = [{ rel: "Settings", bytes: Buffer.from(`${JSON.stringify(settings, null, "\t")}\n`, "utf8") }]; for (const f of testSources(cfg.src, pkg)) files.push({ rel: `Sources/${f.name}`, bytes: Buffer.from(f.text, "utf8") }); + for (const r of resourceFiles(cfg.src, pkg)) files.push({ rel: `Resources/${r.name}`, bytes: r.bytes }); return packFiles(files, `${pkg}Tests`).bytes; } diff --git a/docs/Documentation/Tools-Compiler.md b/docs/Documentation/Tools-Compiler.md index fd0a32e0..39938c43 100644 --- a/docs/Documentation/Tools-Compiler.md +++ b/docs/Documentation/Tools-Compiler.md @@ -179,7 +179,7 @@ A defect that is a compile-time diagnostic is a **compile case** instead: a line | `--llvm` | Compile the whole probe, and the exe, with LLVM. The same as `--compiler-options +llvm`. | | `--compiler-options ` | The project's compiler options, for the run and the exe. | | `--exe` | Also run the built exe; print what it writes with `TbRun.Out`, its exit code, the fault the event log records for it, and the boxes it opened, each closed with OK. | -| `--tests` | Build nothing: judge the compile cases and run each `[TestCase]` Sub in the compiler's test mode, printing `PASS` or `FAIL` for each. `--timeout` is the limit on each case. Not with `--exe`. | +| `--tests` | Build nothing: judge the compile cases and run each `[TestCase]` Sub in the compiler's test mode, printing `PASS` or `FAIL` for each. `--timeout` is the limit on each case, not on the run. A case still running at the limit is reported as `no answer from the compiler`, and the cases after it as `not run: did not return`: the compiler cannot stop a case, and refuses every other request (`main thread is busy`) until it ends. Run the cases after it again in a new `tbrun` with `--only`. Not with `--exe`. | | `--raw` | Keep the console's timestamp column, which is otherwise stripped. | | `--json` | One object with the path of the built file, the target, the captured lines, whether the probe returned, the licence an LLVM run checked, the exe's run, the IDE pid and anything reaped. | | `--keep` | Leave the IDE running and print its pid as `ide-pid: ` (with `--json`, `idePid`) and the `taskkill` commands, as for `tbbuild`. Implies `--no-reap`, and leaves the IDE's registry entries for the probe as they are. | diff --git a/docs/Documentation/Tools-Samples.md b/docs/Documentation/Tools-Samples.md index 154b237c..7757684f 100644 --- a/docs/Documentation/Tools-Samples.md +++ b/docs/Documentation/Tools-Samples.md @@ -118,6 +118,8 @@ For each package and architecture (both by default), the tool stages the project - **fixed** --- `expect.json` says it fails or never returns, and it passes: the page's callout and the entry go; - **changed** --- anything else: a claim of the page is no longer true, or the case is wrong. +A compile case that expects an error is in the package folder's `compile/`, a second project of compile cases only, because a project with compile errors runs none of its test cases. The tool stages it with the package embedded as well and judges its cases with the others; it is not published. + A case that never returns stops the compiler. The tool catches it, runs the cases after it again in a new IDE, and from then on, once `expect.json` says so, runs it alone with `--hang-timeout`. `--update` writes `expect.json` from the run, for the architectures run. The run also writes `RunAll.twin` again whenever the cases or `expect.json` change; [`check_package_tests.mjs`](Tools-Toolchain-Tests#check-package-tests) fails while it is out of date. The build publishes each project as `-tests.twinproj` beside the package's section, and the section's landing page links to it. It carries none of the package's code: it references the package as a [linked package](../../Features/Packages/Linked), which the IDE loads from `%APPDATA%\twinBASIC\packages`. With the package's file there, the project's `Sub Main` runs every case and prints `PASS`, `FAIL` or `SKIP` for each in the DEBUG CONSOLE. diff --git a/docs/Documentation/Tools-Toolchain-Tests.md b/docs/Documentation/Tools-Toolchain-Tests.md index befb63ba..5c04200a 100644 --- a/docs/Documentation/Tools-Toolchain-Tests.md +++ b/docs/Documentation/Tools-Toolchain-Tests.md @@ -370,7 +370,7 @@ A callout names its cases in an HTML comment on the line above it: The same comment anywhere else opens a region, which `` closes: it ties the paragraphs, lists, tables and samples between them, which describe how the package works, to the cases that check them. Regions do not nest, and a callout inside one is marked by it. -Each item is a `Fixture.Sub` for a `[TestCase]`, `compile ` for a compile case, `probe ` for a scenario of the package's `probe/` harness, or `none -- ` for a callout no program can check. For a package with a test project, the gate fails when a callout has neither a marker nor a region around it, when a region is not closed or opens inside another, when an item names nothing that exists, when a case is named by no marker, when a `[TestCase]` does not start with `Expect.Begin` and end with `Expect.Done`, when `expect.json` is for another version than the section's `indexed_from` or names a case that does not exist, and when the generated `RunAll.twin` is out of date. A section with no test project yet is listed in the script's `PENDING` set; the gate fails on a name there with a project, and on a section neither there nor with one, so the list only shrinks. +Each item is a `Fixture.Sub` for a `[TestCase]`, `compile ` for a compile case, `probe ` for a scenario of the package's `probe/` harness, or `none -- ` for a callout no program can check. For a package with a test project, the gate fails when a callout has neither a marker nor a region around it, when a region is not closed or opens inside another, when an item names nothing that exists, when a case is named by no marker (a compile case of the package's `compile/` project included), when `compile/` holds a `[TestCase]`, when a `[TestCase]` does not start with `Expect.Begin` and end with `Expect.Done`, when `expect.json` is for another version than the section's `indexed_from` or names a case that does not exist, and when the generated `RunAll.twin` is out of date. A section with no test project yet is listed in the script's `PENDING` set; the gate fails on a name there with a project, and on a section neither there nor with one, so the list only shrinks. Thirteen probes run first, on pages, sources and an `expect.json` built in memory: how a marker, a region and their items are read, the RunAll module, how `expect.json` is read and written, and how a case's result is judged. diff --git a/docs/Downloads/index.md b/docs/Downloads/index.md index 95205081..cf444614 100644 --- a/docs/Downloads/index.md +++ b/docs/Downloads/index.md @@ -66,7 +66,7 @@ Every page of this site, to read from disk in any browser with no network. Navig ## Package test projects -Each contributed package that has test cases publishes them as a twinBASIC project. The project holds only the tests: it references the package, which goes in `%APPDATA%\twinBASIC\packages`. The package's landing page says what the cases check. +For each contributed package it has test cases for, the documentation project publishes those cases as a twinBASIC project. The project holds only the tests: it references the package, which goes in `%APPDATA%\twinBASIC\packages`. The package's landing page says what the cases check. | Package | Test project | Size | |---------|--------------|------| diff --git a/docs/Reference/Contributed.md b/docs/Reference/Contributed.md index a68773f3..fda89630 100644 --- a/docs/Reference/Contributed.md +++ b/docs/Reference/Contributed.md @@ -18,7 +18,6 @@ Each package is versioned apart from twinBASIC, and its section says which versi - [**Generics**](Generics/) -- tuples, read-only objects that hold one to seven values of declared types - [**OLEGuids**](OLEGuids/) -- OLE and COM interfaces and structures in twinBASIC source, in place of `OLEGuids.tlb` - [**tbFileSysTools**](tbFileSysTools/) -- files, folders, drives and text streams, in place of the Scripting Runtime's **FileSystemObject** -- [**TBMANLIB_ListBoxHScroll**](TBMANLIB_ListBoxHScroll/) -- a horizontal scroll bar for a **ListBox** - [**TBMANLIB_RES**](TBMANLIB_RES/) -- resources embedded in an executable, as bytes, strings, hex, Base64 or pictures - [**tbProjPacker**](tbProjPacker/) -- a `.twinproj` or `.twinpack` file unpacked into a folder of source files, and packed again - [**tbRegExp**](tbRegExp/) -- regular expressions, in place of VBScript's **RegExp** object diff --git a/docs/Reference/Contributed/AutoCompleteLib/AutoComplete.md b/docs/Reference/Contributed/AutoCompleteLib/AutoComplete.md index 112a3708..f456c110 100644 --- a/docs/Reference/Contributed/AutoCompleteLib/AutoComplete.md +++ b/docs/Reference/Contributed/AutoCompleteLib/AutoComplete.md @@ -10,7 +10,9 @@ has_toc: false A class that attaches Windows shell auto-complete behaviour to a text-input control's window handle. + **AutoComplete** wraps the `IAutoComplete2` COM interface. After calling [**Init**](#init) with the window handle of an edit box and a source type, the Windows shell begins offering suggestions as the user types. The source can be the shell history, the file system, the most-recently-used list, all three combined, or a custom string array supplied through [**CustomSource**](#customsource). + ```tb check_build Private m_AC As AutoComplete @@ -34,10 +36,14 @@ Returns or sets a one-dimensional **String** array used as the suggestion list w Syntax: *object*.**CustomSource** [ = *StringArray* ] + *StringArray* -: A one-dimensional **Variant** holding a **String** array. Empty strings and non-string elements are silently ignored. Assigning **Empty** clears the list. A two-or-more-dimensional array raises run-time error 5 ("Array must be single dimensioned"). An unallocated array raises run-time error 91. Any other value that is not an array raises run-time error 380. +: A one-dimensional **Variant** holding a **String** array. Empty strings and non-string elements are silently ignored. Assigning **Empty** clears the list. A two-or-more-dimensional array raises run-time error 5 ("Array must be single dimensioned"). An unallocated array raises run-time error 91. Any other value that is not an array raises run-time error 380. An assignment that raises an error leaves the list as it was. + -The getter returns the filtered array currently stored in the object. Assigning a new value replaces the previous list in full; there is no way to append individual entries. + +The getter returns the filtered array currently stored in the object: a zero-based array of **String**, whatever the bounds of the array that was assigned. While the list is empty, the array is not allocated, and **UBound** of it raises run-time error 9. Assigning a new value replaces the previous list in full, also while the list is attached to a window; there is no way to append individual entries. + ### FileSystemOptions {: .no_toc } @@ -59,6 +65,10 @@ Syntax: *object*.**FileSystemOptions** [ = *value* ] | **AutoCompleteFileSystemOptionFileSysOnly** | 16 | File-system items only (no virtual shell folders). | | **AutoCompleteFileSystemOptionFileSysDirs** | 32 | Directories only. | + +The getter returns the value that was set. The setting applies whether it is made before or after [**Init**](#init), and to the source **AutoCompleteSourceAll** as to **AutoCompleteSourceFileSystem**. With **AutoCompleteFileSystemOptionFileSysDirs**, the list offers directories and no files; with **AutoCompleteFileSystemOptionFileSysOnly**, it offers files. + + ### Options {: .no_toc } @@ -81,6 +91,10 @@ Syntax: *object*.**Options** [ = *value* ] | **AutoCompleteOptionUpDownKeyDropsList** | &H20 | Opens the drop-down list when the user presses the **Up** or **Down** arrow key. | | **AutoCompleteOptionRightToLeft** | &H40 | Shows the text of the drop-down list from right to left. | + +The getter returns the value that was set. The setting applies whether it is made before or after [**Init**](#init). With **AutoCompleteOptionAppend** the text of the best match is appended to what was typed and selected; with **AutoCompleteOptionNone** nothing is appended. + + ## Methods ### Disable @@ -90,7 +104,9 @@ Disables auto-complete without destroying the object. Syntax: *object*.**Disable** + Auto-complete stops offering suggestions. Call [**Enable**](#enable) to turn it back on without calling [**Init**](#init) again. + ### DroppedDown {: .no_toc } @@ -101,10 +117,16 @@ Syntax: *object*.**DroppedDown** ( ) **As Boolean** Returns **True** when the drop-down is open, **False** otherwise. The return value is **False** if the `IAutoCompleteDropDown` interface cannot be obtained. + +An object that has not been initialised, and one whose list has not been opened, return **False**. + + ### Enable {: .no_toc } + Re-enables auto-complete after a call to [**Disable**](#disable). + Syntax: *object*.**Enable** @@ -130,7 +152,17 @@ Syntax: *object*.**Init** *hWndEdit*, *Source* | **AutoCompleteSourceAll** | 4 | History, file system, and MRU combined through an `IObjMgr`. | | **AutoCompleteSourceCustomSource** | 5 | The one-dimensional string array set through [**CustomSource**](#customsource). | -The target control must exist when **Init** is called, and **Init** is called before the user starts typing. Calling **Init** with **AutoCompleteSourceNone**, or with any other value outside the table, raises run-time error 380 (Invalid property value). If the internal `IAutoComplete2` object could not be created when the **AutoComplete** object was created, **Init** exits silently. +The target control must exist when **Init** is called, and **Init** is called before the user starts typing. + + +Calling **Init** with **AutoCompleteSourceNone**, or with any other value outside the table, raises run-time error 380 (Invalid property value). A *hWndEdit* of 0 raises run-time error 5 (Automation error). + + +If the internal `IAutoComplete2` object could not be created when the **AutoComplete** object was created, **Init** exits silently. + + +> [!NOTE] 1.0.3.0 +> An **AutoComplete** object attaches to one window, once. A second call to **Init** on the same object, for the same window or for another, raises run-time error -2147467259 (Automation error) and attaches nothing. Create one **AutoComplete** object for each text box. ### Refresh {: .no_toc } diff --git a/docs/Reference/Contributed/AutoCompleteLib/index.md b/docs/Reference/Contributed/AutoCompleteLib/index.md index 679a285f..6382d292 100644 --- a/docs/Reference/Contributed/AutoCompleteLib/index.md +++ b/docs/Reference/Contributed/AutoCompleteLib/index.md @@ -16,3 +16,7 @@ The **AutoCompleteLib** contributed package provides Windows shell auto-complete ## Classes - [AutoComplete](AutoComplete) -- attaches Windows shell auto-complete behaviour to a text-input control's window handle. + +## Test cases + +The notes and warnings of this section are checked by the test cases in [AutoCompleteLib-tests.twinproj](AutoCompleteLib-tests.twinproj), a twinBASIC project. It uses AutoCompleteLib as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/BigNumberLib/BigNumber.md b/docs/Reference/Contributed/BigNumberLib/BigNumber.md index ba8731fb..3c56eb88 100644 --- a/docs/Reference/Contributed/BigNumberLib/BigNumber.md +++ b/docs/Reference/Contributed/BigNumberLib/BigNumber.md @@ -8,6 +8,7 @@ has_toc: false # BigNumber class {: .no_toc } + A **BigNumber** represents an arbitrary-precision signed integer backed by the TomsFastMath (libtfm) C library, supporting numbers up to 4096 bits. A multiplication accepts operands of up to 2048 bits each. The default property is [**ValueString**](#valuestring). ```tb check_build check_run @@ -20,13 +21,17 @@ b.ValueString = "987654321098765432109876543210" result.AssignAddition a, b Debug.Print result.ValueString ' 1111111110111111111011111111100 ``` + + > [!NOTE] since BETA 434 > The class needs this build of twinBASIC or a later one. + > [!NOTE] 1.0.2.0 > String-to-value conversions silently stop at the first invalid character --- for example, setting `ValueHex = "ABC123he"` stores `&HABC123` without raising an error. + > [!WARNING] 1.0.2.0 > Overflow is not trapped. A result that needs more bits than the library holds loses its high bits, and a string with more digits than fit gives a wrong value. No error is raised in either case. The library holds a little more than the 4096 bits the class supports, 4352 bits in a 32-bit program and 4608 in a 64-bit one, so a result a few bits over 4096 may still be right. @@ -38,6 +43,7 @@ Debug.Print result.ValueString ' 1111111110111111111011111111100 ### ValueString {: .no_toc } + Returns or sets the value as a decimal string representation. **Default property.** Syntax: *object*.**ValueString** [ = *value* ] @@ -46,10 +52,12 @@ Syntax: *object*.**ValueString** [ = *value* ] : A **String** containing a decimal integer, optionally preceded by a minus sign. Conversion stops at the first character that is not a valid decimal digit, so a leading plus sign or space gives 0. A string with no digits gives 0. The getter returns the decimal digits, preceded by a minus sign for a negative value. + ### ValueHex {: .no_toc } + Returns or sets the value as a hexadecimal string representation. Syntax: *object*.**ValueHex** [ = *value* ] @@ -70,10 +78,12 @@ Debug.Print n.ValueHex ' -FF Debug.Print n.ValueOctal ' -377 Debug.Print n.ValueBinary ' -11111111 ``` + ### ValueOctal {: .no_toc } + Returns or sets the value as an octal string representation. Syntax: *object*.**ValueOctal** [ = *value* ] @@ -82,10 +92,12 @@ Syntax: *object*.**ValueOctal** [ = *value* ] : A **String** containing an octal integer (digits `0`--`7`), optionally preceded by a minus sign. Conversion stops at the first character that is not a valid octal digit. The getter returns the digits without leading zeros, preceded by a minus sign for a negative value. + ### ValueBinary {: .no_toc } + Returns or sets the value as a binary string representation. Syntax: *object*.**ValueBinary** [ = *value* ] @@ -94,10 +106,12 @@ Syntax: *object*.**ValueBinary** [ = *value* ] : A **String** containing a binary integer (digits `0` and `1`), optionally preceded by a minus sign. Conversion stops at the first character that is not `0` or `1`. The getter returns the digits without leading zeros, preceded by a minus sign for a negative value. + ### IsNegative {: .no_toc } + Returns or sets whether the value is negative. Syntax: *object*.**IsNegative** [ = *value* ] @@ -106,7 +120,9 @@ Syntax: *object*.**IsNegative** [ = *value* ] : A **Boolean**. Setting **False** makes the number non-negative. Setting **True** is meant to make it negative, but see the warning. The getter returns **True** for a negative number, and **False** for a positive number and for zero, except the negative zero that [**AssignFromLong**](#assignfromlong) describes. + + > [!WARNING] 1.0.2.0 > Setting **IsNegative** to **True** does not make the number negative. The property stores -1, the value of **True**, where the library expects 1, so the number is in a state that the library does not handle: [**ValueString**](#valuestring) and the other conversions show no minus sign, **IsNegative** still returns **True**, and arithmetic with the number gives wrong results. To make a number negative, set **ValueString** to a string that starts with a minus sign, or use [**AssignNegation**](#assignnegation). [**AssignGreatestCommonDivisor**](#assigngreatestcommondivisor) and [**AssignLeastCommonMultiple**](#assignleastcommonmultiple) set this state in a negative operand. @@ -123,6 +139,7 @@ Debug.Print n.ValueString ' 5 ### AssignAddition {: .no_toc } + Sets this instance to `a + b`. Syntax: *object*.**AssignAddition** *a*, *b* @@ -132,10 +149,12 @@ Syntax: *object*.**AssignAddition** *a*, *b* *b* : *required* A **BigNumber** holding the second operand. + ### AssignSubtraction {: .no_toc } + Sets this instance to `a - b`. Syntax: *object*.**AssignSubtraction** *a*, *b* @@ -145,10 +164,12 @@ Syntax: *object*.**AssignSubtraction** *a*, *b* *b* : *required* A **BigNumber** holding the subtrahend. + ### AssignMultiplication {: .no_toc } + Sets this instance to `a * b`. Syntax: *object*.**AssignMultiplication** *a*, *b* @@ -158,10 +179,12 @@ Syntax: *object*.**AssignMultiplication** *a*, *b* *b* : *required* A **BigNumber** holding the second factor. + ### AssignDivision {: .no_toc } + Sets this instance to the integer quotient `a \ b`, truncated toward zero. Raises run-time error 5 if *b* is zero. Syntax: *object*.**AssignDivision** *a*, *b* @@ -189,10 +212,12 @@ q.AssignDivision a, b Debug.Print Err.Number & " " & Err.Description ' 5 invalid operation Debug.Print q.ValueString ' -3 ``` + ### AssignModulus {: .no_toc } + Sets this instance to `a Mod b`. The result is zero or has the sign of *b*, so `-7 Mod 2` is 1 and `7 Mod -2` is -1. Raises run-time error 5 if *b* is zero. Syntax: *object*.**AssignModulus** *a*, *b* @@ -202,7 +227,9 @@ Syntax: *object*.**AssignModulus** *a*, *b* *b* : *required* A **BigNumber** holding the divisor. Must not be zero. + + > [!WARNING] 1.0.2.0 > When *b* is negative and the result is zero, the result is a negative zero: [**IsNegative**](#isnegative) returns **True** and [**IsEqualTo**](#isequalto) does not find it equal to a zero read from a string. [**AssignFromLong**](#assignfromlong) describes it. @@ -224,19 +251,23 @@ Debug.Print r.ValueString ' -1 ### AssignSquared {: .no_toc } + Sets this instance to `a * a` (the square of *a*). Syntax: *object*.**AssignSquared** *a* *a* : *required* A **BigNumber** holding the value to square. + + > [!WARNING] 1.0.2.0 > The square of zero is a negative zero, as [**AssignFromLong**](#assignfromlong) describes. ### AssignAdditionModulus {: .no_toc } + Sets this instance to `(a + b) Mod mod_c`. Raises run-time error 5 if *mod_c* is zero. Syntax: *object*.**AssignAdditionModulus** *a*, *b*, *mod_c* @@ -267,10 +298,12 @@ Debug.Print r.ValueString ' 0 r.AssignMultiplicationModulus a, b, m Debug.Print r.ValueString ' 2 ``` + ### AssignSubtractionModulus {: .no_toc } + Sets this instance to `(a - b) Mod mod_c`. Raises run-time error 5 if *mod_c* is zero. [**AssignAdditionModulus**](#assignadditionmodulus) describes the sign of the result. Syntax: *object*.**AssignSubtractionModulus** *a*, *b*, *mod_c* @@ -283,10 +316,12 @@ Syntax: *object*.**AssignSubtractionModulus** *a*, *b*, *mod_c* *mod_c* : *required* A **BigNumber** holding the modulus. It should be positive. + ### AssignMultiplicationModulus {: .no_toc } + Sets this instance to `(a * b) Mod mod_c`. Raises run-time error 5 if *mod_c* is zero. [**AssignAdditionModulus**](#assignadditionmodulus) describes the sign of the result. Syntax: *object*.**AssignMultiplicationModulus** *a*, *b*, *mod_c* @@ -299,10 +334,12 @@ Syntax: *object*.**AssignMultiplicationModulus** *a*, *b*, *mod_c* *mod_c* : *required* A **BigNumber** holding the modulus. It should be positive. + ### AssignSquaredModulus {: .no_toc } + Sets this instance to `(a * a) Mod mod_b`. Raises run-time error 5 if *mod_b* is zero. [**AssignAdditionModulus**](#assignadditionmodulus) describes the sign of the result. Syntax: *object*.**AssignSquaredModulus** *a*, *mod_b* @@ -312,11 +349,13 @@ Syntax: *object*.**AssignSquaredModulus** *a*, *mod_b* *mod_b* : *required* A **BigNumber** holding the modulus. It should be positive. + ### AssignModularMultiplicativeInverse {: .no_toc } -Sets this instance to the modular multiplicative inverse of *a* modulo *mod_b* --- the value *x* such that `(a * x) Mod mod_b = 1`. Raises run-time error 5 if the inverse does not exist, which is the case when *a* and *mod_b* have a common factor and when *a* is a multiple of *mod_b*. For a negative *a* the result is negative: the inverse of -3 modulo 7 is -5, because `(-3 * -5) Mod 7` is 1. + +Sets this instance to the modular multiplicative inverse of *a* modulo *mod_b* --- the value *x* such that `(a * x) Mod mod_b = 1`. Raises run-time error 5 if the inverse does not exist, which is the case when *a* and *mod_b* have a common factor greater than 1, such as when *a* is a multiple of *mod_b*. For a negative *a* the result is negative: the inverse of -3 modulo 7 is -5, because `(-3 * -5) Mod 7` is 1. Modulo 1 there is no error: the inverse of 5 modulo 1 is 0. Syntax: *object*.**AssignModularMultiplicativeInverse** *a*, *mod_b* @@ -325,7 +364,9 @@ Syntax: *object*.**AssignModularMultiplicativeInverse** *a*, *mod_b* *mod_b* : *required* A **BigNumber** holding the modulus. It must be positive. + + > [!WARNING] 1.0.2.0 > The call does not return, and the program stops responding, when *mod_b* is odd and either *a* is zero or *mod_b* is negative. A zero *a* with an even *mod_b*, and any *a* with a negative even *mod_b*, raise error 5. @@ -348,6 +389,7 @@ Debug.Print Err.Number & " " & Err.Description ' 5 invalid operation ### AssignGreatestCommonDivisor {: .no_toc } + Sets this instance to the greatest common divisor of *a* and *b*. The signs of *a* and *b* are ignored; the result is always non-negative. The greatest common divisor of zero and *b* is the absolute value of *b*, and that of two zeros is zero. Syntax: *object*.**AssignGreatestCommonDivisor** *a*, *b* @@ -357,7 +399,9 @@ Syntax: *object*.**AssignGreatestCommonDivisor** *a*, *b* *b* : *required* A **BigNumber** holding the second value. + + > [!WARNING] 1.0.2.0 > A negative *a* or *b* is changed by the call. The method clears the sign of each operand and puts it back with [**IsNegative**](#isnegative), which leaves the operand in the state that the warning there describes: it shows no minus sign, and arithmetic with it gives wrong results. @@ -375,7 +419,8 @@ Debug.Print a.ValueString ' 12 ### AssignLeastCommonMultiple {: .no_toc } -Sets this instance to the least common multiple of *a* and *b*. The signs of *a* and *b* are ignored; the result is non-negative. The least common multiple of zero and any value is zero. + +Sets this instance to the least common multiple of *a* and *b*. The signs of *a* and *b* are ignored; the result is non-negative. The least common multiple of zero and a value that is not zero is zero. Syntax: *object*.**AssignLeastCommonMultiple** *a*, *b* @@ -384,13 +429,20 @@ Syntax: *object*.**AssignLeastCommonMultiple** *a*, *b* *b* : *required* A **BigNumber** holding the second value. + + > [!WARNING] 1.0.2.0 > A negative *a* or *b* is changed by the call, as [**AssignGreatestCommonDivisor**](#assigngreatestcommondivisor) describes. + +> [!WARNING] 1.0.2.0 +> In a 64-bit program, when *a* and *b* are both zero, this instance keeps the value it had: the result is not set to zero. In a 32-bit program the result is zero or the value the instance had, differing from one build of the program to the next. + ### AssignShiftRight {: .no_toc } + Sets this instance to *a* right-shifted by *shiftAmount_b* bits (equivalent to integer division by 2^*shiftAmount_b*, truncated toward zero, so shifting -1 right by any positive amount gives 0). Syntax: *object*.**AssignShiftRight** *a*, *shiftAmount_b* @@ -411,10 +463,12 @@ a.ValueString = "-1" r.AssignShiftRight a, 1 Debug.Print r.ValueString ' 0 ``` + ### AssignShiftLeft {: .no_toc } + Sets this instance to *a* left-shifted by *shiftAmount_b* bits (equivalent to multiplication by 2^*shiftAmount_b*). Syntax: *object*.**AssignShiftLeft** *a*, *shiftAmount_b* @@ -432,10 +486,12 @@ a.ValueString = "5" r.AssignShiftLeft a, 100 Debug.Print r.ValueString ' 6338253001141147007483516026880 ``` + ### AssignNegation {: .no_toc } + Sets this instance to the arithmetic negation of *a* (`-a`). Syntax: *object*.**AssignNegation** *a* @@ -444,10 +500,12 @@ Syntax: *object*.**AssignNegation** *a* : *required* A **BigNumber** holding the value to negate. **AssignNegation** is declared as a **Function** returning **Boolean**, but the return value is never set and is always **False**. + ### AssignAbsolute {: .no_toc } + Sets this instance to the absolute value of *a*. Syntax: *object*.**AssignAbsolute** *a* @@ -456,17 +514,21 @@ Syntax: *object*.**AssignAbsolute** *a* : *required* A **BigNumber** holding the value whose absolute value is taken. **AssignAbsolute** is declared as a **Function** returning **Boolean**, but the return value is never set and is always **False**. + ### AssignFromLong {: .no_toc } + Sets this instance to a value from a native **Long** integer. Syntax: *object*.**AssignFromLong** *a* *a* : *required* A **Long** holding the value to assign. Negative values (including `&H80000000`) are handled correctly. + + > [!WARNING] 1.0.2.0 > Assigning 0 gives a negative zero: [**IsNegative**](#isnegative) returns **True**, [**IsLessThan**](#islessthan) finds the number less than a zero read from the string `"0"`, and [**IsEqualTo**](#isequalto) does not find it equal. Its text is still `0`, and adding it to another number or subtracting it gives the right result. The same negative zero comes from [**AssignSquared**](#assignsquared) with a zero argument, and from [**AssignModulus**](#assignmodulus) when the divisor is negative and the result is zero. To get a true zero, set [**ValueString**](#valuestring) to `"0"`, or set **IsNegative** to **False** after the assignment. @@ -484,66 +546,79 @@ Debug.Print z.IsLessThan(s) ' True ### HandleError {: .no_toc } + Examines the last status code the class stored from the library and raises run-time error 5 if it is a failure. The error description is "invalid operation" or "out of memory". Syntax: *object*.**HandleError** The string-conversion properties, **AssignDivision**, **AssignModulus**, the four modular-arithmetic methods (**AssignAdditionModulus**, **AssignSubtractionModulus**, **AssignMultiplicationModulus**, **AssignSquaredModulus**) and **AssignModularMultiplicativeInverse** store the library's status and call **HandleError** themselves. The other methods do not store a status. The stored status stays in place until one of those members runs again, so calling **HandleError** later raises the error again. + ## Comparison functions ### IsEqualTo {: .no_toc } + Returns **True** if this instance equals *b*. Syntax: *object*.**IsEqualTo**( *b* ) *b* : *required* A **BigNumber** to compare against. + ### IsLessThan {: .no_toc } + Returns **True** if this instance is less than *b*. Syntax: *object*.**IsLessThan**( *b* ) *b* : *required* A **BigNumber** to compare against. + ### IsLessThanOrEqualTo {: .no_toc } + Returns **True** if this instance is less than or equal to *b*. Syntax: *object*.**IsLessThanOrEqualTo**( *b* ) *b* : *required* A **BigNumber** to compare against. + ### IsGreaterThan {: .no_toc } + Returns **True** if this instance is greater than *b*. Syntax: *object*.**IsGreaterThan**( *b* ) *b* : *required* A **BigNumber** to compare against. + ### IsGreaterThanOrEqualTo {: .no_toc } + Returns **True** if this instance is greater than or equal to *b*. Syntax: *object*.**IsGreaterThanOrEqualTo**( *b* ) *b* : *required* A **BigNumber** to compare against. + ## Example + This example computes the factorial of 50 using **BigNumber** arithmetic. ```tb check_build check_run @@ -562,6 +637,7 @@ Next i Debug.Print result.ValueString ' 30414093201713378043612608166064768844377641568960512000000000000 ``` + ## See Also diff --git a/docs/Reference/Contributed/BigNumberLib/index.md b/docs/Reference/Contributed/BigNumberLib/index.md index 757f3130..e63963e1 100644 --- a/docs/Reference/Contributed/BigNumberLib/index.md +++ b/docs/Reference/Contributed/BigNumberLib/index.md @@ -40,12 +40,14 @@ The **BigNumberLib** contributed package provides arbitrary-precision signed int A number is set and read as a string in decimal, hexadecimal, octal or binary, or is set from a **Long**. The class adds, subtracts, multiplies, divides and squares; takes a remainder, also of a sum, a difference, a product or a square; finds a modular inverse, a greatest common divisor and a least common multiple; shifts; and compares. + > [!WARNING] 1.0.2.0 > Some members give wrong results, or do not return. Each is described under its member on the [**BigNumber**](BigNumber) page: > > - Setting [**IsNegative**](BigNumber#isnegative) to **True** does not make a number negative. [**AssignGreatestCommonDivisor**](BigNumber#assigngreatestcommondivisor) and [**AssignLeastCommonMultiple**](BigNumber#assignleastcommonmultiple) leave a negative operand in the same state, with its minus sign gone. > - [**AssignFromLong**](BigNumber#assignfromlong) with 0 gives a zero that is flagged negative, which [**IsEqualTo**](BigNumber#isequalto) does not find equal to a zero read from a string. [**AssignSquared**](BigNumber#assignsquared) of zero and [**AssignModulus**](BigNumber#assignmodulus) with a negative divisor can give the same zero. > - [**AssignModularMultiplicativeInverse**](BigNumber#assignmodularmultiplicativeinverse) does not return when the modulus is odd and the value is zero or the modulus is negative. +> - [**AssignLeastCommonMultiple**](BigNumber#assignleastcommonmultiple) of two zeros leaves the number as it was. > - Overflow is not trapped: a result of more than the library holds, a little over 4096 bits, loses its high bits, with no error. ## Classes @@ -57,3 +59,7 @@ A number is set and read as a string in decimal, hexadecimal, octal or binary, o The package also contains **BigNumberTests**, a module with one **Sub**, **RunAllTests**, which runs the **RunTests** Sub of 22 test modules, one for each operation and each string conversion (**Addition**, **Divide**, **Modulus**, **ShiftLeft**, **ValueHex**, **Comparisons** and so on). Together they make 777 checks with **Debug.Assert**, which a built program skips. Run in the IDE, version 1.0.2.0 passes all of them. These modules have no pages of their own here. **BigNumber** also declares a structure type, **fp_int**, which holds the number for the library, and a program does not use it. + +## Test cases + +The notes and warnings of this section are checked by the test cases in [BigNumberLib-tests.twinproj](BigNumberLib-tests.twinproj), a twinBASIC project. It uses BigNumberLib as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/FileProps/FileProperties/Count.md b/docs/Reference/Contributed/FileProps/FileProperties/Count.md index 450e701a..19b06807 100644 --- a/docs/Reference/Contributed/FileProps/FileProperties/Count.md +++ b/docs/Reference/Contributed/FileProps/FileProperties/Count.md @@ -14,7 +14,9 @@ Syntax: *object*.**Count** *object* : *required* An object expression that evaluates to a **FileProperties** object. -**Count** returns a **Long** equal to the total number of shell properties available for the file. Valid index values for [**Item**](Item) range from `0` to `Count - 1`. + +**Count** returns a **Long** equal to the total number of shell properties available for the file. Different file types have different numbers of properties. Valid index values for [**Item**](Item) range from `0` to `Count - 1`. + ### Example diff --git a/docs/Reference/Contributed/FileProps/FileProperties/FilePath.md b/docs/Reference/Contributed/FileProps/FileProperties/FilePath.md index faaf3a36..e1f46a46 100644 --- a/docs/Reference/Contributed/FileProps/FileProperties/FilePath.md +++ b/docs/Reference/Contributed/FileProps/FileProperties/FilePath.md @@ -14,7 +14,9 @@ Syntax: *object*.**FilePath** *object* : *required* An object expression that evaluates to a **FileProperties** object. + The value is the full path of the file that was passed to [**FilePropertyExplorer.OpenFile**](../FilePropertyExplorer/OpenFile) or selected through [**FilePropertyExplorer.BrowseAndOpenFile**](../FilePropertyExplorer/BrowseAndOpenFile) when the **FileProperties** collection was created. + ### Example diff --git a/docs/Reference/Contributed/FileProps/FileProperties/Item.md b/docs/Reference/Contributed/FileProps/FileProperties/Item.md index 0f571d0b..a069474a 100644 --- a/docs/Reference/Contributed/FileProps/FileProperties/Item.md +++ b/docs/Reference/Contributed/FileProps/FileProperties/Item.md @@ -17,7 +17,21 @@ Syntax: *object*.**Item**(*IndexOrNameOrID*) *IndexOrNameOrID* : *required* A **Variant** identifying the property to retrieve. Pass an integer index (zero-based, from `0` to `Count - 1`), a **String** matching **FileProperty.Name**, or a **String** matching **FileProperty.ID**. -**Item** forwards the call to the underlying Virtual-COM object. Passing an integer retrieves the property at that position in the collection. Passing a string retrieves the property whose **FileProperty.Name** (the system identity name, such as `"System.Audio.EncodingBitrate"`) or **FileProperty.ID** (the unique identifier of the shell property) equals that string. + +**Item** forwards the call to the underlying Virtual-COM object. Passing an integer retrieves the property at that position in the collection. Passing a string retrieves the property whose **FileProperty.Name** (the system identity name, such as `"System.Audio.EncodingBitrate"`) or **FileProperty.ID** (the unique identifier of the shell property) equals that string. A name or an ID matches without regard to case. A **String** of digits is a name, not an index. + +**Item** raises run-time error -2147211517 (*Requested index is out of bounds*) for an index of **Count** or more, -2147467259 (*Generic failure code*) for a negative index, and -2147211518 (*The requested element was not found in the collection*) for a name that is not the name of a shell property. + +A **For Each** loop over a **FileProperties** object visits the properties in the order of their indexes. + + + +> [!NOTE] 1.7.1.0 +> **Item** raises run-time error 13 (*Generic failure code*) when it is given the ID of some properties, among them `System.ItemTypeText`, `System.ItemFolderNameDisplay`, `System.ItemPathDisplay` and `System.Kind`. Look such a property up by its name or its index. + + +> [!NOTE] 1.7.1.0 +> An ID that no property of the file has raises no error. **Item** returns a property that has that ID, an empty **Name** and an empty **Value**. ### Example diff --git a/docs/Reference/Contributed/FileProps/FileProperties/VCOMObject.md b/docs/Reference/Contributed/FileProps/FileProperties/VCOMObject.md index a57b0b3a..a550ba5b 100644 --- a/docs/Reference/Contributed/FileProps/FileProperties/VCOMObject.md +++ b/docs/Reference/Contributed/FileProps/FileProperties/VCOMObject.md @@ -9,6 +9,7 @@ permalink: /tB/Packages/Contributed/FileProps/FileProperties/VCOMObject Holds the native-code COM object created internally by the Virtual-COM library. For internal use only. + > [!WARNING] timeless > > **VCOMObject** is an internal implementation detail of the FileProps Virtual-COM loader, and the package marks it as for internal use only. Do not use it in application code. diff --git a/docs/Reference/Contributed/FileProps/FileProperties/index.md b/docs/Reference/Contributed/FileProps/FileProperties/index.md index a0229a8a..983acf93 100644 --- a/docs/Reference/Contributed/FileProps/FileProperties/index.md +++ b/docs/Reference/Contributed/FileProps/FileProperties/index.md @@ -38,7 +38,9 @@ Returns the number of shell properties in the collection. **Long**. Read-only. Syntax: *object*.**Count** + The count is the number of properties the shell exposes for the opened file. Different file types expose different property sets. + ### FilePath {: .no_toc } @@ -47,7 +49,9 @@ Returns the full file path of the file that was opened. **String**. Read-only. Syntax: *object*.**FilePath** + The value is the full path of the file passed to [**FilePropertyExplorer.OpenFile**](../FilePropertyExplorer/OpenFile). When a collection is obtained through [**FilePropertyExplorer.BrowseAndOpenFile**](../FilePropertyExplorer/BrowseAndOpenFile), it is the path of the file the user selected in the dialog. + ### Item {: .no_toc } @@ -74,11 +78,24 @@ Set Prop = Props.Item(0) Set Prop = Props.Item("System.Media.Duration") ``` + +A name or an ID matches without regard to case. A **String** of digits is a name, not an index. **Item** raises run-time error -2147211517 (*Requested index is out of bounds*) for an index of **Count** or more, -2147467259 (*Generic failure code*) for a negative index, and -2147211518 (*The requested element was not found in the collection*) for a name that is not the name of a shell property. A **For Each** loop over a **FileProperties** object visits the properties in the order of their indexes. + + + +> [!NOTE] 1.7.1.0 +> **Item** raises run-time error 13 (*Generic failure code*) when it is given the ID of some properties, among them `System.ItemTypeText`, `System.ItemFolderNameDisplay`, `System.ItemPathDisplay` and `System.Kind`. Look such a property up by its name or its index. + + +> [!NOTE] 1.7.1.0 +> An ID that no property of the file has raises no error. **Item** returns a property that has that ID, an empty **Name** and an empty **Value**. + ### VCOMObject {: .no_toc } Holds the native-code COM object created internally by the Virtual-COM library. **Object**. For internal use only. + > [!WARNING] timeless > Do not read, assign, or pass **VCOMObject** to any function. The package marks it as for internal use only, and **Count**, **FilePath** and **Item** forward their calls to it. diff --git a/docs/Reference/Contributed/FileProps/FileProperty/ID.md b/docs/Reference/Contributed/FileProps/FileProperty/ID.md index 6c0de500..4fccd4a8 100644 --- a/docs/Reference/Contributed/FileProps/FileProperty/ID.md +++ b/docs/Reference/Contributed/FileProps/FileProperty/ID.md @@ -14,9 +14,13 @@ Syntax: *object*.**ID** *object* : *required* An object expression that evaluates to a **FileProperty** object. -The **ID** property returns a **String** that is a unique identity for the property and can always be used to refer to it. It consists of a GUID identifying the property schema, followed by an integer identifying the property's index within that schema. + +The **ID** property returns a **String** that is a unique identity for the property. It consists of a GUID identifying the property schema, a slash, and an integer identifying the property's index within that schema, as in `{B725F130-47EF-101A-A5F1-02608C9EEBAC}/12`. No two properties of a file have the same ID. + -Unlike [**Name**](Name), which is a dotted system name such as `System.ItemFolderNameDisplay`, the **ID** string is made of the schema GUID and the index. +Unlike [**Name**](Name), which is a dotted system name such as `System.ItemFolderNameDisplay`, the **ID** string is made of the schema GUID and the index. A property that has no system name still has an ID. + +Pass the ID to [**FileProperties.Item**](../FileProperties/Item) to get the property back. That call raises an error for some properties; see the note on **Item**. ### Example diff --git a/docs/Reference/Contributed/FileProps/FileProperty/Name.md b/docs/Reference/Contributed/FileProps/FileProperty/Name.md index 62dbb6ad..ec644a4f 100644 --- a/docs/Reference/Contributed/FileProps/FileProperty/Name.md +++ b/docs/Reference/Contributed/FileProps/FileProperty/Name.md @@ -14,7 +14,13 @@ Syntax: *object*.**Name** *object* : *required* An object expression that evaluates to a **FileProperty** object. + Returns a **String** containing the system identity name of the property, such as `"System.ItemFolderNameDisplay"`. This name is safe to use as an identity field even on non-English systems. + + + +A property that has no system name returns an empty string. Use [**ID**](ID) to identify such a property. + ### Remarks diff --git a/docs/Reference/Contributed/FileProps/FileProperty/NameDesc.md b/docs/Reference/Contributed/FileProps/FileProperty/NameDesc.md index e8ff1087..ecc22e30 100644 --- a/docs/Reference/Contributed/FileProps/FileProperty/NameDesc.md +++ b/docs/Reference/Contributed/FileProps/FileProperty/NameDesc.md @@ -16,7 +16,11 @@ Syntax: *object*.**NameDesc** Returns a **String** containing a localized version of the property's name. This name is suitable for display to the user. -The localized name is not always available. Use [**Name**](Name) instead when a locale-independent identifier is needed --- for example, when storing or comparing property names across systems. + +The localized name is not always available: for some properties **NameDesc** is an empty string. + + +Use [**Name**](Name) instead when a locale-independent identifier is needed --- for example, when storing or comparing property names across systems. ### See Also diff --git a/docs/Reference/Contributed/FileProps/FileProperty/VCOMObject.md b/docs/Reference/Contributed/FileProps/FileProperty/VCOMObject.md index 93323f72..813cc233 100644 --- a/docs/Reference/Contributed/FileProps/FileProperty/VCOMObject.md +++ b/docs/Reference/Contributed/FileProps/FileProperty/VCOMObject.md @@ -9,6 +9,7 @@ permalink: /tB/Packages/Contributed/FileProps/FileProperty/VCOMObject Holds the native-code COM object created internally by the Virtual-COM library. For internal use only. + > [!WARNING] timeless > > **VCOMObject** is an internal implementation detail of the FileProps Virtual-COM loader, and the package marks it as for internal use only. Do not use it in application code. diff --git a/docs/Reference/Contributed/FileProps/FileProperty/Value.md b/docs/Reference/Contributed/FileProps/FileProperty/Value.md index 55dbf828..9656b69d 100644 --- a/docs/Reference/Contributed/FileProps/FileProperty/Value.md +++ b/docs/Reference/Contributed/FileProps/FileProperty/Value.md @@ -18,7 +18,9 @@ Syntax: *object*.**Value** *object* : *required* An object expression that evaluates to a **FileProperty** object. -The type of the returned value depends on the property. A multi-value property can return an array. + +The type of the returned value depends on the property. A property that has no value for the file returns **Empty**. A multi-value property can return an array. + To obtain a localized string representation of the same data, use [**ValueDesc**](ValueDesc) instead. @@ -33,11 +35,18 @@ Syntax: *object*.**Value** **=** *value* > [!IMPORTANT] timeless > -> Write support requires Windows Vista or newer. Assigning a property that is not writable raises a *Permission denied* error. +> Write support requires Windows Vista or newer. + + +> [!NOTE] 1.7.1.0 +> +> Assigning a property that is not writable raises run-time error -2147352567 (*Exception occurred*), not *Permission denied* (error 70). An assignment to a property of a collection opened without write support raises the same error. ### Remarks -Not all shell properties are writable. To assign **Value**, open the file with write support enabled by passing **True** as the *WriteSupport* argument of [**FilePropertyExplorer.OpenFile**](../FilePropertyExplorer/OpenFile) or [**FilePropertyExplorer.BrowseAndOpenFile**](../FilePropertyExplorer/BrowseAndOpenFile). + +Not all shell properties are writable. To assign **Value**, open the file with write support enabled by passing **True** as the *WriteSupport* argument of [**FilePropertyExplorer.OpenFile**](../FilePropertyExplorer/OpenFile) or [**FilePropertyExplorer.BrowseAndOpenFile**](../FilePropertyExplorer/BrowseAndOpenFile). The value is stored in the file, and a collection opened again reads it. An array assigned to a multi-value property is stored as its values. + ### Example diff --git a/docs/Reference/Contributed/FileProps/FileProperty/ValueDesc.md b/docs/Reference/Contributed/FileProps/FileProperty/ValueDesc.md index 9b0dd9f3..194f2c1f 100644 --- a/docs/Reference/Contributed/FileProps/FileProperty/ValueDesc.md +++ b/docs/Reference/Contributed/FileProps/FileProperty/ValueDesc.md @@ -14,7 +14,9 @@ Syntax: *object*.**ValueDesc** *object* : *required* An object expression that evaluates to a **FileProperty** object. -**ValueDesc** returns a **String** containing the property value in localized form, suitable for display. For multi-value properties, all values are combined into a single string; no array handling is required. + +**ValueDesc** returns a **String** containing the property value in localized form, suitable for display. For multi-value properties, all values are combined into a single string; no array handling is required. A property that has no value returns an empty string. + To retrieve the value as a **Variant** (which can be an array for multi-value properties), use the [**Value**](Value) property instead. diff --git a/docs/Reference/Contributed/FileProps/FileProperty/index.md b/docs/Reference/Contributed/FileProps/FileProperty/index.md index f7c2c164..0bdb0e1e 100644 --- a/docs/Reference/Contributed/FileProps/FileProperty/index.md +++ b/docs/Reference/Contributed/FileProps/FileProperty/index.md @@ -35,7 +35,11 @@ A unique identifier string for the shell property. Syntax: *object*.**ID** **As String** (read-only) -The identifier consists of a GUID identifying the property schema, followed by an integer identifying the property index within that schema. It can always be used to refer to a specific property unambiguously. + +The identifier consists of a GUID identifying the property schema, a slash, and an integer identifying the property index within that schema. It identifies one property unambiguously: no two properties of a file have the same ID. + + +Pass the ID to [**FileProperties.Item**](../FileProperties/Item) to get the property back. That call raises an error for some properties; see the note on **Item**. ### Name {: .no_toc } @@ -44,7 +48,13 @@ The system identity name of the property. Syntax: *object*.**Name** **As String** (read-only) + Returns a system name such as `"System.ItemFolderNameDisplay"`. This name is safe to use as an identity field even on non-English systems. For the localized name, use [**NameDesc**](#namedesc). + + + +A property that has no system name returns an empty string. Use [**ID**](#id) to identify such a property. + ### NameDesc {: .no_toc } @@ -53,7 +63,9 @@ The localized name of the property. Syntax: *object*.**NameDesc** **As String** (read-only) -Returns a localized version of the name. It is not always available. Use [**Name**](#name) for a locale-independent identifier. + +Returns a localized version of the name. It is not always available: for some properties it is an empty string. Use [**Name**](#name) for a locale-independent identifier. + ### Value {: .no_toc } @@ -62,10 +74,19 @@ The current value of the shell property as a **Variant**. Syntax: *object*.**Value** [ **=** *value* ] -Reading this property returns the value of the property as exposed by the operating system. The type of the returned **Variant** depends on the property. A multi-value property can return an array. + +Reading this property returns the value of the property as exposed by the operating system. The type of the returned **Variant** depends on the property. A property that has no value for the file returns **Empty**. A multi-value property can return an array. + + + +Assigning a value writes the property into the file. The file must have been opened with write support, which requires Windows Vista or newer. + -Assigning a value writes the property. Write support requires Windows Vista or newer. Assigning a property that is not writable raises a *Permission denied* error. + +> [!NOTE] 1.7.1.0 +> Assigning a property that is not writable raises run-time error -2147352567 (*Exception occurred*), not *Permission denied* (error 70). An assignment to a property of a collection opened without write support raises the same error. + > [!NOTE] timeless > Multi-value properties can return a **Variant** array. Check `IsArray(Prop.Value)` before using the result as a scalar. @@ -78,7 +99,9 @@ The current value of the property as a localized string. Syntax: *object*.**ValueDesc** **As String** (read-only) -Returns the property value in localized form, combined into a single string. Multi-value properties that can return an array from [**Value**](#value) are already combined into one string here. + +Returns the property value in localized form, combined into a single string. Multi-value properties that can return an array from [**Value**](#value) are already combined into one string here. A property that has no value returns an empty string. + Use this property when displaying values to an end user. Use [**Value**](#value) when the typed value is needed for computation or comparison. @@ -89,6 +112,7 @@ Holds the native-code COM object created internally by the Virtual-COM library. Syntax: *object*.**VCOMObject** **As Object** + > [!WARNING] timeless > This member is for internal use only. Do not read or assign **VCOMObject** from application code. All public members of **FileProperty** forward their calls to it. diff --git a/docs/Reference/Contributed/FileProps/FilePropertyExplorer/BrowseAndOpenFile.md b/docs/Reference/Contributed/FileProps/FilePropertyExplorer/BrowseAndOpenFile.md index 4d850630..00d96238 100644 --- a/docs/Reference/Contributed/FileProps/FilePropertyExplorer/BrowseAndOpenFile.md +++ b/docs/Reference/Contributed/FileProps/FilePropertyExplorer/BrowseAndOpenFile.md @@ -17,7 +17,7 @@ Syntax: *object*.**BrowseAndOpenFile** [ (*WriteSupport*) ] *WriteSupport* : *optional* A **Boolean** that controls whether the returned **FileProperties** collection is opened with write support for property values. **False** (default) opens it without write support. Pass **True** to allow setting [**FileProperty.Value**](../FileProperty/Value). Write support requires Windows Vista or newer. -**BrowseAndOpenFile** opens a file dialog for the user to select a file, and returns a [**FileProperties**](../FileProperties/) collection for the selected file, as [**OpenFile**](OpenFile) does for a path. The returned collection contains the [**FileProperty**](../FileProperty/) entries for the properties the operating system exposes for the selected file. +**BrowseAndOpenFile** opens a file dialog for the user to select a file, and returns a [**FileProperties**](../FileProperties/) collection for the selected file, as [**OpenFile**](OpenFile) does for a path. The returned collection contains the [**FileProperty**](../FileProperty/) entries for the properties the operating system exposes for the selected file. The notes and the warning on [**OpenFile**](OpenFile) about write support and about opening a file again apply to the collection this function returns. ### Example diff --git a/docs/Reference/Contributed/FileProps/FilePropertyExplorer/OpenFile.md b/docs/Reference/Contributed/FileProps/FilePropertyExplorer/OpenFile.md index 13024a18..549106ef 100644 --- a/docs/Reference/Contributed/FileProps/FilePropertyExplorer/OpenFile.md +++ b/docs/Reference/Contributed/FileProps/FilePropertyExplorer/OpenFile.md @@ -20,10 +20,26 @@ Syntax: *object*.**OpenFile**(*FilePath* [ **,** *WriteSupport* ]) *WriteSupport* : *optional* A **Boolean** that controls whether the returned **FileProperties** collection is opened with write support for property values. **False** (default) opens it without write support. Pass **True** to allow setting [**FileProperty.Value**](../FileProperty/Value). + **OpenFile** returns a [**FileProperties**](../FileProperties/) collection for the specified file. The collection contains the [**FileProperty**](../FileProperty/) entries for the properties the operating system exposes for that file. Common examples include the duration of an audio file such as an MP3 and the EXIF properties of a JPEG image. +**OpenFile** raises run-time error -2147212790 (*File not found or is not accessible*) when the file does not exist. It never returns **Nothing**. + + Write support requires Windows Vista or newer. + +> [!NOTE] 1.7.1.0 +> *FilePath* must be a full path that uses backslashes, such as `C:\Folder\File.ext`. A path that is relative, has a `..` segment or uses forward slashes raises the same error as a missing file, although the file exists. + + +> [!NOTE] 1.7.1.0 +> For a file type that has a property handler of its own, such as JPEG, a collection opened with *WriteSupport* set to **True** holds only the properties of that handler. It has a lower **Count** than a collection of the same file opened without write support, and a property of the file system, such as `System.Size`, is in it with an empty **Value**. Read those properties from a collection opened without write support. A file type without a handler, such as a text file, lists the same properties either way. + + +> [!WARNING] 1.7.1.0 +> A file that has a property handler, such as a JPEG file, can be open in one collection at a time when write support is involved. Calling **OpenFile** with *WriteSupport* set to **True** while a collection of the same file opened without write support exists never returns. Calling **OpenFile**, with or without write support, while a collection of the file opened with write support exists returns a collection whose **Count** is 0, with no error. Set the first collection to **Nothing** before opening the file again. Any number of collections opened without write support can exist together. + ### Example This example opens an MP3 file and prints all its shell properties to the Debug console. diff --git a/docs/Reference/Contributed/FileProps/FilePropertyExplorer/index.md b/docs/Reference/Contributed/FileProps/FilePropertyExplorer/index.md index b83e9963..0899b1bf 100644 --- a/docs/Reference/Contributed/FileProps/FilePropertyExplorer/index.md +++ b/docs/Reference/Contributed/FileProps/FilePropertyExplorer/index.md @@ -12,12 +12,14 @@ A predeclared factory class that opens a file and returns a [**FileProperties**] **FilePropertyExplorer** is a Virtual-COM wrapper that gives access to the detailed file properties the operating system exposes, commonly called shell property handlers. Common examples include the duration of an audio file such as an MP3 and the EXIF properties of a JPEG image. + The class has the `[ PredeclaredId ]` attribute, so it acts as a default instance and its methods are callable without creating an explicit object variable: ```tb check_build Dim Props As FileProperties Set Props = FilePropertyExplorer.OpenFile("C:\Music\track.mp3") ``` + * TOC {:toc} @@ -84,9 +86,26 @@ End If The shell property handler infrastructure requires Windows 2000 or later. If the memory for the native code cannot be allocated when the class is created, the class raises error 7 (*Out of memory*). + > [!NOTE] timeless > Write support for [**FileProperty.Value**](../FileProperty/Value) requires Windows Vista or newer. + +**OpenFile** raises run-time error -2147212790 (*File not found or is not accessible*) when the file does not exist. It never returns **Nothing**. + + + +> [!NOTE] 1.7.1.0 +> *FilePath* of **OpenFile** must be a full path that uses backslashes, such as `C:\Folder\File.ext`. A path that is relative, has a `..` segment or uses forward slashes raises the same error as a missing file, although the file exists. + + +> [!NOTE] 1.7.1.0 +> For a file type that has a property handler of its own, such as JPEG, a collection opened with *WriteSupport* set to **True** holds only the properties of that handler. It has a lower **Count** than a collection of the same file opened without write support, and a property of the file system, such as `System.Size`, is in it with an empty **Value**. Read those properties from a collection opened without write support. A file type without a handler, such as a text file, lists the same properties either way. + + +> [!WARNING] 1.7.1.0 +> A file that has a property handler, such as a JPEG file, can be open in one collection at a time when write support is involved. Calling **OpenFile** with *WriteSupport* set to **True** while a collection of the same file opened without write support exists never returns. Calling **OpenFile**, with or without write support, while a collection of the file opened with write support exists returns a collection whose **Count** is 0, with no error. Set the first collection to **Nothing** before opening the file again. Any number of collections opened without write support can exist together. + ## Example This example opens a file-open dialog, then prints all shell properties for the selected file to the Debug console. diff --git a/docs/Reference/Contributed/FileProps/index.md b/docs/Reference/Contributed/FileProps/index.md index e77eda30..228e6e13 100644 --- a/docs/Reference/Contributed/FileProps/index.md +++ b/docs/Reference/Contributed/FileProps/index.md @@ -32,3 +32,7 @@ The **FileProps** contributed package provides access to Windows shell file prop - [FilePropertyExplorer](FilePropertyExplorer/) -- a predeclared factory class that opens a file and returns a FileProperties collection of its Windows shell properties - [BrowseAndOpenFile](FilePropertyExplorer/BrowseAndOpenFile) -- displays a file-open dialog and returns a FileProperties collection for the selected file - [OpenFile](FilePropertyExplorer/OpenFile) -- opens a file by path and returns a FileProperties collection of its shell properties + +## Test cases + +The notes and warnings of this section are checked by the test cases in [FileProps-tests.twinproj](FileProps-tests.twinproj), a twinBASIC project. It uses FileProps as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/Fmt/AsciiEscapeBase.md b/docs/Reference/Contributed/Fmt/AsciiEscapeBase.md index 6463e6e5..0142490e 100644 --- a/docs/Reference/Contributed/Fmt/AsciiEscapeBase.md +++ b/docs/Reference/Contributed/Fmt/AsciiEscapeBase.md @@ -8,6 +8,8 @@ has_toc: false # AsciiEscapeBase enumeration {: .no_toc } + + Selects the number base of the digits in an ASCII escape sequence. **AsciiEscapeBase** is used by the *base* parameter of [**EscapeSequence.Create**](EscapeSequence/Create) and by the [**AsciiBase**](EscapeSequence/AsciiBase) property. It matters only for an escape sequence created with *isAsciiReplacement* set to **True**. The enumeration is declared in the [**EscapeSequence**](EscapeSequence/) class module, and its members are used without a qualifier or with the name `AsciiEscapeBase`. @@ -25,6 +27,8 @@ Debug.Print Hexadecimal ' 1 [**StringHelper**](StringHelper) registers `\xHH` as a **Hexadecimal** escape and `\ooo` as an **Octal** one. + + ## See Also - [EscapeSequence](EscapeSequence/) class -- one escape sequence diff --git a/docs/Reference/Contributed/Fmt/CurrencySFI.md b/docs/Reference/Contributed/Fmt/CurrencySFI.md index f09aa859..5642b345 100644 --- a/docs/Reference/Contributed/Fmt/CurrencySFI.md +++ b/docs/Reference/Contributed/Fmt/CurrencySFI.md @@ -8,6 +8,8 @@ has_toc: false # CurrencySFI class {: .no_toc } + + Formats a number as an amount of money for the **C** identifier. **CurrencySFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) and matches the identifier **C** or **c** in any case. [**StringHelper**](StringHelper) registers it, so `{0:C}` in a call to [**StringFormat**](PublicEntryPoints/StringFormat) uses it for a numeric value. The class is predeclared, and its two members are reached through the interface. @@ -34,6 +36,8 @@ The output shown is for an English (United States) system. Both are implemented as private members of the interface. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/DateTimeSFI.md b/docs/Reference/Contributed/Fmt/DateTimeSFI.md index 1554f779..7ae2ac44 100644 --- a/docs/Reference/Contributed/Fmt/DateTimeSFI.md +++ b/docs/Reference/Contributed/Fmt/DateTimeSFI.md @@ -8,6 +8,8 @@ has_toc: false # DateTimeSFI class {: .no_toc } + + Formats a **Date** for the same identifiers as **GenericDateTimeSFI**, but is not registered with **StringHelper**. **DateTimeSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) and contains the same code as [**GenericDateTimeSFI**](GenericDateTimeSFI): the identifiers **d**, **D**, **f**, **F**, **g**, **G**, **s**, **t**, **T** and **C** give the same results, and any other identifier does not match. The table of identifiers is on the [**GenericDateTimeSFI**](GenericDateTimeSFI#identifiers) page. @@ -37,6 +39,8 @@ Debug.Print f.GetFormattedValue(#2026-10-11 14:05:09#, spec) ' 2026-10-11 Both are implemented as private members of the interface. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/DecimalSFI.md b/docs/Reference/Contributed/Fmt/DecimalSFI.md index 5e6b694a..5cec28c4 100644 --- a/docs/Reference/Contributed/Fmt/DecimalSFI.md +++ b/docs/Reference/Contributed/Fmt/DecimalSFI.md @@ -8,6 +8,8 @@ has_toc: false # DecimalSFI class {: .no_toc } + + Formats a number as whole-number digits for the **D** identifier. **DecimalSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) and matches the identifier **D** or **d** in any case. [**StringHelper**](StringHelper) registers it, so `{0:D}` in a call to [**StringFormat**](PublicEntryPoints/StringFormat) uses it for a numeric value. The class is predeclared, and its two members are reached through the interface. @@ -32,6 +34,8 @@ Debug.Print StringFormat("{0:D2}:{1:D2}", 7, 5) ' 07:05 Both are implemented as private members of the interface. [**GeneralNumericSFI**](GeneralNumericSFI) calls this class for **Integer** and **Long** values. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/EscapeSequence/AsciiBase.md b/docs/Reference/Contributed/Fmt/EscapeSequence/AsciiBase.md index 0cc87430..f05857ba 100644 --- a/docs/Reference/Contributed/Fmt/EscapeSequence/AsciiBase.md +++ b/docs/Reference/Contributed/Fmt/EscapeSequence/AsciiBase.md @@ -8,6 +8,8 @@ has_toc: false # AsciiBase {: .no_toc } + + Returns or sets the number base of the digits that an ASCII escape sequence captures. ## Get @@ -34,6 +36,8 @@ Syntax: *object*.**AsciiBase** **=** *value* [**Create**](Create) sets the property from its *base* argument. [**Execute**](Execute) reads the captured digits with the prefix `&O` for **Octal** and `&H` for **Hexadecimal**. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/EscapeSequence/Create.md b/docs/Reference/Contributed/Fmt/EscapeSequence/Create.md index 8c86fa7c..97e1e228 100644 --- a/docs/Reference/Contributed/Fmt/EscapeSequence/Create.md +++ b/docs/Reference/Contributed/Fmt/EscapeSequence/Create.md @@ -8,6 +8,8 @@ has_toc: false # Create {: .no_toc } + + Creates an escape sequence, plain or ASCII. Syntax: *object*.**Create** ( *escape*, *replacement* [, *isAsciiReplacement* [, *base* ]] ) **As EscapeSequence** @@ -33,6 +35,8 @@ Returns a new **EscapeSequence** whose [**EscapeString**](EscapeString), [**Repl **Create** is the intended way to make an escape sequence. It fills in a new instance through the **Friend** setters of the properties and returns it through [**Self**](Self). + + ## Example This example creates a plain sequence and an ASCII sequence. diff --git a/docs/Reference/Contributed/Fmt/EscapeSequence/EscapeString.md b/docs/Reference/Contributed/Fmt/EscapeSequence/EscapeString.md index f0f29013..00340a2f 100644 --- a/docs/Reference/Contributed/Fmt/EscapeSequence/EscapeString.md +++ b/docs/Reference/Contributed/Fmt/EscapeSequence/EscapeString.md @@ -8,6 +8,8 @@ has_toc: false # EscapeString {: .no_toc } + + Returns or sets the text, or the regular expression, that the escape sequence looks for. ## Get @@ -34,6 +36,8 @@ Syntax: *object*.**EscapeString** **=** *value* [**Create**](Create) sets the property from its *escape* argument. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/EscapeSequence/Execute.md b/docs/Reference/Contributed/Fmt/EscapeSequence/Execute.md index 1d1388e1..580d2e5d 100644 --- a/docs/Reference/Contributed/Fmt/EscapeSequence/Execute.md +++ b/docs/Reference/Contributed/Fmt/EscapeSequence/Execute.md @@ -8,6 +8,8 @@ has_toc: false # Execute {: .no_toc } + + Applies the escape sequence to a string, replacing it in place. Syntax: *object*.**Execute** ( *string_value* ) @@ -27,6 +29,8 @@ An ASCII sequence reads [**EscapeString**](EscapeString) as a regular expression - The expression is applied only when the string contains a backslash. A pattern that does not match a backslash, such as `#(\d{3})`, has no effect on a string that has none. - Digits that are not valid in the base, as in `\189` read as octal, raise run-time error 13, *Type mismatch*. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/EscapeSequence/IsAsciiCharacter.md b/docs/Reference/Contributed/Fmt/EscapeSequence/IsAsciiCharacter.md index 9ed291ba..1710630b 100644 --- a/docs/Reference/Contributed/Fmt/EscapeSequence/IsAsciiCharacter.md +++ b/docs/Reference/Contributed/Fmt/EscapeSequence/IsAsciiCharacter.md @@ -8,6 +8,8 @@ has_toc: false # IsAsciiCharacter {: .no_toc } + + Returns or sets whether the escape sequence stands for a character code instead of fixed text. ## Get @@ -34,6 +36,8 @@ Syntax: *object*.**IsAsciiCharacter** **=** *value* [**Create**](Create) sets the property from its *isAsciiReplacement* argument. [**Execute**](Execute) takes the ASCII path when the value is **True**, reading the digits in the base given by [**AsciiBase**](AsciiBase). + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/EscapeSequence/ReplacementString.md b/docs/Reference/Contributed/Fmt/EscapeSequence/ReplacementString.md index fee79aef..b48760dd 100644 --- a/docs/Reference/Contributed/Fmt/EscapeSequence/ReplacementString.md +++ b/docs/Reference/Contributed/Fmt/EscapeSequence/ReplacementString.md @@ -8,6 +8,8 @@ has_toc: false # ReplacementString {: .no_toc } + + Returns or sets the text that replaces a plain escape sequence. ## Get @@ -34,6 +36,8 @@ Syntax: *object*.**ReplacementString** **=** *value* [**Create**](Create) sets the property from its *replacement* argument. [**Execute**](Execute) puts the value in place of every occurrence of [**EscapeString**](EscapeString). + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/EscapeSequence/Self.md b/docs/Reference/Contributed/Fmt/EscapeSequence/Self.md index dd30bfe4..363c4512 100644 --- a/docs/Reference/Contributed/Fmt/EscapeSequence/Self.md +++ b/docs/Reference/Contributed/Fmt/EscapeSequence/Self.md @@ -8,6 +8,8 @@ has_toc: false # Self {: .no_toc } + + Returns the escape sequence object itself. Syntax: *object*.**Self** @@ -19,7 +21,9 @@ Returns the same **EscapeSequence** that *object* refers to. The property is rea ## Remarks -[**Create**](Create) builds the new instance inside a `With New EscapeSequence` block and returns it through **Self**, because a `With` block does not give a way to name the object it works on. A project that references the package can call **Self** as well (checked on BETA 1005), though it only returns what the caller already holds. +[**Create**](Create) builds the new instance inside a `With New EscapeSequence` block and returns it through **Self**, because a `With` block does not give a way to name the object it works on. A project that references the package can call **Self** as well, though it only returns what the caller already holds. + + ## Example diff --git a/docs/Reference/Contributed/Fmt/EscapeSequence/index.md b/docs/Reference/Contributed/Fmt/EscapeSequence/index.md index 944fac15..d46b3ecd 100644 --- a/docs/Reference/Contributed/Fmt/EscapeSequence/index.md +++ b/docs/Reference/Contributed/Fmt/EscapeSequence/index.md @@ -8,6 +8,8 @@ has_toc: false # EscapeSequence class {: .no_toc } + + Describes one escape sequence of a format string, the text to look for and what replaces it, and applies it to a string. An escape sequence is either plain or ASCII: @@ -30,6 +32,8 @@ Debug.Print text ' aZZb > [!NOTE] BETA 1005 > [**Self**](Self) and the four property setters ([**AsciiBase**](AsciiBase), [**EscapeString**](EscapeString), [**IsAsciiCharacter**](IsAsciiCharacter) and [**ReplacementString**](ReplacementString) with **Let**) are declared **Friend**, for [**Create**](Create) to use. A project that references the package can call them too, so the properties of an instance can be changed after it is created. + + ## Properties - [AsciiBase](AsciiBase) -- returns the number base of an ASCII sequence diff --git a/docs/Reference/Contributed/Fmt/ExponentialSFI.md b/docs/Reference/Contributed/Fmt/ExponentialSFI.md index ceb83b62..2a29b281 100644 --- a/docs/Reference/Contributed/Fmt/ExponentialSFI.md +++ b/docs/Reference/Contributed/Fmt/ExponentialSFI.md @@ -8,6 +8,8 @@ has_toc: false # ExponentialSFI class {: .no_toc } + + Formats a number in exponential notation for the **E** identifier, and reads the exponent back from such a text. **ExponentialSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) and matches the identifier **E** or **e** in any case. [**StringHelper**](StringHelper) registers it, so `{0:E}` in a call to [**StringFormat**](PublicEntryPoints/StringFormat) uses it for a numeric value. The class is predeclared. Its interface members are reached through the interface, and [**ParseExponent**](#parseexponent) is public. @@ -54,6 +56,8 @@ Debug.Print ExponentialSFI.ParseExponent("1.5e12") ' 12 Both are implemented as private members of the interface. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/FixedPointSFI.md b/docs/Reference/Contributed/Fmt/FixedPointSFI.md index 99f890a9..de4ec022 100644 --- a/docs/Reference/Contributed/Fmt/FixedPointSFI.md +++ b/docs/Reference/Contributed/Fmt/FixedPointSFI.md @@ -8,6 +8,8 @@ has_toc: false # FixedPointSFI class {: .no_toc } + + Formats a number with a fixed count of decimal places for the **F** identifier. **FixedPointSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) and matches the identifier **F** or **f** in any case. [**StringHelper**](StringHelper) registers it, so `{0:F}` in a call to [**StringFormat**](PublicEntryPoints/StringFormat) uses it for a numeric value. The class is predeclared, and its two members are reached through the interface. @@ -31,6 +33,8 @@ Debug.Print StringFormat("{0:F2}", 12345.678) ' 12345.68 Both are implemented as private members of the interface. [**GeneralNumericSFI**](GeneralNumericSFI) calls this class when it chooses fixed-point notation. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/FormerEntryPoint/FormerStringFormat.md b/docs/Reference/Contributed/Fmt/FormerEntryPoint/FormerStringFormat.md index b5a40c46..884f31fc 100644 --- a/docs/Reference/Contributed/Fmt/FormerEntryPoint/FormerStringFormat.md +++ b/docs/Reference/Contributed/Fmt/FormerEntryPoint/FormerStringFormat.md @@ -8,6 +8,8 @@ has_toc: false # FormerStringFormat {: .no_toc } + + Builds a string from a format string and a list of values, with the earlier implementation of the formatter. Syntax: **FormerStringFormat** ( *format_string* [, *values* ] ) **As String** @@ -47,6 +49,8 @@ Debug.Print FormerStringFormat("{0:G}", 123.456) ' 123.46 Debug.Print StringFormat("{0:G}", 123.456) ' 1.23456E2 ``` + + ## Example This example uses **N** and a custom number format, which only this function accepts. The results depend on the regional settings, so the output shown is for English (United States). diff --git a/docs/Reference/Contributed/Fmt/FormerEntryPoint/index.md b/docs/Reference/Contributed/Fmt/FormerEntryPoint/index.md index fa8ad666..55266954 100644 --- a/docs/Reference/Contributed/Fmt/FormerEntryPoint/index.md +++ b/docs/Reference/Contributed/Fmt/FormerEntryPoint/index.md @@ -8,6 +8,8 @@ has_toc: false # FormerEntryPoint module {: .no_toc } + + Holds **FormerStringFormat**, the earlier implementation of the formatter, which is kept in the package beside the newer one. **FormerEntryPoint** is a standard module with one function, [**FormerStringFormat**](FormerStringFormat). The function does the whole job in one procedure, in the style of VB6 code. It does not use [**StringHelper**](../StringHelper), [**StringFormatSpecifier**](../StringFormatSpecifier/) or the format identifier classes, and only borrows [**EscapeSequence**](../EscapeSequence/) for the simple escape sequences. It takes the same format strings as [**StringFormat**](../PublicEntryPoints/StringFormat) with a few differences, which are listed on the page of the function. A project that references the package calls it by name or as `FormerEntryPoint.FormerStringFormat`. @@ -16,6 +18,8 @@ Holds **FormerStringFormat**, the earlier implementation of the formatter, which Debug.Print FormerStringFormat("{0} {1:F2}", "x", 2.5) ' x 2.50 ``` + + ## Functions - [FormerStringFormat](FormerStringFormat) -- builds a string from a format string and a list of values, with the earlier implementation diff --git a/docs/Reference/Contributed/Fmt/FormerEscapeSequence/Create.md b/docs/Reference/Contributed/Fmt/FormerEscapeSequence/Create.md index ec94efb7..d989762a 100644 --- a/docs/Reference/Contributed/Fmt/FormerEscapeSequence/Create.md +++ b/docs/Reference/Contributed/Fmt/FormerEscapeSequence/Create.md @@ -8,6 +8,8 @@ has_toc: false # Create {: .no_toc } + + Creates a **FormerEscapeSequence** that holds a text and its replacement. Syntax: *object*.**Create** ( *escape*, *replacement* ) **As FormerEscapeSequence** @@ -27,6 +29,8 @@ Returns a new **FormerEscapeSequence** whose [**EscapeString**](EscapeString) an The class has no constructor with arguments. **Create** takes its place: it makes a new instance, fills in the two properties through their **Friend** setters and returns it. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/FormerEscapeSequence/EscapeString.md b/docs/Reference/Contributed/Fmt/FormerEscapeSequence/EscapeString.md index b0ec5e3d..28435f6d 100644 --- a/docs/Reference/Contributed/Fmt/FormerEscapeSequence/EscapeString.md +++ b/docs/Reference/Contributed/Fmt/FormerEscapeSequence/EscapeString.md @@ -8,6 +8,8 @@ has_toc: false # EscapeString {: .no_toc } + + Returns or sets the text that the escape sequence looks for. ## Get @@ -34,6 +36,8 @@ Syntax: *object*.**EscapeString** **=** *value* [**Create**](Create) sets the property on the instance that it returns. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/FormerEscapeSequence/ReplacementString.md b/docs/Reference/Contributed/Fmt/FormerEscapeSequence/ReplacementString.md index ba3f5648..ff154a6b 100644 --- a/docs/Reference/Contributed/Fmt/FormerEscapeSequence/ReplacementString.md +++ b/docs/Reference/Contributed/Fmt/FormerEscapeSequence/ReplacementString.md @@ -8,6 +8,8 @@ has_toc: false # ReplacementString {: .no_toc } + + Returns or sets the text that replaces the escape sequence. ## Get @@ -34,6 +36,8 @@ Syntax: *object*.**ReplacementString** **=** *value* [**Create**](Create) sets the property on the instance that it returns. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/FormerEscapeSequence/index.md b/docs/Reference/Contributed/Fmt/FormerEscapeSequence/index.md index 96e1449d..2c7ef66e 100644 --- a/docs/Reference/Contributed/Fmt/FormerEscapeSequence/index.md +++ b/docs/Reference/Contributed/Fmt/FormerEscapeSequence/index.md @@ -8,11 +8,13 @@ has_toc: false # FormerEscapeSequence class {: .no_toc } + + Holds a piece of text and its replacement, in the form of the earlier escape sequence class of the package. **FormerEscapeSequence** has two properties, [**EscapeString**](EscapeString) and [**ReplacementString**](ReplacementString), and a [**Create**](Create) method that returns a new instance. It has no method that applies the sequence to a string and cannot describe an ASCII escape. Nothing in the package uses it: [**FormerStringFormat**](../FormerEntryPoint/FormerStringFormat) creates [**EscapeSequence**](../EscapeSequence/) objects. -Unlike **EscapeSequence**, the class is not predeclared, so an instance is created with `New`, and **Create** is called on that instance. The two property setters are declared **Friend**, and a project that references the package can call them as well (checked on BETA 1005). +Unlike **EscapeSequence**, the class is not predeclared, so an instance is created with `New`, and **Create** is called on that instance. The two property setters are declared **Friend**, and a project that references the package can call them as well. ```tb check_build check_run Dim blank As New FormerEscapeSequence @@ -22,6 +24,8 @@ Set esc = blank.Create("\z", "ZZ") Debug.Print esc.EscapeString & " " & esc.ReplacementString ' \z ZZ ``` + + ## Properties - [EscapeString](EscapeString) -- returns or sets the text to look for diff --git a/docs/Reference/Contributed/Fmt/GeneralNumericSFI.md b/docs/Reference/Contributed/Fmt/GeneralNumericSFI.md index 8a36e8ac..28aabb1b 100644 --- a/docs/Reference/Contributed/Fmt/GeneralNumericSFI.md +++ b/docs/Reference/Contributed/Fmt/GeneralNumericSFI.md @@ -8,6 +8,8 @@ has_toc: false # GeneralNumericSFI class {: .no_toc } + + Formats a number for the **G** identifier, as whole-number digits, fixed-point or exponential notation depending on its type and size. **GeneralNumericSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) and matches the identifier **G** or **g** in any case. [**StringHelper**](StringHelper) registers it, so `{0:G}` in a call to [**StringFormat**](PublicEntryPoints/StringFormat) uses it for a numeric value. The class is predeclared. Its interface members are reached through the interface, and [**GetFormattedValue**](#getformattedvalue) is also public. @@ -18,7 +20,7 @@ The result depends on the **TypeName** of the value: - An **Integer** or a **Long** is formatted by [**DecimalSFI**](DecimalSFI): whole-number digits, padded with zeros to the precision. - A **Double** is first formatted by [**ExponentialSFI**](ExponentialSFI) with the same precision. The exponent of that text is read back with [**ParseExponent**](ExponentialSFI#parseexponent). When the exponent is greater than -5 and its absolute value is less than the precision, the value is formatted again by [**FixedPointSFI**](FixedPointSFI), with the precision as the count of decimal places. Otherwise the exponential text is the result. Without a precision the second test cannot succeed, so a **Double** is always in exponential notation. -- A value of any other type, such as a **Single**, **Currency**, **Byte**, **Decimal** or a numeric **String**, gives an empty string. +- A value of any other type, such as a **Single**, **Currency**, **Byte**, **Decimal** or a numeric **String**, is not formatted and gives an empty string. The precision is therefore a count of decimal places and a limit for the exponent. It is not a count of significant digits, as it is in .NET. The case of the identifier is kept: `g` gives a lower-case `e` in exponential notation. The decimal point follows the Windows regional settings. @@ -34,6 +36,9 @@ Debug.Print StringFormat("{0:g}", 12345.678) ' 1.234568e4 Debug.Print "[" & StringFormat("{0:G}", 3.14!) & "]" ' [] ``` +> [!WARNING] 1.0.0.0 +> The value is lost without an error when its type is not **Integer**, **Long** or **Double**: `{0:G}` of a **Single**, **Currency**, **Byte**, **Decimal** or numeric **String** gives an empty string, and alignment pads it. Convert the value to a **Double** or a **Long** first, as in `CDbl(x)`. + > [!NOTE] 1.0.0.0 > **FormerStringFormat** treats a missing precision differently: it derives one from the count of digits after the decimal point in the value. `{0:G}` of `123.456` gives `123.46` there and `1.23456E2` here. See [FormerStringFormat](FormerEntryPoint/FormerStringFormat). @@ -64,6 +69,8 @@ Debug.Print GeneralNumericSFI.GetFormattedValue(12345.678, spec) ' 1.235E4 Returns **True** when the specifier's [**identifier**](StringFormatSpecifier/identifier) is `G` or `g`. It is implemented as a private member of the interface. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/GenericDateTimeSFI.md b/docs/Reference/Contributed/Fmt/GenericDateTimeSFI.md index 99c9c777..22684e66 100644 --- a/docs/Reference/Contributed/Fmt/GenericDateTimeSFI.md +++ b/docs/Reference/Contributed/Fmt/GenericDateTimeSFI.md @@ -8,6 +8,8 @@ has_toc: false # GenericDateTimeSFI class {: .no_toc } + + Formats a **Date** for the date and time identifiers **d**, **D**, **f**, **F**, **g**, **G**, **s**, **t**, **T** and **C**. **GenericDateTimeSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier). [**StringHelper**](StringHelper) registers it as the only date and time identifier, so every date item in a call to [**StringFormat**](PublicEntryPoints/StringFormat) uses it. A value is treated as a date when its **TypeName** is `Date`. The class is predeclared, and its two members are reached through the interface. @@ -55,6 +57,8 @@ The output shown is for an English (United States) system. Both are implemented as private members of the interface. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/HexSFI.md b/docs/Reference/Contributed/Fmt/HexSFI.md index 2128c2e6..678b6d07 100644 --- a/docs/Reference/Contributed/Fmt/HexSFI.md +++ b/docs/Reference/Contributed/Fmt/HexSFI.md @@ -8,6 +8,8 @@ has_toc: false # HexSFI class {: .no_toc } + + Formats a number as hexadecimal digits with an `&H` prefix for the **X** identifier. **HexSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) and matches the identifier **X** or **x** in any case. [**StringHelper**](StringHelper) registers it, so `{0:X}` in a call to [**StringFormat**](PublicEntryPoints/StringFormat) uses it for a numeric value. The class is predeclared, and its two members are reached through the interface. @@ -40,6 +42,8 @@ Debug.Print StringFormat("{0:X}", -1&) ' &HFFFFFFFF Both are implemented as private members of the interface. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/IStringFormatIdentifier.md b/docs/Reference/Contributed/Fmt/IStringFormatIdentifier.md index af856bcf..5a463ac5 100644 --- a/docs/Reference/Contributed/Fmt/IStringFormatIdentifier.md +++ b/docs/Reference/Contributed/Fmt/IStringFormatIdentifier.md @@ -8,6 +8,8 @@ has_toc: false # IStringFormatIdentifier interface {: .no_toc } + + The contract that a format identifier class implements: it says whether it handles an item's identifier, and formats a value for it. Each of the eleven format identifier classes of the package implements **IStringFormatIdentifier**: [**CurrencySFI**](CurrencySFI), [**DateTimeSFI**](DateTimeSFI), [**DecimalSFI**](DecimalSFI), [**ExponentialSFI**](ExponentialSFI), [**FixedPointSFI**](FixedPointSFI), [**GeneralNumericSFI**](GeneralNumericSFI), [**GenericDateTimeSFI**](GenericDateTimeSFI), [**HexSFI**](HexSFI), [**NumericPaddingSFI**](NumericPaddingSFI), [**PercentSFI**](PercentSFI) and [**RoundTripSFI**](RoundTripSFI). [**StringHelper**](StringHelper) holds one list of them for numeric values and one for dates. For each format item it asks every class of the list that suits the value's type whether it matches the item's identifier, and a class that matches formats the value. It does not stop at the first match, so the last matching class decides. The classes of one list match different letters, so at most one of them matches an item. @@ -44,6 +46,8 @@ Syntax: *object*.**IsIdentifierMatch** ( *specifier* ) **As Boolean** Returns **True** when the class formats items with that identifier. The classes of the package compare the specifier's [**identifier**](StringFormatSpecifier/identifier) with their own letter. The numeric classes ignore the case of the identifier. The date classes treat nine of their letters as case-sensitive and accept `C` in either case. + + ## Example This example implements the interface in a class that upper-cases a value for the identifier `U`, and calls it directly. diff --git a/docs/Reference/Contributed/Fmt/NumericPaddingSFI.md b/docs/Reference/Contributed/Fmt/NumericPaddingSFI.md index 5c63a7e7..a93e64e4 100644 --- a/docs/Reference/Contributed/Fmt/NumericPaddingSFI.md +++ b/docs/Reference/Contributed/Fmt/NumericPaddingSFI.md @@ -8,6 +8,8 @@ has_toc: false # NumericPaddingSFI class {: .no_toc } + + A placeholder format identifier that never matches and cannot format. **NumericPaddingSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) but has no identifier of its own. [**StringHelper**](StringHelper) registers it last among the numeric identifiers, and because its **IsIdentifierMatch** always returns **False**, [**StringFormat**](PublicEntryPoints/StringFormat) never calls its **GetFormattedValue**. The package's source states no purpose for it. The class is predeclared, and its two members are reached through the interface. @@ -34,6 +36,8 @@ Debug.Print f.GetFormattedValue(1, spec) Debug.Print Err.Number & ": " & Err.Description ' 5: Invalid procedure call or argument ``` + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/PercentSFI.md b/docs/Reference/Contributed/Fmt/PercentSFI.md index 3a42eba0..c4a69a89 100644 --- a/docs/Reference/Contributed/Fmt/PercentSFI.md +++ b/docs/Reference/Contributed/Fmt/PercentSFI.md @@ -8,6 +8,8 @@ has_toc: false # PercentSFI class {: .no_toc } + + Formats a number as a percentage for the **P** identifier. **PercentSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) and matches the identifier **P** or **p** in any case. [**StringHelper**](StringHelper) registers it, so `{0:P}` in a call to [**StringFormat**](PublicEntryPoints/StringFormat) uses it for a numeric value. The class is predeclared, and its two members are reached through the interface. @@ -31,6 +33,8 @@ Debug.Print StringFormat("{0:P}", 0.005) ' 1% Both are implemented as private members of the interface. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/PublicEntryPoints/StringFormat.md b/docs/Reference/Contributed/Fmt/PublicEntryPoints/StringFormat.md index 0f079a9c..36350a81 100644 --- a/docs/Reference/Contributed/Fmt/PublicEntryPoints/StringFormat.md +++ b/docs/Reference/Contributed/Fmt/PublicEntryPoints/StringFormat.md @@ -24,6 +24,7 @@ The syntax of a format item, the identifiers and the escape sequences are in the ## Remarks + **Which values are needed.** The function finds the format items first. When there are any, the number of distinct indexes must equal the number of values, and the highest index must be below that number. In practice every value must be used, and the indexes must run from `0` to one less than the number of values without a gap; the same index can occur any number of times. Anything else raises error `-2147212503`. A format string with no items accepts any number of values and ignores them. **How a value is formatted.** The type of the value decides which identifiers an item may use: @@ -50,6 +51,8 @@ An item is found again in the text by the text that [**ToString**](../StringForm > [!NOTE] timeless > The results come from **Format$**, so the decimal separator, the thousands separator and the layout of dates and times follow the Windows regional settings. The output shown on this page is for an English (United States) system. + + ## Example This example inserts values into a string, in the order of the format items and out of it. diff --git a/docs/Reference/Contributed/Fmt/PublicEntryPoints/index.md b/docs/Reference/Contributed/Fmt/PublicEntryPoints/index.md index 8a04736d..916c08b1 100644 --- a/docs/Reference/Contributed/Fmt/PublicEntryPoints/index.md +++ b/docs/Reference/Contributed/Fmt/PublicEntryPoints/index.md @@ -8,6 +8,8 @@ has_toc: false # PublicEntryPoints module {: .no_toc } + + Holds **StringFormat**, the main entry point of the package. **PublicEntryPoints** is a standard module with one function, [**StringFormat**](StringFormat). Its **ParamArray** takes the values to format as arguments. It copies them into an array and passes them to [**StringHelper**](../StringHelper), which does the work. A project that references the package calls it by name or as `PublicEntryPoints.StringFormat`. @@ -17,6 +19,8 @@ Debug.Print StringFormat("I'm eating a very nice {0} {1}", "Bramleys", 3.142) ' Debug.Print PublicEntryPoints.StringFormat("{0,-6}|{1,6}|", "ab", "cd") ' ab | cd| ``` + + ## Functions - [StringFormat](StringFormat) -- builds a string from a format string and a list of values diff --git a/docs/Reference/Contributed/Fmt/RoundTripSFI.md b/docs/Reference/Contributed/Fmt/RoundTripSFI.md index 364c1479..c549c050 100644 --- a/docs/Reference/Contributed/Fmt/RoundTripSFI.md +++ b/docs/Reference/Contributed/Fmt/RoundTripSFI.md @@ -8,6 +8,8 @@ has_toc: false # RoundTripSFI class {: .no_toc } + + Converts a number to a **Double** and returns its text for the **R** identifier. **RoundTripSFI** is a format identifier class. It implements [**IStringFormatIdentifier**](IStringFormatIdentifier) and matches the identifier **R** or **r** in any case. [**StringHelper**](StringHelper) registers it, so `{0:R}` in a call to [**StringFormat**](PublicEntryPoints/StringFormat) uses it for a numeric value. The class is predeclared, and its two members are reached through the interface. @@ -29,6 +31,8 @@ Debug.Print StringFormat("{0:R}", "1.50") ' 1.5 Both are implemented as private members of the interface. + + ## See Also - [IStringFormatIdentifier](IStringFormatIdentifier) interface -- the contract every format identifier class implements diff --git a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/Alignment.md b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/Alignment.md index 3e3888f9..18c58f37 100644 --- a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/Alignment.md +++ b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/Alignment.md @@ -8,6 +8,8 @@ has_toc: false # Alignment {: .no_toc } + + Returns or sets the width and the justification of a formatted value. ## Get @@ -34,6 +36,8 @@ Syntax: *object*.**Alignment** **=** *value* [**StringHelper**](../StringHelper) sets the property from the comma part of a format item, so `{0,8}` gives `8` and `{0,-8}` gives `-8`, and it pads the result after the format identifier class has produced it. The padding character is a space. A formatted value that is wider than the alignment is not shortened. The format identifier classes do not read the property. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/CustomSpecifier.md b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/CustomSpecifier.md index 689846cc..83ea88d7 100644 --- a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/CustomSpecifier.md +++ b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/CustomSpecifier.md @@ -8,6 +8,8 @@ has_toc: false # CustomSpecifier {: .no_toc } + + Returns or sets the text of a format item that follows its identifier letter. ## Get @@ -36,6 +38,8 @@ The text is stored as written. When it is numeric and not zero, [**Precision**]( The meaning of the text belongs to the format identifier class. The numeric classes read it as a precision through [**Precision**](Precision). [**GenericDateTimeSFI**](../GenericDateTimeSFI) passes it to **Format$** as a custom date and time format when the identifier is `C`. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/IndexProperty.md b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/IndexProperty.md index 48bb1181..dc703dc5 100644 --- a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/IndexProperty.md +++ b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/IndexProperty.md @@ -8,6 +8,8 @@ has_toc: false # Index {: .no_toc } + + Returns or sets the position of the value that a format item refers to. ## Get @@ -34,6 +36,8 @@ Syntax: *object*.**Index** **=** *value* [**StringHelper**](../StringHelper) sets the property from the number at the start of a format item, so `{2}` gives `2`. The same index can occur in several items. The property is not checked when it is assigned; [**StringHelper**](../StringHelper) checks the indexes of a whole format string against the number of values. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/Precision.md b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/Precision.md index 96f63c0c..96ef943c 100644 --- a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/Precision.md +++ b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/Precision.md @@ -8,6 +8,8 @@ has_toc: false # Precision {: .no_toc } + + Returns the number that follows the identifier letter of a format item. Syntax: *object*.**Precision** @@ -23,6 +25,8 @@ The property is read-only. It changes only when [**CustomSpecifier**](CustomSpec The meaning of the number depends on the format identifier class: a count of decimal places for [**FixedPointSFI**](../FixedPointSFI), [**ExponentialSFI**](../ExponentialSFI), [**CurrencySFI**](../CurrencySFI) and [**PercentSFI**](../PercentSFI), a count of digits for [**DecimalSFI**](../DecimalSFI) and [**HexSFI**](../HexSFI), and both a count of decimal places and a limit for the exponent for [**GeneralNumericSFI**](../GeneralNumericSFI). A precision of `0` selects each class's default. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/ToString.md b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/ToString.md index 555c56ae..9729572b 100644 --- a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/ToString.md +++ b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/ToString.md @@ -8,6 +8,8 @@ has_toc: false # ToString {: .no_toc } + + Returns the text of the format item that the properties describe. Syntax: *object*.**ToString** ( ) **As String** @@ -28,6 +30,8 @@ Debug.Print StringFormat("[{0,08}]", 5) ' [{0,08}] Debug.Print StringFormat("[{0,0}]", 5) ' [{0,0}] ``` + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/identifier.md b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/identifier.md index 37261a38..8ef80bfd 100644 --- a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/identifier.md +++ b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/identifier.md @@ -8,6 +8,8 @@ has_toc: false # identifier {: .no_toc } + + Returns or sets the letter that selects the format of a format item. ## Get @@ -36,6 +38,8 @@ Syntax: *object*.**identifier** **=** *value* The property keeps the letter as written, including its case. The format identifier classes compare it with their own letter, and [**HexSFI**](../HexSFI) and [**GeneralNumericSFI**](../GeneralNumericSFI) copy its case into their results. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/index.md b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/index.md index f585c7d8..b73a772f 100644 --- a/docs/Reference/Contributed/Fmt/StringFormatSpecifier/index.md +++ b/docs/Reference/Contributed/Fmt/StringFormatSpecifier/index.md @@ -8,6 +8,8 @@ has_toc: false # StringFormatSpecifier class {: .no_toc } + + Describes one format item of a format string: its index, alignment, identifier and the text that follows the identifier. [**StringHelper**](../StringHelper) creates one **StringFormatSpecifier** for every format item it finds in a format string, and passes it to the [format identifier classes](../IStringFormatIdentifier), which read the identifier and the precision from it. For the item `{2,-8:G3}` the index is `2`, the alignment is `-8`, the identifier is `G` and the custom specifier is `3`, which is also the precision. @@ -25,6 +27,8 @@ Debug.Print spec.ToString() ' {2,-8:G3} Debug.Print spec.Precision ' 3 ``` + + ## Properties - [Alignment](Alignment) -- returns or sets the width and justification of the formatted value diff --git a/docs/Reference/Contributed/Fmt/StringHelper.md b/docs/Reference/Contributed/Fmt/StringHelper.md index 2cf4ce45..5b5a5df6 100644 --- a/docs/Reference/Contributed/Fmt/StringHelper.md +++ b/docs/Reference/Contributed/Fmt/StringHelper.md @@ -8,6 +8,8 @@ has_toc: false # StringHelper class {: .no_toc } + + The class that does the work behind **StringFormat**: it finds the format items in a format string, formats the values and expands the escape sequences. **StringHelper** is predeclared, so its one public method is called on the class name, `StringHelper.StringFormat`. [**StringFormat**](PublicEntryPoints/StringFormat) in the [**PublicEntryPoints**](PublicEntryPoints/) module copies its **ParamArray** into an array and calls this method. Call the method directly when the values are already in an array. A project can also create an instance with `New StringHelper`, which behaves in the same way. @@ -48,6 +50,8 @@ Debug.Print StringHelper.StringFormat("{0}-{1:F1}", values) ' x-2.5 Debug.Print StringHelper.StringFormat("{0} and {1}", Array("salt", "pepper")) ' salt and pepper ``` + + ## See Also - [StringFormat](PublicEntryPoints/StringFormat) function -- the entry point that takes the values as arguments diff --git a/docs/Reference/Contributed/Fmt/StringValidation/CopyCapitalisation.md b/docs/Reference/Contributed/Fmt/StringValidation/CopyCapitalisation.md index 8934da44..e2c4e9d8 100644 --- a/docs/Reference/Contributed/Fmt/StringValidation/CopyCapitalisation.md +++ b/docs/Reference/Contributed/Fmt/StringValidation/CopyCapitalisation.md @@ -8,6 +8,8 @@ has_toc: false # CopyCapitalisation {: .no_toc } + + Returns a string in upper case or in lower case, following the case of another string. Syntax: **CopyCapitalisation** ( *source*, *applyTo* ) **As String** @@ -26,6 +28,8 @@ The function looks at *source* as a whole and does not copy case letter by lette The format identifier classes use it to give a result the case of the identifier that was written: `{0:X}` and `{0:x}` give `&HFF` and `&hff`. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringValidation/StringContains.md b/docs/Reference/Contributed/Fmt/StringValidation/StringContains.md index 6fd79e03..7d376fcd 100644 --- a/docs/Reference/Contributed/Fmt/StringValidation/StringContains.md +++ b/docs/Reference/Contributed/Fmt/StringValidation/StringContains.md @@ -8,6 +8,8 @@ has_toc: false # StringContains {: .no_toc } + + Returns whether a string contains another string, ignoring letter case unless asked not to. Syntax: **StringContains** ( *haystack*, *needle* [, *caseSensitive* ] ) **As Boolean** @@ -29,6 +31,8 @@ The function calls **InStr** with the text-comparison mode, or the binary mode w [**StringContainsAny**](StringContainsAny) takes *caseSensitive* as a required argument before its list of strings. Here it is the last argument and is optional. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringValidation/StringContainsAny.md b/docs/Reference/Contributed/Fmt/StringValidation/StringContainsAny.md index 2b2ba7d2..e9fd469f 100644 --- a/docs/Reference/Contributed/Fmt/StringValidation/StringContainsAny.md +++ b/docs/Reference/Contributed/Fmt/StringValidation/StringContainsAny.md @@ -8,6 +8,8 @@ has_toc: false # StringContainsAny {: .no_toc } + + Returns whether a string contains at least one of several strings. Syntax: **StringContainsAny** ( *haystack*, *caseSensitive* [, *needles* ] ) **As Boolean** @@ -27,6 +29,8 @@ Returns **True** when at least one of *needles* occurs in *haystack*, and **Fals The function passes each needle in turn to [**StringContains**](StringContains) and returns **True** at the first one that is found, so later needles are not tried. With no needles the result is **False**. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringValidation/StringMatchesAny.md b/docs/Reference/Contributed/Fmt/StringValidation/StringMatchesAny.md index 2fd8597b..eb78ab47 100644 --- a/docs/Reference/Contributed/Fmt/StringValidation/StringMatchesAny.md +++ b/docs/Reference/Contributed/Fmt/StringValidation/StringMatchesAny.md @@ -8,6 +8,8 @@ has_toc: false # StringMatchesAny {: .no_toc } + + Returns whether a string is equal to at least one of several values. Syntax: **StringMatchesAny** ( *searchString* [, *possibleMatches* ] ) **As Boolean** @@ -24,6 +26,8 @@ Returns **True** when *searchString* is equal to at least one of *possibleMatche The comparison is an equality test of whole strings, not a search for a substring. It takes letter case into account, because the module has no `Option Compare` statement and the default comparison is binary. A number among the values is compared as its text, so `1` matches `"1"`. With no values the result is **False**. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringValidation/StringStartsWith.md b/docs/Reference/Contributed/Fmt/StringValidation/StringStartsWith.md index a968d12a..840634a6 100644 --- a/docs/Reference/Contributed/Fmt/StringValidation/StringStartsWith.md +++ b/docs/Reference/Contributed/Fmt/StringValidation/StringStartsWith.md @@ -8,6 +8,8 @@ has_toc: false # StringStartsWith {: .no_toc } + + Returns whether a string begins with another string, taking letter case into account. Syntax: **StringStartsWith** ( *startingSequence*, *source* ) **As Boolean** @@ -29,6 +31,8 @@ The comparison is binary, so `"he"` does not start `"Hello"`. An empty *starting [**StringFormat**](../PublicEntryPoints/StringFormat) uses the function to test whether a format string begins with `@`. + + ## Example ```tb check_build check_run diff --git a/docs/Reference/Contributed/Fmt/StringValidation/index.md b/docs/Reference/Contributed/Fmt/StringValidation/index.md index 75f93acd..d0af1453 100644 --- a/docs/Reference/Contributed/Fmt/StringValidation/index.md +++ b/docs/Reference/Contributed/Fmt/StringValidation/index.md @@ -8,6 +8,8 @@ has_toc: false # StringValidation module {: .no_toc } + + Holds the small string tests and the case helper that the package's formatter is built on. **StringValidation** is a standard module of five public functions. [**StringContains**](StringContains), [**StringContainsAny**](StringContainsAny), [**StringMatchesAny**](StringMatchesAny) and [**StringStartsWith**](StringStartsWith) return a **Boolean** about the text of a string, and [**CopyCapitalisation**](CopyCapitalisation) returns a string in the letter case of another. The formatter calls them to look for backslashes and escape sequences, to test whether a format string begins with `@` and to follow the case of an identifier. A project that references the package can call them too, by name or as `StringValidation.StringContains`. @@ -23,6 +25,8 @@ Debug.Print StringStartsWith("He", "Hello") ' True Debug.Print CopyCapitalisation("HELLO", "world") ' WORLD ``` + + ## Functions - [CopyCapitalisation](CopyCapitalisation) -- returns a string in upper case or lower case, following another string diff --git a/docs/Reference/Contributed/Fmt/index.md b/docs/Reference/Contributed/Fmt/index.md index fd48cb7b..8dfabd79 100644 --- a/docs/Reference/Contributed/Fmt/index.md +++ b/docs/Reference/Contributed/Fmt/index.md @@ -31,6 +31,7 @@ The functions are public, so **StringFormat** is called by name, as the samples ## Format items + A format item is written ``` @@ -57,10 +58,13 @@ Debug.Print StringFormat("[{0, 8}]", 5) ' [{0, 8}] Debug.Print StringFormat("[{00}]", 5) ' [{00}] ``` + + [**StringFormat**](PublicEntryPoints/StringFormat) describes how the items are processed. The properties of one item are held by a [**StringFormatSpecifier**](StringFormatSpecifier/). ## Format identifiers + The type of the value decides which identifiers an item can use. A value for which **IsNumeric** is **True** (a number, a **Boolean**, a **String** that looks like a number) uses the numeric identifiers. A value of type **Date** uses the date identifiers. A value of any other type takes no identifier. An identifier that does not suit the value raises error `-2147212503`, *Invalid format string*. A **Null** value raises error 94. ### Numeric values @@ -98,8 +102,11 @@ Debug.Print StringFormat("{0:C}", 1234567.891) ' 1,234,567.89$ Debug.Print StringFormat("{0:C}", -5) ' -5.00$ ``` + + ### Dates + The date identifiers are the letters of [**GenericDateTimeSFI**](GenericDateTimeSFI). They are case-sensitive, except `C`. | Identifier | Result | Example for 11 October 2026, 14:05:09 | @@ -130,8 +137,11 @@ Debug.Print StringFormat("{0:Cyyyy-MM-dd}", d) ' 2026-10-11 > [!NOTE] timeless > The results come from **Format$**, so the decimal separator, the thousands separator and the layout of dates and times follow the Windows regional settings. The output shown for the currency and date identifiers is for an English (United States) system. + + ## Escape sequences + The formatter replaces these sequences in the finished string, values included. A backslash that starts none of them stays as it is. | Sequence | Result | @@ -163,8 +173,11 @@ Debug.Print StringFormat("@a\nb") ' a\nb An `\xHH` or `\ooo` sequence with digits that are not valid in its base, such as `\xzz` or `\189`, raises error 13. + + ## Errors + | Number | Description | Raised when | |--------|-------------|-------------| | `-2147212503` (`vbObjectError Or 9001`) | *The number indicating an argument to format is less than zero, or greater than or equal to the length of the args array.* | The format string has items, and the distinct indexes do not number exactly the values, or the highest index is not below that number, or no values were given. | @@ -174,8 +187,11 @@ An `\xHH` or `\ooo` sequence with digits that are not valid in its base, such as The first two have the source `StringHelper`. [**FormerStringFormat**](FormerEntryPoint/FormerStringFormat) raises the same numbers from the source `FormerStringFormat`, and adds `-2147212501` (`vbObjectError Or 9003`), *Invalid number argument*. + + ## Differences from .NET + The function follows the notation of .NET's **String.Format** and departs from it in these ways: - Doubled braces do not escape a brace. @@ -186,6 +202,7 @@ The function follows the notation of .NET's **String.Format** and departs from i - `C` writes a `$` after the number whatever the regional settings, `X` writes an `&H` prefix, and `R` keeps 15 significant digits. - Only the identifiers of the date table exist for dates, and they are case-sensitive. The custom format of a date is the `C` identifier with a **Format$** string, whose letters are VBA's. - Escape sequences are expanded by the function itself and use the C-style letters of the table above. + ## Modules @@ -250,3 +267,7 @@ Each of these implements [**IStringFormatIdentifier**](IStringFormatIdentifier) ## Other public names The package also exposes `HelloWorldClass`, `HelloWorldModule` and `Pootle`. They are test code that was left in the package, and they are not part of its formatting API. `HelloWorldClass.Show` and `HelloWorldModule.Show` each display a message box that says *Hello world!*, and `Pootle.SimpleTest` prints the results of four sample calls to **StringFormat** to the Immediate window. These three names have no pages. + +## Test cases + +The notes and warnings of this section are checked by the test cases in [Fmt-tests.twinproj](Fmt-tests.twinproj), a twinBASIC project. It uses Fmt as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/Generics/index.md b/docs/Reference/Contributed/Generics/index.md index cf04e2db..14200fdd 100644 --- a/docs/Reference/Contributed/Generics/index.md +++ b/docs/Reference/Contributed/Generics/index.md @@ -17,12 +17,14 @@ The **Generics** contributed package provides seven tuple classes, **TupleOf1** A tuple lets a function return several values at once, without a user-defined type declared for the purpose or **ByRef** parameters for the results. Each class is a [generic class](../../../../Features/Language/Generics): **TupleOf2(Of Long, String)** holds a **Long** and a **String**, and its **Item1** and **Item2** properties return them with those types. The classes follow .NET's **System.Tuple** classes, and their source comments are taken from .NET's, under the MIT licence. This section documents version 1.0.0.0, published under the MIT licence. + ```tb check_build check_run Dim t As TupleOf3(Of String, Long, Date) = New TupleOf3(Of String, Long, Date)("Ada", 36, #12/10/1815#) Debug.Print t.Item1 ' Ada Debug.Print t.Item2 ' 36 Debug.Print Year(t.Item3) ' 1815 ``` + ## Syntax @@ -45,7 +47,9 @@ Debug.Print Year(t.Item3) ' 1815 *k* : The number of an item, 1 to *n*. + **Item***k* returns the *k*th item, of type *Tk*. The properties are read-only: an item is set only when the tuple is created, and nothing changes it afterwards. The classes have no other members. + | Class | Type parameters | Properties | |:------|:----------------|:-----------| @@ -69,6 +73,7 @@ Set size = New TupleOf2(Of Long, Long)(640, 480) Debug.Print size.Item1 * size.Item2 ' 307200 ``` + A function that returns a tuple declares the tuple's type as its return type: ```tb check_build slot=file @@ -95,7 +100,9 @@ Module GenericsMinMaxDemo End Sub End Module ``` + + > [!NOTE] BETA 1005 > **New** with a generic class from a package compiles only when some declaration in the project or the package names the same class with the same type arguments. A variable declared with the tuple's type is such a declaration. Without one, `Dim t As Any = New TupleOf2(Of String, Long)("a", 2)` fails with TB5074, *Could not bind to parameterized constructor of class 'TupleOf2'*, and so does **New** assigned to an **Object** or **Variant** variable or passed as an argument. A generic class declared in the project's own code is not affected. Assign the tuple to a variable of its own type first; that variable can then be assigned to an **Object** or **Variant** variable, through which the items are read late-bound: > @@ -106,6 +113,7 @@ End Module > Debug.Print o.Item1; o.Item2 ' width 640 > ``` + The constructor's parameters are declared without **ByVal**, so they are **ByRef**. A variable passed for an item must have the item's type exactly: a **Byte** variable passed for a **Long** item fails with TB5001, *cannot coerce type 'Byte' to [ByRef] 'Long'*. A literal or any other expression is converted as usual, so wrap such a variable in a conversion function. The tuple keeps a copy of each value, and a later change to the variable does not change the tuple: ```tb check_build check_run @@ -114,10 +122,12 @@ Dim t As TupleOf1(Of Long) = New TupleOf1(Of Long)(CLng(b)) b = 1 Debug.Print t.Item1 ' 200 ``` + ## Reading the items -Each item is read through its **Item** property, which returns it with the type it was declared with. A tuple is an object, so it is assigned with [**Set**](../../../Core/Set), and a variable that has not been assigned one is **Nothing**. Two tuples with equal items are still two objects: [**Is**](../../../Core/Is) compares the objects themselves, so the items are compared one by one. [**TypeName**](../../../Modules/Information/TypeName) returns the class's name without its type arguments: + +Each item is read through its **Item** property, which returns it with the type it was declared with. An **Object** or **Variant** variable that holds a tuple reads the items late-bound, through the same names. A tuple is an object, so it is assigned with [**Set**](../../../Core/Set), and a variable that has not been assigned one is **Nothing**. Two tuples with equal items are still two objects: [**Is**](../../../Core/Is) compares the objects themselves, so the items are compared one by one. [**TypeName**](../../../Modules/Information/TypeName) returns the class's name without its type arguments: ```tb check_build check_run Dim a As TupleOf2(Of Long, String) = New TupleOf2(Of Long, String)(1, "x") @@ -126,22 +136,35 @@ Debug.Print TypeName(a) ' TupleOf2 Debug.Print a Is b ' False Debug.Print a.Item1 = b.Item1 And a.Item2 = b.Item2 ' True ``` + Unlike .NET's tuples, the classes have no method that compares two tuples or converts one to a string. ## Types of the items -An item can have any numeric type, **String**, **Boolean**, **Date**, **Currency** or **LongPtr**. Three kinds of type cannot be used: + +An item can have any numeric type, **String**, **Boolean**, **Date**, **Currency** or **LongPtr**. + + + +Three kinds of type cannot be used: - **Variant.** twinBASIC refuses **Variant** as a type argument: TB5124, *Cannot pass Variant datatype as a generic specifier, use a more explicit type instead*. -- **Object types.** The constructor stores each item with `=`, not with **Set**. A class whose default member takes an argument, such as **Collection**, fails to compile, with TB5092 *Missing argument 'Index'* reported in the package's source. **Object** compiles, but creating the tuple raises run-time error 91, *Object variable or With block variable not set*, even when the item passed is **Nothing**. So a tuple cannot hold another tuple either. +- **Object types.** A tuple cannot hold an object, another tuple included; see the first note below. - **Arrays**, such as **Long()**. + + + +> [!NOTE] 1.0.0.0 +> The constructor stores each item with `=`, not with **Set**, so creating a tuple of an object type fails. For **Object**, which compiles, **New** raises run-time error 91, *Object variable or With block variable not set*, even when the item passed is **Nothing**. A class whose default member takes an argument, such as **Collection**, fails to compile, with TB5092 *Missing argument 'Index'* reported in the package's source. + > [!WARNING] BETA 1005 > A tuple type with an array as a type argument, such as `TupleOf1(Of Long())`, crashes the compiler wherever it is written: in a declaration, a parameter, a function's result type or after **New**. The compiler restarts and crashes again, so nothing in the project compiles until the line is removed. The same happens with a generic class declared in a project's own code. ## TupleOf4 + > [!NOTE] 1.0.0.0 > **TupleOf4** cannot be used. The package declares it with three type parameters, `TupleOf4(Of T1, T2, T3)`, but its fourth item uses a fourth, *T4*, which is not declared. With four type arguments, a declaration fails with TB5124, *Too many generic specifiers*. With three, the package's source fails with TB5079, *Unrecognized datatype symbol 'T4'*. A project that does not use **TupleOf4** is not affected. For four values, use **TupleOf5** and leave its last item unused. @@ -150,3 +173,7 @@ An item can have any numeric type, **String**, **Boolean**, **Date**, **Currency - [Generics](../../../../Features/Language/Generics) -- generic classes, procedures and types in twinBASIC - [Parameterized class constructors](../../../../Features/Advanced/Classes-and-Modules#parameterized-class-constructors) -- the **Sub New** that each tuple class declares - [New](../../../Core/New) operator -- creates an object, passing arguments to its constructor + +## Test cases + +The notes and warnings of this section are checked by the test cases in [Generics-tests.twinproj](Generics-tests.twinproj), a twinBASIC project. It uses Generics as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/OLEGuids/IDataObject.md b/docs/Reference/Contributed/OLEGuids/IDataObject.md index 7cfe7720..91ca5532 100644 --- a/docs/Reference/Contributed/OLEGuids/IDataObject.md +++ b/docs/Reference/Contributed/OLEGuids/IDataObject.md @@ -10,7 +10,9 @@ has_toc: false Names the COM interface that transfers data between applications, as the clipboard and drag and drop do; declared here as a stub with no methods. + **IDataObject** has the interface identifier `0000010E-0000-0000-C000-000000000046` and extends **stdole.IUnknown**. It is declared `[OleAutomation(False)]`. Its description marks it as a stub and points to the tbShellLib package for the full version. Microsoft Learn describes the Windows interface: [IDataObject](https://learn.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-idataobject). + The stub declares none of the methods that get, set and describe data formats. The type exists so that other declarations of the package can name it: [**IRichEditOle.GetClipboardData**](IRichEditOle#getclipboarddata) and [**IRichEditOleCallback.GetClipboardData**](IRichEditOleCallback#getclipboarddata) return one, and [**IRichEditOle.ImportDataObject**](IRichEditOle#importdataobject) and [**IRichEditOleCallback.QueryAcceptData**](IRichEditOleCallback#queryacceptdata) take one. Nothing can be called through a variable of this type. diff --git a/docs/Reference/Contributed/OLEGuids/IDispatchUnrestricted.md b/docs/Reference/Contributed/OLEGuids/IDispatchUnrestricted.md index c03f960b..a6274f41 100644 --- a/docs/Reference/Contributed/OLEGuids/IDispatchUnrestricted.md +++ b/docs/Reference/Contributed/OLEGuids/IDispatchUnrestricted.md @@ -16,6 +16,7 @@ Finds and calls the members of an automation object by name or by dispatch ident The declaration is complete: it has all four methods of the Windows interface. Each is a **Sub** that hides the HRESULT: a failure code raises a run-time error, and a success code other than `S_OK` is read with [**Err.LastHresult**](../../../Modules/ErrObject/LastHresult). The interface identifiers are the package's [OLECLSID](OLECLSID). **Invoke** takes its three structure parameters as pointers (**LongPtr**): the address of an [OLEDISPPARAMS](OLEDISPPARAMS), of a **Variant** and of an `EXCEPINFO` structure, from [**VarPtr**](../../../Modules/Information/VarPtr), or 0 where the parameter is optional. + > [!NOTE] 1.0.10.0 > The package has no declaration named **IDispatch**. A variable declared `As IDispatch` has the type of **stdole**, and a call through it is late-bound by name, so the four methods are not reached. To reach them, assign the object to an **IDispatchUnrestricted** variable. The assignment asks the object for the **IDispatch** interface, and it accepts an **Object** variable and a **stdole.IDispatch** variable alike. [IOleControlSite.GetExtendedControl](IOleControlSite#getextendedcontrol) returns a **stdole.IDispatch**. @@ -38,7 +39,9 @@ The [IDispatch](../../../../Reference/COM-Interfaces/IDispatch) page in the COM Sub GetTypeInfoCount(ByRef pctInfo As Long) ``` + Says whether the object can describe itself with type information. *pctInfo* receives 1 when the object has type information and 0 when it has none. An object of a twinBASIC class has it. + ### GetTypeInfo @@ -46,7 +49,9 @@ Says whether the object can describe itself with type information. *pctInfo* rec Sub GetTypeInfo(ByVal itinfo As Long, ByVal lcid As Long, ByRef pptinfo As LongPtr) ``` + Returns the object's type information, as an `ITypeInfo*` in *pptinfo*. *itinfo* is the index of the type information: 0 selects the type information of the object's own implementation of **IDispatch**. *lcid* is the locale of the information. An object of a twinBASIC class has the one description, and any other index raises run-time error -2147352565, which is `DISP_E_BADINDEX` (`&H8002000B`). + ### GetIDsOfNames @@ -56,6 +61,7 @@ Sub GetIDsOfNames(ByRef riid As OLEGuids.OLECLSID, ByRef rgszNames As LongPtr, B Converts the names of a member and its parameters into dispatch identifiers. *riid* is reserved: pass an [OLECLSID](OLECLSID) holding only zeros. *rgszNames* is the first element of an array of *cNames* pointers to Unicode strings, such as `StrPtr(name)` in a **LongPtr** variable, and *rgDispId* receives one identifier for each name. + A name that the object does not have raises run-time error 438, *Object doesn't support this property or method*. **Err.LastHresult** then holds `DISP_E_UNKNOWNNAME` (`&H80020006`), and *rgDispId* holds -1 (`DISPID_UNKNOWN`). ```tb check_run @@ -70,6 +76,7 @@ Debug.Print Err.Number ' 438 Debug.Print Hex(Err.LastHresult) ' 80020006 Debug.Print id ' -1 ``` + ### Invoke @@ -83,9 +90,13 @@ Calls the member with the dispatch identifier *DispId*. *riid* is reserved and h The package declares no `EXCEPINFO` type, and the **stdole** type cannot be used: some of its members are of unsigned types, which twinBASIC does not support, as VB6 does not, so a variable of it does not compile (TB5106). A project that wants the details declares a structure of the same layout with signed members, as the [IDispatch](../../../../Reference/COM-Interfaces/IDispatch) page does. + A member that raises an error is reported in one of two ways. With *lpexcepinfo* set to 0, the member's own error is raised: an `Err.Raise 5, , "boom"` in the member raises error 5 with the description *boom*. With *lpexcepinfo* the address of a structure, the call raises error -2147352567, which is `DISP_E_EXCEPTION` (`&H80020009`), and the structure holds the error: *scode* is the Windows code of the member's error (`&H800A0005` for error 5), and *bstrSource* and *bstrDescription* are its source and description. + + An identifier that the object has no member for raises error 438. **Err.LastHresult** holds `DISP_E_MEMBERNOTFOUND` (`&H80020003`). + ## Example @@ -112,6 +123,7 @@ Class Calculator End Class ``` + The arguments are an array of **Variant** values in **reverse** order: the last argument of the call is element 0. The call `Subtract(10, 3)` puts 3 in element 0 and 10 in element 1. ```tb check_run @@ -134,6 +146,7 @@ Dim argErr As Long d.Invoke id, iid, 0, 1, VarPtr(params), VarPtr(result), 0, argErr Debug.Print result ' 7 ``` + ## See Also diff --git a/docs/Reference/Contributed/OLEGuids/IEnumVARIANTUnrestricted.md b/docs/Reference/Contributed/OLEGuids/IEnumVARIANTUnrestricted.md index 0dde0e4e..543fefe3 100644 --- a/docs/Reference/Contributed/OLEGuids/IEnumVARIANTUnrestricted.md +++ b/docs/Reference/Contributed/OLEGuids/IEnumVARIANTUnrestricted.md @@ -18,8 +18,9 @@ The enumerator declared in **stdole** cannot be implemented from twinBASIC, beca The declaration extends **stdole.IDispatch**. The Windows **IEnumVARIANT** extends **IUnknown**. + > [!WARNING] 1.0.10.0 -> Because the declaration extends **stdole.IDispatch**, its four methods come after the seven of **IDispatch** in the method table, where the table of an **IEnumVARIANT** has them straight after the three of **IUnknown**. So every method called through this interface on a real enumerator, such as the one a **Collection** returns, calls a slot past the end of its table: **Next**, **Skip** and **Clone** end the program with an access violation, and **Reset** raises a run-time error or does nothing. **For Each** over an object whose enumerator is a class that implements this interface ends the program the same way, without calling any of the class's methods. A program that calls or writes an enumerator declares its own copy of the interface, which extends **stdole.IUnknown**, as the [IEnumVARIANT](../../../../Reference/COM-Interfaces/IEnumVARIANT) page in the COM Interfaces section shows. +> Because the declaration extends **stdole.IDispatch**, its four methods come after the seven of **IDispatch** in the method table, where the table of an **IEnumVARIANT** has them straight after the three of **IUnknown**: **Next** is entry 7 of the table, not 3, **Skip** 8, **Reset** 9 and **Clone** 10. So every method called through this interface on a real enumerator, such as the one a **Collection** returns, calls an entry past the end of its table, and what that does depends on what lies there: **Next** on the enumerator of a **Collection** ends the program with an access violation. A program that calls or writes an enumerator declares its own copy of the interface, which extends **stdole.IUnknown**, as the [IEnumVARIANT](../../../../Reference/COM-Interfaces/IEnumVARIANT) page in the COM Interfaces section shows. The declaration is complete: it has all four methods of the Windows interface. Each is a **Sub**, so the HRESULT is hidden: a failure code raises a run-time error, and a success code such as `S_FALSE` is read with [**Err.LastHresult**](../../../Modules/ErrObject/LastHresult). The [IEnumVARIANT](../../../../Reference/COM-Interfaces/IEnumVARIANT) page in the COM Interfaces section gives the contract of the four methods, how **For Each** uses them, and a project's own declaration of the interface. diff --git a/docs/Reference/Contributed/OLEGuids/IObjectSafety.md b/docs/Reference/Contributed/OLEGuids/IObjectSafety.md index af7d49b8..26db88fc 100644 --- a/docs/Reference/Contributed/OLEGuids/IObjectSafety.md +++ b/docs/Reference/Contributed/OLEGuids/IObjectSafety.md @@ -10,7 +10,9 @@ has_toc: false Lets a host ask an object which safety options it supports for an interface, and set them. + **IObjectSafety** has the interface identifier `CB5BDC81-93C1-11CF-8F20-00805F2CD064` and extends **stdole.IUnknown**. It is declared `[OleAutomation(False)]`. Microsoft Learn describes the Windows interface: [IObjectSafety](https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa768224(v=vs.85)). + A host, such as a web browser that runs scripts, asks an object that may be called by untrusted code whether it agrees to be. An object implements the interface to say that it does. The declaration is complete: it has both methods of the Windows interface. They are **Sub** members, so the HRESULT is hidden: a failure code raises a run-time error, and a class that implements the interface sets [**Err.ReturnHResult**](../../../Modules/ErrObject/ReturnHResult) to return one. Neither method is marked `[PreserveSig]`, so a class can implement the interface. diff --git a/docs/Reference/Contributed/OLEGuids/IOleClientSite.md b/docs/Reference/Contributed/OLEGuids/IOleClientSite.md index e1953f52..8fc5ee59 100644 --- a/docs/Reference/Contributed/OLEGuids/IOleClientSite.md +++ b/docs/Reference/Contributed/OLEGuids/IOleClientSite.md @@ -10,7 +10,9 @@ has_toc: false Names the container-side site of an embedded OLE object; declared here as a stub with no methods. + **IOleClientSite** has the interface identifier `00000118-0000-0000-C000-000000000046` and extends **stdole.IUnknown**. It is declared `[OleAutomation(False)]`. Its description marks it as a stub and points to the tbShellLib package for the full version. + In Windows, a container implements the interface to give an embedded object the services of its site: saving the object, naming its moniker and container, showing it, and resizing it. Microsoft Learn describes the Windows interface: [IOleClientSite](https://learn.microsoft.com/en-us/windows/win32/api/oleidl/nn-oleidl-ioleclientsite). diff --git a/docs/Reference/Contributed/OLEGuids/IOleControlVB.md b/docs/Reference/Contributed/OLEGuids/IOleControlVB.md index 5cefe03d..b23e19f1 100644 --- a/docs/Reference/Contributed/OLEGuids/IOleControlVB.md +++ b/docs/Reference/Contributed/OLEGuids/IOleControlVB.md @@ -10,7 +10,9 @@ has_toc: false Visual Basic-style versions of the **IOleControl** methods **GetControlInfo** and **OnMnemonic**, which a class can implement. + **IOleControlVB** is not a Windows interface. It has the interface identifier `C895C8F9-6564-4123-8760-529F72AB9322` and extends **stdole.IDispatch**. It is not declared `[OleAutomation(False)]`, as the package's Windows interfaces are. Its two methods have the dispatch identifiers 1 and 2 (`[DispId(1)]` and `[DispId(2)]`). + It exists because [**IOleControl**](IOleControl) has `[PreserveSig]` members, which a twinBASIC class cannot implement. Each method here is a **Sub** that corresponds to one of those two methods, with signature types that Visual Basic code uses. The other two methods of **IOleControl**, **OnAmbientPropertyChange** and **FreezeEvents**, have no counterpart. diff --git a/docs/Reference/Contributed/OLEGuids/IOleInPlaceActiveObjectVB.md b/docs/Reference/Contributed/OLEGuids/IOleInPlaceActiveObjectVB.md index 3ab07e27..0d089b49 100644 --- a/docs/Reference/Contributed/OLEGuids/IOleInPlaceActiveObjectVB.md +++ b/docs/Reference/Contributed/OLEGuids/IOleInPlaceActiveObjectVB.md @@ -10,7 +10,9 @@ has_toc: false A Visual Basic-style version of **IOleInPlaceActiveObject.TranslateAccelerator**, which a class can implement. + **IOleInPlaceActiveObjectVB** is not a Windows interface. It has the interface identifier `38584260-0CFB-45E7-8FBB-5D20B311F5B8` and extends **stdole.IDispatch**. It is not declared `[OleAutomation(False)]`, as the package's Windows interfaces are. + It exists because [**IOleInPlaceActiveObject**](IOleInPlaceActiveObject) has `[PreserveSig]` members, which a twinBASIC class cannot implement. This interface has one **Sub** that corresponds to the **TranslateAccelerator** method of that interface, with signature types that Visual Basic code uses. diff --git a/docs/Reference/Contributed/OLEGuids/IOleInPlaceObjectWindowlessVB.md b/docs/Reference/Contributed/OLEGuids/IOleInPlaceObjectWindowlessVB.md index fadd9d50..dcc38512 100644 --- a/docs/Reference/Contributed/OLEGuids/IOleInPlaceObjectWindowlessVB.md +++ b/docs/Reference/Contributed/OLEGuids/IOleInPlaceObjectWindowlessVB.md @@ -10,7 +10,9 @@ has_toc: false A Visual Basic-style version of **IOleInPlaceObjectWindowless.OnWindowMessage**, which a class can implement. + **IOleInPlaceObjectWindowlessVB** is not a Windows interface. It has the interface identifier `061D3A2F-A213-4016-9756-B89140604819` and extends **stdole.IDispatch**. It is not declared `[OleAutomation(False)]`, as the package's Windows interfaces are. Its one method has the dispatch identifier 1 (`[DispId(1)]`). + It exists because [**IOleInPlaceObjectWindowless**](IOleInPlaceObjectWindowless) has `[PreserveSig]` members, which a twinBASIC class cannot implement. This interface has one **Sub** that corresponds to the **OnWindowMessage** method of that interface, with signature types that Visual Basic code uses. **GetDropTarget** has no counterpart. diff --git a/docs/Reference/Contributed/OLEGuids/IOleWindow.md b/docs/Reference/Contributed/OLEGuids/IOleWindow.md index 3b6618b2..4ab2d2c7 100644 --- a/docs/Reference/Contributed/OLEGuids/IOleWindow.md +++ b/docs/Reference/Contributed/OLEGuids/IOleWindow.md @@ -10,7 +10,9 @@ has_toc: false Returns the handle of a window that an OLE object or container uses, and turns context-sensitive help mode on and off. + **IOleWindow** has the interface identifier `00000114-0000-0000-C000-000000000046` and extends **stdole.IUnknown**. It is declared `[OleAutomation(False)]`. Microsoft Learn describes the Windows interface: [IOleWindow](https://learn.microsoft.com/en-us/windows/win32/api/oleidl/nn-oleidl-iolewindow). + It is the base of the in-place activation interfaces: [IOleInPlaceObject](IOleInPlaceObject), [IOleInPlaceUIWindow](IOleInPlaceUIWindow) (and through it [IOleInPlaceFrame](IOleInPlaceFrame)) and [IOleInPlaceSite](IOleInPlaceSite) extend it. Neither method is marked `[PreserveSig]`, so the HRESULT is hidden: a failure code raises a run-time error. diff --git a/docs/Reference/Contributed/OLEGuids/IPerPropertyBrowsingVB.md b/docs/Reference/Contributed/OLEGuids/IPerPropertyBrowsingVB.md index 5e98d667..175bb0cb 100644 --- a/docs/Reference/Contributed/OLEGuids/IPerPropertyBrowsingVB.md +++ b/docs/Reference/Contributed/OLEGuids/IPerPropertyBrowsingVB.md @@ -10,7 +10,9 @@ has_toc: false Visual Basic-style versions of three **IPerPropertyBrowsing** methods, which a class can implement. + **IPerPropertyBrowsingVB** is not a Windows interface. It has the interface identifier `D5D3BBE3-DB60-4522-AF5B-D767FE736DDB` and extends **stdole.IDispatch**. It is not declared `[OleAutomation(False)]`, as the package's Windows interfaces are. Its three methods have the dispatch identifiers 1, 2 and 3 (`[DispId(1)]`, `[DispId(2)]` and `[DispId(3)]`). + It exists because [**IPerPropertyBrowsing**](IPerPropertyBrowsing) has `[PreserveSig]` members, which a twinBASIC class cannot implement. Each method here is a **Sub** that corresponds to one of that interface's methods, and uses Visual Basic types in place of pointers: a **String** for a `BSTR*`, and arrays of **String** and **Long** for the counted arrays. **MapPropertyToPage** has no counterpart. diff --git a/docs/Reference/Contributed/OLEGuids/ISpecifyPropertyPages.md b/docs/Reference/Contributed/OLEGuids/ISpecifyPropertyPages.md index 673d3ac8..0af52e46 100644 --- a/docs/Reference/Contributed/OLEGuids/ISpecifyPropertyPages.md +++ b/docs/Reference/Contributed/OLEGuids/ISpecifyPropertyPages.md @@ -10,7 +10,9 @@ has_toc: false Lets an object list the class identifiers of its property pages. + **ISpecifyPropertyPages** has the interface identifier `B196B28B-BAB4-101A-B69C-00AA00341D07` and extends **stdole.IUnknown**. It is declared `[OleAutomation(False)]`. Microsoft Learn describes the Windows interface: [ISpecifyPropertyPages](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/nn-ocidl-ispecifypropertypages). + The declaration is complete: it has the one method of the Windows interface. The method is a **Sub**, so the HRESULT is hidden: a failure code raises a run-time error. diff --git a/docs/Reference/Contributed/OLEGuids/IStorage.md b/docs/Reference/Contributed/OLEGuids/IStorage.md index 3f22e8e3..cf97471e 100644 --- a/docs/Reference/Contributed/OLEGuids/IStorage.md +++ b/docs/Reference/Contributed/OLEGuids/IStorage.md @@ -10,7 +10,9 @@ has_toc: false Names the COM structured-storage interface, which holds streams and nested storages in a compound file; declared here as a stub with no methods. + **IStorage** has the interface identifier `0000000B-0000-0000-C000-000000000046` and extends **stdole.IUnknown**. It is declared `[OleAutomation(False)]`. Its description marks it as a stub and points to the tbShellLib package for the full version. Microsoft Learn describes the Windows interface: [IStorage](https://learn.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-istorage). + The stub declares none of the methods that create, open, copy, enumerate and commit streams and storages. The type exists so that other declarations of the package can name it: [**IRichEditOle.SaveCompleted**](IRichEditOle#savecompleted) takes one, and [**IRichEditOleCallback.GetNewStorage**](IRichEditOleCallback#getnewstorage) and [**IRichEditOleCallback.QueryInsertObject**](IRichEditOleCallback#queryinsertobject) pass one. Nothing can be called through a variable of this type. diff --git a/docs/Reference/Contributed/OLEGuids/IUnknownUnrestricted.md b/docs/Reference/Contributed/OLEGuids/IUnknownUnrestricted.md index 14adb82b..4c523212 100644 --- a/docs/Reference/Contributed/OLEGuids/IUnknownUnrestricted.md +++ b/docs/Reference/Contributed/OLEGuids/IUnknownUnrestricted.md @@ -25,10 +25,13 @@ See [IUnknown](../../../../Reference/COM-Interfaces/IUnknown) for the contract o Function QueryInterface(ByVal priid As LongPtr, ByRef ppvObject As LongPtr) As Long ``` + Asks the object for another interface. *priid* is the address of the interface identifier requested, such as the address of an [OLECLSID](OLECLSID) variable. *ppvObject* is passed by reference and receives the interface pointer. The result is the HRESULT: 0 (`S_OK`) on success, `&H80004002` (`E_NOINTERFACE`) when the interface is not supported. A pointer that **QueryInterface** returns comes with a reference, which the caller gives back with **Release**. + + > [!NOTE] BETA 1005 > When the interface is not supported, the COM contract sets *ppvObject* to 0. An object of a class that the project compiles --- a class, a form, a control --- returns `E_NOINTERFACE` and leaves the variable as it was. The runtime's own objects, such as a **Collection**, set it to 0, as every object of VB6 does. Set the variable to 0 before the call, and test the result rather than the pointer. @@ -51,6 +54,7 @@ Debug.Print p ' 0 Function AddRef() As Long ``` + Adds a reference to the object and returns the new reference count. ### Release @@ -61,9 +65,11 @@ Function Release() As Long ``` Removes a reference from the object and returns the new reference count. An object whose count reaches 0 may free itself. + ## Example + This function adds a reference and removes it again, and returns the object's reference count at that moment. The count includes the references that the procedure's own variables hold. ```tb check_build @@ -73,6 +79,7 @@ Private Function ReferenceCount(ByVal Obj As Object) As Long Return u.Release() End Function ``` + ## See Also diff --git a/docs/Reference/Contributed/OLEGuids/OLEACCELMSG.md b/docs/Reference/Contributed/OLEGuids/OLEACCELMSG.md index 33a30fc5..17308c67 100644 --- a/docs/Reference/Contributed/OLEGuids/OLEACCELMSG.md +++ b/docs/Reference/Contributed/OLEGuids/OLEACCELMSG.md @@ -8,9 +8,12 @@ has_toc: false # OLEACCELMSG type {: .no_toc } + A window message, with the layout of the Windows `MSG` structure. **OLEACCELMSG** is a user-defined type declared in the package's `OLEGuids` module, so both `OLEACCELMSG` and `OLEGuids.OLEACCELMSG` name it. It mirrors the Windows structure [MSG](https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-msg). + + ```tb inert=signature Public Type OLEACCELMSG hWnd As LongPtr diff --git a/docs/Reference/Contributed/OLEGuids/OLECADWORD.md b/docs/Reference/Contributed/OLEGuids/OLECADWORD.md index c19b7972..bfc1c2a1 100644 --- a/docs/Reference/Contributed/OLEGuids/OLECADWORD.md +++ b/docs/Reference/Contributed/OLEGuids/OLECADWORD.md @@ -8,9 +8,11 @@ has_toc: false # OLECADWORD type {: .no_toc } + A counted array of 32-bit values, with the layout of the Windows `CADWORD` structure. **OLECADWORD** is a user-defined type declared in the package's `OLEGuids` module, so both `OLECADWORD` and `OLEGuids.OLECADWORD` name it. It mirrors the Windows structure [CADWORD](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/ns-ocidl-cadword), which holds the number of elements of an array and the address of the array. + ```tb inert=signature Public Type OLECADWORD diff --git a/docs/Reference/Contributed/OLEGuids/OLECALPOLESTR.md b/docs/Reference/Contributed/OLEGuids/OLECALPOLESTR.md index f9dc1b3f..a25867d6 100644 --- a/docs/Reference/Contributed/OLEGuids/OLECALPOLESTR.md +++ b/docs/Reference/Contributed/OLEGuids/OLECALPOLESTR.md @@ -8,9 +8,11 @@ has_toc: false # OLECALPOLESTR type {: .no_toc } + A counted array of pointers to Unicode strings, with the layout of the Windows `CALPOLESTR` structure. **OLECALPOLESTR** is a user-defined type declared in the package's `OLEGuids` module, so both `OLECALPOLESTR` and `OLEGuids.OLECALPOLESTR` name it. It mirrors the Windows structure [CALPOLESTR](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/ns-ocidl-calpolestr), which holds the number of elements of an array and the address of the array. + ```tb inert=signature Public Type OLECALPOLESTR diff --git a/docs/Reference/Contributed/OLEGuids/OLECAUUID.md b/docs/Reference/Contributed/OLEGuids/OLECAUUID.md index dca35087..92f37116 100644 --- a/docs/Reference/Contributed/OLEGuids/OLECAUUID.md +++ b/docs/Reference/Contributed/OLEGuids/OLECAUUID.md @@ -8,9 +8,11 @@ has_toc: false # OLECAUUID type {: .no_toc } + A counted array of GUIDs, with the layout of the Windows `CAUUID` structure. **OLECAUUID** is a user-defined type declared in the package's `OLEGuids` module, so both `OLECAUUID` and `OLEGuids.OLECAUUID` name it. It mirrors the Windows structure [CAUUID](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/ns-ocidl-cauuid), which holds the number of elements of an array and the address of the array. + ```tb inert=signature Public Type OLECAUUID diff --git a/docs/Reference/Contributed/OLEGuids/OLECLSID.md b/docs/Reference/Contributed/OLEGuids/OLECLSID.md index 025a4198..dc35a431 100644 --- a/docs/Reference/Contributed/OLEGuids/OLECLSID.md +++ b/docs/Reference/Contributed/OLEGuids/OLECLSID.md @@ -8,9 +8,11 @@ has_toc: false # OLECLSID type {: .no_toc } + A class or interface identifier, with the layout of the Windows `GUID` structure. **OLECLSID** is a user-defined type declared in the package's `OLEGuids` module, so both `OLECLSID` and `OLEGuids.OLECLSID` name it. It mirrors the Windows structure [GUID](https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid), which the interface declarations use for a class identifier (CLSID) and for an interface identifier (IID) alike. + ```tb inert=signature Public Type OLECLSID diff --git a/docs/Reference/Contributed/OLEGuids/OLECONTROLINFO.md b/docs/Reference/Contributed/OLEGuids/OLECONTROLINFO.md index 404c1582..8a5f8d98 100644 --- a/docs/Reference/Contributed/OLEGuids/OLECONTROLINFO.md +++ b/docs/Reference/Contributed/OLEGuids/OLECONTROLINFO.md @@ -8,9 +8,11 @@ has_toc: false # OLECONTROLINFO type {: .no_toc } + The keyboard mnemonic table of an OLE control, with the layout of the Windows `CONTROLINFO` structure. **OLECONTROLINFO** is a user-defined type declared in the package's `OLEGuids` module, so both `OLECONTROLINFO` and `OLEGuids.OLECONTROLINFO` name it. It mirrors the Windows structure [CONTROLINFO](https://learn.microsoft.com/en-us/windows/win32/api/ocidl/ns-ocidl-controlinfo). + ```tb inert=signature Public Type OLECONTROLINFO diff --git a/docs/Reference/Contributed/OLEGuids/OLEDISPPARAMS.md b/docs/Reference/Contributed/OLEGuids/OLEDISPPARAMS.md index a224497e..bdc199a0 100644 --- a/docs/Reference/Contributed/OLEGuids/OLEDISPPARAMS.md +++ b/docs/Reference/Contributed/OLEGuids/OLEDISPPARAMS.md @@ -8,9 +8,11 @@ has_toc: false # OLEDISPPARAMS type {: .no_toc } + The arguments of a call through **IDispatch.Invoke**, with the layout of the Windows `DISPPARAMS` structure. **OLEDISPPARAMS** is a user-defined type declared in the package's `OLEGuids` module, so both `OLEDISPPARAMS` and `OLEGuids.OLEDISPPARAMS` name it. It mirrors the Windows structure [DISPPARAMS](https://learn.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-dispparams), which holds the addresses and the counts of the arguments and of the named arguments of a call. + ```tb inert=signature Public Type OLEDISPPARAMS @@ -37,7 +39,10 @@ End Type A variable that is not filled in holds only zeros, which is a call with no arguments. + An assignment to a property passes the new value as the only argument, named with the reserved identifier `DISPID_PROPERTYPUT` (-3): *cArgs* and *cNamedArgs* are 1, and *rgdispidNamedArgs* is the address of a **Long** holding -3. The [IDispatch](../../../../Reference/COM-Interfaces/IDispatch#the-dispparams-structure) page in the COM Interfaces section describes the structure further. + + ## Where it is used diff --git a/docs/Reference/Contributed/OLEGuids/OLEINPLACEFRAMEINFO.md b/docs/Reference/Contributed/OLEGuids/OLEINPLACEFRAMEINFO.md index 830b0135..ddf1174a 100644 --- a/docs/Reference/Contributed/OLEGuids/OLEINPLACEFRAMEINFO.md +++ b/docs/Reference/Contributed/OLEGuids/OLEINPLACEFRAMEINFO.md @@ -8,9 +8,11 @@ has_toc: false # OLEINPLACEFRAMEINFO type {: .no_toc } + The accelerator information of a container's frame window in in-place activation, with the layout of the Windows `OLEINPLACEFRAMEINFO` structure. **OLEINPLACEFRAMEINFO** is a user-defined type declared in the package's `OLEGuids` module, so both `OLEINPLACEFRAMEINFO` and `OLEGuids.OLEINPLACEFRAMEINFO` name it. It mirrors the Windows structure [OLEINPLACEFRAMEINFO](https://learn.microsoft.com/en-us/windows/win32/api/oleidl/ns-oleidl-oleinplaceframeinfo). + ```tb inert=signature Public Type OLEINPLACEFRAMEINFO diff --git a/docs/Reference/Contributed/OLEGuids/OLEPOINT.md b/docs/Reference/Contributed/OLEGuids/OLEPOINT.md index c5858001..91f88972 100644 --- a/docs/Reference/Contributed/OLEGuids/OLEPOINT.md +++ b/docs/Reference/Contributed/OLEGuids/OLEPOINT.md @@ -8,9 +8,11 @@ has_toc: false # OLEPOINT type {: .no_toc } + A point, with the layout of the Windows `POINT` structure. **OLEPOINT** is a user-defined type declared in the package's `OLEGuids` module, so both `OLEPOINT` and `OLEGuids.OLEPOINT` name it. It mirrors the Windows structure [POINT](https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-point). + ```tb inert=signature Public Type OLEPOINT diff --git a/docs/Reference/Contributed/OLEGuids/OLERECT.md b/docs/Reference/Contributed/OLEGuids/OLERECT.md index ee2bf7ea..8a073e1b 100644 --- a/docs/Reference/Contributed/OLEGuids/OLERECT.md +++ b/docs/Reference/Contributed/OLEGuids/OLERECT.md @@ -8,9 +8,11 @@ has_toc: false # OLERECT type {: .no_toc } + A rectangle, with the layout of the Windows `RECT` structure. **OLERECT** is a user-defined type declared in the package's `OLEGuids` module, so both `OLERECT` and `OLEGuids.OLERECT` name it. It mirrors the Windows structure [RECT](https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect), which holds the coordinates of the upper-left and lower-right corners of a rectangle. + ```tb inert=signature Public Type OLERECT diff --git a/docs/Reference/Contributed/OLEGuids/OLESIZE.md b/docs/Reference/Contributed/OLEGuids/OLESIZE.md index 8f9e56bb..85446519 100644 --- a/docs/Reference/Contributed/OLEGuids/OLESIZE.md +++ b/docs/Reference/Contributed/OLEGuids/OLESIZE.md @@ -8,9 +8,11 @@ has_toc: false # OLESIZE type {: .no_toc } + A width and a height, with the layout of the Windows `SIZE` structure. **OLESIZE** is a user-defined type declared in the package's `OLEGuids` module, so both `OLESIZE` and `OLEGuids.OLESIZE` name it. It mirrors the Windows structure [SIZE](https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-size). + ```tb inert=signature Public Type OLESIZE diff --git a/docs/Reference/Contributed/OLEGuids/index.md b/docs/Reference/Contributed/OLEGuids/index.md index 2d4e5a52..e601eaf9 100644 --- a/docs/Reference/Contributed/OLEGuids/index.md +++ b/docs/Reference/Contributed/OLEGuids/index.md @@ -22,6 +22,7 @@ Add the package in Project → References (Ctrl-T) → Available Packages. The i - **Call it.** Declare a variable of the interface type and assign an object to it. The assignment asks the object for that interface, and a call through the variable runs the object's method. - **Implement it.** Name it in an [**Implements**](../../../Core/Implements) statement in a class, and write each method. A class cannot implement an interface that has a `[PreserveSig]` member (see [How the declarations differ from Windows](#how-the-declarations-differ-from-windows)). + A name can be written bare (`IOleWindow`, `OLERECT`) or qualified with the package name (`OLEGuids.IOleWindow`, `OLEGuids.OLERECT`). The eleven structures are declared in a module named `OLEGuids`, which is why the qualified form works for them; the module has no page of its own. ```tb check_build @@ -30,7 +31,9 @@ Private Function InPlaceWindow(ByVal Obj As Object) As LongPtr Return w.GetWindow() End Function ``` + + > [!NOTE] 1.0.10.0 > The package has no declaration named **IDispatch**. In a project that references **stdole**, as every default project does, the name is **stdole**'s declaration. A call through it is late-bound by name, and its four methods are not reached (see [IDispatch](../../../../Reference/COM-Interfaces/IDispatch#the-stdole-declaration)). The package's [IDispatchUnrestricted](IDispatchUnrestricted) declares the four methods, and an object is assigned to a variable of that type like any other interface. @@ -47,12 +50,15 @@ End Function The interfaces that exist in Windows have their original interface identifiers, so an object written in another language can be called through them. The declarations differ from the C headers in these ways: + - **The HRESULT is hidden.** A **Sub**, and a **Function** that is not marked `[PreserveSig]`, returns nothing of the Windows method's HRESULT. A failure code raises a run-time error, and a success code other than `S_OK` is read with [**Err.LastHresult**](../../../Modules/ErrObject/LastHresult). Where the Windows method has an output parameter for its result, the **Function**'s result takes its place. - **`[PreserveSig]` members return the raw HRESULT** as a **Long**. A twinBASIC class cannot implement a member marked [**[PreserveSig]**](../../../Core/Attributes#preservesig): the compiler reports TB5004, *unable to match this implementation to its interface member*. The interfaces with such a member are for calling, not for implementing: [IUnknownUnrestricted](IUnknownUnrestricted), [IOleObject](IOleObject), [IOleInPlaceObjectWindowless](IOleInPlaceObjectWindowless), [IOleControl](IOleControl), [IOleControlSite](IOleControlSite), [IOleInPlaceActiveObject](IOleInPlaceActiveObject), [IOleInPlaceSite](IOleInPlaceSite), [IPerPropertyBrowsing](IPerPropertyBrowsing) and [IRichEditOle](IRichEditOle). A class sets [**Err.ReturnHResult**](../../../Modules/ErrObject/ReturnHResult) to return a specific HRESULT from a **Sub** it implements. - **Pointers are `LongPtr`.** A parameter that is a pointer to a structure or to an interface the package does not declare (a `RECT*`, a `MSG*`, an `IMoniker*`) is a **LongPtr**. Pass the address of a variable with [**VarPtr**](../../../Modules/Information/VarPtr), or `StrPtr` for a string. - **Some declarations are partial.** [IOleObject](IOleObject) declares the first nine of the 21 methods of the Windows interface. The four stub interfaces --- [IOleClientSite](IOleClientSite), [IOleInPlaceFrame](IOleInPlaceFrame), [IStorage](IStorage) and [IDataObject](IDataObject) --- declare no methods; their description says to use the tbShellLib package for the full version. A stub is useful for naming the type of a parameter or a variable, not for calling. - **Seven declarations are variants of a Windows interface.** [IUnknownUnrestricted](IUnknownUnrestricted) has no base interface, which makes the three methods of **IUnknown** callable. [IDispatchUnrestricted](IDispatchUnrestricted) declares the four methods of **IDispatch**, which the **stdole** declaration does not reach. [IEnumVARIANTUnrestricted](IEnumVARIANTUnrestricted) declares the **IEnumVARIANT** enumerator with **Long** counts. [IOleInPlaceActiveObjectVB](IOleInPlaceActiveObjectVB), [IOleControlVB](IOleControlVB), [IPerPropertyBrowsingVB](IPerPropertyBrowsingVB) and [IOleInPlaceObjectWindowlessVB](IOleInPlaceObjectWindowlessVB) are not Windows interfaces: they have Visual Basic-style signatures that a class can implement. + + Every interface except the four `VB` variants is declared `[OleAutomation(False)]`, so it is not marked as an Automation interface. The contract of each method is that of the Windows interface, which Microsoft Learn documents; each interface page links to it. ## Interfaces @@ -96,3 +102,7 @@ Every interface except the four `VB` variants is declared `[OleAutomation(False) - [OLEPOINT](OLEPOINT) -- a point, with the layout of the Windows **POINT** structure - [OLERECT](OLERECT) -- a rectangle, with the layout of the Windows **RECT** structure - [OLESIZE](OLESIZE) -- a width and a height, with the layout of the Windows **SIZE** structure + +## Test cases + +The notes and warnings of this section are checked by the test cases in [OLEGuids-tests.twinproj](OLEGuids-tests.twinproj), a twinBASIC project. It uses OLEGuids as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/TBMANLIB/Consts.md b/docs/Reference/Contributed/TBMANLIB/Consts.md index 292b1e8f..036ae4c8 100644 --- a/docs/Reference/Contributed/TBMANLIB/Consts.md +++ b/docs/Reference/Contributed/TBMANLIB/Consts.md @@ -8,7 +8,9 @@ has_toc: false # Consts module {: .no_toc } + Constants for HTTP requests: media types for the `Content-Type` header and the names of two header fields. [**cHttpClient**](cHttpClient) uses them; a program can use them to set the headers of a request. Eight constants, all **String**. + ```tb check_build check_run Debug.Print Consts.TOOLS_HTTP_CONTENT_TYPE_JSON @@ -58,7 +60,9 @@ The same media types are also declared in [**ToolsHttp**](ToolsHttp) with other ### TOOLS_HTTP_HEADER_FIELD_CONTENT_LENGHT {: .no_toc } + The text `Content-Lenght`. The name of the constant and its text both misspell `Content-Length`. [**cHttpClient**](cHttpClient) sends a header with this name, with the size of the body, on every request that has a body. Servers do not read it: WinHTTP adds the correct `Content-Length` header by itself, so a request carries both. + ### TOOLS_HTTP_HEADER_FIELD_CONTENT_TYPE {: .no_toc } diff --git a/docs/Reference/Contributed/TBMANLIB/TBMAN.md b/docs/Reference/Contributed/TBMANLIB/TBMAN.md index 42ae8ce0..59b2f1a8 100644 --- a/docs/Reference/Contributed/TBMANLIB/TBMAN.md +++ b/docs/Reference/Contributed/TBMANLIB/TBMAN.md @@ -20,7 +20,9 @@ Debug.Print TypeName(TBMAN.Json) ' cJson ``` + The variables are declared `As New`, so each object is created the first time it is used. Because the instance is shared, its state is shared: the document in [**Json**](#json), the data in [**Csv**](#csv) and the settings in [**Logs**](#logs) are the same wherever the program reads them. A program that needs two documents, or logs with different settings, makes its own objects with `New`. + The name of the package can be put in front of the name of the module, as `TBMANLIB.TBMAN.Json`, when another package the project references also has a module named **TBMAN**. @@ -102,7 +104,9 @@ Returns a version number as text. Syntax: **TBMAN.Version** **As String** + The text is `v`, then `App.Major`, a period, `App.Minor`, a period and `App.Build`. The `App` is that of the program that uses the package, not that of the package: in a program whose version is 2.3.4.7 (major, minor, revision, build), the result is `v2.3.7`. The revision number is not in the text, and the version of the package, 1.0.0.29, is not in it either. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB/Tools.md b/docs/Reference/Contributed/TBMANLIB/Tools.md index 38c99d24..748fa59e 100644 --- a/docs/Reference/Contributed/TBMANLIB/Tools.md +++ b/docs/Reference/Contributed/TBMANLIB/Tools.md @@ -8,6 +8,7 @@ has_toc: false # Tools module {: .no_toc } + The module that holds the shared instances **ToolsStr** and **ToolsStream**. Its two public variables let a program write `ToolsStr.TrimEx(...)` and `ToolsStream.LoadFileAsText(...)` with no `New`, as it could when these two were modules of the package. The module has no procedures. ```tb check_build check_run @@ -23,6 +24,7 @@ Debug.Print TypeName(ToolsStream) ``` The two names can be written alone, as in the first line of the example, because the members of the module are found without the name of the module. The names `TBMANLIB.Tools.ToolsStr` and `TBMANLIB.ToolsStr` also work, and are needed when another package the project references has a member of the same name. + * TOC {:toc} diff --git a/docs/Reference/Contributed/TBMANLIB/ToolsArray.md b/docs/Reference/Contributed/TBMANLIB/ToolsArray.md index e030d1bd..77c09903 100644 --- a/docs/Reference/Contributed/TBMANLIB/ToolsArray.md +++ b/docs/Reference/Contributed/TBMANLIB/ToolsArray.md @@ -47,6 +47,7 @@ Syntax: **ToolsArray.DeArray** ( *Arr* **,** *OutVars* ) *OutVars* : *required* Any number of variables, in a **ParamArray**. The first element of *Arr* is assigned to the first variable, the second element to the second variable, and so on. An argument that is left out, as in `a, , c`, is skipped, and its variable is not changed. + The copying stops at the last element of *Arr* or at the last variable, whichever comes first. The source says that an array whose lower bound is not 0 is not handled. ```tb check_build check_run @@ -57,6 +58,7 @@ Debug.Print a & c ' Output: ' xz ``` + ### Extend {: .no_toc } @@ -65,7 +67,11 @@ Unfinished. The source marks it with a note that means *to be continued*. Syntax: **ToolsArray.Extend** ( *Vars* ) **=** *Value* -A property that is meant to assign the elements of the array *Value* to the elements of the array *Vars*. It assigns into the array *Vars* itself, so variables from which *Vars* was built, as in `Extend(Array(a, c)) = ...`, are not changed. +A property that is meant to assign the elements of the array *Value* to the elements of the array *Vars*. + + +> [!NOTE] 1.0.0.29 +> The property assigns into the array *Vars* itself, so variables from which *Vars* was built, as in `Extend(Array(a, c)) = ...`, are not changed. ### FindByteArray {: .no_toc } @@ -80,7 +86,9 @@ Syntax: **ToolsArray.FindByteArray** ( *Arr* **,** *Keyword* ) **As Long** *Keyword* : *required* A **Byte** array, the bytes to find. + Returns the position of the first place in *Arr* where all the bytes of *Keyword* follow one another, counted from the lower bound of *Arr*. Returns -1 if there is no such place. The comparison is exact, byte by byte. + ```tb check_build check_run Dim Hay() As Byte @@ -106,7 +114,9 @@ Syntax: **ToolsArray.GetArrayLength** ( *Arr* ) **As Long** *Arr* : *required* An array of **Variant**, such as the result of **Array**. An array of another type, such as the **String** array that **Split** returns, is not accepted by the parameter. + Returns the upper bound less the lower bound, plus 1. An array that has not been dimensioned raises error 9. + ```tb check_build check_run Debug.Print ToolsArray.GetArrayLength(Array(10, 20, 30)) @@ -127,7 +137,9 @@ Syntax: **ToolsArray.GetIndexByValue** ( *Arr* **,** *Value* ) **As Long** *Value* : *required* A **String**, the value to find. Each element is compared with it by the `=` operator. + Returns the index of the first element that equals *Value*, in the numbering of the array, or -1 if there is none. + ```tb check_build check_run Debug.Print ToolsArray.GetIndexByValue(Array("a", "b", "c"), "b") @@ -147,7 +159,9 @@ Syntax: **ToolsArray.IsArrayEmpty** ( *Arr* ) **As Boolean** *Arr* : *required* A **Variant** that holds an array. + Returns **True** when the lower bound of *Arr* is greater than its upper bound, as in `Array()`, and also when the bounds cannot be read, as for an array that has not been dimensioned. + ```tb check_build check_run Dim Empty1() As Long @@ -178,6 +192,7 @@ Syntax: **ToolsArray.SliceByteArray** ( *Arr* **,** *StartPos* [ **,** *EndPos* Returns a new **Byte** array, from 0, with the bytes from *StartPos* to *EndPos*. The positions are not checked against the array. The function copies with **RtlMoveMemory**, so a position outside the array makes it read memory that does not belong to the array. + > [!WARNING] 1.0.0.29 > When *EndPos* is equal to *StartPos*, the function does not return the one byte. It returns an array with one element, holding 0, as it does when *EndPos* is less than *StartPos*; the test in the source is a strict "less than". [**SliceLongArray**](#slicelongarray) does not have this error. @@ -191,6 +206,7 @@ Debug.Print UBound(Part) & " " & Part(0) ' 0 0 ``` + ```tb check_build check_run Dim Bytes() As Byte Dim Part() As Byte @@ -204,6 +220,7 @@ Debug.Print ToolsStr.ToString(Part) ' Hello ' World ``` + ### SliceLongArray {: .no_toc } @@ -221,7 +238,9 @@ Syntax: **ToolsArray.SliceLongArray** ( *Arr* **,** *StartPos* [ **,** *EndPos* *EndPos* : *optional* A **Long**, the position of the last element to copy, which is included. The default of -1 means the end of the array. + Returns a new **Long** array, from 0, with the elements from *StartPos* to *EndPos*. When *EndPos* is less than *StartPos*, the result is an array with one element holding 0. As for [**SliceByteArray**](#slicebytearray), the positions are not checked against the array. + ```tb check_build check_run Dim Longs() As Long @@ -255,6 +274,7 @@ Syntax: **ToolsArray.SplitByteArray** ( *Arr* **,** *position* [ **,** *Offset* *Offset* : *optional* A **Long**, 0 by default, the number of bytes to leave out between the two parts: the second part starts at *position* + *Offset*. + Returns a **Variant** array of two elements. Element 0 is a **Byte** array of the first *position* bytes, and element 1 is a **Byte** array of the bytes from *position* + *Offset* to the end. If *position* is at or past the end of *Arr*, the first part is *Arr* and the second part is an array with one element holding 0. The second part is also that array when *position* + *Offset* is at or past the end. ```tb check_build check_run @@ -277,8 +297,11 @@ Debug.Print UBound(P) & " " & P(0) ' two ' 0 0 ``` + + A *position* of 0 raises the error *Unspecified error* (-2147467259), because the first part would be dimensioned with an upper bound of -1. + ### SplitByteArrayByKeyword {: .no_toc } @@ -296,7 +319,13 @@ Syntax: **ToolsArray.SplitByteArrayByKeyword** ( *ByteArray* **,** *Keyword* [ * *SplitCount* : *optional* A **Long**, 0 by default. When it is greater than 0, the array is split at no more than that many occurrences, and the rest of the array is the last part. -Returns a **Variant** array, from 0, of **Byte** arrays: the parts between the occurrences of the keyword, in order. The keyword itself is in none of them. If the keyword does not occur, there is one part, a copy of the array. A keyword at the very end leaves a last part that is an array with one element holding 0, and not an empty array. A keyword at the very start raises the error of a *position* of 0 in [**SplitByteArray**](#splitbytearray), *Unspecified error* (-2147467259). + +Returns a **Variant** array, from 0, of **Byte** arrays: the parts between the occurrences of the keyword, in order. The keyword itself is in none of them. If the keyword does not occur, there is one part, a copy of the array. + + + +> [!WARNING] 1.0.0.29 +> A keyword at the very start raises the error of a *position* of 0 in [**SplitByteArray**](#splitbytearray), *Unspecified error* (-2147467259). A keyword at the very end leaves a last part that is an array with one element holding 0, and not an empty array. ```tb check_build check_run Dim Bytes() As Byte @@ -340,7 +369,9 @@ Syntax: **ToolsArray.StringToByteArray** ( *St* ) **As Byte()** *St* : *required* A **String**. + Returns the bytes of *St* in the ANSI code page of the system, one byte for each character of a single-byte code page, with **StrConv** and **vbFromUnicode**. A character that the code page does not have is not kept as written. To get UTF-8 bytes, use [**ToolsUtf8.Encode**](ToolsUtf8#encode). + ```tb check_build check_run Debug.Print UBound(ToolsArray.StringToByteArray("abc")) diff --git a/docs/Reference/Contributed/TBMANLIB/ToolsCN.md b/docs/Reference/Contributed/TBMANLIB/ToolsCN.md index bf848704..0fb4de9c 100644 --- a/docs/Reference/Contributed/TBMANLIB/ToolsCN.md +++ b/docs/Reference/Contributed/TBMANLIB/ToolsCN.md @@ -27,8 +27,11 @@ Syntax: **ToolsCN.GetPinYin** ( *CnChar* ) **As String** For each character, the code takes the number that **Asc** returns for it. A number from 1 to 159 is an ASCII character and is copied. A number from -20319 to -10247 is the code of a Chinese character in the GBK code page, which **Asc** returns as a negative number on a computer whose code page is a Chinese one; the function looks up the syllable of the table with the greatest start code that does not exceed that number, and returns it with a capital first letter (`Zhong`). Any other character is dropped. The syllables are joined with no separator. + An empty string returns an empty string. + + > [!NOTE] 1.0.0.29 > The function raises error -2147467259, *Unspecified error*, for any text that holds a character with a code from 1 to 159, such as an English letter, a digit or a space. The line that copies such a character is written `G = Chr(Num)()`, with a pair of parentheses after the call to **Chr**, which returns a **Variant**, and that raises the error when it runs. On a computer whose code page is not a Chinese one, a Chinese character becomes `?`, code 63, so it raises the error too. Any other character is dropped with no error: `GetPinYin(ChrW(233))` returns an empty string. The same pair of parentheses after the call to **Asc** on the line before has no effect. The loop over the characters also uses the variable `i` for its own search through the table, so on a Chinese code page it would skip characters after a Chinese character. diff --git a/docs/Reference/Contributed/TBMANLIB/ToolsDic.md b/docs/Reference/Contributed/TBMANLIB/ToolsDic.md index 5ae9f284..9eca7b18 100644 --- a/docs/Reference/Contributed/TBMANLIB/ToolsDic.md +++ b/docs/Reference/Contributed/TBMANLIB/ToolsDic.md @@ -34,7 +34,13 @@ Syntax: **ToolsDic.DeepCopy** ( *srcDic* ) **As Scripting.Dictionary** *srcDic* : *required* A **Scripting.Dictionary**. It is not changed. -Returns a new dictionary with the same keys and values. Only the first level is copied: where a value is an object, such as a dictionary or a collection, the copy holds the same object, not a copy of it. The source says that a recursive copy is still to be written. The copy has the default comparison mode, whatever the mode of the source. + +Returns a new dictionary with the same keys and values. The copy has the default comparison mode, whatever the mode of the source. + + + +> [!WARNING] 1.0.0.29 +> Only the first level is copied. Where a value is an object, such as a dictionary or a collection, the copy holds the same object, not a copy of it, so a change made through the copy reaches the source. The source says that a recursive copy is still to be written. ```tb check_build check_run Dim A As New Scripting.Dictionary @@ -63,7 +69,13 @@ Syntax: **ToolsDic.FromWwwFormUrlencoded** ( *Content* **,** *Dic* ) **As Boolea *Dic* : *required* A **Scripting.Dictionary** that receives the pairs. A name that it already holds gets the new value, and the other entries stay. -The name and the value of each pair lose the spaces at their ends. A pair with an empty name is skipped. A pair with no `=` gets an empty value. A value that holds an `=` is cut at it: in `a=b=c` the value is `b`. The text is not percent-decoded, so `%20` and `+` stay as written, and a value that holds `&` cannot be expressed. The function always returns **True**. The source says that values that are arrays are still to be handled. + +The name and the value of each pair lose the spaces at their ends. A pair with an empty name is skipped. A pair with no `=` gets an empty value. The function always returns **True**. The source says that values that are arrays are still to be handled. + + + +> [!WARNING] 1.0.0.29 +> A value that holds an `=` is cut at it: in `a=b=c` the value is `b`. The text is not decoded, so `%20` and `+` stay as written, and a value that holds `&` cannot be expressed. ### OverWrite {: .no_toc } @@ -81,8 +93,11 @@ Syntax: **ToolsDic.OverWrite** ( *DistDic* **,** *srcDic* [ **,** *OnlyKey* ] ) *OnlyKey* : *optional* A **Boolean**, **True** by default. When it is **True**, only the keys that *DistDic* already has are written, so the structure of *DistDic* stays as it is. When it is **False**, every key of *srcDic* is written, and the missing ones are added. + Where a value of *srcDic* is an object, the method calls itself for that key with the two objects, so the nested dictionary of *DistDic* is updated in place and not replaced. A value that is not an object is assigned. + + > [!NOTE] 1.0.0.29 > With *OnlyKey* set to **False**, a nested dictionary of *srcDic* whose key *DistDic* does not have makes the recursive call receive an empty value for the missing key, where it needs a dictionary. The call raises error 424, *Object required*, and *DistDic* keeps the key that was looked up, with the value **Empty**. @@ -112,6 +127,7 @@ Debug.Print A.Exists("m") & " " & IsEmpty(A("m")) ' True True ``` + ```tb check_build check_run Dim A As New Scripting.Dictionary Dim B As New Scripting.Dictionary @@ -129,6 +145,7 @@ Debug.Print A("z") ' 1 20 False ' 30 ``` + ### TowLevelDicAssign {: .no_toc } @@ -149,7 +166,9 @@ Syntax: **ToolsDic.TowLevelDicAssign** ( *Dic* **,** *Lv1Name* **,** *Lv2Name* * *Value* : *required* A **Variant**, the value to store under *Lv2Name*. + The name has *Tow* for *Two*. If the inner dictionary exists, the value replaces the value of *Lv2Name*, or adds it. If it does not, a new dictionary is made, it receives the value, and *Dic* receives the new dictionary. + ```tb check_build check_run Dim D As New Scripting.Dictionary diff --git a/docs/Reference/Contributed/TBMANLIB/ToolsFso.md b/docs/Reference/Contributed/TBMANLIB/ToolsFso.md index e7990213..b280e69a 100644 --- a/docs/Reference/Contributed/TBMANLIB/ToolsFso.md +++ b/docs/Reference/Contributed/TBMANLIB/ToolsFso.md @@ -20,7 +20,7 @@ Debug.Print ToolsFso.MakeNewFileFulPath("C:\data\a.json", "old") ' a_old.json ``` -[**cJson**](cJson) uses **AutoCompleteFullPath** and **AutoMakeDir** to find and prepare the file for its [**SaveTo**](cJson#saveto), and [**cCsv**](cCsv) uses **AutoMakeDir**. +[**cJson**](cJson) uses **AutoCompleteFullPath** and **AutoMakeDir** to find and prepare the file for its [**SaveTo**](cJson#saveto), and [**cIni**](cIni) uses **AutoMakeDir** for its [**SaveTo**](cIni#saveto). * TOC {:toc} @@ -40,10 +40,13 @@ Syntax: **ToolsFso.AutoCompleteFullPath** ( *Path* [ **,** *IsFile* ] ) **As Str *IsFile* : *optional* A **Boolean**, **False** by default. Set it to **True** when *Path* names a file. When it is **False**, the result ends with a backslash, as the path of a folder does. + If *Path* does not contain the two characters `:\` anywhere, it is taken to be relative: `App.Path`, a backslash and *Path* are joined, and the result goes through [**ClearSpan**](#clearspan). A path that contains `:\` goes through **ClearSpan** alone. In a debug run, `App.Path` is the folder of the project. + + > [!NOTE] 1.0.0.29 -> Only `:\` marks a path as complete. A path with a drive letter written with forward slashes, such as `C:/data/a.json`, and a network path such as `\\server\share\a.json` do not contain it, so they are given the folder of the program in front of them and come out wrong. +> Only `:\` marks a path as complete. A path with a drive letter written with forward slashes, such as `C:/data/a.json`, and a network path such as `\\server\share\a.json` do not contain it, so they are given the folder of the program in front of them and come out wrong. [**ClearSpan**](#clearspan) also turns the two backslashes at the start of a network path into one. ### AutoMakeDir {: .no_toc } @@ -58,10 +61,10 @@ Syntax: **ToolsFso.AutoMakeDir** ( *Path* [ **,** *IsFile* ] ) **As String** *IsFile* : *optional* A **Boolean**, **False** by default. When it is **True**, *Path* names a file, and the folders that are created are those of its parent folder. When it is **False**, the whole of *Path* is taken to be a folder. + All the folders of the path that do not exist are created, by the Windows function **MakeSureDirectoryPathExists**. The function returns the path of the folder after [**ClearSpan**](#clearspan), so it ends with a backslash. The result of the Windows function is not tested: if a folder cannot be created, no error is raised. That function takes ANSI text, so a folder name that has characters outside the system code page is not created as written. -> [!WARNING] 1.0.0.29 -> With *IsFile* left at **False**, the last name of the path becomes a folder: `AutoMakeDir("C:\data\log.txt")` creates a folder named `log.txt`. Pass **True** for a file. [**cCsv.SaveTo**](cCsv#saveto) calls the function without it. +With *IsFile* left at **False**, the whole path is a folder, so the last name of the path becomes one: `AutoMakeDir("C:\data\log.txt")` creates a folder named `log.txt`. Pass **True** for a file. ```tb check_build check_run Dim Base As String @@ -77,6 +80,7 @@ RmDir Base ' True ' True ``` + ### ClearSpan {: .no_toc } @@ -91,7 +95,9 @@ Syntax: **ToolsFso.ClearSpan** ( *Path* [ **,** *IsFile* ] ) **As String** *IsFile* : *optional* A **Boolean**, **False** by default. When it is **False**, a backslash is added at the end of *Path* first. + The function makes four replacements, one after the other, on the whole string: each pair of backslashes becomes one, each pair of forward slashes becomes one backslash, each backslash followed by a forward slash becomes one backslash, and each remaining forward slash becomes a backslash. Each replacement is made once, from left to right, so a run of four backslashes becomes two, not one. A network path loses one of the two backslashes at its start. + ### Inst {: .no_toc } @@ -114,8 +120,11 @@ Syntax: **ToolsFso.MakeNewFileFulPath** ( *FileSrc* **,** *AppendFix* [ **,** *J *JoinStr* : *optional* A **String**, `_` by default. It is placed between the base name and *AppendFix*. + Returns the base name of *FileSrc*, then *JoinStr*, then *AppendFix*, a period and the extension of *FileSrc*: `MakeNewFileFulPath("C:\data\a.json", "old")` returns `a_old.json`. + + > [!NOTE] 1.0.0.29 > Despite the word *FulPath* in the name, the result holds no folder. The function reads the folder of *FileSrc* and does not use it. Join the result to the folder with `&`. A file name with no extension gives a result that ends with a period. diff --git a/docs/Reference/Contributed/TBMANLIB/ToolsHttp.md b/docs/Reference/Contributed/TBMANLIB/ToolsHttp.md index 05a08cb4..7e8577b1 100644 --- a/docs/Reference/Contributed/TBMANLIB/ToolsHttp.md +++ b/docs/Reference/Contributed/TBMANLIB/ToolsHttp.md @@ -29,7 +29,9 @@ Debug.Print ToolsHttp.MapMethodName(ReqPut) ## Constants + The eight constants repeat the ones in [**Consts**](Consts) under shorter names. All are **String**. + ### C_CONTENT_TYPE_FORM_MULTIPART {: .no_toc } @@ -89,7 +91,9 @@ The content types that [**cHttpClient.SetRequestContentType**](cHttpClient#setre ### EnumRequestMethod {: .no_toc } + The methods of a request, for [**cHttpClient.Fetch**](cHttpClient#fetch) and [**cHttpClient.Send**](cHttpClient#send). The value is the position of the name in the list `ANY`, `POST`, `GET`, `PUT`, `DELETE`, `OPTIONS` that [**MapMethod**](#mapmethod) and [**MapMethodName**](#mapmethodname) use. + | Member | Value | Method | |:-------|:------|:-------| @@ -114,7 +118,9 @@ Syntax: **ToolsHttp.AddToQueryString** ( *Url* **,** *QS* ) **As String** *QS* : *required* A **String**, the text to add. + Returns *Url* followed by `?ver=` and *QS*, or by `&ver=` and *QS* when *Url* already has a `?`. The text `ver=` is always inserted, so *QS* is the value of a parameter named `ver`; see the warning at [**cHttpClient.RequestDataQuery**](cHttpClient#requestdataquery), which calls the function. + ```tb check_build check_run Debug.Print ToolsHttp.AddToQueryString("http://host/p", "a=1") @@ -134,7 +140,9 @@ Syntax: **ToolsHttp.MakeContent** ( *Dic* ) **As String** *Dic* : *required* A **Scripting.Dictionary**. + Returns the keys and items as `key=value` pairs joined by `&`, in the order of the dictionary, or an empty string for an empty dictionary. Nothing is encoded. Compare [**ToolsDic.FromWwwFormUrlencoded**](ToolsDic#fromwwwformurlencoded), which does the reverse. + ### MapMethod {: .no_toc } @@ -167,7 +175,13 @@ Syntax: **ToolsHttp.ParseContent** ( *Content* **,** *Obj* ) **As Boolean** *Obj* : *required* A **Scripting.Dictionary**. It receives the pairs; an earlier item of the same key is replaced and the other items stay. -Each pair is cut at its first `=` and both parts are trimmed. A pair without `=` gets an empty value, and a pair whose name is empty is skipped. The text is not decoded: `%20` stays as it is. The function returns **False** always, whatever it did, because it never assigns its result; read the dictionary instead. + +Each pair is cut at its first `=` and both parts are trimmed. A pair without `=` gets an empty value, and a pair whose name is empty is skipped. The text is not decoded: `%20` stays as it is. + + + +> [!WARNING] 1.0.0.29 +> The function returns **False** always, whatever it did, because it never assigns its result. Read the dictionary instead. ```tb check_build check_run Dim Form As New Scripting.Dictionary @@ -193,7 +207,13 @@ Syntax: **ToolsHttp.ParseKeyValue** ( *Content* **,** *Obj* ) **As Boolean** *Obj* : *required* A **Scripting.Dictionary**. It receives one item per line. -Each line is cut at its first colon and both parts are trimmed, so the value of a line that has more colons is cut: `Date: Tue, 13 Oct 2026 18:00:00 GMT` is stored as `Tue, 13 Oct 2026 18`. A line without a colon becomes an item with the empty text, and an empty line (the last line of a header block is empty) becomes the item with the empty key. Like [**ParseContent**](#parsecontent), the function returns **False** always. [**cHttpClient**](cHttpClient) calls it to fill [**ResponseHeaders**](cHttpClient#responseheaders). + +Each line is cut at its first colon and both parts are trimmed. A line without a colon becomes an item with the empty text, and an empty line (the last line of a header block is empty) becomes the item with the empty key. [**cHttpClient**](cHttpClient) calls the function to fill [**ResponseHeaders**](cHttpClient#responseheaders). + + + +> [!WARNING] 1.0.0.29 +> The value of a line that has more colons is cut at its second one: `Date: Tue, 13 Oct 2026 18:00:00 GMT` is stored as `Tue, 13 Oct 2026 18`, and `Location: http://host/path` as `http`. Like [**ParseContent**](#parsecontent), the function returns **False** always. ```tb check_build check_run Dim Head As New Scripting.Dictionary @@ -215,7 +235,9 @@ Decodes `%XX` escapes, assuming the Chinese code page GB2312. Syntax: **ToolsHttp.UrlDecode** ( *Url* ) **As String** + *Url* is a **String**. A `%XX` below hex 80 becomes the character with that code. A `%XX` of hex 80 or above is combined with the `%XX` after it into one double-byte character, and the pair is converted with **Chr$**. The function does not turn `+` into a space. + That conversion works only where the ANSI code page is a double-byte one, such as GBK. On a computer with a single-byte code page, such as 1252, any escape of hex 80 or above raises error 5, *Invalid procedure call or argument*, even a single one such as `%E9`. @@ -226,6 +248,7 @@ Meant to decode a URL written in UTF-8. Syntax: **ToolsHttp.UrlDecodeUtf8** ( *Url* ) **As String** + > [!NOTE] 1.0.0.29 > The function raises error 13, *Type mismatch*, for every text that contains a `%`, and returns the text correctly only when it has none (a `+` becomes a space). The code compares a **Long** with an empty string at its first `%`. @@ -238,6 +261,7 @@ Syntax: **ToolsHttp.UrlEncodUtf8** ( *szInput* ) **As String** *szInput* is a **String** (a **Variant**). A character below hex 80 is copied as it is, so a space, `&` and `=` stay: only the other characters are escaped. A character from hex 1000 up becomes three escapes, `%E4%B8%AD` for the character U+4E2D. The function returns an empty string for an empty text. + > [!WARNING] 1.0.0.29 > A character from hex 80 to hex FFF, such as `é`, is written with only one `%`: `é` becomes `%C3A9` and U+0080 becomes `%C280`. The second byte has no `%` of its own, so the result is not a valid escape, and no decoder gets the character back. Characters from hex 1000 up, such as the Chinese ones, are right. @@ -258,7 +282,9 @@ Encodes a text with `%XX` escapes in the ANSI code page. Syntax: **ToolsHttp.UrlEncode** ( *strURL* ) **As String** + *strURL* is a **String**, passed by reference. A letter of the English alphabet and the characters `-`, `,`, `.`, `/` and `0` to `9` are copied. Every other character becomes `%` and the hex code of its byte: `%20` for a space, `%5F` for `_`. A character with two bytes in the code page (a Chinese character in GBK) becomes two escapes. On a computer with a single-byte code page, a character such as `é` becomes one escape, `%E9`. + ### Test {: .no_toc } diff --git a/docs/Reference/Contributed/TBMANLIB/ToolsHttpMimeType.md b/docs/Reference/Contributed/TBMANLIB/ToolsHttpMimeType.md index 367301a7..d3467117 100644 --- a/docs/Reference/Contributed/TBMANLIB/ToolsHttpMimeType.md +++ b/docs/Reference/Contributed/TBMANLIB/ToolsHttpMimeType.md @@ -35,13 +35,17 @@ Returns the media type of a file name. Syntax: **ToolsHttpMimeType.MapMimeType** ( *FilePath* ) **As String** *FilePath* -: *required* A **String**, a file name or a path. The extension is the text after the last dot, so a name or a path with no dot is its own extension. The comparison respects letter case, and the table holds lower-case extensions: `report.PDF` is not found. A leading dot is fine, so `.html` and `html` give the same result. +: *required* A **String**, a file name or a path. The extension is the text after the last dot, so a name or a path with no dot is its own extension. A leading dot is fine, so `.html` and `html` give the same result. + Returns the media type of the extension in the table below. For an extension that is not in the table it returns `application/octet-stream`. An empty string raises error 9, *Subscript out of range*. + + > [!NOTE] 1.0.0.29 -> The extension `woff` is in the table with no media type, so it returns an empty string, not `application/x-font-woff`; `woff2` returns `application/x-font-woff`. The table also lacks `htm`, `mp3`, `ogg` and `wasm`, which return `application/octet-stream`, and `java` returns the text `text/x-java-source,java`. +> The extension is compared with its letter case, and the table holds lower-case extensions, so `report.PDF` is not found and gives `application/octet-stream`. The extension `woff` is in the table with no media type, so it returns an empty string, not `application/x-font-woff`; `woff2` returns `application/x-font-woff`. The table also lacks `htm`, `mp3`, `ogg` and `wasm`, which return `application/octet-stream`, and `java` returns the text `text/x-java-source,java`. + | Extension | Media type | |:----------|:-----------| | `7z` | `application/x-7z-compressed` | @@ -143,6 +147,7 @@ Returns the media type of the extension in the table below. For an extension tha | `xslt` | `application/xslt+xml` | | `xul` | `application/vnd.mozilla.xul+xml` | | `zip` | `application/zip` | + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB/ToolsMath.md b/docs/Reference/Contributed/TBMANLIB/ToolsMath.md index cf28e12a..66c02741 100644 --- a/docs/Reference/Contributed/TBMANLIB/ToolsMath.md +++ b/docs/Reference/Contributed/TBMANLIB/ToolsMath.md @@ -39,7 +39,13 @@ Syntax: **ToolsMath.Ceil** ( *Num* [ **,** *Dot* ] ) **As Double** *Dot* : *optional* A **Long**, the number of decimal places to keep, 0 by default. A negative value rounds to tens, hundreds and so on: `Ceil(1234, -2)` is 1300. -The result is the smallest number with *Dot* decimal places that is not less than *Num*: `Ceil(1.231, 2)` is 1.24 and `Ceil(-1.5)` is -1. The function multiplies *Num* by 10 to the power *Dot*, rounds up with **Int**, and divides again. The arithmetic is in binary floating point, so a number that has no exact binary form can multiply to a value a little above a whole number and round up one step too far: `Ceil(0.07, 2)` returns 0.08, because 0.07 multiplied by 100 is a little more than 7. + +The result is the smallest number with *Dot* decimal places that is not less than *Num*: `Ceil(1.231, 2)` is 1.24 and `Ceil(-1.5)` is -1. The function multiplies *Num* by 10 to the power *Dot*, rounds up with **Int**, and divides again. + + + +> [!WARNING] 1.0.0.29 +> The arithmetic is in binary floating point, so a number that has no exact binary form can multiply to a value a little above a whole number and round up one step too far: `Ceil(0.07, 2)` returns 0.08, because 0.07 multiplied by 100 is a little more than 7. ### GetRandRange {: .no_toc } @@ -54,7 +60,9 @@ Syntax: **ToolsMath.GetRandRange** ( *a* **,** *b* ) **As Long** *b* : *required* A **Long**, the largest value. It is passed by reference and is not changed. If it is less than *a*, the result is not in the range. + Each call runs **Randomize** with no argument, which seeds the generator from the system timer, then returns `Int((b - a + 1) * Rnd() + a)`. The numbers come from the generator of the language and are not suitable for passwords, keys or any other use where they have to be unpredictable. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB/ToolsUtf8.md b/docs/Reference/Contributed/TBMANLIB/ToolsUtf8.md index 23097aa8..e4beceb9 100644 --- a/docs/Reference/Contributed/TBMANLIB/ToolsUtf8.md +++ b/docs/Reference/Contributed/TBMANLIB/ToolsUtf8.md @@ -40,9 +40,11 @@ Syntax: **ToolsUtf8.Decode** ( *Utf* ) **As String** *Utf* : *required* A **Byte** array that holds UTF-8 text. It is passed by reference and is not changed. + Returns the text as a **String**. A byte order mark (EF BB BF) at the start of the bytes is not removed: it becomes the character U+FEFF at the start of the string. If *Utf* has not been dimensioned, the function returns an empty string. + ### DecodeToByteArray {: .no_toc } @@ -56,6 +58,7 @@ Syntax: **ToolsUtf8.DecodeToByteArray** ( *Utf* ) **As Byte()** The conversion produces two bytes for each character of the text, but the array that the function returns is resized to a length in bytes equal to the number of characters, so it holds the first half of the converted bytes only. + > [!WARNING] 1.0.0.29 > The result is cut off without an error. For the five UTF-8 bytes of the text `caf` followed by the letter e with an acute accent, which is four characters and eight UTF-16 bytes, the array has four bytes. To get the whole text as UTF-16 bytes, convert with [**Decode**](#decode) and assign the string to a **Byte** array. @@ -69,7 +72,9 @@ Debug.Print UBound(Half) + 1 ' 4 ``` + If *Utf* has not been dimensioned, the call raises error 9. + ### Encode {: .no_toc } @@ -81,6 +86,7 @@ Syntax: **ToolsUtf8.Encode** ( *UCS* ) **As Byte()** *UCS* : *required* A **String**. + Returns a **Byte** array that starts at index 0 and holds the text in UTF-8, with no byte order mark and no terminating zero byte. For an empty string the function returns an array that has not been dimensioned, so `UBound` of the result raises error 9. @@ -94,6 +100,7 @@ Debug.Print Err.Number ' Output: ' 9 ``` + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB/cCsv.md b/docs/Reference/Contributed/TBMANLIB/cCsv.md index 6b3325b0..0d06a448 100644 --- a/docs/Reference/Contributed/TBMANLIB/cCsv.md +++ b/docs/Reference/Contributed/TBMANLIB/cCsv.md @@ -50,8 +50,8 @@ Kill Path - **LoadFrom** reads a line at a time and splits it at the character in [**Delimiter**](#delimiter), `,` by default. **SaveTo** joins the fields with the same character and ends each line with a carriage return and line feed. - A field may be enclosed in double quotes. Inside quotes the delimiter is part of the field, and a pair of double quotes stands for one. **LoadFrom** removes the enclosing quotes. **SaveTo** adds them to a field that holds the delimiter or a double quote, and to no other. -- **LoadFrom** removes the spaces at both ends of every field, also of a quoted one. **SaveTo** does not add or remove any. -- A field cannot hold a line break. **LoadFrom** reads one line at a time, so a quoted field that continues on the next line is read as two lines. **SaveTo** writes a line break inside a field as it is, and the file then has more lines than the table. +- **LoadFrom** removes the spaces at both ends of every field, also of a quoted one; see the note at **LoadFrom**. **SaveTo** does not add or remove any. +- A field cannot hold a line break; see the warning at **SaveTo**. - The files are opened as ANSI text, in the code page of the system, not as Unicode or UTF-8, and **SaveTo** cannot write a character that the code page does not have (see the warning at **SaveTo**). - A line that is empty or holds only spaces is skipped. Its number is still counted, so the numbers of the lines in **Data** have a gap there. @@ -137,9 +137,13 @@ Syntax: *object*.**value** ( *LineNumber* **,** *ColumnNumber* ) *ColumnNumber* : *required* A **Long**, the number of the column. + Returns the field as a **Variant**. When the line or the column does not exist, the result is the integer 1 (the value of **vbNull**, which the source uses where it means a null) and [**LastError**](#lasterror) holds a Chinese text that says which of the two is missing. + -The class declares a **Set** accessor for the property and no **Let** accessor, and neither works. `T.value(2, 3) = "x"` and `Set T.value(2, 3) = Nothing` each compile and raise an error (-2147467259 and -2147352567), and the field is not changed. To change a field, use [**SetCurrentLineData**](#setcurrentlinedata) or write into the dictionary of the line. + +> [!NOTE] 1.0.0.29 +> The class declares a **Set** accessor for the property and no **Let** accessor, and neither works. `T.value(2, 3) = "x"` and `Set T.value(2, 3) = Nothing` each compile and raise an error (-2147467259 and -2147352567), and the field is not changed. To change a field, use [**SetCurrentLineData**](#setcurrentlinedata) or write into the dictionary of the line. ## Methods @@ -153,8 +157,11 @@ Syntax: *object*.**DeleteColumn** ( [ *ColumnNumber* ] ) **As Boolean** *ColumnNumber* : *optional* A **Long**, the column to remove. If it is left out, or 0, the last column of the current line is removed. + The function needs line 1 to exist; when it does not, it returns **False** and sets [**LastError**](#lasterror). Otherwise it removes the column from every line, its title and its width, and returns **True**. The other columns keep their numbers. + + > [!WARNING] 1.0.0.29 > Only the last column can be removed without damage. A column in front of others leaves a gap in the column numbers, which the other methods do not expect. [**SaveTo**](#saveto) writes the columns for the numbers 1 to the new count of columns, so it writes an empty field in the place of the missing one and leaves out the last column: after removing column 2 of three, a line `a,b,c` is saved as `a,`. [**value**](#value) with the old numbers still finds the fields that remain. @@ -194,8 +201,9 @@ Syntax: *object*.**GetLine** ( *LineNumber* [ **,** *LineDic* ] ) **As Boolean** : *required* A **Long**, the key of the line in [**Data**](#data). *LineDic* -: *optional* A **Scripting.Dictionary** variable that is meant to receive the line. The variable that is passed is not changed, whether it is **Nothing** or holds a dictionary. Use [**CurrentLineDic**](#currentlinedic). +: *optional* A **Scripting.Dictionary** variable that receives the line. It is set to the same dictionary as [**CurrentLineDic**](#currentlinedic), whether it was **Nothing** or held a dictionary. + Sets [**CurrentLineDic**](#currentlinedic) and [**CurrentLineNumber**](#currentlinenumber) and returns **True**. When the line does not exist, returns **False** and sets [**LastError**](#lasterror). ```tb check_build check_run @@ -214,6 +222,7 @@ Debug.Print T.CurrentLineNumber ' False ' 2 ``` + ### LoadFrom {: .no_toc } @@ -228,10 +237,19 @@ Syntax: *object*.**LoadFrom** ( *FilePath* [ **,** *IsFirstHead* ] ) **As cCsv** *IsFirstHead* : *optional* A **Boolean**, **True** by default. It is meant to say that the first line holds the titles. The function does not use it: the titles are never taken from the file. + Returns the object itself. The file is read as described in *The file format*. **Data** is replaced when the file has been opened, with one line for each line of the file that is not blank, numbered by its place in the file. Lines with more fields than the first line are cut to its count, and shorter ones are filled with empty strings. [**HeadTitle**](#headtitle) and [**HeadTitleWidth**](#headtitlewidth) get an entry for each column that has none. + + An error, such as a file that does not exist, is not raised. It is caught, [**LastError**](#lasterror) is set to its number, a `#`, the description, a colon and the path, and **Data** is not changed. + + + +> [!NOTE] 1.0.0.29 +> **LoadFrom** removes the spaces at both ends of every field, also of a quoted one. In RFC 4180 a space is part of a field, so a field that begins or ends with a space does not come back as it was written. + > [!NOTE] 1.0.0.29 > A blank line in the file leaves a gap in the line numbers of **Data**: for a file of three lines whose second is blank, the keys are 1 and 3, and the count is 2. [**NewLine**](#newline) numbers the new line with the count plus 1, which here is 3, a line that exists, and raises error 457, *This key is already associated with an element of this collection*. Lines can then not be added until the gap is gone. @@ -274,8 +292,11 @@ Syntax: *object*.**NewColumn** ( *Title* [ **,** *Width* ] [ **,** *Content* ] ) *Content* : *optional* A **String**, the field for every other line, an empty string by default. With **IsFirstLineToHead** set to **False**, it is the field of the first line as well. + The new column gets the number after the last column of each line. A table with no lines gets no column, and no title. + + > [!WARNING] 1.0.0.29 > The function always returns **False**, also when it has added the column: the result is never set. A program cannot use it to see whether the call worked. @@ -302,9 +323,11 @@ Syntax: *object*.**NewLine** ( [ *FieldsData* ] ) **As Boolean** *FieldsData* : *optional* Any number of values, in a **ParamArray**: the fields of the line. A **Null** or **Empty** value becomes an empty string. As the only argument, a **Scripting.Dictionary** with the column numbers as keys is copied, with its items, and the copy becomes the line; later changes to the dictionary do not reach the table. + The line gets the number after the count of the lines in [**Data**](#data). It is filled to the number of columns of the first line, or cut to it, and becomes the current line ([**CurrentLineDic**](#currentlinedic), [**CurrentLineNumber**](#currentlinenumber)). **HeadTitle** and **HeadTitleWidth** get an entry for each column that has none. Without arguments the line has one empty field, filled up to the width of the table. The first line of a table fixes the number of columns for all the others. Returns **True**. An error is raised when the number is taken; see the note at [**LoadFrom**](#loadfrom). + ```tb check_build check_run Dim T As New cCsv @@ -330,7 +353,9 @@ Syntax: *object*.**Reset** ( [ *OnlyData* ] ) *OnlyData* : *optional* A **Boolean**, **False** by default. With **True**, only the table is emptied; the settings stay as they are. + Empties [**Data**](#data), [**HeadTitle**](#headtitle), [**HeadTitleWidth**](#headtitlewidth) and [**CurrentLineDic**](#currentlinedic), and sets [**CurrentLineNumber**](#currentlinenumber) to 0. Without *OnlyData*, it also sets [**Delimiter**](#delimiter), [**HasHeadTitle**](#hasheadtitle), [**HeadTitleWidthDefault**](#headtitlewidthdefault), [**HeadTitleWidthMax**](#headtitlewidthmax) and [**IsFirstLineToHead**](#isfirstlinetohead) to their values at the start. [**LastError**](#lasterror) is not cleared. + ### SaveTo {: .no_toc } @@ -342,11 +367,19 @@ Syntax: *object*.**SaveTo** ( *FilePath* ) **As Boolean** *FilePath* : *required* A **String**, the file to write. A file that exists is replaced. The folder must exist: the function does not create it. + Writes the lines of [**Data**](#data) in the order of their keys, each as described in *The file format*, and returns **True**. When an error occurs, such as a folder that does not exist, it returns **False** and [**LastError**](#lasterror) holds its number, a `#` and the description. The titles in [**HeadTitle**](#headtitle) are not written. + + + +> [!WARNING] 1.0.0.29 +> A field that holds a line break is written as it is, and the file then has more lines than the table. **LoadFrom** reads one line at a time, so it reads a line break inside a field, quoted or not, as the end of the line. A table with such a field does not come back as it was written, and no error is raised. RFC 4180 allows a line break in a quoted field. + > [!WARNING] 1.0.0.29 > The file is created, or emptied if it exists, before the first field is written. When a field holds a character that the code page of the system does not have, such as `ChrW(&H4E2D)` on a system with the Western code page 1252, writing it fails with error 5, the function returns **False**, and the file is left with no content. The previous content of a file is lost. + ```tb check_build check_run Dim T As New cCsv Dim Path As String @@ -369,6 +402,7 @@ Kill Path ' plain;a,b;"say ""hi""" ' "x;y";a,b; pad ``` + ### SetCurrentLineData {: .no_toc } @@ -386,7 +420,9 @@ Syntax: *object*.**SetCurrentLineData** ( *ColumnNumber* **,** *Dat* [ **,** *Li *LineDic* : *optional* A **Scripting.Dictionary** variable. The parameter has no effect: the function works on [**CurrentLineDic**](#currentlinedic), and the variable that is passed is not changed. + Returns **True** when the field was changed. It returns **False** and sets [**LastError**](#lasterror) when the current line has no such column, which includes the case that the table has no current line. + ### SetHeadTitleWidth {: .no_toc } @@ -401,7 +437,9 @@ Syntax: *object*.**SetHeadTitleWidth** ( *ColumnNumber* **,** *ColumnWidth* ) ** *ColumnWidth* : *required* A **Long**, the width. A value above [**HeadTitleWidthMax**](#headtitlewidthmax) is replaced by it. + Returns **True** when the width was stored. Returns **False** and sets [**LastError**](#lasterror) when the column has no entry. + ### SetHeadTitleWidthByDic {: .no_toc } @@ -413,8 +451,11 @@ Syntax: *object*.**SetHeadTitleWidthByDic** ( *Dic* ) **As Boolean** *Dic* : *required* A **Scripting.Dictionary** with column numbers as keys and widths as items. + For each key of *Dic* that has an entry in [**HeadTitleWidth**](#headtitlewidth), the width is stored; other keys are skipped. The widths are not limited by [**HeadTitleWidthMax**](#headtitlewidthmax). + + > [!WARNING] 1.0.0.29 > The function always returns **False**, also when it has stored the widths: the result is never set. @@ -431,7 +472,9 @@ Syntax: *object*.**SetLine** ( *LineNumber* **,** *LineDic* ) **As Boolean** *LineDic* : *required* A **Scripting.Dictionary** with the column numbers as keys. + Fills *LineDic* or cuts it to the number of columns of the first line, stores it in **Data** under *LineNumber* in place of the old line, and returns **True**. The dictionary itself is stored, not a copy, so later changes to it change the table. When the line does not exist, the function returns **False** and sets [**LastError**](#lasterror). [**CurrentLineDic**](#currentlinedic) is **Nothing** afterwards, and until the next [**GetLine**](#getline) or [**NewLine**](#newline) [**SetCurrentLineData**](#setcurrentlinedata) returns **False**. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB/cHttpClient.md b/docs/Reference/Contributed/TBMANLIB/cHttpClient.md index 439c0683..a6938cad 100644 --- a/docs/Reference/Contributed/TBMANLIB/cHttpClient.md +++ b/docs/Reference/Contributed/TBMANLIB/cHttpClient.md @@ -30,6 +30,7 @@ Debug.Print Reply.Item("id") ## How a request runs + [**Fetch**](#fetch) does the work for every **Send** method. It opens the request, sets the headers, builds the body, sends it and, unless the request is asynchronous, waits for the answer. In order: 1. It empties [**LastError**](#lasterror) and raises error 500 if the URL is empty. @@ -40,7 +41,9 @@ Debug.Print Reply.Item("id") 6. It encodes the body as UTF-8 when [**RequestChartSet**](#requestchartset) contains `utf`, and in the ANSI code page otherwise, sets the extra header `Content-Lenght` (see [Consts](Consts#tools_http_header_field_content_lenght)), and sends it. 7. Unless the request is asynchronous, it waits [**RequestTimeOut**](#requesttimeout) seconds (at least 5) for the answer, raises the status as an error when it is 400 to 599, and reads the response headers into [**ResponseHeaders**](#responseheaders). 8. It empties the form values, the JSON document and the query values, clears [**DebugStart**](#debugstart) and makes the next request synchronous. The headers are kept. + + A failed request does the last step too, sets **LastError** and raises the error again. The error is raised with the number of the cause: the HTTP status for a 4xx or 5xx answer, 900 for a timeout, or the number that WinHTTP reports (such as -2147012867, *a connection with the server could not be established*). Several error texts of the class are written in Chinese. The ones that are not a status or a WinHTTP text are these: @@ -53,10 +56,13 @@ Several error texts of the class are written in Chinese. The ones that are not a | 58 | the file exists | **DownloadFile**, **FinishDownloadFile** | | 53 | the file does not exist | **UploadFile** | | 1 | no download path was found, or the file could not be read | **FinishDownloadFile**, **UploadFile** | + + > [!WARNING] 1.0.0.29 > The class ignores every certificate error: an expired certificate, one from an unknown authority and one issued for another name are all accepted. An HTTPS request therefore encrypts the traffic, but does not prove who the server is, and a program that sends a password or a key with it can send it to an impostor. + > [!NOTE] BETA 1005 > The compiler refuses an assignment written through a public dictionary field of an object, such as `Http.RequestHeaders("X-Key") = "1"`, with TB5090. Write the default member out: `Http.RequestHeaders.Item("X-Key") = "1"`. Reading `Http.RequestHeaders("X-Key")` is accepted. @@ -67,6 +73,7 @@ Several error texts of the class are written in Chinese. The ones that are not a A **String**, the media type of the body. It is empty in a new object. [**SetRequestContentType**](#setrequestcontenttype) sets it from a number, and the constants of [**Consts**](Consts) and [**ToolsHttp**](ToolsHttp) are the usual values. [**Fetch**](#fetch) reads it to choose the body and, when the headers have no `Content-Type`, to set that header. + > [!WARNING] 1.0.0.29 > The `Content-Type` header is put into [**RequestHeaders**](#requestheaders) by the first request of an object and stays there. A later change of **RequestContentType** changes the body that is built, but not the header: after a form request, `SetRequestContentType JsonString` sends the JSON text with the header `application/x-www-form-urlencoded`. Set the item as well, `Http.RequestHeaders.Item("Content-Type") = "application/json"`, or use a new object. @@ -75,7 +82,9 @@ A **String**, the media type of the body. It is empty in a new object. [**SetReq ### RequestChartSet {: .no_toc } + A **String**, the character set of the request body. It is `utf-8` in a new object. If the name contains `utf`, in any letter case, the body is encoded as UTF-8; any other name encodes it in the ANSI code page of the computer, whatever the name is: `gbk` does not select GBK, and a character that the code page lacks becomes `?`. The field does not set a header. + ### RequestTimeOut {: .no_toc } @@ -92,21 +101,27 @@ A **Scripting.Dictionary** of the request headers: the keys are the header names A **Scripting.Dictionary** of the values of a form: the keys are the field names. When the content type is `application/x-www-form-urlencoded` and the call gives no body, [**Fetch**](#fetch) sends `name=value` pairs joined by `&`, in the order they were added. It is emptied after each request. + > [!WARNING] 1.0.0.29 > The names and values are written as they are, without URL encoding. A value with a space, `&`, `=` or `%` produces a body that a server reads as other fields: `Form.Item("city") = "A B&C"` is sent as `city=A B&C`. Such a value has to be escaped by the program before it is added; [**ToolsHttp.UrlEncode**](ToolsHttp#urlencode) does that for text in the ANSI code page. ### RequestDataJson {: .no_toc } + A [**cJson**](cJson), the document that is sent when the content type is exactly `application/json` and the call gives no body. It is encoded with the characters themselves and not as `\uXXXX` escapes (the *FromUnicode* argument of [**cJson.Encode**](cJson#encode) is **True**), so the warning on that argument applies: a text value with a backslash followed by `u` raises error 13. The document is emptied after each request. If the document is empty, the request has no body and goes with `Content-Length: 0`. + ### RequestDataQuery {: .no_toc } + A **Scripting.Dictionary** of query values. When it has items, [**Fetch**](#fetch) adds them to the URL, after a `?` or, if the URL has one, a `&`. It is emptied after each request. + + > [!WARNING] 1.0.0.29 > The pairs are written after the fixed name `ver`: the URL gets `?ver=` followed by the pairs joined by `&`. With the items `a` = 1 and `b` = 2, the request goes to `?ver=a=1&b=2`: a server reads a parameter `ver` whose value is `a=1`, and a parameter `b`. The name of the first item is lost, and no item is sent as a parameter of that name. The values are not URL encoded either. Put the query values in the URL text instead, which the call sends as given. @@ -123,7 +138,9 @@ An array of **Byte**. No member reads it. ### EnableRedirects {: .no_toc } + A **Boolean**, **True** in a new object. When it is **True**, WinHTTP follows the redirects of the server (status 301, 302, 307 and so on) and the object holds the final answer. [**AllowRedirects**](#allowredirects) sets it. The value stays for every later request. + ### MaxRedirects {: .no_toc } @@ -135,17 +152,15 @@ A **Long**, 10 in a new object. Only [**FollowRedirect**](#followredirect) reads ### ResponseHeaders {: .no_toc } + A **Scripting.Dictionary** of the headers of the last answer, keys compared without regard to letter case. It is filled by each successful synchronous request and by [**UploadFile**](#uploadfile). It is not emptied: the headers of an earlier answer stay, and the headers of a later answer overwrite those of the same name. An answer with an error status, and an asynchronous request, leave it unchanged. -Each header line is split at its first colon and both parts are trimmed, so the value is cut at its own colon: - -| Line | Key | Value | -|:-----|:----|:------| -| `Content-Type: text/plain` | `Content-Type` | `text/plain` | -| `Date: Tue, 13 Oct 2026 18:00:00 GMT` | `Date` | `Tue, 13 Oct 2026 18` | -| `Location: http://host/path` | `Location` | `http` | - The text after a blank line at the end becomes an item with the empty key and the empty value. A header that occurs twice (`Set-Cookie`) keeps its last value. + + + +> [!WARNING] 1.0.0.29 +> Each header line is split at its first colon and both parts are trimmed, so the value is cut at its own colon: `Date: Tue, 13 Oct 2026 18:00:00 GMT` gives the value `Tue, 13 Oct 2026 18`, and `Location: http://host/path` gives `http`. A line with no further colon, such as `Content-Type: text/plain`, gives its whole value. ### Cookies {: .no_toc } @@ -170,7 +185,9 @@ A **Boolean**. When it is set to **True** before a request, [**DebugInfo**](#deb ### DebugInfo {: .no_toc } + A [**cJson**](cJson). **Fetch** replaces it with an empty document at the start of every request. When [**DebugStart**](#debugstart) was set, it holds an object `Request` with `IsAsync`, `Method`, `Url`, `Body`, `Headers` (the request headers), `TimeOut` and `ChartSet`, and, for a synchronous request, an object `Response` with `Status`, `StatusText`, `Headers` and `Content`. A request that failed adds an object `Error` with `Number`, `Source` and `Description`. + ```tb check_build Dim Http As New cHttpClient @@ -185,7 +202,11 @@ Debug.Print Http.DebugInfo.Item("Response")("Status") ### OnResponseFinished {: .no_toc } -An event with no arguments. The class declares it, and nothing raises it: a handler is never called. A program that sends an asynchronous request has to ask for the answer itself. +An event with no arguments. It is meant to be raised when the answer of an asynchronous request has arrived. + + +> [!NOTE] 1.0.0.29 +> The class declares the event, and nothing raises it: a handler is never called. A program that sends an asynchronous request has to ask for the answer itself. ## Methods @@ -211,7 +232,9 @@ Syntax: *object*.**Async** ( *Bool* ) **As cHttpClient** *Bool* : *required* A **Boolean**. + Returns the object itself. The setting is for the next request only. An asynchronous [**Fetch**](#fetch) sends the request and returns at once: it does not wait, does not check the status and does not read [**ResponseHeaders**](#responseheaders). The answer is read later with a **Return** method, which raises an error (*the data necessary to complete this operation is not yet available*) until the answer is there. There is no status property and [**OnResponseFinished**](#onresponsefinished) is never raised, so the program finds out that the answer has come by trying. + ### DownloadFile {: .no_toc } @@ -229,8 +252,11 @@ Syntax: *object*.**DownloadFile** ( *url* **,** *savePath* [ **,** *Overwrite* ] *Overwrite* : *optional* A **Boolean**, **True** by default. With **False**, a file that exists makes the call raise error 58. + Sends the request with [**SendGet**](#sendget) (so a 4xx or 5xx status is raised as in **Fetch**), raises an error for a status other than 200, and writes the body to the file with [**cToolsStream.SaveFileAsBinary**](cToolsStream#savefileasbinary). It returns **True**. The test for **Overwrite** comes after the request, so the answer has been received when error 58 is raised; the file is not changed. + + > [!WARNING] 1.0.0.29 > The call returns **True** also when the file could not be written, for example for a folder that does not exist: **SaveFileAsBinary** returns **False** there, and **DownloadFile** does not look at it. Check that the file exists after the call. @@ -241,7 +267,9 @@ Starts a download without waiting for it. Syntax: *object*.**DownloadFileAsync** ( *url* **,** *savePath* [ **,** *Overwrite* ] ) **As cHttpClient** + The arguments are those of [**DownloadFile**](#downloadfile). The method sends an asynchronous GET request, then stores the path and the overwrite choice in [**RequestHeaders**](#requestheaders) as the items `X-Download-Path` and `X-Download-Overwrite`. [**FinishDownloadFile**](#finishdownloadfile) reads them and writes the file. + The two items stay in **RequestHeaders**, so every later request of the object sends them to the server, with the local path in them. Remove them when the download is done: `Http.RequestHeaders.Remove "X-Download-Path"`, and the same for `X-Download-Overwrite`. @@ -270,7 +298,9 @@ Writes the file of a download that [**DownloadFileAsync**](#downloadfileasync) s Syntax: *object*.**FinishDownloadFile** ( ) **As Boolean** + Reads the answer, raises an error for a status other than 200 and, if the file exists and the overwrite choice was **False**, error 58. It then writes the file and returns **True**. While the answer has not arrived, the call raises the WinHTTP error *the data necessary to complete this operation is not yet available* (-2147483638); a program calls it again after a short wait. If the item `X-Download-Path` is not in [**RequestHeaders**](#requestheaders), it raises error 1. Like [**DownloadFile**](#downloadfile), it returns **True** also when the file could not be written. + ### FollowRedirect {: .no_toc } @@ -284,6 +314,7 @@ Syntax: *object*.**FollowRedirect** ( [ *RedirectMethod* ] ) **As cHttpClient** Reads the address with [**GetRedirectUrl**](#getredirecturl) and sends a GET request to it (for 307 and 308, the given method). Each call counts, and error 310 is raised when the count exceeds [**MaxRedirects**](#maxredirects); [**ResetRedirectCount**](#resetredirectcount) sets the count to 0. The method needs [**EnableRedirects**](#enableredirects) set to **False** with [**AllowRedirects**](#allowredirects), or no redirect answer is left to follow. It returns the object itself, and does nothing if the last answer had no `Location` header. + > [!NOTE] 1.0.0.29 > The method cannot send a request. The address comes from [**ResponseHeaders**](#responseheaders), which cuts a value at its first colon, so `http://host/path` is read as `http`, and WinHTTP raises error -2147012890, *the URL does not use a recognized protocol*. A `Location` header with a relative address, such as `/path`, has no colon and is read whole, but WinHTTP refuses it as well. A program that has to follow a redirect leaves **EnableRedirects** at **True**, so that WinHTTP follows it. @@ -294,7 +325,9 @@ Returns the `Location` header of the last answer. Syntax: *object*.**GetRedirectUrl** ( ) **As String** + Returns the item `Location` of [**ResponseHeaders**](#responseheaders), or an empty string when there is none. The value is cut at its first colon (see [ResponseHeaders](#responseheaders)), so an absolute address comes back as `http` or `https`. + ### MapRequestContentType {: .no_toc } @@ -319,7 +352,9 @@ Returns the body of the last answer as bytes. Syntax: *object*.**ReturnBody** ( ) **As Byte()** + Raises the WinHTTP error -2147012795, *this method cannot be called until the Send method has been called*, when the object has sent no request. + ### ReturnJson {: .no_toc } @@ -334,7 +369,9 @@ Syntax: *object*.**ReturnJson** ( [ *IsUtf8* ] [ **,** *IsConvert* ] ) **As cJso *IsConvert* : *optional* A **Boolean**. + Takes the text from [**ReturnText**](#returntext) with the same arguments and decodes it with [**cJson.Decode**](cJson#decode), in a new **cJson**. A text that is not JSON raises error 10001 from the parser. + ### ReturnStream {: .no_toc } @@ -372,7 +409,9 @@ Sends a DELETE request. Syntax: *object*.**SendDelete** ( *url* [ **,** *Body* ] ) **As cHttpClient** + The same as **Fetch** with **ReqDelete**. + ### SendGet {: .no_toc } @@ -430,7 +469,9 @@ Syntax: *object*.**SetCookies** ( *Value* ) **As cHttpClient** *Value* : *required* A **String**, the text of the header, such as `a=1; b=2`. Nothing happens when it is empty. + Stores the text as the item `Cookie` of [**RequestHeaders**](#requestheaders), where it stays for every later request. It returns the object itself. WinHTTP adds the cookies it has received from the server to the header it sends, so a cookie can appear twice. + ### SetRequestContentType {: .no_toc } @@ -445,7 +486,9 @@ Syntax: *object*.**SetRequestContentType** ( *ReqType* [ **,** *ContentType* ] ) *ContentType* : *optional* A **String**. Not used. + Returns the new text. Read the warning at [**RequestContentType**](#requestcontenttype): the call after a first request does not change the header. + ### ShowPage {: .no_toc } @@ -478,10 +521,13 @@ Syntax: *object*.**UploadFile** ( *url* **,** *filePath* [ **,** *fieldName* ] [ *additionalFormData* : *optional* A **Scripting.Dictionary** of more form fields, as names and text values. + The body has a part with the file and a part for each item of *additionalFormData*. The file is always sent with the type `application/octet-stream` and the file name of the path. The boundary is `----WebKitFormBoundary` followed by a random number. The call waits [**RequestTimeOut**](#requesttimeout) seconds for the answer and raises the status of a 400 or higher as an error, and error 900 for a timeout. It reads [**ResponseHeaders**](#responseheaders) and returns the object itself. The call sends neither the items of **RequestHeaders** nor the extra header of [**Fetch**](#fetch), and it follows redirects as **EnableRedirects** says. It leaves two things behind: **RequestContentType** keeps the multipart value, and **RequestHeaders** gets an item `Content-Length` with the size of the body. A later request therefore has the multipart content type, as the warning at [**RequestContentType**](#requestcontenttype) says. The `Content-Length` item does not reach the server with a later GET request. + + > [!NOTE] 1.0.0.29 > A call that fails with a status or a timeout raises the right number but a general text: *Application-defined or object-defined error* for a status, *Automation error* for error 900. The real text, such as `上传失败: 404#Not Found` (*the upload failed*), is in [**LastError**](#lasterror). diff --git a/docs/Reference/Contributed/TBMANLIB/cIni.md b/docs/Reference/Contributed/TBMANLIB/cIni.md index 58f3284d..a740e1d5 100644 --- a/docs/Reference/Contributed/TBMANLIB/cIni.md +++ b/docs/Reference/Contributed/TBMANLIB/cIni.md @@ -35,6 +35,7 @@ Kill Path ## The file format + The files are opened as ANSI text, in the code page of the system, not as Unicode or UTF-8. The text that **SaveTo** writes, and that **LoadFrom** reads, looks like this: ``` @@ -50,6 +51,7 @@ Back=white - A blank line and a line that starts with a semicolon are skipped. A line that starts with `#` is not a comment: it is read as a key if it holds an `=`, and skipped if it does not. - A line that has no `=` and is not a section is skipped. - **LoadFrom** puts the keys that come before the first section under a section whose name is the empty string. + ## Properties @@ -77,7 +79,9 @@ Syntax: *object*.**Section** ( *Key* ) **As Scripting.Dictionary** *Key* : *required* A **String**, the name of the section. Names are compared with letter case. + The dictionary is the one that [**Root**](#root) holds for the name, so a value assigned to it is stored in **Root**: `Cfg.Section("Window")("Width") = "800"`. A name that is not in **Root** is added to it, with an empty dictionary, by the call itself, even when the program only reads. + ```tb check_build check_run Dim Cfg As New cIni @@ -107,7 +111,9 @@ Syntax: *object*.**LoadFrom** ( *Path* ) **As cIni** Returns the object itself, so a call can be followed by a member: `Cfg.LoadFrom(Path).Section("Window")`. + The call empties **Root** first, so afterwards it holds only what the file holds. If the file cannot be opened, **Root** has been emptied, the call raises no error, and [**LastError**](#lasterror) says why. + ```tb check_build check_run Dim Cfg As New cIni @@ -149,7 +155,9 @@ Syntax: *object*.**SaveTo** ( [ *Path* ] ) **As Boolean** *Path* : *optional* A **String**, the file to write. The extension `.ini` is added to a name that does not end with it. A call without a path uses the name `.ini`. A file that exists is replaced. The folders of the path are created if they are missing. + Returns **True** when the file was written. It returns **False** when **Root** has no entries, or when an error occurred; [**LastError**](#lasterror) then says why. + ```tb check_build check_run Dim Cfg As New cIni diff --git a/docs/Reference/Contributed/TBMANLIB/cJson.md b/docs/Reference/Contributed/TBMANLIB/cJson.md index 30b710e4..7e10b52f 100644 --- a/docs/Reference/Contributed/TBMANLIB/cJson.md +++ b/docs/Reference/Contributed/TBMANLIB/cJson.md @@ -34,6 +34,7 @@ Debug.Print J.Encode() ## How the document is held + The top level of a JSON text is an object or an array, and a **cJson** keeps each in a field of its own: - [**RootItem**](#rootitem) is a **Scripting.Dictionary**, which holds a JSON object. Its keys are the names of the object, in the order they were added, and letter case matters. @@ -42,6 +43,7 @@ The top level of a JSON text is an object or an array, and a **cJson** keeps eac Inside the document, a nested object is another **Scripting.Dictionary** and a nested array is another **Collection**. A JSON string is a **String**, a number is a **Double**, `true` and `false` are **Boolean** values and `null` is **Null**. A number whose text is 16 or more characters long (17 or more with a decimal point) is returned as a **String** instead, so that no digits are lost; [`ToolsJsonVba.JsonOptions`](ToolsJsonVba#jsonoptions) changes that. The parsing and writing are done by [**ToolsJsonVba**](ToolsJsonVba), the VBA-JSON code. A new **cJson** holds an empty object. Only one of the two fields is meant to hold data at a time. [**Root**](#root) returns **RootItems** when it has items and **RootItem** when it has none, and [**Encode**](#encode) with no argument writes **RootItem** when it has entries and **RootItems** only when it has none. + ## Properties @@ -85,7 +87,11 @@ Syntax: *object*.**Items** ( *Index* ) [ **=** *value* ] Reading returns a **Variant**, an object for a nested object or array. If *Index* is greater than the number of items, the result is **Empty**. A position of 0 or less is not checked, and the **Collection** raises its own error. -Assigning a value, with or without **Set**, adds it as a new last item of **RootItems**, whatever *Index* is: the item at *Index* is not replaced, and an *Index* beyond the end does not leave a gap. A value that is an object, such as a **Scripting.Dictionary** or another **Collection**, is added as a nested object or array. +Assigning a value, with or without **Set**, adds it as a new item at the end of **RootItems**; see the warning below. + + +> [!WARNING] 1.0.0.29 +> An assignment to **Items** does not replace the item at *Index*. It adds the value as a new last item, whatever *Index* is, and an *Index* beyond the end does not leave a gap: `J.Items(1) = 5` on `[1,2]` gives `[1,2,5]`. A value that is an object, such as a **Scripting.Dictionary** or another **Collection**, is added as a nested object or array. ```tb check_build check_run Dim J As New cJson @@ -105,12 +111,14 @@ Debug.Print J.Items(2) ### LastError {: .no_toc } + A **String** that is meant to hold the text of the last error. [**Clear**](#clear), [**Decode**](#decode) and [**Encode**](#encode) set it to an empty string, and no member of the class stores a message in it: [**Decode**](#decode) raises its errors instead. The field stays empty unless a program assigns to it. ### LastSuccess {: .no_toc } Returns **True** when [**LastError**](#lasterror) is empty. Because nothing stores a message in **LastError**, it is always **True**. + ### Root {: .no_toc } @@ -159,9 +167,11 @@ Empties the document. Syntax: *object*.**Clear** + Sets [**RootText**](#roottext) and [**LastError**](#lasterror) to empty strings, and releases [**RootItem**](#rootitem) and [**RootItems**](#rootitems). + -Call **Clear** before [**Decode**](#decode) when the object held another document. **Decode** replaces only the field that matches the text it reads, so the array that an earlier call stored stays in **RootItems** after an object is decoded, and [**Root**](#root) keeps returning the array. +Call **Clear** before [**Decode**](#decode) when the object held another document; see the note at **Decode**. ### Decode {: .no_toc } @@ -175,8 +185,17 @@ Syntax: *object*.**Decode** ( *Text* ) **As cJson** Returns the object itself, so a call can be followed by a member: `J.Decode(Text).Item("name")`. -A text that begins with `{` replaces [**RootItem**](#rootitem), and a text that begins with `[` replaces [**RootItems**](#rootitems). An empty text changes nothing in the document. +A text that begins with `{` replaces [**RootItem**](#rootitem), and a text that begins with `[` replaces [**RootItems**](#rootitems). + + +An empty text changes nothing in the document. + + + +> [!NOTE] 1.0.0.29 +> Only the field that matches the text is replaced, and the other one keeps what it held. After an array and then an object, [**Root**](#root) still returns the array, and after an object and then an array, [**Encode**](#encode) with no argument still writes the object. Call [**Clear**](#clear) before decoding a document into an object that held another. + > [!NOTE] 1.0.0.29 > Only spaces are removed from the start of the text before it is checked for `[`. A text that has a line break or a tab in front of its `[` is taken for an object, and the array that it holds cannot be stored in **RootItem**: the call raises the error *No such interface supported* (-2147467262), with the source `DECODE`. @@ -193,7 +212,8 @@ Debug.Print Err.Number & " " & J.Items(2) ' 0 2 ``` -If the text cannot be read, **Decode** raises the error again with the same number (10001 for a syntax error), the source `DECODE`, and the description of the parser followed by `#` and the whole text. The parser is that of [ParseJson](../VBA_JSON/JsonConverter/ParseJson): it carries out no check for commas between items, and it ignores text after the closing bracket of the value. + +If the text cannot be read, **Decode** raises the error again with the same number (10001 for a syntax error), the source `DECODE`, and the description of the parser followed by `#` and the whole text. The parser is that of [ParseJson](../VBA_JSON/JsonConverter/ParseJson). ```tb check_build check_run Dim J As New cJson @@ -203,6 +223,11 @@ Debug.Print Err.Number & " " & Err.Source ' Output: ' 10001 DECODE ``` + + + +> [!NOTE] 1.0.0.29 +> The parser accepts text that is not JSON. It does not check for a comma between the items of an object or an array, and it ignores text after the closing bracket of the value: `{"a":1 "b":2}`, `[1 2]` and `{"a":1} garbage` are all read without an error. ### Encode {: .no_toc } @@ -222,8 +247,11 @@ Syntax: *object*.**Encode** ( [ *Obj* ] [ **,** *Whitespace* ] [ **,** *FromUnic The text is built by [ConvertToJson](../VBA_JSON/JsonConverter/ConvertToJson) and also stored in [**RootText**](#roottext). Non-ASCII characters and control characters are written as `\uXXXX` escapes, so the text is ASCII. With *FromUnicode* set to **True**, every `\uXXXX` in the text is turned back into the character it names, and the result holds the characters themselves. -If the value written has no items, nothing is converted and the function returns [**RootText**](#roottext) as it was: the text of the last call, or an empty string for a new or cleared object. It does not return `{}` or `[]`. + +> [!WARNING] 1.0.0.29 +> A value that has no items is not written as `{}` or `[]`. Nothing is converted, and the function returns [**RootText**](#roottext) as it was: an empty string for a new or cleared object, and, for a document whose items were removed after an earlier call, the text of that call, which no longer matches the document. + > [!WARNING] 1.0.0.29 > With *FromUnicode* set to **True**, the conversion looks for the two characters `\u` anywhere in the text, without regard for the escape that comes before them. A value that holds a backslash followed by the letter `u`, such as `C:\users`, is written by the converter as `C:\\users`, and the conversion meets `\u` in it and tries to read `sers` as four hexadecimal digits. That raises error 13. > @@ -247,6 +275,7 @@ Debug.Print AscW(Mid$(J.Encode(, , True), 7, 1)) ' 1 ``` + ```tb check_build check_run Dim J As New cJson J.Decode "{""a"":[1,2]}" @@ -261,6 +290,7 @@ Debug.Print J.Encode(, , True) = "{""word"":""caf" & ChrW(233) & """}" ' {"word":"caf\u00E9"} ' True ``` + ### LoadFrom {: .no_toc } @@ -277,6 +307,7 @@ Syntax: *object*.**LoadFrom** ( *Path* [ **,** *CharSet* ] ) **As cJson** The file is read by [**ToolsStream.LoadFileAsText**](cToolsStream#loadfileastext) and the text goes to [**Decode**](#decode), so its rules and errors apply. Returns the object itself. + > [!WARNING] 1.0.0.29 > **ToolsStream.LoadFileAsText** leaves the shared stream open, and raises error 3705, *Operation is not allowed when the object is open*, when it finds it open. A **LoadFrom** therefore fails after any earlier **LoadFrom** or [**SaveTo**](#saveto) of the program, and after a file that could not be opened. See [**cToolsStream**](cToolsStream). `ToolsStream.Inst.Close` after each call makes the next one work. @@ -290,6 +321,7 @@ Syntax: *object*.**NewItem** ( [ *ParentKey* ] ) **As cJson** *ParentKey* : *optional* A **String**. If it is left out, the new object becomes the next item of [**RootItems**](#rootitems), an element of the array. If it is given, the new object is stored in [**RootItem**](#rootitem) under that name. + Returns a new **cJson**. The object that it holds in its **RootItem** is the one that was added, so setting values on the returned object changes the document it was made for. ```tb check_build check_run @@ -311,6 +343,7 @@ Debug.Print List.Encode() ' {"name":"Ann","home":{"city":"Oslo"}} ' [{"id":1},{"id":2}] ``` + ### NewItems {: .no_toc } @@ -356,9 +389,11 @@ Syntax: *object*.**SaveTo** ( *FileName* [ **,** *CharSet* ] [ **,** *Whitespace The text is made by [**Encode**](#encode) of the object's [**Root**](#root), so the rules of that method apply, among them that an empty document writes the text of an earlier call. The function returns the text that it wrote to the file. A file written with the character set `UTF-8` begins with the byte order mark EF BB BF. + > [!WARNING] 1.0.0.29 > **ToolsStream.SaveFileAsText** leaves the shared stream open and, when it finds it open, writes over the text that is still in it. After a first **SaveTo**, a second one with a shorter document, in the same or another file, writes the new text with the end of the old text after it, which is not valid JSON: saving `{"a":"long long"}` and then `{"b":1}` leaves `{"b":1}ong long"}` in the second file. No error is raised. See [**cToolsStream**](cToolsStream). `ToolsStream.Inst.Close` after each call avoids it. + ```tb check_build check_run Dim Path As String Path = Environ$("TEMP") & "\tbmanlib-docs\sample.json" @@ -379,6 +414,7 @@ RmDir Environ$("TEMP") & "\tbmanlib-docs" ' {"word":"caf\u00E9"} ' 233 ``` + ### ToArray {: .no_toc } @@ -387,7 +423,9 @@ Returns the object of the document in an array. Syntax: *object*.**ToArray** ( ) **As Variant** + If [**RootItem**](#rootitem) has entries, the result is an array of one element that holds that **Scripting.Dictionary**. In every other case, including a document held in [**RootItems**](#rootitems), the result is **Empty**. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB/cLogs.md b/docs/Reference/Contributed/TBMANLIB/cLogs.md index 97bcbff1..14d6378d 100644 --- a/docs/Reference/Contributed/TBMANLIB/cLogs.md +++ b/docs/Reference/Contributed/TBMANLIB/cLogs.md @@ -44,11 +44,15 @@ The enumerations are declared inside the class. A program names their members wi ## Log lines + A line is made of the time, the level and the text: `[10/12/2026 3:04:05 PM] [WARN] disk is almost full`. The time is the value of `Now` converted to text with the settings of the system, so its form changes from one computer to another. The level is `INFO`, `WARN`, `DANGER` or `ERROR`, the names of the first four members of [**EnumLogLevel**](#enumloglevel). [**Save**](#save), [**FormView**](#formview), [**WebView**](#webview) and [**DbgView**](#dbgview) all make the line the same way, and each returns it. A level is kept when it is equal to or greater than [**LogLevelControl**](#loglevelcontrol). Below that, the member does nothing and returns an empty string. + -The levels **Debugger** and **Custom** have no name for the line: a call with either of them raises error 9, *Subscript out of range*, because the table of names has only four entries. Nothing is written. + +> [!NOTE] 1.0.0.29 +> The levels **Debugger** and **Custom** have no name for the line: a call with either of them raises error 9, *Subscript out of range*, because the table of names has only four entries. Nothing is written. ```tb check_build check_run Dim Logs As New cLogs @@ -66,6 +70,7 @@ Debug.Print Err.Number ## Where a log file goes + [**Save**](#save) builds the path from the properties below. `` is [**LogDir**](#logdir), or the folder of the program (`App.Path`) when **LogDir** is empty. `` is [**LogSubDir**](#logsubdir). | [**LogFileNameRule**](#logfilenamerule) | File | @@ -75,6 +80,7 @@ Debug.Print Err.Number | **byUser** | `\\` | The year, month and day come from `Now`. The folders that the path names are created if they are missing. For **byUser**, the file name may hold folders, but only the folder part made from `` and `` is created, so a missing folder inside **LogFileNameByUer** makes the call fail with the error *Unspecified error* (-2147467259). + ```tb check_build check_run Dim Logs As New cLogs @@ -170,7 +176,9 @@ Syntax: *object*.**DbgView** ( *LogStr* [ **,** *LogLevel* ] ) **As String** *LogLevel* : *optional* An [**EnumLogLevel**](#enumloglevel), the level of the line. The default is **Info**. + The line is passed to the Windows function **OutputDebugString**, with the text `[TBMAN-LOG]` and a full-width colon in front of it, so a tool that shows debugging output, such as DebugView, can filter the lines of the class by that prefix. Returns the line, or an empty string if its level is below [**LogLevelControl**](#loglevelcontrol). + ### FormView {: .no_toc } @@ -185,7 +193,9 @@ Syntax: *object*.**FormView** ( *LogStr* [ **,** *LogLevel* ] ) **As String** *LogLevel* : *optional* An [**EnumLogLevel**](#enumloglevel), the level of the line. The default is **Info**. + The line is added at the top of [**FormListBox**](#formlistbox), where it is the first item, and the oldest lines are removed so that the list holds at most [**FormListBoxMaxItem**](#formlistboxmaxitem) lines. If **FormListBox** is **Nothing**, nothing happens and the result is an empty string. Otherwise the result is the line, or an empty string if its level is below [**LogLevelControl**](#loglevelcontrol). + ```tb check_build Dim Logs As New cLogs @@ -214,6 +224,7 @@ The method first sets [**LogSubDir**](#logsubdir), [**LogFileNameByUer**](#logfi Returns the line, or an empty string if its level is below [**LogLevelControl**](#loglevelcontrol). + > [!WARNING] 1.0.0.29 > The calls to the three other members do not pass the level of the line. They make the line again at the level **Info**. A line sent to a list box, a debugging tool or a web page is therefore labelled `INFO` whatever level **Save** was given, and when **LogLevelControl** is above **Info** it is not sent at all. The result of **Save** is the text that was made last: the `INFO` line, when one of the outputs sent it. @@ -248,7 +259,11 @@ Syntax: *object*.**WebView** ( *LogStr* [ **,** *LogLevel* ] ) **As String** *LogLevel* : *optional* An [**EnumLogLevel**](#enumloglevel), the level of the line. The default is **Info**. -If [**WebUserCode**](#webusercode) is empty, nothing happens and the result is an empty string. Otherwise the line is sent in an asynchronous HTTP POST request, made with the late-bound object `WinHttp.WinHttpRequest.5.1`, to `http://log.vb6.pro/submit?code=` followed by **WebUserCode**, with the body `log=` followed by the line. The call does not wait for the answer and does not report whether the request worked. The result is the line, or an empty string if its level is below [**LogLevelControl**](#loglevelcontrol). + +If [**WebUserCode**](#webusercode) is empty, nothing happens and the result is an empty string. + + +Otherwise the line is sent in an asynchronous HTTP POST request, made with the late-bound object `WinHttp.WinHttpRequest.5.1`, to `http://log.vb6.pro/submit?code=` followed by **WebUserCode**, with the body `log=` followed by the line. The call does not wait for the answer and does not report whether the request worked. The result is the line, or an empty string if its level is below [**LogLevelControl**](#loglevelcontrol). > [!IMPORTANT] 1.0.0.29 > The request uses `http`, not `https`, and goes to a web site that is not part of the package: the code and the text of every line can be read by any computer on the way. Neither the code nor the line is URL-encoded, so a `&`, an `=`, a `%` or a `+` in the line changes what the site receives. Do not send lines that hold private data. diff --git a/docs/Reference/Contributed/TBMANLIB/cSubClass.md b/docs/Reference/Contributed/TBMANLIB/cSubClass.md index 09b77bb3..eb7bcb17 100644 --- a/docs/Reference/Contributed/TBMANLIB/cSubClass.md +++ b/docs/Reference/Contributed/TBMANLIB/cSubClass.md @@ -58,9 +58,11 @@ Syntax: *object*.**Hook** ( *hWnd* ) *hWnd* : *required* A **Long**, the handle of a window of the program. + Stores the handle, saves the address of the window's procedure, installs the package's procedure, and adds the object to a collection of the internal module **mCallBackSubClass** under the handle. A call on an object that already hooks a window does nothing. Hooking a window that another **cSubClass** object has hooked raises error 457, *this key is already associated with an element of this collection*. In the IDE, the call writes a line to the Debug Console. The collection holds a reference to the object, so the object is not destroyed while the window is hooked, and the window keeps its replacement procedure until [**UnHook**](#unhook) is called. A program calls **UnHook** before the window is destroyed and before it ends. + ### UnHook {: .no_toc } @@ -69,7 +71,9 @@ Restores the window's own procedure. Syntax: *object*.**UnHook** + Puts back the procedure that **Hook** saved and removes the object from the collection. A second call, with no hook in place, raises error 5. + ## Events @@ -83,8 +87,10 @@ Syntax: **Event** *object*_**WindowProc** ( *Msg* **,** *wParam* **,** *lParam* *Msg*, *wParam*, *lParam* : **Long** values passed by reference: the message number and its two arguments. + *CancelNextCall* : A **Boolean**, passed by reference, **False** on entry. When the handler sets it to **True**, the message does not reach the window's own procedure, and the window procedure returns 0. Otherwise the window handles the message after the event. + A handler is written for a variable declared with **WithEvents**. The event is raised synchronously, in the thread that sends the message, before the window sees it. A handler cannot change the value that the window procedure returns, or the arguments that the window procedure receives (the arguments are passed by reference but the original procedure is called with the originals). diff --git a/docs/Reference/Contributed/TBMANLIB/cTimer.md b/docs/Reference/Contributed/TBMANLIB/cTimer.md index 18ad25e4..e9ab8372 100644 --- a/docs/Reference/Contributed/TBMANLIB/cTimer.md +++ b/docs/Reference/Contributed/TBMANLIB/cTimer.md @@ -38,7 +38,9 @@ The event is raised from the message loop of the program, so it is not raised wh A **Boolean**: whether the timer is running. It is **False** for a new object. + Setting it to **True** starts the timer, or starts it again from zero when it is running. Setting it to **False** stops it. A start with [**Interval**](#interval) of 0 or less does nothing: **Enabled** stays **False**. + ```tb check_build check_run Dim T As New cTimer @@ -83,7 +85,9 @@ The same as setting [**Enabled**](#enabled) to **False**. ### Enable {: .no_toc } + Starts the timer, with the current [**Interval**](#interval). A running timer is stopped and started again. + Syntax: *object*.**Enable** @@ -121,6 +125,7 @@ A handler is written for a variable declared with **WithEvents**. A timer that b The class keeps every running timer in a collection of the module **mTimer**, by the number that Windows gives the timer, and the callback that Windows calls, **mTimer.TimerProc**, finds the object there and raises its event. The module belongs to the package's internal code and has no page; a program does not call it. + > [!NOTE] 1.0.0.29 > The collection holds a reference to each running timer. A **cTimer** that is enabled is therefore not destroyed when the program releases its last reference: it stays registered and goes on running until [**Enabled**](#enabled) is set to **False**. A timer that was never enabled is destroyed as usual, and the timer of a destroyed object is stopped. diff --git a/docs/Reference/Contributed/TBMANLIB/cTimers.md b/docs/Reference/Contributed/TBMANLIB/cTimers.md index 06b612cc..c4e9a01d 100644 --- a/docs/Reference/Contributed/TBMANLIB/cTimers.md +++ b/docs/Reference/Contributed/TBMANLIB/cTimers.md @@ -44,7 +44,9 @@ Syntax: *object*.**Item** ( *Index* ) **As cTimer** *Index* : *required* An **Integer**, from 1 to [**Count**](#count). + It is the default member, so `Group(2)` is `Group.Item(2)`. A number above **Count** raises error -2147221504 (`vbObjectError`), with the text *Index does not Exist*, and 0 or a negative number raises error 9. + ## Methods @@ -55,7 +57,9 @@ Adds a timer to the group. Syntax: *object*.**Add** ( ) **As Integer** + Returns the number of the new timer, counted from 1. The timer is off, with an [**Interval**](cTimer#interval) of 2000; start it with `Group(n).Enabled = True`. + ### BulkLoad {: .no_toc } @@ -74,7 +78,9 @@ Adds *num* + 1 timers, not *num*: a call with 3 adds 4. Any other value raises e ### Timer {: .no_toc } + Raised when the interval of one of the enabled timers has passed. + Syntax: **Event** *object*_**Timer** ( *Index* ) @@ -100,6 +106,7 @@ Private Sub Group_Timer(ByVal Index As Integer) End Sub ``` + > [!NOTE] 1.0.0.29 > A **cTimers** registers itself in a collection of the package's internal module **mTimer**, which keeps a reference to it. The group is therefore not destroyed when the program releases its last reference, and its enabled timers go on running. Set [**Enabled**](cTimer#enabled) of each timer to **False** first. diff --git a/docs/Reference/Contributed/TBMANLIB/cTimeuse.md b/docs/Reference/Contributed/TBMANLIB/cTimeuse.md index e74e22d5..c1ebfea7 100644 --- a/docs/Reference/Contributed/TBMANLIB/cTimeuse.md +++ b/docs/Reference/Contributed/TBMANLIB/cTimeuse.md @@ -35,7 +35,9 @@ Syntax: *object*.**Show** ( [ *Txt* ] ) **As Variant** *Txt* : *optional* A **String**, the message. Every `{*}` in it is replaced by the number of milliseconds. The default is a Chinese message: the words for *time used*, a colon, `{*}` and the word for *milliseconds*. + Returns a **String** in a **Variant**. The number is the tick count at the call less the tick count at **Start**. If **Start** has not been called, the number is the tick count itself: the milliseconds since the computer started. + ### Start {: .no_toc } diff --git a/docs/Reference/Contributed/TBMANLIB/cToolsStr.md b/docs/Reference/Contributed/TBMANLIB/cToolsStr.md index 68c214ca..aba6acf7 100644 --- a/docs/Reference/Contributed/TBMANLIB/cToolsStr.md +++ b/docs/Reference/Contributed/TBMANLIB/cToolsStr.md @@ -24,7 +24,9 @@ Debug.Print ToolsStr.UnicodeEncode("caf" & ChrW(233)) ' caf\u00E9 ``` -A few functions keep a position or a count in an **Integer** variable: **TrimEx** and the functions built on it, **InsertSpan**, **PercentEncode**, **PercentDecode**, **UnicodeEncode** and **UnicodeDecode**. They raise error 6, *Overflow*, for a string of more than 32,767 characters. + +> [!NOTE] 1.0.0.29 +> A few functions keep a position or a count in an **Integer** variable: **TrimEx** and the functions built on it, **InsertSpan**, **PercentEncode**, **PercentDecode**, **UnicodeEncode** and **UnicodeDecode**. They raise error 6, *Overflow*, for a string of more than 32,767 characters. Most **String** parameters are passed by reference, as the language does by default, and the functions do not change them. The two exceptions are [**IsEmptyEx**](#isemptyex), which trims the variable that is passed to it, and [**MidEx**](#midex), which sets its *starindex* argument. @@ -43,8 +45,11 @@ Syntax: **ToolsStr.IsEmptyEx** ( *Text* ) **As Boolean** *Text* : *required* A **String**. + Returns **True** when *Text* has no characters left after [**TrimEx**](#trimex) removes the spaces, tabs, line breaks and other control characters at both ends. + + > [!WARNING] 1.0.0.29 > The parameter is passed by reference and the function assigns the trimmed text to it, so a variable that is passed is changed: after `IsEmptyEx(t)`, **t** holds the trimmed text. Pass an expression, such as `(t)`, to keep the variable as it is. @@ -70,7 +75,9 @@ Syntax: **ToolsStr.IsString** ( *var* ) **As Boolean** *var* : *required* A **Variant**. + Returns **True** when `VarType(var)` is **vbString**. The text `"12"` is a string, and the number 12 is not. + ### LeftEx {: .no_toc } @@ -85,7 +92,9 @@ Syntax: **ToolsStr.LeftEx** ( *Text* **,** *Length* ) **As String** *Length* : *required* A **Long**, the number of characters. + The spaces at both ends of *Text* are removed with **Trim** first. Tabs and line breaks stay. [**cJson.Decode**](cJson#decode) uses the function to see whether a text begins with `[`. + ### RightEx {: .no_toc } @@ -100,7 +109,9 @@ Syntax: **ToolsStr.RightEx** ( *Text* **,** *Length* ) **As String** *Length* : *required* A **Long**, the number of characters. + The spaces at both ends of *Text* are removed with **Trim** first. Tabs and line breaks stay. + ### TrimEx {: .no_toc } @@ -112,7 +123,9 @@ Syntax: **ToolsStr.TrimEx** ( *Text* ) **As String** *Text* : *required* A **String**. It is passed by reference and is not changed. + Removes the characters space, tab, carriage return, line feed and every character with a code from 0 to 31, from the start and from the end of *Text*. Other characters that look like spaces, such as the non-breaking space, stay. + ## Searching and cutting @@ -129,7 +142,9 @@ Syntax: **ToolsStr.GetFirstChar** ( *Txt* [ **,** *Length* ] ) **As String** *Length* : *optional* A **Long**, 1 by default. + The function skips every character whose code is not from 33 to 126, so spaces, control characters and non-ASCII characters are skipped, and finds the first one that is. With a *Length* of 1 the result is that character. With a larger *Length* the result is that many characters from there, taken as they stand. If *Txt* holds no character with a code from 33 to 126, the function raises error 5. + ```tb check_build check_run Debug.Print ToolsStr.GetFirstChar(" - abc") @@ -152,6 +167,7 @@ Syntax: **ToolsStr.GetLastChar** ( *Txt* [ **,** *Length* ] ) **As String** *Length* : *optional* A **Long**, 1 by default. + > [!WARNING] 1.0.0.29 > The code of this function is a copy of the code of [**GetFirstChar**](#getfirstchar): it searches from the start of the text, and returns the first visible character, not the last. `GetLastChar("abc")` returns `a`. @@ -180,7 +196,9 @@ Syntax: **ToolsStr.HasStr** ( *FindStr* **,** *FullStr* [ **,** *StartPos* ] [ * *CompType* : *optional* A **VbCompareMethod**, **vbTextCompare** by default. + Returns the position of the first match, counted from 1, or 0 when there is none: the result of [**InStr**](../../../Modules/Strings/InStr) with the arguments in a different order. Unlike **InStr**, the search ignores letter case unless *CompType* is **vbBinaryCompare**. + ```tb check_build check_run Debug.Print ToolsStr.HasStr("lo", "Hello") @@ -211,7 +229,9 @@ Syntax: **ToolsStr.MidEx** ( *FullStr* **,** *Lstr* **,** *Rstr* [ **,** *starin *starindex* : *optional* A **Long**, passed by reference. When it is not 0, the search for *Lstr* starts at that position and ignores letter case; a search from the start of *FullStr* is case-sensitive. When a match is found, the variable is set to the position after *Rstr*, so a loop that passes the same variable finds one match after another. + The function finds the first *Lstr*, then the first *Rstr* after it. It returns an empty string if either is missing. Of the text between them, it removes the first and the last character, so the function suits a value that is enclosed in quotation marks: `MidEx("id=[""abc""]", "[", "]")` returns `abc`. If the text between has fewer than two characters, the call raises error 5. + ```tb check_build check_run Dim Start As Long @@ -248,6 +268,7 @@ Syntax: **ToolsStr.SliceString** ( *Arr* **,** *StartPos* [ **,** *EndPos* ] ) * *EndPos* : *optional* A **Long**, the position of the last character, which is included. The default of -1 means the end of the string. + ```tb check_build check_run Debug.Print ToolsStr.SliceString("Hello World", 0, 4) Debug.Print ToolsStr.SliceString("Hello World", 6) @@ -255,6 +276,7 @@ Debug.Print ToolsStr.SliceString("Hello World", 6) ' Hello ' World ``` + ### SubStr {: .no_toc } @@ -275,7 +297,9 @@ Syntax: **ToolsStr.SubStr** ( *Txt* **,** *txtF* [ **,** *txtE* ] [ **,** *RetIn *RetInt* : *optional* A **Boolean**, **False** by default. It chooses what the function returns when nothing is found. + The function finds the first *txtF*, then the first *txtE* after it, and returns what is between them, with **Trim** applied. When *txtF* is not found, when *txtE* is not found, or when the text between is empty, it returns an empty string, or the text `0` if *RetInt* is **True**. + ```tb check_build check_run Debug.Print ToolsStr.SubStr("key = value ;", "=", ";") @@ -310,7 +334,9 @@ Syntax: **ToolsStr.InsertSpan** ( *inputStr* **,** *Span* **,** *SetpNum* [ **,* *HeadFoot* : *optional* A **Boolean**, **False** by default. When it is **True**, *Span* is also placed at the start and at the end of the result. + The separator is placed between groups, and not after the last one: `InsertSpan("abcdefg", "-", 3)` returns `abc-def-g`, and with *HeadFoot* set it returns `-abc-def-g-`. + ### JoinStr {: .no_toc } @@ -325,7 +351,9 @@ Syntax: **ToolsStr.JoinStr** ( *Span* **,** *Strings* ) *Strings* : *required* Any number of values, in a **ParamArray**. + Returns the values joined with *Span* between them, as **Join** does. + ```tb check_build check_run Debug.Print ToolsStr.JoinStr("-", "a", "b", "c") @@ -343,7 +371,9 @@ Syntax: **ToolsStr.PercentDecode** ( *encodedStr* ) **As String** *encodedStr* : *required* A **String**. It is passed by reference and is not changed. + Each `%` and the two characters after it are read as a hexadecimal number, and replaced by **Chr** of the number. The characters are those of the ANSI code page, so the bytes of a UTF-8 text such as `%C3%A9` become two characters, not one. A `%` that is not followed by two hexadecimal digits raises error 13. + ### PercentEncode {: .no_toc } @@ -355,7 +385,12 @@ Syntax: **ToolsStr.PercentEncode** ( *inputStr* ) **As String** *inputStr* : *required* A **String**. It is passed by reference and is not changed. -Despite its name, the function does not follow the rules of percent-encoding for web addresses. It replaces the digits `0` to `9` only, each with `%` and the two hexadecimal digits of its code, and leaves every other character as it is: `PercentEncode("A1b2")` returns `A%31b%32`. A space, a slash or a non-ASCII letter is not encoded. [**PercentDecode**](#percentdecode) turns the result back. + +> [!WARNING] 1.0.0.29 +> The function does not follow the rules of percent-encoding for web addresses. A space, a slash, a percent sign or a non-ASCII letter is not encoded: the function replaces the digits `0` to `9` only. + + +The digits `0` to `9` are each replaced with `%` and the two hexadecimal digits of their code, and every other character is left as it is: `PercentEncode("A1b2")` returns `A%31b%32`. [**PercentDecode**](#percentdecode) turns the result back. ```tb check_build check_run Debug.Print ToolsStr.PercentEncode("A1b2") @@ -364,6 +399,7 @@ Debug.Print ToolsStr.PercentDecode("A%31b%32") ' A%31b%32 ' A1b2 ``` + ### UniVbCrLf {: .no_toc } @@ -375,8 +411,11 @@ Syntax: **ToolsStr.UniVbCrLf** ( *Text* ) **As String** *Text* : *required* A **String**. It is passed by reference and is not changed. + An empty string is returned as it is. A string that has only carriage returns, or only the pairs carriage return and line feed, is converted as expected. + + > [!WARNING] 1.0.0.29 > A line feed that is not part of a pair becomes a carriage return and **two** line feeds. The function turns each line feed into a pair, and then turns the carriage return of that pair into a pair again. `"a" & vbLf & "b"` comes out as `"a" & vbCr & vbLf & vbLf & "b"`, five characters. > @@ -410,7 +449,9 @@ Syntax: **ToolsStr.UnicodeDecode** ( *Text* [ **,** *PreFix* ] ) **As String** *PreFix* : *optional* A **String**, `\u` by default. It has to be two characters long: the function compares two characters of the text with it and then skips six, whatever its length. -The four characters after each prefix are read as a hexadecimal number, and the character with that code replaces the six. The letters in the number may be of either case. Four characters that are not hexadecimal digits raise error 13. The function does not know about backslashes before the prefix, so the text `\\u0041`, a backslash that is escaped, followed by `u0041`, is read from the second backslash. + +The four characters after each prefix are read as a hexadecimal number, and the character with that code replaces the six. The letters in the number may be of either case. Four characters that are not hexadecimal digits raise error 13. + ```tb check_build check_run Debug.Print ToolsStr.UnicodeDecode("\u0041\u0042") @@ -420,6 +461,10 @@ Debug.Print AscW(Right$(ToolsStr.UnicodeDecode("caf\u00E9"), 1)) ' 233 ``` + +> [!NOTE] 1.0.0.29 +> The function does not know about backslashes before the prefix, so a backslash that is escaped and followed by the letter `u` and four hexadecimal digits is read as an escape of the character, from the second backslash. This is the cause of the error that [**cJson.Encode**](cJson#encode) raises for a text such as `C:\users` when *FromUnicode* is **True**. + ### UnicodeEncode {: .no_toc } @@ -433,7 +478,9 @@ Syntax: **ToolsStr.UnicodeEncode** ( *inputString* [ **,** *PreFix* ] ) **As Str *PreFix* : *optional* A **String**, `\u` by default. + Characters with a code from 0 to 127 stay as they are, the control characters included. Every other character becomes *PreFix* and the four hexadecimal digits of its code, with letters in upper case. A character that is made of two UTF-16 units, such as an emoji, becomes two such groups. + ## Lines @@ -447,7 +494,9 @@ Syntax: **ToolsStr.JoinLinesFromCollection** ( *Coll* ) **As String** *Coll* : *required* A **Collection**, passed by reference and not changed. The items are taken in order from 1 and converted to strings. + Returns the items with a carriage return and line feed (**vbCrLf**) between them, and none after the last. A collection with no items gives an empty string. + ```tb check_build check_run Dim Lines As New Collection @@ -471,8 +520,11 @@ Syntax: **ToolsStr.SplitLinesToCollection** ( *Text* ) **As Collection** *Text* : *required* A **String**. + Returns a new **Collection** that holds one string for each line, numbered from 1. Lines end at a carriage return and line feed (**vbCrLf**) only. A line feed or a carriage return alone does not end a line: the lines of a text from another system stay in one item. An empty text gives an empty collection. A text that ends with **vbCrLf** gives an empty last item. + + > [!WARNING] 1.0.0.29 > The loop stops when its position is equal to the length of the text, which is the position of the last character. When the last line has exactly one character, the loop stops before it and the line is lost: `"a" & vbCrLf & "b"` gives one item, `a`, and the text `"a"` gives none. A last line of two or more characters is kept. @@ -503,7 +555,9 @@ Syntax: **ToolsStr.LenBytes** ( *inputArray* ) **As Long** *inputArray* : *required* A **Byte** array. + Returns the upper bound less the lower bound, plus 1. The comment in the source says *Unicode*, but the function counts the elements, whatever they hold. + ### ToBytes {: .no_toc } @@ -515,7 +569,9 @@ Syntax: **ToolsStr.ToBytes** ( *inputString* ) **As Byte()** *inputString* : *required* A **String**. It is passed by reference and is not changed. + Returns the bytes of the string in the ANSI code page of the system, with **StrConv** and **vbFromUnicode**, from 0. The comment in the source says *Unicode*; the result is not UTF-16 and not UTF-8. A character that the code page does not have is not kept as written. For UTF-8, use [**ToolsUtf8.Encode**](ToolsUtf8#encode). + ### ToHex {: .no_toc } @@ -527,7 +583,11 @@ Syntax: **ToolsStr.ToHex** ( *inputArray* ) **As String** *inputArray* : *required* A **Byte** array whose lower bound is 0. -Returns the **Hex** of each element, joined with nothing between them. A byte below 16 gives one digit, and not two: `ToHex` of the bytes 10 and 11 is `AB`, the same as the byte 171 gives. The result cannot always be split back into bytes. +Returns the **Hex** of each element, joined with nothing between them. + + +> [!WARNING] 1.0.0.29 +> A byte below 16 gives one digit, and not two: `ToHex` of the bytes 10 and 11 is `AB`, the same as the byte 171 gives. The result cannot always be split back into bytes. ```tb check_build check_run Debug.Print ToolsStr.ToHex(ToolsStr.ToBytes("AB")) @@ -561,7 +621,9 @@ Syntax: **ToolsStr.GetGUID** ( [ *isFull* ] ) **As String** *isFull* : *optional* A **Boolean**, **False** by default. When it is **True**, the GUID is returned with the braces around it. + The GUID is made by the Windows scripting object `Scriptlet.TypeLib`, which the function creates by name on each call. The result has the form `9A0F3C5E-1B2D-4C6E-8F70-123456789ABC`, 36 characters, or 38 with the braces, with the letters in upper case. + ```tb check_build check_run Dim g As String @@ -596,7 +658,9 @@ Returns ten random bytes. Syntax: **ToolsStr.GetRandByte** ( ) **As Byte()** + Returns a **Byte** array from 0 to 9. The function takes no argument, so the number of bytes is always ten. It calls **Randomize** and fills the array with `Int(Rnd * 256)`. The bytes are not suitable for keys, passwords or tokens. + ```tb check_build check_run Dim Bytes() As Byte @@ -616,10 +680,12 @@ Syntax: **ToolsStr.GetRandStr** ( [ *Lens* ] [ **,** *Zuhe* ] ) **As String** *Lens* : *optional* An **Integer**, the number of characters, 32 by default. A value below 1 is replaced by 8. + *Zuhe* : *optional* A **String** that says which characters can be used, `1aA` by default. It is a set of letters that stand for groups of characters: `1` for the digits, `a` for the lower-case letters `a` to `z`, `A` for the upper-case letters, and `@` for the 32 punctuation characters of ASCII. An empty string stands for all four groups. Other characters in the string are ignored; if no group is named, the result is an empty string. The name means *combination*. Each character is chosen with [**ToolsMath.GetRandRange**](ToolsMath#getrandrange), so the strings are not suitable for passwords, keys or tokens. + ```tb check_build check_run Dim s As String diff --git a/docs/Reference/Contributed/TBMANLIB/cToolsStream.md b/docs/Reference/Contributed/TBMANLIB/cToolsStream.md index 38fafd0a..f8a8e775 100644 --- a/docs/Reference/Contributed/TBMANLIB/cToolsStream.md +++ b/docs/Reference/Contributed/TBMANLIB/cToolsStream.md @@ -27,6 +27,7 @@ Kill Path ' True ``` + > [!WARNING] 1.0.0.29 > [**LoadFileAsText**](#loadfileastext) and [**SaveFileAsText**](#savefileastext) leave the stream open, and neither closes it first. That breaks the next call of either on the same object, and the shared **ToolsStream** is such an object for the whole run of the program: > @@ -100,6 +101,7 @@ Syntax: **ToolsStream.LoadFileAsBinary** ( *Path* **,** *OutData* ) **As Boolean *OutData* : *required* A dynamic **Byte** array. It receives the bytes of the file. + Returns **True** when the file was read, and **False** when an error occurred; the text of the error is then in [**LastError**](#lasterror). The function closes the stream if it is open, sets it to the binary type, reads the file and closes the stream. The text in **LastError** is the description of the error, then `#`, then the description a second time. The number of the error is not in it. @@ -113,6 +115,7 @@ Debug.Print S.LastError ' False ' File could not be opened.#File could not be opened. ``` + ### LoadFileAsText {: .no_toc } @@ -127,7 +130,9 @@ Syntax: **ToolsStream.LoadFileAsText** ( *FileName* [ **,** *CharSet* ] ) **As S *CharSet* : *optional* A **String**, the name of the character set in which the file is stored, `UTF-8` by default. Any name that the ADODB.Stream accepts can be used. A byte order mark at the start of a UTF-8 file is not returned as a character. + The function sets the type of the stream to text, opens it, sets its character set, loads the file and reads all the text from it. It leaves the stream open, with its position at the start. It handles no error: a failure, such as error 3002, *File could not be opened*, for a file that does not exist, is raised to the caller as the error of the stream, and leaves the stream open. + In line mode, the text is also split into lines with [**SplitLinesToCollection**](cToolsStr#splitlinestocollection), which is stored in [**LineData**](#linedata), and [**LineNumber**](#linenumber) is set to 1. The function still returns the whole text. The rules of the split apply, among them that a last line of one character is lost. @@ -143,6 +148,7 @@ Syntax: **ToolsStream.ReadLine** ( [ *Line* ] ) **As String** *Line* : *optional* A **Long**. When it is greater than 0, it is stored in [**LineNumber**](#linenumber) first, and that line is read. + Returns the item of **LineData** that **LineNumber** names, and adds 1 to **LineNumber**. A number that is not the number of an item raises error 9, *Subscript out of range*. When line mode is off, the function raises error 1 with a Chinese message that means *line reading and writing is not switched on*. ```tb check_build check_run @@ -163,6 +169,7 @@ Kill Path ' first second 3 ' first ``` + ### SaveFileAsBinary {: .no_toc } @@ -209,13 +216,20 @@ Syntax: **ToolsStream.SaveFileAsText** ( *FileName* [ **,** *Data* ] [ **,** *Ch : *required* A **String**, the file to write. An existing file is replaced. The folder must exist: when it does not, the call raises error 3004, *Write to file failed*. *Data* -: *optional* A **String**, the text to write. It is declared optional, and meant to default to the lines of [**LineData**](#linedata) in line mode, or to the text the stream already holds. Neither default works: a **String** that is left out is a null string, not a missing argument, so the call tries to write a null string and raises error 3001, *Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another*. Pass the text, an empty string if need be. +: *optional* A **String**, the text to write. It is declared optional, and meant to default to the lines of [**LineData**](#linedata) in line mode, or to the text the stream already holds; see the note below. *CharSet* : *optional* A **String**, the name of the character set, `UTF-8` by default. A character set of `UTF-8` writes the byte order mark EF BB BF at the start of the file. + The function opens the stream if it is closed, sets its character set, writes the text into it and saves the file. It leaves the stream open. The function adds no line break to the text. See the warning at the top of the page for what happens when the stream is already open. + + +> [!NOTE] 1.0.0.29 +> The text cannot be left out. Neither default of *Data* works: a **String** that is left out is a null string, not a missing argument, so the call tries to write a null string and raises error 3001, *Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another*. Pass the text, an empty string if need be. + + > [!WARNING] 1.0.0.29 > The function always returns **False**. Its result is never set, even when the file was written. A program cannot use the result to see whether the call worked, and has to handle the error that the stream raises. @@ -261,6 +275,7 @@ Syntax: **ToolsStream.WriteLine** ( *Text* [ **,** *Line* ] ) **As Boolean** *Line* : *optional* A **Long**. When it is greater than 0, it is stored in [**LineNumber**](#linenumber) first. + > [!NOTE] 1.0.0.29 > The function raises error 13 every time line mode is on. It adds the line to the collection with `LineData.Add Text, LineNumber`, and a collection takes a **String** as the key of an item, not a **Long**. Items can be added to **LineData** directly with its own **Add**. When line mode is off, the function raises error 1 with a Chinese message that means *line reading and writing is not switched on*. diff --git a/docs/Reference/Contributed/TBMANLIB/index.md b/docs/Reference/Contributed/TBMANLIB/index.md index 3ee2ab1d..a49f0657 100644 --- a/docs/Reference/Contributed/TBMANLIB/index.md +++ b/docs/Reference/Contributed/TBMANLIB/index.md @@ -79,6 +79,7 @@ The references of a package are not visible to the project that uses it. A progr The package is built for 32-bit programs. The window procedure that **cSubClass** installs is passed to **SetWindowLong** as a **Long**, which a 64-bit program cannot hold. + > [!WARNING] BETA 1005 > A project that embeds TBMANLIB and is built for 64-bit programs (**Win64**) does not compile. The compiler reports TB5001 in `cSubClass.twin`, *Validation of call to 'SetWindowLong' failed. Argument for 'dwNewLong': cannot coerce type 'Callback' to 'Long'*, even when the program uses nothing of the package. The package can be used in 32-bit programs only. @@ -90,12 +91,13 @@ Part of the code is unfinished, and the source holds test code and notes to the - [**cToolsStream**](cToolsStream), and with it [**cJson.LoadFrom**](cJson#loadfrom) and [**cJson.SaveTo**](cJson#saveto), leave the shared stream open after a text call, so that the next text call fails or writes over the old text. [**SaveFileAsText**](cToolsStream#savefileastext) always returns **False**, and [**WriteLine**](cToolsStream#writeline) always raises error 13. - [**cCsv**](cCsv): the functions that add columns and widths return **False** always, the `value` property cannot be set, removing a column other than the last one corrupts the saved file, and a character that the system code page lacks leaves an empty file. -- [**cHttpClient**](cHttpClient) accepts every certificate, [**RequestDataQuery**](cHttpClient#requestdataquery) loses the name of its first item, header values are cut at their first colon, so [**FollowRedirect**](cHttpClient#followredirect) cannot work, and its content type header does not follow a later change of the content type. +- [**cHttpClient**](cHttpClient) accepts every certificate, [**RequestDataQuery**](cHttpClient#requestdataquery) loses the name of its first item, [**RequestDataForm**](cHttpClient#requestdataform) values are not URL encoded, header values are cut at their first colon, so [**FollowRedirect**](cHttpClient#followredirect) cannot work, and its content type header does not follow a later change of the content type. - [**ToolsHttp.UrlDecodeUtf8**](ToolsHttp#urldecodeutf8) raises error 13 for any text with a `%`, [**UrlEncodUtf8**](ToolsHttp#urlencodutf8) writes the characters from hex 80 to hex FFF wrongly, and [**ToolsCN.GetPinYin**](ToolsCN#getpinyin) raises an error for any text with an English letter, a digit or a space in it. -- [**cJson.Decode**](cJson#decode) rejects an array that follows a line break. -- [**cToolsStr.GetLastChar**](cToolsStr#getlastchar) returns the first character, [**UniVbCrLf**](cToolsStr#univbcrlf) doubles a line feed, [**IsEmptyEx**](cToolsStr#isemptyex) trims the variable it is given, and [**SplitLinesToCollection**](cToolsStr#splitlinestocollection) loses a last line of one character. +- [**cJson.Decode**](cJson#decode) rejects an array that follows a line break, accepts text that is not JSON, and keeps the old array or object when it reads the other kind; [**Items**](cJson#items) adds a value instead of replacing one, and [**Encode**](cJson#encode) does not write `{}` for an empty document. +- [**cToolsStr.GetLastChar**](cToolsStr#getlastchar) returns the first character, [**UniVbCrLf**](cToolsStr#univbcrlf) doubles a line feed, [**IsEmptyEx**](cToolsStr#isemptyex) trims the variable it is given, [**SplitLinesToCollection**](cToolsStr#splitlinestocollection) loses a last line of one character, [**ToHex**](cToolsStr#tohex) writes one digit for a byte below 16, [**PercentEncode**](cToolsStr#percentencode) encodes the digits only, and the functions that keep a position in an **Integer** raise error 6 for a string of more than 32,767 characters. - [**ToolsUtf8.DecodeToByteArray**](ToolsUtf8#decodetobytearray) returns half of the bytes. -- [**ToolsArray.SliceByteArray**](ToolsArray#slicebytearray) returns a wrong result when the start and the end are the same. +- [**ToolsArray.SliceByteArray**](ToolsArray#slicebytearray) returns a wrong result when the start and the end are the same, and [**SplitByteArrayByKeyword**](ToolsArray#splitbytearraybykeyword) raises an error for a keyword at the start. +- [**ToolsFso.AutoCompleteFullPath**](ToolsFso#autocompletefullpath) and [**ClearSpan**](ToolsFso#clearspan) spoil a network path and a drive path written with forward slashes, [**MakeNewFileFulPath**](ToolsFso#makenewfilefulpath) leaves out the folder, [**ToolsDic.DeepCopy**](ToolsDic#deepcopy) copies the first level only, [**ToolsHttp.ParseKeyValue**](ToolsHttp#parsekeyvalue) cuts a value at its colon, and [**ToolsMath.Ceil**](ToolsMath#ceil) rounds `0.07` up to 0.08. - [**cLogs.Save**](cLogs#save) sends the other outputs at the level **Info**, and the levels **Debugger** and **Custom** raise an error. - [**cTimer**](cTimer) and [**cTimers**](cTimers) objects that are enabled are not destroyed when the program releases them, and go on running. @@ -148,3 +150,7 @@ Some types of the package have no members, or none that a program is meant to us - [TBMANLIB_RES](../TBMANLIB_RES/) package -- resources embedded in an executable, by the same author - [TBMANLIB_Xml](../TBMANLIB_Xml/) package -- XML documents, by the same author - [Importing a package from TWINSERV](../../../../Features/Packages/Importing-TWINSERV) -- adding a published package to a project + +## Test cases + +The notes and warnings of this section are checked by the test cases in [TBMANLIB-tests.twinproj](TBMANLIB-tests.twinproj), a twinBASIC project. It uses TBMANLIB as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/TBMANLIB_Aes/cAes.md b/docs/Reference/Contributed/TBMANLIB_Aes/cAes.md index 134494bf..5263a5b2 100644 --- a/docs/Reference/Contributed/TBMANLIB_Aes/cAes.md +++ b/docs/Reference/Contributed/TBMANLIB_Aes/cAes.md @@ -32,6 +32,7 @@ In the syntax lines of this page, *object* is a **cAes**. ## Defaults + A new **cAes** starts with these values. | Property | Starting value | @@ -56,13 +57,17 @@ Debug.Print helper.IsArrayEmpty(keyBytes) ' True Debug.Print aes.IsInitialized ' False Debug.Print Len(aes.LastError) ' 0 ``` + + Every call of an encrypt or a decrypt method starts again from **Key** and **IV**: it makes new Windows key objects, and it does not continue the chaining of an earlier call. Two encryptions in a row with the same properties give the same output. + ## Which mode runs + > [!WARNING] 1.0.0.4 -> The name of a mode is not always the mode that runs. **ECB** runs CBC with an IV of zero bytes, **CBC** runs ECB, and **OFB** and **CTR** run CFB. Neither **ECB** nor **CBC** uses the IV that the program sets, so the same key and plaintext always give the same ciphertext, and in the default mode **CBC** equal plaintext blocks give equal ciphertext blocks. A round trip succeeds in every case, because encryption and decryption use the same mode, so a test that only decrypts what it has encrypted shows nothing. +> The name of a mode is not always the mode that runs. **ECB** runs CBC with an IV of zero bytes, **CBC** runs ECB, and **OFB** and **CTR** run CFB. The CFB that runs has an 8-bit feedback, where the standard CFB has a 128-bit feedback, so **CFB**, **OFB** and **CTR** give the output of none of the standard modes of those names. Neither **ECB** nor **CBC** uses the IV that the program sets, so the same key and plaintext always give the same ciphertext, and in the default mode **CBC** equal plaintext blocks give equal ciphertext blocks. A round trip succeeds in every case, because encryption and decryption use the same mode, so a test that only decrypts what it has encrypted shows nothing. | Mode | Windows interface | What runs | The IV | |---|---|---|---| @@ -104,6 +109,10 @@ The **Key** array is therefore not the AES key of these two modes. Other softwar The class pads the data as [**PaddingMode**](#paddingmode) says, and then calls `CryptEncrypt` for the last block. Windows adds its own PKCS #5 padding at that call, whatever **PaddingMode** is, so the output is longer than the padded data by 1 to 16 bytes: a whole block when the padded data already fills its blocks. On decryption Windows takes off its padding, and then the class takes off its own. + +> [!WARNING] 1.0.0.4 +> The output of **ECB** and **CBC** is not AES under the key in **Key**: the key is the SHA-256 hash of the bytes, and Windows adds a padding of its own, so the output is longer than the data even with **NoPadding**. The AES test vectors of FIPS 197 and NIST SP 800-38A do not come out, and other AES software cannot read the output. A **Key** of any length is accepted, a key of 5 or 40 bytes included, and a program is not told that the key is not an AES key. + In this sample, the default mode **CBC** encrypts two equal blocks. The first two blocks of the output are equal, and so are the last two, which come from the two paddings. **Key** is the 16-byte key of the examples of NIST SP 800-38A, and the mode hashes it before it uses it. ```tb check_build check_run projname=aes-class @@ -176,7 +185,9 @@ Debug.Print HexOf(cipher) ' 4a25e445195fc88dd0b7fff307d49a9a3daa1722a6ebdf652 ### The CNG modes + The modes **CFB**, **OFB**, **CTR** and **GCM** open the CNG provider for AES, set its chaining mode to `ChainingModeCFB` (**CFB**, **OFB**, **CTR**) or `ChainingModeGCM` (**GCM**), and make the key from the bytes of **Key** as they are. The key length is the length of the array: 16, 24 and 32 bytes give AES-128, AES-192 and AES-256. An array of any other length, such as 20 bytes, makes the call fail, and [**KeySize**](#keysize) has no effect: a 32-byte key with **KeySize_128** is an AES-256 key. + The class does not set the message block length of the chaining mode, and CNG then feeds back one byte at a time: the CFB of these modes is CFB with an 8-bit feedback, the mode that NIST SP 800-38A lists as CFB8, and not CFB with a 128-bit feedback. Output of other software in CFB mode, which is usually CFB128, is different from it. @@ -232,7 +243,9 @@ Debug.Print HexOf(cipher) ' 3b79424c9c0dd436bace9e0ed4586a4f32b9 ``` + The IV of these modes is passed to CNG as it is, from the first element, with its length. CNG needs 16 bytes for CFB, and an IV of another length, such as 12 bytes, makes the call fail. With no IV set, the class passes none, and CNG uses 16 zero bytes: the output equals that of an IV of 16 zero bytes. + ### Reading the output with other software @@ -249,6 +262,7 @@ What the standard decryption returns is the data followed by the padding that [* ### Data that is not checked + None of the modes but **GCM** checks that the data is the data that was encrypted. A decryption with a wrong key, a wrong IV or damaged bytes gives other bytes, and nothing reports it. With the **PKCS7** padding of the default, the padding check rejects most of these results, about 255 of 256, and the decryption fails; the rest pass as data. With **NoPadding** or **ISO10126** there is no check, and [**DecryptString**](#decryptstring) returns whatever text the bytes decode to. ```tb check_build check_run @@ -276,6 +290,7 @@ text = reader.DecryptString(writer.EncryptString("Hello, AES!")) Debug.Print Len(text) > 0 ' True Debug.Print text = "Hello, AES!" ' False ``` + ## GCM mode @@ -288,7 +303,12 @@ GCM encrypts and authenticates. A **cAes** in GCM mode works in these steps. To decrypt, a **cAes** with the same key needs the same **IV**, **GcmTag** and **GcmAAD**. Windows checks the tag against the ciphertext and the additional data. When the check fails, [**DecryptBytes**](#decryptbytes) raises the error described under [Failures](#failures) and [**DecryptString**](#decryptstring) returns an empty string. No text is recorded in [**LastError**](#lasterror). -**PaddingMode** applies in GCM mode as in the others. With the default **PKCS7** the ciphertext is 1 to 16 bytes longer than the plaintext, which is not what other GCM software produces, and the tag is a tag of the padded data. Use **NoPadding** to get standard GCM output. The sample is test case 4 of the GCM specification by McGrew and Viega, the document that NIST SP 800-38D is based on: a 60-byte plaintext, 20 bytes of additional data and the standard IV length of 96 bits. + +> [!NOTE] 1.0.0.4 +> **PaddingMode** applies in GCM mode as in the others. With the default **PKCS7** the ciphertext is 1 to 16 bytes longer than the plaintext, which is not what other GCM software produces, and the tag is a tag of the padded data. Use **NoPadding** to get standard GCM output. + + +The sample is test case 4 of the GCM specification by McGrew and Viega, the document that NIST SP 800-38D is based on: a 60-byte plaintext, 20 bytes of additional data and the standard IV length of 96 bits. ```tb check_build check_run projname=aes-class Dim aes As cAes = New cAes(AesKeySize.KeySize_128, AesCipherMode.GCM, AesPaddingMode.NoPadding) @@ -351,7 +371,9 @@ Debug.Print Err.Number ``` A decryption with no tag set fails in the same way, after the line `DoDecryptCNG_GCM failed: GCM tag not set for decryption`. A tag of 16 bytes is the length that the encryption makes. The class passes the length of **GcmTag** to Windows as it is: a tag cut to its first 12 bytes is accepted and the decryption returns the plaintext, and one cut to 4 bytes makes Windows fail with an invalid parameter. + + > [!WARNING] 1.0.0.4 > A GCM encryption with no IV set makes a random 12-byte nonce, uses it, and keeps it nowhere. [**IV**](#iv) stays empty. A decryption with no IV uses a nonce of 12 zero bytes, so its tag check fails and the data cannot be decrypted. The encryption raises no error. Set **IV** before every GCM encryption, and keep it with the ciphertext. @@ -374,7 +396,9 @@ Debug.Print HexOf(first) = HexOf(second) ' False Debug.Print helper.IsArrayEmpty(ivBytes) ' True ``` + GCM requires that no nonce is used twice with the same key (NIST SP 800-38D). The class never changes **IV**, so two encryptions in a row use the same nonce unless the program sets a new **IV** between them. + ## Failures @@ -385,9 +409,11 @@ A failed call does not tell why, and what the caller gets depends on the member. - [**SetKeyFromString**](#setkeyfromstring) raises an error of its own, `vbObjectError + 2`. - [**GenerateRandomIV**](#generaterandomiv) and [**Cleanup**](#cleanup) cannot fail, and the properties raise nothing. + > [!NOTE] 1.0.0.4 > A program that calls **EncryptBytes** or **DecryptBytes** needs an `On Error` statement, and cannot tell a missing key from a wrong tag or a damaged ciphertext by the error. The Debug Console of the IDE shows more: the package prints lines with `Debug.Print`, such as `DecryptBytes error: Key not set` and `BCryptEncrypt failed: 0xC0000206` followed by a message, and a built program writes none of them. + > [!WARNING] 1.0.0.4 > The string members hide a failure in an empty string. **DecryptString** of a GCM message whose tag does not match, of a message with a wrong key in a mode with padding, and of Base64 text that is not valid, all give the same empty string as an empty plaintext would, and **EncryptString** gives an empty string for a missing key. A program must test the result with `Len`. @@ -400,6 +426,7 @@ These are the causes of a failure in the source: - the Base64 text of [**DecryptString**](#decryptstring) is not valid; - Windows reports an error, for example when the CNG provider is not available. + [**LastError**](#lasterror) holds a text only for the failures of the Base64, UTF-8 and padding functions of [**cAesHelper**](cAesHelper#lasterror), and is never cleared. `Err.Number` can also be 9 after a call that succeeded: the class tests its arrays with [**IsArrayEmpty**](cAesHelper#isarrayempty), which leaves the error 9 in `Err` when an array was never sized, for example when no IV is set in the mode **CFB**. A program should not take a non-zero `Err.Number` after a call that raised nothing for a failure. ```tb check_build check_run @@ -416,6 +443,7 @@ Err.Clear Debug.Print Len(aes.EncryptString("text")) ' 0 Debug.Print Len(aes.LastError) ' 0 ``` + ## Constructors @@ -437,7 +465,9 @@ Syntax: **Dim** *aes* **As cAes = New cAes** ( *keySize*, *cipherMode*, *padding *paddingMode* : *required* An [**AesPaddingMode**](mAesConstants#aespaddingmode) member, stored in [**PaddingMode**](#paddingmode). + With no arguments the object starts with the values in [Defaults](#defaults). The constructor does not check the values. The class has the attribute `[COMCreatable(False)]`, so a program creates it with `New` and not with `CreateObject`. + ```tb check_build check_run Dim aes As cAes = New cAes(AesKeySize.KeySize_128, AesCipherMode.GCM, AesPaddingMode.NoPadding) @@ -487,7 +517,9 @@ Whether the Windows key objects of the object exist. **Boolean**, read-only. Def Syntax: *object*.**IsInitialized** + An encrypt or a decrypt call makes the key objects, and **IsInitialized** is **True** after one succeeded in making them. The setters of [**Key**](#key), [**IV**](#iv), [**KeySize**](#keysize), [**CipherMode**](#ciphermode) and [**PaddingMode**](#paddingmode), [**SetKeyFromString**](#setkeyfromstring) and [**Cleanup**](#cleanup) set it to **False**. The property does not say that the key is valid or that the last call succeeded. No call needs it: each call makes its own key objects. + ```tb check_build check_run Dim aes As New cAes @@ -640,10 +672,13 @@ cipher = aes.EncryptBytes(plain) Debug.Print UBound(cipher) + 1 ' 32 ``` + With **NoPadding** in the modes **CFB**, **OFB**, **CTR** and **GCM**, the output has the length of the input. In the modes **ECB** and **CBC** it never does. **ISO10126** gives the output of **NoPadding**, and no padding is added. With **ZeroPadding** the decryption removes every zero byte at the end of the data, and with the data `41 42 00 00` it returns the two bytes `41 42`. + + > [!WARNING] 1.0.0.4 > With **ZeroPadding**, the class gives back less than it was given when the data ends in zero bytes. The decryption removes every zero byte at the end of the data, and not only the ones that the encryption added, and raises no error. Data that is all zero bytes fails the decryption. Use **PKCS7** for data that can end in zero bytes. @@ -656,7 +691,9 @@ Releases the Windows handles of the object. Syntax: *object*.**Cleanup** + The object holds a Crypto API provider and key and a CNG algorithm and key after an encrypt or a decrypt call. **Cleanup** closes and destroys them, and sets [**IsInitialized**](#isinitialized) to **False**. The class calls it when the object is destroyed, so a program needs it only to release the handles earlier. The next encrypt or decrypt call makes new ones, so the object stays usable. + **Cleanup** does not clear the memory of [**Key**](#key), [**IV**](#iv), [**GcmTag**](#gcmtag) or [**GcmAAD**](#gcmaad), and does not empty them. @@ -766,8 +803,11 @@ Fills [**IV**](#iv) with 16 random bytes, and returns them. Syntax: *object*.**GenerateRandomIV** ( ) **As Byte()** + Returns a **Byte** array of 16 bytes, which is also the new value of **IV**. The method calls `Randomize`, which mixes the system timer into the state of `Rnd` for the whole program, and then takes 16 values of `Rnd`. + + > [!WARNING] 1.0.0.4 > The bytes come from `Rnd`, a pseudo-random generator, and not from a cryptographic one; the comment in the source says that the Crypto API would give better randomness. The state of `Rnd` has 24 bits: its sequence repeats after 16,777,216 values, so the method can return at most 16,777,216 different IVs, and two calls that start from the same state of `Rnd` return the same IV. A program that needs an IV that cannot be predicted can set [**IV**](#iv) from a source of its own. @@ -838,6 +878,7 @@ Syntax: *object*.**SetGcmAADFromString** ( *aadString* [, *encoding*] ) The method does not raise an error. When the conversion fails, it prints `SetGcmAADFromString failed: ` and the description with `Debug.Print`, which a built program does not show, and leaves **GcmAAD** as it was. The program that decrypts has to convert the string in the same way, since the bytes with another encoding are other bytes. [**EncryptString**](#encryptstring) uses UTF-8 for its text, and this method does not. + > [!WARNING] 1.0.0.4 > An empty string does not remove the additional data. The method fails for an empty string and keeps the data that was set before, and only the Debug Console shows the message `SetGcmAADFromString failed: Unspecified error`. The next GCM encryption then authenticates the old data. To remove the data, assign an array that was never sized to [**GcmAAD**](#gcmaad). @@ -872,9 +913,11 @@ Syntax: *object*.**SetKeyFromString** ( *keyString* [, *encoding*] ) The method converts *keyString* to bytes with **StrConv** and makes an array of the length that [**KeySize**](#keysize) names, 16, 24 or 32 bytes: the first bytes of the conversion when it is longer, and the bytes followed by zero bytes when it is shorter. The array is the new value of **Key**. The method does not use the string in any other way. + > [!WARNING] 1.0.0.4 > There is no key derivation. The key is the bytes of the text, cut or extended with zero bytes: a string longer than the key is cut without a message, so that two strings that begin with the same 32 bytes give the same key (with **KeySize_256**), and a short string is a short key followed by zeros. The modes **ECB** and **CBC** then hash the bytes once with SHA-256, without a salt; the modes **CFB**, **OFB**, **CTR** and **GCM** use them as the key. With the default encoding, a character that is not in the ANSI code page of the system is converted to another character, and the same text gives different bytes on systems with another code page. + The method raises an error when *keyString* is empty: **Err.Number** is `vbObjectError + 2`, **Err.Source** is `cAes.SetKeyFromString` and **Err.Description** is `Key cannot be empty`. **Key** is not changed, and [**IsInitialized**](#isinitialized) is set to **False**. When **KeySize** has a value that is not a member of the enumeration, the key is left as it was and no error is raised. ```tb check_build check_run projname=aes-class @@ -900,6 +943,7 @@ Debug.Print Err.Number - vbObjectError ' 2 Debug.Print Err.Source ' cAes.SetKeyFromString Debug.Print Err.Description ' Key cannot be empty ``` + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_Aes/cAesHelper.md b/docs/Reference/Contributed/TBMANLIB_Aes/cAesHelper.md index c8f2aaaf..43d90ed0 100644 --- a/docs/Reference/Contributed/TBMANLIB_Aes/cAesHelper.md +++ b/docs/Reference/Contributed/TBMANLIB_Aes/cAesHelper.md @@ -38,8 +38,9 @@ A member that fails does not tell why, and what the caller gets depends on the t - **Base64Encode** and **Utf8BytesToString**, the members that return a **String**, return an empty string and raise no error. They leave the error in `Err`, which is not cleared: after an array that was never sized, `Err.Number` is 9. - **IsArrayEmpty** raises no error, and leaves `Err.Number` at 9 when it tests an array that was never sized. + > [!NOTE] 1.0.0.4 -> The members that raise **Unspecified error** do so for the failures that [**cAes**](cAes) cannot recover from as well. A program that calls one of them with an input that can be empty or damaged needs an `On Error` statement, and cannot tell the causes apart by the error. +> The members that raise **Unspecified error** do so for the failures that [**cAes**](cAes) cannot recover from as well. A program that calls one of them with an input that can be empty or damaged needs an `On Error` statement, and cannot tell the causes apart by the error or by [**LastError**](#lasterror), which holds a general text and not the cause. Every member that reads an array reads its first element as the element with index 0. An array with another lower bound fails: **Base64Encode** returns an empty string with the [**LastError**](#lasterror) text `Base64 encoding exception: Subscript out of range`, and **ApplyPKCS7Padding** raises **Unspecified error**, after it has set the text `PKCS7 padding exception: Subscript out of range`. @@ -98,7 +99,9 @@ Syntax: *object*.**ApplyPKCS7Padding** ( *data*, *blockSize* ) **As Byte()** *blockSize* : *required* A **Long**, the block size in bytes. [**cAes**](cAes) passes 16. The padding bytes hold their own count, so the value must be at most 255. + The function adds from 1 to *blockSize* bytes, to make the length a multiple of *blockSize*. Each added byte holds the number of bytes added. Data whose length is already a multiple of *blockSize* gets a whole block, *blockSize* bytes holding the value *blockSize*. The result is a new array with index 0 as its first element. + An array that was never sized raises **Unspecified error**, as [Failures](#failures) describes. @@ -132,7 +135,9 @@ Syntax: *object*.**ApplyZeroPadding** ( *data*, *blockSize* ) **As Byte()** *blockSize* : *required* A **Long**, the block size in bytes. [**cAes**](cAes) passes 16. + The function adds from 1 to *blockSize* zero bytes, to make the length a multiple of *blockSize*. Data whose length is already a multiple of *blockSize* gets a whole block of zero bytes. An array that was never sized gives one block of *blockSize* zero bytes, and no error. + ```tb check_build check_run Dim helper As New cAesHelper @@ -158,7 +163,9 @@ Syntax: *object*.**Base64Decode** ( *base64* ) **As Byte()** *base64* : *required* A **String** of Base64 text. + The function calls the Windows function `CryptStringToBinaryW` with the format `CRYPT_STRING_BASE64`, Base64 without a header. Line breaks and spaces in the text are skipped. The result is a new array with index 0 as its first element. + An empty string and text that Windows cannot decode both raise **Unspecified error**, and [**LastError**](#lasterror) holds `Base64 decoding exception: Unspecified error`. @@ -172,7 +179,9 @@ Syntax: *object*.**Base64Encode** ( *data* ) **As String** *data* : *required* A **Byte** array, passed by reference. + The function calls the Windows function `CryptBinaryToStringW` with the format `CRYPT_STRING_BASE64` and the flag `CRYPT_STRING_NOCRLF`. The result is Base64 text of the usual alphabet (`A`-`Z`, `a`-`z`, `0`-`9`, `+` and `/`) with `=` at the end when the last group is short, on one line, with no header and no line break. An array that was never sized gives an empty string, with no text in [**LastError**](#lasterror). + ```tb check_build check_run Dim helper As New cAesHelper @@ -192,7 +201,9 @@ Syntax: *object*.**IsArrayEmpty** ( *arr* ) **As Boolean** *arr* : *required* A **Byte** array variable, passed by reference. + Returns **True** when the array has no elements, because its upper bound is lower than its lower bound or because it was never given a size. Returns **False** otherwise. The function does not raise an error and does not use [**LastError**](#lasterror). It leaves `Err.Number` at 9 when the array was never sized, because it tests the array with `UBound` under `On Error Resume Next` and does not clear the error. + A program uses it to test the arrays that [**cAes**](cAes) keeps in its properties, such as [**Key**](cAes#key) and [**GcmTag**](cAes#gcmtag), when they may not be set. @@ -216,9 +227,11 @@ Syntax: *object*.**RemovePKCS7Padding** ( *data* ) **As Byte()** *data* : *required* A **Byte** array, passed by reference. The function does not change it. + The function reads the last byte as the count of padding bytes. It checks that the count is from 1 to 16, that it is not greater than the length of the array, and that the last *count* bytes are all equal to it. The largest count is the constant **BLOCK_SIZE_128**, whatever block size the data was padded with; the function does not check that the length of the array is a multiple of 16. When a check fails, and when the padding is the whole array, the function raises **Unspecified error**, and [**LastError**](#lasterror) holds `PKCS7 unpadding exception: Unspecified error`. The check cannot tell padded data from other data that happens to end in valid padding bytes, so it does not prove that the data is intact. + ### RemoveZeroPadding {: .no_toc } @@ -230,7 +243,9 @@ Syntax: *object*.**RemoveZeroPadding** ( *data* ) **As Byte()** *data* : *required* A **Byte** array, passed by reference. The function does not change it. + The function finds the last byte that is not zero and returns the array up to and including that byte. It does not know how many bytes were added, so it also removes zero bytes that belong to the data. An array that holds only zero bytes raises **Unspecified error**, and [**LastError**](#lasterror) holds `Zero unpadding exception: Unspecified error`. + ```tb check_build check_run Dim helper As New cAesHelper @@ -256,7 +271,9 @@ Syntax: *object*.**StringToUtf8Bytes** ( *text* ) **As Byte()** *text* : *required* A **String**. + The function calls the Windows function `WideCharToMultiByte` with the UTF-8 code page. The result is a new array with index 0 as its first element, without a byte order mark and without a terminating zero byte. An empty string raises **Unspecified error**, and [**LastError**](#lasterror) holds `String conversion exception: Unspecified error`. + [**EncryptString**](cAes#encryptstring) encodes its text with this function. @@ -280,7 +297,9 @@ Syntax: *object*.**Utf8BytesToString** ( *bytes* ) **As String** *bytes* : *required* A **Byte** array, passed by reference. + The function calls the Windows function `MultiByteToWideChar` with the UTF-8 code page. A byte sequence that is not valid UTF-8 becomes the character U+FFFD. An array with no elements gives an empty string. An array that was never sized also gives an empty string, with `Err.Number` left at 9 and the [**LastError**](#lasterror) text `Byte conversion exception: Subscript out of range`. + [**DecryptString**](cAes#decryptstring) decodes its result with this function. diff --git a/docs/Reference/Contributed/TBMANLIB_Aes/index.md b/docs/Reference/Contributed/TBMANLIB_Aes/index.md index 0ae66013..e96dde32 100644 --- a/docs/Reference/Contributed/TBMANLIB_Aes/index.md +++ b/docs/Reference/Contributed/TBMANLIB_Aes/index.md @@ -41,8 +41,9 @@ Debug.Print aes.DecryptString(cipherText) ' Hello, AES! The text that **EncryptString** returns is Base64 of the ciphertext alone. The program keeps the IV, and in GCM mode the tag, and sets them in the object that decrypts. + > [!WARNING] 1.0.0.4 -> The name of a cipher mode is not always the mode that runs: **ECB** runs CBC, **CBC** runs ECB, and **OFB** and **CTR** run CFB. Neither **ECB** nor **CBC** uses the IV, so the same key and plaintext always give the same ciphertext, and the default mode, **CBC**, gives equal ciphertext blocks for equal plaintext blocks. A test that decrypts what it has encrypted does not show this. [Known problems in version 1.0.0.4](#known-problems-in-version-1004) lists this and the other defects. +> The name of a cipher mode is not always the mode that runs: **ECB** runs CBC, **CBC** runs ECB, and **OFB** and **CTR** run CFB, whose feedback has 8 bits where the standard CFB has 128. Neither **ECB** nor **CBC** uses the IV, so the same key and plaintext always give the same ciphertext, and the default mode, **CBC**, gives equal ciphertext blocks for equal plaintext blocks. A test that decrypts what it has encrypted does not show this. [Known problems in version 1.0.0.4](#known-problems-in-version-1004) lists this and the other defects. * TOC {:toc} @@ -146,3 +147,7 @@ The module **mAesUsage** holds ten `Public Sub` procedures that show the package - [cAesHelper](cAesHelper) class -- the Base64, UTF-8 and padding functions - [mAesConstants](mAesConstants) module -- the enumerations and constants - [Importing a package from TWINSERV](../../../../Features/Packages/Importing-TWINSERV) -- adding a published package to a project + +## Test cases + +The notes and warnings of this section are checked by the test cases in [TBMANLIB_Aes-tests.twinproj](TBMANLIB_Aes-tests.twinproj), a twinBASIC project. It uses TBMANLIB_Aes as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/TBMANLIB_Aes/mAesConstants.md b/docs/Reference/Contributed/TBMANLIB_Aes/mAesConstants.md index 555e5f13..85b4c311 100644 --- a/docs/Reference/Contributed/TBMANLIB_Aes/mAesConstants.md +++ b/docs/Reference/Contributed/TBMANLIB_Aes/mAesConstants.md @@ -12,6 +12,7 @@ Holds the three enumerations of the package, the sizes of AES keys and blocks, t The enumerations are the types of the [**CipherMode**](cAes#ciphermode), [**KeySize**](cAes#keysize) and [**PaddingMode**](cAes#paddingmode) properties of **cAes** and of the parameters of its constructor. A member is written with its enumeration, as in `AesCipherMode.GCM`. The constants are written with the module, as in `mAesConstants.KEY_SIZE_256`. + ```tb check_build check_run Debug.Print AesCipherMode.GCM ' 6 Debug.Print AesPaddingMode.NoPadding ' 3 @@ -20,6 +21,7 @@ Debug.Print mAesConstants.KEY_SIZE_192 ' 24 Debug.Print mAesConstants.BLOCK_SIZE_128 ' 16 Debug.Print mAesConstants.VERSION ' 1.0.0 ``` + * TOC {:toc} @@ -40,8 +42,9 @@ The cipher mode that a [**cAes**](cAes) uses. Default: **CBC**. | **CTR** | 5 | CNG | CFB with an 8-bit feedback | | **GCM** | 6 | CNG | GCM, with a 12-byte nonce and a 16-byte tag | + > [!WARNING] 1.0.0.4 -> The name of a mode is not always the mode that runs. **ECB** runs CBC with an IV of zero bytes, **CBC** runs ECB, and **OFB** and **CTR** run CFB. Neither **ECB** nor **CBC** uses the IV that the program sets, so the same key and plaintext always give the same ciphertext, and the default mode, **CBC**, gives equal ciphertext blocks for equal plaintext blocks. Encryption and decryption agree with each other, so a round trip succeeds and shows nothing. [**cAes**](cAes#which-mode-runs) describes each mode in detail. +> The name of a mode is not always the mode that runs. **ECB** runs CBC with an IV of zero bytes, **CBC** runs ECB, and **OFB** and **CTR** run CFB, whose feedback has 8 bits where the standard CFB has 128. Neither **ECB** nor **CBC** uses the IV that the program sets, so the same key and plaintext always give the same ciphertext, and the default mode, **CBC**, gives equal ciphertext blocks for equal plaintext blocks. Encryption and decryption agree with each other, so a round trip succeeds and shows nothing. [**cAes**](cAes#which-mode-runs) describes each mode in detail. The Crypto API is the older interface of Windows (`advapi32.dll`), and CNG, Cryptography API: Next Generation, the newer one (`bcrypt.dll`). **cAes** uses the Crypto API for **ECB** and **CBC**, and CNG for the other four modes. @@ -70,6 +73,7 @@ How [**cAes**](cAes) fills the last block of the data before it encrypts, and ho | **NoPadding** | 3 | Adds nothing. | Takes nothing off. | | **ISO10126** | 4 | Adds nothing, as **NoPadding**. | Takes nothing off, as **NoPadding**. | + > [!WARNING] 1.0.0.4 > **ISO10126** is not implemented. It behaves as **NoPadding**, and no error says so. The comment in the source calls the member "ISO/IEC 7816-4 padding", a scheme that differs from ISO 10126, and neither is applied. @@ -100,10 +104,11 @@ The length in bytes of a 256-bit key: 32, a **Long**. ### VERSION {: .no_toc } -The text `1.0.0`, a **String**. It is the version in the headers of the source files. The version of the package is 1.0.0.2, and **VERSION** is not used by any code of the package. +The text `1.0.0`, a **String**. It is the version in the headers of the source files. The version of the package is 1.0.0.4, and **VERSION** is not used by any code of the package. ## Windows constants + These constants have the names and, with five exceptions, the values of the constants that the Windows header `wincrypt.h` defines. **cAes** and **cAesHelper** pass them to the Crypto API functions. A program does not need them. | Constant | Value | Names | Used by the package | @@ -135,6 +140,7 @@ These constants have the names and, with five exceptions, the values of the cons | **CRYPT_STRING_NOCRLF** | &H40000000 | The `CryptBinaryToString` flag: no line breaks | yes | All are **Long** constants. They are meant for the Crypto API functions that the module **mWin32CryptoAPI** wraps, described on the [landing page](.#declaration-modules). + Five values differ from `wincrypt.h`: @@ -146,7 +152,9 @@ Five values differ from `wincrypt.h`: | **CRYPT_MODE_OFB** | 4 | 3 | | **ZERO_PADDING** | 2 | 3 | + **cAes** gives the value of **CRYPT_MODE_ECB** to the Crypto API when the mode is **ECB**, and the Crypto API reads 1 as CBC. That is why the modes **ECB** and **CBC** are swapped, as the warning under [AesCipherMode](#aesciphermode) says. Nothing in the package reads **ZERO_PADDING**. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/Helpers.md b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/Helpers.md index 148937d7..0f1ec997 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/Helpers.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/Helpers.md @@ -10,6 +10,7 @@ has_toc: false Two small classes that compute values and draw nothing: **cColorHelper** chooses a colour for a number, and **cLayoutHelper** works out positions and rectangles in the console window. + **cColorHelper** needs no console and no state. **cLayoutHelper** is made with a [**cConsole**](../TBMANLIB_ConsoleCore/cConsole), and reads its window size each time a member is called. Both are created with `New`. ```tb check_build check_run @@ -27,16 +28,20 @@ Debug.Print helper.CPUUsageColor(72) ``` The four numbers are **ConsoleColor** values: 10 is **Green**, 14 is **Yellow** and 12 is **Red**. + * TOC {:toc} ## cColorHelper class + Every colour parameter and result is a [**ConsoleColor**](../TBMANLIB_ConsoleCore/Types#consolecolor), and every function is called on an object, as in `helper.DimColor(...)`. The numbers that give the choice of a colour are in each entry; they are fixed in the code. In the syntax lines of this section, *object* is a **cColorHelper**. Known problems in version 1.0.0.1: [**BrightenColor**](#brightencolor) returns 16 for **DarkGray**, [**DimColor**](#dimcolor) returns **DarkGray** for every dark colour, [**IntensityColor**](#intensitycolor) ignores its *baseColor*, and [**GradientColor**](#gradientcolor) chooses among three colours and does not blend. + + ### BrightenColor {: .no_toc } @@ -60,7 +65,9 @@ Debug.Print helper.BrightenColor(ConsoleColor.Red) ' 16 ' 15 ``` + + ### CPUUsageColor {: .no_toc } @@ -72,7 +79,9 @@ Syntax: *object*.**CPUUsageColor** ( *usage* ) **As ConsoleColor** : *required* A **Double**, a percentage. **Green** below 50, **Yellow** from 50 up to, but not including, 80, and **Red** from 80. The value is not limited to 0 to 100. + + ### CycleColor {: .no_toc } @@ -87,7 +96,9 @@ Syntax: *object*.**CycleColor** ( *index* , *colors* ) **As ConsoleColor** : *required* An array of **ConsoleColor**, passed by reference, that starts at index 0. Returns `colors(index Mod count)`, with a negative remainder raised by *count*. Returns **Gray** for an array with no element. The code takes the result of `Mod` as an index, so an array whose lower bound is not 0 gives a wrong element or error 9, *Subscript out of range*. + + ### DimColor {: .no_toc } @@ -99,7 +110,9 @@ Syntax: *object*.**DimColor** ( *color* ) **As ConsoleColor** : *required* A [**ConsoleColor**](../TBMANLIB_ConsoleCore/Types#consolecolor). For a colour from **Blue** (9) to **White** (15) the result is the colour less 8, the dark colour of the same hue: **Red** gives **DarkRed**, **White** gives **Gray**. For every colour from **Black** to **DarkGray**, the result is **DarkGray**; so the dimmer version of **Black** is the brighter **DarkGray**. + + ### DiskUsageColor {: .no_toc } @@ -111,7 +124,9 @@ Syntax: *object*.**DiskUsageColor** ( *usage* ) **As ConsoleColor** : *required* A **Double**, a percentage. **Green** below 80, **Yellow** from 80 up to, but not including, 95, and **Red** from 95. + + ### GetContrastColor {: .no_toc } @@ -123,7 +138,9 @@ Syntax: *object*.**GetContrastColor** ( *bgColor* ) **As ConsoleColor** : *required* A [**ConsoleColor**](../TBMANLIB_ConsoleCore/Types#consolecolor), the background. Returns **White** for a background from **Black** (0) to **DarkGray** (8), and **Black** for **Blue** (9) and up. **DarkYellow** and **Gray** are in the first group, so white text is chosen on a grey background. + + ### GradientColor {: .no_toc } @@ -141,7 +158,9 @@ Syntax: *object*.**GradientColor** ( *value* , *min* , *max* , *color1* , *color : *required* **ConsoleColor** values. The position of *value* in the range, limited to 0 to 1, gives *color1* below 0.25, *color2* from 0.25 up to, but not including, 0.75, and *color3* from 0.75. The comment of the source speaks of a smooth transition; the console has 16 colours and the function does not blend. + + ### HeatmapColor {: .no_toc } @@ -165,7 +184,9 @@ Syntax: *object*.**HeatmapColor** ( *value* , *min* , *max* [ , *lowColor* [ , * : *optional* A **ConsoleColor**, for positions from 0.8. The default is **Red**. The position is `(value - min) / (max - min)`, limited to 0 to 1. + + ### IntensityColor {: .no_toc } @@ -183,7 +204,9 @@ Syntax: *object*.**IntensityColor** ( *value* , *min* , *max* [ , *baseColor* ] : *optional* A **ConsoleColor**. The default is **Gray**. It is not used: the function never reaches the line that returns it. The level is `CLng(position * 7)`, from 0 to 7, which rounds half to even. The result is **Black**, **DarkBlue**, **DarkGreen**, **DarkCyan**, **DarkRed**, **DarkMagenta**, **DarkYellow** or **DarkGray** for the levels 0 to 7, in that order. This is the order of the numbers of the colours, not an order of brightness, so a rising value does not give a steadily lighter colour. + + ### IsDarkColor {: .no_toc } @@ -195,7 +218,9 @@ Syntax: *object*.**IsDarkColor** ( *color* ) **As Boolean** : *required* A [**ConsoleColor**](../TBMANLIB_ConsoleCore/Types#consolecolor). Returns **True** for **Black** (0) up to **DarkGray** (8), which includes **Gray**. + + ### MemoryUsageColor {: .no_toc } @@ -207,9 +232,11 @@ Syntax: *object*.**MemoryUsageColor** ( *usage* ) **As ConsoleColor** : *required* A **Double**, a percentage. **Green** below 70, **Yellow** from 70 up to, but not including, 90, and **Red** from 90. + ## cLayoutHelper class + Computes positions and [**ConsoleRect**](../TBMANLIB_ConsoleCore/Types#consolerect) rectangles from the size of the window of a console. A rectangle has inclusive edges, and columns and rows are counted from 0. Every member except [**SpacedPositions**](#spacedpositions) reads [**Width**](../TBMANLIB_ConsoleCore/cConsole#width) and [**Height**](../TBMANLIB_ConsoleCore/cConsole#height) of the console when it is called, so the results follow a window that the user resizes; without a console they are computed for 80 columns by 25 rows. In the syntax lines of this section, *object* is a **cLayoutHelper**. The members that give several rectangles or numbers return them in a **Variant** that holds an array, with indexes from 0, in the order the entry names. The rectangles of one result do not overlap and have no gap between them, except where the entry says so. A division by a count of 0 raises error 11, *Division by zero*. @@ -234,7 +261,8 @@ Debug.Print columns(0) & " " & columns(1) & " " & columns(2) The output is that of a console of 80 columns by 25 rows, which is also the size that a program without a console gets. > [!NOTE] 1.0.0.1, BETA 1005 -> The members that give a **ConsoleRect** or a **ConsolePosition** can be called, but a program that references the packages TBMANLIB_ConsoleCore and TBMANLIB_ConsoleComponents cannot read the result: the compiler refuses the members of the value and its assignment to a variable of the type. See [The two packages](./#the-two-packages). The members that give a **Long** or an array of **Long** are not affected. +> A program that references the packages TBMANLIB_ConsoleCore and TBMANLIB_ConsoleComponents cannot read what the members that give a **ConsoleRect** or a **ConsolePosition** return: the compiler refuses the members of the value and its assignment to a variable of the type. The members that give a **Long** or an array of **Long** compile. A **cLayoutHelper** cannot be made with the **cConsole** of the published package at all; see [The two packages](./#the-two-packages). + ### New {: .no_toc } @@ -248,6 +276,7 @@ Syntax: **New cLayoutHelper** ( *console* ) Keeps the console. The console does not have to be in full-screen mode. + ### Center {: .no_toc } @@ -259,7 +288,9 @@ Syntax: *object*.**Center** ( *width* , *height* ) **As ConsolePosition** : *required* **Long** values, the size of the area in columns and rows. Returns a [**ConsolePosition**](../TBMANLIB_ConsoleCore/Types#consoleposition) with **Col** `(Width - width) \ 2` and **Row** `(Height - height) \ 2`. The values are negative when the area is larger than the window. + + ### CenterRect {: .no_toc } @@ -271,7 +302,9 @@ Syntax: *object*.**CenterRect** ( *width* , *height* ) **As ConsoleRect** : *required* **Long** values. The rectangle starts at the position that [**Center**](#center) returns and is *width* columns wide and *height* rows high. [**CenterRect**](../TBMANLIB_ConsoleCore/cConsole#centerrect) of **cConsole** gives the same rectangle. + + ### CenterX {: .no_toc } @@ -283,7 +316,9 @@ Syntax: *object*.**CenterX** ( *width* ) **As Long** : *required* A **Long**. Returns `(Width - width) \ 2`. + + ### CenterY {: .no_toc } @@ -295,7 +330,9 @@ Syntax: *object*.**CenterY** ( *height* ) **As Long** : *required* A **Long**. Returns `(Height - height) \ 2`. + + ### ClampRect {: .no_toc } @@ -307,7 +344,9 @@ Syntax: *object*.**ClampRect** ( *rect* ) **As ConsoleRect** : *required* A [**ConsoleRect**](../TBMANLIB_ConsoleCore/Types#consolerect). Raises **Left** and **Top** to 0 when they are negative, and lowers **Right** and **Bottom** to the last column and the last row of the window. A rectangle that lies wholly outside the window is cut to an invalid one; [**IsValidRect**](#isvalidrect) tells. + + ### EvenColumns {: .no_toc } @@ -322,7 +361,9 @@ Syntax: *object*.**EvenColumns** ( *count* , *margin* ) **As Variant** : *required* A **Long**, the number of cells to leave free on every side of the window. Returns *count* rectangles, from left to right. Each is `(Width - 2 * margin) \ count` columns wide and reaches from the row *margin* to the row `Height - 1 - margin`. The last one is widened to the right margin, so it takes the remainder. + + ### EvenRows {: .no_toc } @@ -337,7 +378,9 @@ Syntax: *object*.**EvenRows** ( *count* , *margin* ) **As Variant** : *required* A **Long**, the number of cells to leave free on every side of the window. Returns *count* rectangles, from top to bottom, each as wide as the window less the margins and `(Height - 2 * margin) \ count` rows high. The last one is heightened to the bottom margin. + + ### GetRectHeight {: .no_toc } @@ -349,7 +392,9 @@ Syntax: *object*.**GetRectHeight** ( *rect* ) **As Long** : *required* A [**ConsoleRect**](../TBMANLIB_ConsoleCore/Types#consolerect). Returns `Bottom - Top + 1`, as [**ConsoleRect_Height**](../TBMANLIB_ConsoleCore/Types#consolerect_height) does. + + ### GetRectWidth {: .no_toc } @@ -361,7 +406,9 @@ Syntax: *object*.**GetRectWidth** ( *rect* ) **As Long** : *required* A [**ConsoleRect**](../TBMANLIB_ConsoleCore/Types#consolerect). Returns `Right - Left + 1`, as [**ConsoleRect_Width**](../TBMANLIB_ConsoleCore/Types#consolerect_width) does. + + ### Grid {: .no_toc } @@ -373,7 +420,9 @@ Syntax: *object*.**Grid** ( *cols* , *rows* ) **As Variant** : *required* **Long** values, the number of columns and rows. Returns `cols * rows` rectangles, row by row from the top left. Each cell is `Width \ cols` columns wide and `Height \ rows` rows high, with no gap between cells. The remainder of the division is not covered: the right and bottom edges of the grid can end short of the window. [**CreateGrid**](cConsoleLayout#creategrid) of **cConsoleLayout** leaves a gap and gives the remainder to the last column and row. + + ### HeaderFooter {: .no_toc } @@ -385,7 +434,9 @@ Syntax: *object*.**HeaderFooter** ( *headerHeight* , *footerHeight* ) **As Varia : *required* **Long** values, the number of rows of the header and of the footer. Returns three rectangles as wide as the window: the header (the first *headerHeight* rows), the body (the rows between) and the footer (the last *footerHeight* rows), in that order. + + ### IsValidRect {: .no_toc } @@ -397,7 +448,9 @@ Syntax: *object*.**IsValidRect** ( *rect* ) **As Boolean** : *required* A [**ConsoleRect**](../TBMANLIB_ConsoleCore/Types#consolerect). Returns **True** when `Left <= Right` and `Top <= Bottom`. + + ### Percent {: .no_toc } @@ -409,7 +462,9 @@ Syntax: *object*.**Percent** ( *percentX* , *percentY* ) **As ConsolePosition** : *required* **Double** values, the share of the width and of the height. They are not limited to 0 to 100. Returns a [**ConsolePosition**](../TBMANLIB_ConsoleCore/Types#consoleposition) with **Col** `CLng(Width * percentX / 100)` and **Row** `CLng(Height * percentY / 100)`. + + ### PercentRect {: .no_toc } @@ -421,7 +476,9 @@ Syntax: *object*.**PercentRect** ( *leftPercent* , *topPercent* , *widthPercent* : *required* **Double** values. The first two give the upper left corner, and the last two the size, all as percentages of the width and the height. **Left** is `CLng(Width * leftPercent / 100)`, and **Right** is `CLng(Width * (leftPercent + widthPercent) / 100) - 1`, and **Top** and **Bottom** are computed in the same way from the height. + + ### PercentX {: .no_toc } @@ -433,7 +490,9 @@ Syntax: *object*.**PercentX** ( *percent* ) **As Long** : *required* A **Double**. Unlike [**PercentX**](../TBMANLIB_ConsoleCore/cConsole#percentx) of **cConsole**, this one does not limit it to 0 to 100. Returns `CLng(Width * percent / 100)`. + + ### PercentY {: .no_toc } @@ -445,7 +504,9 @@ Syntax: *object*.**PercentY** ( *percent* ) **As Long** : *required* A **Double**, not limited to 0 to 100. Returns `CLng(Height * percent / 100)`. + + ### Sidebar {: .no_toc } @@ -457,7 +518,9 @@ Syntax: *object*.**Sidebar** ( *sidebarWidth* ) **As Variant** : *required* A **Long**, the number of columns of the sidebar. Returns two rectangles as high as the window: the sidebar (the first *sidebarWidth* columns) and the content area (the rest), in that order. + + ### SpacedPositions {: .no_toc } @@ -478,7 +541,9 @@ Syntax: *object*.**SpacedPositions** ( *count* , *itemWidth* , *spacing* , *star : *required* A **Long**, the column of the first item. Returns *count* **Long** values: the column of item *i* is `startX + i * (itemWidth + spacing)`. This member does not use the size of the window. + + ### SplitHorizontal {: .no_toc } @@ -490,7 +555,9 @@ Syntax: *object*.**SplitHorizontal** ( *topPercent* ) **As Variant** : *required* A **Double**, the share of the height for the top part, in percent. Returns two rectangles as wide as the window: the top one, from row 0 to row `PercentY(topPercent) - 1`, and the bottom one, from row `PercentY(topPercent)` to the last row. The line between the parts is horizontal. The member of the same name in [**cConsoleLayout**](cConsoleLayout#splithorizontal) divides the width instead. + + ### SplitVertical {: .no_toc } @@ -502,7 +569,9 @@ Syntax: *object*.**SplitVertical** ( *leftPercent* ) **As Variant** : *required* A **Double**, the share of the width for the left part, in percent. Returns two rectangles as high as the window: the left one, from column 0 to column `PercentX(leftPercent) - 1`, and the right one, from column `PercentX(leftPercent)` to the last column. The line between the parts is vertical. The member of the same name in [**cConsoleLayout**](cConsoleLayout#splitvertical) divides the height instead. + + ### WithMargins {: .no_toc } @@ -514,6 +583,7 @@ Syntax: *object*.**WithMargins** ( *left* , *top* , *right* , *bottom* ) **As Co : *required* **Long** values, the number of cells to leave free at each side. Returns the rectangle from column *left* and row *top* to column `Width - 1 - right` and row `Height - 1 - bottom`. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsoleLayout.md b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsoleLayout.md index cfa9addd..f7b9b667 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsoleLayout.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsoleLayout.md @@ -10,6 +10,7 @@ has_toc: false Keeps a set of named [**cConsolePanel**](cConsolePanel) objects, creates them in common arrangements (a split, a grid, a sidebar, a header and footer), and draws their borders. + A **cConsoleLayout** is made with a [**cConsole**](../TBMANLIB_ConsoleCore/cConsole). It does not place content: it works out the rectangles of panels and draws the borders and titles of all of them in one call. The program writes the content of each panel into the area that the panel's [**ContentRect**](cConsolePanel#contentrect) returns. ```tb check_build @@ -32,12 +33,14 @@ Debug.Print layout.PanelCount ``` The root panel covers columns 0 to 39 and rows 0 to 11, so its content area is columns 2 to 37 and rows 2 to 9. **list** takes columns 2 to 10 and **detail** columns 12 to 37, both in rows 2 to 9; the sample prints the size of the content area inside the border and padding of each. A program that references the packages TBMANLIB_ConsoleCore and TBMANLIB_ConsoleComponents cannot read the **Rect** of a panel; see [The two packages](./#the-two-packages). + * TOC {:toc} In the syntax lines of this page, *object* is a **cConsoleLayout**. Columns and rows are counted from 0 in the buffer. + ## Panels, names and the root panel Each panel has a name, which is unique in the layout and case-sensitive. [**GetPanel**](#getpanel) finds a panel by it. The layout keeps the panels in the order of creation, which is the order in which [**Render**](#render) draws them, so a later panel is drawn over an earlier one. @@ -64,9 +67,11 @@ Set rightPanel = parts(1) leftPanel.Title = "Files" rightPanel.BorderStyle = PanelBorderStyle.Double ``` + ## Constructor + ### New {: .no_toc } @@ -78,18 +83,22 @@ Syntax: **New cConsoleLayout** ( *console* ) : *required* A [**cConsole**](../TBMANLIB_ConsoleCore/cConsole). Keeps the console, reads its [**Width**](../TBMANLIB_ConsoleCore/cConsole#width) and [**Height**](../TBMANLIB_ConsoleCore/cConsole#height) as the size of the layout, and creates the empty list and the `Scripting.Dictionary` object in which the panels are kept by name. The console does not have to be in full-screen mode yet: the layout reads the console's [**Buffer**](../TBMANLIB_ConsoleCore/cConsole#buffer) each time it draws, not when it is made. + ## Properties + ### PanelCount {: .no_toc } The number of panels in the layout. **Long**, read-only. Syntax: *object*.**PanelCount** + ## Methods + ### AddPanel {: .no_toc } @@ -110,7 +119,9 @@ Syntax: *object*.**AddPanel** ( *name* , *left* , *top* , *width* , *height* [ , : *optional* A **String**, the title on the top border. The default is an empty string, which draws no title. Returns the new [**cConsolePanel**](cConsolePanel), with its default style. The first panel of a layout is its root panel. A name that is in use raises error 457, *This key is already associated with an element of this collection*, from the dictionary, but the panel has been added to the list by then, so [**PanelCount**](#panelcount) counts it, [**Render**](#render) draws it, and no name leads to it. + + ### AddPanelByRect {: .no_toc } @@ -128,7 +139,9 @@ Syntax: *object*.**AddPanelByRect** ( *name* , *rect* [ , *title* ] ) **As cCons : *optional* A **String**. Calls [**AddPanel**](#addpanel) with the left and top edges of *rect* and a width and height of `Right - Left + 1` and `Bottom - Top + 1`. + + ### AutoResize {: .no_toc } @@ -137,7 +150,9 @@ Sets the size of the layout to the current size of the console. Syntax: *object*.**AutoResize** Calls [**Resize**](#resize) with the console's [**Width**](../TBMANLIB_ConsoleCore/cConsole#width) and [**Height**](../TBMANLIB_ConsoleCore/cConsole#height). + + ### Clear {: .no_toc } @@ -146,7 +161,9 @@ Removes every panel. Syntax: *object*.**Clear** Replaces the list and the dictionary with empty ones, and leaves the layout with no root panel. Nothing is erased from the buffer. + + ### ClearAllContent {: .no_toc } @@ -158,7 +175,9 @@ Syntax: *object*.**ClearAllContent** [ ( *theme* ) ] : *optional* A [**cConsoleTheme**](cConsoleTheme). The default is **Nothing**. Calls [**ClearContent**](cConsolePanel#clearcontent) of each panel with the buffer of the console, using the theme's **Foreground** and **Background** colours, or **Gray** on **Black** when no theme is given. Raises error 91 when the console has no buffer. + + ### ClearPanelContent {: .no_toc } @@ -171,7 +190,9 @@ Syntax: *object*.**ClearPanelContent** ( *name* [ , *theme* ] ) *theme* : *optional* A **cConsoleTheme**, as for [**ClearAllContent**](#clearallcontent). + + ### CreateGrid {: .no_toc } @@ -207,7 +228,9 @@ Next ``` The content area of the root panel has 56 columns and 16 rows, so *colStep* is 28 and *rowStep* is 8. The cells are columns 2 to 28 and 30 to 57, and rows 2 to 8 and 10 to 17; the sample prints the size of the content area inside the border and padding of each cell. + + ### CreateHeaderFooter {: .no_toc } @@ -222,7 +245,9 @@ Syntax: *object*.**CreateHeaderFooter** ( *headerHeight* , *footerHeight* , *hea : *required* **String** values, the names of the three panels. Returns the header, the body and the footer, in that order, as an array of three panels. The header takes the first *headerHeight* rows of the area, the footer the last *footerHeight* rows, and the body the rows between them, with no blank row between the panels. Each panel is as wide as the area. See [Panels, names and the root panel](#panels-names-and-the-root-panel) for the area. + + ### CreateSidebar {: .no_toc } @@ -237,7 +262,9 @@ Syntax: *object*.**CreateSidebar** ( *sidebarWidth* , *sidebarName* , *contentNa : *required* **String** values, the names of the two panels. Returns the sidebar and the content panel, in that order, as an array of two panels. The sidebar takes the first *sidebarWidth* columns of the area and the content panel the rest, with no blank column between them. Both are as high as the area. + + ### GetAllPanels {: .no_toc } @@ -246,7 +273,9 @@ Returns every panel. Syntax: *object*.**GetAllPanels** ( ) **As Variant** Returns a **Variant** that holds an array of **cConsolePanel** in the order of creation, with indexes from 0. + + ### GetPanel {: .no_toc } @@ -258,7 +287,9 @@ Syntax: *object*.**GetPanel** ( *name* ) **As cConsolePanel** : *required* A **String**. The comparison is case-sensitive. Returns the panel, or **Nothing** when the layout has no panel of that name. + + ### GetPanelNames {: .no_toc } @@ -267,6 +298,7 @@ Returns the names of the panels. Syntax: *object*.**GetPanelNames** ( ) **As Variant** Returns a **Variant** that holds an array of **String**, with indexes from 0, in the order of the dictionary, which is the order in which the names were added. + ### HasPanel {: .no_toc } @@ -278,6 +310,7 @@ Syntax: *object*.**HasPanel** ( *name* ) **As Boolean** *name* : *required* A **String**. + ### RemovePanel {: .no_toc } @@ -289,7 +322,9 @@ Syntax: *object*.**RemovePanel** ( *name* ) : *required* A **String**. A name that is not in the layout does nothing. Removes the panel from the dictionary and the list. When it was the root panel, the first panel that is left becomes the root, or the layout has none. Nothing is erased from the buffer. + + ### Render {: .no_toc } @@ -316,7 +351,9 @@ layout.ClearAllContent theme layout.Render theme con.Refresh ``` + + ### RenderPanel {: .no_toc } @@ -329,7 +366,9 @@ Syntax: *object*.**RenderPanel** ( *name* [ , *theme* ] ) *theme* : *optional* A **cConsoleTheme**, as for [**Render**](#render). + + ### Resize {: .no_toc } @@ -343,7 +382,9 @@ Syntax: *object*.**Resize** ( *newWidth* , *newHeight* ) The method stores the new size, then computes the factors `newWidth / stored width` and `newHeight / stored height` and moves and resizes every panel by them. The stored size is replaced before the factors are computed, so both are 1, and no panel changes. The only effect is the new size, which [**CreateGrid**](#creategrid), [**CreateSidebar**](#createsidebar) and [**CreateHeaderFooter**](#createheaderfooter) use when the layout has no panel. A *newWidth* or *newHeight* of 0 raises error 6, *Overflow*, since the factor is 0 divided by 0. A program that has to follow the size of the window ([**OnResize**](../TBMANLIB_ConsoleCore/cConsole#onresize) of the console reports it) calls [**Clear**](#clear) and makes the panels again. + + ### SplitHorizontal {: .no_toc } @@ -363,7 +404,9 @@ Syntax: *object*.**SplitHorizontal** ( *parentName* , *ratio* , *leftName* , *ri Returns an array of two panels, the left one first, in a **Variant**. When the parent is not in the layout, the method creates nothing and returns **Nothing** in the **Variant**. The left panel is `CLng(ContentWidth * ratio)` columns wide, starting at the left edge of the parent's content area, and the right panel starts one column after it and reaches the right edge. Both are as high as the content area. The method divides the width, so the line between the panels is vertical. The member of the same name in [**cLayoutHelper**](Helpers#splithorizontal) divides the height instead. + + ### SplitVertical {: .no_toc } @@ -381,6 +424,7 @@ Syntax: *object*.**SplitVertical** ( *parentName* , *ratio* , *topName* , *botto : *required* **String** values, the names of the new panels. Returns an array of two panels, the top one first, or **Nothing** when the parent is not in the layout. The top panel is `CLng(ContentHeight * ratio)` rows high, from the top edge of the parent's content area, and the bottom panel starts one row after it and reaches the bottom edge. The line between them is horizontal. The member of the same name in [**cLayoutHelper**](Helpers#splitvertical) divides the width instead. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsolePanel.md b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsolePanel.md index 05310026..b3d9d542 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsolePanel.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsolePanel.md @@ -10,6 +10,7 @@ has_toc: false A rectangle of the console with a border, a title and padding, that draws its border into a [**cConsoleBuffer**](../TBMANLIB_ConsoleCore/cConsoleBuffer) and tells where its content area is. + A panel holds a position, a size and a few style fields, and draws nothing until [**Render**](#render) is called with a buffer. It does not hold content: the program writes into the area that [**ContentRect**](#contentrect) returns. The panel takes the buffer to draw into as a parameter and holds no console. [**cConsoleLayout**](cConsoleLayout) creates panels and keeps them by name. ```tb check_build @@ -31,28 +32,33 @@ Debug.Print panel.ContentWidth() & " " & panel.ContentHeight() ' L 15 ' 16 2 ``` + * TOC {:toc} In the syntax lines of this page, *object* is a **cConsolePanel**. Columns and rows are counted from 0 in the buffer. + ## The rectangle and the content area [**Rect**](#rect) is the rectangle of the panel with its edges inclusive: a panel at column 2, row 1, 20 columns wide and 6 rows high has `Rect.Left` 2, `Rect.Top` 1, `Rect.Right` 21 and `Rect.Bottom` 6. The border is drawn on the edge cells. The content area is what is left inside. [**ContentRect**](#contentrect) takes one cell from each side for the border, unless [**BorderStyle**](#borderstyle) is **None**, and then [**Padding**](#padding) cells from each side. The sample above has a content area of 16 columns by 2 rows, from column 4, row 3 to column 19, row 4. The content area can be empty or have a negative size when the panel is small. + ## Fields The class has seven public fields, not properties. They are read and written as `panel.Title`, and a write takes effect at the next [**Render**](#render). + ### BorderColor {: .no_toc } The colour of the two spaces that surround the title on the top border. A [**ConsoleColor**](../TBMANLIB_ConsoleCore/Types#consolecolor). The starting value is **Gray**. The border itself is drawn in the colours that are passed to [**Render**](#render), and not in this one. + ### BorderStyle {: .no_toc } @@ -69,11 +75,14 @@ The name of the panel. **String**. The constructor sets it. [**cConsoleLayout**] The number of cells between the border and the content area, on every side. **Long**. The starting value is 1. + ### Rect {: .no_toc } The rectangle of the panel. A [**ConsoleRect**](../TBMANLIB_ConsoleCore/Types#consolerect), with inclusive edges. The constructor, [**Move**](#move) and [**Resize**](#resize) set it. A program that references the packages TBMANLIB_ConsoleCore and TBMANLIB_ConsoleComponents cannot read its fields: see [The two packages](./#the-two-packages). [**ContentWidth**](#contentwidth) and [**ContentHeight**](#contentheight) give sizes as **Long** values. + + ### Title {: .no_toc } @@ -85,13 +94,15 @@ The text on the top border. **String**. The starting value is an empty string, w {: .no_toc } The colour of the title text. A **ConsoleColor**. The starting value is **White**. + + ## Enumeration ### PanelBorderStyle {: .no_toc } -The border style of a panel. The enumeration is declared in the class, and its members are written without the class, as in `PanelBorderStyle.Rounded`; the type is `cConsolePanel.PanelBorderStyle` or `PanelBorderStyle`. +The border style of a panel. The enumeration is declared in the class, and its members are written without the class, as in `PanelBorderStyle.Rounded`; the type is `PanelBorderStyle`. The form with the class, `cConsolePanel.PanelBorderStyle`, is refused in a project that references both [TBMANLIB_ConsoleCore](../TBMANLIB_ConsoleCore/) and this package (TB5079); see [The two packages](./#the-two-packages). | Member | Value | Border | |---|---:|---| @@ -102,9 +113,11 @@ The border style of a panel. The enumeration is declared in the class, and its m | **Bold** | 4 | Heavy lines: U+250F, U+2513, U+2517, U+251B, U+2501, U+2503. | **Single** and **Double** are drawn by [**DrawBox**](../TBMANLIB_ConsoleCore/cConsoleBuffer#drawbox) of the buffer, and the other two by the panel itself. + ## Constructor + ### New {: .no_toc } @@ -122,9 +135,11 @@ Syntax: **New cConsolePanel** [ ( [ *name* [ , *left* [ , *top* [ , *width* [ , : *optional* **Long** values, the size in columns and rows. The defaults are 10 and 5. Sets [**Rect**](#rect) to `left`, `top`, `left + width - 1` and `top + height - 1`, [**BorderStyle**](#borderstyle) to **Single**, [**BorderColor**](#bordercolor) to **Gray**, [**TitleColor**](#titlecolor) to **White** and [**Padding**](#padding) to 1. + ## Methods + ### ClearContent {: .no_toc } @@ -142,7 +157,9 @@ Syntax: *object*.**ClearContent** ( *buffer* [ , *fore* [ , *back* ] ] ) : *optional* A **ConsoleColor**, the background. The default is **Black**. The padding and the border are left as they are. Nothing is done when the content area is less than 2 cells wide or 2 cells high. + + ### ContentHeight {: .no_toc } @@ -151,7 +168,9 @@ Returns the height of the content area. Syntax: *object*.**ContentHeight** ( ) **As Long** Returns `ContentRect.Bottom - ContentRect.Top + 1`. It is 0 or negative when the border and the padding take all the rows. + + ### ContentRect {: .no_toc } @@ -160,7 +179,9 @@ Returns the content area. Syntax: *object*.**ContentRect** ( ) **As ConsoleRect** Returns a [**ConsoleRect**](../TBMANLIB_ConsoleCore/Types#consolerect) that is [**Rect**](#rect) less one cell on each side for the border (none when [**BorderStyle**](#borderstyle) is **None**) and less [**Padding**](#padding) cells on each side. + + ### ContentWidth {: .no_toc } @@ -169,7 +190,9 @@ Returns the width of the content area. Syntax: *object*.**ContentWidth** ( ) **As Long** Returns `ContentRect.Right - ContentRect.Left + 1`. + + ### Move {: .no_toc } @@ -181,7 +204,9 @@ Syntax: *object*.**Move** ( *left* , *top* ) : *required* **Long** values, the new position of the upper left corner. Nothing is drawn, and the old cells are not cleared. + + ### Render {: .no_toc } @@ -199,7 +224,9 @@ Syntax: *object*.**Render** ( *buffer* [ , *fore* [ , *back* ] ] ) : *optional* A **ConsoleColor**, the background of the border. The default is **Black**. Draws the border of the style in [**BorderStyle**](#borderstyle) around [**Rect**](#rect) and then the [**Title**](#title). The inside of the panel is not changed: the text that was there stays, and [**ClearContent**](#clearcontent) removes it. With the style **None** the method draws nothing, not even the title. Cells outside the buffer are dropped. + + ### Resize {: .no_toc } @@ -211,6 +238,7 @@ Syntax: *object*.**Resize** ( *width* , *height* ) : *required* **Long** values, the new size in columns and rows. Sets `Rect.Right` to `Rect.Left + width - 1` and `Rect.Bottom` to `Rect.Top + height - 1`. Nothing is drawn. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsoleTheme.md b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsoleTheme.md index 2f9b88d0..32ff19a1 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsoleTheme.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cConsoleTheme.md @@ -10,6 +10,7 @@ has_toc: false A set of 14 named colours, which a program uses to draw all its parts in one style, with three ready-made sets in the module **mConsoleThemeFactory**. + A **cConsoleTheme** has no behaviour of its own besides two methods. Its fields are [**ConsoleColor**](../TBMANLIB_ConsoleCore/Types#consolecolor) values that a program reads when it draws. The package reads only three of them: [**cConsoleLayout**](cConsoleLayout) uses **Border** and **Background** to draw the borders of its panels, and **Foreground** and **Background** to clear their content areas. The other fields are for the program's own drawing, for example with [**cDataGrid**](cDataGrid) and [**cProgressBar**](cProgressBar). ```tb check_build check_run @@ -26,12 +27,14 @@ Debug.Print blank.Background & " " & blank.Foreground ' 10 ' 0 0 ``` + * TOC {:toc} In the syntax lines of this page, *object* is a **cConsoleTheme**. + ## Fields The class has 14 public fields, all of the type [**ConsoleColor**](../TBMANLIB_ConsoleCore/Types#consolecolor). A theme that is made with `New cConsoleTheme` has every field at 0, **Black**, until the program sets it; the functions of [mConsoleThemeFactory](#mconsolethemefactory-module) return themes with all fields set. @@ -54,9 +57,11 @@ The class has 14 public fields, all of the type [**ConsoleColor**](../TBMANLIB_C | **SelectedForeground** | the text of a selected item | no | The class has no comment on the use of the fields, so the second column describes each from its name. [**cDataGrid**](cDataGrid) has its own colour properties and does not read a theme. + ## Methods + ### Clone {: .no_toc } @@ -65,7 +70,9 @@ Returns a copy of the theme. Syntax: *object*.**Clone** ( ) **As cConsoleTheme** Returns a new **cConsoleTheme** with the 14 fields of the object. Changing the copy does not change the original. + + ### GetColor {: .no_toc } @@ -77,7 +84,9 @@ Syntax: *object*.**GetColor** ( *colorType* ) **As ConsoleColor** : *required* A [**ThemeColorType**](../TBMANLIB_ConsoleCore/Types#themecolortype): **tcPrimary**, **tcSecondary**, **tcSuccess**, **tcWarning**, **tcError**, **tcInfo**, **tcMuted**, **tcBackground**, **tcForeground**, **tcBorder**, **tcHeaderBackground**, **tcHeaderForeground**, **tcSelectedBackground** or **tcSelectedForeground**. Returns the field of the same name. For a value that is not one of the 14 members, it returns **Foreground**. The enumeration is declared in the package [TBMANLIB_ConsoleCore](../TBMANLIB_ConsoleCore/Types#themecolortype). + + ## mConsoleThemeFactory module The module has three functions that return a new, complete theme each time they are called. They have no parameters, and are written with the name of the module, as in `mConsoleThemeFactory.CreateLight()`. @@ -121,6 +130,7 @@ Returns the light theme: black text on white, with the darker colours for succes Syntax: **CreateLight** ( ) **As cConsoleTheme** A console shows the colours of its own colour table, so the "white" of the light theme is the white of that table, and a console whose table is dark can show it differently. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cDataGrid.md b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cDataGrid.md index c3f7e461..093ae505 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cDataGrid.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cDataGrid.md @@ -10,6 +10,7 @@ has_toc: false Draws a table of text into the screen buffer, with a header row, columns of fixed widths, aligned cells, coloured rows and one selected row. + A **cDataGrid** keeps the [**Buffer**](../TBMANLIB_ConsoleCore/cConsole#buffer) of the console it was made with. The program gives it the rows of text, the widths and alignments of the columns and the colours of the rows with the four `Set` members, and then calls [**Render**](#render), which draws the whole table at a position. The grid holds no scroll position and does not read keys: a program that lets the user choose a row calls [**MoveUp**](#moveup) and [**MoveDown**](#movedown) from its own key handler and draws the table again. The key handler is that of [**cConsole**](../TBMANLIB_ConsoleCore/cConsole#onkeypress), which [reports virtual-key codes](../TBMANLIB_ConsoleCore/#defects-in-version-1001), most of which differ from the members of **ConsoleKey**: a program compares them with constants such as `vbKeyUp`. ```tb check_build @@ -56,12 +57,14 @@ Name │ Size readme.txt │ 1,204 setup.exe │ 88,512 ``` + * TOC {:toc} In the syntax lines of this page, *object* is a **cDataGrid**. Columns and rows are counted from 0 in the buffer. + ## The data The text of the table is an array of **String**, one element for each row. Within a row the cells are separated by the tab character, `vbTab`. When [**ShowHeader**](#showheader) is **True**, which is the default, the first element is the header row and the others are data rows. @@ -73,7 +76,9 @@ The number of columns is the number of elements of the widths array. A cell is c With [**ShowBorders**](#showborders) **True**, which is the default, a vertical bar U+2502 is drawn in the column after each column except the last, and a line of U+2500 with U+253C where the bars are lies under the header. That is not a frame: the table has no outer border and no lines between data rows. With **ShowBorders** at **False** there is no separator column and no line, and the columns follow each other with no space between. The colours are these. The header is drawn in the header colour on black. A data row is drawn in the *fore* and *back* colours of [**Render**](#render), or in the colour from [**SetRowColors**](#setrowcolors) when one is given, on *back*. The selected row is white on the selection colour. The separator line and the vertical bars take *fore* and *back*, and in the header the header colour. + + ## Enumeration ### ColumnAlignment @@ -88,6 +93,7 @@ The alignment of the cells of a column. The enumeration is declared in the class | **Right** | 2 | end at the right edge of the column | An element of the alignment array that is missing, because the array is shorter than the number of columns, is **Left**. + ## Constructor @@ -101,8 +107,11 @@ Syntax: **New cDataGrid** ( *console* ) *console* : *required* A [**cConsole**](../TBMANLIB_ConsoleCore/cConsole) that is in full-screen mode. + Keeps the console and its current [**Buffer**](../TBMANLIB_ConsoleCore/cConsole#buffer). The grid starts with the header colour **Cyan**, the selection colour **DarkBlue**, no selected row (the row number -1), the header shown and the separators shown. When the console is not in full-screen mode, the buffer is **Nothing** and [**Render**](#render) raises error 91. See [How a component works](./#how-a-component-works). + + ## Properties All the properties can be set and none can be read: each has a `Let` and no `Get`. They take effect at the next [**Render**](#render). @@ -144,9 +153,11 @@ Syntax: *object*.**ShowBorders** **=** *value* Whether the first row is a header. **Boolean**, write-only. The default is **True**. With **False**, every row is a data row, and no line is drawn under the first row. Syntax: *object*.**ShowHeader** **=** *value* + ## Methods + ### GetRowCount {: .no_toc } @@ -155,7 +166,9 @@ Returns the number of data rows. Syntax: *object*.**GetRowCount** ( ) **As Long** Returns the number of elements of the rows array, less one when [**ShowHeader**](#showheader) is **True**. Raises error 9 when [**SetRows**](#setrows) was never called. + + ### MoveDown {: .no_toc } @@ -167,7 +180,9 @@ Syntax: *object*.**MoveDown** ( *maxRow* ) : *required* A **Long**, the number of data rows. The selection moves down only while the selected row is less than *maxRow* - 1, so the last data row is the last one reachable. [**GetRowCount**](#getrowcount) gives the number. With no selection (-1) the first data row is selected. + + ### MoveUp {: .no_toc } @@ -176,7 +191,9 @@ Selects the previous row. Syntax: *object*.**MoveUp** ( ) The selection moves up only while the selected row is above 0, so the first data row is the first one reachable, and a call with no selection does nothing. + + ### Render {: .no_toc } @@ -194,7 +211,9 @@ Syntax: *object*.**Render** ( *col* , *row* [ , *fore* [ , *back* ] ] ) : *optional* A **ConsoleColor**, the background of the data rows and the separators. The default is **Black**. Draws the header row, the line under it when [**ShowBorders**](#showborders) is on, and then every data row, one row of the buffer each, going down from *row*. The grid is as wide as the column widths plus one column for each separator, and as high as the number of rows plus one for the line. Rows that reach below the buffer are dropped. + + ### ScrollTo {: .no_toc } @@ -206,6 +225,7 @@ Syntax: *object*.**ScrollTo** ( *row* ) : *required* A **Long**, the number of the data row, counted from 0. The method only sets the selected row, as [**SelectedRow**](#selectedrow) does. It does not scroll: the grid has no scroll position, and always draws all its rows. + ### SetColumnAlignments {: .no_toc } @@ -227,6 +247,7 @@ Syntax: *object*.**SetColumnWidths** ( *widths* ) *widths* : *required* An array of **Long**, passed by reference, the width in cells of each column. Its number of elements is the number of columns. The method copies the array. + ### SetRowColors {: .no_toc } @@ -238,7 +259,9 @@ Syntax: *object*.**SetRowColors** ( *colors* ) : *required* An array of [**ConsoleColor**](../TBMANLIB_ConsoleCore/Types#consolecolor), passed by reference. The method copies the array. Both arrays start at index 0. The colour of the row at index *i* of the array of [**SetRows**](#setrows) is `colors(i)`. The header row is counted, so with a header the first data row takes `colors(1)`, and `colors(0)` is not used: the header has its own colour. A row whose index lies beyond the end of the colour array keeps *fore*. The selected row ignores its colour. + + ### SetRows {: .no_toc } @@ -248,6 +271,7 @@ Syntax: *object*.**SetRows** ( *rows* ) *rows* : *required* An array of **String**, passed by reference, one element for each row, with the cells separated by the tab character. The method copies the array: later changes to it do not reach the grid. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cProgressBar.md b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cProgressBar.md index a682de59..385ea144 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cProgressBar.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cProgressBar.md @@ -10,6 +10,7 @@ has_toc: false Draws a progress bar, horizontal or vertical, into the screen buffer of the full-screen mode. + A **cProgressBar** keeps the [**Buffer**](../TBMANLIB_ConsoleCore/cConsole#buffer) of the console it was made with. Its `Render` members write a bar made of a filled part and an empty part into that buffer; they do not clear the cells around the bar and do not write to the console. [**Refresh**](../TBMANLIB_ConsoleCore/cConsole#refresh) of the console shows the result. The filled part is drawn with the character U+2588 (a full block), in green, and the empty part with U+2591 (a light shade), in dark grey, unless the call says otherwise. ```tb check_build @@ -30,12 +31,14 @@ con.ExitFullScreenMode ' 2588 10 ' 2591 8 ``` + * TOC {:toc} In the syntax lines of this page, *object* is a **cProgressBar**. Columns and rows are counted from 0 in the buffer, and a position is a column and a row, in that order. + ## Common behaviour In every `Render` member, *percent* is a **Double** from 0 to 100; a value outside the range is taken as the nearest end. The number of filled cells is `CLng(percent / 100 * size)`, which rounds half to even, so 25 percent of 10 cells is 2 cells. The filled and empty parts together are exactly *size* cells. @@ -43,6 +46,7 @@ In every `Render` member, *percent* is a **Double** from 0 to 100; a value outsi *filledChar* and *emptyChar* are strings of which only the first character is used; an empty string raises error 5, *Invalid procedure call or argument*. The colours *filledColor* and *emptyColor* are [**ConsoleColor**](../TBMANLIB_ConsoleCore/Types#consolecolor) values, and the cells have a black background. A cell outside the buffer is dropped. A bar that is made before the console enters full-screen mode holds no buffer, and its first `Render` raises error 91, *Object variable or With block variable not set*; see [How a component works](./#how-a-component-works). + ## Constructor @@ -56,10 +60,13 @@ Syntax: **New cProgressBar** ( *console* ) *console* : *required* A [**cConsole**](../TBMANLIB_ConsoleCore/cConsole) that is in full-screen mode. + Keeps the console and its current [**Buffer**](../TBMANLIB_ConsoleCore/cConsole#buffer). When the console is not in full-screen mode, the buffer is **Nothing** and every `Render` member raises error 91. The bar keeps that buffer object: after the console leaves full-screen mode and enters it again, the console has a new buffer and the bar still draws into the old one. + ## Methods + ### GetProgressColor {: .no_toc } @@ -89,7 +96,9 @@ Debug.Print bar.GetProgressColor(10) & " " & bar.GetProgressColor(50) & " " & ba ' Output: ' 10 14 12 ``` + + ### Render {: .no_toc } @@ -119,7 +128,9 @@ Syntax: *object*.**Render** ( *col* , *row* , *width* , *percent* [ , *filledCha : *optional* A **ConsoleColor**. The default is **DarkGray**. The filled part starts at *col* and the empty part follows it, to *col* + *width* - 1. A *width* of 0 or less draws nothing. + + ### RenderVertical {: .no_toc } @@ -137,7 +148,9 @@ Syntax: *object*.**RenderVertical** ( *col* , *row* , *height* , *percent* [ , * : As for [**Render**](#render). The filled part grows from the bottom, in the cells from `row + height - 1` upwards, and the empty part is drawn from *row* downwards. + + ### RenderWithAutoColor {: .no_toc } @@ -155,7 +168,9 @@ Syntax: *object*.**RenderWithAutoColor** ( *col* , *row* , *width* , *percent* [ : *optional* **ConsoleColor** values for [**GetProgressColor**](#getprogresscolor). The defaults are **Green**, **Yellow** and **Red**. Chooses the filled colour with **GetProgressColor** and calls **Render**. Note the order of the parameters: the empty colour comes before the three colours, and there is no *filledColor*. + + ### RenderWithBrackets {: .no_toc } @@ -170,7 +185,9 @@ Syntax: *object*.**RenderWithBrackets** ( *col* , *row* , *width* , *percent* [ : *optional* A **ConsoleColor**, the colour of the brackets. The default is **Gray**. Draws `[` at *col*, the bar in the *width* - 2 cells after it, and `]` at *col* + *width* - 1. Despite its source comment, the method draws no percentage; [**RenderWithLabel**](#renderwithlabel) does. + + ### RenderWithLabel {: .no_toc } @@ -188,6 +205,7 @@ Syntax: *object*.**RenderWithLabel** ( *col* , *row* , *width* , *percent* , *la : *optional* A **ConsoleColor**, the colour of the label and the percentage. The default is **Gray**. The row is the label, a space, the bar, a space, and the percentage rounded to a whole number with a percent sign. The bar gets what is left of *width* after the label and the percentage text. The text has one character more for each digit of the rounded percentage, so the bar is one cell narrower at 10 percent and at 100 percent than just below them. When the bar would be less than 3 cells wide, the method draws only the label, a space and the percentage, in *labelColor*, and no bar. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cSparkline.md b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cSparkline.md index c2d14676..388038fb 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cSparkline.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cSparkline.md @@ -10,6 +10,7 @@ has_toc: false Draws a small chart of a series of numbers into the screen buffer, as Braille dots, as asterisks joined by vertical bars, or as columns of blocks. + A **cSparkline** keeps the [**Buffer**](../TBMANLIB_ConsoleCore/cConsole#buffer) of the console it was made with. [**Render**](#render) takes a series of **Double** values, scales them between a minimum and a maximum, and draws one column of the chart for each of *width* columns of the buffer. [**RenderWithStats**](#renderwithstats) also writes the smallest, the average and the largest value under the chart. The class draws into the buffer only; [**Refresh**](../TBMANLIB_ConsoleCore/cConsole#refresh) of the console shows it. ```tb check_build @@ -27,12 +28,14 @@ Next chart.RenderWithStats 2, 1, 10, 4, cpu, 0, 100, ConsoleColor.Green, ConsoleColor.Black, SparklineCharSet.Braille con.Refresh ``` + * TOC {:toc} In the syntax lines of this page, *object* is a **cSparkline**. Columns and rows are counted from 0 in the buffer. + ## The series The series is an array of **Double** values that has to start at index 0: the drawing code addresses the values by position from 0, so an array with a lower bound of 1 raises error 9, *Subscript out of range*. @@ -40,9 +43,11 @@ The series is an array of **Double** values that has to start at index 0: the dr A series of fewer than 2 values draws nothing, and an array that was never allocated raises error 9. Each value is scaled to `(value - minValue) / (maxValue - minValue)` and limited to 0 to 1. A *minValue* equal to *maxValue* raises error 11, *Division by zero*. A *width* of 0 raises error 11 too. The chart has *width* columns. When the series has more values than columns, each column takes the value at position `CLng(x * count / width)`, where *x* is the column counted from 0; the Braille set takes all the values from that position up to the position of the next column. When the series has fewer values than columns, the ASCII and Block sets repeat values, and the Braille set leaves some columns empty. The series is not averaged or smoothed. + ## Character sets + ### Braille {: .no_toc } @@ -110,12 +115,16 @@ con.ExitFullScreenMode ``` The series rises, and the first row (the top one) holds the dots of the values 25 and 50, while the second row holds those of 75 and 100. + + ### ASCII {: .no_toc } The chart is *width* columns wide and *height* rows high. A value is drawn as `*` in the row `CLng((1 - scaled) * (height - 1))` counted from the top, so the maximum is in the top row and the minimum in the bottom row. When the point of a column lies more than one row above or below the point of the column on its left, the cells of that left column strictly between the two rows are filled with U+2502 (a vertical bar). The set writes only the points and the bars; it does not clear the other cells of its area. + + ### Block {: .no_toc } @@ -150,6 +159,7 @@ con.ExitFullScreenMode ' [ ###] ' [ ####] ``` + ## Enumeration @@ -178,10 +188,13 @@ Syntax: **New cSparkline** ( *console* ) *console* : *required* A [**cConsole**](../TBMANLIB_ConsoleCore/cConsole) that is in full-screen mode. + Keeps the console and its current [**Buffer**](../TBMANLIB_ConsoleCore/cConsole#buffer). When the console is not in full-screen mode, the buffer is **Nothing** and [**Render**](#render) raises error 91. See [How a component works](./#how-a-component-works). + ## Methods + ### Render {: .no_toc } @@ -217,7 +230,9 @@ Syntax: *object*.**Render** ( *col* , *row* , *width* , *height* , *values* [ , : *optional* A [**SparklineCharSet**](#sparklinecharset). The default is **Braille**. Returns without drawing when the array has fewer than 2 values. + + ### RenderWithStats {: .no_toc } @@ -232,6 +247,7 @@ Syntax: *object*.**RenderWithStats** ( *col* , *row* , *width* , *height* , *val : *optional* A **ConsoleColor**, the colour of the text line. The default is **Gray**. Draws the chart with **Render**, then writes `Min:`, `Avg:` and `Max:` with the three numbers, each formatted with one decimal, in one line at the row `row + height + 1`, starting at *col*, on the background *back*. The numbers are taken from all the values, and not from *minValue* and *maxValue*. The row is the same for all three character sets, so a Braille chart, which is `(height + 3) \ 4` rows high, has a gap before the line. The decimal separator is that of the regional settings of the user. An array with no element raises error 9. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cVBar.md b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cVBar.md index ffc2498c..92f59e99 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cVBar.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/cVBar.md @@ -8,6 +8,7 @@ has_toc: false # cVBar class {: .no_toc } + Draws vertical bars into the screen buffer: a single bar, a row of bars from an array, bars coloured by value, a bar with a label, and a history of values that scrolls to the left. A **cVBar** keeps the [**Buffer**](../TBMANLIB_ConsoleCore/cConsole#buffer) of the console it was made with. Each bar is one column wide and *height* rows high, and grows from the bottom. The members draw into the buffer only; [**Refresh**](../TBMANLIB_ConsoleCore/cConsole#refresh) of the console shows it. @@ -33,12 +34,14 @@ con.ExitFullScreenMode ' 2 2588 ' 3 2588 ``` + * TOC {:toc} In the syntax lines of this page, *object* is a **cVBar**. Columns and rows are counted from 0 in the buffer. + ## How a bar is drawn A bar of *height* rows is drawn in the rows *row* to *row* + *height* - 1 of one column. The number of filled rows is `CLng(percent / 100 * height)`, which rounds half to even, and a bar with a *percent* above 0 has at least one filled row. The filled rows are the bottom ones, drawn with the bar character in the colours *fore* on *back*. The rows above them are overwritten with spaces in the same colours, so a bar that is drawn again on top of a taller one clears it. @@ -46,7 +49,9 @@ A bar of *height* rows is drawn in the rows *row* to *row* + *height* - 1 of one A *percent* outside 0 to 100 is limited to the range. In the members that take *minValue* and *maxValue*, each value is first converted to a percentage with `100 * (value - minValue) / (maxValue - minValue)`; a *maxValue* equal to *minValue* raises error 11, *Division by zero*. A bar that is made before the console enters full-screen mode holds no buffer, and its first draw raises error 91, *Object variable or With block variable not set*; see [How a component works](./#how-a-component-works). + + ## Enumeration ### BarCharStyle @@ -63,6 +68,7 @@ The character of the filled rows. The enumeration is declared in the class, and | **Custom** | 4 | U+2588, as **Block** | **Custom** is meant for a character chosen by the program, but no member has a parameter for one, so **Custom** draws the same character as **Block**. A value outside 0 to 4 leaves the character empty, and a bar with at least one filled row then raises error 5, *Invalid procedure call or argument*. + ## Constructor @@ -76,10 +82,13 @@ Syntax: **New cVBar** ( *console* ) *console* : *required* A [**cConsole**](../TBMANLIB_ConsoleCore/cConsole) that is in full-screen mode. + Keeps the console and its current [**Buffer**](../TBMANLIB_ConsoleCore/cConsole#buffer). When the console is not in full-screen mode, the buffer is **Nothing** and every draw raises error 91. + ## Methods + ### Render {: .no_toc } @@ -104,7 +113,9 @@ Syntax: *object*.**Render** ( *col* , *row* , *height* , *percent* [ , *fore* [ *charStyle* : *optional* A [**BarCharStyle**](#barcharstyle). The default is **Block**. + + ### RenderBars {: .no_toc } @@ -128,7 +139,9 @@ Syntax: *object*.**RenderBars** ( *col* , *row* , *height* , *values* [ , *spaci : *optional*, as for [**Render**](#render). The bar for the first value is in column *col*, and the next bars are at *col* + (`1 + spacing`), and so on. + + ### RenderHistory {: .no_toc } @@ -146,7 +159,9 @@ Syntax: *object*.**RenderHistory** ( *col* , *row* , *height* , *history* [ , *m : *optional*, as for [**RenderBars**](#renderbars). The last element of the array is drawn in column *col*, the one before it in column *col* - 1, and so on to the left, one column each, so *col* is the right end of the chart and not its start. Columns left of the buffer are dropped. Nothing is drawn for an array with no element. The drawing code addresses the values by position from 0, so an array with a lower bound of 1 raises error 9, *Subscript out of range*. A program that shows a moving history keeps its own array, shifts it by one value for each new reading, and draws it again. + + ### RenderWithGradient {: .no_toc } @@ -170,7 +185,9 @@ Syntax: *object*.**RenderWithGradient** ( *col* , *row* , *height* , *values* [ : *optional*, as for [**Render**](#render). The colour of each bar is one of the three; there is no gradient between them. Note the order of the parameters: the three colours come before *back*, and there is no *fore*. + + ### RenderWithLabel {: .no_toc } @@ -188,6 +205,7 @@ Syntax: *object*.**RenderWithLabel** ( *col* , *row* , *height* , *percent* , *l : *optional* A **ConsoleColor**, the colour of the label. The default is **Gray**. Draws the bar with **Render**, and writes the label in the row `row + height + 1`, which leaves one blank row under the bar. The label starts in column `col - Len(label) \ 2 + 1`, or in column 0 when that is negative. That is one column to the right of the column that would centre the label under the bar. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/index.md b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/index.md index 20391d42..b116e173 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/index.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleComponents/index.md @@ -32,8 +32,9 @@ con.ReadKey True con.Restore ``` + > [!WARNING] 1.0.0.1, BETA 1005 -> The components that take a **cConsole** cannot be used with the published package [TBMANLIB_ConsoleCore](../TBMANLIB_ConsoleCore/). The package compiles, but the constructors of [**cProgressBar**](cProgressBar), [**cSparkline**](cSparkline), [**cVBar**](cVBar), [**cDataGrid**](cDataGrid), [**cConsoleLayout**](cConsoleLayout) and [**cLayoutHelper**](Helpers), and the members of [**cConsolePanel**](cConsolePanel) that take a **cConsoleBuffer**, raise error -2147467262, *No such interface supported*, when they are given an object of the published ConsoleCore, and a program cannot read the **ConsoleRect** and **ConsolePosition** values that the components return. The package carries a copy of ConsoleCore of its own, and the compiler treats the types of that copy as different from the types of the published package. [The two packages](#the-two-packages) has the details, and the one arrangement in which the components work. Only [**cConsoleTheme**](cConsoleTheme), the module **mConsoleThemeFactory**, [**cColorHelper**](Helpers#ccolorhelper-class) and the geometry of **cConsolePanel** work with the package as it is. The outputs that the samples on these pages show are those of the source files of the components compiled into a project that references ConsoleCore, and not of a project that references the two packages. +> The components that take a **cConsole** cannot be used with the published package [TBMANLIB_ConsoleCore](../TBMANLIB_ConsoleCore/). The package compiles, but the constructors of [**cProgressBar**](cProgressBar), [**cSparkline**](cSparkline), [**cVBar**](cVBar), [**cDataGrid**](cDataGrid), [**cConsoleLayout**](cConsoleLayout) and [**cLayoutHelper**](Helpers), and the members of [**cConsolePanel**](cConsolePanel) that take a **cConsoleBuffer**, raise error -2147467262, *No such interface supported*, when they are given an object of the published ConsoleCore, and a program cannot read the **ConsoleRect** and **ConsolePosition** values that the components return. The package carries a copy of ConsoleCore of its own, and the compiler treats the types of that copy as different from the types of the published package. [The two packages](#the-two-packages) has the details, and the one arrangement in which the components work. Only [**cConsoleTheme**](cConsoleTheme), the module **mConsoleThemeFactory**, [**cColorHelper**](Helpers#ccolorhelper-class) and the geometry of **cConsolePanel** work with the package as it is. A sample on these pages that makes a component with a **cConsole** raises this error in a project that references both packages; the output it shows is that of the code of the components compiled into the program's own project. * TOC {:toc} @@ -48,16 +49,20 @@ A component names types of ConsoleCore in its constructor and its parameters: ** The project file of this package lists ConsoleCore as an embedded reference: version 1.0.0.1, with the identifier `{A0CCFE06-651A-40A4-84D9-0CA4200A8224}` of the published package, and the package file holds that copy in its own `Packages` folder. The six source files of the copy are the same as those of the published package. A project that imports this package gets the copy inside it, at `/Packages/TBMANLIB_ConsoleComponents/Packages/TBMANLIB_ConsoleCore`, and a program that imports ConsoleCore as well gets a second copy, at `/Packages/TBMANLIB_ConsoleCore`. That is so in either order of import: the program's copy does not replace the one inside the package. + The components are compiled against the copy inside the package. The compiler treats its types as different from those of the copy that the program references, although they have the same names and the same code. A project that references both therefore meets two sets of types, and this is the result: - **Objects.** Passing a **cConsole** or a **cConsoleBuffer** of the published package to a component compiles. At run time the call raises error -2147467262, *No such interface supported*: the constructors of **cProgressBar**, **cSparkline**, **cVBar**, **cDataGrid**, **cConsoleLayout** and **cLayoutHelper** do, and so do **Render** and **ClearContent** of **cConsolePanel**. - **Structures.** A **ConsoleRect** or **ConsolePosition** that a component returns, such as the **Rect** field of **cConsolePanel**, or the result of **ContentRect**, **CenterRect**, **Center** or **Grid**, has the type of the copy inside the package. The compiler refuses to read a member of it (TB5027, *[ConsoleRect] does not contain 'Left', or it is inaccessible in this context*), to assign it to a **ConsoleRect** variable (TB5028, *Unable to convert type 'ConsoleRect' to type 'ConsoleRect'*) and to pass it to **ConsoleRect_Contains** (TB5001, *Validation of call to 'ConsoleRect_Contains' failed. Argument for 'rect': cannot coerce type 'ConsoleRect' to 'ConsoleRect'*). - **Enumerations.** A **ConsoleColor** is a number, so the colour members work: **cConsoleTheme**, **mConsoleThemeFactory** and **cColorHelper** give and take colours, and **cConsolePanel** keeps its style fields. - **Nested enumerations.** The enumerations of the components, such as **PanelBorderStyle**, are written without the class: `PanelBorderStyle.Double`. The form `cConsolePanel.PanelBorderStyle.Double` is refused (TB5079, *Unrecognized symbol 'cConsolePanel'*). + A program that references only this package cannot create a **cConsole**, and a component cannot be created without one. + The components work when their source files are part of the program's own project and the project references ConsoleCore and not this package: there is then one set of types, and a **cConsole** reaches every component. The files are the `.twin` files of the `Components` folder in the sources of this package, which is published under the MIT licence. The members that this documentation describes for the components are the members of that code, and the sample outputs show what it prints in such a project. + ### How a component works @@ -68,9 +73,11 @@ Every component keeps what its constructor was given and draws only into the buf 3. Call the component's `Render` members to draw into the buffer. 4. Call [**Refresh**](../TBMANLIB_ConsoleCore/cConsole#refresh) to show the buffer. + **cProgressBar**, **cSparkline**, **cVBar** and **cDataGrid** take `console.Buffer` in their constructor and keep it. If the console is not in full-screen mode at that moment, **Buffer** is **Nothing**, the component holds **Nothing**, and its first draw raises error 91, *Object variable or With block variable not set*. A component keeps the buffer it was given: after [**ExitFullScreenMode**](../TBMANLIB_ConsoleCore/cConsole#exitfullscreenmode) and a new **EnterFullScreenMode** the console has a new buffer, and the component still draws into the old one, which the console no longer shows. [**cConsolePanel**](cConsolePanel) and the themes take no console, and [**cConsoleLayout**](cConsoleLayout) reads the buffer of the console each time it draws. The components do not clear the area they draw in, except where a page says so, do not check that what they draw lies inside the buffer (cells outside it are dropped), and do not flush. A bar drawn over earlier text leaves the rest of that text in place. + Positions are columns and rows of the buffer, counted from 0 at the top left, in that order. A component draws on a black background unless a parameter sets another; the defaults of each member are in its entry. The bars, borders and charts use characters such as U+2588 and U+2500, so the font of the console has to contain them. @@ -78,12 +85,16 @@ No component reads a key or a mouse event. A program that reacts to them uses [* ## Defects in version 1.0.0.1 + - [**cSparkline**](cSparkline#braille) with the Braille character set draws a chart taller than 4 dots with its character rows in reverse order, and draws nothing for a value equal to the minimum when the height is a multiple of 4. - [**cDataGrid**](cDataGrid) raises error 9 in **Render** unless all four of its arrays are set; [**SetRowColors**](cDataGrid#setrowcolors) is indexed like the data array, header included; [**ScrollTo**](cDataGrid#scrollto) only selects a row and scrolls nothing; its properties can be set and not read. - [**cConsoleLayout.Resize**](cConsoleLayout#resize) computes the scale factors after it stores the new size, so they are always 1 and no panel changes. [**AddPanel**](cConsoleLayout#addpanel) with a name in use raises error 457 after it has added the panel to the list, which then counts it. The first panel added becomes the root panel whatever method adds it. - [**cConsoleLayout.SplitHorizontal**](cConsoleLayout#splithorizontal) makes a left and a right panel, and [**cLayoutHelper.SplitHorizontal**](Helpers#splithorizontal) makes a top and a bottom rectangle. - [**cColorHelper.BrightenColor**](Helpers#brightencolor) returns 16 for **DarkGray**, which is not a **ConsoleColor**. [**DimColor**](Helpers#dimcolor) of a dark colour returns **DarkGray**. [**IntensityColor**](Helpers#intensitycolor) ignores its base colour, and [**GradientColor**](Helpers#gradientcolor) is a three-step choice, not a gradient. - [**cVBar**](cVBar#barcharstyle) has a style, **Custom**, that draws the same character as **Block**, and [**cProgressBar.RenderWithBrackets**](cProgressBar#renderwithbrackets) draws no percentage. +- [**cVBar.RenderWithLabel**](cVBar#renderwithlabel) writes the label one column to the right of the centre of the bar. +- [**cSparkline.Render**](cSparkline#the-series), [**cVBar.RenderHistory**](cVBar#renderhistory) and [**cColorHelper.CycleColor**](Helpers#cyclecolor) address their array from index 0, and raise error 9, or give a wrong element, for an array with another lower bound. + ## Classes and modules @@ -102,3 +113,7 @@ No component reads a key or a mouse event. A program that reacts to them uses [* - [cConsole](../TBMANLIB_ConsoleCore/cConsole) class -- the full-screen mode and **Refresh** - [cConsoleBuffer](../TBMANLIB_ConsoleCore/cConsoleBuffer) class -- the grid that the components draw into - [Importing a package from TWINSERV](../../../../Features/Packages/Importing-TWINSERV) -- adding a published package to a project + +## Test cases + +The notes and warnings of this section are checked by the test cases in [TBMANLIB_ConsoleComponents-tests.twinproj](TBMANLIB_ConsoleComponents-tests.twinproj), a twinBASIC project. It uses TBMANLIB_ConsoleComponents as a [linked package](../../../../Features/Packages/Linked#manual-management): put the `.twinpack` files of this package and of [TBMANLIB_ConsoleCore](../TBMANLIB_ConsoleCore/), which the project references as well, in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/Types.md b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/Types.md index c86a9ba0..78f33940 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/Types.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/Types.md @@ -35,6 +35,7 @@ Debug.Print ConsoleRect_Contains(r, 12, 4) The 16 colours of the console. The type of the colour parameters and properties of **cConsole** and **cConsoleBuffer**. + | Member | Value | Member | Value | |---|---:|---|---:| | **Black** | 0 | **DarkGray** | 8 | @@ -47,19 +48,23 @@ The 16 colours of the console. The type of the colour parameters and properties | **Gray** | 7 | **White** | 15 | The value is the colour as the Windows console writes it in a colour attribute: bit 0 is blue, bit 1 is green, bit 2 is red, and bit 3 makes the colour brighter. **DarkYellow** (6) is green and red together, and **Gray** (7) is all three without the brightness. A cell takes the text colour in the low four bits of its attribute and the background colour in the next four. The numbers are not those of the ANSI escape sequences, where 1 is red and 4 is blue; the functions of [**mConsoleStyle**](mConsoleStyle) write those sequences. + What a colour looks like depends on the colour table of the console, which the user can change. ### ConsoleEventType {: .no_toc } + Kinds of console event: **EventNone** (0), **EventResize** (1), **EventKeyPress** (2), **EventMouseClick** (3), **EventFocus** (4), **EventCtrlC** (5), **EventCtrlBreak** (6) and **EventClose** (7). + The enumeration is the type of the **EventType** field of [**ConsoleEventInfo**](#consoleeventinfo). No member of the package returns that structure or reads this enumeration. ### ConsoleKey {: .no_toc } + Named key codes: **KeyEnter** (13), **KeyEscape** (27), **KeySpace** (32), **KeyBackspace** (8), **KeyTab** (9), **KeyUp** (72), **KeyDown** (80), **KeyLeft** (75), **KeyRight** (77), **KeyHome** (71), **KeyEnd** (79), **KeyPageUp** (73), **KeyPageDown** (81), **KeyInsert** (82), **KeyDelete** (83), **KeyF1** to **KeyF10** (59 to 68), **KeyF11** (133) and **KeyF12** (134). The **KeyCode** field of a [**ConsoleKeyInfo**](#consolekeyinfo) holds the Windows virtual-key code of the key. Only the first five members of the enumeration, **KeyEnter**, **KeyEscape**, **KeySpace**, **KeyBackspace** and **KeyTab**, are virtual-key codes. The others are the codes that the C library function `_getch` returns for the same keys, so a comparison such as `keyInfo.KeyCode = ConsoleKey.KeyUp` does not test for the Up Arrow key: the virtual-key code 72 is that of the H key. The Up Arrow key has the code 38, and the H key gives 72. @@ -73,6 +78,7 @@ For these keys, compare with the constants of Visual Basic: | Page Up, Page Down | `vbKeyPageUp`, `vbKeyPageDown` | 33, 34 | | Insert, Delete | `vbKeyInsert`, `vbKeyDelete` | 45, 46 | | F1 to F12 | `vbKeyF1` to `vbKeyF12` | 112 to 123 | + ```tb check_build slot=module Public Sub OnKey(ByRef keyInfo As ConsoleKeyInfo, ByRef handled As Boolean) @@ -88,18 +94,23 @@ End Sub ### ConsoleMode {: .no_toc } + Flags: **ModeNormal** (0), **ModeVirtualTerminal** (1), **ModeFullScreen** (2) and **ModeRaw** (4). No member of the package uses the enumeration; it is not the console mode that [**EnableVirtualTerminal**](cConsole#enablevirtualterminal) sets. + ### ConsoleTextStyle {: .no_toc } + Flags: **StyleNone** (0), **StyleBold** (1), **StyleUnderline** (2) and **StyleReverse** (4). The enumeration is the type of the **Style** field of [**ConsoleCharInfo**](#consolecharinfo), and of a variable inside **cConsole**. Nothing sets or reads it: **GetChar** always returns **StyleNone**. Text styles come from the escape sequences of [**mConsoleStyle**](mConsoleStyle). + ### ThemeColorType {: .no_toc } The kinds of colour that a theme holds. It is declared in this package, and used by [**GetColor**](../TBMANLIB_ConsoleComponents/cConsoleTheme#getcolor) of the ConsoleComponents package. + | Member | Value | Member | Value | |---|---:|---|---:| | **tcPrimary** | 1 | **tcBorder** | 10 | @@ -111,6 +122,7 @@ The kinds of colour that a theme holds. It is declared in this package, and used | **tcMuted** | 7 | | | | **tcBackground** | 8 | | | | **tcForeground** | 9 | | | + ## Structures @@ -119,12 +131,14 @@ The kinds of colour that a theme holds. It is declared in this package, and used One cell of a [**cConsoleBuffer**](cConsoleBuffer), as [**GetChar**](cConsoleBuffer#getchar) returns it. + | Field | Type | Meaning | |---|---|---| | **Char** | **String** | The character, one UTF-16 code unit; an empty string for a position outside the buffer. | | **ForeColor** | [**ConsoleColor**](#consolecolor) | The text colour. | | **BackColor** | [**ConsoleColor**](#consolecolor) | The background colour. | | **Style** | [**ConsoleTextStyle**](#consoletextstyle) | Always 0. | + ### ConsoleEventInfo {: .no_toc } @@ -136,6 +150,7 @@ A structure for an event: **EventType** ([**ConsoleEventType**](#consoleeventtyp A key press, as [**ReadKey**](cConsole#readkey), [**PeekEvent**](cConsole#peekevent) and the handler of [**OnKeyPress**](cConsole#onkeypress) deliver it. + | Field | Type | Meaning | |---|---|---| | **KeyCode** | **Long** | The Windows virtual-key code of the key. See [**ConsoleKey**](#consolekey). | @@ -143,17 +158,20 @@ A key press, as [**ReadKey**](cConsole#readkey), [**PeekEvent**](cConsole#peekev | **IsAltPressed** | **Boolean** | Alt, left or right, was down. | | **IsCtrlPressed** | **Boolean** | Ctrl, left or right, was down. | | **IsShiftPressed** | **Boolean** | Shift was down. | + ### ConsoleMouseInfo {: .no_toc } A mouse event, as the handler of [**OnMouseClick**](cConsole#onmouseclick) receives it. + | Field | Type | Meaning | |---|---|---| | **Col**, **Row** | **Long** | The character cell under the pointer, counted from the top left of the screen buffer. | | **Button** | **Long** | The `dwButtonState` value of the Windows event: a bit mask of the buttons that are down, 1 for the left button, 2 for the right button and 4 for the second button from the left, and 0 when none is down. For a turn of the wheel, Windows puts the direction and the distance of the turn in the upper 16 bits. | | **IsDoubleClick** | **Boolean** | The event carries the double-click flag. | + ### ConsolePosition {: .no_toc } @@ -187,7 +205,9 @@ Syntax: **ConsoleRect_Contains** ( *rect* , *x* , *y* ) **As Boolean** *x*, *y* : *required* **Long** values, the column and the row of the point. + Returns **True** when `x >= rect.Left`, `x <= rect.Right`, `y >= rect.Top` and `y <= rect.Bottom`; the edges belong to the rectangle. + ### ConsoleRect_Create {: .no_toc } @@ -196,8 +216,10 @@ Makes a rectangle from four numbers. Syntax: **ConsoleRect_Create** ( *left* , *top* , *right* , *bottom* ) **As ConsoleRect** + *left*, *top*, *right*, *bottom* : *required* **Long** values, the edges, inclusive. Nothing is checked: a rectangle with *right* less than *left* is returned as given. + ### ConsoleRect_Height {: .no_toc } @@ -209,7 +231,9 @@ Syntax: **ConsoleRect_Height** ( *rect* ) **As Long** *rect* : *required* A [**ConsoleRect**](#consolerect). + Returns `rect.Bottom - rect.Top + 1`. + ### ConsoleRect_Width {: .no_toc } @@ -221,7 +245,9 @@ Syntax: **ConsoleRect_Width** ( *rect* ) **As Long** *rect* : *required* A [**ConsoleRect**](#consolerect). + Returns `rect.Right - rect.Left + 1`. + ## Handler types diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/cConsole.md b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/cConsole.md index 40828f3d..16d3d53c 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/cConsole.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/cConsole.md @@ -31,6 +31,7 @@ In the syntax lines of this page, *object* is a **cConsole**. The page uses thes ## Creating a cConsole + A **cConsole** is created with `New`. [**New**](#new) reads the standard handles and records the state that [**Restore**](#restore) puts back: the input and output modes, the colour attributes and the two code pages. The class is predeclared, so the name `cConsole` can also be used as an object, as in `cConsole.WriteLine "Hello"`, without `New`. @@ -38,6 +39,7 @@ The class is predeclared, so the name `cConsole` can also be used as an object, A program should create one **cConsole**. Each object records the console state at the moment it is created, and puts that state back when it is restored. A second object created after the first one has changed the modes records the changed modes as the original ones. The object restores the console when it is destroyed: its terminate event calls [**Restore**](#restore). A program that must put the console back at once, for example before it starts another program, calls **Restore** itself. + ## Which project it needs @@ -51,6 +53,7 @@ The members that read or draw on the console depend on Windows handing the progr The class checks the result of two functions only. [**EnableVirtualTerminal**](#enablevirtualterminal) returns **False** when `SetConsoleMode` fails for the output handle, and [**New**](#new), [**Clear**](#clear), [**ClearLine**](#clearline), [**GetSize**](#getsize), [**GetBufferSize**](#getbuffersize) and [**GetCursorPosition**](#getcursorposition) look at the result of `GetConsoleScreenBufferInfo`. Every other call is unchecked: a failed call raises no error, changes nothing, and leaves the value it should have filled in at its starting value, which is 0, **False** or an empty string. The table lists what a program without a console sees. + | Member | Without a console | |---|---| | [**New**](#new) | Succeeds. The size is taken as 80 columns by 25 rows, and the original colours as Gray on Black. | @@ -71,10 +74,13 @@ The class checks the result of two functions only. [**EnableVirtualTerminal**](# | [**IsKeyPressed**](#iskeypressed) | Works, since it does not use the console. | A buffer made by [**EnterFullScreenMode**](#enterfullscreenmode) is an ordinary in-memory grid, so a program without a console can still draw into it and read the cells back with [**GetChar**](cConsoleBuffer#getchar). + ### Redirected input and output + A console application can also have its output sent to a file or a pipe (`mytool > out.txt`) or its input read from one. The members write and read with `WriteConsoleW`, `ReadConsoleW` and `ReadConsoleInputW`, which work only on a console handle. With the output redirected, the write members therefore write nothing: after [**WriteLine**](#writeline) and [**WriteErrorLine**](#writeerrorline) the files are empty. They raise no error, since they do not check the result; the template's `Console.WriteLine` raises error 5 in the same case (see [Writing a command-line tool](../../../../Features/Project-Configuration/Project-Types#writing-a-command-line-tool-output-exit-code-and-arguments)). The members that need a screen buffer fail as they do without a console: [**GetSize**](#getsize) gives 80 by 25 and [**EnableVirtualTerminal**](#enablevirtualterminal) returns **False**. + A program that must work with redirected output writes with `WriteFile` in that case, as the page cited above shows, and uses **cConsole** only when the handles are console handles. @@ -82,14 +88,17 @@ With the input read from the null device or from a file, [**ReadLine**](#readlin ## Known problems in version 1.0.0.1 + > [!WARNING] 1.0.0.1 > [**ReadKey**](#readkey) and [**ReadPassword**](#readpassword) never return when the program has no console input: `ReadKey` loops until `ReadConsoleInputW` delivers a key-down event, and the call fails at once when the program has no console or its input is not a console. A comparison of a key with a member of the [**ConsoleKey**](Types#consolekey) enumeration is true for the wrong key and raises no error: [**KeyCode**](Types#consolekeyinfo) holds Windows virtual-key codes, but the arrow, navigation and function keys of **ConsoleKey** have other values, so `keyInfo.KeyCode = ConsoleKey.KeyUp` is false for the Up Arrow key (38) and true for the H key (72). + - **KeyAvailable and the key-up events.** [**KeyAvailable**](#keyavailable) is true while the input queue holds any event, including the event for a key that was released, so a loop that tests it and then calls [**ReadKey**](#readkey) can block in **ReadKey** until the next key press. - **The colours of the Write members.** [**WriteColored**](#writecolored), [**WriteLineColored**](#writelinecolored) and [**WriteAtColored**](#writeatcolored) use a black background unless one is given, and put back the colours that the object tracks, which are Gray on Black until a property sets others, and not the colours that the console had. - **Full-screen drawing.** [**Refresh**](#refresh) writes the buffer at the top left of the screen buffer, not of the window; see [**EnterFullScreenMode**](#enterfullscreenmode). A negative column or row in the buffer's dirty rectangle makes the next **Refresh** draw nothing; see [The dirty rectangle](cConsoleBuffer#the-dirty-rectangle). -- **The handlers.** A handler set with [**OnCtrlEvent**](#onctrlevent) cannot be removed again, and the compiler refuses `0` and **Nothing** for the other three handler properties, so none can be cleared. +- **The handlers.** The compiler refuses `0` and **Nothing** (TB5001) for all four handler properties, so none can be cleared. The procedure set with [**OnCtrlEvent**](#onctrlevent) stays registered with Windows: [**Restore**](#restore) does not remove it, and setting another procedure adds a second registration. - **The resize sizes.** [**OnResize**](#onresize) is given the size of the screen buffer, and not of the window that [**Width**](#width) and [**Height**](#height) report. + ## Constructor @@ -103,7 +112,9 @@ Syntax: **New cConsole** [ ( *inputBufferSize* ) ] *inputBufferSize* : *optional* A **Long**, the number of characters that [**ReadLine**](#readline) can read in one call. The default is 4096. + The constructor asks Windows for the three standard handles and for the console window, saves the input and output modes, the colour attributes, the width of the screen buffer and the height of the window, and the two code pages, and sets the tracked colours to Gray on Black. A negative *inputBufferSize* raises error 5, *Invalid procedure call or argument*, since the constructor makes the read buffer with `String$`. + Calls `GetStdHandle` (three times), `GetConsoleWindow`, `GetConsoleMode` (twice), `GetConsoleScreenBufferInfo`, `GetConsoleCP` and `GetConsoleOutputCP`. Without a console, the size is taken as 80 by 25 and the original colours as Gray on Black. @@ -119,7 +130,9 @@ Syntax: *object*.**BackColor** [ **=** *value* ] *value* : A [**ConsoleColor**](Types#consolecolor). + Setting the property stores the value and applies the pair of [**ForeColor**](#forecolor) and **BackColor** to the console. Reading it returns the stored value, which is **Black** at the start, and not a colour read from the console. + Calls `SetConsoleTextAttribute` when it is set. Without a console, nothing is applied. @@ -130,7 +143,9 @@ The screen buffer of the full-screen mode. A [**cConsoleBuffer**](cConsoleBuffer Syntax: *object*.**Buffer** + Returns the buffer that [**EnterFullScreenMode**](#enterfullscreenmode) made, and **Nothing** when the object is not in full-screen mode. A component object of the [ConsoleComponents](../TBMANLIB_ConsoleComponents/) package keeps the buffer that this property returned when it was created, so it must be created after **EnterFullScreenMode**. + ### CursorSize {: .no_toc } @@ -142,7 +157,9 @@ Syntax: *object*.**CursorSize** [ **=** *value* ] *value* : A **Long**. A value below 1 is stored as 1, and a value above 100 as 100. + Calls `GetConsoleCursorInfo`, and `SetConsoleCursorInfo` when it is set. Without a console, reading it gives 0. + ### CursorVisible {: .no_toc } @@ -154,7 +171,9 @@ Syntax: *object*.**CursorVisible** [ **=** *value* ] *value* : A **Boolean**. + Reading the property asks the console each time. [**EnterFullScreenMode**](#enterfullscreenmode) hides the cursor and [**ExitFullScreenMode**](#exitfullscreenmode) shows it, whatever it was before. + Calls `GetConsoleCursorInfo`, and `SetConsoleCursorInfo` when it is set. Without a console, reading it gives **False**. @@ -168,7 +187,9 @@ Syntax: *object*.**ForeColor** [ **=** *value* ] *value* : A [**ConsoleColor**](Types#consolecolor). + Works as [**BackColor**](#backcolor). The stored value is **Gray** at the start. Calls `SetConsoleTextAttribute` when it is set. + ### FPS {: .no_toc } @@ -177,7 +198,9 @@ An estimate of the frames drawn per second. **Double**, read-only. Syntax: *object*.**FPS** + The value is the number of calls of [**Refresh**](#refresh) and [**RefreshAll**](#refreshall) since the start of the current count, multiplied by 1000 and divided by the milliseconds since then. It is 0 before the first call. **Refresh** starts a new count when it finds that a second has passed since the start of the count, and **RefreshAll** only adds to the count, so a program that calls only **RefreshAll** gets the average since its first call. When the program stops drawing, the value falls, since the time keeps running. The clock is `GetTickCount`, whose steps are about 15 milliseconds. + ### FullScreenMode {: .no_toc } @@ -186,7 +209,9 @@ Whether the object is in full-screen mode. **Boolean**, read-only. Syntax: *object*.**FullScreenMode** + **True** between [**EnterFullScreenMode**](#enterfullscreenmode) and [**ExitFullScreenMode**](#exitfullscreenmode). + ### Height {: .no_toc } @@ -195,7 +220,9 @@ The number of rows of the console window. **Long**, read-only. Syntax: *object*.**Height** + The height of the window, and not of the screen buffer; [**GetBufferSize**](#getbuffersize) returns that. Each read calls [**GetSize**](#getsize). Without a console it is 25. + ### InputCodePage {: .no_toc } @@ -207,7 +234,9 @@ Syntax: *object*.**InputCodePage** [ **=** *value* ] *value* : A **Long**, a code page number such as 65001 for UTF-8. + Calls `GetConsoleCP`, and `SetConsoleCP` when it is set. A value that Windows refuses is ignored without an error. [**Restore**](#restore) and [**DisableUTF8**](#disableutf8) put back the code page that the console had when the object was created. Without a console, reading it gives 0. + ### LastRenderTime {: .no_toc } @@ -216,7 +245,9 @@ The time that the last [**Refresh**](#refresh) or [**RefreshAll**](#refreshall) Syntax: *object*.**LastRenderTime** + The value is the difference of two `GetTickCount` readings, so it is a multiple of the clock step, about 15 milliseconds, and is often 0 for a small draw. It is 0 until the first draw. + ### OnCtrlEvent {: .no_toc } @@ -244,7 +275,9 @@ End Sub Windows calls the procedure on a thread of its own, not on the thread that runs the program, so it should do little: set a flag that the main loop reads. -The property registers the address with `SetConsoleCtrlHandler`. The registration is not removed again: assigning another procedure adds a second registration and leaves the first, [**Restore**](#restore) does not remove it, and the code never calls `SetConsoleCtrlHandler` with the remove option. [**ProcessEvents**](#processevents) does not call the procedure; only Windows does. + +The property registers the address with `SetConsoleCtrlHandler`. The registration is not removed again: assigning another procedure adds a second registration and leaves the first, [**Restore**](#restore) does not remove it, and the code never calls `SetConsoleCtrlHandler` with the remove option. The compiler refuses `0` and **Nothing** for the property (TB5001), so the handler cannot be cleared either. [**ProcessEvents**](#processevents) does not call the procedure; only Windows does. + ### OnKeyPress {: .no_toc } @@ -256,7 +289,9 @@ Syntax: *object*.**OnKeyPress** **=** *handler* *handler* : A [**ConsoleKeyHandler**](Types#consolekeyhandler), the address of a procedure of a module with the parameters `ByRef keyInfo As ConsoleKeyInfo, ByRef handled As Boolean`. + **ProcessEvents** calls the procedure once for each key-down event, with the key in *keyInfo*, including the keys that are only modifiers, such as Shift. The key-up events are not passed. The value that the procedure stores in *handled* is not read. While no handler is set, **ProcessEvents** leaves the key events in the queue, so that [**ReadKey**](#readkey) and [**ReadLine**](#readline) can read them. Once a handler is set it cannot be removed: the compiler refuses `0` and **Nothing** for the property. + ```tb check_build slot=module Public Sub OnKey(ByRef keyInfo As ConsoleKeyInfo, ByRef handled As Boolean) @@ -280,9 +315,11 @@ Syntax: *object*.**OnMouseClick** **=** *handler* *handler* : A [**ConsoleMouseHandler**](Types#consolemousehandler), the address of a procedure of a module with the parameters `ByRef mouseInfo As ConsoleMouseInfo, ByRef handled As Boolean`. + Setting a handler also changes the input mode of the console: it turns on mouse input, turns on the extended flags, and turns off quick-edit mode, so that the console passes mouse events to the program instead of selecting text with them. [**DisableVirtualTerminal**](#disablevirtualterminal) and [**Restore**](#restore) put the original mode back, and so does [**ExitFullScreenMode**](#exitfullscreenmode) while the full-screen mode is on. Despite the name, the procedure is called for every mouse event: a move, a button press, a button release and a turn of the wheel, as well as a double click. *mouseInfo* holds the position in **Col** and **Row**, the buttons that are down in **Button** (a bit mask of the Windows `dwButtonState` field: 1 for the left button, 2 for the right button, and 0 when none is down), and **IsDoubleClick**, which is true when the event carries the double-click flag. The program has to compare **Button** with its previous value to find a click. + ### OnResize {: .no_toc } @@ -294,10 +331,14 @@ Syntax: *object*.**OnResize** **=** *handler* *handler* : A [**ConsoleResizeHandler**](Types#consoleresizehandler), the address of a procedure of a module with the parameters `ByVal newWidth As Long, ByVal newHeight As Long`. -Windows reports a change of the size only while the input mode has the window-input flag, which [**EnterFullScreenMode**](#enterfullscreenmode) turns on and nothing else in the class does. Outside full-screen mode the handler is not called. When an event arrives and the size differs from the one the object last saw, the object resizes its buffer to it, which discards the contents of the buffer, and then calls the handler; the program draws the buffer again. + +Windows puts a size event in the queue when the size of the screen buffer changes, for example after [**SetBufferSize**](#setbuffersize). [**EnterFullScreenMode**](#enterfullscreenmode) turns on the window-input flag of the input mode, and nothing else in the class does. [**ProcessEvents**](#processevents) takes every size event from the queue, in full-screen mode or not. When the size differs from the one the object last saw, the object resizes its buffer to it, if it is in full-screen mode, which discards the contents of the buffer, and then calls the handler; the program draws the buffer again. The two numbers are copied from the event record, which holds the new size of the screen buffer. They are not read from the window, whose size [**Width**](#width) and [**Height**](#height) report, and the two can differ, since the screen buffer can be taller than the window. +The compiler refuses `0` and **Nothing** for the property, so the handler cannot be cleared. + + ### OutputCodePage {: .no_toc } @@ -308,7 +349,9 @@ Syntax: *object*.**OutputCodePage** [ **=** *value* ] *value* : A **Long**, a code page number such as 65001 for UTF-8. + Works as [**InputCodePage**](#inputcodepage), with `GetConsoleOutputCP` and `SetConsoleOutputCP`. + ### Title {: .no_toc } @@ -320,7 +363,13 @@ Syntax: *object*.**Title** [ **=** *value* ] *value* : A **String**. -Reading it returns up to 259 characters. Calls `GetConsoleTitleW`, and `SetConsoleTitleW` when it is set. Without a console, reading it gives an empty string and setting it does nothing. + +Reading it returns the title; a title longer than 259 characters is cut. Calls `GetConsoleTitleW`, and `SetConsoleTitleW` when it is set. Without a console, reading it gives an empty string and setting it does nothing. + + + +> [!WARNING] 1.0.0.1 +> A title of 260 characters or more is returned as its first 259 characters followed by a character with code 0, so the string has 260 characters and ends in a null character. ### VirtualTerminalEnabled {: .no_toc } @@ -329,7 +378,9 @@ Whether [**EnableVirtualTerminal**](#enablevirtualterminal) turned the virtual-t Syntax: *object*.**VirtualTerminalEnabled** + The value comes from the object and not from the console. It is **False** when the console had the mode on already, until the program calls **EnableVirtualTerminal**. + ### Width {: .no_toc } @@ -338,7 +389,9 @@ The number of columns of the console window. **Long**, read-only. Syntax: *object*.**Width** + The width of the window, and not of the screen buffer. Each read calls [**GetSize**](#getsize). Without a console it is 80. + ### WindowHandle {: .no_toc } @@ -347,7 +400,9 @@ The window handle of the console, as `GetConsoleWindow` returned it when the obj Syntax: *object*.**WindowHandle** + It is 0 when the program had no console window at that time. + ## Methods @@ -373,7 +428,9 @@ Syntax: *object*.**CenterRect** ( *contentWidth* , *contentHeight* ) **As Consol *contentHeight* : *required* A **Long**, the height in rows. + Returns a [**ConsoleRect**](Types#consolerect) whose **Left** is [**CenterX**](#centerx) of the width and **Top** is [**CenterY**](#centery) of the height, with **Right** and **Bottom** set so that it has the given size. A rectangle larger than the window has negative **Left** and **Top**. Uses the size of the window, 80 by 25 without a console. + ### CenterX {: .no_toc } @@ -385,7 +442,9 @@ Syntax: *object*.**CenterX** ( *textWidth* ) *textWidth* : *required* A **Long**, the width in columns. + Returns `(Width - textWidth) \ 2`, a **Long**; it is negative when the text is wider than the window. + ### CenterY {: .no_toc } @@ -397,7 +456,9 @@ Syntax: *object*.**CenterY** ( *textHeight* ) *textHeight* : *required* A **Long**, the height in rows. + Returns `(Height - textHeight) \ 2`, a **Long**. + ### Clear {: .no_toc } @@ -406,7 +467,9 @@ Fills the screen buffer with spaces in the tracked colours and moves the cursor Syntax: *object*.**Clear** + The method fills the whole screen buffer, not only the part that the window shows, with a space in the colours of [**ForeColor**](#forecolor) and [**BackColor**](#backcolor), and then moves the cursor to column 0, row 0 of the screen buffer. When the window shows another part of the buffer, the cursor moves out of sight. In full-screen mode it does not clear the [**Buffer**](#buffer); [**Clear**](cConsoleBuffer#clear) of the buffer does that. + Calls `GetConsoleScreenBufferInfo`, `FillConsoleOutputCharacterW`, `FillConsoleOutputAttribute` and `SetConsoleCursorPosition`. Without a console, the first call fails and the method returns at once. @@ -417,7 +480,9 @@ Fills the current line, from the cursor to the end of the line, with spaces in t Syntax: *object*.**ClearLine** + The cursor does not move. Calls `GetConsoleScreenBufferInfo`, `FillConsoleOutputCharacterW` and `FillConsoleOutputAttribute`. Without a console, the method returns at once. + ### DisableUTF8 {: .no_toc } @@ -426,7 +491,9 @@ Puts back the code pages that the console had when the object was created. Syntax: *object*.**DisableUTF8** + Despite its name, the method does not switch to a particular code page: it sets the input and the output code page to the saved values, whatever they are now. Calls `SetConsoleCP` and `SetConsoleOutputCP`. + ### DisableVirtualTerminal {: .no_toc } @@ -435,7 +502,9 @@ Puts back the input and output modes that the console had when the object was cr Syntax: *object*.**DisableVirtualTerminal** + The method restores both modes as they were, so it also undoes other changes of the input mode, such as the window input that [**EnterFullScreenMode**](#enterfullscreenmode) turns on and the mouse input that [**OnMouseClick**](#onmouseclick) turns on. It sets [**VirtualTerminalEnabled**](#virtualterminalenabled) to **False**. Calls `SetConsoleMode` twice. + ### DrawBox {: .no_toc } @@ -453,7 +522,9 @@ Syntax: *object*.**DrawBox** ( *left* , *top* , *right* , *bottom* [ , *doubleLi *doubleLine* : *optional* A **Boolean**. **True** draws with the double-line characters (U+2554, U+2557, U+255A, U+255D, U+2550, U+2551), and **False**, the default, with the single-line ones (U+250C, U+2510, U+2514, U+2518, U+2500, U+2502). + The box is drawn with [**WriteAt**](#writeat), in the current colour attributes of the console; the inside is left as it is. *right* has to be greater than *left*: for *right* equal to or less than *left* the method raises error 5, *Invalid procedure call or argument*, since the length of the horizontal line is negative, and it raises it after it has written the four corners. + The method draws on the console, not on the full-screen [**Buffer**](#buffer). To draw a box on the buffer use [**DrawBox**](cConsoleBuffer#drawbox) of the buffer, which takes the colours as parameters. @@ -481,7 +552,9 @@ Syntax: *object*.**DrawProgressBar** ( *col* , *row* , *width* , *percent* [ , * *freeColor* : *optional* A [**ConsoleColor**](Types#consolecolor), the colour of the empty part. The default is **DarkGray**. + Writes `[`, then `CLng(percent / 100 * width)` full blocks (U+2588) in *usedColor*, then the rest as light-shade blocks (U+2591) in *freeColor*, and then `] ` and the percentage rounded to a whole number with a percent sign. The brackets and the percentage are in the current colour attributes of the console; the blocks have a black background. The whole line is *width* + 4 cells plus the digits of the percentage. A negative *width* raises error 5. The cursor is left after the text. The conversion rounds half to even: a width of 10 and 25 percent gives 2 blocks. + Calls `SetConsoleCursorPosition` and `WriteConsoleW`. Without a console, nothing is drawn. @@ -492,7 +565,9 @@ Sets the input and the output code page of the console to UTF-8, code page 65001 Syntax: *object*.**EnableUTF8** + Calls `SetConsoleCP` and `SetConsoleOutputCP`. The write and read members of this class use the wide-character functions of Windows, which do not depend on the code page, so the setting does not change what they put in the screen buffer: a euro sign written with [**WriteTo**](#writeto) is stored as U+20AC with the output code page 437 and with 65001. The setting matters for text that reaches the console in another way, such as the output of a program that this program starts. + The code pages are put back by [**DisableUTF8**](#disableutf8) and [**Restore**](#restore). Without a console, the calls fail without an error. @@ -503,7 +578,9 @@ Turns on the processing of escape sequences, so that the strings of [mConsoleSty Syntax: *object*.**EnableVirtualTerminal** **As Boolean** + Adds `ENABLE_VIRTUAL_TERMINAL_PROCESSING` to the output mode. When Windows refuses, for example on a system or a console that does not have the mode, or when there is no console, the method returns **False** and changes nothing else. Otherwise it adds `ENABLE_VIRTUAL_TERMINAL_INPUT` to the input mode, ignoring whether that call worked, sets [**VirtualTerminalEnabled**](#virtualterminalenabled) and returns **True**. + [**ReadKey**](#readkey) is not affected by the input flag, since it turns the input mode off while it waits. @@ -516,11 +593,13 @@ Starts the full-screen mode: it makes an in-memory screen buffer of the size of Syntax: *object*.**EnterFullScreenMode** -The method does nothing when the mode is already on. It creates a [**cConsoleBuffer**](cConsoleBuffer) with the width and height of the window, which [**Buffer**](#buffer) returns; sets [**CursorVisible**](#cursorvisible) to **False**; and adds `ENABLE_WINDOW_INPUT` to the input mode, so that Windows reports a change of size. + +The method does nothing when the mode is already on. It creates a [**cConsoleBuffer**](cConsoleBuffer) with the width and height of the window, which [**Buffer**](#buffer) returns; sets [**CursorVisible**](#cursorvisible) to **False**; and adds `ENABLE_WINDOW_INPUT` to the input mode, the flag for the reporting of a change of size. Despite the name, the window is not enlarged, the console does not switch to an alternate screen buffer, and the text that is on the screen is neither saved nor cleared. The program draws into the buffer with its [methods](cConsoleBuffer), and [**Refresh**](#refresh) copies the changed cells to the console. [**ExitFullScreenMode**](#exitfullscreenmode) ends the mode and leaves the last picture on the screen. [**Refresh**](#refresh) writes the cells of the buffer to the rows 0 to *Height* - 1 of the screen buffer, from column 0. When the screen buffer is taller than the window and the window shows another part of it, as it does in a classic console window that has scrolled, the picture is drawn out of sight. + Calls `GetConsoleScreenBufferInfo`, `GetConsoleCursorInfo`, `SetConsoleCursorInfo`, `GetConsoleMode` and `SetConsoleMode`. Without a console, the buffer is 80 by 25. @@ -560,7 +639,9 @@ Ends the full-screen mode. Syntax: *object*.**ExitFullScreenMode** + The method does nothing when the mode is off. It releases the buffer, so that [**Buffer**](#buffer) returns **Nothing** again, shows the cursor, and sets the input mode to the one saved when the object was created, which also turns off the mouse input that [**OnMouseClick**](#onmouseclick) turned on. An object that was made with the old buffer, such as a component of the ConsoleComponents package, still holds it, and the console no longer shows it. The screen is not cleared. Calls `SetConsoleMode` and `SetConsoleCursorInfo`. + ### FlushInput {: .no_toc } @@ -569,7 +650,9 @@ Discards the events that are waiting in the input queue. Syntax: *object*.**FlushInput** + All events are discarded: keys, mouse events and size events. Calls `FlushConsoleInputBuffer`. + ### GetBufferSize {: .no_toc } @@ -578,7 +661,9 @@ Returns the size of the screen buffer. Syntax: *object*.**GetBufferSize** **As ConsoleSize** + Returns a [**ConsoleSize**](Types#consolesize) with the columns and rows of the whole screen buffer, which can be larger than the window. Calls `GetConsoleScreenBufferInfo`. Without a console, it is 80 by 300. + ### GetCursorPosition {: .no_toc } @@ -587,7 +672,9 @@ Returns the position of the cursor. Syntax: *object*.**GetCursorPosition** **As ConsolePosition** + Returns a [**ConsolePosition**](Types#consoleposition). The column and the row are counted from the top left of the screen buffer, not of the window. Calls `GetConsoleScreenBufferInfo`. Without a console, it is column 0, row 0. + ### GetSize {: .no_toc } @@ -596,7 +683,9 @@ Returns the size of the console window. Syntax: *object*.**GetSize** **As ConsoleSize** + Returns a [**ConsoleSize**](Types#consolesize) with the columns and rows of the window, from the window rectangle that Windows reports. [**Width**](#width) and [**Height**](#height) return its two fields. Calls `GetConsoleScreenBufferInfo`. Without a console, it is 80 by 25. + ### IsKeyPressed {: .no_toc } @@ -608,7 +697,9 @@ Syntax: *object*.**IsKeyPressed** ( *keyCode* ) **As Boolean** *keyCode* : *required* A **Long**, a Windows virtual-key code, such as 27 for Escape or 38 for the Up Arrow. + Returns **True** when the most significant bit of the result of `GetAsyncKeyState` is set. The function asks Windows for the state of the keyboard, so it does not read the console's input queue, does not remove any event from it, and does not depend on which window has the focus. It works without a console. + ### KeyAvailable {: .no_toc } @@ -617,7 +708,9 @@ Tells whether the input queue holds an event. Syntax: *object*.**KeyAvailable** **As Boolean** + Returns **True** when the queue holds one or more events. The queue holds every kind of input: key presses and key releases, mouse events, focus events and size events, so a **True** result does not mean that a key is waiting. After a key is read, the event for its release is still in the queue, and **KeyAvailable** is true until a following [**ReadKey**](#readkey) or [**FlushInput**](#flushinput) removes it. + Calls `GetNumberOfConsoleInputEvents`. Without a console, it returns **False**. @@ -628,7 +721,9 @@ Returns the first event in the queue, when it is a key press, without removing i Syntax: *object*.**PeekEvent** **As ConsoleKeyInfo** + Returns the [**ConsoleKeyInfo**](Types#consolekeyinfo) of the first event when that event is a key-down event. For any other first event, a key release, a mouse event or an empty queue, it returns an empty structure: **KeyCode** 0 and an empty **KeyChar**. It does not look past the first event. Calls `PeekConsoleInputW`. + ### PercentX {: .no_toc } @@ -640,7 +735,9 @@ Syntax: *object*.**PercentX** ( *percent* ) *percent* : *required* A **Double**. A value below 0 is taken as 0, and a value above 100 as 100. + Returns `CLng(Width * percent / 100)`, a **Long**, rounded half to even. + ### PercentY {: .no_toc } @@ -652,7 +749,9 @@ Syntax: *object*.**PercentY** ( *percent* ) *percent* : *required* A **Double**. A value below 0 is taken as 0, and a value above 100 as 100. + Returns `CLng(Height * percent / 100)`, a **Long**. + ### ProcessEvents {: .no_toc } @@ -663,6 +762,7 @@ Syntax: *object*.**ProcessEvents** A full-screen program calls this method once in each pass of its main loop. It takes the events from the queue in order, while the queue is not empty, and treats each by its kind. + | Event | Handling | |---|---| | Size change | Always taken from the queue. It resizes the full-screen buffer and calls [**OnResize**](#onresize), as described there. | @@ -671,6 +771,7 @@ A full-screen program calls this method once in each pass of its main loop. It t | Any other | Taken from the queue and dropped. | A program that wants to read keys with [**ReadKey**](#readkey) or [**ReadLine**](#readline) therefore sets no key handler. A queue that holds a key event for which nobody asks stops the processing of the size and mouse events behind it. + The method calls `GetNumberOfConsoleInputEvents` (through [**KeyAvailable**](#keyavailable)), `PeekConsoleInputW` and `ReadConsoleInputW`. Without a console, **KeyAvailable** is **False** and nothing happens. @@ -684,9 +785,11 @@ Syntax: *object*.**ReadKey** [ ( *intercept* ) ] **As ConsoleKeyInfo** *intercept* : *optional* A **Boolean**. When it is **False**, the default, the character of the key is also written to the console. **True** writes nothing. -Returns a [**ConsoleKeyInfo**](Types#consolekeyinfo) for the first key-down event. **KeyCode** is the Windows virtual-key code, and **KeyChar** the character of the key, or the character with code 0 when the key has none, such as an arrow key. The three flags say whether Alt, Ctrl and Shift were down. Any key-down event counts, including the keys that are only modifiers, so a press of Shift is returned as **KeyCode** 16. Other events are taken from the queue and dropped while the method waits, and the release of a key is ignored. A key that is held down gives one result for each repeat. + +Returns a [**ConsoleKeyInfo**](Types#consolekeyinfo) for the first key-down event. **KeyCode** is the Windows virtual-key code, and **KeyChar** the character of the key, or the character with code 0 when the key has none, such as an arrow key. The three flags say whether Alt, Ctrl and Shift were down. Any key-down event counts, including the keys that are only modifiers, so a press of Shift is returned as **KeyCode** 16. Other events are taken from the queue and dropped while the method waits, and the release of a key is ignored. One result is returned for each key-down event; the repeat count of the event is not read. While it waits, the method sets the input mode to 0 and puts the old mode back before it returns. With no mode flags the console does not echo the key, does not edit a line, and does not turn Ctrl+C into an interruption. When *intercept* is **False** the method writes **KeyChar** itself with [**WriteTo**](#writeto), even when it is the character with code 0. + Calls `GetConsoleMode`, `SetConsoleMode` (twice) and `ReadConsoleInputW`. Without a console, or with the input redirected, `ReadConsoleInputW` fails at once and the method never returns; see [Known problems](#known-problems-in-version-1001). @@ -713,9 +816,11 @@ Syntax: *object*.**ReadKeyTimeout** ( *timeoutMs* [ , *intercept* ] ) **As Conso *intercept* : *optional* A **Boolean**, as for [**ReadKey**](#readkey). + The method asks [**KeyAvailable**](#keyavailable) every millisecond (it calls `Sleep` between the asks) until the time is over. As soon as the queue holds any event it returns [**ReadKey**](#readkey). When the time is over it returns an empty structure, with **KeyCode** 0. Since **KeyAvailable** is true for any event, a pending event that is not a key press, such as a key release, makes **ReadKey** wait for the next key press with no time limit. Calls `GetNumberOfConsoleInputEvents`, `Sleep`, `GetTickCount` and the functions of **ReadKey**. Without a console, it returns the empty structure after the time. + ### ReadLine {: .no_toc } @@ -727,7 +832,9 @@ Syntax: *object*.**ReadLine** [ ( *prompt* ) ] **As String** *prompt* : *optional* A **String**, written with [**WriteTo**](#writeto) before the console reads. The default is an empty string. + Waits in the console's line mode, with its echo and editing, until the user presses Enter, and returns the text without the carriage return and the line feed (all of them are removed). At most the number of characters of the read buffer (see [**New**](#new)) is read in one call. Calls `ReadConsoleW`. When the console is not in line mode, the result is that of the current mode. Without a console, or with the input redirected, `ReadConsoleW` fails and the method returns an empty string at once. + ```tb check_build Dim con As New cConsole @@ -750,7 +857,9 @@ Syntax: *object*.**ReadPassword** ( [ *prompt* [ , *maskChar* ] ] ) **As String* *maskChar* : *optional* A **String**, written once for each character typed. The default is `*`. An empty string shows nothing. + Reads with [**ReadKey**](#readkey), with the echo off. Enter ends the input and writes a line break. Backspace takes off the last character of the result and writes a backspace, a space and a backspace, which erases one cell. That is one cell even when *maskChar* has more than one character, and when *maskChar* is empty it is a cell of the prompt, since no mask was written. Escape cancels: the method returns an empty string. A key whose character has a code of 32 or more is added to the result and the mask is written. Keys with other characters, such as Tab, and keys without a character are ignored. Without a console it does not return, as **ReadKey** does not. + ```tb check_build Dim con As New cConsole @@ -767,7 +876,9 @@ Writes the changed part of the full-screen buffer to the console. Syntax: *object*.**Refresh** + In full-screen mode the method calls [**Flush**](cConsoleBuffer#flush) of the buffer, which writes only the rectangle that has changed since the last write, measures the time for [**LastRenderTime**](#lastrendertime) and updates [**FPS**](#fps). Outside full-screen mode it does nothing. Calls `WriteConsoleOutputW`, with the standard output handle. Without a console, the call fails and nothing is shown. + ### RefreshAll {: .no_toc } @@ -776,7 +887,9 @@ Writes the whole full-screen buffer to the console. Syntax: *object*.**RefreshAll** + Like [**Refresh**](#refresh), but the whole buffer is written, whether or not it has changed. A program uses it after something else has drawn on the screen, for example after an error message, to restore the picture. Outside full-screen mode it does nothing. + ### ResetColors {: .no_toc } @@ -785,7 +898,9 @@ Puts back the colour attributes that the console had when the object was created Syntax: *object*.**ResetColors** + Sets the attributes with `SetConsoleTextAttribute` and sets [**ForeColor**](#forecolor) and [**BackColor**](#backcolor) to the colours in the saved attributes (the low four bits, and the next four). Without a console, the saved attributes are Gray on Black. + ### Restore {: .no_toc } @@ -794,7 +909,9 @@ Puts the console back as it was when the object was created. Syntax: *object*.**Restore** + The method leaves the full-screen mode if it is on, sets the input mode and the output mode to the saved ones, sets the saved colour attributes, and sets the two saved code pages. It does not clear the screen, and does not put back the cursor size. The object stays usable, and the method can be called again. Calls `SetConsoleMode` (twice), `SetConsoleTextAttribute`, `SetConsoleCP` and `SetConsoleOutputCP`. The terminate event of the object calls it. + ### SetBufferSize {: .no_toc } @@ -806,7 +923,9 @@ Syntax: *object*.**SetBufferSize** ( *width* , *height* ) *width*, *height* : *required* **Long** values, the columns and rows of the buffer. + A value outside the range of an **Integer** raises error 6, *Overflow*. Windows refuses a buffer smaller than the window, and the method does not check. The buffer of full-screen mode is not changed here: it is resized when the size event arrives, which [**ProcessEvents**](#processevents) reads. Calls `SetConsoleScreenBufferSize`. + ### SetColors {: .no_toc } @@ -821,7 +940,9 @@ Syntax: *object*.**SetColors** ( *fore* [ , *back* ] ) *back* : *optional* A [**ConsoleColor**](Types#consolecolor), the background colour. The default is **Black**, and not the current background. + Stores both values in [**ForeColor**](#forecolor) and [**BackColor**](#backcolor), and applies them with `SetConsoleTextAttribute`. + ### SetCursorPosition {: .no_toc } @@ -833,7 +954,9 @@ Syntax: *object*.**SetCursorPosition** ( *col* , *row* ) *col*, *row* : *required* **Long** values, the column and the row, counted from the top left of the screen buffer. + A value outside the range of an **Integer** raises error 6, *Overflow*. A position outside the screen buffer is refused by Windows, and the method does not check. Calls `SetConsoleCursorPosition`. + ### WriteAt {: .no_toc } @@ -848,7 +971,9 @@ Syntax: *object*.**WriteAt** ( *col* , *row* , *text* ) *text* : *required* A **String**. + Moves the cursor with [**SetCursorPosition**](#setcursorposition) and writes the text with [**WriteTo**](#writeto), in the current colour attributes of the console. The cursor stays after the text. + ### WriteAtColored {: .no_toc } @@ -869,7 +994,9 @@ Syntax: *object*.**WriteAtColored** ( *col* , *row* , *text* , *fore* [ , *back* *back* : *optional* A [**ConsoleColor**](Types#consolecolor). The default is **Black**. + Moves the cursor and calls [**WriteColored**](#writecolored), so the colours that were tracked before are put back after the text. + ### WriteColored {: .no_toc } @@ -878,6 +1005,7 @@ Writes text in given colours. Syntax: *object*.**WriteColored** ( *text* , *fore* [ , *back* ] ) + *text* : *required* A **String**. @@ -888,6 +1016,7 @@ Syntax: *object*.**WriteColored** ( *text* , *fore* [ , *back* ] ) : *optional* A [**ConsoleColor**](Types#consolecolor), the background colour. The default is **Black**, whatever the current background is. Sets the colours with [**SetColors**](#setcolors), writes the text with [**WriteTo**](#writeto), and sets [**ForeColor**](#forecolor) and [**BackColor**](#backcolor) back to the values they had before the call. Those are the values that the object tracks: Gray on Black until a property or [**SetColors**](#setcolors) changes them. They are not read from the console, so a console that started in other colours ends up in Gray on Black after the first call, and not in its own colours. [**ResetColors**](#resetcolors) puts back the original ones. + ```tb check_build Dim con As New cConsole @@ -907,7 +1036,9 @@ Syntax: *object*.**WriteError** ( *text* ) *text* : *required* A **String**. + Calls `WriteConsoleW` on the standard error handle, so the text is written when that handle is a console and is lost when the error output is redirected to a file or a pipe. No line break is added. + ### WriteErrorLine {: .no_toc } @@ -919,7 +1050,9 @@ Syntax: *object*.**WriteErrorLine** ( *text* ) *text* : *required* A **String**. + Calls [**WriteError**](#writeerror) with the text and a line feed (code 10). + ### WriteF {: .no_toc } @@ -934,7 +1067,9 @@ Syntax: *object*.**WriteF** ( *format* , *args* ... ) *args* : *optional* A list of values, one for each placeholder, with `{0}` for the first. + Replaces `{0}` with the first value, `{1}` with the second, and so on, in that order, each converted with **CStr**, and writes the result with [**WriteTo**](#writeto). The only form of a placeholder is a number in braces; a format such as `{0:N2}` is not understood and stays as it is. The replacements are made one after the other on the whole text, so a value that contains `{1}` has that text replaced by the second value. A value that **CStr** cannot convert raises the error that **CStr** raises. A placeholder with no value stays in the text. + ```tb check_build Dim con As New cConsole @@ -952,7 +1087,9 @@ Syntax: *object*.**WriteLine** [ ( *text* ) ] *text* : *optional* A **String**. The default is an empty string, which writes only the line feed. + Calls [**WriteTo**](#writeto) with the text and `vbLf`, the line feed (code 10) alone. The console moves to the start of the next line when it processes output, which it does by default. + ### WriteLineColored {: .no_toc } @@ -970,7 +1107,9 @@ Syntax: *object*.**WriteLineColored** ( *text* , *fore* [ , *back* ] ) *back* : *optional* A [**ConsoleColor**](Types#consolecolor). The default is **Black**. + Calls [**WriteColored**](#writecolored) with the text and a line feed, so the line feed is written in the given colours too. + ### WriteLineF {: .no_toc } @@ -979,7 +1118,9 @@ Writes text with numbered placeholders, and a line feed. Syntax: *object*.**WriteLineF** ( *format* , *args* ... ) + Replaces the placeholders as [**WriteF**](#writef) does and writes the result with [**WriteLine**](#writeline). + ### WriteTo {: .no_toc } @@ -991,7 +1132,9 @@ Syntax: *object*.**WriteTo** ( *text* ) *text* : *required* A **String**. Every UTF-16 code unit of the string is written, in the current colour attributes of the console. + Calls `WriteConsoleW` on the standard output handle with the characters of the string, and ignores the result. The text is not converted, so any Unicode character that the console font can draw appears as it is. Control characters are handled by the console: a carriage return, a line feed, a backspace and the bell take effect, and escape sequences take effect only when the virtual-terminal mode is on; see [**EnableVirtualTerminal**](#enablevirtualterminal). When the output is redirected, or the program has no console, nothing is written. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/cConsoleBuffer.md b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/cConsoleBuffer.md index bde8ed14..3e279cd1 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/cConsoleBuffer.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/cConsoleBuffer.md @@ -36,15 +36,19 @@ In the syntax lines of this page, *object* is a **cConsoleBuffer**. Columns and ## The cells + The grid has *Width* columns and *Height* rows. A cell holds one UTF-16 code unit and one colour attribute, which is the foreground [**ConsoleColor**](Types#consolecolor) plus 16 times the background. The cells are kept row after row as the `CHAR_INFO` structure of Windows (two 16-bit values, 4 bytes), so the cell at *col* and *row* is number `row * Width + col`. [**BufferPtr**](#bufferptr) returns the address of the first cell. A character outside the Basic Multilingual Plane, such as most emoji, is two code units and takes two cells. The buffer has no notion of the width of a character: a character that the console draws two cells wide still takes one cell of the grid. Defaults: where a member takes *fore* and *back* and they are omitted, the cell gets **Gray** on **Black**. The members do not use the colours of the [**cConsole**](cConsole#forecolor) object. + ## The dirty rectangle + The buffer records the smallest rectangle that contains every cell changed since the last [**Flush**](#flush). [**SetChar**](#setchar), [**WriteAt**](#writeat), [**FillRect**](#fillrect) and [**CopyRegion**](#copyregion) grow the rectangle, and so do [**DrawBox**](#drawbox), [**DrawHLine**](#drawhline) and [**DrawVLine**](#drawvline) through **SetChar**. [**Clear**](#clear) and [**Resize**](#resize) mark the whole grid, and [**Flush**](#flush) and [**ClearDirty**](#cleardirty) empty the rectangle. [**IsDirty**](#isdirty) tells whether it holds anything, and [**GetDirtyRect**](#getdirtyrect) returns it. After the rectangle is emptied, its **Left** is *Width*, its **Top** is *Height*, and its **Right** and **Bottom** are 0. + ```tb check_build check_run Dim buf As cConsoleBuffer = New cConsoleBuffer(10, 3) @@ -63,11 +67,15 @@ Debug.Print r.Left & " " & r.Top & " " & r.Right & " " & r.Bottom ' 4 1 6 2 ``` -[**WriteAt**](#writeat), [**FillRect**](#fillrect) and [**CopyRegion**](#copyregion) pass the rectangle they were given to the record without checking it against the buffer, so a call with a position outside the grid can leave a dirty rectangle that reaches beyond the grid. [**Flush**](#flush) then asks Windows to write a region that is partly outside the screen buffer. A row below the grid or a column beyond its width does no harm: Windows clips the region. A negative column or row does: the next **Flush** writes nothing, and empties the rectangle all the same, so the cells that changed since the previous flush stay off the screen until they change again or [**RefreshAll**](cConsole#refreshall) of the console writes the whole buffer. + +[**WriteAt**](#writeat) and [**CopyRegion**](#copyregion) pass the positions they were given to the record without checking them against the buffer, and so does [**FillRect**](#fillrect) when the right or the bottom edge of its rectangle is negative, so a call with a position outside the grid can leave a dirty rectangle that reaches beyond the grid. [**Flush**](#flush) then asks Windows to write a region that is partly outside the screen buffer. A row below the grid or a column beyond its width does no harm: Windows clips the region. A negative column or row does: the next **Flush** writes nothing, and empties the rectangle all the same, so the cells that changed since the previous flush stay off the screen until they change again or [**RefreshAll**](cConsole#refreshall) of the console writes the whole buffer. + ## Batch updates + A program that changes most of the grid in one pass can skip the bookkeeping. Between [**BeginBatchUpdate**](#beginbatchupdate) and [**EndBatchUpdate**](#endbatchupdate) the members do not grow the dirty rectangle, and [**EndBatchUpdate**](#endbatchupdate) marks the whole grid as dirty, so the next [**Flush**](#flush) writes everything. Batches nest: the grid is marked when the outermost one ends. + ```tb check_build check_run Dim buf As cConsoleBuffer = New cConsoleBuffer(10, 3) @@ -89,8 +97,9 @@ Debug.Print r.Left & " " & r.Top & " " & r.Right & " " & r.Bottom ## Known problems in version 1.0.0.1 + > [!WARNING] 1.0.0.1 -> [**ScrollRegion**](#scrollregion) moves a region by less than the distance asked for when the region starts closer to the top or left edge than that distance, and clears the full distance, so cells are lost without an error. A [**WriteAt**](#writeat), [**FillRect**](#fillrect) or [**CopyRegion**](#copyregion) with a negative column or row makes the next [**Flush**](#flush) draw nothing, and raises no error; see [The dirty rectangle](#the-dirty-rectangle). +> [**ScrollRegion**](#scrollregion) moves a region by less than the distance asked for when the region starts closer to the top or left edge than that distance, and clears the full distance, so cells are lost without an error. A [**WriteAt**](#writeat) with a negative column or row, a [**CopyRegion**](#copyregion) with a negative destination, and a [**FillRect**](#fillrect) whose right or bottom edge is negative make the next [**Flush**](#flush) draw nothing, and raise no error; see [The dirty rectangle](#the-dirty-rectangle). ## Constructor @@ -107,7 +116,9 @@ Syntax: **New cConsoleBuffer** ( *width* , *height* ) *height* : *required* A **Long**, the number of rows, 1 or more. + The constructor does not check the sizes. It calls [**Resize**](#resize), so every cell holds a space in **Gray** on **Black**, and the whole grid is dirty. The buffer takes 4 bytes for each cell. + ## Properties @@ -118,7 +129,9 @@ The address of the first cell. **LongPtr**, read-only. Syntax: *object*.**BufferPtr** + The address is that of the first element of the array of cells, `VarPtr(m_Buffer(0))`. It is valid until [**Resize**](#resize) is called, which allocates a new array. A program can read or write the cells through it, for example with `CopyMemory`; see [The cells](#the-cells) for the layout. The dirty rectangle does not follow such writes, so the program calls [**Flush**](#flush) with the flag *forceAll*, or [**FlushRegion**](#flushregion). + ### Height {: .no_toc } @@ -168,6 +181,7 @@ Fills every cell with one character and one pair of colours. Syntax: *object*.**Clear** [ *fore* [ , *back* [ , *fillChar* ] ] ] + *fore* : *optional* A [**ConsoleColor**](Types#consolecolor), the text colour. The default is **Gray**. @@ -178,6 +192,7 @@ Syntax: *object*.**Clear** [ *fore* [ , *back* [ , *fillChar* ] ] ] : *optional* A **String** whose first character is used. The default is a space. An empty string raises error 5, *Invalid procedure call or argument*. Marks the whole grid as dirty, also during a batch update. + ### ClearDirty {: .no_toc } @@ -186,7 +201,9 @@ Empties the dirty rectangle without writing anything. Syntax: *object*.**ClearDirty** + Sets [**IsDirty**](#isdirty) to **False** and the rectangle to its empty form. The cells that were dirty are not written by the next [**Flush**](#flush), unless that call forces all. + ### CopyRegion {: .no_toc } @@ -201,7 +218,9 @@ Syntax: *object*.**CopyRegion** ( *srcLeft* , *srcTop* , *srcRight* , *srcBottom *dstLeft*, *dstTop* : *required* **Long** values, the position of the upper left corner of the copy. + The source rectangle is first cut to the grid. Its left and top edges are raised to 0 and its right and bottom edges lowered to the last column and row, and the destination is not moved to match, so a source that reaches outside the grid is copied from its cut position to the unchanged destination. Cells whose destination lies outside the grid are skipped. Source and destination can overlap; the copy runs in the direction that does not overwrite cells before they are read. The method passes the corners of the destination and of the source rectangle to the dirty rectangle, which a batch update suppresses, and it does not cut the destination corners to the grid. + ### DrawBox {: .no_toc } @@ -222,7 +241,9 @@ Syntax: *object*.**DrawBox** ( *left* , *top* , *right* , *bottom* [ , *fore* [ *doubleLine* : *optional* A **Boolean**. **True** uses the double-line characters (U+2554, U+2557, U+255A, U+255D, U+2550, U+2551), **False**, the default, the single-line ones (U+250C, U+2510, U+2514, U+2518, U+2500, U+2502). + The four corners, then the top and bottom lines, then the left and right lines are written with [**SetChar**](#setchar), so the parts outside the grid are left out. The inside of the box is not changed. No error is raised for a rectangle that is one cell wide or high; the corners overwrite each other. + ### DrawHLine {: .no_toc } @@ -237,10 +258,12 @@ Syntax: *object*.**DrawHLine** ( *col* , *row* , *length* [ , *fore* [ , *back* *length* : *required* A **Long**, the number of cells. + *fore*, *back*, *doubleLine* : *optional*, as for [**DrawBox**](#drawbox). The line uses U+2500, or U+2550 when *doubleLine* is **True**. The cells are written with [**SetChar**](#setchar). + ### DrawVLine {: .no_toc } @@ -255,8 +278,10 @@ Syntax: *object*.**DrawVLine** ( *col* , *row* , *length* [ , *fore* [ , *back* *length* : *required* A **Long**, the number of cells, going down. + *fore*, *back*, *doubleLine* : *optional*, as for [**DrawBox**](#drawbox). The line uses U+2502, or U+2551 when *doubleLine* is **True**. + ### EndBatchUpdate {: .no_toc } @@ -265,7 +290,9 @@ Ends a batch update. Syntax: *object*.**EndBatchUpdate** + Subtracts 1 from the counter when it is above 0. When the counter reaches 0 the whole grid is marked as dirty. A call without a matching **BeginBatchUpdate** does nothing. + ### FillRect {: .no_toc } @@ -283,7 +310,9 @@ Syntax: *object*.**FillRect** ( *left* , *top* , *right* , *bottom* [ , *char* [ *fore*, *back* : *optional* [**ConsoleColor**](Types#consolecolor) values. The defaults are **Gray** and **Black**. + The two corners of the cut rectangle are passed to the dirty rectangle. A rectangle with *left* greater than *right* changes no cell. + ### Flush {: .no_toc } @@ -298,7 +327,9 @@ Syntax: *object*.**Flush** ( *hOutput* [ , *forceAll* ] ) *forceAll* : *optional* A **Boolean**. **True** writes every cell. The default, **False**, writes the dirty rectangle only, and returns at once when nothing is dirty. + The cells go to the same positions in the screen buffer: the cell at column *c* and row *r* of the grid is written at column *c* and row *r*, counted from the top left of the screen buffer and not of the window. Afterwards the dirty rectangle is emptied. The method does not check the result of `WriteConsoleOutputW`: when the call fails, because the handle is not a console, the cells are not shown and the dirty rectangle is emptied all the same. Calls `WriteConsoleOutputW`. Without a console, nothing is shown. + ### FlushRegion {: .no_toc } @@ -313,7 +344,9 @@ Syntax: *object*.**FlushRegion** ( *hOutput* , *left* , *top* , *right* , *botto *left*, *top*, *right*, *bottom* : *required* **Long** values, the corners, inclusive. The rectangle is cut to the grid. + Writes the rectangle whether or not it is dirty, at the same position in the screen buffer. The dirty rectangle is left as it is. Calls `WriteConsoleOutputW`. + ### GetChar {: .no_toc } @@ -325,7 +358,9 @@ Syntax: *object*.**GetChar** ( *col* , *row* ) **As ConsoleCharInfo** *col*, *row* : *required* **Long** values, the position of the cell. + Returns a [**ConsoleCharInfo**](Types#consolecharinfo). **Char** holds the one character, **ForeColor** and **BackColor** the colours; **Style** is always 0. For a position outside the grid it returns the empty structure: **Char** is an empty string and both colours are **Black**. + ### GetDirtyRect {: .no_toc } @@ -346,7 +381,9 @@ Syntax: *object*.**Resize** ( *width* , *height* ) *width*, *height* : *required* **Long** values, the new number of columns and rows. + The old contents are lost, not copied: the method allocates a new array and calls [**Clear**](#clear) with its defaults, so every cell is a space in **Gray** on **Black** and the whole grid is dirty. Any address from [**BufferPtr**](#bufferptr) is no longer valid. The full-screen mode of **cConsole** calls this method when a size event shows a new size, so a program redraws after the resize handler. + ### ScrollRegion {: .no_toc } @@ -364,9 +401,11 @@ Syntax: *object*.**ScrollRegion** ( *left* , *top* , *right* , *bottom* , *delta *deltaCol* : *required* A **Long**. A positive value moves the content right, a negative value left. + The rows (or columns) that the move uncovers are filled with spaces in **Gray** on **Black**. Cells moved out of the region are lost. The method does nothing when both deltas are 0. Note the order of the two deltas: rows come first. The method works by copying from a source rectangle that is *deltaRow* rows above and *deltaCol* columns left of the region. When that source reaches above the top of the grid, or left of its left edge, it is cut (see [**CopyRegion**](#copyregion)) and the destination is not moved, so the content moves by the distance between the region and the edge, which is less than the delta, while the full number of rows or columns is cleared. At the top edge a scroll down of one row therefore moves nothing and only clears the first row. Scrolling up and to the left, and scrolling down or right in a region that is as far from the top or left edge as the delta, work as expected. + ```tb check_build check_run Dim a As cConsoleBuffer = New cConsoleBuffer(5, 4) @@ -409,6 +448,7 @@ Syntax: *object*.**SetChar** ( *col* , *row* , *char* [ , *fore* [ , *back* ] ] *col*, *row* : *required* **Long** values, the position of the cell. A position outside the grid is ignored, and nothing is marked. + *char* : *required* A **String**. Only its first character is used. An empty string raises error 5, *Invalid procedure call or argument*. @@ -416,6 +456,7 @@ Syntax: *object*.**SetChar** ( *col* , *row* , *char* [ , *fore* [ , *back* ] ] : *optional* [**ConsoleColor**](Types#consolecolor) values. The defaults are **Gray** and **Black**. Marks the cell as dirty. + ### WriteAt {: .no_toc } @@ -427,6 +468,7 @@ Syntax: *object*.**WriteAt** ( *col* , *row* , *text* [ , *fore* [ , *back* ] ] *col*, *row* : *required* **Long** values, the position of the first character. + *text* : *required* A **String**. Each UTF-16 code unit takes one cell, including carriage returns, line feeds and tabs, which are stored as ordinary characters. Characters that fall to the right of the last column are dropped, and characters to the left of column 0 are skipped. The text does not wrap. @@ -434,6 +476,7 @@ Syntax: *object*.**WriteAt** ( *col* , *row* , *text* [ , *fore* [ , *back* ] ] : *optional* [**ConsoleColor**](Types#consolecolor) values. The defaults are **Gray** and **Black**. Nothing is written when *row* is outside the grid. The dirty rectangle is grown by the first and the last column of the text and by *row* in every case, even when they lie outside the grid, and also when *text* is empty. + ### WriteTextEx {: .no_toc } @@ -457,7 +500,9 @@ Syntax: *object*.**WriteTextEx** ( *text* , *col* , *row* , *fore* , *back* , *m *ellipsis* : *optional* A **String**, the mark that shows the text was cut. The default is the character U+2026, the horizontal ellipsis. + When *text* is longer than *maxWidth*, the method writes the first `maxWidth - Len(ellipsis)` characters of the text followed by the ellipsis, so that the result is *maxWidth* characters. When the ellipsis itself is as long as *maxWidth* or longer, only the first *maxWidth* characters of the ellipsis are written, and nothing of the text. Shorter text is written whole. The order of the parameters differs from [**WriteAt**](#writeat): the text comes first. + ```tb check_build check_run Dim buf As cConsoleBuffer = New cConsoleBuffer(20, 2) diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/index.md b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/index.md index dd8d744d..6c195c89 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/index.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/index.md @@ -29,6 +29,7 @@ con.WriteLine con.Restore ``` + > [!WARNING] 1.0.0.1 > Several members do not do what their names suggest. [**ReadKey**](cConsole#readkey) never returns in a program that has no console input, and a comparison of a key with a member of [**ConsoleKey**](Types#consolekey) is true for the wrong key. [Defects in version 1.0.0.1](#defects-in-version-1001) lists them with the pages that describe them. @@ -52,15 +53,18 @@ A program with no console can create a **cConsole**, and the buffer of the full- ## Defects in version 1.0.0.1 + - [**ReadKey**](cConsole#readkey) and [**ReadPassword**](cConsole#readpassword) never return when the program has no console input. - The key codes of [**ConsoleKey**](Types#consolekey) are not the virtual-key codes that [**ReadKey**](cConsole#readkey) and the key handler return, except for Enter, Escape, Space, Backspace and Tab. - [**KeyAvailable**](cConsole#keyavailable) is true for any input event, including a key release. - [**WriteColored**](cConsole#writecolored), [**WriteLineColored**](cConsole#writelinecolored) and [**WriteAtColored**](cConsole#writeatcolored) default the background to black, and leave the console in Gray on Black. - [**Refresh**](cConsole#refresh) draws at the top of the screen buffer, which is out of sight when the window shows another part of it, and draws nothing after a [**WriteAt**](cConsoleBuffer#writeat) with a negative column or row. - [**OnResize**](cConsole#onresize) is given the size of the screen buffer, not of the window. -- [**OnCtrlEvent**](cConsole#onctrlevent) adds a handler that is never removed, and none of the four handler properties can be cleared. +- [**OnCtrlEvent**](cConsole#onctrlevent) adds a registration with Windows that is never removed, and the compiler refuses `0` and **Nothing** for all four handler properties, so none of them can be cleared. - [**ScrollRegion**](cConsoleBuffer#scrollregion) of the buffer moves a region by less than the distance asked for when the region is near the top or left edge of the buffer. - [**RepeatStr**](mConsoleStyle#repeatstr) repeats only the first character of its string, and [**StripAnsi**](mConsoleStyle#stripansi) removes text after some sequences. +- [**Title**](cConsole#title) of a title longer than 259 characters ends in a character with code 0. + - Of the types in [Types](Types), [**ConsoleTextStyle**](Types#consoletextstyle), [**ConsoleMode**](Types#consolemode), [**ConsoleEventType**](Types#consoleeventtype), [**ConsoleEventInfo**](Types#consoleeventinfo) and the delegates **ConsoleTimerCallback** and **ConsoleRenderCallback** are not used by any member. ## Classes and modules @@ -81,3 +85,7 @@ A fifth public module, **mConsoleAPI**, has no page of its own, since a program - [TBMANLIB_ConsoleComponents](../TBMANLIB_ConsoleComponents/) package -- progress bars, charts, panels, layouts and a data grid that draw into the buffer - [Console Applications](../../../../Features/Project-Configuration/Project-Types#console-applications) -- the project type that the package is made for - [Importing a package from TWINSERV](../../../../Features/Packages/Importing-TWINSERV) -- adding a published package to a project + +## Test cases + +The notes and warnings of this section are checked by the test cases in [TBMANLIB_ConsoleCore-tests.twinproj](TBMANLIB_ConsoleCore-tests.twinproj), a twinBASIC project. It uses TBMANLIB_ConsoleCore as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/mConsoleStyle.md b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/mConsoleStyle.md index 146b415b..b87d28fe 100644 --- a/docs/Reference/Contributed/TBMANLIB_ConsoleCore/mConsoleStyle.md +++ b/docs/Reference/Contributed/TBMANLIB_ConsoleCore/mConsoleStyle.md @@ -46,14 +46,17 @@ con.Restore ## Known problems in version 1.0.0.1 + - [**RepeatStr**](#repeatstr) repeats the first character of its string, not the string. - [**StripAnsi**](#stripansi) ends a sequence only at one of eight letters, so it also removes the text after the sequences that end in another letter, such as the one made by **CursorHide**. + ## Cursor movement ### Cursor functions {: .no_toc } + | Function | Returns | Moves | |---|---|---| | **CursorUp** ( [ *n* ] ) | CSI *n* `A` | up *n* lines | @@ -70,12 +73,14 @@ con.Restore | **CursorShow** | CSI `?25h` | shows the cursor | *n*, *row* and *col* are **Long** values. *n* of the first six functions is optional and defaults to 1; **CursorColumn** has no default. The numbers of these sequences are counted from 1, whereas [**SetCursorPosition**](cConsole#setcursorposition) of **cConsole** counts from 0, takes the column first, and works on the screen buffer, not the window. **CursorPosition** takes the row first, as the sequence does. The value is written as it is: a negative number or 0 is not checked. + ## Clearing ### ClearScreen, ClearLine {: .no_toc } + Syntax: **ClearScreen** ( [ *mode* ] ) **As String** Syntax: **ClearLine** ( [ *mode* ] ) **As String** @@ -84,12 +89,14 @@ Syntax: **ClearLine** ( [ *mode* ] ) **As String** : *optional* A **Long**. The default is 2. For **ClearScreen**: 0 clears from the cursor to the end of the screen, 1 from the start to the cursor, 2 the whole screen, and 3 the whole screen and the scrolled-off lines. For **ClearLine**: 0 clears from the cursor to the end of the line, 1 from the start of the line to the cursor, 2 the whole line. **ClearScreen** returns CSI *mode* `J` and **ClearLine** returns CSI *mode* `K`. They differ from [**Clear**](cConsole#clear) and [**ClearLine**](cConsole#clearline) of **cConsole**, which fill the screen buffer with the tracked colours through the console functions; the sequences clear with the colours that are in force. Whether a console honours mode 3 depends on the console. + ## Text styles ### Style functions {: .no_toc } + | Function | Returns | Effect | |---|---|---| | **Reset** | CSI `0m` | ends every colour and style | @@ -103,12 +110,14 @@ Syntax: **ClearLine** ( [ *mode* ] ) **As String** | **Strikethrough** | CSI `9m` | struck-through text | The function for dim text is called **Dim_** because **Dim** is a keyword of the language. Which of these styles a console draws depends on the console: a console may show bold as a brighter colour, or ignore blinking and italics. + ## Colours ### Colour functions {: .no_toc } + The 16 colours of the escape sequences. The foreground sequence ends in `m` and begins CSI; the number is the code in the table. | Colour | Foreground function | Code | Background function | Code | @@ -132,10 +141,12 @@ The 16 colours of the escape sequences. The foreground sequence ends in `m` and | Bright white | **FgBrightWhite** | 97 | **BgBrightWhite** | 107 | None of these functions has parameters. The order of the colours is that of the ANSI standard, not that of [**ConsoleColor**](Types#consolecolor), whose numbers are those of the Windows console attributes: ANSI red is 1 where **ConsoleColor.DarkRed** is 4. A program that uses both has to map the colours itself. + ### Fg256, Bg256, FgRGB, BgRGB {: .no_toc } + Syntax: **Fg256** ( *colorIndex* ) **As String** Syntax: **Bg256** ( *colorIndex* ) **As String** @@ -151,12 +162,14 @@ Syntax: **BgRGB** ( *r* , *g* , *b* ) **As String** : *required* **Long** values, 0 to 255, the red, green and blue parts of a 24-bit colour. **Fg256** returns CSI `38;5;` *colorIndex* `m` and **Bg256** CSI `48;5;` *colorIndex* `m`. **FgRGB** returns CSI `38;2;` *r* `;` *g* `;` *b* `m` and **BgRGB** CSI `48;2;` *r* `;` *g* `;` *b* `m`. The numbers are written as given and not checked. A console that has no 256-colour or true-colour mode shows the nearest colour or ignores the sequence. + ## Scrolling ### ScrollUp, ScrollDown {: .no_toc } + Syntax: **ScrollUp** ( [ *n* ] ) **As String** Syntax: **ScrollDown** ( [ *n* ] ) **As String** @@ -165,6 +178,7 @@ Syntax: **ScrollDown** ( [ *n* ] ) **As String** : *optional* A **Long**, the number of lines. The default is 1. **ScrollUp** returns CSI *n* `S`, which scrolls the text up, and **ScrollDown** returns CSI *n* `T`, which scrolls it down. + ## Window and screen @@ -178,7 +192,9 @@ Syntax: **SetTitle** ( *title* ) **As String** *title* : *required* A **String**. + Returns `ESC ] 0 ;` *title* and the bell character (code 7). The title of the console window can also be set with the [**Title**](cConsole#title) property of **cConsole**, which calls a Windows function and does not need the virtual-terminal mode. + ### AltScreenOn, AltScreenOff {: .no_toc } @@ -187,7 +203,11 @@ Syntax: **AltScreenOn** **As String** Syntax: **AltScreenOff** **As String** -**AltScreenOn** returns CSI `?1049h`, which switches to the alternate screen buffer, a second screen that leaves the text of the main one intact; **AltScreenOff** returns CSI `?1049l`, which switches back. The full-screen mode of [**cConsole**](cConsole#enterfullscreenmode) does not use the alternate buffer. In a console with virtual-terminal processing on, **AltScreenOn** shows an empty screen, the text written afterwards goes to it, and **AltScreenOff** brings back the earlier text. A program that wants its picture to vanish when it ends writes **AltScreenOn** first and **AltScreenOff** last. + +**AltScreenOn** returns CSI `?1049h`, which switches to the alternate screen buffer, a second screen that leaves the text of the main one intact; **AltScreenOff** returns CSI `?1049l`, which switches back. + + +The full-screen mode of [**cConsole**](cConsole#enterfullscreenmode) does not use the alternate buffer. In a console with virtual-terminal processing on, **AltScreenOn** shows an empty screen, the text written afterwards goes to it, and **AltScreenOff** brings back the earlier text. A program that wants its picture to vanish when it ends writes **AltScreenOn** first and **AltScreenOff** last. ### Hyperlink {: .no_toc } @@ -202,7 +222,9 @@ Syntax: **Hyperlink** ( *url* , *text* ) **As String** *text* : *required* A **String**, the visible text. + Returns `ESC ] 8 ; ;` *url* and the bell character, then *text*, then `ESC ] 8 ; ;` and the bell character. A console that supports this operating system command (OSC 8) shows *text* as a link; another console shows *text* only, or the characters of the sequence. + ## Styled text @@ -219,7 +241,9 @@ Syntax: **Styled** ( *text* , *style* ) **As String** *style* : *required* A **String**, the sequences to put before the text, such as `mConsoleStyle.Bold & mConsoleStyle.FgCyan`. + Returns *style*, *text* and **Reset**. + ### Colored {: .no_toc } @@ -237,7 +261,9 @@ Syntax: **Colored** ( *text* , *fgColor* [ , *bgColor* ] ) **As String** *bgColor* : *optional* A **String**, a background sequence. The default is an empty string, which leaves the background alone. + Returns *fgColor*, *bgColor*, *text* and **Reset**. The function does not check that the two strings are colour sequences; any strings are joined. + ### ColoredRGB {: .no_toc } @@ -255,7 +281,9 @@ Syntax: **ColoredRGB** ( *text* , *fgR* , *fgG* , *fgB* [ , *bgR* [ , *bgG* [ , *bgR*, *bgG*, *bgB* : *optional* **Long** values, 0 to 255, the background colour. They default to -1. The background is used only when all three are 0 or more. + Returns **FgRGB**, the background sequence when all three background values are given, *text*, and **Reset**. + ## Text helpers @@ -277,7 +305,9 @@ Syntax: **CenterText** ( *text* , *width* [ , *fillChar* ] ) **As String** *fillChar* : *optional* A **String** whose first character is used to pad. The default is a space. + Returns a string *width* characters long. The padding on the left is `(width - Len(text)) \ 2` characters, and the rest goes to the right, so an odd amount of padding gives the extra character to the right. Text that is as long as *width* or longer is cut to *width* characters. + ### LeftAlign {: .no_toc } @@ -286,7 +316,9 @@ Pads text on the right to a width. Syntax: **LeftAlign** ( *text* , *width* [ , *fillChar* ] ) **As String** + Parameters as for [**CenterText**](#centertext). Text that is as long as *width* or longer is cut to *width* characters. + ### RightAlign {: .no_toc } @@ -295,7 +327,9 @@ Pads text on the left to a width. Syntax: **RightAlign** ( *text* , *width* [ , *fillChar* ] ) **As String** + Parameters as for [**CenterText**](#centertext). Text that is as long as *width* or longer is cut to its first *width* characters, not its last. + ### RepeatStr {: .no_toc } @@ -310,7 +344,9 @@ Syntax: **RepeatStr** ( *s* , *count* ) **As String** *count* : *required* A **Long**. A value of 0 or less returns an empty string. + The function is implemented with `String$(count, s)`, which repeats only the first character of *s*. `RepeatStr("ab", 3)` therefore returns `aaa`, and not `ababab`. For a one-character *s* the result is as expected. To repeat a longer string, build it with a loop or with `Replace(Space$(count), " ", s)`. + ### StripAnsi {: .no_toc } @@ -322,7 +358,9 @@ Syntax: **StripAnsi** ( *text* ) **As String** *text* : *required* A **String**. + The function drops every character from an `ESC` character up to and including the next letter among `m`, `H`, `J`, `K`, `A`, `B`, `C` and `D`, and returns the rest. That covers the colour and style sequences (they end in `m`) and the cursor positioning and clearing sequences of this module that end in one of those letters. A sequence that ends in another letter, such as `l` of **CursorHide** and `h` of **CursorShow**, `s` and `u` of the save and restore, `S` and `T` of the scroll functions, `E`, `F` and `G` of the line functions, `?1049h` of **AltScreenOn**, or the bell character that ends **SetTitle** and **Hyperlink**, is not recognised: the function goes on dropping characters until it meets one of the eight letters, so some of the text that follows is lost too. + ```tb check_build check_run Debug.Print mConsoleStyle.StripAnsi(mConsoleStyle.Colored("Hello", mConsoleStyle.FgRed)) @@ -352,7 +390,9 @@ Syntax: **Truncate** ( *text* , *maxLen* [ , *ellipsis* ] ) **As String** *ellipsis* : *optional* A **String**, the mark that shows the text was cut. The default is three periods. + Returns *text* when it is no longer than *maxLen*. Otherwise it returns the first `maxLen - Len(ellipsis)` characters of *text* followed by *ellipsis*; when *maxLen* is not larger than the length of *ellipsis*, it returns the first *maxLen* characters of *ellipsis*. + ### WordWrap {: .no_toc } @@ -367,7 +407,9 @@ Syntax: **WordWrap** ( *text* , *width* ) **As String** *width* : *required* A **Long**, the longest line in characters. + Splits *text* at the space characters and joins the words into lines, with a line feed (code 10) between lines. A word that is longer than *width* is not broken; it is put on a line of its own. Other characters, such as line breaks inside *text*, count as part of a word. + ```tb check_build check_run Dim lines() As String @@ -390,6 +432,7 @@ The module holds **String** constants for drawing text with characters that are ### Box-drawing characters {: .no_toc } + | Single line | | Double line | | |---|---|---|---| | **BOX_HORIZONTAL** | U+2500 | **BOX_D_HORIZONTAL** | U+2550 | @@ -403,10 +446,12 @@ The module holds **String** constants for drawing text with characters that are | **BOX_T_TOP** | U+252C | **BOX_D_T_TOP** | U+2566 | | **BOX_T_BOTTOM** | U+2534 | **BOX_D_T_BOTTOM** | U+2569 | | **BOX_CROSS** | U+253C | **BOX_D_CROSS** | U+256C | + ### Block, progress and arrow characters {: .no_toc } + | Constant | Code point | Constant | Code point | |---|---|---|---| | **BLOCK_FULL** | U+2588 | **ARROW_UP** | U+2191 | @@ -417,10 +462,12 @@ The module holds **String** constants for drawing text with characters that are | **BLOCK_LIGHT** | U+2591 | **ARROW_LEFT_RIGHT** | U+2194 | | **BLOCK_MEDIUM** | U+2592 | **PROGRESS_FILLED** | U+2588 | | **BLOCK_DARK** | U+2593 | **PROGRESS_EMPTY** | U+2591 | + ### Symbol characters {: .no_toc } + | Constant | Code point | Constant | Code point | |---|---|---|---| | **CHECK_MARK** | U+2713 | **SQUARE** | U+25A0 | @@ -428,6 +475,7 @@ The module holds **String** constants for drawing text with characters that are | **BULLET** | U+2022 | **STAR** | U+2605 | | **DIAMOND** | U+25C6 | **STAR_EMPTY** | U+2606 | | **CIRCLE** | U+25CF | **CIRCLE_EMPTY** | U+25CB | + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_Diagnostic/Collectors.md b/docs/Reference/Contributed/TBMANLIB_Diagnostic/Collectors.md index 8ce74bff..869dc775 100644 --- a/docs/Reference/Contributed/TBMANLIB_Diagnostic/Collectors.md +++ b/docs/Reference/Contributed/TBMANLIB_Diagnostic/Collectors.md @@ -10,6 +10,7 @@ has_toc: false The package has eleven collector classes. Each one finds one kind of information about the computer or its processes, and returns it as a [**DiagnosticResult**](DiagnosticResult) whose items are pairs of text: a key, which is the label of a line of the report, and a value. All eleven implement [**IDiagnosticCollector**](IDiagnosticCollector), so a [**DiagnosticEngine**](DiagnosticEngine) runs them in the same way, and all eleven report that they need no administrator rights. This page describes what each collector reports, the Windows functions it calls, and what it does when the information is missing. + ```tb check_build check_run Dim collectors(0 To 10) As IDiagnosticCollector Set collectors(0) = New OSInfoCollector @@ -41,6 +42,7 @@ Next i ' Disk: 2 False ' Network: 2 True ``` + * TOC {:toc} @@ -172,6 +174,7 @@ The name is derived from the numbers alone, so a version of Windows Server gets Category `CPU Info`; mode **Static**; no summary. **Description**: `CPU architecture and processor count`. + **Keys.** | Key | Value | @@ -183,6 +186,7 @@ Category `CPU Info`; mode **Static**; no summary. **Description**: `CPU architec | `Page Size` | The size of a memory page, written by [**FormatBytes**](FormatHelper#formatbytes) with no decimals. The text is `4. KB`, with a decimal separator after the number: see [Known problems](.#known-problems-in-version-1001). | | `Processor Name` | The text of the registry value `ProcessorNameString`, without leading and trailing spaces. Left out when it is empty or cannot be read. | | `Frequency` | The registry value `~MHz` and ` MHz`, such as `3600 MHz`. Left out when it cannot be read. | + **Windows functions.** `GetNativeSystemInfo` of `kernel32.dll`. For the two registry values, `RegOpenKeyExW`, `RegQueryValueExW` and `RegCloseKey` of `advapi32.dll`, on `HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0`. Only the first processor is read, so the name and the frequency are those of processor 0. @@ -193,6 +197,7 @@ Category `CPU Info`; mode **Static**; no summary. **Description**: `CPU architec Category `Locale`; mode **Static**; no summary. **Description**: `Regional settings, code pages, and timezone`. + **Keys.** | Key | Value | @@ -207,6 +212,7 @@ Category `Locale`; mode **Static**; no summary. **Description**: `Regional setti | `OEM Code Page` | `CP` and the number of the OEM code page, such as `CP437`. | | `Timezone` | `UTC`, the sign, the hours and, when it is not a whole hour, a colon and two digits of minutes: `UTC+0`, `UTC+1`, `UTC-5`, `UTC+5:30`. | | `Timezone Name` | The name of the time zone in standard time. Left out when empty. | + `Timezone` is built from the **Bias** field of the Windows time-zone record, which is the offset of standard time. The **DaylightBias** field is not read, and neither is the value that tells whether daylight saving time is in effect, so the offset does not change in summer, and `Timezone Name` is the standard-time name all year. On a computer in Central European Summer Time, where the clock is at UTC+2, the items are `Timezone` = `UTC+1` and `Timezone Name` = `W. Europe Standard Time`. See [Known problems](.#known-problems-in-version-1001). @@ -225,7 +231,9 @@ Category `Environment`; mode **Static**; has a summary. **Description**: `System The value of `PATH` is cut to its first 200 characters when it is longer, and `...` is added. + **Summary.** The summary result has [**IsSummary**](DiagnosticResult#issummary) set to **True** and the items for `PATH`, `TEMP` and `USERPROFILE`, when they are defined. The value of `PATH` is the number of its entries in the form `(24 entries)`, where an entry is a part between semicolons. + **Windows functions.** None. The collector reads the variables with the **Environ$** function of the runtime library. @@ -290,7 +298,9 @@ Category `CPU Load`; mode **Dynamic**; no summary. **Description**: `Current CPU | `Usage` | The load as a percentage with one decimal place, such as `12.3%`. | | `Usage Value` | The same number without the percent sign, such as `12.3`. | + The collector measures the load between two calls. It keeps the idle, kernel and user times that `GetSystemTimes` reported the last time, and the load is `(1 - idle change / (kernel change + user change)) * 100`, limited to the range 0 to 100. The first call of a new object, and the first call after [**Reset**](#reset), has nothing to compare with and reports 0 (`0.0%`). A call in which the counters did not change also gives 0. A program that wants a reading keeps one object and calls it again after some time, as the engine does when it runs the same registered collector a second time. + ```tb check_build check_run Dim cpu As New CPULoadCollector @@ -339,6 +349,7 @@ The next result reports 0 and starts a new measurement. It is a method of the cl Category `Memory`; mode **Dynamic**; no summary. **Description**: `Physical and virtual memory status`. + **Keys.** The result has these eight items, in this order: | Key | Value | @@ -351,6 +362,7 @@ Category `Memory`; mode **Dynamic**; no summary. **Description**: `Physical and | `Available Page File` | The part of that limit that is available. | | `Total Virtual` | The size of the user-mode address space of the program that runs the collector. | | `Available Virtual` | The part of that address space that is free. | + The sizes are formatted with [**FormatBytes**](FormatHelper#formatbytes). @@ -363,6 +375,7 @@ The sizes are formatted with [**FormatBytes**](FormatHelper#formatbytes). Category `Uptime`; mode **Dynamic**; no summary. **Description**: `System uptime since last boot`. + **Keys.** | Key | Value | @@ -372,6 +385,7 @@ Category `Uptime`; mode **Dynamic**; no summary. **Description**: `System uptime | `Total Hours` | The whole number of hours. | | `Total Seconds` | The whole number of seconds. | | `Boot Time` | The local time at which the system started, as `yyyy-mm-dd hh:nn:ss`. | + `Boot Time` is calculated: it is the current time less the uptime. @@ -394,7 +408,9 @@ Category `Processes`; mode **Dynamic**; has a summary. **Description**: `Running The key of a process is `PID ` and its process identifier. The process with the identifier 0 is listed as `[System Process]`. + **Summary.** The summary result has [**IsSummary**](DiagnosticResult#issummary) set to **True** and three items: `Total Processes`, `Total Threads` (the sum of the thread counts of all processes) and `Current PID`. + **Windows functions.** `CreateToolhelp32Snapshot` with `TH32CS_SNAPPROCESS`, `Process32FirstW`, `Process32NextW` and `CloseHandle`, and `GetCurrentProcessId`, all of `kernel32.dll`. The snapshot lists the processes of all users, and needs no administrator rights. diff --git a/docs/Reference/Contributed/TBMANLIB_Diagnostic/DiagnosticEngine.md b/docs/Reference/Contributed/TBMANLIB_Diagnostic/DiagnosticEngine.md index 778bff09..55e10830 100644 --- a/docs/Reference/Contributed/TBMANLIB_Diagnostic/DiagnosticEngine.md +++ b/docs/Reference/Contributed/TBMANLIB_Diagnostic/DiagnosticEngine.md @@ -49,7 +49,9 @@ Each notification exists twice, as an event and as a callback that a program set ### A collector that raises an error + A collector that raises a run-time error does not stop the run. The engine takes the text of the error (**Err.Description**), replaces the collector's result with a new, empty result for the same category that holds the text as its [**ErrorMessage**](DiagnosticResult#errormessage), and goes on with the next collector. The number and the source of the error are not kept. The engine raises [**Error**](#error) for it before **CollectorCompleted**, which then reports `success` as **False** and an `itemCount` of 0. [**RunCategory**](#runcategory) and [**RunByRefreshMode**](#runbyrefreshmode) handle an error in the same way. + A collector that fails without raising an error, such as one whose Windows call returns an error code, returns its own result with **Success** set to **False**. The engine does not call **Error** for it. @@ -189,7 +191,9 @@ Whether a run is in progress. Syntax: *engine*.**IsRunning** **As Boolean** + Read-only. [**RunAll**](#runall) and [**RunByRefreshMode**](#runbyrefreshmode) set it to **True** when they start and to **False** when their loop over the collectors ends. It is therefore **True** in the handlers of **CollectorStarted**, **Error**, **CollectorCompleted** and the **Progress** events that come before a collector, and **False** in the last **Progress** event (100, `Complete`) and in **AllCompleted**. [**RunCategory**](#runcategory) sets it to **False** before it raises **CollectorCompleted**. A run that a handler starts sets it to **False** when that run ends, while the outer run goes on. + ### OnCollectorCompleted {: .no_toc } @@ -275,7 +279,9 @@ The results of the last [**RunAll**](#runall). Syntax: *engine*.**Results** **As List(Of DiagnosticResult(Of KeyValuePair(Of String, String)))** + Read-only. It is the list that **RunAll** fills and returns, not a copy of it. **RunAll** empties it when it starts, so a variable that holds the result of an earlier run is empty or refilled once the next run has started. [**RunCategory**](#runcategory) and [**RunByRefreshMode**](#runbyrefreshmode) do not add to it. Copy the items to a list of the program to keep them. + ```tb check_build check_run Dim engine As New DiagnosticEngine @@ -334,12 +340,14 @@ Syntax: *engine*.**RegisterCollector** ( *collector* ) *collector* : *required* An object that implements [**IDiagnosticCollector**](IDiagnosticCollector). + The collector goes to the end of the list. Registering the same collector twice, or two collectors with the same [**CategoryName**](IDiagnosticCollector#categoryname), is an error. The method raises error 5 (*Invalid procedure call or argument*) in these cases, with the source `DiagnosticEngine.RegisterCollector`: - *collector* is **Nothing**. The text is `Collector cannot be Nothing`. - A registered collector has the same category name. The text is `Collector already exists: ` and the name. The names are compared with the `=` operator, and the package has no **Option Compare** statement, so the comparison is binary and the letter case matters: `Alpha` and `ALPHA` are two names. + ```tb check_build check_run projname=diag-engine Dim engine As New DiagnosticEngine @@ -372,7 +380,9 @@ Syntax: *engine*.**RunAll** [ ( *useSummary* ) ] **As List(Of DiagnosticResult(O Returns the list [**Results**](#results), which holds one result for each collector, in the order of registration. [How a run proceeds](#how-a-run-proceeds) describes the steps and the events. A run with no collector raises **Progress** with 100 and `Complete` and then **AllCompleted** with an empty list. + A summary result has [**IsSummary**](DiagnosticResult#issummary) set to **True** when the collector sets it, as the built-in collectors that support a summary do. + ```tb check_build check_run Dim engine As New DiagnosticEngine @@ -401,7 +411,9 @@ Syntax: *engine*.**RunByRefreshMode** ( *mode* [ , *useSummary* ] ) **As List(Of *useSummary* : *optional* A **Boolean**, as for [**RunAll**](#runall). Default: **False**. + Returns a new list with one result for each collector that ran, in the order of registration. The list is not [**Results**](#results), which this method does not change. The method raises **CollectorStarted**, **CollectorCompleted** and **Error** for each collector, and neither **Progress** nor **AllCompleted**. It clears the flag of [**Cancel**](#cancel) when it starts and reads it before each collector, as **RunAll** does. + ```tb check_build check_run Dim engine As New DiagnosticEngine @@ -437,9 +449,11 @@ Syntax: *engine*.**RunCategory** ( *categoryName* [ , *useSummary* ] ) **As Diag *useSummary* : *optional* A **Boolean**, as for [**RunAll**](#runall). Default: **False**. + Returns the result of the collector. The result is not added to [**Results**](#results). The method raises **CollectorStarted**, **CollectorCompleted** and **Error**, and neither **Progress** nor **AllCompleted**. When no collector has the name, the method raises no error and no event. It returns a result for *categoryName* with **Success** set to **False** and the message `Collector not found: ` and the name. + ```tb check_build check_run Dim engine As New DiagnosticEngine @@ -466,7 +480,9 @@ Syntax: *engine*.**UnregisterCollector** ( *categoryName* ) *categoryName* : *required* A **String**, the category name of the collector to remove. + Removes the first collector with that name. A name that no collector has is ignored without an error. The names are compared as in [**RegisterCollector**](#registercollector), so the letter case matters. + ## Events diff --git a/docs/Reference/Contributed/TBMANLIB_Diagnostic/Dictionary.md b/docs/Reference/Contributed/TBMANLIB_Diagnostic/Dictionary.md index 0690b531..22fa80f8 100644 --- a/docs/Reference/Contributed/TBMANLIB_Diagnostic/Dictionary.md +++ b/docs/Reference/Contributed/TBMANLIB_Diagnostic/Dictionary.md @@ -28,7 +28,9 @@ Debug.Print Err.Number ' 91 ``` + The rest of this page describes the members as the source declares them. **Item** is a function, so a value would be read as `ages.Item("Bob")`. The class has no default member and no enumerator: there is no `ages("Bob")`, a value is set with [**SetItem**](#setitem), and the keys are visited with an index loop over [**Keys**](#keys). The keys are compared with the `=` operator. + The name `Dictionary` is also the name of the class of the Scripting Runtime; see [Using the package with the Scripting Runtime](.#using-the-package-with-the-scripting-runtime). A program that needs a dictionary can use that class. @@ -73,8 +75,10 @@ Adds a key and a value. Syntax: *dictionary*.**Add** ( *key*, *value* ) + *key* : *required* A value of type *TKey*. The key must not be there already. + *value* : *required* A value of type *TValue*. @@ -82,14 +86,18 @@ Syntax: *dictionary*.**Add** ( *key*, *value* ) ### Clear {: .no_toc } + Removes all keys and values. + Syntax: *dictionary*.**Clear** ### ContainsKey {: .no_toc } + Tests whether a key is there. + Syntax: *dictionary*.**ContainsKey** ( *key* ) **As Boolean** @@ -103,8 +111,10 @@ Returns the value of a key. Syntax: *dictionary*.**Item** ( *key* ) **As TValue** + *key* : *required* A value of type *TKey*. The key must be there. [**TryGetValue**](#trygetvalue) does not need that. + ### New {: .no_toc } @@ -125,7 +135,9 @@ Syntax: *dictionary*.**Remove** ( *key* ) **As Boolean** *key* : *required* A value of type *TKey*. + Returns **True** when the key was there and is removed, and **False** when it was not. The keys after it move down by one place. + ### SetItem {: .no_toc } @@ -140,7 +152,9 @@ Syntax: *dictionary*.**SetItem** ( *key*, *value* ) *value* : *required* A value of type *TValue*. + Replaces the value of a key that is there, and keeps its place. A key that is not there goes to the end. + ### TryGetValue {: .no_toc } @@ -155,7 +169,9 @@ Syntax: *dictionary*.**TryGetValue** ( *key*, *value* ) **As Boolean** *value* : *required* A variable of type *TValue*, passed **ByRef**. It receives the value when the key is there, and is not changed when it is not. + Returns **True** when the key was there. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_Diagnostic/FileHelper.md b/docs/Reference/Contributed/TBMANLIB_Diagnostic/FileHelper.md index 7a13c51c..ddb9e227 100644 --- a/docs/Reference/Contributed/TBMANLIB_Diagnostic/FileHelper.md +++ b/docs/Reference/Contributed/TBMANLIB_Diagnostic/FileHelper.md @@ -46,6 +46,7 @@ The files that **WriteFile** and **AppendFile** write are UTF-8 with a byte-orde A path that is not absolute is taken as relative to the folder of the program, **App.Path**, and not to the current folder. [**GetFullPath**](#getfullpath) makes the full path, and the functions that open, test, read or delete a file call it first. [**EnsureDirectoryExists**](#ensuredirectoryexists) and the functions that take a path apart ([**GetDirectoryName**](#getdirectoryname), [**GetFileName**](#getfilename), [**GetFileNameWithoutExtension**](#getfilenamewithoutextension), [**GetExtension**](#getextension), [**IsAbsolutePath**](#isabsolutepath) and [**NormalizePath**](#normalizepath)) use the text as it is given. + ```tb check_build check_run Debug.Print FileHelper.GetFullPath("x.txt") = App.Path & "\x.txt" Debug.Print FileHelper.GetFullPath("sub\..\x.txt") = App.Path & "\x.txt" @@ -55,6 +56,7 @@ Debug.Print FileHelper.GetFullPath("C:\a\..\b\x.txt") ' True ' C:\b\x.txt ``` + ## Methods @@ -71,7 +73,9 @@ Syntax: **FileHelper.AppendFile** ( *content*, *filePath* ) **As Variant** *filePath* : *required* A **String**, the path of the file, absolute or relative to **App.Path**. + Creates the file if it does not exist, and the folder that holds it when only that last folder is missing (see [**EnsureDirectoryExists**](#ensuredirectoryexists)). The file is written in UTF-8. The byte-order mark is written only when the file is empty or new. The content goes after the last byte of the file. + Returns **True**, or a **String** of the form `Error ` and the number, a colon, the description, and ` (File: ` and the path as the caller gave it, and `)`. The function raises no error. @@ -85,6 +89,7 @@ Syntax: **FileHelper.DeleteFile** ( *filePath* ) **As Boolean** *filePath* : *required* A **String**, the path of the file, absolute or relative to **App.Path**. + Calls **Kill** and returns **True**. Returns **False** for any error, a file that does not exist included. The path may hold the wildcards `*` and `?` that **Kill** accepts, and then every file that matches is deleted. ```tb check_build check_run @@ -102,6 +107,7 @@ RmDir folder ' False ' False ``` + ### DirectoryExists {: .no_toc } @@ -113,7 +119,9 @@ Syntax: **FileHelper.DirectoryExists** ( *dirPath* ) **As Boolean** *dirPath* : *required* A **String**, the path of the folder, absolute or relative to **App.Path**. + Returns **True** when **GetAttr** succeeds and reports a folder. A file, or a path that does not exist, gives **False**. + ### EnsureDirectoryExists {: .no_toc } @@ -125,7 +133,9 @@ Syntax: **FileHelper.EnsureDirectoryExists** ( *path* ) *path* : *required* A **String**, the path of the folder. + Removes one trailing backslash and calls **MkDir**. It ignores every error, so a folder that exists is not an error, and neither is a folder that cannot be created. **MkDir** creates the last level only. When the folder above it does not exist, nothing is created and no error is reported: [**WriteFile**](#writefile) then returns the error text of **Open**, which is `Error -2147467259: Unspecified error` and the path. VB6 raises error 76, *Path not found*, for the same **Open** statement. + ```tb check_build check_run Dim root As String = Environ$("TEMP") & "\diag-new" @@ -152,7 +162,9 @@ Syntax: **FileHelper.FileExists** ( *filePath* ) **As Boolean** *filePath* : *required* A **String**, the path of the file, absolute or relative to **App.Path**. + Returns **True** when **GetAttr** succeeds and the path is not a folder. A folder, or a path that does not exist, gives **False**. + ### GetDirectoryName {: .no_toc } @@ -212,7 +224,9 @@ Syntax: **FileHelper.GetFileSize** ( *filePath* ) **As LongLong** *filePath* : *required* A **String**, the path of the file, absolute or relative to **App.Path**. + Opens the file in binary mode and returns the result of **LOF**. Returns -1 when an error occurs, such as a folder that does not exist. The function opens the file with `For Binary`, and that mode creates a file that does not exist, so a missing file in a folder that exists is not an error: it is created with no content and the function returns 0. See [Known problems](.#known-problems-in-version-1001). + ```tb check_build check_run Dim path As String = Environ$("TEMP") & "\diag-missing.bin" @@ -267,6 +281,7 @@ Changes every slash to a backslash and every run of backslashes to one. It then Because an empty first part is dropped, a path from the root, such as `\a\b`, loses its first backslash, and so does a UNC path: `\\server\share\f.txt` becomes `server\share\f.txt`, which is a relative path. [**GetFullPath**](#getfullpath) treats such a path as absolute and does not add **App.Path**, so a file in a UNC path is opened as the relative path `server\share\f.txt`. See [Known problems](.#known-problems-in-version-1001). + ```tb check_build check_run Debug.Print FileHelper.NormalizePath("C:/a//b/./c/../d.txt") Debug.Print FileHelper.GetDirectoryName("C:\a\b.txt") @@ -288,6 +303,7 @@ Debug.Print FileHelper.GetFullPath("\\server\share\f.txt") ' server\share\f.txt ' server\share\f.txt ``` + ### ReadFile {: .no_toc } @@ -299,7 +315,9 @@ Syntax: **FileHelper.ReadFile** ( *filePath* ) **As Variant** *filePath* : *required* A **String**, the path of the file, absolute or relative to **App.Path**. + Returns the content of the file as a **String**, and an empty string for an empty file. When an error occurs, such as a folder that does not exist, it returns an error text in the form that [**AppendFile**](#appendfile) describes. A file that does not exist is not an error: it is created with no content, as for [**GetFileSize**](#getfilesize), and the function returns an empty string, so a missing file and an empty file look the same. See [Known problems](.#known-problems-in-version-1001). + The function reads the bytes of the file into a string as characters, and does not decode UTF-8 or remove a byte-order mark. A file that [**WriteFile**](#writefile) wrote therefore starts with the three characters of its byte-order mark, and a character above 127 is several characters. @@ -338,7 +356,9 @@ Syntax: **FileHelper.WriteFile** ( *content*, *filePath* ) **As Variant** *filePath* : *required* A **String**, the path of the file, absolute or relative to **App.Path**. + Creates the file, and the folder that holds it when only that last folder is missing (see [**EnsureDirectoryExists**](#ensuredirectoryexists)). It writes the byte-order mark and then the text in UTF-8, from the start of the file. Returns **True**, or an error text as [**AppendFile**](#appendfile) describes. + **WriteFile** opens the file in binary mode and does not shorten it. When the file exists and is longer than the new content, the bytes after the new content stay in the file. See [Known problems](.#known-problems-in-version-1001). @@ -362,7 +382,9 @@ FileHelper.DeleteFile path ' 3 ``` + An empty string is not an error: the file holds the three bytes of the byte-order mark. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_Diagnostic/FormatHelper.md b/docs/Reference/Contributed/TBMANLIB_Diagnostic/FormatHelper.md index 3ba266d4..ee9c8d9b 100644 --- a/docs/Reference/Contributed/TBMANLIB_Diagnostic/FormatHelper.md +++ b/docs/Reference/Contributed/TBMANLIB_Diagnostic/FormatHelper.md @@ -56,7 +56,9 @@ Syntax: **FormatHelper.EscapeHtml** ( *text* ) **As String** *text* : *required* A **String**. + Replaces `&` with `&`, `<` with `<`, `>` with `>`, `"` with `"` and `'` with `'`, and returns the result. The ampersand is replaced first, so the entities that the other replacements add stay as they are. + ```tb check_build check_run Debug.Print FormatHelper.EscapeHtml("&") @@ -74,7 +76,9 @@ Syntax: **FormatHelper.EscapeJson** ( *text* ) **As String** *text* : *required* A **String**. + Replaces the backslash with `\\`, the double quote with `\"`, a carriage return with `\r`, a line feed with `\n` and a tab with `\t`. The result is meant to go between double quotes. No other character is changed: the control characters with the codes 1 to 31 other than these three, which JSON requires to be escaped, are returned as they are. + ```tb check_build check_run Debug.Print FormatHelper.EscapeJson("a\b" & vbTab & """q""") @@ -97,7 +101,9 @@ Syntax: **FormatHelper.FormatBytes** ( *bytes* [ , *decimals* ] ) **As String** *decimals* : *optional* A **Long**, the number of digits after the decimal separator. Default: 1. + The units are 1024 times larger one after the other, and are written `KB`, `MB`, `GB` and `TB`. The function picks the largest unit that is at most the size, divides, and writes the number with *decimals* digits, a space and the unit: `1.5 KB`. A size below 1024 is written in whole bytes as `512 B`, whatever *decimals* is. A size of 1024 TB or more is shown in TB. The number is made with the **Format** function and a pattern of zeros, so the digits are rounded. A negative size is written in bytes. + With *decimals* set to 0 the pattern is `0.`, and the decimal separator stays in the text: `4. KB` and not `4 KB`. The **Page Size** item of [**CPUInfoCollector**](Collectors#cpuinfocollector) is written this way. See [Known problems](.#known-problems-in-version-1001). @@ -139,6 +145,7 @@ Syntax: **FormatHelper.FormatDateTimeISO** ( *dt* ) **As String** Returns the form `yyyy-mm-ddThh:nn:ss`, such as `2026-01-04T15:30:00`. It has no time zone and no fraction of a second. + ```tb check_build check_run Dim d As Date = DateSerial(2026, 1, 4) + TimeSerial(15, 30, 45) Debug.Print FormatHelper.FormatDateTime(d) @@ -149,6 +156,7 @@ Debug.Print FormatHelper.FormatTime(d) ' 2026-01-04T15:30:45 ' 15:30:45 ``` + ### FormatPercent {: .no_toc } @@ -166,7 +174,9 @@ Syntax: **FormatHelper.FormatPercent** ( *value*, *total* [ , *decimals* ] ) **A *decimals* : *optional* A **Long**, the number of digits after the decimal separator. Default: 0. + Returns *value* divided by *total*, times 100, and `%`. With *decimals* set to 0 the number is made with **CLng**, which rounds a .5 to the even number. When *total* is 0 the function returns `0%`, also when *decimals* is above 0. + ### FormatPercentValue {: .no_toc } @@ -181,7 +191,9 @@ Syntax: **FormatHelper.FormatPercentValue** ( *percent* [ , *decimals* ] ) **As *decimals* : *optional* A **Long**, the number of digits after the decimal separator. Default: 0. + Returns *percent* and `%`, rounded in the same way as [**FormatPercent**](#formatpercent). + ```tb check_build check_run Debug.Print FormatHelper.FormatPercent(1, 0, 1) @@ -217,7 +229,9 @@ Syntax: **FormatHelper.FormatUptime** ( *milliseconds* ) **As String** *milliseconds* : *required* A **LongLong**, the duration. + Returns the duration as `3d 12h 45m 32s`. The days are left out while they are 0, the hours while they and the days are 0, and the minutes while all three are 0. The seconds are always there. Parts of a second are dropped. + ```tb check_build check_run Debug.Print FormatHelper.FormatUptime(0) @@ -274,7 +288,9 @@ Syntax: **FormatHelper.Truncate** ( *text*, *maxLength* [ , *ellipsis* ] ) **As *ellipsis* : *optional* A **String** that marks the cut. Default: `...`. + Text of *maxLength* characters or fewer is returned as it is. A longer text is cut so that, with *ellipsis* at the end, it is *maxLength* characters long. When *maxLength* is not above the length of *ellipsis*, the result is the first *maxLength* characters of *ellipsis* alone. + ```tb check_build check_run Debug.Print FormatHelper.PadRight("42", 5, ".") diff --git a/docs/Reference/Contributed/TBMANLIB_Diagnostic/FormatterFactory.md b/docs/Reference/Contributed/TBMANLIB_Diagnostic/FormatterFactory.md index 779eebc1..ce41d092 100644 --- a/docs/Reference/Contributed/TBMANLIB_Diagnostic/FormatterFactory.md +++ b/docs/Reference/Contributed/TBMANLIB_Diagnostic/FormatterFactory.md @@ -41,7 +41,9 @@ Syntax: **FormatterFactory.CreateFormatter** ( *formatName* ) **As** [**IOutputF *formatName* : *required* A **String**. The letter case does not matter. `text` and `txt` give a [**TextFormatter**](Formatters#textformatter), `json` a [**JsonFormatter**](Formatters#jsonformatter), and `html` and `htm` an [**HtmlFormatter**](Formatters#htmlformatter). + Returns a new formatter each time. Any other name, including an empty string, gives a **TextFormatter** without an error. + ### CreateFormatterFromArg {: .no_toc } @@ -53,7 +55,9 @@ Syntax: **FormatterFactory.CreateFormatterFromArg** ( *arg* ) **As** [**IOutputF *arg* : *required* A **String** such as `/json` or `-html`. The function converts it to lower case, removes one leading slash or hyphen, and passes the rest to [**CreateFormatter**](#createformatter). + The arguments `json` and `/JSON` give a **JsonFormatter**. The argument `--json` has two hyphens and only one is removed, so the function returns a **TextFormatter** for it. + ```tb check_build check_run Debug.Print FormatterFactory.CreateFormatterFromArg("/JSON").FormatterName @@ -113,7 +117,9 @@ Returns the names of the formats. Syntax: **FormatterFactory.GetSupportedFormats** **As String** + Returns the fixed text `text, json, html`. The short names `txt` and `htm` that [**CreateFormatter**](#createformatter) accepts are not in it. + ## Delegates diff --git a/docs/Reference/Contributed/TBMANLIB_Diagnostic/Formatters.md b/docs/Reference/Contributed/TBMANLIB_Diagnostic/Formatters.md index 2aab64f4..fc35694d 100644 --- a/docs/Reference/Contributed/TBMANLIB_Diagnostic/Formatters.md +++ b/docs/Reference/Contributed/TBMANLIB_Diagnostic/Formatters.md @@ -18,12 +18,14 @@ The package has three formatters. Each one turns a list of diagnostic results in The formatters have these properties in common: + - They only build a string. They do not write a file or a screen. [**FileHelper**](FileHelper) writes the string to a file. - The line breaks are **vbCrLf**. - One object can make any number of reports, because **BeginFormat** discards what the last report left in it. - Each report names the tool as `Twinbasic Diagnostic Tool v1.0`, or, in JSON, as `"toolVersion": "1.0"`. The text is fixed in the code, and is not the version of the package. - The JSON and HTML reports give the time at which **BeginFormat** was called. The text report does not. - The classes declare the members of the interface as **Public**, so a variable of the class type reaches them by the long names, such as `IOutputFormatter_FormatItem`. + ```tb check_build check_run Dim f As IOutputFormatter @@ -71,12 +73,14 @@ Path = C:\Tools "x" Memory Load = 54% ``` + - The first line is the title, `# Twinbasic Diagnostic Tool v1.0`, and an empty line follows it. - Each group is a line with its name in square brackets, and then a line for each item: the key, a space, an equals sign, a space and the value. - A group that has items is followed by an empty line. A group with no items has no empty line after it, and its header line is followed at once by the next one. - The report has no time and no closing line. - [**FormatResults**](FormatterFactory#formatresults) passes a failed result as one item with the key `Error`, so its group has the line `Error = ` and the message. - Nothing is escaped. A key or a value that holds ` = ` or a line break is written as it is, and then a reader cannot always split the text back into keys and values. + ```tb check_build check_run Dim f As IOutputFormatter @@ -113,6 +117,7 @@ Next i [**FormatterFactory.CreateFormatter**](FormatterFactory#createformatter) gives a **TextFormatter** for `text`, for `txt` and for every name it does not know. A list of results is formatted with [**FormatResults**](FormatterFactory#formatresults): + ```tb check_build check_run Dim results As New List(Of DiagnosticResult(Of KeyValuePair(Of String, String))) Dim r As DiagnosticResult(Of KeyValuePair(Of String, String)) @@ -131,6 +136,7 @@ Debug.Print Replace(text, vbCrLf, "|") ' Output: ' # Twinbasic Diagnostic Tool v1.0||[Uptime]|Total Days = 3||[Memory]|Error = Failed to get memory status|| ``` + ## JsonFormatter {: .no_toc } @@ -152,10 +158,12 @@ Writes a report as one JSON object. **FormatterName** is `JSON` and **FileExtens } ``` + - `generatedAt` is the local time at which **BeginFormat** was called, in the form `yyyy-mm-ddThh:nn:ss`, without a time zone. - Each group is a member of `categories`, and each item is a member of its group. The name of the group and the key of the item are changed into camel case, as described below, and **every value is a string**, the numbers too: `"processorCount": "8"`. - A failed result has one member, `error`, with the message. - A group with no items is an empty object. + The camel-case rule changes the first character to lower case. The characters space, underscore, hyphen and colon are removed, and the character after each of them is changed to upper case. All the other characters stay as they are. The rule does not look at the rest of a word, so a name in capital letters keeps them: @@ -202,7 +210,9 @@ The document declares ``, the language `en` and the character set The style sheet is in the document. It sets a light and a dark colour scheme, which the browser selects with `prefers-color-scheme`, a layout that is at most 900 pixels wide, and a print style. The document has no script and loads nothing from another address. + The category name, the keys and the values are escaped with [**EscapeHtml**](FormatHelper#escapehtml), which replaces `&`, `<`, `>`, `"` and `'`. A file that is saved from the report should be in UTF-8, which is the character set that the document declares. + ```tb check_build check_run Dim f As IOutputFormatter diff --git a/docs/Reference/Contributed/TBMANLIB_Diagnostic/List.md b/docs/Reference/Contributed/TBMANLIB_Diagnostic/List.md index a178cb76..4e29d464 100644 --- a/docs/Reference/Contributed/TBMANLIB_Diagnostic/List.md +++ b/docs/Reference/Contributed/TBMANLIB_Diagnostic/List.md @@ -135,6 +135,10 @@ Removes all items. Syntax: *list*.**Clear** + +Count and Capacity are 0 afterwards. + + ### Contains {: .no_toc } @@ -145,7 +149,9 @@ Syntax: *list*.**Contains** ( *item* ) **As Boolean** *item* : *required* A value of type *T*. + Returns **True** when an item is equal to *item*. The items are compared with the `=` operator, so the comparison is by value for text and numbers. For objects the operator compares what an object offers by default, and a class that has no default member makes it raise run-time error 438, *Object doesn't support this property or method*, as soon as the list holds an item to compare. **IndexOf** and **Remove**, which calls **IndexOf**, raise the same error. **Item**, **Add** and **RemoveAt** work with objects. + ```tb check_build slot=file projname=diag-list Class Person @@ -186,7 +192,9 @@ Syntax: *list*.**IndexOf** ( *item* ) **As Long** *item* : *required* A value of type *T*. + Returns the index of the first item that is equal to *item*, compared as in [**Contains**](#contains), or -1 when there is none. + ### Insert {: .no_toc } @@ -195,6 +203,7 @@ Inserts an item at a position. Syntax: *list*.**Insert** ( *index*, *item* ) + *index* : *required* A **Long**, the position of the new item, from 0 to [**Count**](#count). With **Count** the item goes at the end. @@ -202,6 +211,7 @@ Syntax: *list*.**Insert** ( *index*, *item* ) : *required* A value of type *T*. Raises error 9 with the source `List(Of T).Insert` and the text `Index out of range: ` and the index, when *index* is below 0 or above **Count**. See the start of the page for the position of the new item and for the empty list. + ### Item {: .no_toc } @@ -213,7 +223,9 @@ Syntax: *list*.**Item** ( *index* ) **As T** *index* : *required* A **Long**, from 0 to [**Count**](#count) - 1. + Raises error 9 with the source `List(Of T).Item` and the text `Index out of range: ` and the index, when *index* is outside the list. + ### New {: .no_toc } @@ -235,7 +247,9 @@ Syntax: *list*.**Remove** ( *item* ) **As Boolean** *item* : *required* A value of type *T*. + Returns **True** when an item was found, with [**IndexOf**](#indexof), and removed. Returns **False** when none is equal to *item*. + ### RemoveAt {: .no_toc } @@ -247,7 +261,9 @@ Syntax: *list*.**RemoveAt** ( *index* ) *index* : *required* A **Long**, from 0 to [**Count**](#count) - 1. + The items after it move down by one place. Raises error 9 with the source `List(Of T).RemoveAt` and the text `Index out of range: ` and the index, when *index* is outside the list. + ### SetItem {: .no_toc } @@ -262,7 +278,9 @@ Syntax: *list*.**SetItem** ( *index*, *item* ) *item* : *required* A value of type *T*. + Removes the item at *index* and adds *item* in its place. Raises error 9 with the source `List(Of T).SetItem` and the text `Index out of range: ` and the index, when *index* is outside the list. See the start of the page for the case of index 0. + ### ToArray {: .no_toc } @@ -271,7 +289,9 @@ Returns the items as an array. Syntax: *list*.**ToArray** **As Variant()** + Returns a **Variant** array whose first element has the index 0. For an empty list it returns an array that has no bounds, and **UBound** and **LBound** raise error 9 for it. + ```tb check_build check_run Dim names As New List(Of String) diff --git a/docs/Reference/Contributed/TBMANLIB_Diagnostic/index.md b/docs/Reference/Contributed/TBMANLIB_Diagnostic/index.md index ba316a8b..b7e7794f 100644 --- a/docs/Reference/Contributed/TBMANLIB_Diagnostic/index.md +++ b/docs/Reference/Contributed/TBMANLIB_Diagnostic/index.md @@ -69,6 +69,7 @@ A result can also be read directly, without a formatter. Each [item](KeyValuePai ## What a report contains {: #what-a-report-contains } + > [!NOTE] timeless > A full report names the user and the computer, the folders in the environment variables such as `USERPROFILE` and `PATH`, every running program, the drives, and the names and IPv4 addresses of the network adapters. Read a report before it goes to someone else. @@ -126,6 +127,7 @@ The package has four modules whose contents a program has no need for: ## Known problems in version 1.0.0.1 + > [!WARNING] 1.0.0.1 > The package gives a wrong result without an error, or loses data, in these cases. > @@ -136,9 +138,13 @@ The package has four modules whose contents a program has no need for: > - **[NormalizePath](FileHelper#normalizepath) and [GetFullPath](FileHelper#getfullpath) turn a UNC path into a relative one.** `\\server\share\f.txt` becomes `server\share\f.txt`, and the file functions then open a file of that relative name. > - **[EscapeJson](FormatHelper#escapejson) leaves the control characters 1 to 31, other than line feed, carriage return and tab, unescaped**, so [**JsonFormatter**](Formatters#jsonformatter) can write JSON that a parser rejects. > - **[FormatBytes](FormatHelper#formatbytes) with no decimals leaves the decimal separator in the text**, as in `4. KB`. The `Page Size` item of [**CPUInfoCollector**](Collectors#cpuinfocollector) is written this way. -> - **[LocaleCollector](Collectors#localecollector) reports the offset of standard time as `Timezone` all year.** In summer time the item is behind the clock by the daylight saving offset: `UTC+1` where the clock is at UTC+2, as in Central European Summer Time. > - **[NetworkInfoCollector](Collectors#networkinfocollector) lists the first adapter only in a 64-bit program.** It reads the adapters in steps of 640 bytes, and the records are 704 bytes apart in a 64-bit program. The summary may then have no `Primary IP`. A 32-bit program lists every adapter. + +> [!WARNING] 1.0.0.1 +> **[LocaleCollector](Collectors#localecollector) reports the offset of standard time as `Timezone` all year.** In summer time the item is behind the clock by the daylight saving offset: `UTC+1` where the clock is at UTC+2, as in Central European Summer Time. + + > [!NOTE] 1.0.0.1 > The package has these other problems. Each raises an error. > @@ -147,7 +153,9 @@ The package has four modules whose contents a program has no need for: > - **[List.Insert](List#insert) with the index 0 raises error 5 on an empty list.** > - **[WriteFile](FileHelper#writefile) and [AppendFile](FileHelper#appendfile) make only the last folder of a path.** For a path whose parent folder is missing they return the error text `Error -2147467259: Unspecified error` and the path. The **Open** statement raises error 76, *Path not found*, for it in VB6. + Two behaviours follow from the code and can look like defects. [**CPULoadCollector**](Collectors#cpuloadcollector) reports 0 for its first reading, because it measures between two calls. [**JsonFormatter**](Formatters#jsonformatter) writes every value as a string and turns `PATH` into `pATH`. + ## Pages @@ -182,3 +190,7 @@ Two behaviours follow from the code and can look like defects. [**CPULoadCollect - [TBMANLIB](../TBMANLIB/) package -- the toolkit of the same author - [VBA_JSON](../VBA_JSON/) package -- reads JSON, such as the report that **JsonFormatter** writes - [Importing a package from TWINSERV](../../../../Features/Packages/Importing-TWINSERV) -- adding a published package to a project + +## Test cases + +The notes and warnings of this section are checked by the test cases in [TBMANLIB_Diagnostic-tests.twinproj](TBMANLIB_Diagnostic-tests.twinproj), a twinBASIC project. It uses TBMANLIB_Diagnostic as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/AddFilter.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/AddFilter.md index f2783770..64671a5c 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/AddFilter.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/AddFilter.md @@ -23,11 +23,13 @@ Syntax: *object*.**AddFilter** ( *strDescription*, *strExtension* ) ## Remarks + **AddFilter** appends `description|pattern` to the text of **Filter**, with a `|` in front of it when **Filter** is not empty. The first pair added is the one that the dialog selects when it opens. A filter with one pair gives a file-type list with one item. Despite its name, *strExtension* is a pattern, and the class uses the text as it is: it adds no `*.` in front. Write the pattern in the form that the Windows documentation of `OPENFILENAME` gives, such as `*.txt`. The class does not check the arguments. A `|` in either one adds an element, so the later elements are no longer paired correctly. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ClearFilter.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ClearFilter.md index 72858c90..ff101322 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ClearFilter.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ClearFilter.md @@ -17,7 +17,9 @@ Syntax: *object*.**ClearFilter** ## Remarks + **ClearFilter** sets **Filter** to an empty string, as `dlg.Filter = ""` does. The four `Set...Filters` methods call it first, so each of them replaces the filter. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/CreatePrompt.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/CreatePrompt.md index 21c981af..a522392b 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/CreatePrompt.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/CreatePrompt.md @@ -20,11 +20,13 @@ Syntax: *object*.**CreatePrompt** [ = *boolean* ] ## Remarks + Only [**ShowOpen**](ShowOpen) uses the property. [**ShowSave**](ShowSave) never sets `OFN_CREATEPROMPT`, and [**ShowBrowseForFolder**](ShowBrowseForFolder) ignores the property. When the user types the name of a file that does not exist, such as `nosuch.txt`, the box *This file doesn't exist. Create the file?* appears, with the buttons *Yes* and *No*. *Yes* closes the dialog and returns the path. *No* leaves the dialog open. This happens whether [**FileMustExist**](FileMustExist) is **True** or **False**, so the question takes the place of the *File not found* box that **FileMustExist** shows. The file is not created: neither the dialog nor the class creates it, and the program has to do so. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/DefaultExt.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/DefaultExt.md index c79e4012..6d20f939 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/DefaultExt.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/DefaultExt.md @@ -20,6 +20,7 @@ Syntax: *object*.**DefaultExt** [ = *string* ] ## Remarks + [**ShowOpen**](ShowOpen) and [**ShowSave**](ShowSave) pass the text as the `lpstrDefExt` member of `OPENFILENAME`. [**ShowBrowseForFolder**](ShowBrowseForFolder) ignores it. Windows appends the extension to a name that the user types without one. Write the extension without a period. The class does not change the text, and does not take an extension from the selected pair of [**Filter**](Filter). @@ -33,6 +34,7 @@ In the Save As dialog, with the user typing `out`: | An empty string | `\out` | `` is the folder of the dialog. A period in **DefaultExt** is kept, and gives a name with two periods. An empty **DefaultExt** appends nothing. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/DialogTitle.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/DialogTitle.md index 17b8608f..23c2a470 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/DialogTitle.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/DialogTitle.md @@ -20,9 +20,11 @@ Syntax: *object*.**DialogTitle** [ = *string* ] ## Remarks + [**ShowOpen**](ShowOpen) and [**ShowSave**](ShowSave) pass the text as the `lpstrTitle` member of `OPENFILENAME`, the text of the title bar. With an empty **DialogTitle** the Open dialog is titled *Open* and the Save As dialog *Save As*. A text that is set replaces the title. [**ShowBrowseForFolder**](ShowBrowseForFolder) passes the text as the `lpszTitle` member of `BROWSEINFO`. The title bar of that dialog always reads *Browse For Folder* (*Browse for Folder* when [**NewDialogStyle**](NewDialogStyle) is **False**), and **DialogTitle** is shown as a line of text above the tree of folders, as an instruction to the user. With an empty **DialogTitle** there is no text there. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/FileMustExist.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/FileMustExist.md index 5ccf31f3..2d8d504c 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/FileMustExist.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/FileMustExist.md @@ -20,11 +20,13 @@ Syntax: *object*.**FileMustExist** [ = *boolean* ] ## Remarks + Only [**ShowOpen**](ShowOpen) uses the property. [**ShowSave**](ShowSave) never sets `OFN_FILEMUSTEXIST`, and Windows documents the flag for Open dialogs only, so **FileMustExist** has no effect on a Save As dialog. [**ShowBrowseForFolder**](ShowBrowseForFolder) ignores it too. With **True**, a name that the user types and that does not exist, such as `nosuch.txt`, makes Windows show the box *File not found. Check the file name and try again.*, and the Open dialog stays open. Windows documents that the flag also applies `OFN_PATHMUSTEXIST`. With **False**, **ShowOpen** returns the path of a file that does not exist, and does not create the file, so the caller has to be ready to meet a missing file. With [**CreatePrompt**](CreatePrompt) **True**, the question of that property is asked in place of the box, whether **FileMustExist** is **True** or **False**. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/FileName.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/FileName.md index ac270089..b1eb02a6 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/FileName.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/FileName.md @@ -20,6 +20,7 @@ Syntax: *object*.**FileName** [ = *string* ] ## Remarks + **Before a dialog.** [**ShowOpen**](ShowOpen) and [**ShowSave**](ShowSave) copy the text into the buffer that Windows uses for the `lpstrFile` member of `OPENFILENAME`, which initialises the File name box. The Save As dialog shows an empty box for an empty **FileName** and `x.txt` for `x.txt`. [**ShowSave**](ShowSave) pads the buffer with spaces, and the dialog does not show the padding. **After a dialog.** A **ShowSave** or **ShowOpen** that the user confirms stores the path it returns in **FileName**. For a [**MultiSelect**](MultiSelect) selection of several files, which **ShowOpen** returns as an array, **FileName** becomes an empty string. A cancelled dialog leaves **FileName** as it was, and [**ShowBrowseForFolder**](ShowBrowseForFolder) neither reads nor changes it. @@ -28,6 +29,7 @@ Syntax: *object*.**FileName** [ = *string* ] > Because a confirmed dialog stores the full path, the next **ShowOpen** or **ShowSave** passes that path as the initial file name. Set **FileName** to an empty string, or call [**Reset**](Reset), to start without it. **Length.** The buffer of **ShowSave** holds 260 characters. A **FileName** of up to 259 characters opens the dialog normally. A **FileName** of exactly 260 characters shows no dialog and returns an empty string at once, without an error, as a cancel does. A **FileName** of 261 characters or more raises run-time error 5, *Invalid procedure call or argument*, before any dialog. The buffer of **ShowOpen** holds 260 characters, or 32,768 when **FileName** is not empty or [**MultiSelect**](MultiSelect) is **True**. A **FileName** of 32,768 characters or more is not used by **ShowOpen**. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/Filter.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/Filter.md index ff71ac9c..587d7a06 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/Filter.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/Filter.md @@ -20,11 +20,13 @@ Syntax: *object*.**Filter** [ = *string* ] ## Remarks + The syntax is that of the **Filter** property of VB6's **CommonDialog** control. A pattern is a file name pattern with wildcards, such as `*.txt`. Several patterns of one pair are separated by semicolons: `*.jpg;*.jpeg`. [**ShowOpen**](ShowOpen) and [**ShowSave**](ShowSave) replace every `|` with a null character and add one more null character at the end, which is the layout of the `lpstrFilter` member of `OPENFILENAME`. The file-type list holds the descriptions in the order of the pairs, and the first pair is selected when the dialog opens (`nFilterIndex` is 1). The class does not report the pair that the user chooses. [**ShowBrowseForFolder**](ShowBrowseForFolder) ignores the property. The class does not check the string. A `|` inside a description or a pattern starts a new element, and moves every later element one place, so the pairs no longer fit. [**AddFilter**](AddFilter) and the four `Set...Filters` methods build the string in pairs, unless an argument of **AddFilter** contains a `|`. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileExtension.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileExtension.md index 5837fc34..1612789d 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileExtension.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileExtension.md @@ -20,8 +20,11 @@ Syntax: *object*.**GetFileExtension** ( *strFullPath* ) **As String** ## Remarks + **GetFileExtension** returns the extension with its period: `.txt` for `C:\Docs\summary.txt`. A string with no period gives an empty string. Of `archive.tar.gz` only `.gz` is returned, and a name that ends with a period gives `.`. + + > [!NOTE] 1.1.0.0 > The method searches the whole string for the last period, not the file name only. A period in a folder name is taken for the start of an extension when the file name has none: `C:\my.dir\notes` gives `.dir\notes`. [**GetFileNameWithoutExt**](GetFileNameWithoutExt) looks in the file name only, and gives `notes` for the same path. diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileName.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileName.md index ff6e8fcf..12c10016 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileName.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileName.md @@ -20,6 +20,7 @@ Syntax: *object*.**GetFileName** ( *strFullPath* ) **As String** ## Remarks + **GetFileName** returns the file name of a path, with its extension: the name in `C:\Docs\summary.txt` is `summary.txt`. - Only the backslash separates folders. A forward slash is an ordinary character, so a path written with forward slashes is returned unchanged. @@ -27,6 +28,7 @@ Syntax: *object*.**GetFileName** ( *strFullPath* ) **As String** - A string that ends with a backslash, such as `C:\Docs\`, gives an empty string. The method works on text only. It does not check that the file exists, and it reads no property of the object. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileNameWithoutExt.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileNameWithoutExt.md index 9658f32a..0b5978d9 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileNameWithoutExt.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFileNameWithoutExt.md @@ -20,6 +20,7 @@ Syntax: *object*.**GetFileNameWithoutExt** ( *strFullPath* ) **As String** ## Remarks + **GetFileNameWithoutExt** takes the part of the string after the last backslash, as [**GetFileName**](GetFileName) does, and removes everything from the last period of that part. `C:\Docs\summary.txt` gives `summary`. The period is searched in the file name only, so a period in a folder name has no effect: `C:\my.dir\notes` gives `notes`. - Only the last extension is removed: `archive.tar.gz` gives `archive.tar`. @@ -28,6 +29,7 @@ Syntax: *object*.**GetFileNameWithoutExt** ( *strFullPath* ) **As String** - A name that starts with a period and has no other gives an empty string: `.gitignore` gives an empty string. The method works on text only. It does not check that the file exists, and it reads no property of the object. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFilePath.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFilePath.md index 481ba53e..056907c4 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFilePath.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/GetFilePath.md @@ -20,6 +20,7 @@ Syntax: *object*.**GetFilePath** ( *strFullPath* ) **As String** ## Remarks + **GetFilePath** returns the folder of a file path, without a trailing backslash: the folder of `C:\Docs\summary.txt` is `C:\Docs`, and that of a file in the root of a drive is `C:`, without the backslash. - Only the backslash separates folders. A forward slash is an ordinary character, so a path written with forward slashes is returned unchanged. @@ -27,6 +28,7 @@ Syntax: *object*.**GetFilePath** ( *strFullPath* ) **As String** - A string that ends with a backslash loses it: `C:\Docs\` gives `C:\Docs`. A string that starts with its only backslash gives an empty string. The method works on text only. It does not check that the folder exists, and it reads no property of the object. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/InitialDir.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/InitialDir.md index 86cca290..37e2ced6 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/InitialDir.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/InitialDir.md @@ -24,6 +24,7 @@ Syntax: *object*.**InitialDir** [ = *string* ] [**ShowBrowseForFolder**](ShowBrowseForFolder) does not use **InitialDir**. Its dialog opens with the folder of the user's profile selected, whatever **InitialDir** holds. + > [!NOTE] 1.1.0.0 > A **ShowBrowseForFolder** that the user confirms replaces **InitialDir** with the folder it returns, a path typed into the edit box of the dialog included. A **ShowOpen** or **ShowSave** after it is given that folder as its starting folder. Set **InitialDir** again if that is not wanted. diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/MultiSelect.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/MultiSelect.md index 4b001e25..cc58d144 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/MultiSelect.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/MultiSelect.md @@ -20,6 +20,7 @@ Syntax: *object*.**MultiSelect** [ = *boolean* ] ## Remarks + Only [**ShowOpen**](ShowOpen) uses the property. [**ShowSave**](ShowSave) and [**ShowBrowseForFolder**](ShowBrowseForFolder) ignore it. With **MultiSelect** **True**, **ShowOpen** gives Windows a buffer of 32,768 characters, in place of 260. The class also always sets `OFN_EXPLORER`, so Windows fills the buffer with the folder, then the name of each selected file, each ended by a null character, and one more null character at the end. When the user selects one file, the buffer holds its full path, with no separator. @@ -27,6 +28,7 @@ With **MultiSelect** **True**, **ShowOpen** gives Windows a buffer of 32,768 cha When the user selects one file, **ShowOpen** returns its full path as a **String**. When the user selects two or more, it returns an array of **String** values, one full path for each file, in the order in which the names stand in the File name box. The array is 0-based. [**FileName**](FileName) is an empty string after a selection of several files. A program can tell the two results apart with **IsArray**, and [**SelectFiles**](SelectFiles) hides the difference by returning a **Collection** in both cases. [**SelectAndOpenFile**](SelectAndOpenFile) opens each file of an array. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/NewDialogStyle.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/NewDialogStyle.md index bd0bef88..0b311096 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/NewDialogStyle.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/NewDialogStyle.md @@ -20,6 +20,7 @@ Syntax: *object*.**NewDialogStyle** [ = *boolean* ] ## Remarks + Only **ShowBrowseForFolder** uses the property. [**ShowOpen**](ShowOpen) and [**ShowSave**](ShowSave) ignore it. `BIF_USENEWUI` is the constant `BIF_NEWDIALOGSTYLE Or BIF_EDITBOX`. With **True**, the dialog is titled *Browse For Folder*, can be resized, and has an edit box, in which the user can type the path of a folder, and a *Make New Folder* button. It opens with the folder of the user's profile selected, whatever [**InitialDir**](InitialDir) holds. A path typed into the edit box and confirmed with *OK* is returned, and is stored in **InitialDir**. @@ -27,6 +28,7 @@ Only **ShowBrowseForFolder** uses the property. [**ShowOpen**](ShowOpen) and [** With **False**, **ShowBrowseForFolder** sets only `BIF_RETURNONLYFSDIRS`, which it always sets, and the dialog has the old style: it is titled *Browse for Folder*, has no edit box and no *Make New Folder* button, selects *This PC* when it opens, and keeps *OK* disabled until the user selects a folder of the file system. The Windows documentation of `SHBrowseForFolder` requires COM to be initialised before the call, with apartment threading if the program calls `CoInitializeEx`, and says that the call fails when `BIF_NEWDIALOGSTYLE` is passed and COM is initialised as multithreaded. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/NoChangeDir.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/NoChangeDir.md index 6de31947..382bfe00 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/NoChangeDir.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/NoChangeDir.md @@ -20,9 +20,11 @@ Syntax: *object*.**NoChangeDir** [ = *boolean* ] ## Remarks + [**ShowOpen**](ShowOpen) and [**ShowSave**](ShowSave) use the property. [**ShowBrowseForFolder**](ShowBrowseForFolder) ignores it. The current folder is the one that **CurDir** returns, and that relative paths are resolved against. With **False**, the current folder is the folder of the chosen file after **ShowOpen** or **ShowSave**. With **True** it stays as it was. The flag works for both dialogs. The Windows documentation of `OFN_NOCHANGEDIR` calls it ineffective for `GetOpenFileName`, but it is effective for **ShowOpen**. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/OpenFile.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/OpenFile.md index 625b3cf0..352c1b3f 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/OpenFile.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/OpenFile.md @@ -37,10 +37,13 @@ The Windows documentation of `ShellExecute` lists the other values. ## Remarks + **OpenFile** calls `ShellExecuteW`, the Unicode form of `ShellExecute`, so a path with any character is passed intact. It passes no owner window, no parameters and no working folder, and shows the program's window normally (`SW_SHOWNORMAL`). The method raises no error: the return value is the only report. A path that does not exist returns 2 and shows no message. + For a program, the verb `open` starts the program. For a document, it starts the program that Windows associates with the document. For a folder, it opens the folder in File Explorer. + > [!NOTE] 1.1.0.0 > An empty *strFilePath* is not an error. `OpenFile("")` returns 42, which is above 32 and looks like success, and opens a File Explorer window on the current folder of the program. diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/OverwritePrompt.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/OverwritePrompt.md index 8e18dfcf..858989b7 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/OverwritePrompt.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/OverwritePrompt.md @@ -20,11 +20,13 @@ Syntax: *object*.**OverwritePrompt** [ = *boolean* ] ## Remarks + [**ShowSave**](ShowSave) uses the property. With **True** and a file that exists, the box *Confirm Save As* asks *name already exists. Do you want to replace it?*, with the buttons *Yes* and *No*. *Yes* returns the path. *No* leaves the Save As dialog open. With **False** there is no box, and the dialog returns the path. The dialog only returns the name. The class does not create, replace or check a file. [**ShowOpen**](ShowOpen) passes the flag too, and the Windows documentation describes `OFN_OVERWRITEPROMPT` for Save As only. [**ShowBrowseForFolder**](ShowBrowseForFolder) ignores the property. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/PathCombine.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/PathCombine.md index 3f0c93ae..f1415181 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/PathCombine.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/PathCombine.md @@ -23,6 +23,7 @@ Syntax: *object*.**PathCombine** ( *strPath1*, *strPath2* ) **As String** ## Remarks + **PathCombine** returns *strPath1* and *strPath2* with exactly one backslash between them, so it does not matter whether *strPath1* ends with a backslash or *strPath2* starts with one. The method follows these steps, in order: 1. An empty *strPath1* returns *strPath2* as it is, and an empty *strPath2* returns *strPath1* as it is. Two empty strings give an empty string. @@ -42,6 +43,7 @@ Syntax: *object*.**PathCombine** ( *strPath1*, *strPath2* ) **As String** | `C:\a\\` | `b` | `C:\a\\b` | The method works on text only. It does not check that a folder or a file exists, and it does not resolve `..` or `.`. A forward slash is an ordinary character: `a/` and `b` give `a/\b`, and `http://x/y` after `C:\a` gives `C:\a\http://x/y`. Only one backslash is removed from each side, so a doubled backslash stays. A *strPath2* with a drive letter but no backslash after the colon (`D:b`) is not absolute and is joined like any other name. [**cDialogEx**](../cDialogEx#pathcombine) has the same method. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/PathMustExist.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/PathMustExist.md index 6338c378..e25f79d6 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/PathMustExist.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/PathMustExist.md @@ -20,9 +20,11 @@ Syntax: *object*.**PathMustExist** [ = *boolean* ] ## Remarks + [**ShowOpen**](ShowOpen) and [**ShowSave**](ShowSave) use the property. With **True**, a path that the user types and that does not exist makes Windows show a warning in a message box, and the dialog stays open. [**ShowBrowseForFolder**](ShowBrowseForFolder) ignores the property. In an Open dialog, [**FileMustExist**](FileMustExist) **True** sets `OFN_FILEMUSTEXIST`, which Windows documents as implying `OFN_PATHMUSTEXIST`. **ShowOpen** with **FileMustExist** **True** therefore checks the path whatever **PathMustExist** holds. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SelectAndOpenFile.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SelectAndOpenFile.md index 559f0978..c1940578 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SelectAndOpenFile.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SelectAndOpenFile.md @@ -22,9 +22,11 @@ Returns a **LongPtr** (a **Long** in a 32-bit program, a **LongLong** in a 64-bi ## Remarks + **SelectAndOpenFile** calls [**ShowOpen**](ShowOpen) with the current properties of the object, so every option that applies to the Open dialog ([**Filter**](Filter), [**InitialDir**](InitialDir), [**MultiSelect**](MultiSelect) and the others) applies here. It then calls **OpenFile** with the verb `open` for the path. The file is run as **OpenFile** describes: a program starts, and a document opens in its associated program. With [**MultiSelect**](MultiSelect) **True** and two or more files selected, **ShowOpen** returns an array, and **SelectAndOpenFile** calls **OpenFile** for each element in turn. It then returns 1, whatever the individual calls returned, so the return value does not report a file that failed to open. For a single file it returns what **OpenFile** returned. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SelectFiles.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SelectFiles.md index 81f3c176..dd4c12db 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SelectFiles.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SelectFiles.md @@ -19,9 +19,11 @@ Returns a **Collection** of **String** values, one full path for each chosen fil ## Remarks + **SelectFiles** calls **ShowOpen** with the current properties of the object, so every option of the Open dialog applies, [**MultiSelect**](MultiSelect) first of all. With **MultiSelect** **False** the collection holds one path at most. With **MultiSelect** **True** and one file selected it holds one path, and with two or more files selected it holds one path for each. The method saves the program from testing whether **ShowOpen** returned a **String** or an array. An empty result and a failure of the dialog look the same, as they do for **ShowOpen**. [**FileName**](FileName) is set as **ShowOpen** sets it: to the path when one file was chosen, and to an empty string after several. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetCodeFilters.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetCodeFilters.md index 1007d2d8..83f3ba25 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetCodeFilters.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetCodeFilters.md @@ -17,6 +17,7 @@ Syntax: *object*.**SetCodeFilters** ## Remarks + **SetCodeFilters** calls [**ClearFilter**](ClearFilter), so any earlier filter is lost, and then [**AddFilter**](AddFilter) for each pair: | Description | Pattern | @@ -32,6 +33,7 @@ Syntax: *object*.**SetCodeFilters** | `All Files (*.*)` | `*.*` | The descriptions are fixed English text. To show other text, build the filter with [**AddFilter**](AddFilter). The first pair, Basic Files, is the one that the dialog selects when it opens. A call of **AddFilter** after **SetCodeFilters** appends a pair after All Files. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetCommonFilters.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetCommonFilters.md index 53f44fb7..e99a57f9 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetCommonFilters.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetCommonFilters.md @@ -17,6 +17,7 @@ Syntax: *object*.**SetCommonFilters** ## Remarks + **SetCommonFilters** calls [**ClearFilter**](ClearFilter), so any earlier filter is lost, and then [**AddFilter**](AddFilter) for each pair: | Description | Pattern | @@ -25,6 +26,7 @@ Syntax: *object*.**SetCommonFilters** | `All Files (*.*)` | `*.*` | The descriptions are fixed English text. To show other text, build the filter with **AddFilter**. The first pair, Text Files, is the one that the dialog selects when it opens. A call of **AddFilter** after **SetCommonFilters** appends a pair at the end. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetDocumentFilters.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetDocumentFilters.md index f27f5da1..01ff16f8 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetDocumentFilters.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetDocumentFilters.md @@ -17,6 +17,7 @@ Syntax: *object*.**SetDocumentFilters** ## Remarks + **SetDocumentFilters** calls [**ClearFilter**](ClearFilter), so any earlier filter is lost, and then [**AddFilter**](AddFilter) for each pair: | Description | Pattern | @@ -29,6 +30,7 @@ Syntax: *object*.**SetDocumentFilters** | `All Files (*.*)` | `*.*` | The descriptions are fixed English text. To show other text, build the filter with [**AddFilter**](AddFilter). The first pair, Word Documents, is the one that the dialog selects when it opens. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetImageFilters.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetImageFilters.md index 17fd2f3d..92c4ed59 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetImageFilters.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/SetImageFilters.md @@ -17,6 +17,7 @@ Syntax: *object*.**SetImageFilters** ## Remarks + **SetImageFilters** calls [**ClearFilter**](ClearFilter), so any earlier filter is lost, and then [**AddFilter**](AddFilter) for each pair: | Description | Pattern | @@ -29,6 +30,7 @@ Syntax: *object*.**SetImageFilters** | `All Files (*.*)` | `*.*` | The descriptions are fixed English text. To show other text, build the filter with [**AddFilter**](AddFilter). The file-type list of the dialog holds the six descriptions in this order. The first, Bitmap Files, is the one that is selected when the dialog opens, not All Image Files, which is the fifth. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowBrowseForFolder.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowBrowseForFolder.md index 3909561d..1fdf4d8d 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowBrowseForFolder.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowBrowseForFolder.md @@ -30,6 +30,7 @@ The method always sets `BIF_RETURNONLYFSDIRS`, so only a folder of the file syst The dialog does not start in the folder that [**InitialDir**](InitialDir) holds. In the new style it opens with the folder of the user's profile selected, and in the old style with *This PC* selected, with *OK* disabled until the user selects a folder of the file system. + > [!NOTE] 1.1.0.0 > When the user confirms a folder, **ShowBrowseForFolder** stores the path in [**InitialDir**](InitialDir). A [**ShowOpen**](ShowOpen) or [**ShowSave**](ShowSave) after it passes that folder to Windows as its starting folder. A path typed into the edit box of the new-style dialog and confirmed is returned and stored in the same way. A cancelled dialog returns an empty string and leaves **InitialDir** as it was. [**FileName**](FileName) is not used or changed. diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowOpen.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowOpen.md index 3cad8e9b..57c0ce43 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowOpen.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowOpen.md @@ -19,6 +19,7 @@ Returns a **Variant**. When the user confirms the dialog, it holds the full path ## Remarks + **ShowOpen** calls `GetOpenFileNameW` and takes its options from the properties of the object: | Property | What **ShowOpen** passes | @@ -46,6 +47,7 @@ A confirmed **ShowOpen** that returns a **String** also stores it in [**FileName ### Several files With [**MultiSelect**](MultiSelect) **True**, Windows returns the folder and then the name of each selected file in one buffer. **ShowOpen** joins the folder to each name with a backslash and returns the full paths as an array of **String** values, in the order in which the names stand in the File name box, and sets [**FileName**](FileName) to an empty string. One selected file gives its full path as a **String**, as without **MultiSelect**. Test the result with **IsArray**, or call [**SelectFiles**](SelectFiles), which returns a **Collection** in both cases. + ## Example diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowSave.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowSave.md index efc12d5b..2e7994a4 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowSave.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/ShowSave.md @@ -35,12 +35,15 @@ Returns a **String**, the full path that the user confirmed, or an empty string A flag is set when its property is **True**. Unlike [**ShowOpen**](ShowOpen), **ShowSave** never sets `OFN_ALLOWMULTISELECT`, `OFN_FILEMUSTEXIST`, `OFN_READONLY` or `OFN_CREATEPROMPT`, so [**MultiSelect**](MultiSelect), [**FileMustExist**](FileMustExist), [**ReadOnly**](ReadOnly) and [**CreatePrompt**](CreatePrompt) have no effect on it. The method always sets `OFN_LONGNAMES`, `OFN_EXPLORER` and `OFN_ENABLESIZING`, selects the first pair of the filter, and has no owner window, hook or template. The [class page](.#settings-without-a-property) lists these settings. + When the user confirms the dialog, **ShowSave** stores the path in [**FileName**](FileName) as well as returning it. A cancelled dialog returns an empty string and leaves **FileName** as it was. The dialog returns only a name: it saves nothing, and the class does not create or write the file. An empty string also results when Windows reports a failure, such as a path that does not fit into the buffer of 260 characters, and no value tells which of the two happened. Neither case raises an error. With [**OverwritePrompt**](OverwritePrompt) **True** and a file that exists, the box *Confirm Save As* asks whether to replace it. *Yes* returns the path, and *No* leaves the dialog open. With **False** there is no box and the path is returned. With [**DefaultExt**](DefaultExt) set, a name typed without an extension gets it. + + > [!NOTE] 1.1.0.0 > The class pads the buffer with spaces to 260 characters, and the dialog does not show the padding. A [**FileName**](FileName) of 260 characters shows no dialog and returns an empty string at once, as a cancel does. A **FileName** of 261 characters or more makes **ShowSave** raise run-time error 5, *Invalid procedure call or argument*, before any dialog opens. diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/index.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/index.md index 85d4daf9..5813e1af 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/index.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialog/index.md @@ -28,11 +28,13 @@ End If The options are the members and flags of the Win32 structures that the dialogs take: [OPENFILENAME](https://learn.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamew) for **ShowOpen** (`GetOpenFileNameW`) and **ShowSave** (`GetSaveFileNameW`), and [BROWSEINFO](https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-browseinfow) for **ShowBrowseForFolder** (`SHBrowseForFolder`). Each property page names the flag it sets and the dialogs in which it takes effect, and the table [below](#which-dialog-uses-which-property) lists them all. + > [!NOTE] 1.1.0.0 > The property names follow VB6's **CommonDialog** control, but **cDialog** is a class with a smaller set of members. It has no **Flags** property (each option is a **Boolean** property of its own), no **FilterIndex** (the first filter is always selected, and the filter that the user chose is not reported), no **CancelError** (a cancelled dialog returns an empty string), no **FileTitle** or **MaxFileSize**, and no owner window. **InitialDir** is the control's **InitDir**. **ShowOpen** returns the file name, as well as storing it in **FileName**, and there is no Color, Font or Print dialog in this class (the package has a class for each of them, listed on its [landing page](../)). The [**cDialogEx**](../cDialogEx) class of this package has a **FilterIndex** and an owner window. The [**CommonDialog**](../../VBComDlg/CommonDialog) class of the VBComDlg package has **Flags**, **FilterIndex**, **CancelError**, **FileTitle**, **MaxFileSize** and those dialogs. ## Defaults + A new **cDialog** starts with these values, and [**Reset**](Reset) restores them. | Property | Starting value | @@ -40,6 +42,7 @@ A new **cDialog** starts with these values, and [**Reset**](Reset) restores them | [**DialogTitle**](DialogTitle), [**InitialDir**](InitialDir), [**DefaultExt**](DefaultExt), [**FileName**](FileName), [**Filter**](Filter) | An empty string | | [**MultiSelect**](MultiSelect), [**ReadOnly**](ReadOnly), [**NoChangeDir**](NoChangeDir), [**CreatePrompt**](CreatePrompt) | **False** | | [**OverwritePrompt**](OverwritePrompt), [**PathMustExist**](PathMustExist), [**FileMustExist**](FileMustExist), [**HideReadOnly**](HideReadOnly), [**NewDialogStyle**](NewDialogStyle) | **True** | + ```tb check_build check_run Dim dlg As New cDialog diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogColor.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogColor.md index 7b5130d3..92a56d6e 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogColor.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogColor.md @@ -28,6 +28,7 @@ Else End If ``` + ## How the dialog is shown [**ShowDialog**](#showdialog) calls `ChooseColorW` with the current [**Color**](#color) as the starting color (flag `CC_RGBINIT`, always set) and the 16 custom colors of the object. The properties [**FullOpen**](#fullopen), [**PreventFullOpen**](#preventfullopen), [**SolidColor**](#solidcolor) and [**AnyColor**](#anycolor) set the flags `CC_FULLOPEN`, `CC_PREVENTFULLOPEN`, `CC_SOLIDCOLOR` and `CC_ANYCOLOR` when they are **True**. The class sets no hook or template, no help button and no owner other than [**OwnerHwnd**](#ownerhwnd). The dialog is modal and has the title *Color*. @@ -40,9 +41,13 @@ The color is a plain number. A system color constant such as `vbButtonFace` (`&H The object keeps 16 custom colors, numbered 0 to 15, for the 16 custom boxes of the dialog. They start as the colors of **QBColor**(0) to **QBColor**(15) and last as long as the object does, so a second dialog of the same object shows the custom colors that the first one left. Nothing is saved to the registry or to a file. + + + > [!NOTE] timeless > The **Add to Custom Colors** button of the expanded dialog writes into the custom colors of the object at once. Choosing **Cancel** afterwards leaves [**Color**](#color) unchanged but keeps the added custom color. [**Reset**](#reset) restores the 16 starting colors. + ## Properties ### AnyColor @@ -400,6 +405,8 @@ Debug.Print dlg.ToRgbString() ' RGB(160, 176, 192) ``` + + ## See Also - [cDialogFont](cDialogFont) class -- shows the Font dialog diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogCountdownMsgBox.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogCountdownMsgBox.md index 038c6db9..7fb8ff91 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogCountdownMsgBox.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogCountdownMsgBox.md @@ -26,6 +26,7 @@ Select Case answer End Select ``` + > [!WARNING] 1.1.0.0 > A box with only an OK button that closes because its time ran out reports [**drOK**](#dialogresult), not [**drTimeout**](#dialogresult), and [**IsTimedOut**](#istimedout) is **False**. This affects the default box and [**ShowInfo**](#showinfo), [**ShowWarning**](#showwarning) and [**ShowError**](#showerror). A box with any other set of buttons reports **drTimeout** when it times out. No error is raised, so a program cannot tell a press of OK from the end of the time with these members alone. @@ -46,6 +47,7 @@ End If ## Properties + ### AutoClose {: .no_toc } @@ -99,7 +101,9 @@ Syntax: *object*.**CountdownSeconds** [ **=** *value* ] Setting it also sets [**RemainingSeconds**](#remainingseconds). The shortened forms [**ShowInfo**](#showinfo) and the others take the time as an argument, store it here, and do not touch [**RemainingSeconds**](#remainingseconds). The countdown text that the box shows (see [**ShowProgress**](#showprogress)) names this value in whole seconds, also for 1 and 0: `in 1 seconds...`. A value of 0 or less does not close the box: it stays open until a button is pressed, and its text says `in 0 seconds...`. + + > [!NOTE] 1.1.0.0 > The class multiplies the value by 1000 to get milliseconds. A value above 2,147,483 (about 24.8 days) overflows, and [**ShowDialog**](#showdialog) handles the error 6 itself: it shows a plain message box with the message and an OK button, with no icon, countdown text or other buttons, and returns **drOK**. The call raises nothing, but **Err.Number** is 6 afterwards. @@ -112,6 +116,7 @@ Debug.Print box.CountdownSeconds ' 45 Debug.Print box.RemainingSeconds ' 45 ``` + ### DefaultButton {: .no_toc } @@ -587,6 +592,7 @@ Else Debug.Print "Keeping the document: " & box.GetResultDescription() End If ``` + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogDateTime.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogDateTime.md index e63ea497..d7dc29b6 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogDateTime.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogDateTime.md @@ -30,6 +30,7 @@ End If ## What the box shows and accepts + The box has the title [**Title**](#title) and the text [**Prompt**](#prompt), a blank line, a line that names the format (`Enter date and time:`, `Enter date (MM/DD/YYYY):` or `Enter time (HH:MM):`, with `HH:MM AM/PM` when [**Is24HourFormat**](#is24hourformat) is **False**), a blank line and `Year range:` with [**MinYear**](#minyear) and [**MaxYear**](#maxyear). The edit box holds the current value as [**FormatDateTime**](#formatdatetime) gives it, such as `03/15/2026 14:05`. [**OwnerHwnd**](#ownerhwnd) has no effect, because the box has no owner. The class reads the text like this: @@ -41,10 +42,13 @@ The class reads the text like this: - A part that is not typed keeps its value, so `11/12/2028` changes only the date, and `23:59` only the hour and minute. The result of [**ShowDialog**](#showdialog) is **False** when the user cancelled or typed a value that fails these rules, which also shows a second box, `Invalid date/time format. Please use:` followed by the text that the edit box offered. The result is **True** when the user pressed OK, and that includes an empty entry and text without a recognisable part (`hello` or `1/2`): such text changes nothing. + + > [!WARNING] 1.1.0.0 > When the text fails the check, the parts that the class has already read stay in the object. After `13/45/2026`, [**Month**](#month) is 13 and [**Day**](#day) is 45; after `02/30/2026`, **Day** is 30 in February; after `25:61`, [**Hour**](#hour) is 25 and [**Minute**](#minute) is 61; after `04/05/2027 25:00` the date is 5 April 2027 and the hour is 25. [**ShowDialog**](#showdialog) returns **False** and [**Cancelled**](#cancelled) is **True**, yet the object does not hold the value it held before. The setters of the parts, which refuse such values, are bypassed. A program that carries on with the object after a **False** result reads wrong values. Copy [**DateTimeValue**](#datetimevalue) before the call and restore it when the call returns **False**. + > [!WARNING] 1.1.0.0 > A time with `AM` or `PM` and no date in front of it is not read correctly. `9:30 PM` is cut at its space into the date `9:30` and the time `PM` and rejected, so the offered text of the time mode with [**Is24HourFormat**](#is24hourformat) **False** (`02:05 PM`) is rejected when the user presses OK without changing it. `9:30pm`, with no space, is accepted and read as 21:03, because the class removes three characters from the end of the text and the third is a digit of the minutes. With a date in front, `03/15/2026 9:30 PM` is read correctly. Typing the time with a 24-hour clock (`21:30`) works in every mode. @@ -61,6 +65,7 @@ End If ## Properties + ### Cancelled {: .no_toc } @@ -469,7 +474,9 @@ Syntax: *object*.**AddHours** ( *hours* ) : *required* A **Long**, negative to move back. Adds the hours to the time of day, and moves the date forward or back by the days that this passes. + + > [!NOTE] 1.1.0.0 > The class works in seconds in a **Long**, so a value of about 596,000 hours (68 years) or more, in either direction, raises error 6 (*Overflow*). The date and time are left as they were. @@ -483,6 +490,7 @@ picker.AddHours -15 Debug.Print Format$(picker.DateTimeValue, "yyyy-mm-dd hh:nn:ss") ' 2026-03-15 09:05:09 ``` + ### FormatDate {: .no_toc } @@ -494,7 +502,9 @@ Syntax: *object*.**FormatDate** ( [ *formatType* ] ) **As String** : *optional* A [**DateFormat**](#dateformat) member. The default is **ShortDate**. **ShortDate** gives `03/15/2026`, with month, day and year in that order. **LongDate** gives `Sunday, March 15, 2026`, in English whatever the language of Windows is. **Custom** gives the text of [**CustomFormat**](#customformat) in which these patterns are replaced, in this order: `YYYY` (four digits), `YY` (two digits), `MMMM` (the month's name), `MMM` (its first three letters), `MM` (two digits), `M`, `DD` (two digits) and `D`. With an empty [**CustomFormat**](#customformat), **Custom** gives the short date. + + > [!WARNING] 1.1.0.0 > The patterns are replaced one after the other in the text that is being built, so the letters `M` and `D` of a month's name, which `MMMM` and `MMM` have just put in, are replaced as well. **March** and **May** lose their first letter to the number of the month (`3arch`, `5ay`) and **December** its first letter to the number of the day (`15ecember`). No error is raised. The same happens to any other `Y`, `M` or `D` in the format, which is not kept as a literal character. Formats with `MMMM` or `MMM` are right only for the other months. @@ -513,6 +523,7 @@ picker.CustomFormat = "MMMM D, YYYY" Debug.Print picker.FormatDate(DateFormat.Custom) ' 3arch 15, 2026 ``` + ### FormatDateTime {: .no_toc } @@ -789,6 +800,7 @@ Else Debug.Print "Kept " & picker.FormatDateTime() End If ``` + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogEx.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogEx.md index 122f7673..376bd093 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogEx.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogEx.md @@ -29,17 +29,21 @@ result = dlg.ShowOpen() If Len(result) > 0 Then Debug.Print "Chosen: " & result ``` + > [!WARNING] 1.1.0.0 > The three dialogs return the **name** of the chosen file or folder, not its full path: [**ShowOpen**](#showopen), [**ShowSave**](#showsave), [**ShowBrowseForFolder**](#showbrowseforfolder) and [**SelectFiles**](#selectfiles) give `a.txt` for `C:\Reports\a.txt`, and `other` for the folder `C:\Reports\other`. A program that opens or saves the result opens or saves a file in its own current folder, and no error is raised. [**FileName**](#filename) holds the same short name afterwards. The class asks Windows for the name that is relative to the parent folder, because the constant `SIGDN_FILESYSPATH` of the source has the value `&H80058001` and the file-system path is `&H80058000`. The folder in which the user chose is not reported, so the result cannot be used to open or save the file. Use [**cDialog**](cDialog/), which returns full paths. + > [!WARNING] 1.1.0.0 > [**InitialDir**](#initialdir) has no effect: the dialogs always open in the *Documents* folder, and no error is raised. The class makes the folder item with `SHCreateItemFromParsingName` and a GUID for `IShellItem` that has a wrong byte (`&H37` where the interface identifier has `&H7B`), so Windows answers that it has no such interface and the class skips `SetDefaultFolder`. + > [!WARNING] 1.1.0.0 > A [**Filter**](#filter) is ignored unless it holds an even number of pipe characters (`|`). A filter of N pairs written `Name|Pattern|Name|Pattern` holds 2N minus 1, an odd number, so the dialog shows no file-type list and accepts every file, and no error is raised. A pipe after the last pattern makes the count even: `Text (*.txt)|*.txt|All (*.*)|*.*|` shows two entries. [**AddFilter**](#addfilter) and the four **Set...Filters** methods build a filter without the closing pipe, and so does the text of [**GetPresetFilter**](#getpresetfilter), so none of them shows a list until the text gets its pipe: add `dlg.Filter = dlg.Filter & "|"` after them. ## Which dialog uses which property + *yes* means that the class passes the value to the dialog. [**ShowOpen**](#showopen) with [**PickFolders**](#pickfolders) **True** and [**ShowBrowseForFolder**](#showbrowseforfolder) show the same folder dialog. | Property | ShowOpen | ShowSave | Folder dialog | @@ -58,9 +62,12 @@ If Len(result) > 0 Then Debug.Print "Chosen: " & result | the icon ([**SetSystemIcon**](#setsystemicon), [**SetCustomIcon**](#setcustomicon)) | yes | yes | yes | Every method catches its errors: it writes a line that starts with the name of the method to the debug console (`Debug.Print`) and returns an empty string. A dialog that Windows cannot create gives an empty string in the same way, so an empty result means a cancel or a failure. + ## Differences from cDialog + + - A new **cDialogEx** has every Boolean property **False**. A new [**cDialog**](cDialog/#defaults) has [**OverwritePrompt**](#overwriteprompt), [**PathMustExist**](#pathmustexist) and [**FileMustExist**](#filemustexist) set to **True**. - The dialogs are the item dialogs of Windows Vista and later: the folder dialog is the file dialog in a folder-picking mode, with a *Select Folder* button, and not the tree of `SHBrowseForFolder`. There is no [**NewDialogStyle**](cDialog/NewDialogStyle), [**HideReadOnly**](cDialog/HideReadOnly) or [**ReadOnly**](cDialog/ReadOnly) property. - A [**ShowOpen**](#showopen) with [**MultiSelect**](#multiselect) puts the first file in [**FileName**](#filename) when two or more files are chosen. **cDialog** empties it. @@ -436,6 +443,7 @@ dlg.PickFolders = True Debug.Print dlg.ShowOpen() dlg.ClearIcon ``` + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogFindReplace.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogFindReplace.md index 9cde0622..49c87596 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogFindReplace.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogFindReplace.md @@ -10,9 +10,11 @@ has_toc: false A **cDialogFindReplace** shows the Windows Find dialog or the Find and Replace dialog. The dialog is modeless: [**ShowFind**](#showfind) and [**ShowReplace**](#showreplace) return at once, and the dialog stays open beside the program's own window. The class holds the text, the options and the last action of the user as properties, and builds the line of text that describes the search. + > [!WARNING] 1.1.0.0 > The program never learns what the user did in the dialog. [**ShowFind**](#showfind) and [**ShowReplace**](#showreplace) keep the structure that Windows works on in a local variable, which no longer exists when the method returns, although Windows keeps its address for as long as the dialog is open. The owner window then receives no message for **Find Next**, **Replace**, **Replace All** or **Cancel**, [**LastAction**](#lastaction) stays **None**, and [**hWnd**](#hwnd) and [**IsVisible**](#isvisible) still show a dialog after it was closed. In a 32-bit program, **ShowReplace** can also end the program with an access violation while the dialog is open, before anything is pressed, because Windows reads memory that has been reused. See [How the dialog reports](#how-the-dialog-reports). A program that needs the dialog calls `FindTextW` or `ReplaceTextW` itself and keeps the `FINDREPLACE` structure in a module-level variable. + **Properties:** [**AllowMatchCase**](#allowmatchcase), [**AllowUpDown**](#allowupdown), [**AllowWholeWord**](#allowwholeword), [**Direction**](#direction), [**FindWhat**](#findwhat), [**hWnd**](#hwnd), [**LastAction**](#lastaction), [**LastError**](#lasterror), [**MatchCase**](#matchcase), [**MessageId**](#messageid), [**Mode**](#mode), [**OwnerHwnd**](#ownerhwnd), [**ReplaceWith**](#replacewith), [**WholeWord**](#wholeword). **Methods:** [**IsVisible**](#isvisible), [**Reset**](#reset), [**ShowFind**](#showfind), [**ShowReplace**](#showreplace), [**ToDescription**](#todescription), [**UpdateFromFlags**](#updatefromflags). **Enumerations:** [**DialogMode**](#dialogmode), [**FindAction**](#findaction), [**SearchDirection**](#searchdirection). @@ -64,12 +66,18 @@ Public Sub FrHandleFindMessage(ByVal dlg As cDialogFindReplace, ByVal lParam As End Sub ``` + + + ### The structure the class keeps in a local variable Each of [**ShowFind**](#showfind) and [**ShowReplace**](#showreplace) declares its `FINDREPLACE` as a local variable and passes it to `FindTextW` or `ReplaceTextW`. Windows does not copy it: it keeps the pointer for the whole life of the dialog and reads and writes the structure each time the user presses a button. When the method returns, the memory belongs to whatever uses the stack next. In a 32-bit and in a 64-bit program alike, the dialog opens, accepts text and closes, and the owner window gets no message at all, so a program that waits for [**LastAction**](#lastaction) never leaves the wait. The fault is in the code of the class and not in Windows: the same call made with the structure in a module-level variable delivers a message for **Find Next** and one for **Cancel**, with the typed text in the buffer. + + + ## Properties ### AllowMatchCase @@ -444,9 +452,13 @@ Debug.Print dlg.Direction = SearchDirection.Up ' True ``` + + ## Enumerations + The enumerations are declared inside the class. A program names their members with the name of the enumeration in front, as in the samples, such as `FindAction.FindNext`. The name of the class cannot be put in front: `cDialogFindReplace.FindAction.FindNext` is refused with TB5079, *Unrecognized symbol 'cDialogFindReplace'*. + ### DialogMode {: .no_toc } diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogFont.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogFont.md index b22bf451..0d36b36d 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogFont.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogFont.md @@ -29,6 +29,7 @@ Else End If ``` + ## How the dialog is shown [**ShowDialog**](#showdialog) calls `ChooseFontW` with a `LOGFONT` made from the properties and the flag `CF_INITTOLOGFONTSTRUCT`, which is always set. The size is passed in points, as the structure holds it in tenths of a point, and as a height in pixels at 96 dots per inch (`-Size * 96 / 72`). Four properties set a flag when they are **True**: [**ScreenFontsOnly**](#screenfontsonly) (`CF_SCREENFONTS`), [**ShowEffects**](#showeffects) (`CF_EFFECTS`), [**ForceFontExist**](#forcefontexist) (`CF_FORCEFONTEXIST`) and [**FixedPitchOnly**](#fixedpitchonly) (`CF_FIXEDPITCHONLY`, together with a fixed pitch in the `LOGFONT`). `CF_LIMITSIZE` is set when [**MinSize**](#minsize) or [**MaxSize**](#maxsize) is above 0. The class sets no hook, template, printer device context or help button. The dialog is modal and has the title *Font*. @@ -37,9 +38,13 @@ When the user chooses **OK**, [**ShowDialog**](#showdialog) stores the face, the The face list holds every font that Windows lists for the screen, including the vertical variants, whose names begin with `@`: the class sets no flag that hides them. The list of styles, the script box and the colors depend on the face and on Windows, and the class reads neither the style name nor the script that the user chose, only the weight and the italic bit. + + + > [!WARNING] 1.1.0.0 > **ShowDialog** also returns **False**, without showing a dialog, when the call to Windows fails. A **MinSize** above 0 with **MaxSize** left at 0, or a **MinSize** above **MaxSize**, makes it fail. The class does not read the error code, so the program cannot tell such a failure from a **Cancel**. Set both [**MinSize**](#minsize) and [**MaxSize**](#maxsize), or neither. + ## Properties ### Bold @@ -123,6 +128,9 @@ Syntax: *object*.**ForceFontExist** [ **=** *value* ] A **Boolean**, **True** by default. When it is **True** and the face box holds a name that is not in the list, **OK** shows a message, *There is no font with that name. Choose a font from the list of fonts.*, and the dialog stays open; **Cancel** then returns **False** and keeps [**FaceName**](#facename). When it is **False**, **OK** closes the dialog. + + + > [!NOTE] 1.1.0.0 > With **ForceFontExist** **False** and a [**FaceName**](#facename) that is not installed, **OK** returns **True** and sets **FaceName** to an empty string. @@ -185,6 +193,7 @@ Syntax: *object*.**MinSize** [ **=** *value* ] A **Long**, 0 by default, which means no limit. Above 0, [**ShowDialog**](#showdialog) sets `CF_LIMITSIZE` and passes the value as the smallest size. The size list then starts at the smallest standard size not below it. + > [!WARNING] 1.1.0.0 > A **MinSize** above 0 needs a [**MaxSize**](#maxsize) of at least the same value. With **MaxSize** left at 0, [**ShowDialog**](#showdialog) returns **False** at once, without a dialog, as if the user had cancelled. @@ -196,6 +205,7 @@ dlg.MaxSize = 72 Debug.Print dlg.MinSize ``` + ### OwnerHwnd {: .no_toc } @@ -376,7 +386,7 @@ The result has the form `font-family: 'Segoe UI'; font-size: 9pt; font-weight: normal; font-style: normal; text-decoration: none;` -with the face name between single quotes, the size in points, `bold` or `normal`, `italic` or `normal`, and `underline` or `none`. It does not write [**StrikeOut**](#strikeout) or [**Color**](#color). A single quote in the face name is written as it is, without an escape, and ends the quoted name early. A fractional size is written with the decimal separator of the regional settings of the computer, so the CSS is wrong where that is a comma. +with the face name between single quotes, the size in points, `bold` or `normal`, `italic` or `normal`, and `underline` or `none`. It does not write [**StrikeOut**](#strikeout) or [**Color**](#color). A fractional size is written with the decimal separator of the regional settings of the computer, so the CSS is wrong where that is a comma. ```tb check_build check_run Dim dlg As New cDialogFont @@ -391,6 +401,13 @@ Debug.Print dlg.ToCssFont() ' font-family: 'Courier New'; font-size: 14pt; font-weight: bold; font-style: italic; text-decoration: underline; ``` + + + +> [!WARNING] 1.1.0.0 +> A single quote in the face name is written as it is, without an escape, so the quoted name ends early and the CSS is wrong for such a face. + + ### ToDescription {: .no_toc } @@ -415,9 +432,13 @@ Debug.Print dlg.ToDescription() ' Courier New 14pt Bold Italic Underline Strikeout ``` + + ## Enumerations + The enumerations are declared inside the class. A program names their members with the name of the enumeration in front, as in the samples, or by the member alone, such as `StyleBold`. The name of the class cannot be put in front: `cDialogFont.FontStyle.StyleBold` is refused with TB5079, *Unrecognized symbol 'cDialogFont'*. Only **FontStyle** is used by a member, the property [**Style**](#style). The other three hold Windows constants for the font structure and are not taken or returned by any member. + ### FontFamily {: .no_toc } diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogInputBox.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogInputBox.md index 303a6c37..69b187f1 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogInputBox.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogInputBox.md @@ -30,6 +30,7 @@ Else End If ``` + ## How the box works The text of the box is [**Prompt**](#prompt), followed by a blank line and `(Hint: ` and [**Placeholder**](#placeholder) and `)` when a placeholder is set, followed by a blank line and a line on the length when [**MinLength**](#minlength) or [**MaxLength**](#maxlength) is above 0: `Length: 3 to 8 characters`, `Length: At least 3 characters` or `Length: Maximum 8 characters`. The edit box holds [**DefaultValue**](#defaultvalue). @@ -43,9 +44,13 @@ The text of the box is [**Prompt**](#prompt), followed by a blank line and `(Hin An empty string passes when [**AllowEmpty**](#allowempty) is **True** (the default) and no [**MinLength**](#minlength) is set. The result of the call does not tell the user's cancelling apart from a refusal; both give **False** and [**Cancelled**](#cancelled) **True**. The two differ only in [**Value**](#value). + + + > [!WARNING] 1.1.0.0 > The properties [**Multiline**](#multiline), [**PasswordChar**](#passwordchar), [**OwnerHwnd**](#ownerhwnd) and [**CustomPattern**](#custompattern) are stored but not used. The box is always a single line, it shows what is typed without masking it, it has no owner window and the owner stays usable while it is open, and the value **Custom** of [**Validation**](#validation) accepts any text that is not empty. A program that sets [**PasswordChar**](#passwordchar) to hide a secret shows the secret on the screen. [**cDialogPassword**](cDialogPassword) masks its entry. + ## Properties ### AllowEmpty @@ -423,6 +428,9 @@ Syntax: *object*.**ShowInteger** ( *prompt* [ , *title* [ , *defaultValue* ] ] ) Sets the three properties that correspond to the arguments, [**Validation**](#validation) (**Integer**) and [**AllowEmpty**](#allowempty) (**False**), calls [**ShowDialog**](#showdialog) and returns [**Value**](#value) as a **Long**. A cancelled box returns 0, which is also a possible entry, so test [**Cancelled**](#cancelled). The text `1e3` passes and returns 1000. + + + > [!NOTE] 1.1.0.0 > A text that passes the check but that a **Long** cannot hold, such as `99999999999`, raises error 6 after the box has closed. [**Value**](#value) then holds the text. @@ -434,6 +442,7 @@ copies = box.ShowInteger("How many copies?", "Print", "1") If Not box.Cancelled Then Debug.Print copies & " copies" ``` + ### ShowNumeric {: .no_toc } @@ -619,6 +628,8 @@ Else End If ``` + + ## See Also - [cDialogPassword](cDialogPassword) class -- asks for a password with a masked entry diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPageSetup.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPageSetup.md index 9c6742e4..7879f520 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPageSetup.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPageSetup.md @@ -10,6 +10,7 @@ has_toc: false A **cDialogPageSetup** shows the Windows Page Setup dialog, in which the user picks the paper, its orientation and the four margins of a page. It holds the page as properties, in inches, with ready-made sizes and margins, and works out the printable area. + > [!WARNING] 1.1.0.0 > Three things give a wrong result without an error. [**PaperWidth**](#paperwidth) and [**PaperHeight**](#paperheight) are not passed to the dialog, so it opens with the paper of the default printer whatever they hold. [**Orientation**](#orientation) is neither passed to the dialog nor read from it: a user who chooses *Landscape* leaves **Orientation** at **Portrait** and swaps the paper width and height instead. With [**Unit**](#unit) set to **Millimeters**, [**ToDescription**](#todescription) writes the values in inches and labels them `mm`. @@ -31,6 +32,7 @@ Else End If ``` + ## How the dialog is shown [**ShowDialog**](#showdialog) calls `PageSetupDlgW` with the flag `PSD_MARGINS`, which is always set, and one of `PSD_INTHOUSANDTHSOFINCHES` and `PSD_INHUNDREDTHSOFMILLIMETERS`, which [**Unit**](#unit) selects. [**AllowMargins**](#allowmargins), [**AllowOrientation**](#alloworientation) and [**AllowPaper**](#allowpaper) set `PSD_DISABLEMARGINS`, `PSD_DISABLEORIENTATION` and `PSD_DISABLEPAPER` when they are **False**. The class sets no hook, template, minimum margins, help button or printer. The dialog is modal and has the title *Page Setup*. @@ -41,6 +43,9 @@ When the user chooses **OK**, [**ShowDialog**](#showdialog) stores the paper siz The class gives the dialog no printer data and frees the data Windows returns, so the printer the user chose, the paper source and the other settings of the printer are lost. What remains is the size of the paper that the dialog shows when it closes, in [**PaperWidth**](#paperwidth) and [**PaperHeight**](#paperheight). The paper that the dialog offers first is the one of the default printer. + + + ## Properties ### AllowMargins @@ -182,6 +187,9 @@ Syntax: *object*.**Orientation** [ **=** *value* ] A [**PaperOrientation**](#paperorientation), **Portrait** by default. + + + > [!WARNING] 1.1.0.0 > The property is only stored. [**ShowDialog**](#showdialog) does not pass it to the dialog, so a **Landscape** set before the call does not select *Landscape* in the dialog, and it does not read the orientation back. After a user chose *Landscape*, **Orientation** is as it was and [**PaperWidth**](#paperwidth) and [**PaperHeight**](#paperheight) have changed places. To find the orientation after the dialog, compare the two: the page is landscape when **PaperWidth** is the larger. [**ToDescription**](#todescription) names the stored value. @@ -238,6 +246,7 @@ Syntax: *object*.**PaperWidth** [ **=** *value* ] A **Double**, 8.5 by default, handled as [**PaperHeight**](#paperheight) is. + > [!WARNING] 1.1.0.0 > Setting **PaperWidth** and **PaperHeight**, or calling [**SetA4Size**](#seta4size) or [**SetLegalSize**](#setlegalsize), does not make the dialog open with that paper. The margins are passed, and the paper is the one of the default printer. After **OK** the two properties hold what the dialog returned, not what was set. @@ -257,6 +266,7 @@ Syntax: *object*.**Unit** [ **=** *value* ] A [**MeasurementUnit**](#measurementunit), **Inches** by default. It selects the unit of the dialog; the properties of the class stay in inches. See [How the dialog is shown](#how-the-dialog-is-shown). + > [!WARNING] 1.1.0.0 > With **Millimeters**, [**ToDescription**](#todescription) still writes inches and labels them `mm`: a 1 inch margin is described as `1.00 mm`. @@ -272,6 +282,7 @@ Debug.Print dlg.Unit ' 1 ``` + ## Methods ### GetPrintableHeight @@ -460,9 +471,13 @@ dlg.SetNarrowMargins Debug.Print dlg.ToDescription() ``` + + ## Enumerations + The enumerations are declared inside the class. A program names their members with the name of the enumeration in front, as in the samples, or by the member alone, such as `Landscape`. The name of the class cannot be put in front: `cDialogPageSetup.PaperOrientation.Landscape` is refused with TB5079, *Unrecognized symbol 'cDialogPageSetup'*. + ### MeasurementUnit {: .no_toc } diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPassword.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPassword.md index d3c40075..f686fc23 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPassword.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPassword.md @@ -30,6 +30,7 @@ Else End If ``` + ## How the box works The box has the title [**Title**](#title). Its text is [**Prompt**](#prompt), a blank line, `Requirements:`, and one line for each rule that is on: `- Minimum N characters` always, then `- At least one uppercase letter`, `- At least one lowercase letter`, `- At least one number` and `- At least one special character`. The maximum length is not named. @@ -38,12 +39,17 @@ The class masks the edit box with the character of [**PasswordChar**](#passwordc [**ShowDialog**](#showdialog) repeats the question until it has a password that passes or [**MaxAttempts**](#maxattempts) attempts have failed. A failure shows a message box with the reason and `Attempts remaining: N`, and the last failure shows the reason and `Maximum attempts reached.`. If [**RequireConfirm**](#requireconfirm) is **True**, a password that passes is asked for again in a second box, `Please confirm your password:` under the title `Title - Confirm`, and a difference counts as a failed attempt (`Passwords do not match!`). Cancelling any box ends the call. + + + > [!WARNING] 1.1.0.0 > Only the first box of [**ShowDialog**](#showdialog) is masked. The confirmation box of [**RequireConfirm**](#requireconfirm) and the three boxes of [**ShowChangeDialog**](#showchangedialog) (`Title - Current Password`, `Title - New Password` and `Title - Confirm New Password`) show what the user types in clear text, because the timer looks for a window with exactly the title [**Title**](#title). A program that asks for a confirmation or a change of password shows the passwords on the screen. To keep the entry masked, ask with [**ShowDialog**](#showdialog) alone and compare the two entries in the program, or use another control. + > [!NOTE] 1.1.0.0 > The properties [**ShowStrength**](#showstrength), [**AllowShowPassword**](#allowshowpassword) and [**OwnerHwnd**](#ownerhwnd) are stored but not used: the box has no strength indicator, no control that shows the password and no owner window. The password stays in [**Password**](#password), an ordinary string, until [**Reset**](#reset) empties it or the object ends. + ## Properties ### AllowShowPassword @@ -371,6 +377,9 @@ Syntax: *object*.**GetStrength** ( *password* ) **As** [**PasswordStrength**](#p The class adds points: 1 for 8 or more characters, 1 more for 12 or more, 1 more for 16 or more, 1 each for an uppercase letter, a lowercase letter and a digit, and 2 for a special character (the list under [**RequireSpecial**](#requirespecial)). A score of 0 to 2 is **VeryWeak**, 3 or 4 **Weak**, 5 or 6 **Fair**, and 7 or 8 **Strong**. The rules of the object (minimum, maximum and the required kinds) play no part. + + + > [!NOTE] 1.1.0.0 > The highest score is 8, so the result is never **VeryStrong**. @@ -384,6 +393,7 @@ Debug.Print box.GetStrengthDescription(box.GetStrength("Zq7-demo-xxxx")) ' Str Debug.Print box.GetStrengthDescription(box.GetStrength("Zq7-demo-xxxxxxxxxxxx")) ' Strong ``` + ### GetStrengthDescription {: .no_toc } @@ -546,6 +556,8 @@ If box.ShowChangeDialog(current) Then End If ``` + + ## See Also - [cDialogInputBox](cDialogInputBox) class -- asks for one line of text, without a mask diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPrint.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPrint.md index 6f98abf9..1b7d003f 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPrint.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogPrint.md @@ -38,6 +38,7 @@ Private Sub ChooseAndPrint() End Sub ``` + ## How the dialog is shown [**ShowDialog**](#showdialog) calls `PrintDlgW`, the classic Print dialog, not the newer `PrintDlgEx`. The properties set these flags when they have the value shown: [**ReturnDC**](#returndc) **True** (`PD_RETURNDC`), [**Collate**](#collate) **True** (`PD_COLLATE`), [**PrintToFile**](#printtofile) **True** (`PD_PRINTTOFILE`), [**AllowSelection**](#allowselection) **False** (`PD_NOSELECTION`), [**AllowPageRange**](#allowpagerange) **False** (`PD_NOPAGENUMS`), [**HidePrintToFile**](#hideprinttofile) **True** (`PD_HIDEPRINTTOFILE`) and [**AllowPrintToFile**](#allowprinttofile) **False** (`PD_DISABLEPRINTTOFILE`). [**PrintRangeMode**](#printrangemode) adds `PD_SELECTION` or `PD_PAGENUMS`. The class passes no printer settings and sets no hook, template or help button. The dialog is modal and has the title *Print*; its printer is the default printer of the computer until the user changes it. @@ -46,12 +47,19 @@ When the user chooses **OK**, [**ShowDialog**](#showdialog) stores the page rang The class frees the printer data that Windows allocates for the dialog. The printer that the user chose, its paper, its orientation and the other settings of its driver are therefore not returned as such. They are in the device context, which is set up for the chosen printer with its settings, so a program that needs to know them asks the context with the API function `GetDeviceCaps`. + + + > [!IMPORTANT] 1.1.0.0 > With [**ReturnDC**](#returndc) **True**, which is the default, every **OK** creates a device context that the class never deletes. The program must delete it with the API function `DeleteDC` when it has finished with it, as the sample at the head of the page does, or set **ReturnDC** to **False** when it does not need the context. [**Reset**](#reset), and a later [**ShowDialog**](#showdialog) that returns **True**, replace the handle without deleting the old context. + + + > [!NOTE] 1.1.0.0 > The page numbers and the number of copies are passed to Windows as 16-bit integers. A [**FromPage**](#frompage), [**ToPage**](#topage), [**MinPage**](#minpage), [**MaxPage**](#maxpage) or [**Copies**](#copies) that does not fit, one above 32,767 or one below -32,768, makes [**ShowDialog**](#showdialog) raise error 6, *Overflow*, before the dialog opens. + ## Properties ### AllowPageRange @@ -426,6 +434,8 @@ Debug.Print dlg.ToDescription() ' Pages 2 - 4, 3 copies, Collated, Print to file ``` + + ## Enumerations ### PrintRange @@ -439,7 +449,9 @@ Which pages are printed, the type of [**PrintRangeMode**](#printrangemode). | **Selection** | 1 | The selection of the program. | | **PageRange** | 2 | The pages from [**FromPage**](#frompage) to [**ToPage**](#topage). | + The enumeration is declared inside the class. A program names its members with the name of the enumeration in front, as in the samples, or by the member alone, such as `Selection`. The name of the class cannot be put in front: `cDialogPrint.PrintRange.Selection` is refused with TB5079, *Unrecognized symbol 'cDialogPrint'*. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogProgress.md b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogProgress.md index 346dd7bc..99885591 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogProgress.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/cDialogProgress.md @@ -34,12 +34,15 @@ If Not progress.Cancelled Then progress.Complete Debug.Print progress.Percentage & "%" ``` + > [!NOTE] 1.1.0.0 > [**GetRemainingTime**](#getremainingtime), and with it [**GetRemainingTimeString**](#getremainingtimestring) and [**GetDescription**](#getdescription), raise error 11 (*Division by zero*) when they are called before the clock has ticked after [**Start**](#start) (the clock of Windows ticks about every 16 milliseconds) and [**Progress**](#progress) is above [**Min**](#min). A program that reports progress as it begins reads the description too early. Call them later, or handle the error. + > [!NOTE] 1.1.0.0 > [**Percentage**](#percentage) and [**SetPercentage**](#setpercentage) multiply in a **Long**, so a range wider than about 21 million units can raise error 6 (*Overflow*): [**Percentage**](#percentage) with **Max** 100,000,000 and a position of 50,000,000 does, and so does [**SetPercentage**](#setpercentage) with 50. Use a smaller range, such as the number of items instead of the number of bytes. + ## Properties ### Cancelled @@ -670,6 +673,8 @@ Else End If ``` + + ## See Also - [cDialogCountdownMsgBox](cDialogCountdownMsgBox) class -- shows a message box that closes by itself diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/index.md b/docs/Reference/Contributed/TBMANLIB_Dialog/index.md index 0cd42760..3b91343a 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/index.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/index.md @@ -21,6 +21,7 @@ exclude_from_docs: A twinBASIC package of dialogs. Its first class, **cDialog**, shows the Windows Open, Save As and Browse For Folder dialogs, builds the file-type filters of the first two, splits a file path into folder, name and extension, and opens a file in the program that Windows associates with it; its property names follow VB6's **CommonDialog** control. **cDialogEx** shows the Open, Save As and folder dialogs of Windows Vista and later. Five more classes show the Windows Color, Font, Find and Replace, Page Setup and Print dialogs, and five classes ask for a value or report progress (a message box with a time limit, a date and time, an input box, a password and a progress state); the last five use the run-time's own **InputBox** and **MsgBox** or draw nothing. Every class except **cDialog** is marked as not creatable with **CreateObject**, and is made with **New**. The module **mDialog** holds the types and conversion functions that they share. This section documents version 1.1.0.0, which added every class except **cDialog**. The **CommonDialog** class of the [VBComDlg](../VBComDlg/CommonDialog) package is a fuller alternative for the Windows common dialogs, with flags, a filter index and cancel errors. + > [!WARNING] 1.1.0.0 > Several classes fail at their main task, and no error is raised. [**cDialogFindReplace**](cDialogFindReplace) reports nothing to the program, and its **ShowReplace** can end a 32-bit program. [**cDialogPageSetup**](cDialogPageSetup) ignores the paper size and the orientation the program sets, and does not read back the orientation the user chose. [**cDialogEx**](cDialogEx) returns file and folder names without their folder, ignores **InitialDir**, and shows no file types for a filter written in the usual form. [**cDialogPassword**](cDialogPassword) shows the password as it is typed in its confirmation boxes. [**cDialogDateTime**](cDialogDateTime) keeps the parts of an entry it refused, refuses a time with AM or PM and no date, and changes the letters of a month name in a custom format. Four properties of [**cDialogInputBox**](cDialogInputBox), among them **PasswordChar**, have no effect. Each class's page describes its defects. [**cDialog**](cDialog/), the class for the Open, Save As and Browse For Folder dialogs, works. @@ -89,3 +90,7 @@ The package contains a module of test procedures, **TestModule**, whose procedur - [VBComDlg](../VBComDlg/) package -- the **CommonDialog** class, with flags, a filter index and cancel errors - [Experimental Packages](../../Experimental/) -- the packages from TWINSERV that are not yet ready for use in a program + +## Test cases + +The notes and warnings of this section are checked by the test cases in [TBMANLIB_Dialog-tests.twinproj](TBMANLIB_Dialog-tests.twinproj), a twinBASIC project. It uses TBMANLIB_Dialog as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/TBMANLIB_Dialog/mDialog.md b/docs/Reference/Contributed/TBMANLIB_Dialog/mDialog.md index 14381390..357cd304 100644 --- a/docs/Reference/Contributed/TBMANLIB_Dialog/mDialog.md +++ b/docs/Reference/Contributed/TBMANLIB_Dialog/mDialog.md @@ -16,6 +16,7 @@ Holds the constants, the four structure types and the eight conversion functions **Types:** [COMDLG_FILTERSPEC](#types), [GUID](#types), [POINT](#types), [RECT](#types). + The members of **mDialog** are global: a program writes `InchesToMM(1)` and also `mDialog.InchesToMM(1)`. ```tb check_build check_run @@ -139,7 +140,11 @@ The structures are those of the Windows API, written for the dialog classes of t | **POINT** | **X** and **Y**, both **Long** | 8 bytes | | **RECT** | **Left**, **Top**, **Right** and **Bottom**, all **Long** | 16 bytes | + + + Declare **GUID** as `mDialog.GUID`: the name **GUID** alone refers to another type, and `Dim g As GUID` is refused with error TB5106 in a project that references the package. Writing the module name before **RECT**, **POINT** and **COMDLG_FILTERSPEC** as well makes sure that the type of the package is the one used. + ```tb check_build check_run Dim g As mDialog.GUID diff --git a/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/Show.md b/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/Show.md index 472e2878..0a2aae93 100644 --- a/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/Show.md +++ b/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/Show.md @@ -11,9 +11,12 @@ Attaches the horizontal scroll bar to a **ListBox** control and initialises scro Syntax: *object*.**Show** *ListBoxInst* + *ListBoxInst* : *required* An object expression that evaluates to a **ListBox** control. The class assigns it to a **ListBox** variable, so an object of another type causes a run-time error. The class holds this reference for the lifetime of the object; [**UnShow**](UnShow) does not release it. + + **Show** performs three actions in sequence: 1. Calls the Win32 `ShowScrollBar` API to make the horizontal scroll bar visible on *ListBoxInst*. @@ -21,10 +24,18 @@ Syntax: *object*.**Show** *ListBoxInst* 3. Installs a window subclass (`SetWindowSubclass` from `comctl32`) on the control's `hWnd` to intercept `WM_HSCROLL` messages. The subclass procedure updates the scroll bar position with `SetScrollInfo` and then passes each message on to `DefSubclassProc`. After **Show** returns, the **ListBox** scrolls horizontally when any item is wider than the visible area. + + +> [!WARNING] 1.0.0.1 +> The thumb of the scroll bar and the contents of the list disagree after a click on a scroll arrow or in the track of the scroll bar. The subclass moves the thumb by 20 pixels for an arrow and by one page for the track, while the list box scrolls its contents by its own step, for example 9 pixels for an arrow and about two-thirds of a page for the track. After a few clicks the thumb is at a position the list does not show. Dragging the thumb, and the scroll to the start or the end, leave them equal. + + > [!IMPORTANT] timeless > **Show** must be called after the **ListBox** has been populated. If items are added or removed after the call, the horizontal extent is not updated automatically. Call **Show** again on the same control to recalculate the extent for the updated item list. + + > [!NOTE] timeless > The subclass installed by **Show** is removed automatically when the class instance is destroyed (`Class_Terminate`). It can also be removed explicitly by calling [**UnShow**](UnShow). diff --git a/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/UnShow.md b/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/UnShow.md index d401f68c..46c6ac85 100644 --- a/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/UnShow.md +++ b/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/UnShow.md @@ -14,13 +14,18 @@ Syntax: *object*.**UnShow** *object* : *required* An object expression that evaluates to a **cListboxHScroll** instance. + **UnShow** calls `RemoveWindowSubclass` (comctl32) to detach the scroll message handler that was installed by **Show**. After this call, the **ListBox** control associated with the instance no longer receives the subclassed `WM_HSCROLL` processing added by the class. + + > [!NOTE] timeless > **UnShow** does not hide the horizontal scrollbar itself, and does not reset the horizontal extent or release the **ListBox** reference---it removes only the subclass procedure. Call `ShowScrollBar` directly with `SB_HORZ` and `0` if the scrollbar must also be hidden. + > [!IMPORTANT] timeless > The class destructor (`Class_Terminate`) calls the internal `RemoveListBoxSubclass` helper automatically, so **UnShow** need only be called explicitly when the subclass must be removed before the **cListboxHScroll** instance goes out of scope. + ### Example diff --git a/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/index.md b/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/index.md index 4a436760..5097419f 100644 --- a/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/index.md +++ b/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/cListboxHScroll/index.md @@ -10,7 +10,7 @@ has_toc: false A helper class that adds a functional horizontal scrollbar to a standard VB **ListBox** control by combining the `LB_SETHORIZONTALEXTENT` message, Win32 scroll-info APIs, and window subclassing. -The class measures the pixel width of each list item using GDI's `GetTextExtentPoint32W`, sets the scroll range to fit the widest item, and installs a subclass procedure to intercept `WM_HSCROLL` messages so the scrollbar thumb tracks correctly. Call [**Show**](#show) after populating the list and [**UnShow**](#unshow) (or allow the class instance to be destroyed) when the scroll handling is no longer needed. +The class measures the pixel width of each list item using GDI's `GetTextExtentPoint32W`, sets the scroll range to fit the widest item, and installs a subclass procedure to intercept `WM_HSCROLL` messages and set the scrollbar thumb's position. Call [**Show**](#show) after populating the list and [**UnShow**](#unshow) (or allow the class instance to be destroyed) when the scroll handling is no longer needed. ```tb check_build Private HScroll As New cListboxHScroll @@ -38,22 +38,35 @@ Activates horizontal scrolling on a **ListBox** control. Syntax: *object*.**Show** *ListBoxInst* + *ListBoxInst* : *required* An **Object** reference to the **ListBox** control to instrument. The class assigns it to a **ListBox** variable, so an object of another type causes a run-time error. The control must already have its items populated; **Show** measures the text width of every item at call time to compute the scroll range. + + **Show** performs three operations in sequence: 1. Calls the Win32 `ShowScrollBar` API to make the horizontal scrollbar visible. 2. Iterates every list item, measures its pixel width using the GDI device context of the control, and sends `LB_SETHORIZONTALEXTENT` with the widest measurement plus a 10-pixel margin. It also calls `SetScrollInfo` to set the scroll range and page size so the scrollbar thumb reflects the visible proportion of the content. When the list is empty, or every item is empty, neither is set. 3. Installs a window subclass procedure via `SetWindowSubclass` (comctl32) to intercept `WM_HSCROLL` messages. The subclass updates the scroll position on line-left / line-right (20 px per step), page-left / page-right, thumb-track / thumb-position, and left / right scrolls, then passes the message to `DefSubclassProc`. + + The scroll range is calculated from the widest item's pixel width measured with the font that the control reports through `WM_GETFONT`, so a **ListBox** that uses a non-default font is measured with that font. + + +> [!WARNING] 1.0.0.1 +> The thumb of the scroll bar and the contents of the list disagree after a click on a scroll arrow or in the track of the scroll bar. The subclass moves the thumb by 20 pixels for an arrow and by one page for the track, while the list box scrolls its contents by its own step, for example 9 pixels for an arrow and about two-thirds of a page for the track. After a few clicks the thumb is at a position the list does not show. Dragging the thumb, and the scroll to the start or the end, leave them equal. + + > [!NOTE] timeless > **Show** measures item widths at the moment it is called. If items are added or removed after **Show**, the scroll range is not updated automatically. Call **Show** again after bulk changes to refresh the extent. + > [!IMPORTANT] timeless > The control's **hWnd** must be valid (the form must be loaded and the control fully created) before calling **Show**. Calling **Show** before the control's window exists returns silently without installing any scrolling behavior. + ### UnShow {: .no_toc } @@ -62,9 +75,13 @@ Removes the window subclass installed by [**Show**](#show). Syntax: *object*.**UnShow** + Calls `RemoveWindowSubclass` to detach the `WM_HSCROLL` handler. After **UnShow** the **ListBox** no longer receives the class's `WM_HSCROLL` handling. The scroll bar and the horizontal extent set by **Show** are not reset; send the `LB_SETHORIZONTALEXTENT` message with a width of 0 to the control, and hide the bar with `ShowScrollBar`, if clearing them is required. + + `Class_Terminate` removes the subclass with the same internal helper, so the subclass is removed when the **cListboxHScroll** instance is destroyed. + ## Example @@ -90,9 +107,19 @@ End Sub ## Remarks -**cListboxHScroll** uses window subclassing via the comctl32 `SetWindowSubclass` / `RemoveWindowSubclass` / `DefSubclassProc` API family. The subclass is identified by the constant ID `1` in every instance, so use one instance per **ListBox**. +**cListboxHScroll** uses window subclassing via the comctl32 `SetWindowSubclass` / `RemoveWindowSubclass` / `DefSubclassProc` API family. The subclass is identified by the constant ID `1` in every instance. + + +> [!NOTE] 1.0.0.1 +> Two instances on the same **ListBox** install two subclasses, and each handles every `WM_HSCROLL` message: a click on a scroll arrow moves the thumb 40 pixels, not 20. **UnShow** of one instance removes only its own subclass. Use one instance per **ListBox**. + + +Instances on different list boxes are independent: **UnShow** of one leaves the scroll handling of the other in place. + + The class stores the **ListBox** reference in a class-level variable (`Ctl`). Replacing the control by calling **Show** with a different **ListBox** does not automatically remove the subclass from the first control; call **UnShow** before switching targets. + ## See Also diff --git a/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/index.md b/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/index.md index 95879ea7..36500b19 100644 --- a/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/index.md +++ b/docs/Reference/Contributed/TBMANLIB_ListBoxHScroll/index.md @@ -1,6 +1,6 @@ --- title: TBMANLIB_ListBoxHScroll Package -parent: Contributed Packages +parent: Experimental Packages has_toc: false indexed_from: 1.0.0.1 permalink: /tB/Packages/Contributed/TBMANLIB_ListBoxHScroll/ @@ -18,3 +18,7 @@ The **TBMANLIB_ListBoxHScroll** contributed package provides a helper class that - [cListboxHScroll](cListboxHScroll/) -- a helper class that adds a functional horizontal scrollbar to a standard VB ListBox control using GDI text measurement, `LB_SETHORIZONTALEXTENT`, and window subclassing - [Show](cListboxHScroll/Show) -- attaches the horizontal scrollbar to a ListBox control and initialises scroll handling - [UnShow](cListboxHScroll/UnShow) -- removes the window subclass installed by Show + +## Test cases + +The notes and warnings of this section are checked by the test cases in [TBMANLIB_ListBoxHScroll-tests.twinproj](TBMANLIB_ListBoxHScroll-tests.twinproj), a twinBASIC project. It uses TBMANLIB_ListBoxHScroll as a [linked package](../../../../Features/Packages/Linked#manual-management): put the package's `.twinpack` in `%APPDATA%\twinBASIC\packages`, then run the project, and each case prints whether it passes. diff --git a/docs/Reference/Contributed/TBMANLIB_RES/Base64Prefix.md b/docs/Reference/Contributed/TBMANLIB_RES/Base64Prefix.md index 5dd15845..a1d8cba1 100644 --- a/docs/Reference/Contributed/TBMANLIB_RES/Base64Prefix.md +++ b/docs/Reference/Contributed/TBMANLIB_RES/Base64Prefix.md @@ -9,6 +9,7 @@ has_toc: false Specifies whether, and with what MIME type, a data URI prefix is prepended to the base64 string returned by [**ReturnBase64**](cRes/#returnbase64). + | Constant | Value | Description | |----------|-------|-------------| | **Base64Prefix_None**{: #Base64Prefix_None } | 0 | No prefix. The returned string is raw base64 with no leading data URI scheme. | @@ -24,6 +25,7 @@ Specifies whether, and with what MIME type, a data URI prefix is prepended to th | **Base64Prefix_AudioMP3**{: #Base64Prefix_AudioMP3 } | 10 | Prepends `data:audio/mpeg;base64,`. | | **Base64Prefix_AudioWAV**{: #Base64Prefix_AudioWAV } | 11 | Prepends `data:audio/wav;base64,`. | | **Base64Prefix_AudioOGG**{: #Base64Prefix_AudioOGG } | 12 | Prepends `data:audio/ogg;base64,`. | + The default value used by **ReturnBase64** when *Prefix* is omitted is **Base64Prefix_None**. diff --git a/docs/Reference/Contributed/TBMANLIB_RES/GLOBAL_RES.md b/docs/Reference/Contributed/TBMANLIB_RES/GLOBAL_RES.md index 47f5a126..379db3b0 100644 --- a/docs/Reference/Contributed/TBMANLIB_RES/GLOBAL_RES.md +++ b/docs/Reference/Contributed/TBMANLIB_RES/GLOBAL_RES.md @@ -20,8 +20,11 @@ A pre-instantiated **cRes** object. Public Res As New cRes ``` + Because of **As New**, the instance is created automatically the first time **Res** is referenced. It provides the same interface as a manually constructed **cRes** instance: [**Read**](cRes/Read), [**ReturnBytes**](cRes/ReturnBytes), [**ReturnString**](cRes/ReturnString), [**ReturnHex**](cRes/ReturnHex), [**ReturnBase64**](cRes/ReturnBase64), and [**ReturnPicture**](cRes/ReturnPicture). + + > [!NOTE] timeless > **GLOBAL_RES.Res** is a single shared instance for the entire project. Calling **Res.Read** replaces its internal byte buffer. If multiple callers read different resources in the same execution path, use separate **cRes** instances rather than sharing **GLOBAL_RES.Res**. diff --git a/docs/Reference/Contributed/TBMANLIB_RES/cRes/Read.md b/docs/Reference/Contributed/TBMANLIB_RES/cRes/Read.md index dc1fe863..e10168e2 100644 --- a/docs/Reference/Contributed/TBMANLIB_RES/cRes/Read.md +++ b/docs/Reference/Contributed/TBMANLIB_RES/cRes/Read.md @@ -11,17 +11,24 @@ Loads a resource from the compiled executable into the **cRes** instance. Syntax: *object*.**Read**( *id*, *Type* ) **As cRes** + *id* : *required* A **Variant** identifying the resource to load. Pass a numeric **Long** to identify the resource by its integer ID, or a **String** to identify it by name. *Type* : *required* A **Variant** identifying the resource type. Pass a numeric **Long** for a predefined resource type (for example, `2` for `RT_BITMAP`), or a **String** for a named custom resource type (for example, `"CUSTOM"`). + + **Read** calls the **LoadResData** function with the supplied *id* and *Type* arguments and stores the resulting byte array internally. The method returns the **cRes** instance itself, so calls to the retrieval methods ([**ReturnBytes**](ReturnBytes), [**ReturnString**](ReturnString), [**ReturnHex**](ReturnHex), [**ReturnBase64**](ReturnBase64), [**ReturnPicture**](ReturnPicture)) can be chained directly onto the **Read** call. + + > [!IMPORTANT] timeless > **Read** must be called before any retrieval method. The resource data is not populated at construction time; a retrieval method called without a prior **Read** will operate on an empty byte array and may raise a runtime error. + + > [!NOTE] 1.0.0.4 > **Read** can load only a resource that is part of the project. *Type* names a folder under **Resources** in the Project Explorer (or is the number of a standard resource type), and *id* identifies a resource in it. If no resource matches, **LoadResData** raises a run-time error. diff --git a/docs/Reference/Contributed/TBMANLIB_RES/cRes/ReturnBase64.md b/docs/Reference/Contributed/TBMANLIB_RES/cRes/ReturnBase64.md index e9a1f62d..94fc5265 100644 --- a/docs/Reference/Contributed/TBMANLIB_RES/cRes/ReturnBase64.md +++ b/docs/Reference/Contributed/TBMANLIB_RES/cRes/ReturnBase64.md @@ -11,10 +11,17 @@ Returns the resource bytes as a Base64-encoded string, with an optional MIME-typ Syntax: *object*.**ReturnBase64** ( [ *Prefix* ] ) **As String** + *Prefix* : *optional* A **Base64Prefix** enumeration value that selects a data-URI prefix to prepend to the encoded string. Defaults to **Base64Prefix_None**, which returns the raw Base64 string with no prefix. + -**ReturnBase64** encodes the byte array held by the **cRes** instance using MSXML2's `bin.base64` data type and returns the result as a **String**. MSXML puts a line feed (`vbLf`) after every 72 characters of Base64, and **ReturnBase64** does not remove them. When *Prefix* is any value other than **Base64Prefix_None**, the corresponding `data:;base64,` header is prepended, producing a data URI that can be used in an ``, `