Clarion · Unicode Preview

USTRING — Unicode that feels like Clarion

Wide text end to end: declare it, type it, store it, browse it. No new programming model. No special “Unicode mode.” If you already know CSTRING, you already know USTRING — and your existing ANSI apps stay byte-identical.

SQL MSSQL · ODBC · PostgreSQL · SQLite ISAM TopSpeedW · ASCII · BASIC · DOS UI ENTRY · TEXT · COMBO · captions · blobs · reports Contract UTF-16 code units · logical length

Hands-off by design

Pick USTRING in the DCT, USE it on a control, PUT it to SQL. No conversion buffers, no “wide mode” flag, no per-screen ceremony. It just works the way Clarion always did.

Σ

All SQL backends speak Unicode

MSSQL, ODBC, PostgreSQL, and SQLite: USTRING(n) ↔ NVARCHAR(n-1) (logical length round-trips). Keys, GET, SET/NEXT, and bound WHERE values stay wide — never narrowed literals.

TopSpeed goes wide

Classic TPS is untouched. New driver: DRIVER('TopSpeedW'), .tpsw files, USTRING fields + UNICODE blobs, keys that work, and clean version rejects instead of silent mojibake.

A blob is a field

On SQL there is no MEMO(n) — long text is the blob. TEXT,USE(blob) displays and edits; IMAGE,USE(blob) displays. Window binding ships today; Report is the same mental model (next surface).

Ω

Reports print wide

Add UNICODE to a REPORT and wide text prints end to end — preview, print-after-preview, direct print. A report left un-opted posts a loud error 546 on wide data — never silent mojibake.

Type your language — everywhere

Source (UTF-8 / UTF-16), Window/Report designers, DCT messages/tips, IME, emoji panel, clipboard. Window titles, menus, POPUP(), LIST columns and headings, captions and free-form ENTRY/TEXT/COMBO carry exact UTF-16 on any system codepage.

ANSI apps stay byte-identical

No USTRING? No wide designer text? Rebuild and ship — same binary behavior as before. Opt-in tokens (e.g. PostgreSQL UNICODECONNECT=1) never change default connects.

Import maps that tell the truth

Import Tables: nvarchar → USTRING, ntext / nvarchar(max) → UNICODE memo/blob, binary max → BINARY blob — attributes already ticked. Data Browser edits wide without narrowing.

Surrogate-safe editing

Emoji and non-BMP text edit as atomic characters on wide ENTRY/COMBO. Color emoji via DirectWrite where available; ordinary edits never leave half a pair behind.

Everything below is implemented and expected to work as described unless marked as a known limitation. If you observe different behavior, that is exactly what we want to hear about. Sections marked not yet supported are not bugs.
mojibake

Garbled text from an encoding mismatch — e.g. UTF-8 bytes read as Windows-1252. Classic example: café instead of café.

narrowing

Converting wide (UTF-16) text down to ANSI/8-bit. Characters with no mapping in the target code page are lost or replaced (often ?).

lossy vs lossless

Lossless round-trips exactly. Lossy trades exactness for compatibility. Keep data in USTRING end to end when it must survive.

What's new in this refresh build-14313 (since build 14258)

If you tested the previous build, these are the changes to look at first. Most items trace back to a beta field report or a request on the beta forum — that is exactly what the beta is for; keep them coming.

Reports and the report previewer

DATETIME — a date-time type for SQL

Language and string functions

The IDE

Help

1. What USTRING is

USTRING declares a string of wide characters — 16-bit UTF-16 code units, the native text form of Windows. A USTRING value is terminated by a null wide character; the value is self-describing (no space padding).

Name   USTRING(24)          ! room for 24 wide characters
Alias  &USTRING             ! reference to a USTRING

USTRING(n) reserves n wide characters including the terminator — so it holds up to n-1 characters of data (the CSTRING rule). SIZE() returns bytes (n * 2); LEN() returns the number of wide characters before the terminator.

USTRING can be initialized and assigned from both Unicode and ANSI sources. Any string or numeric expression may appear on the right side of an assignment to a USTRING — exactly the same as with STRING / CSTRING / PSTRING.

2. The one mental model: UTF-16 code units

String positions, lengths, and limits for USTRING count UTF-16 code units — the same model Windows and the C wide-character library use. A code unit is not always a whole character on screen:

On screenUTF-16 units / LEN()
A, α, 1
😀 (emoji)2 (surrogate pair)
👨‍👩‍👧 (joined family)8
🇺🇸 (flag)4

