Does anyone know what relationship can exist between a non-contact COM interface and a drag and drop function?

Does anyone know what relationship can exist between contactless COM and the drag and drop function?

In particular, we have a huge C ++ CAD / CAM application that includes several EXEs and several hundred DLLs. Many of them are COM servers (both inside and outside processes) and / or clients, and they also implement ActiveX controls.

Most ActiveX controls and the main CMDIFrameWnd single window of one of the EXE implement drag and drop functions. ActiveX controls implement both drop and drop sources, and the main window is just the target point, in particular for files from Windows Explorer.

The drag / drop implementation is fairly standard and is based on two data elements derived from COleDataSource and COleDropTarget for the drop source and drop target, respectively. The COleDropTarget element is registered by the corresponding window in the OnCreate method of the window. It also overrides the OnDragEnter , OnDragOver and OnDrop similar way. Namely, the COleDataObject parameter set by the system is requested for a specific format (in particular, CF_HDROP), and in the case of a positive response, data (for example, the path to the file) is extracted from the clipboard. The code is as follows:

 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); } } 

The implementation of the return source is also simple and looks like this:

 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; } 

where the prv_BeginDrag() function collects the dragged data, packs it and puts it on the clipboard by calling the SetData method from the m_pOleDataSource object IDataObject .

All this worked perfectly until it was decided to make a free registration of the entire application. It took me three months for the application to be isolated (without registering COM components), deploying manifests, launching COM servers out of turn on request and changing the CLSID of some classes to separate instances of the same server running from different folders. Finally, it starts to work, but without the drag and drop functionality, even though my changes were not affected.

On the target side of the drag and drop, when I drag the file from Windows Explorer, pictured above, calling COleDataObject::IsDataAvailable returns false, although before my changes came back. At the same time, if I add one line of code " DragAcceptFiles(); " to the main OnCreate window, the drag and drop starts working through the standard CFrameWnd WM_DROPFILE message handler.

On the transfer source side, the dragged data is successfully packed and placed on the clipboard, but the COleDataSource::DoDragDrop fails because calling API ::DoDragDrop inside the MFC implementation returns the result REGDB_E_CLASSNOTREG "Class not registered".

This means that activating COM somehow influences the drag and drop behavior. How?

PS 1) EXE, to which I drag and drop files from Windows Explorer, has in its properties the project "Runtime level UAC = asInvoker". As far as I understand, it reports that the EXE will start at the same UAC level as Windows Explorer at startup by double-clicking the file.

2). Quite unexpectedly, despite the fact that the drag / drop stopped working with the symptoms described above, Copy / Paste continues to work well, despite the fact that both technologies have a similar implementation.

3) I believe that if you find out when :: DoDragDrop API returns the error "Class is unregistered" and what class it is looking for, it would be possible to solve the problem.

Thanks for the help, Ilia.

+6
source share
1 answer

Following MartinBa's advice, I solved the problem using Process Monitor. The process monitor showed me that while I drag and drop an element in the ActiveX control (mentioned in the question), the system unsuccessfully tries to access the class identifier in the registry. If you are looking for this identifier, I found that this is really not a class identifier, but an IDataObject interface IDataObject . This was indicated in one of my manifest files.

Most of the manifests that I wrote manually, but a few, especially at the beginning of a project that did not have experience in this area, I automatically created Visual Studio from an existing type library. In one of them, Studio included the comInterfaceExternalProxyStub operator for several system interfaces in which the proxyStubClsid32 element was (erroneously) equal to the interface identifier.

I'm still not sure if these system interfaces should be present in the manifest; for example, IDataObject is only referred to as a method parameter in one of the IDL definitions. Anyway, I only corrected the value of proxyStubClsid32 and the problem disappeared ...

The moral of this very painful story for me is to always check the output of automatic tools ...

+2
source

All Articles