Original Post
Hi,
I've been using a multi-threaded COM apartment for my application's threads in order to achieve the best performance I can for anything that uses COM.
However I recently wanted to add support for drag and drop to my program window. Windows uses OLE for this type of operation. According to MSDN, "applications that use the following functionality must call OleInitialize before calling any other function in the COM library:
Clipboard
Drag and drop
Object linking and embedding (OLE)
In-place activation"
Okay fine, so I have to call OleInitialize. The problem is, OleInitialize wants to put the thread into a single-threaded apartment: "OleInitialize calls CoInitializeEx internally to initialize the COM library on the current apartment. Because OLE operations are not thread-safe, OleInitialize specifies the concurrency model as single-thread apartment."
Since I want to be able to stick with an MTA for the rest of the application, I decided to create a separate thread running in an STA for the express purpose of handling drag and drop requests. The thread registers the main window as a drop target and then sits and pumps messages for OLE (which is required in a STA). When it gets the signal to quit, it removes drag and drop support from the main window and exits:
My question is: Is it a bad practice to setup OLE on a dedicated thread like this and give it a handle to the main window running on the original thread? The application runs fine but I'm nervous that I've opened the door to some sort of concurrancy issue or other evil thing.
I know this is probably a difficult question to answer because the RegisterDragDrop function's documentation doesn't include any details on this, but I'm curious as to whether anyone else is familiar with doing something like this as a general OLE practice.
Thanks!!
I've been using a multi-threaded COM apartment for my application's threads in order to achieve the best performance I can for anything that uses COM.
However I recently wanted to add support for drag and drop to my program window. Windows uses OLE for this type of operation. According to MSDN, "applications that use the following functionality must call OleInitialize before calling any other function in the COM library:
Clipboard
Drag and drop
Object linking and embedding (OLE)
In-place activation"
Okay fine, so I have to call OleInitialize. The problem is, OleInitialize wants to put the thread into a single-threaded apartment: "OleInitialize calls CoInitializeEx internally to initialize the COM library on the current apartment. Because OLE operations are not thread-safe, OleInitialize specifies the concurrency model as single-thread apartment."
Since I want to be able to stick with an MTA for the rest of the application, I decided to create a separate thread running in an STA for the express purpose of handling drag and drop requests. The thread registers the main window as a drop target and then sits and pumps messages for OLE (which is required in a STA). When it gets the signal to quit, it removes drag and drop support from the main window and exits:
//////////////////////////////////////////////////////////////////////////// Thread procedure for running OLE drag and drop. We use a separate thread// because OLE's drag and drop feature requires a single-threaded COM// apartment, and we want our application to be able to use a multi-threaded// apartment.uint __stdcall ThreadProc(void *arguments){ // Applications that use drag and drop must call OleInitialize before calling any other function in the COM library. OleInitialize(NULL); CDropTarget *target; try { target = new CDropTarget(); } catch (bad_alloc) { StatusLog_ReportOutOfMemory(); OleUninitialize(); _endthreadex(0); return 0; } // The CoLockObjectExternal function prevents the reference count of an object // from going to zero, thereby "locking" it into existence until the lock is // released. CoLockObjectExternal(target, TRUE, FALSE); // Tell OLE that the window is a drop target. HRESULT hResult = RegisterDragDrop(g_hMainWnd, target); if (hResult != S_OK) StatusLog_ReportWarning(L"Drag and Drop", L"Unable to register drag and drop (error code %d).", hResult); // Each single-threaded apartment must have a message loop to handle calls from other // processes and apartments within the same process. If we didn't have this message loop, // we would hang other applications that are acting as drag-drop sources for us because // their message could not get handled. while (MsgWaitForMultipleObjects(1, &g_hEvtStopThread, FALSE, INFINITE, QS_ALLINPUT)) { MSG msg; if (GetMessage(&msg, NULL, NULL, NULL) <= 0) break; TranslateMessage(&msg); DispatchMessage(&msg); } // Remove drag and drop from the window. RevokeDragDrop(g_hMainWnd); // Remove the strong lock. CoLockObjectExternal(target, FALSE, TRUE); // Release our own reference. target->Release(); OleUninitialize(); // Calling end is not strictly necessary but according to MSDN is a good practice. _endthreadex(0); return 0;}My question is: Is it a bad practice to setup OLE on a dedicated thread like this and give it a handle to the main window running on the original thread? The application runs fine but I'm nervous that I've opened the door to some sort of concurrancy issue or other evil thing.
I know this is probably a difficult question to answer because the RegisterDragDrop function's documentation doesn't include any details on this, but I'm curious as to whether anyone else is familiar with doing something like this as a general OLE practice.
Thanks!!