WinCC Close External Application Using C-Script and VBScript

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

1. Problem Scope and Architecture Overview

WinCC Runtime hosts two fundamentally different kinds of visual surfaces. The first is an internal Picture (PDL) rendered inside the WinCC Graphics Runtime. The second is an external Win32 process (for example, notepad.exe) launched from a script via ProgramExecute or WScript.Shell.Run. The two surfaces do not share a parent-child window relationship, so the standard "picture closes -> children close" rule does not apply when one of the surfaces is an external executable.

The engineering problem becomes: how do you guarantee that notepad.exe (or any external program started from a button on a PDL) is terminated whenever the user closes the picture that originally launched it? This article documents the working C-Script and VBScript solutions for both WinCC V7.x (SIMATIC WinCC) and TIA Portal WinCC Professional / Comfort, including the correct event hooks, the Win32 API sequence (FindWindow -> PostMessage WM_CLOSE), and the taskkill fallback.

Reference: The Siemens Support entry FAQ 415808 - In WinCC / WinCC Professional Runtime, how do you bring the window of another program into the foreground? confirms that external applications are outside the WinCC Graphics Runtime window hierarchy.

2. Internal PictureWindow vs External Process

Attribute Internal PictureWindow External Process (e.g. notepad.exe)
Host WinCC Graphics Runtime (single process) Independent Win32 process
Window Handle Owned by WinCC Runtime Independent top-level window
Closes with parent picture? Yes - automatic via PictureWindow object No - requires script or taskkill
Lifecycle event available? OnOpen / OnClose of PictureWindow None within WinCC; must poll or hook
Lifecycle trigger inside WinCC Picture change, picture close User closes WinCC Runtime or changes picture

The PictureWindow object in WinCC Graphics Designer is a container. When you load a different PDL into the parent picture, the child PictureWindow is destroyed automatically. An notepad.exe window is not embedded in that container - it is a sibling top-level window of the WinCC Runtime itself, so it survives the picture change unless you explicitly close it.

3. WinCC Picture Event Reference

WinCC V7.x Graphics Designer exposes the following events on a picture object (right-click the picture background, choose Properties -> Events):

Event Fires When Typical C Function Name
Open Picture is loaded / shown OpenPicture(...)
Close Picture is closed / replaced ClosePicture(...)
Mouse events Mouse interaction in picture OnClick(...) etc.
Keyboard events Key press while picture has focus OnKeyDown(...) etc.
Base picture exception: The main runtime picture (the one loaded by the Runtime start configuration) does not fire a Close event in the normal lifecycle because the Runtime process ends instead. The standard Siemens guidance - see the FAQ at support.industry.siemens.com/cs/ww/en/view/415808 - is therefore to either hook the global Runtime shutdown, the Windows session logoff event, or to attach the cleanup logic to a PictureWindow that does close.

In TIA Portal WinCC Professional the same events are exposed in the picture properties dialog of the HMI device. The VBScript function name generated is OnClose (or OnOpen).

4. Launching External Applications

There are three reliable launch paths. Pick the one matching your platform.

4.1 WinCC V7.x - C-Script with ProgramExecute

#include "apdefap.h"

void OnClick(char* lpszPictureName,
             char* lpszObjectName,
             char* lpszPropertyName,
             UINT  nFlags,
             int   x,
             int   y)
{
    ProgramExecute("notepad.exe C:\\Logs\\Runtime.log");
}

ProgramExecute is declared in apdefap.h and synchronously launches the external process. If you need the script to continue while the external program runs, use WinExec("notepad.exe", SW_SHOWNORMAL) from the Win32 API instead.

4.2 TIA WinCC Professional - VBScript with WScript.Shell

Sub OnClick(ByVal Item)
    Dim oShell
    Set oShell = CreateObject("WScript.Shell")
    oShell.Run "notepad.exe C:\Logs\Runtime.log", 1, False
    Set oShell = Nothing
End Sub

The third argument (False) means "do not wait for completion". This is mandatory for a button click handler - otherwise the HMI freezes until Notepad is closed by the user.

4.3 WinCC Unified (TIA V16+) - JavaScript

