VIP

[Solved] Drag and Drop with UAC

7 posts in this topic

#1 ·  Posted (edited)

#RequireAdmin
#include <WinAPISys.au3>
#include <WindowsConstants.au3>

_WinAPI_ChangeWindowMessageFilterEx ( $hWnd, $iMsg, $iAction )

;_WinAPI_ChangeWindowMessageFilterEx( $hWnd, $WM_DROPFILES, $MSGFLT_ALLOW)
;_WinAPI_ChangeWindowMessageFilterEx( $hWnd, $WM_COPYDATA, $MSGFLT_ALLOW)
;_WinAPI_ChangeWindowMessageFilterEx( $hWnd, $WM_COPYGLOBALDATA, $MSGFLT_ALLOW)

; $WM_COPYDATA = 0x004A - $WM_DROPFILES = 0x0233 - $WM_COPYGLOBALDATA = 0x0049 - $MSGFLT_ALLOW = 1 - $MSGFLT_DISALLOW = 2

Example:

#RequireAdmin
Opt("TrayAutoPause", 0)
#include <WinAPISys.au3>
#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <ButtonConstants.au3>
Global $AppWindows = GUICreate("Dao Van Trong  - Trong.CF", 320, 50, -1, -1, BitOR($WS_BORDER, $WS_POPUP), BitOR($WS_EX_ACCEPTFILES, $WS_EX_TOPMOST, $WS_EX_WINDOWEDGE))
Global $AppTitle = GUICtrlCreateLabel("=== Drag and drop UAC ===", 56, 0, 210, 25, $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)
GUICtrlSetState(-1, $GUI_DROPACCEPTED)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)
GUICtrlSetFont(-1, 9, 800)
Global $AppTask = GUICtrlCreateLabel("Drag and drop files here ", 56, 24, 220, 17, $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)
GUICtrlSetState(-1, $GUI_DROPACCEPTED)
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)
GUICtrlSetFont(-1, 9, 500)
Global $xCLOSE = GUICtrlCreateButton("X", 308, 0, 12, 12, BitAND($BS_MULTILINE, $BS_VCENTER, $BS_FLAT))
GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)
GUICtrlSetState(-1, $GUI_DROPACCEPTED)
GUISetState(@SW_SHOW)
_WinAPI_ChangeWindowMessageFilterEx($AppWindows, $WM_DROPFILES, $MSGFLT_ALLOW)
_WinAPI_ChangeWindowMessageFilterEx($AppWindows, $WM_COPYDATA, $MSGFLT_ALLOW)
_WinAPI_ChangeWindowMessageFilterEx($AppWindows, $WM_COPYGLOBALDATA, $MSGFLT_ALLOW)
Global $__aDropFiles
GUIRegisterMsg($WM_DROPFILES, "WM_DROPFILES")
Local $nMsg
While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_DROPPED
            If $__aDropFiles[0] > 0 Then
                For $i = 1 To $__aDropFiles[0]
                    ConsoleWrite($__aDropFiles[$i] & @CRLF)
                    GUICtrlSetData($AppTask, $__aDropFiles[$i])
                Next
            EndIf
        Case $GUI_EVENT_CLOSE, $xCLOSE
            Exit
    EndSwitch
WEnd
Func WM_DROPFILES($hWnd, $iMsg, $iwParam, $ilParam)
    #forceref $hWnd, $ilParam
    Switch $iMsg
        Case $WM_DROPFILES
            Local $aReturn = _WinAPI_DragQueryFileEx($iwParam)
            If IsArray($aReturn) Then
                $__aDropFiles = $aReturn
            Else
                Local $aError[1] = [0]
                $__aDropFiles = $aError
            EndIf
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DROPFILES

 

Edited by Trong
Example

Regards,
 

Share this post


Link to post
Share on other sites



An application started with Admin can only get files from apllications which also started with Admin.

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

Thanks !

Edited by Trong

Regards,
 

Share this post


Link to post
Share on other sites

@Trong: would you mind restating (in your first post) what the problem is that you've solved?

_WinAPI_ChangeWindowMessageFilterEx appears to be very useful ... but are you working only with AU3 scripts? ... or have you tried it with other applications?  If so, what is your method for doing that?

Thanks for opening this topic.

 

1 person likes this

Share this post


Link to post
Share on other sites

His problem was, he could not drop files on his GUI's ListView. Script requires Admin privileges (#RequireAdmin). I found after @AdmiralAlex post this solution:

