# Häufige Fragen

> Referenz: Antworten auf häufige Fragen zum OPC UA .NET SDK: Debugging, PowerShell, NuGet, Exceptions und Lizenz, S7-Steuerungen, Node-Typen und Sicherheitsthemen.

## Entwicklung

### Verbindungsabbruch beim Debuggen

**Problem:** Nach kurzer Unterbrechung oder bei schrittweiser Ausführung der Anwendung verliert der Client die Verbindung. Es kommt zur Ausnahme `BadServerNotConnected` mit der Meldung „The operation could not complete because the client is not connected to the server.“

Client und Server prüfen über eine KeepAlive-Logik, ob ihre Verbindung noch besteht: Innerhalb eines Zeitfensters, das Timeout-Einstellungen festlegen, müssen sie mindestens eine Nachricht austauschen. Wenn der Debugger die Anwendung anhält (Haltepunkt oder schrittweise Ausführung), stellt der Client in dieser Zeit keine Anfragen. Wenn die Pause dabei das Zeitfenster überschreitet, gilt die Verbindung als verloren.

Verwenden Sie für Debug-Sitzungen deshalb größere Timeouts, insbesondere einen größeren Wert für `OpcClient.SessionTimeout` (Standard 60000 ms). Wie oft der Client die Verbindung prüft, legt `OpcClient.KeepAlive.Interval` fest (Standard 5000 ms). Die maximale Dauer einer einzelnen Anfrage begrenzt `OpcClient.OperationTimeout` (Standard 120000 ms). Wie der Client einen Verbindungsverlust erkennt und welche Einstellungen dafür gelten, beschreibt der Abschnitt [Verbindungsereignisse im Überblick](https://www.traeger.de/docs/development/opcua/client/long-running-clients#verbindungsereignisse-im-uberblick) auf der Seite „Clients für den Dauerbetrieb“.

> **Detailliert**
> Technischer Hintergrund der Kommunikation zwischen Client und Server:
>
> In diesem Modell gibt es immer einen aktiven und einen passiven Teilnehmer. Vom Reverse-Connect-Verfahren abgesehen ist stets der Server der passive Teilnehmer und lauscht auf eingehende Verbindungen. Der Client ist der aktive Teilnehmer und verbindet sich mit einem Endpunkt, den der Server bereitstellt, um mit ihm zu kommunizieren. Wenn die Verbindung zwischen den Teilnehmern aus irgendeinem Grund verloren geht, muss der Client erneut eine Verbindung zum Server aufbauen.
>
> Dabei ergeben sich jedoch folgende Fragen:
>
> - Wie stellt ein Client fest, dass eine Verbindung verloren gegangen ist?
>   - Ist diese physikalisch, also tatsächlich getrennt?
>   - Ist die Antwortzeit des Servers verzögert?
>   - Ist das Netzwerk überlastet, sodass Antworten nur schwer durchkommen?
> - Wann stellt ein Client fest, dass die Verbindung verloren gegangen ist?
>   - Ist die aktuelle Anfrage zu lange nicht beantwortet worden?
>   - Ist die Antwort des Servers auf eine der letzten Anfragen zu alt?
>   - Ist das Netzwerk und somit der Server nicht mehr erreichbar?
>
> Dieselben Fragen stellen sich auch serverseitig:
> - Wie stellt ein Server fest, dass ein Client noch verbunden ist?
>   - Besteht die physikalische Verbindung noch?
>   - Ist die letzte Anfrage vom Client noch aktuell?
>   - Ist das Netzwerk überlastet, sodass Anfragen nur schwer durchkommen?
> - Wann stellt ein Server fest, dass eine bestimmte Client-Verbindung verloren gegangen ist?
>   - Ist die letzte Anfrage schon zu lange her?
>   - Ist die Anfrage des Clients auf eine der letzten Antworten zu alt?
>   - Ist das Netzwerk und somit der Client nicht mehr erreichbar?
>
> Gegen einen Verbindungsverlust auf physikalischer Ebene können Client und Server nichts ausrichten. Anders sieht es auf logischer Ebene aus. Das OSI-Modell kennt verschiedene Schichten (Layer), auf denen sich der Zustand einer Verbindung prüfen lässt. Auf welcher Schicht die Prüfung auch ansetzt, das Verfahren ist immer gleich: Der Client muss Anfragen stellen und der Server muss sie beantworten. Nur so steht zu 100 % fest, dass die Verbindung für die weitere Kommunikation noch intakt genug ist.
>
> Woran erkennen Client und Server, ab wann eine Verbindung als abgebrochen gilt?
> 1. Client oder Server kann keine Anfrage beziehungsweise Antwort senden.
> 1. Die letzte Anfrage oder Antwort liegt zu lange zurück und ist damit zu alt.
> 1. Der Client stellt keine Anfragen mehr oder der Server beantwortet keine mehr.
>
> Unabhängig davon, welchen Fall die Teilnehmer prüfen, müssen Client und Server diese Fälle an bestimmte Bedingungen knüpfen. Dafür dienen Zeitinformationen:
> - Clientseitig bedeutet das:
>   - Die Zeit, zu der eine Anfrage gesendet wurde.
>   - Die Zeit, zu der eine Antwort erhalten wurde.
> - Serverseitig bedeutet das:
>   - Die Zeit seit der zuletzt erhaltenen Anfrage.
>   - Die Zeit seit der zuletzt gesendeten Antwort.
>
> Wenn die Teilnehmer Anfragen und Antworten mit dem aktuellen Zeitstempel versehen, kann der jeweils andere prüfen, ob er noch eine Antwort beziehungsweise eine weitere Anfrage erwarten kann. Der Zeitstempel ist der Zeitpunkt, zu dem ein Teilnehmer die Nachricht absendet. Wenn der Zeitversatz seit der letzten Antwort oder Anfrage zu groß ist, kann der jeweilige Teilnehmer von einem Verbindungsverlust ausgehen. Wie groß dieser Zeitversatz sein darf, legen Timeout-Einstellungen fest. Sie gelten entweder speziell für Client oder Server, oder Client und Server handeln sie miteinander aus.
>
> Ohne Timeout-Bedingungen für die Verbindung wartet ein Client unbegrenzt auf eine Antwort, und ein Server reserviert unbegrenzt einen Endpunkt für den Client. Der Client bleibt bis zur unwahrscheinlichen Antwort blockiert. Dem Server gehen die möglichen Endpunkte für weitere Clients aus, und er belegt wichtige Systemressourcen.
>
> Unabhängig davon, wie und von wem das Zeitfenster festgelegt wird: Beide Teilnehmer müssen garantiert innerhalb des insgesamt kleinsten Zeitfensters mindestens eine Nachricht ausgetauscht haben. Die Logik dafür heißt KeepAlive-Logik. Der aktive Teilnehmer ist dafür verantwortlich, dem passiven Teilnehmer zu signalisieren, dass die Verbindung weiterhin besteht. Dazu legt jedes Protokoll mit KeepAlive fest, dass innerhalb eines bestimmten Intervalls (also eines Zeitfensters) eine Anfrage gestellt und beantwortet werden soll. Das ist entweder eine bestimmte, leichtgewichtige Anfrage oder eine beliebige Anfrage. Der passive Teilnehmer erkennt dann anhand dieser Anfrage oder allgemein anhand der letzten Anfrage, ob der andere Teilnehmer noch erreichbar und verbunden ist. Damit weiß er auch, ob die Verbindung zu ihm noch intakt ist. Der aktive Teilnehmer wiederum weiß durch die Antwort innerhalb des Zeitfensters ebenso sicher, dass die Verbindung noch besteht.

### SDK in PowerShell verwenden

Für das OPC UA .NET SDK gibt es ein Beispiel samt Bootstrapper auf GitHub:
[OPC UA .NET Samples: PowerShell Client](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/ps)

### Update nach NuGet Preview schlägt fehl

NuGet speichert installierte Pakete je nach Projektformat an unterschiedlichen Orten. In Projekten, die nicht im SDK-Stil angelegt sind, liegen sie im Ordner `packages` neben der Visual-Studio-Solution. In Projekten im SDK-Stil liegen sie im lokalen NuGet-Cache. Damit Sie auf die offizielle Version aktualisieren können, löschen Sie die Preview-Pakete aus Ihrer eigenen NuGet-Quelle und aus dem lokalen NuGet-Cache. Wo sich der lokale NuGet-Cache befindet, hängt von Ihrem Projekt ab.

- Wenn Sie das SDK über NuGet mit einer Datei `packages.config` referenzieren:
  - Schließen Sie Visual Studio
  - Navigieren Sie im Explorer zum Projektverzeichnis
  - Löschen Sie das Verzeichnis `packages` neben Ihrer Solution
  - Öffnen Sie das Projekt und erstellen Sie es erneut
- Wenn Sie das SDK über NuGet ohne Datei `packages.config` referenzieren:
  - Schließen Sie Visual Studio
  - Navigieren Sie zum lokalen NuGet-Cache: `C:\Users\Benutzername\.nuget\packages`
  - Löschen Sie das Verzeichnis `Opc.UaFx.Client` beziehungsweise `Opc.UaFx.Advanced`
  - Öffnen Sie das Projekt und erstellen Sie es erneut

Anschließend können Sie wie gewohnt über den NuGet-Client auf die aktuelle Version aktualisieren beziehungsweise sie installieren.

### TypeInitializationException beim Start

Dieser Fehler kann auftreten, wenn Visual Studio nach einem Update auf eine neuere Version des SDK nicht alle verwendeten Binärdateien im Projektverzeichnis aktualisiert hat. Führen Sie deshalb die folgenden Schritte durch:
- Schließen Sie Visual Studio
- Navigieren Sie im Explorer zum Projektverzeichnis
- Löschen Sie alle Verzeichnisse `bin` und `obj`
- Öffnen Sie das Projekt und erstellen Sie es erneut

Wenn der Fehler bei der Ausführung weiterhin besteht, sollten die folgenden Schritte das Problem lösen:
- Wenn Sie das SDK aus dem ZIP-Archiv referenzieren, prüfen Sie:
  - ob alle Dateien des Zielframework-Ordners im Projekt referenziert sind
  - ob die Dateien aus dem korrekten Zielframework-Ordner referenziert sind
- Wenn Sie das SDK über NuGet mit einer Datei `packages.config` referenzieren:
  - Schließen Sie Visual Studio
  - Navigieren Sie im Explorer zum Projektverzeichnis
  - Löschen Sie das Verzeichnis `packages` neben Ihrer Solution
  - Öffnen Sie das Projekt und erstellen Sie es erneut
- Wenn Sie das SDK über NuGet ohne Datei `packages.config` referenzieren:
  - Schließen Sie Visual Studio
  - Navigieren Sie zum lokalen NuGet-Cache: `C:\Users\Benutzername\.nuget\packages`
  - Löschen Sie das Verzeichnis `Opc.UaFx.Client` beziehungsweise `Opc.UaFx.Advanced`
  - Öffnen Sie das Projekt und erstellen Sie es erneut
- Wenn Sie das SDK in einem Projekt mit `app.config` referenzieren und eine der `InnerExceptions` eine `FileLoadException` ist:
  - Öffnen Sie die Datei `app.config` und prüfen Sie die Einträge `bindingRedirect`
  - Entfernen Sie alle Einträge zu der in der Exception genannten Assembly aus der `app.config`
  - Erstellen Sie das Projekt erneut

Wenn keiner dieser Ansätze das Problem löst, senden Sie eine Beschreibung des Problems mit Details zur Exception beziehungsweise zu ihrer `InnerException` an [support@traeger.de](mailto:support@traeger.de).

### Client und Server in einer Anwendung

Das funktioniert ohne Probleme. Viele unserer Tests beruhen auf Codeausschnitten wie dem folgenden:

```csharp
using (var server = new OpcServer()) {
    server.Start();

    using (var client = new OpcClient(server.Address)) {
        client.Connect();
        ...
    }
}
```

```vbnet
Using server = New OpcServer()
    server.Start()

    Using client = New OpcClient(server.Address)
        client.Connect()
        ...
    End Using
End Using
```

### Benachrichtigungen kommen verzögert (S7)

Beteiligte Typen: `OpcSubscription`, `OpcMonitoredItem`, `OpcMonitoredItemStatus`

Ob ein Server die gewünschten Intervalle unterstützt und ob sie greifen, lässt sich über mehrere Eigenschaften prüfen:
- Das PublishingInterval prüfen Sie über die Eigenschaft `CurrentPublishingInterval`.
- Das SamplingInterval prüfen Sie über die Eigenschaft `Status.SamplingInterval`.

Häufig liegt das Hauptproblem auch in der Konfiguration des Servers. Je nach S7-Steuerung unterstützt der Server nur bestimmte Minimalwerte für die Einstellungen von Subscriptions und MonitoredItems. Laut Siemens gelten die folgenden Minimalwerte:

| Eigenschaft | CPU 1510, 1511, 1512, 1513 | CPU 1515, 1516, 1505S | CPU 1517, 1518, 1507S |
|---|---|---|---|
| Maximale Anzahl Subscriptions mit mehr als 1000 MonitoredItems pro Subscription über alle Sitzungen | 10 | 10 | 10 |
| Maximale Anzahl Subscriptions mit maximal 1000 MonitoredItems pro Subscription über alle Sitzungen | 20 | 20 | 20 |
| Maximale Anzahl Subscriptions pro Sitzung | 20 | 20 | 20 |
| Kleinstmögliches Abtastintervall | 100 ms | 100 ms | 10 ms |
| Kleinstmögliches Sendeintervall | 500 ms | 200 ms | 10 ms |
| Maximale Anzahl Sitzungen | 32 | 48 | 64 |
| Empfohlene maximale Anzahl MonitoredItems über alle Subscriptions | 1000 | 1000 (1505S), 2000 | 10000 |

Quelle: [Welche Systemgrenzen enthält der OPC UA Server bei der S7-1500 mit der Firmware V2.8.x?](https://support.industry.siemens.com/cs/document/109755846/welche-systemgrenzen-enth%C3%A4lt-der-opc-ua-server-bei-der-s7-1500-mit-der-firmware-v2-8-x-?dti=0&lc=de-DE)

Welche Grenzwerte ein Server unterstützt, lässt sich über mehrere Nodes im Pfad `/Objects/Server/ServerCapabilities` und darunter im Node `OperationLimits` prüfen. Eine Clientanwendung hat auf diese Einstellungen keinen Einfluss.

## Lizenz

### LicenseException nach 30 Minuten

Ohne Lizenzschlüssel läuft das OPC UA .NET SDK in der Evaluation: je Anwendungsstart 30 Minuten uneingeschränkt für die Client- und Serverentwicklung. Wenn Ihnen 30 Minuten je Anwendungsstart nicht reichen, können Sie bei uns kostenlos eine verlängerte Evaluation beantragen. Wenden Sie sich dazu an unseren Support unter [support@traeger.de](mailto:support@traeger.de) oder lassen Sie sich direkt von uns beraten. Unser Entwicklungsteam klärt dabei auch offene Fragen. Wie Sie den Lizenzschlüssel eintragen, beschreibt die Seite [Lizenz einrichten](https://www.traeger.de/docs/development/opcua/license-setup).

### BadLicenseExpired nach 30 Minuten

Ein Server, der beim Ablauf der Evaluation bereits läuft, läuft weiter, lehnt aber jede Anfrage seiner Clients mit dem Status `BadLicenseExpired` ab.

Ohne Lizenzschlüssel läuft das OPC UA .NET SDK in der Evaluation: je Anwendungsstart 30 Minuten uneingeschränkt für die Client- und Serverentwicklung. Wenn Ihnen 30 Minuten je Anwendungsstart nicht reichen, können Sie bei uns kostenlos eine verlängerte Evaluation beantragen. Wenden Sie sich dazu an unseren Support unter [support@traeger.de](mailto:support@traeger.de) oder lassen Sie sich direkt von uns beraten. Unser Entwicklungsteam klärt dabei auch offene Fragen. Wie Sie den Lizenzschlüssel eintragen, beschreibt die Seite [Lizenz einrichten](https://www.traeger.de/docs/development/opcua/license-setup).

## Nodes

### OpcObjectNode oder OpcFolderNode?

Beteiligte Typen: `OpcObjectNode`, `OpcFolderNode`

Der Unterschied ist semantischer Natur. Die Klasse `OpcFolderNode` ist eine Unterklasse von `OpcObjectNode`. Ihr Einsatz unterscheidet sich so:
- `OpcObjectNode` gruppiert Nodes, die auch in einer **physikalischen** Beziehung zueinander stehen.
- `OpcFolderNode` gruppiert Nodes, die eher in einer **logischen** Beziehung zueinander stehen.

> **Detailliert**
> In der Dokumentation der Klasse `OpcObjectNode` steht:
> > Definiert eine logische Einheit zur Darstellung komplexerer Informationen als `OpcVariableNode`. Objekte werden verwendet, um Systeme, Systemkomponenten, reale Objekte und Softwareobjekte darzustellen.
>
> > Aus einer abstrakteren Sicht werden Objekte verwendet, um Variablen und andere Objekte im Adressraum zu gruppieren. Daher sollten Objekte verwendet werden, wenn einige gemeinsame Strukturen/Gruppen von Objekten und/oder Variablen beschrieben werden sollen.
> >
> > Einfache Objekte mit nur einem Wert (zum Beispiel ein einfacher Wärmesensor) können ebenfalls als Variablen modelliert werden. Es sollten jedoch Erweiterungsmechanismen berücksichtigt werden (zum Beispiel ein komplexer Wärmesensor-Subtyp könnte mehrere Werte haben) und ob dieses Objekt als Objekt in der GUI des Clients oder nur als Wert verfügbar gemacht werden soll. Wenn ein Modellierer Zweifel hat, welche Lösung er verwenden soll, sollte er das Objekt mit einer Variablen bevorzugen.
>
> In der Dokumentation der Klasse `OpcFolderNode` steht:
>
> > Definiert einen Node, um den Adressraum in einer Hierarchie von Nodes zu organisieren. Sie stellen den Wurzel-Node eines Teilbaums dar und haben keine andere Semantik.
>
> Da `OpcFolderNode` eine Unterklasse von `OpcObjectNode` ist, gibt es keinen direkten strukturellen Unterschied zwischen den beiden Typen. Auch der Referenztyp, über den Kind-Nodes angebunden werden, unterscheidet sich standardmäßig nicht: Beide verwenden HasComponent. `OpcFolderNode` gibt diesen Referenztyp zudem an seine direkten Kind-Nodes weiter. Wenn ein Kind-Node über Organizes angebunden werden soll, legen Sie das über seine Eigenschaft `ReferenceTypeId` ausdrücklich fest (`OpcReferenceTypes.Organizes`). Der Unterschied liegt in der Typdefinition: Ein `OpcFolderNode` ist vom Typ FolderType. Vereinfacht gilt deshalb:
> - `OpcObjectNode` gruppiert Nodes, die auch in einer **physikalischen** Beziehung zueinander stehen.
> - `OpcFolderNode` gruppiert Nodes, die eher in einer **logischen** Beziehung zueinander stehen.

### Trennzeichen in NodeIds ändern

Beteiligte Typen: `OpcNodeId`, `OpcNominalNodeIdFactory`

Dazu ändern Sie die Standard-NodeIdFactory:

```csharp
OpcNodeId.Factory = new OpcNominalNodeIdFactory() {
    Separator = '.'
};
```

```vbnet
OpcNodeId.Factory = New OpcNominalNodeIdFactory With {
    .Separator = "."c
}
```

### Mehrere Rückgabewerte einer Methode

Beteiligte Typen: `OpcMethodNode`, `OpcArgumentAttribute`

Mehrere Werte gibt eine Methode über `return`-, `ref`- und `out`-Werte zurück. Beachten Sie, dass `ref`-Werte sowohl als Eingabe- als auch als Ausgabeargument dienen. Ein solcher `OpcMethodNode` lässt sich zum Beispiel so definieren:

```csharp
private delegate void CalculateDelegate(
        int a,
        int b,
        ref int c,
        out int additionResult,
        out int differenceResult);

private void Calculate(
        [OpcArgument("a", Description = "The left operand.")]
        int a,
        [OpcArgument("b", Description = "The right operand.")]
        int b,
        [OpcArgument("c", Description = "Factor")]
        ref int c,
        [OpcArgument("add", Description = "The result of addition.")]
        out int additionResult,
        [OpcArgument("diff", Description = "The result of difference.")]
        out int differenceResult)
{
    additionResult = (a + b) * c;
    differenceResult = (a - b) * c;
}

this.methodNode = new OpcMethodNode(
        this.folderNode,
        nameof(Calculate),
        new CalculateDelegate(this.Calculate));
```

```vbnet
Private Delegate Sub CalculateDelegate( _
        ByVal a As Integer, _
        ByVal b As Integer, _
        ByRef c As Integer, _
        <Out> ByRef additionResult As Integer, _
        <Out> ByRef differenceResult As Integer)

Private Sub Calculate( _
        <OpcArgument("a", Description:="The left operand.")> _
        ByVal a As Integer, _
        <OpcArgument("b", Description:="The right operand.")> _
        ByVal b As Integer, _
        <OpcArgument("c", Description:="Factor")> _
        ByRef c As Integer, _
        <OpcArgument("add", Description:="The result of addition."), Out> _
        ByRef additionResult As Integer, _
        <OpcArgument("diff", Description:="The result of difference."), Out> _
        ByRef differenceResult As Integer)
    additionResult = (a + b) * c
    differenceResult = (a - b) * c
End Sub

Me.methodNode = New OpcMethodNode( _
        Me.folderNode, _
        NameOf(Calculate), _
        New CalculateDelegate(AddressOf Me.Calculate))
```

### Datentyp beim Schreiben einschränken

Beteiligte Typen: `OpcVariableNode`, `OpcVariableValue`, `OpcWriteVariableValueContext`, `OpcWriteVariableValueCallback`

Den Datentyp schränkt ein benutzerdefinierter Callback ein, der den zu schreibenden Wert prüft. Ein solcher Callback kann so aussehen:

```csharp
this.variableNode.WriteVariableValueCallback = this.WriteToNode;

// Deny write of a specific type of value.
private OpcVariableValue<object> WriteToNode(
        OpcWriteVariableValueContext context,
        OpcVariableValue<object> value)
{
    if (!(value.Value is short))
        value.Status.Update(OpcStatusCode.BadTypeMismatch);

    return value;
}
```

```vbnet
Me.variableNode.WriteVariableValueCallback = AddressOf Me.WriteToNode

'Deny write of a specific type of value.
Private Function WriteToNode( _
        ByVal context As OpcWriteVariableValueContext, _
        ByVal value As OpcVariableValue(Of Object)) As OpcVariableValue(Of Object)
    If Not TypeOf value.Value Is Short Then
        value.Status.Update(OpcStatusCode.BadTypeMismatch)
    End If

    Return value
End Function
```

### Geschriebene Werte umwandeln

Beteiligte Typen: `OpcVariableNode`, `OpcVariableValue`, `OpcWriteVariableValueContext`

Ein benutzerdefinierter Callback kann den Wert vor dem Schreiben in den gewünschten Zieldatentyp konvertieren. Das Vorgehen kann etwa so aussehen:

```csharp
// Inline change type of value to the expected type.
private OpcVariableValue<object> WriteToNode(
        OpcWriteVariableValueContext context,
        OpcVariableValue<object> value)
{
    if (!(value.Value is short)) {
        value = new OpcVariableValue<object>(
                Convert.ChangeType(value.Value, typeof(short)),
                value.Timestamp ?? DateTime.UtcNow,
                value.Status);
    }

    return value;
}
```

```vbnet
'Inline change type of value to the expected type.
Private Function WriteToNode( _
        ByVal context As OpcWriteVariableValueContext, _
        ByVal value As OpcVariableValue(Of Object)) As OpcVariableValue(Of Object)
    If Not TypeOf value.Value Is Short Then
        value = New OpcVariableValue(Of Object)( _
                Convert.ChangeType(value.Value, GetType(Short)), _
                If(value.Timestamp, DateTime.UtcNow), _
                value.Status)
    End If

    Return value
End Function

```

## Sicherheit

### DCOM-Härtung (CVE-2021-26414)

Das SDK setzt die von Microsoft empfohlenen Maßnahmen für **OPC Classic** um, also für die Kommunikation über DCOM (Distributed Component Object Model) mit OPC DA (Data Access), OPC HDA (Historical Data Access) und OPC AE (Alarms & Events).

**Hintergrund:**

Microsoft adressiert das als **CVE-2021-26414** bekannte Problem mit einem Leitfaden für DCOM-Client- und DCOM-Serveranwendungen: [CVE-2021-26414](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-26414).

Als Teil des Leitfadens beschreibt Microsoft die schrittweise ergriffenen Maßnahmen, die das Problem dauerhaft lösen. Nach Umsetzung der letzten Phase am 14.06.2022 kann es zu Kommunikationsproblemen zwischen DCOM-Client- und DCOM-Serveranwendungen kommen. Davon ist voraussichtlich ein Teil der Anwendungen betroffen, die über OPC Classic kommunizieren.

Damit die Kommunikation über OPC Classic auch nach Abschluss der Maßnahmen zu CVE-2021-26414 weiter funktioniert, setzt das SDK die von Microsoft empfohlenen **Authentication Level** um. Ohne eigene Angabe in `OpcClient.Security.AuthenticationLevel` verwendet es `OpcClassicAuthenticationLevel.PacketIntegrity` (`RPC_C_AUTHN_LEVEL_PKT_INTEGRITY`).

Wie sich die DCOM-Sicherheit im Code festlegen lässt, beschreibt der Abschnitt [Entfernte Server und DCOM-Sicherheit](https://www.traeger.de/docs/development/opcua/client/opc-classic#entfernte-server-und-dcom-sicherheit).
