Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Drag & Drop to elevated app on Windows 8 fails


This question is answered. Helpful answers available: 1. Correct answers available: 1.


Permlink Replies: 13 - Last Post: Jun 9, 2014 12:21 PM Last Post By: Peter Van Hove Threads: [ Previous | Next ]
Peter Van Hove

Posts: 48
Registered: 5/7/09
Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 3, 2014 11:49 AM
Hi,

I recently noticed that I can't drag (a file) to my application if/when it's
running elevated (and the rest is not, so via the UAC).

I realize this is due to the elevation and I have found a solution for
Windows 7
( most notably:
http://helgeklein.com/blog/2010/03/how-to-enable-drag-and-drop-for-an-elevated-mfc-application-on-vistawindows-7/
)

But, despite searching, I can't find a solution that also works on Windows 8
(previous method doesn't cut it for Windows 8)

I was wondering if others have ran into this already and if they found a
solution ?
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 3, 2014 12:51 PM   in response to: Peter Van Hove in response to: Peter Van Hove
Peter wrote:

I recently noticed that I can't drag (a file) to my application
if/when it's running elevated (and the rest is not, so via the UAC).

Drag&Drop uses several window messages that are being blocked by UIPI (User
Interface Privilege Isolation) when your process is running in an elevated
state, thus non-elevated processes are not allowed to drag&drop to your app
by default. You need to use ChangeWindowMessageFilter/Ex() to let the following
messages through (as noted in the article you linked to):

WM_DROPFILES
WM_COPYDATA
WM_COPYGLOBALDATA (not declared in the Win32 API, but its value is 0x0049)

Refer to MSDN for more details:

ChangeWindowMessageFilter function
http://msdn.microsoft.com/en-us/library/windows/desktop/ms632675(v=vs.85).aspx

ChangeWindowMessageFilterEx function
http://msdn.microsoft.com/en-us/library/windows/desktop/dd388202(v=vs.85).aspx

Windows Integrity Mechanism Design
http://msdn.microsoft.com/en-us/library/bb625963.aspx
(see the "User Interface Privilege Isolation (UIPI) and integrity" section
near the bottom of the page)

But, despite searching, I can't find a solution that also works on
Windows 8 (previous method doesn't cut it for Windows 8)

Why not? What exactly happens?

I was wondering if others have ran into this already

I use the above solution in my app and it works fine, but I don't target
Windows 8 yet so I had not noticed any problem with it.

and if they found a solution ?

The best solution would be to redesign your app to not run elevated in the
first place. Ever since UAC was introduced, Microsoft has been pushing people
to design their apps for "least privileges needed", which is good practice
anyway for security reasons. If you need to perform elevated tasks once
in awhile, separate them out into their own process or COM object that you
can then run elevated when needed. Don't elevate your main process if you
can avoid it. Then you are not subject to UIPI restrictions.

--
Remy Lebeau (TeamB)
Peter Van Hove

Posts: 48
Registered: 5/7/09
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 3, 2014 1:22 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks Remy,

I actually just noticed that the solution doesn't work under Windows 7 either.
I got confused there for a second during my testing and I thought it worked for Windows 7 but not for Windows 8, which was inline with user comments made in the posted article.
Now it turns out, after double checking, that it doesn't work for Windows 7 either.

I do the following in the application source file, before I call Application->Run();
The code seems to execute correctly but I now wonder if I should call this in the actual main form class/code ?

            typedef BOOL (WINAPI *PFN_CHANGEWINDOWMESSAGEFILTER) (UINT, DWORD);
 
            HMODULE hModule = GetModuleHandle (TEXT("user32.dll"));
            PFN_CHANGEWINDOWMESSAGEFILTER pfnChangeWindowMessageFilter = (PFN_CHANGEWINDOWMESSAGEFILTER) GetProcAddress (hModule, "ChangeWindowMessageFilter");
 
            if (pfnChangeWindowMessageFilter)
                {
                (*pfnChangeWindowMessageFilter) (WM_DROPFILES, MSGFLT_ADD);
                (*pfnChangeWindowMessageFilter) (WM_COPYDATA, MSGFLT_ADD);
                (*pfnChangeWindowMessageFilter) (0x0049, MSGFLT_ADD);
                }
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 3, 2014 1:45 PM   in response to: Peter Van Hove in response to: Peter Van Hove
Peter wrote

I do the following in the application source file, before I call
Application->Run();

On Windows 7 and later, you really should use ChangeWindowMessageFilterEx()
instead. Use ChangeWindowMessageFilter() only on Vista. You can have your
Form override the virtual CreateWnd() method to call ChangeWindowMessageFilterEx()
each time the Form's HWND is (re)created.

One thing I just noticed is the following comment about ChangeWindowMessageFilterEx()
on MSDN:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd388202(v=vs.85).aspx

I'm finding this function returns FALSE under Win8.1 but GetLastError returns
0. The same code returns TRUE under Win8.0 and Win7 and works as expected.
Anyone know why, what has changed under Win8.1?

Maybe Microsoft is changing the rules again?

The code seems to execute correctly

Is ChangeWindowMessageFilter() returning TRUE or FALSE?

I now wonder if I should call this in the actual main form class/code ?

If you do that, use ChangeWindowMessageFilterEx() instead. Since ChangeWindowMessageFilter()
has process-wide scope, it is fine to call it in WinMain() (I call it in
my MainForm's constructor instead, but that is the same effect of calling
it before Application->Run() is called).

--
Remy Lebeau (TeamB)
Peter Van Hove

Posts: 48
Registered: 5/7/09
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 3, 2014 3:54 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Is ChangeWindowMessageFilter() returning TRUE or FALSE?

TRUE !

I will look into using the other functions you mention tomorrow. It's getting late again (CET).

PS. previously posted code in the constructor of the main form doesn't make it work either.
Peter Van Hove

Posts: 48
Registered: 5/7/09
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 4, 2014 7:42 AM   in response to: Peter Van Hove in response to: Peter Van Hove
I'm not having much luck with this.

When I override the CreateWnd() function the application crashes (not even code in the CreateWnd() function yet). It gets called quite a number of times and next there is a weird error (PS. I use CG 2009) followed by CodeGuard complaining about a memory leak in system.pas

So I put following code in the constructor of the main form.
I also tried this with TreeView->Handle (TreeView being the drop target) and this->Handle.
The functions always return TRUE but the drag does not work when the app is elevated (it works fine in normal mode).
PS. Drag is implemented via Anders Melander's component.

Input is appreciated

    typedef BOOL (WINAPI *PFN_CHANGEWINDOWMESSAGEFILTEREX) (HWND , UINT, DWORD, void* /*PCHANGEFILTERSTRUCT*/ );
 
    HMODULE hModule = GetModuleHandle (TEXT("user32.dll"));
    PFN_CHANGEWINDOWMESSAGEFILTEREX pfnChangeWindowMessageFilterEx = (PFN_CHANGEWINDOWMESSAGEFILTEREX) GetProcAddress (hModule, "ChangeWindowMessageFilterEx");
 
    if (pfnChangeWindowMessageFilterEx)
        {
        if ((*pfnChangeWindowMessageFilterEx) (Application->Handle, WM_DROPFILES, MSGFLT_ADD, NULL))
            {
            if ((*pfnChangeWindowMessageFilterEx) (Application->Handle, WM_COPYDATA, MSGFLT_ADD, NULL))
                {
                if ((*pfnChangeWindowMessageFilterEx) (Application->Handle, 0x0049, MSGFLT_ADD, NULL))
                    {
                    Caption = "YEAH" ;
                    }
                }
            }
        }
    }
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 4, 2014 11:41 AM   in response to: Peter Van Hove in response to: Peter Van Hove
Peter wrote:

When I override the CreateWnd() function the application crashes
(not even code in the CreateWnd() function yet).

Did you call the inherited CreateWnd() method? It should look something
like this:

void __fastcall TMyForm::CreateWnd()
{
    TForm::CreateWnd(); // <-- call this first to create the actual HWND
 
    if (!pfnChangeWindowMessageFilterEx)
        return;
 
    if (pfnChangeWindowMessageFilterEx(this->Handle, WM_DROPFILES, MSGFLT_ADD, 
NULL) &&
        pfnChangeWindowMessageFilterEx(this->Handle, WM_COPYDATA, MSGFLT_ADD, 
NULL) &&
        pfnChangeWindowMessageFilterEx(this->Handle, 0x0049, MSGFLT_ADD, 
NULL))
    {
        Caption = "YEAH";
    }
    else
    {
        Caption = "NAY";
    }
}


I also tried this with TreeView->Handle (TreeView being the drop target)

If you want to drag&drop onto the TreeView directly, then you have to enable
the filter for the TreeView's HWND. Which then presents the issue that if
the TreeView's HWND ever gets recreated during the TreeView's lifetime, you
have to re-register the filter when the new HWND is created. To handle that,
you have to subclass the TreeView's WindowProc property so you can detect
when the HWND is created each time, eg:

private:
    TWndMethod DefTreeViewWndProc;
 
__fastcall TMyForm::TMyForm(TComponent *Owner)
    : TForm(Owner)
{
    if (pfnChangeWindowMessageFilterEx)
    {
        DefTreeViewWndProc = TreeView1->WindowProc;
        TreeView1->WindowProc = &TreeViewWndProc;
 
        if (TreeView1->HandleAllocated())
        {
            pfnChangeWindowMessageFilterEx(TreeView1->Handle, WM_DROPFILES, 
MSGFLT_ADD, NULL);
            pfnChangeWindowMessageFilterEx(TreeView1->Handle, WM_COPYDATA, 
MSGFLT_ADD, NULL);
            pfnChangeWindowMessageFilterEx(TreeView1->Handle, 0x0049, MSGFLT_ADD, 
NULL);
        }
    }
}
 
void __fastcall TMyForm::TreeViewWndProc(TMessage &Message)
{
    DefTreeViewWndProc(Message);
 
    if (Message.Msg == WM_CREATE)
    {
        pfnChangeWindowMessageFilterEx(TreeView1->Handle, WM_DROPFILES, MSGFLT_ADD, 
NULL);
        pfnChangeWindowMessageFilterEx(TreeView1->Handle, WM_COPYDATA, MSGFLT_ADD, 
NULL);
        pfnChangeWindowMessageFilterEx(TreeView1->Handle, 0x0049, MSGFLT_ADD, 
NULL);
    }
}


Or else derive and install a new component from TTreeView so you can override
its CreateWnd() method:

class TUIPIDragAndDropTreeView : public TTreeView
{
protected:
    virtual void __fastcall CreateWnd();
    ...
};
 
void __fastcall TUIPIDragAndDropTreeView::CreateWnd()
{
    TTreeView::CreateWnd();
 
    if (pfnChangeWindowMessageFilterEx)
    {
        pfnChangeWindowMessageFilterEx(this->Handle, WM_DROPFILES, MSGFLT_ADD, 
NULL);
        pfnChangeWindowMessageFilterEx(this->Handle, WM_COPYDATA, MSGFLT_ADD, 
NULL);
        pfnChangeWindowMessageFilterEx(this->Handle, 0x0049, MSGFLT_ADD, 
NULL);
    }
}


and this->Handle.

Use the Form's HWND only if you want to drag&drop onto the Form's window
itself rather than a child control of it.

--
Remy Lebeau (TeamB)
Peter Van Hove

Posts: 48
Registered: 5/7/09
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 4, 2014 12:16 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
> Did you call the inherited CreateWnd() method?

DOH

If you want to drag&drop onto the TreeView directly, then you have to enable
the filter for the TreeView's HWND. Which then presents the issue that if
the TreeView's HWND ever gets recreated during the TreeView's lifetime, you
have to re-register the filter when the new HWND is created. To handle that,
you have to subclass the TreeView's WindowProc property so you can detect
when the HWND is created each time, eg:

Tested with TreeView->Handle and now also additionally, per your advice, via the subclassed TreeView's WindowProc

I did not see a handle being recreated but the code is there now.

Sadly it is not working and I have no clue as to why.
The ChangeWindowMessageFilterEx() function returns TRUE
Peter Van Hove

Posts: 48
Registered: 5/7/09
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 5, 2014 2:31 PM   in response to: Peter Van Hove in response to: Peter Van Hove
I'm completely stuck now.
Anybody any idea where I should be looking to get this working ?
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 5, 2014 2:47 PM   in response to: Peter Van Hove in response to: Peter Van Hove
Peter wrote:

Anybody any idea where I should be looking to get this working ?

Other than what has already been explained to you? Nope. If you are still
having problems, you will likely have to contact Microsoft and ask them about
it.

--
Remy Lebeau (TeamB)
Peter Van Hove

Posts: 48
Registered: 5/7/09
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 6, 2014 8:49 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
you will likely have to contact Microsoft and ask them about it.

I'm giving up. I tried everything I can do, tweaked and tested and also went back to VISTA to test. It doesn't work for VISTA - Win7 - Win8.
I have no idea where to start looking and frankly I have lost quite a lot of time on a 'nice to have, but not that important' feature.

Thanks for your help Remy. I sent Anders (D&D component) an email, maybe he has an idea, but if he doesn't know then it will end here for me I'm afraid.
Peter Van Hove

Posts: 48
Registered: 5/7/09
Re: Drag & Drop to elevated app on Windows 8 fails  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 9, 2014 11:54 AM   in response to: Peter Van Hove in response to: Peter Van Hove
As reported I asked Anders Melander to weigh in because I'm using the TDropFileTarget component from Anders' Drag&Drop suite ( http://melander.dk/delphi/dragdrop/ )

His reply:

The Drag and Drop Component Suite implements COM based drag and drop. See Transferring Shell Objects with Drag-and-Drop and the Clipboard ( http://msdn.microsoft.com/en-us/library/windows/desktop/bb776905%28v=vs.85%29.aspx ) for a longer explanation.

The old way of doing drag and drop, from before the COM based was introduced, uses the DragAcceptFiles API function and the WM_DROPFILES message.
ChangeWindowMessageFilter* only works on windows messages, I.e. it only works with the old WM_DROPFILES based drag drop method.

The Drag and Drop Component Suite does not implement WM_DROPFILES based drag and drop but, as I said, one of demos has a Delphi example of how to do it.

A bit of Googling ( https://www.google.dk/search?q=%22c%2B%2B+builder%22+WM_DROPFILES ) found an old example of how to do it with C++ Builder: What a drag ( http://bcbjournal.org/articles/vol1/9709/What_a_drag!.htm ) !
I'm sure there are many more.

This is exactly what I needed and I got it working now, tested on: VISTA, Windows 7 and Windows 8.1, no issues on XP.

For the sake of completeness and should other people be looking for this as wel, here is my code:
PS. I find the 'Elevated' state earlier and use a variable, but that's not really necessary I think. Just setting the filter will work ok.
PS2. I use a TreeView object to 'drop' files on.

in the header (.h)
Classes::TWndMethod TVOldWindowProc;
void __fastcall TVNewWindowProc(Messages::TMessage &Msg) ;
typedef BOOL (WINAPI *PFN_CHANGEWINDOWMESSAGEFILTEREX) (HWND , UINT, DWORD, void* /*PCHANGEFILTERSTRUCT*/ );
PFN_CHANGEWINDOWMESSAGEFILTEREX pfnChangeWindowMessageFilterEx ;
typedef BOOL (WINAPI *PFN_CHANGEWINDOWMESSAGEFILTER) (UINT, DWORD);
PFN_CHANGEWINDOWMESSAGEFILTER pfnChangeWindowMessageFilter ;


in the constructor (.cpp)
DragAcceptFiles(TreeView->Handle, true);
TVOldWindowProc = TreeView->WindowProc; ;
TreeView->WindowProc = TVNewWindowProc;
 
if (Elevated)
    {
    HMODULE hModule = GetModuleHandle (TEXT("user32.dll"));
    pfnChangeWindowMessageFilterEx = (PFN_CHANGEWINDOWMESSAGEFILTEREX) GetProcAddress (hModule, "ChangeWindowMessageFilterEx");
 
    if (!pfnChangeWindowMessageFilterEx) // < Windows 7
        {
        pfnChangeWindowMessageFilter = (PFN_CHANGEWINDOWMESSAGEFILTER) GetProcAddress (hModule, "ChangeWindowMessageFilter"); // VISTA
        }
 
    if (pfnChangeWindowMessageFilterEx)
        {
        if ((*pfnChangeWindowMessageFilterEx) (TreeView->Handle, WM_DROPFILES, MSGFLT_ADD, NULL) &&
            (*pfnChangeWindowMessageFilterEx) (TreeView->Handle, WM_COPYDATA, MSGFLT_ADD, NULL)  &&
            (*pfnChangeWindowMessageFilterEx) (TreeView->Handle, 0x0049, MSGFLT_ADD, NULL)       )
            {
            /* Success */
            }
        }
    else if (pfnChangeWindowMessageFilter)
        {
        if ((*pfnChangeWindowMessageFilter) (WM_DROPFILES, MSGFLT_ADD) &&
            (*pfnChangeWindowMessageFilter) (WM_COPYDATA, MSGFLT_ADD)  &&
            (*pfnChangeWindowMessageFilter) (0x0049, MSGFLT_ADD)       )
            {
            /* Success */
            }
        }
    }


As can be seen in previous code, I subclass the TreeView's WindowProc property:
PS. InitImageFile() is my own code, doing stuff with the dropped file.

void __fastcall TFinder::TVNewWindowProc(Messages::TMessage &Msg)
{
if( TVOldWindowProc ) TVOldWindowProc( Msg );
 
if (Msg.Msg == WM_CREATE)
    {
    if (Elevated)
        {
        if (pfnChangeWindowMessageFilterEx)
            {
            if ((*pfnChangeWindowMessageFilterEx) (TreeView->Handle, WM_DROPFILES, MSGFLT_ADD, NULL) &&
                (*pfnChangeWindowMessageFilterEx) (TreeView->Handle, WM_COPYDATA, MSGFLT_ADD, NULL)  &&
                (*pfnChangeWindowMessageFilterEx) (TreeView->Handle, 0x0049, MSGFLT_ADD, NULL)       )
                {
                /* Success */
                }
            }
        else if (pfnChangeWindowMessageFilter)
            {
            if ((*pfnChangeWindowMessageFilter) (WM_DROPFILES, MSGFLT_ADD) &&
                (*pfnChangeWindowMessageFilter) (WM_COPYDATA, MSGFLT_ADD)  &&
                (*pfnChangeWindowMessageFilter) (0x0049, MSGFLT_ADD)       )
                {
                /* Success */
                }
            }
        }
    }
else if (Msg.Msg == WM_DROPFILES)
    {
    String FileName;
    FileName.SetLength(MAX_PATH*2);
 
    int Cnt = DragQueryFile((HDROP)Msg.WParam, -1, NULL, NULL);
 
    for (int x = 0 ; x < Cnt ; x++)
        {
        DragQueryFile((HDROP)Msg.WParam, x, FileName.c_str(), FileName.Length());
 
        if (FileExists(FileName))
            {
            InitImageFile(FileName, NULL) ;
            }
        }
 
    DragFinish((HDROP)Msg.WParam);
    }
}
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Drag & Drop to elevated app on Windows 8 fails [Edit]
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 9, 2014 12:15 PM   in response to: Peter Van Hove in response to: Peter Van Hove
Peter wrote:

As reported I asked Anders Melander to weigh in because I'm using the
TDropFileTarget component from Anders' Drag&Drop suite

I assume then that you were not originally using it from the beginning?
Were you trying to handle WM_DROPFILES only?

ChangeWindowMessageFilter* only works on windows messages, I.e. it
only works with the old WM_DROPFILES based drag drop method.

It is a little more complex than that. Old OS versions did used to use WM_DROPFILES,
but modern OS versions always use COM internally for drag&drop between apps.
To not break legacy apps, if the target window has registered to receive
WM_DROPFILES and the dragged COM object provides CF_HDROP data, Windows automatically
generates a WM_DROPFILES message for backwards compatibility.

if (Elevated)

I would suggest calling ChangeWindowMessageFilter*() regardless of whether
the app is running Elevated or not.

else if (pfnChangeWindowMessageFilter)

You only need to call ChangeWindowMessageFilter() once, since it is a process-wide
filter, so it is not needed on every HWND (re)creation. That is what ChangeWindowMessageFilterEx()
is for instead.

--
Remy Lebeau (TeamB)
Peter Van Hove

Posts: 48
Registered: 5/7/09
Re: Drag & Drop to elevated app on Windows 8 fails [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 9, 2014 12:21 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
I assume then that you were not originally using it from the beginning?
Were you trying to handle WM_DROPFILES only?

I simply needed to know the filenames that were dropped yes and I was using the mentioned component for that.
I had no idea about the deeper mechanics, COM vs messaging etc.

You only need to call ChangeWindowMessageFilter() once, since it is a process-wide filter, so it is not needed on every HWND (re)creation. That is what ChangeWindowMessageFilterEx() is for instead.

True !
Fixed.
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02