Finding Free Text Annotations in COMOS P&ID Queries
Free text placed on a Siemens COMOS P&ID is not represented as a queryable object in the standard navigation tree. Unlike attribute-bound labels (which are exposed as CDOs with attributes that can be filtered), text drawn with the toolbar "A" button is stored as geometry on the report document. This makes finding, auditing, and translating free text a recurring pain point for plant engineers, particularly on legacy projects where hundreds of P&IDs contain unstructured remarks.
This article documents three working methods to locate free text across a COMOS project: the built-in Bulk Translation Query, a scripted Report.Open walk using the ITEXT class, and a database-level search using SQL Server FREETEXT or Oracle Text. Each method is detailed with verification steps, error handling, and a comparison of when to use which approach.
What "Free Text" Means in a COMOS Report
A COMOS report can contain text originating from four sources. The distinction matters because only some of them are addressable through standard queries.
| Source | Created via | Object in nav tree? | Addressable as ITEXT? |
|---|---|---|---|
| Object-bound label | Attribute on a CDO (e.g., Tag, Description) | Yes | Via ITextAttributeObject subclass |
| Free text / remark | Toolbar "A" text tool on the report | No | Yes, directly as ITEXT
|
| Smart text |
||placeholder|| token in a label |
No (resolved at generation time) | Resolved text appears; token does not |
| Hyperlink / cross-reference | Insert > Cross-reference | No (target is the linked doc) | Yes, as ITEXT with hyperlink flag |
The remainder of this article targets the second row: user-typed remarks that have no CDO behind them. These are invisible to Navigator queries, OwnAttributes searches, and standard Find dialogs.
The COMOS Object Model for Text
The COMOS report object model exposes the relevant interfaces in a flat hierarchy that is easy to traverse in VBScript queries.
| Class / Property | Role |
|---|---|
Report |
Represents a COMOS report document. Opened via doc.Report.Open or by opening the document from the navigator. |
Reportdocument |
The document object inside the report. Accessed as doc.Report.Reportdocument. |
Reportdocument.Item(i) |
Indexed collection of items on the report. Indexing is 1-based (the source material references I-1 to convert to a 0-based loop counter). |
ITEXT |
Base class for text items. Discovered in the COMOS Object Debugger (Tools > Object Debugger). |
ITextAttributeObject |
Subclass of ITEXT for object-bound text. Carries a back-reference to the source CDO. |
ItemCount |
Total number of items on the report, including lines, polylines, symbols, and text. |
To filter free text from object-bound text in a script, test the TypeName(item) string against the value "ITEXT". Object-bound text typically resolves to a longer class name (e.g., "ITextAttributeObject"), so an exact-match test excludes it. Note that on some COMOS 10.4 builds the TypeName may return a different string for compound text objects — verify against the Object Debugger for your installation before relying on the comparison.
Database Architecture: Where Free Text Is Stored
COMOS stores report geometry — including the text and its position, font, and content — in the project database. The two standard back ends are Microsoft SQL Server (typical for COMOS 10.x) and Oracle Database (used in larger enterprise rollouts). Free text values are not in a generic Documents table; they live in COMOS-specific geometry tables whose names depend on the COMOS version and database layer.
For SQL Server deployments, the FREETEXT predicate is the natural-language match operator documented at Microsoft Learn — FREETEXT (Transact-SQL). It operates on full-text indexed columns, supports word stemming and inflectional forms, and is the closest analogue to a user-typed search.
For Oracle deployments, the equivalent functionality is delivered by Oracle Text (formerly Oracle Context). The general free-text search concept is documented at Oracle Help Center — Free Text Search.
For background on the technique itself — tokenization, ranking, indexing — the Google Cloud — Full-Text Search Explained overview is a useful primer, even though it is not COMOS-specific.
Method 1 — Bulk Translation Query (No Scripting)
The fastest path for translators, reviewers, and one-off audits is the Bulk Translation tool built into COMOS.
- Open the COMOS project and navigate to the base object, folder, or document scope you want to search.
- From the main menu, select Extra → Bulk Translation Query.
- In the resulting dialog, set the Query Scope:
- Current project — every report in the open project (slowest).
- Current unit — every report in the current unit/folder.
- Current document — single report.
- Apply a filter to limit the result to free text. The exact filter name varies by build; look for Text type = Free text or Source = Manual.
- Run the query. The result grid shows source text, target text, the host document, and the language.
Advantages: No scripting, no risk of corrupting objects, native translation workflow columns included.
Limitations:
- Cannot be saved as a reusable COMOS query; you re-run it manually each time.
- Filtering by text string is not exposed in the dialog — you can only filter by language, status, or document.
- Read-only — no programmatic export to CSV or another system.
Method 2 — Scripted Report.Open Walk with ITEXT Filter
For automated, repeatable, and content-filtered searches, COMOS provides a script path. The approach recommended in the source thread is to open each report, iterate over Reportdocument.Item(i), and filter on TypeName == "ITEXT".
Reference implementation
' =========================================================
' COMOS VBScript query — Find free text matching a pattern
' Tested against COMOS 10.2 / 10.3 (Object Debugger values
' can differ — verify constants for your build)
' =========================================================
Option Explicit
' --- Configuration ---
Const SEARCH_STRING = "REVIEW" ' substring (case-insensitive)
Const OUTPUT_FILE = "C:\Temp\FreeTextReport.csv"
Const SAVE_REPORT = False ' True to persist report changes
Const MAX_REPORTS = 0 ' 0 = no limit
Const ITEM_TYPE_NAME = "ITEXT" ' verify in Object Debugger
Dim fso, ts
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.CreateTextFile(OUTPUT_FILE, True)
ts.WriteLine "DocumentFullName" & vbTab & "ItemIndex" & vbTab & "TextContent"
Dim reportCount, matchCount
reportCount = 0
matchCount = 0
Dim doc
For Each doc In ThisProject.Documents
ProcessReport doc
If MAX_REPORTS > 0 And reportCount >= MAX_REPORTS Then Exit For
Next
ts.Close
MsgBox "Processed " & reportCount & " reports. " & _
"Matched " & matchCount & " free-text items."
' ---------------------------------------------------------
Sub ProcessReport(doc)
On Error Resume Next
Err.Clear
If doc Is Nothing Then Exit Sub
' Restrict to P&ID documents to avoid hitting datasheets,
' loop diagrams, and other report types.
If LCase(doc.Type) <> "p&id" And LCase(doc.Type) <> "pid" Then
Exit Sub
End If
' Skip documents open in another COMOS session — opening
' them can clobber the other session's unsaved changes.
If doc.OpenInOtherSession = True Then
Debug.Print "Skipped (open elsewhere): " & doc.FullName
Exit Sub
End If
doc.Report.Open
If Err.Number <> 0 Then
Debug.Print "Open failed (" & Err.Number & ") for " & _
doc.FullName & ": " & Err.Description
Err.Clear
Exit Sub
End If
reportCount = reportCount + 1
Dim i, item, txt
Dim rdoc
Set rdoc = doc.Report.Reportdocument
For i = 1 To rdoc.ItemCount
Err.Clear
Set item = rdoc.Item(i)
If Err.Number <> 0 Then
Debug.Print "Item " & i & " in " & doc.FullName & _
" failed: " & Err.Description
Err.Clear
ElseIf TypeName(item) = ITEM_TYPE_NAME Then
txt = item.Text
' Strip embedded newlines to keep the CSV clean
txt = Replace(txt, vbCrLf, " ")
txt = Replace(txt, vbLf, " ")
If InStr(1, txt, SEARCH_STRING, vbTextCompare) > 0 Then
ts.WriteLine doc.FullName & vbTab & CStr(i) & vbTab & txt
matchCount = matchCount + 1
End If
End If
Next
If SAVE_REPORT Then
Err.Clear
doc.Report.Save
If Err.Number <> 0 Then
Debug.Print "Save failed for " & doc.FullName & _
": " & Err.Description
Err.Clear
End If
End If
On Error GoTo 0
End Sub
How to deploy the query
- Open the COMOS project in the COMOS desktop client.
- Navigate to a unit or folder where you have rights to create queries.
- Right-click → New → Query. Choose a Script-based query (German: Skriptbasierte Abfrage).
- Paste the script above into the editor.
- Adjust
SEARCH_STRING,OUTPUT_FILE, andSAVE_REPORTfor your scenario. - Save and execute the query from the navigator context menu.
MAX_REPORTS = 5 and SAVE_REPORT = False for the first run. Confirm the CSV looks correct, then scale up. A single corrupted report item can crash the COMOS shell if the error handlers are missing or mis-scoped.
Error Handling: The Two Failure Modes That Bite First
The field report documents two specific failure modes that surface only when the script is run against a real project. Both must be engineered around before the script is safe to schedule.
1. Save conflicts when documents are open elsewhere
If a report is currently open in another COMOS session (e.g., a colleague editing the same drawing), the script's call to doc.Report.Save will either silently fail or overwrite the other session's unsaved changes. The mitigation is to check doc.OpenInOtherSession before any save, and to default to read-only scans unless persistence is explicitly required.
' Guarded save pattern
If doc.OpenInOtherSession = False And doc.Locked = False Then
doc.Report.Save
End If
If you do not need to persist changes (which is the case for any read-only query), comment out the doc.Report.Save line entirely:
' a.Report.Save '<-- intentionally commented for read-only scan
2. Session-killing faults in report items
When a report contains a corrupted, partially migrated, or version-mismatched object, opening it from script can raise a COM error that propagates to the COMOS shell. The documented behavior is that the entire COMOS session terminates. In some COMOS 10.3+ builds, the error surfaces as a modal dialog that must be clicked away — fatal for an unattended batch run over several hundred reports.
The mitigation is a tight On Error Resume Next envelope around both doc.Report.Open and the per-item access loop, with an explicit skip-and-log path on error:
Sub ProcessReport(doc)
On Error Resume Next
Err.Clear
doc.Report.Open
If Err.Number <> 0 Then
Debug.Print "Skip (open error " & Err.Number & _
"): " & doc.FullName
Err.Clear
Exit Sub
End If
Dim i, item
For i = 1 To doc.Report.Reportdocument.ItemCount
Err.Clear
Set item = doc.Report.Reportdocument.Item(i)
If Err.Number <> 0 Then
Debug.Print "Item " & i & " in " & doc.FullName & _
" failed: " & Err.Description
Err.Clear
ElseIf TypeName(item) = "ITEXT" Then
' process match
End If
Next
On Error GoTo 0
End Sub
3. Suppressing modal error dialogs at the COMOS level
For unattended runs, COMOS has a global switch that suppresses interactive error dialogs. The path differs slightly by build:
- COMOS 10.2 / 10.3: Options → System → Error Handling → Suppress runtime errors in queries (checkbox). In the German build the path is Extras → Optionen → System → Fehlerbehandlung.
- COMOS 10.4+: The same path, but in some builds the toggle sits under Tools → Options → Workflow → Queries.
Verify the option is enabled with a single-report test before scheduling a batch run against a production project. When this option is off, the modal dialog defeats the entire purpose of the On Error Resume Next envelope.
Method 3 — Database-Level Search with SQL FREETEXT
For projects with more than ~2,000 reports, the script-based approach is slow because each doc.Report.Open call performs a database round-trip. A faster path is to query the underlying database directly, using the SQL Server FREETEXT predicate.
From the Microsoft Learn — FREETEXT (Transact-SQL) reference, the predicate is used in a WHERE clause against a full-text indexed column. Conceptual example (table and column names are placeholders — verify against your schema):
-- Conceptual query; table/column names depend on COMOS version.
SELECT TOP 1000
DocumentName,
ItemIndex,
TextContent
FROM dbo.ComosReportItem WITH (NOLOCK)
WHERE FREETEXT(TextContent, 'pump discharge');
Required preconditions:
- A full-text index must exist on the text column. If it does not, create it:
CREATE FULLTEXT INDEX ON dbo.ComosReportItem (TextContent) KEY INDEX PK_ComosReportItem; - The user running the query must have
SELECTrights on the table. COMOS administrators typically grant read-only DB accounts for analytics workloads. - The result will include both object-bound and free text unless your schema exposes a discriminator. If the table has an
ItemTypecolumn, addAND ItemType = 'ITEXT'to the predicate.
For Oracle-based COMOS deployments, the equivalent is delivered through Oracle Text indexes (CTXSYS / CREATE INDEX ... INDEXTYPE IS CTXSYS.CONTEXT). The high-level free-text search pattern is documented at Oracle Help Center — Free Text Search.
For a general treatment of full-text search semantics — tokenization, stemming, ranking, recall vs. precision — see the Google Cloud — Full-Text Search Explained overview.
Performance and Batch Sizing
Observed run-time figures, which vary by hardware, network latency to the COMOS database, and COMOS build:
| Reports scanned | Method | Approx. duration | Notes |
|---|---|---|---|
| 100 | COMOS script (local DB) | 30–90 s | Negligible for ad-hoc use |
| 500 | COMOS script (local DB) | 3–7 min | Watch the error-handling envelope |
| 1,000 | COMOS script (networked DB) | 10–20 min | Round-trip dominates |
| 10,000+ | SQL FREETEXT | Seconds | Requires schema knowledge and DB access |
For script-based runs, batch in chunks of 200–500 reports with a query restart between batches. This limits the blast radius if a single corrupted report kills the session, and lets you resume from a known checkpoint rather than re-scanning.
Verification and Result Validation
- Cross-check with Bulk Translation. Re-run the same search using Extra → Bulk Translation Query. The order of magnitude of matches should align. The Bulk Translation tool may include some object-bound translatable text that the ITEXT filter excludes; a discrepancy of 5–15% is normal.
- Spot-check three random reports. For each, open the report manually, locate the matched text item, and confirm the content matches the CSV row.
-
Validate the CSV file.
- Row count matches the script's reported match count.
- No rows have an empty
TextContent(would indicate a filter failure). - No
DocumentFullNamevalues are truncated (would indicate an unhandled error path).
-
Run a small batch first. Always execute the script with
MAX_REPORTS = 5andSAVE_REPORT = Falseon a test project or a test folder before scaling. The session-kill failure mode hides until the loop reaches the offending document.
Method Comparison
| Criterion | Bulk Translation Query | COMOS Script (ITEXT) | SQL FREETEXT |
|---|---|---|---|
| Setup effort | None | Medium (script + test) | High (schema + index) |
| Run time (1,000 reports) | 1–2 min | 10–20 min | Seconds |
| Free text only | Partial (needs filter) | Yes (ITEXT cast) | Needs ItemType filter |
| Modifies project | No | Optional (SAVE flag) | No |
| Handles corrupted reports | Tolerates | Needs error handling | Tolerates |
| Output format | COMOS grid | Scriptable (CSV, DB) | Native SQL result |
| Required permissions | Standard user | Standard user | DBA or read-only DB user |
| Reusable as saved query | No | Yes (saved as a query) | No (ad-hoc SQL) |
Recommendation by use case:
- One-off audit or translation review: Bulk Translation Query.
- Scheduled audit, CSV export, or repeated runs: ITEXT script, saved as a COMOS query.
-
Very large projects (10,000+ reports) or analytics workloads: SQL
FREETEXTwith a full-text index.
Troubleshooting Matrix
| Symptom | Likely cause | Fix |
|---|---|---|
| COMOS session terminates mid-run | Unhandled error in Report.Open on a corrupted report |
Wrap doc.Report.Open in On Error Resume Next; skip on Err.Number <> 0
|
| Modal error dialog per report | Suppression flag off | Enable Suppress runtime errors in queries in COMOS options |
TypeName(item) returns "Empty" or "Nothing" |
Unsupported item type on the report | Guard with If TypeName(item) = "Empty" Then
|
| Save overwrites another session's changes | Document open in a second COMOS session | Check doc.OpenInOtherSession before Report.Save; default to read-only |
| ITEXT not visible in Object Debugger | Wrong COMOS build or restricted permission | Verify COMOS version; ITEXT is exposed in standard iDB installations |
| Free text not detected on certain reports | Older layout format not migrated | Re-save the report once in the current COMOS version; items become accessible via ITEXT |
| CSV output truncated or broken | Embedded newline in item.Text
|
Replace vbCrLf / vbLf with a space before writing |
| SQL FREETEXT returns zero rows | Column has no full-text index | Create the index: CREATE FULLTEXT INDEX ON <Table> (TextContent) KEY INDEX <PK>
|
| SQL query returns attribute text, not free text | No ItemType filter |
Add AND ItemType = 'ITEXT' if the column exists in your schema |
| Query runs but writes 0 matches |
TypeName string mismatch in this COMOS build |
Open the report in Object Debugger, inspect an item, copy the exact class name |
| First report opens, then script silently stops | Unhandled error leaves the loop in a bad state | Reset Err after every iteration; place the On Error Resume Next inside the subroutine only |
Frequently Asked Questions
How do I find free text on a COMOS P&ID without writing any script?
Use the built-in Bulk Translation Query under Extra → Bulk Translation Query. It iterates over the project and lists translatable text, including free text, without requiring any code. The dialog does not support substring filtering on the text itself, so for content-based searches you need the ITEXT script approach.
What is the ITEXT class in COMOS?
ITEXT is the base COMOS class for text items on a report. It is exposed in the COMOS Object Debugger and is the discriminator for free text when iterating over Reportdocument.Item(i). Object-bound text typically resolves to a longer subclass name (e.g., ITextAttributeObject), so a TypeName(item) = "ITEXT" exact-match test isolates free text from attribute labels.
Why does my COMOS session terminate when I run a script that opens reports?
An unhandled error during doc.Report.Open — usually caused by a corrupted or partially migrated report item — propagates to the COMOS shell and terminates the session. Wrap the open call and the per-item loop in On Error Resume Next, check Err.Number after each call, and skip the document or item on error. Also enable the Suppress runtime errors in queries option in COMOS to prevent modal dialogs from blocking unattended runs.
Can I search free text directly in the COMOS database?
Yes. On SQL Server, use the FREETEXT predicate (documented on Microsoft Learn) against the table that stores report item text. A full-text index on the text column is required. On Oracle, the equivalent is delivered by Oracle Text indexes. The result will include both object-bound and free text unless your schema exposes a discriminator column — add an ItemType = 'ITEXT' filter if available.
How do I prevent my script from overwriting another user's unsaved changes?
Test doc.OpenInOtherSession before calling doc.Report.Save. If the document is open elsewhere, log the skip and either continue with a read-only scan or exit the batch. For pure query use cases, comment out the Report.Save line entirely; persistence is never required for a search.
How should I size a batch run for very large projects?
Cap each run at 200–500 reports with a query restart between batches, and always run a 5-report smoke test first. For projects above ~2,000 reports, prefer the SQL FREETEXT path because the script's per-report Report.Open round-trip dominates the runtime. The general approach to full-text search — including tokenization, indexing, and ranking — is summarized at Google Cloud: Full-Text Search Explained.