Кто-нибудь знает, какая связь может существовать между свободным от регистрации COM и функциональностью перетаскивания?

Кто-нибудь знает, какая связь может существовать между свободным от регистрации COM и функциональностью перетаскивания?

В частности, у нас есть огромное C ++ CAD / CAM-приложение, состоящее из нескольких EXE-файлов и нескольких сотен DLL-файлов. Многие из них служат в качестве COM-серверов (как внутренних, так и внешних) и / или клиентов, а также реализуют элементы управления ActiveX.

Большинство элементов управления ActiveX и основные CMDIFrameWndокно одного из EXE-файлов реализует функцию перетаскивания. Элементы управления ActiveX реализуют как источник перетаскивания, так и цель перетаскивания, а главное окно предназначено только для перетаскивания, в частности, для файлов из проводника Windows.

Реализация перетаскивания является довольно стандартной и основана на двух членах данных, полученных из COleDataSource а также COleDropTarget для удаления источника и удаления цели соответственно. COleDropTargetчлен зарегистрирован в соответствующем окне OnCreate метод. Это также переопределяет OnDragEnter, OnDragOver а также OnDrop методы аналогичным образом. А именно, система поставляется COleDataObject Параметр запрашивается для определенного формата (в частности, CF_HDROP), и в случае положительного ответа данные (например, путь к файлу) извлекаются из буфера обмена. Код выглядит следующим образом:

static FORMATETC g_FileFmt = {CF_HDROP, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
....
// Inside OnDragEnter, OnDragOver or OnDrop method
STGMEDIUM stgmedium = {0,0,0};
if (pDataObject->IsDataAvailable(g_FileFmt.cfFormat))
{
HRESULT hr = pDataObject->GetData(g_FileFmt.cfFormat, &stgmedium);
HDROP hdrop = (HDROP)GlobalLock(stgmedium.hGlobal);
if (hdrop != 0)
{
int FilesCount = DragQueryFile(hdrop, (UINT)-1, 0, 0);
if (FilesCount != 0)
{
TCHAR FileName[_MAX_PATH];
DragQueryFile(hdrop, 0, FileName, _MAX_PATH);
// Check file extension and store the file name for farther use.
}
GlobalUnlock(hdrop);
}
}

Реализация drop source также проста и выглядит следующим образом:

void CDmDocListCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (pNMListView->iItem != -1 && m_pOleDataSource && prv_BeginDrag())
{
DROPEFFECT DE = m_pOleDataSource->DoDragDrop(
DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK, 0);
}
*pResult = 0;
}

где prv_BeginDrag() Функция собирает перетаскиваемые данные, упаковывает их и помещает в буфер обмена, вызывая SetData метод из m_pOleDataSource объекты IDataObject интерфейс.

Все это работало отлично, пока не было решено сделать все приложение без регистрации. Мне потребовалось три месяца, чтобы заставить приложение работать изолированно (без регистрации компонентов COM) путем встраивания манифестов, запуска COM-серверов вне процесса по требованию и изменения CLSID некоторых классов, чтобы отделить экземпляры одного и того же сервера от разных папки. Наконец-то он начинает работать — но без функции перетаскивания, несмотря на то, что мои изменения даже не затронули меня.

На целевой стороне перетаскивания, когда я перетаскиваю файл из проводника Windows, как показано выше, COleDataObject::IsDataAvailable возвращает false, хотя раньше мои изменения возвращали true. В то же время, если я добавлю одну строку кода «DragAcceptFiles();«к методу OnCreate главного окна, перетаскивание начинает работать через стандартный CFrameWnd WM_DROPFILE обработчик сообщений

Со стороны источника отбрасывания перетаскиваемые данные успешно упаковываются и помещаются в буфер обмена, но COleDataSource::DoDragDrop метод терпит неудачу, потому что вызов ::DoDragDrop API внутри реализации MFC возвращает REGDB_E_CLASSNOTREG «Класс не зарегистрирован».

Это означает, что изменения активации COM как-то влияют на поведение перетаскивания. Как?

Постскриптум 1) EXE-файл, в который я перетаскиваю файлы из Проводника Windows, имеет в своем проекте свойства «Уровень выполнения UAC = asInvoker». Насколько я понимаю, это говорит о том, что EXE будет работать на том же уровне UAC, что и Windows Explorer, при запуске двойным щелчком по файлу.

2) Удивительно, но, несмотря на то, что перетаскивание перестало работать с описанными выше симптомами, копирование / вставка продолжает работать хорошо, несмотря на то, что обе технологии имеют схожую реализацию.

3) Я полагаю, что если выяснить, когда :: DoDragDrop API возвращает ошибку «Класс не зарегистрирован», и какой класс он ищет, можно было бы решить эту проблему.

Спасибо за помощь,
Илья.

6

Решение

Следуя совету MartinBa, я решил проблему с помощью Process Monitor. Монитор процессов показал мне, что, когда я перетаскиваю элемент в элементе управления ActiveX (упомянутый в вопросе), система безуспешно пытается получить доступ к идентификатору класса в реестре. Ища этот идентификатор, я обнаружил, что это действительно не идентификатор класса, но IDataObject идентификатор интерфейса На него ссылались в одном из моих файлов манифеста.

Большинство манифестов я написал вручную, но некоторые, особенно в начале проекта, не имеющие опыта в этой области, я автоматически генерировал в Visual Studio из существующей библиотеки типов. В одной из них студия включила comInterfaceExternalProxyStub оператор для пары системных интерфейсов, в которых proxyStubClsid32 элемент был (ошибочно) равен идентификатору интерфейса.

Я все еще не уверен, должны ли эти системные интерфейсы присутствовать в манифесте; например, IDataObject упоминается только как параметр метода в одном из определений IDL. Во всяком случае, я исправил только proxyStubClsid32 значение, и проблема исчезла …

Мораль этой очень болезненной для меня истории — всегда проверять производительность автоматических инструментов …

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]