Enumerating WinCC TagLogging Archive Tags with ODK C API

David Krause11 min read
HMI / SCADASiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Overview

WinCC V7.5 and earlier versions of the SIMATIC HMI WinCC Information System store historical process values in archives configured inside the Tag Logging editor. Each archive is composed of archive tags that reference either internal WinCC tags or external process tags. When the number of archive tags grows beyond a few dozen, hard-coding each name into a screen object (for example a combobox, list box, or trend view selection field) becomes unmaintainable.

The WinCC Open Development Kit (ODK) exposes a C-API that lets a programmer query the runtime project, enumerate the tags in a TagLogging archive, and pass the result back to a graphical object on a screen. This article walks through:

  • Which ODK functions to use (TLGEnumVariablesEx, DMEnumVariables, DMConnect, DMGetRuntimeProject).
  • How to write a callback that receives PTLG_VARIABLE_INFO entries.
  • How to push the enumerated names into a WinCC combobox from C through the __object runtime API.
  • How to call the enumeration from a screen event such as OnOpen.
  • Build, deploy, and verify the ODK DLL inside a WinCC V7.5 project.

For modern V16+ projects using the Unified runtime, an additional, non-C-API path is described at the end of the article based on the official Configuring logging tags (RT Unified) documentation.

Prerequisites

Item Requirement
WinCC version SIMATIC WinCC V7.5 SPx (the ODK described here is part of the WinCC Information System, entry ID 109762744). Tested on V7.4, V7.5.
WinCC option ODK (Open Development Kit) license and files installed under C:\Program Files (x86)\Siemens\Automation\WinCC\bin\.
Toolchain Microsoft Visual Studio 2015/2017/2019/2022, platform toolset matching WinCC (default: v140 for V7.5), Release configuration, Win32 platform.
Languages C or C++ (C is sufficient for the example).
WinCC project A running project with at least one TagLogging archive that contains > 1 tags.
Runtime context The DLL must be loaded by the WinCC Runtime (Graphics Runtime) and is not usable from a pure WinCC Explorer context.
Architectural constraint: The ODK callback runs inside the WinCC graphics process. Long-running work (file I/O, network) inside the callback will block picture redraws. Keep the callback short and buffer the data to user memory for later use by the picture.

ODK API Surface for TagLogging Enumeration