;found https://www.autoitscript.com/forum/topic/124406-drag-and-drop-with-uac/
#RequireAdmin
#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>
#include <WindowsConstants.au3>
Opt("GuiOnEventMode", 1)

Global $DropFilesArr[1]
Global $FilesAllowedMask[4] = [3, ".txt", ".exe", "D"]

$mainFrm = GUICreate("Drop Multiple Files to LV Demo", 500, 400, -1, -1, -1, $WS_EX_ACCEPTFILES+$WS_EX_TOPMOST)
_ChangeWindowMessageFilterEx($mainFrm, 0x233, 1)
_ChangeWindowMessageFilterEx($mainFrm, $WM_COPYDATA, 1)
_ChangeWindowMessageFilterEx($mainFrm, 0x0049, 1)

GUISetOnEvent($GUI_EVENT_CLOSE, "MainEvents")
GUISetOnEvent($GUI_EVENT_DROPPED, "MainEvents")

GUIRegisterMsg(0x233, "WM_DROPFILES_FUNC")

GUICtrlCreateLabel("Drag to the List View some file(s)", 60, 20)

$ListView = GUICtrlCreateListView("Col1", 50, 50, 400, 300, -1, $WS_EX_CLIENTEDGE)
GUICtrlSendMsg(-1, $LVM_SETEXTENDEDLISTVIEWSTYLE, $LVS_EX_HEADERDRAGDROP, $LVS_EX_HEADERDRAGDROP)
GUICtrlSetState(-1, $GUI_DROPACCEPTED)

GUISetState()

While 1
    Sleep(100)
WEnd

Func MainEvents()
    Switch @GUI_CtrlId
        Case $GUI_EVENT_CLOSE
            Exit
        Case $GUI_EVENT_DROPPED
            $NotValidExtPath = ""
            $ValidExtCount = 0
            For $i = 1 To UBound($DropFilesArr)-1
                $ValidExtCount += 1
                GUICtrlCreateListViewItem($DropFilesArr[$i], $ListView)
            Next
            If $ValidExtCount > 0 Then GUICtrlSendMsg($ListView, $LVM_SETCOLUMNWIDTH, 0, -1)
    EndSwitch
EndFunc

Func _IsValidExt($sPath)
    For $i = 1 To $FilesAllowedMask[0]
        If StringRight($sPath, 4) = $FilesAllowedMask[$i] And _
            Not StringInStr(FileGetAttrib($sPath), $FilesAllowedMask[$i]) Then Return True
    Next
    Return False
EndFunc

Func WM_DROPFILES_FUNC($hWnd, $msgID, $wParam, $lParam)
    Local $nSize, $pFileName
    Local $nAmt = DllCall("shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", 0xFFFFFFFF, "ptr", 0, "int", 255)
    For $i = 0 To $nAmt[0] - 1
        $nSize = DllCall("shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", $i, "ptr", 0, "int", 0)
        $nSize = $nSize[0] + 1
        $pFileName = DllStructCreate("char[" & $nSize & "]")
        DllCall("shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", $i, "ptr", _
            DllStructGetPtr($pFileName), "int", $nSize)
        ReDim $DropFilesArr[$i + 2]
        $DropFilesArr[$i+1] = DllStructGetData($pFileName, 1)
        $pFileName = 0
    Next
    $DropFilesArr[0] = UBound($DropFilesArr)-1
EndFunc

Func _ChangeWindowMessageFilterEx($hWnd, $iMsg, $iAction)
    Local $aCall = DllCall("user32.dll", "bool", "ChangeWindowMessageFilterEx", _
            "hwnd", $hWnd, _
            "dword", $iMsg, _
            "dword", $iAction, _
            "ptr", 0)
    If @error Or Not $aCall[0] Then Return SetError(1, 0, 0)
    Return 1
EndFunc

When you remove Lines 12-14 which uses the func _ChangeWindowMessageFilterEx you have the problem similar to @Tong's. When you want to use this func in a other programming language you have to implement the DLL-call for User32.dll.

Share this post


Link to post
Share on other sites

His problem was, he could not drop files on his GUI's ListView. Script requires Admin privileges (#RequireAdmin). I found after @AdmiralAlex post this solution:

