问题标签 [apartments]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
1 回答
517 浏览

c++ - Com Threading/Apartment 行为与编组工厂不一致

我正在尝试使用 CoRegisterClassObject 来自定义加载包含 com 对象的 dll 的方式。我正在尝试解决当线程的单元类型与 com 对象不匹配时遇到的问题。基本思想是,由于在创建 com 对象时使用 coregisterclassobject 会忽略注册表,因此我需要确保 STA 对象是在 STA 线程中创建的,对于 MTA 对象也是如此。这是我写的一个示例,作为概念证明,它的行为并不总是像我预期的那样。

这是我主要功能的相关部分。

这个想法是,由于注册表不应该与 CoRegisterClassObject 一起使用,我需要在 STA 而不是当前的 MTA 线程中手动创建单元线程对象,反之亦然。我注意到,当不使用 CoRegisterClassObject 时,CoGetClassObject 会生成一个新线程并在该线程中调用 DllGetClassObject,所以我认为只需要在 STA 中创建类工厂,然后对象就会在那里。

我看到的问题是,在上面的示例中,线程 ID 并不总是看起来像我期望的那样。如果 FactoryThread 初始化为单元线程,主线程为多线程,则 THREAD_ID_2 == THREAD_ID_3 == THREAD_ID_4 != THREAD_ID_1 符合预期(工厂正在创建这些对象,它们可以存在于工厂的线程中)。如果切换了这些线程模型,那么 thread_id_3 == thread_id_4,但它们与 thread_id_2 和 thread_id_1 不同,即使可以在线程 2 中创建 com 对象。

这似乎不一致,并且在涉及另一个线程的情况下可能导致不需要的行为。当仅依赖注册表而不使用 coregisterclassobject 时,如果我在 STA 中创建了一个自由线程对象,则该对象将在由 MTA 中的 com 生成的不同线程中创建,然后如果我生成了第三个线程也在 STA 中,在那里创建对象会将其放入第一个 com 生成的 MTA 线程中,而不是新线程中(如果对象的线程模型和线程的单元类型颠倒了,情况也是如此)。但是,如果我要使用 coregisterclassobject 像上面那样创建自己的工厂,并且对象是多线程的,但线程在 STA 中,那么创建这些多线程对象的每个新线程都会产生一个新的 MTA 线程,

0 投票
2 回答
3784 浏览

c# - 在线程中执行 Webbrowser 控件的 screen-scape

我正在使用中显示的技术

新线程中的 WebBrowser 控件

尝试获取网页的屏幕截图,当WebBrowser控件放置在WinForm. 但是,当在线程内运行时,它会通过提供桌面的任意图像而失败。

这显然是由于该方法而发生的,Graphics.CopyFromScreen()但我不知道有任何其他方法。有没有人可以建议的方法来解决这个问题?或者是我创建表单、添加控件、使其可见然后屏幕抓取的唯一选择?出于显而易见的原因,我希望避免这种方法。

0 投票
1 回答
4188 浏览

c++ - 在 COM 对象内调用 CoInitializeEx 返回 S_OK

前段时间,我不得不修改一个旧的 COM DLL (Visual C++ 2010, ATL),将它从“Apartment”线程模型迁移到“Both”,即现在可以从 STA 和 MTA 线程调用它而无需序列化调用(当然,我不得不为共享数据添加内部同步)。当我的 DLL 从 .NET 应用程序通过 Interop 调用时,这反过来又在将 COM 事件(连接点)转换为 .NET 事件时引起问题(即使在 .NET 应用程序中,我也必须同时支持 STA 和 MTA)。为了解决这些问题,我改变了事件的触发方式。

1) 如果在 STA 上下文中调用 DLL,它会像以前一样工作,即创建一个不可见的窗口,然后,当必须引发事件时,它调用 PostMessage 到该窗口,然后主 STA 线程调用实际事件-触发代码,即CProxy_IMyEventFiringInterface 成员函数(CProxy_IMyEventFiringInterface 派生自IConnectionPointImpl)。

2) 如果在 MTA 上下文中调用 DLL,我没有主 COM 线程,也无法执行 PostMessage,因此我使用我创建的自定义线程并让该线程调用 IConnectionPointImpl 函数。

但是AFAIK没有检测调用线程是STA还是MTA的Windows API。许多网站建议像这样使用 CoInitializeEx:

我决定在 CMyComComponent::FinalConstruct 中调用 CoInitializeEx。一切正常……直到今天。在客户场景中,我从跟踪工具中看到,对于某个 .NET EXE 应用程序(我没有源代码),由于 CoInitializeEx 返回了 S_OK,上述代码最终出现在默认分支中。这怎么可能?Microsoft 文档说 S_OK 表示“COM 库已在此线程上成功初始化”,但我在一个 COM 对象内,COM 库必须已经初始化!顺便说一句,默认分支不会关闭应用程序,但是,由于返回了 S_OK,它调用了 CoUninitialize(每次成功调用 CoInitialize 或 CoInitializeEx,包括任何返回 S_FALSE 的调用,必须通过对 CoUninitialize 的相应调用来平衡),然后 DLL 继续假设 STA(事后看来是错误的举动)。但它不是 STA:事实上,稍后的 PostMessage 返回 FALSE。

如果 CoInitializeEx(NULL, COINIT_MULTITHREADED) 返回 S_OK,我可以简单地更改代码以使用 MTA 作为默认值,我应该从一开始就这样做。但我也想确定这是正确的做法,以避免将来出现更多问题。非常感谢德米特里奥

0 投票
0 回答
755 浏览

c# - WMI 调用在 GUI 线程(STA 线程)中工作,为什么它在后台线程(MTA 线程)中失败?

好的,我有一种方法可以简单地将 Windows 事件日志备份为 *.evt 文件。在 Windows XP x86 和 x64 上的 .net2.0 中,这很好用,没有问题。

我们最近升级到 .net4.0(特别是 4.0.3),现在这段代码以一种非常特殊的方式失败了。(在 XP x86 和 x64 上失败,Win7 工作正常)

如果该方法由事件处理程序中的主 GUI 线程执行,则它可以正常工作。但是,如果此方法在新线程中执行,或者由 ThreadPool 执行,则会失败并出现访问被拒绝错误。

此应用程序正在以管理员身份运行并访问本地计算机 wmi 提供程序。

我猜想.net4 中有某种我不满意的新线程安全性或类似的东西。

这是一些说明问题的示例代码。这是来自带有 3 个按钮的表单,处理程序位于末尾,并说明了它何时工作以及何时失败。它将所有 Windows 事件日志的副本保存到 ..\Log 文件夹。

使用.Net2.0的目标框架编译以下,所有三种调用方法都可以正常工作。使用.Net4.0.3的目标框架编译,最后2个调用方法失败,拒绝访问。

我在这里想念什么?

更新: 这似乎与执行线程的 Thread.ApartmentState 设置直接相关。默认情况下,线程是使用 ApartmentState.MTA 创建的,而主 GUI 线程是 STA 线程。在 .Net 4.0 中,我的 WMI 代码在 STA 线程中运行时有效,但在 MTA 线程中无效。在 .Net 2.0 中,它适用于两者。

任何想法如何让它在.Net 4.0 上的 MTA 线程中工作?我想使用线程池来运行它。

0 投票
4 回答
721 浏览

c++ - 对于具有 ThreadingModel Both 的对象,COM 编组是否(永远)是必要的?

这是由另一个问题引发的。

具体来说,我有一个进程中的 COM 类,它在CLSID 注册表中定义为具有ThreadingModel.Both

CoCreateInstance我们的进程通过( CoCreateInstanceEx,如果这对进程内 dll 服务器也很重要)激活此对象

给定文档中列出的线程模型Both和规则:

并鉴于汉斯在另一个答案中写道:

...当需要在不同的线程上进行客户端调用时,就会发生编组。...当 comClass 元素中指定的 ThreadingModel 需要它时,可能会发生。换句话说,当 COM 对象在一个线程上创建但在另一个线程上调用时,服务器不是线程安全的。

我的初步结论是,这样的对象永远不需要对其接口的调用进行隐式编组,因为该对象将始终与其客户生活在同一个公寓中。

这是正确的,即使客户端进程作为STA运行?

0 投票
1 回答
140 浏览

.net - COM+ long running method causing other methods to block/hang

I have a long running COM+ method that I need to be able to cancel from another thread. I am using C#/.NET to interact the COM+ objects. I configured both COM+ objects to have a "Free" threading model. This C# sample demonstrates how I intend to use the COM+ objects.

