Здравствуйте, Мизантроп, Вы писали:
S>>Понятно, что конструктор вызывается другим (по отношению к отправляющему события) потоком.
S>>Таким образом, pITypeInfo получается в конструкторе одним потоком, а используется в потоковой функции другим потоком. Нужно ли здесь использовать маршалинг? Ведь по идее должно было все обрушиться и при одном компоненте, а все работает.
М>Ну а с чего бы ему обрушаться
Когда Вы просто передаёте куда-то интерфейс, без маршалинга, Вы фактически передаёте адрес таблицы в памяти, содержащей адреса методов, реаслизующих этот интерфейс. При этом вызов метода интерфейса — это просто самый обыкновенный вызов функции по адресу. Вы же не пишите в реализации каждого метода проверку валидности текущего апартамента и не генерите по этому поводу исключений, откуда же возьмётся обрушение?
Мезантроп, ну как же? У нас указатель получается в конструкторе (один поток), а используется в функции потока (другой поток). Вроде же разные апартменты? В этом месте я вас немного не понял. Поясните пожалуйста свою мысль.
М>Апартаментная модель для потока всегда задаётся одинаково — вызовом CoInitialize(Ex).
М>Потоковая модель клиента — это одно, а поддерживаемая сервером — другое, и они друг от друга не зависят. Каждый из них выбирает ту модель, которую считает для себя наиболее подходящей, и маршалинг как раз и служит для согласования этих моделей.
М>От наличия событий тут тоже мало что зависит, только что общая логика усложняется. И любой событийный интерфейс, и любой "инфраструктурный" интерфейс поддержки событий — это самые обыкновенные интерфейсы, и к ним применимы все те-же правила, что и к любым другим. А правило простое — любой интерфейс должен пердаваться в другой апартамент только через маршалинг.
М>Например, Вы похоже pIConnectionPoint передаёте без маршалинга, что уже является ошибкой.
Да, вы правы. Та же история. pIConnectionPoint создается в конструкторе (один поток), а используется в функции потока (другой поток). Опять возникает вопрос, почему все не обрушивется, когда имеется всего один экземпляр компонента? Ведь в этом случае присутствуют оба потока: и поток клиена, который создает компонент (вызов конструктора) и событийный поток, который отправляет события (функция Routine()). Мезантроп, если требуется маршалинг, подскажите как его лучше сделать?
М>Но вообще мне, честно говоря, не очень нравится Ваша схема. Я бы сразу при подписке маршалировал событийный интерфейс в GIT, а в потоке один раз их демаршалировал. Разумеется, это потребует реализации собственного механизма подписки — IConnectionPointContainer для этого не слишком удачный выбор, но оно того стоит, по-моему.
Мезантроп, GIT у мена реализован в ConnectionPoint:
ConnectionPoint::ConnectionPoint(IConnectionPointContainer* pIConnectionPointContainer, IID iid)
: m_dwNextCookie(0), event_id(iid)
{
ML_ASSERT(pIConnectionPointContainer != NULL);
pContainer = pIConnectionPointContainer; // AddRef is not needed.
HRESULT hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable,
NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&pGIT);
ML_ASSERT( SUCCEEDED(hr) );
}
ConnectionPoint::~ConnectionPoint()
{
pGIT->Release();
}
STDMETHODIMP ConnectionPoint::GetConnectionInterface(IID* piid)
{
if (piid == NULL)
{
return E_POINTER;
}
*piid = event_id;
return S_OK;
}
STDMETHODIMP ConnectionPoint::GetConnectionPointContainer(IConnectionPointContainer** ppIConnectionPointContainer)
{
if (ppIConnectionPointContainer == NULL)
{
return E_POINTER;
}
*ppIConnectionPointContainer = pContainer;
pContainer->AddRef();
return S_OK;
}
STDMETHODIMP ConnectionPoint::Advise(IUnknown* pIUnknownSink, DWORD* pdwCookie)
{
com_ptr<IUnknown, IID_IUnknown> pI;
HRESULT hr;
if (pIUnknownSink == NULL || pdwCookie == NULL)
{
*pdwCookie = 0;
return E_POINTER;
}
hr = pIUnknownSink->QueryInterface(event_id, (void**)&pI);
if (SUCCEEDED(hr))
{
hr = pGIT->RegisterInterfaceInGlobal(pI, IID_IDispatch, &m_dwNextCookie);
ML_ASSERT( SUCCEEDED(hr) );
return S_OK;
}
else
{
return CONNECT_E_CANNOTCONNECT;
}
}
STDMETHODIMP ConnectionPoint::Unadvise(DWORD dwCookie)
{
pGIT->RevokeInterfaceFromGlobal(m_dwNextCookie);
return S_OK;
}
М>Если конечно вообще оправдана генерация событий в отдельном потоке. Может лучше чтобы доп. поток извещал главный о завершении обработки, а тот уже генерировал бы события?
Мезантроп, у меня все работает следующим образом:
Внутри компонента есть рабочий поток, который генерирует данные. Этот поток нельзя затормаживать отправкой событий, поскольку скорость их обработки целиком возложена на клиента. Для рабочего потока быстродействие критично, поэтому он формирует данные в специальные пакеты и ставит в очередь событий. Есть событийный поток, который обслуживает эту очередь: то есть занимается непосредственным отправлением. Событийный поток нужен для асинхронной генерации событий.
Может у вас будут каки-нибудь мысли по модернизации моей модели?