[..]
    Local $aCall = DllCall("user32.dll", "bool", "ChangeWindowMessageFilterEx", _
  [...]

Windows 7 or later Only ChangeWindowMessageFilterEx 

XP and All :

#include <WinAPISys.au3>
_WinAPI_ChangeWindowMessageFilterEx ( $hWnd, $iMsg, $iAction )


 


Regards,
 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Similar Content

    • perlabsrat
      By perlabsrat
      Hello,
      I found a good drag and drop example here: https://www.autoitscript.com/forum/topic/28062-drop-multiple-files-on-any-control/
      I have been searching but not finding a way to do a variant of this example.
      I want to have a form with 2 or more list box controls on the form that have $GUI_DROPACCEPTED enabled. I then want to be able to drop separate groups of files on each list box  so that I have 2 separate list of dropped items that I can act upon independently rather than this example which only has one target.
      I have modified the example to add the 2nd list box but I am not sure how to determine which control hwnd got the drop event and then only add the files to that list box. In the current example both controls add the files to $hList1
       
      Apparently $hWnd  in WM_DROPFILES_FUNC() is not the control that triggers the event as I compared its value to the hwnd of each control.
      hwnd from $hList1 = 0x002F0BB8 hwnd from $hList2 = 0x003C06E0 hwnd from WM_DROPFILES_FUNC = 0x002F1180 hwnd from WM_DROPFILES_FUNC = 0x002F1180  
      Any ideas?

       
      ;Example from https://www.autoitscript.com/forum/topic/28062-drop-multiple-files-on-any-control/ #include <GUIConstants.au3> ;~ Global $WM_DROPFILES = 0x233 Global $gaDropFiles[1], $str = "" ### Koda GUI section start ### ;~ $hGUI = GUICreate("Test", 400, 200, 219, 178, -1, BitOR($WS_EX_ACCEPTFILES, $WS_EX_TOPMOST)) ;~ $hList = GUICtrlCreateList("", 5, 5, 390, 190) ;~ GUICtrlSetState (-1, $GUI_DROPACCEPTED) Global $hGUI = GUICreate("Test", 573, 419, 219, 178, -1, BitOR($WS_EX_ACCEPTFILES, $WS_EX_TOPMOST, $WS_EX_WINDOWEDGE)) Global $hList1 = GUICtrlCreateList("", 5, 5, 390, 188) GUICtrlSetState(-1, $GUI_DROPACCEPTED) GUICtrlSetData(-1, "List1") Global $hList2 = GUICtrlCreateList("", 42, 205, 390, 188) GUICtrlSetState(-1, $GUI_DROPACCEPTED) GUICtrlSetData(-1, "List2") ConsoleWrite("hwnd from $hList1 = " & GUICtrlGetHandle($hList1) & @CRLF) ConsoleWrite("hwnd from $hList2 = " & GUICtrlGetHandle($hList2) & @CRLF) GUISetState(@SW_SHOW) ### Koda GUI section end ### GUIRegisterMsg($WM_DROPFILES, "WM_DROPFILES_FUNC") While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit Case $GUI_EVENT_DROPPED $str = "" For $i = 0 To UBound($gaDropFiles) - 1 $str &= "|" & $gaDropFiles[$i] Next GUICtrlSetData($hList1, $str) EndSwitch WEnd Func WM_DROPFILES_FUNC($hWnd, $msgID, $wParam, $lParam) ConsoleWrite("hwnd from WM_DROPFILES_FUNC = " & $hGUI & @CRLF) Local $nSize, $pFileName Local $nAmt = DllCall("shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", 0xFFFFFFFF, "ptr", 0, "int", 255) For $i = 0 To $nAmt[0] - 1 $nSize = DllCall("shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", $i, "ptr", 0, "int", 0) $nSize = $nSize[0] + 1 $pFileName = DllStructCreate("char[" & $nSize & "]") DllCall("shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", $i, "ptr", DllStructGetPtr($pFileName), "int", $nSize) ReDim $gaDropFiles[$i + 1] $gaDropFiles[$i] = DllStructGetData($pFileName, 1) $pFileName = 0 Next EndFunc ;==>WM_DROPFILES_FUNC  
       
    • tcurran
      By tcurran
      Here's a short UDF that will, at least in most cases, detect whether a window can be copied from or pasted to programmatically--for example, by Send()ing ctl-c, ctl-v. This is often disabled when programs (like your AutoIt script) run at a lower UAC integrity level than the application they are trying to operate on.
      #include <WinAPI.au3> Func _WindowIsPasteable($handle) ;accepts window handle; returns true or false whether a window will accept Ctl-C, Ctl-V Local $bCanPaste = True Local $hTestWindowPID = 0 Local $hTestWindowTID = _WinAPI_GetWindowThreadProcessId($handle, $hTestWindowPID) _WinAPI_AttachThreadInput(_WinAPI_GetCurrentThreadId(), $hTestWindowTID, True);attach to window we want to paste into $bCanPaste = _WinAPI_GetFocus() ;Test whether window is paste-able--returns False if it is not _WinAPI_AttachThreadInput(_WinAPI_GetCurrentThreadId, $hTestWindowTID, False);detach from window thread Return $bCanPaste EndFunc Pass it a window handle; it returns true or false whether a window will accept programmatic pasting. The function may not work on the CMD window, since it handles the clipboard uniquely.
      This function works by attaching to the program thread of the window whose handle it receives, then attempting to perform a GetFocus on that thread. In most cases, the attempt will fail if the window will not accept programmatic copy-paste.
    • dreivilo47
      By dreivilo47
      When I use the following code I receive an UAC message:
       
      #RequireAdmin RunWait("msiexec /i winzip205-64.msi /quiet") Exit How can I hide (bypass) the UAC message?
    • Mbee
      By Mbee
      (Nevermind, I solved it)
       
    • Jon
      By Jon
      I'm writing a set of PowerShell scripts/library for Windows 10 builds. One thing I often want to do is to browse to the location of a script that is part of a larger group of scripts and run it manually to do an install or make a one off change. So I like all my scripts to work well whether run from a task sequence or double-clicked in explorer.
      Most of my build scripts rely on having admin rights so I like to make them able to self-elevate if required - or at least give an error message. In PowerShell 4.0 (Windows 8.1) they added the #Requires -RunAsAdministrator statement but this won't do it for you - it just causes the script to abort if not admin. 
      Below is a PowerShell script that does the following:
      Checks for admin rights using the Test-IsAdmin function If not admin: Get the full script path and working directory using the Get-UNCFromPath function If the paths are mapped drives then get the UNC version (drive mappings are lost when elevating from user to admin in most configurations) Execute PowerShell.exe with the UNC path of the script and the RunAs verb to trigger elevation. ExecutionPolicy is also set to Bypass on the command line. The working directory is also set to the UNC path version. Waits for the new process to finish, and captures its return code Exits using the same return code Script is as follows:
      # Test if admin function Test-IsAdmin() { # Get the current ID and its security principal $windowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent() $windowsPrincipal = new-object System.Security.Principal.WindowsPrincipal($windowsID) # Get the Admin role security principal $adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator # Are we an admin role? if ($windowsPrincipal.IsInRole($adminRole)) { $true } else { $false } } # Get UNC path from mapped drive function Get-UNCFromPath { Param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)] [String] $Path) if ($Path.Contains([io.path]::VolumeSeparatorChar)) { $psdrive = Get-PSDrive -Name $Path.Substring(0, 1) -PSProvider 'FileSystem' # Is it a mapped drive? if ($psdrive.DisplayRoot) { $Path = $Path.Replace($psdrive.Name + [io.path]::VolumeSeparatorChar, $psdrive.DisplayRoot) } } return $Path } # Relaunch the script if not admin function Invoke-RequireAdmin { Param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)] [System.Management.Automation.InvocationInfo] $MyInvocation) if (-not (Test-IsAdmin)) { # Get the script path $scriptPath = $MyInvocation.MyCommand.Path $scriptPath = Get-UNCFromPath -Path $scriptPath # Need to quote the paths in case of spaces $scriptPath = '"' + $scriptPath + '"' # Build base arguments for powershell.exe [string[]]$argList = @('-NoLogo -NoProfile', '-ExecutionPolicy Bypass', '-File', $scriptPath) # Add $argList += $MyInvocation.BoundParameters.GetEnumerator() | Foreach {"-$($_.Key)", "$($_.Value)"} $argList += $MyInvocation.UnboundArguments try { $process = Start-Process PowerShell.exe -PassThru -Verb Runas -Wait -WorkingDirectory $pwd -ArgumentList $argList exit $process.ExitCode } catch {} # Generic failure code exit 1 } } # Relaunch if not admin Invoke-RequireAdmin $script:MyInvocation # Running as admin if here $wshell = New-Object -ComObject Wscript.Shell $wshell.Popup("Script is running as admin", 0, "Done", 0x1) | Out-Null