My long running process correctly checks the cancellation token to determine if we should finish processing, but the Cancel method never makes it to the COM+ objects. It is as if the method is blocking, waiting for LongProcess to finish. I don't know why it is doing this, because I thought the "Free" threading model allowed the implementations to manage synchronization.

Here is a BitBucket repository with a minimal example to reproduce. https://bitbucket.org/theonlylawislove/so-blocking-com-call

Why is Cancel never getting called/blocking?

CancelCOMObject

SampleCOMObject

0 投票
0 回答
267 浏览

ruby-on-rails - 新用户注册 Devise and Apartment 后创建关联记录

背景

我有一个多租户 Rails 应用程序,它使用 Apartment gem 在登录时根据其子域切换架构。和实体在公共模式中UserAccount所有其他相关实体都在帐户特定架构中。

问题

当新用户注册时,UserAccount记录在初始表单发布期间创建(即 in RegistrationsController#create)。

但是,此时我想创建其他记录(例如,一条Family记录),但尚未创建帐户特定架构且上下文尚未切换,因此如果我执行以下操作:

Family记录将在公共架构中错误地创建。

Devise 允许您指定一个after_sign_up_path_for值,所以我在考虑创建一个自定义操作来FamiliesController完成创建新记录的工作,但这似乎有点笨拙且非 RESTful:

这样做的正确方法是什么?

(注意:架构上下文不会切换,直到我重定向到具有帐户特定子域的 URL。因此想到这样做)

0 投票
1 回答
2092 浏览

c# - C# 在线程之间编组 COM 对象

我对 C# marshal 的 COM 对象是否在线程之间感到非常困惑。为此,我有一个应用程序以任务并行方式加载一组文件。我正在使用StaTaskScehduler使用 COM 对象加载文件。加载 COM 对象后,我将该对象存储在中央列表中。

然后我稍后尝试对这些数据执行一些处理,再次使用 STATaskScheduler。然而,在这一点上,我遇到了一个问题。我收到如下异常:

现在我的理解是我收到此错误是因为该对象尚未编组到新线程中。我认为这是 C# 为你做的事情?

如何在一个线程中创建一个单元线程 COM 对象,然后在另一个线程中使用它?

我在这里吠错树了吗?我什至不应该为我的线程使用 Sta 公寓吗?我可以保证该对象永远不会同时从多个线程访问。任何想法都非常感谢。

编辑:COM对象定义如下:

所以据我了解,这是一个单元线程对象,对吧?我刚刚尝试使用未设置公寓状态的修改后的任务调度程序(默认情况下为 MTA?)。当我在一个线程中创建它并从另一个线程使用它时,这个对象似乎确实有效。这是安全的还是会以其他方式回来咬我?

COM 的线程模型总是让我很困惑:/

0 投票
1 回答
3438 浏览

c# - 在 WinForms 线程上使用 CoInitializeEx

我正在为具有以下说明的 DSLR 相机开发 SDK:

开发 Windows 应用程序的注意事项 创建在 Windows 下运行的应用程序时,每个线程都需要进行 COM 初始化,以便从主线程以外的线程访问相机。要创建用户线程并从该线程访问相机,请确保在线程开始时执行 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) 并在结束时执行 CoUnInitialize()。示例代码如下所示。从另一个线程控制 EdsVolumeRef 或 EdsDirectoryItemRef 对象时也是如此,而不仅仅是 EdsCameraRef。

我的应用程序是 C# WinForms 应用程序,通常我使用托管线程类和 Control.Invoke 函数来避免跨线程问题。

由于我没有 C# 中用于使用 SDK 的示例源代码,我的问题是,在标有该属性CoInitializeEx的应用程序中使用它是否有用和/或必要?[STAThread]

我没有遇到过需要让我的应用程序为线程创建一个新单元的情况,这样一些见解将有助于更好地理解线程模型。

更新:在阅读了有关公寓和 COM 的更多信息后,它开始变得有意义。现在我想知道 .NET 托管线程类的默认设置是什么,我们可以在没有 P/Invoke 的情况下以托管方式为每个线程指定一个单元模型吗?

0 投票
0 回答
154 浏览

visual-studio - 如何更改现有 Visual Studio C++ 项目中的公寓模型?

我有一个使用 COM 向导创建的 Visual C++ 2013 项目。创建此组件时选择了公寓模型。有没有办法在不从头开始重新创建项目的情况下将此模型更改为不同的公寓?