export function Button_1_OnClick(item) {
    const { exec } = imports("@runtime/Application");
    // Project property "ExtProgram" = "notepad.exe"
    const program = Tags("ExtProgram").Read();
    exec(program);
}

WinCC Unified uses the JavaScript-based action API. The exec method is documented in the TIA Portal WinCC Unified online help.

5. Capturing the Window Handle with FindWindow

To close a specific external window without killing the entire process, you must obtain its HWND. The Win32 API function FindWindow matches either by window class name or by window title. For Notepad the class name is Notepad (Windows 10/11) or NotepadClass (older). The title is dynamic.

5.1 C-Script (WinCC V7) - Win32 API Access

WinCC C-Scripts can call Win32 API functions directly. Declare once in a project header:

// In a project header file included by every C action
typedef unsigned long DWORD;
typedef unsigned int  UINT;
typedef int           BOOL;
typedef char*         LPSTR;
typedef void*         HWND;
typedef long          LRESULT;

#define WM_CLOSE 0x0010

HWND  __stdcall FindWindowA(LPSTR lpClassName, LPSTR lpWindowName);
LRESULT __stdcall PostMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

5.2 VBScript (TIA WinCC Professional) - Win32 API via external helper

VBScript cannot call FindWindow directly. Two practical options:

  1. Use the AppActivate method on WScript.Shell to focus the window, then send Alt+F4 with SendKeys.
  2. Use a small VB6 / .NET helper COM object that exposes FindWindow and PostMessage as methods.
Sub CloseNotepadWindow()
    Dim oShell
    Set oShell = CreateObject("WScript.Shell")
    ' Returns True if window found
    If oShell.AppActivate("Runtime.log - Notepad") Then
        oShell.SendKeys "%{F4}"   ' Alt+F4 = WM_CLOSE on focused window
    End If
    Set oShell = Nothing
End Sub
Reliability: AppActivate + SendKeys depends on keyboard focus and is fragile in headless / panel deployments. Prefer taskkill /IM or a custom COM helper on production systems.

6. Closing the External Window: WM_CLOSE vs DestroyWindow vs TerminateProcess

Method Behavior Cleanup Recommended?
PostMessage(hWnd, WM_CLOSE, 0, 0) Sends WM_CLOSE; well-behaved apps save & exit Graceful Yes
DestroyWindow(hWnd) Immediately destroys the window Ungraceful - process may survive without a main window No
TerminateProcess(hProcess, 0) Hard-kills the process Ungraceful - no chance to save Only as fallback
taskkill /IM notepad.exe /F Hard-kills the process Ungraceful Production fallback

The correct sequence is:

  1. Save the HWND returned by FindWindow into a project-wide tag (WinCC tag of type Text tag 8-bit character set for hex storage, or a custom internal variable).
  2. In the Close handler, retrieve the saved HWND and call PostMessage(hWnd, WM_CLOSE, 0, 0).
  3. If the process is still alive after a 500 ms delay, fall back to taskkill /IM notepad.exe /F.

7. Why There Is No "PictureClose" Event on the Base Picture

WinCC Runtime architecture reserves Open / Close events for picture change operations. The base picture loaded at Runtime start has no preceding picture, and Runtime termination destroys the entire process rather than closing a picture object. The Siemens support response that there is "no PictureClose event" refers to this base-picture case, and the standard mitigation patterns are:

  • Embed the launch in a PictureWindow: the PictureWindow itself fires OnClose when the parent picture changes, so the cleanup script hooks there.
  • Use a project-wide shutdown hook: in WinCC V7 the Computer Properties -> Shutdown event can run a global cleanup script. TIA WinCC Professional provides Schedules and the Runtime's own OnShutdown event.
  • Use Windows session events: subscribe to WM_QUERYENDSESSION / WM_ENDSESSION from a global C action to receive Windows logoff / shutdown notifications.

8. Complete C-Script Example for WinCC V7.x

This example uses a PictureWindow to launch Notepad on a button click and close it on PictureWindow close.

8.1 Button click action - launch and store HWND

#include "apdefap.h"

// Persisted across actions via a project tag "@NotepadHwnd"
extern "C" long FindWindowA(const char*, const char*);