This is the same contract ANSI LEN always had (bytes under DBCS, not “visible characters”). Practical consequences:

BMP vs non-BMP: where the second unit comes from

Unicode assigns every character a number called a code point (A is U+0041); a UTF-16 code unit is one 16-bit storage element. The two differ only outside the BMP (Basic Multilingual Plane) — Unicode's original and most commonly used 65,536-code-point range:

CharacterCode pointBMP?UTF-16 storage
AU+0041Yes1 unit / 2 bytes
éU+00E9Yes1 unit / 2 bytes
U+4E2DYes1 unit / 2 bytes
U+20ACYes1 unit / 2 bytes
U+2603Yes1 unit / 2 bytes
😀U+1F600No2 units / 4 bytes
𐐀 (Deseret)U+10400No2 units / 4 bytes
𠀀 (CJK Ext B)U+20000No2 units / 4 bytes
Text:                A😀B
Unicode code points: U+0041  U+1F600   U+0042
UTF-16 code units:   0041    D83D DE00 0042

Three characters, four code units — LEN() returns 4.

Do not think of BMP as “normal characters” and non-BMP as “emoji”: some emoji are BMP and take a single unit (, ), and many non-BMP characters are not emoji at all (historic scripts, mathematical alphabets, supplementary CJK ideographs). Everyday Latin text, punctuation, currency symbols and the common CJK range are all BMP. And separately from surrogate pairs, one glyph on screen can be built from several code points — the joined family in the table above is five code points (man, joiner, woman, joiner, girl) totaling eight units; a flag is two code points / four units.

The rule to test by: never assume one 16-bit element is one character. Code that indexes, truncates, copies, or measures UTF-16 text must keep surrogate pairs together, and a round trip must preserve both halves of every pair — no replacement characters, no truncation, no split pairs.

3. Declaration, assignment, conversion

4. Comparison and collation

Selecting codepage and locale

Recommended: PROP:Codepage + PROP:Locale — three equivalent ways:

SYSTEM{PROP:Codepage} = 1253        ! preferred; also readable back
SYSTEM{PROP:Locale}   = 1032
LOCALE('CLACODEPAGE','1253')
LOCALE('CLALCID','1032')
! or in the auto-loaded <exe>.ENV file:
!   CLACODEPAGE=1253
!   CLALCID=1032

Named values work: 'GREEK', 'WESTERN', 'UTF8' for codepage; 'EN-US', 'DE-DE' for LCID. Empty value resets to system defaults. A flip is process-wide and immediate for new conversions/comparisons. Values converted earlier keep their bytes. Legacy CLACASE/CLACOLSEQ/CLADIGRAPH still work — last writer wins.

Explicit conversion: TOANSI() and TOUNICODE()

A = TOANSI(expr [, cp])      ! result is ANSI, encoded at cp
U = TOUNICODE(expr [, cp])   ! result is wide; cp says how to READ ANSI input

Omitted cp = current PROP:Codepage. UTF-8 works as an explicit codepage (65001 / 0FDE9h):

U8 = TOANSI(U, 0FDE9h)            ! USTRING -> UTF-8 byte string
U  = TOUNICODE(U8, 0FDE9h)        ! UTF-8 byte string -> USTRING

5. Typing your language into source, designers, and the DCT

Source files

The compiler reads .clw/.inc in mainstream Unicode formats — write U'Κλόουζ', mixed-script literals, or emoji directly:

The IDE source editor

Exactly two encodings (Tools ▸ Options): Clarion ANSI (default — existing files round-trip byte-identical) and Clarion Unicode (UTF-8 with BOM). The option is the default for new files only; the status bar shows the current file’s encoding. Type a non-codepage character into an ANSI file and the editor offers to switch to UTF-8 at save. The editor never writes a file the compiler cannot read.

Window / Report designers

Type your language into captions, titles, messages, tooltips and report strings — nothing to enable. Save, reopen, generate: exact round-trip and correct draw on any system codepage. Internally, non-ANSI designer strings travel as readable ASCII U'...' metachar form (e.g. BUTTON(U'B<233,20013,937>') for Bé中Ω). Apps without wide designer text stay byte-identical. Older IDE builds cannot open an .app that uses wide designer text.

Dictionary editor

