我有一个帮助类,它为宿主应用程序提供了一些诊断API。隐藏的实现依赖于WMI,可通过Windows COM接口访问。
实现一个“知道COM”的类需要一些开销,以调用CoInitialize
的形式,使用适当的Apartment模型(单线程/多线程)。我不确定谁负责设置 - 我的助手类或消费者。
所以,我的问题是:谁负责调用CoUninitialize
和CoInitialize
:我的助手类或主机应用程序?除了辅助类之外,主机应用程序中的COM可能没有其他依赖性。
选项A:帮助器类在构造函数和析构函数中调用CoUninitialize
和CoInitialize
此选项很方便,并且有效地“隐藏”了COM依赖项。但是,父应用程序可能已经或可能没有初始化COM,它可能与助手类的假定公寓模型匹配也可能不匹配。如果模型没有对齐,辅助类将从CoInitialize
收到错误。
选项B:帮助程序类生成一个单独的线程,并在后台线程上使用单线程单元调用CoInitialize
。所有接口调用都将调度到后台线程并返回。
这可以帮助确保帮助程序类具有“干净的平台”,并避免在任何单个线程上进行重复的COM初始化。它还增加了我的助手类实现的复杂性,并以线程切换和握手的形式增加了开销。
选项C:在文档中记下,并要求主机应用程序在使用帮助程序类之前处理对CoUninitialize
和{
"registry": "https://registry.bower.io"
}
的所有调用
这使得该类使用起来稍微“方便”,因为用户在使用该类之前还有其他初始化步骤。它还要求类的消费者实际阅读文档,这似乎很危险。
答案 0 :(得分:2)
选项A带有一些智能似乎是一个不错的选择。这假设您不关心正在使用的线程模型。如果这样做,您需要明确声明帮助程序的客户端不应初始化COM,以便它可以指定线程模型。
class HelperThatRequiresCOM
{
public:
HelperThatRequiresCOM() : m_CoUninit(false)
{
// Attempt to init COM as a STA
HRESULT ciResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if(ciResult == S_OK || ciResult == S_FALSE)
m_CoUninit = true; // COM initialized or already initialized.
else if(ciResult == RPC_E_CHANGED_MODE)
m_CoUninit = false; // COM initialized as MTA
}
~HelperThatRequiredCOM()
{
if(m_CoUninit == true)
CoUninitialize();
}
private:
bool m_CoUninit;
};
这将尝试初始化COM。如果它已初始化为STA
,或刚刚初始化为STA
,则会记住该信息,以便稍后可以调用CoUninitalize
。
如果COM已经初始化为MTA
,它会记住,而不是CoUninitialize
。
这允许调用线程可以自由地初始化COM,如果不是,则可以自己初始化它。
答案 1 :(得分:0)
在评论部分提出一些优点后,经过进一步思考,我选择了新的选项D :
class ApartmentContext
ApartmentContext
作为帮助程序类中的构造函数参数。这会强制用户通过ApartmentContext
实例的存在来声明已初始化COM。这有很多好处:
以下是我的ApartmentContext类的列表:
// Specifies a single-threaded or multi-threaded COM apartment.
enum class Apartment
{
MultiThreaded = 0,
SingleThreaded = 2
};
// A helper class used for initializing and uninitializing a COM Apartment.
// The constructor of the ApartmentContext will initialize COM, using the
// specified apartment model: single-threaded or multi-threaded.
// The destructor will automatically uninitialize COM.
class ApartmentContext final
{
public:
// Initialize COM using the specified apartment mode.
ApartmentContext(Apartment mode);
// Uninitialize COM
~ApartmentContext();
// Get the current apartment type.
Apartment Current() const;
private:
ApartmentContext& operator=(const ApartmentContext&) = delete;
ApartmentContext(const ApartmentContext&) = delete;
Apartment current_; // Store the current apartment type
};
在实例化助手类之前,用户将被迫创建其中一个:
// Somewhere in main...
// Initialize COM:
ApartmentContext context(Apartment::SingleThreaded);
帮助器类将要求用户在其构造函数中提供上下文:
public class MyHelperClass
{
public:
// The apartment context isn't actually used; it is required by the
// constructor merely as a way to ensure that COM is initialized
// beforehand.
// If the class requires a certain apartment mode,
// it could also check the mode using the "Current" API,
// and throw on mismatch
MyHelperClass(const ApartmentContext&);
};
用法:
// Create the helper class, providing the context as a constructor argument
MyHelperClass helper(context);