COM 线程的确切细节太大,无法放在文章中。我会专注于你问题的细节。创建 COM 对象的线程必须告诉 COM 它希望为具有受限线程选项的 COM 类提供何种支持。这些类中的绝大多数只支持所谓的公寓线程,它们的接口方法只能从创建实例的同一个线程中安全地调用。换句话说,他们宣布“我不支持任何线程,请注意 永远不会从错误的线程调用我”。即使客户端代码实际上从另一个线程调用它。
有两种,STA (单线程公寓)和 MTA。它是在 CoInitializeEx ()调用中指定的,该函数必须由任何使用 COM 执行任何操作的线程调用。CLR 在启动线程时自动执行该调用。对于程序的主启动线程,它从 Main ()方法上的[ STAThread ]或[ MTAThread ]属性获取要传递的值。默认是 MTA。对于自己创建的线程,它由对 SetApartmentState ()的调用决定。默认是 MTA。线程池线程始终是 MTA,不能更改。
Windows 中有很多代码需要 STA。值得注意的例子是剪贴板,拖放和 shell 对话框(如 OpenFileDialog)。还有很多你看不到的代码,比如 UI 自动化程序和用来观察消息的挂钩。这些代码都不必是线程安全的,如果不知道在哪个程序中使用它,它的作者将很难使它安全。因此,WPF 或 Windows 窗体项目的 UI 线程必须始终是 STA,以支持此类代码,创建窗口的任何线程也是如此。
你对 COM 的承诺,你的线程是 STA 然而 是的要求你遵守单线程公寓合同。他们是相当僵硬的,你可以得到棘手的诊断麻烦时,你违反了合同。要求是,您永远不要在任何时间段内阻塞线程,并且您泵入一个消息循环。WPF 或 Winform 的 UI 线程满足后一个要求,但是如果您创建自己的 STA 线程,则需要自己处理它。破坏契约的常见诊断是僵局。
顺便说一下,CLR 内置了相当多的支持来支持这些需求,帮助您避免麻烦。当阻塞 STA 线程时,锁定语句和 WaitOne ()方法会产生一个消息循环。然而,这只能满足永不阻塞的要求,您仍然需要创建自己的消息循环。申请。在 WPF 和 Winform 中运行()。
我之前已经贡献了一个答案,其中包含有关使用消息循环保持 COM 快乐的重要性的更多细节。你会找到 贴在这里。