{"id":21070823,"url":"https://github.com/fafalone/limitinputedit","last_synced_at":"2025-09-19T06:42:32.940Z","repository":{"id":212342322,"uuid":"731261045","full_name":"fafalone/LimitInputEdit","owner":"fafalone","description":"SHLimitInputWithFlags Demo","archived":false,"fork":false,"pushed_at":"2024-03-03T16:05:36.000Z","size":83,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-03T20:43:13.965Z","etag":null,"topics":["shell","twinbasic","undocumented-api","vb6","vba","vba7","windows","windows-shell"],"latest_commit_sha":null,"homepage":"","language":"Visual Basic 6.0","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fafalone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-13T17:24:42.000Z","updated_at":"2024-02-08T07:11:33.000Z","dependencies_parsed_at":"2024-11-19T18:49:08.379Z","dependency_job_id":"726a9feb-9462-4bb9-9f21-0ca5d54e8e97","html_url":"https://github.com/fafalone/LimitInputEdit","commit_stats":null,"previous_names":["fafalone/limitinputedit"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FLimitInputEdit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FLimitInputEdit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FLimitInputEdit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fafalone%2FLimitInputEdit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fafalone","download_url":"https://codeload.github.com/fafalone/LimitInputEdit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254473966,"owners_count":22077201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["shell","twinbasic","undocumented-api","vb6","vba","vba7","windows","windows-shell"],"created_at":"2024-11-19T18:48:29.600Z","updated_at":"2025-09-19T06:42:27.853Z","avatar_url":"https://github.com/fafalone.png","language":"Visual Basic 6.0","readme":"# LimitInputEdit\n## SHLimitInputWithFlags Demo\n### Easily apply category filters, paste handling, and automated tooltips to an edit control.\n\n![image](https://github.com/fafalone/LimitInputEdit/assets/7834493/33f2b7ba-dd27-460e-b8e1-a94ba649750b)\n\n---\n\nThis is an updated, x64 compatible version of my VB6 demo of the shell32.dll API `SHLimitInputEditWithFlags`, an API entirely undocumented either by Microsoft or 3rd parties until my project. There are three versions of the project in this repository, LimitInputEditWithFlags.twinproj, a full twinBASIC version that uses `Return` and `Handles` syntax etc, and a universal compatibility version (VB6, VBA6, VBA7 32bit, VBA7 64bit, twinBASIC 32bit, twinBASIC 64bit) in both VB6 and twinBASIC form with identical code.\n\n \nOriginal description:\n\n\nMicrosoft being Microsoft, they only begrudgingly documented a function called `SHLimitInputEdit` for the DOJ settlement, and did so poorly. This is a weird function; it takes an edit hwnd, and an object that implements `IShellFolder` and `IItemNameLimits`. The former doesn't even matter (unless it's been implemented in newer versions of Windows; I haven't checked). When you implement `IItemNameLimits`, you get a single call to `GetValidCharacters`, where you can supply a string of either included or excluded characters (only 1 can be used, so if you specify any excluded characters, included becomes null). It's an odd way of doing things.\n\nBut it turns out, that's a front end for an actually much more interesting and useful but completely undocumented, `SHLimitInputEditWithFlags`, an API Geoff Chappell found as exported at ordinal #754 in shell32.dll (it's still ordinal only in Windows 10, even though it's been kicking around since Windows XP).\n\nThis function allows a wide variety of options for limits and a tooltip that pops up upon bad input. Instead of just being able to specify an exact string, you can use `CT_TYPE1` categories, which in addition to the standard upper, lower, digits... has some handy options like categories for hexadecimal, punctuation, or control characters. It also implements custom categories; binary, octal, and ASCII a-z/A-Z. It also provides control over a tooltip that pops up when you attempt to enter a bad character-- you can have no tooltip, or specify the title, message, and icon (a `TTI_*` default icon or custom hIcon), and set alignment, width, and timeout (including timing out immediately if a valid input is received). It also handles pasting in several different ways; filtering in the valid chars, pasting until the 1st invalid char, or canceling the paste. If the paste is modified, it puts what was pasted on the clipboard (optionally). The pasting options and automatic control over the tooltip is what really makes this worthwhile over just manually checking KeyPress events or `WM_CHAR` messages.\n\n**Requirements**\\\n-No dependencies.\\\n-Function present on Windows XP through at least Windows 10 (I haven't checked 11).\n\n```vb6\n#If VBA7 Then\nPublic Declare PtrSafe Function SHLimitInputEditWithFlags Lib \"shell32\" Alias \"#754\" (ByVal hwndEdit As LongPtr, pil As LIMITINPUTSTRUCT) As Long\n'...\n#Else\nPublic Declare Function SHLimitInputEditWithFlags Lib \"shell32\" Alias \"#754\" (ByVal hwndEdit As Long, pil As LIMITINPUTSTRUCT) As Long\n'...\n```\n\nOn all Windows versions, this function is exported by ordinal only, so you can't remove the Alias. `SHLimitInputEditWithFlags` takes two arguments, an hWnd for an edit control, and an (until this post) undocumented structure. Here's the members and a description:\n\n```vb6\nPublic Type LIMITINPUT\n    cbSize As Long          'Size of structure. Must set.\n    dwMask As LI_Mask       'LIM_* values.\n    dwFlags As LI_Flags     'LIF_* values.\n    hInst As LongPtr        'App.hInstance or loaded module hInstance.\n    pszFilter As LongPtr    'String via StrPtr, LICF_* category, LPSTR_TEXTCALLBACK to set via LIN_GETDISPINFO, or resource id in .hInst.\n    pszTitle As LongPtr     'Optional. String via StrPtr, LPSTR_TEXTCALLBACK to set via LIN_GETDISPINFO, or resource id in .hInst.\n    pszMessage As LongPtr   'Ignore if tooltip disabled. String via StrPtr, LPSTR_TEXTCALLBACK to set via LIN_GETDISPINFO, or resource id in .hInst.\n    hIcon As LongPtr        'See TTM_SETTITLE. Can be TTI_* default icon, hIcon, or I_ICONCALLBACK to set via LIN_GETDISPINFO.\n    hwndNotify As LongPtr   'Window to send notifications to. Must specify if any callbacks used or bad character notifications enabled.\n    iTimeout As Long        'Timeout in milliseconds. Defaults to 10000 if not set.\n    cxTipWidth As Long      'Tooltip width. Default 500px.\nEnd Type\n```\n\n`dwMask` is just a list of which of the remaining members should be used:\n\n```vb6\n'Values for LIMITINPUT.dwMask\nPublic Enum LI_Mask\n    LIM_FLAGS = \u0026H1      'dwFlags used\n    LIM_FILTER = \u0026H2     'pszFilter used\n    LIM_HINST = \u0026H8      'hinst contains valid data. Generally must be set.\n    LIM_TITLE = \u0026H10     'pszTitle used. Tooltip title.\n    LIM_MESSAGE = \u0026H20   'pszMessage used. Tooltip main message.\n    LIM_ICON = \u0026H40      'hicon used. Can use default icons e.g. IDI_HAND. Loaded from .hInst.\n    LIM_NOTIFY = \u0026H80    'hwndNotify used. NOTE: Must be set to receive notifications. Automatic finding of parent broken.\n    LIM_TIMEOUT = \u0026H100  'iTimeout used. Default timeout=10000.\n    LIM_TIPWIDTH = \u0026H200 'cxTipWidth used. Default 500px.\nEnd Enum\n```\n\nNow we'll get into the core of it with the flags for `dwFlags`:\n\n```vb6\n'Values for LIMITINPUT.dwFlags\nPublic Enum LI_Flags\n    LIF_INCLUDEFILTER = \u0026H0     'Default: pszFilter specifies what to include.\n    LIF_EXCLUDEFILTER = \u0026H1     'pszFilter specifies what to exclude.\n    LIF_CATEGORYFILTER = \u0026H2    'pszFilter uses LICF_* categories, not a string of chars.\n\n    LIF_WARNINGBELOW = \u0026H0      'Default: Tooltip below.\n    LIF_WARNINGABOVE = \u0026H4      'Tooltip above.\n    LIF_WARNINGCENTERED = \u0026H8   'Tooltip centered.\n    LIF_WARNINGOFF = \u0026H10       'Disable tooltip.\n\n    LIF_FORCEUPPERCASE = \u0026H20   'Makes chars uppercase.\n    LIF_FORCELOWERCASE = \u0026H40   'Makes chars lowercase. (This and forceupper mutually exclusive)\n\n    LIF_MESSAGEBEEP = \u0026H0       'Default: System default beep played.\n    LIF_SILENT = \u0026H80           'No beep.\n\n    LIF_NOTIFYONBADCHAR = \u0026H100 'Send WM_NOTIFY LIN_NOTIFYBADCHAR. NOTE: Must set LIM_NOTIFY flag and .hwndNotify member.\n    LIF_HIDETIPONVALID = \u0026H200  'Timeout tooltip early if valid char entered.\n\n    LIF_PASTESKIP = \u0026H0         'Default: Paste any allowed characters, skip disallowed.\n    LIF_PASTESTOP = \u0026H400       'Paste until first disallowed character encountered.\n    LIF_PASTECANCEL = \u0026H800     'Cancel paste entirely if any disallowed character.\n\n    LIF_KEEPCLIPBOARD = \u0026H1000  'If not set, modifies clipboard to what was pasted after paste flags executed.\nEnd Enum\n```\nIf you do not use the `LIF_CATEGORYFILTER` flag, the `.pszFilter` member must be set to `StrPtr(value)` where value is a non-delimited string of which characters to allow (by default) or disallow (if `LIF_EXCLUDEFILTER` flag is included). If you do use the flag, the following categories are valid:\n\n```vb6\n'Filters support CT_TYPE1 categories:\nPublic Const LICF_UPPER = \u0026H1\nPublic Const LICF_LOWER = \u0026H2\nPublic Const LICF_DIGIT = \u0026H4\nPublic Const LICF_SPACE = \u0026H8\nPublic Const LICF_PUNCT = \u0026H10  'Punctuation\nPublic Const LICF_CNTRL = \u0026H20  'Control characters\nPublic Const LICF_BLANK = \u0026H40\nPublic Const LICF_XDIGIT = \u0026H80  'Hexadecimal values, 0-9 and A-F.\nPublic Const LICF_ALPHA = \u0026H100  'Any CT_TYPE1 linguistic character. Includes non-Latin alphabets.\n'Custom categories\nPublic Const LICF_BINARYDIGIT = \u0026H10000\nPublic Const LICF_OCTALDIGIT = \u0026H20000 'Base 8; 0-7.\nPublic Const LICF_ATOZUPPER = \u0026H100000 'ASCII A to Z\nPublic Const LICF_ATOZLOWER = \u0026H200000 'ASCII a to z\nPublic Const LICF_ATOZ = (LICF_ATOZUPPER Or LICF_ATOZLOWER)\n```\n\nFrom there, you're all set to apply basic input limits to an edit control. Remember, if you don't want a tooltip you don't need to set the title, message, and icon, but in that case you must include the LIF_WARNINGOFF flag, or the function will fail. If you are going to have a tooltip, you must as a minimum specify the message.\n\n### Advanced\n\nThere's a couple flags for advanced options. `LIF_NOTIFYONBADCHAR` will send hWnd specified by the .hwndNotify member a `LIN_BADCHAR` notification code in a `WM_NOTIFY` message. You must subclass the specified hWnd to receive the message (on Windows 10, it will not automatically send them to the parent, but directly to the provided hWnd. That automatic behavior may work on earlier versions, but manually specifying it works on all). From there it has it's own NM structure to copy:\n\n```vb6\nPrivate Type NMLIBADCHAR\n    hdr As NMHDR\n    wParam As LongPtr 'WM_CHAR wParam (Char code)\n    lParam As LongPtr 'WM_CHAR lParam (see MSDN for details)\nEnd Type\n```\n\nThat gives you the WM_CHAR message.\n\nThere's also special handling for WM_PASTE operations built in. The default behavior is to paste whatever characters from the clipboard are allowed, then set the contents of the clipboard to the filtered result. You can change that behavior to only pasting up until the first disallowed character with the `LIF_PASTESTOP` flag, or to cancel the paste entirely with `LIF_PASTECANCEL`.\n\n### Callbacks\n\nI didn't implement this option in the demo because I don't see a lot of utility for it, but you can specify `LPSTR_TEXTCALLBACK` for the text fields, and `I_ICONCALLBACK` for the icon field, and the control will send a `LIN_GETDISPINFO` message for the tooltip text and `LIN_GETFILTERINFO` for the filter. I'm not going to detail it, but it works exactly like `LVN_GETDISPINFO` callbacks for the ListView control, and there's plenty of documentation for that. The constants and structure are included in the Demo if you did want to explore this.\n\n### Sample Project\n\nThe demo pictured at the top of this post implements a wide array of features, including subclassing for the bad character notifications, but also includes a simple 'Set to numbers only' to show how simple calls to this function can be:\n\n```vb6\nDim tli As LIMITINPUT\ntli.cbSize = LenB(tli)\ntli.dwMask = LIM_FILTER Or LIM_FLAGS\ntli.dwFlags = LIF_CATEGORYFILTER Or LIF_WARNINGOFF\ntli.pszFilter = LICF_DIGIT\n\nSHLimitInputEditWithFlags Text1.hWnd, tli\n```\n\nThat's all you need to do to have a textbox take only numbers, with no tooltip.\n\nAnd that's it! Enjoy this undocumented treasure from the Windows API.\n\n---\n\n**Thanks:** Thanks ToddB, it is super cool and should have been a documented tool the world knew\n\n\u003e[!IMPORTANT]\n\u003eThis is an undocumented, internal API, with all the issues that involves. There may be small variations in functionality between Windows versions, stability is not guaranteed, and it may be removed at any time from future versions, or have it's ordinal changed.\n\n**UPDATE**\\\n02 Jul 2023: UndocEditLimit-R2.zip (Revision 2) corrects an odd bug where the custom filters weren't working because despite being declared as a Unicode string type (LPWSTR), the API handled it as an ANSI string, and thus is was necessary to convert first.\\\n13 Dec 2023: Project updated for x64 compatibility and re-released as a universal compatibility version.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffafalone%2Flimitinputedit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffafalone%2Flimitinputedit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffafalone%2Flimitinputedit/lists"}