How to Enable Drag and Drop for an Elevated MFC Application on Windows
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.
Step 1: Enable Drag And Drop
In your dialog’s OnInitDialog
method, enable drag and drop. You can either do this for the entire dialog or a specific control:
DragAcceptFiles (); // entire dialog
m_oPath.DragAcceptFiles (); // control represented by variable m_oPath
Step 2: Message Map
Add ON_WM_DROPFILES()
to your message map, e.g.:
BEGIN_MESSAGE_MAP(<dialog class name>, CDialog)
ON_WM_DROPFILES()
END_MESSAGE_MAP()
Step 3: Drop Event Handler
Add an event handler for drop events. In your dialog’s header file:
afx_msg void OnDropFiles(HDROP hDropInfo);
And in the CPP file:
void <dialog class name>::OnDropFiles (HDROP dropInfo)
{
CString sFile;
DWORD nBuffer = 0;
// Get the number of files dropped
UINT nFilesDropped = DragQueryFile (dropInfo, 0xFFFFFFFF, NULL, 0);
// If more than one, only use the first
if (nFilesDropped > 0)
{
// Get the buffer size for the first filename
nBuffer = DragQueryFile (dropInfo, 0, NULL, 0);
// Get path and name of the first file
DragQueryFile (dropInfo, 0, sFile.GetBuffer (nBuffer + 1), nBuffer + 1);
sFile.ReleaseBuffer ();
// Do something with the path
}
// Free the memory block containing the dropped-file information
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 (
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.
Step 4: Allow Messages Through the UIPI Filter
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);
24 Comments
Thank you. This solution worked for my app.
Thank you for an absolutely straightforward, no-nonsense how-to, in (god almighty) FOUR steps!!!
This is the year 2020 and your precise, concise, easy to follow method remains a joy ten years later! And despite being Visual Studio 2019, they have not been able to break it.
I invite you to read the atrocious Microsoft docs that tell you in 18,000 words how not to do it!
From time to time over the past two decades I have implemented drag (and drop) multiple times, but have to start at ground zero each time, because of the drivel you read elsewhere on this subject.
Thank you for a total time saver.
Have you tested this on a windows 7 machine? It seems to fail, even with the for-loop approach.
Tom,yes, I developed and tested my drag and drop solution on Windows 7 x64.
I have created a .NET 3.5 application.In the Load event of my form, I put:ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);ChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD);ChangeWindowMessageFilter(0x0049, MSGFLT_ADD);with:private const uint WM_DROPFILES = 0x233;private const uint WM_COPYDATA = 0x004A;private const uint MSGFLT_ADD = 1;The application has a manifest containing:The application is signed with a 'Trusted Root Certification Authority' certificate and installed in the Program Files folder.Unfortunately the application still does not allow drag and drop from the windows explorer. It does allow drag and drop from other applications that run as administrator.Do you know what is missing, or how I can debug the problem?Thanks in advance,Tom
To do this without installing the Vista SDK in C++ use the following code:#define MSGFLT_ADD 1extern "C" BOOL ( STDAPICALLTYPE *pChangeWindowMessageFilter )( UINT,DWORD ) = NULL;BOOL CYourApp::InitInstance(){HMODULE hMod = 0; if ( ( hMod = ::LoadLibrary( _T( "user32.dll" ) ) ) != 0 ) { pChangeWindowMessageFilter = (BOOL (__stdcall *)( UINT,DWORD ) )::GetProcAddress( hMod, "ChangeWindowMessageFilter" ); } if ( pChangeWindowMessageFilter ) { pChangeWindowMessageFilter (WM_DROPFILES, MSGFLT_ADD); pChangeWindowMessageFilter (WM_COPYDATA, MSGFLT_ADD); pChangeWindowMessageFilter (0x0049, MSGFLT_ADD);} // rest of init instance here}
Thank you for the posting,but it's not working for me on Windows 7 (x64). Don't know why…Strange thing that my view is working correctly under Windows XP, and only as a Drag-n-Drop source under Windows 7, but not as a destination… And ChangeWindowMessageFilter return TRUE
Hi Helge,
thanks for the post, but it doesn’t seem to work for (Windows 7 64). ChangeWindowMessageFilter returns true, but Drag and drop still isn’t allowed. I even tried allowing all messages from 0 to 0x4000. No change.
Any Ideas?
All I can say is it works for me on Windows 7 x64. Sorry I cannot be more helpful.
Maybe you already know that ChangeWindowMessageFilterEx is the future-safe method for Win7.
Thanks. It worked for the most part. However, it appears to cause the app to crash on Windows XP. I believe the problem occurs because the method is not available on XP. Attempting to bypass the lines on XP using an
if ( osversion >= vista ) {
ChangeWindowMessageFilter (WM_DROPFILES, MSGFLT_ADD);
ChangeWindowMessageFilter (WM_COPYDATA, MSGFLT_ADD);
ChangeWindowMessageFilter (0x0049, MSGFLT_ADD);
}
Seemed obvious but it didn’t actually help. If you know of a solution that will work for XP Vista and Windows 7 by working around this limitation please let me know. Thanks again.
You need to do this:
// ChangeWindowMessageFilter
typedef BOOL (WINAPI *PFN_CHANGEWINDOWMESSAGEFILTER) (UINT, DWORD);
HMODULE hModule = GetModuleHandle (TEXT(“user32.dll”));
PFN_CHANGEWINDOWMESSAGEFILTER pfnChangeWindowMessageFilter = (PFN_CHANGEWINDOWMESSAGEFILTER) GetProcAddress (hModule, “ChangeWindowMessageFilter”);
Then you can use it like this:
(*pfnChangeWindowMessageFilter) (WM_DROPFILES, MSGFLT_ADD);
On my Win7_x86 + UAC, this fix works on my RAD Studio XE application.
(i had linked this API dynamically since it has to run on WinXP too).
Really nice work.
Thank you
Aaaa great! works like charm, thanks :)
This does not work for OLE drag&drop.
Hi,
This does not (yet) work for Windows 8. Does anyone have a solution for this ?
Sebastiaan,
Did you ever find out wrt windows 8 ?
There’s one more restriction applicable to IE. It’s discussed here:
http://stackoverflow.com/questions/14470665/drag-and-drop-from-ie-9-into-my-application
Thank you, worked a treat on Windows 8.1(x64) using VS2012
Thank you once again
Hi!
I’m trying to teach myself VB.net by creating a few projects.
My current project is an administration menu, running as an admin. Part of it’s functionality is to launch other applications as an administrator.
I’m trying to use drag and drop to create the controls, which works fine under a user account, but not as an admin.
Please could you advise how I could get the code in the post to work in VB.net (VS 2013)?
This only needs to work on windows 7 x64
Many thanks in advance
(sorry if this is a stupid question!)
Paul
Another way to solve the Drag/Drop issue is to let your application check its integrity level at startup and restart itself at a medium level.
this will not work if the application is running as Admin but elevated (with UAC)
I’m pretty sure that allowing 0x0049 messages (used to be documented as WM_COPYGLOBALDATA) makes it unnecessary to also allow WM_COPYDATA.
For those where it still doesn’t work try:
Bypass UIPI restrictions on SendMessage across privilege levels is available for UI automation programs using a special security attribute in the program’s application manifest, known as UIAccess.The following is an example of an application manifest entry for a UIAccess program.
By specifying UIAccess=”true” in the requestedPrivileges attribute, the application is stating a requirement to bypass UIPI restrictions on sending window messages across privilege levels. Windows Vista implements the following policy checks before starting an application with UIAccess privilege.
See: https://stackoverflow.com/questions/57900192/changewindowmessagefilterex-returns-error-code-5