Drag and Drop

The drag-and-drop functions in SHELL32.DLL allow an application to register itself with the system for drag-and-drop notifications, to query information about the file or files dropped, and to permit or prevent a drop. Before an application can process a drop, it must register itself with the system by using the DragAcceptFiles function. In SHELLFUN, this happens in the processing of the WM_CREATE message for the main window:

case WM_CREATE:
// Indicate that drag and drop is OK.
DragAcceptFiles (hWnd, TRUE);

After making this call, the application receives notification of a drop in the form of the WM_DROPFILES message. In the wParam parameter of the message, the system packages a handle to a structure containing information about the drop object. Using this handle, the application can query information such as the number of files dropped, the filename(s), and the location of the drop. To query the number of files being dropped, you can pass a special value (-1) to the DragQueryFile function in the second parameter. The application can get the name of the file being dropped by subsequent calls to DragQueryFile, with the second parameter specifying the index to the file. The drop operation is completed with a call to DragFinish.

In the SHELLFUN sample, I allow only one file to be dropped at a time. The dropped file is opened and read into a data buffer, to be displayed in a multiline edit control.

case WM_DROPFILES:
{
// A file is being dropped.
int iFiles;
char lpszFile [MAX_PATH];
HDROP hDropInfo = (HANDLE)wParam;

// Get the number of files.
iFiles = DragQueryFile (hDropInfo, (DWORD)(-1), (LPSTR)NULL, 0);

if (iFiles != 1)
MessageBox (hWnd, "One file at a time, please.", NULL, MB_OK);

else
{
HANDLE hFile;
DWORD dwFileSize, dwBytesRead;
char *lpBufPtr;

DragQueryFile (hDropInfo, 0, lpszFile, sizeof (lpszFile));
// Open the file.
if ((hFile = CreateFile (lpszFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)NULL)) == (HANDLE)(-1))
{
MessageBox (hWnd, “File open failed.", NULL, MB_OK);
break;
}

// Get the size of the file.
dwFileSize = GetFileSize (hFile, NULL);
if (dwFileSize == 0xFFFFFFFF)
{
MessageBox (NULL, "GetFileSize failed!", NULL, MB_OK);
break;
}

// Allocate a buffer to read the file into.
lpBufPtr = (char *) malloc (dwFileSize);
if (lpBufPtr == NULL)
{
MessageBox (NULL, "malloc failed!", NULL, MB_OK);
CloseHandle (hFile);
break;
}

// Read the file contents into the buffer.
ReadFile (hFile, (LPVOID)lpBufPtr, dwFileSize, &dwBytesRead,
NULL);

if (dwBytesRead == 0)
{
MessageBox (hWnd, "Zero bytes read.", NULL, MB_OK);
break;
}

// Update the multiline edit control with the file contents.
SendMessage (hwndEdit, WM_SETTEXT, 0, (LPARAM)lpBufPtr);

// Close the file.
CloseHandle (hFile);

free (lpBufPtr);
}

// Signal that the drag-and-drop operation is over.
DragFinish (hDropInfo);

break;
}

Figure 11-1 shows the result of dragging a file named A Sample Text File and dropping it in the SHELLFUN window. Because the SHELLFUN sample simply reads the file into a buffer and does no processing, it's easiest to do this with a text file. (You can drop a binary file, but it'll look funny.)

Figure 11-1.

The result of a drag-and-drop operation in the SHELLFUN sample.

When the application no longer needs to support drag and drop, it must unregister itself by using the DragAcceptFiles function as follows:

DragAcceptFiles (hWnd, FALSE);

To determine where a file is being dropped, the application can use another drag-and-drop function, DragQueryPoint. The DragQueryPoint function returns the drop point, and the application can then draw the item at that point, as you can see here:

// A file is being dropped.
POINT pt;
int idx;
char lpszFile [MAX_PATH];
HDC hDC;

// Find out where the drop is.
DragQueryPoint (hDropInfo, &pt);

// Get a DC.
hDC = GetDC (hWnd);

// For each file dropped, write its name out to the client.
for (idx = 0;
DragQueryFile (hDropInfo, idx, lpszFile, sizeof (lpszFile));
pt.y += 20, idx++)
TextOut (hWnd, pt.x, pt.y, lpszFile, sizeof (lpszFile));

// Signal that the drag-and-drop operation is over.
DragFinish (hDropInfo);

// Release the DC.
ReleaseDC (hDC, hWnd);

NOTE: What you've just seen is a message-based (Windows 3.1) method of using the drag-and-drop functions in the user interface library. A new, alternative method uses OLE to perform drag-and-drop operations. The message-based method is fine for simple drag-and-drop tasks, whereas the OLE method is far richer and allows more complex operations. This book does not cover the OLE-based method in detail, although Chapter 14 contains a sample that shows how to implement the IDropSource interface for drag and drop. (See “Supporting Drag and Drop,” page 355.) For more information about drag and drop using OLE, you can refer to the Win32 SDK, the Microsoft Visual C++ 2 documentation, and Inside OLE, 2d edition, by Kraig Brockschmidt (Microsoft Press, 1995).