Finding good information on how to enable drag and drop for MFC applications is hard enough (why?). But just when you think you should have it working, you hit a very solid wall: UIPI (User Interface Privilege Isolation, a variant of UAC, User Account Control). Because of "security" reasons, UIPI disables drag and drop from medium integrity processes (Explorer) to high integrity (aka elevated) processes by filtering out most window messages, including those required for drag and drop. In essence, drag and drop from Explorer to your elevated application does not work.
To waste your time, Microsoft added the API function ChangeWindowMessageFilter that lets specific window messages through (punching a hole in the UIPI "firewall"), but just adding WM_DROPFILES
to the list of allowed messages does not help. Drag and drop uses more than just WM_DROPFILES
. But where is a list of the window messages required for drag and drop? Even the venerable Raymond Chen refused to explain how to make it work. Well, here is how.
Basic Drag and Drop
This assumes you have a dialog-based MFC application. I have implemented this with Visual Studio 2008 SP1.
DragAcceptFiles ();
m_oPath.DragAcceptFiles ();
BEGIN_MESSAGE_MAP(<dialog class name>, CDialog)
ON_WM_DROPFILES()
END_MESSAGE_MAP()
afx_msg void OnDropFiles(HDROP hDropInfo);
- In your dialog's
OnInitDialog
method, enable drag and drop. You can either do this for the entire dialog or a specific control:
- Add
ON_WM_DROPFILES()
to your message map, e.g.:
- Add an event handler for drop events. In your dialog's header file:
And in the CPP file:
void <dialog class name>::OnDropFiles (HDROP dropInfo)
{
CString sFile;
DWORD nBuffer = 0;
UINT nFilesDropped = DragQueryFile (dropInfo, 0xFFFFFFFF, NULL, 0);
if (nFilesDropped > 0)
{
nBuffer = DragQueryFile (dropInfo, 0, NULL, 0);
DragQueryFile (dropInfo, 0, sFile.GetBuffer (nBuffer + 1), nBuffer + 1);
sFile.ReleaseBuffer ();
}
DragFinish(dropInfo);
}
UAC and Elevation
Now comes the interesting part. All of the above will work - unless your application runs elevated. As mentioned earlier, WM_DROPFILES
messages are simply filtered out by UIPI. You can re-enable individual messages by calling ChangeWindowMessageFilter (<message>, MSGFLT_ADD)
, but enabling WM_DROPFILES
alone is not sufficient. Here is my first attempt to work around that:
for (int i = 0; i <= WM_DROPFILES; i++)
{
ChangeWindowMessageFilter (i, MSGFLT_ADD);
}
Hooray! Drag and drop works! But letting all messages through is probably a little bit too much. So I tried to narrow things down.
After some trial and error, I came up with the following list of messages you need to allow through the UIPI filter in order to enable drag and drop:
ChangeWindowMessageFilter (WM_DROPFILES, MSGFLT_ADD);
ChangeWindowMessageFilter (WM_COPYDATA, MSGFLT_ADD);
ChangeWindowMessageFilter (0x0049, MSGFLT_ADD);