void OnClick(char* lpszPictureName,
             char* lpszObjectName,
             char* lpszPropertyName,
             UINT  nFlags,
             int   x,
             int   y)
{
    ProgramExecute("notepad.exe");

    // Allow notepad to create its window
    DWORD tick = GetTickCount();
    long  hwnd = 0;
    while ((GetTickCount() - tick) < 3000)  // up to 3 s
    {
        hwnd = FindWindowA("Notepad", NULL);
        if (hwnd != 0) break;
        Sleep(100);
    }

    // Persist HWND as decimal text in a WinCC tag
    char buf[16];
    sprintf(buf, "%ld", hwnd);
    SetTagChar("@NotepadHwnd", buf);
}

8.2 PictureWindow close action - send WM_CLOSE

#include "apdefap.h"

extern "C" long PostMessageA(long hWnd, unsigned int Msg, long wParam, long lParam);
extern "C" DWORD GetTickCount(void);
extern "C" void Sleep(DWORD ms);

void OnClose(char* lpszPictureName,
             char* lpszObjectName,
             char* lpszPropertyName,
             BOOL  bClosing)
{
    char buf[16];
    GetTagChar(buf, "@NotepadHwnd");
    long hwnd = atol(buf);
    if (hwnd != 0)
    {
        PostMessageA(hwnd, 0x0010 /* WM_CLOSE */, 0, 0);   // graceful

        // Optional: hard-kill fallback after 500 ms
        DWORD tick = GetTickCount();
        while ((GetTickCount() - tick) < 500) { Sleep(50); }
        // system("taskkill /IM notepad.exe /F >NUL");
    }
    SetTagChar("@NotepadHwnd", "");
}
Compiler note: WinCC's C compiler is based on Microsoft Visual C++. Link with user32.lib via the WinCC Graphics Designer project settings if Win32 APIs are not pre-resolved by apdefap.h.

9. Complete VBScript Example for TIA Portal WinCC Professional

TIA WinCC does not support C-Script. Use VBScript only. Hook the cleanup to the PictureWindow close event.

9.1 PictureWindow close event - hard kill via taskkill

Sub OnClose(ByVal Item)
    Dim oShell
    Set oShell = CreateObject("WScript.Shell")

    ' Wait briefly so any in-flight save dialog can be dismissed
    Dim wshExec
    Set wshExec = oShell.Run("taskkill /IM notepad.exe /F", 0, True)

    ' oShell.Run with bWaitOnReturn=True returns exit code
    ' 0 = success, 128 = no process found
    Set wshExec = Nothing
    Set oShell = Nothing
End Sub

9.2 Graceful alternative - AppActivate + SendKeys

Sub OnClose(ByVal Item)
    Dim oShell
    Set oShell = CreateObject("WScript.Shell")
    If oShell.AppActivate("Notepad") Then
        oShell.SendKeys "%{F4}"   ' Alt+F4 -> WM_CLOSE
    End If
    Set oShell = Nothing
End Sub

9.3 Polling fallback when no event is reliable

Sub CyclicCleanup()
    Dim oShell, intFound
    Set oShell = CreateObject("WScript.Shell")
    intFound = oShell.Run("tasklist /FI ""IMAGENAME eq notepad.exe"" /NH", _
                          0, True)
    If intFound = 0 Then
        oShell.Run "taskkill /IM notepad.exe /F", 0, True
    End If
    Set oShell = Nothing
End Sub

Schedule this VBScript via a 2-second cyclic trigger while the parent picture is visible.

10. Process Termination Fallback Using taskkill

The taskkill utility ships with every Windows installation since Windows XP and is the safest hard-kill mechanism for WinCC panels running Windows Embedded Standard 7 / Windows 10 IoT.

Switch Purpose
/IM <name> Match by image name
/PID <pid> Match by process ID
/F Force termination
/T Terminate child processes too
/F /FI "WINDOWTITLE eq Untitled*" Match by window title
taskkill /IM notepad.exe /F /T
taskkill /F /FI "WINDOWTITLE eq Runtime.log*"

Always verify the exit code (0 = success, 128 = no match, 1 = error) via oShell.Run(..., 0, True) in VBScript.