The header tlg_api.h (included via #include "tlg_api.h" after the ODK include path is added to Visual Studio) declares the prototype:

BOOL TLGEnumVariablesEx(
    LPCSTR lpszArchiveName,
    TLG_ENUMVARIABLES lpfnEnumProc,
    LPVOID pUser,
    CMN_ERROR* pError
);

The matching callback type is defined as:

typedef BOOL (*TLG_ENUMVARIABLES)(
    PTLG_VARIABLE_INFO lpvi,
    LPVOID pUser
);

The TLG_VARIABLE_INFO structure (from tlgdef.h) provides the metadata of every archive tag in the archive:

typedef struct tagTLG_VARIABLE_INFO {
    DWORD  dwSize;                  /* structure size, fill before passing */
    char   szVariableName[MAX_TLG_NAME];
    char   szArchiveName[MAX_TLG_NAME];
    DWORD  dwVariableType;          /* DM_VARTYPE_FLOAT, DM_VARTYPE_DWORD, ... */
    DWORD  dwVariableID;
    LONG   lScaleUnit;              /* scaling factor exponent */
    double dbScaleLinear;           /* linear scaling factor */
    /* ... additional fields depending on WinCC build */
} TLG_VARIABLE_INFO, *PTLG_VARIABLE_INFO;

The older, project-wide DMEnumVariables in dm_api.h can be used as a fallback when an archive name is unknown:

BOOL DMEnumVariables(
    LPCSTR        lpszProjectFile,
    LPDM_VARFILTER lpdmVarFilter,
    DM_ENUMVARPROC lpfnEnumProc,
    LPVOID        pUser,
    CMN_ERROR*    pError
);

Before any enumeration, the ODK must be attached to the runtime with DMConnect, and the runtime project file must be resolved through DMGetRuntimeProject. Both calls are required to obtain a valid project path that TLGEnumVariablesEx accepts.

Step 1 - Skeleton of the Enumeration DLL

Create a new Win32 DLL project in Visual Studio, add the ODK include directory (e.g. C:\Program Files (x86)\Siemens\Automation\WinCC\ODK\include) to Additional Include Directories, and link against dmclient.lib, tlgclient.lib, and odk_lib.lib (paths under ODK\lib). Export every C function with __declspec(dllexport) so WinCC can resolve it through a plan / action or a Global Script C function.

/* File: TagLogEnum.c - WinCC ODK enumeration demo */
#include "dmclient.h"
#include "tlg_api.h"
#include "apdefap.h"      /* for __object_create, GetPicture, GetObject */
#include 
#include 

#define ARCHIVE_NAME "ProcessArchive"
#define COMBO_NAME   "Control1"
#define PIC_NAME     "Picture1"

/* Shared user buffer: filled by the callback, drained by the API call. */
typedef struct {
    char  szItems[64][MAX_TLG_NAME];
    DWORD dwCount;
} USER_CTX;

static USER_CTX g_ctx;          /* simple, single-threaded; not re-entrant */

/*------------------------------------------------------------*/
/*  Callback - receives one TLG_VARIABLE_INFO per call.        */
/*------------------------------------------------------------*/
static BOOL CALLBACK MyTLGEnumVariablesExCallback(
    PTLG_VARIABLE_INFO lpvi,
    LPVOID            pUser)
{
    USER_CTX* p = (USER_CTX*)pUser;
    if (p->dwCount < 64 && lpvi != NULL) {
        strncpy_s(p->szItems[p->dwCount],
                  sizeof(p->szItems[0]),
                  lpvi->szVariableName, _TRUNCATE);
        p->dwCount++;
    }
    return TRUE;                /* return TRUE to receive the next entry */
}

/*------------------------------------------------------------*/
/*  Public C entry - call from a WinCC Global Script action.  */
/*------------------------------------------------------------*/
BOOL __declspec(dllexport) EnumArchiveAndFillCombo(void)
{
    CMN_ERROR scError = {0};
    BOOL      bOK;
    __object *pdl  = NULL;
    __object *pic  = NULL;
    __object *obj  = NULL;
    DWORD     i;

    /* 1. Attach to the ODK runtime. */
    bOK = DMConnect(NULL, NULL, NULL, &scError);
    if (!bOK) {
        printf("#E101: DMConnect failed: %s\r\n", scError.szErrorText);
        return FALSE;
    }

    /* 2. Reset the user buffer. */
    memset(&g_ctx, 0, sizeof(g_ctx));

    /* 3. Enumerate variables of the archive. */
    bOK = TLGEnumVariablesEx(ARCHIVE_NAME,
                             MyTLGEnumVariablesExCallback,
                             &g_ctx,
                             &scError);
    if (!bOK) {
        printf("#E102: TLGEnumVariablesEx failed: %s\r\n",
               scError.szErrorText);
        DMDisconnect(&scError);
        return FALSE;
    }

    /* 4. Acquire the current picture and the combobox object. */
    pdl = __object_create("PDLRuntime");
    if (!pdl) { DMDisconnect(&scError); return FALSE; }

    pic = pdl->GetPicture(PIC_NAME);
    if (pic) {
        obj = pic->GetObject(COMBO_NAME);
        if (obj) {
            /* 5. Empty the existing list, then add each tag name. */
            obj->Clear();
            for (i = 0; i < g_ctx.dwCount; ++i) {
                obj->AddItem(g_ctx.szItems[i]);
            }
            __object_delete(obj);
        } else {
            printf("#W103: combobox '%s' not found on '%s'\r\n",
                   COMBO_NAME, PIC_NAME);
        }
        __object_delete(pic);
    } else {
        printf("#W104: picture '%s' not found in current screen\r\n",
               PIC_NAME);
    }
    __object_delete(pdl);

    DMDisconnect(&scError);
    return TRUE;
}

Step 2 - Calling the Enumeration from a Picture Event

ODK C functions are not directly callable from a picture event; the standard mechanism is a Global Script C action that invokes the exported DLL function. Create a new C action in the WinCC Explorer under Global Script > C-Actions with the following body:

/* Global Script C action - call this from a picture's OnOpen event */
#include "apdefap.h"

void OnOpen_Picture1(void)
{
    /* The exported C function lives in TagLogEnum.dll, */
    /* loaded in the project via 'External Files' or 'Additional Tasks'. */
    EnumArchiveAndFillCombo();
}

Wire the action to the picture:

  1. Open the picture in Graphics Designer.
  2. Select the picture root and open the Event > Open configuration dialog.
  3. Choose Direct Connection or assign a Global Script C action by name.
  4. Confirm by saving the picture and recompiling it (menu File > Save and Compile).
Object access from inside the callback does not work. The original forum post attempted to call __object_create directly inside the ODK callback. The runtime APIs expect to be invoked from the graphics thread, not from a worker callback. Always buffer the data into a user structure and process the picture objects after TLGEnumVariablesEx returns.

Step 3 - Optional Project-Wide Enumeration with DMEnumVariables

When the archive name is dynamic or the project stores a variable prefix, use DMEnumVariables with a name filter to walk the entire runtime project. The pattern below restricts the result to internal float and DWORD variables matching *E103*:

static BOOL CALLBACK MyDMEnumCallback(
    LPDM_VARKEY lpdmVarKey,
    LPVOID      lpvUser)
{
    USER_CTX* p = (USER_CTX*)lpvUser;
    if (p->dwCount < 64) {
        strncpy_s(p->szItems[p->dwCount],
                  sizeof(p->szItems[0]),
                  lpdmVarKey->szName, _TRUNCATE);
        p->dwCount++;
    }
    return TRUE;
}

BOOL __declspec(dllexport) EnumProjectVars(void)
{
    CMN_ERROR     scError = {0};
    char          szProjectFile[MAX_PATH] = {0};
    DM_VARFILTER  scVarFilter = {0};
    DWORD         adwVarType[2] = { DM_VARTYPE_FLOAT, DM_VARTYPE_DWORD };
    char          szName[] = "*E103*";
    BOOL          bOK;

    if (!DMConnect(NULL, NULL, NULL, &scError))               return FALSE;
    if (!DMGetRuntimeProject(szProjectFile,
                             sizeof(szProjectFile),
                             &scError)) { DMDisconnect(&scError); return FALSE; }

    memset(&g_ctx, 0, sizeof(g_ctx));
    scVarFilter.dwFlags    = DM_VARFILTER_NAME | DM_VARFILTER_TYPE;
    scVarFilter.dwNumTypes = 2;
    scVarFilter.pdwTypes   = adwVarType;
    scVarFilter.lpszName   = szName;

    bOK = DMEnumVariables(szProjectFile,
                          &scVarFilter,
                          MyDMEnumCallback,
                          &g_ctx,
                          &scError);
    DMDisconnect(&scError);
    return bOK;
}

Step 4 - Build, Deploy, and Activate

  1. Compile the project in Release / Win32. The output TagLogEnum.dll must match the platform of the WinCC Runtime (always Win32 for V7.5; x64 builds are not loadable).
  2. Copy TagLogEnum.dll into one of: the WinCC project folder, a subfolder registered through Computer > Properties > Graphics Runtime > Additional Tasks > Add-ons, or the WinCC bin directory.
  3. In WinCC Explorer, right-click Computer and verify the DLL is listed under the startup modules.
  4. Activate the project. Open the picture that hosts the combobox. The OnOpen event should fire the action and populate the list within one redraw cycle.

Step 5 - Verification

Check Expected result
Diagnostic output printf All tag names appear in the WinCC Diagnostic Window prefixed with #E102 only on failure.
Combobox on Picture1 Every archive tag from ProcessArchive appears as one entry, in the order defined in Tag Logging.
Selection Choosing a row sets the bound process tag (e.g. ComboIndex) to the array index.
Re-open picture List is rebuilt from scratch - confirms the callback is re-entered on every OnOpen.

Troubleshooting Matrix

Symptom Likely cause Fix
TLGEnumVariablesEx returns FALSE, szErrorText = "archive not found". lpszArchiveName does not match the configured TagLogging archive name (case sensitive). Check the name in Tag Logging editor and pass it byte-exact.
Callback never fires. DMConnect was not called or returned FALSE. Always start with DMConnect(NULL, NULL, NULL, &scError).
Callback fires, __object_create returns NULL. Code is running outside the graphics thread (for example a WinCC Scheduler action instead of a Global Script). Move the picture object manipulation into a Global Script C action triggered by an event.
Combobox stays empty. GetPicture called too early (picture not yet open). Run from OnOpen event, not from project activation.
Only first tag appears. Buffer overflow caused the loop to exit, or callback returned FALSE. Verify the callback returns TRUE for every entry, and increase the buffer size.
DLL not loaded at runtime. Wrong platform (x64 vs Win32) or missing dependency (e.g. msvcr120.dll). Build Win32, distribute matching VC++ runtimes, confirm dumpbin /dependents shows only system DLLs.
Unresolved external TLGEnumVariablesEx. Wrong import library (used DM client lib instead of TLG client lib). Add tlgclient.lib to Additional Dependencies.

CMN_ERROR Reference Values

Field Type Description
dwError1 DWORD Low-level ODK error code; e.g. 0x80040001 archive not found.
dwError2 DWORD Reserved for layer-specific code (TagLogging / DataManager).
szErrorText char[256] Human-readable English text returned by the ODK.
szSource char[256] Source module (e.g. tlg_api.dll).

Modern Alternative - WinCC Unified (V16+)

On TIA Portal based Unified Runtime (V16, V17, V18, V19, V20), archive tags are configured through the Logging tags editor and can be assigned to a Log, an array element, or a UDT element. The official Siemens documentation Configuring logging tags (RT Unified) describes the assignment rules.

For runtime enumeration on Unified, prefer JavaScript inside the HMI screen using the Tags and Logging namespaces, e.g.:

// Unified JavaScript - enumerate HMI tags and fill an IO field list
let tagSet = Tags.GetTagList("Process.*");
let combo  = Screen.Items("ComboBox1");
combo.Items = tagSet.map(t => t.Name);

Use the C-ODK path only when the project must stay on WinCC V7.x or V7.5; otherwise the Unified JavaScript API is the recommended, license-free replacement.

Field-Commissioning Checklist

  • Confirm WinCC is V7.4 or V7.5 with the ODK option installed.
  • Compile the DLL in Release / Win32 and place it in the WinCC project folder.
  • Register the Global Script C action in the picture's OnOpen event.
  • Open the WinCC Diagnostic Window to inspect printf traces during the first activation.
  • Validate the combobox list against the TagLogging editor (it must match, including disabled tags - enumeration returns all configured tags, regardless of acquisition state).
  • Document the archive name in the project header so maintenance engineers can update it without recompiling.

Safety and Best-Practice Notes

Do not call picture APIs from the ODK callback. They must run on the graphics thread, and the runtime will deadlock or silently fail if invoked from the ODK worker. The pattern is: collect in the callback, push to the picture after TLGEnumVariablesEx returns.
  • Wrap the entire flow in DMConnect / DMDisconnect even when only TLG functions are used - it guarantees the runtime database handle is valid.
  • Use strncpy_s / strcpy_s instead of legacy strcpy to stay compatible with the secure CRT default of Visual Studio 2015+.
  • Limit the number of items the callback can store (here: 64) and document the upper bound - it prevents stack overflow when an archive contains thousands of tags.
  • Never run the ODK DLL on a development workstation that is not hosting the runtime; the call will fail with "runtime not started".

FAQ

Which WinCC versions support TLGEnumVariablesEx?

WinCC V7.0 SP3 and later, including V7.2, V7.3, V7.4, and V7.5. The function is part of the ODK client library tlgclient.lib shipped with the SIMATIC WinCC Information System (KB entry 109762744).

Why does the combobox stay empty even though the callback is called?

The ODK callback runs in a worker thread that is not allowed to call __object_create, GetPicture, or GetObject. Buffer the names in a user structure and update the combobox from the calling Global Script C action after TLGEnumVariablesEx returns.

Can I enumerate all WinCC tags, not only those in one archive?

Yes. Use DMEnumVariables with a DM_VARFILTER structure (fields DM_VARFILTER_NAME and DM_VARFILTER_TYPE) and a name pattern such as *E103*. The WinCC DataManager will return every internal or external tag matching the filter.

What is the maximum number of archive tags I can enumerate at once?

The ODK has no hard limit, but practical limits come from the callback's stack usage and the user buffer. For archives with > 1000 tags, allocate the user buffer on the heap and reuse it across calls instead of placing a large array on the stack.

Is there a non-C way to populate a combobox with archive tags?

On TIA Portal WinCC Unified (V16+), use JavaScript with Tags.GetTagList and assign the result to ComboBox.Items. The configuration of the underlying logging tags is documented at Configuring logging tags (RT Unified).

Back to blog