Every user-text field property (Message, ToolTip, Prompt, Column Heading, Description, Initial Value, Choices/Values, True/False) accepts Unicode and round-trips into the running app. The .dct format is unchanged. ANSI-only dictionaries are byte-identical.

Known limitation — embeds

Generated code from the native template engine is always ANSI. Non-ANSI text typed into an embed cannot survive as raw characters — write U'...' with unit metachars, or put the code in a UTF-8 INCLUDE file.

TXA export/import in UTF-8 (beta refresh after 08-21-2026)

A TXA is ANSI text in the system codepage of the machine that wrote it — it never carried an encoding marker, so regional characters (é, Ω, Cyrillic…) were ambiguous on any other machine, and editing one in a UTF-8 editor was a mojibake trap. Two changes, both in the app generator only:

The boundary: the .app stores text in the system codepage, so an imported TXA may only contain characters that codepage can represent. If an edit introduces something outside it, the import stops with an error naming the line rather than silently mangling text. Real Unicode in string literals and designer captions still travels as the U'...' metachar form described above — this is an encoding lane for the file, not a change to what the TXA contains.

Fixed alongside: a prompt value containing a literal < followed by digits (e.g. x<10>y) used to export unescaped, and the next import turned <10> into a control character. It now re-escapes ('x<<10>y') and export/import cycles are stable.

6. USTRING as a dictionary column type

Pick USTRING from the field-type list (driver-admitted — only drivers that support wide columns), set Characters. USTRING(n) = n-1 data characters, 2n bytes on the record. Generation emits it verbatim. The DCT explorer tree shows the size (USTRING(21)).

Importing from a live SQL schema

Server columnImports as
nvarchar(m) / nchar(m)USTRING(m+1)
varchar(m)CSTRING(m+1)
char(m)STRING(m)
nvarchar(max) / ntextMEMO/BLOB + UNICODE
textMEMO/BLOB (narrow, by design)
image / varbinary(max) / PG byteaMEMO/BLOB + BINARY
binary(m) / varbinary(m)STRING(m) (fixed-width)
bigint / PG int8DECIMAL(19,0) (lossless)
uniqueidentifierCSTRING(37) (+READONLY when server-filled)

DDL: MSSQL script generator emits NVARCHAR(n-1) for USTRING(n), NVARCHAR(MAX) past the 4000-unit cap (you run the script yourself). Round trips: USTRING survives DCTX, TXD and TXA export/import (the TXA optionally in UTF-8 — see §5); app-pool variables accept it.

7. Pictures, FORMAT / DEFORMAT

FORMAT/DEFORMAT and picture tokens accept USTRING values and wide runtime picture strings. USTRING(@pic) sizes as picture width plus terminator and formats on store. One deliberate difference: a pictured USTRING QUEUE field sorts by raw stored text, while a pictured ANSI field sorts by the deformatted value — sort wide date fields on a LONG key for chronological order.

Pictures on controls carry Unicode too (beta refresh after 08-23-2026): a picture whose literal text contains characters outside the codepage — a currency symbol like @n~₿~-15.2, a wide date separator — can be set on a LIST column ({PROPLIST:Picture}) or on a pictured display STRING control and formats every value with the real symbol; reading the picture back returns it intact. Previously the picture was stored narrow before the (already wide) formatter ever saw it, so anything outside the codepage became ?. Pure-ANSI pictures take exactly the old path. The pictured-ENTRY editor is the one surface that still presents ANSI (see §8, "known limitations"). (Beta refresh after 08-27-2026): the same holds for a picture written in the LIST's own FORMAT() string (FORMAT('…@n~₿~-15.2@…')) — before, only a picture set at runtime kept its symbol.

8. Screen controls — what to expect

