7.3 Creating a Drop Handler

only for RuBoard - do not distribute or recompile

7.3 Creating a Drop Handler

We need to add a new class to the RadEx project that will implement IPersistFile and IDropTarget . Call the file clsDropHandler.cls . Its declarations section begins as follows :

 'clsDropHandler.cls

Implements IDropTarget
Implements IPersistFile 

7.3.1 Implementing IPersistFile

The only method that we need to implement on this interface is Load . The code is the same as the code we used to create the icon handler back in Chapter 5. Example 7.1 contains the implementation.

Example 7.1. IPersistFile::Load Implementation
 'clsDropHandler.cls

Private m_sTargetFile As String

Private Sub IPersistFile_Load(
    ByVal pszFileName As VBShellLib.LPCOLESTR, _ 
    ByVal dwMode As VBShellLib.DWORD)

    m_sTargetFile = Space(255)
    CopyMemory ByVal StrPtr(m_sTargetFile), _ 
               ByVal pszFileName, _ 
               Len(m_sTargetFile)
    
End Sub 

7.3.2 Implementing IDropTarget

The most interesting aspect of implementing the IDropTarget interface (and, in particular, its DragEnter method) concerns the POINTL parameter to the DragEnter method. Notice from our earlier presentation of the method's syntax that it is an [in] parameter; therefore, it is not a pointer. We have a slight problem here, because Visual Basic does not allow UDTs to be passed ByVal , which is what is going on here. POINTL is a structure that contains the location of the mouse in the drop area. It is defined like this:

 typedef struct _POINTL { 
    LONG x; 
    LONG y; 
} POINTL; 

Fortunately, we do not need this point information, so there is a simple workaround. Instead of passing a point, we can use two Longs, which will occupy the same space on the stack as a POINTL structure. The final definition for DragEnter looks like this:

 HRESULT DragEnter
(
    [in] IDataObject *pDataObj,
    [in] KEYSTATES grfKeyState,
    [in] long x,
    [in] long y,
    [in, out] DROPEFFECT *pdwEffect
); 

DragOver and Drop will also use two Long values in place of POINTL .

7.3.2.1 DragEnter

With that said, let's get on to the actual implementation of IDropTarget . We only need to implement two methods ( DragEnter and Drop ) to satisfy our needs. Let's look at the DragEnter implementation, which is shown in Example 7.2, and then we'll discuss how it works.

Example 7.2. DragEnter Implementation
 Private m_dwDropKey As KEYSTATES
Private m_dwMouseKey As KEYSTATES

Private Sub IDropTarget_DragEnter(
    ByVal pDataObj As VBShellLib.IDataObject, _ 
    ByVal grfKeyState As VBShellLib.KEYSTATES, _ 
    ByVal x As Long, _ 
    ByVal y As Long, _ 
    pdwEffect As VBShellLib.DROPEFFECT)

    
    pdwEffect = DROPEFFECT_COPY
    
    'Get keyboard state

    'Does NOT take into account multiple keys being held down

    If grfKeyState And MK_CONTROL Then
        m_dwDropKey = MK_CONTROL
    ElseIf grfKeyState And MK_SHIFT Then
        m_dwDropKey = MK_SHIFT
    ElseIf grfKeyState And MK_ALT Then
        m_dwDropKey = MK_ALT
    Else
        m_dwDropKey = 0
    End If
    
    'Get mouse state
    If grfKeyState And MK_LBUTTON Then
        m_dwMouseKey = MK_LBUTTON
    ElseIf grfKeyState And MK_RBUTTON Then
        m_dwMouseKey = MK_RBUTTON
    End If
    
End Sub 

The first parameter given to us by the shell is an IDataObject reference. Remember clsDropFiles? This is the class we used in Chapter 6, to implement IShellExtInit::Initialize . We could pass this IDataObject reference to an instance of clsDropFiles to get a list of all the source filenames for our Drop implementation. But we won't, because in this instance it would be a little slow. It would be called every time we enter the drop target, which is way too often. Instead, we'll wait until Drop is actually called to get a list of the source filenames. So, for now, we can ignore the pDataObj parameter. The parameters we are really interested in are pdwEffect and grfKeyState .

The pdwEffect parameter is set to one of the DROPEFFECT enumeration values and is used to visually indicate what the result of a drop operation would be. The shell conveys this by changing the cursor to one of the shapes shown in Figure 7.1. As the cursor moves over a drop target, the shell changes the cursor to visually show what type of drop operation is occurring. To cancel a drop operation, pdwEffect is set to DROPEFFECT_NONE .

Figure 7.1. DROPEFFECT operations
figs/vshl.0701.gif

The grfKeyState parameter contains keyboard- and mouse-state information.

Our implementation sets pdwEffect to DROPEFFECT_COPY regardless of the keys pressed. This will cause the cursor to change (as shown in Figure 7.1), giving us a visual cue that the files can be dropped. Keyboard and mouse states are stored in two separate private data members . These values will be used when we implement Drop .

7.3.2.2 Drop

Drop is where all the action takes place. This is the method that is called when a file is dropped on a drop target. Our implementation, which is shown in Example 7.3, is fairly straightforward. Let's take a look.

Example 7.3. Drop Implementation
 Private Sub IDropTarget_Drop(
    ByVal pDataObj As VBShellLib.IDataObject, _ 
    ByVal grfKeyState As VBShellLib.KEYSTATES, _ 
    ByVal x As Long, _
    ByVal y As Long, _ 
    pdwEffect As VBShellLib.DROPEFFECT)
    
    Dim i As Integer
    Dim sMsg As String  Set m_clsDropFiles = New clsDropFiles
    m_clsDropFiles.GetDropFiles pDataObj, ".rad"
    
    If (m_clsDropFiles.Count = 0) Then
        MsgBox "Only RAD files can be dropped here!", _ 
            vbOKOnly, _ 
            "RAD Drop Handler"
        Exit Sub
    End If  If m_dwMouseKey = MK_LBUTTON Then
        sMsg = "MK_LBUTTON" & vbCrLf
    End If
    
    If m_dwMouseKey = MK_RBUTTON Then
        sMsg = "MK_RBUTTON" & vbCrLf
    End If
    
    Select Case m_dwDropKey
        Case MK_CONTROL
            sMsg = sMsg & "Drop + CTRL"
        Case MK_SHIFT
            sMsg = sMsg & "Drop + SHIFT"
        Case MK_ALT
            sMsg = sMsg & "Drop + ALT"
        Case Else
            sMsg = sMsg & "Normal Drop"
    End Select
    
    sMsg = sMsg & vbCrLf & vbCrLf
    sMsg = sMsg & "Drop File(s):" & vbCrLf
    
    For i = 0 To m_clsDropFiles.Count - 1
        sMsg = sMsg & m_clsDropFiles.Files(i) & vbCrLf
    Next i
    
    sMsg = sMsg & vbCrLf
    sMsg = sMsg & "Target File: " & m_sTargetFile
    
    MsgBox sMsg, vbOKOnly, "RAD Drop Handler"
    
End Sub 

The shell passes in a reference to IDataObject , which we in turn pass on to an instance of clsDropFiles (see Chapter 5). If our file count is at this point, we know that the files dropped were not .rad files, and we can display an error message. Otherwise, a list of the target files, the name of the drop file, and the keyboard and mouse states are displayed in a message box like the one shown in Figure 7.2.

Figure 7.2. Drop handler info
figs/vshl.0702.gif
only for RuBoard - do not distribute or recompile