Если допустить, что класс объекта реализован как внутрипроцессный сервер, то специальный заместитель может стать просто вторым экземпляром того же класса, из чего вытекает следующая реализация GetUnmarshalClass:
STDMETHODIMP Point::GetUnmarshalClass(REFIID, void *, DWORD, void *, DWORD, CLSID *pclsid)
{
*pclsid = CLSID_Point;
// this class's CLSID
// CLSID этого класса return hr;
}
Для обеспечения того, чтобы для инициализационного сообщения было выделено достаточно места, методу объекта GetMarshalSizeMax требуется возвратить правильное количество байт:
STDMETHODIMP Point::GetMarshalSizeMax(REFIID, void *, DWORD, void *, DWORD, DWORD *pcb)
{
*pcb = 3 * sizeof (DWORD);
// m_x + m_y + header
return hr;
}
Когда маршалированная объектная ссылка демаршалируется с помощью CoUnmarshalInterface, тот факт, что она была маршалирована специальным образом, вызовет создание нового специального заместителя. Объектная ссылка содержит CLSID специального заместителя, возвращенный исходным объектом в своем методе GetUnmarshalClass. Когда создан новый специальный заместитель, его метод UnmarshalInterface получает инициализационное сообщение, которое объект записал в своей реализации MarshalInterface:
STDMETHODIMP Point::UnmarshalInterface(IStream *pStm, REFIID riid, void ** ppv)
{
*ppv = 0;
// read endian header // читаем заключительный заголовок DWORD dw; ULONG cbRead;
HRESULT hr = pStm->Read(&dw, sizeof (DWORD), &cbRead);
if (FAILED(hr) || cbRead != sizeof(DWORD)) return RPC_E_INVALID_DATA; bool bSwapEndian = dw == 0x009966FF;
// read m_x and m_y // читаем m_x и m_y
hr = pStm->Read(&dw, sizeof(DWORD), &cbRead);
m_x = dw; if (FAILED(hr) || cbRead != sizeof(DWORD)) return RPC_E_INVALID_DATA;
hr = pStm->Read(&dw, sizeof(DWORD), &cbRead);
m_y = dw; if (FAILED(hr)) || cbRead != sizeof(DWORD)) return RPC_E_INVALID_DATA;
// byte swap members if necessary
// байт переставляет свои биты, если необходимо
if (bSwapEndian) byteswapdata(&m_x, &m_y);
// return pointer to this object
// возвращаем указатель на этот объект return
this->QueryInterface(riid, ppv);
}
Отметим, что реализация MarshalInterface и UnmarshalInterface должна позаботиться о том, чтобы маршалированное состояние могло читаться на любой платформе. Это означает ручную работу по выравниванию, расстановке байтов и учету различий в размерах типов данных.
Приведенная здесь реализация UnmarshalInterface просто возвращает указатель вновь созданному специальному заместителю. Для простого объекта, маршалированного по значению, это может быть приемлемо. Однако более типичные реализации UnmarshalInterface могут захотеть найти несколько демаршалированных указателей, соответствующих одной и той же идентификационной единице СОМ, и возвратить указатель на заместитель той же единицы, чтобы установить отношение идентичности заместителя объекту. Это может не только сэкономить ресурсы, но также повысить чистоту программы.
Маршалер свободной поточной обработки (FreeThreaded Marshaler)
Если в классе установлена опция ThreadingModel="Both" , то она показывает, что экземпляры класса, а также объект класса могут безопасно находиться в любых апартаментах: STA или МТА. В то же время, согласно правилам СОМ, любой данный экземпляр будет находиться только в одном апартаменте. Если бы разработчик объекта прошел все этапы проверки того, что объект может благополучно находиться в МТА, то в этом случае объекту вообще не нужно было бы заботиться об апартаментах. Одновременный доступ к подобному объекту мог бы быть не только для нескольких потоков внутри МТА, но также от потоков вне МТА (например, от потоков, выполняемых в STA). В то же время клиенты не могут знать, что такой доступ является безопасным для отдельно взятого объекта, поэтому любое совместное использование интерфейсного указателя в нескольких апартаментах должно быть установлено с использованием явной технологии маршалинга. Это означает, что доступ к внутрипроцессному объекту будет осуществляться через ORPC-вызовы, если только вызывающий объект не выполняется в том же самом апартаменте, где был создан объект.