Fully Unicode surfaces

  • Free-form ENTRY (@Sn or no picture) + USTRING USE: IME, emoji panel, clipboard — exact UTF-16. Limits in units. {PROP:ScreenText} is wide. (Beta refresh after 08-26-2026: keystrokes that carry UTF-16 — KEYEVENTF_UNICODE/VK_PACKET injectors, the emoji panel on Windows builds that type rather than insert, IME-composed CJK — also arrive exact: the message pump is Unicode. Earlier builds best-fit those keystrokes to O??? even into a USTRING entry. ANSI-bound controls receive the bytes they always did; KEYCHAR() keeps its ANSI value.)
  • LIST cells paint exact wide text across all lanes. Direct wide-QUEUE-field and value-list-box (VLB) columns have been wide since early builds; beta refresh after 08-16-2026 closed the last two narrow surfaces — split columns (one queue field fanned across cells with |) and column headings now draw real glyphs, not best-fit. (FROM(ustring) as LIST source is not accepted — use a wide QUEUE.) Residual: a heading baked into the FORMAT() literal can still narrow — set it at runtime for the wide path; queued. A runtime-set wide heading also now survives a later reformat of the LIST (e.g. after a column-picture change) — in earlier beta builds the reformat wrecked it (beta refresh after 08-23-2026). Column pictures with non-codepage symbols work too (see §7).
  • Window / frame / MDI titles and menu text (beta refresh after 08-16-2026): the caption (designer or runtime {PROP:Text}) and MENU/ITEM text carry exact UTF-16, any codepage, themed or classic; reading a caption back is wide. Runtime writes to the status bar ({PROP:StatusText}), message zone and tips are wide too. (These were previously listed as ANSI limitations — this refresh changed that.)
  • Runtime-bound USE goes wide (beta refresh after 08-16-2026): binding an ENTRY or TEXT to a USTRING at runtime via {PROP:Use} now switches the control onto the wide class on the spot (and back for a STRING) — earlier builds widened only for a compile-time-declared wide USE. This is what makes TopScan's in-cell editor go wide with no tool changes.
  • COMBO free-form: wide entry, type-to-locate, exact row store.
  • DROP lists: selection and closed face paint exact wide values.
  • TEXT (multi-line or SINGLE): exact UTF-16 including line breaks; CONTENTS() / ScreenText / Line,n are wide.
  • Rich text — TEXT,...,RTF + USTRING USE (beta refresh after 08-09-2026): displays, edits and saves back wide. The variable follows the content: plain text stays plain (exact UTF-16); rich content saves as RTF source text — 7-bit, non-ANSI riding standard RTF escapes (\uN / charset byte escapes), lossless round trip and portable to Word/WordPad, emoji included. Emoji are stored exactly (pairs never tear) but the hosted RichEdit draws them monochrome — the plain-TEXT OS floor. RTF(TEXT:FILE): file content carries Unicode via escapes; a non-ANSI filename does not resolve (ANSI lane — known limitation); reading the control back through {PROP:Text}, {PROP:Line} or the RTF templates' GetText returns best-fit ANSI text — the variable itself keeps the units (known limitation). Pre-refresh, this binding silently showed empty and discarded edits; stored values were never damaged.
  • Captions: BUTTON, PROMPT, STRING, CHECK, RADIO, OPTION, GROUP, SHEET TABs, MSG/TIP/HLP — exact draw on any codepage, themed or classic. Runtime rewrites are wide too (beta refresh after 08-19-2026): {PROP:Text} = <wide value> on any of these families keeps exact units — previously a runtime caption rewrite could narrow (it was on the known-limitation list).
  • POPUP() menus (beta refresh after 08-23-2026): the menu text draws its real glyphs — currency signs, CJK, emoji (monochrome or color per the same rendering lane as elsewhere). The item structure (|, {} sub-menus, ~, +/-, [...]) is decided on the wide text itself, so a full-width (U+FF5C) inside an item no longer splits the menu the way its best-fit | once did. Return values, separators, grayed/checked items and sub-menus are unchanged; an all-ANSI POPUP takes exactly the old code path.
  • Color emoji on display surfaces and wide ENTRY/COMBO (DirectWrite; monochrome fallback). Surrogate pairs edit atomically. Desktop Windows has no flag glyphs — flags show as letter pairs (US). (Beta refresh after 08-09-2026: a color emoji at the right edge of an ENTRY was clipped in earlier beta builds — fixed.)

ANSI presentation (value safe)

Pictured ENTRY (@N/@D/@T...), SPIN, report-hosted TEXT: the wide value is never corrupted by display — presentation may show ? for ☃, but the variable keeps exact text. If the user edits, the stored result is the narrowed edited text (they edited the ANSI presentation).

Still ANSI-presented

