Как COM выбирает, как маршалировать интерфейс?
Насколько я понимаю, есть три способа реализовать маршаллинг в COM:
- Typelib Маршаллинг
- прокси / заглушка маршаллинг
- реализация IMarshal объектом
Теперь, как компонент- потребитель (пользователь) выбирает, какой из них будет использоваться? Решает ли он сам и использует ли он предпочтительный способ, или он вызывает какую-то встроенную функцию, и это решает проблему для него?
В настоящее время я испытываю следующее: мой компонент реализует пользовательский интерфейс ICustomInterface
это также реализовано компонентом от другой компании. Мой компонент не имеет typelib и не реализует IMarshal. Системный реестр содержит ключ HKCR\Interface{uuidof(ICustomInterface)}\ProxyStubClsid32 с GUID прокси / заглушки, который можно отследить до библиотеки, предоставленной этой другой компанией.
Теперь, когда мой потребитель компонента инициализирует мой компонент, он вызывает QueryInterface(), запрашивая IMarshal из моего компонента, и когда возвращается E_NOINTERFACE, он просто ничего не делает. Почему это - почему библиотека прокси / заглушки от другой компании не включается?
2 ответа
Среда выполнения COM будет использовать маршаллинг typelib (oleautomation), если вы пометите свой интерфейс как стандартный маршалер, добавив его CLSID {00020424-0000-0000-C000-000000000046}
под HKCR\Interfaces\{iid}\ProxyStubClsid
(где {iid} - это GUID вашего интерфейса). Вам также необходимо зарегистрировать библиотеку типов, чтобы среда выполнения извлекала информацию о параметрах, и вы можете использовать только определенное подмножество типов. Есть еще немного (старая) информация здесь и здесь.
Если вы хотите использовать пользовательский прокси / заглушку, сгенерированный компилятором MIDL из вашей IDL, вам нужно будет вместо этого изменить запись реестра интерфейса на CLSID этого прокси-объекта. Это позволяет использовать более широкий диапазон типов, например, "сырые" массивы.
Если вы поддерживаете IMarshal
тогда это то, что будет использоваться в предпочтении к любому из этих механизмов. Это означает, что вы можете изменить свой объект, чтобы агрегировать маршалер со свободным потоком (используя его реализацию IMarshal
) без необходимости что-либо менять в реестре. Это позволит избежать создания прокси.
Надеюсь это поможет.
Я немного устала от этого, но есть ли у вас функция с именем blindquery в вашем проекте? (обычно он объявляется мастером, если вы создали проект C++ ATL). Точка останова внутри функции. Функция, сгенерированная мастером, часто имеет проблемы с интерфейсом запроса, возвращающим E_NOINTERFACE из-за глючного кода.
редактировать (нашел пример кода) из моего старого проекта _ слепая
class ATL_NO_VTABLE CChildEvents :
public CComObjectRootEx <CComSingleThreadModel>,
public CComCoClass<CChildEvents, &CLSID_ChildEvents>,
public IDispatchImpl<IChildEvents, &IID_IChildEvents, &LIBID_XXX>
{
public:
CChildEvents(void) :
m_pItfMgr(0)
{
}
/* called from internalQI to tear off a new blind interface */
static HRESULT WINAPI _BlindQuery(void *pvThis, REFIID riid, void **ppv, DWORD dw);
DECLARE_REGISTRY_RESOURCEID(IDR_CHILDEVENTS)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CChildEvents)
COM_INTERFACE_ENTRY(IChildEvents)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_FUNC_BLIND(0, _BlindQuery)
END_COM_MAP()
};
HRESULT WINAPI CChildEvents::_BlindQuery(void *pvThis, REFIID riid, void **ppv, DWORD /* dw */ )
{
HRESULT hr = E_NOINTERFACE;
USES_CONVERSION;
try
{
if(pvThis == NULL)
{
ATLASSERT(FALSE);
}
else
{
/*
* cast the pvThis pointer to the actual class £
* so we can use it here £
* reinterpret_cast should be safe since we're calling ourself
*/
CChildEvents *pThis = reinterpret_cast < CChildEvents * > (pvThis);
if(pThis == NULL)
{
ATLASSERT(FALSE);
}
else
{
/* check to see if it matches on of our children's DIID */
if(memcmp(&riid,&l_someotherguid,sizeof(GUID)) == 0) {
/* if so cast to a IDispatch -- the standard for event interfaces */
*ppv = reinterpret_cast < IDispatch * > (pvThis);
/* addref */
pThis->AddRef();
/* reply */
hr = S_OK;
}
}
}
}
catch(...)
{
ATLASSERT(FALSE);
}
/* must not be in our map - tell them to GO FISH */
return(hr);
}