在研究Direct2D支持哪些COM公寓线程模型的过程中,我发现尽管有外观并且可以使用来自.NET的API使用COM互操作性,Direct2D (like other DirectX APIs) is not actually a COM API at all。(1) Wikipedia article on Direct2D (2)以及MSDN blog post by Thomas Olsen (3)都使用“轻量级COM”方法引用这些API。
但是,我还没有找到任何关于这个“轻量级COM”究竟是什么的官方定义。是否有任何此类定义(可能由Microsoft提供)?
Mike Danes对MSDN论坛问题'CoInitialize/CoUninitialize, are they really needed for Direct2D and DirectWrite?'的回答。这是有趣的一点:
“DirectWrite / Direct2D / Direct3D就像API一样,但它们不使用COM。它们没有像普通的COM组件那样在注册表中注册,它们不遵循COM线程模型,它们不支持任何类型的编组等他们不是COM。“
Wikipedia article on Direct2D (Overview section):
“Direct2D是一个基于C ++的本机代码API,可以通过托管代码调用,并使用像Direct3D一样的”轻量级COM“方法,只需要很少的抽象。”
Thomas Olsen's MSDN blog post提到以下作为Direct2D的设计目标:
“轻量级COM - 应该使用C ++样式接口来模拟Direct3D的使用。不支持代理,跨进程远程处理,BSTR,VARIANT,COM注册(例如重量级的东西)。”
答案 0 :(得分:3)
鉴于above comments以及从未发布过COM的规范(除了version 0.9 draft paper from 1995除外)这一事实,要求定义“轻量级COM”可能毫无意义:如果“COM” “不是一个精确定义的东西(但更多的是一个想法),那么”轻量级COM“也许也是如此。理论上,对于使用这个想法的不同API,它可能意味着略有不同。
以下是尝试定义DirectX样式API使用哪种“轻量级COM”。我还包括我自己的“轻量级COM”组件的代码示例。
IUnknown
继承”,“接口永不改变,一旦发布”世界观。IUnknown
界面与COM的IUnknown
相同。QueryInterface
和IID来检索接口指针。__stdcall
调用约定,HRESULT
返回值等等。组件未通过CLSID在注册表中注册。也就是说,组件不会通过调用CoCreateInstance
来实例化;相反,客户端直接引用API的库,该库公开工厂函数(例如D2D1CreateFactory
中的Direct2D的d2d1.dll
。可以从此“入口点”工厂对象中检索其他对象。
由于DLL直接加载到客户端进程中,“轻量级COM”API(与COM不同)仅支持进程内服务器。 Remoting stubs&因此不需要代理,也不支持代理。
理论上,“轻量级COM”库根本不依赖OLE32.dll
,即不需要调用CoXXX
函数(例如CoInitialize
)设置一个线程的公寓,CoCreateInstance
来实例化同类,等等。
(如果它与实际的COM库互操作,“轻量级COM”库可能仍然必须使用COM内存分配器(CoTaskMemAlloc
,CoTaskMemRealloc
,CoTaskMemFree
) ...或者使用.NET marshaller,它假定它正在处理COM库。)
由于不需要CoInitialize
,因此“轻量级COM”不使用COM的公寓线程模型。 “轻量级COM”API通常实现自己的线程模型,例如, Direct2D's multithreading model。 (事实上,这个页面不包含任何COM公寓模型Direct2D支持的提示,暗示COM公寓根本不适用于Direct2D!)
以下C ++文件(Hello.cc
)实现了“轻量级COM”组件Hello
。为了表明这将独立于COM,我不包括任何COM或OLE头文件:
#include <cinttypes>
#include <iostream>
// `HRESULT`:
typedef uint32_t HRESULT;
const HRESULT E_OK = 0x00000000;
const HRESULT E_NOINTERFACE = 0x80004002;
// `GUID` and `IID`:
typedef struct
{
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
} GUID;
bool operator ==(const GUID &left, const GUID &right)
{
return memcmp(&left, &right, sizeof(GUID)) == 0;
}
typedef GUID IID;
// `IUnknown`:
const IID IID_IUnknown = { 0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
class IUnknown
{
public:
virtual HRESULT __stdcall QueryInterface(const IID *riid, void **ppv) = 0;
virtual uint32_t __stdcall AddRef() = 0;
virtual uint32_t __stdcall Release() = 0;
};
// `IHello`:
const IID IID_IHello = { 0xad866b1c, 0x5735, 0x45e7, 0x84, 0x06, 0xcd, 0x19, 0x9e, 0x66, 0x91, 0x3d };
class IHello : public IUnknown
{
public:
virtual HRESULT __stdcall SayHello(const wchar_t *name) = 0;
};
// The `Hello` pseudo-COM component:
class Hello : public IHello
{
private:
uint32_t refcount_;
public:
Hello() : refcount_(0) { }
virtual HRESULT __stdcall QueryInterface(const IID *riid, void **ppv)
{
if (*riid == IID_IUnknown)
{
*ppv = static_cast<IUnknown*>(this);
}
else if (*riid == IID_IHello)
{
*ppv = static_cast<IHello*>(this);
}
else
{
*ppv = nullptr;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return E_OK;
}
virtual uint32_t __stdcall AddRef()
{
return ++refcount_;
}
virtual uint32_t __stdcall Release()
{
auto refcount = --refcount_;
if (refcount == 0)
{
delete this;
}
return refcount;
}
virtual HRESULT __stdcall SayHello(const wchar_t *name)
{
std::wcout << L"Hello, " << name << L"!" << std::endl;
return E_OK;
}
};
// Factory method that replaces `CoCreateInstance(CLSID_Hello, …)`:
extern "C" HRESULT __stdcall __declspec(dllexport) CreateHello(IHello **ppv)
{
*ppv = new Hello();
return E_OK;
}
我使用Clang编译了上面的内容(链接到Visual Studio 2015的C ++标准库),再次没有在任何COM或OLE库中链接:
clang++ -fms-compatibility-version=19 --shared -m32 -o Hello.dll Hello.cc
现在,假设生成的DLL在我的.NET代码的搜索路径中(例如在bin\Debug\
或bin\Release\
目录中),我可以使用COM互操作在.NET中使用上面的组件:
using System.Runtime.InteropServices;
[ComImport]
[Guid("ad866b1c-5735-45e7-8406-cd199e66913d")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IHello
{
void SayHello([In, MarshalAs(UnmanagedType.LPWStr)] string name);
}
class Program
{
[DllImport("Hello.dll", CallingConvention=CallingConvention.StdCall)]
extern static void CreateHello(out IHello outHello);
static void Main(string[] args)
{
IHello hello;
CreateHello(out hello);
hello.SayHello("Fred");
}
}
答案 1 :(得分:2)
当您使用 VTable绑定(也曾经被称为&#34;早期绑定&#34;) + IUnknown 定义时,您可以使用轻量级COM。这是关于它的。 您永远不会在古老的Microsoft出版物中找到对此的定义,因为它从未以此名称存在。
作为API开发人员,当您宣布执行轻量级COM&#34;时,您基本上声明您不关心以下事项:
请注意,并不意味着您不会拥有它,事实上,您将免费拥有它,具体取决于您使用的工具(例如,Visual Studio工具,例如,它&# 39;以某种方式更容易依赖(M)IDL来提供它所提供的抽象),它只是你喜欢拥有可扩展API的想法(使用COM,你可以&#34;投射&#34;对象在二进制组件之间)没有支持所有这些服务的负担。
还要注意&#34;轻量级COM&#34;在任何平台和任何语言(称之为&#34; open&#34;)都非常非常便携,这在今天更有趣。