This list keeps shrinking — this refresh moved runtime caption rewrites, POPUP menus and control pictures to the wide surfaces (the previous one moved window titles, menu text, the status bar, LIST split columns/headings and runtime-bound USE). What remains: the pictured-ENTRY editor — an ENTRY(@pic) edits through the ANSI presentation, including a picture whose literals carry wide symbols (ENTRY(@n~₿~...) shows ? while editing); the in-place editor owns caret movement, digit slots and literal skipping per character, so its wide form is a deliberate separate step — queued (free-form ENTRY(@s..) is fully wide); setting a wide picture at runtime on ENTRY/SPIN/COMBO via {PROP:Text} can still narrow (the display-control and LIST-column picture lanes are the wide ones — §7), and a wide caption write before OPEN(window) best-fits — queued; a heading baked into a FORMAT() literal and and lookup-key / name arguments (an icon name, a non-ANSI filename in the remaining ANSI file-name lanes — DIRECTORY() into a FILE:Queue, RUN) which stay ANSI by nature. Values always survive.

(REPORT printing is no longer on this list — see Reports below. MESSAGE() is no longer on this list either — beta refresh after 08-08-2026: the text, caption and button captions display wide, including custom |-list button captions and the copyable CANCOPY form; astral emoji render via surrogate pairs. The icon name argument is a lookup key and stays ANSI — a non-ANSI icon filename does not resolve. Plain ANSI MESSAGE calls are unchanged.)

BSTRING is COM marshaling, not an entry type — stage input in USTRING and convert at the COM boundary.

Reports — the REPORT UNICODE attribute

Add UNICODE to a REPORT statement and the report prints wide text end to end — USTRING fields, wide captions, TEXT, LIST cells, string tallies, page numbers — through preview, print-after-preview, and direct printing:

Rpt REPORT,AT(500,500,7000,4000),FONT('Arial',10),PREVIEW(PgQ),THOUS,UNICODE

Things to know and test:

9. BLOB-bound controls — a blob is a field

On a SQL backend there is no MEMO(n): long text and binary content are both the BLOB surface, and a FILE BLOB is a first-class USE variable:

FT     FILE,DRIVER('MSSQL','...'),OWNER('...'),NAME('dbo.Notes'),PRE(NT)
PK       KEY(NT:Id),PRIMARY
Body     BLOB,NAME('Body'),UNICODE     ! nvarchar(max) - wide long text
Photo    BLOB,NAME('Photo'),BINARY     ! varbinary(max) - image bytes
Rec      RECORD
Id         LONG,NAME('Id')
         END
       END

W  WINDOW('Note'),AT(,,300,200)
     TEXT,AT(4,4,200,120),USE(NT:Body),HVSCROLL   ! displays AND edits
     IMAGE,AT(210,4,80,60),USE(NT:Photo)          ! displays, read-only
   END
NT:Photo{PROP:Size} = LEN(bmpBytes)     ! size FIRST
NT:Photo[0 : LEN(bmpBytes) - 1] = bmpBytes
PUT(FT)

Performance tip: avoid AUTO on windows bound to large blobs (AUTO compares USE variables every event pass).

Known limitations

Window formatter / AppGen don't offer blobs in USE pickers yet (type USE by hand). IMAGE save-back does not exist (write-in is assignment). Report-hosted controls do not bind blobs. MEMO(n),UNICODE on a control is a compile error (the ISAM memo display floor — on SQL, use the blob).

MEMO versus BLOB — which one, and why

The two have always been different animals, and the Unicode work follows that grain rather than fighting it:

Rule of thumb: new work takes the blob; MEMO(n) remains what it has always been — the ISAM-native narrow capped text field of the existing application base. It is not being dragged into the wide future; the blob (and the USTRING(n) record field) already are the wide future.

10. Files and backends

SQL backends (MSSQL, ODBC, PostgreSQL, SQLite)

SQLite specifics

UTF-8 at rest; driver transports UTF-16; engine converts. Wide columns and UNICODE memos are ordinary UTF-8 TEXT — readable by any SQLite tool. Narrow STRING/CSTRING columns store your ANSI bytes verbatim (byte-faithful round trip, but not valid UTF-8 to other tools — prefer wide columns when data must interoperate). Import maps NVARCHAR/NCHAR → USTRING, NCLOB/NTEXT → UNICODE memos, BLOB → BINARY. Sorting is code-point order; NOCASE folds ASCII only. Blob shrink stores exactly; standalone TIME fields store HH:MM:SS[.hh] text and round-trip.

Beta refresh after 08-10-2026: imported keys now carry NOCASE when the underlying SQLite index is declared with it (primary keys stay case-sensitive); a {PROP:SQL} SELECT that returns no rows now describes its columns instead of failing with error 33; and 64-bit INTEGER columns round-trip exactly through DECIMAL(19,0) — earlier builds carried them through a double, rounding values past 15–16 digits and altering the stored type on write.

