if (dwDestCtx == MSHCTX_LOCAL || dwDestCtx == MSHCTX_INPROC) return this->MyCustomMarshalingRoutine(pcb);
// unsupported context, delegate to std marshal
// контекст не поддерживается, обращаемся к стандартному маршапингу
IMarshal *pMsh = 0;
HRESULT hr = CoGetStandardMarshal (riid, pv, dwDestCtx, pvDestCtx, mshlflags, &pMsh);
if (SUCCEEDED(hr)) {
hr = pMsh->GetMarshalSizeMax(pcb, riid, pv, dwDestCtx, pvDestCtx, mshlflags);
pMsh->Retease();
}
return hr;
}
В этом фрагменте кода не показано, как писать инициализационное сообщение для случая, когда действительно желателен специальный маршалинг. Дело в том, что не существует стандартной реализации каждого из методов IMarshal (отсюда и термин специальный (custom) маршалинг). Существует, однако, несколько общих сценариев, в которых специальный маршалинг чрезвычайно выигрышен и реализация IMarshal в этих сценариях – довольно обычное явление. Безусловно, наиболее общим приложением IMarshal является реализация маршалинга по значению ( marshal-by-value ).
Маршалинг по значению наиболее удобен для таких объектов, которые после инициализации никогда не изменяют своего состояния. Обертки СОМ для структур – вот типичный пример объекта, который просто инициализирован, передан другому объекту для запроса и затем уничтожен. Такой объект является первым кандидатом для специального маршалинга. При реализации маршалинга по значению реализация объекта почти всегда является внутрипроцессным сервером. Это позволяет объекту и заместителю разделять один и тот же класс реализации. Идея маршалинга по значению состоит в том, что специальный заместитель становится клоном исходного объекта. Из этого следует, что маршалированная объектная ссылка должна содержать все состояние исходного объекта, а также (для простоты) то, что CLSID специального заместителя должен быть тем же, что и у исходного объекта.
Представим следующее определение класса СОМ-обертки вокруг простой двумерной точки:
class Point : public IPoint, public IMarshal
{
long m_x;
long m_y;
public:
Point(void) : m_x(0), m_y(0) {}
IMPLEMENT_UNKNOWN (Point)
BEGIN_INTERFACE_TABLE(Point)
IMPLEMENTS_INTERFACE(IPoint)
IMPLEMENTS_INTERFACE(IMarshal)
END_INTERFACE_TABLE()
// IPoint methods
// методы IPoint
// IMarshal methods
// методы IMarshal
};
Для поддержки маршалинга по значению метод MarshalInterface класса должен преобразовать состояние объекта в последовательную форму в качестве инициализационного сообщения для заместителя:
STOMETHODIMP Point::MarshalInterface(IStream *pStm, REFIID, void *, DWORD, void *, DWORD)
{
// write out endian header
// переписываем завершающий заголовок
DWORD dw = OxFF669900;
HRESULT hr = pStm->Write(&dw, sizeof(DWORD), 0);
if (FAILED(hr)) return hr; dw = m_x;
hr = pStm->Write(&dw, sizeof(DWORD), 0);
if (FAILED(hr)) return hr; dw = m_y;
return pStm->Write(&dw, sizeof (DWORD), 0);
}
Если допустить, что класс объекта реализован как внутрипроцессный сервер, то специальный заместитель может стать просто вторым экземпляром того же класса, из чего вытекает следующая реализация 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 должна позаботиться о том, чтобы маршалированное состояние могло читаться на любой платформе. Это означает ручную работу по выравниванию, расстановке байтов и учету различий в размерах типов данных.
Читать дальше