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

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

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

Большинство элементов управления ActiveX и главное окно одного из EXE-файлов на основе CMDIFrameWnd реализуют функцию перетаскивания. Элементы управления 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);
    }
}

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

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 из интерфейса IDataObject объекта m_pOleDataSource.

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

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

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

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

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

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

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

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


person Ilia    schedule 01.04.2014    source источник
comment
Этот вопрос кажется не по теме, потому что в нем не хватает информации, чтобы Стивен Хокинг смог диагностировать проблему. Опишите вашу проблему Стивену Хокингу более подробно или включите минимальный пример в сам вопрос.   -  person Captain Obvlious    schedule 01.04.2014
comment
Программисты, как правило, запускают Visual Studio с повышенными привилегиями при создании компонентов COM. Поскольку это необходимо для их регистрации. Затем отладка приложения вызывает проблемы, поскольку приложение также будет работать с повышенными правами. Это отключает перетаскивание из любого приложения, которое работает без повышенных прав, например Explorer.   -  person Hans Passant    schedule 02.04.2014
comment
Спасибо, Ганс, я тоже думал об этом. Но это противоречит следующему факту. У нас есть еще одно приложение, позволяющее перетаскивать несколько элементов управления ActiveX, которые оно содержит (на самом деле оно служит внепроцессным сервером для первого). Упомянутые элементы управления ActiveX являются как источником перетаскивания, так и целью перетаскивания, и они также перестают работать, как я описал в вопросе, при перетаскивании элемента внутри того же элемента управления. Таким образом, это не может быть результатом различных настроек безопасности для источника перетаскивания и цели перетаскивания. С уважением, Илья.   -  person Ilia    schedule 02.04.2014
comment
Кроме того, перетаскивание хорошо работает, когда COM-компоненты приложения зарегистрированы, и не работает, когда одно и то же приложение с нетронутой реализацией перетаскивания выполняется изолированно. Какая разница?   -  person Ilia    schedule 02.04.2014
comment
Чтобы прояснить ситуацию: работают ли обе стороны проблемной операции D&D на одном уровне UAC?   -  person Simon Mourier    schedule 02.04.2014
comment
Да, я так считаю, потому что в нашем втором приложении (о котором я упоминал в предыдущем комментарии) обе стороны - это одно и то же окно. Я также считаю, что причина, препятствующая перетаскиванию в обоих приложениях, одна и та же. Илья.   -  person Ilia    schedule 02.04.2014
comment
Если проблема D&D возникает в том же окне, я не вижу, как это может быть связано с изменениями активации COM. PS: укажите адрес пользователя SO (например, @ user3153215), когда вы ответите, я не был уведомлен о ваших последних комментариях (Ганс тоже не был)   -  person Simon Mourier    schedule 03.04.2014
comment
Я добавил новую информацию в свой пост; может быть это может дать подсказку. Сначала я пытаюсь разрешить случай с нашим основным приложением, так как оно кажется более простым: это только цель перетаскивания и работает с обычным окном, а не с ActiveX. @Simon Mourier.   -  person Ilia    schedule 07.04.2014
comment
Я переписал весь пост и добавил новую информацию о том, что происходит на стороне источника перетаскивания. Когда реализация отбрасывания источника вызывает :: DoDragDrop API, вызов завершается с ошибкой незарегистрированного класса REGDB_E_CLASSNOTREG. Это типичная ошибка для приложений без регистрации. Однако какой класс имеется в виду в данном конкретном случае? @Simon   -  person Ilia    schedule 29.04.2014
comment
Вы упомянули Проводник Windows. Обычно это может включать UAC, поскольку он запускается (обычно) как неадминистративный. Если ваше приложение работает на другом уровне UAC (например, повышенном), D&D между Explorer и App не будет работать.   -  person Simon Mourier    schedule 29.04.2014
comment
Но почему тогда D&D сразу начинает работать, когда я добавляю одну строчку кода DragAcceptFiles () ?! Эта единственная строка может изменить уровень UAC? И почему при перетаскивании элемента внутри одного и того же элемента ActiveX возникает ошибка «Класс не зарегистрирован»? Какой класс :: DoDragDrop может искать? @Simon   -  person Ilia    schedule 29.04.2014
comment
Кроме того, Exe, в который я перетаскиваю файлы из Windows Explorer, имеет в своем проекте свойства UAC Execution Level = asInvoker. Я не понимаю, как в таком случае приложение может работать с повышенными правами. @Simon   -  person Ilia    schedule 29.04.2014
comment
Нет, одна строка не может изменить уровень UAC (он определяется при запуске процесса). Хммм ... сложно диагностировать такую ​​pb без воспроизводящего образца :)   -  person Simon Mourier    schedule 29.04.2014
comment
Если бы я мог сделать воспроизводящий образец, я бы обратился напрямую в Microsoft ... Этого не происходит в маленьком примере. Есть еще одна удивительная вещь. В том же элементе управления ActiveX, где :: DoDragDrop возвращает ошибку «Класс не зарегистрирован», копирование / вставка работает хорошо. И обе технологии используют один и тот же набор процедур для доступа к буферу обмена ... Может быть, у Ганса есть какие-то идеи? @Simon   -  person Ilia    schedule 29.04.2014
comment
Посмотрите комментарий пользователя о uac здесь msdn.microsoft.com/en-us/library/windows/desktop/. Не объяснение, а ключ к разгадке того, что играет UAC.   -  person dkackman    schedule 04.05.2014
comment
Если вы внимательно прочтете весь пост и просмотрите предыдущие комментарии, вы увидите, что проблема не в UAC. К сожалению, это не так просто. @dkackman   -  person Ilia    schedule 05.05.2014
comment
Wrt. углубившись в ошибку "класс незарегистрированный", вы можете попробовать отслеживать приложения с помощью Process Следите за, пока происходит ошибка.   -  person Martin Ba    schedule 06.05.2014
comment
Благодаря @MartinBa я нашел проблему !!! Монитор процессов показал мне, что пока я перетаскиваю элемент, система безуспешно пытается получить доступ к идентификатору класса в реестре. Тогда я обнаружил, что это действительно не идентификатор класса, а IDataObject идентификатор интерфейса. В конце концов выяснилось, что в одном из нескольких манифестов, автоматически созданных Visual Studio, Studio поставила proxyStubClsid32 равным идентификатору интерфейса для пары системных интерфейсов! Так как это был довольно большой манифест с почти сотней интерфейсов, я не сразу его уловил. Что тут сказать ... Майкрософт ...   -  person Ilia    schedule 07.05.2014
comment
Вы можете и должны ответить на свой вопрос (с соответствующими подробностями из комментариев), если вы нашли решение.   -  person Martin Ba    schedule 07.05.2014


Ответы (1)


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

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

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

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

person Ilia    schedule 08.05.2014
comment
Вы также можете принять свой собственный ответ (после льготного периода), чтобы навести порядок :-) - person Martin Ba; 25.06.2014