Text files — ASCII, BASIC, DOS

TopSpeed files — the TopSpeedW driver

Unicode reaches TPS through a separate driver: DRIVER('TopSpeedW'), default extension .tpsw. Classic TopSpeed, existing .tps files, and ANSI apps are completely untouched.

Big files — 512 GB TopSpeedW format and SEND('BIGFILE')

The classic .tps format tops out at 2 GB, and Clarion now guards that cliff instead of corrupting the file: an ADD/APPEND/PUT that would grow past the limit is refused with err 90 / FILEERRORCODE 8281–8283, "TopSpeed file size limit (2GB) reached". A full file is not a corrupt file — reads keep working, DELETE and shrinking PUTs stay legal in the same session (free space and ADD works again), and CLOSE commits normally.

TopSpeedW-created files use the new big format and reach 512 GB (same guard family at that boundary, with the 512GB message). Everything else is unchanged: same record / key / memo / BLOB limits, same tools story.

To lift an existing small file (typically a classic .tps you are adopting through TopSpeedW — a driver swap alone never changes a file's format):

OPEN(MyFile, 12h)                 ! exclusive: deny-all is required
Ans = SEND(MyFile, 'BIGFILE')     ! query -> 'OFF' (small) / 'ON' (big)
Ans = SEND(MyFile, 'BIGFILE=ON')  ! in-place upgrade, all tables, atomic

What to test: grow a file past 2 GB under TopSpeedW (big format — should just work); hit the 2 GB guard on a classic-driver file and confirm the message + that deletes still work; run the BIGFILE upgrade on a classic-created .tps (keys and old rows must survive, BUILD must work after); confirm an upgraded or big file gives the clean version error in an old-driver app rather than anything silent.

Crash recovery hardened (both TPS drivers)

This build also fixes two long-standing crash-recovery defects — in classic TopSpeed as well as TopSpeedW. A TPS file interrupted mid-commit (power loss, kill, crash) recovers from its on-file journal at the next open; previously a session that only read the crashed file could truncate that journal at close (silently changing which side of the interrupted transaction survives), and a write before the recovery was made permanent could leave the whole file unreadable (err 90 / fec 1477 on every access). Now: readers of a crashed file always see the correctly recovered data, see the same data on every open, and leave the file byte-for-byte untouched; the first write completes the recovery permanently. What to test: kill an app mid-update (Task Manager during a heavy batch is fine), then reopen and read several times — same rows every time; then write once, close, reopen — everything intact, no corruption errors.

The Memory driver (beta refresh after 08-12-2026)

The in-memory driver (DRIVER('Memory')) is now fully wide: USTRING fields, keys over them, and BLOB,UNICODE all work, with sizes and slice bounds in units as everywhere else. Key ordering is linguistic collation under the Windows locale — the same order a keyed QUEUE gives a USTRING component, so a Memory-driver table and a keyed queue holding the same rows agree. NOCASE keys use the case-insensitive linguistic fold. Note this deliberately differs from TopSpeedW's code-unit key order: each driver keeps its native ordering personality (Memory's narrow keys have always been collation-ordered). One known limitation: MEMO(n),UNICODE on the Memory driver is still accepted silently over narrow bytes — do not use it; the loud reject (as TopSpeedW already has) is queued. Narrow memos are untouched.

Dynamic files (DYNAMICFILE)

Runtime-defined files were already wide: the field-type list includes USTRING and sizes are in units. Beta refresh after 08-12-2026 for the DynFile wrapper class: SetFieldValueW is the wide write twin (the classic SetFieldValue takes a STRING value and narrows — Clarion prototypes cannot distinguish value STRING from value USTRING, so the wide form takes the W name); GetField / SetFieldValue now accept both bare and prefixed field labels; and CacheFile onto a Memory-driver target works (the cached copy comes back open, ready to read).

The IP driver — client/server (beta refresh after 08-14-2026)

The client/server IP driver (DRIVER('IPDRV')) now carries USTRING fields and UNICODE memo/blob end to end — client application, requester service, data server, to the real backing table — round-tripping bit-identically at rest. Wide keys sort in the backing driver's order (the data server owns the key, so IP inherits it — e.g. TopSpeedW's code-unit order); nothing IP-specific to configure. Declare the fields USTRING / the memos and blobs ,UNICODE in the shared dictionary and rebuild the data DLL; default all-ANSI tables are byte-identical to before. Non-ANSI external file/table names and filter/order literals are separate planned steps, not in this build.

