COM management
This article applies to QuickOPC 2022.1 and later.
(TBD)
Configuring COM
COM security initialization
In Windows, major part of parameters that influence the COM security can only be set globally for the whole process, and only once. As soon as the COM security is initialized in a process, its parameters cannot be changed.
Furthermore, COM security initialization can either be invoked either
- Explicitly: Some code in the process (your own code, QuickOPC, or execution infrastructure code) calls the CoInitializeSecurity Win32 API. In the explicit initialization case, the code specifies the COM security parameters to be used.
- Implicitly: Some code in the process (your own code, QuickOPC, or execution infrastructure code) marshalls or unmarshalls a COM interface. This can happen e.g. when the code uses some COM object and does not initialize its thread apartment state to MTA. In the implicit initialization case, parameters from computer-wide COM default properties (in DCOMCNFG) are used.
As you can see, whoever initializes the COM security first in the process, either intentionally or intentionally, is the winner for the remainder of the existence of the process. This is unfortunate and unavoidable, although understandable aspect of how COM on Windows work. The danger here is that the initialization made "for you", earlier than you intended, would not have the parameters you want. Large parts of this article deal with approaches that aim at assuring that the COM security parameters can be set "your way".
QuickOPC concepts
!!!
Execution flow
Here is what generally happens (with regard to COM security initialization) in a program that uses QuickOPC.
- Your program's process starts. In some cases (e.g. standalone console apps, Windows Forms apps, WPF apps), this is a process dedicated to you app. In other cases (e.g. Web apps or Web services, or Windows services), this is some kind of hosting process over which you have less or no control.
- Before you own code gets a chance to execute, the process may (or may not) initialize COM security, either explicitly (by calling the CoInitializeSecurity function in Win32 API), or implicitly (usually by using some COM object from an STA thread). In case of explicit initialization, the hosting process determines the parameters used. In case of implicit initialization, the computer-wide COM default properties (from DCOMCNFG) are used.
- A code in you project runs. In some project types, there is a "main" method that gets executed. In other project types, there can be just objects that get instantiated and their constructors and other methods get executed.
- A code in your project may (usually unintentionally, by using some COM object from an STA thread) cause an implicit COM security initialization, if COM security has not been initialized yet.
- When your code makes a ComManagement.Instance.AssureSecurityInitialization() call, or it attempts a first OPC "Classic" operation, QuickOPC attempts to initialize the COM security, using the parameters provided in ComManagement.Instance.Configuration.SecurityParameters. This attempt will succeed if the COM security has not been initialized earlier; otherwise, it will fail with RPC_E_TOO_LATE.
In short, QuickOPC allows you to initialize the COM security and specify the parameters that should be used for it, but it only works if some other code in the process has not already initialized the COM security earlier.
Determining what happens
QuickOPC applications write an entry to the Windows Application log whenever they attempt to explicitly initialize the COM security, indicating a success or failure, and additional relevant information. In order to view this log, start "Event Viewer" from the Windows search box, and in the "Event Viewer" window, select the Event Viewer (Local) -> Windows Logs -> Application node in the tree. The Source of the events is "OPCLabs-ComInterop".
A successful COM security initialization is indicated by an event with Level "Information" and ID 1. The message text will be something like
COM security initialization (process name "...", Id ...) for requestor 'ComSecurityInitializingEasyDAClient' succeeded; the initialization object was: (Default). Made system call: yes, current thread name: "", from thread pool: no, apartment state: MTA.
A failed COM security initialization is indicated by an event with Level "Error" and ID 2. The message text will be something like
COM security initialization (process name "…", Id …) for requestor 'ComSecurityInitializingEasyDAClient' failed; the initialization object was: (Default). CoInitializeSecurity failure (0x80010119): Security must be initialized before any interfaces are marshalled or unmarshalled. It cannot be changed once initialized. + This error (RPC_E_TOO_LATE) is not uncommon in hosted .NET applications. Depending on various factors, it might be possible to prevent it, or safely ignore it (if COM works as intended). Consult the product documentation. + Current thread name: "…", from thread pool: yes, managed thread Id: …, apartment state: MTA.
There may be different reasons for COM security initialization to fail (and the message text will differ); however, the failure with code 0x80010119 (RPC_E_TOO_LATE) is by far the most common, and it is the one we will focus on here. It means that the COM security has already been initialized, before QuickOPC code got a chance to do it.
Guidance for specific project types
Use the steps below to for proper COM security initialization in various project types.
- Console apps in C#
- The main method runs on an MTA thread, unless specified otherwise. This is generally good, because it prevents premature COM security initialization. If COM security initialization fails with RPC_E_TOO_LATE, and there is an [STAThread] attribute on the program's main method, remove it (and optionally replace with an [MTAThread] attribute). If it still fails with RPC_E_TOO_LATE, add a ComManagement.Instance.AssureSecurityInitialization() call to the very beginning of the program's main method.
- Console apps in VB.NET
- The main method runs on an STA thread, unless specified otherwise. This causes a premature COM security initialization. If there is an <STAThread> attribute on the program's main method, remove it. Then, add an <MTAThread> attribute to the program's main method. If the COM security initialization still fails with RPC_E_TOO_LATE, add a ComManagement.Instance.AssureSecurityInitialization() call to the very beginning of the program's main method.
- Web apps and Web services
- The COM security will most likely be initialized before your code gets a chance to do so, and further attempts will fail with RPC_E_TOO_LATE. In this case, the parameters used for the COM security initialization depend on the host (Web server), and you need to figure out whether the host allows them to be configured. If not, there is a chance that a DCOM configuration for the app (in DCOMCNFG) that represents the Web server, or the computer-wide COM default properties, will have effect.
- Windows Forms apps in C#
- The main method runs on an MTA thread, unless specified otherwise. This is generally good, because it prevents premature COM security initialization. If COM security initialization fails with RPC_E_TOO_LATE, and there is an [STAThread] attribute on the program's main method, remove it (and optionally replace with an [MTAThread] attribute). If it still fails with RPC_E_TOO_LATE, add a ComManagement.Instance.AssureSecurityInitialization() call to the very beginning of the program's main method.
- Windows Forms apps in VB.NET
- The main method runs on an STA thread, unless specified otherwise. This causes a premature COM security initialization. If there is an <STAThread> attribute on the program's main method, remove it. Then, add an <MTAThread> attribute to the program's main method. If the COM security initialization still fails with RPC_E_TOO_LATE, add a ComManagement.Instance.AssureSecurityInitialization() call to the very beginning of the program's main method. Note: Depending on how your project has been created, there may be no Program.vb file with the main method. In this case, create or locate Program.vb in some other Windows Forms project, copy it over to your project and edit accordingly (the namespace etc.). Then, go to project Properties -> Application, uncheck "Enable application framework", and set "Startup object" to "Sub Main".
- Windows services
- COM security is not initialized implicitly, therefore the initialization provided by QuickOPC works well. You can add ComManagement.Instance.AssureSecurityInitialization() call to the Main() method in Program.cs or Program.vb.
- WPF apps
- COM security initialization fails with RPC_E_TOO_LATE in the generated project. It should be possible to customize the program's main method (search the Web for "WPF main method" for hints on how to do that). Make the program's main method use the MTA thread and add a ComManagement.Instance.AssureSecurityInitialization() call to it. Alternatively, there is a chance computer-wide COM default properties (in DCOMCNFG) will have effect.
If the steps you took required you to switch from using an STA thread to an MTA thread, and you find that for some reason the remainder of your code has a problem running under an MTA thread, use the ComManagement.Instance.AssureSecurityInitializationAndRunOnStaThread() to run the remainder on your code on an STA thread.
Dealing with CVE-2021-26414
!!!
COM settings in OPC Classic client components
KB5004442—Manage changes for Windows DCOM Server Security Feature Bypass (CVE-2021-26414)