11. Commissioning and Verification Procedure

  1. Compile the script: in WinCC V7 Graphics Designer press Compile -> C-Editor. Look for unresolved Win32 references in the build log.
  2. Set Runtime properties: in Computer Properties -> Graphics Runtime ensure Runtime start picture is set to the picture containing your test button.
  3. Enable diagnostic output: use printf("%s\n", "...") in C-Script or HMIRuntime.Trace in VBScript to log to the WinCC diagnostic file WinCC_Sys_.log.
  4. Activate Runtime: launch the project. Click the button - Notepad must appear within 1 s.
  5. Verify handle capture: read the @NotepadHwnd tag in WinCC Explorer. It must contain a non-zero decimal value.
  6. Trigger the close path: change the base picture or close the PictureWindow. Verify Notepad disappears. If it does not, switch to taskkill /F fallback.
  7. Test Runtime shutdown: stop the Runtime. If the external program remains, hook the Shutdown event in Computer Properties to execute the cleanup script.
  8. Test Windows logoff: log off the panel. Verify that the WinCC shutdown event fires and terminates external processes.

12. Troubleshooting Matrix

Symptom Likely Cause Fix
Notepad does not close when picture changes HWND tag is empty or zero Re-check FindWindow timing; the window may not be created yet - add the 3 s wait loop
Notepad closes on the development PC but not on the panel Different Notepad version with different window class Use FindWindow(NULL, "Notepad") matching by title; or query class with Spy++
C-Script fails to compile - unresolved external symbol FindWindowA Win32 library not linked Add #pragma comment(lib, "user32.lib") at top of the action file
VBScript AppActivate returns False Window title does not exactly match (wildcards unsupported) Use partial title; or switch to taskkill
Notepad re-spawns itself Notepad has session-restart logic (Windows 10/11) Disable "Automatically save my restartable apps when I sign out" in Windows settings
taskkill returns exit code 128 Process already exited - not an error Ignore exit code 128 in the cleanup script
External program blocks HMI after launch Shell.Run called without False return Use oShell.Run "notepad.exe", 1, False
PictureWindow close event never fires Base picture is unloaded instead of picture change Hook Runtime shutdown or session logoff instead

13. Frequently Asked Questions

Does WinCC automatically close external programs when a picture is unloaded?

No. Only internal PictureWindow objects follow the parent picture lifecycle. External Win32 processes such as notepad.exe must be closed explicitly via PostMessage(WM_CLOSE) in C-Script or taskkill /F in VBScript. The Siemens FAQ at support.industry.siemens.com/cs/ww/en/view/415808 confirms this behavior.

Is there a PictureClose event I can attach a C-Script to in WinCC V7?

The Close event exists on picture objects that are loaded via picture change. The base runtime picture does not raise a Close event on Runtime exit because the process is terminated instead. Use the Runtime Shutdown event or a PictureWindow's OnClose as the hook point for cleanup scripts.

Can TIA Portal WinCC Professional run C-Scripts like WinCC V7?

No. TIA WinCC Professional, Comfort and Unified support VBScript (and JavaScript in Unified) only. C-Script is exclusive to WinCC V7.x and WinCC Flexible. Translate C-Script cleanup logic to WScript.Shell.Run "taskkill ..." or to a custom COM helper for Win32 API access.

What is the difference between DestroyWindow and PostMessage WM_CLOSE?

PostMessage(hWnd, WM_CLOSE, 0, 0) - documented on Microsoft Learn PostMessage - sends a WM_CLOSE notification that well-behaved applications handle by saving state and exiting. DestroyWindow immediately removes the window without notifying the application; the process may keep running without a UI. Prefer PostMessage WM_CLOSE; fall back to taskkill /F if the process does not exit within 500 ms.

How do I reliably close Notepad from a WinCC Comfort Panel running Windows IoT?

Use VBScript on the PictureWindow's OnClose event with oShell.Run "taskkill /IM notepad.exe /F", 0, True. Combine with a 2-second cyclic polling trigger while the picture is visible so any orphaned Notepad instance from a previous Runtime crash is also terminated at Runtime startup.

Back to blog