The IP driver's SSL/TLS runtime refresh (OpenSSL 3.5) and the IP Data Server changes ship with the IP driver release, separately from this build.

Remaining narrow drivers

Btrieve, xBase, Clarion .DAT and classic TopSpeed reject USTRING at CREATE/OPEN (err 47) — never a silently narrowed file.

IDE Data Browser

USTRING columns display wide content; UNICODE memos/blobs render and edit wide. MSSQL browse uses the newest installed Microsoft driver and honors trust-certificate consent.

11. String functions

Known limitations

MATCH:Regular and STRPOS narrow to ANSI today (byte-based regex). Match:Soundex narrows by design (ASCII phonetic algorithm).

Windows INI files and the registry (beta refresh after 08-22-2026)

12. Debugger & interop

13. Quick recipes

! Hold exact typed text (any language, any emoji)
Name  USTRING(64)
      ...
  WINDOW ... ENTRY(@s60),USE(Name) ...   ! free-form -> Unicode entry

! Detect a high surrogate before slicing/truncating — keep the pair together
IF i <= LEN(Name) AND VAL(Name[i]) >= 0D800h AND VAL(Name[i]) <= 0DBFFh
  ! Name[i] and Name[i+1] are ONE character (UTF-16 surrogate pair)
END

! Deliberate narrowing at an ANSI boundary
AnsiOut CSTRING(65)
AnsiOut = Name                 ! chars outside the codepage become '?'

! Lossless COM handoff
B BSTRING
B = Name                       ! wide -> BSTR, no codepage
Name = B                       ! BSTR -> wide

! UTF-8 interop
U8 = TOANSI(U, 0FDE9h)         ! USTRING -> UTF-8 bytes
U  = TOUNICODE(U8, 0FDE9h)     ! UTF-8 bytes -> USTRING

! Long text / images on SQL: the blob IS the field
!   Notes BLOB,NAME('Notes'),UNICODE   <-> nvarchar(max)
!   Photo BLOB,NAME('Photo'),BINARY    <-> varbinary(max)
  WINDOW ... TEXT,AT(...),USE(Pre:Notes),HVSCROLL
             IMAGE,AT(...),USE(Pre:Photo) ...

! Store bytes into a blob: SIZE FIRST
Pre:Photo{PROP:Size} = LEN(bytes)
Pre:Photo[0 : LEN(bytes) - 1] = bytes
PUT(File)

! A wide TopSpeed file
WF  FILE,DRIVER('TopSpeedW'),CREATE,PRE(W1)   ! lands as .tpsw
UK    KEY(W1:UName),DUP,NOCASE
Rec   RECORD
Id      LONG
UName   USTRING(20)
      END
    END

14. What to focus on when testing

  1. Your language, your keyboard — IME, dead keys, clipboard from other apps through ENTRY/TEXT/COMBO into USTRING and SQL/TopSpeedW columns; exact round trips including reopening the app.
  2. Schema import on real databases — wide/narrow columns, long text, binary, 64-bit integers; check types and attributes against the import table, then browse and edit in the Data Browser.
  3. TopSpeedW adoption paths — narrow-only TpsW files opened by classic apps; wide files rejected cleanly by old drivers; key order on your data (code-unit order, not locale).
  4. Mixed-era apps — existing ANSI apps rebuilt on this version should behave byte-identically. Any change in an app that uses no USTRING/Unicode feature is a bug we want reported.
  5. Conversion boundaries — where wide data crosses to ANSI (title bars, third-party libraries). Values should always survive even where presentation narrows.
  6. Reports — your real reports with ,UNICODE added: preview, print-after-preview, direct print, band splits, LISTs. The same report un-opted should post error 546 on wide data and still complete; an all-ANSI report with the attribute should look pixel-identical to its classic twin. Export the UNICODE report to TEXT/HTML/XML and verify your language survives at rest (TEXT opens as UTF-8 with a BOM); a PDF export of the same report should show exactly one Report Export Notice. If you use a third-party preview/report tool, check how it reacts to a UNICODE report's .emf page files (expected: it will not read them — that is why the attribute is opt-in).
When reporting an issue

Most useful evidence: the exact text (as a U'...' literal or code points), the declaration (field / control / driver string), what you expected, what happened — and whether the same steps work with plain ANSI text.