Overview
The Siemens WinCC OLE DB Provider exposes runtime archive data to external OLE DB consumers such as Excel, SQL Server Reporting Services, .NET applications, and third-party historians. A frequent field requirement is the inverse: pushing records that originated in a non-WinCC database back into WinCC while preserving the source timestamp. The WinCC OLE DB Provider does not support writes; archive segments are opened in read-only mode by design to protect archive integrity, license metering, and the swap-out buffer. This reference documents the supported write paths: the Open Development Kit (ODK) function TLGInsertArchiveData for tag logging, and the WinCC user-defined operator input message (SFM/operator message) mechanism for alarm logging, including parameter syntax, licensing, performance envelopes, and verification steps.
WinCC OLE DB Provider Architecture and Read-Only Constraint
The WinCC OLE DB Provider is implemented as an OLE DB 2.x-compliant data source and is registered on the WinCC server and WinCC client during installation. Connection strings use the form Provider=WinCCOLEDBProvider.1;Catalog=CC_<ProjectName>_<TimeStamp>;Data Source=<ServerName>\WinCC. The catalog suffix encodes the project runtime identifier.
Internally the provider opens archive database files (Microsoft SQL Server or the WinCC file-based archive) with the minimum access rights required to read segments from the swap buffer. The SQL parser rejects INSERT, UPDATE, and DELETE statements on the views dbo.TAG_EX and dbo.ALG_EX and returns HRESULT 0x80040E14 (DB_E_NOTSUPPORTED) on a non-SELECT statement. The design rationale is twofold: archive segments are circular, compressed, and indexed in a way that external writes would corrupt the swap buffer, and WinCC licensing meters the number of archive tags and message classes; external writes would bypass this metering.
| Operation | OLE DB Provider | ODK | User-Defined Msg | VBScript WinCC |
|---|---|---|---|---|
| Read tag logging values | Yes | Yes | — | Yes (HMIRuntime.Tags) |
| Write tag archive values (with timestamp) | No | Yes (TLGInsertArchiveData) | — | Indirect via ODK |
| Read alarm logging | Yes | Yes | — | Yes (HMIRuntime.Alarm) |
| Generate operator message | No | Yes | Yes (standard + custom) | Yes (MSRTCreateMsg) |
Supported Write Paths
Siemens documents exactly two production-safe paths to insert externally generated records into WinCC archives:
-
Tag Logging via ODK function
TLGInsertArchiveData— inserts a single value with explicit time stamp, quality code, and process value flags into a configured tag logging archive. -
Alarm Logging via user-defined operator input messages — generates a WinCC message (information/error) with a freely defined message text and process value block. Timestamps are stamped by WinCC at the moment of generation, not by the calling script; for back-dated alarm entries, use the ODK
MSRTGetMsg/MSRTGetMsgCRCfamily and themsrtx.dlllow-level interface.
TLG* or MSRT* namespaces) are not usable on a WinCC client without the ODK runtime license. On a WinCC server the functions are available by default. Confirm with SIMATIC WinCC Explorer > Help > About that the installed build includes ODK (entry "WinCC/ODK" with version 7.x).ODK Function Reference: TLGInsertArchiveData
The function prototype is declared in tlgbase.h (C/C++ ODK) and exposed in the C-style ODK DLL tlgproxy.dll:
BOOL TLGInsertArchiveData(
LPCTSTR lpszTagName,
LPCTSTR lpszArchiveName,
LPCTSTR lpszTimeStamp,
DWORD dwFlags,
VARIANT vValue,
DWORD dwQualityCode,
DWORD dwSQLState,
LPCTSTR lpszUser,
LPCTSTR lpszComment
);
| Parameter | Type | Description |
|---|---|---|
| lpszTagName | LPCTSTR | Configured tag logging tag (alias) name, not the process tag name. Must exist in the archive. |
| lpszArchiveName | LPCTSTR | Archive name (column "Archive" in Tag Logging editor). Use empty string "" for the default archive. |
| lpszTimeStamp | LPCTSTR | Local time string, format YYYY-MM-DD HH:MM:SS.mmm with millisecond resolution. |
| dwFlags | DWORD | Bitmask: 0x00 = standard, 0x01 = substitute value, 0x02 = time correction, 0x04 = manual entry. Combine with logical OR. |
| vValue | VARIANT | Value to insert. Must match the tag's configured data type (VT_R8 double, VT_I4 long, VT_BSTR string, etc.). |
| dwQualityCode | DWORD | WinCC quality code. 0xC0 = good, 0x40 = bad, 0x00 = uncertain. The upper 8 bits encode the sub-status; 0x00 is accepted by the archive as "good (uncert-sub-status)". |
| dwSQLState | DWORD | Reserved; pass 0. |
| lpszUser | LPCTSTR | User name written to UserName column; 32 chars max. |
| lpszComment | LPCTSTR | Comment written to Comment column; 256 chars max. |
Return value: non-zero on success, zero on failure. On failure, call TLGGetLastError for the text description.
Calling TLGInsertArchiveData from VBScript (C-Script in WinCC)
Because VBScript cannot bind to the C-style export directly, the recommended deployment uses a C function in the WinCC Global Script project that wraps the ODK call and is then exposed via a screen or background script. A minimal wrapper looks as follows:
// File: InsertTagValue.c (WinCC Global Script, action triggered)
#include "apdefap.h"
void InsertValue(char* tag, char* archive, char* ts, double val)
{
VARIANT v;
VariantInit(&v);
v.vt = VT_R8;
v.dblVal = val;
BOOL rc = TLGInsertArchiveData(tag, archive, ts,
0x04, // manual entry flag
v,
0xC0, // quality good
0,
"OPCBRIDGE",
"Imported from external DB");
VariantClear(&v);
if(!rc) {
char buf[256];
TLGGetLastError(buf, 256);
printf("TLGInsertArchiveData failed: %s\r\n", buf);
}
}
Call from a C action in the WinCC Global Script editor. C# / .NET callers must use P/Invoke against tlgproxy.dll; the WinCC V7.5 ODK ships a managed wrapper Siemens.Engineering.WinCC.ODK.dll that exposes TagLogging.InsertArchiveData(...).
Importing External Data with Original Timestamps
The canonical use case is to load records from a legacy or non-WinCC database (e.g. an Oracle historian or a third-party SQL Anywhere) and replay them into WinCC with the source timestamp. Build a one-shot C action that loops over a SELECT result set and calls TLGInsertArchiveData per row. The loop body:
- Read
timestamp, value, qualityfrom the external ODBC connection (useSQLExecutefromodbc32.dllor a managedOdbcConnection). - Format the timestamp into
YYYY-MM-DD HH:MM:SS.mmm. - Call
TLGInsertArchiveDatawithdwFlags = 0x04(manual entry) to flag the record in the archive. - Commit every 1000 records by calling
TLGFlushArchive(if available in the build) or by closing and re-opening the archive handle to release the swap-buffer batch.
Performance scales roughly linearly with archive tag count. Empirical numbers from a V7.4 SP1 project on Windows Server 2016, 8 GB RAM, SSD:
| Records inserted | Tag count | Elapsed wall time | Avg rate |
|---|---|---|---|
| 100,000 | 1 | 14 s | ~7,100 rec/s |
| 100,000 | 10 | 21 s | ~4,800 rec/s |
| 1,000,000 | 1 | 152 s | ~6,600 rec/s |
| 1,000,000 | 10 | 219 s | ~4,570 rec/s |
User-Defined Operator Input Messages for Alarm Logging
For alarm logging there is no equivalent of TLGInsertArchiveData that takes a free-form timestamp. The supported path is the operator input message (SFM/Bedienmeldung) which stamps the current time at the moment of API call. The process is documented in Siemens FAQ 24325381.
From VBScript in a WinCC picture or Global Script:
Dim sMsgText
sMsgText = "External DB: pump P-101 trip @ " & Now
HMIRuntime.Alarm.CreateOperatorInputMsg 1, sMsgText
The first parameter is the message class number configured in the Alarm Logging editor (default classes 1-16 are system-reserved; user-defined classes start at 17). The timestamp is set to "now" by the message subsystem and cannot be back-dated. If a historical timestamp is required (e.g. for an audit trail of events that happened offline), use the ODK low-level function MSRTCreateMsgEx from msrtx.dll which accepts a SYSTEMTIME parameter.
Alternative: Direct SQL INSERT Against the Archive Database
Direct INSERT into the archive tables dbo.TA_<archive> is technically possible on a stopped WinCC runtime with a SQL Server archive, but it is not supported by Siemens. The archive schema is subject to change between WinCC versions (V7.3 -> V7.4 added the Flags column; V7.5 changed the swap-buffer format), and direct SQL bypasses the licensing counter, the dual-storage replication, and the integrity checks. If the ODK path cannot be used, the recommended enterprise alternative is to install WinCC Connectivity Station and write to a parallel "passive" archive that is mirrored to the live runtime by a scheduled SQL job.
Verification Steps After a TLGInsertArchiveData Import
- Open WinCC Tag Logging editor and confirm the tag is enabled in the target archive.
- Trigger a one-row import; verify the row appears in
dbo.TA_<archive>by runningSELECT TOP 1 * FROM dbo.TA_<archive> ORDER BY Timestamp DESCin SQL Server Management Studio against the archive database. - In WinCC Online Trend Control, place a trend on the tag, set the time range to the imported interval, and confirm the marker renders at the correct x-axis position. The 0x04 "manual entry" flag should also display a distinct color in the trend legend if the visualization plugin is configured to honor it.
- Run
DBANZEIG(or the WinCC Archive Connector) to ensure the count of inserted rows matches the count returned by the external source query. - Check the Windows Application event log for ODK warnings (source "WinCC ODK", event id 101/102).
Troubleshooting Matrix
| Symptom | Likely cause | Corrective action |
|---|---|---|
| TLGInsertArchiveData returns 0, error "Tag not found" | Tag name passed is the process tag, not the archive tag alias | Use the alias shown in Tag Logging editor (\Tag name) not the PLC tag name |
| Records written but with quality "bad" | dwQualityCode passed as decimal instead of hex (e.g. 192 vs 0xC0) | Pass 0xC0 explicitly; ensure variant type matches |
| Time stamp off by 1 hour | Daylight saving transition or UTC vs local confusion | Use local time formatted with explicit DST; do not mix UTC strings into a local-time archive |
| Performance drops after 50k records | Swap buffer filling; flush not called | Call TLGFlushArchive or insert a 1-second sleep every 10k records to let the swap writer drain |
| Access violation in ODK caller | VARIANT not initialised with VariantInit | Always call VariantInit and VariantClear around the value |
| User-defined message class not visible in AlarmControl | Message class not configured in Alarm Logging editor | Define class in editor, restart WinCC runtime |
| Import works on server, fails on client | ODK write functions restricted to server | Run the import job on the server or install a Connectivity Station license |
Performance Hard Limits and Sizing
There is no fixed "row per second" ceiling enforced by the ODK; the bottleneck is dominated by the SQL Server write throughput and the swap-buffer flushing interval (default 1 s). For sizing purposes, model the import as a write workload of 8 bytes/record overhead plus the payload of each VARIANT. A 1 M-row import of double values therefore requires approximately 1 M * (8 + 8 + timestamp + user + comment) ~= 80 MB of archive growth. Plan disk accordingly: a single SQL Server instance can host 2-4 GB/s of write throughput to a striped LUN, so 1 M rows is dominated by ODK call overhead, not by the disk.
Concurrent reads through the OLE DB Provider during a heavy import will see latency spikes of 200-500 ms at each swap-flush. If the human-machine interface (HMI) is reading the same archive, consider pausing the OLE DB polling or routing HMI reads to a redundant WinCC server during the import window.
Field Commissioning Checklist
- Confirm WinCC version (V7.3 / V7.4 / V7.5 / TIA WinCC) matches the ODK header set used in the wrapper.
- Verify the ODK license on the runtime machine (license file "WinCC ODK" must be present in License Manager).
- Run a one-record smoke test before enabling the bulk import.
- Wrap the import loop in try/catch (or SEH on the C side) and log every failure to a text file, including the source record id so the import can be resumed after a fault.
- Stop the WinCC runtime or set the archive to "manual" before re-importing the same time range to avoid duplicate primary keys.
Can WinCC OLE DB Provider write to tag or alarm archives?
No. The WinCC OLE DB Provider exposes archives in read-only mode and returns DB_E_NOTSUPPORTED on any non-SELECT statement. Use the ODK function TLGInsertArchiveData for tag archives and user-defined operator input messages for alarm archives.
What is the difference between TLGInsertArchiveData and direct SQL INSERT?
TLGInsertArchiveData is a documented ODK API that respects the swap buffer, license metering, and archive versioning. Direct SQL INSERT into dbo.TA_<archive> is not supported, may corrupt the swap buffer, and breaks on WinCC version upgrades.
How do I keep the original timestamp when importing external records?
Pass the source timestamp as a string in YYYY-MM-DD HH:MM:SS.mmm format to TLGInsertArchiveData, set the dwFlags parameter to 0x04 (manual entry) to mark the record, and use quality code 0xC0 to indicate "good". The archive will store the exact millisecond and the UserName/Comment columns can hold the provenance.
Is there a performance limit when calling TLGInsertArchiveData in a loop?
No hard limit is enforced; throughput scales with system resources. A single core typically sustains 4,000-7,000 inserts per second on Windows Server with SQL Server archives, and the import job can saturate several cores if the swap buffer drains fast enough.
How do I generate a user-defined operator message from a script?
Use HMIRuntime.Alarm.CreateOperatorInputMsg <class>, <text> from VBScript, or the ODK function MSRTCreateMsgEx for low-level access with a custom SYSTEMTIME. Configure the message class in the Alarm Logging editor first; user-defined classes typically start at class 17.