--- name: session-and-project description: Session and project management for TIA Portal Openness. Use when working with TiaPortal instances, opening/closing projects, attaching to processes, archiving, and managing the project lifecycle. metadata: siemens-depends-on: "openness-base" --- # Session and Project Management ## Overview This skill covers patterns for managing TIA Portal sessions and projects using the Siemens Engineering SDK. It includes starting new instances, attaching to running processes, opening/closing projects, and archiving/retrieving project files. These operations form the foundation of any TIA Portal Openness automation — you must establish a session and load a project before performing any engineering tasks. ## Required Namespaces ```csharp using Siemens.Engineering; ``` ## Required Assemblies - `Siemens.Engineering.Base.dll` --- ## 🛑 MANDATORY CHECK BEFORE CODING: TIA Portal Version Requirement **BEFORE you write ANY code that uses the Siemens Engineering SDK, you MUST ask the user for their TIA Portal version.** This is a **blocking requirement** — do NOT proceed to generate code without a confirmed, valid TIA Portal version. ### Minimum Supported Version These skills only support **TIA Portal V21 and higher**. Any version below V21 is **NOT supported**. ### How to Ask Use the `ask_user` tool with a clear message and a freeform text input for the version number. Example: ``` I need to know your TIA Portal version to set up the SDK references correctly. What TIA Portal version are you using? ``` Use the `ask_user` tool as follows: ``` question: "What TIA Portal version are you using? (minimum V21)" allow_freeform: true ``` ### Validation Rules After receiving the user's response, you MUST validate: | Condition | Action | |---|---| | Version **>= 21** | Accept and use it in the code | | Version **< 21** | **REJECT** — inform the user that TIA Portal V21 or higher is required, then re-ask | | Non-numeric response | **REJECT** — clarify that a numeric version is expected, then re-ask | | No response / unclear | **REJECT** — re-ask politely | ### Example of Rejection & Re-asking If the user says "18" or "19": ``` I'm sorry, but these skills only support TIA Portal V21 and higher. V18 is not compatible with this automation framework. Please provide a TIA Portal version of 21 or higher, or upgrade your TIA Portal installation. ``` ### What You Must NOT Do - ❌ Do NOT assume a default version without asking - ❌ Do NOT use V18 or V19 as default in generated code - ❌ Do NOT skip asking just because the user did not mention a version - ❌ Do NOT start generating code before you have a valid TIA Portal version (>= 21) --- ### Find SDK DLLs via Registry **Description:** The Siemens Engineering SDK DLLs are not in the GAC and their locations depend on the installed TIA Portal version. Use the Windows registry to discover the installation path dynamically. **SDK-Style .csproj Example:** Always use the SDK-style project format with `net48` target framework. ```xml 21 $([MSBuild]::GetRegistryValueFromView(`HKEY_LOCAL_MACHINE\SOFTWARE\Siemens\Automation\_InstalledSW\TIAP$(TiaVersion)\Global`, `Path`, '', RegistryView.Registry64, RegistryView.Registry32)) net48 latest enable Exe $(TiaPortalLocation)\PublicAPI\V$(TiaVersion)\net48\Siemens.Engineering.Base.dll False False ``` **Key Details:** - The registry key path is `HKEY_LOCAL_MACHINE\SOFTWARE\Siemens\Automation\_InstalledSW\TIAP$(TiaVersion)\Global` - The `Path` value under this key contains the TIA Portal installation directory - DLLs are located under `\PublicAPI\V$(TiaVersion)\net48\` - Use `RegistryView.Registry64` first, falling back to `RegistryView.Registry32` for compatibility - **Minimum version is V21** — always set `21` as the minimum default ### Assembly Resolver — `[MethodImpl(NoInlining)]` Requirement Any method that registers `AppDomain.CurrentDomain.AssemblyResolve` **must not** also contain references to `Siemens.Engineering` types in the same method body. If they are in the same method, the JIT may compile the `Siemens.Engineering` references before the resolve handler is registered, causing an `AssemblyLoadException` on startup. **Pattern:** split into two methods and annotate the entry point `[MethodImpl(MethodImplOptions.NoInlining)]` to prevent the JIT from merging them: ```csharp // Entry point — registers the resolver BEFORE any Openness types are referenced [MethodImpl(MethodImplOptions.NoInlining)] public static void Initialize() { AppDomain.CurrentDomain.AssemblyResolve += ResolveOpenness; InitializeCore(); // Openness types are only referenced inside here } // Separated method — JIT compiles this only AFTER the resolver is already registered [MethodImpl(MethodImplOptions.NoInlining)] private static void InitializeCore() { var tia = new TiaPortal(TiaPortalMode.WithoutUserInterface); // ... use Openness types freely ... } ``` Never merge the `AssemblyResolve` registration and Openness API calls into a single method. ## Common Patterns ### Start New TIA Portal Instance (with UI) **Description:** Launches a new TIA Portal process with a visible user interface. Use this mode when you want the user to see and interact with TIA Portal during automation. **Example:** ```csharp var tia = new Siemens.Engineering.TiaPortal(TiaPortalMode.WithUserInterface); ``` **Key Types and Methods:** - `TiaPortal` — the main entry point for all Openness operations - `TiaPortalMode.WithUserInterface` — enum value that shows the TIA Portal UI --- ### Discover Running TIA Portal Processes **Description:** Retrieves a list of all running TIA Portal processes on the machine. Useful when you want to attach to an already-open instance instead of starting a new one. **Example:** ```csharp var tiaProcesses = Siemens.Engineering.TiaPortal.GetProcesses(); ``` **Key Types and Methods:** - `TiaPortal.GetProcesses()` — returns a collection of `TiaPortalProcess` objects - `TiaPortalProcess` — represents a running TIA Portal process --- ### Attach to Existing TIA Portal Process **Description:** Connects to an already-running TIA Portal process. This avoids starting a duplicate instance and lets your automation work alongside the user's active session. **Example:** ```csharp foreach (var tiaPortalProcess in tiaProcesses) { TiaPortalInstance = tiaPortalProcess.Attach(); } ``` **Key Types and Methods:** - `TiaPortalProcess.Attach()` — attaches to the process and returns a `TiaPortal` instance - `TiaPortal` — the session object used for all subsequent operations --- ### Get & Dispose Current Process **Description:** Retrieves the TIA Portal process that owns the current automation context and disposes of it when no longer needed. Important for cleanup and avoiding orphaned processes. **Example:** ```csharp tia.GetCurrentProcess().Dispose(); ``` **Key Types and Methods:** - `TiaPortal.GetCurrentProcess()` — returns the process associated with the session - `Process.Dispose()` — releases resources held by the process 🛑 **Do NOT call `Dispose()` on `TiaPortalProcess` handles obtained from `TiaPortal.GetProcesses()` in TIA Portal V21.** Confirmed root cause: `Dispose()` on a process handle from `GetProcesses()` **tears down the live TIA Portal instance**, not just the local proxy/handle. This is easy to trigger accidentally — e.g. a "refresh the process list" action that calls `Dispose()` on each previously-enumerated handle before re-listing will silently crash the user's running TIA Portal session. `GetCurrentProcess().Dispose()` (your own session's process, shown above) is a different, safe case — the danger is specifically iterating `GetProcesses()` results (e.g. to build an attach-target list) and disposing them. ```csharp // WRONG — kills the live TIA Portal instance in V21 foreach (var p in Siemens.Engineering.TiaPortal.GetProcesses()) { // ... use p for display/attach ... p.Dispose(); // <-- do not do this } // CORRECT — just don't dispose handles from GetProcesses(); let them be garbage collected foreach (var p in Siemens.Engineering.TiaPortal.GetProcesses()) { // ... use p for display/attach only ... } ``` --- ### Iterate Open Projects **Description:** Enumerates all projects currently loaded in the TIA Portal session. Use this pattern to discover available projects without knowing their file paths. **Example:** ```csharp foreach (var project in TiaPortalInstance.Projects) { // Access each project } ``` **Key Types and Methods:** - `TiaPortal.Projects` — a `ProjectCollection` (implements `IEngineeringComposition`) --- ### Open Project from File (.ap19) **Description:** Opens an existing TIA Portal project from its `.ap19` file on disk. **Example:** ```csharp FileInfo fileInfo = new("C:/MyPathToTheTiaProject.ap19"); tia.Projects.Open(fileInfo); ``` **Key Types and Methods:** - `ProjectCollection.Open(FileInfo)` — opens the project file and returns a `Project` - `Project` — represents an open TIA Portal project --- ### Retrieve Project from Archive (.zap) **Description:** Extracts a TIA Portal project from a `.zap` archive file. The archive is unpacked to a temporary directory before being loaded. **Example:** ```csharp FileInfo sourcePath = new(myStep7ProjectArchivePath); DirectoryInfo targetDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); Project = TiaPortalInstance.Projects.Retrieve(sourcePath, targetDir); ``` **Key Types and Methods:** - `ProjectCollection.Retrieve(FileInfo, DirectoryInfo)` — extracts and loads the archived project - `Project` — the loaded project instance --- ### Save Project **Description:** Saves all changes to the current project on disk. **Example:** ```csharp project.Save(); ``` **Key Types and Methods:** - `Project.Save()` — persists project changes to disk --- ### Archive (Export) Project to .zap **Description:** Exports the project to a compressed `.zap` archive file. Useful for backup or distribution. **Example:** ```csharp project.Archive(new DirectoryInfo(Path.GetTempPath()), "dummy.zap21", ProjectArchivationMode.Compressed); ``` **Key Types and Methods:** - `Project.Archive(DirectoryInfo, string, ProjectArchivationMode)` — creates the archive - `ProjectArchivationMode.Compressed` — produces a smaller archive file --- ### Close Project **Description:** Closes the project, releasing it from the TIA Portal session. **Example:** ```csharp project.Close(); ``` **Key Types and Methods:** - `Project.Close()` — unloads the project from the session --- ### Gate teardown by project and portal-instance ownership independently Do **not** unconditionally call `Project.Close()` or `TiaPortal.Dispose()` from shared teardown code. Track ownership of the project and the portal instance separately: an application can attach to an existing, empty portal instance and then open a project itself. A single ownership flag conflates those independent lifecycles. ```csharp public Task CloseAsync() { if (_ownsProject) { _project?.Close(); } if (_ownsTiaPortalInstance) { _tiaPortal?.Dispose(); } return Task.CompletedTask; } ``` **Key points:** - Close the project only when `_ownsProject`; dispose the portal only when `_ownsTiaPortalInstance`. - An attached portal can be user-owned while a project opened by this application is application-owned; close only the latter during teardown. - `PlugNew`, `CreateWithItem`, and other mutations stay **in memory** until `Project.Save()` is called. Save explicitly before teardown when this application owns and intends to persist its changes; shared teardown must not save automatically. --- ### One Project Per TiaPortal Instance **Description:** A single `TiaPortal` instance can hold only **one** open project at a time. This is a hard runtime limit — it is NOT lifted by `TiaPortalMode.WithUserInterface`. Attempting to open a second project into the same portal instance throws `EngineeringTargetInvocationException` with the message "Another project is already open". To work with two projects simultaneously (e.g. for cross-project comparison or copying), create two separate `TiaPortal` instances: **Example:** ```csharp // WRONG — throws if a project is already open in tia tia.Projects.Open(secondProjectFile); // CORRECT — use a second TiaPortal instance for the second project using var tia1 = new TiaPortal(TiaPortalMode.WithoutUserInterface); using var tia2 = new TiaPortal(TiaPortalMode.WithoutUserInterface); var project1 = tia1.Projects.Open(firstProjectFile); var project2 = tia2.Projects.Open(secondProjectFile); ``` **Key Types and Methods:** - `TiaPortal` — each instance is an independent portal process; one project per instance - `EngineeringTargetInvocationException` — thrown when opening a second project into an already-occupied portal instance --- ### TIA Portal Reference (Secondary) Project **Description:** Openness has no equivalent of the TIA Portal GUI's "Reference project" feature. Instead, it offers its own **read-only, hidden secondary project** that can be opened alongside the primary project. Use it to copy data into the primary project or to compare the two — the primary project remains the only one the interactive user sees and edits. **Key points:** - The secondary project is read-only from Openness — do not attempt to modify it directly; copy objects from it into the primary project instead. - Because a `TiaPortal` instance holds only **one** project open for *editing* (see above), the secondary/reference project is a distinct mechanism, not a second primary project. ### Multiuser Support via ProjectBase **Description:** Since TIA Portal V17, single-user and multiuser projects share a common base class, `ProjectBase`. Write code against `ProjectBase` instead of the concrete `Project` (single-user) or `MultiuserProject` (multiuser, via TIA Project-Server) type, and the same code supports both project kinds without branching. **Example:** ```csharp // Works for both single-user (Project) and multiuser (MultiuserProject) projects void ProcessProject(ProjectBase project) { Console.WriteLine(project.Name); } ``` **Key Types and Methods:** - `ProjectBase` — common base class for `Project` and `MultiuserProject` - `ProjectBase.CreationTime` / `ProjectBase.LastModified` — project-level change detection (see [`change-detection`](../change-detection/SKILL.md)) ## Quick Reference | Method / Pattern | Purpose | |---|---| | `new TiaPortal(TiaPortalMode.WithUserInterface)` | Start a new TIA Portal instance with UI | | `TiaPortal.GetProcesses()` | Discover running TIA Portal processes | | `TiaPortalProcess.Attach()` | Attach to an existing TIA Portal process | | `TiaPortal.GetCurrentProcess().Dispose()` | Get and dispose the current process (safe — your own session's process) | | Never `Dispose()` handles from `GetProcesses()` | Disposing a process handle obtained via `GetProcesses()` kills the live TIA Portal instance in V21 | | `TiaPortal.Projects` (iterate) | Enumerate open projects | | `ProjectCollection.Open(FileInfo)` | Open a project from `.ap19` file | | `ProjectCollection.Retrieve(FileInfo, DirectoryInfo)` | Extract project from `.zap` archive | | `Project.Save()` | Save project changes to disk | | `Project.Archive(...)` | Export project to `.zap` archive | | `Project.Close()` | Close and unload the project | | Two `TiaPortal` instances | Required to have two projects open simultaneously | | `[MethodImpl(NoInlining)]` split pattern | Register `AssemblyResolve` and use Openness types in separate methods to prevent JIT premature compilation | | Secondary/reference project | Read-only hidden project for copying data into or comparing against the primary project | | `ProjectBase` | Common base type for `Project`/`MultiuserProject` — write code once for single- and multiuser projects | ## Related Files - [`engineering-objects`](../engineering-objects/SKILL.md) — patterns for working with engineering objects within a project - [`crash-diagnosis`](../crash-diagnosis/SKILL.md) — diagnosing hard TIA Portal crashes; breadcrumb patterns; assembly resolver and IPC context - [`change-detection`](../change-detection/SKILL.md) — `ProjectBase.CreationTime`/`LastModified` for project-level change detection - [`threading-and-concurrency`](../threading-and-concurrency/SKILL.md) — thread affinity of the `TiaPortal` instance created/attached here ## Exception Handling - **Process Attach Failures:** If no TIA Portal processes are found, `GetProcesses()` returns an empty collection. Verify the collection before attempting to attach. - **File Not Found:** Opening or retrieving a project with an invalid path will throw an `ArgumentException` or `IOException`. Validate file existence before calling `Open()` or `Retrieve()`. - **Project Already Open:** A single `TiaPortal` instance can only hold one project. Opening a second project throws `EngineeringTargetInvocationException` ("Another project is already open"). Use a second `TiaPortal` instance for simultaneous project access. - **Archive Corruption:** A corrupted `.zap` archive will throw during `Retrieve()`. Wrap archive operations in try/catch blocks. - **Killing the live instance:** Calling `Dispose()` on a `TiaPortalProcess` obtained from `GetProcesses()` (as opposed to your own `GetCurrentProcess()`) tears down the live TIA Portal instance in V21 — see the warning above. This is a common trap in "refresh process list"/attach-picker UI code.